@offerpilot/axiomruntime 0.0.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 +185 -0
- package/dist/cli/commands/add.js +29 -0
- package/dist/cli/commands/context.js +29 -0
- package/dist/cli/commands/doctor.js +62 -0
- package/dist/cli/commands/edit.js +85 -0
- package/dist/cli/commands/help.js +16 -0
- package/dist/cli/commands/list.js +25 -0
- package/dist/cli/commands/log.js +63 -0
- package/dist/cli/commands/memory.js +241 -0
- package/dist/cli/commands/report.js +35 -0
- package/dist/cli/commands/session.js +74 -0
- package/dist/cli/commands/setup.js +86 -0
- package/dist/cli/commands/status.js +50 -0
- package/dist/cli/commands/telegram.js +701 -0
- package/dist/cli/commands/use.js +108 -0
- package/dist/cli/commands/version.js +12 -0
- package/dist/cli/index.js +276 -0
- package/dist/cli/output/table.js +22 -0
- package/dist/cli/prompts/prompt.js +37 -0
- package/dist/cli/registry.js +16 -0
- package/dist/core/config/cache-store.js +193 -0
- package/dist/core/config/json-store.js +114 -0
- package/dist/core/config/paths.js +85 -0
- package/dist/core/config/providers-store.js +89 -0
- package/dist/core/config/schema.js +60 -0
- package/dist/core/config/session-store.js +30 -0
- package/dist/core/config/usage-store.js +18 -0
- package/dist/core/context/context-service.js +186 -0
- package/dist/core/integrations/integration-state.js +105 -0
- package/dist/core/logs/log-service.js +56 -0
- package/dist/core/memory/embedding-check.js +121 -0
- package/dist/core/memory/memory-config.js +122 -0
- package/dist/core/models/model-discovery.js +430 -0
- package/dist/core/models/model-filter.js +13 -0
- package/dist/core/providers/provider-service.js +212 -0
- package/dist/core/reports/report-service.js +166 -0
- package/dist/core/runner/command-resolver.js +60 -0
- package/dist/core/runner/engine-registry.js +93 -0
- package/dist/core/runner/fallback.js +114 -0
- package/dist/core/runner/openai-usage-http.js +82 -0
- package/dist/core/runner/openai-usage-proxy.js +1 -0
- package/dist/core/runner/openai-usage-recording.js +172 -0
- package/dist/core/runner/openai-usage-responses.js +469 -0
- package/dist/core/runner/openai-usage-server.js +319 -0
- package/dist/core/runner/openai-usage-types.js +1 -0
- package/dist/core/runner/tool-runner.js +138 -0
- package/dist/core/sessions/session-service.js +47 -0
- package/dist/core/status/doctor-service.js +391 -0
- package/dist/core/status/status-service.js +60 -0
- package/dist/core/types.js +1 -0
- package/dist/core/usage/pricing.js +113 -0
- package/dist/core/usage/usage-service.js +30 -0
- package/dist/core/utils/is-record.js +3 -0
- package/dist/server/index.js +28 -0
- package/dist/server/runtime-server.js +430 -0
- package/dist/telegram/bot-registry.js +80 -0
- package/dist/telegram/bot.js +128 -0
- package/dist/telegram/config.js +235 -0
- package/dist/telegram/engine/claude-engine.js +240 -0
- package/dist/telegram/engine/codex-engine.js +437 -0
- package/dist/telegram/engine/engine-utils.js +67 -0
- package/dist/telegram/engine/process-utils.js +132 -0
- package/dist/telegram/engine/registry.js +31 -0
- package/dist/telegram/engine/types.js +1 -0
- package/dist/telegram/handler-registry.js +28 -0
- package/dist/telegram/handlers/callback.js +311 -0
- package/dist/telegram/handlers/command.js +272 -0
- package/dist/telegram/handlers/document.js +108 -0
- package/dist/telegram/handlers/memory.js +305 -0
- package/dist/telegram/handlers/message.js +701 -0
- package/dist/telegram/handlers/provider.js +332 -0
- package/dist/telegram/handlers/setup.js +527 -0
- package/dist/telegram/handlers/usage.js +124 -0
- package/dist/telegram/index.js +93 -0
- package/dist/telegram/interaction/approval.js +108 -0
- package/dist/telegram/interaction/command-menu.js +253 -0
- package/dist/telegram/interaction/formatter.js +487 -0
- package/dist/telegram/interaction/keyboards.js +145 -0
- package/dist/telegram/interaction/progress-reporter.js +160 -0
- package/dist/telegram/interaction/prompt-middleware.js +168 -0
- package/dist/telegram/interaction/result-store.js +41 -0
- package/dist/telegram/interaction/token-budget.js +21 -0
- package/dist/telegram/interaction/tool-name.js +41 -0
- package/dist/telegram/lifecycle-registry.js +47 -0
- package/dist/telegram/log.js +46 -0
- package/dist/telegram/memory/memory-inject.js +52 -0
- package/dist/telegram/memory/memory-service.js +413 -0
- package/dist/telegram/memory/memory-store.js +216 -0
- package/dist/telegram/memory/types.js +1 -0
- package/dist/telegram/network-retry.js +22 -0
- package/dist/telegram/network.js +53 -0
- package/dist/telegram/session/manager.js +229 -0
- package/dist/telegram/session/store.js +363 -0
- package/dist/telegram/session/types.js +1 -0
- package/dist/telegram/supervisor.js +57 -0
- package/dist/telegram/templates/messages.js +1 -0
- package/docs/README.md +98 -0
- package/docs/USAGE.html +853 -0
- package/package.json +57 -0
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
import { fetch } from "undici";
|
|
2
|
+
export function isResponsesStreamRequest(incomingUrl, requestBody) {
|
|
3
|
+
const local = new URL(incomingUrl, "http://127.0.0.1");
|
|
4
|
+
if (local.pathname !== "/responses" && local.pathname !== "/v1/responses")
|
|
5
|
+
return false;
|
|
6
|
+
const body = parseJsonObject(requestBody);
|
|
7
|
+
return body?.stream === true;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Decide whether a failed native `/responses` attempt should be retried by
|
|
11
|
+
* translating the request through chat completions. Only "the upstream does not
|
|
12
|
+
* speak the Responses protocol" cases fall back; transient/no-channel failures
|
|
13
|
+
* are passed through so provider rotation handles them instead of wasting a
|
|
14
|
+
* chat attempt that would fail the same way.
|
|
15
|
+
*/
|
|
16
|
+
export function shouldFallbackResponsesToChat(outcome) {
|
|
17
|
+
const effectiveStatus = resolveEffectiveStatus(outcome);
|
|
18
|
+
if (effectiveStatus >= 200 && effectiveStatus < 300 && isEventStream(outcome.contentType)) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
if (isTransientStatus(effectiveStatus))
|
|
22
|
+
return false;
|
|
23
|
+
if (mentionsResponsesUnsupported(outcome.bodyText))
|
|
24
|
+
return true;
|
|
25
|
+
// Classic "this route/endpoint does not exist" from chat-only gateways.
|
|
26
|
+
return effectiveStatus === 400 || effectiveStatus === 404 || effectiveStatus === 405 || effectiveStatus === 501;
|
|
27
|
+
}
|
|
28
|
+
function resolveEffectiveStatus(outcome) {
|
|
29
|
+
// Many domestic new-api gateways wrap the real error in an HTTP 200 body,
|
|
30
|
+
// e.g. `{"error":{"message":"... status_code=400 ..."}}`. Trust the wrapped
|
|
31
|
+
// status when the transport status looks successful but the body is an error.
|
|
32
|
+
if (outcome.status >= 200 && outcome.status < 300 && !isEventStream(outcome.contentType)) {
|
|
33
|
+
const wrapped = extractWrappedStatus(outcome.bodyText);
|
|
34
|
+
if (wrapped !== undefined)
|
|
35
|
+
return wrapped;
|
|
36
|
+
}
|
|
37
|
+
return outcome.status;
|
|
38
|
+
}
|
|
39
|
+
function extractWrappedStatus(bodyText) {
|
|
40
|
+
const compact = bodyText.replace(/\s+/g, " ");
|
|
41
|
+
const match = compact.match(/status[_-]?code["']?\s*[:=]\s*["']?(\d{3})/i);
|
|
42
|
+
return match ? Number(match[1]) : undefined;
|
|
43
|
+
}
|
|
44
|
+
function mentionsResponsesUnsupported(bodyText) {
|
|
45
|
+
const message = bodyText.replace(/\s+/g, " ");
|
|
46
|
+
const rejection = "(?:不支持|不支援|does not support|doesn't support|not supported|unsupported|invalid|no such)";
|
|
47
|
+
const protocol = "(?:responses|/v1/responses|response api|responses api)";
|
|
48
|
+
return new RegExp(`${rejection}.{0,160}${protocol}`, "i").test(message)
|
|
49
|
+
|| new RegExp(`${protocol}.{0,160}${rejection}`, "i").test(message);
|
|
50
|
+
}
|
|
51
|
+
function isEventStream(contentType) {
|
|
52
|
+
return contentType.toLowerCase().includes("text/event-stream");
|
|
53
|
+
}
|
|
54
|
+
function isTransientStatus(status) {
|
|
55
|
+
return status === 408 || status === 409 || status === 425 || status === 429
|
|
56
|
+
|| status === 500 || status === 502 || status === 503 || status === 504
|
|
57
|
+
|| (status >= 520 && status <= 527) || status === 530;
|
|
58
|
+
}
|
|
59
|
+
export async function buildChatCompletionsResponsesSse(input) {
|
|
60
|
+
const responsesRequest = parseJsonObject(input.requestBody);
|
|
61
|
+
if (!responsesRequest) {
|
|
62
|
+
return {
|
|
63
|
+
status: 400,
|
|
64
|
+
statusText: "Bad Request",
|
|
65
|
+
contentType: "application/json; charset=utf-8",
|
|
66
|
+
body: Buffer.from(JSON.stringify({ error: { message: "Invalid Responses request body." } }))
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
const chatResponse = await fetch(buildChatCompletionsUrl(input.upstreamBaseUrl), {
|
|
70
|
+
method: "POST",
|
|
71
|
+
headers: {
|
|
72
|
+
authorization: `Bearer ${input.apiKey}`,
|
|
73
|
+
"content-type": "application/json",
|
|
74
|
+
accept: "application/json"
|
|
75
|
+
},
|
|
76
|
+
body: JSON.stringify(buildChatCompletionsRequest(responsesRequest)),
|
|
77
|
+
dispatcher: input.dispatcher
|
|
78
|
+
});
|
|
79
|
+
const text = await chatResponse.text();
|
|
80
|
+
if (!chatResponse.ok) {
|
|
81
|
+
return {
|
|
82
|
+
status: chatResponse.status,
|
|
83
|
+
statusText: chatResponse.statusText,
|
|
84
|
+
contentType: chatResponse.headers.get("content-type") ?? "application/json; charset=utf-8",
|
|
85
|
+
body: Buffer.from(text)
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
const chatJson = parseJsonTextObject(text);
|
|
89
|
+
if (!chatJson) {
|
|
90
|
+
return {
|
|
91
|
+
status: 502,
|
|
92
|
+
statusText: "Bad Gateway",
|
|
93
|
+
contentType: "application/json; charset=utf-8",
|
|
94
|
+
body: Buffer.from(JSON.stringify({ error: { message: "Chat completions fallback returned invalid JSON." } }))
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
status: 200,
|
|
99
|
+
statusText: "OK",
|
|
100
|
+
contentType: "text/event-stream; charset=utf-8",
|
|
101
|
+
body: Buffer.from(buildResponsesSseFromChatCompletion(chatJson, responsesRequest))
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function buildChatCompletionsUrl(upstreamBaseUrl) {
|
|
105
|
+
return `${upstreamBaseUrl.replace(/\/+$/, "")}/chat/completions`;
|
|
106
|
+
}
|
|
107
|
+
function buildChatCompletionsRequest(responsesRequest) {
|
|
108
|
+
const request = {
|
|
109
|
+
model: typeof responsesRequest.model === "string" ? responsesRequest.model : undefined,
|
|
110
|
+
messages: buildChatMessages(responsesRequest),
|
|
111
|
+
stream: false
|
|
112
|
+
};
|
|
113
|
+
const tools = buildChatTools(responsesRequest.tools);
|
|
114
|
+
if (tools.length)
|
|
115
|
+
request.tools = tools;
|
|
116
|
+
const toolChoice = buildChatToolChoice(responsesRequest.tool_choice);
|
|
117
|
+
if (toolChoice !== undefined)
|
|
118
|
+
request.tool_choice = toolChoice;
|
|
119
|
+
for (const [sourceKey, targetKey] of [
|
|
120
|
+
["parallel_tool_calls", "parallel_tool_calls"],
|
|
121
|
+
["max_output_tokens", "max_tokens"],
|
|
122
|
+
["temperature", "temperature"],
|
|
123
|
+
["top_p", "top_p"]
|
|
124
|
+
]) {
|
|
125
|
+
const value = responsesRequest[sourceKey];
|
|
126
|
+
if (value !== undefined && value !== null)
|
|
127
|
+
request[targetKey] = value;
|
|
128
|
+
}
|
|
129
|
+
return request;
|
|
130
|
+
}
|
|
131
|
+
function buildChatMessages(responsesRequest) {
|
|
132
|
+
const messages = [];
|
|
133
|
+
if (typeof responsesRequest.instructions === "string" && responsesRequest.instructions.trim()) {
|
|
134
|
+
messages.push({ role: "system", content: responsesRequest.instructions.trim() });
|
|
135
|
+
}
|
|
136
|
+
const input = responsesRequest.input;
|
|
137
|
+
if (typeof input === "string") {
|
|
138
|
+
messages.push({ role: "user", content: input });
|
|
139
|
+
}
|
|
140
|
+
else if (Array.isArray(input)) {
|
|
141
|
+
for (const item of input) {
|
|
142
|
+
const message = responsesItemToChatMessage(item);
|
|
143
|
+
if (message)
|
|
144
|
+
messages.push(message);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (!messages.length) {
|
|
148
|
+
messages.push({ role: "user", content: "" });
|
|
149
|
+
}
|
|
150
|
+
return messages;
|
|
151
|
+
}
|
|
152
|
+
function responsesItemToChatMessage(item) {
|
|
153
|
+
if (!isPlainObject(item))
|
|
154
|
+
return null;
|
|
155
|
+
const type = typeof item.type === "string" ? item.type : "";
|
|
156
|
+
if (type === "function_call") {
|
|
157
|
+
const name = typeof item.name === "string" ? item.name : "";
|
|
158
|
+
if (!name)
|
|
159
|
+
return null;
|
|
160
|
+
return {
|
|
161
|
+
role: "assistant",
|
|
162
|
+
content: null,
|
|
163
|
+
tool_calls: [{
|
|
164
|
+
id: typeof item.call_id === "string" ? item.call_id : typeof item.id === "string" ? item.id : `call_${Date.now()}`,
|
|
165
|
+
type: "function",
|
|
166
|
+
function: {
|
|
167
|
+
name,
|
|
168
|
+
arguments: stringifyToolArguments(item.arguments)
|
|
169
|
+
}
|
|
170
|
+
}]
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
if (type === "function_call_output") {
|
|
174
|
+
return {
|
|
175
|
+
role: "tool",
|
|
176
|
+
tool_call_id: typeof item.call_id === "string" ? item.call_id : "",
|
|
177
|
+
content: contentToText(item.output)
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
const role = normalizeChatRole(item.role);
|
|
181
|
+
if (!role)
|
|
182
|
+
return null;
|
|
183
|
+
const content = contentToText(item.content);
|
|
184
|
+
if (!content && role !== "system")
|
|
185
|
+
return null;
|
|
186
|
+
return { role, content };
|
|
187
|
+
}
|
|
188
|
+
function normalizeChatRole(value) {
|
|
189
|
+
if (value === "developer" || value === "system")
|
|
190
|
+
return "system";
|
|
191
|
+
if (value === "user")
|
|
192
|
+
return "user";
|
|
193
|
+
if (value === "assistant")
|
|
194
|
+
return "assistant";
|
|
195
|
+
if (value === "tool")
|
|
196
|
+
return "tool";
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
function contentToText(value) {
|
|
200
|
+
if (typeof value === "string")
|
|
201
|
+
return value;
|
|
202
|
+
if (Array.isArray(value)) {
|
|
203
|
+
return value
|
|
204
|
+
.map((item) => {
|
|
205
|
+
if (typeof item === "string")
|
|
206
|
+
return item;
|
|
207
|
+
if (isPlainObject(item)) {
|
|
208
|
+
for (const key of ["text", "output_text", "input_text", "content", "output"]) {
|
|
209
|
+
const nested = item[key];
|
|
210
|
+
if (typeof nested === "string")
|
|
211
|
+
return nested;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return "";
|
|
215
|
+
})
|
|
216
|
+
.filter((item) => item.trim())
|
|
217
|
+
.join("\n\n");
|
|
218
|
+
}
|
|
219
|
+
if (value === undefined || value === null)
|
|
220
|
+
return "";
|
|
221
|
+
return typeof value === "object" ? JSON.stringify(value) : String(value);
|
|
222
|
+
}
|
|
223
|
+
function stringifyToolArguments(value) {
|
|
224
|
+
if (typeof value === "string")
|
|
225
|
+
return value;
|
|
226
|
+
if (value === undefined || value === null)
|
|
227
|
+
return "{}";
|
|
228
|
+
return JSON.stringify(value);
|
|
229
|
+
}
|
|
230
|
+
function buildChatTools(value) {
|
|
231
|
+
if (!Array.isArray(value))
|
|
232
|
+
return [];
|
|
233
|
+
const tools = [];
|
|
234
|
+
for (const tool of value) {
|
|
235
|
+
if (!isPlainObject(tool) || tool.type !== "function")
|
|
236
|
+
continue;
|
|
237
|
+
const functionSpec = isPlainObject(tool.function) ? tool.function : tool;
|
|
238
|
+
const name = typeof functionSpec.name === "string" ? functionSpec.name : "";
|
|
239
|
+
if (!name)
|
|
240
|
+
continue;
|
|
241
|
+
const next = {
|
|
242
|
+
type: "function",
|
|
243
|
+
function: {
|
|
244
|
+
name,
|
|
245
|
+
description: typeof functionSpec.description === "string" ? functionSpec.description : "",
|
|
246
|
+
parameters: isPlainObject(functionSpec.parameters) ? functionSpec.parameters : { type: "object", properties: {} }
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
const strict = functionSpec.strict ?? tool.strict;
|
|
250
|
+
if (typeof strict === "boolean" && isPlainObject(next.function)) {
|
|
251
|
+
next.function.strict = strict;
|
|
252
|
+
}
|
|
253
|
+
tools.push(next);
|
|
254
|
+
}
|
|
255
|
+
return tools;
|
|
256
|
+
}
|
|
257
|
+
function buildChatToolChoice(value) {
|
|
258
|
+
if (value === "auto" || value === "none" || value === "required")
|
|
259
|
+
return value;
|
|
260
|
+
if (!isPlainObject(value))
|
|
261
|
+
return undefined;
|
|
262
|
+
if (value.type === "function" && typeof value.name === "string") {
|
|
263
|
+
return { type: "function", function: { name: value.name } };
|
|
264
|
+
}
|
|
265
|
+
if (isPlainObject(value.function) && typeof value.function.name === "string") {
|
|
266
|
+
return { type: "function", function: { name: value.function.name } };
|
|
267
|
+
}
|
|
268
|
+
return undefined;
|
|
269
|
+
}
|
|
270
|
+
function buildResponsesSseFromChatCompletion(chatCompletion, responsesRequest) {
|
|
271
|
+
const model = typeof chatCompletion.model === "string"
|
|
272
|
+
? chatCompletion.model
|
|
273
|
+
: typeof responsesRequest.model === "string"
|
|
274
|
+
? responsesRequest.model
|
|
275
|
+
: "unknown";
|
|
276
|
+
const responseId = typeof chatCompletion.id === "string" ? chatCompletion.id.replace(/^chatcmpl-/, "resp_") : `resp_${Date.now()}`;
|
|
277
|
+
const outputItems = buildResponsesOutputItems(chatCompletion);
|
|
278
|
+
const startedResponse = buildResponsesResponse({
|
|
279
|
+
id: responseId,
|
|
280
|
+
model,
|
|
281
|
+
status: "in_progress",
|
|
282
|
+
output: [],
|
|
283
|
+
usage: null
|
|
284
|
+
});
|
|
285
|
+
const completedResponse = buildResponsesResponse({
|
|
286
|
+
id: responseId,
|
|
287
|
+
model,
|
|
288
|
+
status: "completed",
|
|
289
|
+
output: outputItems,
|
|
290
|
+
usage: convertChatUsage(chatCompletion.usage)
|
|
291
|
+
});
|
|
292
|
+
const chunks = [
|
|
293
|
+
writeSse("response.created", { type: "response.created", response: startedResponse }),
|
|
294
|
+
writeSse("response.in_progress", { type: "response.in_progress", response: startedResponse })
|
|
295
|
+
];
|
|
296
|
+
outputItems.forEach((item, index) => {
|
|
297
|
+
if (item.type === "message") {
|
|
298
|
+
chunks.push(...buildMessageSseChunks(item, index));
|
|
299
|
+
}
|
|
300
|
+
else if (item.type === "function_call") {
|
|
301
|
+
chunks.push(...buildFunctionCallSseChunks(item, index));
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
chunks.push(writeSse("response.completed", { type: "response.completed", response: completedResponse }));
|
|
305
|
+
chunks.push("data: [DONE]\n\n");
|
|
306
|
+
return chunks.join("");
|
|
307
|
+
}
|
|
308
|
+
function buildResponsesOutputItems(chatCompletion) {
|
|
309
|
+
const choices = Array.isArray(chatCompletion.choices) ? chatCompletion.choices : [];
|
|
310
|
+
const firstChoice = choices.find(isPlainObject);
|
|
311
|
+
const message = isPlainObject(firstChoice?.message) ? firstChoice.message : {};
|
|
312
|
+
const output = [];
|
|
313
|
+
const content = typeof message.content === "string" ? message.content : "";
|
|
314
|
+
if (content) {
|
|
315
|
+
output.push({
|
|
316
|
+
id: `msg_${Date.now()}`,
|
|
317
|
+
type: "message",
|
|
318
|
+
status: "completed",
|
|
319
|
+
role: "assistant",
|
|
320
|
+
content: [{ type: "output_text", text: content, annotations: [] }]
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
if (Array.isArray(message.tool_calls)) {
|
|
324
|
+
message.tool_calls.forEach((toolCall, index) => {
|
|
325
|
+
if (!isPlainObject(toolCall) || !isPlainObject(toolCall.function))
|
|
326
|
+
return;
|
|
327
|
+
const name = typeof toolCall.function.name === "string" ? toolCall.function.name : "";
|
|
328
|
+
if (!name)
|
|
329
|
+
return;
|
|
330
|
+
output.push({
|
|
331
|
+
id: `fc_${Date.now()}_${index}`,
|
|
332
|
+
type: "function_call",
|
|
333
|
+
status: "completed",
|
|
334
|
+
call_id: typeof toolCall.id === "string" ? toolCall.id : `call_${Date.now()}_${index}`,
|
|
335
|
+
name,
|
|
336
|
+
arguments: stringifyToolArguments(toolCall.function.arguments)
|
|
337
|
+
});
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
return output;
|
|
341
|
+
}
|
|
342
|
+
function buildResponsesResponse(input) {
|
|
343
|
+
return {
|
|
344
|
+
id: input.id,
|
|
345
|
+
object: "response",
|
|
346
|
+
created_at: Math.floor(Date.now() / 1000),
|
|
347
|
+
status: input.status,
|
|
348
|
+
error: null,
|
|
349
|
+
incomplete_details: null,
|
|
350
|
+
instructions: null,
|
|
351
|
+
max_output_tokens: null,
|
|
352
|
+
model: input.model,
|
|
353
|
+
output: input.output,
|
|
354
|
+
parallel_tool_calls: true,
|
|
355
|
+
previous_response_id: null,
|
|
356
|
+
reasoning: { effort: null, summary: null },
|
|
357
|
+
store: false,
|
|
358
|
+
temperature: 1,
|
|
359
|
+
text: { format: { type: "text" } },
|
|
360
|
+
tool_choice: "auto",
|
|
361
|
+
tools: [],
|
|
362
|
+
top_p: 1,
|
|
363
|
+
truncation: "disabled",
|
|
364
|
+
usage: input.usage
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
function buildMessageSseChunks(item, outputIndex) {
|
|
368
|
+
const content = Array.isArray(item.content) ? item.content : [];
|
|
369
|
+
const firstPart = content.find(isPlainObject) ?? { type: "output_text", text: "", annotations: [] };
|
|
370
|
+
const text = typeof firstPart.text === "string" ? firstPart.text : "";
|
|
371
|
+
const itemId = typeof item.id === "string" ? item.id : `msg_${Date.now()}`;
|
|
372
|
+
const inProgressItem = { ...item, id: itemId, status: "in_progress", content: [] };
|
|
373
|
+
return [
|
|
374
|
+
writeSse("response.output_item.added", { type: "response.output_item.added", output_index: outputIndex, item: inProgressItem }),
|
|
375
|
+
writeSse("response.content_part.added", {
|
|
376
|
+
type: "response.content_part.added",
|
|
377
|
+
item_id: itemId,
|
|
378
|
+
output_index: outputIndex,
|
|
379
|
+
content_index: 0,
|
|
380
|
+
part: { type: "output_text", text: "", annotations: [] }
|
|
381
|
+
}),
|
|
382
|
+
writeSse("response.output_text.delta", {
|
|
383
|
+
type: "response.output_text.delta",
|
|
384
|
+
item_id: itemId,
|
|
385
|
+
output_index: outputIndex,
|
|
386
|
+
content_index: 0,
|
|
387
|
+
delta: text
|
|
388
|
+
}),
|
|
389
|
+
writeSse("response.output_text.done", {
|
|
390
|
+
type: "response.output_text.done",
|
|
391
|
+
item_id: itemId,
|
|
392
|
+
output_index: outputIndex,
|
|
393
|
+
content_index: 0,
|
|
394
|
+
text
|
|
395
|
+
}),
|
|
396
|
+
writeSse("response.content_part.done", {
|
|
397
|
+
type: "response.content_part.done",
|
|
398
|
+
item_id: itemId,
|
|
399
|
+
output_index: outputIndex,
|
|
400
|
+
content_index: 0,
|
|
401
|
+
part: firstPart
|
|
402
|
+
}),
|
|
403
|
+
writeSse("response.output_item.done", { type: "response.output_item.done", output_index: outputIndex, item })
|
|
404
|
+
];
|
|
405
|
+
}
|
|
406
|
+
function buildFunctionCallSseChunks(item, outputIndex) {
|
|
407
|
+
const itemId = typeof item.id === "string" ? item.id : `fc_${Date.now()}`;
|
|
408
|
+
const args = typeof item.arguments === "string" ? item.arguments : "{}";
|
|
409
|
+
return [
|
|
410
|
+
writeSse("response.output_item.added", {
|
|
411
|
+
type: "response.output_item.added",
|
|
412
|
+
output_index: outputIndex,
|
|
413
|
+
item: { ...item, id: itemId, status: "in_progress", arguments: "" }
|
|
414
|
+
}),
|
|
415
|
+
writeSse("response.function_call_arguments.delta", {
|
|
416
|
+
type: "response.function_call_arguments.delta",
|
|
417
|
+
item_id: itemId,
|
|
418
|
+
output_index: outputIndex,
|
|
419
|
+
delta: args
|
|
420
|
+
}),
|
|
421
|
+
writeSse("response.function_call_arguments.done", {
|
|
422
|
+
type: "response.function_call_arguments.done",
|
|
423
|
+
item_id: itemId,
|
|
424
|
+
output_index: outputIndex,
|
|
425
|
+
arguments: args
|
|
426
|
+
}),
|
|
427
|
+
writeSse("response.output_item.done", { type: "response.output_item.done", output_index: outputIndex, item })
|
|
428
|
+
];
|
|
429
|
+
}
|
|
430
|
+
function convertChatUsage(value) {
|
|
431
|
+
if (!isPlainObject(value))
|
|
432
|
+
return null;
|
|
433
|
+
const inputTokens = readNumber(value.prompt_tokens) ?? readNumber(value.input_tokens) ?? 0;
|
|
434
|
+
const outputTokens = readNumber(value.completion_tokens) ?? readNumber(value.output_tokens) ?? 0;
|
|
435
|
+
const totalTokens = readNumber(value.total_tokens) ?? inputTokens + outputTokens;
|
|
436
|
+
const promptDetails = isPlainObject(value.prompt_tokens_details) ? value.prompt_tokens_details : {};
|
|
437
|
+
const inputDetails = isPlainObject(value.input_tokens_details) ? value.input_tokens_details : {};
|
|
438
|
+
const cachedTokens = readNumber(promptDetails.cached_tokens) ?? readNumber(inputDetails.cached_tokens) ?? 0;
|
|
439
|
+
return {
|
|
440
|
+
input_tokens: inputTokens,
|
|
441
|
+
output_tokens: outputTokens,
|
|
442
|
+
total_tokens: totalTokens,
|
|
443
|
+
input_tokens_details: { cached_tokens: cachedTokens }
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
function readNumber(value) {
|
|
447
|
+
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
448
|
+
}
|
|
449
|
+
function writeSse(event, data) {
|
|
450
|
+
return `event: ${event}\ndata: ${JSON.stringify(data)}\n\n`;
|
|
451
|
+
}
|
|
452
|
+
function parseJsonObject(body) {
|
|
453
|
+
const text = body.toString("utf8").trim();
|
|
454
|
+
if (!text || !text.startsWith("{"))
|
|
455
|
+
return null;
|
|
456
|
+
return parseJsonTextObject(text);
|
|
457
|
+
}
|
|
458
|
+
function parseJsonTextObject(text) {
|
|
459
|
+
try {
|
|
460
|
+
const parsed = JSON.parse(text);
|
|
461
|
+
return isPlainObject(parsed) ? parsed : null;
|
|
462
|
+
}
|
|
463
|
+
catch {
|
|
464
|
+
return null;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
function isPlainObject(value) {
|
|
468
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
469
|
+
}
|