🌐 US-Proxy
class="logged-out env-production page-responsive" style="word-wrap: break-word;" >
Skip to content

feat(site/src): add Known Model autocomplete and frontend defaults - #24842

Merged
ThomasK33 merged 28 commits into
mainfrom
onboarding-zaj7
May 4, 2026
Merged

feat(site/src): add Known Model autocomplete and frontend defaults#24842
ThomasK33 merged 28 commits into
mainfrom
onboarding-zaj7

Conversation

@ThomasK33

@ThomasK33 ThomasK33 commented Apr 30, 2026

Copy link
Copy Markdown
Member

Replaces the blank Model Identifier free-text input on the Add Model page with provider-scoped Known Model autocomplete and frontend-only metadata defaults for native OpenAI and Anthropic providers. Selecting a Known Model, or typing an exact canonical identifier and blurring the field, prefills contextLimit, the appropriate max-output-tokens field, and flat base pricing in the existing form. Edit mode, duplicate mode, and unsupported providers preserve the existing plain Input behavior and submit payload byte-for-byte.

The catalog is curated TypeScript records sourced from models.dev, scoped initially to 6 OpenAI and 5 Anthropic models in declared display order. The pure applyKnownModelDefaults helper only writes a field when its current value still equals the form's initial value (or was last applied by Known Model defaulting in this form session, tracked cumulatively across selections). It never sets compressionThreshold or any reasoning/thinking fields, ignores tiered pricing, and never writes to the model field (canonicalization is the caller's responsibility).

This PR also makes two narrow, additive changes outside the panel directory:

  • site/src/components/Autocomplete/Autocomplete.tsx gains optional triggerAriaInvalid, triggerAriaDescribedBy, and onEscapeKeyDown props so the new catalog branch can preserve aria-invalid / aria-describedby parity with the plain input and observe Escape close intent reliably across the Radix portal. Existing Autocomplete consumers are unaffected; stopPropagation is gated on onEscapeKeyDown being provided.
  • site/src/pages/AgentsPage/components/ChatModelAdminPanel/modelConfigFormLogic.ts exports deepGet / deepSet so the defaulting helper can reuse them instead of re-implementing the same path traversal.

No backend, API, SDK, or DB changes. No edits to ModelsSection.tsx, ModelConfigFields.tsx, pricingFields.ts, or providerPolicyDefaults.ts.

Validation

  • 37 colocated unit tests across knownModels/ (catalog, search, exact-canonical lookup, exact-alias lookup, badge, defaulting helper).
  • 134 unit tests across the full ChatModelAdminPanel directory pass.
  • 50 Storybook play tests on ChatModelAdminPanel.stories.tsx pass, including 17 DEREM-traceable interaction tests covering each plan-listed and review-driven scenario (open-no-error, Escape cancellation, sequential selection, double-apply guard, blur-canonical, alias cancellation, provider-change reset, ARIA parity, no-options copy, off-catalog substring commit, stale-cost-field, off-catalog interleaving, chain tracking, keyboard selection, clearable-disabled, off-catalog punctuation variant).
  • tsc -p . passes.

Dogfooding

Storybook was run locally and the user-facing flows were exercised end-to-end via agent-browser, capturing screenshots for:

  1. OpenAI happy path (selection → defaults applied note → populated fields).
  2. Anthropic happy path (selection → populated fields, reasoning/thinking blank).
  3. Unsupported provider fallback (Google plain input, no popover).
  4. OpenAI suggestion popover at empty focus (declared catalog order, context badges).
  5. OpenAI search filter (typing 5.4 filters to GPT-5.4 / 5.4 mini / 5.4 nano).
  6. Edit mode plain input (autocomplete correctly gated to add mode only).
  7. DEREM-3: empty popover open on Add Model — no premature Model ID is required. error.
  8. DEREM-1: autocomplete trigger aria-invalid="true" and aria-describedby matching the rendered error element.
  9. DEREM-6: exact No matching known models. You can still use this identifier. copy.

📋 Implementation Plan

Plan: Known Model autocomplete and frontend-only defaults for Chat Model Admin

Goal

Improve the admin Add Model onboarding flow by replacing the blank Model Identifier experience with provider-scoped Known Model discovery suggestions for native OpenAI and Anthropic providers. Selecting a Known Model, or typing an exact canonical Known Model identifier and blurring the field, should prefill safe objective model metadata in the existing form without changing backend APIs, database schema, or runtime behavior.

The primary UX goal is discovery for admins who do not know exact provider model identifiers or metadata. Typing convenience is a secondary benefit.

Evidence and current code facts

  • The current Model Identifier field is a plain free-text Input in site/src/pages/AgentsPage/components/ChatModelAdminPanel/ModelForm.tsx. It submits as model and is only validated as a non-empty string.
  • The provider selector is disabled in edit and duplicate modes. In add mode, ModelsSection.tsx keys the form by provider, so provider changes remount ModelForm.
  • The shared site/src/components/Autocomplete/Autocomplete.tsx primitive already supports free-text input with suggestions and is the right UI primitive for this feature.
  • modelConfigFormLogic.ts owns form initialization via buildInitialModelFormValues(...), and modelConfigFormLogic.test.ts already covers this pure logic area.
  • No frontend or backend Known Model catalog exists today.
  • The database has a non-unique (provider, model) index, not a uniqueness constraint. Multiple Model Configs can share the same Provider and Model Identifier, so suggestions must not hide already-configured models.
  • models.dev/api.json has provider-keyed model metadata with canonical IDs, names, limits, pricing, release dates, and last_updated values. The Phase 1 catalog should copy a curated subset into TypeScript records, not fetch at runtime.

Domain language

