Skip to content

Make agent selection exclusive with model and reasoning in V2 chat - #1391

Merged
Paul Lizer (paullizer) merged 1 commit into
paullizer-react-v2-uifrom
paullizer-agent-model-selector-exclusivity
Sep 3, 2026
Merged

Make agent selection exclusive with model and reasoning in V2 chat#1391
Paul Lizer (paullizer) merged 1 commit into
paullizer-react-v2-uifrom
paullizer-agent-model-selector-exclusivity

Conversation

@paullizer

@paullizer Paul Lizer (paullizer) commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Fixes a wrong-model bug in the V2 chat composer. Selecting an agent left the Model picker showing a selected model and the Reasoning picker offering a level, but an agent can act on neither — it answers with its own azure_openai_gpt_deployment, and reasoning_effort only ever 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 in route_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.
  • User-visible behaviour: picking an agent now dims the Model picker back to the plain word "Model" and hides Reasoning. The chosen model is retained, not cleared, and returns when the agent is cleared. The dimmed picker stays clickable — using it is how you switch back, and doing so clears the agent for you.
  • The rule now lives in one place. New buildSelectionFields is 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: getCurrentAgentSelection checks that agent mode is active, but getCurrentModelSelection reads 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.py pins that asymmetry so the decision is revisited if V1 ever changes.

Linked issue

Fixes #1392

Release Notes & Latest Features

  • New Feature
  • Bug Fix
  • UI Enhancement
  • Breaking Change
  • Internal only

Is this visible to end users?

  • Yes
  • No

Is this admin-facing (Admin Settings, governance, deployment, config)?

  • Yes
  • No

Should this become a Latest Feature card?

  • Yes
  • No
  • Already added

Screenshot needed for the card?

  • Yes
  • No
  • Attached

Version bump

  • application/single_app/config.py VERSION third segment bumped, or not needed because this is docs-only — 0.261.0330.261.034
  • deployers/version.txt bumped, or not needed because deployers/ was not changed — deployers/ untouched

Testing / validation

  • cd application\v2_ui; npm run typecheck — clean
  • cd application\v2_ui; npm run build — succeeds, 2152 modules transformed
  • python functional_tests\test_v2_agent_model_exclusivity.pynew, 9/9 groups including 27 bundled behavioural checks
  • python functional_tests\test_v2_model_identity_and_scope.py — 9/9
  • python functional_tests\test_v2_chat_phase1_fixes.py — 10/10
  • python functional_tests\test_v2_conversation_details_and_gating.py — 9/9
  • python functional_tests\test_v2_api_payload_shapes.py — 6/6
  • python functional_tests\test_v2_dropdown_placement.py — 6/6
  • python functional_tests\test_v2_message_actions.py — 7/7
  • python functional_tests\test_v2_visual_style_controls.py — 16/16
  • python functional_tests\test_v2_ui_local_assets.py — 4/4
  • python functional_tests\test_v2_ui_spa_route.py — 3/3
  • python functional_tests\test_docs_app_surface_coverage.py — 7/7
  • python functional_tests\test_docs_site_quality.py — 6/6

Negative validation: the new test was verified against a deliberately reintroduced defect. Making buildSelectionFields emit agent_info alongside 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) and test_v2_chat_phase1_fixes.py (test_agent_selection_is_sent_as_agent_info).

Documentation

  • Release notes updated, or not needed — entry added under v0.261.034
  • Feature documentation updated, or not needed — not needed, this is a fix
  • Fix documentation updated, or not needed — docs/explanation/fixes/V2_AGENT_MODEL_EXCLUSIVITY_FIX.md

Security checklist

  • New Flask routes include @swagger_route(security=get_auth_security()) — no new routes; this is a front-end-only change with no server modifications
  • Settings sent to non-admin frontends use sanitize_settings_for_user() — no settings handling changed
  • Browser JavaScript is served from local SimpleChat static assets only; no CDN-hosted JS — the new module is TypeScript compiled into the existing local V2 bundle; no new dependency, import or asset URL
  • No secrets, keys, connection strings, or local-only artifacts are included — build output under application/single_app/static/v2/ remains gitignored and is not part of this diff

Notes 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:

  1. Image generation hides the model picker but leaves the agent picker visible, and the classic client forces image_generation = false when an agent is explicitly tagged. Same family of inconsistency, separate decision.
  2. retryMessage is still invoked with no options from MessageActions.tsx, so a retry uses server defaults rather than the composer's current selection. Routing retry through buildSelectionFields means it cannot reintroduce this conflict when that is eventually wired up.

One judgement call worth a reviewer's eye: showReasoning is now also false during image generation, matching updateReasoningButtonVisibility in static/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.

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>
@paullizer
Paul Lizer (paullizer) merged commit 505e30c into paullizer-react-v2-ui Sep 3, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant