Add canonical per-provider model lists with picker UI
The catalog only carried one defaultModel string per provider, which forced users to type model ids by hand and made the onboarding / providers panel feel like the original AgentCompany was missing models. - src/shared/provider-models.ts holds the canonical id/name list per provider key (~165 entries across OpenAI, Anthropic, xAI, Groq, GitHub Copilot, Antigravity, GPT account, Azure, Bedrock, Vertex) - ProviderCatalogEntry now exposes a models[] array - Onboarding ProviderStep renders a <select> populated from the active provider's catalog instead of a freeform text input - ProvidersPanel connect dialog uses the same picker for API key, OAuth, and account auth flows - TeamPage hire dialog adds a <datalist> with every catalog model so manual entry is autocompletable
This commit is contained in:
1 parent
dc791ec984
commit
2c3760ca11
6 files changed
+257
-6
No files matched your search
@@ -266,7 +266,19 @@ function ProviderStep({
|
||||
/>
|
||||
</Field>
|
||||
<Field label="Model">
|
||||
<input className="input" value={draft.providerModel} onChange={(event) => onChange({ providerModel: event.target.value })} />
|
||||
{providers.find((entry) => entry.key === draft.providerKey)?.models.length ? (
|
||||
<select
|
||||
className="input"
|
||||
value={draft.providerModel}
|
||||
onChange={(event) => onChange({ providerModel: event.target.value })}
|
||||
>
|
||||
{providers.find((entry) => entry.key === draft.providerKey)?.models.map((model) => (
|
||||
<option key={model.id} value={model.id}>{model.name}</option>
|
||||
))}
|
||||
</select>
|
||||
) : (
|
||||
<input className="input" value={draft.providerModel} onChange={(event) => onChange({ providerModel: event.target.value })} />
|
||||
)}
|
||||
</Field>
|
||||
<Actions onBack={onBack} onNext={onNext} />
|
||||
</div>
|
||||
|
||||
@@ -113,7 +113,21 @@ export function TeamPage({ snapshot, actions }: PageProps) {
|
||||
{providers.map((provider) => <option key={provider.id} value={provider.id}>{provider.label}</option>)}
|
||||
</select>
|
||||
</Field>
|
||||
<Field label="Model"><input className="input" name="modelId" /></Field>
|
||||
<Field label="Model">
|
||||
<input
|
||||
className="input"
|
||||
name="modelId"
|
||||
list="agent-model-suggestions"
|
||||
placeholder="Leave blank to inherit provider default"
|
||||
/>
|
||||
</Field>
|
||||
<datalist id="agent-model-suggestions">
|
||||
{snapshot.providerCatalog.flatMap((entry) =>
|
||||
entry.models.map((model) => (
|
||||
<option key={`${entry.key}-${model.id}`} value={model.id}>{entry.label} · {model.name}</option>
|
||||
)),
|
||||
)}
|
||||
</datalist>
|
||||
<Field label="Monthly budget"><input className="input" name="budget" type="number" min="0" step="1" defaultValue="250" /></Field>
|
||||
<Field label="Seniority">
|
||||
<select className="input" name="roleLevel" defaultValue="staff">
|
||||
|
||||
@@ -125,7 +125,7 @@ function ProviderConnectFields({
|
||||
<>
|
||||
<p style={{ fontSize: 12.5, color: "var(--text-3)", lineHeight: 1.5, margin: 0 }}>{entry.credentialHelp}</p>
|
||||
<Field label="API key"><input className="input" name="secret" type="password" required autoFocus /></Field>
|
||||
<Field label="Default model"><input className="input" name="model" defaultValue={provider.model ?? entry.defaultModel ?? ""} /></Field>
|
||||
<ModelPicker entry={entry} provider={provider} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -140,13 +140,33 @@ function ProviderConnectFields({
|
||||
{entry.signInUrl && <span className="mono" style={{ fontSize: 11, color: "var(--text-3)" }}>{entry.signInUrl}</span>}
|
||||
</div>
|
||||
<Field label="Access token"><input className="input" name="secret" type="password" required /></Field>
|
||||
<Field label="Default model"><input className="input" name="model" defaultValue={provider.model ?? entry.defaultModel ?? ""} /></Field>
|
||||
<ModelPicker entry={entry} provider={provider} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
return <CloudFieldsForm entry={entry} provider={provider} />;
|
||||
}
|
||||
|
||||
function ModelPicker({ entry, provider }: { entry: ProviderCatalogEntry; provider: ProviderRecord }) {
|
||||
const defaultValue = provider.model ?? entry.defaultModel ?? entry.models[0]?.id ?? "";
|
||||
if (entry.models.length === 0) {
|
||||
return (
|
||||
<Field label="Default model">
|
||||
<input className="input" name="model" defaultValue={defaultValue} />
|
||||
</Field>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Field label="Default model">
|
||||
<select className="input" name="model" defaultValue={defaultValue}>
|
||||
{entry.models.map((model) => (
|
||||
<option key={model.id} value={model.id}>{model.name}</option>
|
||||
))}
|
||||
</select>
|
||||
</Field>
|
||||
);
|
||||
}
|
||||
|
||||
function CloudFieldsForm({ entry, provider }: { entry: ProviderCatalogEntry; provider: ProviderRecord }) {
|
||||
const [state, setState] = useState<CloudFields>(CLOUD_DEFAULTS);
|
||||
const update = (patch: Partial<CloudFields>) => setState((current) => ({ ...current, ...patch }));
|
||||
|
||||
@@ -273,6 +273,11 @@ export interface ProviderRecord {
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export interface ProviderCatalogModel {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface ProviderCatalogEntry {
|
||||
key: string;
|
||||
label: string;
|
||||
@@ -281,6 +286,7 @@ export interface ProviderCatalogEntry {
|
||||
capabilityTags: string[];
|
||||
signInUrl: string | null;
|
||||
credentialHelp: string;
|
||||
models: ProviderCatalogModel[];
|
||||
}
|
||||
|
||||
export interface ProviderRequestRecord {
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { PROVIDER_AUTH_TYPE, type ProviderCatalogEntry } from "./domain";
|
||||
import { listProviderModels } from "./provider-models";
|
||||
|
||||
export const PROVIDER_CATALOG: ProviderCatalogEntry[] = [
|
||||
const RAW_CATALOG: Omit<ProviderCatalogEntry, "models">[] = [
|
||||
{
|
||||
key: "openai",
|
||||
label: "OpenAI",
|
||||
authType: PROVIDER_AUTH_TYPE.API_KEY,
|
||||
defaultModel: "gpt-5.1",
|
||||
defaultModel: "gpt-5.4",
|
||||
capabilityTags: ["responses", "tools", "background"],
|
||||
signInUrl: null,
|
||||
credentialHelp: "Generate an API key at platform.openai.com → API keys.",
|
||||
@@ -92,3 +93,8 @@ export const PROVIDER_CATALOG: ProviderCatalogEntry[] = [
|
||||
credentialHelp: "Provide GCP project id, region, and a service-account JSON key with aiplatform.user role.",
|
||||
},
|
||||
];
|
||||
|
||||
export const PROVIDER_CATALOG: ProviderCatalogEntry[] = RAW_CATALOG.map((entry) => ({
|
||||
...entry,
|
||||
models: listProviderModels(entry.key),
|
||||
}));
|
||||
@@ -0,0 +1,193 @@
|
||||
export interface ProviderModelEntry {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export const PROVIDER_MODELS: Record<string, ProviderModelEntry[]> = {
|
||||
openai: [
|
||||
{ id: "gpt-5.4-pro", name: "GPT-5.4 Pro" },
|
||||
{ id: "gpt-5.4", name: "GPT-5.4" },
|
||||
{ id: "gpt-5.4-mini", name: "GPT-5.4 mini" },
|
||||
{ id: "gpt-5.4-nano", name: "GPT-5.4 nano" },
|
||||
{ id: "gpt-5.3-codex", name: "GPT-5.3 Codex" },
|
||||
{ id: "gpt-5.3-codex-spark", name: "GPT-5.3 Codex Spark" },
|
||||
{ id: "gpt-5.3-chat-latest", name: "GPT-5.3 Chat" },
|
||||
{ id: "gpt-5.2-pro", name: "GPT-5.2 Pro" },
|
||||
{ id: "gpt-5.2", name: "GPT-5.2" },
|
||||
{ id: "gpt-5.2-codex", name: "GPT-5.2 Codex" },
|
||||
{ id: "gpt-5.2-chat-latest", name: "GPT-5.2 Chat" },
|
||||
{ id: "gpt-5.1", name: "GPT-5.1" },
|
||||
{ id: "gpt-5.1-codex", name: "GPT-5.1 Codex" },
|
||||
{ id: "gpt-5.1-codex-max", name: "GPT-5.1 Codex Max" },
|
||||
{ id: "gpt-5.1-codex-mini", name: "GPT-5.1 Codex mini" },
|
||||
{ id: "gpt-5.1-chat-latest", name: "GPT-5.1 Chat" },
|
||||
{ id: "gpt-5-pro", name: "GPT-5 Pro" },
|
||||
{ id: "gpt-5", name: "GPT-5" },
|
||||
{ id: "gpt-5-codex", name: "GPT-5 Codex" },
|
||||
{ id: "gpt-5-mini", name: "GPT-5 mini" },
|
||||
{ id: "gpt-5-nano", name: "GPT-5 nano" },
|
||||
{ id: "gpt-5-chat-latest", name: "GPT-5 Chat" },
|
||||
{ id: "o4-mini", name: "o4-mini" },
|
||||
{ id: "o4-mini-deep-research", name: "o4-mini deep research" },
|
||||
{ id: "o3", name: "o3" },
|
||||
{ id: "o3-pro", name: "o3-pro" },
|
||||
{ id: "o3-mini", name: "o3-mini" },
|
||||
{ id: "o3-deep-research", name: "o3 deep research" },
|
||||
{ id: "o1", name: "o1" },
|
||||
{ id: "o1-pro", name: "o1-pro" },
|
||||
{ id: "gpt-4.1", name: "GPT-4.1" },
|
||||
{ id: "gpt-4.1-mini", name: "GPT-4.1 mini" },
|
||||
{ id: "gpt-4.1-nano", name: "GPT-4.1 nano" },
|
||||
{ id: "gpt-4o", name: "GPT-4o" },
|
||||
{ id: "gpt-4o-mini", name: "GPT-4o mini" },
|
||||
{ id: "gpt-4-turbo", name: "GPT-4 Turbo" },
|
||||
{ id: "gpt-4", name: "GPT-4" },
|
||||
],
|
||||
anthropic: [
|
||||
{ id: "claude-opus-4-7", name: "Claude Opus 4.7" },
|
||||
{ id: "claude-opus-4-6", name: "Claude Opus 4.6" },
|
||||
{ id: "claude-opus-4-5", name: "Claude Opus 4.5 (latest)" },
|
||||
{ id: "claude-opus-4-1", name: "Claude Opus 4.1 (latest)" },
|
||||
{ id: "claude-opus-4-0", name: "Claude Opus 4 (latest)" },
|
||||
{ id: "claude-sonnet-4-6", name: "Claude Sonnet 4.6" },
|
||||
{ id: "claude-sonnet-4-5", name: "Claude Sonnet 4.5 (latest)" },
|
||||
{ id: "claude-sonnet-4-0", name: "Claude Sonnet 4 (latest)" },
|
||||
{ id: "claude-haiku-4-5", name: "Claude Haiku 4.5 (latest)" },
|
||||
{ id: "claude-3-7-sonnet-20250219", name: "Claude Sonnet 3.7" },
|
||||
{ id: "claude-3-5-sonnet-20241022", name: "Claude Sonnet 3.5 v2" },
|
||||
{ id: "claude-3-5-sonnet-20240620", name: "Claude Sonnet 3.5" },
|
||||
{ id: "claude-3-5-haiku-latest", name: "Claude Haiku 3.5 (latest)" },
|
||||
{ id: "claude-3-5-haiku-20241022", name: "Claude Haiku 3.5" },
|
||||
{ id: "claude-3-haiku-20240307", name: "Claude Haiku 3" },
|
||||
{ id: "claude-3-sonnet-20240229", name: "Claude Sonnet 3" },
|
||||
{ id: "claude-3-opus-20240229", name: "Claude Opus 3" },
|
||||
],
|
||||
xai: [
|
||||
{ id: "grok-4", name: "Grok 4" },
|
||||
{ id: "grok-4-fast", name: "Grok 4 Fast" },
|
||||
{ id: "grok-4-fast-non-reasoning", name: "Grok 4 Fast (Non-Reasoning)" },
|
||||
{ id: "grok-4-1-fast", name: "Grok 4.1 Fast" },
|
||||
{ id: "grok-4-1-fast-non-reasoning", name: "Grok 4.1 Fast (Non-Reasoning)" },
|
||||
{ id: "grok-4.20-0309-reasoning", name: "Grok 4.20 (Reasoning)" },
|
||||
{ id: "grok-4.20-0309-non-reasoning", name: "Grok 4.20 (Non-Reasoning)" },
|
||||
{ id: "grok-code-fast-1", name: "Grok Code Fast 1" },
|
||||
{ id: "grok-3", name: "Grok 3" },
|
||||
{ id: "grok-3-fast", name: "Grok 3 Fast" },
|
||||
{ id: "grok-3-mini", name: "Grok 3 Mini" },
|
||||
{ id: "grok-3-mini-fast", name: "Grok 3 Mini Fast" },
|
||||
{ id: "grok-2", name: "Grok 2" },
|
||||
{ id: "grok-2-vision", name: "Grok 2 Vision" },
|
||||
{ id: "grok-beta", name: "Grok Beta" },
|
||||
{ id: "grok-vision-beta", name: "Grok Vision Beta" },
|
||||
],
|
||||
groq: [
|
||||
{ id: "groq/compound", name: "Compound" },
|
||||
{ id: "groq/compound-mini", name: "Compound Mini" },
|
||||
{ id: "moonshotai/kimi-k2-instruct-0905", name: "Kimi K2 Instruct 0905" },
|
||||
{ id: "moonshotai/kimi-k2-instruct", name: "Kimi K2 Instruct" },
|
||||
{ id: "openai/gpt-oss-120b", name: "GPT OSS 120B" },
|
||||
{ id: "openai/gpt-oss-20b", name: "GPT OSS 20B" },
|
||||
{ id: "openai/gpt-oss-safeguard-20b", name: "Safety GPT OSS 20B" },
|
||||
{ id: "llama-3.3-70b-versatile", name: "Llama 3.3 70B Versatile" },
|
||||
{ id: "llama-3.1-8b-instant", name: "Llama 3.1 8B Instant" },
|
||||
{ id: "meta-llama/llama-4-maverick-17b-128e-instruct", name: "Llama 4 Maverick 17B" },
|
||||
{ id: "meta-llama/llama-4-scout-17b-16e-instruct", name: "Llama 4 Scout 17B" },
|
||||
{ id: "qwen-qwq-32b", name: "Qwen QwQ 32B" },
|
||||
{ id: "qwen/qwen3-32b", name: "Qwen3 32B" },
|
||||
{ id: "mistral-saba-24b", name: "Mistral Saba 24B" },
|
||||
{ id: "deepseek-r1-distill-llama-70b", name: "DeepSeek R1 Distill Llama 70B" },
|
||||
{ id: "gemma2-9b-it", name: "Gemma 2 9B" },
|
||||
],
|
||||
"github-copilot": [
|
||||
{ id: "claude-opus-4.7", name: "Claude Opus 4.7" },
|
||||
{ id: "claude-opus-4.6", name: "Claude Opus 4.6" },
|
||||
{ id: "claude-opus-4.5", name: "Claude Opus 4.5" },
|
||||
{ id: "claude-sonnet-4.6", name: "Claude Sonnet 4.6" },
|
||||
{ id: "claude-sonnet-4.5", name: "Claude Sonnet 4.5" },
|
||||
{ id: "claude-sonnet-4", name: "Claude Sonnet 4" },
|
||||
{ id: "claude-haiku-4.5", name: "Claude Haiku 4.5" },
|
||||
{ id: "gpt-5.4", name: "GPT-5.4" },
|
||||
{ id: "gpt-5.4-mini", name: "GPT-5.4 mini" },
|
||||
{ id: "gpt-5.3-codex", name: "GPT-5.3 Codex" },
|
||||
{ id: "gpt-5.2", name: "GPT-5.2" },
|
||||
{ id: "gpt-5.2-codex", name: "GPT-5.2 Codex" },
|
||||
{ id: "gpt-5.1", name: "GPT-5.1" },
|
||||
{ id: "gpt-5.1-codex", name: "GPT-5.1 Codex" },
|
||||
{ id: "gpt-5.1-codex-max", name: "GPT-5.1 Codex Max" },
|
||||
{ id: "gpt-5.1-codex-mini", name: "GPT-5.1 Codex Mini" },
|
||||
{ id: "gpt-5", name: "GPT-5" },
|
||||
{ id: "gpt-5-mini", name: "GPT-5 mini" },
|
||||
{ id: "gpt-4.1", name: "GPT-4.1" },
|
||||
{ id: "gpt-4o", name: "GPT-4o" },
|
||||
{ id: "gemini-3.1-pro-preview", name: "Gemini 3.1 Pro Preview" },
|
||||
{ id: "gemini-3-pro-preview", name: "Gemini 3 Pro Preview" },
|
||||
{ id: "gemini-3-flash-preview", name: "Gemini 3 Flash" },
|
||||
{ id: "gemini-2.5-pro", name: "Gemini 2.5 Pro" },
|
||||
{ id: "grok-code-fast-1", name: "Grok Code Fast 1" },
|
||||
],
|
||||
"google-antigravity": [
|
||||
{ id: "claude-sonnet-4-6", name: "Claude Sonnet 4.6 (Antigravity)" },
|
||||
{ id: "claude-sonnet-4-5", name: "Claude Sonnet 4.5 (Antigravity)" },
|
||||
{ id: "claude-sonnet-4-5-thinking", name: "Claude Sonnet 4.5 Thinking" },
|
||||
{ id: "claude-opus-4-6-thinking", name: "Claude Opus 4.6 Thinking" },
|
||||
{ id: "claude-opus-4-5-thinking", name: "Claude Opus 4.5 Thinking" },
|
||||
{ id: "gemini-3.1-pro-high", name: "Gemini 3.1 Pro High" },
|
||||
{ id: "gemini-3.1-pro-low", name: "Gemini 3.1 Pro Low" },
|
||||
{ id: "gemini-3-flash", name: "Gemini 3 Flash" },
|
||||
{ id: "gpt-oss-120b-medium", name: "GPT-OSS 120B Medium" },
|
||||
],
|
||||
"gpt-account": [
|
||||
{ id: "gpt-5.4", name: "GPT-5.4" },
|
||||
{ id: "gpt-5.4-mini", name: "GPT-5.4 Mini" },
|
||||
{ id: "gpt-5.3-codex", name: "GPT-5.3 Codex" },
|
||||
{ id: "gpt-5.3-codex-spark", name: "GPT-5.3 Codex Spark" },
|
||||
{ id: "gpt-5.2", name: "GPT-5.2" },
|
||||
{ id: "gpt-5.2-codex", name: "GPT-5.2 Codex" },
|
||||
{ id: "gpt-5.1", name: "GPT-5.1" },
|
||||
{ id: "gpt-5.1-codex-max", name: "GPT-5.1 Codex Max" },
|
||||
{ id: "gpt-5.1-codex-mini", name: "GPT-5.1 Codex Mini" },
|
||||
],
|
||||
"azure-openai": [
|
||||
{ id: "gpt-5.4", name: "GPT-5.4" },
|
||||
{ id: "gpt-5.4-mini", name: "GPT-5.4 mini" },
|
||||
{ id: "gpt-5.3-codex", name: "GPT-5.3 Codex" },
|
||||
{ id: "gpt-5.2", name: "GPT-5.2" },
|
||||
{ id: "gpt-5.1", name: "GPT-5.1" },
|
||||
{ id: "gpt-5", name: "GPT-5" },
|
||||
{ id: "gpt-5-codex", name: "GPT-5 Codex" },
|
||||
{ id: "gpt-4.1", name: "GPT-4.1" },
|
||||
{ id: "gpt-4o", name: "GPT-4o" },
|
||||
{ id: "o4-mini", name: "o4-mini" },
|
||||
{ id: "o3", name: "o3" },
|
||||
],
|
||||
"aws-bedrock": [
|
||||
{ id: "anthropic.claude-opus-4-7", name: "Claude Opus 4.7" },
|
||||
{ id: "anthropic.claude-opus-4-6-v1", name: "Claude Opus 4.6" },
|
||||
{ id: "anthropic.claude-opus-4-5-20251101-v1:0", name: "Claude Opus 4.5" },
|
||||
{ id: "anthropic.claude-sonnet-4-6", name: "Claude Sonnet 4.6" },
|
||||
{ id: "anthropic.claude-sonnet-4-5-20250929-v1:0", name: "Claude Sonnet 4.5" },
|
||||
{ id: "anthropic.claude-haiku-4-5-20251001-v1:0", name: "Claude Haiku 4.5" },
|
||||
{ id: "amazon.nova-2-lite-v1:0", name: "Nova 2 Lite" },
|
||||
{ id: "amazon.nova-premier-v1:0", name: "Nova Premier" },
|
||||
{ id: "amazon.nova-pro-v1:0", name: "Nova Pro" },
|
||||
{ id: "deepseek.v3.2", name: "DeepSeek V3.2" },
|
||||
{ id: "meta.llama3-3-70b-instruct-v1:0", name: "Llama 3.3 70B" },
|
||||
{ id: "moonshotai.kimi-k2.5", name: "Kimi K2.5" },
|
||||
],
|
||||
"vertex-ai": [
|
||||
{ id: "gemini-3.1-pro-preview", name: "Gemini 3.1 Pro Preview" },
|
||||
{ id: "gemini-3-pro-preview", name: "Gemini 3 Pro Preview" },
|
||||
{ id: "gemini-3-flash-preview", name: "Gemini 3 Flash Preview" },
|
||||
{ id: "gemini-2.5-pro", name: "Gemini 2.5 Pro" },
|
||||
{ id: "gemini-2.5-flash", name: "Gemini 2.5 Flash" },
|
||||
{ id: "gemini-2.5-flash-lite", name: "Gemini 2.5 Flash Lite" },
|
||||
{ id: "gemini-2.0-flash", name: "Gemini 2.0 Flash" },
|
||||
{ id: "gemini-2.0-flash-lite", name: "Gemini 2.0 Flash Lite" },
|
||||
{ id: "gemini-1.5-pro", name: "Gemini 1.5 Pro" },
|
||||
{ id: "gemini-1.5-flash", name: "Gemini 1.5 Flash" },
|
||||
],
|
||||
};
|
||||
|
||||
export function listProviderModels(providerKey: string): ProviderModelEntry[] {
|
||||
return PROVIDER_MODELS[providerKey] ?? [];
|
||||
}
|
||||
Reference in new issue
Block a user