diff --git a/apps/server/src/serverRuntimeStartup.test.ts b/apps/server/src/serverRuntimeStartup.test.ts index 0bfebaa5157..09614c19b18 100644 --- a/apps/server/src/serverRuntimeStartup.test.ts +++ b/apps/server/src/serverRuntimeStartup.test.ts @@ -1,10 +1,9 @@ import * as NodeServices from "@effect/platform-node/NodeServices"; import { - DEFAULT_MODEL, - DEFAULT_MODEL_BY_PROVIDER, + DEFAULT_PROVIDER_DRIVER_KIND, + DEFAULT_PROVIDER_INSTANCE_ID, ProjectId, - ProviderDriverKind, - ProviderInstanceId, + getDefaultModelForProvider, ThreadId, } from "@t3tools/contracts"; import { assert, it } from "@effect/vitest"; @@ -33,8 +32,8 @@ import { it("uses the OpenCode default for auto-bootstrapped model selection", () => { assert.deepStrictEqual(getAutoBootstrapDefaultModelSelection(), { - instanceId: ProviderInstanceId.make("opencode"), - model: DEFAULT_MODEL_BY_PROVIDER[ProviderDriverKind.make("opencode")] ?? DEFAULT_MODEL, + instanceId: DEFAULT_PROVIDER_INSTANCE_ID, + model: getDefaultModelForProvider(DEFAULT_PROVIDER_DRIVER_KIND), }); }); diff --git a/apps/server/src/serverRuntimeStartup.ts b/apps/server/src/serverRuntimeStartup.ts index cc94b620fc0..b992e7b0592 100644 --- a/apps/server/src/serverRuntimeStartup.ts +++ b/apps/server/src/serverRuntimeStartup.ts @@ -1,12 +1,11 @@ import { CommandId, - DEFAULT_MODEL, - DEFAULT_MODEL_BY_PROVIDER, + DEFAULT_PROVIDER_DRIVER_KIND, + DEFAULT_PROVIDER_INSTANCE_ID, DEFAULT_PROVIDER_INTERACTION_MODE, + getDefaultModelForProvider, type ModelSelection, - ProviderDriverKind, ProjectId, - ProviderInstanceId, ThreadId, } from "@t3tools/contracts"; import * as Data from "effect/Data"; @@ -156,8 +155,8 @@ export const launchStartupHeartbeat = recordStartupHeartbeat.pipe( ); export const getAutoBootstrapDefaultModelSelection = (): ModelSelection => ({ - instanceId: ProviderInstanceId.make("opencode"), - model: DEFAULT_MODEL_BY_PROVIDER[ProviderDriverKind.make("opencode")] ?? DEFAULT_MODEL, + instanceId: DEFAULT_PROVIDER_INSTANCE_ID, + model: getDefaultModelForProvider(DEFAULT_PROVIDER_DRIVER_KIND), }); export const resolveWelcomeBase = Effect.gen(function* () { diff --git a/apps/server/src/serverSettings.ts b/apps/server/src/serverSettings.ts index 5ea2e03813f..ecde630be30 100644 --- a/apps/server/src/serverSettings.ts +++ b/apps/server/src/serverSettings.ts @@ -11,9 +11,8 @@ * @module ServerSettings */ import { - DEFAULT_GIT_TEXT_GENERATION_MODEL, - DEFAULT_GIT_TEXT_GENERATION_MODEL_BY_PROVIDER, DEFAULT_SERVER_SETTINGS, + getDefaultGitTextGenerationModelForProvider, isProviderDriverKind, type ModelSelection, type ProviderInstanceConfig, @@ -206,9 +205,7 @@ function fallbackTextGenerationProvider(settings: ServerSettings): ServerSetting ...settings, textGenerationModelSelection: { instanceId: ProviderInstanceId.make(fallback), - model: - DEFAULT_GIT_TEXT_GENERATION_MODEL_BY_PROVIDER[fallback] ?? - DEFAULT_GIT_TEXT_GENERATION_MODEL, + model: getDefaultGitTextGenerationModelForProvider(fallback), } satisfies ModelSelection, }; } diff --git a/apps/web/src/components/ChatView.tsx b/apps/web/src/components/ChatView.tsx index c7237cfbbe6..54f504848c5 100644 --- a/apps/web/src/components/ChatView.tsx +++ b/apps/web/src/components/ChatView.tsx @@ -1,8 +1,9 @@ import { type ApprovalRequestId, - DEFAULT_MODEL, - DEFAULT_MODEL_BY_PROVIDER, + DEFAULT_PROVIDER_DRIVER_KIND, + DEFAULT_PROVIDER_INSTANCE_ID, defaultInstanceIdForDriver, + getDefaultModelForProvider, type EnvironmentId, type MessageId, type ModelSelection, @@ -798,9 +799,8 @@ export default function ChatView(props: ChatViewProps) { threadId, draftThread, fallbackDraftProject?.defaultModelSelection ?? { - instanceId: ProviderInstanceId.make("opencode"), - model: - DEFAULT_MODEL_BY_PROVIDER[ProviderDriverKind.make("opencode")] ?? DEFAULT_MODEL, + instanceId: DEFAULT_PROVIDER_INSTANCE_ID, + model: getDefaultModelForProvider(DEFAULT_PROVIDER_DRIVER_KIND), }, localDraftError, ) @@ -1261,7 +1261,7 @@ export default function ChatView(props: ChatViewProps) { const providerStatuses = serverConfig?.providers ?? EMPTY_PROVIDERS; const unlockedSelectedProvider = resolveSelectableProvider( providerStatuses, - selectedProviderByThreadId ?? threadProvider ?? ProviderDriverKind.make("opencode"), + selectedProviderByThreadId ?? threadProvider ?? DEFAULT_PROVIDER_DRIVER_KIND, ); const selectedProvider: ProviderDriverKind = lockedProvider ?? unlockedSelectedProvider; const phase = derivePhase(activeThread?.session ?? null); @@ -2803,8 +2803,7 @@ export default function ChatView(props: ChatViewProps) { ctxSelectedModelSelection.instanceId, ctxSelectedModel || activeProject.defaultModelSelection?.model || - DEFAULT_MODEL_BY_PROVIDER[ctxSelectedProvider] || - DEFAULT_MODEL, + getDefaultModelForProvider(ctxSelectedProvider), ctxSelectedModelSelection.options, ); diff --git a/apps/web/src/components/chat/ChatComposer.tsx b/apps/web/src/components/chat/ChatComposer.tsx index 369e49b8d90..8f42168a65b 100644 --- a/apps/web/src/components/chat/ChatComposer.tsx +++ b/apps/web/src/components/chat/ChatComposer.tsx @@ -5,6 +5,7 @@ import type { ProjectEntry, ProviderApprovalDecision, ProviderInteractionMode, + ProviderInstanceId, ResolvedKeybindingsConfig, RuntimeMode, ScopedThreadRef, @@ -13,8 +14,9 @@ import type { TurnId, } from "@t3tools/contracts"; import { + DEFAULT_PROVIDER_DRIVER_KIND, + DEFAULT_PROVIDER_INSTANCE_ID, ProviderDriverKind, - ProviderInstanceId, PROVIDER_SEND_TURN_MAX_ATTACHMENTS, PROVIDER_SEND_TURN_MAX_IMAGE_BYTES, } from "@t3tools/contracts"; @@ -607,7 +609,7 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) const unlockedSelectedProvider = resolveSelectableProvider( providerStatuses, - explicitSelectedInstanceId ?? ProviderDriverKind.make("opencode"), + explicitSelectedInstanceId ?? DEFAULT_PROVIDER_DRIVER_KIND, ); const selectedProvider: ProviderDriverKind = lockedProvider ?? unlockedSelectedProvider; const lockedContinuationGroupKey = useMemo((): string | null => { @@ -675,7 +677,7 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) providerInstanceEntries[0]?.instanceId ?? activeThreadModelSelection?.instanceId ?? activeProjectDefaultModelSelection?.instanceId ?? - ProviderInstanceId.make("opencode") + DEFAULT_PROVIDER_INSTANCE_ID ); }, [ activeProjectDefaultModelSelection?.instanceId, diff --git a/apps/web/src/components/settings/AddProviderInstanceDialog.tsx b/apps/web/src/components/settings/AddProviderInstanceDialog.tsx index c5570e5b317..0973a01fb4e 100644 --- a/apps/web/src/components/settings/AddProviderInstanceDialog.tsx +++ b/apps/web/src/components/settings/AddProviderInstanceDialog.tsx @@ -4,6 +4,7 @@ import { CheckIcon } from "lucide-react"; import { Radio as RadioPrimitive } from "@base-ui/react/radio"; import { useCallback, useEffect, useMemo, useState } from "react"; import { + DEFAULT_PROVIDER_DRIVER_KIND, ProviderInstanceId, ProviderDriverKind, type ProviderInstanceConfig, @@ -61,7 +62,6 @@ function deriveInstanceId(driver: ProviderDriverKind, label: string): string { } const INSTANCE_ID_PATTERN = /^[a-zA-Z][a-zA-Z0-9_-]*$/; -const DEFAULT_DRIVER_KIND = ProviderDriverKind.make("opencode"); const DEFAULT_DRIVER_OPTION = DRIVER_OPTIONS[0]!; const EMPTY_CONFIG_DRAFT: Record = {}; interface ComingSoonDriverOption { @@ -118,7 +118,7 @@ export function AddProviderInstanceDialog({ open, onOpenChange }: AddProviderIns const { updateSettings } = useUpdateSettings(); const [wizardStep, setWizardStep] = useState(0); - const [driver, setDriver] = useState(DEFAULT_DRIVER_KIND); + const [driver, setDriver] = useState(DEFAULT_PROVIDER_DRIVER_KIND); const [label, setLabel] = useState(""); const [accentColor, setAccentColor] = useState(""); const [instanceId, setInstanceId] = useState(""); @@ -139,7 +139,7 @@ export function AddProviderInstanceDialog({ open, onOpenChange }: AddProviderIns // from a clean slate. useEffect(() => { if (!open) return; - setDriver(DEFAULT_DRIVER_KIND); + setDriver(DEFAULT_PROVIDER_DRIVER_KIND); setLabel(""); setAccentColor(""); setInstanceId(""); diff --git a/apps/web/src/components/settings/SettingsPanels.tsx b/apps/web/src/components/settings/SettingsPanels.tsx index 6427a63c017..20444276f04 100644 --- a/apps/web/src/components/settings/SettingsPanels.tsx +++ b/apps/web/src/components/settings/SettingsPanels.tsx @@ -3,6 +3,7 @@ import { useQueryClient } from "@tanstack/react-query"; import { Link } from "@tanstack/react-router"; import { useCallback, useMemo, useRef, useState } from "react"; import { + DEFAULT_PROVIDER_DRIVER_KIND, defaultInstanceIdForDriver, type DesktopUpdateChannel, PROVIDER_DISPLAY_NAMES, @@ -99,8 +100,6 @@ const TIMESTAMP_FORMAT_LABELS = { "24-hour": "24-hour", } as const; -const DEFAULT_DRIVER_KIND = ProviderDriverKind.make("opencode"); - function withoutProviderInstanceKey( record: Readonly> | undefined, key: ProviderInstanceId, @@ -502,7 +501,7 @@ export function GeneralSettingsPanel() { (entry) => entry.instanceId === textGenInstanceId, ); const textGenProvider: ProviderDriverKind = - textGenInstanceEntry?.driverKind ?? DEFAULT_DRIVER_KIND; + textGenInstanceEntry?.driverKind ?? DEFAULT_PROVIDER_DRIVER_KIND; const gitModelOptionsByInstance = getCustomModelOptionsByInstance( settings, serverProviders, diff --git a/apps/web/src/composerDraftStore.ts b/apps/web/src/composerDraftStore.ts index e7c77005cc5..41d8f324f9d 100644 --- a/apps/web/src/composerDraftStore.ts +++ b/apps/web/src/composerDraftStore.ts @@ -1,7 +1,7 @@ import { - DEFAULT_MODEL, - DEFAULT_MODEL_BY_PROVIDER, + DEFAULT_PROVIDER_DRIVER_KIND, defaultInstanceIdForDriver, + getDefaultModelForProvider, type EnvironmentId, ModelSelection, ProjectId, @@ -723,7 +723,7 @@ function normalizeModelSelection( // into a driver kind here; they get generic default normalization. const driverKindHint = normalizeProviderDriverKind(candidate?.provider ?? legacy?.provider) ?? - ProviderDriverKind.make("opencode"); + DEFAULT_PROVIDER_DRIVER_KIND; const model = normalizeModelSlug(rawModel, driverKindHint); if (!model) { return null; @@ -823,7 +823,7 @@ function legacyToModelSelectionByProvider( instanceKey, modelSelection?.instanceId === instanceKey ? modelSelection.model - : (DEFAULT_MODEL_BY_PROVIDER[driverKind] ?? DEFAULT_MODEL), + : getDefaultModelForProvider(driverKind), options, ); } @@ -2424,7 +2424,7 @@ const composerDraftStore = create()( if (opts && opts.length > 0) { nextMap[instanceKey] = createModelSelection( instanceKey, - current?.model ?? DEFAULT_MODEL_BY_PROVIDER[driverKind] ?? DEFAULT_MODEL, + current?.model ?? getDefaultModelForProvider(driverKind), opts, ); } else if (current?.options) { @@ -2460,8 +2460,7 @@ const composerDraftStore = create()( const instanceKey = options?.instanceId ?? defaultInstanceIdForDriver(normalizedProvider); const fallbackModel = normalizeModelSlug(options?.model, normalizedProvider) ?? - DEFAULT_MODEL_BY_PROVIDER[normalizedProvider] ?? - DEFAULT_MODEL; + getDefaultModelForProvider(normalizedProvider); const providerOpts = nextProviderOptions && nextProviderOptions.length > 0 ? nextProviderOptions : undefined; diff --git a/apps/web/src/modelSelection.ts b/apps/web/src/modelSelection.ts index c88510b40fc..b2fcbe2aeee 100644 --- a/apps/web/src/modelSelection.ts +++ b/apps/web/src/modelSelection.ts @@ -1,7 +1,8 @@ import { - DEFAULT_GIT_TEXT_GENERATION_MODEL, - DEFAULT_GIT_TEXT_GENERATION_MODEL_BY_PROVIDER, + DEFAULT_PROVIDER_DRIVER_KIND, + DEFAULT_PROVIDER_INSTANCE_ID, defaultInstanceIdForDriver, + getDefaultGitTextGenerationModelForProvider, type ModelSelection, ProviderDriverKind, ProviderInstanceId, @@ -25,8 +26,6 @@ import { sortModelsForProviderInstance } from "./modelOrdering"; const MAX_CUSTOM_MODEL_COUNT = 32; export const MAX_CUSTOM_MODEL_LENGTH = 256; -const DEFAULT_TEXT_GENERATION_INSTANCE_ID = ProviderInstanceId.make("opencode"); -const DEFAULT_TEXT_GENERATION_DRIVER_KIND = ProviderDriverKind.make("opencode"); /** * Resolve the custom-model list for a given instance, preferring the @@ -116,7 +115,7 @@ function applyInstanceModelPreferences( export function normalizeCustomModelSlugs( models: Iterable, builtInModelSlugs: ReadonlySet, - provider: ProviderDriverKind = ProviderDriverKind.make("opencode"), + provider: ProviderDriverKind = DEFAULT_PROVIDER_DRIVER_KIND, ): string[] { const normalizedModels: string[] = []; const seen = new Set(); @@ -275,10 +274,8 @@ export function resolveAppModelSelectionState( providers: ReadonlyArray, ): ModelSelection { const selection = settings.textGenerationModelSelection ?? { - instanceId: DEFAULT_TEXT_GENERATION_INSTANCE_ID, - model: - DEFAULT_GIT_TEXT_GENERATION_MODEL_BY_PROVIDER[DEFAULT_TEXT_GENERATION_DRIVER_KIND] ?? - DEFAULT_GIT_TEXT_GENERATION_MODEL, + instanceId: DEFAULT_PROVIDER_INSTANCE_ID, + model: getDefaultGitTextGenerationModelForProvider(DEFAULT_PROVIDER_DRIVER_KIND), }; const entries = deriveProviderInstanceEntries(providers); const selectedEntry = entries.find( @@ -293,7 +290,7 @@ export function resolveAppModelSelectionState( const model = resolveAppModelSelectionForInstance(entry.instanceId, settings, providers, selectedModel) ?? entry.models[0]?.slug ?? - DEFAULT_GIT_TEXT_GENERATION_MODEL_BY_PROVIDER[entry.driverKind]; + getDefaultGitTextGenerationModelForProvider(entry.driverKind); if (!model) { return createModelSelection(entry.instanceId, "", []); } diff --git a/apps/web/src/providerModels.ts b/apps/web/src/providerModels.ts index 28bc61391d2..70cb31a2fa8 100644 --- a/apps/web/src/providerModels.ts +++ b/apps/web/src/providerModels.ts @@ -1,7 +1,7 @@ import { - DEFAULT_MODEL, - DEFAULT_MODEL_BY_PROVIDER, + DEFAULT_PROVIDER_DRIVER_KIND, defaultInstanceIdForDriver, + getDefaultModelForProvider, ProviderDriverKind, type ModelCapabilities, type ProviderInstanceId, @@ -13,7 +13,6 @@ import { createModelCapabilities, normalizeModelSlug } from "@t3tools/shared/mod const EMPTY_CAPABILITIES: ModelCapabilities = createModelCapabilities({ optionDescriptors: [], }); -const DEFAULT_DRIVER_KIND = ProviderDriverKind.make("opencode"); export function formatProviderDriverKindLabel(provider: ProviderDriverKind): string { return provider .replace(/([a-z])([A-Z])/g, "$1 $2") @@ -75,7 +74,7 @@ export function resolveSelectableProvider( } return ( providers.find((candidate) => candidate.enabled && candidate.availability !== "unavailable") - ?.driver ?? DEFAULT_DRIVER_KIND + ?.driver ?? DEFAULT_PROVIDER_DRIVER_KIND ); } @@ -96,7 +95,6 @@ export function getDefaultServerModel( return ( models.find((model) => !model.isCustom)?.slug ?? models[0]?.slug ?? - DEFAULT_MODEL_BY_PROVIDER[provider] ?? - DEFAULT_MODEL + getDefaultModelForProvider(provider) ); } diff --git a/apps/web/src/store.ts b/apps/web/src/store.ts index f255af925d5..5721bc8b34f 100644 --- a/apps/web/src/store.ts +++ b/apps/web/src/store.ts @@ -18,7 +18,11 @@ import type { ScopedProjectRef, ScopedThreadRef, } from "@t3tools/contracts"; -import { isProviderDriverKind, ProviderDriverKind } from "@t3tools/contracts"; +import { + DEFAULT_PROVIDER_DRIVER_KIND, + isProviderDriverKind, + ProviderDriverKind, +} from "@t3tools/contracts"; import type { ThreadId, TurnId } from "@t3tools/contracts"; import * as Schema from "effect/Schema"; import { resolveModelSlugForProvider } from "@t3tools/shared/model"; @@ -1010,7 +1014,7 @@ function toLegacyProvider(providerName: string | null): ProviderDriverKind { if (isProviderDriverKindValue(providerName)) { return providerName; } - return ProviderDriverKind.make("opencode"); + return DEFAULT_PROVIDER_DRIVER_KIND; } function attachmentPreviewRoutePath(attachmentId: string): string { diff --git a/packages/contracts/src/model.ts b/packages/contracts/src/model.ts index 5b1363f5544..dffc176d0e5 100644 --- a/packages/contracts/src/model.ts +++ b/packages/contracts/src/model.ts @@ -2,7 +2,7 @@ import * as Effect from "effect/Effect"; import * as Schema from "effect/Schema"; import * as SchemaTransformation from "effect/SchemaTransformation"; import { TrimmedNonEmptyString } from "./baseSchemas.ts"; -import { ProviderDriverKind } from "./providerInstance.ts"; +import { defaultInstanceIdForDriver, ProviderDriverKind } from "./providerInstance.ts"; export const ProviderOptionDescriptorType = Schema.Literals(["select", "boolean"]); export type ProviderOptionDescriptorType = typeof ProviderOptionDescriptorType.Type; @@ -132,6 +132,10 @@ const CLAUDE_DRIVER_KIND = ProviderDriverKind.make("claudeAgent"); const CURSOR_DRIVER_KIND = ProviderDriverKind.make("cursor"); const OPENCODE_DRIVER_KIND = ProviderDriverKind.make("opencode"); +export const DEFAULT_PROVIDER_DRIVER_KIND = OPENCODE_DRIVER_KIND; +export const DEFAULT_PROVIDER_INSTANCE_ID = defaultInstanceIdForDriver( + DEFAULT_PROVIDER_DRIVER_KIND, +); export const DEFAULT_MODEL = "gpt-5.4"; export const DEFAULT_GIT_TEXT_GENERATION_MODEL = "gpt-5.4-mini"; @@ -152,6 +156,16 @@ export const DEFAULT_GIT_TEXT_GENERATION_MODEL_BY_PROVIDER: Partial< [OPENCODE_DRIVER_KIND]: "openai/gpt-5", }; +export function getDefaultModelForProvider(provider: ProviderDriverKind): string { + return DEFAULT_MODEL_BY_PROVIDER[provider] ?? DEFAULT_MODEL; +} + +export function getDefaultGitTextGenerationModelForProvider(provider: ProviderDriverKind): string { + return ( + DEFAULT_GIT_TEXT_GENERATION_MODEL_BY_PROVIDER[provider] ?? DEFAULT_GIT_TEXT_GENERATION_MODEL + ); +} + export const MODEL_SLUG_ALIASES_BY_PROVIDER: Partial< Record> > = { diff --git a/packages/contracts/src/settings.ts b/packages/contracts/src/settings.ts index fa8b8b99268..7749ce7ae82 100644 --- a/packages/contracts/src/settings.ts +++ b/packages/contracts/src/settings.ts @@ -4,16 +4,13 @@ import * as Schema from "effect/Schema"; import * as SchemaTransformation from "effect/SchemaTransformation"; import { TrimmedNonEmptyString, TrimmedString } from "./baseSchemas.ts"; import { - DEFAULT_GIT_TEXT_GENERATION_MODEL, - DEFAULT_GIT_TEXT_GENERATION_MODEL_BY_PROVIDER, + DEFAULT_PROVIDER_DRIVER_KIND, + DEFAULT_PROVIDER_INSTANCE_ID, + getDefaultGitTextGenerationModelForProvider, ProviderOptionSelections, } from "./model.ts"; import { ModelSelection } from "./orchestration.ts"; -import { - ProviderDriverKind, - ProviderInstanceConfig, - ProviderInstanceId, -} from "./providerInstance.ts"; +import { ProviderInstanceConfig, ProviderInstanceId } from "./providerInstance.ts"; // ── Client Settings (local-only) ─────────────────────────────── @@ -361,10 +358,8 @@ export const ServerSettings = Schema.Struct({ textGenerationModelSelection: ModelSelection.pipe( Schema.withDecodingDefault( Effect.succeed({ - instanceId: ProviderInstanceId.make("opencode"), - model: - DEFAULT_GIT_TEXT_GENERATION_MODEL_BY_PROVIDER[ProviderDriverKind.make("opencode")] ?? - DEFAULT_GIT_TEXT_GENERATION_MODEL, + instanceId: DEFAULT_PROVIDER_INSTANCE_ID, + model: getDefaultGitTextGenerationModelForProvider(DEFAULT_PROVIDER_DRIVER_KIND), }), ), ), diff --git a/packages/shared/src/model.ts b/packages/shared/src/model.ts index 3b57d30e9c7..ae92130cc56 100644 --- a/packages/shared/src/model.ts +++ b/packages/shared/src/model.ts @@ -1,6 +1,6 @@ import { - DEFAULT_MODEL, - DEFAULT_MODEL_BY_PROVIDER, + DEFAULT_PROVIDER_DRIVER_KIND, + getDefaultModelForProvider, MODEL_SLUG_ALIASES_BY_PROVIDER, type ModelCapabilities, type ModelSelection, @@ -10,8 +10,6 @@ import { type ProviderOptionSelection, } from "@t3tools/contracts"; -const DEFAULT_PROVIDER_DRIVER_KIND = ProviderDriverKind.make("opencode"); - export interface SelectableModelOption { slug: string; name: string; @@ -288,7 +286,7 @@ export function resolveSelectableModel( function resolveModelSlug(model: string | null | undefined, provider: ProviderDriverKind): string { const normalized = normalizeModelSlug(model, provider); if (!normalized) { - return DEFAULT_MODEL_BY_PROVIDER[provider] ?? DEFAULT_MODEL; + return getDefaultModelForProvider(provider); } return normalized; }