Pass chat history to agents as structured messages#22607
Merged
mvdbeek merged 4 commits intogalaxyproject:devfrom May 1, 2026
Merged
Pass chat history to agents as structured messages#22607mvdbeek merged 4 commits intogalaxyproject:devfrom
mvdbeek merged 4 commits intogalaxyproject:devfrom
Conversation
The chat router was flattening prior conversation messages into a text blob via _build_query_with_context, which loses the role/content semantics chat models already know how to use. The structured pydantic-ai message format already exists -- ChatManager.get_chat_history has a format_for_pydantic_ai flag returning ModelMessage objects -- but the chat API was calling it with False and the router was re-flattening the result. Flips the API call to format_for_pydantic_ai=True, plumbs the structured list[ModelMessage] through to agent.run(message_history=...), and adds truncate_message_history that keeps the first message + last MAX_HISTORY_MESSAGES (40) so original intent is preserved across long conversations. Tighter cap (8) for sub-agent calls from inside @agent.tool since tool-context turns burn token budget faster. Drops the magic max_history=6 from router._build_query_with_context and the 200-char-per-message text-concat from BaseGalaxyAgent's _call_agent_from_tool.
CLAUDE.md is explicit that test files shouldn't use inline imports. The pydantic_ai.messages and truncate_message_history imports were duplicated across seven new tests; move them to module level (under the existing pytest.importorskip guard so the skip behavior is preserved when pydantic_ai isn't installed).
Mypy on CI runs against test files too (not just lib/), and list invariance tripped it up: ``history = [ModelRequest(...), ModelResponse(...)]`` gets inferred as ``list[ModelRequest]``, which can't be passed to a function expecting ``list[ModelMessage]``. Annotate the four test fixtures explicitly.
mvdbeek
approved these changes
May 1, 2026
|
This PR was merged without a "kind/" label, please correct. |
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 chat router was flattening prior turns into a "Previous conversation:\nrole: content\n..." text blob and prepending it to the user's query. pydantic-ai already supports a structured
message_historyparameter onagent.run(), andChatManager.get_chat_history(format_for_pydantic_ai=True)already returns alist[ModelMessage]-- the API just wasn't using it.This switches the chat API to the structured path and plumbs
message_historythroughBaseGalaxyAgent.process()and the sub-agent calls inside_call_agent_from_tool. The router's_build_query_with_contexttext-concat goes away, along with the magicmax_history = 6cap; truncation now goes throughtruncate_message_history()(first message + last 40, configurable viaMAX_HISTORY_MESSAGES). Sub-agents called from inside a tool get a tighterTOOL_HELPER_HISTORY_MESSAGES = 8window since tool-context turns burn token budget faster._extract_message_history()accepts both shapes: pydantic-aiModelMessageobjects from the chat API path, and legacy{role, content}dicts from any direct caller still on the old shape. Malformed entries are logged and dropped rather than crashing the run.Test plan
pytest test/unit/app/test_agents.py::TestAgentUnitMocked-- 30 passed (added 9 new tests covering truncation boundaries, legacy-shape coercion, unsupported-shape rejection, router passthrough, fresh-conversation path)