@dahawa/hawa-cli-analysis 1.0.4

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,986 @@
1
+ // Converted from TypeScript to JavaScript (ESM)
2
+ import { v4 as uuidv4 } from "uuid";
3
+
4
+ function getThinkLevel(thinking_budget) {
5
+ if (thinking_budget <= 0) return "none";
6
+ if (thinking_budget <= 1024) return "low";
7
+ if (thinking_budget <= 8192) return "medium";
8
+ return "high";
9
+ }
10
+
11
+ function createApiError(
12
+ message,
13
+ statusCode = 500,
14
+ code = "internal_error",
15
+ type = "api_error"
16
+ ) {
17
+ const error = new Error(message);
18
+ error.statusCode = statusCode;
19
+ error.code = code;
20
+ error.type = type;
21
+ return error;
22
+ }
23
+
24
+
25
+ class AnthropicTransformer {
26
+ name = "Anthropic";
27
+ endPoint = "/v1/messages";
28
+ useBearer;
29
+
30
+ constructor(options) {
31
+ this.options = options;
32
+ this.useBearer = this.options?.UseBearer ?? false;
33
+ }
34
+
35
+ async auth(request, provider) {
36
+ const headers = {};
37
+
38
+ if (this.useBearer) {
39
+ headers["authorization"] = `Bearer ${provider.apiKey}`;
40
+ headers["x-api-key"] = undefined;
41
+ } else {
42
+ headers["x-api-key"] = provider.apiKey;
43
+ headers["authorization"] = undefined;
44
+ }
45
+
46
+ return {
47
+ body: request,
48
+ config: {
49
+ headers,
50
+ },
51
+ };
52
+ }
53
+
54
+ async transformRequestOut(request) {
55
+ const messages = [];
56
+
57
+ if (request.system) {
58
+ if (typeof request.system === "string") {
59
+ messages.push({
60
+ role: "system",
61
+ content: request.system,
62
+ });
63
+ } else if (Array.isArray(request.system) && request.system.length) {
64
+ const textParts = request.system
65
+ .filter((item) => item.type === "text" && item.text)
66
+ .map((item) => ({
67
+ type: "text",
68
+ text: item.text,
69
+ cache_control: item.cache_control,
70
+ }));
71
+ messages.push({
72
+ role: "system",
73
+ content: textParts,
74
+ });
75
+ }
76
+ }
77
+
78
+ const requestMessages = JSON.parse(JSON.stringify(request.messages || []));
79
+
80
+ requestMessages?.forEach((msg) => {
81
+ if (msg.role === "user" || msg.role === "assistant") {
82
+ if (typeof msg.content === "string") {
83
+ messages.push({
84
+ role: msg.role,
85
+ content: msg.content,
86
+ });
87
+ return;
88
+ }
89
+
90
+ if (Array.isArray(msg.content)) {
91
+ if (msg.role === "user") {
92
+ const toolParts = msg.content.filter(
93
+ (c) => c.type === "tool_result" && c.tool_use_id
94
+ );
95
+ if (toolParts.length) {
96
+ toolParts.forEach((tool) => {
97
+ const toolMessage = {
98
+ role: "tool",
99
+ content:
100
+ typeof tool.content === "string"
101
+ ? tool.content
102
+ : JSON.stringify(tool.content),
103
+ tool_call_id: tool.tool_use_id,
104
+ cache_control: tool.cache_control,
105
+ };
106
+ messages.push(toolMessage);
107
+ });
108
+ }
109
+
110
+ const textAndMediaParts = msg.content.filter(
111
+ (c) =>
112
+ (c.type === "text" && c.text) ||
113
+ (c.type === "image" && c.source)
114
+ );
115
+ if (textAndMediaParts.length) {
116
+ messages.push({
117
+ role: "user",
118
+ content: textAndMediaParts.map((part) => {
119
+ if (part?.type === "image") {
120
+ return {
121
+ type: "image_url",
122
+ image_url: {
123
+ url:
124
+ part.source?.type === "base64"
125
+ ? `data:${part.source.media_type},${part.source.data}`
126
+ : part.source.url,
127
+ },
128
+ media_type: part.source.media_type,
129
+ };
130
+ }
131
+ return part;
132
+ }),
133
+ });
134
+ }
135
+ } else if (msg.role === "assistant") {
136
+ const assistantMessage = {
137
+ role: "assistant",
138
+ content: "",
139
+ };
140
+ const textParts = msg.content.filter(
141
+ (c) => c.type === "text" && c.text
142
+ );
143
+ if (textParts.length) {
144
+ assistantMessage.content = textParts
145
+ .map((text) => text.text)
146
+ .join("\n");
147
+ }
148
+
149
+ const toolCallParts = msg.content.filter(
150
+ (c) => c.type === "tool_use" && c.id
151
+ );
152
+ if (toolCallParts.length) {
153
+ assistantMessage.tool_calls = toolCallParts.map((tool) => {
154
+ return {
155
+ id: tool.id,
156
+ type: "function",
157
+ function: {
158
+ name: tool.name,
159
+ arguments: JSON.stringify(tool.input || {}),
160
+ },
161
+ };
162
+ });
163
+ }
164
+ messages.push(assistantMessage);
165
+ }
166
+ return;
167
+ }
168
+ }
169
+ });
170
+
171
+ const result = {
172
+ messages,
173
+ model: request.model,
174
+ max_tokens: request.max_tokens,
175
+ temperature: request.temperature,
176
+ stream: request.stream,
177
+ tools: request.tools?.length
178
+ ? this.convertAnthropicToolsToUnified(request.tools)
179
+ : undefined,
180
+ tool_choice: request.tool_choice,
181
+ };
182
+ if (request.thinking) {
183
+ result.reasoning = {
184
+ effort: getThinkLevel(request.thinking.budget_tokens),
185
+ // max_tokens: request.thinking.budget_tokens,
186
+ enabled: request.thinking.type === "enabled",
187
+ };
188
+ }
189
+ if (request.tool_choice) {
190
+ if (request.tool_choice.type === "tool") {
191
+ result.tool_choice = {
192
+ type: "function",
193
+ function: { name: request.tool_choice.name },
194
+ };
195
+ } else {
196
+ result.tool_choice = request.tool_choice.type;
197
+ }
198
+ }
199
+ return result;
200
+ }
201
+
202
+ async transformResponseIn(response) {
203
+ const isStream = true; //response.headers.get("Content-Type")?.includes("text/event-stream");
204
+ //处理流式返回
205
+ if (isStream) {
206
+ if (!response.body) {
207
+ throw new Error("Stream response body is null");
208
+ }
209
+ const convertedStream = await this.convertOpenAIStreamToAnthropic(
210
+ response.body
211
+ );
212
+ //console.log("转换后的流:", convertedStream);
213
+ return new Response(convertedStream, {
214
+ headers: {
215
+ "Content-Type": response.headers.get("Content-Type"),
216
+ "Cache-Control": "no-cache",
217
+ "Connection": response.headers.get("Connection"),
218
+ },
219
+ });
220
+ } else {
221
+ const data = await response.json();
222
+ //console.log("转换后的非流式响应:", data);
223
+ const anthropicResponse = this.convertOpenAIResponseToAnthropic(data);
224
+ return new Response(JSON.stringify(anthropicResponse), {
225
+ headers: { "Content-Type": "application/json" },
226
+ });
227
+ }
228
+ }
229
+
230
+ convertAnthropicToolsToUnified(tools) {
231
+ return tools.map((tool) => ({
232
+ type: "function",
233
+ function: {
234
+ name: tool.name,
235
+ description: tool.description || "",
236
+ parameters: tool.input_schema,
237
+ },
238
+ }));
239
+ }
240
+
241
+ async convertOpenAIStreamToAnthropic(openaiStream) {
242
+ const readable = new ReadableStream({
243
+ start: async (controller) => {
244
+ const encoder = new TextEncoder();
245
+ const messageId = `msg_${Date.now()}`;
246
+ let stopReasonMessageDelta = null;
247
+ let model = "unknown";
248
+ let hasStarted = false;
249
+ let hasTextContentStarted = false;
250
+ let hasFinished = false;
251
+ const toolCalls = new Map();
252
+ const toolCallIndexToContentBlockIndex = new Map();
253
+ let totalChunks = 0;
254
+ let contentChunks = 0;
255
+ let toolCallChunks = 0;
256
+ let isClosed = false;
257
+ let isThinkingStarted = false;
258
+ let contentIndex = 0;
259
+ let currentContentBlockIndex = -1;
260
+
261
+ const safeEnqueue = (data) => {
262
+ if (!isClosed) {
263
+ try {
264
+ controller.enqueue(data);
265
+ const dataStr = new TextDecoder().decode(data);
266
+ this.logger && this.logger.debug({ dataStr }, `send data`);
267
+ } catch (error) {
268
+ if (
269
+ error instanceof TypeError &&
270
+ error.message.includes("Controller is already closed")
271
+ ) {
272
+ isClosed = true;
273
+ } else {
274
+ this.logger && this.logger.debug(`send data error: ${error.message}`);
275
+ throw error;
276
+ }
277
+ }
278
+ }
279
+ };
280
+
281
+ const safeClose = () => {
282
+ if (!isClosed) {
283
+ try {
284
+ if (currentContentBlockIndex >= 0) {
285
+ const contentBlockStop = {
286
+ type: "content_block_stop",
287
+ index: currentContentBlockIndex,
288
+ };
289
+ safeEnqueue(
290
+ encoder.encode(
291
+ `event: content_block_stop\ndata: ${JSON.stringify(
292
+ contentBlockStop
293
+ )}\n\n`
294
+ )
295
+ );
296
+ currentContentBlockIndex = -1;
297
+ }
298
+
299
+ if (stopReasonMessageDelta) {
300
+ safeEnqueue(
301
+ encoder.encode(
302
+ `event: message_delta\ndata: ${JSON.stringify(
303
+ stopReasonMessageDelta
304
+ )}\n\n`
305
+ )
306
+ );
307
+ stopReasonMessageDelta = null;
308
+ } else {
309
+ safeEnqueue(
310
+ encoder.encode(
311
+ `event: message_delta\ndata: ${JSON.stringify({
312
+ type: "message_delta",
313
+ delta: {
314
+ stop_reason: "end_turn",
315
+ stop_sequence: null,
316
+ },
317
+ usage: {
318
+ input_tokens: 0,
319
+ output_tokens: 0,
320
+ cache_read_input_tokens: 0,
321
+ },
322
+ })}\n\n`
323
+ )
324
+ );
325
+ }
326
+ const messageStop = {
327
+ type: "message_stop",
328
+ };
329
+ safeEnqueue(
330
+ encoder.encode(
331
+ `event: message_stop\ndata: ${JSON.stringify(
332
+ messageStop
333
+ )}\n\n`
334
+ )
335
+ );
336
+ controller.close();
337
+ isClosed = true;
338
+ } catch (error) {
339
+ if (
340
+ error instanceof TypeError &&
341
+ error.message.includes("Controller is already closed")
342
+ ) {
343
+ isClosed = true;
344
+ } else {
345
+ throw error;
346
+ }
347
+ }
348
+ }
349
+ };
350
+
351
+ let reader = null;
352
+
353
+ try {
354
+ reader = openaiStream.getReader();
355
+ const decoder = new TextDecoder();
356
+ let buffer = "";
357
+
358
+ while (true) {
359
+ if (isClosed) {
360
+ break;
361
+ }
362
+
363
+ const { done, value } = await reader.read();
364
+ if (done) break;
365
+
366
+ buffer += decoder.decode(value, { stream: true });
367
+ const lines = buffer.split("\n");
368
+ buffer = lines.pop() || "";
369
+
370
+ for (const line of lines) {
371
+ if (isClosed || hasFinished) break;
372
+
373
+ if (!line.startsWith("data:")) continue;
374
+ const data = line.slice(5).trim();
375
+ this.logger && this.logger.debug(`recieved data: ${data}`);
376
+
377
+ if (data === "[DONE]") {
378
+ continue;
379
+ }
380
+
381
+ try {
382
+ const chunk = JSON.parse(data);
383
+ totalChunks++;
384
+ this.logger && this.logger.debug({ response: chunk }, `Original Response`);
385
+ if (chunk.error) {
386
+ const errorMessage = {
387
+ type: "error",
388
+ message: {
389
+ type: "api_error",
390
+ message: JSON.stringify(chunk.error),
391
+ },
392
+ };
393
+
394
+ safeEnqueue(
395
+ encoder.encode(
396
+ `event: error\ndata: ${JSON.stringify(errorMessage)}\n\n`
397
+ )
398
+ );
399
+ continue;
400
+ }
401
+
402
+ model = chunk.model || model;
403
+
404
+ if (!hasStarted && !isClosed && !hasFinished) {
405
+ hasStarted = true;
406
+
407
+ const messageStart = {
408
+ type: "message_start",
409
+ message: {
410
+ id: messageId,
411
+ type: "message",
412
+ role: "assistant",
413
+ content: [],
414
+ model: model,
415
+ stop_reason: null,
416
+ stop_sequence: null,
417
+ usage: {
418
+ input_tokens: 0,
419
+ output_tokens: 0,
420
+ },
421
+ },
422
+ };
423
+
424
+ safeEnqueue(
425
+ encoder.encode(
426
+ `event: message_start\ndata: ${JSON.stringify(
427
+ messageStart
428
+ )}\n\n`
429
+ )
430
+ );
431
+ }
432
+
433
+ const choice = chunk.choices?.[0];
434
+ if (chunk.usage) {
435
+ if (!stopReasonMessageDelta) {
436
+ stopReasonMessageDelta = {
437
+ type: "message_delta",
438
+ delta: {
439
+ stop_reason: "end_turn",
440
+ stop_sequence: null,
441
+ },
442
+ usage: {
443
+ input_tokens: chunk.usage?.prompt_tokens || 0,
444
+ output_tokens: chunk.usage?.completion_tokens || 0,
445
+ cache_read_input_tokens:
446
+ chunk.usage?.cache_read_input_tokens || 0,
447
+ },
448
+ };
449
+ } else {
450
+ stopReasonMessageDelta.usage = {
451
+ input_tokens: chunk.usage?.prompt_tokens || 0,
452
+ output_tokens: chunk.usage?.completion_tokens || 0,
453
+ cache_read_input_tokens:
454
+ chunk.usage?.cache_read_input_tokens || 0,
455
+ };
456
+ }
457
+ }
458
+ if (!choice) {
459
+ continue;
460
+ }
461
+
462
+ if (choice?.delta?.thinking && !isClosed && !hasFinished) {
463
+ if (currentContentBlockIndex >= 0) {
464
+ const contentBlockStop = {
465
+ type: "content_block_stop",
466
+ index: currentContentBlockIndex,
467
+ };
468
+ safeEnqueue(
469
+ encoder.encode(
470
+ `event: content_block_stop\ndata: ${JSON.stringify(
471
+ contentBlockStop
472
+ )}\n\n`
473
+ )
474
+ );
475
+ currentContentBlockIndex = -1;
476
+ }
477
+
478
+ if (!isThinkingStarted) {
479
+ const contentBlockStart = {
480
+ type: "content_block_start",
481
+ index: contentIndex,
482
+ content_block: { type: "thinking", thinking: "" },
483
+ };
484
+ safeEnqueue(
485
+ encoder.encode(
486
+ `event: content_block_start\ndata: ${JSON.stringify(
487
+ contentBlockStart
488
+ )}\n\n`
489
+ )
490
+ );
491
+ currentContentBlockIndex = contentIndex;
492
+ isThinkingStarted = true;
493
+ }
494
+ if (choice.delta.thinking.signature) {
495
+ const thinkingSignature = {
496
+ type: "content_block_delta",
497
+ index: contentIndex,
498
+ delta: {
499
+ type: "signature_delta",
500
+ signature: choice.delta.thinking.signature,
501
+ },
502
+ };
503
+ safeEnqueue(
504
+ encoder.encode(
505
+ `event: content_block_delta\ndata: ${JSON.stringify(
506
+ thinkingSignature
507
+ )}\n\n`
508
+ )
509
+ );
510
+ const contentBlockStop = {
511
+ type: "content_block_stop",
512
+ index: contentIndex,
513
+ };
514
+ safeEnqueue(
515
+ encoder.encode(
516
+ `event: content_block_stop\ndata: ${JSON.stringify(
517
+ contentBlockStop
518
+ )}\n\n`
519
+ )
520
+ );
521
+ currentContentBlockIndex = -1;
522
+ contentIndex++;
523
+ } else if (choice.delta.thinking.content) {
524
+ const thinkingChunk = {
525
+ type: "content_block_delta",
526
+ index: contentIndex,
527
+ delta: {
528
+ type: "thinking_delta",
529
+ thinking: choice.delta.thinking.content || "",
530
+ },
531
+ };
532
+ safeEnqueue(
533
+ encoder.encode(
534
+ `event: content_block_delta\ndata: ${JSON.stringify(
535
+ thinkingChunk
536
+ )}\n\n`
537
+ )
538
+ );
539
+ }
540
+ }
541
+
542
+ if (choice?.delta?.content && !isClosed && !hasFinished) {
543
+ contentChunks++;
544
+
545
+ if (currentContentBlockIndex >= 0) {
546
+ const isCurrentTextBlock = hasTextContentStarted;
547
+ if (!isCurrentTextBlock) {
548
+ const contentBlockStop = {
549
+ type: "content_block_stop",
550
+ index: currentContentBlockIndex,
551
+ };
552
+ safeEnqueue(
553
+ encoder.encode(
554
+ `event: content_block_stop\ndata: ${JSON.stringify(
555
+ contentBlockStop
556
+ )}\n\n`
557
+ )
558
+ );
559
+ currentContentBlockIndex = -1;
560
+ }
561
+ }
562
+
563
+ if (!hasTextContentStarted && !hasFinished) {
564
+ hasTextContentStarted = true;
565
+ const contentBlockStart = {
566
+ type: "content_block_start",
567
+ index: contentIndex,
568
+ content_block: {
569
+ type: "text",
570
+ text: "",
571
+ },
572
+ };
573
+ safeEnqueue(
574
+ encoder.encode(
575
+ `event: content_block_start\ndata: ${JSON.stringify(
576
+ contentBlockStart
577
+ )}\n\n`
578
+ )
579
+ );
580
+ currentContentBlockIndex = contentIndex;
581
+ }
582
+
583
+ if (!isClosed && !hasFinished) {
584
+ const anthropicChunk = {
585
+ type: "content_block_delta",
586
+ index: currentContentBlockIndex,
587
+ delta: {
588
+ type: "text_delta",
589
+ text: choice.delta.content,
590
+ },
591
+ };
592
+ safeEnqueue(
593
+ encoder.encode(
594
+ `event: content_block_delta\ndata: ${JSON.stringify(
595
+ anthropicChunk
596
+ )}\n\n`
597
+ )
598
+ );
599
+ }
600
+ }
601
+
602
+ if (
603
+ choice?.delta?.annotations?.length &&
604
+ !isClosed &&
605
+ !hasFinished
606
+ ) {
607
+ if (currentContentBlockIndex >= 0 && hasTextContentStarted) {
608
+ const contentBlockStop = {
609
+ type: "content_block_stop",
610
+ index: currentContentBlockIndex,
611
+ };
612
+ safeEnqueue(
613
+ encoder.encode(
614
+ `event: content_block_stop\ndata: ${JSON.stringify(
615
+ contentBlockStop
616
+ )}\n\n`
617
+ )
618
+ );
619
+ currentContentBlockIndex = -1;
620
+ hasTextContentStarted = false;
621
+ }
622
+
623
+ choice?.delta?.annotations.forEach((annotation) => {
624
+ contentIndex++;
625
+ const contentBlockStart = {
626
+ type: "content_block_start",
627
+ index: contentIndex,
628
+ content_block: {
629
+ type: "web_search_tool_result",
630
+ tool_use_id: `srvtoolu_${uuidv4()}`,
631
+ content: [
632
+ {
633
+ type: "web_search_result",
634
+ title: annotation.url_citation.title,
635
+ url: annotation.url_citation.url,
636
+ },
637
+ ],
638
+ },
639
+ };
640
+ safeEnqueue(
641
+ encoder.encode(
642
+ `event: content_block_start\ndata: ${JSON.stringify(
643
+ contentBlockStart
644
+ )}\n\n`
645
+ )
646
+ );
647
+
648
+ const contentBlockStop = {
649
+ type: "content_block_stop",
650
+ index: contentIndex,
651
+ };
652
+ safeEnqueue(
653
+ encoder.encode(
654
+ `event: content_block_stop\ndata: ${JSON.stringify(
655
+ contentBlockStop
656
+ )}\n\n`
657
+ )
658
+ );
659
+ currentContentBlockIndex = -1;
660
+ });
661
+ }
662
+
663
+ if (choice?.delta?.tool_calls && !isClosed && !hasFinished) {
664
+ toolCallChunks++;
665
+ const processedInThisChunk = new Set();
666
+
667
+ for (const toolCall of choice.delta.tool_calls) {
668
+ if (isClosed) break;
669
+ const toolCallIndex = toolCall.index ?? 0;
670
+ if (processedInThisChunk.has(toolCallIndex)) {
671
+ continue;
672
+ }
673
+ processedInThisChunk.add(toolCallIndex);
674
+ const isUnknownIndex =
675
+ !toolCallIndexToContentBlockIndex.has(toolCallIndex);
676
+
677
+ if (isUnknownIndex) {
678
+ if (currentContentBlockIndex >= 0) {
679
+ const contentBlockStop = {
680
+ type: "content_block_stop",
681
+ index: currentContentBlockIndex,
682
+ };
683
+ safeEnqueue(
684
+ encoder.encode(
685
+ `event: content_block_stop\ndata: ${JSON.stringify(
686
+ contentBlockStop
687
+ )}\n\n`
688
+ )
689
+ );
690
+ currentContentBlockIndex = -1;
691
+ }
692
+
693
+ const newContentBlockIndex = contentIndex;
694
+ toolCallIndexToContentBlockIndex.set(
695
+ toolCallIndex,
696
+ newContentBlockIndex
697
+ );
698
+ contentIndex++;
699
+ const toolCallId =
700
+ toolCall.id || `call_${Date.now()}_${toolCallIndex}`;
701
+ const toolCallName =
702
+ toolCall.function?.name || `tool_${toolCallIndex}`;
703
+ const contentBlockStart = {
704
+ type: "content_block_start",
705
+ index: newContentBlockIndex,
706
+ content_block: {
707
+ type: "tool_use",
708
+ id: toolCallId,
709
+ name: toolCallName,
710
+ input: {},
711
+ },
712
+ };
713
+
714
+ safeEnqueue(
715
+ encoder.encode(
716
+ `event: content_block_start\ndata: ${JSON.stringify(
717
+ contentBlockStart
718
+ )}\n\n`
719
+ )
720
+ );
721
+ currentContentBlockIndex = newContentBlockIndex;
722
+
723
+ const toolCallInfo = {
724
+ id: toolCallId,
725
+ name: toolCallName,
726
+ arguments: "",
727
+ contentBlockIndex: newContentBlockIndex,
728
+ };
729
+ toolCalls.set(toolCallIndex, toolCallInfo);
730
+ } else if (toolCall.id && toolCall.function?.name) {
731
+ const existingToolCall = toolCalls.get(toolCallIndex);
732
+ const wasTemporary =
733
+ existingToolCall.id.startsWith("call_") &&
734
+ existingToolCall.name.startsWith("tool_");
735
+
736
+ if (wasTemporary) {
737
+ existingToolCall.id = toolCall.id;
738
+ existingToolCall.name = toolCall.function.name;
739
+ }
740
+ }
741
+
742
+ if (
743
+ toolCall.function?.arguments &&
744
+ !isClosed &&
745
+ !hasFinished
746
+ ) {
747
+ const blockIndex =
748
+ toolCallIndexToContentBlockIndex.get(toolCallIndex);
749
+ if (blockIndex === undefined) {
750
+ continue;
751
+ }
752
+ const currentToolCall = toolCalls.get(toolCallIndex);
753
+ if (currentToolCall) {
754
+ currentToolCall.arguments +=
755
+ toolCall.function.arguments;
756
+ }
757
+
758
+ try {
759
+ const anthropicChunk = {
760
+ type: "content_block_delta",
761
+ index: blockIndex,
762
+ delta: {
763
+ type: "input_json_delta",
764
+ partial_json: toolCall.function.arguments,
765
+ },
766
+ };
767
+ safeEnqueue(
768
+ encoder.encode(
769
+ `event: content_block_delta\ndata: ${JSON.stringify(
770
+ anthropicChunk
771
+ )}\n\n`
772
+ )
773
+ );
774
+ } catch (error) {
775
+ try {
776
+ const fixedArgument = toolCall.function.arguments
777
+ .replace(/[\x00-\x1F\x7F-\x9F]/g, "")
778
+ .replace(/\\/g, "\\\\")
779
+ .replace(/"/g, '\\"');
780
+
781
+ const fixedChunk = {
782
+ type: "content_block_delta",
783
+ index: blockIndex,
784
+ delta: {
785
+ type: "input_json_delta",
786
+ partial_json: fixedArgument,
787
+ },
788
+ };
789
+ safeEnqueue(
790
+ encoder.encode(
791
+ `event: content_block_delta\ndata: ${JSON.stringify(
792
+ fixedChunk
793
+ )}\n\n`
794
+ )
795
+ );
796
+ } catch (fixError) {
797
+ console.error(fixError);
798
+ }
799
+ }
800
+ }
801
+ }
802
+ }
803
+
804
+ if (choice?.finish_reason && !isClosed && !hasFinished) {
805
+ if (contentChunks === 0 && toolCallChunks === 0) {
806
+ console.error(
807
+ "Warning: No content in the stream response!"
808
+ );
809
+ }
810
+
811
+ if (currentContentBlockIndex >= 0) {
812
+ const contentBlockStop = {
813
+ type: "content_block_stop",
814
+ index: currentContentBlockIndex,
815
+ };
816
+ safeEnqueue(
817
+ encoder.encode(
818
+ `event: content_block_stop\ndata: ${JSON.stringify(
819
+ contentBlockStop
820
+ )}\n\n`
821
+ )
822
+ );
823
+ currentContentBlockIndex = -1;
824
+ }
825
+
826
+ if (!isClosed) {
827
+ const stopReasonMapping = {
828
+ stop: "end_turn",
829
+ length: "max_tokens",
830
+ tool_calls: "tool_use",
831
+ content_filter: "stop_sequence",
832
+ };
833
+
834
+ const anthropicStopReason =
835
+ stopReasonMapping[choice.finish_reason] || "end_turn";
836
+
837
+ stopReasonMessageDelta = {
838
+ type: "message_delta",
839
+ delta: {
840
+ stop_reason: anthropicStopReason,
841
+ stop_sequence: null,
842
+ },
843
+ usage: {
844
+ input_tokens: chunk.usage?.prompt_tokens || 0,
845
+ output_tokens: chunk.usage?.completion_tokens || 0,
846
+ cache_read_input_tokens:
847
+ chunk.usage?.cache_read_input_tokens || 0,
848
+ },
849
+ };
850
+ }
851
+
852
+ break;
853
+ }
854
+ } catch (parseError) {
855
+ this.logger?.error &&
856
+ this.logger?.error(
857
+ `parseError: ${parseError.name} message: ${parseError.message} stack: ${parseError.stack} data: ${data}`
858
+ );
859
+ }
860
+ }
861
+ }
862
+ safeClose();
863
+ } catch (error) {
864
+ if (!isClosed) {
865
+ try {
866
+ controller.error(error);
867
+ } catch (controllerError) {
868
+ console.error(controllerError);
869
+ }
870
+ }
871
+ } finally {
872
+ if (reader) {
873
+ try {
874
+ reader.releaseLock();
875
+ } catch (releaseError) {
876
+ console.error(releaseError);
877
+ }
878
+ }
879
+ }
880
+ },
881
+ cancel: (reason) => {
882
+ this.logger && this.logger.debug(`cancle stream: ${reason}`);
883
+ },
884
+ });
885
+
886
+ return readable;
887
+ }
888
+
889
+ convertOpenAIResponseToAnthropic(openaiResponse) {
890
+ this.logger && this.logger.debug({ response: openaiResponse }, `Original OpenAI response`);
891
+ try {
892
+ const choice = openaiResponse.choices[0];
893
+ if (!choice) {
894
+ throw new Error("No choices found in OpenAI response");
895
+ }
896
+ const content = [];
897
+ if (choice.message.annotations) {
898
+ const id = `srvtoolu_${uuidv4()}`;
899
+ content.push({
900
+ type: "server_tool_use",
901
+ id,
902
+ name: "web_search",
903
+ input: {
904
+ query: "",
905
+ },
906
+ });
907
+ content.push({
908
+ type: "web_search_tool_result",
909
+ tool_use_id: id,
910
+ content: choice.message.annotations.map((item) => {
911
+ return {
912
+ type: "web_search_result",
913
+ url: item.url_citation.url,
914
+ title: item.url_citation.title,
915
+ };
916
+ }),
917
+ });
918
+ }
919
+ if (choice.message.content) {
920
+ content.push({
921
+ type: "text",
922
+ text: choice.message.content,
923
+ });
924
+ }
925
+ if (choice.message.tool_calls && choice.message.tool_calls.length > 0) {
926
+ choice.message.tool_calls.forEach((toolCall) => {
927
+ let parsedInput = {};
928
+ try {
929
+ const argumentsStr = toolCall.function.arguments || "{}";
930
+
931
+ if (typeof argumentsStr === "object") {
932
+ parsedInput = argumentsStr;
933
+ } else if (typeof argumentsStr === "string") {
934
+ parsedInput = JSON.parse(argumentsStr);
935
+ }
936
+ } catch (parseError) {
937
+ parsedInput = { text: toolCall.function.arguments || "" };
938
+ }
939
+
940
+ content.push({
941
+ type: "tool_use",
942
+ id: toolCall.id,
943
+ name: toolCall.function.name,
944
+ input: parsedInput,
945
+ });
946
+ });
947
+ }
948
+
949
+ const result = {
950
+ id: openaiResponse.id,
951
+ type: "message",
952
+ role: "assistant",
953
+ model: openaiResponse.model,
954
+ content: content,
955
+ stop_reason:
956
+ choice.finish_reason === "stop"
957
+ ? "end_turn"
958
+ : choice.finish_reason === "length"
959
+ ? "max_tokens"
960
+ : choice.finish_reason === "tool_calls"
961
+ ? "tool_use"
962
+ : choice.finish_reason === "content_filter"
963
+ ? "stop_sequence"
964
+ : "end_turn",
965
+ stop_sequence: null,
966
+ usage: {
967
+ input_tokens: openaiResponse.usage?.prompt_tokens || 0,
968
+ output_tokens: openaiResponse.usage?.completion_tokens || 0,
969
+ },
970
+ };
971
+ this.logger && this.logger.debug(
972
+ { result },
973
+ `Conversion complete, final Anthropic response`
974
+ );
975
+ return result;
976
+ } catch (e) {
977
+ throw createApiError(
978
+ `Provider error: ${JSON.stringify(openaiResponse)}`,
979
+ 500,
980
+ "provider_error"
981
+ );
982
+ }
983
+ }
984
+ }
985
+
986
+ export default new AnthropicTransformer();