Skip to content

WO-03: per-Dot model and desk key (stacked on #1) - #2

Merged
RoseBludd merged 1 commit into
devvy/wo-07-direct-modelfrom
devvy/wo-03-per-dot-model
Oct 16, 2026
Merged

RoseBludd merged 1 commit into
devvy/wo-07-direct-modelfrom
devvy/wo-03-per-dot-model

Conversation

@RoseBludd

Copy link
Copy Markdown
Owner

WO-TWIN-03: per-Dot model selection

Stacked on #1 (WO-07). Base: devvy/wo-07-direct-model, because the direct-model path also needs per-Dot resolution. Once #1 merges, retarget this PR to devvy/host-base-timeouts. The only commit here is 4b8a888.

No edits to runner.ts, store.ts, tools/mcp/** or handoff/**.

What changes

  • Dot fields: nullable model and desk columns, added through WorkspaceStore's existing additive column migration. setDotModel() is the only new store method.
  • Allowlist (src/shared/dot-model.ts): cadis-free, cadis. Desk names must match ^[a-z0-9][a-z0-9-]{0,31}$ and map to env CADIS_API_KEY_<DESK>, with - becoming _.
  • Resolution (src/server/dot-model.ts, resolveDotModel):
    • Model: dot.model, then OPENAI_MODEL, then cadis-free.
    • Key, Dot with a desk: only CADIS_API_KEY_<DESK>, plus the header X-Cadis-Desk: <desk>. If the key is empty, it throws Model not configured for desk "<desk>": set CADIS_API_KEY_<DESK> on the server. This Dot does not fall back to another key. The shared key and the fallback model are never used for a desk Dot.
    • Key, Dot without a desk: OPENAI_API_KEY, which is today's behavior.
  • Model call sites:
    • dot-agent.ts: only the model-resolution lines change (the config check and openaiCompatibleText(model, {apiKey, defaultHeaders})). With no desk and no model set, the result is identical to before.
    • direct-model: DirectModel.modelFor(dotId). A desk Dot never uses OPENAI_FALLBACK_MODEL, since a desk key is scoped to its own models.
  • Setters:
    • API: PUT /api/dots/:id/model {model?, desk?}, available in both Intelligence and direct-model modes. POST/PUT /api/dots also accept model/desk.
    • UI: a "Model" fieldset in the Dot dialog, with a model select and a desk input.
  • Keys stay empty. No key values exist anywhere in code or tests, and the shared key is not used. sec. issues the CADIS_API_KEY_<DESK> values later (Infisical /host/opendots).

Reused components (cited)

Component Where Reused for
Hermes per-job model and provider RoseBludd/hermes vendor/hermes-agent/cron/scheduler.py L1358 (job.get("model") or os.getenv("HERMES_MODEL")) and L1493–1514 (a per-job provider resolves or fails with a clear error) Precedence dot.model → OPENAI_MODEL → default, and failing clearly with no fallback. Note: the WO names scheduler_provider.py, but that file holds the cron trigger provider, so the model logic was taken from scheduler.py.
OpenDots Dot model call src/server/dot-agent.ts (openaiCompatibleText, resilientFetch) Same adapter, with the resolved model, key and headers
Gateway desk keys RoseBludd/cadis-ai-gateway PR #4 (CADIS_DESK_KEYS sha256 map, desk scope by model) Bearer is the desk key; X-Cadis-Desk names the desk
Workspace store src/server/workspace.ts additive column migration model/desk columns

Acceptance and proof (tests/dot-model.test.ts, 8 tests)

Criterion Test
Model and header resolution per Dot uses the server key and model for a Dot without a desk, uses the desk key and X-Cadis-Desk header per Dot
Empty key gives a clear "model not configured" error with no fallback to any other key fails clearly with no fallback when the desk key is empty, returns "Model not configured" without calling the model when the desk key is empty (the stub is never called)
The request carries the Dot model, desk key and header sends the Dot model, desk key and X-Cadis-Desk header (stub HTTP server)
Setter validation rejects models outside the allowlist and bad desk names
Persistence and migration persists model and desk on a Dot, including after reopening the database
Env naming maps desk names to env names

Results:

…S_API_KEY_<DESK> with X-Cadis-Desk, no fallback (WO-03)

Adds nullable model and desk to Dots with a UI/API setter (PUT /api/dots/:id/model, and in the Dot dialog). Resolution: dot.model, then OPENAI_MODEL, then cadis-free; a desk Dot uses only CADIS_API_KEY_<DESK> and fails with 'Model not configured' when it is empty. Applied in DotAgent (Intelligence path) and the direct-model path. Precedence ported from RoseBludd/hermes cron/scheduler.py.
@RoseBludd
RoseBludd merged commit 621ec6d into devvy/wo-07-direct-model Oct 16, 2026
RoseBludd added a commit that referenced this pull request Oct 16, 2026
WO-02: per-Dot memory scope and seat import (stacked on #2)
RoseBludd added a commit that referenced this pull request Oct 16, 2026
Stack catch-up: land #2 (WO-03), #3 (WO-02), #4 (WO-08) on devvy/host-base-timeouts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant