Smart auto (#476)

This commit is contained in:
Will Chen
2025-06-23 23:08:29 -07:00
committed by GitHub
parent 3041563809
commit 5d678c2ead
10 changed files with 490 additions and 52 deletions

View File

@@ -1,4 +1,4 @@
import type { LargeLanguageModel } from "@/lib/schemas";
import { isDyadProEnabled, type LargeLanguageModel } from "@/lib/schemas";
import { Button } from "@/components/ui/button";
import {
Tooltip,
@@ -119,6 +119,8 @@ export function ModelPicker() {
return null;
}
const selectedModel = settings?.selectedModel;
const isSmartAutoEnabled =
settings.enableProSmartFilesContextMode && isDyadProEnabled(settings);
const modelDisplayName = getModelDisplayName();
return (
@@ -190,21 +192,37 @@ export function ModelPicker() {
>
<div className="flex justify-between items-start w-full">
<span className="flex flex-col items-start">
<span>{model.displayName}</span>
<span className="text-xs text-muted-foreground">
auto
<span>
{isSmartAutoEnabled
? "Smart Auto"
: model.displayName}
</span>
</span>
{model.tag && (
<span className="text-[10px] bg-primary/10 text-primary px-1.5 py-0.5 rounded-full font-medium">
{model.tag}
</span>
)}
<div className="flex items-center gap-1.5">
{isSmartAutoEnabled && (
<span className="text-[10px] bg-gradient-to-r from-indigo-600 via-indigo-500 to-indigo-600 bg-[length:200%_100%] animate-[shimmer_5s_ease-in-out_infinite] text-white px-1.5 py-0.5 rounded-full font-medium">
Dyad Pro
</span>
)}
{model.tag && (
<span className="text-[10px] bg-primary/10 text-primary px-1.5 py-0.5 rounded-full font-medium">
{model.tag}
</span>
)}
</div>
</div>
</DropdownMenuItem>
</TooltipTrigger>
<TooltipContent side="right">
{model.description}
{isSmartAutoEnabled ? (
<p>
<strong>Smart Auto</strong> uses a cheaper model for
easier tasks
<br /> and a flagship model for harder tasks
</p>
) : (
model.description
)}
</TooltipContent>
</Tooltip>
))}

View File

@@ -8,6 +8,7 @@ import { eq } from "drizzle-orm";
export const PROVIDERS_THAT_SUPPORT_THINKING: (keyof typeof MODEL_OPTIONS)[] = [
"google",
"auto",
];
export interface ModelOption {
@@ -139,6 +140,11 @@ export const MODEL_OPTIONS: Record<string, ModelOption[]> = {
displayName: "Auto",
description: "Automatically selects the best model",
tag: "Default",
// These are below Gemini 2.5 Pro & Flash limits
// which are the ones defaulted to for both regular auto
// and smart auto.
maxOutputTokens: 32_000,
contextWindow: 1_000_000,
},
],
};
@@ -186,7 +192,7 @@ export const CLOUD_PROVIDERS: Record<
auto: {
displayName: "Dyad",
websiteUrl: "https://academy.dyad.sh/settings",
gatewayPrefix: "",
gatewayPrefix: "dyad/",
},
};

View File

@@ -54,39 +54,6 @@ export async function getModelClient(
const allProviders = await getLanguageModelProviders();
const dyadApiKey = settings.providerSettings?.auto?.apiKey?.value;
// Handle 'auto' provider by trying each model in AUTO_MODELS until one works
if (model.provider === "auto") {
for (const autoModel of AUTO_MODELS) {
const providerInfo = allProviders.find(
(p) => p.id === autoModel.provider,
);
const envVarName = providerInfo?.envVarName;
const apiKey =
dyadApiKey ||
settings.providerSettings?.[autoModel.provider]?.apiKey?.value ||
(envVarName ? getEnvVar(envVarName) : undefined);
if (apiKey) {
logger.log(
`Using provider: ${autoModel.provider} model: ${autoModel.name}`,
);
// Recursively call with the specific model found
return await getModelClient(
{
provider: autoModel.provider,
name: autoModel.name,
},
settings,
files,
);
}
}
// If no models have API keys, throw an error
throw new Error(
"No API keys available for any model supported by the 'auto' provider.",
);
}
// --- Handle specific provider ---
const providerConfig = allProviders.find((p) => p.id === model.provider);
@@ -161,6 +128,38 @@ export async function getModelClient(
// Fall through to regular provider logic if gateway prefix is missing
}
}
// Handle 'auto' provider by trying each model in AUTO_MODELS until one works
if (model.provider === "auto") {
for (const autoModel of AUTO_MODELS) {
const providerInfo = allProviders.find(
(p) => p.id === autoModel.provider,
);
const envVarName = providerInfo?.envVarName;
const apiKey =
settings.providerSettings?.[autoModel.provider]?.apiKey?.value ||
(envVarName ? getEnvVar(envVarName) : undefined);
if (apiKey) {
logger.log(
`Using provider: ${autoModel.provider} model: ${autoModel.name}`,
);
// Recursively call with the specific model found
return await getModelClient(
{
provider: autoModel.provider,
name: autoModel.name,
},
settings,
files,
);
}
}
// If no models have API keys, throw an error
throw new Error(
"No API keys available for any model supported by the 'auto' provider.",
);
}
return getRegularModelClient(model, settings, providerConfig);
}

View File

@@ -168,6 +168,13 @@ export const UserSettingsSchema = z.object({
*/
export type UserSettings = z.infer<typeof UserSettingsSchema>;
export function isDyadProEnabled(settings: UserSettings): boolean {
return (
settings.enableDyadPro === true &&
!!settings.providerSettings?.auto?.apiKey?.value
);
}
// Define interfaces for the props
export interface SecurityRisk {
type: "warning" | "danger";

View File

@@ -290,6 +290,15 @@
}
}
@keyframes shimmer {
0% {
background-position: -200% 0;
}
100% {
background-position: 200% 0;
}
}
.animate-marquee {
animation: marquee 2s linear infinite;
}