pip install llmsurgeryDialogs are LLM conversations kept as Jupyter notebooks: notes, runnable code with outputs, and prompt/reply pairs in one editable, diffable document. This library is the data model and surgery toolkit for them, consisting of:
llmsurgery.dialog:Message,Dialog, andAttachment— a message carries exactly what the ipynb spec provides (content, output, type, id, attachments) plus a verbatim metadata dict, so host annotations round-trip untouched; hosts declare their own fields viameta_attrsand inject their subclasses throughmsg_clsandread_ipynb(cls=).llmsurgery.ipynb: reading and writing dialogs as.ipynbfiles.llmsurgery.hist: converting dialogs to LLM chat history, including recovering tool calls from replies as structured messages. Rendering defaults are deliberately unopinionated (bare prompts, pass-through media, verbatim latex); hosts install their policies as class members (prompt_txt,prep_img,media_extra,ai_renderers,UNSUPPORTED_MSG).llmsurgery.ant: Claude Code session transcripts: read, write, search, curate, and build them from dialogs, soclaude --resumeopens an authored conversation.llmsurgery.oai: Codex threads: drivecodex app-serverto inject authored histories, ready forcodex resume.
A dialog is a conversation between a human, an AI, and an interpreter. Each message type addresses one of them and expects a certain kind of answer:
- A prompt asks the AI a question and holds its reply.
- A code message gives the interpreter source and holds its outputs.
- A note is read by everyone and answered by nobody.
- A raw message addresses no one. It is inert matter the conversation carries along.
A reply may itself contain runnable code with results, so a whole dialog can live inside one message. reply2dlg opens a reply up as a dialog and dlg2reply puts it back.
Dialogs and Jupyter notebooks both serialize to the ipynb format, but they are not the same thing. A notebook has cells. A dialog has messages. Messages can be prompts, which notebooks cannot express, and they make structure explicit that notebooks leave implicit. E.g a heading opens a section that runs to the next heading of the same level; an export directive marks the code that belongs to a module. The shared file format means the same tools read both. The word tells you which layer you are on. File-level tools such as fastcore.nbio and exhash speak of cells and notebooks. Everything in this library speaks of messages and dialogs.
The Dialog is the center of the library. Everything else is a projection of it. A storage projection must preserve everything that means something. What it does not understand it carries verbatim in metadata, and what is broken it heals rather than rejects. A transmission projection normalizes on purpose, and what it drops is written into its contract. A display projection only goes one way. The rule is to convert in, edit at the center, and project out. The function names say the same thing. Every converter has dlg on exactly one side.
| projection | contract | in | out |
|---|---|---|---|
| ipynb file | storage, pragmatically lossless | read_ipynb |
write_ipynb |
| Claude Code session | storage | sess2dlg |
dlg2sess |
| Codex thread | storage (write-only so far) | dlg2thread |
|
fastllm chat (Msg/Part) |
transmission, normalizing | chat2dlg |
dlg2chat |
| fastllm hist (live call input) | transmission, one-way | dlg2hist |
|
| a prompt’s reply | self-similar | reply2dlg |
dlg2reply |
| XML views | display, one-way | view_dlg, msg2xml |
The session codecs route through chat on their way to the wire: ant’s dlg2msgs and oai’s dlg2items are each denorm_msgs(dlg2chat(...)).