Use these terms consistently in code, tests, docs, and review discussion:

  • Provider: configured external AI service such as native openai or anthropic.
  • Model Config: persisted admin-defined config row used by Coder chat runtime.
  • Model Identifier: exact provider API string submitted as model, such as gpt-5.5.
  • Known Model: curated frontend catalog entry with advisory metadata for one canonical Model Identifier.
  • Model Catalog: checked-in frontend-only list of Known Models.
  • Off-catalog Model Identifier: user-entered Model Identifier that does not match any Known Model and remains valid.
  • Default application: copying advisory Known Model metadata into a draft add-mode Model Config form.

Resolved design decisions

UX scope

  • Implement this on the Add Model page/form only.
  • Do not add provider success popups, provider-side calls to action, or new deep-link behavior in this pass.
  • Use Autocomplete only when all are true:
    • form mode is add;
    • selected Provider is native openai or native anthropic;
    • that Provider has Known Models.
  • Edit mode, duplicate mode, and unsupported providers keep the existing free-text input behavior.

Suggestion behavior

  • Suggestions open on focus only when the Model Identifier field is empty.
  • Once the field has text, suggestions open while typing or interacting with the autocomplete.
  • Empty unsupported-provider catalogs degrade silently to the existing plain input behavior.
  • When a supported provider has zero matches for a non-empty query, show a non-blocking empty state such as: No matching known models. You can still use this identifier.
  • Suggestion rows show:
    • display name;
    • canonical Model Identifier;
    • context-window badge, for example 1.05M context.
  • Format context badges with a deterministic helper covered by tests, for example 200K context, 400K context, and 1.05M context.
  • Do not show pricing, recommendations, capability tags, or large-context caveats in suggestion rows.
  • Keep catalog display order as product ordering. Do not show a visible Recommended badge.

Canonical IDs and aliases

  • Selecting a Known Model always writes its canonical Model Identifier into the form.
  • Use non-date latest aliases as canonical onboarding IDs when the provider exposes them, such as gpt-5.5 or claude-sonnet-4-6.
  • Date-pinned IDs may be aliases for search, but selecting a Known Model writes the non-date canonical ID.
  • Typing aliases filters suggestions but does not rewrite the field and does not apply defaults by itself.
  • Search over canonical ID, display name, and explicit aliases.
  • Search is case-insensitive and normalizes spaces, hyphens, underscores, and dots before substring matching.
  • Aliases are objective name or identifier variants only. Do not include editorial intent tags such as best, cheap, fast, coding, or reasoning.
  • Do not implement typo-tolerant fuzzy search in Phase 1.

Default application rules

  • Default application only runs in add mode.
  • Explicit Known Model selection applies defaults immediately.
  • Exact typed or pasted canonical Model Identifier applies defaults on blur, not on every keystroke. This avoids prematurely applying gpt-5.5 while the admin is typing gpt-5.5-pro.
  • Defaults fill only target fields whose current values still equal this form session's initial values.
  • Do not use Formik touched state as the source of truth for safety.
  • Do not implement field-level provenance tracking in Phase 1.
  • Capture an immutable initialValuesRef at ModelForm mount/remount and compare against that snapshot for safe default application. Do not compare against a live Formik reference that can drift.
  • Do not reapply repeatedly for the same provider/model pair in a single form session.
  • The defaulting helper must return both the next values and the list of applied form paths:
interface ApplyKnownModelDefaultsResult {
  values: ModelFormValues;
  appliedFields: readonly string[];
}
  • Treat Model Identifier canonicalization separately from metadata default application. appliedFields tracks populated metadata/form paths only, not the model field change caused by selecting a Known Model.
  • Show an inline note near Model Identifier only when appliedFields.length > 0, such as: Defaults applied from GPT-5.5. Review and adjust before saving.
  • Do not show a note for off-catalog identifiers, no-op Known Model selections, or selections that only canonicalize the Model Identifier.

Initial Model Catalog

Use curated TypeScript records with source metadata copied from models.dev. Do not check in the full models.dev/api.json snapshot and do not add a generator in Phase 1. Add a file-level comment that array order controls suggestion order so future cleanup does not accidentally change onboarding UX.

Initial native OpenAI entries, in display order:

  1. gpt-5.5
  2. gpt-5.5-pro
  3. gpt-5.4
  4. gpt-5.4-mini
  5. gpt-5.4-nano
  6. gpt-5.3-codex

Initial native Anthropic entries, in display order:

  1. claude-opus-4-7
  2. claude-opus-4-6
  3. claude-sonnet-4-6
  4. claude-haiku-4-5
  5. claude-sonnet-4-5

Do not include GPT-4.x, pre-5.3 GPT models, or Claude models older than 4.5 in this onboarding catalog unless product intentionally expands scope.

Each Known Model record should include:

  • provider;
  • canonical Model Identifier;
  • display name;
  • aliases;
  • source metadata, including sourceName: "models.dev", sourceRetrievedAt, and the model record's last_updated value;
  • contextLimit from limit.context;
  • maxOutputTokens from limit.output;
  • flat base pricing from supported cost.* fields.

Field mapping

  • models.dev.limit.context maps to contextLimit.
  • models.dev.limit.output maps to the selected provider's exact max-output-tokens field when one exists, otherwise to generic config.maxOutputTokens.
  • Never fill both generic and provider-specific output-token fields for the same Known Model.
  • Ignore models.dev.limit.input unless the current form schema already exposes an exact matching field.
  • Map only flat base pricing fields that the existing form can persist:
    • cost.input;
    • cost.output;
    • cost.cache_read;
    • cost.cache_write.
  • Reuse pricingFields.ts or the existing pricing field descriptors instead of hard-coding cost form paths.
  • If cache_read or cache_write is absent from a models.dev entry, leave the corresponding field at its initial value and do not include it in appliedFields.
  • Ignore tiered pricing such as context_over_200k in Phase 1. Add a code comment in the adapter explaining that Coder currently persists flat pricing only.
  • Do not show a UI caveat for tiered pricing in Phase 1.
  • Do not set compressionThreshold from Known Models.
  • Do not prefill provider-specific reasoning or thinking fields in Phase 1, including:
    • OpenAI reasoningEffort and reasoningSummary;
    • Anthropic sendReasoning, effort, and thinking.budgetTokens.

