@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.cjs
CHANGED
|
@@ -61,6 +61,60 @@ function isLocalBackendUrl(baseUrl) {
|
|
|
61
61
|
return false;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
// src/output-ceiling.ts
|
|
65
|
+
var TTL_MS = 24 * 60 * 60 * 1e3;
|
|
66
|
+
var MIN_PLAUSIBLE_CEILING = 256;
|
|
67
|
+
var MAX_PLAUSIBLE_CEILING = 1e7;
|
|
68
|
+
var ceilings = /* @__PURE__ */ new Map();
|
|
69
|
+
function outputRouteKey(route) {
|
|
70
|
+
return `${route.provider}\0${route.baseUrl ?? "default"}\0${route.model}`;
|
|
71
|
+
}
|
|
72
|
+
function parseOutputTokenCeiling(err) {
|
|
73
|
+
if (!(err instanceof Error)) return null;
|
|
74
|
+
const msg = err.message;
|
|
75
|
+
if (!/max_tokens|max output tokens|output tokens|completion tokens/i.test(msg)) return null;
|
|
76
|
+
if (err.statusCode === 402) return null;
|
|
77
|
+
if (/credit|billing|payment|insufficient|balance|quota/i.test(msg)) return null;
|
|
78
|
+
const patterns = [
|
|
79
|
+
// Anthropic: "max_tokens: 100000 > 64000, which is the maximum allowed…"
|
|
80
|
+
/max_tokens:\s*\d+\s*>\s*(\d+)/i,
|
|
81
|
+
// OpenAI: "…this model supports at most 16384 completion tokens"
|
|
82
|
+
/(?:at most|maximum of|limit of)\s+([\d,_]+)\s*(?:output|completion)?\s*tokens/i,
|
|
83
|
+
// Generic: "max output tokens is 8192" / "max_tokens must be <= 4096"
|
|
84
|
+
/(?:max_tokens|max output tokens)\b[^\d]{0,30}?([\d,_]+)/i
|
|
85
|
+
];
|
|
86
|
+
for (const pattern of patterns) {
|
|
87
|
+
const raw = msg.match(pattern)?.[1];
|
|
88
|
+
if (raw === void 0) continue;
|
|
89
|
+
const limit = Number(raw.replace(/[,_]/g, ""));
|
|
90
|
+
if (Number.isFinite(limit) && limit >= MIN_PLAUSIBLE_CEILING && limit <= MAX_PLAUSIBLE_CEILING) {
|
|
91
|
+
return limit;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
function rememberOutputCeiling(key, limit) {
|
|
97
|
+
const known = outputTokenCeiling(key);
|
|
98
|
+
ceilings.set(key, {
|
|
99
|
+
limit: known === void 0 ? limit : Math.min(known, limit),
|
|
100
|
+
expiresAt: Date.now() + TTL_MS
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
function outputTokenCeiling(key) {
|
|
104
|
+
const entry = ceilings.get(key);
|
|
105
|
+
if (!entry) return void 0;
|
|
106
|
+
if (entry.expiresAt <= Date.now()) {
|
|
107
|
+
ceilings.delete(key);
|
|
108
|
+
return void 0;
|
|
109
|
+
}
|
|
110
|
+
return entry.limit;
|
|
111
|
+
}
|
|
112
|
+
function clampOutputTokens(key, requested) {
|
|
113
|
+
const ceiling = outputTokenCeiling(key);
|
|
114
|
+
if (ceiling === void 0) return requested;
|
|
115
|
+
return requested === void 0 ? ceiling : Math.min(requested, ceiling);
|
|
116
|
+
}
|
|
117
|
+
|
|
64
118
|
// src/agent-loop.ts
|
|
65
119
|
var DEFAULT_MAX_TURNS = 300;
|
|
66
120
|
var DEFAULT_TOOL_TIMEOUT_MS = 3e5;
|
|
@@ -297,6 +351,16 @@ async function* agentLoop(messages, options) {
|
|
|
297
351
|
const MAX_OUTPUT_CONTINUATIONS = 2;
|
|
298
352
|
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.]";
|
|
299
353
|
let maxTokensContinuations = 0;
|
|
354
|
+
let providerCalls = 0;
|
|
355
|
+
let nonStreamingCalls = 0;
|
|
356
|
+
let warnedNonStreaming = false;
|
|
357
|
+
const MAX_OUTPUT_CEILING_RETRIES = 1;
|
|
358
|
+
let outputCeilingRetries = 0;
|
|
359
|
+
const ceilingKey = outputRouteKey({
|
|
360
|
+
provider: options.provider,
|
|
361
|
+
model: options.model,
|
|
362
|
+
baseUrl: options.baseUrl
|
|
363
|
+
});
|
|
300
364
|
const OVERLOAD_BASE_DELAY_MS = 2e3;
|
|
301
365
|
const OVERLOAD_MAX_DELAY_MS = 3e4;
|
|
302
366
|
const STREAM_FIRST_EVENT_TIMEOUT_MS = 45e3;
|
|
@@ -424,6 +488,18 @@ async function* agentLoop(messages, options) {
|
|
|
424
488
|
}, hardTimeoutMs);
|
|
425
489
|
try {
|
|
426
490
|
diag("stream_call", { nonStreaming: useNonStreamingFallback });
|
|
491
|
+
providerCalls++;
|
|
492
|
+
if (useNonStreamingFallback) nonStreamingCalls++;
|
|
493
|
+
if (!warnedNonStreaming && nonStreamingCalls >= 3) {
|
|
494
|
+
warnedNonStreaming = true;
|
|
495
|
+
diag("non_streaming_session", {
|
|
496
|
+
nonStreamingCalls,
|
|
497
|
+
providerCalls,
|
|
498
|
+
provider: options.provider,
|
|
499
|
+
model: options.model,
|
|
500
|
+
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"
|
|
501
|
+
});
|
|
502
|
+
}
|
|
427
503
|
streamCallStart = Date.now();
|
|
428
504
|
providerAttemptStartedAt = streamCallStart;
|
|
429
505
|
let liveApiKey = options.apiKey;
|
|
@@ -449,7 +525,9 @@ async function* agentLoop(messages, options) {
|
|
|
449
525
|
serverTools: options.serverTools,
|
|
450
526
|
toolChoice: options.toolChoice,
|
|
451
527
|
webSearch: options.webSearch,
|
|
452
|
-
|
|
528
|
+
// Clamped to whatever ceiling this route has already rejected us for
|
|
529
|
+
// (identity when nothing has been learned).
|
|
530
|
+
maxTokens: clampOutputTokens(ceilingKey, options.maxTokens),
|
|
453
531
|
temperature: options.temperature,
|
|
454
532
|
thinking: options.thinking,
|
|
455
533
|
apiKey: liveApiKey,
|
|
@@ -599,6 +677,29 @@ async function* agentLoop(messages, options) {
|
|
|
599
677
|
});
|
|
600
678
|
throw err;
|
|
601
679
|
}
|
|
680
|
+
const statedCeiling = parseOutputTokenCeiling(err);
|
|
681
|
+
if (statedCeiling !== null) {
|
|
682
|
+
rememberOutputCeiling(ceilingKey, statedCeiling);
|
|
683
|
+
diag("output_ceiling_learned", {
|
|
684
|
+
ceiling: statedCeiling,
|
|
685
|
+
requested: options.maxTokens,
|
|
686
|
+
provider: options.provider,
|
|
687
|
+
model: options.model
|
|
688
|
+
});
|
|
689
|
+
if (outputCeilingRetries < MAX_OUTPUT_CEILING_RETRIES) {
|
|
690
|
+
outputCeilingRetries++;
|
|
691
|
+
yield {
|
|
692
|
+
type: "retry",
|
|
693
|
+
reason: "provider_error",
|
|
694
|
+
attempt: outputCeilingRetries,
|
|
695
|
+
maxAttempts: MAX_OUTPUT_CEILING_RETRIES,
|
|
696
|
+
delayMs: 0,
|
|
697
|
+
silent: true
|
|
698
|
+
};
|
|
699
|
+
turn--;
|
|
700
|
+
continue;
|
|
701
|
+
}
|
|
702
|
+
}
|
|
602
703
|
if (isContextOverflow(err)) {
|
|
603
704
|
const overflowDetails = extractContextOverflowDetails(err);
|
|
604
705
|
diag("context_overflow_detected", {
|