@fleetagent/pi-ai 0.1.8 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +29 -24
- package/dist/cli.js.map +1 -1
- package/dist/env-api-keys.d.ts.map +1 -1
- package/dist/env-api-keys.js +1 -1
- package/dist/env-api-keys.js.map +1 -1
- package/dist/image-models.generated.d.ts +225 -0
- package/dist/image-models.generated.d.ts.map +1 -1
- package/dist/image-models.generated.js +227 -2
- package/dist/image-models.generated.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/models.d.ts +37 -2
- package/dist/models.d.ts.map +1 -1
- package/dist/models.generated.d.ts +6120 -2593
- package/dist/models.generated.d.ts.map +1 -1
- package/dist/models.generated.js +5347 -2110
- package/dist/models.generated.js.map +1 -1
- package/dist/models.js +259 -9
- package/dist/models.js.map +1 -1
- package/dist/oauth.d.ts +6 -1
- package/dist/oauth.d.ts.map +1 -1
- package/dist/oauth.js +4 -1
- package/dist/oauth.js.map +1 -1
- package/dist/providers/amazon-bedrock.d.ts +5 -4
- package/dist/providers/amazon-bedrock.d.ts.map +1 -1
- package/dist/providers/amazon-bedrock.js +303 -330
- package/dist/providers/amazon-bedrock.js.map +1 -1
- package/dist/providers/anthropic.d.ts +5 -4
- package/dist/providers/anthropic.d.ts.map +1 -1
- package/dist/providers/anthropic.js +492 -499
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/azure-openai-responses.d.ts +4 -2
- package/dist/providers/azure-openai-responses.d.ts.map +1 -1
- package/dist/providers/azure-openai-responses.js +52 -35
- package/dist/providers/azure-openai-responses.js.map +1 -1
- package/dist/providers/faux.d.ts +19 -22
- package/dist/providers/faux.d.ts.map +1 -1
- package/dist/providers/faux.js +106 -70
- package/dist/providers/faux.js.map +1 -1
- package/dist/providers/github-copilot-headers.d.ts +7 -4
- package/dist/providers/github-copilot-headers.d.ts.map +1 -1
- package/dist/providers/github-copilot-headers.js.map +1 -1
- package/dist/providers/google-shared.d.ts +24 -5
- package/dist/providers/google-shared.d.ts.map +1 -1
- package/dist/providers/google-shared.js +139 -140
- package/dist/providers/google-shared.js.map +1 -1
- package/dist/providers/google-vertex.d.ts +3 -7
- package/dist/providers/google-vertex.d.ts.map +1 -1
- package/dist/providers/google-vertex.js +216 -219
- package/dist/providers/google-vertex.js.map +1 -1
- package/dist/providers/google.d.ts +4 -7
- package/dist/providers/google.d.ts.map +1 -1
- package/dist/providers/google.js +214 -218
- package/dist/providers/google.js.map +1 -1
- package/dist/providers/images/openrouter.d.ts.map +1 -1
- package/dist/providers/images/openrouter.js +37 -27
- package/dist/providers/images/openrouter.js.map +1 -1
- package/dist/providers/mistral.d.ts +7 -6
- package/dist/providers/mistral.d.ts.map +1 -1
- package/dist/providers/mistral.js +287 -265
- package/dist/providers/mistral.js.map +1 -1
- package/dist/providers/openai-codex-responses.d.ts +8 -5
- package/dist/providers/openai-codex-responses.d.ts.map +1 -1
- package/dist/providers/openai-codex-responses.js +493 -391
- package/dist/providers/openai-codex-responses.js.map +1 -1
- package/dist/providers/openai-completions.d.ts +7 -10
- package/dist/providers/openai-completions.d.ts.map +1 -1
- package/dist/providers/openai-completions.js +670 -664
- package/dist/providers/openai-completions.js.map +1 -1
- package/dist/providers/openai-responses-shared.d.ts +5 -4
- package/dist/providers/openai-responses-shared.d.ts.map +1 -1
- package/dist/providers/openai-responses-shared.js +390 -360
- package/dist/providers/openai-responses-shared.js.map +1 -1
- package/dist/providers/openai-responses.d.ts +5 -5
- package/dist/providers/openai-responses.d.ts.map +1 -1
- package/dist/providers/openai-responses.js +134 -109
- package/dist/providers/openai-responses.js.map +1 -1
- package/dist/providers/simple-options.d.ts +4 -2
- package/dist/providers/simple-options.d.ts.map +1 -1
- package/dist/providers/simple-options.js.map +1 -1
- package/dist/providers/transform-messages.d.ts.map +1 -1
- package/dist/providers/transform-messages.js +112 -137
- package/dist/providers/transform-messages.js.map +1 -1
- package/dist/stream.d.ts +0 -1
- package/dist/stream.d.ts.map +1 -1
- package/dist/stream.js +0 -1
- package/dist/stream.js.map +1 -1
- package/dist/types.d.ts +91 -63
- 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 +15 -4
- package/dist/utils/error-body.js.map +1 -1
- package/dist/utils/estimate.d.ts +4 -0
- package/dist/utils/estimate.d.ts.map +1 -1
- package/dist/utils/estimate.js +3 -1
- package/dist/utils/estimate.js.map +1 -1
- package/dist/utils/headers.d.ts +3 -0
- package/dist/utils/headers.d.ts.map +1 -1
- package/dist/utils/headers.js +21 -0
- package/dist/utils/headers.js.map +1 -1
- package/dist/utils/json-parse.d.ts.map +1 -1
- package/dist/utils/json-parse.js +17 -19
- package/dist/utils/json-parse.js.map +1 -1
- package/dist/utils/oauth/anthropic.d.ts +4 -11
- package/dist/utils/oauth/anthropic.d.ts.map +1 -1
- package/dist/utils/oauth/anthropic.js +100 -117
- package/dist/utils/oauth/anthropic.js.map +1 -1
- package/dist/utils/oauth/device-code.d.ts.map +1 -1
- package/dist/utils/oauth/device-code.js +51 -38
- package/dist/utils/oauth/device-code.js.map +1 -1
- package/dist/utils/oauth/github-copilot.d.ts +6 -9
- package/dist/utils/oauth/github-copilot.d.ts.map +1 -1
- package/dist/utils/oauth/github-copilot.js +47 -42
- package/dist/utils/oauth/github-copilot.js.map +1 -1
- package/dist/utils/oauth/index.d.ts +8 -17
- package/dist/utils/oauth/index.d.ts.map +1 -1
- package/dist/utils/oauth/index.js +27 -23
- package/dist/utils/oauth/index.js.map +1 -1
- package/dist/utils/oauth/oauth-page.d.ts.map +1 -1
- package/dist/utils/oauth/oauth-page.js.map +1 -1
- package/dist/utils/oauth/openai-codex.d.ts +7 -12
- package/dist/utils/oauth/openai-codex.d.ts.map +1 -1
- package/dist/utils/oauth/openai-codex.js +72 -89
- package/dist/utils/oauth/openai-codex.js.map +1 -1
- package/dist/utils/oauth/pkce.d.ts +5 -4
- package/dist/utils/oauth/pkce.d.ts.map +1 -1
- package/dist/utils/oauth/pkce.js.map +1 -1
- package/dist/utils/oauth/types.d.ts +7 -2
- package/dist/utils/oauth/types.d.ts.map +1 -1
- package/dist/utils/oauth/types.js.map +1 -1
- package/dist/utils/provider-retry.d.ts +16 -0
- package/dist/utils/provider-retry.d.ts.map +1 -0
- package/dist/utils/provider-retry.js +94 -0
- package/dist/utils/provider-retry.js.map +1 -0
- package/dist/utils/retry.d.ts +14 -0
- package/dist/utils/retry.d.ts.map +1 -1
- package/dist/utils/retry.js +84 -0
- package/dist/utils/retry.js.map +1 -1
- package/dist/utils/typebox-helpers.d.ts +5 -4
- package/dist/utils/typebox-helpers.d.ts.map +1 -1
- package/dist/utils/typebox-helpers.js.map +1 -1
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +102 -99
- package/dist/utils/validation.js.map +1 -1
- package/package.json +2 -2
|
@@ -3,9 +3,6 @@ import { shortHash } from "../utils/hash.js";
|
|
|
3
3
|
import { parseStreamingJson } from "../utils/json-parse.js";
|
|
4
4
|
import { sanitizeSurrogates } from "../utils/sanitize-unicode.js";
|
|
5
5
|
import { transformMessages } from "./transform-messages.js";
|
|
6
|
-
// =============================================================================
|
|
7
|
-
// Utilities
|
|
8
|
-
// =============================================================================
|
|
9
6
|
function encodeTextSignatureV1(id, phase) {
|
|
10
7
|
const payload = { v: 1, id };
|
|
11
8
|
if (phase)
|
|
@@ -34,169 +31,162 @@ function parseTextSignature(signature) {
|
|
|
34
31
|
// =============================================================================
|
|
35
32
|
// Message conversion
|
|
36
33
|
// =============================================================================
|
|
37
|
-
|
|
38
|
-
const
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
34
|
+
function normalizeResponsesIdPart(part) {
|
|
35
|
+
const sanitized = part.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
36
|
+
const normalized = sanitized.length > 64 ? sanitized.slice(0, 64) : sanitized;
|
|
37
|
+
return normalized.replace(/_+$/, "");
|
|
38
|
+
}
|
|
39
|
+
function buildForeignResponsesItemId(itemId) {
|
|
40
|
+
const normalized = `fc_${shortHash(itemId)}`;
|
|
41
|
+
return normalized.length > 64 ? normalized.slice(0, 64) : normalized;
|
|
42
|
+
}
|
|
43
|
+
function normalizeResponsesToolCallId(id, model, source, allowedToolCallProviders) {
|
|
44
|
+
if (!allowedToolCallProviders.has(model.provider))
|
|
45
|
+
return normalizeResponsesIdPart(id);
|
|
46
|
+
if (!id.includes("|"))
|
|
47
|
+
return normalizeResponsesIdPart(id);
|
|
48
|
+
const [callId, itemId] = id.split("|");
|
|
49
|
+
const normalizedCallId = normalizeResponsesIdPart(callId);
|
|
50
|
+
const isForeignToolCall = source.provider !== model.provider || source.api !== model.api;
|
|
51
|
+
let normalizedItemId = isForeignToolCall ? buildForeignResponsesItemId(itemId) : normalizeResponsesIdPart(itemId);
|
|
52
|
+
// OpenAI Responses API requires item IDs to start with "fc".
|
|
53
|
+
if (!normalizedItemId.startsWith("fc_"))
|
|
54
|
+
normalizedItemId = normalizeResponsesIdPart(`fc_${normalizedItemId}`);
|
|
55
|
+
return `${normalizedCallId}|${normalizedItemId}`;
|
|
56
|
+
}
|
|
57
|
+
function convertResponsesUserMessage(message) {
|
|
58
|
+
if (typeof message.content === "string") {
|
|
59
|
+
return [
|
|
60
|
+
{
|
|
61
|
+
role: "user",
|
|
62
|
+
content: [{ type: "input_text", text: sanitizeSurrogates(message.content) }],
|
|
63
|
+
},
|
|
64
|
+
];
|
|
65
|
+
}
|
|
66
|
+
const content = message.content.map((item) => {
|
|
67
|
+
if (item.type === "text") {
|
|
68
|
+
return {
|
|
69
|
+
type: "input_text",
|
|
70
|
+
text: sanitizeSurrogates(item.text),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
type: "input_image",
|
|
75
|
+
detail: "auto",
|
|
76
|
+
image_url: `data:${item.mimeType};base64,${item.data}`,
|
|
77
|
+
};
|
|
78
|
+
});
|
|
79
|
+
return content.length === 0 ? [] : [{ role: "user", content }];
|
|
80
|
+
}
|
|
81
|
+
function resolveResponsesMessageId(textBlock, messageIndex, textBlockIndex) {
|
|
82
|
+
const parsedSignature = parseTextSignature(textBlock.textSignature);
|
|
83
|
+
const fallbackId = textBlockIndex === 0 ? `msg_pi_${messageIndex}` : `msg_pi_${messageIndex}_${textBlockIndex}`;
|
|
84
|
+
if (!parsedSignature?.id)
|
|
85
|
+
return { id: fallbackId };
|
|
86
|
+
return {
|
|
87
|
+
...parsedSignature,
|
|
88
|
+
id: parsedSignature.id.length > 64 ? `msg_${shortHash(parsedSignature.id)}` : parsedSignature.id,
|
|
43
89
|
};
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
90
|
+
}
|
|
91
|
+
function convertResponsesTextBlock(block, messageIndex, textBlockIndex) {
|
|
92
|
+
const signature = resolveResponsesMessageId(block, messageIndex, textBlockIndex);
|
|
93
|
+
return {
|
|
94
|
+
type: "message",
|
|
95
|
+
role: "assistant",
|
|
96
|
+
content: [{ type: "output_text", text: sanitizeSurrogates(block.text), annotations: [] }],
|
|
97
|
+
status: "completed",
|
|
98
|
+
id: signature.id,
|
|
99
|
+
phase: signature.phase,
|
|
47
100
|
};
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if (!normalizedItemId.startsWith("fc_")) {
|
|
59
|
-
normalizedItemId = normalizeIdPart(`fc_${normalizedItemId}`);
|
|
60
|
-
}
|
|
61
|
-
return `${normalizedCallId}|${normalizedItemId}`;
|
|
101
|
+
}
|
|
102
|
+
function convertResponsesToolCall(block, isDifferentModel) {
|
|
103
|
+
const [callId, rawItemId] = block.id.split("|");
|
|
104
|
+
const itemId = isDifferentModel && rawItemId?.startsWith("fc_") ? undefined : rawItemId;
|
|
105
|
+
return {
|
|
106
|
+
type: "function_call",
|
|
107
|
+
id: itemId,
|
|
108
|
+
call_id: callId,
|
|
109
|
+
name: block.name,
|
|
110
|
+
arguments: JSON.stringify(block.arguments),
|
|
62
111
|
};
|
|
112
|
+
}
|
|
113
|
+
function convertResponsesAssistantMessage(message, model, messageIndex) {
|
|
114
|
+
const output = [];
|
|
115
|
+
const isDifferentModel = message.model !== model.id && message.provider === model.provider && message.api === model.api;
|
|
116
|
+
let textBlockIndex = 0;
|
|
117
|
+
for (const block of message.content) {
|
|
118
|
+
if (block.type === "thinking") {
|
|
119
|
+
if (block.thinkingSignature)
|
|
120
|
+
output.push(JSON.parse(block.thinkingSignature));
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (block.type === "text") {
|
|
124
|
+
output.push(convertResponsesTextBlock(block, messageIndex, textBlockIndex));
|
|
125
|
+
textBlockIndex++;
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (block.type === "toolCall")
|
|
129
|
+
output.push(convertResponsesToolCall(block, isDifferentModel));
|
|
130
|
+
}
|
|
131
|
+
return output;
|
|
132
|
+
}
|
|
133
|
+
function buildResponsesToolOutput(message, supportsImages) {
|
|
134
|
+
const textResult = message.content
|
|
135
|
+
.filter((content) => content.type === "text")
|
|
136
|
+
.map((content) => content.text)
|
|
137
|
+
.join("\n");
|
|
138
|
+
const hasImages = message.content.some((content) => content.type === "image");
|
|
139
|
+
const hasText = textResult.length > 0;
|
|
140
|
+
if (!hasImages || !supportsImages) {
|
|
141
|
+
return sanitizeSurrogates(hasText ? textResult : hasImages ? "(see attached image)" : "(no tool output)");
|
|
142
|
+
}
|
|
143
|
+
const contentParts = [];
|
|
144
|
+
if (hasText)
|
|
145
|
+
contentParts.push({ type: "input_text", text: sanitizeSurrogates(textResult) });
|
|
146
|
+
for (const block of message.content) {
|
|
147
|
+
if (block.type !== "image")
|
|
148
|
+
continue;
|
|
149
|
+
contentParts.push({
|
|
150
|
+
type: "input_image",
|
|
151
|
+
detail: "auto",
|
|
152
|
+
image_url: `data:${block.mimeType};base64,${block.data}`,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
return contentParts;
|
|
156
|
+
}
|
|
157
|
+
function convertResponsesToolResult(message, model) {
|
|
158
|
+
const [callId] = message.toolCallId.split("|");
|
|
159
|
+
return [
|
|
160
|
+
{
|
|
161
|
+
type: "function_call_output",
|
|
162
|
+
call_id: callId,
|
|
163
|
+
output: buildResponsesToolOutput(message, model.input.includes("image")),
|
|
164
|
+
},
|
|
165
|
+
];
|
|
166
|
+
}
|
|
167
|
+
export function convertResponsesMessages(model, context, allowedToolCallProviders, options) {
|
|
168
|
+
const messages = [];
|
|
169
|
+
const normalizeToolCallId = (id, _targetModel, source) => normalizeResponsesToolCallId(id, model, source, allowedToolCallProviders);
|
|
63
170
|
const transformedMessages = transformMessages(context.messages, model, normalizeToolCallId);
|
|
64
|
-
|
|
65
|
-
if (includeSystemPrompt && context.systemPrompt) {
|
|
66
|
-
const role = model.reasoning ? "developer" : "system";
|
|
171
|
+
if ((options?.includeSystemPrompt ?? true) && context.systemPrompt) {
|
|
67
172
|
messages.push({
|
|
68
|
-
role,
|
|
173
|
+
role: model.reasoning ? "developer" : "system",
|
|
69
174
|
content: sanitizeSurrogates(context.systemPrompt),
|
|
70
175
|
});
|
|
71
176
|
}
|
|
72
|
-
let
|
|
73
|
-
for (const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
type: "input_text",
|
|
86
|
-
text: sanitizeSurrogates(item.text),
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
return {
|
|
90
|
-
type: "input_image",
|
|
91
|
-
detail: "auto",
|
|
92
|
-
image_url: `data:${item.mimeType};base64,${item.data}`,
|
|
93
|
-
};
|
|
94
|
-
});
|
|
95
|
-
if (content.length === 0)
|
|
96
|
-
continue;
|
|
97
|
-
messages.push({
|
|
98
|
-
role: "user",
|
|
99
|
-
content,
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
else if (msg.role === "assistant") {
|
|
104
|
-
const output = [];
|
|
105
|
-
const assistantMsg = msg;
|
|
106
|
-
const isDifferentModel = assistantMsg.model !== model.id &&
|
|
107
|
-
assistantMsg.provider === model.provider &&
|
|
108
|
-
assistantMsg.api === model.api;
|
|
109
|
-
let textBlockIndex = 0;
|
|
110
|
-
for (const block of msg.content) {
|
|
111
|
-
if (block.type === "thinking") {
|
|
112
|
-
if (block.thinkingSignature) {
|
|
113
|
-
const reasoningItem = JSON.parse(block.thinkingSignature);
|
|
114
|
-
output.push(reasoningItem);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
else if (block.type === "text") {
|
|
118
|
-
const textBlock = block;
|
|
119
|
-
const parsedSignature = parseTextSignature(textBlock.textSignature);
|
|
120
|
-
const fallbackMessageId = textBlockIndex === 0 ? `msg_pi_${msgIndex}` : `msg_pi_${msgIndex}_${textBlockIndex}`;
|
|
121
|
-
textBlockIndex++;
|
|
122
|
-
// OpenAI requires id to be max 64 characters
|
|
123
|
-
let msgId = parsedSignature?.id;
|
|
124
|
-
if (!msgId) {
|
|
125
|
-
msgId = fallbackMessageId;
|
|
126
|
-
}
|
|
127
|
-
else if (msgId.length > 64) {
|
|
128
|
-
msgId = `msg_${shortHash(msgId)}`;
|
|
129
|
-
}
|
|
130
|
-
output.push({
|
|
131
|
-
type: "message",
|
|
132
|
-
role: "assistant",
|
|
133
|
-
content: [{ type: "output_text", text: sanitizeSurrogates(textBlock.text), annotations: [] }],
|
|
134
|
-
status: "completed",
|
|
135
|
-
id: msgId,
|
|
136
|
-
phase: parsedSignature?.phase,
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
else if (block.type === "toolCall") {
|
|
140
|
-
const toolCall = block;
|
|
141
|
-
const [callId, itemIdRaw] = toolCall.id.split("|");
|
|
142
|
-
let itemId = itemIdRaw;
|
|
143
|
-
// For different-model messages, set id to undefined to avoid pairing validation.
|
|
144
|
-
// OpenAI tracks which fc_xxx IDs were paired with rs_xxx reasoning items.
|
|
145
|
-
// By omitting the id, we avoid triggering that validation (like cross-provider does).
|
|
146
|
-
if (isDifferentModel && itemId?.startsWith("fc_")) {
|
|
147
|
-
itemId = undefined;
|
|
148
|
-
}
|
|
149
|
-
output.push({
|
|
150
|
-
type: "function_call",
|
|
151
|
-
id: itemId,
|
|
152
|
-
call_id: callId,
|
|
153
|
-
name: toolCall.name,
|
|
154
|
-
arguments: JSON.stringify(toolCall.arguments),
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
if (output.length === 0)
|
|
159
|
-
continue;
|
|
160
|
-
messages.push(...output);
|
|
161
|
-
}
|
|
162
|
-
else if (msg.role === "toolResult") {
|
|
163
|
-
const textResult = msg.content
|
|
164
|
-
.filter((c) => c.type === "text")
|
|
165
|
-
.map((c) => c.text)
|
|
166
|
-
.join("\n");
|
|
167
|
-
const hasImages = msg.content.some((c) => c.type === "image");
|
|
168
|
-
const hasText = textResult.length > 0;
|
|
169
|
-
const [callId] = msg.toolCallId.split("|");
|
|
170
|
-
let output;
|
|
171
|
-
if (hasImages && model.input.includes("image")) {
|
|
172
|
-
const contentParts = [];
|
|
173
|
-
if (hasText) {
|
|
174
|
-
contentParts.push({
|
|
175
|
-
type: "input_text",
|
|
176
|
-
text: sanitizeSurrogates(textResult),
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
for (const block of msg.content) {
|
|
180
|
-
if (block.type === "image") {
|
|
181
|
-
contentParts.push({
|
|
182
|
-
type: "input_image",
|
|
183
|
-
detail: "auto",
|
|
184
|
-
image_url: `data:${block.mimeType};base64,${block.data}`,
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
output = contentParts;
|
|
189
|
-
}
|
|
190
|
-
else {
|
|
191
|
-
output = sanitizeSurrogates(hasText ? textResult : "(see attached image)");
|
|
192
|
-
}
|
|
193
|
-
messages.push({
|
|
194
|
-
type: "function_call_output",
|
|
195
|
-
call_id: callId,
|
|
196
|
-
output,
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
msgIndex++;
|
|
177
|
+
let messageIndex = 0;
|
|
178
|
+
for (const message of transformedMessages) {
|
|
179
|
+
let converted;
|
|
180
|
+
if (message.role === "user")
|
|
181
|
+
converted = convertResponsesUserMessage(message);
|
|
182
|
+
else if (message.role === "assistant")
|
|
183
|
+
converted = convertResponsesAssistantMessage(message, model, messageIndex);
|
|
184
|
+
else
|
|
185
|
+
converted = convertResponsesToolResult(message, model);
|
|
186
|
+
if (converted.length === 0)
|
|
187
|
+
continue;
|
|
188
|
+
messages.push(...converted);
|
|
189
|
+
messageIndex++;
|
|
200
190
|
}
|
|
201
191
|
return messages;
|
|
202
192
|
}
|
|
@@ -213,34 +203,50 @@ export function convertResponsesTools(tools, options) {
|
|
|
213
203
|
strict,
|
|
214
204
|
}));
|
|
215
205
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
206
|
+
function applyResponsesMessagePhaseStopReason(output, item) {
|
|
207
|
+
if (item.type === "message" && item.phase === "final_answer")
|
|
208
|
+
output.stopReason = "stop";
|
|
209
|
+
}
|
|
210
|
+
function getResponsesThinkingSlot(state, outputIndex) {
|
|
211
|
+
const slot = state.outputSlots.get(outputIndex);
|
|
212
|
+
return slot?.type === "thinking" ? slot : undefined;
|
|
213
|
+
}
|
|
214
|
+
function getResponsesTextSlot(state, outputIndex) {
|
|
215
|
+
const slot = state.outputSlots.get(outputIndex);
|
|
216
|
+
return slot?.type === "text" ? slot : undefined;
|
|
217
|
+
}
|
|
218
|
+
function getResponsesToolCallSlot(state, outputIndex) {
|
|
219
|
+
const slot = state.outputSlots.get(outputIndex);
|
|
220
|
+
return slot?.type === "toolCall" ? slot : undefined;
|
|
221
|
+
}
|
|
222
|
+
function createResponsesOutputSlot(state, outputIndex, item) {
|
|
223
|
+
switch (item.type) {
|
|
224
|
+
case "reasoning": {
|
|
224
225
|
const block = { type: "thinking", thinking: "" };
|
|
225
|
-
output.content.push(block);
|
|
226
|
+
state.output.content.push(block);
|
|
226
227
|
const slot = {
|
|
227
228
|
type: "thinking",
|
|
228
229
|
block,
|
|
229
|
-
contentIndex: output.content.length - 1,
|
|
230
|
+
contentIndex: state.output.content.length - 1,
|
|
230
231
|
};
|
|
231
|
-
outputSlots.set(outputIndex, slot);
|
|
232
|
-
stream.push({ type: "thinking_start", contentIndex: slot.contentIndex, partial: output });
|
|
232
|
+
state.outputSlots.set(outputIndex, slot);
|
|
233
|
+
state.stream.push({ type: "thinking_start", contentIndex: slot.contentIndex, partial: state.output });
|
|
233
234
|
return slot;
|
|
234
235
|
}
|
|
235
|
-
|
|
236
|
+
case "message": {
|
|
237
|
+
applyResponsesMessagePhaseStopReason(state.output, item);
|
|
236
238
|
const block = { type: "text", text: "" };
|
|
237
|
-
output.content.push(block);
|
|
238
|
-
const slot = {
|
|
239
|
-
|
|
240
|
-
|
|
239
|
+
state.output.content.push(block);
|
|
240
|
+
const slot = {
|
|
241
|
+
type: "text",
|
|
242
|
+
block,
|
|
243
|
+
contentIndex: state.output.content.length - 1,
|
|
244
|
+
};
|
|
245
|
+
state.outputSlots.set(outputIndex, slot);
|
|
246
|
+
state.stream.push({ type: "text_start", contentIndex: slot.contentIndex, partial: state.output });
|
|
241
247
|
return slot;
|
|
242
248
|
}
|
|
243
|
-
|
|
249
|
+
case "function_call": {
|
|
244
250
|
const block = {
|
|
245
251
|
type: "toolCall",
|
|
246
252
|
id: `${item.call_id}|${item.id}`,
|
|
@@ -248,203 +254,227 @@ export async function processResponsesStream(openaiStream, output, stream, model
|
|
|
248
254
|
arguments: {},
|
|
249
255
|
partialJson: item.arguments || "",
|
|
250
256
|
};
|
|
251
|
-
output.content.push(block);
|
|
257
|
+
state.output.content.push(block);
|
|
252
258
|
const slot = {
|
|
253
259
|
type: "toolCall",
|
|
254
260
|
block,
|
|
255
|
-
contentIndex: output.content.length - 1,
|
|
261
|
+
contentIndex: state.output.content.length - 1,
|
|
256
262
|
};
|
|
257
|
-
outputSlots.set(outputIndex, slot);
|
|
258
|
-
stream.push({ type: "toolcall_start", contentIndex: slot.contentIndex, partial: output });
|
|
263
|
+
state.outputSlots.set(outputIndex, slot);
|
|
264
|
+
state.stream.push({ type: "toolcall_start", contentIndex: slot.contentIndex, partial: state.output });
|
|
259
265
|
return slot;
|
|
260
266
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
267
|
+
default:
|
|
268
|
+
return undefined;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
function getOrCreateResponsesOutputSlot(state, outputIndex, item) {
|
|
272
|
+
return state.outputSlots.get(outputIndex) ?? createResponsesOutputSlot(state, outputIndex, item);
|
|
273
|
+
}
|
|
274
|
+
function backfillResponsesReasoningSignatures(state, responseOutput) {
|
|
275
|
+
for (const item of responseOutput) {
|
|
276
|
+
if (item.type !== "reasoning" || !item.encrypted_content)
|
|
277
|
+
continue;
|
|
278
|
+
const block = state.reasoningBlocksById.get(item.id);
|
|
279
|
+
if (!block?.thinkingSignature)
|
|
280
|
+
continue;
|
|
281
|
+
const storedItem = JSON.parse(block.thinkingSignature);
|
|
282
|
+
if (storedItem.encrypted_content)
|
|
283
|
+
continue;
|
|
284
|
+
block.thinkingSignature = JSON.stringify({ ...storedItem, encrypted_content: item.encrypted_content });
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
function finalizeResponsesResponse(state, response) {
|
|
288
|
+
state.sawTerminalResponseEvent = true;
|
|
289
|
+
backfillResponsesReasoningSignatures(state, response.output ?? []);
|
|
290
|
+
if (response.id)
|
|
291
|
+
state.output.responseId = response.id;
|
|
292
|
+
if (response.usage) {
|
|
293
|
+
const inputDetails = response.usage.input_tokens_details;
|
|
294
|
+
const cachedTokens = inputDetails?.cached_tokens || 0;
|
|
295
|
+
const cacheWriteTokens = inputDetails?.cache_write_tokens || 0;
|
|
296
|
+
state.output.usage = {
|
|
297
|
+
input: Math.max(0, (response.usage.input_tokens || 0) - cachedTokens - cacheWriteTokens),
|
|
298
|
+
output: response.usage.output_tokens || 0,
|
|
299
|
+
cacheRead: cachedTokens,
|
|
300
|
+
cacheWrite: cacheWriteTokens,
|
|
301
|
+
reasoning: response.usage.output_tokens_details?.reasoning_tokens || 0,
|
|
302
|
+
totalTokens: response.usage.total_tokens || 0,
|
|
303
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
calculateCost(state.model, state.output.usage);
|
|
307
|
+
if (state.options?.applyServiceTierPricing) {
|
|
308
|
+
const serviceTier = state.options.resolveServiceTier
|
|
309
|
+
? state.options.resolveServiceTier(response.service_tier, state.options.serviceTier)
|
|
310
|
+
: (response.service_tier ?? state.options.serviceTier);
|
|
311
|
+
state.options.applyServiceTierPricing(state.output.usage, serviceTier);
|
|
312
|
+
}
|
|
313
|
+
state.output.rawStopReason = response.status;
|
|
314
|
+
state.output.stopReason = mapStopReason(response.status);
|
|
315
|
+
if (state.output.content.some((block) => block.type === "toolCall") && state.output.stopReason === "stop") {
|
|
316
|
+
state.output.stopReason = "toolUse";
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
function appendResponsesThinkingDelta(state, outputIndex, delta) {
|
|
320
|
+
const slot = getResponsesThinkingSlot(state, outputIndex);
|
|
321
|
+
if (!slot)
|
|
322
|
+
return;
|
|
323
|
+
slot.block.thinking += delta;
|
|
324
|
+
state.stream.push({
|
|
325
|
+
type: "thinking_delta",
|
|
326
|
+
contentIndex: slot.contentIndex,
|
|
327
|
+
delta,
|
|
328
|
+
partial: state.output,
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
function appendResponsesTextDelta(state, outputIndex, delta) {
|
|
332
|
+
const slot = getResponsesTextSlot(state, outputIndex);
|
|
333
|
+
if (!slot)
|
|
334
|
+
return;
|
|
335
|
+
slot.block.text += delta;
|
|
336
|
+
state.stream.push({ type: "text_delta", contentIndex: slot.contentIndex, delta, partial: state.output });
|
|
337
|
+
}
|
|
338
|
+
function appendResponsesToolCallDelta(state, outputIndex, delta) {
|
|
339
|
+
const slot = getResponsesToolCallSlot(state, outputIndex);
|
|
340
|
+
if (!slot)
|
|
341
|
+
return;
|
|
342
|
+
slot.block.partialJson += delta;
|
|
343
|
+
slot.block.arguments = parseStreamingJson(slot.block.partialJson);
|
|
344
|
+
state.stream.push({ type: "toolcall_delta", contentIndex: slot.contentIndex, delta, partial: state.output });
|
|
345
|
+
}
|
|
346
|
+
function completeResponsesToolCallArguments(state, outputIndex, argumentsJson) {
|
|
347
|
+
const slot = getResponsesToolCallSlot(state, outputIndex);
|
|
348
|
+
if (!slot)
|
|
349
|
+
return;
|
|
350
|
+
const previousPartialJson = slot.block.partialJson;
|
|
351
|
+
slot.block.partialJson = argumentsJson;
|
|
352
|
+
slot.block.arguments = parseStreamingJson(slot.block.partialJson);
|
|
353
|
+
if (!argumentsJson.startsWith(previousPartialJson))
|
|
354
|
+
return;
|
|
355
|
+
const delta = argumentsJson.slice(previousPartialJson.length);
|
|
356
|
+
if (delta.length > 0) {
|
|
357
|
+
state.stream.push({ type: "toolcall_delta", contentIndex: slot.contentIndex, delta, partial: state.output });
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
function finalizeResponsesOutputItem(state, outputIndex, item) {
|
|
361
|
+
applyResponsesMessagePhaseStopReason(state.output, item);
|
|
362
|
+
const slot = getOrCreateResponsesOutputSlot(state, outputIndex, item);
|
|
363
|
+
switch (item.type) {
|
|
364
|
+
case "reasoning":
|
|
365
|
+
if (slot?.type !== "thinking")
|
|
366
|
+
return;
|
|
367
|
+
slot.block.thinking =
|
|
368
|
+
item.summary?.map((summary) => summary.text).join("\n\n") ||
|
|
369
|
+
item.content?.map((content) => content.text).join("\n\n") ||
|
|
370
|
+
slot.block.thinking;
|
|
371
|
+
slot.block.thinkingSignature = JSON.stringify(item);
|
|
372
|
+
state.reasoningBlocksById.set(item.id, slot.block);
|
|
373
|
+
state.stream.push({
|
|
374
|
+
type: "thinking_end",
|
|
316
375
|
contentIndex: slot.contentIndex,
|
|
317
|
-
|
|
318
|
-
partial: output,
|
|
376
|
+
content: slot.block.thinking,
|
|
377
|
+
partial: state.output,
|
|
319
378
|
});
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
if (
|
|
324
|
-
|
|
325
|
-
slot.block.text
|
|
326
|
-
|
|
327
|
-
|
|
379
|
+
state.outputSlots.delete(outputIndex);
|
|
380
|
+
return;
|
|
381
|
+
case "message":
|
|
382
|
+
if (slot?.type !== "text")
|
|
383
|
+
return;
|
|
384
|
+
slot.block.text =
|
|
385
|
+
item.content
|
|
386
|
+
?.map((content) => (content.type === "output_text" ? content.text : content.refusal))
|
|
387
|
+
.join("") || "";
|
|
388
|
+
slot.block.textSignature = encodeTextSignatureV1(item.id, item.phase ?? undefined);
|
|
389
|
+
state.stream.push({
|
|
390
|
+
type: "text_end",
|
|
328
391
|
contentIndex: slot.contentIndex,
|
|
329
|
-
|
|
330
|
-
partial: output,
|
|
392
|
+
content: slot.block.text,
|
|
393
|
+
partial: state.output,
|
|
331
394
|
});
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
if (
|
|
336
|
-
|
|
337
|
-
slot.block.partialJson
|
|
338
|
-
|
|
339
|
-
stream.push({
|
|
340
|
-
type: "
|
|
395
|
+
state.outputSlots.delete(outputIndex);
|
|
396
|
+
return;
|
|
397
|
+
case "function_call":
|
|
398
|
+
if (slot?.type !== "toolCall")
|
|
399
|
+
return;
|
|
400
|
+
slot.block.arguments = parseStreamingJson(item.arguments || slot.block.partialJson || "{}");
|
|
401
|
+
delete slot.block.partialJson;
|
|
402
|
+
state.stream.push({
|
|
403
|
+
type: "toolcall_end",
|
|
341
404
|
contentIndex: slot.contentIndex,
|
|
342
|
-
|
|
343
|
-
partial: output,
|
|
405
|
+
toolCall: slot.block,
|
|
406
|
+
partial: state.output,
|
|
344
407
|
});
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
slot.block.arguments = parseStreamingJson(item.arguments || slot.block.partialJson || "{}");
|
|
397
|
-
delete slot.block.partialJson;
|
|
398
|
-
stream.push({
|
|
399
|
-
type: "toolcall_end",
|
|
400
|
-
contentIndex: slot.contentIndex,
|
|
401
|
-
toolCall: slot.block,
|
|
402
|
-
partial: output,
|
|
403
|
-
});
|
|
404
|
-
outputSlots.delete(event.output_index);
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
else if (event.type === "response.completed") {
|
|
408
|
-
const response = event.response;
|
|
409
|
-
if (response?.id) {
|
|
410
|
-
output.responseId = response.id;
|
|
411
|
-
}
|
|
412
|
-
if (response?.usage) {
|
|
413
|
-
const cachedTokens = response.usage.input_tokens_details?.cached_tokens || 0;
|
|
414
|
-
output.usage = {
|
|
415
|
-
input: (response.usage.input_tokens || 0) - cachedTokens,
|
|
416
|
-
output: response.usage.output_tokens || 0,
|
|
417
|
-
cacheRead: cachedTokens,
|
|
418
|
-
cacheWrite: 0,
|
|
419
|
-
totalTokens: response.usage.total_tokens || 0,
|
|
420
|
-
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
421
|
-
};
|
|
422
|
-
}
|
|
423
|
-
calculateCost(model, output.usage);
|
|
424
|
-
if (options?.applyServiceTierPricing) {
|
|
425
|
-
const serviceTier = options.resolveServiceTier
|
|
426
|
-
? options.resolveServiceTier(response?.service_tier, options.serviceTier)
|
|
427
|
-
: (response?.service_tier ?? options.serviceTier);
|
|
428
|
-
options.applyServiceTierPricing(output.usage, serviceTier);
|
|
429
|
-
}
|
|
430
|
-
output.stopReason = mapStopReason(response?.status);
|
|
431
|
-
if (output.content.some((block) => block.type === "toolCall") && output.stopReason === "stop") {
|
|
432
|
-
output.stopReason = "toolUse";
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
else if (event.type === "error") {
|
|
408
|
+
state.outputSlots.delete(outputIndex);
|
|
409
|
+
return;
|
|
410
|
+
default:
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
function failResponsesStream(state, response) {
|
|
415
|
+
state.sawTerminalResponseEvent = true;
|
|
416
|
+
state.output.rawStopReason = response?.status;
|
|
417
|
+
const error = response?.error;
|
|
418
|
+
const details = response?.incomplete_details;
|
|
419
|
+
const message = error
|
|
420
|
+
? `${error.code || "unknown"}: ${error.message || "no message"}`
|
|
421
|
+
: details?.reason
|
|
422
|
+
? `incomplete: ${details.reason}`
|
|
423
|
+
: "Unknown error (no error details in response)";
|
|
424
|
+
throw new Error(message);
|
|
425
|
+
}
|
|
426
|
+
function processResponsesStreamEvent(state, event) {
|
|
427
|
+
switch (event.type) {
|
|
428
|
+
case "response.created":
|
|
429
|
+
state.output.responseId = event.response.id;
|
|
430
|
+
break;
|
|
431
|
+
case "response.output_item.added":
|
|
432
|
+
createResponsesOutputSlot(state, event.output_index, event.item);
|
|
433
|
+
break;
|
|
434
|
+
case "response.reasoning_summary_text.delta":
|
|
435
|
+
case "response.reasoning_text.delta":
|
|
436
|
+
appendResponsesThinkingDelta(state, event.output_index, event.delta);
|
|
437
|
+
break;
|
|
438
|
+
case "response.reasoning_summary_part.done":
|
|
439
|
+
appendResponsesThinkingDelta(state, event.output_index, "\n\n");
|
|
440
|
+
break;
|
|
441
|
+
case "response.output_text.delta":
|
|
442
|
+
case "response.refusal.delta":
|
|
443
|
+
appendResponsesTextDelta(state, event.output_index, event.delta);
|
|
444
|
+
break;
|
|
445
|
+
case "response.function_call_arguments.delta":
|
|
446
|
+
appendResponsesToolCallDelta(state, event.output_index, event.delta);
|
|
447
|
+
break;
|
|
448
|
+
case "response.function_call_arguments.done":
|
|
449
|
+
completeResponsesToolCallArguments(state, event.output_index, event.arguments);
|
|
450
|
+
break;
|
|
451
|
+
case "response.output_item.done":
|
|
452
|
+
finalizeResponsesOutputItem(state, event.output_index, event.item);
|
|
453
|
+
break;
|
|
454
|
+
case "response.completed":
|
|
455
|
+
case "response.incomplete":
|
|
456
|
+
finalizeResponsesResponse(state, event.response);
|
|
457
|
+
break;
|
|
458
|
+
case "error":
|
|
436
459
|
throw new Error(`Error Code ${event.code}: ${event.message}` || "Unknown error");
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
460
|
+
case "response.failed":
|
|
461
|
+
failResponsesStream(state, event.response);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
export async function processResponsesStream(openaiStream, output, stream, model, options) {
|
|
465
|
+
const state = {
|
|
466
|
+
output,
|
|
467
|
+
stream,
|
|
468
|
+
model,
|
|
469
|
+
options,
|
|
470
|
+
sawTerminalResponseEvent: false,
|
|
471
|
+
outputSlots: new Map(),
|
|
472
|
+
reasoningBlocksById: new Map(),
|
|
473
|
+
};
|
|
474
|
+
for await (const event of openaiStream)
|
|
475
|
+
processResponsesStreamEvent(state, event);
|
|
476
|
+
if (!state.sawTerminalResponseEvent) {
|
|
477
|
+
throw new Error("OpenAI Responses stream ended before a terminal response event");
|
|
448
478
|
}
|
|
449
479
|
}
|
|
450
480
|
function mapStopReason(status) {
|