@earendil-works/pi-ai 1.2.0 → 1.4.0
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/README.md +83 -37
- package/dist/api/anthropic-messages.d.ts.map +1 -1
- package/dist/api/anthropic-messages.js +66 -64
- package/dist/api/anthropic-messages.js.map +1 -1
- package/dist/api/azure-openai-responses.d.ts.map +1 -1
- package/dist/api/azure-openai-responses.js +8 -4
- package/dist/api/azure-openai-responses.js.map +1 -1
- package/dist/api/bedrock-converse-stream.d.ts.map +1 -1
- package/dist/api/bedrock-converse-stream.js +28 -18
- package/dist/api/bedrock-converse-stream.js.map +1 -1
- package/dist/api/classifier-shared.d.ts.map +1 -1
- package/dist/api/classifier-shared.js +4 -3
- package/dist/api/classifier-shared.js.map +1 -1
- package/dist/api/cloudflare-workers-ai-system-one.d.ts.map +1 -1
- package/dist/api/cloudflare-workers-ai-system-one.js +8 -0
- package/dist/api/cloudflare-workers-ai-system-one.js.map +1 -1
- package/dist/api/google-generative-ai.js +4 -4
- package/dist/api/google-generative-ai.js.map +1 -1
- package/dist/api/google-shared.d.ts.map +1 -1
- package/dist/api/google-shared.js +3 -1
- package/dist/api/google-shared.js.map +1 -1
- package/dist/api/google-vertex.d.ts.map +1 -1
- package/dist/api/google-vertex.js +9 -9
- package/dist/api/google-vertex.js.map +1 -1
- package/dist/api/lazy.d.ts +7 -2
- package/dist/api/lazy.d.ts.map +1 -1
- package/dist/api/lazy.js +24 -5
- package/dist/api/lazy.js.map +1 -1
- package/dist/api/llama-cpp-classify.d.ts.map +1 -1
- package/dist/api/llama-cpp-classify.js +2 -1
- package/dist/api/llama-cpp-classify.js.map +1 -1
- package/dist/api/mistral-conversations.js +4 -4
- package/dist/api/mistral-conversations.js.map +1 -1
- package/dist/api/openai-codex-responses.d.ts.map +1 -1
- package/dist/api/openai-codex-responses.js +17 -11
- package/dist/api/openai-codex-responses.js.map +1 -1
- package/dist/api/openai-completions.d.ts.map +1 -1
- package/dist/api/openai-completions.js +43 -35
- package/dist/api/openai-completions.js.map +1 -1
- package/dist/api/openai-responses-shared.d.ts +26 -0
- package/dist/api/openai-responses-shared.d.ts.map +1 -1
- package/dist/api/openai-responses-shared.js +76 -11
- package/dist/api/openai-responses-shared.js.map +1 -1
- package/dist/api/openai-responses.d.ts.map +1 -1
- package/dist/api/openai-responses.js +38 -13
- package/dist/api/openai-responses.js.map +1 -1
- package/dist/api/openrouter-images.d.ts.map +1 -1
- package/dist/api/openrouter-images.js +4 -3
- package/dist/api/openrouter-images.js.map +1 -1
- package/dist/api/pi-messages.d.ts.map +1 -1
- package/dist/api/pi-messages.js +3 -3
- package/dist/api/pi-messages.js.map +1 -1
- package/dist/api/simple-options.d.ts.map +1 -1
- package/dist/api/simple-options.js +2 -1
- package/dist/api/simple-options.js.map +1 -1
- package/dist/api/system-one-shared.d.ts +29 -2
- package/dist/api/system-one-shared.d.ts.map +1 -1
- package/dist/api/system-one-shared.js +55 -3
- package/dist/api/system-one-shared.js.map +1 -1
- package/dist/api/transform-messages.d.ts +16 -1
- package/dist/api/transform-messages.d.ts.map +1 -1
- package/dist/api/transform-messages.js +28 -15
- package/dist/api/transform-messages.js.map +1 -1
- package/dist/api/typesafe-system-one.d.ts.map +1 -1
- package/dist/api/typesafe-system-one.js +10 -0
- package/dist/api/typesafe-system-one.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/models.d.ts +4 -0
- package/dist/models.d.ts.map +1 -1
- package/dist/models.js +19 -6
- package/dist/models.js.map +1 -1
- package/dist/providers/compat-schema.d.ts +171 -0
- package/dist/providers/compat-schema.d.ts.map +1 -1
- package/dist/providers/compat-schema.js +23 -2
- package/dist/providers/compat-schema.js.map +1 -1
- package/dist/providers/data/.manifest.json +1 -1
- package/dist/providers/data/amazon-bedrock.json +1 -1
- package/dist/providers/data/anthropic.json +1 -1
- package/dist/providers/data/azure.json +1 -1
- package/dist/providers/data/baseten.json +1 -1
- package/dist/providers/data/cerebras.json +1 -1
- package/dist/providers/data/cloudflare-ai-gateway.json +1 -1
- package/dist/providers/data/cloudflare-workers-ai.json +1 -1
- package/dist/providers/data/fireworks.json +1 -1
- package/dist/providers/data/github-copilot.json +1 -1
- package/dist/providers/data/google-vertex.json +1 -1
- package/dist/providers/data/google.json +1 -1
- package/dist/providers/data/groq.json +1 -1
- package/dist/providers/data/huggingface.json +1 -1
- package/dist/providers/data/kimi-coding.json +1 -1
- package/dist/providers/data/meta.json +1 -1
- package/dist/providers/data/minimax-cn.json +1 -1
- package/dist/providers/data/minimax.json +1 -1
- package/dist/providers/data/mistral.json +1 -1
- package/dist/providers/data/moonshotai-cn.json +1 -1
- package/dist/providers/data/nvidia.json +1 -1
- package/dist/providers/data/openai-codex.json +1 -1
- package/dist/providers/data/openai.json +1 -1
- package/dist/providers/data/opencode-go.json +1 -1
- package/dist/providers/data/opencode.json +1 -1
- package/dist/providers/data/openrouter.json +1 -1
- package/dist/providers/data/qwen-token-plan-cn.json +1 -1
- package/dist/providers/data/qwen-token-plan.json +1 -1
- package/dist/providers/data/radius.json +1 -1
- package/dist/providers/data/together.json +1 -1
- package/dist/providers/data/typesafe.json +1 -1
- package/dist/providers/data/vercel-ai-gateway.json +1 -1
- package/dist/providers/data/xai.json +1 -1
- package/dist/providers/data/xiaomi-token-plan-ams.json +1 -1
- package/dist/providers/data/xiaomi-token-plan-cn.json +1 -1
- package/dist/providers/data/xiaomi-token-plan-sgp.json +1 -1
- package/dist/providers/data/xiaomi.json +1 -1
- package/dist/providers/data/zai-coding-cn.json +1 -1
- package/dist/providers/kimi-coding.d.ts +1 -1
- package/dist/providers/kimi-coding.js +3 -3
- package/dist/providers/kimi-coding.js.map +1 -1
- package/dist/providers/model-schema.d.ts.map +1 -1
- package/dist/providers/model-schema.js +4 -2
- package/dist/providers/model-schema.js.map +1 -1
- package/dist/providers/opencode-go.d.ts +3 -1
- package/dist/providers/opencode-go.d.ts.map +1 -1
- package/dist/providers/opencode-go.js +2 -0
- package/dist/providers/opencode-go.js.map +1 -1
- package/dist/telemetry.d.ts +288 -0
- package/dist/telemetry.d.ts.map +1 -0
- package/dist/telemetry.js +191 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/types.d.ts +18 -5
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/identity.d.ts +65 -0
- package/dist/utils/identity.d.ts.map +1 -0
- package/dist/utils/identity.js +141 -0
- package/dist/utils/identity.js.map +1 -0
- package/dist/utils/json-parse.d.ts +10 -0
- package/dist/utils/json-parse.d.ts.map +1 -1
- package/dist/utils/json-parse.js +26 -1
- package/dist/utils/json-parse.js.map +1 -1
- package/dist/utils/overflow.d.ts +1 -0
- package/dist/utils/overflow.d.ts.map +1 -1
- package/dist/utils/overflow.js +6 -1
- package/dist/utils/overflow.js.map +1 -1
- package/dist/utils/retry.d.ts.map +1 -1
- package/dist/utils/retry.js +21 -8
- package/dist/utils/retry.js.map +1 -1
- package/dist/utils/transcript.d.ts +22 -8
- package/dist/utils/transcript.d.ts.map +1 -1
- package/dist/utils/transcript.js +29 -18
- package/dist/utils/transcript.js.map +1 -1
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +3 -0
- package/dist/utils/validation.js.map +1 -1
- package/dist/utils/version.d.ts +2 -0
- package/dist/utils/version.d.ts.map +1 -0
- package/dist/utils/version.js +3 -0
- package/dist/utils/version.js.map +1 -0
- package/package.json +2 -2
- package/dist/utils/assistant-message-frame.d.ts +0 -77
- package/dist/utils/assistant-message-frame.d.ts.map +0 -1
- package/dist/utils/assistant-message-frame.js +0 -425
- package/dist/utils/assistant-message-frame.js.map +0 -1
- package/dist/utils/pi-user-agent.d.ts +0 -2
- package/dist/utils/pi-user-agent.d.ts.map +0 -1
- package/dist/utils/pi-user-agent.js +0 -12
- package/dist/utils/pi-user-agent.js.map +0 -1
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { Models, MutableModels } from "../models.ts";
|
|
2
|
+
/**
|
|
3
|
+
* The `User-Agent` sent to providers:
|
|
4
|
+
* - `"detailed"`: `pi/<version> (<platform>; <runtime>/<runtime version>; <arch>)`, or `pi/<version> (browser)`
|
|
5
|
+
* outside Node.js and Bun.
|
|
6
|
+
* - `"short"`: `pi`.
|
|
7
|
+
* - `{ custom }`: the given value.
|
|
8
|
+
*/
|
|
9
|
+
export type UserAgentMode = "short" | "detailed" | {
|
|
10
|
+
custom: string;
|
|
11
|
+
};
|
|
12
|
+
/** Who requests are attributed to. `getAttributionHeaders()` maps the fields to each provider's headers. */
|
|
13
|
+
export interface ClientAttribution {
|
|
14
|
+
/** App name: OpenRouter app title, NVIDIA invoke origin (first letter upper-cased), OpenCode client. */
|
|
15
|
+
name: string;
|
|
16
|
+
/** App URL: OpenRouter `HTTP-Referer`, the app's identity in OpenRouter's rankings, grouped by origin. */
|
|
17
|
+
url: string;
|
|
18
|
+
/** App categories, e.g. `["cli-agent"]`: OpenRouter `X-OpenRouter-Categories`, comma-separated. */
|
|
19
|
+
categories?: string[];
|
|
20
|
+
}
|
|
21
|
+
/** pi's attribution, used by pi-agent-core, pi-durable, and the coding agent. */
|
|
22
|
+
export declare const PI_ATTRIBUTION: ClientAttribution;
|
|
23
|
+
/**
|
|
24
|
+
* How pi-ai identifies the calling app to providers. Every field is optional; absent fields use pi-ai's defaults:
|
|
25
|
+
* a detailed `User-Agent` with pi-ai's version and no attribution headers. Plain JSON, so it can be persisted.
|
|
26
|
+
*/
|
|
27
|
+
export interface ClientIdentity {
|
|
28
|
+
/** Default: `"detailed"`. */
|
|
29
|
+
userAgent?: UserAgentMode;
|
|
30
|
+
/** Provider attribution headers. Absent or `null`: none; `null` also turns off attribution set by a merged identity. */
|
|
31
|
+
attribution?: ClientAttribution | null;
|
|
32
|
+
/** Version in the detailed `User-Agent`. Default: the pi-ai version. */
|
|
33
|
+
version?: string;
|
|
34
|
+
}
|
|
35
|
+
/** The provider and endpoint a request goes to. */
|
|
36
|
+
export interface ClientIdentityTarget {
|
|
37
|
+
readonly provider: string;
|
|
38
|
+
readonly baseUrl: string;
|
|
39
|
+
}
|
|
40
|
+
/** Merge identities field by field; later sources win and undefined fields are skipped. */
|
|
41
|
+
export declare function mergeClientIdentity(...sources: (ClientIdentity | undefined)[]): ClientIdentity | undefined;
|
|
42
|
+
/** The version in the detailed `User-Agent`. */
|
|
43
|
+
export declare function getClientVersion(identity?: ClientIdentity): string;
|
|
44
|
+
/** The `User-Agent` value of an identity. */
|
|
45
|
+
export declare function formatUserAgent(identity?: ClientIdentity): string;
|
|
46
|
+
/** Provider attribution headers of an identity for a request target. Empty when attribution is off. */
|
|
47
|
+
export declare function getAttributionHeaders(target: ClientIdentityTarget, identity?: ClientIdentity): Record<string, string>;
|
|
48
|
+
/**
|
|
49
|
+
* OpenCode's per-conversation routing header for requests to OpenCode, including custom providers pointing at it.
|
|
50
|
+
* Undefined without a session or for other targets.
|
|
51
|
+
*/
|
|
52
|
+
export declare function getOpenCodeSessionHeaders(target: ClientIdentityTarget, sessionId: string | undefined): Record<string, string> | undefined;
|
|
53
|
+
/**
|
|
54
|
+
* Default headers an identity adds to a request: `User-Agent` and provider attribution. APIs apply them below model,
|
|
55
|
+
* provider, and request headers, so configured headers override them and `null` request headers remove them.
|
|
56
|
+
*/
|
|
57
|
+
export declare function getIdentityHeaders(target: ClientIdentityTarget, identity?: ClientIdentity): Record<string, string>;
|
|
58
|
+
/**
|
|
59
|
+
* `models` with `identity` as the default identity of its requests: a request's `identity` overrides it field by
|
|
60
|
+
* field, and it overrides the identity `models` was created with. Everything else is delegated unchanged; a
|
|
61
|
+
* `MutableModels` stays mutable.
|
|
62
|
+
*/
|
|
63
|
+
export declare function withClientIdentity(models: MutableModels, identity: ClientIdentity): MutableModels;
|
|
64
|
+
export declare function withClientIdentity(models: Models, identity: ClientIdentity): Models;
|
|
65
|
+
//# sourceMappingURL=identity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../src/utils/identity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAG1D;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,UAAU,GAAG;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtE,4GAA4G;AAC5G,MAAM,WAAW,iBAAiB;IACjC,wGAAwG;IACxG,IAAI,EAAE,MAAM,CAAC;IACb,0GAA0G;IAC1G,GAAG,EAAE,MAAM,CAAC;IACZ,mGAAmG;IACnG,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,iFAAiF;AACjF,eAAO,MAAM,cAAc,EAAE,iBAAoF,CAAC;AAElH;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC9B,6BAA6B;IAC7B,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,wHAAwH;IACxH,WAAW,CAAC,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACvC,wEAAwE;IACxE,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,mDAAmD;AACnD,MAAM,WAAW,oBAAoB;IACpC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB;AAKD,2FAA2F;AAC3F,wBAAgB,mBAAmB,CAAC,GAAG,OAAO,EAAE,CAAC,cAAc,GAAG,SAAS,CAAC,EAAE,GAAG,cAAc,GAAG,SAAS,CAU1G;AAED,gDAAgD;AAChD,wBAAgB,gBAAgB,CAAC,QAAQ,CAAC,EAAE,cAAc,GAAG,MAAM,CAElE;AAUD,6CAA6C;AAC7C,wBAAgB,eAAe,CAAC,QAAQ,CAAC,EAAE,cAAc,GAAG,MAAM,CAOjE;AAwBD,uGAAuG;AACvG,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,oBAAoB,EAAE,QAAQ,CAAC,EAAE,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAiBrH;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CACxC,MAAM,EAAE,oBAAoB,EAC5B,SAAS,EAAE,MAAM,GAAG,SAAS,GAC3B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAEpC;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,oBAAoB,EAAE,QAAQ,CAAC,EAAE,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAElH;AAMD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,aAAa,EAAE,QAAQ,EAAE,cAAc,GAAG,aAAa,CAAC;AACnG,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,GAAG,MAAM,CAAC"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { PI_AI_VERSION } from "./version.js";
|
|
2
|
+
/** pi's attribution, used by pi-agent-core, pi-durable, and the coding agent. */
|
|
3
|
+
export const PI_ATTRIBUTION = { name: "pi", url: "https://pi.dev", categories: ["cli-agent"] };
|
|
4
|
+
const NVIDIA_NIM_HOST = "integrate.api.nvidia.com";
|
|
5
|
+
const OPENCODE_HOST = "opencode.ai";
|
|
6
|
+
/** Merge identities field by field; later sources win and undefined fields are skipped. */
|
|
7
|
+
export function mergeClientIdentity(...sources) {
|
|
8
|
+
let merged;
|
|
9
|
+
for (const source of sources) {
|
|
10
|
+
if (!source)
|
|
11
|
+
continue;
|
|
12
|
+
merged = { ...merged };
|
|
13
|
+
if (source.userAgent !== undefined)
|
|
14
|
+
merged.userAgent = source.userAgent;
|
|
15
|
+
if (source.attribution !== undefined)
|
|
16
|
+
merged.attribution = source.attribution;
|
|
17
|
+
if (source.version !== undefined)
|
|
18
|
+
merged.version = source.version;
|
|
19
|
+
}
|
|
20
|
+
return merged;
|
|
21
|
+
}
|
|
22
|
+
/** The version in the detailed `User-Agent`. */
|
|
23
|
+
export function getClientVersion(identity) {
|
|
24
|
+
return identity?.version ?? PI_AI_VERSION;
|
|
25
|
+
}
|
|
26
|
+
function runtimeDescription() {
|
|
27
|
+
if (typeof process === "undefined")
|
|
28
|
+
return undefined;
|
|
29
|
+
const versions = process.versions;
|
|
30
|
+
if (versions?.bun)
|
|
31
|
+
return `bun/${versions.bun}`;
|
|
32
|
+
if (versions?.node)
|
|
33
|
+
return `node/${process.version}`;
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
/** The `User-Agent` value of an identity. */
|
|
37
|
+
export function formatUserAgent(identity) {
|
|
38
|
+
const mode = identity?.userAgent ?? "detailed";
|
|
39
|
+
if (typeof mode === "object")
|
|
40
|
+
return mode.custom;
|
|
41
|
+
if (mode === "short")
|
|
42
|
+
return "pi";
|
|
43
|
+
const product = `pi/${getClientVersion(identity)}`;
|
|
44
|
+
const runtime = runtimeDescription();
|
|
45
|
+
return runtime ? `${product} (${process.platform}; ${runtime}; ${process.arch})` : `${product} (browser)`;
|
|
46
|
+
}
|
|
47
|
+
function matchesHost(baseUrl, expectedHost) {
|
|
48
|
+
try {
|
|
49
|
+
return new URL(baseUrl).hostname === expectedHost;
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function isOpenRouter(target) {
|
|
56
|
+
return target.provider === "openrouter" || target.baseUrl.includes("openrouter.ai");
|
|
57
|
+
}
|
|
58
|
+
function isNvidiaNim(target) {
|
|
59
|
+
return target.provider === "nvidia" || matchesHost(target.baseUrl, NVIDIA_NIM_HOST);
|
|
60
|
+
}
|
|
61
|
+
function isOpenCode(target) {
|
|
62
|
+
return (target.provider === "opencode" || target.provider === "opencode-go" || matchesHost(target.baseUrl, OPENCODE_HOST));
|
|
63
|
+
}
|
|
64
|
+
/** Provider attribution headers of an identity for a request target. Empty when attribution is off. */
|
|
65
|
+
export function getAttributionHeaders(target, identity) {
|
|
66
|
+
const attribution = identity?.attribution;
|
|
67
|
+
if (!attribution)
|
|
68
|
+
return {};
|
|
69
|
+
const { name, url, categories } = attribution;
|
|
70
|
+
if (isOpenRouter(target)) {
|
|
71
|
+
return {
|
|
72
|
+
"HTTP-Referer": url,
|
|
73
|
+
"X-OpenRouter-Title": name,
|
|
74
|
+
...(categories === undefined || categories.length === 0
|
|
75
|
+
? {}
|
|
76
|
+
: { "X-OpenRouter-Categories": categories.join(",") }),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
// NVIDIA expects a capitalized origin, such as `Pi`.
|
|
80
|
+
if (isNvidiaNim(target))
|
|
81
|
+
return { "X-BILLING-INVOKE-ORIGIN": name.charAt(0).toUpperCase() + name.slice(1) };
|
|
82
|
+
if (isOpenCode(target))
|
|
83
|
+
return { "x-opencode-client": name };
|
|
84
|
+
return {};
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* OpenCode's per-conversation routing header for requests to OpenCode, including custom providers pointing at it.
|
|
88
|
+
* Undefined without a session or for other targets.
|
|
89
|
+
*/
|
|
90
|
+
export function getOpenCodeSessionHeaders(target, sessionId) {
|
|
91
|
+
return sessionId && isOpenCode(target) ? { "x-opencode-session": sessionId } : undefined;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Default headers an identity adds to a request: `User-Agent` and provider attribution. APIs apply them below model,
|
|
95
|
+
* provider, and request headers, so configured headers override them and `null` request headers remove them.
|
|
96
|
+
*/
|
|
97
|
+
export function getIdentityHeaders(target, identity) {
|
|
98
|
+
return { "User-Agent": formatUserAgent(identity), ...getAttributionHeaders(target, identity) };
|
|
99
|
+
}
|
|
100
|
+
function isMutableModels(models) {
|
|
101
|
+
return "setProvider" in models && "deleteProvider" in models && "clearProviders" in models;
|
|
102
|
+
}
|
|
103
|
+
export function withClientIdentity(models, identity) {
|
|
104
|
+
const withIdentity = (options) => ({ ...options, identity: mergeClientIdentity(identity, options?.identity) });
|
|
105
|
+
const wrapped = {
|
|
106
|
+
getProviders: () => models.getProviders(),
|
|
107
|
+
getProvider: (id) => models.getProvider(id),
|
|
108
|
+
getModels: (provider) => models.getModels(provider),
|
|
109
|
+
getModel: (provider, id) => models.getModel(provider, id),
|
|
110
|
+
getModelsOfType: (type, provider) => models.getModelsOfType(type, provider),
|
|
111
|
+
getModelOfType: (type, provider, id) => models.getModelOfType(type, provider, id),
|
|
112
|
+
getAllModels: (provider) => models.getAllModels(provider),
|
|
113
|
+
refresh: (options) => models.refresh(options),
|
|
114
|
+
checkAuth: (providerId, options) => models.checkAuth(providerId, options),
|
|
115
|
+
getAvailable: (providerId, options) => models.getAvailable(providerId, options),
|
|
116
|
+
getAvailableOfType: (type, providerId, options) => models.getAvailableOfType(type, providerId, options),
|
|
117
|
+
getAllAvailable: (providerId, options) => models.getAllAvailable(providerId, options),
|
|
118
|
+
getAuth: models.getAuth.bind(models),
|
|
119
|
+
login: (providerId, type, interaction, options) => models.login(providerId, type, interaction, options),
|
|
120
|
+
logout: (providerId, options) => models.logout(providerId, options),
|
|
121
|
+
stream: (model, context, options) => models.stream(model, context, withIdentity(options)),
|
|
122
|
+
complete: (model, context, options) => models.complete(model, context, withIdentity(options)),
|
|
123
|
+
streamSimple: (model, context, options) => models.streamSimple(model, context, withIdentity(options)),
|
|
124
|
+
completeSimple: (model, context, options) => models.completeSimple(model, context, withIdentity(options)),
|
|
125
|
+
streamDeferred: (model, handle, options) => models.streamDeferred(model, handle, withIdentity(options)),
|
|
126
|
+
fetchDeferred: (model, handle, options) => models.fetchDeferred(model, handle, withIdentity(options)),
|
|
127
|
+
cancelDeferred: (model, handle, options) => models.cancelDeferred(model, handle, withIdentity(options)),
|
|
128
|
+
generateImages: (model, context, options) => models.generateImages(model, context, withIdentity(options)),
|
|
129
|
+
classify: (model, context, options) => models.classify(model, context, withIdentity(options)),
|
|
130
|
+
};
|
|
131
|
+
if (!isMutableModels(models))
|
|
132
|
+
return wrapped;
|
|
133
|
+
const mutable = {
|
|
134
|
+
...wrapped,
|
|
135
|
+
setProvider: (provider) => models.setProvider(provider),
|
|
136
|
+
deleteProvider: (id) => models.deleteProvider(id),
|
|
137
|
+
clearProviders: () => models.clearProviders(),
|
|
138
|
+
};
|
|
139
|
+
return mutable;
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=identity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/utils/identity.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Models, MutableModels } from \"../models.ts\";\nimport { PI_AI_VERSION } from \"./version.ts\";\n\n/**\n * The `User-Agent` sent to providers:\n * - `\"detailed\"`: `pi/<version> (<platform>; <runtime>/<runtime version>; <arch>)`, or `pi/<version> (browser)`\n * outside Node.js and Bun.\n * - `\"short\"`: `pi`.\n * - `{ custom }`: the given value.\n */\nexport type UserAgentMode = \"short\" | \"detailed\" | { custom: string };\n\n/** Who requests are attributed to. `getAttributionHeaders()` maps the fields to each provider's headers. */\nexport interface ClientAttribution {\n\t/** App name: OpenRouter app title, NVIDIA invoke origin (first letter upper-cased), OpenCode client. */\n\tname: string;\n\t/** App URL: OpenRouter `HTTP-Referer`, the app's identity in OpenRouter's rankings, grouped by origin. */\n\turl: string;\n\t/** App categories, e.g. `[\"cli-agent\"]`: OpenRouter `X-OpenRouter-Categories`, comma-separated. */\n\tcategories?: string[];\n}\n\n/** pi's attribution, used by pi-agent-core, pi-durable, and the coding agent. */\nexport const PI_ATTRIBUTION: ClientAttribution = { name: \"pi\", url: \"https://pi.dev\", categories: [\"cli-agent\"] };\n\n/**\n * How pi-ai identifies the calling app to providers. Every field is optional; absent fields use pi-ai's defaults:\n * a detailed `User-Agent` with pi-ai's version and no attribution headers. Plain JSON, so it can be persisted.\n */\nexport interface ClientIdentity {\n\t/** Default: `\"detailed\"`. */\n\tuserAgent?: UserAgentMode;\n\t/** Provider attribution headers. Absent or `null`: none; `null` also turns off attribution set by a merged identity. */\n\tattribution?: ClientAttribution | null;\n\t/** Version in the detailed `User-Agent`. Default: the pi-ai version. */\n\tversion?: string;\n}\n\n/** The provider and endpoint a request goes to. */\nexport interface ClientIdentityTarget {\n\treadonly provider: string;\n\treadonly baseUrl: string;\n}\n\nconst NVIDIA_NIM_HOST = \"integrate.api.nvidia.com\";\nconst OPENCODE_HOST = \"opencode.ai\";\n\n/** Merge identities field by field; later sources win and undefined fields are skipped. */\nexport function mergeClientIdentity(...sources: (ClientIdentity | undefined)[]): ClientIdentity | undefined {\n\tlet merged: ClientIdentity | undefined;\n\tfor (const source of sources) {\n\t\tif (!source) continue;\n\t\tmerged = { ...merged };\n\t\tif (source.userAgent !== undefined) merged.userAgent = source.userAgent;\n\t\tif (source.attribution !== undefined) merged.attribution = source.attribution;\n\t\tif (source.version !== undefined) merged.version = source.version;\n\t}\n\treturn merged;\n}\n\n/** The version in the detailed `User-Agent`. */\nexport function getClientVersion(identity?: ClientIdentity): string {\n\treturn identity?.version ?? PI_AI_VERSION;\n}\n\nfunction runtimeDescription(): string | undefined {\n\tif (typeof process === \"undefined\") return undefined;\n\tconst versions = process.versions;\n\tif (versions?.bun) return `bun/${versions.bun}`;\n\tif (versions?.node) return `node/${process.version}`;\n\treturn undefined;\n}\n\n/** The `User-Agent` value of an identity. */\nexport function formatUserAgent(identity?: ClientIdentity): string {\n\tconst mode = identity?.userAgent ?? \"detailed\";\n\tif (typeof mode === \"object\") return mode.custom;\n\tif (mode === \"short\") return \"pi\";\n\tconst product = `pi/${getClientVersion(identity)}`;\n\tconst runtime = runtimeDescription();\n\treturn runtime ? `${product} (${process.platform}; ${runtime}; ${process.arch})` : `${product} (browser)`;\n}\n\nfunction matchesHost(baseUrl: string, expectedHost: string): boolean {\n\ttry {\n\t\treturn new URL(baseUrl).hostname === expectedHost;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction isOpenRouter(target: ClientIdentityTarget): boolean {\n\treturn target.provider === \"openrouter\" || target.baseUrl.includes(\"openrouter.ai\");\n}\n\nfunction isNvidiaNim(target: ClientIdentityTarget): boolean {\n\treturn target.provider === \"nvidia\" || matchesHost(target.baseUrl, NVIDIA_NIM_HOST);\n}\n\nfunction isOpenCode(target: ClientIdentityTarget): boolean {\n\treturn (\n\t\ttarget.provider === \"opencode\" || target.provider === \"opencode-go\" || matchesHost(target.baseUrl, OPENCODE_HOST)\n\t);\n}\n\n/** Provider attribution headers of an identity for a request target. Empty when attribution is off. */\nexport function getAttributionHeaders(target: ClientIdentityTarget, identity?: ClientIdentity): Record<string, string> {\n\tconst attribution = identity?.attribution;\n\tif (!attribution) return {};\n\tconst { name, url, categories } = attribution;\n\tif (isOpenRouter(target)) {\n\t\treturn {\n\t\t\t\"HTTP-Referer\": url,\n\t\t\t\"X-OpenRouter-Title\": name,\n\t\t\t...(categories === undefined || categories.length === 0\n\t\t\t\t? {}\n\t\t\t\t: { \"X-OpenRouter-Categories\": categories.join(\",\") }),\n\t\t};\n\t}\n\t// NVIDIA expects a capitalized origin, such as `Pi`.\n\tif (isNvidiaNim(target)) return { \"X-BILLING-INVOKE-ORIGIN\": name.charAt(0).toUpperCase() + name.slice(1) };\n\tif (isOpenCode(target)) return { \"x-opencode-client\": name };\n\treturn {};\n}\n\n/**\n * OpenCode's per-conversation routing header for requests to OpenCode, including custom providers pointing at it.\n * Undefined without a session or for other targets.\n */\nexport function getOpenCodeSessionHeaders(\n\ttarget: ClientIdentityTarget,\n\tsessionId: string | undefined,\n): Record<string, string> | undefined {\n\treturn sessionId && isOpenCode(target) ? { \"x-opencode-session\": sessionId } : undefined;\n}\n\n/**\n * Default headers an identity adds to a request: `User-Agent` and provider attribution. APIs apply them below model,\n * provider, and request headers, so configured headers override them and `null` request headers remove them.\n */\nexport function getIdentityHeaders(target: ClientIdentityTarget, identity?: ClientIdentity): Record<string, string> {\n\treturn { \"User-Agent\": formatUserAgent(identity), ...getAttributionHeaders(target, identity) };\n}\n\nfunction isMutableModels(models: Models): models is MutableModels {\n\treturn \"setProvider\" in models && \"deleteProvider\" in models && \"clearProviders\" in models;\n}\n\n/**\n * `models` with `identity` as the default identity of its requests: a request's `identity` overrides it field by\n * field, and it overrides the identity `models` was created with. Everything else is delegated unchanged; a\n * `MutableModels` stays mutable.\n */\nexport function withClientIdentity(models: MutableModels, identity: ClientIdentity): MutableModels;\nexport function withClientIdentity(models: Models, identity: ClientIdentity): Models;\nexport function withClientIdentity(models: Models, identity: ClientIdentity): Models {\n\tconst withIdentity = <T extends { identity?: ClientIdentity }>(options: T | undefined): T =>\n\t\t({ ...options, identity: mergeClientIdentity(identity, options?.identity) }) as T;\n\tconst wrapped: Models = {\n\t\tgetProviders: () => models.getProviders(),\n\t\tgetProvider: (id) => models.getProvider(id),\n\t\tgetModels: (provider) => models.getModels(provider),\n\t\tgetModel: (provider, id) => models.getModel(provider, id),\n\t\tgetModelsOfType: (type, provider) => models.getModelsOfType(type, provider),\n\t\tgetModelOfType: (type, provider, id) => models.getModelOfType(type, provider, id),\n\t\tgetAllModels: (provider) => models.getAllModels(provider),\n\t\trefresh: (options) => models.refresh(options),\n\t\tcheckAuth: (providerId, options) => models.checkAuth(providerId, options),\n\t\tgetAvailable: (providerId, options) => models.getAvailable(providerId, options),\n\t\tgetAvailableOfType: (type, providerId, options) => models.getAvailableOfType(type, providerId, options),\n\t\tgetAllAvailable: (providerId, options) => models.getAllAvailable(providerId, options),\n\t\tgetAuth: models.getAuth.bind(models),\n\t\tlogin: (providerId, type, interaction, options) => models.login(providerId, type, interaction, options),\n\t\tlogout: (providerId, options) => models.logout(providerId, options),\n\t\tstream: (model, context, options) => models.stream(model, context, withIdentity(options)),\n\t\tcomplete: (model, context, options) => models.complete(model, context, withIdentity(options)),\n\t\tstreamSimple: (model, context, options) => models.streamSimple(model, context, withIdentity(options)),\n\t\tcompleteSimple: (model, context, options) => models.completeSimple(model, context, withIdentity(options)),\n\t\tstreamDeferred: (model, handle, options) => models.streamDeferred(model, handle, withIdentity(options)),\n\t\tfetchDeferred: (model, handle, options) => models.fetchDeferred(model, handle, withIdentity(options)),\n\t\tcancelDeferred: (model, handle, options) => models.cancelDeferred(model, handle, withIdentity(options)),\n\t\tgenerateImages: (model, context, options) => models.generateImages(model, context, withIdentity(options)),\n\t\tclassify: (model, context, options) => models.classify(model, context, withIdentity(options)),\n\t};\n\tif (!isMutableModels(models)) return wrapped;\n\tconst mutable: MutableModels = {\n\t\t...wrapped,\n\t\tsetProvider: (provider) => models.setProvider(provider),\n\t\tdeleteProvider: (id) => models.deleteProvider(id),\n\t\tclearProviders: () => models.clearProviders(),\n\t};\n\treturn mutable;\n}\n"]}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ToolCall } from "../types.ts";
|
|
1
2
|
/**
|
|
2
3
|
* Repairs malformed JSON string literals by:
|
|
3
4
|
* - escaping raw control characters inside strings
|
|
@@ -5,6 +6,15 @@
|
|
|
5
6
|
*/
|
|
6
7
|
export declare function repairJson(json: string): string;
|
|
7
8
|
export declare function parseJsonWithRepair<T>(json: string): T;
|
|
9
|
+
/**
|
|
10
|
+
* Parses the model's complete arguments text of a finished tool call.
|
|
11
|
+
*
|
|
12
|
+
* Unlike {@link parseStreamingJson}, this never keeps a partially parsed prefix: once the call is
|
|
13
|
+
* finished, text that is not a valid JSON object means the model sent malformed arguments. In that
|
|
14
|
+
* case `arguments` is `{}` and `argumentsError` describes the problem, including the text the
|
|
15
|
+
* model sent, so tool validation can report it back to the model.
|
|
16
|
+
*/
|
|
17
|
+
export declare function parseToolCallArguments(json: string | undefined): Pick<ToolCall, "arguments" | "argumentsError">;
|
|
8
18
|
/**
|
|
9
19
|
* Attempts to parse potentially incomplete JSON during streaming.
|
|
10
20
|
* Always returns a valid object, even if the JSON is incomplete.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"json-parse.d.ts","sourceRoot":"","sources":["../../src/utils/json-parse.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"json-parse.d.ts","sourceRoot":"","sources":["../../src/utils/json-parse.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AA0B5C;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAmD/C;AAED,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,CAUtD;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC,QAAQ,EAAE,WAAW,GAAG,gBAAgB,CAAC,CAc/G;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,CAAC,CAoBlG"}
|
package/dist/utils/json-parse.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { parse as partialParse } from "partial-json";
|
|
2
|
-
const VALID_JSON_ESCAPES = new Set(['"', "\\", "/", "b", "f", "n", "r", "t"
|
|
2
|
+
const VALID_JSON_ESCAPES = new Set(['"', "\\", "/", "b", "f", "n", "r", "t"]);
|
|
3
3
|
function isControlCharacter(char) {
|
|
4
4
|
const codePoint = char.codePointAt(0);
|
|
5
5
|
return codePoint !== undefined && codePoint >= 0x00 && codePoint <= 0x1f;
|
|
@@ -80,6 +80,31 @@ export function parseJsonWithRepair(json) {
|
|
|
80
80
|
throw error;
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* Parses the model's complete arguments text of a finished tool call.
|
|
85
|
+
*
|
|
86
|
+
* Unlike {@link parseStreamingJson}, this never keeps a partially parsed prefix: once the call is
|
|
87
|
+
* finished, text that is not a valid JSON object means the model sent malformed arguments. In that
|
|
88
|
+
* case `arguments` is `{}` and `argumentsError` describes the problem, including the text the
|
|
89
|
+
* model sent, so tool validation can report it back to the model.
|
|
90
|
+
*/
|
|
91
|
+
export function parseToolCallArguments(json) {
|
|
92
|
+
if (!json?.trim())
|
|
93
|
+
return { arguments: {} };
|
|
94
|
+
try {
|
|
95
|
+
const parsed = parseJsonWithRepair(json);
|
|
96
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
97
|
+
throw new Error("Tool arguments must be a JSON object");
|
|
98
|
+
}
|
|
99
|
+
return { arguments: parsed };
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
return {
|
|
103
|
+
arguments: {},
|
|
104
|
+
argumentsError: `${error instanceof Error ? error.message : String(error)}\n\nReceived arguments:\n${json}`,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
}
|
|
83
108
|
/**
|
|
84
109
|
* Attempts to parse potentially incomplete JSON during streaming.
|
|
85
110
|
* Always returns a valid object, even if the JSON is incomplete.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"json-parse.js","sourceRoot":"","sources":["../../src/utils/json-parse.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,IAAI,YAAY,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"json-parse.js","sourceRoot":"","sources":["../../src/utils/json-parse.ts"],"names":[],"mappings":"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","sourcesContent":["import { parse as partialParse } from \"partial-json\";\nimport type { ToolCall } from \"../types.ts\";\n\nconst VALID_JSON_ESCAPES = new Set(['\"', \"\\\\\", \"/\", \"b\", \"f\", \"n\", \"r\", \"t\"]);\n\nfunction isControlCharacter(char: string): boolean {\n\tconst codePoint = char.codePointAt(0);\n\treturn codePoint !== undefined && codePoint >= 0x00 && codePoint <= 0x1f;\n}\n\nfunction escapeControlCharacter(char: string): string {\n\tswitch (char) {\n\t\tcase \"\\b\":\n\t\t\treturn \"\\\\b\";\n\t\tcase \"\\f\":\n\t\t\treturn \"\\\\f\";\n\t\tcase \"\\n\":\n\t\t\treturn \"\\\\n\";\n\t\tcase \"\\r\":\n\t\t\treturn \"\\\\r\";\n\t\tcase \"\\t\":\n\t\t\treturn \"\\\\t\";\n\t\tdefault:\n\t\t\treturn `\\\\u${char.codePointAt(0)?.toString(16).padStart(4, \"0\") ?? \"0000\"}`;\n\t}\n}\n\n/**\n * Repairs malformed JSON string literals by:\n * - escaping raw control characters inside strings\n * - doubling backslashes before invalid escape characters\n */\nexport function repairJson(json: string): string {\n\tlet repaired = \"\";\n\tlet inString = false;\n\n\tfor (let index = 0; index < json.length; index++) {\n\t\tconst char = json[index];\n\n\t\tif (!inString) {\n\t\t\trepaired += char;\n\t\t\tif (char === '\"') {\n\t\t\t\tinString = true;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (char === '\"') {\n\t\t\trepaired += char;\n\t\t\tinString = false;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (char === \"\\\\\") {\n\t\t\tconst nextChar = json[index + 1];\n\t\t\tif (nextChar === undefined) {\n\t\t\t\trepaired += \"\\\\\\\\\";\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (nextChar === \"u\") {\n\t\t\t\tconst unicodeDigits = json.slice(index + 2, index + 6);\n\t\t\t\tif (/^[0-9a-fA-F]{4}$/.test(unicodeDigits)) {\n\t\t\t\t\trepaired += `\\\\u${unicodeDigits}`;\n\t\t\t\t\tindex += 5;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (VALID_JSON_ESCAPES.has(nextChar)) {\n\t\t\t\trepaired += `\\\\${nextChar}`;\n\t\t\t\tindex += 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\trepaired += \"\\\\\\\\\";\n\t\t\tcontinue;\n\t\t}\n\n\t\trepaired += isControlCharacter(char) ? escapeControlCharacter(char) : char;\n\t}\n\n\treturn repaired;\n}\n\nexport function parseJsonWithRepair<T>(json: string): T {\n\ttry {\n\t\treturn JSON.parse(json) as T;\n\t} catch (error) {\n\t\tconst repairedJson = repairJson(json);\n\t\tif (repairedJson !== json) {\n\t\t\treturn JSON.parse(repairedJson) as T;\n\t\t}\n\t\tthrow error;\n\t}\n}\n\n/**\n * Parses the model's complete arguments text of a finished tool call.\n *\n * Unlike {@link parseStreamingJson}, this never keeps a partially parsed prefix: once the call is\n * finished, text that is not a valid JSON object means the model sent malformed arguments. In that\n * case `arguments` is `{}` and `argumentsError` describes the problem, including the text the\n * model sent, so tool validation can report it back to the model.\n */\nexport function parseToolCallArguments(json: string | undefined): Pick<ToolCall, \"arguments\" | \"argumentsError\"> {\n\tif (!json?.trim()) return { arguments: {} };\n\ttry {\n\t\tconst parsed = parseJsonWithRepair<ToolCall[\"arguments\"] | null>(json);\n\t\tif (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n\t\t\tthrow new Error(\"Tool arguments must be a JSON object\");\n\t\t}\n\t\treturn { arguments: parsed };\n\t} catch (error) {\n\t\treturn {\n\t\t\targuments: {},\n\t\t\targumentsError: `${error instanceof Error ? error.message : String(error)}\\n\\nReceived arguments:\\n${json}`,\n\t\t};\n\t}\n}\n\n/**\n * Attempts to parse potentially incomplete JSON during streaming.\n * Always returns a valid object, even if the JSON is incomplete.\n *\n * @param partialJson The partial JSON string from streaming\n * @returns Parsed object or empty object if parsing fails\n */\nexport function parseStreamingJson<T = Record<string, unknown>>(partialJson: string | undefined): T {\n\tif (!partialJson || partialJson.trim() === \"\") {\n\t\treturn {} as T;\n\t}\n\n\ttry {\n\t\treturn parseJsonWithRepair<T>(partialJson);\n\t} catch {\n\t\ttry {\n\t\t\tconst result = partialParse(partialJson);\n\t\t\treturn (result ?? {}) as T;\n\t\t} catch {\n\t\t\ttry {\n\t\t\t\tconst result = partialParse(repairJson(partialJson));\n\t\t\t\treturn (result ?? {}) as T;\n\t\t\t} catch {\n\t\t\t\treturn {} as T;\n\t\t\t}\n\t\t}\n\t}\n}\n"]}
|
package/dist/utils/overflow.d.ts
CHANGED
|
@@ -18,6 +18,7 @@ import type { AssistantMessage } from "../types.ts";
|
|
|
18
18
|
* - Google Gemini: "input token count exceeds the maximum"
|
|
19
19
|
* - xAI (Grok): "maximum prompt length is X but request contains Y"
|
|
20
20
|
* - Groq: "reduce the length of the messages"
|
|
21
|
+
* - GitHub Copilot: HTTP 413 "failed to parse request" (request byte-size overflow)
|
|
21
22
|
* - Cerebras: 400/413 status code (no body)
|
|
22
23
|
* - Mistral: "Prompt contains X tokens ... too large for model with Y maximum context length"
|
|
23
24
|
* - OpenRouter (most backends): "maximum context length is X tokens"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"overflow.d.ts","sourceRoot":"","sources":["../../src/utils/overflow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"overflow.d.ts","sourceRoot":"","sources":["../../src/utils/overflow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAqFpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,gBAAgB,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAkC5F;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAEhG;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,EAAE,CAE9C"}
|
package/dist/utils/overflow.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Regex patterns to detect context overflow errors from different providers.
|
|
3
3
|
*
|
|
4
4
|
* These patterns match error messages returned when the input exceeds
|
|
5
|
-
* the model's context window.
|
|
5
|
+
* the model's context window or the provider's request byte-size limit.
|
|
6
6
|
*
|
|
7
7
|
* Provider-specific patterns (with example error messages):
|
|
8
8
|
*
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
* - llama.cpp: "the request exceeds the available context size, try increasing it"
|
|
21
21
|
* - LM Studio: "tokens to keep from the initial prompt is greater than the context length"
|
|
22
22
|
* - GitHub Copilot: "prompt token count of X exceeds the limit of Y"
|
|
23
|
+
* - GitHub Copilot: 'github-copilot API error (413): {"message":"failed to parse request","code":""}'
|
|
23
24
|
* - MiniMax: "invalid params, context window exceeds limit"
|
|
24
25
|
* - Kimi For Coding: "Your request exceeded model token limit: X (requested: Y)"
|
|
25
26
|
* - DS4: "Prompt has X tokens, but the configured context size is Y tokens"
|
|
@@ -58,6 +59,9 @@ const OVERFLOW_PATTERNS = [
|
|
|
58
59
|
/context[_ ]length[_ ]exceeded/i, // Generic fallback
|
|
59
60
|
/too many tokens/i, // Generic fallback
|
|
60
61
|
/token limit exceeded/i, // Generic fallback
|
|
62
|
+
// HTTP 413 means request-size overflow regardless of provider or body text.
|
|
63
|
+
// Match raw SDK errors and formatProviderError's prefix, not a status mentioned in the body.
|
|
64
|
+
/^(?:[\w -]+ API error \(413\):|413(?:\s|:|$))/i,
|
|
61
65
|
];
|
|
62
66
|
const CEREBRAS_BODYLESS_OVERFLOW_PATTERN = /^4(?:00|13)\s*(?:status code)?\s*\(no body\)/i;
|
|
63
67
|
/**
|
|
@@ -93,6 +97,7 @@ const NON_OVERFLOW_PATTERNS = [
|
|
|
93
97
|
* - Google Gemini: "input token count exceeds the maximum"
|
|
94
98
|
* - xAI (Grok): "maximum prompt length is X but request contains Y"
|
|
95
99
|
* - Groq: "reduce the length of the messages"
|
|
100
|
+
* - GitHub Copilot: HTTP 413 "failed to parse request" (request byte-size overflow)
|
|
96
101
|
* - Cerebras: 400/413 status code (no body)
|
|
97
102
|
* - Mistral: "Prompt contains X tokens ... too large for model with Y maximum context length"
|
|
98
103
|
* - OpenRouter (most backends): "maximum context length is X tokens"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"overflow.js","sourceRoot":"","sources":["../../src/utils/overflow.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AssistantMessage } from \"../types.ts\";\n\n/**\n * Regex patterns to detect context overflow errors from different providers.\n *\n * These patterns match error messages returned when the input exceeds\n * the model's context window.\n *\n * Provider-specific patterns (with example error messages):\n *\n * - Anthropic: \"prompt is too long: 213462 tokens > 200000 maximum\"\n * - Anthropic: \"413 {\\\"error\\\":{\\\"type\\\":\\\"request_too_large\\\",\\\"message\\\":\\\"Request exceeds the maximum size\\\"}}\"\n * - OpenAI: \"Your input exceeds the context window of this model\"\n * - OpenAI/LiteLLM: \"Requested token count exceeds the model's maximum context length of 131072 tokens\"\n * - OpenAI-compatible: \"Input length (265330) exceeds model's maximum context length (262144).\"\n * - Google: \"The input token count (1196265) exceeds the maximum number of tokens allowed (1048575)\"\n * - xAI: \"This model's maximum prompt length is 131072 but the request contains 537812 tokens\"\n * - Groq: \"Please reduce the length of the messages or completion\"\n * - OpenRouter: \"This endpoint's maximum context length is X tokens. However, you requested about Y tokens\"\n * - OpenRouter/Poolside: \"Input length X exceeds the maximum allowed input length of Y tokens.\"\n * - Together AI: \"The input (X tokens) is longer than the model's context length (Y tokens).\"\n * - llama.cpp: \"the request exceeds the available context size, try increasing it\"\n * - LM Studio: \"tokens to keep from the initial prompt is greater than the context length\"\n * - GitHub Copilot: \"prompt token count of X exceeds the limit of Y\"\n * - MiniMax: \"invalid params, context window exceeds limit\"\n * - Kimi For Coding: \"Your request exceeded model token limit: X (requested: Y)\"\n * - DS4: \"Prompt has X tokens, but the configured context size is Y tokens\"\n * - Cerebras: \"400/413 status code (no body)\"\n * - Mistral: \"Prompt contains X tokens ... too large for model with Y maximum context length\"\n * - z.ai: `{\"code\":\"1261\",\"message\":\"Prompt too long\"}`, `{\"code\":\"1261\",\"message\":\"Prompt exceeds max length\"}` (CN endpoint), or silent overflow via usage.input > contextWindow\n * - Xiaomi MiMo: Truncates input to fill contextWindow exactly, then returns finish_reason \"length\"\n * with output=0 (no room left to generate). Detected via stopReason \"length\" + zero output +\n * input filling the context window.\n * - DashScope/Qwen: \"Range of input length should be [1, X]\" (HTTP 400 invalid_parameter_error)\n * - Ollama: Some deployments truncate silently, others return errors like \"prompt too long; exceeded max context length by X tokens\"\n */\nconst OVERFLOW_PATTERNS = [\n\t/prompt (?:is )?too long/i, // Anthropic and z.ai token overflow\n\t/prompt exceeds max length/i, // z.ai CN endpoint token overflow\n\t/request_too_large/i, // Anthropic request byte-size overflow (HTTP 413)\n\t/input is too long for requested model/i, // Amazon Bedrock\n\t/exceeds the context window/i, // OpenAI (Completions & Responses API)\n\t/exceeds (?:the )?(?:model'?s )?maximum context length(?: of [\\d,]+ tokens?|\\s*\\([\\d,]+\\))/i, // OpenAI-compatible proxies (LiteLLM)\n\t/input token count.*exceeds the maximum/i, // Google (Gemini)\n\t/maximum prompt length is \\d+/i, // xAI (Grok)\n\t/reduce the length of the messages/i, // Groq\n\t/maximum context length is \\d+ tokens/i, // OpenRouter (most backends)\n\t/exceeds (?:the )?maximum allowed input length of [\\d,]+ tokens?/i, // OpenRouter/Poolside\n\t/input \\(\\d+ tokens\\) is longer than the model'?s context length \\(\\d+ tokens\\)/i, // Together AI\n\t/exceeds the limit of \\d+/i, // GitHub Copilot\n\t/exceeds the available context size/i, // llama.cpp server\n\t/greater than the context length/i, // LM Studio\n\t/context window exceeds limit/i, // MiniMax\n\t/exceeded model token limit/i, // Kimi For Coding\n\t/too large for model with \\d+ maximum context length/i, // Mistral\n\t/prompt has [\\d,]+ tokens?, but the configured context size is [\\d,]+ tokens?/i, // DS4 server\n\t/model_context_window_exceeded/i, // z.ai non-standard finish_reason surfaced as error text\n\t/prompt too long; exceeded (?:max )?context length/i, // Ollama explicit overflow error\n\t/range of input length should be/i, // DashScope / Qwen Token Plan\n\t/context[_ ]length[_ ]exceeded/i, // Generic fallback\n\t/too many tokens/i, // Generic fallback\n\t/token limit exceeded/i, // Generic fallback\n];\n\nconst CEREBRAS_BODYLESS_OVERFLOW_PATTERN = /^4(?:00|13)\\s*(?:status code)?\\s*\\(no body\\)/i;\n\n/**\n * Patterns that indicate non-overflow errors (e.g. rate limiting, server errors).\n * Error messages matching any of these are excluded from overflow detection\n * even if they also match an OVERFLOW_PATTERN.\n *\n * Example: Bedrock formats throttling errors as \"ThrottlingException: Too many tokens,\n * please wait before trying again.\" which would match the /too many tokens/i overflow\n * pattern without this exclusion.\n */\nconst NON_OVERFLOW_PATTERNS = [\n\t/^(Throttling error|Service unavailable):/i, // AWS Bedrock non-overflow errors (human-readable prefixes from formatBedrockError)\n\t/rate limit/i, // Generic rate limiting\n\t/too many requests/i, // Generic HTTP 429 style\n];\n\n/**\n * Check if an assistant message represents a context overflow error.\n *\n * This handles three cases:\n * 1. Error-based overflow: Most providers return stopReason \"error\" with a\n * specific error message pattern.\n * 2. Silent overflow: Some providers accept overflow requests and return\n * successfully. For these, we check if usage.input exceeds the context window.\n * 3. Length-stop overflow: Xiaomi MiMo can return \"length\" with zero output when\n * the input fills the context window.\n *\n * ## Reliability by Provider\n *\n * **Reliable detection (returns error with detectable message):**\n * - Anthropic: \"prompt is too long: X tokens > Y maximum\" or \"request_too_large\"\n * - OpenAI (Completions & Responses): \"exceeds the context window\", \"exceeds the model's maximum context length of X tokens\", or \"exceeds model's maximum context length (X)\"\n * - Google Gemini: \"input token count exceeds the maximum\"\n * - xAI (Grok): \"maximum prompt length is X but request contains Y\"\n * - Groq: \"reduce the length of the messages\"\n * - Cerebras: 400/413 status code (no body)\n * - Mistral: \"Prompt contains X tokens ... too large for model with Y maximum context length\"\n * - OpenRouter (most backends): \"maximum context length is X tokens\"\n * - OpenRouter/Poolside: \"Input length X exceeds the maximum allowed input length of Y tokens.\"\n * - Together AI: \"The input (X tokens) is longer than the model's context length (Y tokens).\"\n * - llama.cpp: \"exceeds the available context size\"\n * - LM Studio: \"greater than the context length\"\n * - Kimi For Coding: \"exceeded model token limit: X (requested: Y)\"\n * - DS4: \"Prompt has X tokens, but the configured context size is Y tokens\"\n * - DashScope/Qwen: \"Range of input length should be [1, X]\"\n * - z.ai: \"Prompt too long\"\n *\n * **Unreliable detection:**\n * - z.ai: Sometimes accepts overflow silently (detectable via usage.input > contextWindow),\n * sometimes returns rate limit errors. Pass contextWindow param to detect silent overflow.\n * - Xiaomi MiMo: Truncates input to fit contextWindow then returns stopReason \"length\" with\n * output=0. Pass contextWindow param to detect via the \"filled context + zero output\" signal.\n * - Ollama: May truncate input silently for some setups, but may also return explicit\n * overflow errors that match the patterns above. Silent truncation still cannot be\n * detected here because we do not know the expected token count.\n *\n * ## Custom Providers\n *\n * If you've added custom models via settings.json, this function may not detect\n * overflow errors from those providers. To add support:\n *\n * 1. Send a request that exceeds the model's context window\n * 2. Check the errorMessage in the response\n * 3. Create a regex pattern that matches the error\n * 4. The pattern should be added to OVERFLOW_PATTERNS in this file, or\n * check the errorMessage yourself before calling this function\n *\n * @param message - The assistant message to check\n * @param contextWindow - Optional context window size for detecting silent overflow (z.ai)\n * @returns true if the message indicates a context overflow\n */\nexport function isContextOverflow(message: AssistantMessage, contextWindow?: number): boolean {\n\t// Case 1: Check error message patterns\n\tif (message.stopReason === \"error\" && message.errorMessage) {\n\t\t// Skip messages matching known non-overflow patterns (e.g. throttling / rate-limit)\n\t\tconst isNonOverflow = NON_OVERFLOW_PATTERNS.some((p) => p.test(message.errorMessage!));\n\t\tif (!isNonOverflow) {\n\t\t\tif (OVERFLOW_PATTERNS.some((p) => p.test(message.errorMessage!))) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (message.provider === \"cerebras\" && CEREBRAS_BODYLESS_OVERFLOW_PATTERN.test(message.errorMessage)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Case 2: Silent overflow (z.ai style) - successful but usage exceeds context\n\tif (contextWindow && message.stopReason === \"stop\") {\n\t\tconst inputTokens = message.usage.input + message.usage.cacheRead;\n\t\tif (inputTokens > contextWindow) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\t// Case 3: Length-stop overflow (Xiaomi MiMo style) - server truncates oversized input\n\t// to fit the context window, leaving no room for output. Returns stopReason \"length\"\n\t// with output=0 and input+cacheRead filling the context window.\n\tif (contextWindow && message.stopReason === \"length\" && message.usage.output === 0) {\n\t\tconst inputTokens = message.usage.input + message.usage.cacheRead;\n\t\tif (inputTokens >= contextWindow * 0.99) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n/**\n * Check whether a length stop ended below the caller or model's intended output limit.\n * Such responses may be caused by context pressure or provider-side truncation, so callers\n * can make one bounded compact-and-retry attempt. `desiredMaxOutput` must be the original\n * limit before any context-based clamping.\n */\nexport function isRecoverableLength(message: AssistantMessage, desiredMaxOutput: number): boolean {\n\treturn message.stopReason === \"length\" && desiredMaxOutput > 0 && message.usage.output < desiredMaxOutput;\n}\n\n/**\n * Get the overflow patterns for testing purposes.\n */\nexport function getOverflowPatterns(): RegExp[] {\n\treturn [...OVERFLOW_PATTERNS];\n}\n"]}
|
|
1
|
+
{"version":3,"file":"overflow.js","sourceRoot":"","sources":["../../src/utils/overflow.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AssistantMessage } from \"../types.ts\";\n\n/**\n * Regex patterns to detect context overflow errors from different providers.\n *\n * These patterns match error messages returned when the input exceeds\n * the model's context window or the provider's request byte-size limit.\n *\n * Provider-specific patterns (with example error messages):\n *\n * - Anthropic: \"prompt is too long: 213462 tokens > 200000 maximum\"\n * - Anthropic: \"413 {\\\"error\\\":{\\\"type\\\":\\\"request_too_large\\\",\\\"message\\\":\\\"Request exceeds the maximum size\\\"}}\"\n * - OpenAI: \"Your input exceeds the context window of this model\"\n * - OpenAI/LiteLLM: \"Requested token count exceeds the model's maximum context length of 131072 tokens\"\n * - OpenAI-compatible: \"Input length (265330) exceeds model's maximum context length (262144).\"\n * - Google: \"The input token count (1196265) exceeds the maximum number of tokens allowed (1048575)\"\n * - xAI: \"This model's maximum prompt length is 131072 but the request contains 537812 tokens\"\n * - Groq: \"Please reduce the length of the messages or completion\"\n * - OpenRouter: \"This endpoint's maximum context length is X tokens. However, you requested about Y tokens\"\n * - OpenRouter/Poolside: \"Input length X exceeds the maximum allowed input length of Y tokens.\"\n * - Together AI: \"The input (X tokens) is longer than the model's context length (Y tokens).\"\n * - llama.cpp: \"the request exceeds the available context size, try increasing it\"\n * - LM Studio: \"tokens to keep from the initial prompt is greater than the context length\"\n * - GitHub Copilot: \"prompt token count of X exceeds the limit of Y\"\n * - GitHub Copilot: 'github-copilot API error (413): {\"message\":\"failed to parse request\",\"code\":\"\"}'\n * - MiniMax: \"invalid params, context window exceeds limit\"\n * - Kimi For Coding: \"Your request exceeded model token limit: X (requested: Y)\"\n * - DS4: \"Prompt has X tokens, but the configured context size is Y tokens\"\n * - Cerebras: \"400/413 status code (no body)\"\n * - Mistral: \"Prompt contains X tokens ... too large for model with Y maximum context length\"\n * - z.ai: `{\"code\":\"1261\",\"message\":\"Prompt too long\"}`, `{\"code\":\"1261\",\"message\":\"Prompt exceeds max length\"}` (CN endpoint), or silent overflow via usage.input > contextWindow\n * - Xiaomi MiMo: Truncates input to fill contextWindow exactly, then returns finish_reason \"length\"\n * with output=0 (no room left to generate). Detected via stopReason \"length\" + zero output +\n * input filling the context window.\n * - DashScope/Qwen: \"Range of input length should be [1, X]\" (HTTP 400 invalid_parameter_error)\n * - Ollama: Some deployments truncate silently, others return errors like \"prompt too long; exceeded max context length by X tokens\"\n */\nconst OVERFLOW_PATTERNS = [\n\t/prompt (?:is )?too long/i, // Anthropic and z.ai token overflow\n\t/prompt exceeds max length/i, // z.ai CN endpoint token overflow\n\t/request_too_large/i, // Anthropic request byte-size overflow (HTTP 413)\n\t/input is too long for requested model/i, // Amazon Bedrock\n\t/exceeds the context window/i, // OpenAI (Completions & Responses API)\n\t/exceeds (?:the )?(?:model'?s )?maximum context length(?: of [\\d,]+ tokens?|\\s*\\([\\d,]+\\))/i, // OpenAI-compatible proxies (LiteLLM)\n\t/input token count.*exceeds the maximum/i, // Google (Gemini)\n\t/maximum prompt length is \\d+/i, // xAI (Grok)\n\t/reduce the length of the messages/i, // Groq\n\t/maximum context length is \\d+ tokens/i, // OpenRouter (most backends)\n\t/exceeds (?:the )?maximum allowed input length of [\\d,]+ tokens?/i, // OpenRouter/Poolside\n\t/input \\(\\d+ tokens\\) is longer than the model'?s context length \\(\\d+ tokens\\)/i, // Together AI\n\t/exceeds the limit of \\d+/i, // GitHub Copilot\n\t/exceeds the available context size/i, // llama.cpp server\n\t/greater than the context length/i, // LM Studio\n\t/context window exceeds limit/i, // MiniMax\n\t/exceeded model token limit/i, // Kimi For Coding\n\t/too large for model with \\d+ maximum context length/i, // Mistral\n\t/prompt has [\\d,]+ tokens?, but the configured context size is [\\d,]+ tokens?/i, // DS4 server\n\t/model_context_window_exceeded/i, // z.ai non-standard finish_reason surfaced as error text\n\t/prompt too long; exceeded (?:max )?context length/i, // Ollama explicit overflow error\n\t/range of input length should be/i, // DashScope / Qwen Token Plan\n\t/context[_ ]length[_ ]exceeded/i, // Generic fallback\n\t/too many tokens/i, // Generic fallback\n\t/token limit exceeded/i, // Generic fallback\n\t// HTTP 413 means request-size overflow regardless of provider or body text.\n\t// Match raw SDK errors and formatProviderError's prefix, not a status mentioned in the body.\n\t/^(?:[\\w -]+ API error \\(413\\):|413(?:\\s|:|$))/i,\n];\n\nconst CEREBRAS_BODYLESS_OVERFLOW_PATTERN = /^4(?:00|13)\\s*(?:status code)?\\s*\\(no body\\)/i;\n\n/**\n * Patterns that indicate non-overflow errors (e.g. rate limiting, server errors).\n * Error messages matching any of these are excluded from overflow detection\n * even if they also match an OVERFLOW_PATTERN.\n *\n * Example: Bedrock formats throttling errors as \"ThrottlingException: Too many tokens,\n * please wait before trying again.\" which would match the /too many tokens/i overflow\n * pattern without this exclusion.\n */\nconst NON_OVERFLOW_PATTERNS = [\n\t/^(Throttling error|Service unavailable):/i, // AWS Bedrock non-overflow errors (human-readable prefixes from formatBedrockError)\n\t/rate limit/i, // Generic rate limiting\n\t/too many requests/i, // Generic HTTP 429 style\n];\n\n/**\n * Check if an assistant message represents a context overflow error.\n *\n * This handles three cases:\n * 1. Error-based overflow: Most providers return stopReason \"error\" with a\n * specific error message pattern.\n * 2. Silent overflow: Some providers accept overflow requests and return\n * successfully. For these, we check if usage.input exceeds the context window.\n * 3. Length-stop overflow: Xiaomi MiMo can return \"length\" with zero output when\n * the input fills the context window.\n *\n * ## Reliability by Provider\n *\n * **Reliable detection (returns error with detectable message):**\n * - Anthropic: \"prompt is too long: X tokens > Y maximum\" or \"request_too_large\"\n * - OpenAI (Completions & Responses): \"exceeds the context window\", \"exceeds the model's maximum context length of X tokens\", or \"exceeds model's maximum context length (X)\"\n * - Google Gemini: \"input token count exceeds the maximum\"\n * - xAI (Grok): \"maximum prompt length is X but request contains Y\"\n * - Groq: \"reduce the length of the messages\"\n * - GitHub Copilot: HTTP 413 \"failed to parse request\" (request byte-size overflow)\n * - Cerebras: 400/413 status code (no body)\n * - Mistral: \"Prompt contains X tokens ... too large for model with Y maximum context length\"\n * - OpenRouter (most backends): \"maximum context length is X tokens\"\n * - OpenRouter/Poolside: \"Input length X exceeds the maximum allowed input length of Y tokens.\"\n * - Together AI: \"The input (X tokens) is longer than the model's context length (Y tokens).\"\n * - llama.cpp: \"exceeds the available context size\"\n * - LM Studio: \"greater than the context length\"\n * - Kimi For Coding: \"exceeded model token limit: X (requested: Y)\"\n * - DS4: \"Prompt has X tokens, but the configured context size is Y tokens\"\n * - DashScope/Qwen: \"Range of input length should be [1, X]\"\n * - z.ai: \"Prompt too long\"\n *\n * **Unreliable detection:**\n * - z.ai: Sometimes accepts overflow silently (detectable via usage.input > contextWindow),\n * sometimes returns rate limit errors. Pass contextWindow param to detect silent overflow.\n * - Xiaomi MiMo: Truncates input to fit contextWindow then returns stopReason \"length\" with\n * output=0. Pass contextWindow param to detect via the \"filled context + zero output\" signal.\n * - Ollama: May truncate input silently for some setups, but may also return explicit\n * overflow errors that match the patterns above. Silent truncation still cannot be\n * detected here because we do not know the expected token count.\n *\n * ## Custom Providers\n *\n * If you've added custom models via settings.json, this function may not detect\n * overflow errors from those providers. To add support:\n *\n * 1. Send a request that exceeds the model's context window\n * 2. Check the errorMessage in the response\n * 3. Create a regex pattern that matches the error\n * 4. The pattern should be added to OVERFLOW_PATTERNS in this file, or\n * check the errorMessage yourself before calling this function\n *\n * @param message - The assistant message to check\n * @param contextWindow - Optional context window size for detecting silent overflow (z.ai)\n * @returns true if the message indicates a context overflow\n */\nexport function isContextOverflow(message: AssistantMessage, contextWindow?: number): boolean {\n\t// Case 1: Check error message patterns\n\tif (message.stopReason === \"error\" && message.errorMessage) {\n\t\t// Skip messages matching known non-overflow patterns (e.g. throttling / rate-limit)\n\t\tconst isNonOverflow = NON_OVERFLOW_PATTERNS.some((p) => p.test(message.errorMessage!));\n\t\tif (!isNonOverflow) {\n\t\t\tif (OVERFLOW_PATTERNS.some((p) => p.test(message.errorMessage!))) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (message.provider === \"cerebras\" && CEREBRAS_BODYLESS_OVERFLOW_PATTERN.test(message.errorMessage)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Case 2: Silent overflow (z.ai style) - successful but usage exceeds context\n\tif (contextWindow && message.stopReason === \"stop\") {\n\t\tconst inputTokens = message.usage.input + message.usage.cacheRead;\n\t\tif (inputTokens > contextWindow) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\t// Case 3: Length-stop overflow (Xiaomi MiMo style) - server truncates oversized input\n\t// to fit the context window, leaving no room for output. Returns stopReason \"length\"\n\t// with output=0 and input+cacheRead filling the context window.\n\tif (contextWindow && message.stopReason === \"length\" && message.usage.output === 0) {\n\t\tconst inputTokens = message.usage.input + message.usage.cacheRead;\n\t\tif (inputTokens >= contextWindow * 0.99) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n/**\n * Check whether a length stop ended below the caller or model's intended output limit.\n * Such responses may be caused by context pressure or provider-side truncation, so callers\n * can make one bounded compact-and-retry attempt. `desiredMaxOutput` must be the original\n * limit before any context-based clamping.\n */\nexport function isRecoverableLength(message: AssistantMessage, desiredMaxOutput: number): boolean {\n\treturn message.stopReason === \"length\" && desiredMaxOutput > 0 && message.usage.output < desiredMaxOutput;\n}\n\n/**\n * Get the overflow patterns for testing purposes.\n */\nexport function getOverflowPatterns(): RegExp[] {\n\treturn [...OVERFLOW_PATTERNS];\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../../src/utils/retry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../../src/utils/retry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AA0HpD;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,qFAAqF;IACrF,UAAU,EAAE,MAAM,CAAC;IACnB,0FAA0F;IAC1F,WAAW,EAAE,MAAM,CAAC;IACpB,+EAA+E;IAC/E,eAAe,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,eAAO,MAAM,gCAAgC,QAAS,CAAC;AAEvD,wBAAgB,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,WAAW,EAAE,aAAa,GAAG,iBAAiB,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAIlH;AAED,kFAAkF;AAClF,MAAM,WAAW,cAAc;IAC9B,0EAA0E;IAC1E,gBAAgB,CAAC,EAAE,CAClB,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,KAChB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,mFAAmF;IACnF,mBAAmB,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,mFAAmF;IACnF,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnG;AA0BD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,kBAAkB,CACvC,OAAO,EAAE,MAAM,OAAO,CAAC,gBAAgB,CAAC,EACxC,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,SAAS,CAAC,EAAE,cAAc,GACxB,OAAO,CAAC,gBAAgB,CAAC,CA6C3B;AAED;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAK5E"}
|
package/dist/utils/retry.js
CHANGED
|
@@ -19,6 +19,9 @@ const NON_RETRYABLE_PROVIDER_LIMIT_ERROR_PATTERN = buildProviderErrorPattern([
|
|
|
19
19
|
// Sign in with ChatGPT: the subscription's shared usage limit, which resets
|
|
20
20
|
// after hours rather than seconds.
|
|
21
21
|
"subscription_sharing_usage_limit_exceeded",
|
|
22
|
+
// Sign in with ChatGPT on a ChatGPT Business plan: a usage limit set by the
|
|
23
|
+
// workspace admin. Only the admin can raise it.
|
|
24
|
+
"subscription_sharing_workplace_individual_usage_limit_exceeded",
|
|
22
25
|
]);
|
|
23
26
|
const RETRYABLE_PROVIDER_ERROR_PATTERN = buildProviderErrorPattern([
|
|
24
27
|
// Generic provider load, HTTP status, and server-side transient failures.
|
|
@@ -29,14 +32,10 @@ const RETRYABLE_PROVIDER_ERROR_PATTERN = buildProviderErrorPattern([
|
|
|
29
32
|
"model is at capacity",
|
|
30
33
|
"rate.?limit",
|
|
31
34
|
"too many requests",
|
|
32
|
-
|
|
33
|
-
"500",
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"504",
|
|
37
|
-
"520",
|
|
38
|
-
"524",
|
|
39
|
-
"service.?unavailable",
|
|
35
|
+
// Status codes must not be part of a longer word or number, e.g. a request id or byte count (#11057).
|
|
36
|
+
"(?<![a-z0-9])(?:429|500|502|503|504|520|524)(?![a-z0-9])",
|
|
37
|
+
// Also matches "The service is temporarily unavailable" (#10965).
|
|
38
|
+
"service.?(?:is.?)?(?:temporarily.?)?unavailable",
|
|
40
39
|
"server.?error",
|
|
41
40
|
"internal.?error",
|
|
42
41
|
// Wrapper/provider text for transient upstream failures, including OpenRouter
|
|
@@ -44,6 +43,14 @@ const RETRYABLE_PROVIDER_ERROR_PATTERN = buildProviderErrorPattern([
|
|
|
44
43
|
// "The upstream provider returned an error" (#10842).
|
|
45
44
|
"provider.?returned.?(?:an.?)?error",
|
|
46
45
|
"exceeded request buffer limit while retrying upstream",
|
|
46
|
+
// Gateway failure relaying the upstream response stream, sent as an OpenAI
|
|
47
|
+
// Responses `response.failed` event without an error code (#10920).
|
|
48
|
+
"unable to forward upstream",
|
|
49
|
+
// Other gateway upstream failures relayed as `response.failed` error codes (#10920).
|
|
50
|
+
"upstream.?request.?failed",
|
|
51
|
+
"upstream_stream_read_error",
|
|
52
|
+
"stream was interrupted",
|
|
53
|
+
"http/?2 stream failed",
|
|
47
54
|
// Network, proxy, and fetch transport failures. This includes OpenAI Codex
|
|
48
55
|
// raw-fetch failures such as "upstream connect", "connection refused", and
|
|
49
56
|
// "reset before headers" (#733), plus OpenRouter connection drops (#3317).
|
|
@@ -53,6 +60,9 @@ const RETRYABLE_PROVIDER_ERROR_PATTERN = buildProviderErrorPattern([
|
|
|
53
60
|
"connection.?lost",
|
|
54
61
|
"other side closed",
|
|
55
62
|
"fetch failed",
|
|
63
|
+
// node-fetch system errors, e.g. the Google ADC OAuth token refresh used by
|
|
64
|
+
// google-vertex. The reason is often empty (#10996).
|
|
65
|
+
"request to \\S+ failed, reason:",
|
|
56
66
|
"getaddrinfo",
|
|
57
67
|
"ENOTFOUND",
|
|
58
68
|
"EAI_AGAIN",
|
|
@@ -73,6 +83,9 @@ const RETRYABLE_PROVIDER_ERROR_PATTERN = buildProviderErrorPattern([
|
|
|
73
83
|
"stream ended before message_stop",
|
|
74
84
|
"stream ended before a terminal response event",
|
|
75
85
|
"http2 request did not get a response",
|
|
86
|
+
// Codex CLI mid-stream disconnect, relayed by proxies that wrap the Codex CLI,
|
|
87
|
+
// e.g. "stream disconnected before completion: stream closed before response.completed" (#11057).
|
|
88
|
+
"stream disconnected before completion",
|
|
76
89
|
// Node ERR_HTTP2_STREAM_CANCEL: the HTTP/2 session died before the request was
|
|
77
90
|
// sent, e.g. after the Bedrock SDK's 5-minute session timeout (#10379).
|
|
78
91
|
"pending stream has been canceled",
|