diff --git a/apps/x/apps/main/src/ipc.ts b/apps/x/apps/main/src/ipc.ts
index 96d1424e..94f96663 100644
--- a/apps/x/apps/main/src/ipc.ts
+++ b/apps/x/apps/main/src/ipc.ts
@@ -26,7 +26,7 @@ import { RunEvent } from '@x/shared/dist/runs.js';
import { ServiceEvent } from '@x/shared/dist/service-events.js';
import container from '@x/core/dist/di/container.js';
import { listOnboardingModels } from '@x/core/dist/models/models-dev.js';
-import { testModelConnection, generateOneShot } from '@x/core/dist/models/models.js';
+import { testModelConnection, listModelsForProvider, generateOneShot } from '@x/core/dist/models/models.js';
import { getDefaultModelAndProvider } from '@x/core/dist/models/defaults.js';
import { isSignedIn } from '@x/core/dist/account/account.js';
import { listGatewayModels } from '@x/core/dist/models/gateway.js';
@@ -853,6 +853,15 @@ export function setupIpcHandlers() {
'models:test': async (_event, args) => {
return await testModelConnection(args.provider, args.model);
},
+ 'models:listForProvider': async (_event, args) => {
+ try {
+ const models = await listModelsForProvider(args.provider);
+ return { success: true, models };
+ } catch (err) {
+ const message = err instanceof Error ? err.message : 'Failed to list models';
+ return { success: false, error: message };
+ }
+ },
'llm:getDefaultModel': async () => {
return await getDefaultModelAndProvider();
},
diff --git a/apps/x/apps/renderer/src/components/onboarding/steps/llm-setup-step.tsx b/apps/x/apps/renderer/src/components/onboarding/steps/llm-setup-step.tsx
index 44e842c3..3c27ff31 100644
--- a/apps/x/apps/renderer/src/components/onboarding/steps/llm-setup-step.tsx
+++ b/apps/x/apps/renderer/src/components/onboarding/steps/llm-setup-step.tsx
@@ -2,13 +2,6 @@ import { Loader2, CheckCircle2, ArrowLeft, X, Lightbulb } from "lucide-react"
import { motion } from "motion/react"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
-import {
- Select,
- SelectContent,
- SelectItem,
- SelectTrigger,
- SelectValue,
-} from "@/components/ui/select"
import { cn } from "@/lib/utils"
import {
OpenAIIcon,
@@ -40,16 +33,22 @@ const moreProviders: Array<{ id: LlmProviderFlavor; name: string; description: s
export function LlmSetupStep({ state }: LlmSetupStepProps) {
const {
- llmProvider, setLlmProvider, modelsCatalog, modelsLoading, modelsError,
+ llmProvider, setLlmProvider, modelsLoading, modelsError,
activeConfig, testState, setTestState, showApiKey,
- showBaseURL, isLocalProvider, canTest, showMoreProviders, setShowMoreProviders,
+ showBaseURL, canTest, showMoreProviders, setShowMoreProviders,
updateProviderConfig, handleTestAndSaveLlmConfig, handleBack,
upsellDismissed, setUpsellDismissed, handleSwitchToRowboat,
} = state
const isMoreProvider = moreProviders.some(p => p.id === llmProvider)
- const modelsForProvider = modelsCatalog[llmProvider] || []
- const showModelInput = isLocalProvider || modelsForProvider.length === 0
+ // Hosted providers (openai/anthropic/google) get a default model, so we only
+ // ask for a model on providers that truly need one (local/custom/gateway),
+ // or as a fallback if no model is set yet.
+ // Hosted providers (openai/anthropic/google) fetch their models from the API
+ // key on test, so they never need a manual model field. Only local/custom/
+ // gateway providers, where the user must specify a model, show the input.
+ const hostedProviders: LlmProviderFlavor[] = ["openai", "anthropic", "google"]
+ const showModelInput = !hostedProviders.includes(llmProvider)
const renderProviderCard = (provider: typeof primaryProviders[0], index: number) => {
const isSelected = llmProvider === provider.id
@@ -87,7 +86,7 @@ export function LlmSetupStep({ state }: LlmSetupStepProps) {
{/* Title */}
- Choose your model
+ Choose your provider
Select a provider and configure your API key
@@ -145,153 +144,33 @@ export function LlmSetupStep({ state }: LlmSetupStepProps) {
{/* Separator */}
- {/* Model configuration */}
+ {/* Provider configuration */}
-
Model Configuration
-
-
-
+ {/* Cloud providers get a default model auto-selected; only local/custom
+ providers (no catalog) need a model here. Users can pick any of the
+ provider's models later in the chat view. */}
+ {showModelInput && (
+