graph context: a separate marker message before the first user turn

The map picks no longer glue into the opening prompt as seed text
(owner, 2026-10-04 late). POST /api/chats accepts graphContext
{propositions}; the route resolves the picks against the live graph,
derives the reasoning chains among them (chainsForSelection →
formatChainLine), and writes a graph-context custom marker as
message[0] — before the learner's first user message, in either mode.

- Core: newGraphContextMessage (model-facing content = the
  contextualization framing + proposition lines + chain lines;
  details = the structured pick); toLlmMessages flattens it to a user
  message like every marker, so the model reads it for free.
- View: projects to a user-side data-graph-context part.
- UI: GraphContextCard — minimal, muted, dashed, collapsed by default
  (counts only); expand reveals name — statement rows and the chain
  lines. Same compact-expandable pattern as the practice cards.
- Home: sends graphContext with the creation whenever picks exist;
  the prompt param carries the learner's own words only.
This commit is contained in:
amosblomqvist
2026-10-04 23:07:13 +03:00
parent 87f8e1e6f4
commit 90893ae236
8 changed files with 327 additions and 13 deletions
+11 -12
View File
@@ -112,26 +112,25 @@ export function Home({ initialConfigured }: { initialConfigured: boolean }) {
}
setBusy(true);
try {
// The practice seed: the picks ride the OPENING PROMPT (never
// the title — the title stays the learner's own words).
const seed =
mode === "practice" && practiceProps.length > 0
? `\n\nPropositions from the map:\n${practiceProps
.map((p) => `- ${p.name}: ${p.statement}`)
.join("\n")}`
: "";
// The map context (owner, 2026-10-04): picks in EITHER mode ride
// the CREATION as graphContext — the server writes a
// graph-context marker as message[0] (a separate custom message
// before the learner's first user message, propositions + the
// chains among them); the prompt stays the learner's own words.
const res = await fetch("/api/chats", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(
mode === "practice" ? { title: t, kind: "practice" } : { title: t },
),
body: JSON.stringify({
title: t,
...(mode === "practice" ? { kind: "practice" } : {}),
...(practiceProps.length > 0 ? { graphContext: { propositions: practiceProps } } : {}),
}),
});
const { id } = (await res.json()) as { id: string };
// No settings PUT on create (per-session models, owner
// 2026-09-28): the SERVER stamps the newborn from the strong
// slot — the same default this surface's model control edits.
router.push(`/chat/${id}?prompt=${encodeURIComponent(t + seed)}`);
router.push(`/chat/${id}?prompt=${encodeURIComponent(t)}`);
} catch {
setBusy(false);
}