Repository navigation
LLM_Integration
Orrin's flagship design rule: the LLM is the smallest part of the agent, not the whole of it. The decision loop, goals, memory, control signals, and action selection are fully symbolic. The LLM is one explicit tool among many, and the brain never silently depends on it — with no provider key configured, Orrin runs fully and simply skips LLM-backed tool calls.
The interpretability argument is real, but the deciding one is that a pretrained external LLM is stagnant while everything else in Orrin grows. Goals, memory, causal models, rules, and control set points all change with what the runtime lives through; the frozen weights behind an API call do not. Putting that model in charge would make the only decision-maker the only component that can't learn from its decisions. So the LLM stays a gated tool, and the language ability that would grow is grown natively instead — a from-scratch transformer that trains on Orrin's own experience and improves with it (see Native Language Model). See Symbolic-First Design for the full argument.
-
ORRIN_LLM_TOOL_ONLY=1is the default. Free-form generation is off; the LLM is only reachable as an explicit tool call (brain/cognition/tools/ask_llm.py). -
brain/utils/generate_response.pyis the single chokepoint for every LLM call. In tool-only mode it rejects any caller not on the_LLM_TOOL_CALLERSallowlist, so a new code path cannot quietly start using the LLM. - The gate fails closed: disabled, keyless, or erroring calls return an error result, never
fabricated content.
llm_ok(result, caller)is the required check — error messages are never returned as content. - A circuit breaker tracks per-caller failures (
llm_failure_counts.json) and opens after repeated errors, so a broken provider degrades to symbolic-only instead of stalling the loop. - Goals and cognitive functions carry an
llm_callable_bymarking, so symbolic goal generators and the symbolic inner loop keep working when the LLM is unavailable.
brain/utils/llm_providers/ defines a provider interface (base.py) with adapters for:
- OpenAI (
openai_provider.py, key:OPENAI_API_KEY) - Anthropic (
anthropic_provider.py, key:ANTHROPIC_API_KEY) - Gemini (
gemini_provider.py, key:GOOGLE_API_KEY) - Any OpenAI-compatible or local endpoint
The active provider is selected in the UI Settings page; generate_response.py resolves it per
call. The symbolic-first, fail-closed contract is identical regardless of provider. Keys pasted in
Settings are stored in the OS keychain (brain/utils/secrets.py), never in the app bundle.
-
ORRIN_LLM_DAILY_TOKEN_BUDGET— hard daily token cap; unset means no cap. - Token usage is tracked per call and surfaced in the UI's Cognition room ("Thinking cost").
- The cost-prediction layer (
brain/cognition/cost_prediction.py) makes LLM-backed functions compete on expected value against cheap symbolic functions, so quiet cycles drift toward cheap work.
brain/cognition/finetuning/finetune_pipeline.py filters conversation traces with outcome ≥ 0.65,
submits a fine-tune job, and on completion repoints model_config.json so generation drifts toward
what has worked for this runtime. Symbolic-only mode never touches it.
-
brain/utils/generate_response.py— chokepoint, tool-only gate, caller allowlist -
brain/utils/llm_gate.py— availability checks used by planners before proposing LLM steps -
brain/utils/llm_providers/— provider adapters -
brain/cognition/tools/ask_llm.py— the tool interface the agent actually calls -
brain/utils/secrets.py— OS-keychain key storage
- Cognitive Loop
- Workspace & Ignition
- Binding & Writeback
- Thinking & Action Selection
- Learning & Adaptation
- Effect Ledger
- Quality Standard
- Expression Membrane
- Self-Code
- Native Language Model
- Existence & Lifecycle