@byfriends/kosong 0.1.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/LICENSE +28 -0
- package/README.md +11 -0
- package/dist/anthropic-Dm_GqFgS.d.mts +69 -0
- package/dist/capability-registry-CMBuEYcf.mjs +161 -0
- package/dist/chat-completions-stream-BuMu_xr9.mjs +62 -0
- package/dist/errors-DweKbIOf.d.mts +42 -0
- package/dist/errors-WFxxzL1B.mjs +80 -0
- package/dist/google-genai-hX0X6CF3.d.mts +98 -0
- package/dist/index.d.mts +161 -0
- package/dist/index.mjs +287 -0
- package/dist/openai-common-08qin3UI.mjs +278 -0
- package/dist/openai-common-B6cK2ig3.d.mts +105 -0
- package/dist/openai-compat-CMrIk-ib.d.mts +132 -0
- package/dist/openai-compat-CWbwO4b7.mjs +801 -0
- package/dist/openai-legacy-B6CVfLlr.d.mts +71 -0
- package/dist/openai-responses-BxOwxtd3.d.mts +65 -0
- package/dist/provider-DiJKWMsQ.d.mts +371 -0
- package/dist/providers/anthropic.d.mts +2 -0
- package/dist/providers/anthropic.mjs +720 -0
- package/dist/providers/google-genai.d.mts +2 -0
- package/dist/providers/google-genai.mjs +562 -0
- package/dist/providers/openai-common.d.mts +2 -0
- package/dist/providers/openai-common.mjs +2 -0
- package/dist/providers/openai-compat.d.mts +2 -0
- package/dist/providers/openai-compat.mjs +2 -0
- package/dist/providers/openai-legacy.d.mts +2 -0
- package/dist/providers/openai-legacy.mjs +248 -0
- package/dist/providers/openai-responses.d.mts +2 -0
- package/dist/providers/openai-responses.mjs +623 -0
- package/dist/request-auth-DCWSyCKI.mjs +63 -0
- package/package.json +89 -0
|
@@ -0,0 +1,623 @@
|
|
|
1
|
+
import { c as thinkingEffortToReasoningEffort, n as convertOpenAIError, p as extractText, s as reasoningEffortToThinkingEffort } from "../openai-common-08qin3UI.mjs";
|
|
2
|
+
import { o as ChatProviderError } from "../errors-WFxxzL1B.mjs";
|
|
3
|
+
import { n as requireProviderApiKey, r as resolveAuthBackedClient, t as mergeRequestHeaders } from "../request-auth-DCWSyCKI.mjs";
|
|
4
|
+
import { a as usesOpenAIResponsesDeveloperRole, i as getOpenAIResponsesModelCapability } from "../capability-registry-CMBuEYcf.mjs";
|
|
5
|
+
import OpenAI from "openai";
|
|
6
|
+
//#region src/providers/openai-responses.ts
|
|
7
|
+
/**
|
|
8
|
+
* Normalize the Responses API status / incomplete_details into the unified
|
|
9
|
+
* {@link FinishReason} enum.
|
|
10
|
+
*
|
|
11
|
+
* Note: the Responses API has no `tool_calls`-style status. When a response
|
|
12
|
+
* completes with `function_call` items inline the status is still
|
|
13
|
+
* `'completed'`; callers detect tool calls via `message.toolCalls.length`,
|
|
14
|
+
* not via finishReason.
|
|
15
|
+
*/
|
|
16
|
+
function normalizeResponsesFinishReason(status, incompleteReason) {
|
|
17
|
+
if (status === null || status === void 0) return {
|
|
18
|
+
finishReason: null,
|
|
19
|
+
rawFinishReason: null
|
|
20
|
+
};
|
|
21
|
+
if (status === "completed") return {
|
|
22
|
+
finishReason: "completed",
|
|
23
|
+
rawFinishReason: "completed"
|
|
24
|
+
};
|
|
25
|
+
if (status === "incomplete") {
|
|
26
|
+
if (incompleteReason === "max_output_tokens") return {
|
|
27
|
+
finishReason: "truncated",
|
|
28
|
+
rawFinishReason: "max_output_tokens"
|
|
29
|
+
};
|
|
30
|
+
if (incompleteReason === "content_filter") return {
|
|
31
|
+
finishReason: "filtered",
|
|
32
|
+
rawFinishReason: "content_filter"
|
|
33
|
+
};
|
|
34
|
+
return {
|
|
35
|
+
finishReason: "other",
|
|
36
|
+
rawFinishReason: incompleteReason ?? "incomplete"
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
if (status === "failed") return {
|
|
40
|
+
finishReason: "other",
|
|
41
|
+
rawFinishReason: "failed"
|
|
42
|
+
};
|
|
43
|
+
return {
|
|
44
|
+
finishReason: null,
|
|
45
|
+
rawFinishReason: null
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function asRawObject(value) {
|
|
49
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return null;
|
|
50
|
+
return value;
|
|
51
|
+
}
|
|
52
|
+
function readStringField(object, key) {
|
|
53
|
+
const value = object[key];
|
|
54
|
+
return typeof value === "string" ? value : void 0;
|
|
55
|
+
}
|
|
56
|
+
function readNullableStringField(object, key) {
|
|
57
|
+
const value = object[key];
|
|
58
|
+
if (value === null) return null;
|
|
59
|
+
return typeof value === "string" ? value : void 0;
|
|
60
|
+
}
|
|
61
|
+
function readNumberField(object, key) {
|
|
62
|
+
const value = object[key];
|
|
63
|
+
return typeof value === "number" ? value : void 0;
|
|
64
|
+
}
|
|
65
|
+
function readObjectField(object, key) {
|
|
66
|
+
return asRawObject(object[key]) ?? void 0;
|
|
67
|
+
}
|
|
68
|
+
function readObjectArrayField(object, key) {
|
|
69
|
+
const value = object[key];
|
|
70
|
+
if (!Array.isArray(value)) return void 0;
|
|
71
|
+
return value.flatMap((item) => {
|
|
72
|
+
const objectItem = asRawObject(item);
|
|
73
|
+
return objectItem === null ? [] : [objectItem];
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
function failResponsesDecode(context, detail) {
|
|
77
|
+
throw new ChatProviderError(`OpenAI Responses decode error: ${context} ${detail}`);
|
|
78
|
+
}
|
|
79
|
+
function requireStringField(object, key, context) {
|
|
80
|
+
const value = readStringField(object, key);
|
|
81
|
+
if (value === void 0) failResponsesDecode(`${context}.${key}`, "must be a string.");
|
|
82
|
+
return value;
|
|
83
|
+
}
|
|
84
|
+
function requireObjectField(object, key, context) {
|
|
85
|
+
const value = readObjectField(object, key);
|
|
86
|
+
if (value === void 0) failResponsesDecode(`${context}.${key}`, "must be an object.");
|
|
87
|
+
return value;
|
|
88
|
+
}
|
|
89
|
+
function readResponseOutputItem(value, context) {
|
|
90
|
+
const item = asRawObject(value);
|
|
91
|
+
if (item === null) failResponsesDecode(context, "must be an object.");
|
|
92
|
+
const type = requireStringField(item, "type", context);
|
|
93
|
+
if (type === "message") return {
|
|
94
|
+
type,
|
|
95
|
+
content: readObjectArrayField(item, "content") ?? []
|
|
96
|
+
};
|
|
97
|
+
if (type === "function_call") return {
|
|
98
|
+
type,
|
|
99
|
+
itemId: readStringField(item, "id"),
|
|
100
|
+
callId: readStringField(item, "call_id"),
|
|
101
|
+
name: readStringField(item, "name"),
|
|
102
|
+
arguments: readNullableStringField(item, "arguments")
|
|
103
|
+
};
|
|
104
|
+
if (type === "reasoning") return {
|
|
105
|
+
type,
|
|
106
|
+
encryptedContent: readStringField(item, "encrypted_content"),
|
|
107
|
+
summary: readObjectArrayField(item, "summary") ?? []
|
|
108
|
+
};
|
|
109
|
+
return { type: "other" };
|
|
110
|
+
}
|
|
111
|
+
function responseStreamIndex(itemId, outputIndex) {
|
|
112
|
+
return itemId ?? outputIndex;
|
|
113
|
+
}
|
|
114
|
+
function formatResponseStreamIndex(streamIndex) {
|
|
115
|
+
return streamIndex === void 0 ? "<unindexed>" : String(streamIndex);
|
|
116
|
+
}
|
|
117
|
+
function requireFunctionCallName(item) {
|
|
118
|
+
if (item.name === void 0) throw new ChatProviderError("OpenAI Responses function_call item is missing a name.");
|
|
119
|
+
return item.name;
|
|
120
|
+
}
|
|
121
|
+
function functionCallId(callId) {
|
|
122
|
+
return callId === void 0 || callId.length === 0 ? crypto.randomUUID() : callId;
|
|
123
|
+
}
|
|
124
|
+
function formatResponsesErrorEvent(code, message, param) {
|
|
125
|
+
return `${code ?? "unknown"}: ${message}${param === null ? "" : ` (param: ${param})`}`;
|
|
126
|
+
}
|
|
127
|
+
function formatResponsesFailedResponse(response) {
|
|
128
|
+
const error = readObjectField(response, "error");
|
|
129
|
+
if (error !== void 0) return `${readNullableStringField(error, "code") ?? "unknown"}: ${readStringField(error, "message") ?? "no message"}`;
|
|
130
|
+
const incompleteDetails = readObjectField(response, "incomplete_details");
|
|
131
|
+
const reason = incompleteDetails === void 0 ? void 0 : readStringField(incompleteDetails, "reason");
|
|
132
|
+
return reason === void 0 ? "Unknown error (no error details in response)" : `incomplete: ${reason}`;
|
|
133
|
+
}
|
|
134
|
+
function contentPartsToInputItems(parts) {
|
|
135
|
+
const items = [];
|
|
136
|
+
for (const part of parts) switch (part.type) {
|
|
137
|
+
case "text":
|
|
138
|
+
if (part.text) items.push({
|
|
139
|
+
type: "input_text",
|
|
140
|
+
text: part.text
|
|
141
|
+
});
|
|
142
|
+
break;
|
|
143
|
+
case "image_url":
|
|
144
|
+
items.push({
|
|
145
|
+
type: "input_image",
|
|
146
|
+
detail: "auto",
|
|
147
|
+
image_url: part.imageUrl.url
|
|
148
|
+
});
|
|
149
|
+
break;
|
|
150
|
+
case "audio_url": {
|
|
151
|
+
const mapped = mapAudioUrlToInputItem(part.audioUrl.url);
|
|
152
|
+
if (mapped !== null) items.push(mapped);
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
case "think":
|
|
156
|
+
case "video_url": break;
|
|
157
|
+
}
|
|
158
|
+
return items;
|
|
159
|
+
}
|
|
160
|
+
function contentPartsToOutputItems(parts) {
|
|
161
|
+
const items = [];
|
|
162
|
+
for (const part of parts) if (part.type === "text" && part.text) items.push({
|
|
163
|
+
type: "output_text",
|
|
164
|
+
text: part.text,
|
|
165
|
+
annotations: []
|
|
166
|
+
});
|
|
167
|
+
return items;
|
|
168
|
+
}
|
|
169
|
+
function messageContentToFunctionOutputItems(content) {
|
|
170
|
+
const items = [];
|
|
171
|
+
for (const part of content) switch (part.type) {
|
|
172
|
+
case "text":
|
|
173
|
+
if (part.text) items.push({
|
|
174
|
+
type: "input_text",
|
|
175
|
+
text: part.text
|
|
176
|
+
});
|
|
177
|
+
break;
|
|
178
|
+
case "image_url":
|
|
179
|
+
items.push({
|
|
180
|
+
type: "input_image",
|
|
181
|
+
image_url: part.imageUrl.url
|
|
182
|
+
});
|
|
183
|
+
break;
|
|
184
|
+
case "audio_url": {
|
|
185
|
+
const mapped = mapAudioUrlToInputItem(part.audioUrl.url);
|
|
186
|
+
if (mapped !== null) items.push(mapped);
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
case "think":
|
|
190
|
+
case "video_url": break;
|
|
191
|
+
}
|
|
192
|
+
return items;
|
|
193
|
+
}
|
|
194
|
+
function mapAudioUrlToInputItem(url) {
|
|
195
|
+
if (url.startsWith("data:audio/")) try {
|
|
196
|
+
const parts = url.split(",", 2);
|
|
197
|
+
if (parts.length !== 2 || parts[0] === void 0 || parts[1] === void 0) return null;
|
|
198
|
+
const header = parts[0];
|
|
199
|
+
const b64 = parts[1];
|
|
200
|
+
const subtypePart = header.split("/")[1];
|
|
201
|
+
if (subtypePart === void 0) return null;
|
|
202
|
+
const [subtypeHead = ""] = subtypePart.split(";");
|
|
203
|
+
const subtype = subtypeHead.toLowerCase();
|
|
204
|
+
const ext = subtype === "mp3" || subtype === "mpeg" ? "mp3" : subtype === "wav" ? "wav" : null;
|
|
205
|
+
if (ext === null) return null;
|
|
206
|
+
return {
|
|
207
|
+
type: "input_file",
|
|
208
|
+
file_data: b64,
|
|
209
|
+
filename: `inline.${ext}`
|
|
210
|
+
};
|
|
211
|
+
} catch {
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
if (url.startsWith("http://") || url.startsWith("https://")) return {
|
|
215
|
+
type: "input_file",
|
|
216
|
+
file_url: url
|
|
217
|
+
};
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
function convertMessage(message, modelName, toolMessageConversion) {
|
|
221
|
+
let role = message.role;
|
|
222
|
+
if (usesOpenAIResponsesDeveloperRole(modelName) && role === "system") role = "developer";
|
|
223
|
+
if (role === "tool") return [{
|
|
224
|
+
call_id: message.toolCallId ?? "",
|
|
225
|
+
output: toolMessageConversion === "extract_text" ? extractText(message) : messageContentToFunctionOutputItems(message.content),
|
|
226
|
+
type: "function_call_output"
|
|
227
|
+
}];
|
|
228
|
+
const result = [];
|
|
229
|
+
if (message.content.length > 0) {
|
|
230
|
+
const pendingParts = [];
|
|
231
|
+
const flushPendingParts = () => {
|
|
232
|
+
if (pendingParts.length === 0) return;
|
|
233
|
+
if (role === "assistant") result.push({
|
|
234
|
+
content: contentPartsToOutputItems(pendingParts),
|
|
235
|
+
role,
|
|
236
|
+
type: "message"
|
|
237
|
+
});
|
|
238
|
+
else result.push({
|
|
239
|
+
content: contentPartsToInputItems(pendingParts),
|
|
240
|
+
role,
|
|
241
|
+
type: "message"
|
|
242
|
+
});
|
|
243
|
+
pendingParts.length = 0;
|
|
244
|
+
};
|
|
245
|
+
let i = 0;
|
|
246
|
+
const n = message.content.length;
|
|
247
|
+
while (i < n) {
|
|
248
|
+
const part = message.content[i];
|
|
249
|
+
if (part === void 0) break;
|
|
250
|
+
if (part.type === "think") {
|
|
251
|
+
flushPendingParts();
|
|
252
|
+
const encryptedValue = part.encrypted;
|
|
253
|
+
const summaries = [{
|
|
254
|
+
type: "summary_text",
|
|
255
|
+
text: part.think || ""
|
|
256
|
+
}];
|
|
257
|
+
i += 1;
|
|
258
|
+
while (i < n) {
|
|
259
|
+
const nextPart = message.content[i];
|
|
260
|
+
if (nextPart === void 0) break;
|
|
261
|
+
if (nextPart.type !== "think") break;
|
|
262
|
+
if (nextPart.encrypted !== encryptedValue) break;
|
|
263
|
+
summaries.push({
|
|
264
|
+
type: "summary_text",
|
|
265
|
+
text: nextPart.think || ""
|
|
266
|
+
});
|
|
267
|
+
i += 1;
|
|
268
|
+
}
|
|
269
|
+
result.push({
|
|
270
|
+
summary: summaries,
|
|
271
|
+
type: "reasoning",
|
|
272
|
+
encrypted_content: encryptedValue
|
|
273
|
+
});
|
|
274
|
+
} else {
|
|
275
|
+
pendingParts.push(part);
|
|
276
|
+
i += 1;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
flushPendingParts();
|
|
280
|
+
}
|
|
281
|
+
for (const toolCall of message.toolCalls) result.push({
|
|
282
|
+
arguments: toolCall.arguments ?? "{}",
|
|
283
|
+
call_id: toolCall.id,
|
|
284
|
+
name: toolCall.name,
|
|
285
|
+
type: "function_call"
|
|
286
|
+
});
|
|
287
|
+
return result;
|
|
288
|
+
}
|
|
289
|
+
function convertTool(tool) {
|
|
290
|
+
return {
|
|
291
|
+
type: "function",
|
|
292
|
+
name: tool.name,
|
|
293
|
+
description: tool.description,
|
|
294
|
+
parameters: tool.parameters,
|
|
295
|
+
strict: false
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
var OpenAIResponsesStreamedMessage = class {
|
|
299
|
+
_id = null;
|
|
300
|
+
_usage = null;
|
|
301
|
+
_finishReason = null;
|
|
302
|
+
_rawFinishReason = null;
|
|
303
|
+
_iter;
|
|
304
|
+
constructor(response, isStream) {
|
|
305
|
+
if (isStream) this._iter = this._convertStreamResponse(response);
|
|
306
|
+
else this._iter = this._convertNonStreamResponse(response);
|
|
307
|
+
}
|
|
308
|
+
get id() {
|
|
309
|
+
return this._id;
|
|
310
|
+
}
|
|
311
|
+
get usage() {
|
|
312
|
+
return this._usage;
|
|
313
|
+
}
|
|
314
|
+
get finishReason() {
|
|
315
|
+
return this._finishReason;
|
|
316
|
+
}
|
|
317
|
+
get rawFinishReason() {
|
|
318
|
+
return this._rawFinishReason;
|
|
319
|
+
}
|
|
320
|
+
async *[Symbol.asyncIterator]() {
|
|
321
|
+
yield* this._iter;
|
|
322
|
+
}
|
|
323
|
+
_captureFinishReasonFromResponse(response) {
|
|
324
|
+
const status = readNullableStringField(response, "status");
|
|
325
|
+
const incomplete = readObjectField(response, "incomplete_details");
|
|
326
|
+
const normalized = normalizeResponsesFinishReason(status, incomplete ? readStringField(incomplete, "reason") : null);
|
|
327
|
+
this._finishReason = normalized.finishReason;
|
|
328
|
+
this._rawFinishReason = normalized.rawFinishReason;
|
|
329
|
+
}
|
|
330
|
+
_extractUsage(usage) {
|
|
331
|
+
const inputTokens = readNumberField(usage, "input_tokens") ?? 0;
|
|
332
|
+
const outputTokens = readNumberField(usage, "output_tokens") ?? 0;
|
|
333
|
+
const details = readObjectField(usage, "input_tokens_details");
|
|
334
|
+
const cached = details ? readNumberField(details, "cached_tokens") ?? 0 : 0;
|
|
335
|
+
this._usage = {
|
|
336
|
+
inputOther: inputTokens - cached,
|
|
337
|
+
output: outputTokens,
|
|
338
|
+
inputCacheRead: cached,
|
|
339
|
+
inputCacheCreation: 0
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
async *_convertNonStreamResponse(response) {
|
|
343
|
+
this._id = readStringField(response, "id") ?? null;
|
|
344
|
+
const usage = readObjectField(response, "usage");
|
|
345
|
+
if (usage !== void 0) this._extractUsage(usage);
|
|
346
|
+
this._captureFinishReasonFromResponse(response);
|
|
347
|
+
const output = readObjectArrayField(response, "output");
|
|
348
|
+
if (output === void 0) return;
|
|
349
|
+
for (const item of output) {
|
|
350
|
+
const outputItem = readResponseOutputItem(item, "response.output item");
|
|
351
|
+
if (outputItem.type === "message") {
|
|
352
|
+
for (const contentItem of outputItem.content) if (contentItem["type"] === "output_text") {
|
|
353
|
+
const text = readStringField(contentItem, "text");
|
|
354
|
+
if (text !== void 0) yield {
|
|
355
|
+
type: "text",
|
|
356
|
+
text
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
} else if (outputItem.type === "function_call") yield {
|
|
360
|
+
type: "function",
|
|
361
|
+
id: functionCallId(outputItem.callId),
|
|
362
|
+
name: requireFunctionCallName(outputItem),
|
|
363
|
+
arguments: outputItem.arguments ?? null
|
|
364
|
+
};
|
|
365
|
+
else if (outputItem.type === "reasoning") for (const summary of outputItem.summary) {
|
|
366
|
+
const text = readStringField(summary, "text");
|
|
367
|
+
if (text === void 0) continue;
|
|
368
|
+
const thinkPart = {
|
|
369
|
+
type: "think",
|
|
370
|
+
think: text
|
|
371
|
+
};
|
|
372
|
+
if (outputItem.encryptedContent !== void 0) thinkPart.encrypted = outputItem.encryptedContent;
|
|
373
|
+
yield thinkPart;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
async *_convertStreamResponse(response) {
|
|
378
|
+
const functionCallArgumentsByIndex = /* @__PURE__ */ new Map();
|
|
379
|
+
let unindexedFunctionCallArguments;
|
|
380
|
+
const hasFunctionCallArguments = (streamIndex) => streamIndex === void 0 ? unindexedFunctionCallArguments !== void 0 : functionCallArgumentsByIndex.has(streamIndex);
|
|
381
|
+
const getFunctionCallArguments = (streamIndex) => streamIndex === void 0 ? unindexedFunctionCallArguments : functionCallArgumentsByIndex.get(streamIndex);
|
|
382
|
+
const setFunctionCallArguments = (streamIndex, argumentsValue) => {
|
|
383
|
+
if (streamIndex === void 0) unindexedFunctionCallArguments = argumentsValue;
|
|
384
|
+
else functionCallArgumentsByIndex.set(streamIndex, argumentsValue);
|
|
385
|
+
};
|
|
386
|
+
const appendFunctionCallArguments = (streamIndex, argumentsPart, context) => {
|
|
387
|
+
if (!hasFunctionCallArguments(streamIndex)) failResponsesDecode(context, `received function-call arguments for unknown stream index ${formatResponseStreamIndex(streamIndex)}.`);
|
|
388
|
+
setFunctionCallArguments(streamIndex, getFunctionCallArguments(streamIndex) + argumentsPart);
|
|
389
|
+
};
|
|
390
|
+
const yieldFinalArgumentsSuffix = function* (streamIndex, finalArguments, context) {
|
|
391
|
+
if (!hasFunctionCallArguments(streamIndex)) failResponsesDecode(context, `received final function-call arguments for unknown stream index ${formatResponseStreamIndex(streamIndex)}.`);
|
|
392
|
+
const accumulatedArguments = getFunctionCallArguments(streamIndex);
|
|
393
|
+
if (finalArguments === accumulatedArguments) return;
|
|
394
|
+
if (!finalArguments.startsWith(accumulatedArguments)) throw new ChatProviderError(`OpenAI Responses final function-call arguments for stream index ${formatResponseStreamIndex(streamIndex)} do not match the streamed argument deltas.`);
|
|
395
|
+
const suffix = finalArguments.slice(accumulatedArguments.length);
|
|
396
|
+
setFunctionCallArguments(streamIndex, finalArguments);
|
|
397
|
+
if (suffix.length === 0) return;
|
|
398
|
+
const part = {
|
|
399
|
+
type: "tool_call_part",
|
|
400
|
+
argumentsPart: suffix
|
|
401
|
+
};
|
|
402
|
+
if (streamIndex !== void 0) part.index = streamIndex;
|
|
403
|
+
yield part;
|
|
404
|
+
};
|
|
405
|
+
try {
|
|
406
|
+
for await (const chunk of response) {
|
|
407
|
+
const type = requireStringField(chunk, "type", "stream event");
|
|
408
|
+
switch (type) {
|
|
409
|
+
case "response.output_text.delta":
|
|
410
|
+
yield {
|
|
411
|
+
type: "text",
|
|
412
|
+
text: requireStringField(chunk, "delta", type)
|
|
413
|
+
};
|
|
414
|
+
break;
|
|
415
|
+
case "response.created":
|
|
416
|
+
case "response.in_progress": {
|
|
417
|
+
const respId = readStringField(requireObjectField(chunk, "response", type), "id");
|
|
418
|
+
if (respId !== void 0) this._id = respId;
|
|
419
|
+
break;
|
|
420
|
+
}
|
|
421
|
+
case "response.output_item.added": {
|
|
422
|
+
const item = readResponseOutputItem(chunk["item"], `${type}.item`);
|
|
423
|
+
const outputIndex = readNumberField(chunk, "output_index");
|
|
424
|
+
if (item.type === "function_call") {
|
|
425
|
+
const streamIndex = responseStreamIndex(item.itemId, outputIndex);
|
|
426
|
+
setFunctionCallArguments(streamIndex, item.arguments ?? "");
|
|
427
|
+
const tc = {
|
|
428
|
+
type: "function",
|
|
429
|
+
id: functionCallId(item.callId),
|
|
430
|
+
name: requireFunctionCallName(item),
|
|
431
|
+
arguments: item.arguments ?? null
|
|
432
|
+
};
|
|
433
|
+
if (streamIndex !== void 0) tc._streamIndex = streamIndex;
|
|
434
|
+
yield tc;
|
|
435
|
+
}
|
|
436
|
+
break;
|
|
437
|
+
}
|
|
438
|
+
case "response.output_item.done": {
|
|
439
|
+
const item = readResponseOutputItem(chunk["item"], `${type}.item`);
|
|
440
|
+
const outputIndex = readNumberField(chunk, "output_index");
|
|
441
|
+
if (item.type === "reasoning") {
|
|
442
|
+
const thinkPart = {
|
|
443
|
+
type: "think",
|
|
444
|
+
think: ""
|
|
445
|
+
};
|
|
446
|
+
if (item.encryptedContent !== void 0) thinkPart.encrypted = item.encryptedContent;
|
|
447
|
+
yield thinkPart;
|
|
448
|
+
} else if (item.type === "function_call" && typeof item.arguments === "string") yield* yieldFinalArgumentsSuffix(responseStreamIndex(item.itemId, outputIndex), item.arguments, type);
|
|
449
|
+
break;
|
|
450
|
+
}
|
|
451
|
+
case "response.function_call_arguments.delta": {
|
|
452
|
+
const streamIndex = responseStreamIndex(readStringField(chunk, "item_id"), readNumberField(chunk, "output_index"));
|
|
453
|
+
const argumentsPart = requireStringField(chunk, "delta", type);
|
|
454
|
+
const part = {
|
|
455
|
+
type: "tool_call_part",
|
|
456
|
+
argumentsPart
|
|
457
|
+
};
|
|
458
|
+
appendFunctionCallArguments(streamIndex, argumentsPart, type);
|
|
459
|
+
if (streamIndex !== void 0) part.index = streamIndex;
|
|
460
|
+
yield part;
|
|
461
|
+
break;
|
|
462
|
+
}
|
|
463
|
+
case "response.function_call_arguments.done": {
|
|
464
|
+
const functionArguments = requireStringField(chunk, "arguments", type);
|
|
465
|
+
yield* yieldFinalArgumentsSuffix(responseStreamIndex(readStringField(chunk, "item_id"), readNumberField(chunk, "output_index")), functionArguments, type);
|
|
466
|
+
break;
|
|
467
|
+
}
|
|
468
|
+
case "response.reasoning_summary_part.added":
|
|
469
|
+
yield {
|
|
470
|
+
type: "think",
|
|
471
|
+
think: ""
|
|
472
|
+
};
|
|
473
|
+
break;
|
|
474
|
+
case "response.reasoning_summary_text.delta":
|
|
475
|
+
yield {
|
|
476
|
+
type: "think",
|
|
477
|
+
think: requireStringField(chunk, "delta", type)
|
|
478
|
+
};
|
|
479
|
+
break;
|
|
480
|
+
case "response.completed":
|
|
481
|
+
case "response.incomplete": {
|
|
482
|
+
const responseObject = requireObjectField(chunk, "response", type);
|
|
483
|
+
const respId = readStringField(responseObject, "id");
|
|
484
|
+
if (respId !== void 0) this._id = respId;
|
|
485
|
+
const usage = readObjectField(responseObject, "usage");
|
|
486
|
+
if (usage !== void 0) this._extractUsage(usage);
|
|
487
|
+
this._captureFinishReasonFromResponse(responseObject);
|
|
488
|
+
break;
|
|
489
|
+
}
|
|
490
|
+
case "error": {
|
|
491
|
+
const message = requireStringField(chunk, "message", type);
|
|
492
|
+
throw new ChatProviderError(`OpenAI Responses stream error: ${formatResponsesErrorEvent(readNullableStringField(chunk, "code") ?? null, message, readNullableStringField(chunk, "param") ?? null)}`);
|
|
493
|
+
}
|
|
494
|
+
case "response.failed": throw new ChatProviderError(`OpenAI Responses response.failed: ${formatResponsesFailedResponse(requireObjectField(chunk, "response", type))}`);
|
|
495
|
+
default: break;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
} catch (error) {
|
|
499
|
+
throw convertOpenAIError(error);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
};
|
|
503
|
+
var OpenAIResponsesChatProvider = class {
|
|
504
|
+
name = "openai-responses";
|
|
505
|
+
_model;
|
|
506
|
+
_stream;
|
|
507
|
+
_apiKey;
|
|
508
|
+
_baseUrl;
|
|
509
|
+
_defaultHeaders;
|
|
510
|
+
_generationKwargs;
|
|
511
|
+
_toolMessageConversion;
|
|
512
|
+
_client;
|
|
513
|
+
_httpClient;
|
|
514
|
+
_clientFactory;
|
|
515
|
+
constructor(options) {
|
|
516
|
+
const apiKey = options.apiKey ?? process.env["OPENAI_API_KEY"];
|
|
517
|
+
this._apiKey = apiKey === void 0 || apiKey.length === 0 ? void 0 : apiKey;
|
|
518
|
+
this._baseUrl = options.baseUrl ?? "https://api.openai.com/v1";
|
|
519
|
+
this._defaultHeaders = options.defaultHeaders;
|
|
520
|
+
this._model = options.model;
|
|
521
|
+
this._stream = true;
|
|
522
|
+
this._generationKwargs = {};
|
|
523
|
+
this._toolMessageConversion = options.toolMessageConversion ?? null;
|
|
524
|
+
this._httpClient = options.httpClient;
|
|
525
|
+
this._clientFactory = options.clientFactory;
|
|
526
|
+
if (options.maxOutputTokens !== void 0) this._generationKwargs.max_output_tokens = options.maxOutputTokens;
|
|
527
|
+
this._client = this._apiKey === void 0 ? void 0 : this._buildClient(this._apiKey);
|
|
528
|
+
}
|
|
529
|
+
get modelName() {
|
|
530
|
+
return this._model;
|
|
531
|
+
}
|
|
532
|
+
get thinkingEffort() {
|
|
533
|
+
return reasoningEffortToThinkingEffort(this._generationKwargs.reasoning_effort);
|
|
534
|
+
}
|
|
535
|
+
get modelParameters() {
|
|
536
|
+
return {
|
|
537
|
+
model: this._model,
|
|
538
|
+
baseUrl: this._baseUrl,
|
|
539
|
+
...this._generationKwargs
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
getCapability(model) {
|
|
543
|
+
return getOpenAIResponsesModelCapability(model ?? this._model);
|
|
544
|
+
}
|
|
545
|
+
async generate(systemPrompt, tools, history, options) {
|
|
546
|
+
const input = [];
|
|
547
|
+
if (systemPrompt) {
|
|
548
|
+
const sysItem = {
|
|
549
|
+
role: "system",
|
|
550
|
+
content: systemPrompt
|
|
551
|
+
};
|
|
552
|
+
if (usesOpenAIResponsesDeveloperRole(this._model)) sysItem["role"] = "developer";
|
|
553
|
+
input.push(sysItem);
|
|
554
|
+
}
|
|
555
|
+
for (const msg of history) input.push(...convertMessage(msg, this._model, this._toolMessageConversion));
|
|
556
|
+
const kwargs = { ...this._generationKwargs };
|
|
557
|
+
const reasoningEffort = kwargs["reasoning_effort"];
|
|
558
|
+
delete kwargs["reasoning_effort"];
|
|
559
|
+
if (reasoningEffort !== void 0) {
|
|
560
|
+
kwargs["reasoning"] = {
|
|
561
|
+
effort: reasoningEffort,
|
|
562
|
+
summary: "auto"
|
|
563
|
+
};
|
|
564
|
+
kwargs["include"] = ["reasoning.encrypted_content"];
|
|
565
|
+
}
|
|
566
|
+
for (const key of Object.keys(kwargs)) if (kwargs[key] === void 0) delete kwargs[key];
|
|
567
|
+
try {
|
|
568
|
+
const client = this._createClient(options?.auth);
|
|
569
|
+
const createParams = {
|
|
570
|
+
model: this._model,
|
|
571
|
+
input,
|
|
572
|
+
tools: tools.map((t) => convertTool(t)),
|
|
573
|
+
store: false,
|
|
574
|
+
stream: this._stream,
|
|
575
|
+
...kwargs
|
|
576
|
+
};
|
|
577
|
+
if (!("responses" in client) || typeof client.responses?.create !== "function") throw new Error("OpenAI SDK version does not support Responses API. Upgrade to >=4.x with responses support.");
|
|
578
|
+
return new OpenAIResponsesStreamedMessage(await client.responses.create(createParams, options?.signal ? { signal: options.signal } : void 0), this._stream);
|
|
579
|
+
} catch (error) {
|
|
580
|
+
throw convertOpenAIError(error);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
withThinking(effort) {
|
|
584
|
+
const reasoningEffort = thinkingEffortToReasoningEffort(effort);
|
|
585
|
+
const clone = this._clone();
|
|
586
|
+
clone._generationKwargs = {
|
|
587
|
+
...clone._generationKwargs,
|
|
588
|
+
reasoning_effort: reasoningEffort
|
|
589
|
+
};
|
|
590
|
+
return clone;
|
|
591
|
+
}
|
|
592
|
+
withGenerationKwargs(kwargs) {
|
|
593
|
+
const clone = this._clone();
|
|
594
|
+
clone._generationKwargs = {
|
|
595
|
+
...clone._generationKwargs,
|
|
596
|
+
...kwargs
|
|
597
|
+
};
|
|
598
|
+
return clone;
|
|
599
|
+
}
|
|
600
|
+
_clone() {
|
|
601
|
+
const clone = Object.assign(Object.create(Object.getPrototypeOf(this)), this);
|
|
602
|
+
clone._generationKwargs = { ...this._generationKwargs };
|
|
603
|
+
return clone;
|
|
604
|
+
}
|
|
605
|
+
_createClient(auth) {
|
|
606
|
+
return resolveAuthBackedClient({
|
|
607
|
+
cachedClient: this._client,
|
|
608
|
+
clientFactory: this._clientFactory
|
|
609
|
+
}, auth, (a) => this._buildClient(requireProviderApiKey("OpenAIResponsesChatProvider", a, this._apiKey), a));
|
|
610
|
+
}
|
|
611
|
+
_buildClient(apiKey, auth) {
|
|
612
|
+
const clientOpts = {
|
|
613
|
+
apiKey,
|
|
614
|
+
baseURL: this._baseUrl
|
|
615
|
+
};
|
|
616
|
+
const defaultHeaders = mergeRequestHeaders(this._defaultHeaders, auth?.headers);
|
|
617
|
+
if (defaultHeaders !== void 0) clientOpts["defaultHeaders"] = defaultHeaders;
|
|
618
|
+
if (this._httpClient !== void 0) clientOpts["httpClient"] = this._httpClient;
|
|
619
|
+
return new OpenAI(clientOpts);
|
|
620
|
+
}
|
|
621
|
+
};
|
|
622
|
+
//#endregion
|
|
623
|
+
export { OpenAIResponsesChatProvider, OpenAIResponsesStreamedMessage };
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { o as ChatProviderError } from "./errors-WFxxzL1B.mjs";
|
|
2
|
+
//#region src/capability.ts
|
|
3
|
+
const UNKNOWN_CAPABILITY_MARKER = Symbol.for("byf.kosong.UNKNOWN_CAPABILITY");
|
|
4
|
+
/**
|
|
5
|
+
* Shared read-only default returned when a provider has not catalogued a
|
|
6
|
+
* given model. Frozen so accidental mutation at one call site cannot leak
|
|
7
|
+
* into another.
|
|
8
|
+
*/
|
|
9
|
+
const UNKNOWN_CAPABILITY = Object.freeze(Object.defineProperty({
|
|
10
|
+
image_in: false,
|
|
11
|
+
video_in: false,
|
|
12
|
+
audio_in: false,
|
|
13
|
+
thinking: false,
|
|
14
|
+
tool_use: false,
|
|
15
|
+
max_context_tokens: 0
|
|
16
|
+
}, UNKNOWN_CAPABILITY_MARKER, { value: true }));
|
|
17
|
+
function isUnknownCapability(capability) {
|
|
18
|
+
if (capability === UNKNOWN_CAPABILITY) return true;
|
|
19
|
+
if (capability[UNKNOWN_CAPABILITY_MARKER] === true) return true;
|
|
20
|
+
return !capability.image_in && !capability.video_in && !capability.audio_in && !capability.thinking && !capability.tool_use && capability.max_context_tokens === 0;
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region src/providers/request-auth.ts
|
|
24
|
+
function requireProviderApiKey(providerName, auth, defaultApiKey) {
|
|
25
|
+
const apiKey = auth?.apiKey ?? defaultApiKey;
|
|
26
|
+
if (apiKey === void 0 || apiKey.length === 0) throw new ChatProviderError(`${providerName}: apiKey is required. Provide it via the constructor options, the provider's API-key environment variable, options.auth.apiKey on each request, or an OAuth login.`);
|
|
27
|
+
return apiKey;
|
|
28
|
+
}
|
|
29
|
+
function mergeRequestHeaders(defaultHeaders, requestHeaders) {
|
|
30
|
+
const merged = {};
|
|
31
|
+
if (defaultHeaders !== void 0) Object.assign(merged, defaultHeaders);
|
|
32
|
+
if (requestHeaders !== void 0) Object.assign(merged, requestHeaders);
|
|
33
|
+
return Object.keys(merged).length > 0 ? merged : void 0;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Resolve the SDK client to use for a single provider request, applying the
|
|
37
|
+
* standard precedence shared by every provider adapter:
|
|
38
|
+
*
|
|
39
|
+
* 1. If a `clientFactory` was supplied, delegate to it (it receives the
|
|
40
|
+
* per-request {@link ProviderRequestAuth}, defaulting to `{}`).
|
|
41
|
+
* 2. Otherwise, if no per-request auth is needed AND a constructor-time
|
|
42
|
+
* client was cached, reuse the cached instance.
|
|
43
|
+
* 3. Otherwise, call `build(auth)` to construct a fresh client for this
|
|
44
|
+
* request — typically using `requireProviderApiKey` plus
|
|
45
|
+
* `mergeRequestHeaders`.
|
|
46
|
+
*
|
|
47
|
+
* Note: when per-request `auth` is provided (e.g. an OAuth bearer token
|
|
48
|
+
* resolved immediately before each call), step 3 fires and a brand-new SDK
|
|
49
|
+
* client is constructed per request. This is intentional — it keeps short-lived
|
|
50
|
+
* credentials out of any long-lived shared state and avoids racing concurrent
|
|
51
|
+
* requests on a mutable client. The trade-off is that connection-pool / keep-
|
|
52
|
+
* alive state inside the SDK client isn't reused across requests on the OAuth
|
|
53
|
+
* path. For the current agent-CLI workload (one LLM call per turn step) this
|
|
54
|
+
* is fine; if a future host needs high-throughput per-request auth, the
|
|
55
|
+
* obvious optimization is a small LRU keyed on `(apiKey, headers digest)`.
|
|
56
|
+
*/
|
|
57
|
+
function resolveAuthBackedClient(state, auth, build) {
|
|
58
|
+
if (state.clientFactory !== void 0) return state.clientFactory(auth ?? {});
|
|
59
|
+
if (auth === void 0 && state.cachedClient !== void 0) return state.cachedClient;
|
|
60
|
+
return build(auth);
|
|
61
|
+
}
|
|
62
|
+
//#endregion
|
|
63
|
+
export { isUnknownCapability as a, UNKNOWN_CAPABILITY as i, requireProviderApiKey as n, resolveAuthBackedClient as r, mergeRequestHeaders as t };
|