Proposed file structure

Use knownModels/ rather than modelDefaults/ because the data powers both discovery and default application.

New files:

  • site/src/pages/AgentsPage/components/ChatModelAdminPanel/knownModels/types.ts
  • site/src/pages/AgentsPage/components/ChatModelAdminPanel/knownModels/openai.ts
  • site/src/pages/AgentsPage/components/ChatModelAdminPanel/knownModels/anthropic.ts
  • site/src/pages/AgentsPage/components/ChatModelAdminPanel/knownModels/index.ts
  • site/src/pages/AgentsPage/components/ChatModelAdminPanel/knownModels/applyKnownModelDefaults.ts
  • site/src/pages/AgentsPage/components/ChatModelAdminPanel/ModelIdentifierField.tsx

Existing files to modify:

  • site/src/pages/AgentsPage/components/ChatModelAdminPanel/ModelForm.tsx
  • site/src/pages/AgentsPage/components/ChatModelAdminPanel/modelConfigFormLogic.ts
  • site/src/pages/AgentsPage/components/ChatModelAdminPanel/modelConfigFormLogic.test.ts
  • site/src/pages/AgentsPage/components/ChatModelAdminPanel/ChatModelAdminPanel.stories.tsx

Documentation artifacts to keep in sync if implementing from a clean workspace:

  • site/src/pages/AgentsPage/components/ChatModelAdminPanel/CONTEXT.md
  • site/src/pages/AgentsPage/components/ChatModelAdminPanel/docs/adr/0001-frontend-known-model-catalog.md

Implementation plan

Phase 1: Red, define pure behavior first

  1. Add tests in modelConfigFormLogic.test.ts or a colocated knownModels test file for:
    • provider-scoped lookup;
    • normalized alias search;
    • canonicalization on selection;
    • unknown model leaves values unchanged;
    • exact canonical ID lookup;
    • safe initial-value patching;
    • appliedFields output that excludes Model Identifier canonicalization;
    • tiered pricing ignored;
    • missing cache pricing fields left at initial values;
    • compression threshold not populated;
    • reasoning/thinking fields not populated;
    • output-token mapping prefers provider-specific exact field and never fills both;
    • context badge formatting.
  2. Add lifecycle tests where feasible:
    • provider change in add mode remounts the form and resets initialValuesRef, lastAppliedProviderModelRef, and inline default-feedback state.
  3. Add edge-case tests for event and reapplication semantics:
    • selecting gpt-5.5 then blurring does not apply defaults a second time;
    • typing gpt-5.5-pro then blurring applies only pro defaults, never prefix gpt-5.5 defaults;
    • selecting one Known Model, then another, does not overwrite fields already populated by the first selection because they no longer match initial values;
    • typing an alias then blurring does not canonicalize or apply defaults;
    • an Off-catalog value for a supported provider remains valid and preserves existing required-field validation behavior.
  4. Add tests for the initial OpenAI and Anthropic catalog entries to ensure IDs, source metadata, and display order remain intentional.

Quality gate: targeted unit tests fail for missing implementation.

Phase 2: Green, add Known Model catalog and pure helpers

  1. Add knownModels/types.ts with readonly types for catalog records and source metadata.
  2. Add knownModels/openai.ts and knownModels/anthropic.ts with the initial catalog entries and file-level refresh comments.
  3. Add lookup and search helpers in knownModels/index.ts.
  4. Add applyKnownModelDefaults(...) as a pure helper that accepts:
    • current form values;
    • initial form values;
    • selected provider;
    • Known Model;
    • provider field mapping helpers if needed.
  5. Ensure assertions or explicit guards make impossible cases fail fast during tests, for example missing provider, missing canonical ID, or invalid source metadata.

Quality gate: targeted unit tests pass.

Phase 3: Wire Model Identifier autocomplete UX

Autocomplete integration constraints:

  • Control the shared Autocomplete with inputValue for the free-text Model Identifier string and value: KnownModel | null for selected suggestions.
  • Pass pre-filtered Known Model options to Autocomplete; do not rely on cmdk internal filtering once inputValue is controlled.
  • Clear the selected KnownModel | null value whenever the admin types arbitrary text that no longer corresponds to the selected Known Model.
  • Guard selection and blur event ordering so selecting a row does not cause the input blur handler to apply defaults a second time.
  • Run exact-match blur behavior only when focus leaves the whole field/combobox, not when focus moves into the suggestion list.
  • Store the last-applied provider/model pair in form-local state or a ref so add-mode provider remounts reset it naturally.
  • Preserve the existing field contract: label, tooltip/help text, name, validation error rendering, aria-invalid, aria-describedby, disabled state, Formik blur/touched behavior, and submitted request shape.
  1. Add ModelIdentifierField.tsx.
  2. Preserve existing plain Input markup for edit mode, duplicate mode, and unsupported providers.
  3. For add-mode supported providers, render Autocomplete with:
    • controlled free-text value tied to Formik's model field;
    • custom row rendering with display name, canonical ID, and context badge;
    • open-on-empty-focus behavior;
    • non-blocking no-match copy for non-empty supported-provider queries;
    • keyboard support inherited from Autocomplete.
  4. On Known Model selection:
    • set the form's model field to the canonical ID;
    • apply defaults immediately;
    • show inline feedback only if fields changed.
  5. On blur:
    • if the final field value exactly equals a Known Model canonical ID, apply defaults safely;
    • do not auto-apply aliases on blur.
  6. Track the last applied provider/model pair in the form session to avoid repeated reapplication.

Quality gate: Storybook stories compile and the main interaction paths work locally.

Phase 4: Storybook and UX coverage

