diff --git a/packages/types/src/provider-settings.ts b/packages/types/src/provider-settings.ts index 65e3f9b5b6..10216b84c0 100644 --- a/packages/types/src/provider-settings.ts +++ b/packages/types/src/provider-settings.ts @@ -56,6 +56,7 @@ const baseProviderSettingsSchema = z.object({ diffEnabled: z.boolean().optional(), fuzzyMatchThreshold: z.number().optional(), modelTemperature: z.number().nullish(), + modelSeed: z.number().optional(), rateLimitSeconds: z.number().optional(), // Model reasoning. diff --git a/src/api/providers/base-openai-compatible-provider.ts b/src/api/providers/base-openai-compatible-provider.ts index f196b5f309..256c8672ff 100644 --- a/src/api/providers/base-openai-compatible-provider.ts +++ b/src/api/providers/base-openai-compatible-provider.ts @@ -78,6 +78,7 @@ export abstract class BaseOpenAiCompatibleProvider model, max_tokens, temperature, + seed: this.options.modelSeed, messages: [{ role: "system", content: systemPrompt }, ...convertToOpenAiMessages(messages)], stream: true, stream_options: { include_usage: true }, diff --git a/src/api/providers/openai.ts b/src/api/providers/openai.ts index b4f256f43a..85521a1495 100644 --- a/src/api/providers/openai.ts +++ b/src/api/providers/openai.ts @@ -152,6 +152,7 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl const requestOptions: OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming = { model: modelId, temperature: this.options.modelTemperature ?? (deepseekReasoner ? DEEP_SEEK_DEFAULT_TEMPERATURE : 0), + seed: this.options.modelSeed, messages: convertedMessages, stream: true as const, ...(isGrokXAI ? {} : { stream_options: { include_usage: true } }), diff --git a/src/api/providers/openrouter.ts b/src/api/providers/openrouter.ts index 26019ce34a..ee8dc103dd 100644 --- a/src/api/providers/openrouter.ts +++ b/src/api/providers/openrouter.ts @@ -116,6 +116,7 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH model: modelId, ...(maxTokens && maxTokens > 0 && { max_tokens: maxTokens }), temperature, + seed: this.options.modelSeed, top_p: topP, messages: openAiMessages, stream: true, @@ -219,6 +220,7 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH model: modelId, max_tokens: maxTokens, temperature, + seed: this.options.modelSeed, messages: [{ role: "user", content: prompt }], stream: false, // Only include provider if openRouterSpecificProvider is not "[default]". diff --git a/webview-ui/src/components/settings/ApiOptions.tsx b/webview-ui/src/components/settings/ApiOptions.tsx index 905f34a860..85a8da9715 100644 --- a/webview-ui/src/components/settings/ApiOptions.tsx +++ b/webview-ui/src/components/settings/ApiOptions.tsx @@ -53,6 +53,7 @@ import { DiffSettingsControl } from "./DiffSettingsControl" import { TemperatureControl } from "./TemperatureControl" import { RateLimitSecondsControl } from "./RateLimitSecondsControl" import { BedrockCustomArn } from "./providers/BedrockCustomArn" +import { SeedControl } from "./SeedControl" import { buildDocLink } from "@src/utils/docLinks" export interface ApiOptionsProps { @@ -488,6 +489,10 @@ const ApiOptions = ({ value={apiConfiguration.rateLimitSeconds || 0} onChange={(value) => setApiConfigurationField("rateLimitSeconds", value)} /> + )} diff --git a/webview-ui/src/components/settings/SeedControl.tsx b/webview-ui/src/components/settings/SeedControl.tsx new file mode 100644 index 0000000000..414914832b --- /dev/null +++ b/webview-ui/src/components/settings/SeedControl.tsx @@ -0,0 +1,82 @@ +import { VSCodeCheckbox } from "@vscode/webview-ui-toolkit/react" +import { useEffect, useState } from "react" +import { useAppTranslation } from "@/i18n/TranslationContext" +import { useDebounce } from "react-use" + +interface SeedControlProps { + value: number | undefined | null + onChange: (value: number | undefined) => void +} + +export const SeedControl = ({ value, onChange }: SeedControlProps) => { + const { t } = useAppTranslation() + const [isCustomSeed, setIsCustomSeed] = useState(value !== undefined && value !== null) + const [inputValue, setInputValue] = useState(value?.toString() ?? "") + + useDebounce( + () => { + if (inputValue === "") { + onChange(undefined) + } else { + const numValue = parseInt(inputValue, 10) + if (!isNaN(numValue)) { + onChange(numValue) + } + } + }, + 50, + [onChange, inputValue], + ) + + // Sync internal state with prop changes when switching profiles. + useEffect(() => { + const hasCustomSeed = value !== undefined && value !== null + setIsCustomSeed(hasCustomSeed) + setInputValue(value?.toString() ?? "") + }, [value]) + + const handleCheckboxChange = (e: React.ChangeEvent) => { + const isChecked = e.target.checked + setIsCustomSeed(isChecked) + + if (!isChecked) { + setInputValue("") + } else { + setInputValue(value?.toString() ?? "42") + } + } + + const handleInputChange = (e: React.ChangeEvent) => { + setInputValue(e.target.value) + } + + return ( + <> +
+ { + handleCheckboxChange(e as React.ChangeEvent) + }}> + + +
{t("settings:seed.description")}
+
+ + {isCustomSeed && ( +
+
+ { + handleInputChange(e as React.ChangeEvent) + }} + className="w-full" + /> +
+
+ )} + + ) +} diff --git a/webview-ui/src/components/settings/__tests__/ApiOptions.test.tsx b/webview-ui/src/components/settings/__tests__/ApiOptions.test.tsx index 17421d3960..3d0b6e00bc 100644 --- a/webview-ui/src/components/settings/__tests__/ApiOptions.test.tsx +++ b/webview-ui/src/components/settings/__tests__/ApiOptions.test.tsx @@ -21,6 +21,12 @@ jest.mock("@vscode/webview-ui-toolkit/react", () => ({ VSCodeRadio: ({ value, checked }: any) => , VSCodeRadioGroup: ({ children }: any) =>
{children}
, VSCodeButton: ({ children }: any) =>
{children}
, + VSCodeCheckbox: ({ children, checked, onChange }: any) => ( + + ), })) // Mock other components diff --git a/webview-ui/src/i18n/locales/ca/settings.json b/webview-ui/src/i18n/locales/ca/settings.json index c88005ea61..71451a2cc9 100644 --- a/webview-ui/src/i18n/locales/ca/settings.json +++ b/webview-ui/src/i18n/locales/ca/settings.json @@ -519,6 +519,11 @@ "description": "Controla l'aleatorietat en les respostes del model.", "rangeDescription": "Valors més alts fan que la sortida sigui més aleatòria, valors més baixos la fan més determinista." }, + "seed": { + "useCustom": "Utilitzar llavor personalitzada", + "description": "Controla la naturalesa determinista de les respostes del model. Deixeu en blanc per a aleatori.", + "label": "Introduïu un valor de llavor" + }, "modelInfo": { "supportsImages": "Suporta imatges", "noImages": "No suporta imatges", diff --git a/webview-ui/src/i18n/locales/de/settings.json b/webview-ui/src/i18n/locales/de/settings.json index 27a7486436..a2c842f512 100644 --- a/webview-ui/src/i18n/locales/de/settings.json +++ b/webview-ui/src/i18n/locales/de/settings.json @@ -519,6 +519,11 @@ "description": "Steuert die Zufälligkeit in den Antworten des Modells.", "rangeDescription": "Höhere Werte machen die Ausgabe zufälliger, niedrigere Werte machen sie deterministischer." }, + "seed": { + "useCustom": "Benutzerdefinierten Seed verwenden", + "description": "Steuert die deterministische Natur der Antworten des Modells. Für zufällig leer lassen.", + "label": "Einen Seed-Wert eingeben" + }, "modelInfo": { "supportsImages": "Unterstützt Bilder", "noImages": "Unterstützt keine Bilder", diff --git a/webview-ui/src/i18n/locales/en/settings.json b/webview-ui/src/i18n/locales/en/settings.json index b8e51afc50..2eca916034 100644 --- a/webview-ui/src/i18n/locales/en/settings.json +++ b/webview-ui/src/i18n/locales/en/settings.json @@ -519,6 +519,11 @@ "description": "Controls randomness in the model's responses.", "rangeDescription": "Higher values make output more random, lower values make it more deterministic." }, + "seed": { + "useCustom": "Use custom seed", + "description": "Controls the deterministic nature of the model's responses. Leave blank for random.", + "label": "Enter a seed value" + }, "modelInfo": { "supportsImages": "Supports images", "noImages": "Does not support images", diff --git a/webview-ui/src/i18n/locales/es/settings.json b/webview-ui/src/i18n/locales/es/settings.json index db8b4736eb..bc04cd62a9 100644 --- a/webview-ui/src/i18n/locales/es/settings.json +++ b/webview-ui/src/i18n/locales/es/settings.json @@ -519,6 +519,11 @@ "description": "Controla la aleatoriedad en las respuestas del modelo.", "rangeDescription": "Valores más altos hacen que la salida sea más aleatoria, valores más bajos la hacen más determinista." }, + "seed": { + "useCustom": "Usar semilla personalizada", + "description": "Controla la naturaleza determinista de las respuestas del modelo. Dejar en blanco para aleatorio.", + "label": "Introducir un valor de semilla" + }, "modelInfo": { "supportsImages": "Soporta imágenes", "noImages": "No soporta imágenes", diff --git a/webview-ui/src/i18n/locales/fr/settings.json b/webview-ui/src/i18n/locales/fr/settings.json index 0bf837accb..89175f6c2a 100644 --- a/webview-ui/src/i18n/locales/fr/settings.json +++ b/webview-ui/src/i18n/locales/fr/settings.json @@ -519,6 +519,11 @@ "description": "Contrôle l'aléatoire dans les réponses du modèle.", "rangeDescription": "Des valeurs plus élevées rendent la sortie plus aléatoire, des valeurs plus basses la rendent plus déterministe." }, + "seed": { + "useCustom": "Utiliser une graine personnalisée", + "description": "Contrôle la nature déterministe des réponses du modèle. Laisser vide pour aléatoire.", + "label": "Entrez une valeur de graine" + }, "modelInfo": { "supportsImages": "Prend en charge les images", "noImages": "Ne prend pas en charge les images", diff --git a/webview-ui/src/i18n/locales/hi/settings.json b/webview-ui/src/i18n/locales/hi/settings.json index fec1b27007..163c41f2c8 100644 --- a/webview-ui/src/i18n/locales/hi/settings.json +++ b/webview-ui/src/i18n/locales/hi/settings.json @@ -519,6 +519,11 @@ "description": "मॉडल की प्रतिक्रियाओं में यादृच्छिकता को नियंत्रित करता है।", "rangeDescription": "उच्च मान आउटपुट को अधिक यादृच्छिक बनाते हैं, निम्न मान इसे अधिक निर्धारित बनाते हैं।" }, + "seed": { + "useCustom": "कस्टम सीड का प्रयोग करें", + "description": "मॉडल की प्रतिक्रियाओं की déterministe प्रकृति को नियंत्रित करता है। यादृच्छिक के लिए खाली छोड़ दें।", + "label": "एक सीड मान दर्ज करें" + }, "modelInfo": { "supportsImages": "छवियों का समर्थन करता है", "noImages": "छवियों का समर्थन नहीं करता है", diff --git a/webview-ui/src/i18n/locales/it/settings.json b/webview-ui/src/i18n/locales/it/settings.json index fcb389a4a7..823b072a9e 100644 --- a/webview-ui/src/i18n/locales/it/settings.json +++ b/webview-ui/src/i18n/locales/it/settings.json @@ -519,6 +519,11 @@ "description": "Controlla la casualità nelle risposte del modello.", "rangeDescription": "Valori più alti rendono l'output più casuale, valori più bassi lo rendono più deterministico." }, + "seed": { + "useCustom": "Usa seed personalizzato", + "description": "Controlla la natura deterministica delle risposte del modello. Lasciare vuoto per casuale.", + "label": "Inserisci un valore di seed" + }, "modelInfo": { "supportsImages": "Supporta immagini", "noImages": "Non supporta immagini", diff --git a/webview-ui/src/i18n/locales/ja/settings.json b/webview-ui/src/i18n/locales/ja/settings.json index eabd751308..6faaab3edb 100644 --- a/webview-ui/src/i18n/locales/ja/settings.json +++ b/webview-ui/src/i18n/locales/ja/settings.json @@ -519,6 +519,11 @@ "description": "モデルの応答のランダム性を制御します。", "rangeDescription": "高い値は出力をよりランダムに、低い値はより決定論的にします。" }, + "seed": { + "useCustom": "カスタムシードを使用", + "description": "モデルの応答の決定性を制御します。ランダムの場合は空白のままにします。", + "label": "シード値を入力してください" + }, "modelInfo": { "supportsImages": "画像をサポート", "noImages": "画像をサポートしていません", diff --git a/webview-ui/src/i18n/locales/ko/settings.json b/webview-ui/src/i18n/locales/ko/settings.json index 68ca2a963c..6276746971 100644 --- a/webview-ui/src/i18n/locales/ko/settings.json +++ b/webview-ui/src/i18n/locales/ko/settings.json @@ -519,6 +519,11 @@ "description": "모델 응답의 무작위성을 제어합니다.", "rangeDescription": "높은 값은 출력을 더 무작위하게, 낮은 값은 더 결정적으로 만듭니다." }, + "seed": { + "useCustom": "사용자 지정 시드 사용", + "description": "모델 응답의 결정적 특성을 제어합니다. 무작위의 경우 비워 둡니다.", + "label": "시드 값 입력" + }, "modelInfo": { "supportsImages": "이미지 지원", "noImages": "이미지 지원 안 함", diff --git a/webview-ui/src/i18n/locales/nl/settings.json b/webview-ui/src/i18n/locales/nl/settings.json index 996c0c673c..6548b35b47 100644 --- a/webview-ui/src/i18n/locales/nl/settings.json +++ b/webview-ui/src/i18n/locales/nl/settings.json @@ -519,6 +519,11 @@ "description": "Bepaalt de willekeurigheid in de antwoorden van het model.", "rangeDescription": "Hogere waarden maken de output willekeuriger, lagere waarden maken deze deterministischer." }, + "seed": { + "useCustom": "Aangepaste seed gebruiken", + "description": "Bepaalt de deterministische aard van de antwoorden van het model. Laat leeg voor willekeurig.", + "label": "Voer een seed-waarde in" + }, "modelInfo": { "supportsImages": "Ondersteunt afbeeldingen", "noImages": "Ondersteunt geen afbeeldingen", diff --git a/webview-ui/src/i18n/locales/pl/settings.json b/webview-ui/src/i18n/locales/pl/settings.json index cf4421e00e..f13890c644 100644 --- a/webview-ui/src/i18n/locales/pl/settings.json +++ b/webview-ui/src/i18n/locales/pl/settings.json @@ -519,6 +519,11 @@ "description": "Kontroluje losowość w odpowiedziach modelu.", "rangeDescription": "Wyższe wartości sprawiają, że wyjście jest bardziej losowe, niższe wartości czynią je bardziej deterministycznym." }, + "seed": { + "useCustom": "Użyj niestandardowego ziarna", + "description": "Kontroluje deterministyczną naturę odpowiedzi modelu. Pozostaw puste dla losowości.", + "label": "Wprowadź wartość ziarna" + }, "modelInfo": { "supportsImages": "Obsługuje obrazy", "noImages": "Nie obsługuje obrazów", diff --git a/webview-ui/src/i18n/locales/pt-BR/settings.json b/webview-ui/src/i18n/locales/pt-BR/settings.json index 229419dd23..86045e7d67 100644 --- a/webview-ui/src/i18n/locales/pt-BR/settings.json +++ b/webview-ui/src/i18n/locales/pt-BR/settings.json @@ -519,6 +519,11 @@ "description": "Controla a aleatoriedade nas respostas do modelo.", "rangeDescription": "Valores mais altos tornam a saída mais aleatória, valores mais baixos a tornam mais determinística." }, + "seed": { + "useCustom": "Usar semente personalizada", + "description": "Controla a natureza determinística das respostas do modelo. Deixe em branco para aleatório.", + "label": "Insira um valor de semente" + }, "modelInfo": { "supportsImages": "Suporta imagens", "noImages": "Não suporta imagens", diff --git a/webview-ui/src/i18n/locales/ru/settings.json b/webview-ui/src/i18n/locales/ru/settings.json index dcce5e5b1a..0b4d44bcd3 100644 --- a/webview-ui/src/i18n/locales/ru/settings.json +++ b/webview-ui/src/i18n/locales/ru/settings.json @@ -519,6 +519,11 @@ "description": "Управляет случайностью ответов модели.", "rangeDescription": "Более высокие значения делают ответы более случайными, низкие — более детерминированными." }, + "seed": { + "useCustom": "Использовать пользовательское начальное значение", + "description": "Контролирует детерминированность ответов модели. Оставьте пустым для случайного значения.", + "label": "Введите начальное значение" + }, "modelInfo": { "supportsImages": "Поддерживает изображения", "noImages": "Не поддерживает изображения", diff --git a/webview-ui/src/i18n/locales/tr/settings.json b/webview-ui/src/i18n/locales/tr/settings.json index f8f53ae21c..99dbf4c17e 100644 --- a/webview-ui/src/i18n/locales/tr/settings.json +++ b/webview-ui/src/i18n/locales/tr/settings.json @@ -519,6 +519,11 @@ "description": "Model yanıtlarındaki rastgeleliği kontrol eder.", "rangeDescription": "Daha yüksek değerler çıktıyı daha rastgele yapar, daha düşük değerler daha deterministik hale getirir." }, + "seed": { + "useCustom": "Özel tohum kullan", + "description": "Modelin yanıtlarının deterministik doğasını kontrol eder. Rastgele için boş bırakın.", + "label": "Bir tohum değeri girin" + }, "modelInfo": { "supportsImages": "Görüntüleri destekler", "noImages": "Görüntüleri desteklemez", diff --git a/webview-ui/src/i18n/locales/vi/settings.json b/webview-ui/src/i18n/locales/vi/settings.json index edb2b386b2..cfd3a004f3 100644 --- a/webview-ui/src/i18n/locales/vi/settings.json +++ b/webview-ui/src/i18n/locales/vi/settings.json @@ -519,6 +519,11 @@ "description": "Kiểm soát tính ngẫu nhiên trong phản hồi của mô hình.", "rangeDescription": "Giá trị cao hơn làm cho đầu ra ngẫu nhiên hơn, giá trị thấp hơn làm cho nó xác định hơn." }, + "seed": { + "useCustom": "Sử dụng hạt giống tùy chỉnh", + "description": "Kiểm soát tính xác định của các phản hồi của mô hình. Để trống cho ngẫu nhiên.", + "label": "Nhập giá trị hạt giống" + }, "modelInfo": { "supportsImages": "Hỗ trợ hình ảnh", "noImages": "Không hỗ trợ hình ảnh", diff --git a/webview-ui/src/i18n/locales/zh-CN/settings.json b/webview-ui/src/i18n/locales/zh-CN/settings.json index 51ae2269e4..d2ec046f85 100644 --- a/webview-ui/src/i18n/locales/zh-CN/settings.json +++ b/webview-ui/src/i18n/locales/zh-CN/settings.json @@ -519,6 +519,11 @@ "description": "控制模型响应的随机性", "rangeDescription": "值越高回答越多样,值越低越保守" }, + "seed": { + "useCustom": "使用自定义种子", + "description": "控制模型响应的确定性。留空则随机。", + "label": "输入种子值" + }, "modelInfo": { "supportsImages": "支持图像", "noImages": "不支持图像", diff --git a/webview-ui/src/i18n/locales/zh-TW/settings.json b/webview-ui/src/i18n/locales/zh-TW/settings.json index 07544879cd..683a98e15d 100644 --- a/webview-ui/src/i18n/locales/zh-TW/settings.json +++ b/webview-ui/src/i18n/locales/zh-TW/settings.json @@ -519,6 +519,11 @@ "description": "控制模型回應的隨機性", "rangeDescription": "較高值使輸出更隨機,較低值更確定" }, + "seed": { + "useCustom": "使用自訂種子", + "description": "控制模型回應的確定性。留空則隨機。", + "label": "輸入種子值" + }, "modelInfo": { "supportsImages": "支援影像", "noImages": "不支援影像",