mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-31 04:12:10 +03:00
fix(hyperagent): default 1M context for fable/opus/sonnet (#8496)
* fix(hyperagent): default 1M context for fable/opus/sonnet
HyperAgent Claude-family models (fable, opus, sonnet) were falling through
getTokenLimit to the generic 128k default. Agentic tool-loop prompts with
large catalogs then failed with context_length_exceeded (~137k tokens).
- defaultContextLength + per-model contextLength = 1_000_000 on hyperagent registry
- DEFAULT_LIMITS.hyperagent / ha = 1M
- Resolve hyperagent/ha (and fable/opus wire ids) before models.dev DB fallback
Verified: getTokenLimit('hyperagent','fable-latest') === 1000000; context-manager tests 30/30.
* fix(sse): scope hyperagent 1M context fix to the registry, drop unscoped model-name match
The step-1b branch in resolveTokenLimit() matched fable/opus/sonnet model
name substrings for ANY provider, before the models.dev DB lookup. That
collided with anthropic/claude, kiro, windsurf and bluesminds registries,
which serve the same Claude model ids (e.g. claude-opus-4.7-max,
claude-sonnet-5) with their own accurate per-model contextLength — those
were being clobbered to 1M instead of their real (often 200k) limit.
The registry-level defaultContextLength added on the hyperagent provider
entry already fixes the reported bug (getTokenLimit('hyperagent', ...) ===
1_000_000) on its own, scoped correctly by provider. Remove the redundant,
unscoped substring branch and add regression coverage for every hyperagent
fallback model id plus the cross-provider collision.
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
---------
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
This commit is contained in:
@@ -4,6 +4,9 @@ import { HYPERAGENT_FALLBACK_MODELS } from "../../../../services/hyperagentModel
|
||||
// HyperAgent (hyperagent.com) — unofficial reverse-engineered web session.
|
||||
// Auth: browser Cookie header. Chat: POST /api/threads/{id}/chat (SSE).
|
||||
// Credits: GET /api/settings/billing/usage → creditData.creditBlocks.
|
||||
/** Claude-family agent models on HyperAgent — 1M context (not the 128k openai default). */
|
||||
const HYPERAGENT_CONTEXT_LENGTH = 1_000_000;
|
||||
|
||||
export const hyperagentProvider: RegistryEntry = {
|
||||
id: "hyperagent",
|
||||
alias: "ha",
|
||||
@@ -13,8 +16,11 @@ export const hyperagentProvider: RegistryEntry = {
|
||||
authType: "apikey",
|
||||
authHeader: "cookie",
|
||||
passthroughModels: true,
|
||||
/** Used by getTokenLimit when model-specific context is missing. */
|
||||
defaultContextLength: HYPERAGENT_CONTEXT_LENGTH,
|
||||
models: HYPERAGENT_FALLBACK_MODELS.map((m) => ({
|
||||
id: m.id,
|
||||
name: m.name,
|
||||
contextLength: HYPERAGENT_CONTEXT_LENGTH,
|
||||
})),
|
||||
};
|
||||
|
||||
@@ -15,6 +15,10 @@ const DEFAULT_LIMITS: Record<string, number> = {
|
||||
openai: 128000,
|
||||
gemini: 1000000,
|
||||
codex: 400000,
|
||||
// HyperAgent Claude-family agents (fable/opus/sonnet) — 1M default; was falling
|
||||
// through to 128k and blocking normal agentic tool loops with huge catalogs.
|
||||
hyperagent: 1_000_000,
|
||||
ha: 1_000_000,
|
||||
default: 128000,
|
||||
};
|
||||
|
||||
@@ -205,6 +209,8 @@ function resolveTokenLimit(
|
||||
const envOverride = getEnvOverride(provider);
|
||||
if (envOverride) return { limit: envOverride, specific: true };
|
||||
|
||||
const lowerModel = (model || "").toLowerCase();
|
||||
|
||||
// 2. Check models.dev synced DB for per-model context limit
|
||||
if (model) {
|
||||
const dbLimit = getModelContextLimit(provider, model);
|
||||
@@ -219,15 +225,14 @@ function resolveTokenLimit(
|
||||
|
||||
// 4. Check if model name hints at a known limit
|
||||
if (model) {
|
||||
const lower = model.toLowerCase();
|
||||
if (lower.includes("claude")) return { limit: DEFAULT_LIMITS.claude, specific: true };
|
||||
if (lower.includes("gemini")) return { limit: DEFAULT_LIMITS.gemini, specific: true };
|
||||
if (lowerModel.includes("claude")) return { limit: DEFAULT_LIMITS.claude, specific: true };
|
||||
if (lowerModel.includes("gemini")) return { limit: DEFAULT_LIMITS.gemini, specific: true };
|
||||
if (
|
||||
lower.includes("gpt") ||
|
||||
lower.includes("o1") ||
|
||||
lower.includes("o3") ||
|
||||
lower.includes("o4") ||
|
||||
lower.includes("codex")
|
||||
lowerModel.includes("gpt") ||
|
||||
lowerModel.includes("o1") ||
|
||||
lowerModel.includes("o3") ||
|
||||
lowerModel.includes("o4") ||
|
||||
lowerModel.includes("codex")
|
||||
)
|
||||
return { limit: DEFAULT_LIMITS.codex, specific: true };
|
||||
}
|
||||
|
||||
@@ -19,40 +19,56 @@ export interface HyperAgentModel {
|
||||
subagent: "fable" | "opus" | "sonnet" | "haiku";
|
||||
/** Agent runtime for Claude family models. */
|
||||
runtimeId?: string;
|
||||
/** Context window for OmniRoute getTokenLimit / compression (Claude-family → 1M). */
|
||||
contextLength?: number;
|
||||
}
|
||||
|
||||
/** Default context for Fable / Opus / Sonnet on HyperAgent (1M tokens). */
|
||||
export const HYPERAGENT_DEFAULT_CONTEXT_LENGTH = 1_000_000;
|
||||
|
||||
/** Valid selectable models (live-validated). */
|
||||
export const HYPERAGENT_FALLBACK_MODELS: HyperAgentModel[] = [
|
||||
{ id: "fable-latest", name: "Fable 5", subagent: "fable", runtimeId: "claude-agents-sdk" },
|
||||
{
|
||||
id: "fable-latest",
|
||||
name: "Fable 5",
|
||||
subagent: "fable",
|
||||
runtimeId: "claude-agents-sdk",
|
||||
contextLength: HYPERAGENT_DEFAULT_CONTEXT_LENGTH,
|
||||
},
|
||||
{
|
||||
id: "claude-fable-5",
|
||||
name: "Claude Fable 5",
|
||||
subagent: "fable",
|
||||
runtimeId: "claude-agents-sdk",
|
||||
contextLength: HYPERAGENT_DEFAULT_CONTEXT_LENGTH,
|
||||
},
|
||||
{
|
||||
id: "opus-latest",
|
||||
name: "Claude Opus Latest",
|
||||
subagent: "opus",
|
||||
runtimeId: "claude-agents-sdk",
|
||||
contextLength: HYPERAGENT_DEFAULT_CONTEXT_LENGTH,
|
||||
},
|
||||
{
|
||||
id: "claude-opus-4-8",
|
||||
name: "Claude Opus 4.8",
|
||||
subagent: "opus",
|
||||
runtimeId: "claude-agents-sdk",
|
||||
contextLength: HYPERAGENT_DEFAULT_CONTEXT_LENGTH,
|
||||
},
|
||||
{
|
||||
id: "sonnet-latest",
|
||||
name: "Claude Sonnet Latest",
|
||||
subagent: "sonnet",
|
||||
runtimeId: "claude-agents-sdk",
|
||||
contextLength: HYPERAGENT_DEFAULT_CONTEXT_LENGTH,
|
||||
},
|
||||
{
|
||||
id: "claude-sonnet-5",
|
||||
name: "Claude Sonnet 5",
|
||||
subagent: "sonnet",
|
||||
runtimeId: "claude-agents-sdk",
|
||||
contextLength: HYPERAGENT_DEFAULT_CONTEXT_LENGTH,
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -34,6 +34,39 @@ test("getTokenLimit: default fallback", () => {
|
||||
assert.equal(getTokenLimit("unknown"), 128000);
|
||||
});
|
||||
|
||||
// Regression for #8496: hyperagent Claude-family agents (fable/opus/sonnet) must
|
||||
// resolve to the 1M context window for every fallback model id, driven solely by
|
||||
// the registry's `defaultContextLength` (open-sse/config/providers/registry/hyperagent) —
|
||||
// not by a provider-unscoped model-name substring match, which previously collided
|
||||
// with unrelated providers serving the same Claude model ids (see
|
||||
// "getTokenLimit: does not force 1M onto non-hyperagent providers" below).
|
||||
const HYPERAGENT_FALLBACK_MODEL_IDS = [
|
||||
"fable-latest",
|
||||
"claude-fable-5",
|
||||
"opus-latest",
|
||||
"claude-opus-4-8",
|
||||
"sonnet-latest",
|
||||
"claude-sonnet-5",
|
||||
];
|
||||
|
||||
for (const modelId of HYPERAGENT_FALLBACK_MODEL_IDS) {
|
||||
test(`getTokenLimit: hyperagent/${modelId} resolves to 1M context`, () => {
|
||||
assert.equal(getTokenLimit("hyperagent", modelId), 1_000_000);
|
||||
});
|
||||
|
||||
test(`getTokenLimit: ha (alias)/${modelId} resolves to 1M context`, () => {
|
||||
assert.equal(getTokenLimit("ha", modelId), 1_000_000);
|
||||
});
|
||||
}
|
||||
|
||||
test("getTokenLimit: does not force 1M onto non-hyperagent providers serving the same model ids", () => {
|
||||
// windsurf declares an explicit per-model contextLength of 200000 for this exact id —
|
||||
// a provider-unscoped substring match on "claude-opus-4" would have clobbered it to 1M.
|
||||
assert.equal(getTokenLimit("windsurf", "claude-opus-4.7-max"), 200000);
|
||||
// bluesminds likewise pins its own claude-opus-4-5 entry to 200000.
|
||||
assert.equal(getTokenLimit("bluesminds", "claude-opus-4-5"), 200000);
|
||||
});
|
||||
|
||||
// ─── compressContext ────────────────────────────────────────────────────────
|
||||
|
||||
test("compressContext: returns unchanged if fits", () => {
|
||||
|
||||
Reference in New Issue
Block a user