# Thinking Provider Architecture

Solstone is local-first software for personal use. It supports one active
provider/model profile, configured in Thinking, and never silently switches
providers. The implementation deliberately has no special Vertex AI, Azure
OpenAI, Bedrock, or other enterprise-cloud integration.

For the broader pipeline, see `docs/THINK.md`.

## One Active Brain

`config/journal.json` stores the selected profile at:

```json
{
  "providers": {
    "active": {
      "provider": "local",
      "model": "local/qwen3.5-4b"
    }
  }
}
```

`solstone/think/models.py::resolve_provider()` is the only runtime resolver.
The `generate` and `cogitate` arguments identify the interface being invoked,
but both resolve the same `providers.active` profile. A missing profile is an
explicit no-brain state. Key presence and local readiness never choose a
provider implicitly.

Provider and model overrides are rejected in talent frontmatter, cortex
requests, batch requests, and direct generate calls. Thinking is the sole
configuration surface for the active brain. Talent `disabled` and `extract`
controls are separate metadata under `talent_overrides`; they do not route
models.

## Supported Owner Choices

The Thinking app exposes five setup choices:

- Bundled local, using Solstone's installed llama-server or mlx-vlm runtime.
- An owner-supplied OpenAI-compatible URL, model id, and optional bearer key.
- OpenAI with an owner-supplied API key and model id.
- Anthropic with an owner-supplied API key and model id.
- Google AI Studio with an owner-supplied API key and Gemini model id.

The direct cloud options are convenience presets. The arbitrary endpoint is a
plain compatibility contract: Solstone sends OpenAI-compatible requests, but
does not add vendor-specific support for whatever sits behind that URL.

Managed personal cloud keys remain journal-local:

- `env.OPENAI_API_KEY`
- `env.ANTHROPIC_API_KEY`
- `env.GOOGLE_API_KEY`

## Dispatch

`solstone/think/providers/__init__.py` has a deliberately small registry:

- `google`, `openai`, and `anthropic` all map to
  `solstone/think/providers/openhands.py`.
- `local` maps to `solstone/think/providers/local.py`.

The effective modules implement:

- `run_generate()` for synchronous single-shot generation.
- `run_agenerate()` for asynchronous single-shot generation.
- `run_cogitate()` for tool-using OpenHands conversations.

### Personal cloud

`openhands.py` is the single cloud transport. It builds an OpenHands `LLM` and
lets LiteLLM translate the request to OpenAI, Anthropic, or Google AI Studio.
It also normalizes text, usage, finish reasons, and thinking blocks back into
Solstone's `GenerateResult`.

Generate calls explicitly neutralize OpenHands' agent-oriented defaults and
then apply only Solstone's requested behavior. The transport preserves:

- sync and async calls;
- multimodal message content;
- JSON object and JSON Schema response formats;
- OpenAI reasoning-effort suffixes;
- Anthropic and Gemini thinking budgets;
- normalized usage, resolved model, and finish reason.

Direct OpenAI generation uses the Responses API. Anthropic and Google use chat
completion through LiteLLM's provider translation. Key/model validation sends
a tiny request through this same runtime path, so validation can incur a small
provider charge.

OpenHands/LiteLLM may internally contain code for many providers. That does not
make them Solstone-supported providers: Solstone exposes no registry entry,
config, UI, credential flow, or validation path for enterprise integrations.

### Local and arbitrary endpoints

`local.py` remains a thin product-policy wrapper rather than a second general
cloud adapter. It owns guarantees that OpenHands alone does not provide:

- bundled runtime installation and manifest-backed readiness;
- context-budget fitting and local schema preparation;
- Qwen sampling and chat-template controls;
- cross-process local admission and bounded retry;
- content-free local inference telemetry;
- confidential egress/attestation gates;
- stable local error classification.

Bundled local posts to the supervisor-owned loopback server. Its install status
lives under `health/providers/`, while artifact truth lives in provider manifests
and the affirmative proof cache. A configured endpoint uses:

- `providers.local.endpoint_url`
- `providers.local.served_model_id`
- `providers.local.credential` (optional)
- `providers.local.parallel_slots` (optional)

Both generate and cogitate use the endpoint's OpenAI-compatible contract. The
configured logical provider remains `local`, so the same readiness and safety
boundary applies without maintaining vendor-specific adapters.

## Local Admission

Bundled local and non-confidential arbitrary endpoints share the governed local
admission boundary. Cloud and confidential processing bypass it. Capacity is
kept intentionally small: one slot on the Linux floor and Apple mlx-vlm, two on
the capable Linux tier, or the explicit `parallel_slots` value for an arbitrary
endpoint.

Admission uses per-slot `flock` files under
`health/local-inference-admission/`, coordinating independent journal
processes. Queue time consumes the caller's existing timeout. Cogitate yields
its permit while a nested `sol` command runs and reacquires it before the next
model turn.

Bundled attempts append content-free telemetry to
`health/local-inference/YYYYMMDD.jsonl`. Records include timing, capacity,
token counts, retry index, finish reason, and safe failure codes—never prompts,
responses, schemas, images, URLs, or credentials.

## Failure Semantics

Provider failure is not a routing signal. Solstone surfaces the failure and
recovery action for the active profile.

- Quota failures are recorded through `record_brain_runtime_failure` into `health/brain.json`.
- Endpoint reachability and contract errors are classified by the local
  endpoint wrapper.
- Local generate retries once only for narrow capacity/truncation cases, using
  the same provider.
- Missing local runtime, model files, RAM, endpoint readiness, or confidential
  attestation fails closed rather than falling back to cloud.

Owner-facing brain health and Thinking readiness read canonical evidence from
`health/brain.json`. Confidential SPP egress remains authorized only by the
current process-local attestation state in `spp_transport`.

## Migration Boundary

The Thinking maintenance task collapses legacy `providers.generate` and
`providers.cogitate` into `providers.active`. If they differ, cogitate wins
because its model already satisfies the tool-capable interface. A key-only
legacy install is materialized once in Google, Anthropic, OpenAI order. The task
selects bundled local when no prior profile or personal cloud key exists. It
also:

- removes tier, backup, model-map, Google-backend, and Vertex fields;
- deletes the canonical legacy Vertex credential file;
- moves `providers.contexts` enable/extract controls to `talent_overrides`;
- moves Rev.ai/Plaud validation state to `service_key_validation`.

The next Thinking maintenance task moves legacy provider install truth out of
`providers.bundled`. It promotes only artifacts that can be proven against the
current pins, writes provider-owned status and manifests, and then removes the
retired operational fields. Missing or mismatched proof exits successfully
without promotion and is repaired by the provider installer under the provider
lease. Unreadable proof exits successfully without promotion and is preserved
until the owner fixes the underlying access or I/O problem.

There are no runtime compatibility shims for the retired shapes.
