Skip to content

fix(agent): pin reply language to the latest live user message - #58348

Open
lkz-de wants to merge 1 commit into
NousResearch:mainfrom
lkz-de:lkz/context-compaction-language-fix
Open

fix(agent): pin reply language to the latest live user message#58348
lkz-de wants to merge 1 commit into
NousResearch:mainfrom
lkz-de:lkz/context-compaction-language-fix

Conversation

@lkz-de

@lkz-de lkz-de commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR closes a stale-language steering path in resumed conversations.

A resumed conversation can contain a large historical compaction summary. The active reply language should follow the user's latest live message, not the language of that historical summary or older quoted context.

This refresh narrows the branch to that rule:

  • the compaction handoff now states that the latest live user message controls reply language
  • the stable system prompt now says older context, summaries, quoted text, tool output, or a prior assistant mistake must not flip the reply language away from the latest user message
  • retired summary prefixes stay frozen so older persisted summaries still strip and renormalize correctly

Why

The original handoff fix addressed stale task resumption, but the reply-language rule still needed to be explicit. Without that guard, a long historical summary can remain salient enough to steer a later answer into the wrong language even when the latest live user message is in English.

Fix

  • add latest-live-user reply-language authority to SUMMARY_PREFIX
  • add RESPONSE_LANGUAGE_GUIDANCE to the stable prompt
  • preserve the immediately previous summary prefix in _HISTORICAL_SUMMARY_PREFIXES
  • add regression coverage for:
    • the stable reply-language guard
    • the summary-prefix reply-language rule
    • renormalization of retired prefixes
    • ordering of the reply-language rule before a foreign-language historical summary block

Testing

Run after the refresh:

scripts/run_tests.sh tests/agent/test_compress_focus.py tests/agent/test_system_prompt.py tests/agent/test_summary_prefix_semantics.py tests/agent/test_summary_prefix_tool_use.py -- --tb=short -q

Result:

  • 29 tests passed, 0 failed

Overlap check

This remains in the original bug family: replies should follow the latest live user message, not stale compaction context. The refresh deliberately avoids changing the broader summary-language policy.

Risk

Low. The change is limited to compaction handoff wording, stable prompt guidance, and regression tests. It does not change session persistence, tool execution, or message routing behavior.

@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades labels Jul 4, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Related to the context-compaction handoff family (merged #44454, #42895, #41650) but a distinct mechanism: those addressed stale task headings hijacking resumed sessions, whereas this fixes the summarizer preamble making the summary language behave like a latent reply-language instruction after resume. Not a duplicate — same file (agent/context_compressor.py), different bug.

@lkz-de lkz-de changed the title fix(context-compression): stop summary language steering resumed replies fix(agent): pin reply language to the latest live user message Jul 5, 2026
@lkz-de

lkz-de commented Jul 5, 2026

Copy link
Copy Markdown
Contributor Author

Amended this PR to close the remaining language-steering path more completely.

This update:

  • removes the hardcoded Dutch summarizer examples from agent/context_compressor.py and replaces them with neutral English examples
  • adds an always-on reply-language guard so the latest live user message controls reply language even in the presence of older summaries or prior context
  • adds regression coverage for the Dutch-example bias and the stable prompt guard

Validation run:

  • python3 -m py_compile agent/context_compressor.py agent/prompt_builder.py agent/system_prompt.py tests/agent/test_compress_focus.py tests/agent/test_context_compressor_temporal_anchoring.py tests/agent/test_system_prompt.py tests/agent/test_summary_prefix_semantics.py
  • python -m pytest -q tests/agent/test_compress_focus.py tests/agent/test_context_compressor_temporal_anchoring.py tests/agent/test_system_prompt.py tests/agent/test_summary_prefix_semantics.py
  • 28 passed in 1.36s

@lkz-de

lkz-de commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

Small note on the stable prompt wording: the final sentence in the reply-language guard ("If the latest user message is English ... reply in English") is intentionally explicit even though it is logically covered by the more general rule above it.

Reason: in practice, English is the easiest case for a model to treat as an unstated default, so spelling it out reduces the chance that older summary language, quoted text, or a prior assistant turn is treated as stronger than the user's latest live message. The intent is not to privilege English as a policy exception; it is to make the default-case interpretation unambiguous in the prompt itself.

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for tracing the compaction handoff path and preserving historical-prefix renormalization.

Problems

  • agent/context_compressor.py:1720 reverses the current same-language summary policy at agent/context_compressor.py:1996-1997. Maintainer commit 13294c2d added that policy specifically because English summaries muddied non-English conversations. This needs an explicit product-direction decision before replacing it with neutral English.
  • The new “must NEVER be treated as a reply-language instruction” text is sent only to the auxiliary summarizer (agent/context_compressor.py:2115-2144); the subsequent chat context stores only its returned summary (:2205-2213). It cannot itself constrain a later reply.

Suggested changes

  • Keep the current summary-language policy unless maintainers explicitly choose to reverse 13294c2d; evaluate the handoff/system-prompt guard as the narrower fix.
  • Add one transcript-assembly regression covering a foreign summary followed by a newer live user message.

Automated hermes-sweeper review.

@@ -1694,8 +1720,10 @@ def _generate_summary(
"compact record of prior work. "

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reverses the current same-language summary policy introduced by maintainer commit 13294c2d, whose stated purpose was to avoid muddling non-English conversations with English context. Please retain that policy unless maintainers explicitly choose the product-direction reversal; the reply-language guard can be considered independently.

@@ -1694,8 +1720,10 @@ def _generate_summary(
"compact record of prior work. "
"Produce only the structured summary; do not add a greeting, "
"preamble, or prefix. "

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This instruction is sent only to the auxiliary summarizer, and _generate_summary() stores only its returned content in the later transcript. It cannot directly control the reply model on a later turn; the handoff/system-prompt guidance is the mechanism that reaches that model.

@teknium1 teknium1 added the sweeper:blast-broad Sweeper blast radius: broad — a core path most sessions hit label Jul 15, 2026
@lkz-de

lkz-de commented Jul 16, 2026

Copy link
Copy Markdown
Contributor Author

Thanks — agreed on the boundary.

The stable prompt guard is the narrow load-bearing fix here: the latest live user message should control reply language even when older summaries, quoted text, or prior assistant turns are in a different language. I also agree that reversing the existing same-language summary policy needs an explicit product-direction decision rather than being smuggled in under this bugfix.

I’ll refresh this by keeping the existing summary-language policy, dropping that policy reversal from this PR, and adding the transcript-assembly regression for a foreign-language summary followed by a newer live user message so the later reply-language guard is exercised on the assembled context rather than only in isolation.

@lkz-de
lkz-de force-pushed the lkz/context-compaction-language-fix branch from c54c7c0 to 1d11378 Compare July 24, 2026 00:17
@lkz-de

lkz-de commented Jul 24, 2026

Copy link
Copy Markdown
Contributor Author

Refreshed the branch onto current main and narrowed it to the reply-language guard.

The current head keeps the latest-live-user language authority in the compaction handoff and stable prompt, preserves retired summary-prefix stripping, and drops the broader summary-language policy change from the earlier version of this PR.

Verification on the refreshed branch:

scripts/run_tests.sh tests/agent/test_compress_focus.py tests/agent/test_system_prompt.py tests/agent/test_summary_prefix_semantics.py tests/agent/test_summary_prefix_tool_use.py -- --tb=short -q

Result: 29 tests passed, 0 failed.

@lkz-de
lkz-de force-pushed the lkz/context-compaction-language-fix branch 2 times, most recently from a1ca33a to 8032018 Compare July 27, 2026 14:30
@lkz-de
lkz-de force-pushed the lkz/context-compaction-language-fix branch from 8032018 to 253277a Compare August 1, 2026 15:54
@lkz-de

lkz-de commented Aug 1, 2026

Copy link
Copy Markdown
Contributor Author

Rebased this onto current main at 470cf66b0 and pushed head 253277a00092239bffa2fdfdd1caab7b76642429.

Verification:

  • python3 -m py_compile agent/context_compressor.py agent/prompt_builder.py agent/system_prompt.py tests/agent/test_compress_focus.py tests/agent/test_summary_prefix_semantics.py tests/agent/test_summary_prefix_tool_use.py tests/agent/test_system_prompt.py
  • uv run --with pytest --with python-dotenv python -m pytest tests/agent/test_compress_focus.py tests/agent/test_summary_prefix_semantics.py tests/agent/test_summary_prefix_tool_use.py tests/agent/test_system_prompt.py -q → 22 passed

I also attempted the wider tests/agent tests/run_agent sweep. With the needed async/dependency extras it reached 516 passed and then failed in tests/agent/test_auxiliary_named_custom_providers.py, whose expectations flip depending on whether the anthropic SDK is installed. This PR’s patch has no overlap with auxiliary/provider/LSP modules, so I’m treating that as unrelated test-environment fallout rather than a blocker for this refresh.

Scope note: the previous SUMMARY_PREFIX generation is now frozen as a historical prefix, and the historical-prefix reflow was byte-verified against the prior generated strings so already-persisted summaries still strip normally. Adding the stable language-guidance prompt part will cause a one-time prompt-cache prefix miss for existing sessions after deploy.

@lkz-de
lkz-de force-pushed the lkz/context-compaction-language-fix branch from 253277a to 223281c Compare August 1, 2026 19:52
@lkz-de

lkz-de commented Aug 1, 2026

Copy link
Copy Markdown
Contributor Author

Pushed a refreshed follow-up on current main.

This keeps the PR's user-facing language guard intact and updates the micro-compaction regression after the CI slice caught the expected accounting edge from the longer summary preamble. The test still asserts cumulative savings go positive across repeated passes; the synthetic fixture now needs five passes instead of four because the added live-language sentence increases the one-time marker overhead.

Local verification on the refreshed head:

python -m pytest tests/agent/test_micro_compaction.py::TestMicroCompaction::test_cumulative_savings_accumulate_across_passes tests/agent/test_compress_focus.py tests/agent/test_summary_prefix_semantics.py -q --maxfail=1
# 10 passed in 1.03s

I also spot-checked that the newly frozen historical prefix entry matches the previous origin/main SUMMARY_PREFIX byte-for-byte before pushing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P2 Medium — degraded but workaround exists sweeper:blast-broad Sweeper blast radius: broad — a core path most sessions hit sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants