plans/dyamic-models.md
Replace the baked-in builtin language model catalog in src/ipc/shared/language_model_constants.ts with an API-first catalog fetched from api.dyad.sh, while preserving language_model_constants.ts as a local fallback when the API is unavailable or invalid.
Also remove product-facing hardcoded model IDs where we currently encode specific model names in feature code, and instead derive those choices from API-provided aliases and ordered selections.
api.dyad.sh.Today src/ipc/shared/language_model_constants.ts mixes several responsibilities:
Builtin model data is surfaced through src/ipc/shared/language_model_helpers.ts, which is already the main-process source of truth behind:
get-language-model-providersget-language-modelsget-language-models-by-providersThis is good because we can make the model catalog dynamic inside the main process without forcing a renderer-wide contract change.
Keep src/ipc/shared/language_model_constants.ts, but reposition it as fallback data and app-local metadata instead of the primary source of builtin models.
The remote API should own:
The local app code should continue to own:
Add a main-process fetch utility for the language model catalog, similar in spirit to src/ipc/utils/template_utils.ts.
Behavior:
https://api.dyad.sh/v1/language-model-catalogexpiresAt from the responsenullsrc/ipc/shared/language_model_helpers.ts should become the source that:
This keeps the existing IPC contracts intact while changing the builtin data source underneath.
Any product code that currently hardcodes a concrete builtin model should stop importing exact model IDs and instead resolve an alias to a { providerId, apiName } pair.
This allows the API to update the concrete model without requiring an app release.
We agreed to keep the alias surface minimal and not add provider-level aliases yet.
Required aliases:
dyad/theme-generator/googledyad/theme-generator/anthropicdyad/theme-generator/openaidyad/auto/openaidyad/auto/anthropicdyad/auto/googledyad/help-bot/defaultNot needed:
dyad/theme-generator/defaultFor theme generation, the UI will use the first option returned by the API as the default selected option.
Endpoint:
GET https://api.dyad.sh/v1/language-model-catalog
Suggested response shape:
type LanguageModelCatalogResponse = {
version: string;
expiresAt?: string;
providers: Array<{
id: string;
displayName: string;
type: "cloud";
hasFreeTier?: boolean;
websiteUrl?: string;
secondary?: boolean;
supportsThinking?: boolean;
gatewayPrefix?: string;
}>;
modelsByProvider: Record<
string,
Array<{
apiName: string;
displayName: string;
description: string;
tag?: string;
tagColor?: string;
dollarSigns?: number;
temperature?: number;
maxOutputTokens?: number;
contextWindow?: number;
lifecycle?: {
stage?: "stable" | "preview" | "deprecated";
};
}>
>;
aliases: Array<{
id: string;
resolvedModel: {
providerId: string;
apiName: string;
};
displayName?: string;
purpose?: "theme-generation" | "auto-mode" | "help-bot";
}>;
curatedSelections?: {
themeGenerationOptions: Array<{
id:
| "dyad/theme-generator/google"
| "dyad/theme-generator/anthropic"
| "dyad/theme-generator/openai";
label: string;
}>;
};
};
Aliases are stable app-facing identifiers for product decisions.
For example:
dyad/theme-generator/google resolves to the concrete Google model to use for theme generation.dyad/auto/openai resolves to the concrete OpenAI model used in auto mode.dyad/help-bot/default resolves to the concrete model used by the help bot.The API should return curatedSelections.themeGenerationOptions in display order.
The client will:
This removes the need for a dedicated dyad/theme-generator/default alias.
Keep auto-mode order in app code for now.
The app can try aliases in this order:
dyad/auto/openaidyad/auto/anthropicdyad/auto/googleThat keeps the API smaller while still eliminating hardcoded concrete model IDs.
Add a new main-process utility to:
getRemoteLanguageModelCatalog()resolveBuiltinModelAlias(aliasId)Update src/ipc/shared/language_model_constants.ts so it is clearly the fallback builtin catalog plus app-local metadata.
Avoid using it as the source of product-curated model choices.
Refactor src/ipc/shared/language_model_helpers.ts:
getLanguageModelProviders()
getLanguageModels({ providerId })
getLanguageModelsByProviders()
Add a helper that resolves aliases from the remote catalog, with local fallback mapping if the API is unavailable.
Suggested shape:
type ResolvedBuiltinModel = {
providerId: string;
apiName: string;
};
async function resolveBuiltinModelAlias(
aliasId: string,
): Promise<ResolvedBuiltinModel | null>;
Replace the hardcoded theme generator model enum and mapping with API-derived ordered options.
Desired end state:
gemini-3-pro, claude-opus-4.5, gpt-5.2ThemeGenerationModel becomes a string alias ID rather than a fixed z.enum([...])getModelClientReplace the current hardcoded concrete auto-model list with:
dyad/auto/openaidyad/auto/anthropicdyad/auto/googleThe app keeps the fallback ordering logic locally.
Replace the concrete help-bot model ID with:
dyad/help-bot/defaultDo not broaden scope into:
gpt-4unless the implementation forces us to touch them.
File:
src/components/AIGeneratorTab.tsxCurrent issues:
Planned change:
File:
src/ipc/types/templates.tsCurrent issues:
ThemeGenerationModelSchema is a fixed z.enum([...])Planned change:
z.string() or a constrained alias-oriented schemaFile:
src/pro/main/ipc/handlers/themes_handlers.tsCurrent issues:
THEME_GENERATION_MODEL_MAP hardcodes alias-like UI values to concrete provider/model pairsPlanned change:
File:
src/ipc/utils/get_model_client.tsCurrent issues:
AUTO_MODELS hardcodes exact provider/model pairsPlanned change:
dyad/auto/openaidyad/auto/anthropicdyad/auto/googleFile:
src/ipc/handlers/help_bot_handlers.tsCurrent issues:
Planned change:
dyad/help-bot/defaultFile:
src/pro/main/ipc/handlers/local_agent/tools/generate_image.tsCurrent issue:
Reason not in first pass:
Examples:
src/__tests__/local_agent_handler.test.tssrc/__tests__/prepare_step_utils.test.tssrc/__tests__/readSettings.test.tsReason not in first pass:
language_model_helpers.ts to API-first with fallbackRisk:
Mitigation:
Risk:
Mitigation:
Risk:
ThemeGenerationModel from fixed enum to alias string touches both UI and IPC contractsMitigation:
Risk:
Mitigation:
api.dyad.sh when available.language_model_constants.ts when the API fails or returns invalid data.