fix(providers): honor authIndex for OpenAI discovery and connectivity test

- useModelDiscovery: openaiCompatibility branch now forwards
  resolvedAuthIndex into modelsApi.fetchModelsViaApiCall, mirroring the
  gemini/codex/claude branches. Configs that rely on a backend-resolved
  auth index without a local plaintext key can now list models.
- useConnectivityTest: runOpenAIKey accepts an authIndex fallback when
  entry.apiKey is empty, sets Authorization: Bearer $TOKEN$ in that
  case, and forwards authIndex on the /api-call request so the gateway
  can substitute the upstream token.
- useModelDiscovery: reset stale results when baseUrl / apiKey /
  apiKeyEntries / headers / authIndex change, so reopening the discovery
  panel after editing fields re-fetches instead of showing the previous
  endpoint's models.
This commit is contained in:
LTbinglingfeng
2026-05-25 23:52:18 +08:00
parent d2ab416f6c
commit 1aa99887da
2 changed files with 29 additions and 4 deletions
@@ -199,7 +199,8 @@ export function useConnectivityTest(
}
const entry = apiKeyEntries?.[idx];
const entryKey = (entry?.apiKey ?? '').trim();
if (!entryKey) {
const resolvedAuthIndex = (authIndex ?? '').trim() || undefined;
if (!entryKey && !resolvedAuthIndex) {
updateOpenaiStatus(idx, {
state: 'error',
message: messages.apiKeyRequired,
@@ -221,7 +222,11 @@ export function useConnectivityTest(
...parseHeadersText(entry?.headersText ?? ''),
};
if (!hasHeader(headerObj, 'authorization')) {
headerObj.Authorization = `Bearer ${entryKey}`;
if (entryKey) {
headerObj.Authorization = `Bearer ${entryKey}`;
} else if (resolvedAuthIndex) {
headerObj.Authorization = 'Bearer $TOKEN$';
}
}
updateOpenaiStatus(idx, { state: 'loading', message: '' });
@@ -229,6 +234,7 @@ export function useConnectivityTest(
try {
const result = await apiCallApi.request(
{
authIndex: resolvedAuthIndex,
method: 'POST',
url: endpoint,
header: headerObj,
@@ -267,6 +273,7 @@ export function useConnectivityTest(
},
[
apiKeyEntries,
authIndex,
baseUrl,
brand,
formHeaders,
@@ -1,4 +1,4 @@
import { useCallback, useState } from 'react';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { modelsApi } from '@/services/api';
import { buildHeaderObject } from '@/utils/headers';
import type { ModelInfo } from '@/utils/models';
@@ -119,7 +119,8 @@ export function useModelDiscovery(
next = await modelsApi.fetchModelsViaApiCall(
baseUrl,
entryKey,
headers
headers,
resolvedAuthIndex
);
} catch (firstErr) {
// Some OpenAI-compatible endpoints expose /models without auth, or
@@ -159,5 +160,22 @@ export function useModelDiscovery(
setHasFetched(false);
}, []);
const inputSignature = useMemo(() => {
const headerSig = formHeaders
.map((h) => `${h.key}:${h.value}`)
.join('|');
const entriesSig = (apiKeyEntries ?? [])
.map((e) => `${e.apiKey ?? ''}::${e.headersText ?? ''}`)
.join('|');
return `${baseUrl}||${apiKey ?? ''}||${fallbackApiKey ?? ''}||${authIndex ?? ''}||${headerSig}||${entriesSig}`;
}, [apiKey, apiKeyEntries, authIndex, baseUrl, fallbackApiKey, formHeaders]);
const lastSignatureRef = useRef(inputSignature);
useEffect(() => {
if (lastSignatureRef.current === inputSignature) return;
lastSignatureRef.current = inputSignature;
reset();
}, [inputSignature, reset]);
return { available, loading, error, models, hasFetched, fetch, reset };
}