Files
Socrates/apps/web/lib/llm/prompts/socrates/review.md
dtoro 78faca9968 MVP M6: live Socrates dock — LLM gateway + persisted thread + reply loop
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)
2026-04-29 07:50:34 +02:00

1.6 KiB
Raw Blame History

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. 14 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 3
    • label — ≤5 words, the choice
    • sub — ≤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 text field
  • Reference any element id not in the model JSON shown to you

Output schema (strict)

{
  "text": "string (14 sentences)",
  "options": [
    { "n": 1, "label": "string", "sub": "string" }
  ]
}

Return ONLY the JSON object. No prose preamble, no code fences.