Merge 429364f01cdac31a6df6f0b67c1cab43acbdf48b into 5e417b44e1540f528d2ae63e3e20229a902d1db2

This commit is contained in:
Antonio Mello 2026-03-21 05:00:17 +03:00 committed by GitHub
commit 2ad79d644d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 92 additions and 2 deletions

View File

@ -338,6 +338,81 @@ describe("normalizeModelCompat", () => {
});
});
describe("normalizeModelCompat — vLLM and SGLang providers", () => {
function makeVllmModel(baseUrl: string): Model<Api> {
return {
id: "Qwen3.5-27B",
name: "Qwen3.5-27B",
api: "openai-completions",
provider: "vllm",
baseUrl,
reasoning: false,
input: ["text"],
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: 32768,
maxTokens: 4096,
} as Model<Api>;
}
function makeSglangModel(baseUrl: string): Model<Api> {
return {
...makeVllmModel(baseUrl),
provider: "sglang",
} as Model<Api>;
}
it("defaults supportsUsageInStreaming to true for vLLM self-hosted endpoints", () => {
const model = makeVllmModel("http://192.168.88.35:9090/v1");
delete (model as { compat?: unknown }).compat;
const normalized = normalizeModelCompat(model);
expect(supportsUsageInStreaming(normalized)).toBe(true);
});
it("defaults supportsUsageInStreaming to true for vLLM localhost endpoints", () => {
const model = makeVllmModel("http://127.0.0.1:8000/v1");
delete (model as { compat?: unknown }).compat;
const normalized = normalizeModelCompat(model);
expect(supportsUsageInStreaming(normalized)).toBe(true);
});
it("defaults supportsUsageInStreaming to true for SGLang self-hosted endpoints", () => {
const model = makeSglangModel("http://192.168.1.10:30000/v1");
delete (model as { compat?: unknown }).compat;
const normalized = normalizeModelCompat(model);
expect(supportsUsageInStreaming(normalized)).toBe(true);
});
it("still forces supportsDeveloperRole off for vLLM endpoints", () => {
const model = makeVllmModel("http://192.168.88.35:9090/v1");
delete (model as { compat?: unknown }).compat;
const normalized = normalizeModelCompat(model);
expect(supportsDeveloperRole(normalized)).toBe(false);
});
it("still forces supportsStrictMode off for vLLM endpoints", () => {
const model = makeVllmModel("http://192.168.88.35:9090/v1");
delete (model as { compat?: unknown }).compat;
const normalized = normalizeModelCompat(model);
expect(supportsStrictMode(normalized)).toBe(false);
});
it("respects explicit supportsUsageInStreaming false override for vLLM", () => {
const model = makeVllmModel("http://192.168.88.35:9090/v1");
(model as { compat?: unknown }).compat = { supportsUsageInStreaming: false };
const normalized = normalizeModelCompat(model);
expect(supportsUsageInStreaming(normalized)).toBe(false);
});
it("does not mutate caller model reference for vLLM", () => {
const model = makeVllmModel("http://192.168.88.35:9090/v1");
delete (model as { compat?: unknown }).compat;
const normalized = normalizeModelCompat(model);
expect(normalized).not.toBe(model);
expect(supportsUsageInStreaming(model)).toBeUndefined();
expect(supportsUsageInStreaming(normalized)).toBe(true);
});
});
describe("isModernModelRef", () => {
it("uses provider runtime hooks before fallback heuristics", () => {
providerRuntimeMocks.resolveProviderModernModelRef.mockReturnValue(false);

View File

@ -82,6 +82,14 @@ function isOpenAINativeEndpoint(baseUrl: string): boolean {
}
}
/**
* Self-hosted providers known to correctly support `stream_options.include_usage`
* (i.e., they return a terminal usage-only chunk at the end of the stream).
* Unlike arbitrary OpenAI-compatible proxies, these are well-tested inference
* engines that faithfully implement the OpenAI streaming usage spec.
*/
const STREAMING_USAGE_ALLOWLIST = new Set(["vllm", "sglang"]);
function isAnthropicMessagesModel(model: Model<Api>): model is Model<"anthropic-messages"> {
return model.api === "anthropic-messages";
}
@ -138,6 +146,13 @@ export function normalizeModelCompat(model: Model<Api>): Model<Api> {
return model;
}
// Self-hosted inference engines (vLLM, SGLang) correctly implement the
// OpenAI streaming usage spec and return a terminal usage-only chunk.
// Default supportsUsageInStreaming to true for these known providers so
// token counts are recorded even when no explicit compat override is set.
const providerLower = (model.provider ?? "").toLowerCase();
const defaultStreamingUsage = STREAMING_USAGE_ALLOWLIST.has(providerLower);
// Return a new object — do not mutate the caller's model reference.
return {
...model,
@ -145,12 +160,12 @@ export function normalizeModelCompat(model: Model<Api>): Model<Api> {
? {
...compat,
supportsDeveloperRole: forcedDeveloperRole || false,
...(hasStreamingUsageOverride ? {} : { supportsUsageInStreaming: false }),
...(hasStreamingUsageOverride ? {} : { supportsUsageInStreaming: defaultStreamingUsage }),
supportsStrictMode: targetStrictMode,
}
: {
supportsDeveloperRole: false,
supportsUsageInStreaming: false,
supportsUsageInStreaming: defaultStreamingUsage,
supportsStrictMode: false,
},
} as typeof model;