@yukino.js/yukino 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/LICENSE +21 -0
- package/README.md +320 -0
- package/dist/acp-IVY5HZR4.js +14 -0
- package/dist/agent-QANDY7FO.js +4 -0
- package/dist/anthropic-6YLT4JZR.js +4 -0
- package/dist/bwrap-MLSZLWHD.js +4 -0
- package/dist/chunk-2LHY3FND.js +67 -0
- package/dist/chunk-3KKA6537.js +4 -0
- package/dist/chunk-3QL446GD.js +4 -0
- package/dist/chunk-4GE22WHI.js +4 -0
- package/dist/chunk-5VOCXSSX.js +5 -0
- package/dist/chunk-77W2UPS2.js +4 -0
- package/dist/chunk-7MKA3SHV.js +4 -0
- package/dist/chunk-AGSYLE4E.js +16 -0
- package/dist/chunk-CU3IISCC.js +4 -0
- package/dist/chunk-GYBJR2RB.js +65 -0
- package/dist/chunk-HLE7243Q.js +4 -0
- package/dist/chunk-HMZQYFYG.js +4 -0
- package/dist/chunk-JFKC4VCA.js +4 -0
- package/dist/chunk-K4BYVVW7.js +5 -0
- package/dist/chunk-KCMBAHWU.js +4 -0
- package/dist/chunk-L2BWSPCF.js +10 -0
- package/dist/chunk-MDPSQ6L7.js +44 -0
- package/dist/chunk-OFC2GD5K.js +4 -0
- package/dist/chunk-RAS3ORX3.js +164 -0
- package/dist/chunk-RCXKUD3Y.js +4 -0
- package/dist/chunk-SAJCBGHL.js +4 -0
- package/dist/chunk-SOO5OXX2.js +237 -0
- package/dist/chunk-T4O4GQVL.js +607 -0
- package/dist/chunk-UPNNYP7M.js +60 -0
- package/dist/chunk-VCIRD267.js +4 -0
- package/dist/chunk-VU3JWAJR.js +4 -0
- package/dist/chunk-VWUGGYUP.js +6 -0
- package/dist/chunk-XO7ITAWR.js +4 -0
- package/dist/devtools-YWVUN4HX.js +7 -0
- package/dist/esm-57CJEGGK.js +4 -0
- package/dist/esm-6L5BJ5GT.js +4 -0
- package/dist/esm-DIYJ5HX6.js +4 -0
- package/dist/esm-HW2JMFYC.js +4 -0
- package/dist/esm-RPR2GBJ2.js +4 -0
- package/dist/esm-YKAO4ES6.js +4 -0
- package/dist/esm-ZTIYTMTS.js +229 -0
- package/dist/getMachineId-bsd-E27XJQLX.js +4 -0
- package/dist/getMachineId-darwin-35UPAIXP.js +5 -0
- package/dist/getMachineId-linux-MKXIYT6A.js +4 -0
- package/dist/getMachineId-unsupported-ZXVGGFX2.js +4 -0
- package/dist/getMachineId-win-RNVIYJG5.js +4 -0
- package/dist/lib/agent-6FZX4P2Y.js +16 -0
- package/dist/lib/anthropic-IP23YZ3P.js +21 -0
- package/dist/lib/bwrap-2OL7VPSO.js +7 -0
- package/dist/lib/chunk-2VF3XTGH.js +3382 -0
- package/dist/lib/chunk-5DVQ64YL.js +619 -0
- package/dist/lib/chunk-5TGXH7BX.js +624 -0
- package/dist/lib/chunk-6OMQD7MK.js +591 -0
- package/dist/lib/chunk-AZRC67RW.js +512 -0
- package/dist/lib/chunk-F2YJG72O.js +150 -0
- package/dist/lib/chunk-FFM2LB4C.js +132 -0
- package/dist/lib/chunk-FLKB4BK7.js +50 -0
- package/dist/lib/chunk-I34MAQRE.js +255 -0
- package/dist/lib/chunk-KA6POJKI.js +889 -0
- package/dist/lib/chunk-MLKGABMK.js +9 -0
- package/dist/lib/chunk-O7ELJUEK.js +262 -0
- package/dist/lib/chunk-QYBQT2DM.js +655 -0
- package/dist/lib/chunk-YIWNQ7OA.js +47 -0
- package/dist/lib/index.d.ts +8581 -0
- package/dist/lib/index.js +15905 -0
- package/dist/lib/openai-EMJ6U4FW.js +15 -0
- package/dist/lib/permissions-KZ5ACZAX.js +19 -0
- package/dist/lib/providers-2CGZPBFI.js +9 -0
- package/dist/lib/sandbox-runtime-CCQ3X3SM.js +9 -0
- package/dist/lib/seatbelt-WO7YWKLR.js +7 -0
- package/dist/lib/tool-filter-CKHZUUBQ.js +21 -0
- package/dist/main.js +332 -0
- package/dist/node-634LOHDX.js +15 -0
- package/dist/openai-YXYO5OW6.js +34 -0
- package/dist/permissions-VCDMNX3L.js +4 -0
- package/dist/providers-HNMBGGW6.js +4 -0
- package/dist/sandbox-runtime-ELKAYRIL.js +4 -0
- package/dist/seatbelt-JOCANLYS.js +4 -0
- package/dist/server-ZHQILOBP.js +4 -0
- package/dist/src-43CPCEK2.js +12 -0
- package/dist/src-JBRNH4FA.js +4 -0
- package/dist/src-R4BSSIJJ.js +4 -0
- package/dist/src-X6JWWHHT.js +4 -0
- package/dist/tool-filter-BQZDV3TF.js +4 -0
- package/dist/turndown.es-NQ3KDXM7.js +83 -0
- package/package.json +146 -0
|
@@ -0,0 +1,889 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AuthenticationError,
|
|
3
|
+
ContextTooLongError,
|
|
4
|
+
LLMError,
|
|
5
|
+
NetworkError,
|
|
6
|
+
RateLimitError,
|
|
7
|
+
clampThinkingLevel,
|
|
8
|
+
ensureToolPairing,
|
|
9
|
+
getMaxOutputTokens,
|
|
10
|
+
getSupportedThinkingLevels,
|
|
11
|
+
getThinkingLevel,
|
|
12
|
+
resolveAPIKey,
|
|
13
|
+
toReasoningEffort
|
|
14
|
+
} from "./chunk-QYBQT2DM.js";
|
|
15
|
+
import {
|
|
16
|
+
asRecord,
|
|
17
|
+
asString,
|
|
18
|
+
contentToText,
|
|
19
|
+
createChildLogger,
|
|
20
|
+
isRecord,
|
|
21
|
+
strArg
|
|
22
|
+
} from "./chunk-6OMQD7MK.js";
|
|
23
|
+
import {
|
|
24
|
+
__export
|
|
25
|
+
} from "./chunk-MLKGABMK.js";
|
|
26
|
+
|
|
27
|
+
// src/llm/openai.ts
|
|
28
|
+
var openai_exports = {};
|
|
29
|
+
__export(openai_exports, {
|
|
30
|
+
OpenAIClient: () => OpenAIClient,
|
|
31
|
+
OpenAICompatClient: () => OpenAICompatClient,
|
|
32
|
+
buildChatCompletionMessages: () => buildChatCompletionMessages,
|
|
33
|
+
buildOpenAIInput: () => buildOpenAIInput
|
|
34
|
+
});
|
|
35
|
+
import OpenAI from "openai";
|
|
36
|
+
var log = createChildLogger({ module: "llm" });
|
|
37
|
+
function computerActionArguments(action) {
|
|
38
|
+
switch (action.type) {
|
|
39
|
+
case "scroll":
|
|
40
|
+
return {
|
|
41
|
+
type: "scroll",
|
|
42
|
+
x: action.x,
|
|
43
|
+
y: action.y,
|
|
44
|
+
scrollX: action.scroll_x,
|
|
45
|
+
scrollY: action.scroll_y,
|
|
46
|
+
...action.keys?.length ? { keys: action.keys } : {}
|
|
47
|
+
};
|
|
48
|
+
case "click":
|
|
49
|
+
case "double_click":
|
|
50
|
+
case "drag":
|
|
51
|
+
case "move": {
|
|
52
|
+
const { keys, ...rest } = action;
|
|
53
|
+
return {
|
|
54
|
+
...rest,
|
|
55
|
+
...keys?.length ? { keys } : {}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
default:
|
|
59
|
+
return { ...action };
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function computerCallArguments(item) {
|
|
63
|
+
const actions = item.actions ?? (item.action ? [item.action] : []);
|
|
64
|
+
return {
|
|
65
|
+
actions: actions.map(computerActionArguments),
|
|
66
|
+
pendingSafetyChecks: item.pending_safety_checks.map((check) => ({
|
|
67
|
+
id: check.id,
|
|
68
|
+
...check.code ? { code: check.code } : {},
|
|
69
|
+
...check.message ? { message: check.message } : {}
|
|
70
|
+
})),
|
|
71
|
+
status: item.status
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function toOpenAIResponsesTool(schema) {
|
|
75
|
+
if ("input_schema" in schema) {
|
|
76
|
+
const tool = schema;
|
|
77
|
+
return {
|
|
78
|
+
type: "function",
|
|
79
|
+
name: tool.name,
|
|
80
|
+
description: tool.description,
|
|
81
|
+
parameters: tool.input_schema,
|
|
82
|
+
strict: tool.strict ?? false
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
if ("type" in schema && schema.type === "function" && "parameters" in schema) {
|
|
86
|
+
return { ...schema };
|
|
87
|
+
}
|
|
88
|
+
throw new Error(
|
|
89
|
+
"OpenAI Responses received a tool schema serialized for another protocol."
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
function toOpenAICompatTool(schema) {
|
|
93
|
+
if ("input_schema" in schema) {
|
|
94
|
+
const tool = schema;
|
|
95
|
+
return {
|
|
96
|
+
type: "function",
|
|
97
|
+
function: {
|
|
98
|
+
name: tool.name,
|
|
99
|
+
description: tool.description,
|
|
100
|
+
parameters: tool.input_schema,
|
|
101
|
+
strict: tool.strict ?? false
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
if ("function" in schema) {
|
|
106
|
+
return { ...schema };
|
|
107
|
+
}
|
|
108
|
+
throw new Error(
|
|
109
|
+
"OpenAI Chat Completions received a tool schema serialized for another protocol."
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
var OpenAIClient = class {
|
|
113
|
+
protocol = "openai";
|
|
114
|
+
client;
|
|
115
|
+
model;
|
|
116
|
+
systemPrompt;
|
|
117
|
+
maxOutputTokens;
|
|
118
|
+
thinkingLevel;
|
|
119
|
+
config;
|
|
120
|
+
constructor(config, systemPrompt) {
|
|
121
|
+
const apiKey = resolveAPIKey(config);
|
|
122
|
+
if (!apiKey) {
|
|
123
|
+
throw new AuthenticationError(
|
|
124
|
+
"OpenAI API key not found, set OPENAI_API_KEY in ~/.yukino/config.yaml, or via OPENAI_API_KEY env variable."
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
this.client = new OpenAI({
|
|
128
|
+
apiKey,
|
|
129
|
+
baseURL: config.base_url
|
|
130
|
+
});
|
|
131
|
+
this.model = config.model;
|
|
132
|
+
this.systemPrompt = systemPrompt;
|
|
133
|
+
this.maxOutputTokens = getMaxOutputTokens(config);
|
|
134
|
+
this.config = { ...config };
|
|
135
|
+
this.thinkingLevel = getThinkingLevel(config);
|
|
136
|
+
}
|
|
137
|
+
async *stream(conversation, toolSchemas, abortSignal) {
|
|
138
|
+
const messages = buildOpenAIInput(
|
|
139
|
+
ensureToolPairing(conversation.getMessages())
|
|
140
|
+
);
|
|
141
|
+
const input = [];
|
|
142
|
+
input.push({
|
|
143
|
+
role: "system",
|
|
144
|
+
content: this.systemPrompt
|
|
145
|
+
});
|
|
146
|
+
for (const message of messages) {
|
|
147
|
+
input.push(message);
|
|
148
|
+
}
|
|
149
|
+
const tools = toolSchemas.map(toOpenAIResponsesTool);
|
|
150
|
+
const effort = toReasoningEffort(this.getThinkingLevel(), this.config);
|
|
151
|
+
const params = {
|
|
152
|
+
model: this.model,
|
|
153
|
+
input,
|
|
154
|
+
stream: true,
|
|
155
|
+
max_output_tokens: this.maxOutputTokens,
|
|
156
|
+
...tools.length > 0 ? { tools } : {},
|
|
157
|
+
// Only explicit reasoning:false omits the field. Off sends none to defeat
|
|
158
|
+
// server-default reasoning; requesting a summary while off is unnecessary.
|
|
159
|
+
...effort !== null ? {
|
|
160
|
+
reasoning: {
|
|
161
|
+
effort,
|
|
162
|
+
...effort !== "none" ? { summary: "auto" } : {}
|
|
163
|
+
}
|
|
164
|
+
} : {}
|
|
165
|
+
};
|
|
166
|
+
let inputTokens = 0;
|
|
167
|
+
let outputTokens = 0;
|
|
168
|
+
let cacheReadInputTokens = 0;
|
|
169
|
+
const cacheCreationInputTokens = 0;
|
|
170
|
+
try {
|
|
171
|
+
const stream = await this.client.responses.create(params, {
|
|
172
|
+
...abortSignal ? { signal: abortSignal } : {}
|
|
173
|
+
});
|
|
174
|
+
let currentToolName = "";
|
|
175
|
+
let currentToolId = "";
|
|
176
|
+
let jsonAccumulate = "";
|
|
177
|
+
let reasoningId = "";
|
|
178
|
+
let reasoningText = "";
|
|
179
|
+
let sawTerminalResponse = false;
|
|
180
|
+
for await (const event of stream) {
|
|
181
|
+
if (event.type === "response.output_text.delta") {
|
|
182
|
+
yield {
|
|
183
|
+
type: "text_delta",
|
|
184
|
+
text: event.delta
|
|
185
|
+
};
|
|
186
|
+
} else if (event.type === "response.reasoning_summary_text.delta") {
|
|
187
|
+
reasoningText += event.delta;
|
|
188
|
+
yield { type: "thinking_delta", text: event.delta };
|
|
189
|
+
} else if (event.type === "response.reasoning_summary_text.done") {
|
|
190
|
+
yield {
|
|
191
|
+
type: "thinking_complete",
|
|
192
|
+
thinking: reasoningText,
|
|
193
|
+
signature: reasoningId
|
|
194
|
+
};
|
|
195
|
+
} else if (event.type === "response.function_call_arguments.delta") {
|
|
196
|
+
jsonAccumulate += event.delta;
|
|
197
|
+
yield {
|
|
198
|
+
type: "tool_call_delta",
|
|
199
|
+
text: event.delta
|
|
200
|
+
};
|
|
201
|
+
} else if (event.type === "response.output_item.added") {
|
|
202
|
+
if (event.item.type === "function_call") {
|
|
203
|
+
currentToolName = event.item.name;
|
|
204
|
+
currentToolId = event.item.call_id;
|
|
205
|
+
jsonAccumulate = "";
|
|
206
|
+
yield {
|
|
207
|
+
type: "tool_call_start",
|
|
208
|
+
toolName: currentToolName,
|
|
209
|
+
toolId: currentToolId
|
|
210
|
+
};
|
|
211
|
+
} else if (event.item.type === "computer_call") {
|
|
212
|
+
yield {
|
|
213
|
+
type: "tool_call_start",
|
|
214
|
+
toolName: "ComputerUse",
|
|
215
|
+
toolId: event.item.call_id
|
|
216
|
+
};
|
|
217
|
+
} else if (event.item.type === "reasoning") {
|
|
218
|
+
reasoningId = event.item.id ?? "";
|
|
219
|
+
reasoningText = "";
|
|
220
|
+
}
|
|
221
|
+
} else if (event.type === "response.output_item.done") {
|
|
222
|
+
if (event.item.type === "function_call" && currentToolName) {
|
|
223
|
+
let args = {};
|
|
224
|
+
if (jsonAccumulate) {
|
|
225
|
+
try {
|
|
226
|
+
const parsed = JSON.parse(jsonAccumulate);
|
|
227
|
+
args = isRecord(parsed) ? asRecord(parsed) : {};
|
|
228
|
+
} catch (err) {
|
|
229
|
+
log.error({ err }, "llm operation failed");
|
|
230
|
+
args = {};
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
yield {
|
|
234
|
+
type: "tool_call_complete",
|
|
235
|
+
toolId: currentToolId,
|
|
236
|
+
toolName: currentToolName,
|
|
237
|
+
arguments: args
|
|
238
|
+
};
|
|
239
|
+
currentToolName = "";
|
|
240
|
+
currentToolId = "";
|
|
241
|
+
jsonAccumulate = "";
|
|
242
|
+
} else if (event.item.type === "computer_call") {
|
|
243
|
+
yield {
|
|
244
|
+
type: "tool_call_complete",
|
|
245
|
+
toolId: event.item.call_id,
|
|
246
|
+
toolName: "ComputerUse",
|
|
247
|
+
arguments: computerCallArguments(event.item),
|
|
248
|
+
providerItemId: event.item.id
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
} else if (event.type === "response.completed" || event.type === "response.incomplete") {
|
|
252
|
+
sawTerminalResponse = true;
|
|
253
|
+
const usage = event.response.usage;
|
|
254
|
+
if (usage) {
|
|
255
|
+
outputTokens = usage.output_tokens;
|
|
256
|
+
cacheReadInputTokens = usage.input_tokens_details?.cached_tokens ?? 0;
|
|
257
|
+
inputTokens = Math.max(
|
|
258
|
+
0,
|
|
259
|
+
usage.input_tokens - cacheReadInputTokens
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
let stopReason = "end_turn";
|
|
263
|
+
const resp = event.response;
|
|
264
|
+
if (event.type === "response.incomplete" || resp.status === "incomplete") {
|
|
265
|
+
const details = resp.incomplete_details;
|
|
266
|
+
if (details?.reason === "max_output_tokens") {
|
|
267
|
+
stopReason = "max_tokens";
|
|
268
|
+
} else {
|
|
269
|
+
throw new LLMError(
|
|
270
|
+
`Response incomplete: ${details?.reason ?? "unknown reason"}`
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
yield {
|
|
275
|
+
type: "stream_end",
|
|
276
|
+
stopReason,
|
|
277
|
+
usage: {
|
|
278
|
+
inputTokens,
|
|
279
|
+
outputTokens,
|
|
280
|
+
cacheReadInputTokens,
|
|
281
|
+
cacheCreationInputTokens
|
|
282
|
+
// 0
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
} else if (event.type === "response.failed") {
|
|
286
|
+
const error = event.response.error;
|
|
287
|
+
const message = `${error?.code ?? "unknown"}: ${error?.message ?? "Response failed"}`;
|
|
288
|
+
throw containsContextLengthError(message) ? new ContextTooLongError(message) : new LLMError(message);
|
|
289
|
+
} else if (event.type === "error") {
|
|
290
|
+
const message = `${event.code ?? "unknown"}: ${event.message}`;
|
|
291
|
+
throw containsContextLengthError(message) ? new ContextTooLongError(message) : new LLMError(message);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
if (!sawTerminalResponse) {
|
|
295
|
+
throw new NetworkError(
|
|
296
|
+
"OpenAI Responses stream ended before a terminal response event"
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
} catch (err) {
|
|
300
|
+
log.error({ err }, "llm operation failed");
|
|
301
|
+
throw classifyOpenAIError(err);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
setSystemPrompt(prompt) {
|
|
305
|
+
this.systemPrompt = prompt;
|
|
306
|
+
}
|
|
307
|
+
setMaxOutputTokens(maxTokens) {
|
|
308
|
+
this.maxOutputTokens = maxTokens;
|
|
309
|
+
}
|
|
310
|
+
setThinkingLevel(level) {
|
|
311
|
+
this.thinkingLevel = clampThinkingLevel(this.config, level);
|
|
312
|
+
return this.thinkingLevel;
|
|
313
|
+
}
|
|
314
|
+
getThinkingLevel() {
|
|
315
|
+
return this.thinkingLevel;
|
|
316
|
+
}
|
|
317
|
+
getSupportedThinkingLevels() {
|
|
318
|
+
return getSupportedThinkingLevels(this.config);
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
function imageDataUrl(block) {
|
|
322
|
+
if (!isRecord(block) || block.type !== "image" || !isRecord(block.source)) {
|
|
323
|
+
return null;
|
|
324
|
+
}
|
|
325
|
+
if (block.source.type === "url") {
|
|
326
|
+
return strArg(block.source, "url") || null;
|
|
327
|
+
}
|
|
328
|
+
if (block.source.type !== "base64") {
|
|
329
|
+
return null;
|
|
330
|
+
}
|
|
331
|
+
const mediaType = strArg(block.source, "media_type");
|
|
332
|
+
const data = strArg(block.source, "data");
|
|
333
|
+
return mediaType && data ? `data:${mediaType};base64,${data}` : null;
|
|
334
|
+
}
|
|
335
|
+
function documentForResponses(block) {
|
|
336
|
+
if (!isRecord(block) || block.type !== "document" || !isRecord(block.source)) {
|
|
337
|
+
return null;
|
|
338
|
+
}
|
|
339
|
+
const title = typeof block.title === "string" && block.title ? block.title : "tool-result";
|
|
340
|
+
if (block.source.type === "url") {
|
|
341
|
+
const fileUrl = strArg(block.source, "url");
|
|
342
|
+
return fileUrl ? { type: "input_file", file_url: fileUrl } : null;
|
|
343
|
+
}
|
|
344
|
+
if (block.source.type === "base64") {
|
|
345
|
+
const data = strArg(block.source, "data");
|
|
346
|
+
return data ? { type: "input_file", file_data: data, filename: `${title}.pdf` } : null;
|
|
347
|
+
}
|
|
348
|
+
if (block.source.type === "text") {
|
|
349
|
+
const data = strArg(block.source, "data");
|
|
350
|
+
return data ? {
|
|
351
|
+
type: "input_file",
|
|
352
|
+
file_data: Buffer.from(data, "utf-8").toString("base64"),
|
|
353
|
+
filename: `${title}.txt`
|
|
354
|
+
} : null;
|
|
355
|
+
}
|
|
356
|
+
return null;
|
|
357
|
+
}
|
|
358
|
+
function documentForChat(block) {
|
|
359
|
+
if (!isRecord(block) || block.type !== "document" || !isRecord(block.source)) {
|
|
360
|
+
return null;
|
|
361
|
+
}
|
|
362
|
+
const title = typeof block.title === "string" && block.title ? block.title : "tool-result";
|
|
363
|
+
if (block.source.type === "base64") {
|
|
364
|
+
const data = strArg(block.source, "data");
|
|
365
|
+
return data ? { type: "file", file: { file_data: data, filename: `${title}.pdf` } } : null;
|
|
366
|
+
}
|
|
367
|
+
if (block.source.type === "text") {
|
|
368
|
+
const data = strArg(block.source, "data");
|
|
369
|
+
return data ? {
|
|
370
|
+
type: "file",
|
|
371
|
+
file: {
|
|
372
|
+
file_data: Buffer.from(data, "utf-8").toString("base64"),
|
|
373
|
+
filename: `${title}.txt`
|
|
374
|
+
}
|
|
375
|
+
} : null;
|
|
376
|
+
}
|
|
377
|
+
return null;
|
|
378
|
+
}
|
|
379
|
+
function toolOutputForResponses(tr) {
|
|
380
|
+
if (!tr.contentBlocks?.length) {
|
|
381
|
+
return tr.content;
|
|
382
|
+
}
|
|
383
|
+
const rich = [];
|
|
384
|
+
if (tr.content) {
|
|
385
|
+
rich.push({ type: "input_text", text: tr.content });
|
|
386
|
+
}
|
|
387
|
+
for (const block of tr.contentBlocks) {
|
|
388
|
+
const imageUrl = imageDataUrl(block);
|
|
389
|
+
if (imageUrl) {
|
|
390
|
+
rich.push({ type: "input_image", image_url: imageUrl, detail: "auto" });
|
|
391
|
+
continue;
|
|
392
|
+
}
|
|
393
|
+
const file = documentForResponses(block);
|
|
394
|
+
if (file) {
|
|
395
|
+
rich.push(file);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
return rich.length > 0 ? rich : tr.content;
|
|
399
|
+
}
|
|
400
|
+
function computerActionsForResponses(args) {
|
|
401
|
+
if (!Array.isArray(args.actions)) {
|
|
402
|
+
return [];
|
|
403
|
+
}
|
|
404
|
+
const actions = [];
|
|
405
|
+
for (const raw of args.actions) {
|
|
406
|
+
if (!isRecord(raw) || typeof raw.type !== "string") {
|
|
407
|
+
continue;
|
|
408
|
+
}
|
|
409
|
+
if (raw.type === "scroll") {
|
|
410
|
+
actions.push({
|
|
411
|
+
type: "scroll",
|
|
412
|
+
x: Number(raw.x),
|
|
413
|
+
y: Number(raw.y),
|
|
414
|
+
scroll_x: Number(raw.scrollX),
|
|
415
|
+
scroll_y: Number(raw.scrollY),
|
|
416
|
+
...Array.isArray(raw.keys) ? { keys: raw.keys.map(String) } : {}
|
|
417
|
+
});
|
|
418
|
+
continue;
|
|
419
|
+
}
|
|
420
|
+
actions.push(raw);
|
|
421
|
+
}
|
|
422
|
+
return actions;
|
|
423
|
+
}
|
|
424
|
+
function safetyChecksForResponses(args) {
|
|
425
|
+
if (!Array.isArray(args.pendingSafetyChecks)) {
|
|
426
|
+
return [];
|
|
427
|
+
}
|
|
428
|
+
return args.pendingSafetyChecks.flatMap((raw) => {
|
|
429
|
+
if (!isRecord(raw) || typeof raw.id !== "string") {
|
|
430
|
+
return [];
|
|
431
|
+
}
|
|
432
|
+
return [
|
|
433
|
+
{
|
|
434
|
+
id: raw.id,
|
|
435
|
+
...typeof raw.code === "string" ? { code: raw.code } : {},
|
|
436
|
+
...typeof raw.message === "string" ? { message: raw.message } : {}
|
|
437
|
+
}
|
|
438
|
+
];
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
function computerScreenshotUrl(tr) {
|
|
442
|
+
for (const block of tr.contentBlocks ?? []) {
|
|
443
|
+
const url = imageDataUrl(block);
|
|
444
|
+
if (url) {
|
|
445
|
+
return url;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
return void 0;
|
|
449
|
+
}
|
|
450
|
+
function collectRichParts(tr) {
|
|
451
|
+
if (!tr.contentBlocks?.length) {
|
|
452
|
+
return [];
|
|
453
|
+
}
|
|
454
|
+
const rich = [];
|
|
455
|
+
for (const block of tr.contentBlocks) {
|
|
456
|
+
const imageUrl = imageDataUrl(block);
|
|
457
|
+
if (imageUrl) {
|
|
458
|
+
rich.push({ type: "image_url", image_url: { url: imageUrl } });
|
|
459
|
+
continue;
|
|
460
|
+
}
|
|
461
|
+
const file = documentForChat(block);
|
|
462
|
+
if (file) {
|
|
463
|
+
rich.push(file);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
if (rich.length === 0) {
|
|
467
|
+
return [];
|
|
468
|
+
}
|
|
469
|
+
return [
|
|
470
|
+
{
|
|
471
|
+
type: "text",
|
|
472
|
+
text: `[Rich content returned by tool call ${tr.toolUseId}]`
|
|
473
|
+
},
|
|
474
|
+
...rich
|
|
475
|
+
];
|
|
476
|
+
}
|
|
477
|
+
function userContentsFor(content) {
|
|
478
|
+
if (typeof content === "string") {
|
|
479
|
+
return content;
|
|
480
|
+
}
|
|
481
|
+
const parts = [];
|
|
482
|
+
for (const block of content) {
|
|
483
|
+
if (block.type === "text") {
|
|
484
|
+
parts.push({ type: "input_text", text: strArg(block, "text") });
|
|
485
|
+
} else {
|
|
486
|
+
const url = imageDataUrl(block);
|
|
487
|
+
if (url) {
|
|
488
|
+
parts.push({ type: "input_image", image_url: url, detail: "auto" });
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
return parts;
|
|
493
|
+
}
|
|
494
|
+
function userPartsFor(content) {
|
|
495
|
+
if (typeof content === "string") {
|
|
496
|
+
return content;
|
|
497
|
+
}
|
|
498
|
+
const parts = [];
|
|
499
|
+
for (const block of content) {
|
|
500
|
+
if (block.type === "text") {
|
|
501
|
+
parts.push({ type: "text", text: strArg(block, "text") });
|
|
502
|
+
} else {
|
|
503
|
+
const url = imageDataUrl(block);
|
|
504
|
+
if (url) {
|
|
505
|
+
parts.push({ type: "image_url", image_url: { url } });
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
return parts;
|
|
510
|
+
}
|
|
511
|
+
function buildOpenAIInput(messages) {
|
|
512
|
+
const result = [];
|
|
513
|
+
const computerCalls = /* @__PURE__ */ new Map();
|
|
514
|
+
for (const message of messages) {
|
|
515
|
+
for (const toolUse of message.toolUses ?? []) {
|
|
516
|
+
if (toolUse.toolName === "ComputerUse") {
|
|
517
|
+
computerCalls.set(toolUse.toolUseId, toolUse);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
for (const m of messages) {
|
|
522
|
+
if (m.thinkingBlocks) {
|
|
523
|
+
for (const tb of m.thinkingBlocks) {
|
|
524
|
+
result.push({
|
|
525
|
+
type: "reasoning",
|
|
526
|
+
id: tb.signature,
|
|
527
|
+
summary: [{ type: "summary_text", text: tb.thinking }]
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
if (m.toolUses && m.toolUses.length > 0) {
|
|
532
|
+
const assistantText = typeof m.content === "string" ? m.content : contentToText(m.content);
|
|
533
|
+
if (assistantText) {
|
|
534
|
+
result.push({
|
|
535
|
+
role: "assistant",
|
|
536
|
+
content: assistantText
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
for (const tu of m.toolUses) {
|
|
540
|
+
if (tu.toolName === "ComputerUse") {
|
|
541
|
+
const status = tu.arguments.status;
|
|
542
|
+
result.push({
|
|
543
|
+
type: "computer_call",
|
|
544
|
+
id: tu.providerItemId ?? tu.toolUseId,
|
|
545
|
+
call_id: tu.toolUseId,
|
|
546
|
+
status: status === "in_progress" || status === "incomplete" ? status : "completed",
|
|
547
|
+
actions: computerActionsForResponses(tu.arguments),
|
|
548
|
+
pending_safety_checks: safetyChecksForResponses(tu.arguments)
|
|
549
|
+
});
|
|
550
|
+
} else {
|
|
551
|
+
result.push({
|
|
552
|
+
type: "function_call",
|
|
553
|
+
name: tu.toolName,
|
|
554
|
+
call_id: tu.toolUseId,
|
|
555
|
+
arguments: JSON.stringify(tu.arguments)
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
} else if (m.toolResults && m.toolResults.length > 0) {
|
|
560
|
+
for (const tr of m.toolResults) {
|
|
561
|
+
const computerCall = computerCalls.get(tr.toolUseId);
|
|
562
|
+
if (computerCall) {
|
|
563
|
+
const imageUrl = computerScreenshotUrl(tr);
|
|
564
|
+
result.push({
|
|
565
|
+
type: "computer_call_output",
|
|
566
|
+
call_id: tr.toolUseId,
|
|
567
|
+
output: {
|
|
568
|
+
type: "computer_screenshot",
|
|
569
|
+
...imageUrl ? { image_url: imageUrl } : {}
|
|
570
|
+
},
|
|
571
|
+
acknowledged_safety_checks: safetyChecksForResponses(
|
|
572
|
+
computerCall.arguments
|
|
573
|
+
)
|
|
574
|
+
});
|
|
575
|
+
if (tr.isError || !imageUrl) {
|
|
576
|
+
result.push({ role: "user", content: tr.content });
|
|
577
|
+
}
|
|
578
|
+
} else {
|
|
579
|
+
result.push({
|
|
580
|
+
type: "function_call_output",
|
|
581
|
+
call_id: tr.toolUseId,
|
|
582
|
+
output: toolOutputForResponses(tr)
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
if (m.content.length > 0) {
|
|
587
|
+
result.push({ role: "user", content: userContentsFor(m.content) });
|
|
588
|
+
}
|
|
589
|
+
} else if (m.role === "assistant") {
|
|
590
|
+
result.push({
|
|
591
|
+
role: "assistant",
|
|
592
|
+
content: typeof m.content === "string" ? m.content : contentToText(m.content)
|
|
593
|
+
});
|
|
594
|
+
} else {
|
|
595
|
+
result.push({
|
|
596
|
+
role: m.role,
|
|
597
|
+
content: userContentsFor(m.content)
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
return result;
|
|
602
|
+
}
|
|
603
|
+
function containsContextLengthError(msg) {
|
|
604
|
+
return /context_length_exceeded/i.test(msg) || /maximum\scontext\slength/i.test(msg) || /prompts?\s+too\s+long/i.test(msg);
|
|
605
|
+
}
|
|
606
|
+
var OpenAICompatClient = class {
|
|
607
|
+
protocol = "openai-compat";
|
|
608
|
+
client;
|
|
609
|
+
model;
|
|
610
|
+
systemPrompt;
|
|
611
|
+
maxOutputTokens;
|
|
612
|
+
thinkingLevel;
|
|
613
|
+
config;
|
|
614
|
+
constructor(config, systemPrompt) {
|
|
615
|
+
const apiKey = resolveAPIKey(config);
|
|
616
|
+
if (!apiKey) {
|
|
617
|
+
throw new AuthenticationError(
|
|
618
|
+
"OpenAI API key not found. Set OPENAI_API_KEY in ~/.yukino/config.yaml, or via OPENAI_API_KEY env variable."
|
|
619
|
+
);
|
|
620
|
+
}
|
|
621
|
+
this.client = new OpenAI({ apiKey, baseURL: config.base_url });
|
|
622
|
+
this.model = config.model;
|
|
623
|
+
this.systemPrompt = systemPrompt;
|
|
624
|
+
this.maxOutputTokens = getMaxOutputTokens(config);
|
|
625
|
+
this.config = { ...config };
|
|
626
|
+
this.thinkingLevel = getThinkingLevel(config);
|
|
627
|
+
}
|
|
628
|
+
setSystemPrompt(prompt) {
|
|
629
|
+
this.systemPrompt = prompt;
|
|
630
|
+
}
|
|
631
|
+
setMaxOutputTokens(maxTokens) {
|
|
632
|
+
this.maxOutputTokens = maxTokens;
|
|
633
|
+
}
|
|
634
|
+
setThinkingLevel(level) {
|
|
635
|
+
this.thinkingLevel = clampThinkingLevel(this.config, level);
|
|
636
|
+
return this.thinkingLevel;
|
|
637
|
+
}
|
|
638
|
+
getThinkingLevel() {
|
|
639
|
+
return this.thinkingLevel;
|
|
640
|
+
}
|
|
641
|
+
getSupportedThinkingLevels() {
|
|
642
|
+
return getSupportedThinkingLevels(this.config);
|
|
643
|
+
}
|
|
644
|
+
async *stream(conversation, toolSchemas, abortSignal) {
|
|
645
|
+
const messages = [
|
|
646
|
+
{
|
|
647
|
+
role: "system",
|
|
648
|
+
content: this.systemPrompt
|
|
649
|
+
},
|
|
650
|
+
...buildChatCompletionMessages(
|
|
651
|
+
ensureToolPairing(conversation.getMessages())
|
|
652
|
+
)
|
|
653
|
+
];
|
|
654
|
+
const tools = toolSchemas.map(toOpenAICompatTool);
|
|
655
|
+
const effort = toReasoningEffort(this.getThinkingLevel(), this.config);
|
|
656
|
+
const params = {
|
|
657
|
+
model: this.model,
|
|
658
|
+
messages,
|
|
659
|
+
stream: true,
|
|
660
|
+
stream_options: { include_usage: true },
|
|
661
|
+
max_tokens: this.maxOutputTokens,
|
|
662
|
+
...tools.length > 0 ? { tools } : {},
|
|
663
|
+
// Configured non-reasoning providers omit the field; off otherwise sends
|
|
664
|
+
// none explicitly so a server default cannot silently enable reasoning.
|
|
665
|
+
...effort !== null ? { reasoning_effort: effort } : {}
|
|
666
|
+
};
|
|
667
|
+
let inputTokens = 0;
|
|
668
|
+
let outputTokens = 0;
|
|
669
|
+
let cacheReadInputTokens = 0;
|
|
670
|
+
const cacheCreationInputTokens = 0;
|
|
671
|
+
try {
|
|
672
|
+
const stream = await this.client.chat.completions.create(params, {
|
|
673
|
+
...abortSignal ? { signal: abortSignal } : {}
|
|
674
|
+
});
|
|
675
|
+
const toolCalls = /* @__PURE__ */ new Map();
|
|
676
|
+
let finishReason = null;
|
|
677
|
+
let reasoningAccumulate = "";
|
|
678
|
+
for await (const chunk of stream) {
|
|
679
|
+
if (chunk.usage) {
|
|
680
|
+
outputTokens = chunk.usage.completion_tokens ?? 0;
|
|
681
|
+
cacheReadInputTokens = chunk.usage.prompt_tokens_details?.cached_tokens ?? 0;
|
|
682
|
+
inputTokens = Math.max(
|
|
683
|
+
0,
|
|
684
|
+
(chunk.usage.prompt_tokens ?? 0) - cacheReadInputTokens
|
|
685
|
+
);
|
|
686
|
+
}
|
|
687
|
+
if (chunk.choices.length === 0) {
|
|
688
|
+
continue;
|
|
689
|
+
}
|
|
690
|
+
const delta = chunk.choices[0].delta;
|
|
691
|
+
if (delta.content) {
|
|
692
|
+
yield { type: "text_delta", text: delta.content };
|
|
693
|
+
}
|
|
694
|
+
const reasoningContent = strArg(asRecord(delta), "reasoning_content");
|
|
695
|
+
if (reasoningContent) {
|
|
696
|
+
reasoningAccumulate += reasoningContent;
|
|
697
|
+
yield { type: "thinking_delta", text: reasoningContent };
|
|
698
|
+
}
|
|
699
|
+
if (delta.tool_calls) {
|
|
700
|
+
for (const tc of delta.tool_calls) {
|
|
701
|
+
if (!toolCalls.has(tc.index)) {
|
|
702
|
+
toolCalls.set(tc.index, {
|
|
703
|
+
id: tc.id ?? "",
|
|
704
|
+
name: tc.function?.name ?? "",
|
|
705
|
+
args: ""
|
|
706
|
+
});
|
|
707
|
+
if (tc.id) {
|
|
708
|
+
yield {
|
|
709
|
+
type: "tool_call_start",
|
|
710
|
+
toolName: tc.function?.name ?? "",
|
|
711
|
+
toolId: tc.id ?? ""
|
|
712
|
+
};
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
const existing = toolCalls.get(tc.index);
|
|
716
|
+
if (existing) {
|
|
717
|
+
if (tc.id) {
|
|
718
|
+
existing.id = tc.id;
|
|
719
|
+
}
|
|
720
|
+
if (tc.function?.name) {
|
|
721
|
+
existing.name = tc.function.name;
|
|
722
|
+
}
|
|
723
|
+
if (tc.function?.arguments) {
|
|
724
|
+
existing.args += tc.function.arguments;
|
|
725
|
+
yield {
|
|
726
|
+
type: "tool_call_delta",
|
|
727
|
+
text: tc.function.arguments
|
|
728
|
+
};
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
if (chunk.choices[0].finish_reason) {
|
|
734
|
+
finishReason = chunk.choices[0].finish_reason;
|
|
735
|
+
if (reasoningAccumulate) {
|
|
736
|
+
yield {
|
|
737
|
+
type: "thinking_complete",
|
|
738
|
+
thinking: reasoningAccumulate,
|
|
739
|
+
signature: ""
|
|
740
|
+
};
|
|
741
|
+
reasoningAccumulate = "";
|
|
742
|
+
}
|
|
743
|
+
for (const tu of toolCalls.values()) {
|
|
744
|
+
let args = {};
|
|
745
|
+
const jsonArgs = tu.args;
|
|
746
|
+
if (jsonArgs) {
|
|
747
|
+
try {
|
|
748
|
+
const parsed = JSON.parse(jsonArgs);
|
|
749
|
+
args = isRecord(parsed) ? asRecord(parsed) : {};
|
|
750
|
+
} catch (err) {
|
|
751
|
+
log.error({ err }, "llm operation failed");
|
|
752
|
+
args = {};
|
|
753
|
+
}
|
|
754
|
+
yield {
|
|
755
|
+
type: "tool_call_complete",
|
|
756
|
+
toolName: tu.name,
|
|
757
|
+
toolId: tu.id,
|
|
758
|
+
arguments: args
|
|
759
|
+
};
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
if (finishReason === null) {
|
|
765
|
+
throw new NetworkError(
|
|
766
|
+
"Chat Completions stream ended before a finish reason"
|
|
767
|
+
);
|
|
768
|
+
}
|
|
769
|
+
let stopReason;
|
|
770
|
+
if (finishReason === "length") {
|
|
771
|
+
stopReason = "max_tokens";
|
|
772
|
+
} else if (finishReason === "tool_calls" || toolCalls.size > 0) {
|
|
773
|
+
stopReason = "tool_use";
|
|
774
|
+
} else {
|
|
775
|
+
stopReason = "end_turn";
|
|
776
|
+
}
|
|
777
|
+
yield {
|
|
778
|
+
type: "stream_end",
|
|
779
|
+
stopReason,
|
|
780
|
+
usage: {
|
|
781
|
+
inputTokens,
|
|
782
|
+
outputTokens,
|
|
783
|
+
cacheReadInputTokens,
|
|
784
|
+
cacheCreationInputTokens
|
|
785
|
+
}
|
|
786
|
+
};
|
|
787
|
+
} catch (err) {
|
|
788
|
+
log.error({ err }, "llm operation failed");
|
|
789
|
+
throw classifyOpenAIError(err);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
};
|
|
793
|
+
function classifyOpenAIError(err) {
|
|
794
|
+
if (err instanceof LLMError) {
|
|
795
|
+
return err;
|
|
796
|
+
}
|
|
797
|
+
if (err instanceof OpenAI.APIError) {
|
|
798
|
+
if (err.status === 413 /* PromptTooLong */ || err.status === 400 /* BadRequest */ && containsContextLengthError(err.message)) {
|
|
799
|
+
return new ContextTooLongError(`Context Too Long: ${err.message}`);
|
|
800
|
+
}
|
|
801
|
+
if (err.status === 401 /* InvalidAPIKey */) {
|
|
802
|
+
return new AuthenticationError(`Invalid API key: ${err.message}`);
|
|
803
|
+
}
|
|
804
|
+
if (err.status === 429 /* RateLimitError */) {
|
|
805
|
+
const headers = err.headers;
|
|
806
|
+
return new RateLimitError(
|
|
807
|
+
"Rate limit error, please wait.",
|
|
808
|
+
headers instanceof Headers ? headers.get("retry-after") ?? void 0 : void 0
|
|
809
|
+
);
|
|
810
|
+
}
|
|
811
|
+
return new LLMError(
|
|
812
|
+
`OpenAI API error (${asString(err.status)}): ${err.message}`
|
|
813
|
+
);
|
|
814
|
+
}
|
|
815
|
+
return new NetworkError(
|
|
816
|
+
`Network error: ${err instanceof Error ? err.message : asString(err)}`
|
|
817
|
+
);
|
|
818
|
+
}
|
|
819
|
+
function buildChatCompletionMessages(messages) {
|
|
820
|
+
const params = [];
|
|
821
|
+
for (const m of messages) {
|
|
822
|
+
const reasoning = m.thinkingBlocks?.map((tb) => tb.thinking).join("") ?? "";
|
|
823
|
+
const assistantText = typeof m.content === "string" ? m.content : contentToText(m.content);
|
|
824
|
+
if (m.toolUses && m.toolUses.length > 0) {
|
|
825
|
+
params.push({
|
|
826
|
+
role: "assistant",
|
|
827
|
+
content: assistantText || null,
|
|
828
|
+
...reasoning ? { reasoning_content: reasoning } : {},
|
|
829
|
+
tool_calls: m.toolUses.map((tu) => ({
|
|
830
|
+
id: tu.toolUseId,
|
|
831
|
+
type: "function",
|
|
832
|
+
function: {
|
|
833
|
+
name: tu.toolName,
|
|
834
|
+
arguments: JSON.stringify(tu.arguments)
|
|
835
|
+
}
|
|
836
|
+
}))
|
|
837
|
+
});
|
|
838
|
+
} else if (m.toolResults && m.toolResults.length > 0) {
|
|
839
|
+
const pendingRichParts = [];
|
|
840
|
+
for (const tr of m.toolResults) {
|
|
841
|
+
params.push({
|
|
842
|
+
role: "tool",
|
|
843
|
+
tool_call_id: tr.toolUseId,
|
|
844
|
+
content: tr.content
|
|
845
|
+
});
|
|
846
|
+
pendingRichParts.push(...collectRichParts(tr));
|
|
847
|
+
}
|
|
848
|
+
if (m.content.length > 0) {
|
|
849
|
+
const content = userPartsFor(m.content);
|
|
850
|
+
pendingRichParts.push(
|
|
851
|
+
...typeof content === "string" ? [{ type: "text", text: content }] : content
|
|
852
|
+
);
|
|
853
|
+
}
|
|
854
|
+
if (pendingRichParts.length > 0) {
|
|
855
|
+
params.push({
|
|
856
|
+
role: "user",
|
|
857
|
+
content: pendingRichParts
|
|
858
|
+
});
|
|
859
|
+
}
|
|
860
|
+
} else if (m.role === "assistant") {
|
|
861
|
+
params.push({
|
|
862
|
+
role: "assistant",
|
|
863
|
+
content: assistantText,
|
|
864
|
+
...reasoning ? {
|
|
865
|
+
reasoning_content: reasoning
|
|
866
|
+
} : {}
|
|
867
|
+
});
|
|
868
|
+
} else if (m.role === "system") {
|
|
869
|
+
params.push({
|
|
870
|
+
role: "system",
|
|
871
|
+
content: typeof m.content === "string" ? m.content : contentToText(m.content)
|
|
872
|
+
});
|
|
873
|
+
} else {
|
|
874
|
+
params.push({
|
|
875
|
+
role: "user",
|
|
876
|
+
content: userPartsFor(m.content)
|
|
877
|
+
});
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
return params;
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
export {
|
|
884
|
+
OpenAIClient,
|
|
885
|
+
buildOpenAIInput,
|
|
886
|
+
OpenAICompatClient,
|
|
887
|
+
buildChatCompletionMessages,
|
|
888
|
+
openai_exports
|
|
889
|
+
};
|