Add or extend ChatModelAdminPanel.stories.tsx with three user-visible flows:

  1. OpenAI happy path:
    • open Add Model for OpenAI;
    • focus empty Model Identifier;
    • suggestions appear;
    • select GPT-5.5;
    • assert gpt-5.5 is in the input;
    • assert inline defaults note appears;
    • assert visible context limit and max output fields populate;
    • expand the pricing section before asserting pricing fields, or keep detailed pricing assertions in unit tests if the Storybook UI would become brittle.
  2. Anthropic happy path:
    • open Add Model for Anthropic;
    • select Claude Opus 4.7;
    • assert canonical ID, visible context limit, and output field populate;
    • expand the pricing section before asserting pricing fields, or keep detailed pricing assertions in unit tests if the Storybook UI would become brittle;
    • assert Anthropic reasoning/thinking fields remain blank.
  3. Unsupported provider fallback:
    • open Add Model for Azure or openai-compat;
    • assert Model Identifier behaves as plain free text and no suggestion popover appears.

If practical, include one keyboard selection path in Storybook or manual dogfooding:

  • tab/focus Model Identifier;
  • arrow to a suggestion;
  • press Enter;
  • verify canonicalization and defaults.

Quality gate: Storybook interaction tests pass for touched stories.

Phase 5: Refactor and documentation pass

  1. Keep catalog data isolated from UI rendering code.
  2. Keep provider field mapping in one helper so future Google, Bedrock, OpenRouter, or Azure support does not require editing defaulting logic everywhere.
  3. Ensure comments explain why tiered pricing and reasoning defaults are excluded.
  4. Update CONTEXT.md and ADR if implementation changes any design decision captured there.
  5. Run formatting and linting for touched frontend files.

Quality gate: no broad refactors beyond this feature's files.

Validation commands

Use the repo's existing frontend validation commands, scoped where possible:

  • pnpm -C site test <targeted ChatModelAdminPanel pattern>
  • pnpm -C site test <targeted modelConfigFormLogic pattern>
  • pnpm -C site test:storybook
  • pnpm -C site lint:types
  • pnpm -C site check

If command names differ in this workspace, inspect site/package.json and use the closest existing targeted commands. Do not claim success until the actual commands run and pass.

Dogfooding plan

Primary dogfood path is Storybook because this is a form-level UI improvement using mocked admin data.

  1. Run Storybook for the Chat Model Admin Panel.
  2. Record a short video showing:
    • OpenAI Add Model, focus empty Model Identifier, suggestions appear, select GPT-5.5, defaults note appears, fields populate;
    • Anthropic Add Model, select Claude Opus 4.7, fields populate, reasoning/thinking fields remain blank;
    • unsupported provider Add Model, Model Identifier stays free text with no suggestions.
  3. Capture screenshots for the final state of each flow and attach them for review.
  4. If implementation touches routing, ModelsSection URL state, or provider pages, also run the local UI and record /agents/settings/models?newModel=openai exercising the same OpenAI flow.

Acceptance criteria

  • Add-mode native OpenAI and Anthropic Model Identifier fields provide discovery suggestions from the curated Known Model catalog.
  • Suggestions appear on empty focus and filter as the admin types.
  • Unsupported providers, edit mode, and duplicate mode preserve the current plain input behavior.
  • Selecting a Known Model canonicalizes the field and safely applies objective defaults.
  • Exact typed/pasted canonical IDs apply defaults on blur.
  • Off-catalog Model Identifiers remain valid and non-blocking.
  • Display name, context limit, output-token field, and flat pricing fill only when target fields still match initial values.
  • Compression threshold, tiered pricing, and provider-specific reasoning/thinking fields are not populated by Phase 1 defaults.
  • Inline feedback appears only when default application changed at least one field.
  • Unit tests, Storybook coverage, typecheck, formatting, and lint/check commands pass.
  • Dogfooding includes screenshots and video recordings.

Risks and mitigations

  • Catalog staleness: models change frequently. Mitigate with source metadata and clear file-level refresh comments.
  • Provider namespace mistakes: Azure, Bedrock, OpenRouter, and openai-compat use different identifier semantics. Mitigate by supporting only native OpenAI and Anthropic in Phase 1.
  • Auto-fill surprise: defaults can feel magical. Mitigate with selection-first UX, blur-only exact-match behavior, initial-value safety checks, and inline feedback.
  • Pricing inaccuracy for tiered models: current form persists flat prices only. Mitigate by mapping base flat prices only and documenting tiered pricing as out of scope.
  • Reasoning option overreach: generic source metadata does not map cleanly to provider-specific controls. Mitigate by leaving reasoning/thinking fields blank in Phase 1.
  • Overbroad UI changes: replacing an input can affect accessibility and keyboard users. Mitigate by using the shared Autocomplete primitive, preserving plain Input fallback, and dogfooding keyboard selection.

Generated with mux • Model: anthropic:claude-opus-4-7 • Thinking: max

@ThomasK33

Copy link
Copy Markdown
Member Author

/coder-agents-review

@coder-agents-review coder-agents-review Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The catalog architecture is well-designed: pure helpers separated from UI rendering, satisfies constraints catching structural drift at compile time, and initial-value comparison instead of Formik touched state for safe default application. The test suite for the pure layer is thorough. CONTEXT.md and the ADR are genuinely useful documentation that resolves the "model" term ambiguity and preserves design rationale.

1 P1, 3 P2, 6 P3, 1 P4, 6 Nit.

The critical cluster (DEREM-4, DEREM-5) stems from conflating the popover's search text with the Formik model field. handleInputChange writes every keystroke to form.values.model, and handleOpenChange(false) reads a stale currentModel from the render-time closure. Together these create two data-correctness bugs: re-selecting a different model can revert to the old selection (DEREM-4), and dismissing the popover after browsing replaces the model with search text (DEREM-5). Separating search state from form state fixes both.

