@kenkaiiii/gg-agent 5.49.4 → 5.49.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +102 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +102 -1
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -35,6 +35,60 @@ function isLocalBackendUrl(baseUrl) {
|
|
|
35
35
|
return false;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
// src/output-ceiling.ts
|
|
39
|
+
var TTL_MS = 24 * 60 * 60 * 1e3;
|
|
40
|
+
var MIN_PLAUSIBLE_CEILING = 256;
|
|
41
|
+
var MAX_PLAUSIBLE_CEILING = 1e7;
|
|
42
|
+
var ceilings = /* @__PURE__ */ new Map();
|
|
43
|
+
function outputRouteKey(route) {
|
|
44
|
+
return `${route.provider}\0${route.baseUrl ?? "default"}\0${route.model}`;
|
|
45
|
+
}
|
|
46
|
+
function parseOutputTokenCeiling(err) {
|
|
47
|
+
if (!(err instanceof Error)) return null;
|
|
48
|
+
const msg = err.message;
|
|
49
|
+
if (!/max_tokens|max output tokens|output tokens|completion tokens/i.test(msg)) return null;
|
|
50
|
+
if (err.statusCode === 402) return null;
|
|
51
|
+
if (/credit|billing|payment|insufficient|balance|quota/i.test(msg)) return null;
|
|
52
|
+
const patterns = [
|
|
53
|
+
// Anthropic: "max_tokens: 100000 > 64000, which is the maximum allowed…"
|
|
54
|
+
/max_tokens:\s*\d+\s*>\s*(\d+)/i,
|
|
55
|
+
// OpenAI: "…this model supports at most 16384 completion tokens"
|
|
56
|
+
/(?:at most|maximum of|limit of)\s+([\d,_]+)\s*(?:output|completion)?\s*tokens/i,
|
|
57
|
+
// Generic: "max output tokens is 8192" / "max_tokens must be <= 4096"
|
|
58
|
+
/(?:max_tokens|max output tokens)\b[^\d]{0,30}?([\d,_]+)/i
|
|
59
|
+
];
|
|
60
|
+
for (const pattern of patterns) {
|
|
61
|
+
const raw = msg.match(pattern)?.[1];
|
|
62
|
+
if (raw === void 0) continue;
|
|
63
|
+
const limit = Number(raw.replace(/[,_]/g, ""));
|
|
64
|
+
if (Number.isFinite(limit) && limit >= MIN_PLAUSIBLE_CEILING && limit <= MAX_PLAUSIBLE_CEILING) {
|
|
65
|
+
return limit;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
function rememberOutputCeiling(key, limit) {
|
|
71
|
+
const known = outputTokenCeiling(key);
|
|
72
|
+
ceilings.set(key, {
|
|
73
|
+
limit: known === void 0 ? limit : Math.min(known, limit),
|
|
74
|
+
expiresAt: Date.now() + TTL_MS
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
function outputTokenCeiling(key) {
|
|
78
|
+
const entry = ceilings.get(key);
|
|
79
|
+
if (!entry) return void 0;
|
|
80
|
+
if (entry.expiresAt <= Date.now()) {
|
|
81
|
+
ceilings.delete(key);
|
|
82
|
+
return void 0;
|
|
83
|
+
}
|
|
84
|
+
return entry.limit;
|
|
85
|
+
}
|
|
86
|
+
function clampOutputTokens(key, requested) {
|
|
87
|
+
const ceiling = outputTokenCeiling(key);
|
|
88
|
+
if (ceiling === void 0) return requested;
|
|
89
|
+
return requested === void 0 ? ceiling : Math.min(requested, ceiling);
|
|
90
|
+
}
|
|
91
|
+
|
|
38
92
|
// src/agent-loop.ts
|
|
39
93
|
var DEFAULT_MAX_TURNS = 300;
|
|
40
94
|
var DEFAULT_TOOL_TIMEOUT_MS = 3e5;
|
|
@@ -271,6 +325,16 @@ async function* agentLoop(messages, options) {
|
|
|
271
325
|
const MAX_OUTPUT_CONTINUATIONS = 2;
|
|
272
326
|
const MAX_TOKENS_CONTINUATION_PROMPT = "[Your previous response hit the output-token limit and was cut off. The text above is what was already delivered to the user. Continue exactly from where it stopped \u2014 do not repeat or restart it.]";
|
|
273
327
|
let maxTokensContinuations = 0;
|
|
328
|
+
let providerCalls = 0;
|
|
329
|
+
let nonStreamingCalls = 0;
|
|
330
|
+
let warnedNonStreaming = false;
|
|
331
|
+
const MAX_OUTPUT_CEILING_RETRIES = 1;
|
|
332
|
+
let outputCeilingRetries = 0;
|
|
333
|
+
const ceilingKey = outputRouteKey({
|
|
334
|
+
provider: options.provider,
|
|
335
|
+
model: options.model,
|
|
336
|
+
baseUrl: options.baseUrl
|
|
337
|
+
});
|
|
274
338
|
const OVERLOAD_BASE_DELAY_MS = 2e3;
|
|
275
339
|
const OVERLOAD_MAX_DELAY_MS = 3e4;
|
|
276
340
|
const STREAM_FIRST_EVENT_TIMEOUT_MS = 45e3;
|
|
@@ -398,6 +462,18 @@ async function* agentLoop(messages, options) {
|
|
|
398
462
|
}, hardTimeoutMs);
|
|
399
463
|
try {
|
|
400
464
|
diag("stream_call", { nonStreaming: useNonStreamingFallback });
|
|
465
|
+
providerCalls++;
|
|
466
|
+
if (useNonStreamingFallback) nonStreamingCalls++;
|
|
467
|
+
if (!warnedNonStreaming && nonStreamingCalls >= 3) {
|
|
468
|
+
warnedNonStreaming = true;
|
|
469
|
+
diag("non_streaming_session", {
|
|
470
|
+
nonStreamingCalls,
|
|
471
|
+
providerCalls,
|
|
472
|
+
provider: options.provider,
|
|
473
|
+
model: options.model,
|
|
474
|
+
impact: "streaming is disabled for this session after repeated stalls: failed turns are re-billed in full instead of resuming from partial output, and replies appear only when complete"
|
|
475
|
+
});
|
|
476
|
+
}
|
|
401
477
|
streamCallStart = Date.now();
|
|
402
478
|
providerAttemptStartedAt = streamCallStart;
|
|
403
479
|
let liveApiKey = options.apiKey;
|
|
@@ -423,7 +499,9 @@ async function* agentLoop(messages, options) {
|
|
|
423
499
|
serverTools: options.serverTools,
|
|
424
500
|
toolChoice: options.toolChoice,
|
|
425
501
|
webSearch: options.webSearch,
|
|
426
|
-
|
|
502
|
+
// Clamped to whatever ceiling this route has already rejected us for
|
|
503
|
+
// (identity when nothing has been learned).
|
|
504
|
+
maxTokens: clampOutputTokens(ceilingKey, options.maxTokens),
|
|
427
505
|
temperature: options.temperature,
|
|
428
506
|
thinking: options.thinking,
|
|
429
507
|
apiKey: liveApiKey,
|
|
@@ -573,6 +651,29 @@ async function* agentLoop(messages, options) {
|
|
|
573
651
|
});
|
|
574
652
|
throw err;
|
|
575
653
|
}
|
|
654
|
+
const statedCeiling = parseOutputTokenCeiling(err);
|
|
655
|
+
if (statedCeiling !== null) {
|
|
656
|
+
rememberOutputCeiling(ceilingKey, statedCeiling);
|
|
657
|
+
diag("output_ceiling_learned", {
|
|
658
|
+
ceiling: statedCeiling,
|
|
659
|
+
requested: options.maxTokens,
|
|
660
|
+
provider: options.provider,
|
|
661
|
+
model: options.model
|
|
662
|
+
});
|
|
663
|
+
if (outputCeilingRetries < MAX_OUTPUT_CEILING_RETRIES) {
|
|
664
|
+
outputCeilingRetries++;
|
|
665
|
+
yield {
|
|
666
|
+
type: "retry",
|
|
667
|
+
reason: "provider_error",
|
|
668
|
+
attempt: outputCeilingRetries,
|
|
669
|
+
maxAttempts: MAX_OUTPUT_CEILING_RETRIES,
|
|
670
|
+
delayMs: 0,
|
|
671
|
+
silent: true
|
|
672
|
+
};
|
|
673
|
+
turn--;
|
|
674
|
+
continue;
|
|
675
|
+
}
|
|
676
|
+
}
|
|
576
677
|
if (isContextOverflow(err)) {
|
|
577
678
|
const overflowDetails = extractContextOverflowDetails(err);
|
|
578
679
|
diag("context_overflow_detected", {
|