Unverified 提交 5d678c2e authored 作者: Will Chen's avatar Will Chen 提交者: GitHub

Smart auto (#476)

上级 30415638
...@@ -34,10 +34,29 @@ testSkipIfWindows( ...@@ -34,10 +34,29 @@ testSkipIfWindows(
}, },
); );
testSkipIfWindows("auto should send message to engine", async ({ po }) => { testSkipIfWindows(
await po.setUpDyadPro(); "smart auto should send message to engine",
await po.sendPrompt("[dump] tc=turbo-edits"); async ({ po }) => {
await po.setUpDyadPro();
await po.sendPrompt("[dump] tc=turbo-edits");
await po.snapshotServerDump("request"); await po.snapshotServerDump("request");
await po.snapshotMessages({ replaceDumpPath: true }); await po.snapshotMessages({ replaceDumpPath: true });
}); },
);
testSkipIfWindows(
"regular auto should send message to engine",
async ({ po }) => {
await po.setUpDyadPro();
const proModesDialog = await po.openProModesDialog({
location: "home-chat-input-container",
});
await proModesDialog.toggleSmartContext();
await proModesDialog.close();
await po.sendPrompt("[dump] tc=turbo-edits");
await po.snapshotServerDump("request");
await po.snapshotMessages({ replaceDumpPath: true });
},
);
- paragraph: "[dump] tc=turbo-edits"
- paragraph: "[[dyad-dump-path=*]]"
- button "Retry":
- img
\ No newline at end of file
{ {
"body": { "body": {
"model": "gemini/gemini-2.5-flash", "model": "dyad/auto",
"max_tokens": 8000, "max_tokens": 32000,
"temperature": 0, "temperature": 0,
"messages": [ "messages": [
{ {
......
import type { LargeLanguageModel } from "@/lib/schemas"; import { isDyadProEnabled, type LargeLanguageModel } from "@/lib/schemas";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { import {
Tooltip, Tooltip,
...@@ -119,6 +119,8 @@ export function ModelPicker() { ...@@ -119,6 +119,8 @@ export function ModelPicker() {
return null; return null;
} }
const selectedModel = settings?.selectedModel; const selectedModel = settings?.selectedModel;
const isSmartAutoEnabled =
settings.enableProSmartFilesContextMode && isDyadProEnabled(settings);
const modelDisplayName = getModelDisplayName(); const modelDisplayName = getModelDisplayName();
return ( return (
...@@ -190,21 +192,37 @@ export function ModelPicker() { ...@@ -190,21 +192,37 @@ export function ModelPicker() {
> >
<div className="flex justify-between items-start w-full"> <div className="flex justify-between items-start w-full">
<span className="flex flex-col items-start"> <span className="flex flex-col items-start">
<span>{model.displayName}</span> <span>
<span className="text-xs text-muted-foreground"> {isSmartAutoEnabled
auto ? "Smart Auto"
: model.displayName}
</span> </span>
</span> </span>
{model.tag && ( <div className="flex items-center gap-1.5">
<span className="text-[10px] bg-primary/10 text-primary px-1.5 py-0.5 rounded-full font-medium"> {isSmartAutoEnabled && (
{model.tag} <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">
</span> 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> </div>
</DropdownMenuItem> </DropdownMenuItem>
</TooltipTrigger> </TooltipTrigger>
<TooltipContent side="right"> <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> </TooltipContent>
</Tooltip> </Tooltip>
))} ))}
......
...@@ -8,6 +8,7 @@ import { eq } from "drizzle-orm"; ...@@ -8,6 +8,7 @@ import { eq } from "drizzle-orm";
export const PROVIDERS_THAT_SUPPORT_THINKING: (keyof typeof MODEL_OPTIONS)[] = [ export const PROVIDERS_THAT_SUPPORT_THINKING: (keyof typeof MODEL_OPTIONS)[] = [
"google", "google",
"auto",
]; ];
export interface ModelOption { export interface ModelOption {
...@@ -139,6 +140,11 @@ export const MODEL_OPTIONS: Record<string, ModelOption[]> = { ...@@ -139,6 +140,11 @@ export const MODEL_OPTIONS: Record<string, ModelOption[]> = {
displayName: "Auto", displayName: "Auto",
description: "Automatically selects the best model", description: "Automatically selects the best model",
tag: "Default", 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< ...@@ -186,7 +192,7 @@ export const CLOUD_PROVIDERS: Record<
auto: { auto: {
displayName: "Dyad", displayName: "Dyad",
websiteUrl: "https://academy.dyad.sh/settings", websiteUrl: "https://academy.dyad.sh/settings",
gatewayPrefix: "", gatewayPrefix: "dyad/",
}, },
}; };
......
...@@ -54,39 +54,6 @@ export async function getModelClient( ...@@ -54,39 +54,6 @@ export async function getModelClient(
const allProviders = await getLanguageModelProviders(); const allProviders = await getLanguageModelProviders();
const dyadApiKey = settings.providerSettings?.auto?.apiKey?.value; 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 --- // --- Handle specific provider ---
const providerConfig = allProviders.find((p) => p.id === model.provider); const providerConfig = allProviders.find((p) => p.id === model.provider);
...@@ -161,6 +128,38 @@ export async function getModelClient( ...@@ -161,6 +128,38 @@ export async function getModelClient(
// Fall through to regular provider logic if gateway prefix is missing // 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); return getRegularModelClient(model, settings, providerConfig);
} }
......
...@@ -168,6 +168,13 @@ export const UserSettingsSchema = z.object({ ...@@ -168,6 +168,13 @@ export const UserSettingsSchema = z.object({
*/ */
export type UserSettings = z.infer<typeof UserSettingsSchema>; 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 // Define interfaces for the props
export interface SecurityRisk { export interface SecurityRisk {
type: "warning" | "danger"; type: "warning" | "danger";
......
...@@ -290,6 +290,15 @@ ...@@ -290,6 +290,15 @@
} }
} }
@keyframes shimmer {
0% {
background-position: -200% 0;
}
100% {
background-position: 200% 0;
}
}
.animate-marquee { .animate-marquee {
animation: marquee 2s linear infinite; animation: marquee 2s linear infinite;
} }
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论