A related but independent issue (DEREM-3) is that the Radix portal focus transfer fires the wrapper's onBlur, calling markTouched() and flashing "Model ID is required." before the admin has done anything. 7 of 17 reviewers independently found this.

The three duplication findings (DEREM-7, DEREM-8, DEREM-13) all occur in applyKnownModelDefaults.ts. snakeToCamel, getPath/setPath, and formPathForPricingField each have identical implementations in sibling files. These are one-line imports.

"This asymmetry between the selection path (safe by accident of stale closures) and the typing path (correct only if React re-rendered) is subtle, fragile, and completely unproven by any test." (Bisky)

🤖 This review was automatically generated with Coder Agents.

Comment thread site/src/pages/AgentsPage/components/ChatModelAdminPanel/ModelIdentifierField.tsx Outdated
Comment thread site/src/pages/AgentsPage/components/ChatModelAdminPanel/ModelIdentifierField.tsx Outdated
Comment thread site/src/pages/AgentsPage/components/ChatModelAdminPanel/ModelIdentifierField.tsx Outdated
Comment thread site/src/pages/AgentsPage/components/ChatModelAdminPanel/knownModels/index.ts Outdated
Comment thread site/src/pages/AgentsPage/components/ChatModelAdminPanel/knownModels/types.ts Outdated
Comment thread site/src/pages/AgentsPage/components/ChatModelAdminPanel/knownModels/openai.ts Outdated
Comment thread site/src/pages/AgentsPage/components/ChatModelAdminPanel/ModelIdentifierField.tsx Outdated
…euse Known Model helpers

Rename KnownModel.model to modelIdentifier and reuse shared provider,
path, and deep object helpers for Known Model default application.

Addresses DEREM-2, DEREM-7, DEREM-8, DEREM-13, DEREM-14, DEREM-15,
DEREM-16.

_Generated with mux_ • Model: `openai:gpt-5.5` • Thinking: `high`
DEREM-1 DEREM-3 DEREM-4 DEREM-5 DEREM-6 DEREM-9 DEREM-11 DEREM-17
… Known Model autocomplete edges

Add Storybook play coverage for Known Model autocomplete review edges:

- DEREM-1: assert autocomplete trigger aria error linkage.
- DEREM-3: assert opening the empty popover does not show required errors.
- DEREM-5: assert Escape cancels transient search without drifting the committed model.
- DEREM-6: assert no-options copy for unmatched Known Model searches.
- DEREM-10: assert sequential defaults replacement, double-apply guarding, exact canonical blur, alias cancellation, and provider-change reset behavior.

_Generated with mux • Model: openai:gpt-5.5 • Thinking: high_
@ThomasK33

Copy link
Copy Markdown
Member Author

/coder-agents-review

@ThomasK33 ThomasK33 changed the title feat(site/src/pages/AgentsPage/components/ChatModelAdminPanel): add Known Model autocomplete and frontend defaults feat(site/src): add Known Model autocomplete and frontend defaults Apr 30, 2026

@coder-agents-review coder-agents-review Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All 17 R1 findings addressed. The fix commits are solid: search state separated from form state (DEREM-4/5), portal blur guarded (DEREM-3), React Compiler compatibility restored (DEREM-11), helper duplication eliminated (DEREM-7/8/13), and 9 new Storybook interaction tests covering each plan-listed scenario (DEREM-10). R1 fixes verified by panel.

1 P2, 1 P3 new. R1 findings all closed.

The new P2 (DEREM-19) is a regression introduced by the DEREM-5 fix: the close handler uses searchKnownModels (normalized substring matching) to decide whether typed text is search text or a committable identifier. Any off-catalog string like "gpt-5", "mini", "codex", "opus" that substring-matches a catalog entry is silently discarded with no feedback. 8 of 14 reviewers independently found this. It violates the PR's own acceptance criterion: "Off-catalog Model Identifiers remain valid and non-blocking."

The P3 (DEREM-20) is a shared-component change: the Autocomplete's Escape handler was moved to capture phase with unconditional stopPropagation, changing the event contract for all 5 current consumers. No consumer is affected today, but the change is broader than needed.

Process note: the PR description claims "No edits to modelConfigFormLogic.ts" but the R2 commit exported deepGet/deepSet from that file. The test counts (31/34/3) are also stale after R2 (actual: 32/43/12). Consider updating the description.

"What happens at three thousand admins typing model IDs the catalog hasn't heard of?" (Hisoka)

🤖 This review was automatically generated with Coder Agents.

Comment thread site/src/pages/AgentsPage/components/ChatModelAdminPanel/ModelIdentifierField.tsx Outdated
Comment thread site/src/components/Autocomplete/Autocomplete.tsx
@ThomasK33

Copy link
Copy Markdown
Member Author

/coder-agents-review

@coder-agents-review coder-agents-review Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All 19 R1/R2 findings addressed and verified by panel. 6 of 13 reviewers reported clean. The fix quality is high: DEREM-19 (off-catalog discard) was replaced with exact-alias-only cancellation via findKnownModelByAlias, and DEREM-20 (Escape stopPropagation) was gated on onEscapeKeyDown opt-in.

0 P2, 1 P3, 1 Nit new.

The remaining P3 (DEREM-24) is a state-tracking edge case in previouslyAppliedRef: the ref is overwritten wholesale on each selection, so a three-model sequence can leave stale pricing from an earlier model. Three reviewers found this independently via different paths (stale cost field, off-catalog interleaving, chain tracking loss). The consequence is visible to the admin (pricing fields are in the form, and the inline feedback says "Review and adjust before saving"), so the risk is bounded.

The Nit (DEREM-25) is a test coverage gap: no Storybook play function exercises keyboard selection (arrow-down + Enter) despite the plan calling for one.

Neither finding blocks merge. This needs a human decision on whether to address them in this PR or defer.

