@theokit/sdk 4.9.0 → 4.9.1
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/cron.cjs +177 -168
- package/dist/cron.cjs.map +1 -1
- package/dist/cron.js +177 -168
- package/dist/cron.js.map +1 -1
- package/dist/eval.cjs +177 -168
- package/dist/eval.cjs.map +1 -1
- package/dist/eval.js +177 -168
- package/dist/eval.js.map +1 -1
- package/dist/index.cjs +177 -168
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +177 -168
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/eval.cjs
CHANGED
|
@@ -12948,6 +12948,172 @@ function assistantMessage(message) {
|
|
|
12948
12948
|
return result;
|
|
12949
12949
|
}
|
|
12950
12950
|
|
|
12951
|
+
// src/internal/llm/pool-aware-client.ts
|
|
12952
|
+
init_errors();
|
|
12953
|
+
|
|
12954
|
+
// src/internal/resilience/circuit-breaker.ts
|
|
12955
|
+
var DEFAULT_MAX_TIMEOUTS = 3;
|
|
12956
|
+
var DEFAULT_COOLDOWN_MS2 = 6e4;
|
|
12957
|
+
var CircuitBreaker = class {
|
|
12958
|
+
constructor(opts = {}) {
|
|
12959
|
+
this.opts = opts;
|
|
12960
|
+
}
|
|
12961
|
+
opts;
|
|
12962
|
+
states = /* @__PURE__ */ new Map();
|
|
12963
|
+
/** @returns true when the breaker is open and the call should be skipped. */
|
|
12964
|
+
shouldSkip(key) {
|
|
12965
|
+
const state2 = this.states.get(key);
|
|
12966
|
+
if (state2 === void 0) return false;
|
|
12967
|
+
if (state2.cooldownUntilMs === 0) return false;
|
|
12968
|
+
if (this.now() < state2.cooldownUntilMs) return true;
|
|
12969
|
+
state2.cooldownUntilMs = 0;
|
|
12970
|
+
state2.consecutiveTimeouts = 0;
|
|
12971
|
+
return false;
|
|
12972
|
+
}
|
|
12973
|
+
recordSuccess(key) {
|
|
12974
|
+
const state2 = this.states.get(key);
|
|
12975
|
+
if (state2 === void 0) return;
|
|
12976
|
+
state2.consecutiveTimeouts = 0;
|
|
12977
|
+
state2.cooldownUntilMs = 0;
|
|
12978
|
+
}
|
|
12979
|
+
recordTimeout(key) {
|
|
12980
|
+
const state2 = this.states.get(key) ?? { consecutiveTimeouts: 0, cooldownUntilMs: 0 };
|
|
12981
|
+
state2.consecutiveTimeouts += 1;
|
|
12982
|
+
if (state2.consecutiveTimeouts >= (this.opts.maxTimeouts ?? DEFAULT_MAX_TIMEOUTS)) {
|
|
12983
|
+
state2.cooldownUntilMs = this.now() + (this.opts.cooldownMs ?? DEFAULT_COOLDOWN_MS2);
|
|
12984
|
+
}
|
|
12985
|
+
this.states.set(key, state2);
|
|
12986
|
+
}
|
|
12987
|
+
/** @internal — tests inspect counter state. */
|
|
12988
|
+
inspect(key) {
|
|
12989
|
+
return this.states.get(key) ?? { consecutiveTimeouts: 0, cooldownUntilMs: 0 };
|
|
12990
|
+
}
|
|
12991
|
+
now() {
|
|
12992
|
+
return this.opts.now?.() ?? Date.now();
|
|
12993
|
+
}
|
|
12994
|
+
};
|
|
12995
|
+
|
|
12996
|
+
// src/internal/llm/pool-aware-client.ts
|
|
12997
|
+
init_retry();
|
|
12998
|
+
var PoolAwareLlmClient = class {
|
|
12999
|
+
constructor(pool, buildClient2, waitForAvailableMs = 3e4, resilience = {}) {
|
|
13000
|
+
this.pool = pool;
|
|
13001
|
+
this.buildClient = buildClient2;
|
|
13002
|
+
this.waitForAvailableMs = waitForAvailableMs;
|
|
13003
|
+
this.name = `pool-aware:${pool.provider}`;
|
|
13004
|
+
this.breaker = resilience.breaker ?? new CircuitBreaker();
|
|
13005
|
+
this.backoffBaseMs = resilience.backoffBaseMs;
|
|
13006
|
+
this.rng = resilience.rng;
|
|
13007
|
+
this.onRateLimit = resilience.onRateLimit;
|
|
13008
|
+
}
|
|
13009
|
+
pool;
|
|
13010
|
+
buildClient;
|
|
13011
|
+
waitForAvailableMs;
|
|
13012
|
+
name;
|
|
13013
|
+
onRateLimit;
|
|
13014
|
+
/** M2 #60 — provider-level circuit breaker (consecutive-failure). */
|
|
13015
|
+
breaker;
|
|
13016
|
+
backoffBaseMs;
|
|
13017
|
+
rng;
|
|
13018
|
+
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: stream() must serialize pool-select → build client → first-event probe → classify → retry/rotate/propagate. Extracting helpers fragments the linear narrative; the comments above each branch keep it readable.
|
|
13019
|
+
async *stream(request, signal) {
|
|
13020
|
+
if (this.breaker.shouldSkip(this.pool.provider)) {
|
|
13021
|
+
throw new NetworkError(`${this.pool.provider} circuit open \u2014 failing fast`, {
|
|
13022
|
+
code: "circuit_open"
|
|
13023
|
+
});
|
|
13024
|
+
}
|
|
13025
|
+
let hasRetried429 = false;
|
|
13026
|
+
while (true) {
|
|
13027
|
+
if (signal.aborted) throw abortError2(signal);
|
|
13028
|
+
let entry = await this.pool.select();
|
|
13029
|
+
if (entry === null) {
|
|
13030
|
+
if (this.waitForAvailableMs > 0) {
|
|
13031
|
+
const available = await this.pool.waitForAvailable(signal, {
|
|
13032
|
+
maxWaitMs: this.waitForAvailableMs
|
|
13033
|
+
});
|
|
13034
|
+
if (available) {
|
|
13035
|
+
entry = await this.pool.select();
|
|
13036
|
+
}
|
|
13037
|
+
}
|
|
13038
|
+
if (entry === null) {
|
|
13039
|
+
this.breaker.recordTimeout(this.pool.provider);
|
|
13040
|
+
throw new CredentialPoolExhaustedError(
|
|
13041
|
+
`All ${this.pool.provider} credentials exhausted; next retry available at ${this.nextRetryHint() ?? "unknown"}`,
|
|
13042
|
+
{ provider: this.pool.provider, nextRetryAt: this.nextRetryHint() }
|
|
13043
|
+
);
|
|
13044
|
+
}
|
|
13045
|
+
}
|
|
13046
|
+
const realClient = this.buildClient(entry.accessToken);
|
|
13047
|
+
const attempt = await tryFirstEvent(realClient, request, signal);
|
|
13048
|
+
if (attempt.kind === "ok") {
|
|
13049
|
+
this.breaker.recordSuccess(this.pool.provider);
|
|
13050
|
+
return yield* relayStream(attempt.generator, attempt.firstResult);
|
|
13051
|
+
}
|
|
13052
|
+
const decision = classifyAndDecide(attempt.error, hasRetried429);
|
|
13053
|
+
if (decision === "retry") {
|
|
13054
|
+
const backoffMs = computeBackoffMs({
|
|
13055
|
+
attempt: 0,
|
|
13056
|
+
...this.backoffBaseMs !== void 0 ? { baseMs: this.backoffBaseMs } : {},
|
|
13057
|
+
...this.rng !== void 0 ? { rng: this.rng } : {}
|
|
13058
|
+
});
|
|
13059
|
+
this.onRateLimit?.({ attempt: 1, retryAfterMs: backoffMs });
|
|
13060
|
+
await sleepWithAbort(backoffMs, signal);
|
|
13061
|
+
hasRetried429 = true;
|
|
13062
|
+
continue;
|
|
13063
|
+
}
|
|
13064
|
+
if (decision === "rotate") {
|
|
13065
|
+
try {
|
|
13066
|
+
await this.pool.markExhaustedAndRotate({
|
|
13067
|
+
entryId: entry.id,
|
|
13068
|
+
statusCode: attempt.error.metadata?.statusCode ?? inferStatusCode(attempt.error),
|
|
13069
|
+
...parseRetryAfterMs(attempt.error) !== void 0 ? { resetAtMs: parseRetryAfterMs(attempt.error) } : {}
|
|
13070
|
+
});
|
|
13071
|
+
} catch (persistErr) {
|
|
13072
|
+
process.stderr.write(
|
|
13073
|
+
`[theokit-sdk] credential-pool: persist failed during rotate; continuing in-memory: ${persistErr instanceof Error ? persistErr.message : String(persistErr)}
|
|
13074
|
+
`
|
|
13075
|
+
);
|
|
13076
|
+
}
|
|
13077
|
+
hasRetried429 = false;
|
|
13078
|
+
continue;
|
|
13079
|
+
}
|
|
13080
|
+
this.breaker.recordTimeout(this.pool.provider);
|
|
13081
|
+
throw attempt.error;
|
|
13082
|
+
}
|
|
13083
|
+
}
|
|
13084
|
+
/**
|
|
13085
|
+
* Earliest epoch ms among entries' `lastErrorResetAt` — best estimate
|
|
13086
|
+
* for the caller's `CredentialPoolExhaustedError.nextRetryAt`.
|
|
13087
|
+
*/
|
|
13088
|
+
nextRetryHint() {
|
|
13089
|
+
const resets = this.pool.list().map((e) => e.lastErrorResetAt).filter((v) => v !== void 0);
|
|
13090
|
+
return resets.length === 0 ? void 0 : Math.min(...resets);
|
|
13091
|
+
}
|
|
13092
|
+
};
|
|
13093
|
+
function classifyAndDecide(error, hasRetried429) {
|
|
13094
|
+
if (error instanceof NetworkError) return "propagate";
|
|
13095
|
+
if (error instanceof AuthenticationError) return "rotate";
|
|
13096
|
+
const status = error.metadata?.statusCode ?? 429;
|
|
13097
|
+
if (status === 402) return "rotate";
|
|
13098
|
+
return hasRetried429 ? "rotate" : "retry";
|
|
13099
|
+
}
|
|
13100
|
+
function parseRetryAfterMs(error) {
|
|
13101
|
+
const seconds = error.metadata?.retryAfter;
|
|
13102
|
+
if (typeof seconds === "number" && seconds > 0) {
|
|
13103
|
+
return Date.now() + seconds * 1e3;
|
|
13104
|
+
}
|
|
13105
|
+
return void 0;
|
|
13106
|
+
}
|
|
13107
|
+
function inferStatusCode(error) {
|
|
13108
|
+
if (error instanceof AuthenticationError) return 401;
|
|
13109
|
+
if (error instanceof RateLimitError) return 429;
|
|
13110
|
+
return 0;
|
|
13111
|
+
}
|
|
13112
|
+
function abortError2(signal) {
|
|
13113
|
+
if (signal.reason instanceof Error) return signal.reason;
|
|
13114
|
+
return new Error("AbortError");
|
|
13115
|
+
}
|
|
13116
|
+
|
|
12951
13117
|
// src/internal/llm/responses.ts
|
|
12952
13118
|
function messageToInputItems(message) {
|
|
12953
13119
|
const items = [];
|
|
@@ -12996,7 +13162,9 @@ function buildResponsesBody(request) {
|
|
|
12996
13162
|
for (const message of request.messages) {
|
|
12997
13163
|
for (const item of messageToInputItems(message)) input.push(item);
|
|
12998
13164
|
}
|
|
12999
|
-
const
|
|
13165
|
+
const slash = request.model.lastIndexOf("/");
|
|
13166
|
+
const model = slash >= 0 ? request.model.slice(slash + 1) : request.model;
|
|
13167
|
+
const body = { model, input, stream: true, store: false };
|
|
13000
13168
|
const instructions = collapseSystemText(request.system);
|
|
13001
13169
|
if (instructions.length > 0) body.instructions = instructions;
|
|
13002
13170
|
if (request.maxTokens !== void 0) body.max_output_tokens = request.maxTokens;
|
|
@@ -13130,175 +13298,16 @@ var ResponsesApiClient = class {
|
|
|
13130
13298
|
}
|
|
13131
13299
|
if (toolCalls.length > 0 && stopReason === "end_turn") stopReason = "tool_use";
|
|
13132
13300
|
yield { type: "stop", reason: stopReason };
|
|
13133
|
-
return makeLlmFinish({
|
|
13134
|
-
|
|
13135
|
-
|
|
13136
|
-
|
|
13137
|
-
|
|
13138
|
-
|
|
13139
|
-
|
|
13140
|
-
|
|
13141
|
-
var DEFAULT_MAX_TIMEOUTS = 3;
|
|
13142
|
-
var DEFAULT_COOLDOWN_MS2 = 6e4;
|
|
13143
|
-
var CircuitBreaker = class {
|
|
13144
|
-
constructor(opts = {}) {
|
|
13145
|
-
this.opts = opts;
|
|
13146
|
-
}
|
|
13147
|
-
opts;
|
|
13148
|
-
states = /* @__PURE__ */ new Map();
|
|
13149
|
-
/** @returns true when the breaker is open and the call should be skipped. */
|
|
13150
|
-
shouldSkip(key) {
|
|
13151
|
-
const state2 = this.states.get(key);
|
|
13152
|
-
if (state2 === void 0) return false;
|
|
13153
|
-
if (state2.cooldownUntilMs === 0) return false;
|
|
13154
|
-
if (this.now() < state2.cooldownUntilMs) return true;
|
|
13155
|
-
state2.cooldownUntilMs = 0;
|
|
13156
|
-
state2.consecutiveTimeouts = 0;
|
|
13157
|
-
return false;
|
|
13158
|
-
}
|
|
13159
|
-
recordSuccess(key) {
|
|
13160
|
-
const state2 = this.states.get(key);
|
|
13161
|
-
if (state2 === void 0) return;
|
|
13162
|
-
state2.consecutiveTimeouts = 0;
|
|
13163
|
-
state2.cooldownUntilMs = 0;
|
|
13164
|
-
}
|
|
13165
|
-
recordTimeout(key) {
|
|
13166
|
-
const state2 = this.states.get(key) ?? { consecutiveTimeouts: 0, cooldownUntilMs: 0 };
|
|
13167
|
-
state2.consecutiveTimeouts += 1;
|
|
13168
|
-
if (state2.consecutiveTimeouts >= (this.opts.maxTimeouts ?? DEFAULT_MAX_TIMEOUTS)) {
|
|
13169
|
-
state2.cooldownUntilMs = this.now() + (this.opts.cooldownMs ?? DEFAULT_COOLDOWN_MS2);
|
|
13170
|
-
}
|
|
13171
|
-
this.states.set(key, state2);
|
|
13172
|
-
}
|
|
13173
|
-
/** @internal — tests inspect counter state. */
|
|
13174
|
-
inspect(key) {
|
|
13175
|
-
return this.states.get(key) ?? { consecutiveTimeouts: 0, cooldownUntilMs: 0 };
|
|
13176
|
-
}
|
|
13177
|
-
now() {
|
|
13178
|
-
return this.opts.now?.() ?? Date.now();
|
|
13179
|
-
}
|
|
13180
|
-
};
|
|
13181
|
-
|
|
13182
|
-
// src/internal/llm/pool-aware-client.ts
|
|
13183
|
-
init_retry();
|
|
13184
|
-
var PoolAwareLlmClient = class {
|
|
13185
|
-
constructor(pool, buildClient2, waitForAvailableMs = 3e4, resilience = {}) {
|
|
13186
|
-
this.pool = pool;
|
|
13187
|
-
this.buildClient = buildClient2;
|
|
13188
|
-
this.waitForAvailableMs = waitForAvailableMs;
|
|
13189
|
-
this.name = `pool-aware:${pool.provider}`;
|
|
13190
|
-
this.breaker = resilience.breaker ?? new CircuitBreaker();
|
|
13191
|
-
this.backoffBaseMs = resilience.backoffBaseMs;
|
|
13192
|
-
this.rng = resilience.rng;
|
|
13193
|
-
this.onRateLimit = resilience.onRateLimit;
|
|
13194
|
-
}
|
|
13195
|
-
pool;
|
|
13196
|
-
buildClient;
|
|
13197
|
-
waitForAvailableMs;
|
|
13198
|
-
name;
|
|
13199
|
-
onRateLimit;
|
|
13200
|
-
/** M2 #60 — provider-level circuit breaker (consecutive-failure). */
|
|
13201
|
-
breaker;
|
|
13202
|
-
backoffBaseMs;
|
|
13203
|
-
rng;
|
|
13204
|
-
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: stream() must serialize pool-select → build client → first-event probe → classify → retry/rotate/propagate. Extracting helpers fragments the linear narrative; the comments above each branch keep it readable.
|
|
13205
|
-
async *stream(request, signal) {
|
|
13206
|
-
if (this.breaker.shouldSkip(this.pool.provider)) {
|
|
13207
|
-
throw new NetworkError(`${this.pool.provider} circuit open \u2014 failing fast`, {
|
|
13208
|
-
code: "circuit_open"
|
|
13209
|
-
});
|
|
13210
|
-
}
|
|
13211
|
-
let hasRetried429 = false;
|
|
13212
|
-
while (true) {
|
|
13213
|
-
if (signal.aborted) throw abortError2(signal);
|
|
13214
|
-
let entry = await this.pool.select();
|
|
13215
|
-
if (entry === null) {
|
|
13216
|
-
if (this.waitForAvailableMs > 0) {
|
|
13217
|
-
const available = await this.pool.waitForAvailable(signal, {
|
|
13218
|
-
maxWaitMs: this.waitForAvailableMs
|
|
13219
|
-
});
|
|
13220
|
-
if (available) {
|
|
13221
|
-
entry = await this.pool.select();
|
|
13222
|
-
}
|
|
13223
|
-
}
|
|
13224
|
-
if (entry === null) {
|
|
13225
|
-
this.breaker.recordTimeout(this.pool.provider);
|
|
13226
|
-
throw new CredentialPoolExhaustedError(
|
|
13227
|
-
`All ${this.pool.provider} credentials exhausted; next retry available at ${this.nextRetryHint() ?? "unknown"}`,
|
|
13228
|
-
{ provider: this.pool.provider, nextRetryAt: this.nextRetryHint() }
|
|
13229
|
-
);
|
|
13230
|
-
}
|
|
13231
|
-
}
|
|
13232
|
-
const realClient = this.buildClient(entry.accessToken);
|
|
13233
|
-
const attempt = await tryFirstEvent(realClient, request, signal);
|
|
13234
|
-
if (attempt.kind === "ok") {
|
|
13235
|
-
this.breaker.recordSuccess(this.pool.provider);
|
|
13236
|
-
return yield* relayStream(attempt.generator, attempt.firstResult);
|
|
13237
|
-
}
|
|
13238
|
-
const decision = classifyAndDecide(attempt.error, hasRetried429);
|
|
13239
|
-
if (decision === "retry") {
|
|
13240
|
-
const backoffMs = computeBackoffMs({
|
|
13241
|
-
attempt: 0,
|
|
13242
|
-
...this.backoffBaseMs !== void 0 ? { baseMs: this.backoffBaseMs } : {},
|
|
13243
|
-
...this.rng !== void 0 ? { rng: this.rng } : {}
|
|
13244
|
-
});
|
|
13245
|
-
this.onRateLimit?.({ attempt: 1, retryAfterMs: backoffMs });
|
|
13246
|
-
await sleepWithAbort(backoffMs, signal);
|
|
13247
|
-
hasRetried429 = true;
|
|
13248
|
-
continue;
|
|
13249
|
-
}
|
|
13250
|
-
if (decision === "rotate") {
|
|
13251
|
-
try {
|
|
13252
|
-
await this.pool.markExhaustedAndRotate({
|
|
13253
|
-
entryId: entry.id,
|
|
13254
|
-
statusCode: attempt.error.metadata?.statusCode ?? inferStatusCode(attempt.error),
|
|
13255
|
-
...parseRetryAfterMs(attempt.error) !== void 0 ? { resetAtMs: parseRetryAfterMs(attempt.error) } : {}
|
|
13256
|
-
});
|
|
13257
|
-
} catch (persistErr) {
|
|
13258
|
-
process.stderr.write(
|
|
13259
|
-
`[theokit-sdk] credential-pool: persist failed during rotate; continuing in-memory: ${persistErr instanceof Error ? persistErr.message : String(persistErr)}
|
|
13260
|
-
`
|
|
13261
|
-
);
|
|
13262
|
-
}
|
|
13263
|
-
hasRetried429 = false;
|
|
13264
|
-
continue;
|
|
13265
|
-
}
|
|
13266
|
-
this.breaker.recordTimeout(this.pool.provider);
|
|
13267
|
-
throw attempt.error;
|
|
13268
|
-
}
|
|
13269
|
-
}
|
|
13270
|
-
/**
|
|
13271
|
-
* Earliest epoch ms among entries' `lastErrorResetAt` — best estimate
|
|
13272
|
-
* for the caller's `CredentialPoolExhaustedError.nextRetryAt`.
|
|
13273
|
-
*/
|
|
13274
|
-
nextRetryHint() {
|
|
13275
|
-
const resets = this.pool.list().map((e) => e.lastErrorResetAt).filter((v) => v !== void 0);
|
|
13276
|
-
return resets.length === 0 ? void 0 : Math.min(...resets);
|
|
13301
|
+
return makeLlmFinish({
|
|
13302
|
+
stopReason,
|
|
13303
|
+
text,
|
|
13304
|
+
toolCalls,
|
|
13305
|
+
inputTokens,
|
|
13306
|
+
outputTokens,
|
|
13307
|
+
reasoningTokens
|
|
13308
|
+
});
|
|
13277
13309
|
}
|
|
13278
13310
|
};
|
|
13279
|
-
function classifyAndDecide(error, hasRetried429) {
|
|
13280
|
-
if (error instanceof NetworkError) return "propagate";
|
|
13281
|
-
if (error instanceof AuthenticationError) return "rotate";
|
|
13282
|
-
const status = error.metadata?.statusCode ?? 429;
|
|
13283
|
-
if (status === 402) return "rotate";
|
|
13284
|
-
return hasRetried429 ? "rotate" : "retry";
|
|
13285
|
-
}
|
|
13286
|
-
function parseRetryAfterMs(error) {
|
|
13287
|
-
const seconds = error.metadata?.retryAfter;
|
|
13288
|
-
if (typeof seconds === "number" && seconds > 0) {
|
|
13289
|
-
return Date.now() + seconds * 1e3;
|
|
13290
|
-
}
|
|
13291
|
-
return void 0;
|
|
13292
|
-
}
|
|
13293
|
-
function inferStatusCode(error) {
|
|
13294
|
-
if (error instanceof AuthenticationError) return 401;
|
|
13295
|
-
if (error instanceof RateLimitError) return 429;
|
|
13296
|
-
return 0;
|
|
13297
|
-
}
|
|
13298
|
-
function abortError2(signal) {
|
|
13299
|
-
if (signal.reason instanceof Error) return signal.reason;
|
|
13300
|
-
return new Error("AbortError");
|
|
13301
|
-
}
|
|
13302
13311
|
|
|
13303
13312
|
// src/internal/llm/vertex-anthropic.ts
|
|
13304
13313
|
init_errors();
|