Skip to content

Fix History replay dropping output-format reminders - #10554

Merged
isaacbmiller merged 1 commit into
mainfrom
fix/history-replay-format-reminders
Oct 5, 2026
Merged

isaacbmiller merged 1 commit into
mainfrom
fix/history-replay-format-reminders

Conversation

@isaacbmiller

Copy link
Copy Markdown
Collaborator

Summary

Current user turns include the adapter’s output-format reminder, but History replay drops it. This rewrites a previously sent message rather than extending the request, preventing reuse of the cached trajectory prefix on affected providers.

Render historical input turns with main_request=True, preserving the reminder across Chat, JSON, and XML adapters. Keep output-only events free of synthetic user messages. No new state, public API, or provider-specific cache logic.

This is separate from #10387. Its specialized REPL-event renderer will need to adopt the same behavior; this PR changes only general History replay.

Verification

  • Added a three-turn regression asserting each subsequent request preserves the previous request as an exact prefix. All three adapter variants fail before the fix and pass afterward.
  • Updated exact-message expectations; existing native-tool history tests cover output-only events.
  • pytest -q tests/adapters tests/predict/test_react.py tests/predict/test_predict.py: 464 passed, 1 skipped.
  • Ruff and git diff --check pass.

This preserves input-turn reminders for the same adapter/signature; it does not claim lossless replay across signature changes or resolve native-reasoning replay.

Render historical input turns with the same format reminder as the original request, without adding user messages for output-only events.

Add a three-turn prefix regression across Chat, JSON, and XML adapters and update exact-message expectations.

Amp-Thread-ID: https://ampcode.com/threads/T-01a0f840-fb6d-771c-889d-a19606285691
Co-authored-by: Isaac Miller <isaac@cmpnd.ai>
@greptile-apps

greptile-apps Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 5/5

[Medium risk] Fixes prompt formatting in conversation history replay.

Safe to merge based on the checked history-formatting paths.

What we checked:

  • Compared sparse-history formatting before and after the change across Chat, JSON, and XML adapters. T-Rex
  • Compared native tool-call histories before and after the change through Chat and JSON adapters, and verified ordering and matching IDs. T-Rex
  • Compared replay behavior for reminders in partial input turns, confirming after the change turns matched the original requests and prefixes aligned. T-Rex
  • Verified both runs preserved ordering, tool calls, and produced no reminder-only user messages; after the change only historical turns containing an input gained a reminder. T-Rex

Summary

The PR preserves output-format reminders when replaying historical input turns, allowing successive requests to retain the same message prefix. Checks of sparse histories and native tool-call histories found no blocking issue.

Reviews (1) · Last reviewed commit: "Preserve output-format reminders in Hist..."

@isaacbmiller
isaacbmiller merged commit f207d35 into main Oct 5, 2026
23 checks passed
@isaacbmiller
isaacbmiller deleted the fix/history-replay-format-reminders branch October 5, 2026 16:49
mannuch added a commit to mannuch/dspy that referenced this pull request Oct 5, 2026
Pulls in 49 upstream commits, three of which touch the RLM/adapter code this
branch changes:

- stanfordnlp#10554 (reminders in History replay): adopted for the REPL-event branch too,
  so a replayed step's user message is now byte-identical to the one sent at
  that step. Pinned by test_replayed_step_message_matches_the_live_one.
- stanfordnlp#10493 (interpreter factories at call time): the positional `interpreter`
  argument is gone. `history_processor` follows upstream's convention for
  runtime configuration and is now keyword-only on forward/aforward; the
  "second positional argument" TypeError is removed. RLM tests move from the
  pooled-interpreter fixture to `interpreter_factory=PythonInterpreter`.
- stanfordnlp#10478 (sub-dspy interpreters): `_LLMCallBudget`, `_setup_facade` and
  `sub_agent_rules` thread through the action loop alongside the history
  plumbing.

Conflict resolutions of note:
- Reserved-name validation keeps upstream's `interpreter_factory` guard and
  `is_reserved()` (now covering sandbox-facade names) plus this branch's
  `_RESERVED_INPUT_NAMES` check, each with its own error message.
- Upstream's new facade tests read `result.trajectory`; renamed to
  `repl_trajectory` to match this branch.
- Adapter exact-message tests take upstream's expectations (ours were
  formatting-only); the RLM request snapshot is regenerated so it carries both
  upstream's sub-agent instructions and this branch's signature fields.

