@draht/ai 2026.10.20 → 2026.10.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/anthropic-messages.d.ts.map +1 -1
- package/dist/api/anthropic-messages.js +155 -111
- package/dist/api/anthropic-messages.js.map +1 -1
- package/dist/api/azure-openai-config.d.ts +15 -0
- package/dist/api/azure-openai-config.d.ts.map +1 -0
- package/dist/api/azure-openai-config.js +76 -0
- package/dist/api/azure-openai-config.js.map +1 -0
- package/dist/api/azure-openai-responses.d.ts +3 -6
- package/dist/api/azure-openai-responses.d.ts.map +1 -1
- package/dist/api/azure-openai-responses.js +19 -83
- 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 +86 -22
- package/dist/api/bedrock-converse-stream.js.map +1 -1
- package/dist/api/classifier-shared.d.ts +21 -0
- package/dist/api/classifier-shared.d.ts.map +1 -0
- package/dist/api/classifier-shared.js +99 -0
- package/dist/api/classifier-shared.js.map +1 -0
- package/dist/api/cloudflare-workers-ai-system-one.d.ts.map +1 -1
- package/dist/api/cloudflare-workers-ai-system-one.js +15 -9
- 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 +28 -8
- 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 +4 -1
- package/dist/api/llama-cpp-classify.js.map +1 -1
- package/dist/api/mistral-conversations.d.ts.map +1 -1
- package/dist/api/mistral-conversations.js +38 -18
- 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 +21 -13
- 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 +49 -38
- package/dist/api/openai-completions.js.map +1 -1
- package/dist/api/openai-decisions.d.ts +4 -0
- package/dist/api/openai-decisions.d.ts.map +1 -0
- package/dist/api/openai-decisions.js +162 -0
- package/dist/api/openai-decisions.js.map +1 -0
- package/dist/api/openai-decisions.lazy.d.ts +3 -0
- package/dist/api/openai-decisions.lazy.d.ts.map +1 -0
- package/dist/api/openai-decisions.lazy.js +4 -0
- package/dist/api/openai-decisions.lazy.js.map +1 -0
- 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 +82 -16
- 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 +47 -18
- 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 +2 -1
- package/dist/api/simple-options.d.ts.map +1 -1
- package/dist/api/simple-options.js +12 -2
- package/dist/api/simple-options.js.map +1 -1
- package/dist/api/system-one-shared.d.ts +0 -1
- package/dist/api/system-one-shared.d.ts.map +1 -1
- package/dist/api/system-one-shared.js +5 -89
- 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 +2 -1
- package/dist/api/typesafe-system-one.js.map +1 -1
- package/dist/auth/oauth/anthropic.d.ts.map +1 -1
- package/dist/auth/oauth/anthropic.js +65 -7
- package/dist/auth/oauth/anthropic.js.map +1 -1
- package/dist/auth/oauth/load.d.ts +2 -0
- package/dist/auth/oauth/load.d.ts.map +1 -1
- package/dist/auth/oauth/load.js +5 -0
- package/dist/auth/oauth/load.js.map +1 -1
- package/dist/auth/oauth/openai-chatgpt.d.ts.map +1 -1
- package/dist/auth/oauth/openai-chatgpt.js +11 -14
- package/dist/auth/oauth/openai-chatgpt.js.map +1 -1
- package/dist/auth/oauth/openai-codex.d.ts.map +1 -1
- package/dist/auth/oauth/openai-codex.js +7 -5
- package/dist/auth/oauth/openai-codex.js.map +1 -1
- package/dist/auth/oauth/opencode-go.d.ts +3 -12
- package/dist/auth/oauth/opencode-go.d.ts.map +1 -1
- package/dist/auth/oauth/opencode-go.js +229 -34
- package/dist/auth/oauth/opencode-go.js.map +1 -1
- package/dist/auth/resolve.d.ts +16 -1
- package/dist/auth/resolve.d.ts.map +1 -1
- package/dist/auth/resolve.js +51 -27
- package/dist/auth/resolve.js.map +1 -1
- package/dist/auth/types.d.ts +5 -0
- package/dist/auth/types.d.ts.map +1 -1
- package/dist/auth/types.js.map +1 -1
- package/dist/bun-oauth.d.ts.map +1 -1
- package/dist/bun-oauth.js +2 -1
- package/dist/bun-oauth.js.map +1 -1
- package/dist/env-api-keys.js +1 -1
- package/dist/env-api-keys.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 +9 -0
- package/dist/models.d.ts.map +1 -1
- package/dist/models.generated.d.ts +4 -4
- package/dist/models.generated.d.ts.map +1 -1
- package/dist/models.generated.js +4 -4
- package/dist/models.generated.js.map +1 -1
- package/dist/models.js +26 -14
- package/dist/models.js.map +1 -1
- package/dist/providers/all.js +2 -2
- package/dist/providers/all.js.map +1 -1
- package/dist/providers/azure.d.ts +3 -0
- package/dist/providers/azure.d.ts.map +1 -0
- package/dist/providers/azure.js +46 -0
- package/dist/providers/azure.js.map +1 -0
- package/dist/providers/azure.models.d.ts +6 -0
- package/dist/providers/azure.models.d.ts.map +1 -0
- package/dist/providers/azure.models.js +8 -0
- package/dist/providers/azure.models.js.map +1 -0
- package/dist/providers/compat-schema.d.ts +491 -0
- package/dist/providers/compat-schema.d.ts.map +1 -0
- package/dist/providers/compat-schema.js +386 -0
- package/dist/providers/compat-schema.js.map +1 -0
- 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 -0
- 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/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/faux.d.ts.map +1 -1
- package/dist/providers/faux.js +25 -8
- package/dist/providers/faux.js.map +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 +91 -0
- package/dist/providers/model-schema.d.ts.map +1 -0
- package/dist/providers/model-schema.js +57 -0
- package/dist/providers/model-schema.js.map +1 -0
- package/dist/providers/openai.d.ts.map +1 -1
- package/dist/providers/openai.js +12 -3
- package/dist/providers/openai.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 +4 -3
- package/dist/providers/opencode-go.js.map +1 -1
- package/dist/providers/opencode.d.ts.map +1 -1
- package/dist/providers/opencode.js +10 -2
- package/dist/providers/opencode.js.map +1 -1
- package/dist/providers/openrouter-api.d.ts +40 -0
- package/dist/providers/openrouter-api.d.ts.map +1 -0
- package/dist/providers/openrouter-api.js +41 -0
- package/dist/providers/openrouter-api.js.map +1 -0
- package/dist/providers/openrouter.d.ts +11 -1
- package/dist/providers/openrouter.d.ts.map +1 -1
- package/dist/providers/openrouter.js +52 -1
- package/dist/providers/openrouter.js.map +1 -1
- package/dist/providers/radius.d.ts.map +1 -1
- package/dist/providers/radius.js +4 -12
- package/dist/providers/radius.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 +42 -310
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/estimate.js +1 -1
- package/dist/utils/estimate.js.map +1 -1
- package/dist/utils/event-stream.d.ts +10 -1
- package/dist/utils/event-stream.d.ts.map +1 -1
- package/dist/utils/event-stream.js +25 -0
- package/dist/utils/event-stream.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 +145 -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/model-operations.d.ts +3 -1
- package/dist/utils/model-operations.d.ts.map +1 -1
- package/dist/utils/model-operations.js +6 -0
- package/dist/utils/model-operations.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/provider-retry.d.ts +2 -0
- package/dist/utils/provider-retry.d.ts.map +1 -1
- package/dist/utils/provider-retry.js +2 -0
- package/dist/utils/provider-retry.js.map +1 -1
- package/dist/utils/retry.d.ts.map +1 -1
- package/dist/utils/retry.js +30 -10
- package/dist/utils/retry.js.map +1 -1
- package/dist/utils/transcript.d.ts +27 -10
- package/dist/utils/transcript.d.ts.map +1 -1
- package/dist/utils/transcript.js +34 -20
- 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 +5 -0
- package/dist/utils/version.js.map +1 -0
- package/package.json +5 -5
- package/dist/providers/azure-openai-responses.d.ts +0 -3
- package/dist/providers/azure-openai-responses.d.ts.map +0 -1
- package/dist/providers/azure-openai-responses.js +0 -14
- package/dist/providers/azure-openai-responses.js.map +0 -1
- package/dist/providers/azure-openai-responses.models.d.ts +0 -6
- package/dist/providers/azure-openai-responses.models.d.ts.map +0 -1
- package/dist/providers/azure-openai-responses.models.js +0 -8
- package/dist/providers/azure-openai-responses.models.js.map +0 -1
- package/dist/providers/data/azure-openai-responses.json +0 -1
- 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/draht-user-agent.d.ts +0 -2
- package/dist/utils/draht-user-agent.d.ts.map +0 -1
- package/dist/utils/draht-user-agent.js +0 -12
- package/dist/utils/draht-user-agent.js.map +0 -1
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { formatProviderError, normalizeProviderError } from "../utils/error-body.js";
|
|
2
|
+
import { isRecord, parseClassifierUsage, postClassifierRequest, requiredNumber, } from "./classifier-shared.js";
|
|
3
|
+
/**
|
|
4
|
+
* OpenAI's Decisions API: `POST /v1/decisions` with `{ model, input, questions }`.
|
|
5
|
+
* https://developers.openai.com/api/docs/guides/decisions
|
|
6
|
+
*
|
|
7
|
+
* The state is sent as JSON text. With images, the input becomes one user message with the state
|
|
8
|
+
* as `input_text` followed by `input_image` data URLs. Questions map to Decisions types:
|
|
9
|
+
* `choice` to `choice`, `score` to `score`, and `bool` to `predicate`. Predicates have no
|
|
10
|
+
* criteria field, so the meanings of true and false are appended to the instructions.
|
|
11
|
+
*
|
|
12
|
+
* Only OpenAI API keys work: Sign in with ChatGPT tokens are rejected on this route.
|
|
13
|
+
*/
|
|
14
|
+
const LABEL = "OpenAI Decisions";
|
|
15
|
+
/** The endpoint accepts at most this many image parts per request. */
|
|
16
|
+
const MAX_IMAGES = 128;
|
|
17
|
+
function predicateInstructions(question) {
|
|
18
|
+
const meanings = [
|
|
19
|
+
question.criteria.true ? `True means: ${question.criteria.true}` : "",
|
|
20
|
+
question.criteria.false ? `False means: ${question.criteria.false}` : "",
|
|
21
|
+
].filter(Boolean);
|
|
22
|
+
return meanings.length > 0 ? `${question.instructions}\n\n${meanings.join("\n")}` : question.instructions;
|
|
23
|
+
}
|
|
24
|
+
function wireQuestion(name, question) {
|
|
25
|
+
if (question.type === "choice") {
|
|
26
|
+
return {
|
|
27
|
+
type: "choice",
|
|
28
|
+
name,
|
|
29
|
+
instructions: question.instructions,
|
|
30
|
+
choices: Object.entries(question.criteria).map(([value, description]) => description ? { value, description } : { value }),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
if (question.type === "score") {
|
|
34
|
+
return {
|
|
35
|
+
type: "score",
|
|
36
|
+
name,
|
|
37
|
+
instructions: question.instructions,
|
|
38
|
+
levels: question.criteria.map((label) => ({ label })),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
return { type: "predicate", name, instructions: predicateInstructions(question) };
|
|
42
|
+
}
|
|
43
|
+
function wireInput(context) {
|
|
44
|
+
const state = JSON.stringify(context.state);
|
|
45
|
+
const images = context.images ?? [];
|
|
46
|
+
if (images.length === 0)
|
|
47
|
+
return state;
|
|
48
|
+
if (images.length > MAX_IMAGES) {
|
|
49
|
+
throw new Error(`${LABEL} accepts at most ${MAX_IMAGES} images, got ${images.length}`);
|
|
50
|
+
}
|
|
51
|
+
return [
|
|
52
|
+
{
|
|
53
|
+
role: "user",
|
|
54
|
+
content: [
|
|
55
|
+
{ type: "input_text", text: state },
|
|
56
|
+
...images.map((image) => ({
|
|
57
|
+
type: "input_image",
|
|
58
|
+
image_url: `data:${image.mimeType};base64,${image.data}`,
|
|
59
|
+
})),
|
|
60
|
+
],
|
|
61
|
+
},
|
|
62
|
+
];
|
|
63
|
+
}
|
|
64
|
+
function choiceProbabilities(value, id) {
|
|
65
|
+
if (!Array.isArray(value))
|
|
66
|
+
throw new Error(`${LABEL} returned invalid probabilities for ${id}`);
|
|
67
|
+
return Object.fromEntries(value.map((entry) => {
|
|
68
|
+
if (!isRecord(entry) || typeof entry.value !== "string") {
|
|
69
|
+
throw new Error(`${LABEL} returned invalid probabilities for ${id}`);
|
|
70
|
+
}
|
|
71
|
+
return [entry.value, requiredNumber(LABEL, entry.probability, `probability for ${id}.${entry.value}`)];
|
|
72
|
+
}));
|
|
73
|
+
}
|
|
74
|
+
function parseAnswer(id, question, answer) {
|
|
75
|
+
if (answer.type === "refusal")
|
|
76
|
+
throw new Error(`${LABEL} refused to answer ${id}`);
|
|
77
|
+
if (question.type === "choice") {
|
|
78
|
+
if (answer.type !== "choice" || typeof answer.choice !== "string") {
|
|
79
|
+
throw new Error(`${LABEL} did not return a choice answer for ${id}`);
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
type: "choice",
|
|
83
|
+
choice: answer.choice,
|
|
84
|
+
probabilities: choiceProbabilities(answer.probabilities, id),
|
|
85
|
+
confidence: requiredNumber(LABEL, answer.confidence, `confidence for ${id}`),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
if (question.type === "score") {
|
|
89
|
+
if (answer.type !== "score")
|
|
90
|
+
throw new Error(`${LABEL} did not return a score answer for ${id}`);
|
|
91
|
+
return {
|
|
92
|
+
type: "score",
|
|
93
|
+
score: requiredNumber(LABEL, answer.score, `score for ${id}`),
|
|
94
|
+
confidence: requiredNumber(LABEL, answer.confidence, `confidence for ${id}`),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
if (answer.type !== "predicate")
|
|
98
|
+
throw new Error(`${LABEL} did not return a predicate answer for ${id}`);
|
|
99
|
+
return { type: "bool", probability: requiredNumber(LABEL, answer.probability, `probability for ${id}`) };
|
|
100
|
+
}
|
|
101
|
+
function parseAnswers(value, context) {
|
|
102
|
+
if (!Array.isArray(value))
|
|
103
|
+
throw new Error(`${LABEL} returned an unexpected response`);
|
|
104
|
+
const byName = new Map();
|
|
105
|
+
for (const answer of value) {
|
|
106
|
+
if (isRecord(answer) && typeof answer.name === "string")
|
|
107
|
+
byName.set(answer.name, answer);
|
|
108
|
+
}
|
|
109
|
+
return Object.fromEntries(Object.entries(context.questions).map(([id, question]) => {
|
|
110
|
+
const answer = byName.get(id);
|
|
111
|
+
if (!answer)
|
|
112
|
+
throw new Error(`${LABEL} did not return an answer for ${id}`);
|
|
113
|
+
return [id, parseAnswer(id, question, answer)];
|
|
114
|
+
}));
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Cloudflare in front of api.openai.com answers 504 with an HTML page when a request runs longer
|
|
118
|
+
* than about five seconds. Large inputs, currently above roughly 600K tokens, hit this limit, and
|
|
119
|
+
* retrying the same input runs into it again, so 504 is not retried.
|
|
120
|
+
*/
|
|
121
|
+
const NO_RETRY_STATUSES = [504];
|
|
122
|
+
function errorMessage(error) {
|
|
123
|
+
if (error.status === 504) {
|
|
124
|
+
return `${LABEL} error (504): the request timed out at the gateway. Very large inputs (above roughly 600K tokens) currently exceed its time limit.`;
|
|
125
|
+
}
|
|
126
|
+
return formatProviderError(normalizeProviderError(error), `${LABEL} error`);
|
|
127
|
+
}
|
|
128
|
+
/** Classification through OpenAI's Decisions API. */
|
|
129
|
+
export const classify = async (model, context, options) => {
|
|
130
|
+
const output = {
|
|
131
|
+
api: model.api,
|
|
132
|
+
provider: model.provider,
|
|
133
|
+
model: model.id,
|
|
134
|
+
answers: {},
|
|
135
|
+
stopReason: "stop",
|
|
136
|
+
timestamp: Date.now(),
|
|
137
|
+
};
|
|
138
|
+
try {
|
|
139
|
+
if (model.api !== "openai-decisions")
|
|
140
|
+
throw new Error(`Unsupported classifier API: ${model.api}`);
|
|
141
|
+
const body = await postClassifierRequest(LABEL, new URL("decisions", `${model.baseUrl.replace(/\/+$/u, "")}/`), model, {
|
|
142
|
+
model: model.id,
|
|
143
|
+
input: wireInput(context),
|
|
144
|
+
questions: Object.entries(context.questions).map(([id, question]) => wireQuestion(id, question)),
|
|
145
|
+
}, options, NO_RETRY_STATUSES);
|
|
146
|
+
if (!isRecord(body))
|
|
147
|
+
throw new Error(`${LABEL} returned an unexpected response`);
|
|
148
|
+
// Set before parsing answers: a request with malformed or refused answers was still billed.
|
|
149
|
+
const usage = parseClassifierUsage(body.usage, model);
|
|
150
|
+
if (usage)
|
|
151
|
+
output.usage = usage;
|
|
152
|
+
output.answers = parseAnswers(body.answers, context);
|
|
153
|
+
return output;
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
output.answers = {};
|
|
157
|
+
output.stopReason = options?.signal?.aborted ? "aborted" : "error";
|
|
158
|
+
output.errorMessage = errorMessage(error);
|
|
159
|
+
return output;
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
//# sourceMappingURL=openai-decisions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai-decisions.js","sourceRoot":"","sources":["../../src/api/openai-decisions.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n\tClassifierAnswer,\n\tClassifierContext,\n\tClassifierFunction,\n\tClassifierOptions,\n\tClassifierQuestion,\n\tClassifierResult,\n} from \"../types.ts\";\nimport { formatProviderError, normalizeProviderError } from \"../utils/error-body.ts\";\nimport {\n\ttype ClassifierHttpError,\n\tisRecord,\n\tparseClassifierUsage,\n\tpostClassifierRequest,\n\trequiredNumber,\n} from \"./classifier-shared.ts\";\n\n/**\n * OpenAI's Decisions API: `POST /v1/decisions` with `{ model, input, questions }`.\n * https://developers.openai.com/api/docs/guides/decisions\n *\n * The state is sent as JSON text. With images, the input becomes one user message with the state\n * as `input_text` followed by `input_image` data URLs. Questions map to Decisions types:\n * `choice` to `choice`, `score` to `score`, and `bool` to `predicate`. Predicates have no\n * criteria field, so the meanings of true and false are appended to the instructions.\n *\n * Only OpenAI API keys work: Sign in with ChatGPT tokens are rejected on this route.\n */\n\nconst LABEL = \"OpenAI Decisions\";\n\n/** The endpoint accepts at most this many image parts per request. */\nconst MAX_IMAGES = 128;\n\ninterface DecisionQuestion {\n\ttype: \"predicate\" | \"choice\" | \"score\";\n\tname: string;\n\tinstructions: string;\n\tchoices?: Array<{ value: string; description?: string }>;\n\tlevels?: Array<{ label: string }>;\n}\n\nfunction predicateInstructions(question: Extract<ClassifierQuestion, { type: \"bool\" }>): string {\n\tconst meanings = [\n\t\tquestion.criteria.true ? `True means: ${question.criteria.true}` : \"\",\n\t\tquestion.criteria.false ? `False means: ${question.criteria.false}` : \"\",\n\t].filter(Boolean);\n\treturn meanings.length > 0 ? `${question.instructions}\\n\\n${meanings.join(\"\\n\")}` : question.instructions;\n}\n\nfunction wireQuestion(name: string, question: ClassifierQuestion): DecisionQuestion {\n\tif (question.type === \"choice\") {\n\t\treturn {\n\t\t\ttype: \"choice\",\n\t\t\tname,\n\t\t\tinstructions: question.instructions,\n\t\t\tchoices: Object.entries(question.criteria).map(([value, description]) =>\n\t\t\t\tdescription ? { value, description } : { value },\n\t\t\t),\n\t\t};\n\t}\n\tif (question.type === \"score\") {\n\t\treturn {\n\t\t\ttype: \"score\",\n\t\t\tname,\n\t\t\tinstructions: question.instructions,\n\t\t\tlevels: question.criteria.map((label) => ({ label })),\n\t\t};\n\t}\n\treturn { type: \"predicate\", name, instructions: predicateInstructions(question) };\n}\n\nfunction wireInput(context: ClassifierContext): unknown {\n\tconst state = JSON.stringify(context.state);\n\tconst images = context.images ?? [];\n\tif (images.length === 0) return state;\n\tif (images.length > MAX_IMAGES) {\n\t\tthrow new Error(`${LABEL} accepts at most ${MAX_IMAGES} images, got ${images.length}`);\n\t}\n\treturn [\n\t\t{\n\t\t\trole: \"user\",\n\t\t\tcontent: [\n\t\t\t\t{ type: \"input_text\", text: state },\n\t\t\t\t...images.map((image) => ({\n\t\t\t\t\ttype: \"input_image\",\n\t\t\t\t\timage_url: `data:${image.mimeType};base64,${image.data}`,\n\t\t\t\t})),\n\t\t\t],\n\t\t},\n\t];\n}\n\nfunction choiceProbabilities(value: unknown, id: string): Record<string, number> {\n\tif (!Array.isArray(value)) throw new Error(`${LABEL} returned invalid probabilities for ${id}`);\n\treturn Object.fromEntries(\n\t\tvalue.map((entry) => {\n\t\t\tif (!isRecord(entry) || typeof entry.value !== \"string\") {\n\t\t\t\tthrow new Error(`${LABEL} returned invalid probabilities for ${id}`);\n\t\t\t}\n\t\t\treturn [entry.value, requiredNumber(LABEL, entry.probability, `probability for ${id}.${entry.value}`)];\n\t\t}),\n\t);\n}\n\nfunction parseAnswer(id: string, question: ClassifierQuestion, answer: Record<string, unknown>): ClassifierAnswer {\n\tif (answer.type === \"refusal\") throw new Error(`${LABEL} refused to answer ${id}`);\n\tif (question.type === \"choice\") {\n\t\tif (answer.type !== \"choice\" || typeof answer.choice !== \"string\") {\n\t\t\tthrow new Error(`${LABEL} did not return a choice answer for ${id}`);\n\t\t}\n\t\treturn {\n\t\t\ttype: \"choice\",\n\t\t\tchoice: answer.choice,\n\t\t\tprobabilities: choiceProbabilities(answer.probabilities, id),\n\t\t\tconfidence: requiredNumber(LABEL, answer.confidence, `confidence for ${id}`),\n\t\t};\n\t}\n\tif (question.type === \"score\") {\n\t\tif (answer.type !== \"score\") throw new Error(`${LABEL} did not return a score answer for ${id}`);\n\t\treturn {\n\t\t\ttype: \"score\",\n\t\t\tscore: requiredNumber(LABEL, answer.score, `score for ${id}`),\n\t\t\tconfidence: requiredNumber(LABEL, answer.confidence, `confidence for ${id}`),\n\t\t};\n\t}\n\tif (answer.type !== \"predicate\") throw new Error(`${LABEL} did not return a predicate answer for ${id}`);\n\treturn { type: \"bool\", probability: requiredNumber(LABEL, answer.probability, `probability for ${id}`) };\n}\n\nfunction parseAnswers(value: unknown, context: ClassifierContext): Record<string, ClassifierAnswer> {\n\tif (!Array.isArray(value)) throw new Error(`${LABEL} returned an unexpected response`);\n\tconst byName = new Map<string, Record<string, unknown>>();\n\tfor (const answer of value) {\n\t\tif (isRecord(answer) && typeof answer.name === \"string\") byName.set(answer.name, answer);\n\t}\n\treturn Object.fromEntries(\n\t\tObject.entries(context.questions).map(([id, question]) => {\n\t\t\tconst answer = byName.get(id);\n\t\t\tif (!answer) throw new Error(`${LABEL} did not return an answer for ${id}`);\n\t\t\treturn [id, parseAnswer(id, question, answer)];\n\t\t}),\n\t);\n}\n\n/**\n * Cloudflare in front of api.openai.com answers 504 with an HTML page when a request runs longer\n * than about five seconds. Large inputs, currently above roughly 600K tokens, hit this limit, and\n * retrying the same input runs into it again, so 504 is not retried.\n */\nconst NO_RETRY_STATUSES = [504];\n\nfunction errorMessage(error: unknown): string {\n\tif ((error as Partial<ClassifierHttpError>).status === 504) {\n\t\treturn `${LABEL} error (504): the request timed out at the gateway. Very large inputs (above roughly 600K tokens) currently exceed its time limit.`;\n\t}\n\treturn formatProviderError(normalizeProviderError(error), `${LABEL} error`);\n}\n\n/** Classification through OpenAI's Decisions API. */\nexport const classify: ClassifierFunction<ClassifierOptions> = async (model, context, options) => {\n\tconst output: ClassifierResult = {\n\t\tapi: model.api,\n\t\tprovider: model.provider,\n\t\tmodel: model.id,\n\t\tanswers: {},\n\t\tstopReason: \"stop\",\n\t\ttimestamp: Date.now(),\n\t};\n\n\ttry {\n\t\tif (model.api !== \"openai-decisions\") throw new Error(`Unsupported classifier API: ${model.api}`);\n\t\tconst body = await postClassifierRequest(\n\t\t\tLABEL,\n\t\t\tnew URL(\"decisions\", `${model.baseUrl.replace(/\\/+$/u, \"\")}/`),\n\t\t\tmodel,\n\t\t\t{\n\t\t\t\tmodel: model.id,\n\t\t\t\tinput: wireInput(context),\n\t\t\t\tquestions: Object.entries(context.questions).map(([id, question]) => wireQuestion(id, question)),\n\t\t\t},\n\t\t\toptions,\n\t\t\tNO_RETRY_STATUSES,\n\t\t);\n\t\tif (!isRecord(body)) throw new Error(`${LABEL} returned an unexpected response`);\n\t\t// Set before parsing answers: a request with malformed or refused answers was still billed.\n\t\tconst usage = parseClassifierUsage(body.usage, model);\n\t\tif (usage) output.usage = usage;\n\t\toutput.answers = parseAnswers(body.answers, context);\n\t\treturn output;\n\t} catch (error) {\n\t\toutput.answers = {};\n\t\toutput.stopReason = options?.signal?.aborted ? \"aborted\" : \"error\";\n\t\toutput.errorMessage = errorMessage(error);\n\t\treturn output;\n\t}\n};\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai-decisions.lazy.d.ts","sourceRoot":"","sources":["../../src/api/openai-decisions.lazy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEtD,eAAO,MAAM,kBAAkB,QAAO,kBAGpC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai-decisions.lazy.js","sourceRoot":"","sources":["../../src/api/openai-decisions.lazy.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAAuB,EAAE,CAAC,CAAC;IAC5D,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,CAC3C,CAAC,MAAM,MAAM,CAAC,uBAAuB,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC;CAC1E,CAAC,CAAC","sourcesContent":["import type { ProviderClassifier } from \"../types.ts\";\n\nexport const openAIDecisionsApi = (): ProviderClassifier => ({\n\tclassify: async (model, context, options) =>\n\t\t(await import(\"./openai-decisions.ts\")).classify(model, context, options),\n});\n"]}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Tool as OpenAITool, ResponseCreateParamsStreaming, ResponseInput, ResponseStreamEvent } from "openai/resources/responses/responses.js";
|
|
2
2
|
import type { Api, AssistantMessage, Model, StreamOptions, Tool, TranscriptContext, Usage } from "../types.ts";
|
|
3
3
|
import type { AssistantMessageEventStream } from "../utils/event-stream.ts";
|
|
4
|
+
import { type TranscriptToolSupport } from "../utils/transcript.ts";
|
|
4
5
|
export interface OpenAIResponsesStreamOptions {
|
|
5
6
|
onProviderStreamEvent?: StreamOptions["onProviderStreamEvent"];
|
|
6
7
|
serviceTier?: ResponseCreateParamsStreaming["service_tier"];
|
|
@@ -16,6 +17,16 @@ export interface ConvertResponsesMessagesOptions {
|
|
|
16
17
|
supportsAdditionalTools?: boolean;
|
|
17
18
|
supportsToolSearch?: boolean;
|
|
18
19
|
toolOptions?: ConvertResponsesToolsOptions;
|
|
20
|
+
/**
|
|
21
|
+
* Effort for the reply being generated. When set, the thinking level is sent as
|
|
22
|
+
* configuration_update items instead of a changing reasoning.effort, which would invalidate the
|
|
23
|
+
* prompt cache. Each item sets the effort from that point on: one goes before every earlier
|
|
24
|
+
* reply with the level recorded on it (`providerThinkingLevel`), and one with this effort goes
|
|
25
|
+
* at the end. Earlier input therefore never changes. OpenAI documents the item before user
|
|
26
|
+
* messages only. After tool results it was verified on gpt-6-luna (October 2026) to be accepted
|
|
27
|
+
* and to apply to the next reply.
|
|
28
|
+
*/
|
|
29
|
+
configurationUpdateEffort?: string;
|
|
19
30
|
}
|
|
20
31
|
export interface ConvertResponsesToolsOptions {
|
|
21
32
|
strict?: boolean | null;
|
|
@@ -23,6 +34,21 @@ export interface ConvertResponsesToolsOptions {
|
|
|
23
34
|
supportsOpenAIGrammarTools?: boolean;
|
|
24
35
|
toolSearchResult?: boolean;
|
|
25
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* How a Responses transport expresses tool changes.
|
|
39
|
+
*
|
|
40
|
+
* `additional_tools` items accumulate: a later item adds tools and replaces same-name
|
|
41
|
+
* definitions from earlier items, but it cannot replace a top-level tool and cannot remove any
|
|
42
|
+
* tool (verified with strict schemas on the OpenAI, Codex, and Copilot backends, October 2026).
|
|
43
|
+
* Like Codex, every tool is therefore declared in `additional_tools` items, starting with one
|
|
44
|
+
* at the head of the input, and removals are announced as text. Synthetic tool search can
|
|
45
|
+
* only add tools. Without mid-conversation system messages there is nothing to anchor.
|
|
46
|
+
*/
|
|
47
|
+
export declare function getResponsesToolSupport(options: {
|
|
48
|
+
supportsMidConvoSystemMessages?: boolean;
|
|
49
|
+
supportsAdditionalTools?: boolean;
|
|
50
|
+
supportsToolSearch?: boolean;
|
|
51
|
+
}): TranscriptToolSupport;
|
|
26
52
|
export declare function convertResponsesMessages<TApi extends Api>(model: Model<TApi>, context: TranscriptContext, allowedToolCallProviders: ReadonlySet<string>, options?: ConvertResponsesMessagesOptions): ResponseInput;
|
|
27
53
|
export declare function convertResponsesTools(tools: readonly Tool[], options?: ConvertResponsesToolsOptions): OpenAITool[];
|
|
28
54
|
export declare function processResponsesStream<TApi extends Api>(openaiStream: AsyncIterable<ResponseStreamEvent>, output: AssistantMessage, stream: AssistantMessageEventStream, model: Model<TApi>, options?: OpenAIResponsesStreamOptions): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openai-responses-shared.d.ts","sourceRoot":"","sources":["../../src/api/openai-responses-shared.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACX,IAAI,IAAI,UAAU,
|
|
1
|
+
{"version":3,"file":"openai-responses-shared.d.ts","sourceRoot":"","sources":["../../src/api/openai-responses-shared.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACX,IAAI,IAAI,UAAU,EAElB,6BAA6B,EAC7B,aAAa,EAQb,mBAAmB,EAEnB,MAAM,yCAAyC,CAAC;AAEjD,OAAO,KAAK,EACX,GAAG,EACH,gBAAgB,EAEhB,KAAK,EAEL,aAAa,EAKb,IAAI,EAEJ,iBAAiB,EACjB,KAAK,EACL,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AAK5E,OAAO,EAA6C,KAAK,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAwE/G,MAAM,WAAW,4BAA4B;IAC5C,qBAAqB,CAAC,EAAE,aAAa,CAAC,uBAAuB,CAAC,CAAC;IAC/D,WAAW,CAAC,EAAE,6BAA6B,CAAC,cAAc,CAAC,CAAC;IAC5D,0BAA0B,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzD,kBAAkB,CAAC,EAAE,CACpB,mBAAmB,EAAE,6BAA6B,CAAC,cAAc,CAAC,GAAG,SAAS,EAC9E,kBAAkB,EAAE,6BAA6B,CAAC,cAAc,CAAC,GAAG,SAAS,KACzE,6BAA6B,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAC/D,uBAAuB,CAAC,EAAE,CACzB,KAAK,EAAE,KAAK,EACZ,WAAW,EAAE,6BAA6B,CAAC,cAAc,CAAC,GAAG,SAAS,KAClE,IAAI,CAAC;CACV;AAED,MAAM,WAAW,+BAA+B;IAC/C,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,0BAA0B,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzD,0GAA0G;IAC1G,8BAA8B,CAAC,EAAE,OAAO,CAAC;IACzC,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,WAAW,CAAC,EAAE,4BAA4B,CAAC;IAC3C;;;;;;;;OAQG;IACH,yBAAyB,CAAC,EAAE,MAAM,CAAC;CACnC;AAED,MAAM,WAAW,4BAA4B;IAC5C,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IACxB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC3B;AAMD;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE;IAChD,8BAA8B,CAAC,EAAE,OAAO,CAAC;IACzC,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC7B,GAAG,qBAAqB,CAGxB;AAkBD,wBAAgB,wBAAwB,CAAC,IAAI,SAAS,GAAG,EACxD,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,EAClB,OAAO,EAAE,iBAAiB,EAC1B,wBAAwB,EAAE,WAAW,CAAC,MAAM,CAAC,EAC7C,OAAO,CAAC,EAAE,+BAA+B,GACvC,aAAa,CA4Of;AAMD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,SAAS,IAAI,EAAE,EAAE,OAAO,CAAC,EAAE,4BAA4B,GAAG,UAAU,EAAE,CAqClH;AAoCD,wBAAsB,sBAAsB,CAAC,IAAI,SAAS,GAAG,EAC5D,YAAY,EAAE,aAAa,CAAC,mBAAmB,CAAC,EAChD,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,2BAA2B,EACnC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,EAClB,OAAO,CAAC,EAAE,4BAA4B,GACpC,OAAO,CAAC,IAAI,CAAC,CAkVf"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { calculateCost } from "../models.js";
|
|
2
2
|
import { shortHash } from "../utils/hash.js";
|
|
3
|
-
import { parseStreamingJson } from "../utils/json-parse.js";
|
|
3
|
+
import { parseStreamingJson, parseToolCallArguments } from "../utils/json-parse.js";
|
|
4
4
|
import { sanitizeSurrogates } from "../utils/sanitize-unicode.js";
|
|
5
5
|
import { getSystemMessageText, renderSystemMessageUpdate } from "../utils/text.js";
|
|
6
6
|
import { resolveTranscript, resolveTranscriptTools } from "../utils/transcript.js";
|
|
@@ -40,7 +40,7 @@ function convertToolResultOutput(model, content) {
|
|
|
40
40
|
.map((c) => c.text)
|
|
41
41
|
.join("\n");
|
|
42
42
|
const images = content.filter((c) => c.type === "image");
|
|
43
|
-
const hasText = textResult.length > 0;
|
|
43
|
+
const hasText = textResult.trim().length > 0;
|
|
44
44
|
if (images.length === 0 || !model.input.includes("image")) {
|
|
45
45
|
return sanitizeSurrogates(hasText ? textResult : images.length > 0 ? "(see attached image)" : "(no tool output)");
|
|
46
46
|
}
|
|
@@ -60,6 +60,34 @@ function convertToolResultOutput(model, content) {
|
|
|
60
60
|
// =============================================================================
|
|
61
61
|
// Message conversion
|
|
62
62
|
// =============================================================================
|
|
63
|
+
/**
|
|
64
|
+
* How a Responses transport expresses tool changes.
|
|
65
|
+
*
|
|
66
|
+
* `additional_tools` items accumulate: a later item adds tools and replaces same-name
|
|
67
|
+
* definitions from earlier items, but it cannot replace a top-level tool and cannot remove any
|
|
68
|
+
* tool (verified with strict schemas on the OpenAI, Codex, and Copilot backends, October 2026).
|
|
69
|
+
* Like Codex, every tool is therefore declared in `additional_tools` items, starting with one
|
|
70
|
+
* at the head of the input, and removals are announced as text. Synthetic tool search can
|
|
71
|
+
* only add tools. Without mid-conversation system messages there is nothing to anchor.
|
|
72
|
+
*/
|
|
73
|
+
export function getResponsesToolSupport(options) {
|
|
74
|
+
if (options.supportsMidConvoSystemMessages && options.supportsAdditionalTools)
|
|
75
|
+
return "changes";
|
|
76
|
+
return options.supportsToolSearch ? "additions" : "none";
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Precedes every additional_tools item after the first. Without it, models read the latest item as
|
|
80
|
+
* the complete tool set once a removal was announced; Codex precedes its incremental tool batches
|
|
81
|
+
* with the same guidance. Restating the tools that are still unavailable keeps smaller models
|
|
82
|
+
* (gpt-5.6-luna, gpt-6-luna) from calling them again after later loads (verified October 2026).
|
|
83
|
+
*/
|
|
84
|
+
function renderIncrementalToolsNotice(unavailable) {
|
|
85
|
+
const notice = "This is an incremental tools update. Previously declared tools remain available unless explicitly marked unavailable. A tool declared again here is available again, and its latest definition replaces the earlier one.";
|
|
86
|
+
return unavailable.size > 0 ? `${notice}\nStill unavailable: ${[...unavailable].join(", ")}.` : notice;
|
|
87
|
+
}
|
|
88
|
+
function renderToolRemovalNotice(names) {
|
|
89
|
+
return `The following tools are no longer available. Do not call them:\n${names.map((name) => `- ${name}`).join("\n")}`;
|
|
90
|
+
}
|
|
63
91
|
export function convertResponsesMessages(model, context, allowedToolCallProviders, options) {
|
|
64
92
|
const normalizedContext = resolveTranscript(context, options?.supportsMidConvoSystemMessages);
|
|
65
93
|
const messages = [];
|
|
@@ -88,20 +116,30 @@ export function convertResponsesMessages(model, context, allowedToolCallProvider
|
|
|
88
116
|
return `${normalizedCallId}|${normalizedItemId}`;
|
|
89
117
|
};
|
|
90
118
|
const transformedMessages = transformMessages(normalizedContext.messages, model, normalizeToolCallId);
|
|
91
|
-
const
|
|
119
|
+
const toolSupport = getResponsesToolSupport(options ?? {});
|
|
120
|
+
const transcriptTools = resolveTranscriptTools(normalizedContext.messages, toolSupport);
|
|
121
|
+
let declaredAdditionalTools = false;
|
|
122
|
+
/** Tools announced as removed and not declared again since. */
|
|
123
|
+
const unavailableTools = new Set();
|
|
92
124
|
const appendSystemToolAdditions = (message, seed) => {
|
|
93
|
-
const tools =
|
|
125
|
+
const tools = message.toolsAdded ?? [];
|
|
126
|
+
for (const tool of tools)
|
|
127
|
+
unavailableTools.delete(tool.name);
|
|
94
128
|
if (tools.length === 0)
|
|
95
129
|
return;
|
|
96
|
-
if (
|
|
130
|
+
if (toolSupport === "changes") {
|
|
131
|
+
if (declaredAdditionalTools) {
|
|
132
|
+
messages.push({ role: instructionRole, content: renderIncrementalToolsNotice(unavailableTools) });
|
|
133
|
+
}
|
|
134
|
+
declaredAdditionalTools = true;
|
|
97
135
|
messages.push({
|
|
98
136
|
type: "additional_tools",
|
|
99
137
|
role: "developer",
|
|
100
|
-
tools: convertResponsesTools(tools, options
|
|
138
|
+
tools: convertResponsesTools(tools, options?.toolOptions),
|
|
101
139
|
});
|
|
102
140
|
return;
|
|
103
141
|
}
|
|
104
|
-
if (
|
|
142
|
+
if (toolSupport !== "additions")
|
|
105
143
|
return;
|
|
106
144
|
const names = tools.map((tool) => tool.name);
|
|
107
145
|
const callId = `pi_tool_load_${shortHash(`${seed}:${names.join(",")}`)}`;
|
|
@@ -117,21 +155,41 @@ export function convertResponsesMessages(model, context, allowedToolCallProvider
|
|
|
117
155
|
call_id: callId,
|
|
118
156
|
execution: "client",
|
|
119
157
|
status: "completed",
|
|
120
|
-
tools: convertResponsesTools(tools, { ...options
|
|
158
|
+
tools: convertResponsesTools(tools, { ...options?.toolOptions, toolSearchResult: true }),
|
|
121
159
|
});
|
|
122
160
|
};
|
|
123
161
|
const includeInitialSystemMessage = options?.includeSystemPrompt ?? true;
|
|
162
|
+
const pushConfigurationUpdate = (effort) => {
|
|
163
|
+
messages.push({
|
|
164
|
+
type: "configuration_update",
|
|
165
|
+
reasoning: { effort: effort },
|
|
166
|
+
});
|
|
167
|
+
};
|
|
124
168
|
const compat = model.compat;
|
|
125
169
|
const instructionRole = model.reasoning && compat?.supportsDeveloperRole !== false ? "developer" : "system";
|
|
126
170
|
let msgIndex = 0;
|
|
127
171
|
let sourceIndex = 0;
|
|
172
|
+
let systemIndex = 0;
|
|
128
173
|
for (const msg of transformedMessages) {
|
|
129
174
|
const isLeadingSystemMessage = sourceIndex++ === 0 && msg.role === "system";
|
|
130
175
|
if (msg.role === "system") {
|
|
131
|
-
|
|
176
|
+
// transformMessages keeps system messages in order, so their position matches the transcript.
|
|
177
|
+
const anchorsToolChanges = systemIndex++ >= transcriptTools.foldedSystemMessages;
|
|
178
|
+
if (anchorsToolChanges)
|
|
132
179
|
appendSystemToolAdditions(msg, `system:${msgIndex}`);
|
|
133
180
|
if (!isLeadingSystemMessage || includeInitialSystemMessage) {
|
|
134
|
-
const
|
|
181
|
+
const parts = [isLeadingSystemMessage ? getSystemMessageText(msg) : renderSystemMessageUpdate(msg)];
|
|
182
|
+
if (anchorsToolChanges) {
|
|
183
|
+
// A redefinition is a removal plus an addition of the same name; only true removals are announced.
|
|
184
|
+
const added = new Set((msg.toolsAdded ?? []).map((tool) => tool.name));
|
|
185
|
+
const removed = (msg.toolsRemoved ?? []).map((tool) => tool.name).filter((name) => !added.has(name));
|
|
186
|
+
if (removed.length > 0)
|
|
187
|
+
parts.push(renderToolRemovalNotice(removed));
|
|
188
|
+
// Recorded after this message's additions, whose notice precedes this removal notice.
|
|
189
|
+
for (const name of removed)
|
|
190
|
+
unavailableTools.add(name);
|
|
191
|
+
}
|
|
192
|
+
const text = parts.filter((part) => part.length > 0).join("\n\n");
|
|
135
193
|
if (text.length > 0) {
|
|
136
194
|
messages.push({ role: instructionRole, content: sanitizeSurrogates(text) });
|
|
137
195
|
}
|
|
@@ -208,12 +266,13 @@ export function convertResponsesMessages(model, context, allowedToolCallProvider
|
|
|
208
266
|
const customInputProperty = options?.grammarToolInputProperties?.get(toolCall.name);
|
|
209
267
|
let itemId = itemIdRaw;
|
|
210
268
|
// For different-model messages, set id to undefined to avoid pairing validation.
|
|
211
|
-
// OpenAI tracks which
|
|
269
|
+
// OpenAI tracks which item IDs were paired with rs_xxx reasoning items.
|
|
212
270
|
// By omitting the id, we avoid triggering that validation (like cross-provider does).
|
|
213
|
-
//
|
|
214
|
-
//
|
|
215
|
-
|
|
216
|
-
|
|
271
|
+
// Also drop ids that do not match the replayed item type: function_call ids must be fc_*
|
|
272
|
+
// and custom_tool_call ids must be ctc_*. Foreign tool call ids are normalized to fc_*, and
|
|
273
|
+
// a call can switch between the two types when grammar tool support differs.
|
|
274
|
+
const itemIdPrefix = customInputProperty === undefined ? "fc_" : "ctc_";
|
|
275
|
+
if (isDifferentModel || !itemId?.startsWith(itemIdPrefix)) {
|
|
217
276
|
itemId = undefined;
|
|
218
277
|
}
|
|
219
278
|
if (customInputProperty !== undefined) {
|
|
@@ -240,6 +299,11 @@ export function convertResponsesMessages(model, context, allowedToolCallProvider
|
|
|
240
299
|
}
|
|
241
300
|
if (output.length === 0)
|
|
242
301
|
continue;
|
|
302
|
+
if (options?.configurationUpdateEffort !== undefined &&
|
|
303
|
+
isSameModel &&
|
|
304
|
+
msg.providerThinkingLevel !== undefined) {
|
|
305
|
+
pushConfigurationUpdate(msg.providerThinkingLevel);
|
|
306
|
+
}
|
|
243
307
|
messages.push(...output);
|
|
244
308
|
}
|
|
245
309
|
else if (msg.role === "toolResult") {
|
|
@@ -263,6 +327,8 @@ export function convertResponsesMessages(model, context, allowedToolCallProvider
|
|
|
263
327
|
if (!isLeadingSystemMessage)
|
|
264
328
|
msgIndex++;
|
|
265
329
|
}
|
|
330
|
+
if (options?.configurationUpdateEffort !== undefined)
|
|
331
|
+
pushConfigurationUpdate(options.configurationUpdateEffort);
|
|
266
332
|
return messages;
|
|
267
333
|
}
|
|
268
334
|
// =============================================================================
|
|
@@ -607,7 +673,7 @@ export async function processResponsesStream(openaiStream, output, stream, model
|
|
|
607
673
|
else if (item.type === "function_call" &&
|
|
608
674
|
slot?.type === "toolCall" &&
|
|
609
675
|
slot.block.partialJson !== undefined) {
|
|
610
|
-
slot.block
|
|
676
|
+
Object.assign(slot.block, parseToolCallArguments(item.arguments || slot.block.partialJson));
|
|
611
677
|
if (item.namespace !== undefined)
|
|
612
678
|
slot.block.namespace = item.namespace;
|
|
613
679
|
// Finalize in-place and strip the scratch buffer so replay only
|