Configurable thinking budget (default to medium) (#494)
This commit is contained in:
80
src/components/ThinkingBudgetSelector.tsx
Normal file
80
src/components/ThinkingBudgetSelector.tsx
Normal file
@@ -0,0 +1,80 @@
|
||||
import React from "react";
|
||||
import { useSettings } from "@/hooks/useSettings";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
|
||||
interface OptionInfo {
|
||||
value: string;
|
||||
label: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
const defaultValue = "medium";
|
||||
|
||||
const options: OptionInfo[] = [
|
||||
{
|
||||
value: "low",
|
||||
label: "Low",
|
||||
description:
|
||||
"Minimal thinking tokens for faster responses and lower costs.",
|
||||
},
|
||||
{
|
||||
value: defaultValue,
|
||||
label: "Medium (default)",
|
||||
description: "Balanced thinking for most conversations.",
|
||||
},
|
||||
{
|
||||
value: "high",
|
||||
label: "High",
|
||||
description:
|
||||
"Extended thinking for complex problems requiring deep analysis.",
|
||||
},
|
||||
];
|
||||
|
||||
export const ThinkingBudgetSelector: React.FC = () => {
|
||||
const { settings, updateSettings } = useSettings();
|
||||
|
||||
const handleValueChange = (value: string) => {
|
||||
updateSettings({ thinkingBudget: value as "low" | "medium" | "high" });
|
||||
};
|
||||
|
||||
// Determine the current value
|
||||
const currentValue = settings?.thinkingBudget || defaultValue;
|
||||
|
||||
// Find the current option to display its description
|
||||
const currentOption =
|
||||
options.find((opt) => opt.value === currentValue) || options[1];
|
||||
|
||||
return (
|
||||
<div className="space-y-1">
|
||||
<div className="flex items-center gap-4">
|
||||
<label
|
||||
htmlFor="thinking-budget"
|
||||
className="text-sm font-medium text-gray-700 dark:text-gray-300"
|
||||
>
|
||||
Thinking Budget
|
||||
</label>
|
||||
<Select value={currentValue} onValueChange={handleValueChange}>
|
||||
<SelectTrigger className="w-[180px]" id="thinking-budget">
|
||||
<SelectValue placeholder="Select budget" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{options.map((option) => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="text-sm text-gray-500 dark:text-gray-400">
|
||||
{currentOption.description}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -464,6 +464,7 @@ This conversation includes one or more image attachments. When the user uploads
|
||||
providerOptions: {
|
||||
"dyad-gateway": getExtraProviderOptions(
|
||||
modelClient.builtinProviderId,
|
||||
settings,
|
||||
),
|
||||
google: {
|
||||
thinkingConfig: {
|
||||
|
||||
@@ -84,6 +84,7 @@ export async function getModelClient(
|
||||
: settings.enableProLazyEditsMode,
|
||||
enableSmartFilesContext: settings.enableProSmartFilesContextMode,
|
||||
},
|
||||
settings,
|
||||
})
|
||||
: createOpenAICompatible({
|
||||
name: "dyad-gateway",
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
import { OpenAICompatibleChatSettings } from "@ai-sdk/openai-compatible";
|
||||
import log from "electron-log";
|
||||
import { getExtraProviderOptions } from "./thinking_utils";
|
||||
import type { UserSettings } from "../../lib/schemas";
|
||||
|
||||
const logger = log.scope("llm_engine_provider");
|
||||
|
||||
@@ -48,6 +49,7 @@ or to provide a custom fetch implementation for e.g. testing.
|
||||
enableLazyEdits?: boolean;
|
||||
enableSmartFilesContext?: boolean;
|
||||
};
|
||||
settings: UserSettings;
|
||||
}
|
||||
|
||||
export interface DyadEngineProvider {
|
||||
@@ -125,7 +127,10 @@ export function createDyadEngine(
|
||||
// Parse the request body to manipulate it
|
||||
const parsedBody = {
|
||||
...JSON.parse(init.body),
|
||||
...getExtraProviderOptions(options.originalProviderId),
|
||||
...getExtraProviderOptions(
|
||||
options.originalProviderId,
|
||||
options.settings,
|
||||
),
|
||||
};
|
||||
|
||||
// Add files to the request if they exist
|
||||
|
||||
@@ -1,16 +1,37 @@
|
||||
import { PROVIDERS_THAT_SUPPORT_THINKING } from "../shared/language_model_helpers";
|
||||
import type { UserSettings } from "../../lib/schemas";
|
||||
|
||||
function getThinkingBudgetTokens(
|
||||
thinkingBudget?: "low" | "medium" | "high",
|
||||
): number {
|
||||
switch (thinkingBudget) {
|
||||
case "low":
|
||||
return 1_000;
|
||||
case "medium":
|
||||
return 4_000;
|
||||
case "high":
|
||||
return -1;
|
||||
default:
|
||||
return 4_000; // Default to medium
|
||||
}
|
||||
}
|
||||
|
||||
export function getExtraProviderOptions(
|
||||
providerId: string | undefined,
|
||||
settings: UserSettings,
|
||||
): Record<string, any> {
|
||||
if (!providerId) {
|
||||
return {};
|
||||
}
|
||||
if (PROVIDERS_THAT_SUPPORT_THINKING.includes(providerId)) {
|
||||
const budgetTokens = getThinkingBudgetTokens(settings?.thinkingBudget);
|
||||
return {
|
||||
thinking: {
|
||||
type: "enabled",
|
||||
include_thoughts: true,
|
||||
// -1 means dynamic thinking where model determines.
|
||||
// budget_tokens: 128, // minimum for Gemini Pro is 128
|
||||
budget_tokens: budgetTokens,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -142,6 +142,7 @@ export const UserSettingsSchema = z.object({
|
||||
experiments: ExperimentsSchema.optional(),
|
||||
lastShownReleaseNotesVersion: z.string().optional(),
|
||||
maxChatTurnsInContext: z.number().optional(),
|
||||
thinkingBudget: z.enum(["low", "medium", "high"]).optional(),
|
||||
enableProLazyEditsMode: z.boolean().optional(),
|
||||
enableProSmartFilesContextMode: z.boolean().optional(),
|
||||
selectedTemplateId: z.string().optional(),
|
||||
|
||||
@@ -7,6 +7,7 @@ import { showSuccess, showError } from "@/lib/toast";
|
||||
import { AutoApproveSwitch } from "@/components/AutoApproveSwitch";
|
||||
import { TelemetrySwitch } from "@/components/TelemetrySwitch";
|
||||
import { MaxChatTurnsSelector } from "@/components/MaxChatTurnsSelector";
|
||||
import { ThinkingBudgetSelector } from "@/components/ThinkingBudgetSelector";
|
||||
import { useSettings } from "@/hooks/useSettings";
|
||||
import { useAppVersion } from "@/hooks/useAppVersion";
|
||||
import { Button } from "@/components/ui/button";
|
||||
@@ -138,6 +139,10 @@ export default function SettingsPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-4">
|
||||
<ThinkingBudgetSelector />
|
||||
</div>
|
||||
|
||||
<div className="mt-4">
|
||||
<MaxChatTurnsSelector />
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user