@code-yeongyu/senpi-ai 2026.9.30 → 2026.10.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/README.md +189 -19
- package/dist/api/anthropic-messages.js +168 -32
- package/dist/api/azure-openai-responses.js +23 -10
- package/dist/api/bedrock-converse-stream.js +12 -4
- package/dist/api/cloudflare-workers-ai-system-one.d.ts +4 -0
- package/dist/api/cloudflare-workers-ai-system-one.js +43 -0
- package/dist/api/cloudflare-workers-ai-system-one.lazy.d.ts +3 -0
- package/dist/api/cloudflare-workers-ai-system-one.lazy.js +4 -0
- package/dist/api/cloudflare.d.ts +2 -0
- package/dist/api/cloudflare.js +2 -0
- package/dist/api/context-room.d.ts +2 -2
- package/dist/api/cursor-agent.js +19 -10
- package/dist/api/devin-agent/request.d.ts +9 -9
- package/dist/api/devin-agent/request.js +14 -10
- package/dist/api/google-generative-ai.js +20 -80
- package/dist/api/google-shared.d.ts +13 -4
- package/dist/api/google-shared.js +54 -4
- package/dist/api/google-vertex.js +19 -62
- package/dist/api/llama-cpp-classify.d.ts +33 -0
- package/dist/api/llama-cpp-classify.js +365 -0
- package/dist/api/llama-cpp-classify.lazy.d.ts +3 -0
- package/dist/api/llama-cpp-classify.lazy.js +4 -0
- package/dist/api/mistral-conversations.d.ts +1 -1
- package/dist/api/mistral-conversations.js +36 -29
- package/dist/api/openai-codex-responses.d.ts +1 -1
- package/dist/api/openai-codex-responses.js +72 -37
- package/dist/api/openai-completions.d.ts +3 -2
- package/dist/api/openai-completions.js +76 -50
- package/dist/api/openai-images-params.d.ts +2 -2
- package/dist/api/openai-images.d.ts +1 -1
- package/dist/api/openai-responses-shared.d.ts +31 -8
- package/dist/api/openai-responses-shared.js +115 -27
- package/dist/api/openai-responses.d.ts +1 -1
- package/dist/api/openai-responses.js +38 -33
- package/dist/api/openrouter-images.d.ts +2 -1
- package/dist/api/openrouter-images.js +1 -0
- package/dist/api/pi-messages.d.ts +3 -3
- package/dist/api/pi-messages.js +3 -2
- package/dist/api/simple-options.d.ts +2 -2
- package/dist/api/simple-options.js +1 -0
- package/dist/api/system-one-shared.d.ts +23 -0
- package/dist/api/system-one-shared.js +183 -0
- package/dist/api/transform-messages.js +5 -2
- package/dist/api/typesafe-system-one.d.ts +4 -0
- package/dist/api/typesafe-system-one.js +19 -0
- package/dist/api/typesafe-system-one.lazy.d.ts +3 -0
- package/dist/api/typesafe-system-one.lazy.js +4 -0
- package/dist/api-registry.d.ts +3 -3
- package/dist/auth/helpers.js +1 -1
- package/dist/auth/oauth/anthropic-callback-listener.js +1 -1
- package/dist/auth/oauth/callback-server.d.ts +55 -0
- package/dist/auth/oauth/callback-server.js +146 -0
- package/dist/auth/oauth/chatgpt-subscription.d.ts +1 -1
- package/dist/auth/oauth/chatgpt-subscription.js +20 -124
- package/dist/auth/oauth/devin-callback.js +1 -1
- package/dist/auth/oauth/load.d.ts +4 -0
- package/dist/auth/oauth/load.js +10 -0
- package/dist/auth/oauth/meta.d.ts +17 -0
- package/dist/auth/oauth/meta.js +190 -0
- package/dist/auth/oauth/openai-chatgpt.d.ts +9 -0
- package/dist/auth/oauth/openai-chatgpt.js +266 -0
- package/dist/auth/oauth/openrouter.d.ts +1 -1
- package/dist/auth/oauth/openrouter.js +19 -138
- package/dist/auth/oauth/radius.d.ts +1 -1
- package/dist/auth/oauth/radius.js +21 -89
- package/dist/auth/resolve.d.ts +3 -8
- package/dist/auth/resolve.js +3 -18
- package/dist/auth/types.d.ts +10 -1
- package/dist/bun-oauth.js +4 -0
- package/dist/cli.js +3 -1
- package/dist/compat.js +17 -14
- package/dist/env-api-keys.js +2 -0
- package/dist/image-models.d.ts +19 -8
- package/dist/image-models.js +14 -13
- package/dist/images-api-registry.d.ts +8 -8
- package/dist/images.d.ts +7 -2
- package/dist/images.js +5 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/model-catalog.d.ts +27 -9
- package/dist/model-catalog.js +24 -2
- package/dist/model.d.ts +13 -13
- package/dist/models-store.d.ts +3 -2
- package/dist/models.d.ts +106 -36
- package/dist/models.generated.d.ts +142 -42
- package/dist/models.generated.js +142 -42
- package/dist/models.js +174 -36
- package/dist/providers/alibaba-token-plan.models.d.ts +4 -2
- package/dist/providers/alibaba-token-plan.models.js +4 -2
- package/dist/providers/all.d.ts +72 -19
- package/dist/providers/all.js +28 -22
- package/dist/providers/amazon-bedrock.models.d.ts +4 -2
- package/dist/providers/amazon-bedrock.models.js +4 -2
- package/dist/providers/ant-ling.models.d.ts +4 -2
- package/dist/providers/ant-ling.models.js +4 -2
- package/dist/providers/anthropic.models.d.ts +4 -2
- package/dist/providers/anthropic.models.js +4 -2
- package/dist/providers/azure-openai-responses.models.d.ts +4 -2
- package/dist/providers/azure-openai-responses.models.js +4 -2
- package/dist/providers/bai.models.d.ts +4 -2
- package/dist/providers/bai.models.js +4 -2
- package/dist/providers/baseten.models.d.ts +4 -2
- package/dist/providers/baseten.models.js +4 -2
- package/dist/providers/cerebras.models.d.ts +4 -2
- package/dist/providers/cerebras.models.js +4 -2
- package/dist/providers/chatgpt-subscription.models.d.ts +4 -2
- package/dist/providers/chatgpt-subscription.models.js +4 -2
- package/dist/providers/cloudflare-ai-gateway.models.d.ts +4 -2
- package/dist/providers/cloudflare-ai-gateway.models.js +4 -2
- package/dist/providers/cloudflare-stream.d.ts +6 -2
- package/dist/providers/cloudflare-stream.js +6 -0
- package/dist/providers/cloudflare-workers-ai.js +10 -3
- package/dist/providers/cloudflare-workers-ai.models.d.ts +4 -2
- package/dist/providers/cloudflare-workers-ai.models.js +4 -2
- package/dist/providers/data/.manifest.json +1 -1
- package/dist/providers/data/alibaba-token-plan.json +1 -1
- package/dist/providers/data/amazon-bedrock.json +1 -1
- package/dist/providers/data/ant-ling.json +1 -1
- package/dist/providers/data/anthropic.json +1 -1
- package/dist/providers/data/azure-openai-responses.json +1 -1
- package/dist/providers/data/bai.json +1 -1
- package/dist/providers/data/baseten.json +1 -1
- package/dist/providers/data/cerebras.json +1 -1
- package/dist/providers/data/chatgpt-subscription.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/deepseek.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/meta.json +1 -0
- 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/moonshotai.json +1 -1
- package/dist/providers/data/nvidia.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/opengateway.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-individual.json +1 -1
- package/dist/providers/data/qwen-token-plan.json +1 -1
- package/dist/providers/data/radius.json +1 -0
- package/dist/providers/data/together.json +1 -1
- package/dist/providers/data/typesafe.json +1 -0
- package/dist/providers/data/venice.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/data/zai.json +1 -1
- package/dist/providers/deepseek.models.d.ts +4 -2
- package/dist/providers/deepseek.models.js +4 -2
- package/dist/providers/faux.d.ts +7 -2
- package/dist/providers/faux.js +31 -22
- package/dist/providers/fireworks.models.d.ts +4 -2
- package/dist/providers/fireworks.models.js +4 -2
- package/dist/providers/github-copilot.models.d.ts +4 -2
- package/dist/providers/github-copilot.models.js +4 -2
- package/dist/providers/google-vertex.models.d.ts +4 -2
- package/dist/providers/google-vertex.models.js +4 -2
- package/dist/providers/google.models.d.ts +4 -2
- package/dist/providers/google.models.js +4 -2
- package/dist/providers/groq.models.d.ts +4 -2
- package/dist/providers/groq.models.js +4 -2
- package/dist/providers/huggingface.models.d.ts +4 -2
- package/dist/providers/huggingface.models.js +4 -2
- package/dist/providers/images/register-builtins.d.ts +2 -2
- package/dist/providers/kimi-coding.models.d.ts +54 -7
- package/dist/providers/kimi-coding.models.js +34 -7
- package/dist/providers/meta.d.ts +3 -0
- package/dist/providers/meta.js +24 -0
- package/dist/providers/meta.models.d.ts +6 -0
- package/dist/providers/meta.models.js +8 -0
- package/dist/providers/minimax-cn.models.d.ts +4 -2
- package/dist/providers/minimax-cn.models.js +4 -2
- package/dist/providers/minimax.models.d.ts +4 -2
- package/dist/providers/minimax.models.js +4 -2
- package/dist/providers/mistral.models.d.ts +4 -2
- package/dist/providers/mistral.models.js +4 -2
- package/dist/providers/moonshotai-cn.models.d.ts +4 -2
- package/dist/providers/moonshotai-cn.models.js +4 -2
- package/dist/providers/moonshotai.models.d.ts +4 -2
- package/dist/providers/moonshotai.models.js +4 -2
- package/dist/providers/nvidia.models.d.ts +4 -2
- package/dist/providers/nvidia.models.js +4 -2
- package/dist/providers/openai.js +4 -2
- package/dist/providers/openai.models.d.ts +4 -2
- package/dist/providers/openai.models.js +4 -2
- package/dist/providers/opencode-go.models.d.ts +4 -2
- package/dist/providers/opencode-go.models.js +4 -2
- package/dist/providers/opencode.d.ts +3 -1
- package/dist/providers/opencode.js +5 -2
- package/dist/providers/opencode.models.d.ts +4 -2
- package/dist/providers/opencode.models.js +4 -2
- package/dist/providers/opengateway.models.d.ts +4 -2
- package/dist/providers/opengateway.models.js +4 -2
- package/dist/providers/openrouter.js +11 -2
- package/dist/providers/openrouter.models.d.ts +4 -2
- package/dist/providers/openrouter.models.js +4 -2
- package/dist/providers/qwen-token-plan-cn.models.d.ts +4 -2
- package/dist/providers/qwen-token-plan-cn.models.js +4 -2
- package/dist/providers/qwen-token-plan-individual.models.d.ts +4 -2
- package/dist/providers/qwen-token-plan-individual.models.js +4 -2
- package/dist/providers/qwen-token-plan.models.d.ts +4 -2
- package/dist/providers/qwen-token-plan.models.js +4 -2
- package/dist/providers/radius.js +19 -5
- package/dist/providers/radius.models.d.ts +6 -0
- package/dist/providers/radius.models.js +8 -0
- package/dist/providers/together.models.d.ts +4 -2
- package/dist/providers/together.models.js +4 -2
- package/dist/providers/typesafe.d.ts +3 -0
- package/dist/providers/typesafe.js +18 -0
- package/dist/providers/typesafe.models.d.ts +6 -0
- package/dist/providers/typesafe.models.js +8 -0
- package/dist/providers/venice.models.d.ts +4 -2
- package/dist/providers/venice.models.js +4 -2
- package/dist/providers/vercel-ai-gateway.js +5 -2
- package/dist/providers/vercel-ai-gateway.models.d.ts +4 -2
- package/dist/providers/vercel-ai-gateway.models.js +4 -2
- package/dist/providers/xai.models.d.ts +4 -2
- package/dist/providers/xai.models.js +4 -2
- package/dist/providers/xiaomi-token-plan-ams.models.d.ts +4 -2
- package/dist/providers/xiaomi-token-plan-ams.models.js +4 -2
- package/dist/providers/xiaomi-token-plan-cn.models.d.ts +4 -2
- package/dist/providers/xiaomi-token-plan-cn.models.js +4 -2
- package/dist/providers/xiaomi-token-plan-sgp.models.d.ts +4 -2
- package/dist/providers/xiaomi-token-plan-sgp.models.js +4 -2
- package/dist/providers/xiaomi.models.d.ts +4 -2
- package/dist/providers/xiaomi.models.js +4 -2
- package/dist/providers/zai-coding-cn.models.d.ts +4 -2
- package/dist/providers/zai-coding-cn.models.js +4 -2
- package/dist/providers/zai.models.d.ts +4 -2
- package/dist/providers/zai.models.js +4 -2
- package/dist/tool-call-middleware/context-transformer.d.ts +8 -5
- package/dist/tool-call-middleware/context-transformer.js +44 -15
- package/dist/types.d.ts +262 -28
- package/dist/utils/diagnostics.d.ts +3 -2
- package/dist/utils/estimate.d.ts +2 -2
- package/dist/utils/estimate.js +22 -30
- package/dist/utils/headers.d.ts +1 -1
- package/dist/utils/headers.js +10 -8
- package/dist/utils/model-operations.d.ts +11 -0
- package/dist/utils/model-operations.js +47 -0
- package/dist/utils/models-error.d.ts +8 -0
- package/dist/utils/models-error.js +18 -0
- package/dist/utils/overflow.d.ts +1 -0
- package/dist/utils/overflow.js +11 -5
- package/dist/utils/prompt-cache-ttl.js +10 -3
- package/dist/utils/retry.js +9 -0
- package/dist/utils/text.d.ts +9 -1
- package/dist/utils/text.js +26 -0
- package/dist/utils/transcript.d.ts +85 -0
- package/dist/utils/transcript.js +205 -0
- package/package.json +3 -4
- package/dist/image-models.generated.d.ts +0 -925
- package/dist/image-models.generated.js +0 -927
- package/dist/images-models.d.ts +0 -95
- package/dist/images-models.js +0 -141
- package/dist/providers/openai-images.d.ts +0 -3
- package/dist/providers/openai-images.js +0 -16
- package/dist/providers/openrouter-images.d.ts +0 -3
- package/dist/providers/openrouter-images.js +0 -22
- /package/dist/{auth/oauth → utils}/oauth-page.d.ts +0 -0
- /package/dist/{auth/oauth → utils}/oauth-page.js +0 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { ModelsError } from "./models-error.js";
|
|
2
|
+
/** The type of a model. Models without `type` are chat models. */
|
|
3
|
+
export function getModelType(model) {
|
|
4
|
+
return model.type ?? "chat";
|
|
5
|
+
}
|
|
6
|
+
/** Runtime-checked model type narrowing, including legacy chat models without `type`. */
|
|
7
|
+
export function isModelType(model, type) {
|
|
8
|
+
return getModelType(model) === type;
|
|
9
|
+
}
|
|
10
|
+
export function assertChatModel(model) {
|
|
11
|
+
if (!isModelType(model, "chat")) {
|
|
12
|
+
throw new ModelsError("provider", `Model ${model.provider}/${model.id} is not a chat model`);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export function assertImageModel(model) {
|
|
16
|
+
if (!isModelType(model, "image")) {
|
|
17
|
+
throw new ModelsError("provider", `Model ${model.provider}/${model.id} is not an image model`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export function assertClassifierModel(model) {
|
|
21
|
+
if (!isModelType(model, "classifier")) {
|
|
22
|
+
throw new ModelsError("provider", `Model ${model.provider}/${model.id} is not a classifier model`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export function imageErrorResult(model, error, aborted = false) {
|
|
26
|
+
return {
|
|
27
|
+
api: model.api,
|
|
28
|
+
provider: model.provider,
|
|
29
|
+
model: model.id,
|
|
30
|
+
output: [],
|
|
31
|
+
stopReason: aborted ? "aborted" : "error",
|
|
32
|
+
errorMessage: error instanceof Error ? error.message : String(error),
|
|
33
|
+
timestamp: Date.now(),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export function classifierErrorResult(model, error, aborted = false) {
|
|
37
|
+
return {
|
|
38
|
+
api: model.api,
|
|
39
|
+
provider: model.provider,
|
|
40
|
+
model: model.id,
|
|
41
|
+
answers: {},
|
|
42
|
+
stopReason: aborted ? "aborted" : "error",
|
|
43
|
+
errorMessage: error instanceof Error ? error.message : String(error),
|
|
44
|
+
timestamp: Date.now(),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=model-operations.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type ModelsErrorCode = "model_source" | "model_validation" | "provider" | "stream" | "auth" | "oauth";
|
|
2
|
+
export declare class ModelsError extends Error {
|
|
3
|
+
readonly code: ModelsErrorCode;
|
|
4
|
+
constructor(code: ModelsErrorCode, message: string, options?: {
|
|
5
|
+
cause?: unknown;
|
|
6
|
+
});
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=models-error.d.ts.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { formatThrownValue } from "./diagnostics.js";
|
|
2
|
+
export class ModelsError extends Error {
|
|
3
|
+
constructor(code, message, options) {
|
|
4
|
+
super(withCauseDetail(message, options?.cause), options);
|
|
5
|
+
this.name = "ModelsError";
|
|
6
|
+
this.code = code;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
/** Callers surface `error.message` only, so keep the underlying reason in it. */
|
|
10
|
+
function withCauseDetail(message, cause) {
|
|
11
|
+
if (cause === undefined || cause === null)
|
|
12
|
+
return message;
|
|
13
|
+
const detail = formatThrownValue(cause).trim();
|
|
14
|
+
if (!detail || message.includes(detail))
|
|
15
|
+
return message;
|
|
16
|
+
return `${message}: ${detail}`;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=models-error.js.map
|
package/dist/utils/overflow.d.ts
CHANGED
|
@@ -28,6 +28,7 @@ import type { AssistantMessage } from "../types.ts";
|
|
|
28
28
|
* - Kimi For Coding: "exceeded model token limit: X (requested: Y)"
|
|
29
29
|
* - DS4: "Prompt has X tokens, but the configured context size is Y tokens"
|
|
30
30
|
* - DashScope/Qwen: "Range of input length should be [1, X]"
|
|
31
|
+
* - z.ai: "Prompt too long"
|
|
31
32
|
*
|
|
32
33
|
* **Unreliable detection:**
|
|
33
34
|
* - z.ai: Sometimes accepts overflow silently (detectable via usage.input > contextWindow),
|
package/dist/utils/overflow.js
CHANGED
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
* - kiro-lb gateways: "Request payload is 1095225 bytes, over the 1085435 byte limit Kiro accepts." / "Request payload is N tokens, over the M token limit Kiro accepts." (HTTP 400 local payload guard)
|
|
30
30
|
* - Kiro upstream via kiro-lb: "Model context limit reached. Conversation size exceeds model capacity." (CONTENT_LENGTH_EXCEEDS_THRESHOLD token overflow)
|
|
31
31
|
* - Mistral: "Prompt contains X tokens ... too large for model with Y maximum context length"
|
|
32
|
-
* - z.ai:
|
|
32
|
+
* - z.ai: `{"code":"1261","message":"Prompt too long"}` or silent overflow via usage.input > contextWindow
|
|
33
33
|
* - Xiaomi MiMo: Truncates input to fill contextWindow exactly, then returns finish_reason "length"
|
|
34
34
|
* with output=0 (no room left to generate). Detected via stopReason "length" + zero output +
|
|
35
35
|
* input filling the context window.
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
const OVERFLOW_PATTERNS = [
|
|
42
42
|
/^Context window exhausted: /, // pi-ai pre-flight guard: no answer room left, provider never called
|
|
43
43
|
/^The conversation is too long to resend \(about \d+ tokens, limit \d+\)/, // anthropic-subscription cold-seed budget: re-send refused before dispatch
|
|
44
|
-
/prompt is too long/i, // Anthropic token overflow
|
|
44
|
+
/prompt (?:is )?too long/i, // Anthropic and z.ai token overflow
|
|
45
45
|
/request_too_large/i, // Anthropic request byte-size overflow (HTTP 413)
|
|
46
46
|
/input is too long for requested model/i, // Amazon Bedrock
|
|
47
47
|
/exceeds (?:(?:the|this) )?(?:model'?s )?context window/i, // OpenAI (Completions & Responses API)
|
|
@@ -65,11 +65,11 @@ const OVERFLOW_PATTERNS = [
|
|
|
65
65
|
/context[_ ]length[_ ]exceeded/i, // Generic fallback
|
|
66
66
|
/too many tokens/i, // Generic fallback
|
|
67
67
|
/token limit exceeded/i, // Generic fallback
|
|
68
|
-
/^4(?:00|13)\s*(?:status code)?\s*\(no body\)/i, // Cerebras: 400/413 with no body
|
|
69
68
|
/(?:request[ _])?(?:body|entity|payload)[_ ]too[_ ]large/i, // Gateway HTTP 413 byte-size rejections ("Request body too large", "Request Entity Too Large", "body_too_large", "Payload Too Large"). Substring-anchored by design (JSON bodies lack an adjacent status code); a non-context size rejection (e.g. an oversized image) can over-match, which costs one bounded shrink-retry, never a wedge.
|
|
70
69
|
/Request payload is \d+ (?:bytes, over the \d+ byte|tokens, over the \d+ token) limit Kiro accepts\./, // kiro-lb local byte/token payload guard (HTTP 400; Anthropic uses invalid_request_error, OpenAI uses detail).
|
|
71
70
|
/Model context limit reached\. Conversation size exceeds model capacity\./, // kiro-lb enhancement of Kiro CONTENT_LENGTH_EXCEEDS_THRESHOLD
|
|
72
71
|
];
|
|
72
|
+
const CEREBRAS_BODYLESS_OVERFLOW_PATTERN = /^4(?:00|13)\s*(?:status code)?\s*\(no body\)/i;
|
|
73
73
|
/**
|
|
74
74
|
* Patterns that indicate non-overflow errors (e.g. rate limiting, server errors).
|
|
75
75
|
* Error messages matching any of these are excluded from overflow detection
|
|
@@ -133,6 +133,7 @@ const RESOURCE_EXHAUSTED_PATTERN = /resource.?exhausted/i;
|
|
|
133
133
|
* - Kimi For Coding: "exceeded model token limit: X (requested: Y)"
|
|
134
134
|
* - DS4: "Prompt has X tokens, but the configured context size is Y tokens"
|
|
135
135
|
* - DashScope/Qwen: "Range of input length should be [1, X]"
|
|
136
|
+
* - z.ai: "Prompt too long"
|
|
136
137
|
*
|
|
137
138
|
* **Unreliable detection:**
|
|
138
139
|
* - z.ai: Sometimes accepts overflow silently (detectable via usage.input > contextWindow),
|
|
@@ -163,8 +164,13 @@ export function isContextOverflow(message, contextWindow) {
|
|
|
163
164
|
if (message.stopReason === "error" && message.errorMessage) {
|
|
164
165
|
// Skip messages matching known non-overflow patterns (e.g. throttling / rate-limit)
|
|
165
166
|
const isNonOverflow = NON_OVERFLOW_PATTERNS.some((p) => p.test(message.errorMessage));
|
|
166
|
-
if (!isNonOverflow
|
|
167
|
-
|
|
167
|
+
if (!isNonOverflow) {
|
|
168
|
+
if (OVERFLOW_PATTERNS.some((p) => p.test(message.errorMessage))) {
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
if (message.provider === "cerebras" && CEREBRAS_BODYLESS_OVERFLOW_PATTERN.test(message.errorMessage)) {
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
168
174
|
}
|
|
169
175
|
const usage = message.usage;
|
|
170
176
|
const hasTokenEvidence = (usage.totalTokens || usage.input + usage.output + usage.cacheRead + usage.cacheWrite) > 0;
|
|
@@ -70,6 +70,8 @@ export function getAnthropicCompat(model) {
|
|
|
70
70
|
unsignedThinkingReplay: model.compat?.unsignedThinkingReplay ?? (model.compat?.allowEmptySignature ? "empty-signature" : "text"),
|
|
71
71
|
allowedFallbackModels: model.compat?.allowedFallbackModels ?? [],
|
|
72
72
|
supportsStrictTools: model.compat?.supportsStrictTools ?? false,
|
|
73
|
+
supportsMidConvoSystemMessages: model.compat?.supportsMidConvoSystemMessages ?? false,
|
|
74
|
+
supportsMidConvoToolChanges: model.compat?.supportsMidConvoToolChanges ?? false,
|
|
73
75
|
supportsToolReferences: model.compat?.supportsToolReferences ?? defaultSupportsToolReferences(model),
|
|
74
76
|
// Default: first-party Anthropic only. Anthropic-compatible providers
|
|
75
77
|
// (kimi-coding, fireworks, copilot, gateways) may execute the server-side
|
|
@@ -98,10 +100,10 @@ function detectOpenAICompletionsCompat(model) {
|
|
|
98
100
|
const isCloudflareAiGateway = provider === "cloudflare-ai-gateway" || baseUrl.includes("gateway.ai.cloudflare.com");
|
|
99
101
|
const isNvidia = provider === "nvidia" || baseUrl.includes("integrate.api.nvidia.com");
|
|
100
102
|
const isAntLing = provider === "ant-ling" || baseUrl.includes("api.ant-ling.com");
|
|
103
|
+
const isCerebras = provider === "cerebras" || baseUrl.includes("cerebras.ai");
|
|
101
104
|
const isDeepSeek = provider === "deepseek" || baseUrl.toLowerCase().includes("deepseek.com");
|
|
102
105
|
const isNonStandard = isNvidia ||
|
|
103
|
-
|
|
104
|
-
baseUrl.includes("cerebras.ai") ||
|
|
106
|
+
isCerebras ||
|
|
105
107
|
provider === "xai" ||
|
|
106
108
|
baseUrl.includes("api.x.ai") ||
|
|
107
109
|
isTogether ||
|
|
@@ -154,11 +156,14 @@ function detectOpenAICompletionsCompat(model) {
|
|
|
154
156
|
vercelGatewayRouting: {},
|
|
155
157
|
chatTemplateKwargs: {},
|
|
156
158
|
zaiToolStream: false,
|
|
157
|
-
|
|
159
|
+
// OpenAI compatibility alone does not imply strict JSON-schema tool support.
|
|
160
|
+
supportsStrictMode: false,
|
|
158
161
|
toolSchemaFlavor: isMoonshot ? "moonshot-mfjs" : undefined,
|
|
159
162
|
supportsDisabledThinking: true,
|
|
160
163
|
toolCallFormat: undefined,
|
|
161
164
|
supportsOpenAIGrammarTools: false,
|
|
165
|
+
supportsMidConvoSystemMessages: false,
|
|
166
|
+
supportsMidConvoToolAdditions: false,
|
|
162
167
|
cacheControlFormat,
|
|
163
168
|
sendSessionAffinityHeaders: isOpenRouter,
|
|
164
169
|
deferredToolsMode: undefined,
|
|
@@ -205,6 +210,8 @@ export function getOpenAICompletionsCompat(model) {
|
|
|
205
210
|
toolSchemaFlavor: model.compat.toolSchemaFlavor ?? detected.toolSchemaFlavor,
|
|
206
211
|
toolCallFormat: model.compat.toolCallFormat ?? detected.toolCallFormat,
|
|
207
212
|
supportsOpenAIGrammarTools: model.compat.supportsOpenAIGrammarTools ?? detected.supportsOpenAIGrammarTools,
|
|
213
|
+
supportsMidConvoSystemMessages: model.compat.supportsMidConvoSystemMessages ?? detected.supportsMidConvoSystemMessages,
|
|
214
|
+
supportsMidConvoToolAdditions: model.compat.supportsMidConvoToolAdditions ?? detected.supportsMidConvoToolAdditions,
|
|
208
215
|
cacheControlFormat: model.compat.cacheControlFormat ?? detected.cacheControlFormat,
|
|
209
216
|
sendSessionAffinityHeaders: model.compat.sendSessionAffinityHeaders ?? detected.sendSessionAffinityHeaders,
|
|
210
217
|
deferredToolsMode: model.compat.deferredToolsMode ?? detected.deferredToolsMode,
|
package/dist/utils/retry.js
CHANGED
|
@@ -84,6 +84,9 @@ const NON_RETRYABLE_PROVIDER_ERROR_PATTERN = buildProviderErrorPattern([
|
|
|
84
84
|
"tools\\.[^ ]*function\\.parameters",
|
|
85
85
|
"tools\\.\\d+\\.function\\.parameters",
|
|
86
86
|
"invalid tool schema",
|
|
87
|
+
// Sign in with ChatGPT: the subscription's shared usage limit, which resets
|
|
88
|
+
// after hours rather than seconds.
|
|
89
|
+
"subscription_sharing_usage_limit_exceeded",
|
|
87
90
|
]);
|
|
88
91
|
const RETRYABLE_PROVIDER_ERROR_PATTERN = buildProviderErrorPattern([
|
|
89
92
|
// Local credential/auth sidecar lock exhaustion is infrastructure contention,
|
|
@@ -91,6 +94,7 @@ const RETRYABLE_PROVIDER_ERROR_PATTERN = buildProviderErrorPattern([
|
|
|
91
94
|
"Credential store is busy: lock",
|
|
92
95
|
// Generic provider load, HTTP status, and server-side transient failures.
|
|
93
96
|
"overloaded",
|
|
97
|
+
"currently experiencing high demand",
|
|
94
98
|
"rate.?limit",
|
|
95
99
|
"too many requests",
|
|
96
100
|
"429",
|
|
@@ -98,6 +102,7 @@ const RETRYABLE_PROVIDER_ERROR_PATTERN = buildProviderErrorPattern([
|
|
|
98
102
|
"502",
|
|
99
103
|
"503",
|
|
100
104
|
"504",
|
|
105
|
+
"520",
|
|
101
106
|
// Cloudflare 522 (Connection timed out): origin stopped responding; transient
|
|
102
107
|
// like the other 5xx gateway statuses, surfaced as "Error: error code: 522".
|
|
103
108
|
"522",
|
|
@@ -192,6 +197,10 @@ const RETRYABLE_PROVIDER_ERROR_PATTERN = buildProviderErrorPattern([
|
|
|
192
197
|
// hits proper-lockfile while the previous subprocess still holds the file.
|
|
193
198
|
// Same-process retry recovers; hopping providers cannot release that lock.
|
|
194
199
|
"Lock file is already being held",
|
|
200
|
+
// Sign in with ChatGPT: usage or user data temporarily unavailable. Usage
|
|
201
|
+
// failures can arrive mid-stream without an HTTP 503 in the message.
|
|
202
|
+
"subscription_sharing_usage_unavailable",
|
|
203
|
+
"subscription_sharing_user_unavailable",
|
|
195
204
|
]);
|
|
196
205
|
export const DEFAULT_MAX_AGENT_RETRY_DELAY_MS = 60_000;
|
|
197
206
|
/**
|
package/dist/utils/text.d.ts
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
|
-
import type { ImageContent, ProviderNativeContent, TextContent, ThinkingContent, ToolCall } from "../types.ts";
|
|
1
|
+
import type { ImageContent, ProviderNativeContent, SystemMessage, TextContent, ThinkingContent, ToolCall } from "../types.ts";
|
|
2
2
|
type Content = TextContent | ImageContent | ThinkingContent | ToolCall | ProviderNativeContent;
|
|
3
3
|
/** Extract and join text from message content. */
|
|
4
4
|
export declare function contentText(content: string | readonly Content[], separator?: string): string;
|
|
5
|
+
/** Render a system message as a complete prompt: its content followed by its sections. */
|
|
6
|
+
export declare function getSystemMessageText(message: SystemMessage): string;
|
|
7
|
+
/**
|
|
8
|
+
* Render a later system message for APIs that accept system messages mid-conversation.
|
|
9
|
+
* Section changes are framed by name so the model can relate them to the leading prompt.
|
|
10
|
+
* This framing is request-time only and may change between versions.
|
|
11
|
+
*/
|
|
12
|
+
export declare function renderSystemMessageUpdate(message: SystemMessage): string;
|
|
5
13
|
export {};
|
|
6
14
|
//# sourceMappingURL=text.d.ts.map
|
package/dist/utils/text.js
CHANGED
|
@@ -7,4 +7,30 @@ export function contentText(content, separator = "\n") {
|
|
|
7
7
|
.map((block) => block.text)
|
|
8
8
|
.join(separator);
|
|
9
9
|
}
|
|
10
|
+
/** Render a system message as a complete prompt: its content followed by its sections. */
|
|
11
|
+
export function getSystemMessageText(message) {
|
|
12
|
+
const parts = [contentText(message.content)];
|
|
13
|
+
for (const text of Object.values(message.sections ?? {})) {
|
|
14
|
+
if (text !== null)
|
|
15
|
+
parts.push(text);
|
|
16
|
+
}
|
|
17
|
+
return parts.filter((part) => part.length > 0).join("\n\n");
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Render a later system message for APIs that accept system messages mid-conversation.
|
|
21
|
+
* Section changes are framed by name so the model can relate them to the leading prompt.
|
|
22
|
+
* This framing is request-time only and may change between versions.
|
|
23
|
+
*/
|
|
24
|
+
export function renderSystemMessageUpdate(message) {
|
|
25
|
+
const parts = [];
|
|
26
|
+
const text = contentText(message.content);
|
|
27
|
+
if (text.length > 0)
|
|
28
|
+
parts.push(text);
|
|
29
|
+
for (const [name, value] of Object.entries(message.sections ?? {})) {
|
|
30
|
+
parts.push(value === null
|
|
31
|
+
? `Removed system prompt section "${name}".`
|
|
32
|
+
: `Updated system prompt section "${name}":\n\n${value}`);
|
|
33
|
+
}
|
|
34
|
+
return parts.join("\n\n");
|
|
35
|
+
}
|
|
10
36
|
//# sourceMappingURL=text.js.map
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { Context, Message, SystemMessage, Tool, ToolReference, TranscriptContext } from "../types.ts";
|
|
2
|
+
export type { TranscriptContext } from "../types.ts";
|
|
3
|
+
/**
|
|
4
|
+
* Build the leading system message for a prompt and tool set. Returns undefined when
|
|
5
|
+
* both are empty, so an empty transcript stays empty.
|
|
6
|
+
*/
|
|
7
|
+
export declare function createInitialSystemMessage(systemPrompt: string | undefined, tools: Tool[] | undefined): SystemMessage | undefined;
|
|
8
|
+
/**
|
|
9
|
+
* Fold `Context.systemPrompt` and `Context.tools` into a leading system message.
|
|
10
|
+
* This is the only entry point that produces a {@link TranscriptContext}; every
|
|
11
|
+
* provider-facing function expects the result. `Context.activeToolNames` is carried
|
|
12
|
+
* over unchanged (senpi#2095 allowed-tools): it has no system-message representation.
|
|
13
|
+
*/
|
|
14
|
+
export declare function normalizeContext(context: Context): TranscriptContext;
|
|
15
|
+
/**
|
|
16
|
+
* Any message list. The replay helpers only read entries whose role is `"system"`, so
|
|
17
|
+
* agent transcripts that carry custom message roles can be passed without filtering.
|
|
18
|
+
*/
|
|
19
|
+
export type TranscriptMessages = readonly {
|
|
20
|
+
role: string;
|
|
21
|
+
}[];
|
|
22
|
+
/** Return the leading system message, if the transcript starts with one. */
|
|
23
|
+
export declare function getInitialSystemMessage(messages: TranscriptMessages): SystemMessage | undefined;
|
|
24
|
+
/** Drop the leading system message for APIs that carry the prompt outside the message list. */
|
|
25
|
+
export declare function withoutInitialSystemMessage(messages: Message[]): Message[];
|
|
26
|
+
/** Resolve the tools available after applying every transcript delta in order. */
|
|
27
|
+
export declare function getCurrentTools(messages: TranscriptMessages): Tool[];
|
|
28
|
+
/**
|
|
29
|
+
* Replay every system message into one leading system message holding the current
|
|
30
|
+
* prompt and tools. Later `content` is appended to the base prompt, `sections` are
|
|
31
|
+
* patched by name, and tools are resolved with {@link getCurrentTools}.
|
|
32
|
+
*/
|
|
33
|
+
export declare function getCurrentSystemMessage(messages: TranscriptMessages): SystemMessage | undefined;
|
|
34
|
+
/** Render the current system prompt text after replaying every system message. */
|
|
35
|
+
export declare function getCurrentSystemPrompt(messages: TranscriptMessages): string;
|
|
36
|
+
/**
|
|
37
|
+
* Rebuild the transcript for APIs without mid-conversation system messages: the replayed
|
|
38
|
+
* system message leads, and every later system message is dropped.
|
|
39
|
+
*/
|
|
40
|
+
export declare function collapseSystemMessages(context: TranscriptContext): TranscriptContext;
|
|
41
|
+
/** Keep later system messages in place when the model accepts them; otherwise collapse them. */
|
|
42
|
+
export declare function resolveTranscript(context: TranscriptContext, supportsMidConvoSystemMessages: boolean | undefined): TranscriptContext;
|
|
43
|
+
/** Strip executable and display-only fields from a tool before transcript comparison or persistence. */
|
|
44
|
+
export declare function toToolDeclaration(tool: Tool): Tool;
|
|
45
|
+
/**
|
|
46
|
+
* Whether two tools declare the same interface to the model.
|
|
47
|
+
*
|
|
48
|
+
* Both sides go through {@link toToolDeclaration} first: its JSON round-trip drops the
|
|
49
|
+
* typebox symbol keys and `undefined` fields that a structural comparison would see, and
|
|
50
|
+
* builds both objects with the same key order, so comparing the serialized declarations
|
|
51
|
+
* is exact. This avoids a deep-equal dependency in a browser-safe package.
|
|
52
|
+
*/
|
|
53
|
+
export declare function declarationsEqual(left: Tool, right: Tool): boolean;
|
|
54
|
+
export interface ToolStateChanges {
|
|
55
|
+
toolsAdded: Tool[];
|
|
56
|
+
toolsRemoved: ToolReference[];
|
|
57
|
+
}
|
|
58
|
+
/** Compare two complete tool states. A changed definition is a removal followed by an addition. */
|
|
59
|
+
export declare function getToolStateChanges(previous: readonly Tool[], current: readonly Tool[]): ToolStateChanges;
|
|
60
|
+
/** Every definition referenced by transcript tool state, in first-declaration order. */
|
|
61
|
+
export declare function getDeclaredTools(messages: TranscriptMessages): Tool[];
|
|
62
|
+
/**
|
|
63
|
+
* Whether a tool name was declared twice with different definitions. Transports that
|
|
64
|
+
* reference tools by name (Anthropic `tool_addition`/`tool_removal`) cannot express that.
|
|
65
|
+
*/
|
|
66
|
+
export declare function hasToolRedefinitions(messages: TranscriptMessages): boolean;
|
|
67
|
+
/** Whether tool history contains a removal or same-name redeclaration that an addition-only transport cannot replay. */
|
|
68
|
+
export declare function hasNonAdditiveToolChanges(messages: TranscriptMessages): boolean;
|
|
69
|
+
export interface TranscriptTools {
|
|
70
|
+
/** Tools sent in the top-level request field. */
|
|
71
|
+
requestTools: Tool[];
|
|
72
|
+
/**
|
|
73
|
+
* Whether later system messages carry their own `toolsAdded` as in-place additions.
|
|
74
|
+
* When false, `requestTools` already holds the complete current tool set.
|
|
75
|
+
*/
|
|
76
|
+
anchorsAdditions: boolean;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Split tool declarations between the top-level request field and in-place additions.
|
|
80
|
+
* Transports that can anchor additions at a system message keep the initial tools at the
|
|
81
|
+
* top and load later ones where they appear; that only works when no tool was removed or
|
|
82
|
+
* redeclared, so everything else sends the current tool list.
|
|
83
|
+
*/
|
|
84
|
+
export declare function resolveTranscriptTools(messages: TranscriptMessages, supportsToolAdditions: boolean): TranscriptTools;
|
|
85
|
+
//# sourceMappingURL=transcript.d.ts.map
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { contentText, getSystemMessageText } from "./text.js";
|
|
2
|
+
/**
|
|
3
|
+
* Build the leading system message for a prompt and tool set. Returns undefined when
|
|
4
|
+
* both are empty, so an empty transcript stays empty.
|
|
5
|
+
*/
|
|
6
|
+
export function createInitialSystemMessage(systemPrompt, tools) {
|
|
7
|
+
const hasSystemPrompt = systemPrompt !== undefined && systemPrompt.length > 0;
|
|
8
|
+
const hasTools = tools !== undefined && tools.length > 0;
|
|
9
|
+
if (!hasSystemPrompt && !hasTools)
|
|
10
|
+
return undefined;
|
|
11
|
+
return {
|
|
12
|
+
role: "system",
|
|
13
|
+
content: systemPrompt ?? "",
|
|
14
|
+
...(hasTools ? { toolsAdded: tools } : {}),
|
|
15
|
+
timestamp: 0,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Fold `Context.systemPrompt` and `Context.tools` into a leading system message.
|
|
20
|
+
* This is the only entry point that produces a {@link TranscriptContext}; every
|
|
21
|
+
* provider-facing function expects the result. `Context.activeToolNames` is carried
|
|
22
|
+
* over unchanged (senpi#2095 allowed-tools): it has no system-message representation.
|
|
23
|
+
*/
|
|
24
|
+
export function normalizeContext(context) {
|
|
25
|
+
const initialMessage = createInitialSystemMessage(context.systemPrompt, context.tools);
|
|
26
|
+
const messages = initialMessage ? [initialMessage, ...context.messages] : context.messages;
|
|
27
|
+
return (context.activeToolNames ? { messages, activeToolNames: context.activeToolNames } : { messages });
|
|
28
|
+
}
|
|
29
|
+
function isSystemMessage(message) {
|
|
30
|
+
return message.role === "system";
|
|
31
|
+
}
|
|
32
|
+
/** Return the leading system message, if the transcript starts with one. */
|
|
33
|
+
export function getInitialSystemMessage(messages) {
|
|
34
|
+
const first = messages[0];
|
|
35
|
+
return first && isSystemMessage(first) ? first : undefined;
|
|
36
|
+
}
|
|
37
|
+
/** Drop the leading system message for APIs that carry the prompt outside the message list. */
|
|
38
|
+
export function withoutInitialSystemMessage(messages) {
|
|
39
|
+
return getInitialSystemMessage(messages) ? messages.slice(1) : messages;
|
|
40
|
+
}
|
|
41
|
+
/** Resolve the tools available after applying every transcript delta in order. */
|
|
42
|
+
export function getCurrentTools(messages) {
|
|
43
|
+
const tools = new Map();
|
|
44
|
+
for (const message of messages) {
|
|
45
|
+
if (!isSystemMessage(message))
|
|
46
|
+
continue;
|
|
47
|
+
for (const tool of message.toolsRemoved ?? [])
|
|
48
|
+
tools.delete(tool.name);
|
|
49
|
+
for (const tool of message.toolsAdded ?? [])
|
|
50
|
+
tools.set(tool.name, tool);
|
|
51
|
+
}
|
|
52
|
+
return [...tools.values()];
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Replay every system message into one leading system message holding the current
|
|
56
|
+
* prompt and tools. Later `content` is appended to the base prompt, `sections` are
|
|
57
|
+
* patched by name, and tools are resolved with {@link getCurrentTools}.
|
|
58
|
+
*/
|
|
59
|
+
export function getCurrentSystemMessage(messages) {
|
|
60
|
+
const content = [];
|
|
61
|
+
const sections = new Map();
|
|
62
|
+
let timestamp;
|
|
63
|
+
for (const message of messages) {
|
|
64
|
+
if (!isSystemMessage(message))
|
|
65
|
+
continue;
|
|
66
|
+
timestamp ??= message.timestamp;
|
|
67
|
+
const text = contentText(message.content);
|
|
68
|
+
if (text.length > 0)
|
|
69
|
+
content.push(text);
|
|
70
|
+
for (const [name, value] of Object.entries(message.sections ?? {})) {
|
|
71
|
+
if (value === null)
|
|
72
|
+
sections.delete(name);
|
|
73
|
+
else
|
|
74
|
+
sections.set(name, value);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const tools = getCurrentTools(messages);
|
|
78
|
+
if (timestamp === undefined && tools.length === 0)
|
|
79
|
+
return undefined;
|
|
80
|
+
return {
|
|
81
|
+
role: "system",
|
|
82
|
+
content: content.join("\n\n"),
|
|
83
|
+
...(sections.size > 0 ? { sections: Object.fromEntries(sections) } : {}),
|
|
84
|
+
...(tools.length > 0 ? { toolsAdded: tools } : {}),
|
|
85
|
+
timestamp: timestamp ?? 0,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/** Render the current system prompt text after replaying every system message. */
|
|
89
|
+
export function getCurrentSystemPrompt(messages) {
|
|
90
|
+
const message = getCurrentSystemMessage(messages);
|
|
91
|
+
return message ? getSystemMessageText(message) : "";
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Rebuild the transcript for APIs without mid-conversation system messages: the replayed
|
|
95
|
+
* system message leads, and every later system message is dropped.
|
|
96
|
+
*/
|
|
97
|
+
export function collapseSystemMessages(context) {
|
|
98
|
+
const head = getCurrentSystemMessage(context.messages);
|
|
99
|
+
const messages = context.messages.filter((message) => message.role !== "system");
|
|
100
|
+
return { ...context, messages: head ? [head, ...messages] : messages };
|
|
101
|
+
}
|
|
102
|
+
/** Keep later system messages in place when the model accepts them; otherwise collapse them. */
|
|
103
|
+
export function resolveTranscript(context, supportsMidConvoSystemMessages) {
|
|
104
|
+
return supportsMidConvoSystemMessages ? context : collapseSystemMessages(context);
|
|
105
|
+
}
|
|
106
|
+
/** Strip executable and display-only fields from a tool before transcript comparison or persistence. */
|
|
107
|
+
export function toToolDeclaration(tool) {
|
|
108
|
+
return {
|
|
109
|
+
name: tool.name,
|
|
110
|
+
description: tool.description,
|
|
111
|
+
parameters: JSON.parse(JSON.stringify(tool.parameters)),
|
|
112
|
+
...(tool.constrainedSampling === undefined ? {} : { constrainedSampling: tool.constrainedSampling }),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Whether two tools declare the same interface to the model.
|
|
117
|
+
*
|
|
118
|
+
* Both sides go through {@link toToolDeclaration} first: its JSON round-trip drops the
|
|
119
|
+
* typebox symbol keys and `undefined` fields that a structural comparison would see, and
|
|
120
|
+
* builds both objects with the same key order, so comparing the serialized declarations
|
|
121
|
+
* is exact. This avoids a deep-equal dependency in a browser-safe package.
|
|
122
|
+
*/
|
|
123
|
+
export function declarationsEqual(left, right) {
|
|
124
|
+
return JSON.stringify(toToolDeclaration(left)) === JSON.stringify(toToolDeclaration(right));
|
|
125
|
+
}
|
|
126
|
+
/** Compare two complete tool states. A changed definition is a removal followed by an addition. */
|
|
127
|
+
export function getToolStateChanges(previous, current) {
|
|
128
|
+
const previousTools = new Map(previous.map((tool) => [tool.name, tool]));
|
|
129
|
+
const currentTools = new Map(current.map((tool) => [tool.name, tool]));
|
|
130
|
+
return {
|
|
131
|
+
toolsAdded: current
|
|
132
|
+
.filter((tool) => {
|
|
133
|
+
const previousTool = previousTools.get(tool.name);
|
|
134
|
+
return previousTool === undefined || !declarationsEqual(previousTool, tool);
|
|
135
|
+
})
|
|
136
|
+
.map(toToolDeclaration),
|
|
137
|
+
toolsRemoved: previous
|
|
138
|
+
.filter((tool) => {
|
|
139
|
+
const currentTool = currentTools.get(tool.name);
|
|
140
|
+
return currentTool === undefined || !declarationsEqual(tool, currentTool);
|
|
141
|
+
})
|
|
142
|
+
.map((tool) => ({ name: tool.name })),
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
/** Every definition referenced by transcript tool state, in first-declaration order. */
|
|
146
|
+
export function getDeclaredTools(messages) {
|
|
147
|
+
const definitions = new Map();
|
|
148
|
+
for (const message of messages) {
|
|
149
|
+
if (!isSystemMessage(message))
|
|
150
|
+
continue;
|
|
151
|
+
for (const tool of message.toolsAdded ?? [])
|
|
152
|
+
definitions.set(tool.name, tool);
|
|
153
|
+
}
|
|
154
|
+
return [...definitions.values()];
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Whether a tool name was declared twice with different definitions. Transports that
|
|
158
|
+
* reference tools by name (Anthropic `tool_addition`/`tool_removal`) cannot express that.
|
|
159
|
+
*/
|
|
160
|
+
export function hasToolRedefinitions(messages) {
|
|
161
|
+
const declared = new Map();
|
|
162
|
+
for (const message of messages) {
|
|
163
|
+
if (!isSystemMessage(message))
|
|
164
|
+
continue;
|
|
165
|
+
for (const tool of message.toolsAdded ?? []) {
|
|
166
|
+
const previous = declared.get(tool.name);
|
|
167
|
+
if (previous !== undefined && !declarationsEqual(previous, tool))
|
|
168
|
+
return true;
|
|
169
|
+
declared.set(tool.name, tool);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
/** Whether tool history contains a removal or same-name redeclaration that an addition-only transport cannot replay. */
|
|
175
|
+
export function hasNonAdditiveToolChanges(messages) {
|
|
176
|
+
const declared = new Set();
|
|
177
|
+
for (const message of messages) {
|
|
178
|
+
if (!isSystemMessage(message))
|
|
179
|
+
continue;
|
|
180
|
+
if ((message.toolsRemoved?.length ?? 0) > 0)
|
|
181
|
+
return true;
|
|
182
|
+
for (const tool of message.toolsAdded ?? []) {
|
|
183
|
+
if (declared.has(tool.name))
|
|
184
|
+
return true;
|
|
185
|
+
declared.add(tool.name);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Split tool declarations between the top-level request field and in-place additions.
|
|
192
|
+
* Transports that can anchor additions at a system message keep the initial tools at the
|
|
193
|
+
* top and load later ones where they appear; that only works when no tool was removed or
|
|
194
|
+
* redeclared, so everything else sends the current tool list.
|
|
195
|
+
*/
|
|
196
|
+
export function resolveTranscriptTools(messages, supportsToolAdditions) {
|
|
197
|
+
const anchorsAdditions = supportsToolAdditions && !hasNonAdditiveToolChanges(messages);
|
|
198
|
+
return {
|
|
199
|
+
requestTools: anchorsAdditions
|
|
200
|
+
? (getInitialSystemMessage(messages)?.toolsAdded ?? [])
|
|
201
|
+
: getCurrentTools(messages),
|
|
202
|
+
anchorsAdditions,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
//# sourceMappingURL=transcript.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@code-yeongyu/senpi-ai",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.10.1",
|
|
4
4
|
"description": "Unified LLM API with automatic model discovery and provider configuration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -73,19 +73,18 @@
|
|
|
73
73
|
"generate-models": "tsx scripts/generate-models.ts --strict",
|
|
74
74
|
"hydrate-model-data": "tsx scripts/generate-models.ts --strict --data-only",
|
|
75
75
|
"generate-model-catalog": "tsx scripts/generate-models.ts --strict --json-only --json-output ../../.artifacts/model-catalog",
|
|
76
|
-
"generate-image-models": "tsx scripts/generate-image-models.ts --strict",
|
|
77
76
|
"check:model-data": "tsx scripts/check-model-data.ts",
|
|
78
77
|
"build": "tsgo -p tsconfig.build.json && shx chmod +x dist/cli.js && shx rm -rf dist/providers/data && shx cp -r src/providers/data dist/providers/data",
|
|
79
78
|
"build:offline": "npm run check:model-data && tsc -p tsconfig.build.json && shx chmod +x dist/cli.js && shx rm -rf dist/providers/data && shx cp -r src/providers/data dist/providers/data",
|
|
80
79
|
"test": "vitest --run",
|
|
81
|
-
"prepublishOnly": "shx rm -rf dist && tsx scripts/generate-models.ts &&
|
|
80
|
+
"prepublishOnly": "shx rm -rf dist && tsx scripts/generate-models.ts && tsc -p tsconfig.build.json && shx chmod +x dist/cli.js",
|
|
82
81
|
"dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput",
|
|
83
82
|
"dev:tsc": "tsc -p tsconfig.build.json --watch --preserveWatchOutput"
|
|
84
83
|
},
|
|
85
84
|
"dependencies": {
|
|
86
85
|
"@anthropic-ai/sdk": "0.127.0",
|
|
87
86
|
"@aws-sdk/client-bedrock-runtime": "3.1136.0",
|
|
88
|
-
"@earendil-works/pi-telemetry": "npm:@code-yeongyu/senpi-telemetry@2026.
|
|
87
|
+
"@earendil-works/pi-telemetry": "npm:@code-yeongyu/senpi-telemetry@2026.10.1",
|
|
89
88
|
"@google/genai": "2.23.0",
|
|
90
89
|
"@smithy/node-http-handler": "4.12.1",
|
|
91
90
|
"http-proxy-agent": "9.1.0",
|