mirror of
https://github.com/musistudio/claude-code-router.git
synced 2026-07-09 17:18:24 +00:00
Refine protocol probe filtering for Gemini
This commit is contained in:
parent
f13e3b4598
commit
d45e88387a
6 changed files with 78 additions and 11 deletions
|
|
@ -535,7 +535,7 @@ async function probeProtocolConnectivity(
|
|||
for (const candidate of endpoints) {
|
||||
const result = await requestJson(candidate.endpoint, requestForProtocol(protocol, model, apiKey));
|
||||
const message = readResponseMessage(result);
|
||||
const supported = isProtocolSupported(result.status, message);
|
||||
const supported = isProtocolSupported(result.status, message, protocol);
|
||||
const probeResult = {
|
||||
baseUrl: candidate.baseUrl,
|
||||
endpoint: candidate.endpoint,
|
||||
|
|
@ -982,7 +982,11 @@ function protocolHints(value: string): GatewayProviderProtocol[] {
|
|||
return hints;
|
||||
}
|
||||
|
||||
function isProtocolSupported(status: number | undefined, message: string): boolean {
|
||||
function isProtocolSupported(
|
||||
status: number | undefined,
|
||||
message: string,
|
||||
protocol?: GatewayProviderProtocol
|
||||
): boolean {
|
||||
if (status === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -997,7 +1001,9 @@ function isProtocolSupported(status: number | undefined, message: string): boole
|
|||
|
||||
if (status === 400) {
|
||||
const normalized = message.toLowerCase();
|
||||
return /model|max_tokens|max output|messages|input|required/.test(normalized) && !/not found|unknown endpoint|unknown route|no route/.test(normalized);
|
||||
const schemaError = /model|max_tokens|max output|messages|input|required/.test(normalized) ||
|
||||
(protocol === "gemini_generate_content" && /contents|generatecontentrequest|generationconfig/.test(normalized));
|
||||
return schemaError && !/not found|unknown endpoint|unknown route|no route/.test(normalized);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
@ -1009,7 +1015,7 @@ export function isProviderProtocolEndpointSupportedForProbe(
|
|||
protocol: GatewayProviderProtocol,
|
||||
hints: GatewayProviderProtocol[] = []
|
||||
): boolean {
|
||||
if (isProtocolSupported(status, message)) {
|
||||
if (isProtocolSupported(status, message, protocol)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1361,7 +1361,7 @@ function App() {
|
|||
const fallbackBaseUrl = probe?.normalizedBaseUrl || providerDraft.baseUrl;
|
||||
const selectableProtocols = providerSelectableProtocolsFromProbe(probe);
|
||||
const selectedProtocols = providerDraft.selectedProtocols.length > 0
|
||||
? providerDraft.selectedProtocols.filter((protocol) => selectableProtocols.length === 0 || selectableProtocols.includes(protocol))
|
||||
? providerDraft.selectedProtocols.filter((protocol) => !probe || selectableProtocols.includes(protocol))
|
||||
: [];
|
||||
if (selectableProtocols.length > 0 && selectedProtocols.length === 0) {
|
||||
setProviderProbeError(t("Select at least one protocol."));
|
||||
|
|
|
|||
|
|
@ -1680,7 +1680,7 @@ export function AddProviderForm({
|
|||
<div className="space-y-1.5">
|
||||
{probe.protocols.map((item) => {
|
||||
const selectable = item.supported && selectableProtocols.includes(item.protocol);
|
||||
const checked = draft.selectedProtocols.includes(item.protocol);
|
||||
const checked = selectable && draft.selectedProtocols.includes(item.protocol);
|
||||
return (
|
||||
<div className="grid grid-cols-[20px_minmax(118px,0.7fr)_72px_minmax(0,1fr)] items-center gap-2 text-[11px]" key={`${item.protocol}-${item.endpoint}`}>
|
||||
<Checkbox
|
||||
|
|
|
|||
|
|
@ -1750,10 +1750,7 @@ export function providerSelectableProtocolsFromProbe(probe: GatewayProviderProbe
|
|||
return [];
|
||||
}
|
||||
|
||||
return uniqueProviderProtocols([
|
||||
...(probe.capabilities ?? []).map((capability) => capability.type),
|
||||
...probe.protocols.filter((item) => item.supported).map((item) => item.protocol)
|
||||
]);
|
||||
return uniqueProviderProtocols(probe.protocols.filter((item) => item.supported).map((item) => item.protocol));
|
||||
}
|
||||
|
||||
export function selectedProviderProtocolsFromCapabilities(
|
||||
|
|
@ -1772,7 +1769,7 @@ export function selectedProviderProtocolsForProbe(
|
|||
): GatewayProviderProtocol[] {
|
||||
const selectable = providerSelectableProtocolsFromProbe(probe);
|
||||
if (selectable.length === 0) {
|
||||
return selectedProtocols.length > 0 ? uniqueProviderProtocols(selectedProtocols) : [fallback];
|
||||
return [];
|
||||
}
|
||||
|
||||
if (selectedProtocolsMatchPresetDefault(selectedProtocols, presetId)) {
|
||||
|
|
|
|||
|
|
@ -32,3 +32,16 @@ test("protocol support probe keeps auth-only fallback for unhinted endpoints", (
|
|||
true
|
||||
);
|
||||
});
|
||||
|
||||
test("protocol support probe treats Gemini contents validation as Gemini support only", () => {
|
||||
const message = "HTTP 400: * GenerateContentRequest.contents: contents is not specified";
|
||||
|
||||
assert.equal(
|
||||
isProviderProtocolEndpointSupportedForProbe(400, message, "gemini_generate_content", ["gemini_generate_content"]),
|
||||
true
|
||||
);
|
||||
assert.equal(
|
||||
isProviderProtocolEndpointSupportedForProbe(400, message, "openai_chat_completions", ["openai_chat_completions"]),
|
||||
false
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import assert from "node:assert/strict";
|
|||
import test from "node:test";
|
||||
import { geminiProviderPreset } from "../../src/main/presets/gemini/index.ts";
|
||||
import {
|
||||
applyProviderProbeResult,
|
||||
createProviderDraft,
|
||||
providerProtocolOptions,
|
||||
providerProbeCandidates,
|
||||
|
|
@ -20,3 +21,53 @@ test("Gemini preset keeps full protocol probing candidates", () => {
|
|||
assert.equal(candidates.length, 1);
|
||||
assert.deepEqual(candidates[0].protocols, providerProtocolOptions.map((option) => option.value));
|
||||
});
|
||||
|
||||
test("provider probe result drops unavailable selected protocols", () => {
|
||||
const draft = {
|
||||
...createProviderDraft([]),
|
||||
baseUrl: "https://generativelanguage.googleapis.com",
|
||||
protocol: "gemini_generate_content",
|
||||
selectedProtocols: providerProtocolOptions.map((option) => option.value)
|
||||
};
|
||||
|
||||
const next = applyProviderProbeResult(draft, {
|
||||
capabilities: [],
|
||||
detectedProtocol: "gemini_generate_content",
|
||||
models: [],
|
||||
normalizedBaseUrl: draft.baseUrl,
|
||||
protocols: providerProtocolOptions.map((option) => ({
|
||||
endpoint: draft.baseUrl,
|
||||
message: "HTTP 404",
|
||||
protocol: option.value,
|
||||
status: 404,
|
||||
supported: false
|
||||
}))
|
||||
});
|
||||
|
||||
assert.deepEqual(next.selectedProtocols, []);
|
||||
});
|
||||
|
||||
test("provider probe result keeps only supported selected protocols", () => {
|
||||
const draft = {
|
||||
...createProviderDraft([]),
|
||||
baseUrl: "https://generativelanguage.googleapis.com",
|
||||
protocol: "gemini_generate_content",
|
||||
selectedProtocols: providerProtocolOptions.map((option) => option.value)
|
||||
};
|
||||
|
||||
const next = applyProviderProbeResult(draft, {
|
||||
capabilities: [],
|
||||
detectedProtocol: "gemini_generate_content",
|
||||
models: [],
|
||||
normalizedBaseUrl: draft.baseUrl,
|
||||
protocols: providerProtocolOptions.map((option) => ({
|
||||
endpoint: draft.baseUrl,
|
||||
message: option.value === "gemini_generate_content" ? "HTTP 400: contents is not specified" : "HTTP 404",
|
||||
protocol: option.value,
|
||||
status: option.value === "gemini_generate_content" ? 400 : 404,
|
||||
supported: option.value === "gemini_generate_content"
|
||||
}))
|
||||
});
|
||||
|
||||
assert.deepEqual(next.selectedProtocols, ["gemini_generate_content"]);
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue