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>
Migration m023 creates messages and message_parts tables. The old
parts table (from m022) is left in place for databases that already
ran that migration but is no longer read or written.
log_to_db now walks prompt.messages and response.messages, inserting
one messages row per Message and one message_parts row per Part.
Response.from_row loads via _load_messages_from_db into
_loaded_messages, which Response.messages returns directly — no more
group-parts-back-into-messages dance on load.
Tests updated to assert against the new schema. Per the branch
decision to ignore prior logs, no backfill migration is provided.
New m022_parts_table migration creates a parts table with direction
(input/output), role, part_type, content, content_json, tool_call_id,
and server_executed columns.
log_to_db() writes both input parts (from prompt.input_parts) and
output parts (from response.parts) to the table. from_row() loads
output parts and makes them available via the parts property.
Tested live: parts table created, input/output parts written and
loaded correctly with gpt-5.4-mini via CLI.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Closes#1014
- llm.ToolOutput(output='...', attachments=[...]) for tools to return attachments
- New table: `tool_results_attachments`
- Table is populated when tools return attachments
- llm --tools-debug shows attachments returned by tools
- llm logs shows attachments returned by tools
* Docs and shape of register_fragment_loaders hook, refs #863
* Update docs for fragment loaders returning a list of FragmentString
* Support multiple fragments with same content, closes#888
* Call the pm.hook.register_fragment_loaders hook
* Test for register_fragment_loaders hook
* Rename FragmentString to Fragment
Closes#863
* WIP fragments: schema plus reading but not yet writing, refs #617
* Unique index on fragments.alias, refs #617
* Fragments are now persisted, added basic CLI commands
* Fragment aliases work now, refs #617
* Improved help for -f/--fragment
* Support fragment hash as well
* Documentation for fragments
* Better non-JSON display of llm fragments list
* llm fragments -q search option
* _truncate_string is now truncate_string
* Use condense_json to avoid duplicate data in JSON in DB, refs #617
* Follow up to 3 redirects for fragments
* Python API docs for fragments= and system_fragments=
* Fragment aliases cannot contain a : - this is to ensure we can add custom fragment loaders later on, refs https://github.com/simonw/llm/pull/859#issuecomment-2761534692
* Use template fragments when running prompts
* llm fragments show command plus llm fragments group tests
* Tests for fragments family of commands
* Test for --save with fragments
* Add fragments tables to docs/logging.md
* Slightly better llm fragments --help
* Handle fragments in past conversations correctly
* Hint at llm prompt --help in llm --help, closes#868
* llm logs -f filter plus show fragments in llm logs --json
* Include prompt and system fragments in llm logs -s
* llm logs markdown fragment output and tests, refs #617
Refs #776
* Implemented new llm prompt --schema and model.prompt(schema=)
* Log schema to responses.schema_id and schemas table
* Include schema in llm logs Markdown output
* Test for schema=pydantic_model
* Initial --schema CLI documentation
* Python docs for schema=
* Advanced plugin docs on schemas