Resume a codegen thread from its checkpoint instead of re-entering it - #192
Open
shellygr wants to merge 1 commit into
Open
Resume a codegen thread from its checkpoint instead of re-entering it#192shellygr wants to merge 1 commit into
shellygr wants to merge 1 commit into
Conversation
Codegen takes --thread-id, so the way to pick up a run that crashed is to
invoke the same command again against the same thread. That did not work:
the second invocation handed langgraph the original input, which langgraph
treats as an update from START, so the graph re-entered the entry node on
top of a message history that already held an initial prompt. The provider
rejected the request outright:
ValueError: Received multiple non-consecutive system messages.
The machinery for resuming was already there -- run_graph drops the input
when the run config names a checkpoint, which is why floor retries resume
correctly -- but nothing resolved a bare --thread-id into a checkpoint.
So the codegen entry point now looks the thread's latest checkpoint up and
passes it as the resume point, unless the caller named a checkpoint itself.
A fresh thread still finds no checkpoint and starts from START as before.
Resuming a thread that already finished returns its final state rather than
authoring a second time.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug
console-codegen/tui-codegentake--thread-id, so the way to pick up a runthat crashed partway is to invoke the same command again against the same thread.
That did not work. The second invocation died at the first LLM call:
langgraph treats a non-
Noneinput as an update fromSTART. Passing theoriginal input again therefore re-entered the entry node on top of a message
history that already held an initial prompt, appending a second system message
mid-history. The provider rejects that, so the failure is loud rather than a
quiet duplicate.
The fix
Most of the machinery was already in place.
run_graphdrops the input when therun config names a checkpoint (
composer/io/graph_runner.py:105), which is whyfloor retries resume correctly. What was missing is that nothing turned a bare
--thread-idinto a checkpoint, so the manual path never took that branch.latest_checkpoint_of(checkpointer, thread_id)incomposer/io/context.pyreturns the thread's most recent checkpoint id, or
Nonefor a fresh thread.--thread-idwas given and--checkpoint-idwas not, and passes it as the resume point.Behaviour elsewhere is untouched: a fresh run mints a new thread id, finds no
checkpoint, and starts from
STARTexactly as before. An explicit--checkpoint-idstill wins. No sub-agent is affected, since every otherrun_to_completioncaller either mints a unique thread per invocation oralready passes a checkpoint.
Resuming a thread that already ran to completion returns its final state instead
of authoring a second time.
Tests
tests/test_graph_resume.py— four tests over a two-node pregel loop with anentry node that injects a system prompt the way the real one does, on an
in-memory checkpointer. No LLM, no Postgres, 0.8s.
The third test is the one that gives the others teeth: it runs the identical
second invocation without the resume point and asserts the entry node runs
twice and the system prompt is duplicated, so a regression in the fix fails a
test rather than silently passing.
Routine pass:
1122 passed, 10 skipped, 11 deselected, pyright0 errors.Note for review
If a caller ever passed both an already-checkpointed
--thread-idand aresume_work_key, the crash snapshot merged intoflow_inputwould go unused,since a checkpoint resume discards the input. That combination does not occur
today (the snapshot path mints a fresh thread), and the snapshot is salvaged from
the very checkpoint being resumed, so I left it alone rather than guard a case
that has no caller.
🤖 Generated with Claude Code