SpiralReality Dual Brain LLM is a research playground for dual-agent orchestration: a “left brain” (drafting/speech) and a “right brain” (deepening/lead) collaborate via an in-memory corpus callosum while sharing short-term and long-term memory.
The goal is not “AGI”. The goal is observability: make it easy to see why the system chose a collaboration pattern via metrics, traces, and an explicit “architecture path” view — without mixing those internals into the chat output.
This repository intentionally optimizes for:
- Experimenting with orchestration policies (when to consult, when to braid, when to stay solo)
- Inspectable runtime behavior (telemetry, inner-dialogue steps, coherence signals, architecture path)
- A stable REST surface without Python web frameworks (C# Minimal API gateway → Python engine over stdio/pipes;
pipesdefault on macOS/Linux)
- Run a browser UI with clean chat + separate metrics window.
- Watch policy + module activation change as prompts shift from “small talk” to “hard reasoning”.
- Upload images via
POST /v1/blobsand reference them onPOST /v1/process(multimodal plumbing). - Inspect any turn via
GET /v1/trace/{qid}(metrics + dialogue flow + telemetry). - Optionally persist memory/telemetry to Postgres.
The engine can run without external LLMs (deterministic heuristic fallbacks), but you’ll want real models for quality.
Example (OpenAI-style):
export LLM_PROVIDER=openai
export LLM_MODEL_ID=gpt-4o-mini
export OPENAI_API_KEY=sk-...Tip: if you plan to use images at all, start with a vision-capable model even for text-only turns to keep the “thinking substrate” consistent across the session.
Security note:
- Never paste real API keys into chat logs, issues, or this repository. If a key is ever exposed, revoke/rotate it immediately.
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
# Optional (Postgres-backed state)
pip install -r requirements-pg.txt
# Optional (tests)
pip install -r requirements-dev.txtPrereqs:
- Python 3.10+
- .NET 8 SDK (
dotnetonPATH; on some macOS setups it may live at/usr/local/share/dotnet/dotnet)
Run:
export DUALBRAIN_REPO_ROOT="$PWD"
dotnet run --project csharp/SrDualBrain.Gateway --urls http://127.0.0.1:8080IPC transport:
- macOS/Linux: defaults to
pipes(Unix domain sockets under the hood) for fast blob/image uploads. - Windows: defaults to
stdio(pipestransport is not supported by the current Python engine).
Optional: force a transport explicitly:
export DUALBRAIN_ENGINE_TRANSPORT=pipes # macOS/Linux only
export DUALBRAIN_ENGINE_TRANSPORT=stdio # portable fallbackNotes (macOS):
- .NET named pipes use Unix domain sockets under the hood and have a strict path length limit.
- If you hit socket path length issues, set
TMPDIR=/tmp/before runningdotnet run.
Optional gateway timeout override (useful for long generations / auto-continue):
export DUALBRAIN_ENGINE_TIMEOUT_SECONDS=120Optional engine-side corpus-callosum timeout (bounds right-brain waits):
# Default: 20000 (20s)
export DUALBRAIN_CALLOSUM_TIMEOUT_MS=20000
# Trade latency for accuracy (longer right-brain / System2 waits):
export DUALBRAIN_TIMEOUT_MULTIPLIER=1.5
export DUALBRAIN_SYSTEM2_TIMEOUT_MULTIPLIER=1.2 # optional
export DUALBRAIN_TIMEOUT_MAX_MS=180000 # optional safety cap
# Encourage deeper System2 refinement (max supported: 3):
export DUALBRAIN_SYSTEM2_ROUND_TARGET_MIN=3Open:
http://127.0.0.1:8080/
Recommended “research loop”:
- Keep the main window chat-only.
- Click Pop out metrics to open
/metrics.htmlin a separate window and monitor:- coherence/tension/routing/policy/latency
- metacognition (drift / repetition / cleanup)
- active modules
- architecture path (stage → modules)
- executive memo (out-of-band)
curl -s http://127.0.0.1:8080/v1/process \
-H 'content-type: application/json' \
-d '{
"session_id":"demo",
"question":"Explain (briefly) what the architecture path is.",
"leading_brain":"auto",
"executive_mode":"assist",
"return_telemetry": true,
"return_dialogue_flow": true,
"llm": { "provider":"openai", "model":"gpt-4o-mini" }
}'Notes:
- API keys are read from environment variables (do not send keys in requests).
session_idscopes memory state inside the Python engine process.
To measure reasoning refinement quality over time (initial_issues -> final_issues), run:
python3 sr-dual-brain-llm/scripts/benchmark_system2.py \
--system2-mode on \
--questions sr-dual-brain-llm/examples/system2_benchmark_questions_en.jsonLonger suite:
python3 sr-dual-brain-llm/scripts/benchmark_system2.py \
--system2-mode on \
--questions sr-dual-brain-llm/examples/system2_benchmark_questions_en_xl.jsonXL2 suite:
python3 sr-dual-brain-llm/scripts/benchmark_system2.py \
--system2-mode on \
--questions sr-dual-brain-llm/examples/system2_benchmark_questions_en_xl2.jsonReasoning-heavy (open-ended) suite:
python3 sr-dual-brain-llm/scripts/benchmark_system2.py \
--system2-mode on \
--questions sr-dual-brain-llm/examples/system2_benchmark_questions_reasoning_openended.jsonOutputs:
- Full report:
sr-dual-brain-llm/samples/system2_benchmark_last.json - History rows (for trend over repeated runs):
sr-dual-brain-llm/samples/system2_benchmark_history.jsonl
Useful flags:
--limit 5(quick smoke run)--shuffle --seed 42(order randomization)--history-limit 50(trend window size)--only-tags percent,units(domain slices; report also includessummary_by_tag)--questions a.json,b.json(comma-separated multi-file suite)--output path.json(save a named report file)ts=$(date -u +%Y%m%d_%H%M%SZ)then--output "..._${ts}.json"(avoid overwriting)--critic-health-check off(skip external-critic preflight for micro-only runs)
For mode-to-mode comparison (off vs auto vs on) on the exact same question set:
python3 sr-dual-brain-llm/scripts/benchmark_system2_ab.py \
--modes off,auto,on \
--questions sr-dual-brain-llm/examples/system2_benchmark_questions_en.jsonOutputs:
- Full comparative report:
sr-dual-brain-llm/samples/system2_ab_last.json - History rows:
sr-dual-brain-llm/samples/system2_ab_history.jsonl - Pairwise deltas include quality (
issue_reduction_rate,resolved_issue_rate) and latency (avg_latency_ms,avg_phase_latency_ms)
Unconscious-field benchmark reports live under target/benchmarks/ by default.
For the creativity and multi-turn incubation suites, see
docs/unconscious_benchmark_results.md
for run commands and metric interpretation.
To turn any benchmark JSON report into a Markdown brief with summary metrics,
count breakdowns, tag coverage, and per-case detail rows:
python3 sr-dual-brain-llm/scripts/summarize_benchmark_report.py path/to/report.json --output path/to/report.md --top 8.
To compare two reports or summarize a JSONL history trend:
python3 sr-dual-brain-llm/scripts/compare_benchmark_reports.py before.json after.json --output compare.md
or
python3 sr-dual-brain-llm/scripts/compare_benchmark_reports.py --history target/benchmarks/unconscious_incubation_history.jsonl --limit 10 --output trend.md.
This repo includes an ACC-like conflict monitor + cerebellum-like micro-correction loop.
When the deterministic micro-critic finds a high-confidence inconsistency, ACC can:
- force a right-brain consult (even if policy would skip)
- lower temperature (bias toward precision)
- bump
system2_mode=autointo System2 when it would otherwise stay off (if both hemispheres have external LLMs configured)
Enable (suggested):
export DUALBRAIN_ACC_OVERRIDE_CONSULT=1
export DUALBRAIN_ACC_TEMPERATURE_DROP=0.25
export DUALBRAIN_ACC_SYSTEM2_BUMP=1Tuning (optional):
export DUALBRAIN_ACC_CONFLICT_THRESHOLD=0.75
export DUALBRAIN_ACC_MICRO_MIN_CONFIDENCE=0.86
export DUALBRAIN_ACC_MICRO_MAX_ISSUES=6Benchmark reports now also include summary keys for these loops (when active):
acc_override_consult_rate,acc_system2_bump_rate,acc_temperature_drop_avgcerebellum_applied_rate,cerebellum_issue_reduction_rate,cerebellum_resolved_issue_rate
Keep a short log here so benchmark trends are visible without opening JSON files.
| Date (UTC) | Provider / Model | Modes | N | Critic health gate | Key result (on vs auto) |
Report |
|---|---|---|---|---|---|---|
| 2026-02-15 | OpenAI / gpt-4o |
auto,on |
15 | enabled (attempts=3, min_successes=1) |
issue_reduction_rate_delta=+0.125, avg_latency_ms_all_cases_delta=-1479.57ms, activation: 0.933 -> 1.0 |
sr-dual-brain-llm/samples/system2_ab_reasoning_openai4o_15_latest.json |
| 2026-02-26 | OpenAI / gpt-4o-mini |
auto,on |
35 | enabled (attempts=3, min_successes=2) |
issue_reduction_rate_delta=+0.1786, avg_latency_ms_all_cases_delta=-1030.03ms, activation: 0.857 -> 1.0 |
sr-dual-brain-llm/samples/system2_ab_reasoning_openended_openai_20260226_042832Z.json |
| 2026-02-26 | (none; micro-only) | off,auto,on |
35 | disabled (--critic-health-check off) |
activation: 0.0 -> 1.0 (auto no-op), issue_reduction_rate(on)=1.0 |
sr-dual-brain-llm/samples/system2_ab_reasoning_openended_20260226_000523Z.json |
| 2026-03-06 | (none; micro-only, Stage 3) | off,auto,on |
35 | disabled (--critic-health-check off) |
auto remained a no-op without external LLM capability (activation: 0.0 -> 0.0); on stayed at issue_reduction_rate=1.0, avg_latency_ms_all_cases=447.44ms |
sr-dual-brain-llm/samples/system2_ab_reasoning_openended_stage3_20260306_185841Z.json |
Single-mode runs:
| Date (UTC) | Provider / Model | Mode | N | LLM capable | Critic health gate | Key result | Report |
|---|---|---|---|---|---|---|---|
| 2026-02-25 | (none; micro-only) | on |
15 | false | enabled (attempts=3, min_successes=2) → FAILED (not configured) |
issue_reduction_rate=0.3529, resolved_issue_rate=0.4286, avg_latency_ms_all_cases=404.28ms, activation=1.0 |
sr-dual-brain-llm/samples/system2_benchmark_last.json |
| 2026-02-25 | (none; micro-only) | on |
70 | false | enabled (attempts=3, min_successes=2) → FAILED (not configured) |
issue_reduction_rate=0.8143, resolved_issue_rate=0.8507, avg_latency_ms_all_cases=485.53ms, activation=1.0 |
sr-dual-brain-llm/samples/system2_benchmark_last.json |
| 2026-02-25 | (none; micro-only) | on |
151 | false | enabled (attempts=3, min_successes=2) → FAILED (not configured) |
issue_reduction_rate=0.8919, resolved_issue_rate=0.9103, avg_latency_ms_all_cases=498.57ms, activation=1.0 |
sr-dual-brain-llm/samples/system2_benchmark_last.json |
| 2026-02-26 | (none; micro-only) | on |
35 | false | disabled (--critic-health-check off) |
issue_reduction_rate=1.0, resolved_issue_rate=1.0, avg_latency_ms_all_cases=447.06ms, activation=1.0 |
sr-dual-brain-llm/samples/system2_reasoning_openended_20260226_000442Z.json |
| 2026-02-26 | OpenAI / gpt-4o-mini |
on |
35 | true | enabled (attempts=3, min_successes=2) |
issue_reduction_rate=0.0294 (34 -> 33), resolved_issue_rate=0.0769, avg_latency_ms_all_cases=6407.06ms, activation=1.0 |
sr-dual-brain-llm/samples/system2_reasoning_openended_openai_20260226_034800Z.json |
Repro command:
PYTHONPATH=sr-dual-brain-llm:/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages \
LLM_PROVIDER=openai LLM_MODEL_ID=gpt-4o OPENAI_API_KEY=... \
python3 -u -S sr-dual-brain-llm/scripts/benchmark_system2_ab.py \
--modes auto,on \
--limit 15 \
--critic-health-check on \
--critic-health-attempts 3 \
--critic-health-min-successes 1 \
--critic-health-retries 1 \
--critic-health-timeout 40 \
--critic-health-rate-limit-backoff 3 \
--output sr-dual-brain-llm/samples/system2_ab_reasoning_openai4o_15_latest.json \
--history sr-dual-brain-llm/samples/system2_ab_history.jsonlflowchart TD
U[User Input] --> P[Perception Layer<br/>Amygdala + Prefrontal Cortex + Schema Profiler]
P --> D[Inner Dialogue Loop<br/>Left/Right Brains + Corpus Callosum]
D --> I[Integration & Auditing<br/>Coherence Resonator + Auditor + Default Mode]
I --> M[Memory & Consolidation<br/>Working Memory + Shared Memory + Hippocampus]
M --> R[Final Response]
By default, /v1/process returns a clean user-facing answer (no internal debug blocks).
Everything “internal” lives out-of-band:
metrics(small, stable UI payload)dialogue_flow(inner steps + architecture path)telemetry(structured per-module events)/v1/trace/{qid}(fetch traces later without polluting the chat output)
The core orchestration happens in sr-dual-brain-llm/core/dual_brain.py (DualBrainController.process).
Treat “brains” as roles with strict I/O contracts:
| Role | Responsibility | Output | Where |
|---|---|---|---|
| Left brain (speech/draft) | Produce the primary user-facing answer draft | Draft answer text | sr-dual-brain-llm/core/models.py |
| Right brain (deepener/lead) | Add missing substance; can create a “lead prelude” | Additive notes (not a second full answer) | sr-dual-brain-llm/core/models.py |
| Corpus callosum | In-process message bus for right-brain requests/responses | ASK_DETAIL / ASK_LEAD messages |
sr-dual-brain-llm/core/callosum.py |
| Right worker | Consumes callosum requests and calls the right model | notes_sum / lead_notes |
sr-dual-brain-llm/scripts/engine_stdio.py |
| Executive reasoner | Private meta-control + optional “mix-in” | memo (private) + mix_in (user-facing micro-addition) + directives |
sr-dual-brain-llm/core/executive_reasoner.py |
| Policy + timing | Decide whether to consult, set temperature/slot/timeouts | action, temperature, slot_ms |
sr-dual-brain-llm/core/policy*.py, sr-dual-brain-llm/core/hypothalamus.py |
| Auditor | Sanity checks (keep answers clean) | ok/fail | sr-dual-brain-llm/core/auditor.py |
| Coherence + motifs | Post-hoc scoring + “why it routed” signals | coherence_signal + tags |
sr-dual-brain-llm/core/coherence_resonator.py |
This is the “3-layer” structure:
- Speech brain (left): produces the final answer
- Advice brain (right): produces additive content that can be blended into the answer
- Reasoning brain (executive): produces:
memo: private constraints + priorities (out-of-band only)mix_in: tiny user-facing addition (blended only whenexecutive_mode=assist)
Design intent:
- Keep the chat transcript clean (no “writing coach notes”, no internal orchestration talk).
- Still observe the system via the metrics panel and
/v1/trace/{qid}.
This repo intentionally separates “sticky memory” from “recent context”:
- Working memory (PrefrontalCortex): short-term, small, clipped summaries of the last few turns. Included only when the question structure suggests it’s needed (language-agnostic heuristics: punctuation, length, digits).
Code:sr-dual-brain-llm/core/prefrontal_cortex.py - Shared memory: lightweight recall of prior Q/A traces.
Code:sr-dual-brain-llm/core/shared_memory.py - Hippocampus (episodic memory): embedding-backed episodic recall (powerful, but can be noisy if overused).
Code:sr-dual-brain-llm/core/temporal_hippocampal_indexing.py - Schema memory (optional, Postgres): consolidation output from a deterministic “sleep” job. Intended to be slower-changing and less chatty than raw episodic recall.
Reset behavior:
POST /v1/resetresets in-process state for asession_id(and deletes persisted rows when Postgres is enabled).
You’ll see three levels of “what happened”:
metrics(small): coherence/policy/latency + active modules and stage→modulesmetrics.modules.active: union of modules seen in the architecture pathmetrics.modules.stages: ordered stage breakdown (used by the UI “Architecture path” panel)metrics.latency.phases_ms: per-turn phase breakdown (e.g.left_draft,right_consult,integration,metacognition,executive)
dialogue_flow(structured): inner steps + architecture path summary (what modules ran in what order)telemetry(raw): per-module event payloads for dashboards/evals
If you stream via SSE (/v1/process/stream), fetch traces afterward:
GET /v1/trace/{qid}?session_id=demo&include_telemetry=true&include_dialogue_flow=true
Open http://127.0.0.1:8080/.
- Chat transcript stays answer-only.
- Internal data (executive memo, telemetry, dialogue flow) stays in the metrics pane / pop-out.
Restart engine: restarts the Python engine process (drops all in-process sessions). Useful after code changes or when recovering from an engine error.
Session: scopes memory state inside the engine process.Leading brain:auto|left|right(force-leading is useful for experiments).Provider/Model: optional per-session override (keys still come from env).Max output tokens: request-time override (not a secret).Executive mode:off: no executiveobserve: executive memo only (answer unchanged)assist: memo + mix-in blended into answerpolish: may apply directives as a second-pass rewrite (may reset streams)
System2 mode (reasoning):auto: enables the right-brain critic only when both left/right are backed by external LLMs and the turn looks like a reasoning task (structural complexity + loaded context)on: force critic mode (left drafts → right critiques → left revises → verify pass)off: disable critic mode- Metrics panel (
system2) now shows round progress and issue decay (initial → final) when available.
Executive observer (experiment):off: disabledmetrics: after the turn, the Executive receives a compact metrics/context report and emits an out-of-band memo (never blended into chat)
- Key numbers: coherence / tension / routing / action / temperature / latency (including top latency phases when available)
- Active modules: quick chip list
- Architecture path: stage-by-stage module timeline (the “what ran” view)
- Executive memo: out-of-band
memo+ optionalmix_in+ directives metadata
Click Pop out metrics to open /metrics.html in a separate window. This is the recommended “research loop”:
- Main window: chat only
- Pop-out: metrics + executive memo + architecture path + traces
Returns gateway + engine health.
Uploads a binary blob (e.g., an image) into the Python engine.
This endpoint is intended for multimodal experiments and uses a separate named-pipe "blob" channel when DUALBRAIN_ENGINE_TRANSPORT=pipes is enabled.
Request (multipart/form-data):
session_id(optional, default"default")file(required)
Notes:
- The engine stores blobs under
DUALBRAIN_BLOB_DIR(default: your OS temp dir undersrdb_blobs/). - The browser UI uses this endpoint to upload images and then references them via
attachmentson/v1/process. - The engine keeps a small in-memory base64 cache for re-used images (
DUALBRAIN_VISION_CACHE_ITEMS, default:4).
Fetches stored telemetry and/or dialogue_flow for a prior turn (even if /v1/process returned only the clean answer + metrics).
Query params:
session_id(default:"default")include_telemetry(default:true)include_dialogue_flow(default:true)include_executive(default:true): include executive memo/mix-in/directives and any observer memo (if available)
Resets a session inside the Python engine.
Request:
{ "session_id": "demo" }Restarts the Python engine process. This drops all in-process sessions (equivalent to restarting the gateway for the engine).
Runs one orchestration turn.
Request fields:
question(string, required)session_id(string, default:"default")leading_brain("auto"|"left"|"right", default:"auto")answer_mode("plain"|"debug"|"annotated"|"meta", default:"plain")executive_mode("off"|"observe"|"assist"|"polish", default:"off")off: no executive reasoner runobserve: run executive reasoner and store memo (out-of-band only; does not change the answer)assist: run executive reasoner and blend a small user-facing mix-in into the final answer (memo stays out-of-band)polish: executive directives may trigger a second-pass integration (may reset stream)
executive_observer_mode("off"|"director"|"metrics"|"both", default:"off")director: pre-turn steering (memory gating + optional clarifying question)metrics: post-turn feedback memo (out-of-band)both: director + post-turn feedback
system2_mode("auto"|"on"|"off", default:"auto")auto: enable critic mode only when both left/right are backed by external LLMs and the turn looks like a reasoning task (structural complexity + loaded context)on: force critic mode (left drafts → right critiques → left revises → verify pass)off: disable critic mode- Optional env tuning (engine):
DUALBRAIN_SYSTEM2_LOW_SIGNAL_FILTER(1|0, default:1)DUALBRAIN_SYSTEM2_MAX_NEW_ISSUES(0..8, default:2)DUALBRAIN_SYSTEM2_FOLLOWUP_MIN_SCORE(-1.0..4.0, default:1.6)DUALBRAIN_SYSTEM2_FOLLOWUP_MIN_OVERLAP(0.0..1.0, default:0.2)DUALBRAIN_SYSTEM2_FOLLOWUP_MAX_REMAINING(1..12, default:2)DUALBRAIN_SYSTEM2_FOLLOWUP_MIN_PROGRESS(1..12, default:1)
return_telemetry(bool, default:false)return_dialogue_flow(bool, default:true)return_executive(bool, default:false)qid(string, optional): supply your own ID for dataset runs / trace correlationllm(object, optional): select provider/model for the session (keys still come from env)provider(string, required ifllmprovided):openai|google|anthropic|mistral|xai|huggingfacemodel(string, required ifllmprovided)left_model/right_model/executive_model(string, optional): per-role model override- Optional request-time knobs (no secrets):
api_base,organization,max_output_tokens,timeout_seconds,auto_continue,max_continuations
attachments(array, optional): references previously uploaded blobs (e.g., images) to include as vision inputs- Each item supports:
{ blob_id, content_type, file_name, size_bytes } - Vision is currently implemented for OpenAI-style providers (
openai|mistral|xai) viadata:<mime>;base64,...URLs generated inside the Python engine. - Recommendation: always use a vision-capable model even for text-only turns to keep behavior stable across “text-only vs. image” conversations.
- Each item supports:
Response fields:
qid(string)answer(string)session_id(string)metrics(object): lightweight summary (coherence/policy/latency/modules) for UI dashboards- Includes
latency.total_ms,latency.phases_ms,latency.accounted_ms,latency.other_ms
- Includes
dialogue_flow(object, optional): inner steps + architecture path captured for this turntelemetry(array, optional): structured per-module events emitted during the turnexecutive/executive_observer(object, optional): out-of-band memos (only whenreturn_executive=true)
Streams the answer via Server-Sent Events (SSE).
To avoid “draft → reset → regenerate” artifacts in the browser, streaming mode emits the final integrated answer only. delta events are chunked slices of the final answer (currently ~512 characters per chunk). No reset event is emitted.
SSE events:
start:{ qid, session_id }delta:{ text }final:{ qid, answer, session_id, metrics, executive?, executive_observer? }done:{}error:{ message }
Fetch /v1/trace/{qid} afterward if you want telemetry or dialogue_flow.
Example:
curl -N http://127.0.0.1:8080/v1/process/stream \
-H 'content-type: application/json' \
-d '{
"session_id":"demo",
"question":"Write a 2 minute explanation of what coherence means here.",
"leading_brain":"auto",
"llm": { "provider":"openai", "model":"gpt-4o-mini" }
}'Searches hippocampal episodic memory for a session.
Request:
{ "session_id": "demo", "query": "coherence audit", "topk": 5 }Response:
backend:"pgvector" | "postgres" | "memory" | "none"results: array of{ similarity, qid, question, answer, ts, tags, ... }
Queries persisted telemetry events (Postgres only).
Request:
{ "session_id": "demo", "limit": 250, "event": "policy_decision" }Lists consolidated schema memories (Postgres only).
Request:
{ "session_id": "demo", "limit": 16 }The gateway currently runs one Python engine process and the engine processes requests sequentially (one line in → one line out). This is intentional for determinism during research runs. Scaling options are in the roadmap.
The Python engine can call external providers directly via plain HTTP (no SDK deps).
Example (OpenAI):
export LLM_PROVIDER=openai
export LLM_MODEL_ID=gpt-4o-mini
export OPENAI_API_KEY=sk-...Optional overrides:
LEFT_BRAIN_MODEL,RIGHT_BRAIN_MODEL,EXECUTIVE_MODELLLM_API_BASEor<PROVIDER>_API_BASELLM_MAX_OUTPUT_TOKENS/LEFT_BRAIN_MAX_TOKENS/RIGHT_BRAIN_MAX_TOKENS/EXECUTIVE_MAX_TOKENS(default: 1024)LLM_TIMEOUT/LEFT_BRAIN_TIMEOUT/RIGHT_BRAIN_TIMEOUT/EXECUTIVE_TIMEOUTLLM_AUTO_CONTINUE/LEFT_BRAIN_AUTO_CONTINUE/RIGHT_BRAIN_AUTO_CONTINUE/EXECUTIVE_AUTO_CONTINUE(default: on)LLM_MAX_CONTINUATIONS/LEFT_BRAIN_MAX_CONTINUATIONS/RIGHT_BRAIN_MAX_CONTINUATIONS/EXECUTIVE_MAX_CONTINUATIONS(default: 2)OPENAI_ORGANIZATION
Auto-continue will issue follow-up calls when a provider reports that output stopped due to token limits (e.g., OpenAI-style finish_reason=length, Anthropic stop_reason=max_tokens).
Use provider-specific keys (e.g., ANTHROPIC_API_KEY) or the shared LLM_API_KEY.
Supported providers: openai, google, anthropic, mistral, xai, huggingface.
Why this exists:
- Sending
data:<mime>;base64,...directly from the browser is slow and bloats logs. - Switching models when an image appears can change behavior mid-conversation.
Recommended flow:
- Pick a vision-capable model from the start (even for text-only).
- Upload images via
POST /v1/blobs(multipart). - Reference them via
attachmentsonPOST /v1/process.
Under the hood:
- The engine stores blobs on disk under
DUALBRAIN_BLOB_DIR. - The engine converts blobs to data URLs inside the Python process (for providers that require it) and keeps a small in-memory cache (
DUALBRAIN_VISION_CACHE_ITEMS) to avoid repeated base64 work. - On macOS/Linux the gateway defaults to
DUALBRAIN_ENGINE_TRANSPORT=pipes, so blob uploads use a dedicated named-pipe channel for higher throughput.
To persist state across engine restarts, set:
DUALBRAIN_PG_DSN(e.g.,postgresql://user:pass@localhost:5432/dual_brain)
If your DSN uses a Docker service name like host (e.g., ...@host:5432/...), it will not resolve from your macOS host shell; use localhost (or 127.0.0.1) with the published port instead.
The engine auto-creates tables on startup:
SharedMemorytraces- Hippocampal episodes (+ embedding version)
- Telemetry events
- Schema memory (sleep/consolidation output) + consolidation cursor
If pgvector is installed/enabled in your database, the store will add a vector_pg column and use cosine search to accelerate POST /v1/episodes/search. If not available, vectors are still stored as JSON and search falls back to a Python similarity scan over recent candidates.
Set DUALBRAIN_PG_PERSIST_TELEMETRY=0 to disable telemetry writes (default: enabled when Postgres is enabled).
Run the deterministic consolidation job:
python3 sr-dual-brain-llm/scripts/sleep_consolidate.py --session-id demoOn session creation, the engine loads recent schema memories (default: 16) and may inject them into the context as [Schema memory] .... Override with:
DUALBRAIN_SCHEMA_MEMORY_LIMIT
Resetting a session via POST /v1/reset will also delete the persisted rows for that session.
If you want to debug the engine protocol directly:
printf '{"id":"1","method":"health","params":{}}\n' | python3 sr-dual-brain-llm/scripts/engine_stdio.pyThe engine also supports a streaming method (process_stream) which emits intermediate event lines before the final ok response:
python3 sr-dual-brain-llm/scripts/engine_stdio.py <<'EOF'
{"id":"1","method":"process_stream","params":{"session_id":"demo","question":"hello"}}
EOFpython3 sr-dual-brain-llm/scripts/run_server.py- Python tests:
python3 -m pytest -q - C# build:
dotnet build csharp/SrDualBrain.Gateway -c Release
.
├── csharp/
│ └── SrDualBrain.Gateway/ # C# Minimal API REST gateway (HTTP → Python stdio/pipes)
├── docs/ # Design notes / playbooks
├── sr-dual-brain-llm/
│ ├── core/ # Orchestration + memory + telemetry modules
│ └── scripts/
│ ├── engine_stdio.py # JSONL engine entrypoint (used by the C# gateway)
│ └── run_server.py # Interactive CLI loop
└── tests/
- Stateful memory backends:
- Redis for fast shared state + TTL / decay
- Postgres for durable episodic traces + queryable analytics
- Eval harness: deterministic scenario packs + metrics for coherence, safety, memory retention
- Scaling: multi-engine pool behind the gateway (process pool / queue) + per-session routing
See LICENSE.txt for third-party notices and licensing information.