refactor(provider): provider API validation & CN defaults (#47)
This commit is contained in:
committed by
GitHub
Unverified
parent
01f4d4800e
commit
0ced0b042c
@@ -160,7 +160,7 @@ export function ProvidersSettings() {
|
||||
);
|
||||
setEditingProvider(null);
|
||||
}}
|
||||
onValidateKey={(key) => validateApiKey(provider.id, key)}
|
||||
onValidateKey={(key, options) => validateApiKey(provider.id, key, options)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@@ -172,7 +172,7 @@ export function ProvidersSettings() {
|
||||
existingTypes={new Set(providers.map((p) => p.type))}
|
||||
onClose={() => setShowAddDialog(false)}
|
||||
onAdd={handleAddProvider}
|
||||
onValidateKey={(type, key) => validateApiKey(type, key)}
|
||||
onValidateKey={(type, key, options) => validateApiKey(type, key, options)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
@@ -189,7 +189,10 @@ interface ProviderCardProps {
|
||||
onSetDefault: () => void;
|
||||
onToggleEnabled: () => void;
|
||||
onSaveEdits: (payload: { newApiKey?: string; updates?: Partial<ProviderConfig> }) => Promise<void>;
|
||||
onValidateKey: (key: string) => Promise<{ valid: boolean; error?: string }>;
|
||||
onValidateKey: (
|
||||
key: string,
|
||||
options?: { baseUrl?: string }
|
||||
) => Promise<{ valid: boolean; error?: string }>;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -245,7 +248,9 @@ function ProviderCard({
|
||||
|
||||
if (newKey.trim()) {
|
||||
setValidating(true);
|
||||
const result = await onValidateKey(newKey);
|
||||
const result = await onValidateKey(newKey, {
|
||||
baseUrl: baseUrl.trim() || undefined,
|
||||
});
|
||||
setValidating(false);
|
||||
if (!result.valid) {
|
||||
toast.error(result.error || 'Invalid API key');
|
||||
@@ -426,7 +431,11 @@ interface AddProviderDialogProps {
|
||||
apiKey: string,
|
||||
options?: { baseUrl?: string; model?: string }
|
||||
) => Promise<void>;
|
||||
onValidateKey: (type: string, apiKey: string) => Promise<{ valid: boolean; error?: string }>;
|
||||
onValidateKey: (
|
||||
type: string,
|
||||
apiKey: string,
|
||||
options?: { baseUrl?: string }
|
||||
) => Promise<{ valid: boolean; error?: string }>;
|
||||
}
|
||||
|
||||
function AddProviderDialog({ existingTypes, onClose, onAdd, onValidateKey }: AddProviderDialogProps) {
|
||||
@@ -461,7 +470,9 @@ function AddProviderDialog({ existingTypes, onClose, onAdd, onValidateKey }: Add
|
||||
return;
|
||||
}
|
||||
if (requiresKey && apiKey) {
|
||||
const result = await onValidateKey(selectedType, apiKey);
|
||||
const result = await onValidateKey(selectedType, apiKey, {
|
||||
baseUrl: baseUrl.trim() || undefined,
|
||||
});
|
||||
if (!result.valid) {
|
||||
setValidationError(result.error || 'Invalid API key');
|
||||
setSaving(false);
|
||||
|
||||
@@ -59,8 +59,8 @@ export const PROVIDER_TYPE_INFO: ProviderTypeInfo[] = [
|
||||
{ id: 'openai', name: 'OpenAI', icon: '💚', placeholder: 'sk-proj-...', model: 'GPT', requiresApiKey: true },
|
||||
{ id: 'google', name: 'Google', icon: '🔷', placeholder: 'AIza...', model: 'Gemini', requiresApiKey: true },
|
||||
{ id: 'openrouter', name: 'OpenRouter', icon: '🌐', placeholder: 'sk-or-v1-...', model: 'Multi-Model', requiresApiKey: true },
|
||||
{ id: 'moonshot', name: 'Moonshot', icon: '🌙', placeholder: 'sk-...', model: 'Kimi', requiresApiKey: true, defaultBaseUrl: 'https://api.moonshot.cn/v1', defaultModelId: 'kimi-k2.5' },
|
||||
{ id: 'siliconflow', name: 'SiliconFlow', icon: '🌊', placeholder: 'sk-...', model: 'Multi-Model', requiresApiKey: true, defaultBaseUrl: 'https://api.siliconflow.com/v1', defaultModelId: 'moonshotai/Kimi-K2.5' },
|
||||
{ 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', defaultModelId: 'Pro/moonshotai/Kimi-K2.5' },
|
||||
{ id: 'ollama', name: 'Ollama', icon: '🦙', placeholder: 'Not required', requiresApiKey: false, defaultBaseUrl: 'http://localhost:11434', 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' },
|
||||
];
|
||||
|
||||
@@ -655,6 +655,7 @@ function ProviderContent({
|
||||
const [showKey, setShowKey] = useState(false);
|
||||
const [validating, setValidating] = useState(false);
|
||||
const [keyValid, setKeyValid] = useState<boolean | null>(null);
|
||||
const [selectedProviderConfigId, setSelectedProviderConfigId] = useState<string | null>(null);
|
||||
const [baseUrl, setBaseUrl] = useState('');
|
||||
const [modelId, setModelId] = useState('');
|
||||
|
||||
@@ -673,6 +674,7 @@ function ProviderContent({
|
||||
|| setupCandidates[0];
|
||||
if (preferred && !cancelled) {
|
||||
onSelectProvider(preferred.type);
|
||||
setSelectedProviderConfigId(preferred.id);
|
||||
const typeInfo = providers.find((p) => p.id === preferred.type);
|
||||
const requiresKey = typeInfo?.requiresApiKey ?? false;
|
||||
onConfiguredChange(!requiresKey || preferred.hasKey);
|
||||
@@ -706,6 +708,7 @@ function ProviderContent({
|
||||
|| sameType.find((p) => p.hasKey)
|
||||
|| sameType[0];
|
||||
const providerIdForLoad = preferredInstance?.id || selectedProvider;
|
||||
setSelectedProviderConfigId(providerIdForLoad);
|
||||
|
||||
const savedProvider = await window.electron.ipcRenderer.invoke(
|
||||
'provider:get',
|
||||
@@ -746,8 +749,9 @@ function ProviderContent({
|
||||
if (requiresKey && apiKey) {
|
||||
const result = await window.electron.ipcRenderer.invoke(
|
||||
'provider:validateKey',
|
||||
selectedProvider,
|
||||
apiKey
|
||||
selectedProviderConfigId || selectedProvider,
|
||||
apiKey,
|
||||
{ baseUrl: baseUrl.trim() || undefined }
|
||||
) as { valid: boolean; error?: string };
|
||||
|
||||
setKeyValid(result.valid);
|
||||
@@ -766,11 +770,18 @@ function ProviderContent({
|
||||
modelId.trim() ||
|
||||
undefined;
|
||||
|
||||
const providerIdForSave =
|
||||
selectedProvider === 'custom'
|
||||
? (selectedProviderConfigId?.startsWith('custom-')
|
||||
? selectedProviderConfigId
|
||||
: `custom-${crypto.randomUUID()}`)
|
||||
: selectedProvider;
|
||||
|
||||
// Save provider config + API key, then set as default
|
||||
const saveResult = await window.electron.ipcRenderer.invoke(
|
||||
'provider:save',
|
||||
{
|
||||
id: selectedProvider,
|
||||
id: providerIdForSave,
|
||||
name: selectedProviderData?.name || selectedProvider,
|
||||
type: selectedProvider,
|
||||
baseUrl: baseUrl.trim() || undefined,
|
||||
@@ -788,13 +799,14 @@ function ProviderContent({
|
||||
|
||||
const defaultResult = await window.electron.ipcRenderer.invoke(
|
||||
'provider:setDefault',
|
||||
selectedProvider
|
||||
providerIdForSave
|
||||
) as { success: boolean; error?: string };
|
||||
|
||||
if (!defaultResult.success) {
|
||||
throw new Error(defaultResult.error || 'Failed to set default provider');
|
||||
}
|
||||
|
||||
setSelectedProviderConfigId(providerIdForSave);
|
||||
onConfiguredChange(true);
|
||||
toast.success('Provider configured');
|
||||
} catch (error) {
|
||||
@@ -824,6 +836,7 @@ function ProviderContent({
|
||||
onChange={(e) => {
|
||||
const val = e.target.value || null;
|
||||
onSelectProvider(val);
|
||||
setSelectedProviderConfigId(null);
|
||||
onConfiguredChange(false);
|
||||
onApiKeyChange('');
|
||||
setKeyValid(null);
|
||||
|
||||
@@ -27,7 +27,11 @@ interface ProviderState {
|
||||
) => Promise<void>;
|
||||
deleteApiKey: (providerId: string) => Promise<void>;
|
||||
setDefaultProvider: (providerId: string) => Promise<void>;
|
||||
validateApiKey: (providerId: string, apiKey: string) => Promise<{ valid: boolean; error?: string }>;
|
||||
validateApiKey: (
|
||||
providerId: string,
|
||||
apiKey: string,
|
||||
options?: { baseUrl?: string }
|
||||
) => Promise<{ valid: boolean; error?: string }>;
|
||||
getApiKey: (providerId: string) => Promise<string | null>;
|
||||
}
|
||||
|
||||
@@ -188,9 +192,14 @@ export const useProviderStore = create<ProviderState>((set, get) => ({
|
||||
}
|
||||
},
|
||||
|
||||
validateApiKey: async (providerId, apiKey) => {
|
||||
validateApiKey: async (providerId, apiKey, options) => {
|
||||
try {
|
||||
const result = await window.electron.ipcRenderer.invoke('provider:validateKey', providerId, apiKey) as { valid: boolean; error?: string };
|
||||
const result = await window.electron.ipcRenderer.invoke(
|
||||
'provider:validateKey',
|
||||
providerId,
|
||||
apiKey,
|
||||
options
|
||||
) as { valid: boolean; error?: string };
|
||||
return result;
|
||||
} catch (error) {
|
||||
return { valid: false, error: String(error) };
|
||||
|
||||
Reference in New Issue
Block a user