@prestyj/ai 4.2.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,1071 @@
1
+ // src/errors.ts
2
+ var GGAIError = class extends Error {
3
+ constructor(message, options) {
4
+ super(message, options);
5
+ this.name = "GGAIError";
6
+ }
7
+ };
8
+ var ProviderError = class extends GGAIError {
9
+ provider;
10
+ statusCode;
11
+ constructor(provider, message, options) {
12
+ super(`[${provider}] ${message}`, { cause: options?.cause });
13
+ this.name = "ProviderError";
14
+ this.provider = provider;
15
+ this.statusCode = options?.statusCode;
16
+ }
17
+ };
18
+
19
+ // src/providers/anthropic.ts
20
+ import Anthropic from "@anthropic-ai/sdk";
21
+
22
+ // src/utils/event-stream.ts
23
+ var EventStream = class {
24
+ queue = [];
25
+ resolve = null;
26
+ done = false;
27
+ error = null;
28
+ push(event) {
29
+ if (this.queue.length > 5e4) {
30
+ this.queue.splice(0, this.queue.length - 25e3);
31
+ }
32
+ this.queue.push(event);
33
+ this.resolve?.();
34
+ this.resolve = null;
35
+ }
36
+ close() {
37
+ this.done = true;
38
+ this.resolve?.();
39
+ this.resolve = null;
40
+ }
41
+ abort(error) {
42
+ this.error = error;
43
+ this.done = true;
44
+ this.resolve?.();
45
+ this.resolve = null;
46
+ }
47
+ async *[Symbol.asyncIterator]() {
48
+ let index = 0;
49
+ while (true) {
50
+ while (index < this.queue.length) {
51
+ yield this.queue[index++];
52
+ }
53
+ this.queue.splice(0, index);
54
+ index = 0;
55
+ if (this.error) throw this.error;
56
+ if (this.done) return;
57
+ await new Promise((r) => {
58
+ this.resolve = r;
59
+ });
60
+ }
61
+ }
62
+ };
63
+ var StreamResult = class {
64
+ events;
65
+ response;
66
+ resolveResponse;
67
+ rejectResponse;
68
+ hasConsumer = false;
69
+ constructor() {
70
+ this.events = new EventStream();
71
+ this.response = new Promise((resolve, reject) => {
72
+ this.resolveResponse = resolve;
73
+ this.rejectResponse = reject;
74
+ });
75
+ }
76
+ push(event) {
77
+ this.events.push(event);
78
+ }
79
+ complete(response) {
80
+ this.events.close();
81
+ this.resolveResponse(response);
82
+ }
83
+ abort(error) {
84
+ this.events.abort(error);
85
+ this.rejectResponse(error);
86
+ }
87
+ [Symbol.asyncIterator]() {
88
+ this.hasConsumer = true;
89
+ return this.events[Symbol.asyncIterator]();
90
+ }
91
+ then(onfulfilled, onrejected) {
92
+ this.drainEvents().catch(() => {
93
+ });
94
+ return this.response.then(onfulfilled, onrejected);
95
+ }
96
+ async drainEvents() {
97
+ if (this.hasConsumer) return;
98
+ this.hasConsumer = true;
99
+ for await (const _ of this.events) {
100
+ }
101
+ }
102
+ };
103
+
104
+ // src/utils/zod-to-json-schema.ts
105
+ import { z } from "zod";
106
+ function zodToJsonSchema(schema) {
107
+ const jsonSchema = z.toJSONSchema(schema);
108
+ const { $schema: _schema, ...rest } = jsonSchema;
109
+ return rest;
110
+ }
111
+
112
+ // src/providers/transform.ts
113
+ function toAnthropicCacheControl(retention, baseUrl) {
114
+ const resolved = retention ?? "short";
115
+ if (resolved === "none") return void 0;
116
+ const ttl = resolved === "long" && (!baseUrl || baseUrl.includes("api.anthropic.com")) ? "1h" : void 0;
117
+ return { type: "ephemeral", ...ttl && { ttl } };
118
+ }
119
+ function toAnthropicMessages(messages, cacheControl) {
120
+ let systemText;
121
+ const out = [];
122
+ for (const msg of messages) {
123
+ if (msg.role === "system") {
124
+ systemText = msg.content;
125
+ continue;
126
+ }
127
+ if (msg.role === "user") {
128
+ out.push({
129
+ role: "user",
130
+ content: typeof msg.content === "string" ? msg.content : msg.content.map((part) => {
131
+ if (part.type === "text") return { type: "text", text: part.text };
132
+ return {
133
+ type: "image",
134
+ source: {
135
+ type: "base64",
136
+ media_type: part.mediaType,
137
+ data: part.data
138
+ }
139
+ };
140
+ })
141
+ });
142
+ continue;
143
+ }
144
+ if (msg.role === "assistant") {
145
+ const content = typeof msg.content === "string" ? msg.content : msg.content.filter((part) => {
146
+ if (part.type === "thinking" && !part.signature) return false;
147
+ return true;
148
+ }).map((part) => {
149
+ if (part.type === "text") return { type: "text", text: part.text };
150
+ if (part.type === "thinking")
151
+ return { type: "thinking", thinking: part.text, signature: part.signature };
152
+ if (part.type === "tool_call")
153
+ return {
154
+ type: "tool_use",
155
+ id: part.id,
156
+ name: part.name,
157
+ input: part.args
158
+ };
159
+ if (part.type === "server_tool_call")
160
+ return {
161
+ type: "server_tool_use",
162
+ id: part.id,
163
+ name: part.name,
164
+ input: part.input
165
+ };
166
+ if (part.type === "server_tool_result")
167
+ return part.data;
168
+ if (part.type === "raw") return part.data;
169
+ return { type: "text", text: "" };
170
+ });
171
+ out.push({ role: "assistant", content });
172
+ continue;
173
+ }
174
+ if (msg.role === "tool") {
175
+ out.push({
176
+ role: "user",
177
+ content: msg.content.map((result) => ({
178
+ type: "tool_result",
179
+ tool_use_id: result.toolCallId,
180
+ content: result.content,
181
+ is_error: result.isError
182
+ }))
183
+ });
184
+ }
185
+ }
186
+ if (cacheControl && out.length > 0) {
187
+ for (let i = out.length - 1; i >= 0; i--) {
188
+ if (out[i].role === "user") {
189
+ const content = out[i].content;
190
+ if (typeof content === "string") {
191
+ out[i] = {
192
+ role: "user",
193
+ content: [
194
+ {
195
+ type: "text",
196
+ text: content,
197
+ cache_control: cacheControl
198
+ }
199
+ ]
200
+ };
201
+ } else if (Array.isArray(content) && content.length > 0) {
202
+ const last = content[content.length - 1];
203
+ content[content.length - 1] = {
204
+ ...last,
205
+ cache_control: cacheControl
206
+ };
207
+ }
208
+ break;
209
+ }
210
+ }
211
+ }
212
+ let system;
213
+ if (systemText) {
214
+ const marker = "<!-- uncached -->";
215
+ const markerIdx = systemText.indexOf(marker);
216
+ if (markerIdx !== -1 && cacheControl) {
217
+ const cachedPart = systemText.slice(0, markerIdx).trimEnd();
218
+ const uncachedPart = systemText.slice(markerIdx + marker.length).trimStart();
219
+ system = [
220
+ { type: "text", text: cachedPart, cache_control: cacheControl },
221
+ ...uncachedPart ? [{ type: "text", text: uncachedPart }] : []
222
+ ];
223
+ } else {
224
+ system = [
225
+ {
226
+ type: "text",
227
+ text: systemText,
228
+ ...cacheControl && { cache_control: cacheControl }
229
+ }
230
+ ];
231
+ }
232
+ }
233
+ return { system, messages: out };
234
+ }
235
+ function toAnthropicTools(tools) {
236
+ return tools.map((tool) => ({
237
+ name: tool.name,
238
+ description: tool.description,
239
+ input_schema: tool.rawInputSchema ?? zodToJsonSchema(tool.parameters)
240
+ }));
241
+ }
242
+ function toAnthropicToolChoice(choice) {
243
+ if (choice === "auto") return { type: "auto" };
244
+ if (choice === "none") return { type: "none" };
245
+ if (choice === "required") return { type: "any" };
246
+ return { type: "tool", name: choice.name };
247
+ }
248
+ function supportsAdaptiveThinking(model) {
249
+ return /opus-4-6|sonnet-4-6/.test(model);
250
+ }
251
+ function toAnthropicThinking(level, maxTokens, model) {
252
+ if (supportsAdaptiveThinking(model)) {
253
+ let effort = level;
254
+ if (level === "max" && !model.includes("opus")) {
255
+ effort = "high";
256
+ }
257
+ return {
258
+ thinking: { type: "adaptive" },
259
+ maxTokens,
260
+ outputConfig: { effort }
261
+ };
262
+ }
263
+ const effectiveLevel = level === "max" ? "high" : level;
264
+ const budgetMap = {
265
+ low: Math.max(1024, Math.floor(maxTokens * 0.25)),
266
+ medium: Math.max(2048, Math.floor(maxTokens * 0.5)),
267
+ high: Math.max(4096, maxTokens)
268
+ };
269
+ const budget = budgetMap[effectiveLevel];
270
+ return {
271
+ thinking: { type: "enabled", budget_tokens: budget },
272
+ maxTokens: maxTokens + budget
273
+ };
274
+ }
275
+ function remapToolCallId(id, idMap) {
276
+ if (id.startsWith("call_")) return id;
277
+ const existing = idMap.get(id);
278
+ if (existing) return existing;
279
+ const mapped = `call_${id.replace(/^toolu_/, "")}`;
280
+ idMap.set(id, mapped);
281
+ return mapped;
282
+ }
283
+ function toOpenAIMessages(messages) {
284
+ const out = [];
285
+ const idMap = /* @__PURE__ */ new Map();
286
+ for (const msg of messages) {
287
+ if (msg.role === "system") {
288
+ out.push({ role: "system", content: msg.content });
289
+ continue;
290
+ }
291
+ if (msg.role === "user") {
292
+ if (typeof msg.content === "string") {
293
+ out.push({ role: "user", content: msg.content });
294
+ } else {
295
+ out.push({
296
+ role: "user",
297
+ content: msg.content.map(
298
+ (part) => {
299
+ if (part.type === "text") return { type: "text", text: part.text };
300
+ return {
301
+ type: "image_url",
302
+ image_url: {
303
+ url: `data:${part.mediaType};base64,${part.data}`
304
+ }
305
+ };
306
+ }
307
+ )
308
+ });
309
+ }
310
+ continue;
311
+ }
312
+ if (msg.role === "assistant") {
313
+ const parts = typeof msg.content === "string" ? msg.content : void 0;
314
+ const toolCalls = typeof msg.content !== "string" ? msg.content.filter(
315
+ (p) => p.type === "tool_call"
316
+ ).map(
317
+ (tc) => ({
318
+ id: remapToolCallId(tc.id, idMap),
319
+ type: "function",
320
+ function: { name: tc.name, arguments: JSON.stringify(tc.args) }
321
+ })
322
+ ) : void 0;
323
+ const textParts = typeof msg.content !== "string" ? msg.content.filter((p) => p.type === "text").map((p) => p.text).join("") : void 0;
324
+ const thinkingParts = typeof msg.content !== "string" ? msg.content.filter((p) => p.type === "thinking").map((p) => p.text).join("") : void 0;
325
+ const assistantMsg = {
326
+ role: "assistant",
327
+ content: parts || textParts || null,
328
+ ...toolCalls?.length ? { tool_calls: toolCalls } : {}
329
+ };
330
+ if (thinkingParts || toolCalls?.length) {
331
+ assistantMsg.reasoning_content = thinkingParts || " ";
332
+ }
333
+ out.push(assistantMsg);
334
+ continue;
335
+ }
336
+ if (msg.role === "tool") {
337
+ for (const result of msg.content) {
338
+ out.push({
339
+ role: "tool",
340
+ tool_call_id: remapToolCallId(result.toolCallId, idMap),
341
+ content: result.content
342
+ });
343
+ }
344
+ }
345
+ }
346
+ return out;
347
+ }
348
+ function toOpenAITools(tools) {
349
+ return tools.map((tool) => ({
350
+ type: "function",
351
+ function: {
352
+ name: tool.name,
353
+ description: tool.description,
354
+ parameters: tool.rawInputSchema ?? zodToJsonSchema(tool.parameters)
355
+ }
356
+ }));
357
+ }
358
+ function toOpenAIToolChoice(choice) {
359
+ if (choice === "auto") return "auto";
360
+ if (choice === "none") return "none";
361
+ if (choice === "required") return "required";
362
+ return { type: "function", function: { name: choice.name } };
363
+ }
364
+ function toOpenAIReasoningEffort(level) {
365
+ return level === "max" ? "high" : level;
366
+ }
367
+ function normalizeAnthropicStopReason(reason) {
368
+ switch (reason) {
369
+ case "tool_use":
370
+ return "tool_use";
371
+ case "max_tokens":
372
+ return "max_tokens";
373
+ case "pause_turn":
374
+ return "pause_turn";
375
+ case "stop_sequence":
376
+ return "stop_sequence";
377
+ case "refusal":
378
+ return "refusal";
379
+ default:
380
+ return "end_turn";
381
+ }
382
+ }
383
+ function normalizeOpenAIStopReason(reason) {
384
+ switch (reason) {
385
+ case "tool_calls":
386
+ return "tool_use";
387
+ case "length":
388
+ return "max_tokens";
389
+ case "stop":
390
+ return "stop_sequence";
391
+ default:
392
+ return "end_turn";
393
+ }
394
+ }
395
+
396
+ // src/providers/anthropic.ts
397
+ function streamAnthropic(options) {
398
+ const result = new StreamResult();
399
+ runStream(options, result).catch((err) => result.abort(toError(err)));
400
+ return result;
401
+ }
402
+ async function runStream(options, result) {
403
+ const isOAuth = options.apiKey?.startsWith("sk-ant-oat");
404
+ const client = new Anthropic({
405
+ ...isOAuth ? { apiKey: null, authToken: options.apiKey } : { apiKey: options.apiKey },
406
+ ...options.baseUrl ? { baseURL: options.baseUrl } : {}
407
+ });
408
+ const cacheControl = toAnthropicCacheControl(options.cacheRetention, options.baseUrl);
409
+ const { system, messages } = toAnthropicMessages(options.messages, cacheControl);
410
+ let maxTokens = options.maxTokens ?? 4096;
411
+ let thinking;
412
+ let outputConfig;
413
+ if (options.thinking) {
414
+ const t = toAnthropicThinking(options.thinking, maxTokens, options.model);
415
+ thinking = t.thinking;
416
+ maxTokens = t.maxTokens;
417
+ if (t.outputConfig) {
418
+ outputConfig = t.outputConfig;
419
+ }
420
+ }
421
+ const params = {
422
+ model: options.model,
423
+ max_tokens: maxTokens,
424
+ messages,
425
+ ...system ? { system } : {},
426
+ ...thinking ? { thinking } : {},
427
+ ...outputConfig ? { output_config: outputConfig } : {},
428
+ ...options.temperature != null && !thinking ? { temperature: options.temperature } : {},
429
+ ...options.topP != null ? { top_p: options.topP } : {},
430
+ ...options.stop ? { stop_sequences: options.stop } : {},
431
+ ...options.tools?.length || options.serverTools?.length || options.webSearch ? {
432
+ tools: [
433
+ ...options.tools?.length ? toAnthropicTools(options.tools) : [],
434
+ ...options.serverTools ?? [],
435
+ ...options.webSearch ? [{ type: "web_search_20250305", name: "web_search" }] : []
436
+ ]
437
+ } : {},
438
+ ...options.toolChoice && options.tools?.length ? { tool_choice: toAnthropicToolChoice(options.toolChoice) } : {},
439
+ ...options.compaction ? { context_management: { edits: [{ type: "compact_20260112" }] } } : {},
440
+ stream: true
441
+ };
442
+ const betaHeaders = [
443
+ ...isOAuth ? ["oauth-2025-04-20"] : [],
444
+ ...options.compaction ? ["compact-2026-01-12"] : []
445
+ ];
446
+ const stream2 = client.messages.stream(params, {
447
+ signal: options.signal ?? void 0,
448
+ ...betaHeaders.length ? { headers: { "anthropic-beta": betaHeaders.join(",") } } : {}
449
+ });
450
+ const contentParts = [];
451
+ let currentToolId = "";
452
+ let currentToolName = "";
453
+ stream2.on("text", (text) => {
454
+ result.push({ type: "text_delta", text });
455
+ });
456
+ stream2.on("thinking", (thinkingDelta) => {
457
+ result.push({ type: "thinking_delta", text: thinkingDelta });
458
+ });
459
+ stream2.on("streamEvent", (event) => {
460
+ if (event.type === "content_block_start") {
461
+ if (event.content_block.type === "tool_use") {
462
+ currentToolId = event.content_block.id;
463
+ currentToolName = event.content_block.name;
464
+ }
465
+ if (event.content_block.type === "server_tool_use") {
466
+ currentToolId = event.content_block.id;
467
+ currentToolName = event.content_block.name;
468
+ }
469
+ }
470
+ });
471
+ stream2.on("inputJson", (delta) => {
472
+ result.push({
473
+ type: "toolcall_delta",
474
+ id: currentToolId,
475
+ name: currentToolName,
476
+ argsJson: delta
477
+ });
478
+ });
479
+ stream2.on("contentBlock", (block) => {
480
+ if (block.type === "text") {
481
+ contentParts.push({ type: "text", text: block.text });
482
+ } else if (block.type === "thinking") {
483
+ contentParts.push({ type: "thinking", text: block.thinking, signature: block.signature });
484
+ } else if (block.type === "tool_use") {
485
+ const tc = {
486
+ type: "tool_call",
487
+ id: block.id,
488
+ name: block.name,
489
+ args: block.input
490
+ };
491
+ contentParts.push(tc);
492
+ result.push({
493
+ type: "toolcall_done",
494
+ id: tc.id,
495
+ name: tc.name,
496
+ args: tc.args
497
+ });
498
+ } else if (block.type === "server_tool_use") {
499
+ const stc = {
500
+ type: "server_tool_call",
501
+ id: block.id,
502
+ name: block.name,
503
+ input: block.input
504
+ };
505
+ contentParts.push(stc);
506
+ result.push({
507
+ type: "server_toolcall",
508
+ id: stc.id,
509
+ name: stc.name,
510
+ input: stc.input
511
+ });
512
+ } else {
513
+ const raw = block;
514
+ const blockType = raw.type;
515
+ if (blockType === "web_search_tool_result") {
516
+ const str = {
517
+ type: "server_tool_result",
518
+ toolUseId: raw.tool_use_id,
519
+ resultType: blockType,
520
+ data: raw
521
+ };
522
+ contentParts.push(str);
523
+ result.push({
524
+ type: "server_toolresult",
525
+ toolUseId: str.toolUseId,
526
+ resultType: str.resultType,
527
+ data: str.data
528
+ });
529
+ } else {
530
+ contentParts.push({ type: "raw", data: raw });
531
+ }
532
+ }
533
+ });
534
+ try {
535
+ const finalMessage = await stream2.finalMessage();
536
+ const stopReason = normalizeAnthropicStopReason(finalMessage.stop_reason);
537
+ const response = {
538
+ message: {
539
+ role: "assistant",
540
+ content: contentParts.length > 0 ? contentParts : ""
541
+ },
542
+ stopReason,
543
+ usage: {
544
+ inputTokens: finalMessage.usage.input_tokens,
545
+ outputTokens: finalMessage.usage.output_tokens,
546
+ ...finalMessage.usage.cache_read_input_tokens != null && {
547
+ cacheRead: finalMessage.usage.cache_read_input_tokens
548
+ },
549
+ ...finalMessage.usage.cache_creation_input_tokens != null && {
550
+ cacheWrite: finalMessage.usage.cache_creation_input_tokens
551
+ }
552
+ }
553
+ };
554
+ result.push({ type: "done", stopReason });
555
+ result.complete(response);
556
+ } catch (err) {
557
+ const error = toError(err);
558
+ result.push({ type: "error", error });
559
+ result.abort(error);
560
+ }
561
+ }
562
+ function toError(err) {
563
+ if (err instanceof Anthropic.APIError) {
564
+ return new ProviderError("anthropic", err.message, {
565
+ statusCode: err.status,
566
+ cause: err
567
+ });
568
+ }
569
+ if (err instanceof Error) {
570
+ return new ProviderError("anthropic", err.message, { cause: err });
571
+ }
572
+ return new ProviderError("anthropic", String(err));
573
+ }
574
+
575
+ // src/providers/openai.ts
576
+ import OpenAI from "openai";
577
+ function streamOpenAI(options) {
578
+ const result = new StreamResult();
579
+ const providerName = options.provider ?? "openai";
580
+ runStream2(options, result).catch((err) => result.abort(toError2(err, providerName)));
581
+ return result;
582
+ }
583
+ async function runStream2(options, result) {
584
+ const client = new OpenAI({
585
+ apiKey: options.apiKey,
586
+ ...options.baseUrl ? { baseURL: options.baseUrl } : {}
587
+ });
588
+ const usesThinkingParam = options.provider === "glm" || options.provider === "moonshot";
589
+ const messages = toOpenAIMessages(options.messages);
590
+ const params = {
591
+ model: options.model,
592
+ messages,
593
+ stream: true,
594
+ ...options.maxTokens ? { max_tokens: options.maxTokens } : {},
595
+ ...options.temperature != null && !options.thinking ? { temperature: options.temperature } : {},
596
+ ...options.topP != null ? { top_p: options.topP } : {},
597
+ ...options.stop ? { stop: options.stop } : {},
598
+ ...options.thinking && !usesThinkingParam ? { reasoning_effort: toOpenAIReasoningEffort(options.thinking) } : {},
599
+ ...options.tools?.length ? { tools: toOpenAITools(options.tools) } : {},
600
+ ...options.toolChoice && options.tools?.length ? { tool_choice: toOpenAIToolChoice(options.toolChoice) } : {},
601
+ stream_options: { include_usage: true }
602
+ };
603
+ if (options.webSearch) {
604
+ if (options.provider === "moonshot") {
605
+ const raw = params;
606
+ const tools = (raw.tools ?? []).slice();
607
+ tools.push({ type: "builtin_function", function: { name: "$web_search" } });
608
+ raw.tools = tools;
609
+ }
610
+ }
611
+ if (usesThinkingParam) {
612
+ params.thinking = options.thinking ? { type: "enabled" } : { type: "disabled" };
613
+ }
614
+ const stream2 = await client.chat.completions.create(params, {
615
+ signal: options.signal ?? void 0
616
+ });
617
+ const contentParts = [];
618
+ const toolCallAccum = /* @__PURE__ */ new Map();
619
+ let textAccum = "";
620
+ let thinkingAccum = "";
621
+ let inputTokens = 0;
622
+ let outputTokens = 0;
623
+ let cacheRead = 0;
624
+ let finishReason = null;
625
+ for await (const chunk of stream2) {
626
+ const choice = chunk.choices?.[0];
627
+ if (chunk.usage) {
628
+ inputTokens = chunk.usage.prompt_tokens;
629
+ outputTokens = chunk.usage.completion_tokens;
630
+ const details = chunk.usage.prompt_tokens_details;
631
+ if (details?.cached_tokens) {
632
+ cacheRead = details.cached_tokens;
633
+ }
634
+ }
635
+ if (!choice) continue;
636
+ if (choice.finish_reason) {
637
+ finishReason = choice.finish_reason;
638
+ }
639
+ const delta = choice.delta;
640
+ const reasoningContent = delta.reasoning_content;
641
+ if (typeof reasoningContent === "string" && reasoningContent) {
642
+ thinkingAccum += reasoningContent;
643
+ result.push({ type: "thinking_delta", text: reasoningContent });
644
+ }
645
+ if (delta.content) {
646
+ textAccum += delta.content;
647
+ result.push({ type: "text_delta", text: delta.content });
648
+ }
649
+ if (delta.tool_calls) {
650
+ for (const tc of delta.tool_calls) {
651
+ let accum = toolCallAccum.get(tc.index);
652
+ if (!accum) {
653
+ accum = {
654
+ id: tc.id ?? "",
655
+ name: tc.function?.name ?? "",
656
+ argsJson: ""
657
+ };
658
+ toolCallAccum.set(tc.index, accum);
659
+ }
660
+ if (tc.id) accum.id = tc.id;
661
+ if (tc.function?.name) accum.name = tc.function.name;
662
+ if (tc.function?.arguments) {
663
+ accum.argsJson += tc.function.arguments;
664
+ result.push({
665
+ type: "toolcall_delta",
666
+ id: accum.id,
667
+ name: accum.name,
668
+ argsJson: tc.function.arguments
669
+ });
670
+ }
671
+ }
672
+ }
673
+ }
674
+ if (thinkingAccum) {
675
+ contentParts.push({ type: "thinking", text: thinkingAccum });
676
+ }
677
+ if (textAccum) {
678
+ contentParts.push({ type: "text", text: textAccum });
679
+ }
680
+ for (const [, tc] of toolCallAccum) {
681
+ let args = {};
682
+ try {
683
+ args = JSON.parse(tc.argsJson);
684
+ } catch {
685
+ }
686
+ const toolCall = {
687
+ type: "tool_call",
688
+ id: tc.id,
689
+ name: tc.name,
690
+ args
691
+ };
692
+ contentParts.push(toolCall);
693
+ result.push({
694
+ type: "toolcall_done",
695
+ id: tc.id,
696
+ name: tc.name,
697
+ args
698
+ });
699
+ }
700
+ const stopReason = normalizeOpenAIStopReason(finishReason);
701
+ const response = {
702
+ message: {
703
+ role: "assistant",
704
+ content: contentParts.length > 0 ? contentParts : textAccum || ""
705
+ },
706
+ stopReason,
707
+ usage: { inputTokens, outputTokens, ...cacheRead > 0 && { cacheRead } }
708
+ };
709
+ result.push({ type: "done", stopReason });
710
+ result.complete(response);
711
+ }
712
+ function toError2(err, provider) {
713
+ if (err instanceof OpenAI.APIError) {
714
+ let msg = err.message;
715
+ const body = err.error;
716
+ if (body) {
717
+ msg += ` | body: ${JSON.stringify(body)}`;
718
+ }
719
+ return new ProviderError(provider, msg, {
720
+ statusCode: err.status,
721
+ cause: err
722
+ });
723
+ }
724
+ if (err instanceof Error) {
725
+ return new ProviderError(provider, err.message, { cause: err });
726
+ }
727
+ return new ProviderError(provider, String(err));
728
+ }
729
+
730
+ // src/providers/openai-codex.ts
731
+ import os from "os";
732
+ var DEFAULT_BASE_URL = "https://chatgpt.com/backend-api";
733
+ function streamOpenAICodex(options) {
734
+ const result = new StreamResult();
735
+ runStream3(options, result).catch((err) => result.abort(toError3(err)));
736
+ return result;
737
+ }
738
+ async function runStream3(options, result) {
739
+ const baseUrl = (options.baseUrl || DEFAULT_BASE_URL).replace(/\/+$/, "");
740
+ const url = `${baseUrl}/codex/responses`;
741
+ const { system, input } = toCodexInput(options.messages);
742
+ const body = {
743
+ model: options.model,
744
+ store: false,
745
+ stream: true,
746
+ instructions: system,
747
+ input,
748
+ tool_choice: "auto",
749
+ parallel_tool_calls: true,
750
+ include: ["reasoning.encrypted_content"]
751
+ };
752
+ if (options.tools?.length) {
753
+ body.tools = toCodexTools(options.tools);
754
+ }
755
+ if (options.temperature != null && !options.thinking) {
756
+ body.temperature = options.temperature;
757
+ }
758
+ if (options.thinking) {
759
+ body.reasoning = {
760
+ effort: options.thinking,
761
+ summary: "auto"
762
+ };
763
+ }
764
+ const headers = {
765
+ "Content-Type": "application/json",
766
+ Accept: "text/event-stream",
767
+ Authorization: `Bearer ${options.apiKey}`,
768
+ "OpenAI-Beta": "responses=experimental",
769
+ originator: "ezcoder",
770
+ "User-Agent": `ezcoder (${os.platform()} ${os.release()}; ${os.arch()})`
771
+ };
772
+ if (options.accountId) {
773
+ headers["chatgpt-account-id"] = options.accountId;
774
+ }
775
+ const response = await fetch(url, {
776
+ method: "POST",
777
+ headers,
778
+ body: JSON.stringify(body),
779
+ signal: options.signal
780
+ });
781
+ if (!response.ok) {
782
+ const text = await response.text().catch(() => "");
783
+ let message = `Codex API error (${response.status}): ${text}`;
784
+ if (response.status === 400 && text.includes("not supported")) {
785
+ message += `
786
+
787
+ Hint: Codex models require a ChatGPT Plus ($20/mo) or Pro ($200/mo) subscription. The "codex-spark" variants require ChatGPT Pro. Ensure your account has an active subscription at https://chatgpt.com/settings`;
788
+ }
789
+ throw new ProviderError("openai", message, {
790
+ statusCode: response.status
791
+ });
792
+ }
793
+ if (!response.body) {
794
+ throw new ProviderError("openai", "No response body from Codex API");
795
+ }
796
+ const contentParts = [];
797
+ let textAccum = "";
798
+ const toolCalls = /* @__PURE__ */ new Map();
799
+ let inputTokens = 0;
800
+ let outputTokens = 0;
801
+ for await (const event of parseSSE(response.body)) {
802
+ const type = event.type;
803
+ if (!type) continue;
804
+ if (type === "error") {
805
+ const msg = event.message || JSON.stringify(event);
806
+ throw new ProviderError("openai", `Codex error: ${msg}`);
807
+ }
808
+ if (type === "response.failed") {
809
+ const msg = event.error?.message || "Codex response failed";
810
+ throw new ProviderError("openai", msg);
811
+ }
812
+ if (type === "response.output_text.delta") {
813
+ const delta = event.delta;
814
+ textAccum += delta;
815
+ result.push({ type: "text_delta", text: delta });
816
+ }
817
+ if (type === "response.reasoning_summary_text.delta") {
818
+ const delta = event.delta;
819
+ result.push({ type: "thinking_delta", text: delta });
820
+ }
821
+ if (type === "response.output_item.added") {
822
+ const item = event.item;
823
+ if (item?.type === "function_call") {
824
+ const callId = item.call_id;
825
+ const itemId = item.id;
826
+ const id = `${callId}|${itemId}`;
827
+ const name = item.name;
828
+ toolCalls.set(id, { id, name, argsJson: item.arguments || "" });
829
+ }
830
+ }
831
+ if (type === "response.function_call_arguments.delta") {
832
+ const delta = event.delta;
833
+ const itemId = event.item_id;
834
+ for (const [key, tc] of toolCalls) {
835
+ if (key.endsWith(`|${itemId}`)) {
836
+ tc.argsJson += delta;
837
+ result.push({
838
+ type: "toolcall_delta",
839
+ id: tc.id,
840
+ name: tc.name,
841
+ argsJson: delta
842
+ });
843
+ break;
844
+ }
845
+ }
846
+ }
847
+ if (type === "response.function_call_arguments.done") {
848
+ const itemId = event.item_id;
849
+ const argsStr = event.arguments;
850
+ for (const [key, tc] of toolCalls) {
851
+ if (key.endsWith(`|${itemId}`)) {
852
+ tc.argsJson = argsStr;
853
+ break;
854
+ }
855
+ }
856
+ }
857
+ if (type === "response.output_item.done") {
858
+ const item = event.item;
859
+ if (item?.type === "function_call") {
860
+ const callId = item.call_id;
861
+ const itemId = item.id;
862
+ const id = `${callId}|${itemId}`;
863
+ const tc = toolCalls.get(id);
864
+ if (tc) {
865
+ let args = {};
866
+ try {
867
+ args = JSON.parse(tc.argsJson);
868
+ } catch {
869
+ }
870
+ result.push({
871
+ type: "toolcall_done",
872
+ id: tc.id,
873
+ name: tc.name,
874
+ args
875
+ });
876
+ }
877
+ }
878
+ }
879
+ if (type === "response.completed" || type === "response.done") {
880
+ const resp = event.response;
881
+ const usage = resp?.usage;
882
+ if (usage) {
883
+ inputTokens = usage.input_tokens ?? 0;
884
+ outputTokens = usage.output_tokens ?? 0;
885
+ }
886
+ }
887
+ }
888
+ if (textAccum) {
889
+ contentParts.push({ type: "text", text: textAccum });
890
+ }
891
+ for (const [, tc] of toolCalls) {
892
+ let args = {};
893
+ try {
894
+ args = JSON.parse(tc.argsJson);
895
+ } catch {
896
+ }
897
+ const toolCall = {
898
+ type: "tool_call",
899
+ id: tc.id,
900
+ name: tc.name,
901
+ args
902
+ };
903
+ contentParts.push(toolCall);
904
+ }
905
+ const hasToolCalls = contentParts.some((p) => p.type === "tool_call");
906
+ const stopReason = hasToolCalls ? "tool_use" : "end_turn";
907
+ const streamResponse = {
908
+ message: {
909
+ role: "assistant",
910
+ content: contentParts.length > 0 ? contentParts : textAccum || ""
911
+ },
912
+ stopReason,
913
+ usage: { inputTokens, outputTokens }
914
+ };
915
+ result.push({ type: "done", stopReason });
916
+ result.complete(streamResponse);
917
+ }
918
+ async function* parseSSE(body) {
919
+ const reader = body.getReader();
920
+ const decoder = new TextDecoder();
921
+ let buffer = "";
922
+ try {
923
+ while (true) {
924
+ const { done, value } = await reader.read();
925
+ if (done) break;
926
+ buffer += decoder.decode(value, { stream: true });
927
+ let idx = buffer.indexOf("\n\n");
928
+ while (idx !== -1) {
929
+ const chunk = buffer.slice(0, idx);
930
+ buffer = buffer.slice(idx + 2);
931
+ const dataLines = chunk.split("\n").filter((l) => l.startsWith("data:")).map((l) => l.slice(5).trim());
932
+ if (dataLines.length > 0) {
933
+ const data = dataLines.join("\n").trim();
934
+ if (data && data !== "[DONE]") {
935
+ try {
936
+ yield JSON.parse(data);
937
+ } catch {
938
+ }
939
+ }
940
+ }
941
+ idx = buffer.indexOf("\n\n");
942
+ }
943
+ }
944
+ } finally {
945
+ reader.releaseLock();
946
+ }
947
+ }
948
+ function remapCodexId(id, idMap) {
949
+ if (id.startsWith("fc_") || id.startsWith("fc-")) return id;
950
+ const existing = idMap.get(id);
951
+ if (existing) return existing;
952
+ const mapped = `fc_${id.replace(/^toolu_/, "")}`;
953
+ idMap.set(id, mapped);
954
+ return mapped;
955
+ }
956
+ function toCodexInput(messages) {
957
+ let system;
958
+ const input = [];
959
+ const idMap = /* @__PURE__ */ new Map();
960
+ for (const msg of messages) {
961
+ if (msg.role === "system") {
962
+ system = msg.content;
963
+ continue;
964
+ }
965
+ if (msg.role === "user") {
966
+ const content = typeof msg.content === "string" ? [{ type: "input_text", text: msg.content }] : msg.content.map((part) => {
967
+ if (part.type === "text") return { type: "input_text", text: part.text };
968
+ return {
969
+ type: "input_image",
970
+ detail: "auto",
971
+ image_url: `data:${part.mediaType};base64,${part.data}`
972
+ };
973
+ });
974
+ input.push({ role: "user", content });
975
+ continue;
976
+ }
977
+ if (msg.role === "assistant") {
978
+ if (typeof msg.content === "string") {
979
+ input.push({
980
+ type: "message",
981
+ role: "assistant",
982
+ content: [{ type: "output_text", text: msg.content, annotations: [] }],
983
+ status: "completed"
984
+ });
985
+ continue;
986
+ }
987
+ for (const part of msg.content) {
988
+ if (part.type === "text") {
989
+ input.push({
990
+ type: "message",
991
+ role: "assistant",
992
+ content: [{ type: "output_text", text: part.text, annotations: [] }],
993
+ status: "completed"
994
+ });
995
+ } else if (part.type === "tool_call") {
996
+ const [callId, itemId] = part.id.includes("|") ? part.id.split("|", 2) : [part.id, part.id];
997
+ input.push({
998
+ type: "function_call",
999
+ id: remapCodexId(itemId, idMap),
1000
+ call_id: remapCodexId(callId, idMap),
1001
+ name: part.name,
1002
+ arguments: JSON.stringify(part.args)
1003
+ });
1004
+ }
1005
+ }
1006
+ continue;
1007
+ }
1008
+ if (msg.role === "tool") {
1009
+ for (const result of msg.content) {
1010
+ const [callId] = result.toolCallId.includes("|") ? result.toolCallId.split("|", 2) : [result.toolCallId];
1011
+ input.push({
1012
+ type: "function_call_output",
1013
+ call_id: remapCodexId(callId, idMap),
1014
+ output: result.content
1015
+ });
1016
+ }
1017
+ }
1018
+ }
1019
+ return { system, input };
1020
+ }
1021
+ function toCodexTools(tools) {
1022
+ return tools.map((tool) => ({
1023
+ type: "function",
1024
+ name: tool.name,
1025
+ description: tool.description,
1026
+ parameters: tool.rawInputSchema ?? zodToJsonSchema(tool.parameters),
1027
+ strict: null
1028
+ }));
1029
+ }
1030
+ function toError3(err) {
1031
+ if (err instanceof ProviderError) return err;
1032
+ if (err instanceof Error) {
1033
+ return new ProviderError("openai", err.message, { cause: err });
1034
+ }
1035
+ return new ProviderError("openai", String(err));
1036
+ }
1037
+
1038
+ // src/stream.ts
1039
+ function stream(options) {
1040
+ switch (options.provider) {
1041
+ case "anthropic":
1042
+ return streamAnthropic(options);
1043
+ case "openai":
1044
+ if (options.accountId) {
1045
+ return streamOpenAICodex(options);
1046
+ }
1047
+ return streamOpenAI(options);
1048
+ case "glm":
1049
+ return streamOpenAI({
1050
+ ...options,
1051
+ baseUrl: options.baseUrl ?? "https://api.z.ai/api/coding/paas/v4"
1052
+ });
1053
+ case "moonshot":
1054
+ return streamOpenAI({
1055
+ ...options,
1056
+ baseUrl: options.baseUrl ?? "https://api.moonshot.ai/v1"
1057
+ });
1058
+ default:
1059
+ throw new GGAIError(
1060
+ `Unknown provider: ${options.provider}. Supported: "anthropic", "openai", "glm", "moonshot"`
1061
+ );
1062
+ }
1063
+ }
1064
+ export {
1065
+ EventStream,
1066
+ GGAIError,
1067
+ ProviderError,
1068
+ StreamResult,
1069
+ stream
1070
+ };
1071
+ //# sourceMappingURL=index.js.map