semantic router ui revamp - #5865
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughSummary by CodeRabbit
WalkthroughThe change makes semantic reference phrases the only complexity-routing mechanism. It adds semantic configuration validation, classifier recovery on provider changes, a redesigned router UI, height-based tag collapsing, enabled-key propagation, and updated complexity documentation. ChangesSemantic complexity router
Estimated code review effort: 4 (Complex) | ~60 minutes Mergeability Score: ⚪ Minimal · up to The current changes are merge-ready after normal checks and review, with no actionable merge-blocking risk remaining. Sequence Diagram(s)sequenceDiagram
participant Admin
participant ComplexityRouter
participant EmbeddingConfigSheet
participant SemanticClassifier
participant GovernancePlugin
Admin->>ComplexityRouter: Configure reference phrases and embedding settings
ComplexityRouter->>EmbeddingConfigSheet: Validate provider and semantic fields
EmbeddingConfigSheet-->>ComplexityRouter: Return normalized configuration
ComplexityRouter->>SemanticClassifier: Save classifier configuration
SemanticClassifier-->>ComplexityRouter: Report classifier state
GovernancePlugin->>SemanticClassifier: Classify request
SemanticClassifier-->>GovernancePlugin: Return semantic tier, score, or skipped result
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
ui/app/workspace/complexity-router/page.tsx (1)
301-319: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winThe "Not configured" branch hides a classifier that is still serving.
isNotConfiguredis derived from the live form values at line 695, not from the saved config. If a saved semantic config exists and the operator clears the provider or model in the form, this branch renders and reports "Not configured". The saved classifier keeps serving requests, and thehasUnsavedChangesnotice at lines 383-387 is in the other branch, so it never appears.Gate this branch on the saved config as well, or add the unsaved-changes notice to it.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@ui/app/workspace/complexity-router/page.tsx` around lines 301 - 319, Update the semantic status rendering around the isNotConfigured/isNotSaved branch so clearing provider or model values in the live form cannot hide the existing saved classifier or its unsaved-changes notice. Gate the “Not configured” path using the saved semantic configuration, or include the hasUnsavedChanges notice in this branch, while preserving the existing status messages for genuinely unsaved configurations.
🧹 Nitpick comments (3)
ui/lib/types/complexityRouter.ts (1)
84-102: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueReuse
TIER_PHRASE_LIST_DEFINITIONSfor the validation labels.
TIER_PHRASE_LIST_DEFINITIONSnow holds the canonical key and label for each tier.ui/app/workspace/complexity-router/page.tsxlines 131-135 declares a secondlistsarray with the same keys and labels for the duplicate-phrase check. If a label changes here, the validation message keeps the old wording.Derive the validation list from this constant instead.
♻️ Proposed change in ui/app/workspace/complexity-router/page.tsx
- const lists: Array<{ key: KeywordListKey; label: string }> = [ - { key: "simple_keywords", label: "Simple" }, - { key: "medium_keywords", label: "Medium" }, - { key: "complex_keywords", label: "Complex" }, - ]; + const lists = TIER_PHRASE_LIST_DEFINITIONS;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@ui/lib/types/complexityRouter.ts` around lines 84 - 102, Update the duplicate-phrase validation in the complexity-router page to derive its lists array from TIER_PHRASE_LIST_DEFINITIONS instead of redeclaring tier keys and labels. Reuse each definition’s canonical key and label so validation messages stay synchronized with the shared constant.ui/app/workspace/complexity-router/page.tsx (2)
533-540: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick winOmitting
semanticdeletes the saved classifier configuration.The endpoint replaces the whole record, so a payload without
semanticclears the stored embedding configuration. Today thesuperRefinecheck at lines 111-127 blocks the half-filled case, and only a fully blank provider and model reachesonValid. The protection is therefore indirect: it lives in a validation rule, not at the point where the destructive payload is built.Make the intent explicit at this site, so a later change to
superRefinecannot silently enable configuration loss.♻️ Proposed change
+ const hasProvider = values.semantic.provider.trim() !== ""; + const hasModel = values.semantic.embedding_model.trim() !== ""; + // A half-filled classifier must never reach here: it would drop the whole + // semantic block and delete the saved configuration. + if (hasProvider !== hasModel) return; const payload: AnalyzerConfig = { tier_boundaries: values.tier_boundaries, keywords: values.keywords, - ...(values.semantic.provider && values.semantic.embedding_model ? { semantic: values.semantic } : {}), + ...(hasProvider && hasModel ? { semantic: values.semantic } : {}), };🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@ui/app/workspace/complexity-router/page.tsx` around lines 533 - 540, Update the payload construction in the form submit handler around AnalyzerConfig so an unconfigured semantic classifier does not omit semantic and clear the saved configuration; preserve or explicitly handle the existing stored semantic settings when provider and embedding_model are blank, while retaining the current fully configured semantic behavior. Make this protection local to payload creation rather than relying on the values.semantic superRefine validation.
650-653: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueRemove the redundant
rules.validatefrom the phraseController.
useFormuseszodResolver, and the Zod schema already enforces this constraint with the same messages. Keep validation in the schema as the single source of truth.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@ui/app/workspace/complexity-router/page.tsx` around lines 650 - 653, Remove the redundant rules.validate configuration from the phrase Controller using the keywords.${key} field name. Rely exclusively on the existing useForm zodResolver schema for non-empty phrase validation and its messages, leaving the Controller’s control and name configuration unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@ui/app/workspace/complexity-router/page.tsx`:
- Around line 233-244: Update InfoTip in page.tsx so its guidance is reachable
on touch devices, since TooltipTrigger/TooltipContent only works on hover/focus
and not a plain tap. Use the InfoTip symbol as the entry point and switch to a
touch-friendly pattern such as rendering the same content in a Popover on touch
pointers or showing a short inline description near the affected fields. Keep
the desktop hover/focus tooltip behavior unchanged for non-touch users and
preserve the existing help text content.
- Around line 915-919: Update the timeout field’s onChange handler to preserve
the raw input string, including "0", in field.onChange instead of passing it
through formatSemanticTimeout; continue handling an empty value as empty and
leave display parsing unchanged. Let schema validation reject non-positive
values.
- Around line 62-67: Update the supportsEmbedding check so custom providers
without allowed_requests are treated as unrestricted instead of blocked; in the
workspace router logic, change the custom_provider_config path to accept
embedding when allowed_requests is missing or when allowed_requests.embedding is
true, while preserving the existing provider.name fallback for non-custom
providers. Apply the same unrestricted-missing rule in cachingView.tsx so both
embeddingProviders selection and the no-provider warning stay consistent across
the two locations.
In `@ui/components/ui/tagInput.tsx`:
- Around line 138-145: Update the toggle button in tagInput.tsx’s expanding
control so it exposes its current state with aria-expanded. Use the existing
isCollapsed / setTagsExpanded logic around the expandButtonTestId button, and
bind the attribute to reflect whether the tag area is expanded or collapsed
without changing the click behavior or label text.
---
Outside diff comments:
In `@ui/app/workspace/complexity-router/page.tsx`:
- Around line 301-319: Update the semantic status rendering around the
isNotConfigured/isNotSaved branch so clearing provider or model values in the
live form cannot hide the existing saved classifier or its unsaved-changes
notice. Gate the “Not configured” path using the saved semantic configuration,
or include the hasUnsavedChanges notice in this branch, while preserving the
existing status messages for genuinely unsaved configurations.
---
Nitpick comments:
In `@ui/app/workspace/complexity-router/page.tsx`:
- Around line 533-540: Update the payload construction in the form submit
handler around AnalyzerConfig so an unconfigured semantic classifier does not
omit semantic and clear the saved configuration; preserve or explicitly handle
the existing stored semantic settings when provider and embedding_model are
blank, while retaining the current fully configured semantic behavior. Make this
protection local to payload creation rather than relying on the values.semantic
superRefine validation.
- Around line 650-653: Remove the redundant rules.validate configuration from
the phrase Controller using the keywords.${key} field name. Rely exclusively on
the existing useForm zodResolver schema for non-empty phrase validation and its
messages, leaving the Controller’s control and name configuration unchanged.
In `@ui/lib/types/complexityRouter.ts`:
- Around line 84-102: Update the duplicate-phrase validation in the
complexity-router page to derive its lists array from
TIER_PHRASE_LIST_DEFINITIONS instead of redeclaring tier keys and labels. Reuse
each definition’s canonical key and label so validation messages stay
synchronized with the shared constant.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: b1fb28f4-e837-4b03-bc1f-5c61d16927a3
📒 Files selected for processing (6)
plugins/governance/complexity/config.goplugins/governance/complexity/exemplars_test.goui/app/globals.cssui/app/workspace/complexity-router/page.tsxui/components/ui/tagInput.tsxui/lib/types/complexityRouter.ts
a322e41 to
2c0c800
Compare
|
Note GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer. |
2c0c800 to
9482b61
Compare
|
Note GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer. |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@plugins/governance/complexity/config.go`:
- Around line 154-163: Update the merge loop using key and combined so valid
entries append the trimmed phrase rather than the original phrase, while
continuing to skip blank values and case-insensitive duplicates. Add table cases
covering whitespace trimming, blank values, and duplicate values that differ
only by case.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 19f1a04c-3306-4a7f-94a3-97410a90919f
📒 Files selected for processing (7)
plugins/governance/complexity/config.goplugins/governance/complexity/exemplars_test.goui/app/globals.cssui/app/workspace/complexity-router/page.tsxui/app/workspace/config/views/cachingView.tsxui/components/ui/tagInput.tsxui/lib/types/complexityRouter.ts
🚧 Files skipped from review as they are similar to previous changes (6)
- plugins/governance/complexity/exemplars_test.go
- ui/app/globals.css
- ui/components/ui/tagInput.tsx
- ui/app/workspace/config/views/cachingView.tsx
- ui/lib/types/complexityRouter.ts
- ui/app/workspace/complexity-router/page.tsx
6c826c0 to
f3efdc9
Compare
3560754 to
1acbdbd
Compare
|
Note GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer. |
f3efdc9 to
99e7268
Compare
1acbdbd to
7824de2
Compare
7824de2 to
b9be59c
Compare
b50f5ad to
f376470
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@ui/app/workspace/complexity-router/formSchema.ts`:
- Line 60: Update ui/app/workspace/complexity-router/formSchema.ts at lines
60-60 and 179-179: in the schema enum, replace the obsolete "vector_store" value
with "auto"; in toFormValues, normalize persisted "external" to "auto" before
returning the form values so legacy configurations validate and save correctly.
- Around line 27-28: Update normalizeSessionTtl so its fallback no longer
references the undefined DEFAULT_SESSION_CONFIG identifier; use the module’s
existing in-scope session TTL default, or import the owning constant if that is
the established source.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 99596557-6bfc-4b28-a1cd-9f550a0b8f67
📒 Files selected for processing (2)
ui/app/workspace/complexity-router/formSchema.tsui/app/workspace/complexity-router/page.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- ui/app/workspace/complexity-router/page.tsx
b9be59c to
32f18de
Compare
f376470 to
13287ed
Compare
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
plugins/governance/complexity/semanticclassifier.go (1)
270-285: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winUse a prior turn when the current turn is blank.
Line 270 reserves one slot for
LastUserTextbefore Line 283 determines that it is blank. WithmessageHistoryCount == 3, three nonblank prior turns and a blank current turn produce only two embedded turns. Compute the prior capacity after checking the current turn, so the function returns up to the configured count of nonblank user turns. Add a regression test for this case.Proposed fix
- if priorCount := messageHistoryCount - 1; priorCount > 0 && len(input.PriorUserTexts) > 0 { + hasLastUserText := strings.TrimSpace(input.LastUserText) != "" + priorCount := messageHistoryCount + if hasLastUserText { + priorCount-- + } + if priorCount > 0 && len(input.PriorUserTexts) > 0 { // Walk backwards so blank turns are skipped before the window is counted: // slicing first would let blanks eat into the requested history count. prior := make([]string, 0, priorCount) @@ - if strings.TrimSpace(input.LastUserText) != "" { + if hasLastUserText { texts = append(texts, input.LastUserText) }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@plugins/governance/complexity/semanticclassifier.go` around lines 270 - 285, The history assembly around LastUserText currently reserves a slot even when the current turn is blank, reducing the number of prior turns included. Determine whether LastUserText is nonblank before calculating priorCount, reserve capacity only when it will be appended, and collect up to messageHistoryCount total nonblank user turns. Add a regression test covering three nonblank prior turns with messageHistoryCount equal to 3 and a blank current turn.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@plugins/governance/complexity/semanticclassifier.go`:
- Around line 270-285: The history assembly around LastUserText currently
reserves a slot even when the current turn is blank, reducing the number of
prior turns included. Determine whether LastUserText is nonblank before
calculating priorCount, reserve capacity only when it will be appended, and
collect up to messageHistoryCount total nonblank user turns. Add a regression
test covering three nonblank prior turns with messageHistoryCount equal to 3 and
a blank current turn.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 1367efc3-6167-4623-b5f2-dfe6070701d8
📒 Files selected for processing (5)
core/schemas/bifrost.goplugins/governance/complexity/semanticclassifier.goplugins/governance/complexity/semanticclassifier_test.gotransports/config.schema.jsonui/app/workspace/complexity-router/formSchema.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- transports/config.schema.json
32f18de to
9c6b1dc
Compare
0d95748 to
9ca5d04
Compare
9c6b1dc to
95eba34
Compare
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
framework/configstore/complexityconfig.go (2)
497-506: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winNormalize phrases identically in the UI form.
The backend treats
"shared phrase"and"shared phrase"as the same phrase. The suppliedui/app/workspace/complexity-router/formSchema.tsonly trims whitespace. That input passes client validation, then fails with an opaque API error.Use the same whitespace-collapse rule in the form validation. Add a cross-tier duplicate test with internal whitespace.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@framework/configstore/complexityconfig.go` around lines 497 - 506, Update the phrase normalization used by the UI form schema in the complexity router so validation lowercases phrases and collapses internal whitespace with the same strings.Fields-style rule as the backend, rather than only trimming edges. Add a cross-tier validation test covering equivalent phrases with different internal spacing, ensuring the duplicate is rejected client-side.
248-263: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winSerialize semantic timeouts as millisecond numbers.
ComplexitySemanticConfig.MarshalJSONemits"timeout":"250ms". Emit250instead. Keep string decoding for backward compatibility.Update the full stack:
ui/lib/types/complexityRouter.ts: accept numeric timeouts and handle them before calling.trim().ui/app/workspace/complexity-router/formSchema.ts: normalize numeric API values or accept them in the schema. Otherwise numeric responses failz.string()validation and the current helpers throw.framework/configstore/complexityconfig_test.go: assert numeric serialization and round-trip behavior.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@framework/configstore/complexityconfig.go` around lines 248 - 263, Change ComplexitySemanticConfig.MarshalJSON in framework/configstore/complexityconfig.go:248-263 to serialize nonzero timeouts as millisecond numbers while preserving zero/omitted behavior and string decoding compatibility. Update timeout handling in ui/lib/types/complexityRouter.ts:14-17 and 146-157 to accept numeric values before string operations, and update ui/app/workspace/complexity-router/formSchema.ts to normalize or validate numeric API timeouts. Extend framework/configstore/complexityconfig_test.go:59-69 to assert numeric JSON serialization and round-trip behavior.Source: Coding guidelines
transports/config.schema.json (1)
1588-1599: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winBind
vector_store.configto the parentvector_store.type. The current conditions evaluateconfig, so they do not enforce that the nested configuration matches the parent type. Achromemvector store can accept Redis settings, and a Redis vector store can accept Chromem settings. Apply the conditional dispatch at thevector_storelevel.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@transports/config.schema.json` around lines 1588 - 1599, Update the conditional schema dispatch around the vector_store type/config definitions so the if/then checks the parent vector_store.type and routes vector_store.config to the matching schema. Ensure chromem selects chromem_config and the equivalent Redis branch selects redis_config, preventing configurations from being mixed across vector store types.Source: Path instructions
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@framework/configstore/complexityconfig.go`:
- Around line 497-506: Update the phrase normalization used by the UI form
schema in the complexity router so validation lowercases phrases and collapses
internal whitespace with the same strings.Fields-style rule as the backend,
rather than only trimming edges. Add a cross-tier validation test covering
equivalent phrases with different internal spacing, ensuring the duplicate is
rejected client-side.
- Around line 248-263: Change ComplexitySemanticConfig.MarshalJSON in
framework/configstore/complexityconfig.go:248-263 to serialize nonzero timeouts
as millisecond numbers while preserving zero/omitted behavior and string
decoding compatibility. Update timeout handling in
ui/lib/types/complexityRouter.ts:14-17 and 146-157 to accept numeric values
before string operations, and update
ui/app/workspace/complexity-router/formSchema.ts to normalize or validate
numeric API timeouts. Extend
framework/configstore/complexityconfig_test.go:59-69 to assert numeric JSON
serialization and round-trip behavior.
In `@transports/config.schema.json`:
- Around line 1588-1599: Update the conditional schema dispatch around the
vector_store type/config definitions so the if/then checks the parent
vector_store.type and routes vector_store.config to the matching schema. Ensure
chromem selects chromem_config and the equivalent Redis branch selects
redis_config, preventing configurations from being mixed across vector store
types.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 171ddeb5-59e8-4bec-82c0-2f2f3481d3b1
📒 Files selected for processing (8)
framework/configstore/complexityconfig.goframework/configstore/complexityconfig_test.goplugins/governance/complexity/config.gotransports/bifrost-http/handlers/governance_test.gotransports/config.schema.jsonui/app/workspace/complexity-router/formSchema.tsui/app/workspace/complexity-router/page.tsxui/lib/types/complexityRouter.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- ui/app/workspace/complexity-router/page.tsx
- ui/app/workspace/complexity-router/formSchema.ts
95eba34 to
a0ce181
Compare
9ca5d04 to
87cc608
Compare
a0ce181 to
9dafc96
Compare
87cc608 to
0d7bd2a
Compare

Summary
Simplifies the Complexity Router to be semantic-only, removing the lexical/semantic mode toggle and making embedding-based classification the single path. The page now treats phrase lists as reference phrases for the semantic classifier rather than dual-purpose keyword/exemplar lists, and the backend merges editable phrases onto the built-in keyword vocabulary instead of replacing it.
Changes
ClassificationModestate or tab switcher. Semantic classification is the only offered mode; the lexical scorer still runs internally but is no longer user-facing.DefaultEditableKeywordConfigreturns only the semantic exemplars fromconfigstore, not the combined keyword+exemplar list. The lexical scorer's built-in vocabulary is kept out of the administrator-facing lists.mergeEditableKeywordsOntoDefaultsnow adds to built-ins: Instead of replacing a tier's keyword list when the editable list is non-empty, it appends the editable phrases onto the built-in vocabulary, deduplicating by normalized key. This prevents the lexical scorer from silently losing its short-word signals when only sentence-length exemplars are present.sharedTierDefaultsdeduplicates: The helper now tracks seen entries (lowercased, trimmed) and skips duplicates when merging extra phrases onto a base list.fallback: "lexical"option is no longer offered. Configs carrying it are normalized to"none"on the next save. Theexternalvector store option is similarly hidden; persisted values are presented and re-saved as"auto".SemanticStatusPanelupdated: Reflects the new single-mode design — shows "Not configured" when no provider/model is set, and consolidates the failed-state messaging to a single callout regardless of fallback setting.TagInputcollapse behavior changed from count-based to height-based:collapsedTagLimitreplaced bycollapsedMaxHeight(px). AResizeObservermeasures actual rendered height so columns of different-length phrases collapse to the same visual size. The expand/collapse toggle is a single button that switches label rather than two separate buttons.ScrollAreasibling rather than a sticky child inside a Radix scroll container, which was unreliable insidedisplay:tableboxes. A@layer utilitiesrule with.own-scroll-parentoverrides the shell'soverflow: auto !importantto prevent a double scrollbar.InfoTip,FieldLabel,Callout, andSectionHeadingcomponents extracted: Inline description paragraphs replaced with tooltip-bearing labels and callout boxes to reduce visual noise.Type of change
Affected areas
How to test
Breaking changes
Configs with
fallback: "lexical"are silently normalized to"none"on the next save. Configs withvector_store: "external"are presented and re-saved as"auto". Administrators relying on lexical fallback behavior should be aware that unclassified requests will now skip complexity tier routing rather than falling through to keyword scoring.Related issues
Security considerations
No new auth surfaces, secrets handling, or PII exposure introduced.
Checklist
docs/contributing/README.mdand followed the guidelines