feat(model): opt model provider doc url (#475)
This commit is contained in:
@@ -31,6 +31,7 @@ import {
|
|||||||
} from '@/stores/providers';
|
} from '@/stores/providers';
|
||||||
import {
|
import {
|
||||||
PROVIDER_TYPE_INFO,
|
PROVIDER_TYPE_INFO,
|
||||||
|
getProviderDocsUrl,
|
||||||
type ProviderType,
|
type ProviderType,
|
||||||
getProviderIconUrl,
|
getProviderIconUrl,
|
||||||
resolveProviderApiKeyForSave,
|
resolveProviderApiKeyForSave,
|
||||||
@@ -318,6 +319,7 @@ function ProviderCard({
|
|||||||
const [saving, setSaving] = useState(false);
|
const [saving, setSaving] = useState(false);
|
||||||
|
|
||||||
const typeInfo = PROVIDER_TYPE_INFO.find((t) => t.id === account.vendorId);
|
const typeInfo = PROVIDER_TYPE_INFO.find((t) => t.id === account.vendorId);
|
||||||
|
const providerDocsUrl = getProviderDocsUrl(typeInfo, i18n.language);
|
||||||
const showModelIdField = shouldShowProviderModelId(typeInfo, devModeUnlocked);
|
const showModelIdField = shouldShowProviderModelId(typeInfo, devModeUnlocked);
|
||||||
const canEditModelConfig = Boolean(typeInfo?.showBaseUrl || showModelIdField);
|
const canEditModelConfig = Boolean(typeInfo?.showBaseUrl || showModelIdField);
|
||||||
|
|
||||||
@@ -522,12 +524,10 @@ function ProviderCard({
|
|||||||
|
|
||||||
{isEditing && (
|
{isEditing && (
|
||||||
<div className="space-y-6 mt-4 pt-4 border-t border-black/5 dark:border-white/5">
|
<div className="space-y-6 mt-4 pt-4 border-t border-black/5 dark:border-white/5">
|
||||||
{account.vendorId === 'custom' && (
|
{providerDocsUrl && (
|
||||||
<div className="flex justify-end -mt-2 mb-2">
|
<div className="flex justify-end -mt-2 mb-2">
|
||||||
<a
|
<a
|
||||||
href={i18n.language.startsWith('zh')
|
href={providerDocsUrl}
|
||||||
? 'https://icnnp7d0dymg.feishu.cn/wiki/BmiLwGBcEiloZDkdYnGc8RWnn6d#IWQCdfe5fobGU3xf3UGcgbLynGh'
|
|
||||||
: 'https://icnnp7d0dymg.feishu.cn/wiki/BmiLwGBcEiloZDkdYnGc8RWnn6d#Ee1ldfvKJoVGvfxc32mcILwenth'}
|
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="text-[12px] text-blue-500 hover:text-blue-600 font-medium inline-flex items-center gap-1"
|
className="text-[12px] text-blue-500 hover:text-blue-600 font-medium inline-flex items-center gap-1"
|
||||||
@@ -799,6 +799,7 @@ function AddProviderDialog({
|
|||||||
const [authMode, setAuthMode] = useState<'oauth' | 'apikey'>('apikey');
|
const [authMode, setAuthMode] = useState<'oauth' | 'apikey'>('apikey');
|
||||||
|
|
||||||
const typeInfo = PROVIDER_TYPE_INFO.find((t) => t.id === selectedType);
|
const typeInfo = PROVIDER_TYPE_INFO.find((t) => t.id === selectedType);
|
||||||
|
const providerDocsUrl = getProviderDocsUrl(typeInfo, i18n.language);
|
||||||
const showModelIdField = shouldShowProviderModelId(typeInfo, devModeUnlocked);
|
const showModelIdField = shouldShowProviderModelId(typeInfo, devModeUnlocked);
|
||||||
const isOAuth = typeInfo?.isOAuth ?? false;
|
const isOAuth = typeInfo?.isOAuth ?? false;
|
||||||
const supportsApiKey = typeInfo?.supportsApiKey ?? false;
|
const supportsApiKey = typeInfo?.supportsApiKey ?? false;
|
||||||
@@ -1092,13 +1093,11 @@ function AddProviderDialog({
|
|||||||
>
|
>
|
||||||
{t('aiProviders.dialog.change')}
|
{t('aiProviders.dialog.change')}
|
||||||
</button>
|
</button>
|
||||||
{selectedType === 'custom' && (
|
{providerDocsUrl && (
|
||||||
<>
|
<>
|
||||||
<span className="mx-2 text-foreground/20">|</span>
|
<span className="mx-2 text-foreground/20">|</span>
|
||||||
<a
|
<a
|
||||||
href={i18n.language.startsWith('zh')
|
href={providerDocsUrl}
|
||||||
? 'https://icnnp7d0dymg.feishu.cn/wiki/BmiLwGBcEiloZDkdYnGc8RWnn6d#IWQCdfe5fobGU3xf3UGcgbLynGh'
|
|
||||||
: 'https://icnnp7d0dymg.feishu.cn/wiki/BmiLwGBcEiloZDkdYnGc8RWnn6d#Ee1ldfvKJoVGvfxc32mcILwenth'}
|
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="text-[13px] text-blue-500 hover:text-blue-600 font-medium inline-flex items-center gap-1"
|
className="text-[13px] text-blue-500 hover:text-blue-600 font-medium inline-flex items-center gap-1"
|
||||||
|
|||||||
@@ -73,6 +73,8 @@ export interface ProviderTypeInfo {
|
|||||||
isOAuth?: boolean;
|
isOAuth?: boolean;
|
||||||
supportsApiKey?: boolean;
|
supportsApiKey?: boolean;
|
||||||
apiKeyUrl?: string;
|
apiKeyUrl?: string;
|
||||||
|
docsUrl?: string;
|
||||||
|
docsUrlZh?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProviderAuthMode =
|
export type ProviderAuthMode =
|
||||||
@@ -121,7 +123,15 @@ import { providerIcons } from '@/assets/providers';
|
|||||||
|
|
||||||
/** All supported provider types with UI metadata */
|
/** All supported provider types with UI metadata */
|
||||||
export const PROVIDER_TYPE_INFO: ProviderTypeInfo[] = [
|
export const PROVIDER_TYPE_INFO: ProviderTypeInfo[] = [
|
||||||
{ id: 'anthropic', name: 'Anthropic', icon: '🤖', placeholder: 'sk-ant-api03-...', model: 'Claude', requiresApiKey: true },
|
{
|
||||||
|
id: 'anthropic',
|
||||||
|
name: 'Anthropic',
|
||||||
|
icon: '🤖',
|
||||||
|
placeholder: 'sk-ant-api03-...',
|
||||||
|
model: 'Claude',
|
||||||
|
requiresApiKey: true,
|
||||||
|
docsUrl: 'https://platform.claude.com/docs/en/api/overview',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'openai',
|
id: 'openai',
|
||||||
name: 'OpenAI',
|
name: 'OpenAI',
|
||||||
@@ -145,15 +155,26 @@ export const PROVIDER_TYPE_INFO: ProviderTypeInfo[] = [
|
|||||||
defaultModelId: 'gemini-3.1-pro-preview',
|
defaultModelId: 'gemini-3.1-pro-preview',
|
||||||
apiKeyUrl: 'https://aistudio.google.com/app/apikey',
|
apiKeyUrl: 'https://aistudio.google.com/app/apikey',
|
||||||
},
|
},
|
||||||
{ id: 'openrouter', name: 'OpenRouter', icon: '🌐', placeholder: 'sk-or-v1-...', model: 'Multi-Model', requiresApiKey: true, showModelId: true, modelIdPlaceholder: 'anthropic/claude-opus-4.6', defaultModelId: 'anthropic/claude-opus-4.6' },
|
{ id: 'openrouter', name: 'OpenRouter', icon: '🌐', placeholder: 'sk-or-v1-...', model: 'Multi-Model', requiresApiKey: true, showModelId: true, modelIdPlaceholder: 'openai/gpt-5.4', defaultModelId: 'openai/gpt-5.4', docsUrl: 'https://openrouter.ai/models' },
|
||||||
{ id: 'ark', name: 'ByteDance Ark', icon: 'A', placeholder: 'your-ark-api-key', model: 'Doubao', requiresApiKey: true, defaultBaseUrl: 'https://ark.cn-beijing.volces.com/api/v3', showBaseUrl: true, showModelId: true, modelIdPlaceholder: 'ep-20260228000000-xxxxx' },
|
|
||||||
{ id: 'moonshot', name: 'Moonshot (CN)', icon: '🌙', placeholder: 'sk-...', model: 'Kimi', requiresApiKey: true, defaultBaseUrl: 'https://api.moonshot.cn/v1', defaultModelId: 'kimi-k2.5' },
|
|
||||||
{ id: 'siliconflow', name: 'SiliconFlow (CN)', icon: '🌊', placeholder: 'sk-...', model: 'Multi-Model', requiresApiKey: true, defaultBaseUrl: 'https://api.siliconflow.cn/v1', showModelId: true, modelIdPlaceholder: 'deepseek-ai/DeepSeek-V3', defaultModelId: 'deepseek-ai/DeepSeek-V3' },
|
|
||||||
{ id: 'minimax-portal', name: 'MiniMax (Global)', icon: '☁️', placeholder: 'sk-...', model: 'MiniMax', requiresApiKey: false, isOAuth: true, supportsApiKey: true, defaultModelId: 'MiniMax-M2.5', apiKeyUrl: 'https://intl.minimaxi.com/' },
|
|
||||||
{ id: 'minimax-portal-cn', name: 'MiniMax (CN)', icon: '☁️', placeholder: 'sk-...', model: 'MiniMax', requiresApiKey: false, isOAuth: true, supportsApiKey: true, defaultModelId: 'MiniMax-M2.5', apiKeyUrl: 'https://platform.minimaxi.com/' },
|
{ id: 'minimax-portal-cn', name: 'MiniMax (CN)', icon: '☁️', placeholder: 'sk-...', model: 'MiniMax', requiresApiKey: false, isOAuth: true, supportsApiKey: true, defaultModelId: 'MiniMax-M2.5', apiKeyUrl: 'https://platform.minimaxi.com/' },
|
||||||
{ id: 'qwen-portal', name: 'Qwen', icon: '☁️', placeholder: 'sk-...', model: 'Qwen', requiresApiKey: false, isOAuth: true, defaultModelId: 'coder-model' },
|
{ id: 'moonshot', name: 'Moonshot (CN)', icon: '🌙', placeholder: 'sk-...', model: 'Kimi', requiresApiKey: true, defaultBaseUrl: 'https://api.moonshot.cn/v1', defaultModelId: 'kimi-k2.5', docsUrl: 'https://platform.moonshot.cn/' },
|
||||||
|
{ id: 'siliconflow', name: 'SiliconFlow (CN)', icon: '🌊', placeholder: 'sk-...', model: 'Multi-Model', requiresApiKey: true, defaultBaseUrl: 'https://api.siliconflow.cn/v1', showModelId: true, modelIdPlaceholder: 'deepseek-ai/DeepSeek-V3', defaultModelId: 'deepseek-ai/DeepSeek-V3', docsUrl: 'https://docs.siliconflow.cn/cn/userguide/introduction' },
|
||||||
|
{ id: 'minimax-portal', name: 'MiniMax (Global)', icon: '☁️', placeholder: 'sk-...', model: 'MiniMax', requiresApiKey: false, isOAuth: true, supportsApiKey: true, defaultModelId: 'MiniMax-M2.5', apiKeyUrl: 'https://intl.minimaxi.com/' },
|
||||||
|
{ id: 'qwen-portal', name: 'Qwen (Global)', icon: '☁️', placeholder: 'sk-...', model: 'Qwen', requiresApiKey: false, isOAuth: true, defaultModelId: 'coder-model' },
|
||||||
|
{ id: 'ark', name: 'ByteDance Ark', icon: 'A', placeholder: 'your-ark-api-key', model: 'Doubao', requiresApiKey: true, defaultBaseUrl: 'https://ark.cn-beijing.volces.com/api/v3', showBaseUrl: true, showModelId: true, modelIdPlaceholder: 'ep-20260228000000-xxxxx', docsUrl: 'https://www.volcengine.com/' },
|
||||||
{ id: 'ollama', name: 'Ollama', icon: '🦙', placeholder: 'Not required', requiresApiKey: false, defaultBaseUrl: 'http://localhost:11434/v1', showBaseUrl: true, showModelId: true, modelIdPlaceholder: 'qwen3:latest' },
|
{ id: 'ollama', name: 'Ollama', icon: '🦙', placeholder: 'Not required', requiresApiKey: false, defaultBaseUrl: 'http://localhost:11434/v1', showBaseUrl: true, showModelId: true, modelIdPlaceholder: 'qwen3:latest' },
|
||||||
{ id: 'custom', name: 'Custom', icon: '⚙️', placeholder: 'API key...', requiresApiKey: true, showBaseUrl: true, showModelId: true, modelIdPlaceholder: 'your-provider/model-id' },
|
{
|
||||||
|
id: 'custom',
|
||||||
|
name: 'Custom',
|
||||||
|
icon: '⚙️',
|
||||||
|
placeholder: 'API key...',
|
||||||
|
requiresApiKey: true,
|
||||||
|
showBaseUrl: true,
|
||||||
|
showModelId: true,
|
||||||
|
modelIdPlaceholder: 'your-provider/model-id',
|
||||||
|
docsUrl: 'https://icnnp7d0dymg.feishu.cn/wiki/BmiLwGBcEiloZDkdYnGc8RWnn6d#Ee1ldfvKJoVGvfxc32mcILwenth',
|
||||||
|
docsUrlZh: 'https://icnnp7d0dymg.feishu.cn/wiki/BmiLwGBcEiloZDkdYnGc8RWnn6d#IWQCdfe5fobGU3xf3UGcgbLynGh',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
/** Get the SVG logo URL for a provider type, falls back to undefined */
|
/** Get the SVG logo URL for a provider type, falls back to undefined */
|
||||||
@@ -174,6 +195,21 @@ export function getProviderTypeInfo(type: ProviderType): ProviderTypeInfo | unde
|
|||||||
return PROVIDER_TYPE_INFO.find((t) => t.id === type);
|
return PROVIDER_TYPE_INFO.find((t) => t.id === type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getProviderDocsUrl(
|
||||||
|
provider: Pick<ProviderTypeInfo, 'docsUrl' | 'docsUrlZh'> | undefined,
|
||||||
|
language: string
|
||||||
|
): string | undefined {
|
||||||
|
if (!provider?.docsUrl) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (language.startsWith('zh') && provider.docsUrlZh) {
|
||||||
|
return provider.docsUrlZh;
|
||||||
|
}
|
||||||
|
|
||||||
|
return provider.docsUrl;
|
||||||
|
}
|
||||||
|
|
||||||
export function shouldShowProviderModelId(
|
export function shouldShowProviderModelId(
|
||||||
provider: Pick<ProviderTypeInfo, 'showModelId' | 'showModelIdInDevModeOnly'> | undefined,
|
provider: Pick<ProviderTypeInfo, 'showModelId' | 'showModelIdInDevModeOnly'> | undefined,
|
||||||
devModeUnlocked: boolean
|
devModeUnlocked: boolean
|
||||||
|
|||||||
@@ -97,6 +97,7 @@ import {
|
|||||||
type ProviderAccount,
|
type ProviderAccount,
|
||||||
type ProviderType,
|
type ProviderType,
|
||||||
type ProviderTypeInfo,
|
type ProviderTypeInfo,
|
||||||
|
getProviderDocsUrl,
|
||||||
getProviderIconUrl,
|
getProviderIconUrl,
|
||||||
resolveProviderApiKeyForSave,
|
resolveProviderApiKeyForSave,
|
||||||
resolveProviderModelForSave,
|
resolveProviderModelForSave,
|
||||||
@@ -713,7 +714,7 @@ function ProviderContent({
|
|||||||
onApiKeyChange,
|
onApiKeyChange,
|
||||||
onConfiguredChange,
|
onConfiguredChange,
|
||||||
}: ProviderContentProps) {
|
}: ProviderContentProps) {
|
||||||
const { t } = useTranslation(['setup', 'settings']);
|
const { t, i18n } = useTranslation(['setup', 'settings']);
|
||||||
const devModeUnlocked = useSettingsStore((state) => state.devModeUnlocked);
|
const devModeUnlocked = useSettingsStore((state) => state.devModeUnlocked);
|
||||||
const [showKey, setShowKey] = useState(false);
|
const [showKey, setShowKey] = useState(false);
|
||||||
const [validating, setValidating] = useState(false);
|
const [validating, setValidating] = useState(false);
|
||||||
@@ -975,6 +976,7 @@ function ProviderContent({
|
|||||||
}, [providerMenuOpen]);
|
}, [providerMenuOpen]);
|
||||||
|
|
||||||
const selectedProviderData = providers.find((p) => p.id === selectedProvider);
|
const selectedProviderData = providers.find((p) => p.id === selectedProvider);
|
||||||
|
const providerDocsUrl = getProviderDocsUrl(selectedProviderData, i18n.language);
|
||||||
const selectedProviderIconUrl = selectedProviderData
|
const selectedProviderIconUrl = selectedProviderData
|
||||||
? getProviderIconUrl(selectedProviderData.id)
|
? getProviderIconUrl(selectedProviderData.id)
|
||||||
: undefined;
|
: undefined;
|
||||||
@@ -1139,7 +1141,20 @@ function ProviderContent({
|
|||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
{/* Provider selector — dropdown */}
|
{/* Provider selector — dropdown */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label>{t('provider.label')}</Label>
|
<div className="flex items-center justify-between gap-3">
|
||||||
|
<Label>{t('provider.label')}</Label>
|
||||||
|
{selectedProvider && providerDocsUrl && (
|
||||||
|
<a
|
||||||
|
href={providerDocsUrl}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="text-[13px] text-blue-500 hover:text-blue-600 font-medium inline-flex items-center gap-1"
|
||||||
|
>
|
||||||
|
{t('settings:aiProviders.dialog.customDoc')}
|
||||||
|
<ExternalLink className="h-3 w-3" />
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
<div className="relative" ref={providerMenuRef}>
|
<div className="relative" ref={providerMenuRef}>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { describe, expect, it } from 'vitest';
|
|||||||
import {
|
import {
|
||||||
PROVIDER_TYPES,
|
PROVIDER_TYPES,
|
||||||
PROVIDER_TYPE_INFO,
|
PROVIDER_TYPE_INFO,
|
||||||
|
getProviderDocsUrl,
|
||||||
resolveProviderApiKeyForSave,
|
resolveProviderApiKeyForSave,
|
||||||
resolveProviderModelForSave,
|
resolveProviderModelForSave,
|
||||||
shouldShowProviderModelId,
|
shouldShowProviderModelId,
|
||||||
@@ -72,13 +73,37 @@ describe('provider metadata', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('exposes provider documentation links', () => {
|
||||||
|
const anthropic = PROVIDER_TYPE_INFO.find((provider) => provider.id === 'anthropic');
|
||||||
|
const openrouter = PROVIDER_TYPE_INFO.find((provider) => provider.id === 'openrouter');
|
||||||
|
const moonshot = PROVIDER_TYPE_INFO.find((provider) => provider.id === 'moonshot');
|
||||||
|
const siliconflow = PROVIDER_TYPE_INFO.find((provider) => provider.id === 'siliconflow');
|
||||||
|
const ark = PROVIDER_TYPE_INFO.find((provider) => provider.id === 'ark');
|
||||||
|
const custom = PROVIDER_TYPE_INFO.find((provider) => provider.id === 'custom');
|
||||||
|
|
||||||
|
expect(anthropic).toMatchObject({
|
||||||
|
docsUrl: 'https://platform.claude.com/docs/en/api/overview',
|
||||||
|
});
|
||||||
|
expect(getProviderDocsUrl(anthropic, 'en')).toBe('https://platform.claude.com/docs/en/api/overview');
|
||||||
|
expect(getProviderDocsUrl(openrouter, 'en')).toBe('https://openrouter.ai/models');
|
||||||
|
expect(getProviderDocsUrl(moonshot, 'en')).toBe('https://platform.moonshot.cn/');
|
||||||
|
expect(getProviderDocsUrl(siliconflow, 'en')).toBe('https://docs.siliconflow.cn/cn/userguide/introduction');
|
||||||
|
expect(getProviderDocsUrl(ark, 'en')).toBe('https://www.volcengine.com/');
|
||||||
|
expect(getProviderDocsUrl(custom, 'en')).toBe(
|
||||||
|
'https://icnnp7d0dymg.feishu.cn/wiki/BmiLwGBcEiloZDkdYnGc8RWnn6d#Ee1ldfvKJoVGvfxc32mcILwenth'
|
||||||
|
);
|
||||||
|
expect(getProviderDocsUrl(custom, 'zh-CN')).toBe(
|
||||||
|
'https://icnnp7d0dymg.feishu.cn/wiki/BmiLwGBcEiloZDkdYnGc8RWnn6d#IWQCdfe5fobGU3xf3UGcgbLynGh'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('exposes OpenRouter and SiliconFlow model overrides by default', () => {
|
it('exposes OpenRouter and SiliconFlow model overrides by default', () => {
|
||||||
const openrouter = PROVIDER_TYPE_INFO.find((provider) => provider.id === 'openrouter');
|
const openrouter = PROVIDER_TYPE_INFO.find((provider) => provider.id === 'openrouter');
|
||||||
const siliconflow = PROVIDER_TYPE_INFO.find((provider) => provider.id === 'siliconflow');
|
const siliconflow = PROVIDER_TYPE_INFO.find((provider) => provider.id === 'siliconflow');
|
||||||
|
|
||||||
expect(openrouter).toMatchObject({
|
expect(openrouter).toMatchObject({
|
||||||
showModelId: true,
|
showModelId: true,
|
||||||
defaultModelId: 'anthropic/claude-opus-4.6',
|
defaultModelId: 'openai/gpt-5.4',
|
||||||
});
|
});
|
||||||
expect(siliconflow).toMatchObject({
|
expect(siliconflow).toMatchObject({
|
||||||
showModelId: true,
|
showModelId: true,
|
||||||
@@ -102,8 +127,8 @@ describe('provider metadata', () => {
|
|||||||
expect(resolveProviderModelForSave(openrouter, 'openai/gpt-5', true)).toBe('openai/gpt-5');
|
expect(resolveProviderModelForSave(openrouter, 'openai/gpt-5', true)).toBe('openai/gpt-5');
|
||||||
expect(resolveProviderModelForSave(siliconflow, 'Qwen/Qwen3-Coder-480B-A35B-Instruct', true)).toBe('Qwen/Qwen3-Coder-480B-A35B-Instruct');
|
expect(resolveProviderModelForSave(siliconflow, 'Qwen/Qwen3-Coder-480B-A35B-Instruct', true)).toBe('Qwen/Qwen3-Coder-480B-A35B-Instruct');
|
||||||
|
|
||||||
expect(resolveProviderModelForSave(openrouter, ' ', false)).toBe('anthropic/claude-opus-4.6');
|
expect(resolveProviderModelForSave(openrouter, ' ', false)).toBe('openai/gpt-5.4');
|
||||||
expect(resolveProviderModelForSave(openrouter, ' ', true)).toBe('anthropic/claude-opus-4.6');
|
expect(resolveProviderModelForSave(openrouter, ' ', true)).toBe('openai/gpt-5.4');
|
||||||
expect(resolveProviderModelForSave(siliconflow, ' ', true)).toBe('deepseek-ai/DeepSeek-V3');
|
expect(resolveProviderModelForSave(siliconflow, ' ', true)).toBe('deepseek-ai/DeepSeek-V3');
|
||||||
expect(resolveProviderModelForSave(ark, ' ep-custom-model ', false)).toBe('ep-custom-model');
|
expect(resolveProviderModelForSave(ark, ' ep-custom-model ', false)).toBe('ep-custom-model');
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user