@antseed/node 0.2.21 → 0.2.23

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.
@@ -0,0 +1,891 @@
1
+ const ANTHROPIC_PROVIDER_NAMES = new Set(['anthropic', 'claude-code', 'claude-oauth']);
2
+ const OPENAI_CHAT_PROVIDER_NAMES = new Set(['openai', 'local-llm']);
3
+ function parseJsonObject(body) {
4
+ try {
5
+ const parsed = JSON.parse(new TextDecoder().decode(body));
6
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
7
+ return null;
8
+ }
9
+ return parsed;
10
+ }
11
+ catch {
12
+ return null;
13
+ }
14
+ }
15
+ function encodeJson(value) {
16
+ return new TextEncoder().encode(JSON.stringify(value));
17
+ }
18
+ function toStringContent(value) {
19
+ if (typeof value === 'string') {
20
+ return value;
21
+ }
22
+ if (Array.isArray(value)) {
23
+ return value
24
+ .map((entry) => {
25
+ if (!entry || typeof entry !== 'object') {
26
+ return '';
27
+ }
28
+ const block = entry;
29
+ if ((block.type === 'text' || block.type === 'input_text') && typeof block.text === 'string') {
30
+ return block.text;
31
+ }
32
+ if (block.type === 'output_text' && typeof block.text === 'string') {
33
+ return block.text;
34
+ }
35
+ if (block.type === 'refusal' && typeof block.refusal === 'string') {
36
+ return block.refusal;
37
+ }
38
+ if (block.type === 'tool_result') {
39
+ return toStringContent(block.content);
40
+ }
41
+ return '';
42
+ })
43
+ .filter((entry) => entry.length > 0)
44
+ .join('\n');
45
+ }
46
+ if (value === null || value === undefined) {
47
+ return '';
48
+ }
49
+ // Handle a single content block object (e.g. {type:'text', text:'...'})
50
+ if (typeof value === 'object') {
51
+ const block = value;
52
+ if ((block.type === 'text' || block.type === 'input_text') && typeof block.text === 'string') {
53
+ return block.text;
54
+ }
55
+ if (block.type === 'output_text' && typeof block.text === 'string') {
56
+ return block.text;
57
+ }
58
+ if (block.type === 'refusal' && typeof block.refusal === 'string') {
59
+ return block.refusal;
60
+ }
61
+ if (block.type === 'tool_result') {
62
+ return toStringContent(block.content);
63
+ }
64
+ return '';
65
+ }
66
+ return String(value);
67
+ }
68
+ function parseJsonSafe(raw) {
69
+ try {
70
+ return JSON.parse(raw);
71
+ }
72
+ catch {
73
+ return raw;
74
+ }
75
+ }
76
+ function mapFinishReasonToAnthropicStopReason(value) {
77
+ if (typeof value !== 'string' || value.length === 0) {
78
+ return null;
79
+ }
80
+ if (value === 'stop')
81
+ return 'end_turn';
82
+ if (value === 'length')
83
+ return 'max_tokens';
84
+ if (value === 'tool_calls' || value === 'function_call')
85
+ return 'tool_use';
86
+ return value;
87
+ }
88
+ function toNonNegativeInt(value) {
89
+ const parsed = Number(value);
90
+ if (!Number.isFinite(parsed) || parsed <= 0) {
91
+ return 0;
92
+ }
93
+ return Math.floor(parsed);
94
+ }
95
+ function convertAnthropicMessagesToOpenAI(body) {
96
+ const out = [];
97
+ if (body.system !== undefined) {
98
+ const systemText = toStringContent(body.system);
99
+ if (systemText.length > 0) {
100
+ out.push({ role: 'system', content: systemText });
101
+ }
102
+ }
103
+ const messagesRaw = body.messages;
104
+ if (!Array.isArray(messagesRaw)) {
105
+ return out;
106
+ }
107
+ for (const messageRaw of messagesRaw) {
108
+ if (!messageRaw || typeof messageRaw !== 'object') {
109
+ continue;
110
+ }
111
+ const message = messageRaw;
112
+ const role = typeof message.role === 'string' ? message.role : 'user';
113
+ const content = message.content;
114
+ if (role === 'assistant' && Array.isArray(content)) {
115
+ const textParts = [];
116
+ const toolCalls = [];
117
+ for (const blockRaw of content) {
118
+ if (!blockRaw || typeof blockRaw !== 'object') {
119
+ continue;
120
+ }
121
+ const block = blockRaw;
122
+ const blockType = typeof block.type === 'string' ? block.type : '';
123
+ if (blockType === 'tool_use') {
124
+ const callName = typeof block.name === 'string' && block.name.length > 0 ? block.name : 'tool';
125
+ const callId = typeof block.id === 'string' && block.id.length > 0
126
+ ? block.id
127
+ : `call_${toolCalls.length + 1}`;
128
+ const input = block.input && typeof block.input === 'object' ? block.input : {};
129
+ toolCalls.push({
130
+ id: callId,
131
+ type: 'function',
132
+ function: {
133
+ name: callName,
134
+ arguments: JSON.stringify(input),
135
+ },
136
+ });
137
+ continue;
138
+ }
139
+ const text = toStringContent(block);
140
+ if (text.length > 0) {
141
+ textParts.push(text);
142
+ }
143
+ }
144
+ const assistantMessage = {
145
+ role: 'assistant',
146
+ content: textParts.join('\n'),
147
+ };
148
+ if (toolCalls.length > 0) {
149
+ assistantMessage.tool_calls = toolCalls;
150
+ }
151
+ out.push(assistantMessage);
152
+ continue;
153
+ }
154
+ if (role === 'user' && Array.isArray(content)) {
155
+ const textParts = [];
156
+ const toolResults = [];
157
+ for (const blockRaw of content) {
158
+ if (!blockRaw || typeof blockRaw !== 'object') {
159
+ continue;
160
+ }
161
+ const block = blockRaw;
162
+ const blockType = typeof block.type === 'string' ? block.type : '';
163
+ if (blockType === 'tool_result') {
164
+ const toolCallId = typeof block.tool_use_id === 'string' ? block.tool_use_id : '';
165
+ if (toolCallId.length > 0) {
166
+ toolResults.push({
167
+ role: 'tool',
168
+ tool_call_id: toolCallId,
169
+ content: toStringContent(block.content),
170
+ });
171
+ continue;
172
+ }
173
+ }
174
+ const text = toStringContent(block);
175
+ if (text.length > 0) {
176
+ textParts.push(text);
177
+ }
178
+ }
179
+ if (textParts.length > 0) {
180
+ out.push({
181
+ role: 'user',
182
+ content: textParts.join('\n'),
183
+ });
184
+ }
185
+ out.push(...toolResults);
186
+ continue;
187
+ }
188
+ out.push({
189
+ role,
190
+ content: toStringContent(content),
191
+ });
192
+ }
193
+ return out;
194
+ }
195
+ function convertAnthropicToolsToOpenAI(toolsRaw) {
196
+ if (!Array.isArray(toolsRaw) || toolsRaw.length === 0) {
197
+ return undefined;
198
+ }
199
+ const out = [];
200
+ for (const toolRaw of toolsRaw) {
201
+ if (!toolRaw || typeof toolRaw !== 'object') {
202
+ continue;
203
+ }
204
+ const tool = toolRaw;
205
+ if (typeof tool.name !== 'string' || tool.name.length === 0) {
206
+ continue;
207
+ }
208
+ out.push({
209
+ type: 'function',
210
+ function: {
211
+ name: tool.name,
212
+ ...(typeof tool.description === 'string' && tool.description.length > 0
213
+ ? { description: tool.description }
214
+ : {}),
215
+ parameters: tool.input_schema && typeof tool.input_schema === 'object'
216
+ ? tool.input_schema
217
+ : { type: 'object', properties: {} },
218
+ },
219
+ });
220
+ }
221
+ return out.length > 0 ? out : undefined;
222
+ }
223
+ function convertAnthropicToolChoiceToOpenAI(toolChoice) {
224
+ if (typeof toolChoice === 'string') {
225
+ if (toolChoice === 'auto' || toolChoice === 'none' || toolChoice === 'required') {
226
+ return toolChoice;
227
+ }
228
+ return undefined;
229
+ }
230
+ if (!toolChoice || typeof toolChoice !== 'object') {
231
+ return undefined;
232
+ }
233
+ const choice = toolChoice;
234
+ const type = typeof choice.type === 'string' ? choice.type : '';
235
+ if (type === 'auto') {
236
+ return 'auto';
237
+ }
238
+ if (type === 'any') {
239
+ return 'required';
240
+ }
241
+ if (type === 'tool' && typeof choice.name === 'string' && choice.name.length > 0) {
242
+ return {
243
+ type: 'function',
244
+ function: {
245
+ name: choice.name,
246
+ },
247
+ };
248
+ }
249
+ return undefined;
250
+ }
251
+ function buildAnthropicStreamFromMessage(message) {
252
+ const chunks = [];
253
+ const pushEvent = (event, data) => {
254
+ chunks.push(`event: ${event}\n`);
255
+ chunks.push(`data: ${JSON.stringify(data)}\n\n`);
256
+ };
257
+ pushEvent('message_start', {
258
+ type: 'message_start',
259
+ message: {
260
+ id: message.id,
261
+ type: 'message',
262
+ role: 'assistant',
263
+ model: message.model,
264
+ content: [],
265
+ stop_reason: null,
266
+ stop_sequence: null,
267
+ usage: {
268
+ input_tokens: message.usage.inputTokens,
269
+ output_tokens: 0,
270
+ },
271
+ },
272
+ });
273
+ message.content.forEach((block, index) => {
274
+ pushEvent('content_block_start', {
275
+ type: 'content_block_start',
276
+ index,
277
+ content_block: block.type === 'text'
278
+ ? { type: 'text', text: '' }
279
+ : { type: 'tool_use', id: block.id, name: block.name, input: {} },
280
+ });
281
+ if (block.type === 'text' && block.text.length > 0) {
282
+ pushEvent('content_block_delta', {
283
+ type: 'content_block_delta',
284
+ index,
285
+ delta: {
286
+ type: 'text_delta',
287
+ text: block.text,
288
+ },
289
+ });
290
+ }
291
+ if (block.type === 'tool_use') {
292
+ pushEvent('content_block_delta', {
293
+ type: 'content_block_delta',
294
+ index,
295
+ delta: {
296
+ type: 'input_json_delta',
297
+ partial_json: JSON.stringify(block.input),
298
+ },
299
+ });
300
+ }
301
+ pushEvent('content_block_stop', {
302
+ type: 'content_block_stop',
303
+ index,
304
+ });
305
+ });
306
+ pushEvent('message_delta', {
307
+ type: 'message_delta',
308
+ delta: {
309
+ stop_reason: message.stopReason,
310
+ stop_sequence: null,
311
+ },
312
+ usage: {
313
+ output_tokens: message.usage.outputTokens,
314
+ },
315
+ });
316
+ pushEvent('message_stop', {
317
+ type: 'message_stop',
318
+ });
319
+ return new TextEncoder().encode(chunks.join(''));
320
+ }
321
+ export function detectRequestModelApiProtocol(request) {
322
+ const normalizedPath = request.path.toLowerCase();
323
+ if (normalizedPath.startsWith('/v1/messages') || normalizedPath.startsWith('/v1/complete')) {
324
+ return 'anthropic-messages';
325
+ }
326
+ if (normalizedPath.startsWith('/v1/chat/completions')) {
327
+ return 'openai-chat-completions';
328
+ }
329
+ if (normalizedPath.startsWith('/v1/completions')) {
330
+ return 'openai-completions';
331
+ }
332
+ if (normalizedPath.startsWith('/v1/responses')) {
333
+ return 'openai-responses';
334
+ }
335
+ const hasAnthropicVersionHeader = Object.keys(request.headers)
336
+ .some((key) => key.toLowerCase() === 'anthropic-version');
337
+ if (hasAnthropicVersionHeader) {
338
+ return 'anthropic-messages';
339
+ }
340
+ return null;
341
+ }
342
+ export function inferProviderDefaultModelApiProtocols(providerName) {
343
+ const normalized = providerName.trim().toLowerCase();
344
+ if (normalized.length === 0) {
345
+ return [];
346
+ }
347
+ if (ANTHROPIC_PROVIDER_NAMES.has(normalized)) {
348
+ return ['anthropic-messages'];
349
+ }
350
+ if (OPENAI_CHAT_PROVIDER_NAMES.has(normalized)) {
351
+ return ['openai-chat-completions'];
352
+ }
353
+ return [];
354
+ }
355
+ export function selectTargetProtocolForRequest(requestProtocol, supportedProtocols) {
356
+ if (!requestProtocol) {
357
+ return null;
358
+ }
359
+ if (supportedProtocols.includes(requestProtocol)) {
360
+ return { targetProtocol: requestProtocol, requiresTransform: false };
361
+ }
362
+ if (requestProtocol === 'anthropic-messages' && supportedProtocols.includes('openai-chat-completions')) {
363
+ return { targetProtocol: 'openai-chat-completions', requiresTransform: true };
364
+ }
365
+ if (requestProtocol === 'openai-responses' && supportedProtocols.includes('openai-chat-completions')) {
366
+ return { targetProtocol: 'openai-chat-completions', requiresTransform: true };
367
+ }
368
+ return null;
369
+ }
370
+ export function transformAnthropicMessagesRequestToOpenAIChat(request) {
371
+ if (!request.path.toLowerCase().startsWith('/v1/messages')) {
372
+ return null;
373
+ }
374
+ const body = parseJsonObject(request.body);
375
+ if (!body) {
376
+ return null;
377
+ }
378
+ const streamRequested = body.stream === true;
379
+ const requestedModel = typeof body.model === 'string' && body.model.trim().length > 0
380
+ ? body.model.trim()
381
+ : null;
382
+ const mappedMessages = convertAnthropicMessagesToOpenAI(body);
383
+ const mappedTools = convertAnthropicToolsToOpenAI(body.tools);
384
+ const mappedToolChoice = convertAnthropicToolChoiceToOpenAI(body.tool_choice);
385
+ const transformedBody = {
386
+ ...(requestedModel ? { model: requestedModel } : {}),
387
+ messages: mappedMessages,
388
+ stream: false,
389
+ };
390
+ if (typeof body.max_tokens === 'number') {
391
+ transformedBody.max_tokens = body.max_tokens;
392
+ }
393
+ if (typeof body.temperature === 'number') {
394
+ transformedBody.temperature = body.temperature;
395
+ }
396
+ if (typeof body.top_p === 'number') {
397
+ transformedBody.top_p = body.top_p;
398
+ }
399
+ if (Array.isArray(body.stop_sequences)) {
400
+ transformedBody.stop = body.stop_sequences;
401
+ }
402
+ if (mappedTools) {
403
+ transformedBody.tools = mappedTools;
404
+ }
405
+ if (mappedToolChoice !== undefined) {
406
+ transformedBody.tool_choice = mappedToolChoice;
407
+ }
408
+ if (body.metadata && typeof body.metadata === 'object' && !Array.isArray(body.metadata)) {
409
+ transformedBody.metadata = body.metadata;
410
+ }
411
+ if (typeof body.user === 'string') {
412
+ transformedBody.user = body.user;
413
+ }
414
+ const transformedHeaders = { ...request.headers };
415
+ for (const headerName of Object.keys(transformedHeaders)) {
416
+ const lower = headerName.toLowerCase();
417
+ if (lower === 'anthropic-version' || lower === 'anthropic-beta') {
418
+ delete transformedHeaders[headerName];
419
+ }
420
+ }
421
+ transformedHeaders['content-type'] = 'application/json';
422
+ return {
423
+ request: {
424
+ ...request,
425
+ path: '/v1/chat/completions',
426
+ headers: transformedHeaders,
427
+ body: encodeJson(transformedBody),
428
+ },
429
+ streamRequested,
430
+ requestedModel,
431
+ };
432
+ }
433
+ export function transformOpenAIChatResponseToAnthropicMessage(response, options) {
434
+ const parsed = parseJsonObject(response.body);
435
+ if (!parsed) {
436
+ return response;
437
+ }
438
+ if (response.statusCode >= 400) {
439
+ const openaiError = parsed.error && typeof parsed.error === 'object'
440
+ ? parsed.error
441
+ : null;
442
+ const message = openaiError && typeof openaiError.message === 'string'
443
+ ? openaiError.message
444
+ : 'Upstream error';
445
+ const anthropicError = {
446
+ type: 'error',
447
+ error: {
448
+ type: 'api_error',
449
+ message,
450
+ },
451
+ };
452
+ return {
453
+ ...response,
454
+ headers: {
455
+ ...response.headers,
456
+ 'content-type': options.streamRequested ? 'text/event-stream' : 'application/json',
457
+ },
458
+ body: options.streamRequested
459
+ ? new TextEncoder().encode(`event: error\ndata: ${JSON.stringify(anthropicError)}\n\n`)
460
+ : encodeJson(anthropicError),
461
+ };
462
+ }
463
+ const choices = Array.isArray(parsed.choices) ? parsed.choices : [];
464
+ const firstChoice = choices[0] && typeof choices[0] === 'object'
465
+ ? choices[0]
466
+ : null;
467
+ const message = firstChoice?.message && typeof firstChoice.message === 'object'
468
+ ? firstChoice.message
469
+ : null;
470
+ const finishReason = mapFinishReasonToAnthropicStopReason(firstChoice?.finish_reason);
471
+ const contentBlocks = [];
472
+ const textContent = toStringContent(message?.content);
473
+ if (textContent.length > 0) {
474
+ contentBlocks.push({ type: 'text', text: textContent });
475
+ }
476
+ const toolCalls = Array.isArray(message?.tool_calls) ? message.tool_calls : [];
477
+ for (const [index, toolCallRaw] of toolCalls.entries()) {
478
+ if (!toolCallRaw || typeof toolCallRaw !== 'object') {
479
+ continue;
480
+ }
481
+ const toolCall = toolCallRaw;
482
+ const functionPayload = toolCall.function && typeof toolCall.function === 'object'
483
+ ? toolCall.function
484
+ : {};
485
+ const id = typeof toolCall.id === 'string' && toolCall.id.length > 0
486
+ ? toolCall.id
487
+ : `toolu_${index + 1}`;
488
+ const name = typeof functionPayload.name === 'string' && functionPayload.name.length > 0
489
+ ? functionPayload.name
490
+ : 'tool';
491
+ const argsRaw = typeof functionPayload.arguments === 'string' ? functionPayload.arguments : '{}';
492
+ const parsedArgs = parseJsonSafe(argsRaw);
493
+ contentBlocks.push({
494
+ type: 'tool_use',
495
+ id,
496
+ name,
497
+ input: parsedArgs && typeof parsedArgs === 'object' && !Array.isArray(parsedArgs)
498
+ ? parsedArgs
499
+ : { raw: argsRaw },
500
+ });
501
+ }
502
+ const usage = parsed.usage && typeof parsed.usage === 'object'
503
+ ? parsed.usage
504
+ : {};
505
+ const inputTokens = toNonNegativeInt(usage.prompt_tokens ?? usage.input_tokens);
506
+ const outputTokens = toNonNegativeInt(usage.completion_tokens ?? usage.output_tokens);
507
+ const id = typeof parsed.id === 'string' && parsed.id.length > 0 ? parsed.id : `msg_${response.requestId}`;
508
+ const model = typeof parsed.model === 'string' && parsed.model.length > 0
509
+ ? parsed.model
510
+ : (options.fallbackModel ?? 'unknown');
511
+ const anthropicMessage = {
512
+ id,
513
+ type: 'message',
514
+ role: 'assistant',
515
+ model,
516
+ content: contentBlocks,
517
+ stop_reason: finishReason,
518
+ stop_sequence: null,
519
+ usage: {
520
+ input_tokens: inputTokens,
521
+ output_tokens: outputTokens,
522
+ },
523
+ };
524
+ if (options.streamRequested) {
525
+ return {
526
+ ...response,
527
+ headers: {
528
+ ...response.headers,
529
+ 'content-type': 'text/event-stream',
530
+ 'cache-control': 'no-cache',
531
+ },
532
+ body: buildAnthropicStreamFromMessage({
533
+ id,
534
+ model,
535
+ content: contentBlocks,
536
+ stopReason: finishReason,
537
+ usage: {
538
+ inputTokens,
539
+ outputTokens,
540
+ },
541
+ }),
542
+ };
543
+ }
544
+ return {
545
+ ...response,
546
+ headers: {
547
+ ...response.headers,
548
+ 'content-type': 'application/json',
549
+ },
550
+ body: encodeJson(anthropicMessage),
551
+ };
552
+ }
553
+ function convertResponsesToolsToChatTools(tools) {
554
+ const out = [];
555
+ for (const toolRaw of tools) {
556
+ if (!toolRaw || typeof toolRaw !== 'object') {
557
+ continue;
558
+ }
559
+ const tool = toolRaw;
560
+ if (typeof tool.name !== 'string' || tool.name.length === 0) {
561
+ continue;
562
+ }
563
+ out.push({
564
+ type: 'function',
565
+ function: {
566
+ name: tool.name,
567
+ ...(typeof tool.description === 'string' ? { description: tool.description } : {}),
568
+ ...(tool.parameters && typeof tool.parameters === 'object' ? { parameters: tool.parameters } : {}),
569
+ },
570
+ });
571
+ }
572
+ return out.length > 0 ? out : undefined;
573
+ }
574
+ function convertResponsesInputToMessages(body) {
575
+ const out = [];
576
+ // "instructions" maps to a system message
577
+ if (typeof body.instructions === 'string' && body.instructions.length > 0) {
578
+ out.push({ role: 'system', content: body.instructions });
579
+ }
580
+ const input = body.input;
581
+ // Simple string input → single user message
582
+ if (typeof input === 'string') {
583
+ out.push({ role: 'user', content: input });
584
+ return out;
585
+ }
586
+ // Array of message objects
587
+ if (Array.isArray(input)) {
588
+ for (const item of input) {
589
+ if (!item || typeof item !== 'object') {
590
+ continue;
591
+ }
592
+ const msg = item;
593
+ const type = typeof msg.type === 'string' ? msg.type : '';
594
+ // function_call_output → tool role message with tool_call_id
595
+ if (type === 'function_call_output') {
596
+ out.push({
597
+ role: 'tool',
598
+ tool_call_id: typeof msg.call_id === 'string' ? msg.call_id : '',
599
+ content: typeof msg.output === 'string' ? msg.output : toStringContent(msg.output),
600
+ });
601
+ continue;
602
+ }
603
+ // function_call → assistant message with tool_calls
604
+ if (type === 'function_call') {
605
+ const chatCallId = typeof msg.call_id === 'string' && msg.call_id.length > 0
606
+ ? msg.call_id
607
+ : (typeof msg.id === 'string' ? msg.id : '');
608
+ out.push({
609
+ role: 'assistant',
610
+ content: null,
611
+ tool_calls: [{
612
+ id: chatCallId,
613
+ type: 'function',
614
+ function: {
615
+ name: typeof msg.name === 'string' ? msg.name : '',
616
+ arguments: typeof msg.arguments === 'string' ? msg.arguments : JSON.stringify(msg.arguments ?? {}),
617
+ },
618
+ }],
619
+ });
620
+ continue;
621
+ }
622
+ const role = typeof msg.role === 'string' ? msg.role : 'user';
623
+ out.push({ role, content: toStringContent(msg.content) });
624
+ }
625
+ return out;
626
+ }
627
+ return out;
628
+ }
629
+ export function transformOpenAIResponsesRequestToOpenAIChat(request) {
630
+ if (!request.path.toLowerCase().startsWith('/v1/responses')) {
631
+ return null;
632
+ }
633
+ const body = parseJsonObject(request.body);
634
+ if (!body) {
635
+ return null;
636
+ }
637
+ const streamRequested = body.stream === true;
638
+ const requestedModel = typeof body.model === 'string' && body.model.trim().length > 0
639
+ ? body.model.trim()
640
+ : null;
641
+ const messages = convertResponsesInputToMessages(body);
642
+ const transformedBody = {
643
+ ...(requestedModel ? { model: requestedModel } : {}),
644
+ messages,
645
+ stream: false,
646
+ };
647
+ if (typeof body.max_output_tokens === 'number') {
648
+ transformedBody.max_tokens = body.max_output_tokens;
649
+ }
650
+ if (typeof body.temperature === 'number') {
651
+ transformedBody.temperature = body.temperature;
652
+ }
653
+ if (typeof body.top_p === 'number') {
654
+ transformedBody.top_p = body.top_p;
655
+ }
656
+ if (Array.isArray(body.tools)) {
657
+ const chatTools = convertResponsesToolsToChatTools(body.tools);
658
+ if (chatTools) {
659
+ transformedBody.tools = chatTools;
660
+ }
661
+ }
662
+ if (body.tool_choice !== undefined) {
663
+ const tc = body.tool_choice;
664
+ if (tc && typeof tc === 'object' && !Array.isArray(tc)) {
665
+ const tcObj = tc;
666
+ if (tcObj.type === 'function' && typeof tcObj.name === 'string') {
667
+ transformedBody.tool_choice = { type: 'function', function: { name: tcObj.name } };
668
+ }
669
+ else {
670
+ transformedBody.tool_choice = tc;
671
+ }
672
+ }
673
+ else {
674
+ transformedBody.tool_choice = tc;
675
+ }
676
+ }
677
+ if (body.metadata && typeof body.metadata === 'object' && !Array.isArray(body.metadata)) {
678
+ transformedBody.metadata = body.metadata;
679
+ }
680
+ return {
681
+ request: {
682
+ ...request,
683
+ path: '/v1/chat/completions',
684
+ headers: { ...request.headers, 'content-type': 'application/json' },
685
+ body: encodeJson(transformedBody),
686
+ },
687
+ streamRequested,
688
+ requestedModel,
689
+ };
690
+ }
691
+ function buildOpenAIResponsesBody(response, parsed, options) {
692
+ const choices = Array.isArray(parsed.choices) ? parsed.choices : [];
693
+ const firstChoice = choices[0] && typeof choices[0] === 'object'
694
+ ? choices[0]
695
+ : null;
696
+ const message = firstChoice?.message && typeof firstChoice.message === 'object'
697
+ ? firstChoice.message
698
+ : null;
699
+ const outputItems = [];
700
+ const textContent = toStringContent(message?.content);
701
+ if (textContent.length > 0) {
702
+ outputItems.push({
703
+ type: 'message',
704
+ id: `${typeof parsed.id === 'string' && parsed.id.length > 0 ? parsed.id : `resp_${response.requestId}`}_msg_1`,
705
+ role: 'assistant',
706
+ status: 'completed',
707
+ content: [{
708
+ type: 'output_text',
709
+ text: textContent,
710
+ annotations: [],
711
+ }],
712
+ });
713
+ }
714
+ const toolCalls = Array.isArray(message?.tool_calls) ? message.tool_calls : [];
715
+ for (const [index, toolCallRaw] of toolCalls.entries()) {
716
+ if (!toolCallRaw || typeof toolCallRaw !== 'object') {
717
+ continue;
718
+ }
719
+ const toolCall = toolCallRaw;
720
+ const functionPayload = toolCall.function && typeof toolCall.function === 'object'
721
+ ? toolCall.function
722
+ : {};
723
+ const callId = typeof toolCall.id === 'string' && toolCall.id.length > 0
724
+ ? toolCall.id
725
+ : `call_${index + 1}`;
726
+ outputItems.push({
727
+ type: 'function_call',
728
+ id: callId,
729
+ call_id: callId,
730
+ name: typeof functionPayload.name === 'string' ? functionPayload.name : '',
731
+ arguments: typeof functionPayload.arguments === 'string' ? functionPayload.arguments : '{}',
732
+ status: 'completed',
733
+ });
734
+ }
735
+ const usage = parsed.usage && typeof parsed.usage === 'object'
736
+ ? parsed.usage
737
+ : {};
738
+ const inputTokens = toNonNegativeInt(usage.prompt_tokens ?? usage.input_tokens);
739
+ const outputTokens = toNonNegativeInt(usage.completion_tokens ?? usage.output_tokens);
740
+ const id = typeof parsed.id === 'string' && parsed.id.length > 0 ? parsed.id : `resp_${response.requestId}`;
741
+ const model = typeof parsed.model === 'string' && parsed.model.length > 0
742
+ ? parsed.model
743
+ : (options.fallbackModel ?? 'unknown');
744
+ return {
745
+ id,
746
+ object: 'response',
747
+ model,
748
+ status: 'completed',
749
+ created_at: Math.floor(Date.now() / 1000),
750
+ output: outputItems,
751
+ output_text: textContent,
752
+ usage: {
753
+ input_tokens: inputTokens,
754
+ output_tokens: outputTokens,
755
+ total_tokens: inputTokens + outputTokens,
756
+ },
757
+ };
758
+ }
759
+ function buildOpenAIResponsesStream(body) {
760
+ const sseEvents = [];
761
+ let sequenceNumber = 0;
762
+ const pushEvent = (event, data) => {
763
+ sseEvents.push(`event: ${event}\ndata: ${JSON.stringify({ type: event, sequence_number: sequenceNumber++, ...data })}\n\n`);
764
+ };
765
+ pushEvent('response.created', {
766
+ response: {
767
+ ...body,
768
+ status: 'in_progress',
769
+ output: [],
770
+ output_text: '',
771
+ },
772
+ });
773
+ for (const [outputIndex, outputItem] of body.output.entries()) {
774
+ if (outputItem.type === 'message') {
775
+ const pendingItem = {
776
+ ...outputItem,
777
+ status: 'in_progress',
778
+ content: outputItem.content.map((part) => ({ ...part, text: '' })),
779
+ };
780
+ pushEvent('response.output_item.added', {
781
+ output_index: outputIndex,
782
+ item: pendingItem,
783
+ });
784
+ for (const [contentIndex, part] of outputItem.content.entries()) {
785
+ const pendingPart = { ...part, text: '' };
786
+ pushEvent('response.content_part.added', {
787
+ output_index: outputIndex,
788
+ item_id: outputItem.id,
789
+ content_index: contentIndex,
790
+ part: pendingPart,
791
+ });
792
+ pushEvent('response.output_text.delta', {
793
+ output_index: outputIndex,
794
+ item_id: outputItem.id,
795
+ content_index: contentIndex,
796
+ delta: part.text,
797
+ logprobs: [],
798
+ });
799
+ pushEvent('response.output_text.done', {
800
+ output_index: outputIndex,
801
+ item_id: outputItem.id,
802
+ content_index: contentIndex,
803
+ text: part.text,
804
+ logprobs: [],
805
+ });
806
+ pushEvent('response.content_part.done', {
807
+ output_index: outputIndex,
808
+ item_id: outputItem.id,
809
+ content_index: contentIndex,
810
+ part,
811
+ });
812
+ }
813
+ pushEvent('response.output_item.done', {
814
+ output_index: outputIndex,
815
+ item: outputItem,
816
+ });
817
+ continue;
818
+ }
819
+ const pendingItem = {
820
+ ...outputItem,
821
+ status: 'in_progress',
822
+ arguments: '',
823
+ };
824
+ pushEvent('response.output_item.added', {
825
+ output_index: outputIndex,
826
+ item: pendingItem,
827
+ });
828
+ pushEvent('response.function_call_arguments.delta', {
829
+ output_index: outputIndex,
830
+ item_id: outputItem.id,
831
+ call_id: outputItem.call_id,
832
+ delta: outputItem.arguments,
833
+ });
834
+ pushEvent('response.function_call_arguments.done', {
835
+ output_index: outputIndex,
836
+ item_id: outputItem.id,
837
+ call_id: outputItem.call_id,
838
+ name: outputItem.name,
839
+ arguments: outputItem.arguments,
840
+ });
841
+ pushEvent('response.output_item.done', {
842
+ output_index: outputIndex,
843
+ item: outputItem,
844
+ });
845
+ }
846
+ pushEvent('response.completed', { response: body });
847
+ sseEvents.push('data: [DONE]\n\n');
848
+ return new TextEncoder().encode(sseEvents.join(''));
849
+ }
850
+ export function transformOpenAIChatResponseToOpenAIResponses(response, options) {
851
+ const parsed = parseJsonObject(response.body);
852
+ if (!parsed) {
853
+ return response;
854
+ }
855
+ if (response.statusCode >= 400) {
856
+ const errorPayload = parsed.error && typeof parsed.error === 'object'
857
+ ? parsed.error
858
+ : parsed;
859
+ const errorBody = errorPayload === parsed ? parsed : { error: errorPayload };
860
+ if (options.streamRequested) {
861
+ return {
862
+ ...response,
863
+ headers: {
864
+ ...response.headers,
865
+ 'content-type': 'text/event-stream',
866
+ 'cache-control': 'no-cache',
867
+ },
868
+ body: new TextEncoder().encode(`event: error\ndata: ${JSON.stringify(errorBody)}\n\n`),
869
+ };
870
+ }
871
+ return {
872
+ ...response,
873
+ headers: { ...response.headers, 'content-type': 'application/json' },
874
+ body: encodeJson(errorBody),
875
+ };
876
+ }
877
+ const responsesBody = buildOpenAIResponsesBody(response, parsed, options);
878
+ if (!options.streamRequested) {
879
+ return {
880
+ ...response,
881
+ headers: { ...response.headers, 'content-type': 'application/json' },
882
+ body: encodeJson(responsesBody),
883
+ };
884
+ }
885
+ return {
886
+ ...response,
887
+ headers: { ...response.headers, 'content-type': 'text/event-stream', 'cache-control': 'no-cache' },
888
+ body: buildOpenAIResponsesStream(responsesBody),
889
+ };
890
+ }
891
+ //# sourceMappingURL=model-api-adapter.js.map