Repository navigation
fix(tools): persist tool output reference labels and keep keys unique across runs - #629
Open
Shabonasar wants to merge 2 commits into
Open
Shabonasar wants to merge 2 commits into
Shabonasar wants to merge 2 commits into
Conversation
… across runs
[ref: toolNturnM] labels were added only when the key was live in the current
run's registry, and every run restarted its turn counter at 0. A restored
history therefore had no labels (the first labelled result of each later turn
changed the prefix and missed the provider cache), and keys repeated between
turns, so {{tool0turn0}} from an old turn silently resolved to the current one.
- ON_RUN_STEP_COMPLETED tool_call now carries `ref` and `unresolved_refs`
(direct, early, eager and batch completions); the content aggregator keeps them
- formatAgentMessages restores them as `_refKey` / `_unresolvedRefs`
- annotateMessagesForLLM labels by `_refKey` without a registry lookup
- Run seeds the registry turn counter past the highest restored key, so a
placeholder naming an older turn is reported as unresolved
Fixes LibreChat-AI#627
This was referenced Oct 20, 2026
… across runs
[ref: toolNturnM] labels were added only when the key was live in the current
run's registry, and every run restarted its turn counter at 0. A restored
history therefore had no labels (the first labelled result of each later turn
changed the prefix and missed the provider cache), and keys repeated between
turns, so {{tool0turn0}} from an old turn silently resolved to the current one.
- ON_RUN_STEP_COMPLETED tool_call now carries `ref` and `unresolved_refs`
(direct, early, eager and batch completions); the content aggregator keeps them
- formatAgentMessages restores them as `_refKey` / `_unresolvedRefs`
- annotateMessagesForLLM labels by `_refKey` without a registry lookup
- Run seeds the registry turn counter past the highest restored key, so a
placeholder naming an older turn is reported as unresolved
Fixes LibreChat-AI#627
This was referenced Oct 27, 2026
Author
|
Updated to current
|
This branch has not been deployed
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.
Summary
With tool output references on,
ToolNodelabels each tool result with[ref: tool<i>turn<n>]in the request it sends live. The label was shown only while the key was in the current run's registry, and every run restarted atturn0. As a result, a restored history lost the labels: the first request of each later turn missed the prompt cache, and Claude dropped thinking blocks bound to the old prefix. Keys also repeated between turns, so{{tool0turn0}}from an old turn silently resolved to the current turn's output. With this PR the label is the stored name of the output: it is saved with the result, restored with the history, and turn numbers continue across runs.Fixes #627
Related to LibreChat-AI/LibreChat#17102. Base:
main, no dependency on other PRs. LibreChat needs no change: the fields ride in the storedtool_callpart.How it works
ref/unresolved_refsfor direct tools, event-driven tools (batch and early completion) and eager executions.[unresolved refs: ...]warning.Type of change
Testing
Tested environments/configuration:
The table comes from an earlier run on a head with the same code. Real Anthropic API through LibreChat, 6 turns, adaptive thinking, code interpreter running code, chained
bash_toolcalls, MCP tool, restart after turn 3, turn 5 references a key from turn 1. "series" is the LibreChat series plus agents #613, #614, #615, #619 (LibreChat-AI/LibreChat#17102);devis LibreChatdevwith agentsmain. "before" used a 1h cache TTL, "after" 5m.devdevWith the series, keys run
tool0turn0...tool0turn8without repeats, and the turn-5 probe returns[unresolved refs: tool0turn0]instead of a wrong output. Ondevthe labels are now stable too; its misses come from other restore differences that the series fixes.Automated tests:
anthropic,google,vertexaillm.spec.ts).ToolNode.outputReferences.test.ts: key and unresolved refs on the completedtool_call(direct and event-driven), none when the feature is off, an earlier run's key reported as unresolved, a run seeded from the keys in its input history.invoke.test.ts: hydrated run 1 messages labelled, run 2 keys not repeating them.formatAgentMessages.tools.test.ts: stored key and unresolved refs restored and labelled as live, including the parts stored by the content aggregator.toolOutputReferences.test.ts,annotateMessagesForLLM.test.ts: turn counter continuity, labelling by_refKeywithout a registry entry.durability-checkpoint.integration.test.tsandpersistent-handoffs.test.ts, which need MongoDB and fail onmaintoo. LibreChat agent suites against all five:packages/apisrc/agents6913 passed (1 failure inhandlers.shutdown.spec.ts, a shutdown timing test);apiserver/controllers/agents1371 passed.Risk / compatibility
refand stays unlabelled, as it is today.Checklist