@fractaal/pi-ai 0.86.8 → 1.4.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 +105 -45
- package/dist/api/anthropic-messages.d.ts.map +1 -1
- package/dist/api/anthropic-messages.js +180 -141
- package/dist/api/anthropic-messages.js.map +1 -1
- package/dist/api/azure-openai-responses.d.ts.map +1 -1
- package/dist/api/azure-openai-responses.js +8 -10
- 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 +130 -96
- 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 +10 -1
- package/dist/api/cloudflare-workers-ai-system-one.js.map +1 -1
- package/dist/api/constrained-sampling.d.ts +11 -1
- package/dist/api/constrained-sampling.d.ts.map +1 -1
- package/dist/api/constrained-sampling.js +33 -18
- package/dist/api/constrained-sampling.js.map +1 -1
- package/dist/api/google-generative-ai.d.ts.map +1 -1
- package/dist/api/google-generative-ai.js +4 -10
- package/dist/api/google-generative-ai.js.map +1 -1
- package/dist/api/google-shared.d.ts +2 -1
- package/dist/api/google-shared.d.ts.map +1 -1
- package/dist/api/google-shared.js +60 -3
- 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 -15
- 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 +50 -60
- 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 +23 -14
- 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 +83 -87
- 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 +132 -66
- package/dist/api/openai-responses-shared.js.map +1 -1
- package/dist/api/openai-responses.d.ts.map +1 -1
- package/dist/api/openai-responses.js +38 -19
- 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 +5 -3
- package/dist/api/openrouter-images.js.map +1 -1
- package/dist/api/pi-messages.d.ts +2 -0
- package/dist/api/pi-messages.d.ts.map +1 -1
- package/dist/api/pi-messages.js +18 -12
- package/dist/api/pi-messages.js.map +1 -1
- package/dist/api/simple-options.d.ts.map +1 -1
- package/dist/api/simple-options.js +2 -1
- package/dist/api/simple-options.js.map +1 -1
- package/dist/api/system-one-shared.d.ts +29 -3
- package/dist/api/system-one-shared.d.ts.map +1 -1
- package/dist/api/system-one-shared.js +54 -86
- 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 +12 -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 +14 -6
- package/dist/auth/oauth/anthropic.js.map +1 -1
- package/dist/auth/oauth/openai-chatgpt.js +1 -1
- 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 +4 -4
- package/dist/auth/oauth/openai-codex.js.map +1 -1
- package/dist/auth/types.d.ts +9 -0
- package/dist/auth/types.d.ts.map +1 -1
- package/dist/auth/types.js.map +1 -1
- package/dist/compat/extension-oauth-types.d.ts +4 -0
- package/dist/compat/extension-oauth-types.d.ts.map +1 -1
- package/dist/compat/extension-oauth-types.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -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.js +23 -7
- package/dist/models.js.map +1 -1
- package/dist/providers/compat-schema.d.ts +509 -0
- package/dist/providers/compat-schema.d.ts.map +1 -0
- package/dist/providers/compat-schema.js +396 -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 -1
- package/dist/providers/data/baseten.json +1 -1
- package/dist/providers/data/cerebras.json +1 -1
- package/dist/providers/data/cloudflare-ai-gateway.json +1 -1
- package/dist/providers/data/cloudflare-workers-ai.json +1 -1
- package/dist/providers/data/fireworks.json +1 -1
- package/dist/providers/data/github-copilot.json +1 -1
- package/dist/providers/data/google-vertex.json +1 -1
- package/dist/providers/data/google.json +1 -1
- package/dist/providers/data/groq.json +1 -1
- package/dist/providers/data/huggingface.json +1 -1
- package/dist/providers/data/kimi-coding.json +1 -1
- package/dist/providers/data/meta.json +1 -1
- package/dist/providers/data/minimax-cn.json +1 -1
- package/dist/providers/data/minimax.json +1 -1
- package/dist/providers/data/mistral.json +1 -1
- package/dist/providers/data/moonshotai-cn.json +1 -1
- package/dist/providers/data/moonshotai.json +1 -1
- package/dist/providers/data/nvidia.json +1 -1
- package/dist/providers/data/openai-codex.json +1 -1
- package/dist/providers/data/openai.json +1 -1
- package/dist/providers/data/opencode-go.json +1 -1
- package/dist/providers/data/opencode.json +1 -1
- package/dist/providers/data/openrouter.json +1 -1
- package/dist/providers/data/qwen-token-plan-cn.json +1 -1
- package/dist/providers/data/qwen-token-plan.json +1 -1
- package/dist/providers/data/radius.json +1 -1
- package/dist/providers/data/together.json +1 -1
- package/dist/providers/data/typesafe.json +1 -1
- package/dist/providers/data/vercel-ai-gateway.json +1 -1
- package/dist/providers/data/xai.json +1 -1
- package/dist/providers/data/xiaomi-token-plan-ams.json +1 -1
- package/dist/providers/data/xiaomi-token-plan-cn.json +1 -1
- package/dist/providers/data/xiaomi-token-plan-sgp.json +1 -1
- package/dist/providers/data/xiaomi.json +1 -1
- package/dist/providers/data/zai-coding-cn.json +1 -1
- package/dist/providers/faux.d.ts.map +1 -1
- package/dist/providers/faux.js +31 -9
- 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 +2 -0
- package/dist/providers/opencode-go.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-config.d.ts.map +1 -1
- package/dist/providers/radius-config.js +2 -1
- package/dist/providers/radius-config.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 +192 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/types.d.ts +44 -307
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/error-body.d.ts.map +1 -1
- package/dist/utils/error-body.js +2 -1
- package/dist/utils/error-body.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 +141 -0
- package/dist/utils/identity.js.map +1 -0
- package/dist/utils/json-parse.d.ts +10 -0
- package/dist/utils/json-parse.d.ts.map +1 -1
- package/dist/utils/json-parse.js +26 -1
- package/dist/utils/json-parse.js.map +1 -1
- package/dist/utils/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/openrouter-cost.d.ts +24 -0
- package/dist/utils/openrouter-cost.d.ts.map +1 -0
- package/dist/utils/openrouter-cost.js +34 -0
- package/dist/utils/openrouter-cost.js.map +1 -0
- 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 +26 -10
- package/dist/utils/retry.js.map +1 -1
- package/dist/utils/streaming-json.d.ts +84 -0
- package/dist/utils/streaming-json.d.ts.map +1 -0
- package/dist/utils/streaming-json.js +513 -0
- package/dist/utils/streaming-json.js.map +1 -0
- package/dist/utils/text.d.ts +7 -0
- package/dist/utils/text.d.ts.map +1 -1
- package/dist/utils/text.js +9 -0
- package/dist/utils/text.js.map +1 -1
- package/dist/utils/transcript.d.ts +22 -8
- package/dist/utils/transcript.d.ts.map +1 -1
- package/dist/utils/transcript.js +29 -18
- package/dist/utils/transcript.js.map +1 -1
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +3 -0
- package/dist/utils/validation.js.map +1 -1
- package/dist/utils/version.d.ts +2 -0
- package/dist/utils/version.d.ts.map +1 -0
- package/dist/utils/version.js +3 -0
- package/dist/utils/version.js.map +1 -0
- package/package.json +3 -3
- package/dist/utils/assistant-message-frame.d.ts +0 -77
- package/dist/utils/assistant-message-frame.d.ts.map +0 -1
- package/dist/utils/assistant-message-frame.js +0 -425
- package/dist/utils/assistant-message-frame.js.map +0 -1
- package/dist/utils/pi-user-agent.d.ts +0 -2
- package/dist/utils/pi-user-agent.d.ts.map +0 -1
- package/dist/utils/pi-user-agent.js +0 -12
- package/dist/utils/pi-user-agent.js.map +0 -1
|
@@ -0,0 +1,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;AAgF/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;AA2BD,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,CAgVf"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { calculateCost } from "../models.js";
|
|
2
2
|
import { shortHash } from "../utils/hash.js";
|
|
3
|
-
import { parseStreamingJson } from "../utils/json-parse.js";
|
|
4
3
|
import { sanitizeSurrogates } from "../utils/sanitize-unicode.js";
|
|
4
|
+
import { ToolCallArgumentsStream } from "../utils/streaming-json.js";
|
|
5
5
|
import { getSystemMessageText, renderSystemMessageUpdate } from "../utils/text.js";
|
|
6
6
|
import { resolveTranscript, resolveTranscriptTools } from "../utils/transcript.js";
|
|
7
|
-
import { appendGrammarToolInputJsonDelta, getGrammarToolInput, getJsonSchemaToolParameters, resolveGrammarConstrainedSampling, resolveJsonSchemaStrictSampling, } from "./constrained-sampling.js";
|
|
7
|
+
import { appendGrammarToolInputJsonDelta, closeGrammarToolInputJson, getGrammarToolInput, getJsonSchemaToolParameters, resolveGrammarConstrainedSampling, resolveJsonSchemaStrictSampling, } from "./constrained-sampling.js";
|
|
8
8
|
import { transformMessages } from "./transform-messages.js";
|
|
9
9
|
// =============================================================================
|
|
10
10
|
// Utilities
|
|
@@ -46,7 +46,7 @@ function convertToolResultOutput(model, content) {
|
|
|
46
46
|
.map((c) => c.text)
|
|
47
47
|
.join("\n");
|
|
48
48
|
const images = content.filter((c) => c.type === "image");
|
|
49
|
-
const hasText = textResult.length > 0;
|
|
49
|
+
const hasText = textResult.trim().length > 0;
|
|
50
50
|
if (images.length === 0 || !model.input.includes("image")) {
|
|
51
51
|
return sanitizeSurrogates(hasText ? textResult : images.length > 0 ? "(see attached image)" : "(no tool output)");
|
|
52
52
|
}
|
|
@@ -66,6 +66,34 @@ function convertToolResultOutput(model, content) {
|
|
|
66
66
|
// =============================================================================
|
|
67
67
|
// Message conversion
|
|
68
68
|
// =============================================================================
|
|
69
|
+
/**
|
|
70
|
+
* How a Responses transport expresses tool changes.
|
|
71
|
+
*
|
|
72
|
+
* `additional_tools` items accumulate: a later item adds tools and replaces same-name
|
|
73
|
+
* definitions from earlier items, but it cannot replace a top-level tool and cannot remove any
|
|
74
|
+
* tool (verified with strict schemas on the OpenAI, Codex, and Copilot backends, October 2026).
|
|
75
|
+
* Like Codex, every tool is therefore declared in `additional_tools` items, starting with one
|
|
76
|
+
* at the head of the input, and removals are announced as text. Synthetic tool search can
|
|
77
|
+
* only add tools. Without mid-conversation system messages there is nothing to anchor.
|
|
78
|
+
*/
|
|
79
|
+
export function getResponsesToolSupport(options) {
|
|
80
|
+
if (options.supportsMidConvoSystemMessages && options.supportsAdditionalTools)
|
|
81
|
+
return "changes";
|
|
82
|
+
return options.supportsToolSearch ? "additions" : "none";
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Precedes every additional_tools item after the first. Without it, models read the latest item as
|
|
86
|
+
* the complete tool set once a removal was announced; Codex precedes its incremental tool batches
|
|
87
|
+
* with the same guidance. Restating the tools that are still unavailable keeps smaller models
|
|
88
|
+
* (gpt-5.6-luna, gpt-6-luna) from calling them again after later loads (verified October 2026).
|
|
89
|
+
*/
|
|
90
|
+
function renderIncrementalToolsNotice(unavailable) {
|
|
91
|
+
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.";
|
|
92
|
+
return unavailable.size > 0 ? `${notice}\nStill unavailable: ${[...unavailable].join(", ")}.` : notice;
|
|
93
|
+
}
|
|
94
|
+
function renderToolRemovalNotice(names) {
|
|
95
|
+
return `The following tools are no longer available. Do not call them:\n${names.map((name) => `- ${name}`).join("\n")}`;
|
|
96
|
+
}
|
|
69
97
|
export function convertResponsesMessages(model, context, allowedToolCallProviders, options) {
|
|
70
98
|
const normalizedContext = resolveTranscript(context, options?.supportsMidConvoSystemMessages);
|
|
71
99
|
const messages = [];
|
|
@@ -94,20 +122,30 @@ export function convertResponsesMessages(model, context, allowedToolCallProvider
|
|
|
94
122
|
return `${normalizedCallId}|${normalizedItemId}`;
|
|
95
123
|
};
|
|
96
124
|
const transformedMessages = transformMessages(normalizedContext.messages, model, normalizeToolCallId);
|
|
97
|
-
const
|
|
125
|
+
const toolSupport = getResponsesToolSupport(options ?? {});
|
|
126
|
+
const transcriptTools = resolveTranscriptTools(normalizedContext.messages, toolSupport);
|
|
127
|
+
let declaredAdditionalTools = false;
|
|
128
|
+
/** Tools announced as removed and not declared again since. */
|
|
129
|
+
const unavailableTools = new Set();
|
|
98
130
|
const appendSystemToolAdditions = (message, seed) => {
|
|
99
|
-
const tools =
|
|
131
|
+
const tools = message.toolsAdded ?? [];
|
|
132
|
+
for (const tool of tools)
|
|
133
|
+
unavailableTools.delete(tool.name);
|
|
100
134
|
if (tools.length === 0)
|
|
101
135
|
return;
|
|
102
|
-
if (
|
|
136
|
+
if (toolSupport === "changes") {
|
|
137
|
+
if (declaredAdditionalTools) {
|
|
138
|
+
messages.push({ role: instructionRole, content: renderIncrementalToolsNotice(unavailableTools) });
|
|
139
|
+
}
|
|
140
|
+
declaredAdditionalTools = true;
|
|
103
141
|
messages.push({
|
|
104
142
|
type: "additional_tools",
|
|
105
143
|
role: "developer",
|
|
106
|
-
tools: convertResponsesTools(tools, options
|
|
144
|
+
tools: convertResponsesTools(tools, options?.toolOptions),
|
|
107
145
|
});
|
|
108
146
|
return;
|
|
109
147
|
}
|
|
110
|
-
if (
|
|
148
|
+
if (toolSupport !== "additions")
|
|
111
149
|
return;
|
|
112
150
|
const names = tools.map((tool) => tool.name);
|
|
113
151
|
const callId = `pi_tool_load_${shortHash(`${seed}:${names.join(",")}`)}`;
|
|
@@ -123,21 +161,41 @@ export function convertResponsesMessages(model, context, allowedToolCallProvider
|
|
|
123
161
|
call_id: callId,
|
|
124
162
|
execution: "client",
|
|
125
163
|
status: "completed",
|
|
126
|
-
tools: convertResponsesTools(tools, { ...options
|
|
164
|
+
tools: convertResponsesTools(tools, { ...options?.toolOptions, toolSearchResult: true }),
|
|
127
165
|
});
|
|
128
166
|
};
|
|
129
167
|
const includeInitialSystemMessage = options?.includeSystemPrompt ?? true;
|
|
168
|
+
const pushConfigurationUpdate = (effort) => {
|
|
169
|
+
messages.push({
|
|
170
|
+
type: "configuration_update",
|
|
171
|
+
reasoning: { effort: effort },
|
|
172
|
+
});
|
|
173
|
+
};
|
|
130
174
|
const compat = model.compat;
|
|
131
175
|
const instructionRole = model.reasoning && compat?.supportsDeveloperRole !== false ? "developer" : "system";
|
|
132
176
|
let msgIndex = 0;
|
|
133
177
|
let sourceIndex = 0;
|
|
178
|
+
let systemIndex = 0;
|
|
134
179
|
for (const msg of transformedMessages) {
|
|
135
180
|
const isLeadingSystemMessage = sourceIndex++ === 0 && msg.role === "system";
|
|
136
181
|
if (msg.role === "system") {
|
|
137
|
-
|
|
182
|
+
// transformMessages keeps system messages in order, so their position matches the transcript.
|
|
183
|
+
const anchorsToolChanges = systemIndex++ >= transcriptTools.foldedSystemMessages;
|
|
184
|
+
if (anchorsToolChanges)
|
|
138
185
|
appendSystemToolAdditions(msg, `system:${msgIndex}`);
|
|
139
186
|
if (!isLeadingSystemMessage || includeInitialSystemMessage) {
|
|
140
|
-
const
|
|
187
|
+
const parts = [isLeadingSystemMessage ? getSystemMessageText(msg) : renderSystemMessageUpdate(msg)];
|
|
188
|
+
if (anchorsToolChanges) {
|
|
189
|
+
// A redefinition is a removal plus an addition of the same name; only true removals are announced.
|
|
190
|
+
const added = new Set((msg.toolsAdded ?? []).map((tool) => tool.name));
|
|
191
|
+
const removed = (msg.toolsRemoved ?? []).map((tool) => tool.name).filter((name) => !added.has(name));
|
|
192
|
+
if (removed.length > 0)
|
|
193
|
+
parts.push(renderToolRemovalNotice(removed));
|
|
194
|
+
// Recorded after this message's additions, whose notice precedes this removal notice.
|
|
195
|
+
for (const name of removed)
|
|
196
|
+
unavailableTools.add(name);
|
|
197
|
+
}
|
|
198
|
+
const text = parts.filter((part) => part.length > 0).join("\n\n");
|
|
141
199
|
if (text.length > 0) {
|
|
142
200
|
messages.push({ role: instructionRole, content: sanitizeSurrogates(text) });
|
|
143
201
|
}
|
|
@@ -247,6 +305,11 @@ export function convertResponsesMessages(model, context, allowedToolCallProvider
|
|
|
247
305
|
}
|
|
248
306
|
if (output.length === 0)
|
|
249
307
|
continue;
|
|
308
|
+
if (options?.configurationUpdateEffort !== undefined &&
|
|
309
|
+
isSameModel &&
|
|
310
|
+
msg.providerThinkingLevel !== undefined) {
|
|
311
|
+
pushConfigurationUpdate(msg.providerThinkingLevel);
|
|
312
|
+
}
|
|
250
313
|
messages.push(...output);
|
|
251
314
|
}
|
|
252
315
|
else if (msg.role === "toolResult") {
|
|
@@ -270,6 +333,8 @@ export function convertResponsesMessages(model, context, allowedToolCallProvider
|
|
|
270
333
|
if (!isLeadingSystemMessage)
|
|
271
334
|
msgIndex++;
|
|
272
335
|
}
|
|
336
|
+
if (options?.configurationUpdateEffort !== undefined)
|
|
337
|
+
pushConfigurationUpdate(options.configurationUpdateEffort);
|
|
273
338
|
return messages;
|
|
274
339
|
}
|
|
275
340
|
// =============================================================================
|
|
@@ -309,24 +374,11 @@ export function convertResponsesTools(tools, options) {
|
|
|
309
374
|
return functionTool;
|
|
310
375
|
});
|
|
311
376
|
}
|
|
312
|
-
function getCustomToolCallInput(block) {
|
|
313
|
-
const property = block.customInput?.property;
|
|
314
|
-
if (property === undefined)
|
|
315
|
-
return "";
|
|
316
|
-
const value = block.arguments[property];
|
|
317
|
-
return typeof value === "string" ? value : "";
|
|
318
|
-
}
|
|
319
|
-
function appendCustomToolCallInput(block, nextInput, close) {
|
|
320
|
-
const customInput = block.customInput;
|
|
321
|
-
if (!customInput)
|
|
322
|
-
return undefined;
|
|
323
|
-
const delta = appendGrammarToolInputJsonDelta(customInput.jsonBuffer, customInput.property, nextInput, close);
|
|
324
|
-
block.arguments = { [customInput.property]: nextInput };
|
|
325
|
-
return delta;
|
|
326
|
-
}
|
|
327
377
|
export async function processResponsesStream(openaiStream, output, stream, model, options) {
|
|
328
378
|
let sawTerminalResponseEvent = false;
|
|
329
379
|
const outputSlots = new Map();
|
|
380
|
+
// Tool calls whose output_item.done has not arrived yet.
|
|
381
|
+
const unfinishedToolCalls = new Set();
|
|
330
382
|
const reasoningBlocksById = new Map();
|
|
331
383
|
const applyMessagePhaseStopReason = (item) => {
|
|
332
384
|
if (item.type === "message" && item.phase === "final_answer") {
|
|
@@ -371,19 +423,21 @@ export async function processResponsesStream(openaiStream, output, stream, model
|
|
|
371
423
|
return slot;
|
|
372
424
|
}
|
|
373
425
|
if (item.type === "function_call") {
|
|
426
|
+
const args = new ToolCallArgumentsStream();
|
|
374
427
|
const block = {
|
|
375
428
|
type: "toolCall",
|
|
376
429
|
id: `${item.call_id}|${item.id}`,
|
|
377
430
|
name: item.name,
|
|
378
|
-
arguments: {},
|
|
431
|
+
arguments: item.arguments ? args.append(item.arguments) : {},
|
|
379
432
|
...(item.namespace !== undefined ? { namespace: item.namespace } : {}),
|
|
380
|
-
partialJson: item.arguments || "",
|
|
381
433
|
};
|
|
382
434
|
output.content.push(block);
|
|
435
|
+
unfinishedToolCalls.add(block);
|
|
383
436
|
const slot = {
|
|
384
437
|
type: "toolCall",
|
|
385
438
|
block,
|
|
386
439
|
contentIndex: output.content.length - 1,
|
|
440
|
+
input: { kind: "json", arguments: args },
|
|
387
441
|
};
|
|
388
442
|
outputSlots.set(outputIndex, slot);
|
|
389
443
|
stream.push({ type: "toolcall_start", contentIndex: slot.contentIndex, partial: output });
|
|
@@ -398,16 +452,14 @@ export async function processResponsesStream(openaiStream, output, stream, model
|
|
|
398
452
|
name: item.name,
|
|
399
453
|
arguments: { [inputProperty]: input },
|
|
400
454
|
...(item.namespace !== undefined ? { namespace: item.namespace } : {}),
|
|
401
|
-
customInput: {
|
|
402
|
-
property: inputProperty,
|
|
403
|
-
jsonBuffer: { input: "", started: false, closed: false },
|
|
404
|
-
},
|
|
405
455
|
};
|
|
406
456
|
output.content.push(block);
|
|
457
|
+
unfinishedToolCalls.add(block);
|
|
407
458
|
const slot = {
|
|
408
459
|
type: "toolCall",
|
|
409
460
|
block,
|
|
410
461
|
contentIndex: output.content.length - 1,
|
|
462
|
+
input: customInput(inputProperty, input),
|
|
411
463
|
};
|
|
412
464
|
outputSlots.set(outputIndex, slot);
|
|
413
465
|
stream.push({ type: "toolcall_start", contentIndex: slot.contentIndex, partial: output });
|
|
@@ -567,36 +619,31 @@ export async function processResponsesStream(openaiStream, output, stream, model
|
|
|
567
619
|
}
|
|
568
620
|
else if (event.type === "response.function_call_arguments.delta") {
|
|
569
621
|
const slot = getSlot(event.output_index, "toolCall");
|
|
570
|
-
if (
|
|
622
|
+
if (slot?.input.kind !== "json")
|
|
571
623
|
continue;
|
|
572
|
-
slot.block.
|
|
573
|
-
slot.block.arguments = parseStreamingJson(slot.block.partialJson);
|
|
624
|
+
slot.block.arguments = slot.input.arguments.append(event.delta);
|
|
574
625
|
pushToolCallDelta(slot, event.delta);
|
|
575
626
|
}
|
|
576
627
|
else if (event.type === "response.function_call_arguments.done") {
|
|
577
628
|
const slot = getSlot(event.output_index, "toolCall");
|
|
578
|
-
if (
|
|
629
|
+
if (slot?.input.kind !== "json")
|
|
579
630
|
continue;
|
|
580
|
-
const
|
|
581
|
-
slot.block.
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
const delta = event.arguments.slice(previousPartialJson.length);
|
|
585
|
-
if (delta.length > 0)
|
|
586
|
-
pushToolCallDelta(slot, delta);
|
|
587
|
-
}
|
|
631
|
+
const replaced = slot.input.arguments.replace(event.arguments);
|
|
632
|
+
slot.block.arguments = replaced.arguments;
|
|
633
|
+
if (replaced.delta)
|
|
634
|
+
pushToolCallDelta(slot, replaced.delta);
|
|
588
635
|
}
|
|
589
636
|
else if (event.type === "response.custom_tool_call_input.delta") {
|
|
590
637
|
const slot = getSlot(event.output_index, "toolCall");
|
|
591
|
-
if (
|
|
638
|
+
if (slot?.input.kind !== "custom")
|
|
592
639
|
continue;
|
|
593
|
-
pushToolCallDelta(slot,
|
|
640
|
+
pushToolCallDelta(slot, appendCustomInput(slot.block, slot.input, event.delta));
|
|
594
641
|
}
|
|
595
642
|
else if (event.type === "response.custom_tool_call_input.done") {
|
|
596
643
|
const slot = getSlot(event.output_index, "toolCall");
|
|
597
|
-
if (
|
|
644
|
+
if (slot?.input.kind !== "custom")
|
|
598
645
|
continue;
|
|
599
|
-
pushToolCallDelta(slot,
|
|
646
|
+
pushToolCallDelta(slot, closeCustomInput(slot.block, slot.input, event.input));
|
|
600
647
|
}
|
|
601
648
|
else if (event.type === "response.output_item.done") {
|
|
602
649
|
const item = event.item;
|
|
@@ -628,15 +675,13 @@ export async function processResponsesStream(openaiStream, output, stream, model
|
|
|
628
675
|
});
|
|
629
676
|
outputSlots.delete(event.output_index);
|
|
630
677
|
}
|
|
631
|
-
else if (item.type === "function_call" &&
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
slot.block
|
|
678
|
+
else if (item.type === "function_call" && slot?.type === "toolCall" && slot.input.kind === "json") {
|
|
679
|
+
if (item.arguments)
|
|
680
|
+
slot.input.arguments.replace(item.arguments);
|
|
681
|
+
Object.assign(slot.block, slot.input.arguments.finish());
|
|
635
682
|
if (item.namespace !== undefined)
|
|
636
683
|
slot.block.namespace = item.namespace;
|
|
637
|
-
|
|
638
|
-
// carries parsed arguments.
|
|
639
|
-
delete slot.block.partialJson;
|
|
684
|
+
unfinishedToolCalls.delete(slot.block);
|
|
640
685
|
stream.push({
|
|
641
686
|
type: "toolcall_end",
|
|
642
687
|
contentIndex: slot.contentIndex,
|
|
@@ -645,11 +690,11 @@ export async function processResponsesStream(openaiStream, output, stream, model
|
|
|
645
690
|
});
|
|
646
691
|
outputSlots.delete(event.output_index);
|
|
647
692
|
}
|
|
648
|
-
else if (item.type === "custom_tool_call" && slot?.type === "toolCall" && slot.
|
|
649
|
-
pushToolCallDelta(slot,
|
|
693
|
+
else if (item.type === "custom_tool_call" && slot?.type === "toolCall" && slot.input.kind === "custom") {
|
|
694
|
+
pushToolCallDelta(slot, closeCustomInput(slot.block, slot.input, item.input));
|
|
650
695
|
if (item.namespace !== undefined)
|
|
651
696
|
slot.block.namespace = item.namespace;
|
|
652
|
-
delete
|
|
697
|
+
unfinishedToolCalls.delete(slot.block);
|
|
653
698
|
stream.push({
|
|
654
699
|
type: "toolcall_end",
|
|
655
700
|
contentIndex: slot.contentIndex,
|
|
@@ -683,18 +728,39 @@ export async function processResponsesStream(openaiStream, output, stream, model
|
|
|
683
728
|
}
|
|
684
729
|
// The agent runs every tool call in the final message. Refuse to hand over calls whose
|
|
685
730
|
// output_item.done never arrived: their arguments may be cut off or mixed up, e.g. when a
|
|
686
|
-
// non-compliant server omits output_index.
|
|
731
|
+
// non-compliant server omits output_index.
|
|
687
732
|
if (output.stopReason === "toolUse") {
|
|
688
|
-
for (const
|
|
689
|
-
|
|
690
|
-
continue;
|
|
691
|
-
const toolCall = block;
|
|
692
|
-
if (toolCall.partialJson !== undefined || toolCall.customInput !== undefined) {
|
|
693
|
-
throw new Error(`OpenAI Responses stream completed with an unfinished tool call: ${toolCall.name} (${toolCall.id})`);
|
|
694
|
-
}
|
|
733
|
+
for (const toolCall of unfinishedToolCalls) {
|
|
734
|
+
throw new Error(`OpenAI Responses stream completed with an unfinished tool call: ${toolCall.name} (${toolCall.id})`);
|
|
695
735
|
}
|
|
696
736
|
}
|
|
697
737
|
}
|
|
738
|
+
/** Stream state of a custom tool call that starts with `initialInput`. */
|
|
739
|
+
function customInput(property, initialInput) {
|
|
740
|
+
const buffer = { input: "", started: false, closed: false };
|
|
741
|
+
return {
|
|
742
|
+
kind: "custom",
|
|
743
|
+
property,
|
|
744
|
+
buffer,
|
|
745
|
+
unsent: appendGrammarToolInputJsonDelta(buffer, property, initialInput) ?? "",
|
|
746
|
+
};
|
|
747
|
+
}
|
|
748
|
+
/** Append to a streaming custom tool input; returns the matching piece of its JSON arguments text. */
|
|
749
|
+
function appendCustomInput(block, input, delta) {
|
|
750
|
+
return sendCustomInput(block, input, appendGrammarToolInputJsonDelta(input.buffer, input.property, delta));
|
|
751
|
+
}
|
|
752
|
+
/** Close a custom tool input with the provider's complete input, if it sent one. */
|
|
753
|
+
function closeCustomInput(block, input, finalInput) {
|
|
754
|
+
return sendCustomInput(block, input, closeGrammarToolInputJson(input.buffer, input.property, finalInput));
|
|
755
|
+
}
|
|
756
|
+
function sendCustomInput(block, input, jsonDelta) {
|
|
757
|
+
block.arguments = { [input.property]: input.buffer.input };
|
|
758
|
+
if (input.unsent === "")
|
|
759
|
+
return jsonDelta;
|
|
760
|
+
const sent = input.unsent + (jsonDelta ?? "");
|
|
761
|
+
input.unsent = "";
|
|
762
|
+
return sent;
|
|
763
|
+
}
|
|
698
764
|
function mapStopReason(status, incompleteReason) {
|
|
699
765
|
if (!status)
|
|
700
766
|
return { stopReason: "stop" };
|