Full suite: 2616 passed, 365 skipped, 2 xfailed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
marcodelpin added a commit to marcodelpin/dspy that referenced this pull request Oct 7, 2026
Upstream sync of 7 commits (stanfordnlp#10569 stanfordnlp#10408 stanfordnlp#10417 stanfordnlp#10576 stanfordnlp#10553 stanfordnlp#10554 stanfordnlp#10539).
Three conflicts, each resolved keeping the fork side and porting the upstream change:
- dspy/streaming/streaming_listener.py: keep the fork's self.reset() (stanfordnlp#8425) at the
  allow_reuse site; reset() now also clears value_started and held_whitespace, the
  per-stream state upstream stanfordnlp#10553 added.
- tests/adapters/test_chat_adapter.py: keep the fork's objective line (stanfordnlp#9901, history
  not listed) and take stanfordnlp#10554's format reminder on the first history turn.
- tests/adapters/test_xml_adapter.py: keep the fork's rebuilt golden and add stanfordnlp#10554's
  reminder to the history turn.
New test test_reset_clears_boundary_whitespace_state pins the reset() addition
(red on a mutant without it, green with it).
isaacbmiller pushed a commit that referenced this pull request Oct 12, 2026
…age_content

Adapter.format_conversation_history passes main_request since #10554, so
TwoStepAdapter.format_conversation_history raised TypeError.

Amp-Thread-ID: https://ampcode.com/threads/T-01a12205-b058-723a-ad3e-3dd53a6cd097
Co-authored-by: Isaac Miller <isaac@cmpnd.ai>
isaacbmiller added a commit that referenced this pull request Oct 12, 2026
…age_content (#10628)

Adapter.format_conversation_history passes main_request since #10554, so
TwoStepAdapter.format_conversation_history raised TypeError.

Amp-Thread-ID: https://ampcode.com/threads/T-01a12205-b058-723a-ad3e-3dd53a6cd097

Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: Isaac Miller <isaac@cmpnd.ai>
dbreunig added a commit to dbreunig/dspy that referenced this pull request Oct 16, 2026
Each call sent the turn budget in its trailing user message, but history
did not store it, so the next call replayed that turn without it. Call N
was no longer a prefix of call N+1, which defeats provider prompt-cache
checkpoints on the trailing message.

Store each turn's turn_budget in that turn's history event. Since stanfordnlp#10554
replays the output-format reminder for events with inputs, replay now
rebuilds the exact request, and with native function calling every
request is a prefix of the next. That also covers turns 2+, which already
dropped the trailing reminder before this change.

Also default `show_turn_budget` to False at class level so agents pickled
before the flag existed (unpickling skips __init__) keep working.
isaacbmiller added a commit that referenced this pull request Oct 16, 2026
#10662)

* feat(dspy): ReActV2 show the agent its remaining turn budget each turn

ReActV2 never told the model about `max_iters`; it ran into a forced
`submit` without warning. Each LM call now carries a `turn_budget` input
("Turn 3 of 10 (7 more after this one).") and the instructions say the run
must end with `submit`. The forced final call says no turns remain.

The budget is rendered only in the newest user message and is not stored
in history, so earlier counts are not replayed and the cached history
prefix is unchanged. It follows per-call `max_iters` overrides and can be
turned off with `show_turn_budget=False`, which also frees the reserved
`turn_budget` field name.

Motivated by "Budget-Aware Tool-Use Enables Effective Agent Scaling"
(arXiv 2511.17006). Tracks CMP-1454.

* test(dspy): expect ReActV2 turn_budget in GEPA reflective dataset inputs

* fix(dspy): keep ReActV2 requests cache-prefixable with turn_budget

Each call sent the turn budget in its trailing user message, but history
did not store it, so the next call replayed that turn without it. Call N
was no longer a prefix of call N+1, which defeats provider prompt-cache
checkpoints on the trailing message.

Store each turn's turn_budget in that turn's history event. Since #10554
replays the output-format reminder for events with inputs, replay now
rebuilds the exact request, and with native function calling every
request is a prefix of the next. That also covers turns 2+, which already
dropped the trailing reminder before this change.

Also default `show_turn_budget` to False at class level so agents pickled
before the flag existed (unpickling skips __init__) keep working.

* refactor(dspy): always show ReActV2 the turn budget

Drop the `show_turn_budget` setting per review; the budget is always on.
`turn_budget` joins the reserved input names. Agents pickled before this
change restore an inner signature without the field, so the budget is
skipped when the field is absent.

* refactor(dspy): drop ReActV2 turn budget instruction line

The turn_budget field description already says the model sees its current turn and how many remain before it must submit. Keeping the explanation only there leaves the agent instructions identical to main, and field descriptions are not rewritten by instruction optimizers.

* docs(dspy): reword ReActV2 turn budget description

* Simplify explanation of turn_budget in ReActV2

Removed redundant explanation about turn_budget storage and replay rebuilds.

* Update dspy.ReActV2 function documentation

Clarify description of dspy.ReActV2 function.

---------

Co-authored-by: Isaac Miller <17116851+isaacbmiller@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants