Make agent selection exclusive with model and reasoning in V2 chat - #1391
Merged
Paul Lizer (paullizer) merged 1 commit intoSep 3, 2026
Conversation
Selecting an agent in the V2 composer left the Model picker showing a selected model and the Reasoning picker offering a level, though an agent can act on neither: it answers with its own azure_openai_gpt_deployment, and reasoning_effort only reaches the direct-model path. The invisible half mattered more. chatStore.sendMessage assigned the model identity unconditionally and then appended agent_info and reasoning_effort, so V2 posted all three. The route only lets an agent request pick its own model when no model identity was sent (should_use_default_model), so that branch never fired and an agent could answer through the wrong model. The rule now lives in one place, buildSelectionFields, read by both the toolbar and the request builder -- the original bug arose because each decided separately. With an agent selected the request carries agent_info alone; the model picker is shown as overridden rather than removed, keeping its selection and staying clickable because using it is how the user switches back; and the reasoning picker is hidden. This diverges from V1 deliberately: its getCurrentModelSelection reads the model select without checking that agent mode hid it, so it posts a model alongside an agent too. The test pins that asymmetry. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
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.
Summary
azure_openai_gpt_deployment, andreasoning_effortonly ever reaches the direct-model path.chatStore.sendMessageassigned the model identity unconditionally and then appendedagent_infoandreasoning_effort, so V2 posted all three. The route only lets an agent request pick its own model when no model identity was sent (should_use_default_modelinroute_backend_chats.py), so that branch never fired in V2 and an agent could quietly answer through a different model than the one it is configured with.buildSelectionFieldsis read by both the toolbar and the request builder; the original bug arose precisely because each decided separately and drifted apart.Deliberate divergence from the classic UI
V1 has the same asymmetry and posts a model alongside an agent too:
getCurrentAgentSelectionchecks that agent mode is active, butgetCurrentModelSelectionreads the model select without checking that agent mode has hidden it. Suppressing the fields is therefore a considered divergence, not a parity break.test_v2_agent_model_exclusivity.pypins that asymmetry so the decision is revisited if V1 ever changes.Linked issue
Fixes #1392
Release Notes & Latest Features
Is this visible to end users?
Is this admin-facing (Admin Settings, governance, deployment, config)?
Should this become a Latest Feature card?
Screenshot needed for the card?
Version bump
application/single_app/config.pyVERSIONthird segment bumped, or not needed because this is docs-only —0.261.033→0.261.034deployers/version.txtbumped, or not needed becausedeployers/was not changed —deployers/untouchedTesting / validation
cd application\v2_ui; npm run typecheck— cleancd application\v2_ui; npm run build— succeeds, 2152 modules transformedpython functional_tests\test_v2_agent_model_exclusivity.py— new, 9/9 groups including 27 bundled behavioural checkspython functional_tests\test_v2_model_identity_and_scope.py— 9/9python functional_tests\test_v2_chat_phase1_fixes.py— 10/10python functional_tests\test_v2_conversation_details_and_gating.py— 9/9python functional_tests\test_v2_api_payload_shapes.py— 6/6python functional_tests\test_v2_dropdown_placement.py— 6/6python functional_tests\test_v2_message_actions.py— 7/7python functional_tests\test_v2_visual_style_controls.py— 16/16python functional_tests\test_v2_ui_local_assets.py— 4/4python functional_tests\test_v2_ui_spa_route.py— 3/3python functional_tests\test_docs_app_surface_coverage.py— 7/7python functional_tests\test_docs_site_quality.py— 6/6Negative validation: the new test was verified against a deliberately reintroduced defect. Making
buildSelectionFieldsemitagent_infoalongside the model identity fails three checks (an agent selection sends no model identity at all,an agent selection sends no reasoning level,nothing but agent_info is sent for an agent), confirming the test catches the original bug rather than merely passing.Existing tests updated, not relaxed: three assertions checked for a literal call site that moved into the shared rule. Each was updated to follow the indirection and still asserts the same guarantee —
test_v2_model_identity_and_scope.py(test_client_sends_the_whole_model_identity,test_retry_resolves_the_model_the_same_way) andtest_v2_chat_phase1_fixes.py(test_agent_selection_is_sent_as_agent_info).Documentation
v0.261.034docs/explanation/fixes/V2_AGENT_MODEL_EXCLUSIVITY_FIX.mdSecurity checklist
@swagger_route(security=get_auth_security())— no new routes; this is a front-end-only change with no server modificationssanitize_settings_for_user()— no settings handling changedapplication/single_app/static/v2/remains gitignored and is not part of this diffNotes for review
Two pre-existing gaps were found while tracing this and deliberately left alone, both recorded under "Known limitations" in the fix doc and in #1392:
image_generation = falsewhen an agent is explicitly tagged. Same family of inconsistency, separate decision.retryMessageis still invoked with no options fromMessageActions.tsx, so a retry uses server defaults rather than the composer's current selection. Routing retry throughbuildSelectionFieldsmeans it cannot reintroduce this conflict when that is eventually wired up.One judgement call worth a reviewer's eye:
showReasoningis now also false during image generation, matchingupdateReasoningButtonVisibilityinstatic/js/chat/chat-reasoning.js, which hides the control for image generation as well as for agents. It is one extra boolean in the same rule and trivial to drop if unwanted.