文件历史

3 次代码提交

作者 SHA1 备注 提交日期
Simon Willison 94cde01b30 DAG phase 4: fork
- MessageStore.fork(source_message_id, name=, model=) creates a new
  conversation rooted at an existing message. The shared prefix is
  reused in place; only one conversations row is written.
- Model defaults to whatever most recently wrote a call touching the
  source message; can be overridden. Raises ValueError if the message
  doesn't exist or no model can be inferred.
- New `llm fork <message_id>` CLI prints the new conversation id.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 08:27:38 -07:00
Simon Willison 1985f98401 DAG phase 3: continuation dedup + Conversation.head_message_id
- Conversation (sync + async) gains head_message_id; from_row populates
  it from the DB so CLI -c continuation resumes the existing chain
  instead of starting a parallel one.
- MessageStore gains save_with_dedup(): one call that locates the
  longest existing prefix and appends only the unmatched tail. Writes
  zero rows when the full chain already exists — the stateless-API
  continuation case from plans/dag-schema.md.
- Tests cover find_longest_existing_prefix (miss / partial / full),
  save_with_dedup idempotency, and end-to-end: a second prompt on a
  reloaded Conversation extends the chain via head_message_id.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 08:26:13 -07:00
Simon Willison ee08b572c8 DAG phase 2: schema replacement + MessageStore
Rewrites m023 in place to the DAG-shaped message store from
plans/dag-schema.md:

- messages: id, parent_id, content_hash, role, provider_metadata_json,
  created_at. Chain roots point at a self-referencing sentinel row
  ("root") so the unique (parent_id, content_hash) index works at
  every chain position — NULL-parent uniqueness footgun avoided.
- message_parts: structurally unchanged.
- calls: one row per LLM call, anchoring head_input/head_output
  message ids and recording model + timing + usage.
- conversations.head_message_id: advances each turn; history is
  reconstructed by walking parent_id from the head.

New llm/storage.py provides MessageStore.save_chain (with dedup),
load_chain, and find_longest_existing_prefix (for the stateless-API
case wired in phase 3).

Response.log_to_db now writes the DAG + a calls row alongside the
existing responses-table writes (kept for llm logs compatibility
until phase 5). Response._load_messages_from_db walks the chain
using calls pointers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 08:23:31 -07:00