Skip to content

feat: dynamic model options from cli-registry.yaml#64

Merged
khaliqgant merged 7 commits intomainfrom
feat/dynamic-model-options
Mar 11, 2026
Merged

feat: dynamic model options from cli-registry.yaml#64
khaliqgant merged 7 commits intomainfrom
feat/dynamic-model-options

Conversation

@khaliqgant
Copy link
Copy Markdown
Member

@khaliqgant khaliqgant commented Mar 11, 2026

Summary

  • Remove hardcoded RegistryModelOptions from SpawnModal — model options now fetched from /api/models server endpoint
  • Server endpoint sources models from @agent-relay/config (codegen from cli-registry.yaml)
  • Add OpenCode and Droid as fully supported CLIs with model selection
  • Generalize per-CLI model state/UI into a single generic pattern (removes 4 duplicate model selectors)
  • Update default models: Cursor → opus-4.6-thinking, Gemini → gemini-3.1-pro-preview

Test plan

  • Verify SpawnModal shows model dropdown for all CLIs (Claude, Codex, Gemini, Cursor, OpenCode, Droid)
  • Verify Settings page shows default model selectors for all CLIs
  • Verify /api/models endpoint returns correct data
  • Verify model options load dynamically (no hardcoded fallback)
  • Verify spawning with each CLI appends --model flag correctly

🤖 Generated with Claude Code


Open with Devin

khaliqgant and others added 2 commits March 11, 2026 11:32
…oint

Remove hardcoded RegistryModelOptions from SpawnModal. Model options are now
fetched from the server which sources them from @agent-relay/config (codegen
from cli-registry.yaml). Adds support for OpenCode and Droid model selection.
Generalizes per-CLI model state and UI into a single generic approach.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
devin-ai-integration[bot]

This comment was marked as resolved.

Use a ref to track when the modal freshly opens (isOpen transitions
from false to true). Only reset all form fields on this transition,
not when modelOptions finish loading while the modal is already open.

This fixes a regression where async /api/models loading would cause
the useEffect to re-fire and discard any user input the user had
already entered.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
devin-ai-integration[bot]

This comment was marked as resolved.

Agent Relay and others added 4 commits March 11, 2026 11:02
Add missing registerModelsRoutes call in proxy-server.ts to ensure the
/api/models endpoint is available in proxy/standalone mode. Without this,
the model selection dropdown was hidden when running in proxy mode because
the fetch to /api/models returned 404.

Addresses Devin review comment on PR #64.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove unused defaultModels from useModelOptions hook
- Add runtime validation (isModelOption type guard) for API response
- Expose error state and refetch callback from useModelOptions
- Remove dead DEFAULT_MODEL_OPTIONS empty object and simplify fallback chains
- Move MODEL_CLIS to module scope to avoid re-creation on every render
- Fix codex default model to match cli-registry.yaml (gpt-5.4, not gpt-5.2-codex)
- Use AbortController instead of manual cancelled flag

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Default models now come from @agent-relay/config via /api/models instead of
being hardcoded. The fallback chain is: user setting > registry default > first
model option in list. types.ts defaults are kept as last-resort offline fallback.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@khaliqgant khaliqgant merged commit a8bfa81 into main Mar 11, 2026
1 check passed
@khaliqgant khaliqgant deleted the feat/dynamic-model-options branch March 11, 2026 15:00
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

View 14 additional findings in Devin Review.

Open in Devin Review

Comment on lines +51 to +53
if (data.defaultModels && typeof data.defaultModels === 'object') {
setDefaultModels(data.defaultModels as DefaultModelsMap);
}
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.

🟡 defaultModels keys not normalized to lowercase, causing lookup failures

In useModelOptions.ts, the modelOptions keys are explicitly normalized to lowercase (line 45: normalized[key.toLowerCase()]), because the codegen source (@agent-relay/config) uses PascalCase keys (e.g., Claude, Codex). However, defaultModels from the same codegen source is stored without any normalization (lines 51-53). All downstream consumers look up registryDefaultModels by lowercase CLI id (e.g., registryDefaultModels?.['claude'] at packages/dashboard/src/components/SpawnModal.tsx:177 and packages/dashboard/src/components/settings/SettingsPage.tsx:413), so the lookup returns undefined if the keys are PascalCase. This means the registry default model fallback silently fails, and the system falls through to either getModelsForCli(cli)[0]?.value or ''.

Suggested change
if (data.defaultModels && typeof data.defaultModels === 'object') {
setDefaultModels(data.defaultModels as DefaultModelsMap);
}
if (data.defaultModels && typeof data.defaultModels === 'object') {
const normalizedDefaults: DefaultModelsMap = {};
for (const [key, value] of Object.entries(data.defaultModels)) {
if (typeof value === 'string') {
normalizedDefaults[key.toLowerCase()] = value;
}
}
setDefaultModels(normalizedDefaults);
}
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

khaliqgant added a commit that referenced this pull request Mar 11, 2026
The modelOptions keys are normalized to lowercase (PascalCase from
codegen → lowercase), but defaultModels keys weren't. Downstream
lookups like registryDefaultModels['claude'] returned undefined when
the key was 'Claude', causing the fallback to silently fail.

Addresses: #64 (comment)
@khaliqgant
Copy link
Copy Markdown
Member Author

The last Devin comment (defaultModels keys not normalized) is addressed in #65.

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