From baedfdc2bd9ff7c8dbe89ed444434bdc1ec0341e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=AAMurtaza=20Akbari=E2=80=AC=E2=80=8F?= Date: Mon, 7 Sep 2026 22:27:24 +0800 Subject: [PATCH] feat: new models added and custom model code entry for all AI providers --- public/locales/de/translation.json | 11 +- public/locales/en/translation.json | 61 ++++++++-- public/locales/es/translation.json | 11 +- public/locales/fr/translation.json | 11 +- public/locales/ja/translation.json | 11 +- public/locales/tr/translation.json | 11 +- public/locales/zh/translation.json | 11 +- src/components/SettingsModal/AISettings.tsx | 117 +++++++++++++++++--- src/config/aiProviders.ts | 8 +- src/i18n/locales/de/translation.json | 11 +- src/i18n/locales/en/translation.json | 61 ++++++++-- src/i18n/locales/es/translation.json | 11 +- src/i18n/locales/fr/translation.json | 11 +- src/i18n/locales/ja/translation.json | 11 +- src/i18n/locales/tr/translation.json | 11 +- src/i18n/locales/zh/translation.json | 11 +- 16 files changed, 333 insertions(+), 46 deletions(-) diff --git a/public/locales/de/translation.json b/public/locales/de/translation.json index dd3b6117..f9708339 100644 --- a/public/locales/de/translation.json +++ b/public/locales/de/translation.json @@ -1161,7 +1161,16 @@ "control": "Full control over cost and rate limits", "flexibility": "Switch providers anytime without re-linking", "cuttingEdge": "Access cutting-edge models as soon as they launch" - } + }, + "customModelRequired": "Model ID is required", + "chooseFromList": "Select from presets", + "enterModelCode": "Enter model code", + "customModelPlaceholder": "e.g. {{defaultModel}} or custom model code", + "providerModelHint": "Enter any valid model ID for {{provider}} (e.g. fine-tuned, preview, or newly released models).", + "presetsLabel": "Presets:", + "enterCustomModelOption": "✏️ Enter custom model code...", + "enterCustomModelOptionHint": "Type any custom or fine-tuned model ID", + "customGroup": "Custom" }, "brand": { "title": "Marken-Kits", diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index b0b7559d..9c35bd98 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -983,6 +983,15 @@ "keySetupNote": "Key Setup Note", "enterApiKey": "Enter your API key", "selectModel": "Select a model", + "enterModelCode": "Enter model code", + "chooseFromList": "Select from presets", + "customModelRequired": "Model ID is required", + "customModelPlaceholder": "e.g. {{defaultModel}} or custom model code", + "providerModelHint": "Enter any valid model ID for {{provider}} (e.g. fine-tuned, preview, or newly released models).", + "presetsLabel": "Presets:", + "enterCustomModelOption": "✏️ Enter custom model code...", + "enterCustomModelOptionHint": "Type any custom or fine-tuned model ID", + "customGroup": "Custom", "customEndpoint": "Custom Base URL (e.g., https://api.example.com)", "providers": { "gemini": { @@ -1019,32 +1028,66 @@ }, "models": { "gemini": { + "gemini-2.5-flash": { + "label": "2.5 Flash", + "hint": "Best price/performance balance", + "category": "Speed", + "badge": "Fast" + }, + "gemini-2.5-pro": { + "label": "2.5 Pro", + "hint": "Best reasoning · Complex diagrams", + "category": "Reasoning", + "badge": "Flagship" + }, "gemini-2.5-flash-lite": { "label": "2.5 Flash Lite", "hint": "Fastest · Free tier default", "category": "Speed", "badge": "Default" }, - "gemini-2.5-flash": { - "label": "2.5 Flash", - "hint": "Best price/performance balance", + "gemini-2.0-flash": { + "label": "2.0 Flash", + "hint": "Next-gen multimodal · Low latency", "category": "Speed" }, - "gemini-2.5-pro": { - "label": "2.5 Pro", - "hint": "Best reasoning · Complex diagrams", - "category": "Reasoning" + "gemini-2.0-flash-lite": { + "label": "2.0 Flash Lite", + "hint": "High throughput · Cost efficient", + "category": "Speed" + }, + "gemini-2.0-flash-thinking-exp-01-21": { + "label": "2.0 Flash Thinking", + "hint": "Thinking process · Complex logic", + "category": "Reasoning", + "badge": "Thinking" + }, + "gemini-2.0-pro-exp-02-05": { + "label": "2.0 Pro Experimental", + "hint": "Coding & advanced reasoning", + "category": "Reasoning", + "badge": "Experimental" + }, + "gemini-1.5-pro": { + "label": "1.5 Pro", + "hint": "Long context · High capability", + "category": "Legacy" + }, + "gemini-1.5-flash": { + "label": "1.5 Flash", + "hint": "Fast and versatile", + "category": "Legacy" }, "gemini-3-flash": { "label": "3 Flash", "hint": "Frontier speed + intelligence", - "category": "Legacy", + "category": "Preview", "badge": "New" }, "gemini-3-pro": { "label": "3 Pro", "hint": "Most powerful · Multimodal", - "category": "Legacy", + "category": "Preview", "badge": "New" } }, diff --git a/public/locales/es/translation.json b/public/locales/es/translation.json index 160d5730..416dd19f 100644 --- a/public/locales/es/translation.json +++ b/public/locales/es/translation.json @@ -1161,7 +1161,16 @@ "control": "Full control over cost and rate limits", "flexibility": "Switch providers anytime without re-linking", "cuttingEdge": "Access cutting-edge models as soon as they launch" - } + }, + "customModelRequired": "Model ID is required", + "chooseFromList": "Select from presets", + "enterModelCode": "Enter model code", + "customModelPlaceholder": "e.g. {{defaultModel}} or custom model code", + "providerModelHint": "Enter any valid model ID for {{provider}} (e.g. fine-tuned, preview, or newly released models).", + "presetsLabel": "Presets:", + "enterCustomModelOption": "✏️ Enter custom model code...", + "enterCustomModelOptionHint": "Type any custom or fine-tuned model ID", + "customGroup": "Custom" }, "brand": { "title": "Kits de marca", diff --git a/public/locales/fr/translation.json b/public/locales/fr/translation.json index 2b2b240d..66e8f824 100644 --- a/public/locales/fr/translation.json +++ b/public/locales/fr/translation.json @@ -1161,7 +1161,16 @@ "control": "Full control over cost and rate limits", "flexibility": "Switch providers anytime without re-linking", "cuttingEdge": "Access cutting-edge models as soon as they launch" - } + }, + "customModelRequired": "Model ID is required", + "chooseFromList": "Select from presets", + "enterModelCode": "Enter model code", + "customModelPlaceholder": "e.g. {{defaultModel}} or custom model code", + "providerModelHint": "Enter any valid model ID for {{provider}} (e.g. fine-tuned, preview, or newly released models).", + "presetsLabel": "Presets:", + "enterCustomModelOption": "✏️ Enter custom model code...", + "enterCustomModelOptionHint": "Type any custom or fine-tuned model ID", + "customGroup": "Custom" }, "brand": { "title": "Kits de marque", diff --git a/public/locales/ja/translation.json b/public/locales/ja/translation.json index 23d26220..43b2977a 100644 --- a/public/locales/ja/translation.json +++ b/public/locales/ja/translation.json @@ -1161,7 +1161,16 @@ "control": "Full control over cost and rate limits", "flexibility": "Switch providers anytime without re-linking", "cuttingEdge": "Access cutting-edge models as soon as they launch" - } + }, + "customModelRequired": "Model ID is required", + "chooseFromList": "Select from presets", + "enterModelCode": "Enter model code", + "customModelPlaceholder": "e.g. {{defaultModel}} or custom model code", + "providerModelHint": "Enter any valid model ID for {{provider}} (e.g. fine-tuned, preview, or newly released models).", + "presetsLabel": "Presets:", + "enterCustomModelOption": "✏️ Enter custom model code...", + "enterCustomModelOptionHint": "Type any custom or fine-tuned model ID", + "customGroup": "Custom" }, "brand": { "title": "ブランドキット", diff --git a/public/locales/tr/translation.json b/public/locales/tr/translation.json index 8155085e..d7cf1443 100644 --- a/public/locales/tr/translation.json +++ b/public/locales/tr/translation.json @@ -1181,7 +1181,16 @@ "browserFriendly": "Tarayıcı uyumlu", "proxyLikely": "Muhtemelen vekil sunucu gerekli", "mixed": "Uç noktasına bağlı" - } + }, + "customModelRequired": "Model ID is required", + "chooseFromList": "Select from presets", + "enterModelCode": "Enter model code", + "customModelPlaceholder": "e.g. {{defaultModel}} or custom model code", + "providerModelHint": "Enter any valid model ID for {{provider}} (e.g. fine-tuned, preview, or newly released models).", + "presetsLabel": "Presets:", + "enterCustomModelOption": "✏️ Enter custom model code...", + "enterCustomModelOptionHint": "Type any custom or fine-tuned model ID", + "customGroup": "Custom" }, "brand": { "title": "Marka Kitleri", diff --git a/public/locales/zh/translation.json b/public/locales/zh/translation.json index cd9e2c7d..7e7449d4 100644 --- a/public/locales/zh/translation.json +++ b/public/locales/zh/translation.json @@ -1161,7 +1161,16 @@ "control": "Full control over cost and rate limits", "flexibility": "Switch providers anytime without re-linking", "cuttingEdge": "Access cutting-edge models as soon as they launch" - } + }, + "customModelRequired": "Model ID is required", + "chooseFromList": "Select from presets", + "enterModelCode": "Enter model code", + "customModelPlaceholder": "e.g. {{defaultModel}} or custom model code", + "providerModelHint": "Enter any valid model ID for {{provider}} (e.g. fine-tuned, preview, or newly released models).", + "presetsLabel": "Presets:", + "enterCustomModelOption": "✏️ Enter custom model code...", + "enterCustomModelOptionHint": "Type any custom or fine-tuned model ID", + "customGroup": "Custom" }, "brand": { "title": "品牌套件", diff --git a/src/components/SettingsModal/AISettings.tsx b/src/components/SettingsModal/AISettings.tsx index 1482ac7c..cb8804d5 100644 --- a/src/components/SettingsModal/AISettings.tsx +++ b/src/components/SettingsModal/AISettings.tsx @@ -3,7 +3,7 @@ import { useFlowStore, AIProvider, AISettingsStorageMode } from '../../store'; import { Input } from '../ui/Input'; import { Label } from '../ui/Label'; import { Select } from '../ui/Select'; -import { ExternalLink, Check, Shield, Lock, AlertCircle, Info } from 'lucide-react'; +import { ExternalLink, Check, Shield, Lock, AlertCircle, Info, Pencil, List } from 'lucide-react'; import { useTranslation, Trans } from 'react-i18next'; import { BYOK_KEYS, @@ -96,6 +96,10 @@ export function AISettings(): React.ReactElement { const providerMeta = PROVIDERS.find(p => p.id === currentProvider) ?? PROVIDERS[0]; const models = PROVIDER_MODELS[currentProvider] ?? []; const currentModel = aiSettings.model ?? providerMeta.defaultModel; + const isPresetModel = models.some(m => m.id === currentModel); + const [isCustomModelMode, setIsCustomModelMode] = React.useState( + currentProvider === 'custom' || (!isPresetModel && Boolean(aiSettings.model)) + ); const readiness = getAIReadinessState(aiSettings); const providerRisk = getProviderRiskPresentation(currentProvider); const providerRiskIcon = providerRisk.tone === 'warning' ? AlertCircle : Info; @@ -108,8 +112,8 @@ export function AISettings(): React.ReactElement { const customBaseUrlError = currentProvider === 'custom' && readiness.blockingIssue?.detail.includes('base URL') ? readiness.blockingIssue.detail : undefined; - const customModelError = currentProvider === 'custom' && readiness.blockingIssue?.detail.includes('model ID') - ? readiness.blockingIssue.detail + const customModelError = (currentProvider === 'custom' || isCustomModelMode) && (!aiSettings.model || aiSettings.model.trim().length === 0) + ? t('settingsModal.ai.customModelRequired') : undefined; const storageMode = aiSettings.storageMode ?? 'local'; @@ -120,6 +124,18 @@ export function AISettings(): React.ReactElement { function selectProvider(id: AIProvider): void { const meta = PROVIDERS.find(p => p.id === id)!; setAISettings({ provider: id, model: meta.defaultModel }); + setIsCustomModelMode(id === 'custom'); + } + + function toggleCustomModelMode(): void { + if (isCustomModelMode) { + setIsCustomModelMode(false); + if (!isPresetModel) { + setAISettings({ model: providerMeta.defaultModel }); + } + } else { + setIsCustomModelMode(true); + } } return ( @@ -206,28 +222,99 @@ export function AISettings(): React.ReactElement {
{/* Model Selector */}
- - {currentProvider === 'custom' ? ( +
+ + {currentProvider !== 'custom' && ( + + )} +
+ + {currentProvider === 'custom' || isCustomModelMode ? (
setAISettings({ model: e.target.value })} - placeholder="e.g. llama3-70b-8192 or gpt-4o" + placeholder={ + currentProvider === 'custom' + ? 'e.g. llama3-70b-8192 or gpt-4o' + : t('settingsModal.ai.customModelPlaceholder', { + defaultValue: `e.g. ${providerMeta.defaultModel} or custom model code`, + defaultModel: providerMeta.defaultModel, + }) + } error={customModelError} - helperText={t('settingsModal.ai.customModelHint')} + helperText={ + currentProvider === 'custom' + ? t('settingsModal.ai.customModelHint') + : t('settingsModal.ai.providerModelHint', { + defaultValue: `Enter any model code supported by ${providerMeta.name} (e.g. fine-tuned or newly released models).`, + provider: providerMeta.name, + }) + } /> + {currentProvider !== 'custom' && models.length > 0 && ( +
+ + {t('settingsModal.ai.presetsLabel', { defaultValue: 'Presets:' })} + + {models.slice(0, 5).map(m => ( + + ))} +
+ )}
) : (