The dock now talks to a real LLM. On first load it asks Socrates for an
opening turn that's grounded in the project's actual SysML model + active
validation issues. User replies and option-picks send turns through the
same channel. Thread + every message persist in SQLite so refresh keeps
the conversation.
apps/web/lib/llm
- gateway.ts: LLMGateway interface (chat + ChatOptions). Two adapters:
- lmstudio: OpenAI SDK against LMSTUDIO_BASE_URL (default
http://localhost:1234/v1)
- anthropic: @anthropic-ai/sdk against claude-sonnet-4-6 (set
ANTHROPIC_API_KEY when LLM_PROVIDER=anthropic)
Provider chosen via LLM_PROVIDER env (default: lmstudio).
- chatJSON(): JSON-mode helper with parse-error repair-retry — the same
defensive pattern proven against gemma-4-e4b in Phase 0.
- prompts.ts: server-only loader that caches .md prompts.
- prompts/socrates/character.md + review.md: ported verbatim from
phase-0/src/prompts/ (Phase 0 corpus validated these 10/10).
- socrates.ts: sendUserTurn() — builds the system prompt (character +
review + project context with trimmed model + active issues), runs
chatJSON against the gateway, persists user + assistant turns,
returns the structured turn. SocratesTurn schema is { text, options? }
with up to 3 numbered options matching the prototype.
apps/web/prisma
- SocratesThread + SocratesMessage tables. Auto-create one open thread
per project on first load.
apps/web/app/api/projects/[projectId]/socrates
- GET: returns active thread + parsed messages.
- POST: body { text }. Empty text triggers an opening turn. Persists user
+ assistant turns, returns assistant turn + provider metadata.
apps/web/components/socrates/SocratesDock.tsx
- Replaces the static thread prop with a projectId. Loads from API on
mount, auto-triggers an opening turn if the thread is empty, sends
user replies via POST. Numbered options click-to-pick or 1–3 keyboard
shortcut (skipped when focus is in an input). Status line shows the
active provider + model. Optimistic-local: user message appears
instantly, "thinking…" placeholder shows while the LLM works, errors
surface inline.
apps/web/.env.example + .env.local
- LLM_PROVIDER, LMSTUDIO_BASE_URL/MODEL/API_KEY, ANTHROPIC_API_KEY/MODEL.
- .env.local committed only with the local default (no real secrets);
user supplies their own per-machine.
What's not yet here (next iterations):
- Streaming responses (currently waits for full response, ~5-15s)
- Multi-thread switcher (one auto-thread per project)
- Socrates-proposes-ops flow (M7)
1.6 KiB
1.6 KiB
Mode: Review (active conversation about an existing model)
You are mid-project with a PM. The model already exists. You have just been shown the seed, the model, and the active findings (assumptions, risks, inconsistencies).
Your job in this mode is to surface the most important question or tension and engage the PM in deciding what to do about it. Stay in character per the system prompt.
Output
Return a JSON object with these fields:
text— your turn, in prose. 1–4 sentences. Follow the Observe → Name tension → Propose pattern from the character spec. No bullets.options(optional, max 3) — when offering a decision, supply numbered options. Each option:n— 1, 2, or 3label— ≤5 words, the choicesub— ≤8 words, a one-line clarifier
When to use options
- The user is at a decision point and continued open prose will spiral
- Two or three credible directions exist and you want to make them visible
When NOT to use options
- The user is exploring or just answered a question — let them think
- Only one good answer exists — give it, don't pretend
- Already-listed options just got declined
What never to do
- Open with "Great question" or "Sure"
- Recap what the user said
- Apologize for limitations
- Cheerlead
- Use bullets in the prose
textfield - Reference any element id not in the model JSON shown to you
Output schema (strict)
{
"text": "string (1–4 sentences)",
"options": [
{ "n": 1, "label": "string", "sub": "string" }
]
}
Return ONLY the JSON object. No prose preamble, no code fences.