feat(provider/messages)Enable cross provider switch within conversation#3530
Conversation
|
Hi @garyzhang99, this is your 17th Pull Request. 💡 Quick TipGreat job using the PR template! Don't forget to fill in the Testing section with how to test your changes. 🙌 Join Developer CommunityThanks so much for your contribution! We'd love to invite you to join the official QwenPaw developer group! You can find the Discord and DingTalk group links under the "Developer Community" section on our docs page: We truly appreciate your enthusiasm—and look forward to your future contributions! 😊 We'll review your PR soon. |
There was a problem hiding this comment.
Pull request overview
This PR hardens request-time message normalization so users can switch model provider families (Gemini/OpenAI/Anthropic) mid-conversation without leaking provider-specific fields from previously-stored message blocks into the next provider’s API payload.
Changes:
- Added provider-family-aware normalization that strips
raw_inputuniversally and strips Gemini-onlyextra_contentunless targeting Gemini. - Threaded
target_familythrough the formatter normalization path and fixed the ordering so tool-input repair runs before strippingraw_input. - Adjusted formatter-time injections so
extra_contentis only injected for Gemini andreasoning_contentis not injected for Anthropic; added unit/integration tests covering cross-provider switches.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| src/qwenpaw/agents/utils/message_request_normalizer.py | Adds _clean_provider_specific_fields() and target_family support; reorders sanitization before provider-field cleanup. |
| src/qwenpaw/agents/model_factory.py | Derives target_family from formatter family; gates extra_content and reasoning_content injection by formatter family. |
| tests/unit/agents/test_message_request_normalizer.py | Adds direct unit tests for _clean_provider_specific_fields() and target_family behavior + raw_input repair ordering. |
| tests/unit/agents/test_model_factory_message_normalization.py | Adds tests asserting formatter-level normalization strips/preserves extra_content appropriately without mutating originals. |
| tests/unit/agents/test_cross_provider_normalization.py | New integration-style tests simulating provider switches (Gemini→OpenAI/Anthropic/Gemini) and raw_input repair. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Description
Clean up provider-specific field leakage when switching models mid-conversation.
When a user switches between providers (e.g. Gemini → OpenAI) during a chat session, provider-specific artifacts stored on
Msgcontent blocks could leak into API requests for the new provider, potentially causing request rejections or wasted payload:extra_content(Gemini'sthought_signature): Sent to OpenAI/Anthropic APIs which don't recognize it on tool call objects.raw_input(AgentScope stream-parsing artifact): Leaked to all providers; some reject unknown fields.reasoning_contentinjection: Applied unconditionally to all providers, including Anthropic which already passesthinkingblocks natively via_format_anthropic_messages.Changes
_clean_provider_specific_fields()inmessage_request_normalizer.py: Stripsextra_content(unless target is Gemini) andraw_input(unconditionally) from cloned messages at normalization time.target_familyparameter threaded from_normalize_messages_for_formatter→normalize_messages_for_model_requestso the normalizer knows which provider family to clean for.extra_contentinjection: Only injected into formatted payload when target is Gemini formatter.reasoning_contentinjection: Skipped for Anthropic (which handles thinking blocks natively), applied for OpenAI/Gemini._sanitize_tool_messages(which usesraw_inputto repair emptyinputfields) now runs before_clean_provider_specific_fields(which stripsraw_input).All operations run on deep-cloned messages; the stored conversation history is never mutated.
Related Issue: Relates to #2314
Security Considerations: N/A
Type of Change
Component(s) Affected
Checklist
pre-commit run --all-fileslocally and it passespytestor as relevant) and they pass