mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-31 04:12:10 +03:00
This commit is contained in:
committed by
GitHub
parent
adde9e4bae
commit
1473261c4c
@@ -10,6 +10,7 @@
|
||||
|
||||
- **fix(api):** relay worker now binds the SSRF guard to a stable `const` name so minified standalone (Docker) builds resolve it ([#6149](https://github.com/diegosouzapw/OmniRoute/issues/6149)) — the Vercel/Deno relay generators embedded the shared `resolveRelayTarget` guard as a bare `${fn.toString()}` declaration while the worker body called the hardcoded literal name; SWC minification mangled the source function's name, so the deployed worker defined `<mangled>` but still called `resolveRelayTarget` → `ReferenceError`. Both templates now emit `const resolveRelayTarget = ${fn.toString()};` (the const name is a template literal, immune to minification). Regression guard: `tests/unit/relay-minified-fn-6149.test.ts` (4). (thanks @SeaXen)
|
||||
- **fix(providers):** refresh the stale NVIDIA NIM model registry — drop EOL `z-ai/glm-5.1`, add `z-ai/glm-5.2` and `nvidia/nemotron-3-ultra-550b-a55b` ([#6108](https://github.com/diegosouzapw/OmniRoute/issues/6108)). Regression guard: `tests/unit/nvidia-nim-registry-6108.test.ts`. (thanks @andrea-kingautomation)
|
||||
- **fix(backend):** GPT-family (codex) models now report a distinct `max_input_tokens` (272000) below their 400K `context_length` via an optional `maxInputTokens` on `RegistryModel`, so coding agents auto-compact correctly instead of overflowing the real input cap ([#6191](https://github.com/diegosouzapw/OmniRoute/issues/6191)). Regression guard: `tests/unit/gpt-max-input-tokens-6191.test.ts`. (thanks @luweiCN)
|
||||
- **fix(backend):** call logs now record a **reasoning source/char-count** (migration 116, `reasoning_source`/`reasoning_chars`) for models that emit `reasoning_content`/`<think>` but report zero reasoning tokens in usage, so `tokens_reasoning` no longer silently under-represents reasoning — cost math is unchanged (the priced `tokens_reasoning` stays usage-derived) ([#6187](https://github.com/diegosouzapw/OmniRoute/issues/6187)). Regression guard: `tests/unit/reasoning-token-source-6187.test.ts`. (thanks @andrea-kingautomation)
|
||||
- **fix(auth):** a stale/changed `STORAGE_ENCRYPTION_KEY` now surfaces as a clear **424 `storage_encryption_stale`** ("re-enter the API key") instead of a misleading "Auth failed: 401" — the connection's ciphertext failed to decrypt and was coerced to an empty Bearer, hiding the real cause ([#6148](https://github.com/diegosouzapw/OmniRoute/issues/6148)). Regression guard: `tests/unit/decrypt-stale-key-hint-6148.test.ts`. (thanks @chirag127)
|
||||
- **fix(backend):** memory injection now keeps the injected system message **first** for providers that require it (via a `PROVIDERS_SYSTEM_MUST_BE_FIRST` capability), instead of the cache-safe mid-array splice that made strict providers reject the request with a 400 ([#6135](https://github.com/diegosouzapw/OmniRoute/issues/6135)). Regression guard: `tests/unit/memory-system-first-6135.test.ts`.
|
||||
|
||||
@@ -28,11 +28,17 @@ export const codexProvider: RegistryEntry = {
|
||||
// 1.05M). Public refs : openai/codex#19208, #19319, #19464 ;
|
||||
// opencode#24171. max_output_tokens is stripped server-side
|
||||
// (litellm#21193, codex#4138) so 128K is informational only.
|
||||
// The usable INPUT budget is smaller than the 400K window (part is
|
||||
// reserved for output), so max_input_tokens must be distinct from
|
||||
// context_length or coding agents never auto-compact (#6191). OpenAI's
|
||||
// own live catalog reports ~272K for gpt-5.5 in Codex.
|
||||
{
|
||||
id: "gpt-5.5",
|
||||
name: "GPT 5.5",
|
||||
...GPT_5_5_CODEX_CAPABILITIES,
|
||||
contextLength: 400000,
|
||||
// #6191: input cap per reporter; TODO confirm exact value
|
||||
maxInputTokens: 272000,
|
||||
maxOutputTokens: 128000,
|
||||
},
|
||||
{
|
||||
@@ -40,6 +46,8 @@ export const codexProvider: RegistryEntry = {
|
||||
name: "GPT 5.5 (xHigh)",
|
||||
...GPT_5_5_CODEX_CAPABILITIES,
|
||||
contextLength: 400000,
|
||||
// #6191: input cap per reporter; TODO confirm exact value
|
||||
maxInputTokens: 272000,
|
||||
maxOutputTokens: 128000,
|
||||
},
|
||||
{
|
||||
@@ -47,6 +55,8 @@ export const codexProvider: RegistryEntry = {
|
||||
name: "GPT 5.5 (High)",
|
||||
...GPT_5_5_CODEX_CAPABILITIES,
|
||||
contextLength: 400000,
|
||||
// #6191: input cap per reporter; TODO confirm exact value
|
||||
maxInputTokens: 272000,
|
||||
maxOutputTokens: 128000,
|
||||
},
|
||||
{
|
||||
@@ -54,6 +64,8 @@ export const codexProvider: RegistryEntry = {
|
||||
name: "GPT 5.5 (Medium)",
|
||||
...GPT_5_5_CODEX_CAPABILITIES,
|
||||
contextLength: 400000,
|
||||
// #6191: input cap per reporter; TODO confirm exact value
|
||||
maxInputTokens: 272000,
|
||||
maxOutputTokens: 128000,
|
||||
},
|
||||
{
|
||||
@@ -61,6 +73,8 @@ export const codexProvider: RegistryEntry = {
|
||||
name: "GPT 5.5 (Low)",
|
||||
...GPT_5_5_CODEX_CAPABILITIES,
|
||||
contextLength: 400000,
|
||||
// #6191: input cap per reporter; TODO confirm exact value
|
||||
maxInputTokens: 272000,
|
||||
maxOutputTokens: 128000,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -56,6 +56,13 @@ export interface RegistryModel {
|
||||
unsupportedParams?: readonly string[];
|
||||
/** Maximum context window in tokens */
|
||||
contextLength?: number;
|
||||
/**
|
||||
* Explicit maximum input-token budget, when it is smaller than the full
|
||||
* context window (e.g. OAuth backends that reserve part of the window for
|
||||
* output). When set, catalog/capability builders prefer this over deriving
|
||||
* max_input_tokens from contextLength (#6191).
|
||||
*/
|
||||
maxInputTokens?: number;
|
||||
/**
|
||||
* Interleaved-reasoning signal, mirroring models.dev's `interleaved_field`.
|
||||
* Set to "reasoning_content" for models whose upstream runs DeepSeek thinking
|
||||
|
||||
@@ -292,9 +292,13 @@ export async function getUnifiedModelsResponse(
|
||||
registryContext ??
|
||||
specContext ??
|
||||
(getTokenLimit(providerId, modelId) || undefined);
|
||||
const maxInputTokens = isPositiveFiniteNumber(synced?.limit_input)
|
||||
const registryInputLimit = isPositiveFiniteNumber(registryModel?.maxInputTokens)
|
||||
? registryModel.maxInputTokens
|
||||
: undefined;
|
||||
const syncedInputLimit = isPositiveFiniteNumber(synced?.limit_input)
|
||||
? synced.limit_input
|
||||
: contextLength;
|
||||
: undefined;
|
||||
const maxInputTokens = registryInputLimit ?? syncedInputLimit ?? contextLength;
|
||||
const maxOutputTokens = isPositiveFiniteNumber(synced?.limit_output)
|
||||
? synced.limit_output
|
||||
: isPositiveFiniteNumber(spec?.maxOutputTokens)
|
||||
|
||||
@@ -406,7 +406,11 @@ export function getResolvedModelCapabilities(input: CapabilityInput): ResolvedMo
|
||||
structuredOutput: synced?.structured_output ?? null,
|
||||
temperature: synced?.temperature ?? null,
|
||||
contextWindow,
|
||||
maxInputTokens: authoritativeContextWindow ?? synced?.limit_input ?? contextWindow,
|
||||
maxInputTokens:
|
||||
(typeof registryModel?.maxInputTokens === "number" ? registryModel.maxInputTokens : null) ??
|
||||
authoritativeContextWindow ??
|
||||
synced?.limit_input ??
|
||||
contextWindow,
|
||||
maxOutputTokens:
|
||||
synced?.limit_output ??
|
||||
(typeof registryModel?.maxOutputTokens === "number" ? registryModel.maxOutputTokens : null) ??
|
||||
|
||||
67
tests/unit/gpt-max-input-tokens-6191.test.ts
Normal file
67
tests/unit/gpt-max-input-tokens-6191.test.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
|
||||
// #6191: GPT-family codex models advertised their full 400K context window as
|
||||
// BOTH context_length and max_input_tokens, so coding agents never triggered
|
||||
// auto-compaction. The real usable input budget is smaller (~272K). These tests
|
||||
// pin the distinct-input-cap behavior and guard the contextLength fallback for
|
||||
// models that do NOT declare an explicit maxInputTokens.
|
||||
|
||||
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-gpt-input-cap-6191-"));
|
||||
process.env.DATA_DIR = TEST_DATA_DIR;
|
||||
|
||||
const core = await import("../../src/lib/db/core.ts");
|
||||
const modelCapabilities = await import("../../src/lib/modelCapabilities.ts");
|
||||
|
||||
function resetStorage() {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
test.beforeEach(() => {
|
||||
resetStorage();
|
||||
});
|
||||
|
||||
test.after(() => {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
test("codex gpt-5.5 reports max_input_tokens smaller than its context window (#6191)", () => {
|
||||
const caps = modelCapabilities.getResolvedModelCapabilities("codex/gpt-5.5");
|
||||
assert.equal(caps.contextWindow, 400000);
|
||||
assert.equal(caps.maxInputTokens, 272000);
|
||||
assert.ok(
|
||||
(caps.maxInputTokens ?? 0) < (caps.contextWindow ?? 0),
|
||||
"max_input_tokens must be strictly smaller than context_length so agents compact"
|
||||
);
|
||||
});
|
||||
|
||||
test("all codex gpt-5.5 effort variants carry the distinct input cap (#6191)", () => {
|
||||
for (const modelId of [
|
||||
"codex/gpt-5.5-xhigh",
|
||||
"codex/gpt-5.5-high",
|
||||
"codex/gpt-5.5-medium",
|
||||
"codex/gpt-5.5-low",
|
||||
]) {
|
||||
const caps = modelCapabilities.getResolvedModelCapabilities(modelId);
|
||||
assert.equal(caps.contextWindow, 400000, modelId);
|
||||
assert.equal(caps.maxInputTokens, 272000, modelId);
|
||||
}
|
||||
});
|
||||
|
||||
test("regression: a model without maxInputTokens still falls back to its context window", () => {
|
||||
// codex gpt-5.4 declares no maxInputTokens, so max_input_tokens must equal
|
||||
// the context window (the historical fallback) — no under-reporting.
|
||||
const caps = modelCapabilities.getResolvedModelCapabilities("codex/gpt-5.4");
|
||||
assert.ok((caps.contextWindow ?? 0) > 0, "gpt-5.4 should have a context window");
|
||||
assert.equal(
|
||||
caps.maxInputTokens,
|
||||
caps.contextWindow,
|
||||
"without an explicit input cap, max_input_tokens falls back to context_length"
|
||||
);
|
||||
});
|
||||
@@ -108,7 +108,8 @@ test("canonical model capability resolver lets exact synced metadata override gl
|
||||
|
||||
const codexGpt55 = modelCapabilities.getResolvedModelCapabilities("codex/gpt-5.5");
|
||||
assert.equal(codexGpt55.contextWindow, 400000);
|
||||
assert.equal(codexGpt55.maxInputTokens, 400000);
|
||||
// #6191: max_input_tokens is a distinct, smaller cap than the context window.
|
||||
assert.equal(codexGpt55.maxInputTokens, 272000);
|
||||
assert.equal(codexGpt55.maxOutputTokens, 128000);
|
||||
assert.equal(codexGpt55.supportsThinking, true);
|
||||
assert.equal(codexGpt55.supportsVision, true);
|
||||
|
||||
Reference in New Issue
Block a user