fix(agent): align history trim to user boundary for provider compatibility#5257
fix(agent): align history trim to user boundary for provider compatibility#5257WanZheng wants to merge 7 commits intozeroclaw-labs:masterfrom
Conversation
PR Review: fix(agent): align history trim to user boundary for provider compatibilityComprehension SummaryWhat: After any history trim operation (across four code paths: Why: Providers like Zhipu GLM and MiniMax require Blast radius: Four trim paths in Security & Performance
Overlap with PR #5264PR #5264 ( The two fixes are not redundant — they enforce different constraints:
Recommendation: Whoever merges second must reconcile the merge conflict. Ideally both invariants should be enforced in a single post-trim pass. Consider coordinating merge order or combining. Issues
VerdictAPPROVE -- The fix is correct, well-scoped, and addresses a real provider-compatibility bug. Test coverage is strong (12 new test cases across the helper, Merge coordination note: this PR will conflict with #5264. The maintainer merging second should ensure both invariants (user-first ordering AND tool-pair integrity) are preserved in the reconciled code. Reviewed by ZeroClaw PR Review Agent |
…ility After any trim operation (trim_history, emergency_history_trim, prune_history, Agent::trim_history), the first non-system message could be `assistant` or `tool`. Providers like Zhipu GLM reject such sequences, requiring `system* -> user` ordering. Add boundary alignment to every trim path: after removing messages, continue dropping leading non-system messages until the first one has role `user`. Extract shared `align_to_user_boundary` helper in history.rs and reuse it in history_pruner Phase 3. Changes: - history::trim_history: extend drain end to next user message - history::emergency_history_trim: call align_to_user_boundary after drop - history::align_to_user_boundary: new pub(crate) helper - history_pruner::prune_history: add Phase 3 via align_to_user_boundary - Agent::trim_history: extend drain_end to next user ConversationMessage Made-with: Cursor
…e APIs After proactive trim and length-based truncation, drop leading non-user turns so a prepended system message still yields system→user ordering (e.g. Zhipu GLM). Add unit tests for the alignment behavior. In OpenAI-compatible provider: centralize tool_stream via tool_stream_for_tools, omit tool_stream when tools are absent, and emit debug logs with message role sequences on HTTP 400 for easier diagnosis. Made-with: Cursor
…and test - Refactor `trim_history` to call shared `align_to_user_boundary` instead of inline while-loop, making the deduplication claim accurate - Add doc comment on no-user edge case in `trim_history` (no `keep_recent` guard; acceptable for degenerate conversations) - Add doc comment on `Agent::trim_history` explaining why it cannot reuse the shared helper (ConversationMessage vs ChatMessage type mismatch) - Add dedicated test for `Agent::trim_history` boundary alignment - Fix pre-existing rustfmt issues in agent.rs (assert! format, import order) Made-with: Cursor
Made-with: Cursor
5bacecc to
76756a6
Compare
Post-review update summaryReviewer feedback addressed (all items from @JordanTheJet's review)
Additional fix in this push
Validation evidence (local, post-update)Label request for maintainersThe PR body specifies
Note on PR #5264 overlapPR #5264 (tool_use/tool_result pair preservation) is still open and addresses a complementary invariant. Both PRs modify |
|
I closed #5264 |
Combine orphan ToolResults guard with user-boundary alignment on ConversationMessage, and keep both unit tests. Made-with: Cursor
…rim-user-boundary
|
I think this needs a rebase before meaningful review. GitHub reports DIRTY and local merge checking conflicts across provider compatibility, runtime history, the old src/agent/history_pruner.rs path, and channel code. Current master now has substantial history-pruner/tool_use/tool_result preservation work in crates/zeroclaw-runtime/src/agent/history.rs and history_pruner.rs, so the original fix may overlap with later changes. Could you rebase against current master and confirm the remaining provider-compatibility failure this PR still fixes? |
|
Hi @WanZheng — thanks for the careful work on this. The fix shape is sound, but this PR has been overtaken by parallel work and isn't reviewable as-is:
Closing as superseded. If #6303 lands and there's still a missed trim path that produces a leading-assistant or leading-tool message, please open a fresh issue with the exact reproduction — happy to revisit. Thanks again for the patch and for the rigorous validation evidence in the original description. |
Summary
masterfor all contributions):masterassistantortool, causing providers like Zhipu GLM to reject the request with a 400 error (they requiresystem* -> userordering).user. Extracted sharedalign_to_user_boundaryhelper to avoid duplication.Label Snapshot (required)
risk: low|medium|high):risk: mediumsize: XS|S|M|L|XL, auto-managed/read-only):size: Sagentagent: historyChange Metadata
bug|feature|refactor|docs|security|chore):bugruntime|provider|channel|memory|security|ci|docs|multi):agentLinked Issue
Supersede Attribution (required when
Supersedes #is used)N/A
Validation Evidence (required)
Commands and result summary:
Security Impact (required)
Privacy and Data Hygiene (required)
passsys,u1,a1,t1, etc.)Compatibility / Migration
i18n Follow-Through (required when docs or user-facing wording changes)
Human Verification (required)
What was personally validated beyond CI:
[system, user, assistant, tool, user, assistant], confirmed trim leavesuseras first non-system message across all four trim pathsSide Effects / Blast Radius (required)
history::trim_history,history::emergency_history_trim,history_pruner::prune_history,Agent::trim_historykeep_recentconfig protects the most recent messages.Agent Collaboration Notes (recommended)
ensure_valid_sequenceremoval claim), deduplicated Phase 3 logic in history_pruner by reusing sharedalign_to_user_boundary, validated with fmt/clippy/testAGENTS.md+CONTRIBUTING.md)Rollback Plan (required)
git revert <merge-commit>— restores previous trim behaviorsystem* -> userordering (Zhipu GLM, MiniMax) would return 400 errors after history trimming in long conversationsRisks and Mitigations
usermessage exists after the trim point,trim_historymay drain all non-system messages.keep_recentparameter protects the most recent messages from removal.