🤖 This review was automatically generated with Coder Agents.

@ThomasK33

Copy link
Copy Markdown
Member Author

/coder-agents-review

@coder-agents-review coder-agents-review Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All 21 R1-R3 findings addressed and verified. The DEREM-24 fix (cumulative field tracking with stale-field cleanup) and DEREM-25 fix (keyboard selection test) are solid. 2 of 7 R4 reviewers reported completely clean.

1 P3 new.

The remaining P3 (DEREM-26) is a one-line fix: the Autocomplete renders with clearable={true} (default), so re-clicking the selected model toggles it off, clearing the required field and leaving orphaned defaults. Pass clearable={false}. Three reviewers found this independently.

This does not block merge. The validation catches the empty model field, and the admin must actively type a replacement before submitting. The orphaned-defaults risk is the same class as DEREM-24 (stale metadata visible in the form), which the cumulative tracking now handles for the Known Model selection path.

🤖 This review was automatically generated with Coder Agents.

…e Autocomplete clearable for required Model Identifier (DEREM-26)
@ThomasK33

Copy link
Copy Markdown
Member Author

/coder-agents-review

@coder-agents-review coder-agents-review Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All 22 R1-R4 findings addressed and verified. DEREM-26 fix (clearable={false}) confirmed by 4/7 clean reviewers (Hisoka, Mafuuu, Nami, Pariston).

1 P3 new. 4/7 clean.

The new P3 (DEREM-27) is a narrow edge case in the alias detection on the blur-commit path: findKnownModelByAlias uses normalized comparison (strips hyphens, dots, underscores), so an off-catalog value like claude.haiku.4.5.20251001 (dots instead of hyphens) silently matches the alias and gets discarded. The fix is to check canonical ID first and use exact string comparison for alias detection on the commit path. This does not block merge; the affected input set is small (values that differ from known aliases only in punctuation).

Across 5 rounds: 28 findings tracked. 22 addressed by author, 5 dropped, 1 open (DEREM-27, P3). The PR is ready for human review.

🤖 This review was automatically generated with Coder Agents.

Comment thread site/src/pages/AgentsPage/components/ChatModelAdminPanel/ModelIdentifierField.tsx Outdated
…cal-first commit-path alias detection (DEREM-27)
@ThomasK33

Copy link
Copy Markdown
Member Author

/coder-agents-review

@coder-agents-review coder-agents-review Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All 23 R1-R5 findings addressed and verified. DEREM-27 fix (canonical-first ordering with findKnownModelByExactAlias) confirmed correct. 3 of 5 R6 reviewers reported fully clean.

0 P3+, 1 Nit new. No open findings above Nit.

The Nit (DEREM-29) is a dead-code cleanup: findKnownModelByAlias (normalized matching) is still exported and tested but has no production consumer after DEREM-27 replaced it with findKnownModelByExactAlias. Remove the export and its tests, or keep it as a tested utility for future use.

Process note (recurring): the PR description test counts are stale. Actual at HEAD: 42 knownModels unit tests, 139 ChatModelAdminPanel unit tests, 50 Storybook stories with play functions.

Across 6 rounds: 29 findings tracked. 23 addressed by author, 5 dropped, 1 Nit open. The PR is ready for merge.

🤖 This review was automatically generated with Coder Agents.

Comment thread site/src/pages/AgentsPage/components/ChatModelAdminPanel/knownModels/index.ts Outdated
…emove unused findKnownModelByAlias (DEREM-29)
@ThomasK33

Copy link
Copy Markdown
Member Author

/coder-agents-review

@coder-agents-review coder-agents-review Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All 24 R1-R6 findings addressed. DEREM-29 cleanup confirmed. 4 of 5 R7 reviewers reported clean.

1 P3 new. No open findings above P3.

The P3 (DEREM-30) is a cosmetic feedback issue: when the admin opens the popover, types any character, presses Escape, then tabs away, the "Defaults applied from X" message reappears despite no value change. The cause is handleInputChange clearing lastAppliedProviderModelRef, which survives the Escape cancellation. The fix is to snapshot and restore the guard on Escape. No data corruption; purely a misleading feedback flash.

This does not block merge. The PR has been thoroughly reviewed across 7 rounds with 30 findings tracked, 24 posted and addressed. This needs a human decision on whether to address DEREM-30 in this PR or defer.

🤖 This review was automatically generated with Coder Agents.

Comment thread site/src/pages/AgentsPage/components/ChatModelAdminPanel/ModelIdentifierField.tsx Outdated
…ve last-applied tracking across Escape (DEREM-30)
@ThomasK33

Copy link
Copy Markdown
Member Author

/coder-agents-review

… Anthropic happy-path reasoningEffort is medium

DEREM-33: AnthropicKnownModelHappyPath asserted all Reasoning Effort radios unchecked, but the Anthropic catalog now sets effort=medium (commit ae75425). Switch to expectReasoningEffort(body, "medium") matching the OpenAI happy-path pattern. Fixes the UI Tests CI failure.

---
_Generated with [`mux`](https://github.com/coder/mux) • Model: `anthropic:claude-opus-4-7` • Thinking: `max`_
@ThomasK33

Copy link
Copy Markdown
Member Author

/coder-agents-review

@coder-agents-review coder-agents-review Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All 28 posted findings from R1-R12 addressed and verified. DEREM-33 fix confirmed (53/53 storybook tests pass per author). 1/3 panel clean, remaining 2 reported only previously-noted Nits.

No new findings. 0 open.

Across 13 rounds: 33 findings tracked (1 P1, 4 P2, 11 P3, 1 P4, 10 Nit, 1 Note). 28 posted and addressed by author. 5 dropped by orchestrator. 0 open. Clean board. The review is complete.

🤖 This review was automatically generated with Coder Agents.

@ThomasK33

Copy link
Copy Markdown
Member Author

/coder-agents-review

@coder-agents-review coder-agents-review Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The inline autocomplete search rewrite (9f641e6c3) introduces 2 P2 and 6 P3 findings. The shared Autocomplete component gained ~220 lines of new inlineSearch behavior with no dedicated Storybook story, and the keyboard event handling has gaps.

2 P2, 6 P3 new.

DEREM-34 (P2): Enter with no highlighted option does not preventDefault(), so the surrounding form submits. Confirmed by Netero and Meruem.

DEREM-37 (P2): onMouseDown on the inline input unconditionally calls handleOpenChange(true) even when the popover is already open, triggering the parent's open handler and silently resetting in-progress search text.

DEREM-36/38/39/40/41 (P3): keyboard selection stories still use click (DEREM-25 regression), aria-selected tracks cursor not committed value, Escape in inline mode doesn't stopPropagation, ArrowDown reads stale closure on rapid key-repeat, close path re-applies defaults without the double-apply guard.

🤖 This review was automatically generated with Coder Agents.

Comment thread site/src/components/Autocomplete/Autocomplete.tsx
Comment thread site/src/components/Autocomplete/Autocomplete.tsx
Comment thread site/src/components/Autocomplete/Autocomplete.tsx
Comment thread site/src/components/Autocomplete/Autocomplete.tsx Outdated
Comment thread site/src/components/Autocomplete/Autocomplete.tsx
Comment thread site/src/components/Autocomplete/Autocomplete.tsx Outdated
Comment thread site/src/pages/AgentsPage/components/ChatModelAdminPanel/ModelIdentifierField.tsx Outdated
ThomasK33 added 2 commits May 1, 2026 16:02
…dress R13 review

Inline search popovers now render only while loading or when suggestions exist, so the Chat Model Admin Panel no longer shows the "No matching known models" copy for off-catalog identifiers.

- DEREM-34: prevent form submit for every inline Enter key path and keep off-catalog Enter commits covered.
- DEREM-35: add an Autocomplete inlineSearch story that covers typing, keyboard selection, empty Enter, focus retention, Escape, and aria-expanded.
- DEREM-36: exercise ArrowDown plus Enter selection in Storybook instead of clicking an option.
- DEREM-37: avoid reopening an already-open inline input from focus or mousedown.
- DEREM-38: keep aria-selected tied to the committed value rather than the highlighted cursor.
- DEREM-39: stop Escape propagation in inline mode only when a caller handles Escape.
- DEREM-40: use the highlighted ref for rapid ArrowDown and ArrowUp repeats.
- DEREM-41: skip duplicate close-path default application for the same known model.
…inking budget for Haiku 4.5 and Sonnet 4.5

Anthropic adaptive thinking is only supported on Opus 4.7, Opus 4.6, and Sonnet 4.6 in this catalog. Haiku 4.5 and Sonnet 4.5 now use thinkingBudgetTokens=8192 instead of reasoningEffort so Coder defaults them onto the legacy budget_tokens path.

Also remove the "(latest)" suffix from the Haiku 4.5 and Sonnet 4.5 display names.
@ThomasK33

Copy link
Copy Markdown
Member Author

/coder-agents-review

1 similar comment
@ThomasK33

Copy link
Copy Markdown
Member Author

/coder-agents-review

@coder-agents-review coder-agents-review Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All 36 R1-R14 findings addressed and verified. The R14 fix commit (e86b6bfb6) is comprehensive: Enter/preventDefault, onMouseDown guard, inlineSearch story, keyboard selection via Enter, aria-selected, Escape stopPropagation, ArrowDown ref, and close-path guard all addressed in one commit.

3 P3 new. No P2+.

DEREM-42: The DEREM-38 fix (aria-selected={isSelected(option)}) is a dead store. cmdk's CommandItem spreads user props first, then unconditionally sets aria-selected to the keyboard cursor position, overwriting the fix. This is a cmdk limitation, not fixable without patching or replacing cmdk.

DEREM-43: The DEREM-6 no-match reassurance message is absent in inlineSearch mode because the popover auto-hides when no results match. The admin can still submit any value (the inline input stays focused and editable), but the explicit reassurance is lost.

DEREM-44: ArrowDown/ArrowUp in handleInlineKeyDown unconditionally calls handleOpenChange(true), the same pattern DEREM-37 fixed for onMouseDown/focus. Guard with if (!isOpen).

None block merge. The core functionality is solid across 15 rounds of review.

🤖 This review was automatically generated with Coder Agents.

Comment thread site/src/components/Autocomplete/Autocomplete.tsx
Comment thread site/src/components/Autocomplete/Autocomplete.tsx
Comment thread site/src/components/Autocomplete/Autocomplete.tsx Outdated
…p dead aria-selected

DEREM-44: ArrowDown/ArrowUp inside the inline-search keyboard handler now mirror the DEREM-37 onMouseDown/onFocus pattern, only calling handleOpenChange(true) when the popover is not already open. Prevents resetting parent search snapshot state via a stray arrow keypress mid-search.

DEREM-42: Drop the explicit aria-selected prop on CommandItem in inline-search mode. cmdk's CommandItem spreads consumer props before its own aria-selected (which it ties to the keyboard cursor for the listbox pattern), so the override was a dead store. The CheckIcon rendered by renderOptionContent for isSelected options remains the visible indicator of the committed value.

---
_Generated with [`mux`](https://github.com/coder/mux) • Model: `anthropic:claude-opus-4-7` • Thinking: `max`_
@ThomasK33

Copy link
Copy Markdown
Member Author

/coder-agents-review

@coder-agents-review coder-agents-review Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DEREM-42 and DEREM-44 addressed. DEREM-43 contested by author (product decision: inline search auto-hides, no empty state); panel closed it 2/3 (Mafuuu and Pariston accept, Bisky did not vote on the contested finding directly).

1 P3 new. 0 open above P3.

DEREM-45 is a test gap: the blur-after-auto-hide path for off-catalog identifiers (tab/click away when popover is auto-hidden) has no Storybook story. Only Enter-based commit is tested. The code handles this path explicitly (comment at line 346-348), but a mutation flipping the guard would silently drop off-catalog identifiers for tab/click-away users.

This does not block merge. Across 16 rounds: 45 findings tracked, 38 addressed, 5 dropped, 1 contested and closed by panel, 1 P3 open (DEREM-45).

🤖 This review was automatically generated with Coder Agents.

… blur-after-auto-hide off-catalog commit

DEREM-45: add Storybook story exercising the handleBlur branch that commits an off-catalog identifier when the popover is auto-hidden (zero matches) and focus leaves the field via Tab. Complements the existing Enter-based off-catalog commit coverage from DEREM-42.

---
_Generated with [`mux`](https://github.com/coder/mux) • Model: `anthropic:claude-opus-4-7` • Thinking: `max`_
@ThomasK33

Copy link
Copy Markdown
Member Author

/coder-agents-review

@coder-agents-review coder-agents-review Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DEREM-45 addressed. All 39 prior posted findings closed. Netero clean (1 pre-existing Nit). Mafuuu clean.

1 P2 new.

DEREM-46: the DEREM-45 story (KnownModelBlurAfterAutoHideCommitsOffCatalog) has vacuous assertions. Removing the if (openRef.current) { handleOpenChange(false); } branch from handleBlur still passes both assertions because the inline input shows the search text in both the fixed and buggy paths. The DOM value is identical; only form.values.model differs. Adding expect(queryByText("Model ID is required.")).not.toBeInTheDocument() after the tab-away would distinguish the paths: the mount-time error surfaces via markTouched() in the buggy path but is cleared by setFieldValue in the correct path.

Across 17 rounds: 46 findings tracked. 39 addressed, 5 dropped, 1 contested and panel-closed, 1 P2 open (DEREM-46).

🤖 This review was automatically generated with Coder Agents.

…t no required-field error after blur-commit (DEREM-46)

DEREM-46: the original DEREM-45 assertions were vacuous because the controlled inline-search inputValue keeps rendering the typed search text in both the buggy and fixed paths; only form.values.model differs, which the DOM-value assertion couldn't observe.

Add an explicit "Model ID is required." not-in-document assertion plus an inline comment explaining the buggy-path mechanic. In the buggy variant, markTouched() fires while form.values.model is still empty and the validation message surfaces; in the fixed path, handleOpenChange(false) commits the typed text via setFieldValue first.

---
_Generated with [`mux`](https://github.com/coder/mux) • Model: `anthropic:claude-opus-4-7` • Thinking: `max`_
@ThomasK33

Copy link
Copy Markdown
Member Author

/coder-agents-review

@coder-agents-review coder-agents-review Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All 40 posted findings from R1-R17 addressed or closed. DEREM-46 fix verified (negative assertion for mount-time validation error added). Netero flagged 1 P3 (potentially vacuous Anthropic off-catalog story) not corroborated by panel. Pariston: 1 Nit (comment ordering).

No new findings posted. 0 open.

Across 18 rounds: 46 findings tracked. 40 posted and addressed/closed by author, 5 dropped, 1 contested and panel-closed. 0 open. Clean board. The review is complete.

🤖 This review was automatically generated with Coder Agents.

Comment thread site/src/pages/AgentsPage/components/ChatModelAdminPanel/knownModels/anthropic.ts Outdated
Comment thread site/src/pages/AgentsPage/components/ChatModelAdminPanel/knownModels/anthropic.ts Outdated
Comment thread site/src/pages/AgentsPage/components/ChatModelAdminPanel/knownModels/openai.ts Outdated
ThomasK33 added 2 commits May 4, 2026 09:29
…t flagship models to high reasoning effort

Bump reasoningEffort to high for the flagship Pro/Opus tier per @ibetitsmike review feedback: claude-opus-4-7, claude-opus-4-6, and gpt-5.5-pro. Mid-tier and smaller models (claude-sonnet-4-6, gpt-5.5, gpt-5.4-mini, gpt-5.3-codex) remain at medium.

---
_Generated with [`mux`](https://github.com/coder/mux) • Model: `anthropic:claude-opus-4-7` • Thinking: `max`_
…ss required error on click-off without committing

QA flagged that opening the Model Identifier popover and clicking off without typing or selecting still surfaces "Model ID is required." The close path in handleOpenChange unconditionally marked the field touched, and the bottom path of handleBlur did the same. Validation then fired against the still-empty value, so an admin who merely focused the field and changed their mind saw the same error as a failed submit.

Gate markTouched on whether the interaction committed (or attempted to commit) a value: selection commits via handleSelect, exact-canonical and off-catalog typing commit via the close path, and Escape or open-and-close-without-typing leave validation untouched. handleBlur's bottom path now skips markTouched when currentModel is empty, so a click-off after a focus-only interaction stays quiet. Formik continues to flip touched on submit, so the required error still appears at the appropriate time.

Add DEREM-47 Storybook story covering the QA flow (click input, click off empty, no error). Rewrite DEREM-1 to surface the required-field validation through a real user path (type then clear then click off, which legitimately commits an empty value) so the existing aria-invalid/aria-describedby parity assertions remain meaningful under the new gating.

---
_Generated with [`mux`](https://github.com/coder/mux) • Model: `anthropic:claude-opus-4-7` • Thinking: `max`_
@ThomasK33
ThomasK33 marked this pull request as ready for review May 4, 2026 14:39
@ThomasK33
ThomasK33 merged commit 69610cc into main May 4, 2026
32 checks passed
@ThomasK33
ThomasK33 deleted the onboarding-zaj7 branch May 4, 2026 14:40
@github-actions github-actions Bot locked and limited conversation to collaborators May 4, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants