FEAT: Allow creation and display of RoundRobinTarget in GUI#1944
Open
jsong468 wants to merge 7 commits into
Open
FEAT: Allow creation and display of RoundRobinTarget in GUI#1944jsong468 wants to merge 7 commits into
RoundRobinTarget in GUI#1944jsong468 wants to merge 7 commits into
Conversation
… target creation and display in gui
romanlutz
reviewed
Jun 5, 2026
Mirrors the dedup logic in TargetInitializer._auto_group_targets so the GUI/API flow can't produce a round-robin that hits the same underlying endpoint twice. Weights for deduped entries are dropped alongside them. If fewer than 2 distinct targets remain after dedup, raise a clear error listing the skipped duplicate names. Addresses review comment on PR microsoft#1944. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…, fix isCompatible fallback
Addresses Roman's review comment on isCompatible() in CreateTargetDialog:
1. Hash-based dedup in the picker (Option A from review discussion).
- Surfaces ComponentIdentifier.hash as a new optional identifier_hash field
on the TargetInstance DTO.
- target_object_to_instance now populates it; the existing recursive
_build_inner_targets call propagates it to inner targets for free.
- eligibleTargets in CreateTargetDialog filters out candidates whose
identifier_hash matches any already-selected target's hash. Targets with
null/undefined hash are NOT collapsed together (safe handling).
- This plugs the last gap left by isCompatible: behavioral-param mismatches
are already pre-filtered, but two registry entries that resolve to the
same backend config (same hash) were still pickable and would hit the
service-layer dedup error from the previous commit.
2. Fix isCompatible() fallback bug (Roman's specific repro).
- Adds effectiveUnderlyingModel(t) helper using || (catches both null AND
empty string, matching the backend's �alue is None or value == "" rule
in RoundRobinTarget._resolve_param).
- isCompatible() now compares effective underlying models so that, for
example, picking azure_foundry_deepseek (DeepSeek-R1, underlying=None)
no longer shows azure_foundry_mistral_large / google_gemini / ollama
as compatible just because all four have underlying_model_name=None.
3. Surface RFC 7807 backend detail on failed creation.
- Replaces the catch block's err.message with toApiError(err).detail so the
dialog shows the actual validation message (e.g. "Behavioral parameter
'underlying_model_name' differs...") instead of the generic axios
"Request failed with status code 400".
- Updates one pre-existing test ("non-Error exceptions") that expected a
hard-coded "Failed to create target" string — toApiError now surfaces the
thrown string verbatim, which is strictly more informative.
4. Extend the frontend/backend drift guard.
- Adds test_target_eval_param_fallbacks_match_frontend asserting
TARGET_EVAL_PARAM_FALLBACKS == {'underlying_model_name': 'model_name'}
so future fallback additions surface a clear failure pointing at
effectiveUnderlyingModel() in CreateTargetDialog.tsx.
Tests added:
- backend: test_target_eval_param_fallbacks_match_frontend
- backend: extended test_maps_target_with_identifier to cover identifier_hash
- frontend: filters duplicate-by-identifier-hash targets out of the picker
- frontend: applies underlying_model_name -> model_name fallback when filtering
- frontend: surfaces the backend error detail when target creation fails
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Roman's review comment #2: parseInt("2.5") silently truncates to 2, parseInt("1e10") returns 1, and 99999999999 was accepted with no upper bound. Typing 0 silently reverted. Changes: - Extract parseWeight + MAX_WEIGHT=1000 to weightValidation.ts - Strict regex-based parser rejects empty, non-integers (2.5, 1e10, -3), values < 1, and values > MAX_WEIGHT - Refactor SelectedInnerTarget to single source of truth (weightInput: string), avoiding silent revert - Add step='1', min='1', max='1000', aria-invalid, aria-label to Input; render alert below row on invalid - Disable Create when any weight is invalid; re-validate in handleSubmit to defend against Enter-key bypass Tests: 9 parseWeight unit tests + 2 dialog integration tests (alert+disabled, end-to-end submit with parsed ints). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Reformat multi-line f-string in target_service.py per ruff-format - Add 3 dialog tests covering removeInnerTarget, submit-time weight re-validation, and the <2 inner targets guard, lifting branch coverage from 84.97% to 86.23% Co-authored-by: Copilot <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.
Description
Improves the GUI experience for
RoundRobinTargetacross three surfaces: the Target Configuration table, the Create Target dialog, and the Chat window target badge. Also fixes duplicate inner targets inTargetsInitializercaused by overlapping.enventries.Target Configuration Table
model_nameonly when all inner targets have the same deployment name; otherwise shows "—" to avoid misleading display.InnerTargetRowscomponent: Sub-row rendering is shared between the active summary and main table to eliminate duplication.Create Target Dialog
RoundRobinTargetto the target type dropdown. When selected, the endpoint/model/auth fields are replaced with a target picker that lets users select existing targets from the registry.target_type,underlying_model_name,temperature,top_p). Backend performs definitive validation on submit..pyrit_confauto-populate hint is hidden for RoundRobinTarget since users are picking existing targets, not configuring new ones.Chat Window Badge & Tooltip
RoundRobinTarget (gpt-4o ×3)usingunderlying_model_name(the shared model) rather than a potentially inconsistent deployment name.Backend Changes
TargetInstancemodel: Added optionalinner_targets: list[TargetInstance]field for composite target support.model_nameonly when all inner targets agree; hoistsunderlying_model_namewhen uniform.create_target_asynchandlesRoundRobinTargetby resolvingtarget_registry_namesfrom the registry and delegating all validation to theRoundRobinTargetconstructor._auto_group_targetsnow deduplicates inner targets byComponentIdentifier.hashbefore creating round-robin groups. This prevents duplicate entries when.envfiles have overlapping configurations that resolve to identical targets (e.g., two env var blocks pointing to the same endpoint + model + key). Previously, these duplicates wasted rotation slots in the round-robin without adding any load-balancing benefit.Drift Guard
TestFrontendBackendCompatibilitySync— a backend test that assertsTARGET_EVAL_PARAMSmatches the expected set. If someone adds a behavioral param, this test fails with a message directing them to update the frontendisCompatible()function.Tests and Documentation
Backend Tests
test_mappers.py): 4 tests — inner target population, model name hoisting when uniform, model name omission when divergent, null for non-composite targets.test_target_service.py): 3 tests — registry name resolution with mocked constructor, <2 names error, unknown name error.test_targets_initializer.py): 1 test — deduplication of identical targets in auto-grouping verifies that a duplicate target (same endpoint + model + key registered under a different name) is excluded from the round-robin group.test_target_service.py): 1 test —TARGET_EVAL_PARAMSmatches frontendisCompatible()fields.Frontend Tests
TargetTable.test.tsx): 2 tests — expand button renders only for RoundRobinTarget, click expands sub-rows with inner target details.TargetBadge.test.tsx): 2 tests —×Ncount in display name,underlying_model_namepreferred overmodel_name.CreateTargetDialog.test.tsx): 2 tests — RoundRobinTarget shows target picker (hides endpoint fields), Create button disabled with <2 selections.Manual Testing