ai-protocol-adapters 1.0.0-alpha.2 → 1.0.0-alpha.20

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.mjs CHANGED
@@ -397,523 +397,463 @@ function getGlobalLogger() {
397
397
  return globalLogger;
398
398
  }
399
399
 
400
- // src/core/streaming/streaming-protocol-adapter.ts
401
- var StreamingProtocolAdapter = class {
402
- constructor(options = {}) {
403
- this.config = {
404
- debugMode: options.debugMode ?? false,
405
- validateInput: options.validateInput ?? false,
406
- validateOutput: options.validateOutput ?? false,
407
- autoHeal: options.autoHeal ?? false,
408
- timeout: options.timeout ?? 3e4,
409
- retries: options.retries ?? 3,
410
- bufferSize: options.bufferSize ?? 1024,
411
- logger: options.logger ?? getGlobalLogger()
412
- };
400
+ // src/core/a2o-request-adapter/config.ts
401
+ var DEFAULT_CONFIG = {
402
+ // 原有配置
403
+ debugMode: false,
404
+ maxDescriptionLength: 100,
405
+ enableToolNameValidation: true,
406
+ enableFormatValidation: true,
407
+ // 新增默认配置
408
+ validation: {
409
+ enabled: true,
410
+ strict: false,
411
+ // 默认开启自动修复
412
+ customSchemas: {}
413
+ },
414
+ healing: {
415
+ enabled: true,
416
+ maxAttempts: 3,
417
+ enableCustomRules: true
418
+ },
419
+ recovery: {
420
+ enabled: true,
421
+ maxRetries: 2,
422
+ backoffMs: 1e3
423
+ },
424
+ monitoring: {
425
+ enabled: false,
426
+ logLevel: "warn",
427
+ enableMetrics: false
428
+ },
429
+ imageProxy: {
430
+ enabled: true,
431
+ // 默认启用图片代理(解决GitHub Copilot等不支持外部URL的问题)
432
+ timeout: 1e4,
433
+ // 10秒超时
434
+ maxSize: 10 * 1024 * 1024
435
+ // 10MB最大文件大小
413
436
  }
437
+ };
438
+ var SUPPORTED_IMAGE_TYPES = [
439
+ "image/jpeg",
440
+ "image/png",
441
+ "image/gif",
442
+ "image/webp"
443
+ ];
444
+ var TOOL_CONVERSION = {
414
445
  /**
415
- * 转换Anthropic请求为OpenAI格式
446
+ * 终极泛化:完全移除工具名称映射
447
+ * 基于GitHub Copilot API测试结果,100%保持原始格式
416
448
  */
417
- convertAnthropicToOpenAI(anthropicRequest) {
418
- const logger = this.config.logger;
419
- if (this.config.debugMode) {
420
- logger.debug("Converting Anthropic request to OpenAI format", { model: anthropicRequest.model });
449
+ PRESERVE_ORIGINAL_NAMES: true,
450
+ /**
451
+ * 默认工具描述
452
+ */
453
+ DEFAULT_DESCRIPTION: "Tool description",
454
+ /**
455
+ * 未知工具回退名称
456
+ */
457
+ UNKNOWN_TOOL_FALLBACK: "unknown_tool"
458
+ };
459
+
460
+ // src/core/a2o-request-adapter/image-proxy.ts
461
+ var SUPPORTED_IMAGE_MIME_TYPES = [
462
+ "image/jpeg",
463
+ "image/png",
464
+ "image/gif",
465
+ "image/webp"
466
+ ];
467
+ async function downloadImageAsBase64(url, options = {}) {
468
+ const {
469
+ timeout = 1e4,
470
+ maxSize = 10 * 1024 * 1024,
471
+ // 10MB
472
+ userAgent = "ai-protocol-adapters/1.0"
473
+ } = options;
474
+ try {
475
+ const controller = new AbortController();
476
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
477
+ const response = await fetch(url, {
478
+ signal: controller.signal,
479
+ headers: {
480
+ "User-Agent": userAgent
481
+ }
482
+ });
483
+ clearTimeout(timeoutId);
484
+ if (!response.ok) {
485
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
421
486
  }
422
- const openaiRequest = {
423
- model: this.mapAnthropicModelToOpenAI(anthropicRequest.model),
424
- messages: this.convertMessages(anthropicRequest.messages),
425
- stream: anthropicRequest.stream ?? true,
426
- temperature: anthropicRequest.temperature,
427
- max_tokens: anthropicRequest.max_tokens
428
- };
429
- if (anthropicRequest.tools) {
430
- openaiRequest.tools = anthropicRequest.tools.map((tool) => ({
431
- type: "function",
432
- function: {
433
- name: tool.name,
434
- description: tool.description,
435
- parameters: tool.input_schema
436
- }
437
- }));
487
+ const contentType = response.headers.get("content-type");
488
+ if (!contentType || !SUPPORTED_IMAGE_MIME_TYPES.some((type) => contentType.includes(type))) {
489
+ throw new Error(`Unsupported content type: ${contentType}`);
438
490
  }
439
- const hasImages = this.hasImageContent(anthropicRequest);
440
- return {
441
- openaiRequest,
442
- metadata: {
443
- hasImages,
444
- requiresVisionHeaders: hasImages
445
- }
446
- };
491
+ const contentLength = response.headers.get("content-length");
492
+ if (contentLength && parseInt(contentLength) > maxSize) {
493
+ throw new Error(`Image too large: ${contentLength} bytes (max: ${maxSize} bytes)`);
494
+ }
495
+ const arrayBuffer = await response.arrayBuffer();
496
+ if (arrayBuffer.byteLength > maxSize) {
497
+ throw new Error(`Image too large: ${arrayBuffer.byteLength} bytes (max: ${maxSize} bytes)`);
498
+ }
499
+ const base64 = Buffer.from(arrayBuffer).toString("base64");
500
+ return `data:${contentType};base64,${base64}`;
501
+ } catch (error) {
502
+ if (error.name === "AbortError") {
503
+ throw new Error(`Image download timeout after ${timeout}ms`);
504
+ }
505
+ throw new Error(`Failed to download image from ${url}: ${error.message}`);
447
506
  }
507
+ }
508
+ function isExternalUrl(url) {
509
+ return url.startsWith("http://") || url.startsWith("https://");
510
+ }
511
+ function isBase64DataUri(url) {
512
+ return url.startsWith("data:");
513
+ }
514
+
515
+ // src/core/a2o-request-adapter/message-converter.ts
516
+ var MessageConverter = class {
448
517
  /**
449
- * 转换OpenAI流式响应为Anthropic SSE格式
518
+ * 转换消息格式,正确处理工具调用和工具结果
519
+ * 修复关键问题:将tool_use转换为tool_calls,tool_result转换为role:"tool"消息
520
+ * 使用tool_use_id溯回工具名称解决unknown_tool问题
450
521
  */
451
- convertOpenAIStreamToAnthropic(openaiStream, originalRequest) {
452
- const logger = this.config.logger;
453
- try {
454
- if (this.config.debugMode) {
455
- logger.debug("Converting OpenAI stream to Anthropic SSE", {
456
- streamLength: openaiStream.length,
457
- model: originalRequest.model
458
- });
522
+ static convertMessages(messages, system) {
523
+ const debugEnabled = process.env.AI_PROTOCOL_DEBUG === "true";
524
+ if (debugEnabled) {
525
+ if (system !== void 0) {
526
+ console.debug("[MessageConverter] convertMessages called with system:", JSON.stringify(system, null, 2));
527
+ } else {
528
+ console.debug("[MessageConverter] convertMessages called WITHOUT system parameter");
459
529
  }
460
- if (!openaiStream || openaiStream.trim() === "") {
461
- return {
462
- success: false,
463
- error: "Empty stream response",
464
- anthropicSSE: "",
465
- anthropicStandardResponse: null
466
- };
530
+ }
531
+ const context = this.createConversionContext(messages);
532
+ const convertedMessages = [];
533
+ for (const msg of messages) {
534
+ if (Array.isArray(msg.content)) {
535
+ const processedMessages = this.processComplexMessage(msg, context);
536
+ convertedMessages.push(...processedMessages);
537
+ } else {
538
+ const safeMsg = { ...msg };
539
+ if (safeMsg.content === null || safeMsg.content === void 0) {
540
+ safeMsg.content = "";
541
+ }
542
+ convertedMessages.push(safeMsg);
543
+ }
544
+ }
545
+ if (system) {
546
+ const systemMessage = this.processSystemMessage(system);
547
+ if (systemMessage) {
548
+ convertedMessages.unshift(systemMessage);
549
+ if (debugEnabled) {
550
+ console.debug("[MessageConverter] System message added to messages array at index 0");
551
+ }
467
552
  }
468
- const anthropicSSE = this.convertToAnthropicSSE(openaiStream, originalRequest.model);
469
- const anthropicStandardResponse = this.buildStandardResponse(openaiStream);
470
- return {
471
- success: true,
472
- anthropicSSE,
473
- anthropicStandardResponse
474
- };
475
- } catch (error) {
476
- const errorMessage = error instanceof Error ? error.message : "Unknown conversion error";
477
- logger.error("Stream conversion failed", { error: errorMessage });
478
- return {
479
- success: false,
480
- error: errorMessage,
481
- anthropicSSE: "",
482
- anthropicStandardResponse: null
483
- };
484
553
  }
554
+ if (debugEnabled) {
555
+ console.debug("[MessageConverter] Final converted messages count:", convertedMessages.length);
556
+ console.debug("[MessageConverter] First message:", JSON.stringify(convertedMessages[0], null, 2));
557
+ }
558
+ return convertedMessages.map((msg) => {
559
+ if (Array.isArray(msg.tools)) {
560
+ msg.tools = msg.tools.map((tool) => {
561
+ if (tool?.type === "function" && tool.function) {
562
+ const description = tool.function.description?.trim() || "Converted tool with no description provided.";
563
+ return {
564
+ ...tool,
565
+ function: {
566
+ ...tool.function,
567
+ description
568
+ }
569
+ };
570
+ }
571
+ return tool;
572
+ });
573
+ }
574
+ return msg;
575
+ });
485
576
  }
486
577
  /**
487
- * 将OpenAI流转换为Anthropic SSE格式
578
+ * 创建消息转换上下文
488
579
  */
489
- convertToAnthropicSSE(openaiStream, modelName) {
490
- const lines = openaiStream.split("\n");
491
- const sseLines = [];
492
- const state = this.createConversionState();
493
- sseLines.push(
494
- "event: message_start",
495
- `data: {"type":"message_start","message":{"id":"msg_${Date.now()}","type":"message","role":"assistant","model":"${modelName}","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":0,"output_tokens":0}}}`,
496
- ""
497
- );
498
- for (const line of lines) {
499
- if (line.startsWith("data:")) {
500
- const dataLine = line.substring(5);
501
- if (dataLine.trim() === "[DONE]") {
502
- this.addFinalEvents(state, sseLines);
503
- break;
504
- }
505
- try {
506
- const chunk = JSON.parse(dataLine);
507
- this.processStreamChunk(chunk, state, sseLines);
508
- } catch (error) {
509
- if (this.config.debugMode) {
510
- this.config.logger.warn("Failed to parse stream chunk", { line: dataLine.substring(0, 200) });
580
+ static createConversionContext(messages) {
581
+ const toolIdToNameMap = /* @__PURE__ */ new Map();
582
+ for (const msg of messages) {
583
+ if (Array.isArray(msg.content)) {
584
+ for (const item of msg.content) {
585
+ if (typeof item === "object" && item !== null && item.type === "tool_use") {
586
+ toolIdToNameMap.set(item.id, item.name);
511
587
  }
512
588
  }
513
589
  }
514
590
  }
515
- return sseLines.join("\n");
591
+ return {
592
+ toolIdToNameMap,
593
+ hasSystemMessage: false
594
+ };
516
595
  }
517
596
  /**
518
- * 处理单个流式数据块 - 支持thinking和content双模式
597
+ * 处理复杂消息(包含多种内容类型)
519
598
  */
520
- processStreamChunk(chunk, state, sseLines) {
521
- const choice = chunk.choices?.[0];
522
- if (!choice) return;
523
- const delta = choice.delta;
524
- if (delta.reasoning_content) {
525
- state.reasoningContent += delta.reasoning_content;
526
- if (!state.thinkingBlockStarted) {
527
- sseLines.push(
528
- "event: content_block_start",
529
- 'data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":"<thinking>"}}',
530
- ""
531
- );
532
- state.thinkingBlockStarted = true;
599
+ static processComplexMessage(msg, context) {
600
+ const { textContent, toolUses, toolResults } = this.categorizeContent(msg.content);
601
+ const resultMessages = [];
602
+ if (msg.role === "assistant" && toolUses.length > 0) {
603
+ const assistantMessage = this.createAssistantMessageWithToolCalls(textContent, toolUses);
604
+ resultMessages.push(assistantMessage);
605
+ } else if (toolResults.length > 0) {
606
+ const toolMessages = this.createToolResultMessages(toolResults, context.toolIdToNameMap);
607
+ resultMessages.push(...toolMessages);
608
+ const textMessage = this.createTextMessage(msg.role, textContent);
609
+ if (textMessage) {
610
+ resultMessages.push(textMessage);
533
611
  }
534
- sseLines.push(
535
- "event: content_block_delta",
536
- `data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"${this.escapeJsonString(delta.reasoning_content)}"}}`,
537
- ""
538
- );
539
- }
540
- if (delta.content && delta.content !== "") {
541
- if (state.thinkingBlockStarted && !state.contentBlockStarted) {
542
- sseLines.push(
543
- "event: content_block_delta",
544
- 'data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"</thinking>\\n\\n"}}',
545
- "",
546
- "event: content_block_stop",
547
- 'data: {"type":"content_block_stop","index":0}',
548
- "",
549
- "event: content_block_start",
550
- 'data: {"type":"content_block_start","index":1,"content_block":{"type":"text","text":""}}',
551
- ""
552
- );
553
- state.contentBlockStarted = true;
554
- } else if (!state.contentBlockStarted && !state.thinkingBlockStarted) {
555
- sseLines.push(
556
- "event: content_block_start",
557
- 'data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}',
558
- ""
559
- );
560
- state.contentBlockStarted = true;
612
+ } else if (textContent.length > 0) {
613
+ const textMessage = this.createTextMessage(msg.role, textContent);
614
+ if (textMessage) {
615
+ resultMessages.push(textMessage);
561
616
  }
562
- state.textContent += delta.content;
563
- const blockIndex = state.thinkingBlockStarted ? 1 : 0;
564
- sseLines.push(
565
- "event: content_block_delta",
566
- `data: {"type":"content_block_delta","index":${blockIndex},"delta":{"type":"text_delta","text":"${this.escapeJsonString(delta.content)}"}}`,
567
- ""
568
- );
569
- }
570
- if (delta.tool_calls) {
571
- this.processToolCalls(delta.tool_calls, state, sseLines);
572
- }
573
- if (chunk.usage) {
574
- state.usage.input_tokens = chunk.usage.prompt_tokens;
575
- state.usage.output_tokens = chunk.usage.completion_tokens;
576
617
  }
618
+ return resultMessages;
577
619
  }
578
620
  /**
579
- * 处理工具调用
621
+ * 分类内容块
580
622
  */
581
- processToolCalls(toolCalls, state, sseLines) {
582
- for (const toolCall of toolCalls) {
583
- if (toolCall.id && toolCall.function?.name) {
584
- const toolData = {
585
- id: toolCall.id,
586
- name: toolCall.function.name,
587
- input: toolCall.function.arguments || ""
588
- };
589
- state.toolCallsMap.set(toolCall.id, toolData);
590
- sseLines.push(
591
- "event: content_block_start",
592
- `data: {"type":"content_block_start","index":${state.completedToolCalls.length + 1},"content_block":{"type":"tool_use","id":"${toolCall.id}","name":"${toolCall.function.name}","input":{}}}`,
593
- ""
594
- );
623
+ static categorizeContent(content) {
624
+ const textContent = [];
625
+ const toolUses = [];
626
+ const toolResults = [];
627
+ for (const item of content) {
628
+ if (typeof item === "string") {
629
+ textContent.push({ type: "text", text: item });
630
+ } else if (typeof item === "object" && item !== null) {
631
+ switch (item.type) {
632
+ case "text":
633
+ textContent.push(item);
634
+ break;
635
+ case "tool_use":
636
+ toolUses.push(item);
637
+ break;
638
+ case "tool_result":
639
+ toolResults.push(item);
640
+ break;
641
+ case "image":
642
+ const imageContent = this.convertImageContent(item);
643
+ if (imageContent) {
644
+ textContent.push(imageContent);
645
+ }
646
+ break;
647
+ }
595
648
  }
596
649
  }
650
+ return { textContent, toolUses, toolResults };
597
651
  }
598
652
  /**
599
- * 添加最终事件 - 支持thinking+content双模式
653
+ * 转换图片内容格式
654
+ * 支持两种格式:URL 和 base64
600
655
  */
601
- addFinalEvents(state, sseLines) {
602
- if (state.contentBlockStarted) {
603
- const blockIndex = state.thinkingBlockStarted ? 1 : 0;
604
- sseLines.push(
605
- "event: content_block_stop",
606
- `data: {"type":"content_block_stop","index":${blockIndex}}`,
607
- ""
608
- );
609
- } else if (state.thinkingBlockStarted) {
610
- sseLines.push(
611
- "event: content_block_delta",
612
- 'data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"</thinking>"}}',
613
- "",
614
- "event: content_block_stop",
615
- 'data: {"type":"content_block_stop","index":0}',
616
- ""
617
- );
656
+ static convertImageContent(item) {
657
+ if (!item.source) {
658
+ return null;
618
659
  }
619
- sseLines.push(
620
- "event: message_delta",
621
- `data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"output_tokens":${state.usage.output_tokens}}}`,
622
- "",
623
- "event: message_stop",
624
- 'data: {"type":"message_stop"}',
625
- ""
626
- );
627
- }
628
- /**
629
- * 构建标准响应格式
630
- */
631
- buildStandardResponse(openaiStream) {
632
- const state = this.createConversionState();
633
- const lines = openaiStream.split("\n");
634
- for (const line of lines) {
635
- if (line.startsWith("data: ")) {
636
- const dataLine = line.substring(6);
637
- if (dataLine.trim() === "[DONE]") break;
638
- try {
639
- const chunk = JSON.parse(dataLine);
640
- const choice = chunk.choices?.[0];
641
- if (!choice) continue;
642
- const delta = choice.delta;
643
- if (delta.content) {
644
- state.textContent += delta.content;
645
- }
646
- if (chunk.usage) {
647
- state.usage.input_tokens = chunk.usage.prompt_tokens;
648
- state.usage.output_tokens = chunk.usage.completion_tokens;
649
- }
650
- } catch (error) {
660
+ if (item.source.type === "url" && item.source.url) {
661
+ return {
662
+ type: "image_url",
663
+ image_url: {
664
+ url: item.source.url,
665
+ detail: "auto"
666
+ // OpenAI 支持的可选参数
651
667
  }
652
- }
668
+ };
653
669
  }
654
- return {
655
- id: `msg_${Date.now()}`,
656
- type: "message",
657
- role: "assistant",
658
- content: [
659
- {
660
- type: "text",
661
- text: state.textContent
670
+ if (item.source.type === "base64" && item.source.data && item.source.media_type) {
671
+ if (!SUPPORTED_IMAGE_TYPES.includes(item.source.media_type)) {
672
+ console.warn(`\u4E0D\u652F\u6301\u7684\u56FE\u7247\u683C\u5F0F: ${item.source.media_type}`);
673
+ return null;
674
+ }
675
+ const dataUri = `data:${item.source.media_type};base64,${item.source.data}`;
676
+ return {
677
+ type: "image_url",
678
+ image_url: {
679
+ url: dataUri,
680
+ detail: "auto"
662
681
  }
663
- ],
664
- model: "claude-3-sonnet-20240229",
665
- stop_reason: "end_turn",
666
- stop_sequence: null,
667
- usage: state.usage
668
- };
682
+ };
683
+ }
684
+ return null;
669
685
  }
670
686
  /**
671
- * 创建转换状态对象
687
+ * 创建包含工具调用的助手消息
672
688
  */
673
- createConversionState() {
674
- return {
675
- processedLines: 0,
676
- textContent: "",
677
- reasoningContent: "",
678
- toolCallsMap: /* @__PURE__ */ new Map(),
679
- completedToolCalls: [],
680
- allSSELines: [],
681
- errors: [],
682
- usage: {
683
- input_tokens: 0,
684
- output_tokens: 0
685
- },
686
- thinkingBlockStarted: false,
687
- contentBlockStarted: false
689
+ static createAssistantMessageWithToolCalls(textContent, toolUses) {
690
+ const assistantMessage = {
691
+ role: "assistant",
692
+ content: ""
693
+ // 默认为空字符串,避免null值
688
694
  };
689
- }
690
- /**
691
- * 转换消息格式
692
- */
693
- convertMessages(messages) {
694
- return messages.map((msg) => ({
695
- role: msg.role,
696
- content: msg.content
697
- }));
698
- }
699
- /**
700
- * 映射Anthropic模型到OpenAI模型
701
- */
702
- mapAnthropicModelToOpenAI(model) {
703
- const supportedModels = [
704
- "glm-4.5",
705
- "kimi-k2",
706
- "deepseek-v3.1",
707
- "deepseek-r1",
708
- "deepseek-v3",
709
- "qwen3-32b",
710
- "qwen3-coder",
711
- "qwen3-235b",
712
- "tstars2.0"
713
- ];
714
- if (supportedModels.includes(model)) {
715
- return model;
695
+ if (textContent.length > 0) {
696
+ const textOnly = textContent.map((item) => item.text || "").join("");
697
+ if (textOnly.trim()) {
698
+ assistantMessage.content = textOnly.trim();
699
+ }
716
700
  }
717
- const mapping = {
718
- "claude-3-sonnet-20240229": "glm-4.5",
719
- "claude-3-haiku-20240307": "kimi-k2",
720
- "claude-3-opus-20240229": "deepseek-v3.1"
721
- };
722
- return mapping[model] || "glm-4.5";
701
+ assistantMessage.tool_calls = toolUses.map((toolUse) => ({
702
+ id: toolUse.id,
703
+ type: "function",
704
+ function: {
705
+ name: toolUse.name,
706
+ arguments: JSON.stringify(toolUse.input || {})
707
+ }
708
+ }));
709
+ return assistantMessage;
723
710
  }
724
711
  /**
725
- * 检查请求是否包含图片内容
712
+ * 创建工具结果消息
726
713
  */
727
- hasImageContent(request) {
728
- return request.messages.some(
729
- (msg) => Array.isArray(msg.content) && msg.content.some((content) => content?.type === "image")
730
- );
714
+ static createToolResultMessages(toolResults, toolIdToNameMap) {
715
+ return toolResults.map((toolResult) => {
716
+ let resultContent = "No content";
717
+ if (toolResult.content) {
718
+ if (typeof toolResult.content === "string") {
719
+ resultContent = toolResult.content;
720
+ } else {
721
+ resultContent = JSON.stringify(toolResult.content, null, 2);
722
+ }
723
+ }
724
+ const toolName = toolIdToNameMap.get(toolResult.tool_use_id) || TOOL_CONVERSION.UNKNOWN_TOOL_FALLBACK;
725
+ return {
726
+ role: "tool",
727
+ tool_call_id: toolResult.tool_use_id,
728
+ name: toolName,
729
+ content: resultContent
730
+ };
731
+ });
731
732
  }
732
733
  /**
733
- * 转义JSON字符串
734
+ * 创建文本消息
734
735
  */
735
- escapeJsonString(str) {
736
- return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
736
+ static createTextMessage(role, textContent) {
737
+ if (textContent.length === 0) return null;
738
+ const hasNonTextContent = textContent.some((item) => item.type !== "text");
739
+ if (hasNonTextContent) {
740
+ return {
741
+ role,
742
+ content: textContent
743
+ };
744
+ } else {
745
+ const textOnly = textContent.map((item) => item.text || "").join("");
746
+ return {
747
+ role,
748
+ content: textOnly.trim() || ""
749
+ // 确保content为字符串,避免null
750
+ };
751
+ }
737
752
  }
738
753
  /**
739
- * 获取初始SSE事件(message_start + ping)
754
+ * 处理系统消息
740
755
  */
741
- getInitialSSEEvents(modelName = "claude-sonnet-4", messageId = `msg_${Date.now()}_${Math.random().toString(36).substring(2, 8)}`) {
742
- return [
743
- "event: message_start",
744
- `data: {"type":"message_start","message":{"id":"${messageId}","type":"message","role":"assistant","model":"${modelName}","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":0,"output_tokens":0}}}`,
745
- "",
746
- "event: ping",
747
- 'data: {"type":"ping"}',
748
- ""
749
- ];
756
+ static processSystemMessage(system) {
757
+ let systemContent;
758
+ if (Array.isArray(system)) {
759
+ systemContent = system.map((s) => {
760
+ if (typeof s === "string") {
761
+ return s;
762
+ }
763
+ return s.text || "";
764
+ }).filter((text) => text.length > 0).join("\n").trim();
765
+ } else {
766
+ systemContent = system;
767
+ }
768
+ if (systemContent) {
769
+ return {
770
+ role: "system",
771
+ content: systemContent
772
+ };
773
+ }
774
+ return null;
750
775
  }
751
776
  /**
752
- * 增量转换单个OpenAI数据块为Anthropic SSE事件
753
- * 用于逐个处理流式数据片段
777
+ * 异步转换图片内容格式(支持URL自动下载转base64)
778
+ * @param item 图片内容项
779
+ * @param downloadUrls 是否下载URL并转换为base64(默认true)
754
780
  */
755
- convertIncrementalChunk(openaiDataLine, state) {
756
- const logger = this.config.logger;
757
- const sseEvents = [];
758
- if (openaiDataLine.trim() === "[DONE]") {
759
- this.addFinalEvents(state, sseEvents);
760
- return sseEvents;
781
+ static async convertImageContentAsync(item, downloadUrls = true) {
782
+ if (!item.source) {
783
+ return null;
761
784
  }
762
- try {
763
- const chunk = JSON.parse(openaiDataLine);
764
- this.processStreamChunk(chunk, state, sseEvents);
765
- return sseEvents;
766
- } catch (error) {
767
- if (this.config.debugMode) {
768
- logger.warn("Failed to parse OpenAI stream chunk in convertIncrementalChunk", {
769
- line: openaiDataLine.substring(0, 200),
770
- error: error instanceof Error ? error.message : String(error)
771
- });
785
+ if (item.source.type === "url" && item.source.url) {
786
+ const url = item.source.url;
787
+ if (isBase64DataUri(url)) {
788
+ return {
789
+ type: "image_url",
790
+ image_url: {
791
+ url,
792
+ detail: "auto"
793
+ }
794
+ };
772
795
  }
773
- return [];
774
- }
775
- }
776
- };
777
-
778
- // src/core/a2o-request-adapter/config.ts
779
- var DEFAULT_CONFIG = {
780
- // 原有配置
781
- debugMode: false,
782
- maxDescriptionLength: 100,
783
- enableToolNameValidation: true,
784
- enableFormatValidation: true,
785
- // 新增默认配置
786
- validation: {
787
- enabled: true,
788
- strict: false,
789
- // 默认开启自动修复
790
- customSchemas: {}
791
- },
792
- healing: {
793
- enabled: true,
794
- maxAttempts: 3,
795
- enableCustomRules: true
796
- },
797
- recovery: {
798
- enabled: true,
799
- maxRetries: 2,
800
- backoffMs: 1e3
801
- },
802
- monitoring: {
803
- enabled: false,
804
- logLevel: "warn",
805
- enableMetrics: false
806
- }
807
- };
808
- var SUPPORTED_IMAGE_TYPES = [
809
- "image/jpeg",
810
- "image/png",
811
- "image/gif",
812
- "image/webp"
813
- ];
814
- var TOOL_CONVERSION = {
815
- /**
816
- * 终极泛化:完全移除工具名称映射
817
- * 基于GitHub Copilot API测试结果,100%保持原始格式
818
- */
819
- PRESERVE_ORIGINAL_NAMES: true,
820
- /**
821
- * 默认工具描述
822
- */
823
- DEFAULT_DESCRIPTION: "Tool description",
824
- /**
825
- * 未知工具回退名称
826
- */
827
- UNKNOWN_TOOL_FALLBACK: "unknown_tool"
828
- };
829
-
830
- // src/core/a2o-request-adapter/message-converter.ts
831
- var MessageConverter = class {
832
- /**
833
- * 转换消息格式,正确处理工具调用和工具结果
834
- * 修复关键问题:将tool_use转换为tool_calls,tool_result转换为role:"tool"消息
835
- * 使用tool_use_id溯回工具名称解决unknown_tool问题
836
- */
837
- static convertMessages(messages, system) {
838
- const context = this.createConversionContext(messages);
839
- const convertedMessages = [];
840
- for (const msg of messages) {
841
- if (Array.isArray(msg.content)) {
842
- const processedMessages = this.processComplexMessage(msg, context);
843
- convertedMessages.push(...processedMessages);
844
- } else {
845
- const safeMsg = { ...msg };
846
- if (safeMsg.content === null || safeMsg.content === void 0) {
847
- safeMsg.content = "";
796
+ if (downloadUrls && isExternalUrl(url)) {
797
+ try {
798
+ console.log(`[MessageConverter] Downloading image from URL: ${url}`);
799
+ const base64DataUri = await downloadImageAsBase64(url);
800
+ console.log(`[MessageConverter] Successfully converted image to base64`);
801
+ return {
802
+ type: "image_url",
803
+ image_url: {
804
+ url: base64DataUri,
805
+ detail: "auto"
806
+ }
807
+ };
808
+ } catch (error) {
809
+ console.error(`[MessageConverter] Failed to download image: ${error.message}`);
810
+ return {
811
+ type: "image_url",
812
+ image_url: {
813
+ url,
814
+ detail: "auto"
815
+ }
816
+ };
848
817
  }
849
- convertedMessages.push(safeMsg);
850
- }
851
- }
852
- if (system) {
853
- const systemMessage = this.processSystemMessage(system);
854
- if (systemMessage) {
855
- convertedMessages.unshift(systemMessage);
856
818
  }
857
- }
858
- return convertedMessages;
859
- }
860
- /**
861
- * 创建消息转换上下文
862
- */
863
- static createConversionContext(messages) {
864
- const toolIdToNameMap = /* @__PURE__ */ new Map();
865
- for (const msg of messages) {
866
- if (Array.isArray(msg.content)) {
867
- for (const item of msg.content) {
868
- if (typeof item === "object" && item !== null && item.type === "tool_use") {
869
- toolIdToNameMap.set(item.id, item.name);
870
- }
819
+ return {
820
+ type: "image_url",
821
+ image_url: {
822
+ url,
823
+ detail: "auto"
871
824
  }
872
- }
825
+ };
873
826
  }
874
- return {
875
- toolIdToNameMap,
876
- hasSystemMessage: false
877
- };
878
- }
879
- /**
880
- * 处理复杂消息(包含多种内容类型)
881
- */
882
- static processComplexMessage(msg, context) {
883
- const { textContent, toolUses, toolResults } = this.categorizeContent(msg.content);
884
- const resultMessages = [];
885
- if (msg.role === "assistant" && toolUses.length > 0) {
886
- const assistantMessage = this.createAssistantMessageWithToolCalls(textContent, toolUses);
887
- resultMessages.push(assistantMessage);
888
- } else if (toolResults.length > 0) {
889
- const toolMessages = this.createToolResultMessages(toolResults, context.toolIdToNameMap);
890
- resultMessages.push(...toolMessages);
891
- const textMessage = this.createTextMessage(msg.role, textContent);
892
- if (textMessage) {
893
- resultMessages.push(textMessage);
894
- }
895
- } else if (textContent.length > 0) {
896
- const textMessage = this.createTextMessage(msg.role, textContent);
897
- if (textMessage) {
898
- resultMessages.push(textMessage);
827
+ if (item.source.type === "base64" && item.source.data && item.source.media_type) {
828
+ if (!SUPPORTED_IMAGE_TYPES.includes(item.source.media_type)) {
829
+ console.warn(`\u4E0D\u652F\u6301\u7684\u56FE\u7247\u683C\u5F0F: ${item.source.media_type}`);
830
+ return null;
899
831
  }
832
+ const dataUri = `data:${item.source.media_type};base64,${item.source.data}`;
833
+ return {
834
+ type: "image_url",
835
+ image_url: {
836
+ url: dataUri,
837
+ detail: "auto"
838
+ }
839
+ };
900
840
  }
901
- return resultMessages;
841
+ return null;
902
842
  }
903
843
  /**
904
- * 分类内容块
844
+ * 异步处理消息内容(支持图片URL下载)
905
845
  */
906
- static categorizeContent(content) {
846
+ static async processMessageContentAsync(content, downloadUrls = true) {
907
847
  const textContent = [];
908
848
  const toolUses = [];
909
849
  const toolResults = [];
910
850
  for (const item of content) {
911
- if (typeof item === "string") {
912
- textContent.push({ type: "text", text: item });
913
- } else if (typeof item === "object" && item !== null) {
851
+ if (item.type) {
914
852
  switch (item.type) {
915
853
  case "text":
916
- textContent.push(item);
854
+ if (item.text) {
855
+ textContent.push({ type: "text", text: item.text });
856
+ }
917
857
  break;
918
858
  case "tool_use":
919
859
  toolUses.push(item);
@@ -922,7 +862,7 @@ var MessageConverter = class {
922
862
  toolResults.push(item);
923
863
  break;
924
864
  case "image":
925
- const imageContent = this.convertImageContent(item);
865
+ const imageContent = await this.convertImageContentAsync(item, downloadUrls);
926
866
  if (imageContent) {
927
867
  textContent.push(imageContent);
928
868
  }
@@ -933,146 +873,102 @@ var MessageConverter = class {
933
873
  return { textContent, toolUses, toolResults };
934
874
  }
935
875
  /**
936
- * 转换图片内容格式
876
+ * 异步转换消息格式(支持图片URL自动下载)
877
+ * @param messages Claude格式的消息数组
878
+ * @param system 系统消息
879
+ * @param downloadImageUrls 是否下载图片URL并转换为base64(默认true,解决GitHub Copilot等API不支持外部URL的问题)
937
880
  */
938
- static convertImageContent(item) {
939
- if (item.source && item.source.type === "base64" && item.source.data && item.source.media_type) {
940
- if (!SUPPORTED_IMAGE_TYPES.includes(item.source.media_type)) {
941
- console.warn(`\u4E0D\u652F\u6301\u7684\u56FE\u7247\u683C\u5F0F: ${item.source.media_type}`);
942
- return null;
943
- }
944
- const dataUri = `data:${item.source.media_type};base64,${item.source.data}`;
945
- return {
946
- type: "image_url",
947
- image_url: {
948
- url: dataUri
881
+ static async convertMessagesAsync(messages, system, downloadImageUrls = true) {
882
+ const debugEnabled = process.env.AI_PROTOCOL_DEBUG === "true";
883
+ if (debugEnabled) {
884
+ console.debug(
885
+ `[MessageConverter] convertMessagesAsync called (downloadImageUrls: ${downloadImageUrls})`
886
+ );
887
+ }
888
+ const context = this.createConversionContext(messages);
889
+ const convertedMessages = [];
890
+ for (const msg of messages) {
891
+ if (Array.isArray(msg.content)) {
892
+ const processedMessages = await this.processComplexMessageAsync(msg, context, downloadImageUrls);
893
+ convertedMessages.push(...processedMessages);
894
+ } else {
895
+ const safeMsg = { ...msg };
896
+ if (safeMsg.content === null || safeMsg.content === void 0) {
897
+ safeMsg.content = "";
949
898
  }
950
- };
899
+ convertedMessages.push(safeMsg);
900
+ }
951
901
  }
952
- return null;
902
+ const systemMessage = this.processSystemMessage(system);
903
+ if (systemMessage) {
904
+ return [systemMessage, ...convertedMessages];
905
+ }
906
+ return convertedMessages;
953
907
  }
954
908
  /**
955
- * 创建包含工具调用的助手消息
909
+ * 异步处理复杂消息(支持图片URL下载)
956
910
  */
957
- static createAssistantMessageWithToolCalls(textContent, toolUses) {
958
- const assistantMessage = {
959
- role: "assistant",
960
- content: ""
961
- // 默认为空字符串,避免null值
962
- };
963
- if (textContent.length > 0) {
964
- const textOnly = textContent.map((item) => item.text || "").join("");
965
- if (textOnly.trim()) {
966
- assistantMessage.content = textOnly.trim();
911
+ static async processComplexMessageAsync(msg, context, downloadUrls) {
912
+ const { textContent, toolUses, toolResults } = await this.processMessageContentAsync(
913
+ msg.content,
914
+ downloadUrls
915
+ );
916
+ const result = [];
917
+ if (msg.role === "user") {
918
+ const toolMessages = this.createToolResultMessages(toolResults, context.toolIdToNameMap);
919
+ result.push(...toolMessages);
920
+ const textMessage = this.createTextMessage("user", textContent);
921
+ if (textMessage) {
922
+ result.push(textMessage);
923
+ }
924
+ } else if (msg.role === "assistant") {
925
+ if (toolUses.length > 0) {
926
+ const assistantMessage = this.createAssistantMessageWithToolCalls(textContent, toolUses);
927
+ result.push(assistantMessage);
928
+ toolUses.forEach((toolUse) => {
929
+ context.toolIdToNameMap.set(toolUse.id, toolUse.name);
930
+ });
931
+ } else {
932
+ const textMessage = this.createTextMessage("assistant", textContent);
933
+ if (textMessage) {
934
+ result.push(textMessage);
935
+ }
967
936
  }
968
937
  }
969
- assistantMessage.tool_calls = toolUses.map((toolUse) => ({
970
- id: toolUse.id,
938
+ return result;
939
+ }
940
+ };
941
+
942
+ // src/core/a2o-request-adapter/tool-converter.ts
943
+ var ToolConverter = class {
944
+ /**
945
+ * 将Anthropic工具定义转换为OpenAI格式
946
+ */
947
+ static convertAnthropicToolToOpenAI(anthropicTool) {
948
+ if (!anthropicTool || !anthropicTool.name) {
949
+ throw new Error("Invalid tool definition: missing name");
950
+ }
951
+ const openaiName = anthropicTool.name;
952
+ const description = this.simplifyDescription(anthropicTool.description || TOOL_CONVERSION.DEFAULT_DESCRIPTION);
953
+ if (!anthropicTool.input_schema) {
954
+ throw new Error(`Invalid tool definition for ${anthropicTool.name}: missing input_schema`);
955
+ }
956
+ const parameters = {
957
+ type: anthropicTool.input_schema.type || "object",
958
+ properties: anthropicTool.input_schema.properties || {},
959
+ ...anthropicTool.input_schema.required && { required: anthropicTool.input_schema.required }
960
+ };
961
+ return {
971
962
  type: "function",
972
963
  function: {
973
- name: toolUse.name,
974
- arguments: JSON.stringify(toolUse.input || {})
964
+ name: openaiName,
965
+ description,
966
+ parameters
975
967
  }
976
- }));
977
- return assistantMessage;
968
+ };
978
969
  }
979
970
  /**
980
- * 创建工具结果消息
981
- */
982
- static createToolResultMessages(toolResults, toolIdToNameMap) {
983
- return toolResults.map((toolResult) => {
984
- let resultContent = "No content";
985
- if (toolResult.content) {
986
- if (typeof toolResult.content === "string") {
987
- resultContent = toolResult.content;
988
- } else {
989
- resultContent = JSON.stringify(toolResult.content, null, 2);
990
- }
991
- }
992
- const toolName = toolIdToNameMap.get(toolResult.tool_use_id) || TOOL_CONVERSION.UNKNOWN_TOOL_FALLBACK;
993
- return {
994
- role: "tool",
995
- tool_call_id: toolResult.tool_use_id,
996
- name: toolName,
997
- content: resultContent
998
- };
999
- });
1000
- }
1001
- /**
1002
- * 创建文本消息
1003
- */
1004
- static createTextMessage(role, textContent) {
1005
- if (textContent.length === 0) return null;
1006
- const hasNonTextContent = textContent.some((item) => item.type !== "text");
1007
- if (hasNonTextContent) {
1008
- return {
1009
- role,
1010
- content: textContent
1011
- };
1012
- } else {
1013
- const textOnly = textContent.map((item) => item.text || "").join("");
1014
- return {
1015
- role,
1016
- content: textOnly.trim() || ""
1017
- // 确保content为字符串,避免null
1018
- };
1019
- }
1020
- }
1021
- /**
1022
- * 处理系统消息
1023
- */
1024
- static processSystemMessage(system) {
1025
- let systemContent;
1026
- if (Array.isArray(system)) {
1027
- systemContent = system.map((s) => {
1028
- if (typeof s === "string") {
1029
- return s;
1030
- }
1031
- return s.text || "";
1032
- }).filter((text) => text.length > 0).join("\n").trim();
1033
- } else {
1034
- systemContent = system;
1035
- }
1036
- if (systemContent) {
1037
- return {
1038
- role: "system",
1039
- content: systemContent
1040
- };
1041
- }
1042
- return null;
1043
- }
1044
- };
1045
-
1046
- // src/core/a2o-request-adapter/tool-converter.ts
1047
- var ToolConverter = class {
1048
- /**
1049
- * 将Anthropic工具定义转换为OpenAI格式
1050
- */
1051
- static convertAnthropicToolToOpenAI(anthropicTool) {
1052
- if (!anthropicTool || !anthropicTool.name) {
1053
- throw new Error("Invalid tool definition: missing name");
1054
- }
1055
- const openaiName = anthropicTool.name;
1056
- const description = this.simplifyDescription(anthropicTool.description || TOOL_CONVERSION.DEFAULT_DESCRIPTION);
1057
- if (!anthropicTool.input_schema) {
1058
- throw new Error(`Invalid tool definition for ${anthropicTool.name}: missing input_schema`);
1059
- }
1060
- const parameters = {
1061
- type: anthropicTool.input_schema.type || "object",
1062
- properties: anthropicTool.input_schema.properties || {},
1063
- ...anthropicTool.input_schema.required && { required: anthropicTool.input_schema.required }
1064
- };
1065
- return {
1066
- type: "function",
1067
- function: {
1068
- name: openaiName,
1069
- description,
1070
- parameters
1071
- }
1072
- };
1073
- }
1074
- /**
1075
- * 将OpenAI工具调用转换为Claude格式
971
+ * 将OpenAI工具调用转换为Claude格式
1076
972
  */
1077
973
  static convertOpenAIToolCallsToClaude(toolCalls) {
1078
974
  return toolCalls.map((toolCall) => {
@@ -1097,6 +993,24 @@ var ToolConverter = class {
1097
993
  static isOpenAIToolFormat(tool) {
1098
994
  return tool && tool.type === "function" && tool.function && tool.function.name;
1099
995
  }
996
+ /**
997
+ * 确保OpenAI格式工具有有效描述
998
+ * 处理空字符串、undefined、null等情况
999
+ */
1000
+ static ensureOpenAIToolDescription(tool) {
1001
+ if (!tool?.function) return tool;
1002
+ const description = tool.function.description?.trim();
1003
+ if (!description) {
1004
+ return {
1005
+ ...tool,
1006
+ function: {
1007
+ ...tool.function,
1008
+ description: TOOL_CONVERSION.DEFAULT_DESCRIPTION
1009
+ }
1010
+ };
1011
+ }
1012
+ return tool;
1013
+ }
1100
1014
  /**
1101
1015
  * 简化Claude的详细描述为OpenAI兼容的简短描述
1102
1016
  */
@@ -3693,324 +3607,1174 @@ var A2ORequestAdapter = class {
3693
3607
  }
3694
3608
  }
3695
3609
  /**
3696
- * 执行核心转换逻辑(原有逻辑保持不变)
3610
+ * 执行核心转换逻辑(支持图片代理)
3697
3611
  */
3698
3612
  async performCoreConversion(anthropicRequest) {
3699
3613
  if (this.config.enableFormatValidation) {
3700
3614
  FormatValidator.validateClaudeRequest(anthropicRequest);
3701
3615
  }
3616
+ const messages = this.config.imageProxy.enabled ? await MessageConverter.convertMessagesAsync(
3617
+ anthropicRequest.messages,
3618
+ anthropicRequest.system,
3619
+ true
3620
+ // 启用图片下载
3621
+ ) : MessageConverter.convertMessages(anthropicRequest.messages, anthropicRequest.system);
3702
3622
  const openaiRequest = {
3703
3623
  model: anthropicRequest.model,
3704
- messages: MessageConverter.convertMessages(anthropicRequest.messages, anthropicRequest.system),
3624
+ messages,
3705
3625
  max_tokens: anthropicRequest.max_tokens,
3706
3626
  temperature: anthropicRequest.temperature,
3707
3627
  stream: anthropicRequest.stream
3708
3628
  };
3709
- if (anthropicRequest.tools && anthropicRequest.tools.length > 0) {
3710
- openaiRequest.tools = this.convertToolDefinitions(anthropicRequest.tools);
3711
- }
3712
- const specialFields = ["_anthropic_protocol", "_rovo_tool_injected", "_routeResult"];
3713
- for (const field of specialFields) {
3714
- if (anthropicRequest[field] !== void 0) {
3715
- openaiRequest[field] = anthropicRequest[field];
3716
- }
3629
+ if (anthropicRequest.tools && anthropicRequest.tools.length > 0) {
3630
+ openaiRequest.tools = this.convertToolDefinitions(anthropicRequest.tools);
3631
+ }
3632
+ const specialFields = ["_anthropic_protocol", "_rovo_tool_injected", "_routeResult"];
3633
+ for (const field of specialFields) {
3634
+ if (anthropicRequest[field] !== void 0) {
3635
+ openaiRequest[field] = anthropicRequest[field];
3636
+ }
3637
+ }
3638
+ return openaiRequest;
3639
+ }
3640
+ /**
3641
+ * 转换Anthropic请求格式为OpenAI兼容格式 - 原有方法保持兼容
3642
+ */
3643
+ convertAnthropicRequestToOpenAI(anthropicRequest) {
3644
+ if (this.config.enableFormatValidation) {
3645
+ FormatValidator.validateClaudeRequest(anthropicRequest);
3646
+ }
3647
+ const openaiRequest = {
3648
+ model: anthropicRequest.model,
3649
+ messages: MessageConverter.convertMessages(anthropicRequest.messages, anthropicRequest.system),
3650
+ max_tokens: anthropicRequest.max_tokens,
3651
+ temperature: anthropicRequest.temperature,
3652
+ stream: anthropicRequest.stream,
3653
+ n: 1
3654
+ };
3655
+ if (anthropicRequest.tools && anthropicRequest.tools.length > 0) {
3656
+ openaiRequest.tools = this.convertToolDefinitions(anthropicRequest.tools);
3657
+ }
3658
+ const specialFields = ["_anthropic_protocol", "_rovo_tool_injected", "_routeResult"];
3659
+ for (const field of specialFields) {
3660
+ if (anthropicRequest[field] !== void 0) {
3661
+ openaiRequest[field] = anthropicRequest[field];
3662
+ }
3663
+ }
3664
+ if (this.config.enableFormatValidation && !FormatValidator.validateOpenAIRequest(openaiRequest)) {
3665
+ throw new Error("Generated OpenAI request format is invalid");
3666
+ }
3667
+ return openaiRequest;
3668
+ }
3669
+ /**
3670
+ * 转换OpenAI响应格式为Claude兼容格式
3671
+ */
3672
+ convertOpenAIResponseToClaude(openaiResponse) {
3673
+ const claudeContent = [];
3674
+ const message = openaiResponse.choices?.[0]?.message;
3675
+ if (message?.content) {
3676
+ claudeContent.push({
3677
+ type: "text",
3678
+ text: message.content
3679
+ });
3680
+ }
3681
+ if (message?.tool_calls) {
3682
+ const toolUseContents = ToolConverter.convertOpenAIToolCallsToClaude(message.tool_calls);
3683
+ claudeContent.push(...toolUseContents);
3684
+ }
3685
+ const claudeResponse = {
3686
+ role: "assistant",
3687
+ content: claudeContent
3688
+ };
3689
+ return claudeResponse;
3690
+ }
3691
+ /**
3692
+ * 转换工具定义列表
3693
+ * 确保所有工具都有有效描述,无论是Anthropic还是OpenAI格式
3694
+ */
3695
+ convertToolDefinitions(tools) {
3696
+ return tools.map((tool) => {
3697
+ let openaiTool;
3698
+ if (ToolConverter.isOpenAIToolFormat(tool)) {
3699
+ openaiTool = tool;
3700
+ } else {
3701
+ openaiTool = ToolConverter.convertAnthropicToolToOpenAI(tool);
3702
+ }
3703
+ return ToolConverter.ensureOpenAIToolDescription(openaiTool);
3704
+ });
3705
+ }
3706
+ /**
3707
+ * 验证Claude请求格式
3708
+ */
3709
+ validateClaudeRequest(request) {
3710
+ return FormatValidator.validateClaudeRequest(request);
3711
+ }
3712
+ /**
3713
+ * 验证OpenAI请求格式
3714
+ */
3715
+ validateOpenAIRequest(request) {
3716
+ return FormatValidator.validateOpenAIRequest(request);
3717
+ }
3718
+ /**
3719
+ * 获取支持的工具列表
3720
+ */
3721
+ getSupportedTools() {
3722
+ return [];
3723
+ }
3724
+ /**
3725
+ * 检查工具是否支持
3726
+ */
3727
+ isToolSupported(_toolName) {
3728
+ return true;
3729
+ }
3730
+ /**
3731
+ * 获取工具映射(已弃用,保持兼容性)
3732
+ */
3733
+ getToolMapping(claudeToolName) {
3734
+ return claudeToolName;
3735
+ }
3736
+ /**
3737
+ * 更新配置
3738
+ */
3739
+ updateConfig(newConfig) {
3740
+ this.config = { ...this.config, ...newConfig };
3741
+ }
3742
+ /**
3743
+ * 获取当前配置
3744
+ */
3745
+ getConfig() {
3746
+ return { ...this.config };
3747
+ }
3748
+ /**
3749
+ * 执行带验证的核心转换(同步版本)
3750
+ * 为静态方法提供增强功能,但保持同步特性
3751
+ */
3752
+ performCoreConversionWithValidation(anthropicRequest) {
3753
+ if (this.config.validation.enabled) {
3754
+ try {
3755
+ validateAnthropicRequest(anthropicRequest);
3756
+ } catch (error) {
3757
+ if (this.config.validation.strict) {
3758
+ throw error;
3759
+ } else {
3760
+ const errorSummary = this.getValidationErrorSummary(error);
3761
+ console.warn(`[A2ORequestAdapter] Input validation warning: ${errorSummary}. Details saved to logs.`);
3762
+ }
3763
+ }
3764
+ }
3765
+ let processedRequest = anthropicRequest;
3766
+ if (this.config.healing.enabled) {
3767
+ try {
3768
+ processedRequest = this.applySyncHealing(anthropicRequest);
3769
+ } catch (healingError) {
3770
+ console.warn("[A2ORequestAdapter] Healing failed:", healingError);
3771
+ }
3772
+ }
3773
+ const result = this.performBasicConversion(processedRequest, true);
3774
+ if (this.config.validation.enabled) {
3775
+ try {
3776
+ validateOpenAIRequest(result);
3777
+ } catch (error) {
3778
+ if (this.config.validation.strict) {
3779
+ throw error;
3780
+ } else {
3781
+ console.warn("[A2ORequestAdapter] Output validation warning:", error);
3782
+ }
3783
+ }
3784
+ }
3785
+ return result;
3786
+ }
3787
+ /**
3788
+ * 执行基础转换逻辑(原有逻辑的提取)
3789
+ */
3790
+ performBasicConversion(anthropicRequest, skipValidation = false) {
3791
+ if (!skipValidation && this.config.enableFormatValidation) {
3792
+ FormatValidator.validateClaudeRequest(anthropicRequest);
3793
+ }
3794
+ const openaiRequest = {
3795
+ model: anthropicRequest.model,
3796
+ messages: MessageConverter.convertMessages(anthropicRequest.messages, anthropicRequest.system),
3797
+ max_tokens: anthropicRequest.max_tokens,
3798
+ temperature: anthropicRequest.temperature,
3799
+ stream: anthropicRequest.stream,
3800
+ n: 1
3801
+ };
3802
+ if (anthropicRequest.tools && anthropicRequest.tools.length > 0) {
3803
+ openaiRequest.tools = this.convertToolDefinitions(anthropicRequest.tools);
3804
+ }
3805
+ const specialFields = ["_anthropic_protocol", "_rovo_tool_injected", "_routeResult"];
3806
+ for (const field of specialFields) {
3807
+ if (anthropicRequest[field] !== void 0) {
3808
+ openaiRequest[field] = anthropicRequest[field];
3809
+ }
3810
+ }
3811
+ if (this.config.enableFormatValidation && !FormatValidator.validateOpenAIRequest(openaiRequest)) {
3812
+ throw new Error("Generated OpenAI request format is invalid");
3813
+ }
3814
+ return openaiRequest;
3815
+ }
3816
+ /**
3817
+ * 应用同步修复逻辑
3818
+ * 简化版的修复,不依赖异步操作
3819
+ */
3820
+ applySyncHealing(request) {
3821
+ const healedRequest = { ...request };
3822
+ if (!healedRequest.max_tokens || healedRequest.max_tokens <= 0) {
3823
+ healedRequest.max_tokens = 4096;
3824
+ }
3825
+ if (!healedRequest.messages || !Array.isArray(healedRequest.messages)) {
3826
+ throw new Error("Invalid messages array");
3827
+ }
3828
+ if (!healedRequest.model) {
3829
+ healedRequest.model = "claude-sonnet-4";
3830
+ }
3831
+ for (const message of healedRequest.messages) {
3832
+ if (!message.role) {
3833
+ message.role = "user";
3834
+ }
3835
+ if (!message.content) {
3836
+ message.content = "";
3837
+ }
3838
+ }
3839
+ return healedRequest;
3840
+ }
3841
+ /**
3842
+ * 获取验证错误详情
3843
+ */
3844
+ getValidationErrors(request, type) {
3845
+ return FormatValidator.getValidationErrors(request, type);
3846
+ }
3847
+ /**
3848
+ * 生成简洁的验证错误摘要
3849
+ */
3850
+ getValidationErrorSummary(error) {
3851
+ if (error?.issues?.length > 0) {
3852
+ const invalidEnums = error.issues.filter((i) => i.code === "invalid_enum_value");
3853
+ const missingFields = error.issues.filter((i) => i.code === "invalid_type");
3854
+ const summary = [];
3855
+ if (invalidEnums.length > 0) {
3856
+ const first = invalidEnums[0];
3857
+ summary.push(`invalid_${first.path?.join(".")}: '${first.received}'`);
3858
+ }
3859
+ if (missingFields.length > 0) {
3860
+ summary.push(`${missingFields.length} missing fields`);
3861
+ }
3862
+ return summary.slice(0, 2).join(", ") + (error.issues.length > 5 ? ` (+${error.issues.length - 5} more)` : "");
3863
+ }
3864
+ return error.message || "Validation failed";
3865
+ }
3866
+ };
3867
+ var A2ORequestAdapterStatic = {
3868
+ /**
3869
+ * 转换Anthropic请求格式为OpenAI兼容格式(静态方法)
3870
+ * 内部使用增强转换器,所有调用点自动获得增强功能
3871
+ */
3872
+ convertAnthropicRequestToOpenAI: (anthropicRequest) => {
3873
+ const adapter = new A2ORequestAdapter({
3874
+ debugMode: false,
3875
+ maxDescriptionLength: 100,
3876
+ enableToolNameValidation: true,
3877
+ enableFormatValidation: true,
3878
+ validation: { enabled: true, strict: false },
3879
+ healing: { enabled: true, maxAttempts: 2, enableCustomRules: true },
3880
+ recovery: { enabled: false, maxRetries: 0, backoffMs: 1e3 },
3881
+ monitoring: { enabled: false, logLevel: "none", enableMetrics: false }
3882
+ });
3883
+ try {
3884
+ const result = adapter.performCoreConversionWithValidation(anthropicRequest);
3885
+ return result;
3886
+ } catch (error) {
3887
+ console.warn(`[A2ORequestAdapterStatic] Enhanced conversion failed, using basic conversion: ${error?.message || error}`);
3888
+ return adapter.performBasicConversion(anthropicRequest, true);
3889
+ }
3890
+ },
3891
+ /**
3892
+ * 转换OpenAI响应格式为Claude兼容格式(静态方法)
3893
+ * 内部使用增强转换器
3894
+ */
3895
+ convertOpenAIResponseToClaude: (openaiResponse) => {
3896
+ const adapter = new A2ORequestAdapter({
3897
+ debugMode: false,
3898
+ maxDescriptionLength: 100,
3899
+ enableToolNameValidation: true,
3900
+ enableFormatValidation: true,
3901
+ validation: { enabled: true, strict: false },
3902
+ healing: { enabled: true, maxAttempts: 2, enableCustomRules: true },
3903
+ recovery: { enabled: false, maxRetries: 0, backoffMs: 1e3 },
3904
+ monitoring: { enabled: false, logLevel: "none", enableMetrics: false }
3905
+ });
3906
+ return adapter.convertOpenAIResponseToClaude(openaiResponse);
3907
+ },
3908
+ /**
3909
+ * 验证Claude请求格式(静态方法)
3910
+ */
3911
+ validateClaudeRequest: (request) => {
3912
+ return FormatValidator.validateClaudeRequest(request);
3913
+ },
3914
+ /**
3915
+ * 验证OpenAI请求格式(静态方法)
3916
+ */
3917
+ validateOpenAIRequest: (request) => {
3918
+ return FormatValidator.validateOpenAIRequest(request);
3919
+ },
3920
+ /**
3921
+ * 获取支持的工具列表(静态方法)
3922
+ */
3923
+ getSupportedTools: () => {
3924
+ return [];
3925
+ },
3926
+ /**
3927
+ * 检查工具是否支持(静态方法)
3928
+ */
3929
+ isToolSupported: (_toolName) => {
3930
+ return true;
3931
+ },
3932
+ /**
3933
+ * 获取工具映射(静态方法,已弃用)
3934
+ */
3935
+ getToolMapping: (claudeToolName) => {
3936
+ return claudeToolName;
3937
+ },
3938
+ /**
3939
+ * 转换Anthropic请求格式为OpenAI兼容格式(异步版本,支持图片URL自动下载)
3940
+ * 解决GitHub Copilot等API不支持外部图片URL的问题
3941
+ * @param anthropicRequest Claude格式的请求
3942
+ * @param downloadImageUrls 是否下载图片URL并转换为base64(默认true)
3943
+ */
3944
+ convertAnthropicRequestToOpenAIAsync: async (anthropicRequest, downloadImageUrls = true) => {
3945
+ const adapter = new A2ORequestAdapter({
3946
+ debugMode: false,
3947
+ maxDescriptionLength: 100,
3948
+ enableToolNameValidation: true,
3949
+ enableFormatValidation: true,
3950
+ validation: { enabled: true, strict: false },
3951
+ healing: { enabled: true, maxAttempts: 2, enableCustomRules: true },
3952
+ recovery: { enabled: false, maxRetries: 0, backoffMs: 1e3 },
3953
+ monitoring: { enabled: false, logLevel: "none", enableMetrics: false },
3954
+ imageProxy: {
3955
+ enabled: downloadImageUrls,
3956
+ timeout: 1e4,
3957
+ maxSize: 10 * 1024 * 1024
3958
+ }
3959
+ });
3960
+ try {
3961
+ const result = await adapter.performCoreConversion(anthropicRequest);
3962
+ return result;
3963
+ } catch (error) {
3964
+ console.warn(`[A2ORequestAdapterStatic] Async conversion failed: ${error?.message || error}`);
3965
+ return adapter.performBasicConversion(anthropicRequest, true);
3966
+ }
3967
+ }
3968
+ };
3969
+
3970
+ // src/core/streaming/streaming-protocol-adapter.ts
3971
+ var StreamingProtocolAdapter = class {
3972
+ constructor(options = {}) {
3973
+ this.config = {
3974
+ debugMode: options.debugMode ?? false,
3975
+ validateInput: options.validateInput ?? false,
3976
+ validateOutput: options.validateOutput ?? false,
3977
+ autoHeal: options.autoHeal ?? false,
3978
+ timeout: options.timeout ?? 3e4,
3979
+ retries: options.retries ?? 3,
3980
+ bufferSize: options.bufferSize ?? 1024,
3981
+ logger: options.logger ?? getGlobalLogger()
3982
+ };
3983
+ }
3984
+ logDebug(message, meta) {
3985
+ if (this.config.debugMode) {
3986
+ this.config.logger.debug(message, meta);
3717
3987
  }
3718
- return openaiRequest;
3719
3988
  }
3720
3989
  /**
3721
- * 转换Anthropic请求格式为OpenAI兼容格式 - 原有方法保持兼容
3990
+ * 转换Anthropic请求为OpenAI格式
3722
3991
  */
3723
- convertAnthropicRequestToOpenAI(anthropicRequest) {
3724
- if (this.config.enableFormatValidation) {
3725
- FormatValidator.validateClaudeRequest(anthropicRequest);
3992
+ convertAnthropicToOpenAI(anthropicRequest) {
3993
+ const logger = this.config.logger;
3994
+ if (this.config.debugMode) {
3995
+ logger.debug("Converting Anthropic request to OpenAI format", { model: anthropicRequest.model });
3726
3996
  }
3727
- const openaiRequest = {
3728
- model: anthropicRequest.model,
3729
- messages: MessageConverter.convertMessages(anthropicRequest.messages, anthropicRequest.system),
3730
- max_tokens: anthropicRequest.max_tokens,
3731
- temperature: anthropicRequest.temperature,
3732
- stream: anthropicRequest.stream,
3733
- n: 1
3997
+ const openaiRequest = A2ORequestAdapterStatic.convertAnthropicRequestToOpenAI(anthropicRequest);
3998
+ openaiRequest.stream = true;
3999
+ const hasImages = this.hasImageContent(anthropicRequest);
4000
+ return {
4001
+ openaiRequest,
4002
+ metadata: {
4003
+ hasImages,
4004
+ requiresVisionHeaders: hasImages
4005
+ }
3734
4006
  };
3735
- if (anthropicRequest.tools && anthropicRequest.tools.length > 0) {
3736
- openaiRequest.tools = this.convertToolDefinitions(anthropicRequest.tools);
3737
- }
3738
- const specialFields = ["_anthropic_protocol", "_rovo_tool_injected", "_routeResult"];
3739
- for (const field of specialFields) {
3740
- if (anthropicRequest[field] !== void 0) {
3741
- openaiRequest[field] = anthropicRequest[field];
4007
+ }
4008
+ /**
4009
+ * 与StandardProtocolAdapter保持一致的API,用于集成测试和向后兼容。
4010
+ */
4011
+ convertRequest(anthropicRequest) {
4012
+ return this.convertAnthropicToOpenAI(anthropicRequest);
4013
+ }
4014
+ /**
4015
+ * 转换OpenAI流式响应为Anthropic SSE格式
4016
+ */
4017
+ convertOpenAIStreamToAnthropic(openaiStream, originalRequest) {
4018
+ const logger = this.config.logger;
4019
+ try {
4020
+ if (this.config.debugMode) {
4021
+ logger.debug("Converting OpenAI stream to Anthropic SSE", {
4022
+ streamLength: openaiStream.length,
4023
+ model: originalRequest.model
4024
+ });
3742
4025
  }
4026
+ if (!openaiStream || openaiStream.trim() === "") {
4027
+ return {
4028
+ success: false,
4029
+ error: "Empty stream response",
4030
+ anthropicSSE: "",
4031
+ anthropicStandardResponse: null
4032
+ };
4033
+ }
4034
+ const anthropicSSE = this.convertToAnthropicSSE(openaiStream, originalRequest.model);
4035
+ const anthropicStandardResponse = this.buildStandardResponse(openaiStream);
4036
+ return {
4037
+ success: true,
4038
+ anthropicSSE,
4039
+ anthropicStandardResponse
4040
+ };
4041
+ } catch (error) {
4042
+ const errorMessage = error instanceof Error ? error.message : "Unknown conversion error";
4043
+ logger.error("Stream conversion failed", { error: errorMessage });
4044
+ return {
4045
+ success: false,
4046
+ error: errorMessage,
4047
+ anthropicSSE: "",
4048
+ anthropicStandardResponse: null
4049
+ };
3743
4050
  }
3744
- if (this.config.enableFormatValidation && !FormatValidator.validateOpenAIRequest(openaiRequest)) {
3745
- throw new Error("Generated OpenAI request format is invalid");
3746
- }
3747
- return openaiRequest;
3748
4051
  }
3749
4052
  /**
3750
- * 转换OpenAI响应格式为Claude兼容格式
4053
+ * 增量解析Anthropic SSE,转换为OpenAI流式chunk
4054
+ * 供 OpenAI Chat Completions 端点直接复用
3751
4055
  */
3752
- convertOpenAIResponseToClaude(openaiResponse) {
3753
- const claudeContent = [];
3754
- const message = openaiResponse.choices?.[0]?.message;
3755
- if (message?.content) {
3756
- claudeContent.push({
3757
- type: "text",
3758
- text: message.content
3759
- });
4056
+ convertAnthropicSSEChunkToOpenAI(params) {
4057
+ const { buffer, chunk, model, flush = false } = params;
4058
+ let localBuffer = buffer + (chunk || "");
4059
+ const emittedChunks = [];
4060
+ let finishReason;
4061
+ let streamStopped = false;
4062
+ const processEvent = (eventText) => {
4063
+ const { eventType, data } = this.parseAnthropicSSEEvent(eventText);
4064
+ if (!eventType || !data) {
4065
+ return;
4066
+ }
4067
+ if (eventType === "content_block_delta") {
4068
+ const text = this.extractTextFromAnthropicDelta(data);
4069
+ if (text) {
4070
+ emittedChunks.push(this.buildOpenAIStreamChunk(model, text));
4071
+ }
4072
+ } else if (eventType === "message_stop") {
4073
+ finishReason = this.mapAnthropicStopReasonToOpenAI(data?.stop_reason);
4074
+ streamStopped = true;
4075
+ }
4076
+ };
4077
+ while (true) {
4078
+ const separatorIndex = localBuffer.indexOf("\n\n");
4079
+ if (separatorIndex === -1) {
4080
+ break;
4081
+ }
4082
+ const rawEvent = localBuffer.slice(0, separatorIndex);
4083
+ localBuffer = localBuffer.slice(separatorIndex + 2);
4084
+ if (!rawEvent.trim()) {
4085
+ continue;
4086
+ }
4087
+ processEvent(rawEvent);
4088
+ if (streamStopped) {
4089
+ break;
4090
+ }
3760
4091
  }
3761
- if (message?.tool_calls) {
3762
- const toolUseContents = ToolConverter.convertOpenAIToolCallsToClaude(message.tool_calls);
3763
- claudeContent.push(...toolUseContents);
4092
+ if (flush && localBuffer.trim()) {
4093
+ processEvent(localBuffer);
4094
+ localBuffer = "";
3764
4095
  }
3765
- const claudeResponse = {
3766
- role: "assistant",
3767
- content: claudeContent
4096
+ return {
4097
+ buffer: localBuffer,
4098
+ chunks: emittedChunks,
4099
+ finishReason,
4100
+ streamStopped
3768
4101
  };
3769
- return claudeResponse;
3770
4102
  }
3771
4103
  /**
3772
- * 转换工具定义列表
4104
+ * 将OpenAI流转换为Anthropic SSE格式
3773
4105
  */
3774
- convertToolDefinitions(tools) {
3775
- return tools.map((tool) => {
3776
- if (ToolConverter.isOpenAIToolFormat(tool)) {
3777
- return tool;
3778
- } else {
3779
- return ToolConverter.convertAnthropicToolToOpenAI(tool);
4106
+ convertToAnthropicSSE(openaiStream, modelName) {
4107
+ const lines = openaiStream.split("\n");
4108
+ const sseLines = [];
4109
+ const state = this.createConversionState();
4110
+ sseLines.push(
4111
+ "event: message_start",
4112
+ `data: {"type":"message_start","message":{"id":"msg_${Date.now()}","type":"message","role":"assistant","model":"${modelName}","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":0,"output_tokens":0}}}`,
4113
+ ""
4114
+ );
4115
+ for (const line of lines) {
4116
+ if (line.startsWith("data:")) {
4117
+ const dataLine = line.substring(5);
4118
+ if (dataLine.trim() === "[DONE]") {
4119
+ this.addFinalEvents(state, sseLines);
4120
+ break;
4121
+ }
4122
+ try {
4123
+ const chunk = JSON.parse(dataLine);
4124
+ this.processStreamChunk(chunk, state, sseLines);
4125
+ } catch (error) {
4126
+ if (this.config.debugMode) {
4127
+ this.config.logger.warn("Failed to parse stream chunk", { line: dataLine.substring(0, 200) });
4128
+ }
4129
+ }
3780
4130
  }
3781
- });
4131
+ }
4132
+ return sseLines.join("\n");
3782
4133
  }
3783
4134
  /**
3784
- * 验证Claude请求格式
4135
+ * 处理单个流式数据块 - 支持thinking和content双模式
3785
4136
  */
3786
- validateClaudeRequest(request) {
3787
- return FormatValidator.validateClaudeRequest(request);
4137
+ processStreamChunk(chunk, state, sseLines) {
4138
+ if (this.isResponsesEvent(chunk)) {
4139
+ this.processResponsesEvent(chunk, state, sseLines);
4140
+ return;
4141
+ }
4142
+ const choice = chunk.choices?.[0];
4143
+ if (choice) {
4144
+ const hasToolCalls = choice.delta?.tool_calls;
4145
+ const hasFinishReason = choice.finish_reason;
4146
+ const isNonText = !choice.delta?.content;
4147
+ if (this.config.debugMode && (hasToolCalls || hasFinishReason || isNonText && choice.delta)) {
4148
+ this.logDebug("Streaming chunk processed", { chunk });
4149
+ }
4150
+ }
4151
+ if (!choice) {
4152
+ this.updateUsageFromChunk(chunk, state);
4153
+ return;
4154
+ }
4155
+ const delta = choice.delta ?? {};
4156
+ this.appendThinkingContent(this.coalesceContent(delta.reasoning_content), state, sseLines);
4157
+ this.appendTextContent(this.coalesceContent(delta.content), state, sseLines);
4158
+ if (delta.tool_calls) {
4159
+ this.processToolCalls(delta.tool_calls, state, sseLines);
4160
+ }
4161
+ this.updateUsageFromChunk(chunk, state);
3788
4162
  }
3789
4163
  /**
3790
- * 验证OpenAI请求格式
4164
+ * 处理工具调用 - 支持OpenAI流式分块累积
4165
+ * OpenAI流式API会将tool_calls分多个chunk发送:
4166
+ * - Chunk 1: {index:0, id:"call_xxx", type:"function", function:{name:"web_search"}}
4167
+ * - Chunk 2: {index:0, function:{arguments:"{\"query\":\"xxx\"}"}}
4168
+ * - Chunk N: 继续累积arguments
3791
4169
  */
3792
- validateOpenAIRequest(request) {
3793
- return FormatValidator.validateOpenAIRequest(request);
4170
+ processToolCalls(toolCalls, state, sseLines) {
4171
+ this.logDebug("processToolCalls called", { toolCalls });
4172
+ for (const toolCall of toolCalls) {
4173
+ const index = toolCall.index ?? 0;
4174
+ const toolId = toolCall.id;
4175
+ const toolName = toolCall.function?.name;
4176
+ const toolArgs = toolCall.function?.arguments;
4177
+ this.logDebug(`Processing tool chunk for index ${index}`, {
4178
+ hasId: !!toolId,
4179
+ hasName: !!toolName,
4180
+ hasArgs: !!toolArgs,
4181
+ argsLength: toolArgs?.length
4182
+ });
4183
+ const stateKey = `openai_tool_${index}`;
4184
+ const toolData = this.getOrCreateToolCallState(state, stateKey);
4185
+ if (toolId && !toolData.id) {
4186
+ toolData.id = toolId;
4187
+ }
4188
+ if (toolName) {
4189
+ toolData.name = toolName;
4190
+ }
4191
+ this.registerToolCallAlias(state, toolId ? `openai_tool_id_${toolId}` : void 0, toolData);
4192
+ this.registerToolCallAlias(state, `openai_tool_index_${index}`, toolData);
4193
+ if (toolArgs) {
4194
+ toolData.pendingChunks.push(toolArgs);
4195
+ this.logDebug(`Accumulated tool arguments for index ${index}`, {
4196
+ currentLength: toolData.pendingChunks.reduce((acc, chunk) => acc + chunk.length, 0)
4197
+ });
4198
+ }
4199
+ const started = this.maybeStartToolBlock(toolData, state, sseLines);
4200
+ if (started || toolData.blockStartSent) {
4201
+ this.flushPendingToolChunks(toolData, sseLines);
4202
+ }
4203
+ }
3794
4204
  }
3795
- /**
3796
- * 获取支持的工具列表
3797
- */
3798
- getSupportedTools() {
3799
- return [];
4205
+ getOrCreateToolCallState(state, key) {
4206
+ let existing = state.toolCallsMap.get(key);
4207
+ if (!existing) {
4208
+ existing = {
4209
+ id: "",
4210
+ name: "",
4211
+ input: "",
4212
+ blockStartSent: false,
4213
+ blockStopSent: false,
4214
+ pendingChunks: []
4215
+ };
4216
+ state.toolCallsMap.set(key, existing);
4217
+ }
4218
+ return existing;
3800
4219
  }
3801
- /**
3802
- * 检查工具是否支持
3803
- */
3804
- isToolSupported(_toolName) {
4220
+ registerToolCallAlias(state, alias, toolData) {
4221
+ if (!alias) return;
4222
+ const current = state.toolCallsMap.get(alias);
4223
+ if (!current || current !== toolData) {
4224
+ state.toolCallsMap.set(alias, toolData);
4225
+ }
4226
+ }
4227
+ maybeStartToolBlock(toolData, state, sseLines) {
4228
+ if (toolData.blockStartSent) return false;
4229
+ if (!toolData.name) {
4230
+ return false;
4231
+ }
4232
+ if (!toolData.id) {
4233
+ toolData.id = `call_${++state.toolCallCounter}`;
4234
+ }
4235
+ const blockIndex = toolData.blockIndex ?? state.nextToolBlockIndex++;
4236
+ toolData.blockIndex = blockIndex;
4237
+ sseLines.push(
4238
+ "event: content_block_start",
4239
+ `data: {"type":"content_block_start","index":${blockIndex},"content_block":{"type":"tool_use","id":"${this.escapeJsonString(toolData.id)}","name":"${this.escapeJsonString(toolData.name)}","input":{}}}`,
4240
+ ""
4241
+ );
4242
+ toolData.blockStartSent = true;
4243
+ this.logDebug("Sent content_block_start", { toolName: toolData.name, blockIndex });
3805
4244
  return true;
3806
4245
  }
3807
- /**
3808
- * 获取工具映射(已弃用,保持兼容性)
3809
- */
3810
- getToolMapping(claudeToolName) {
3811
- return claudeToolName;
4246
+ flushPendingToolChunks(toolData, sseLines) {
4247
+ if (!toolData.blockStartSent || toolData.blockIndex === void 0) {
4248
+ return;
4249
+ }
4250
+ while (toolData.pendingChunks.length > 0) {
4251
+ const chunk = toolData.pendingChunks.shift();
4252
+ if (chunk === void 0) continue;
4253
+ toolData.input += chunk;
4254
+ sseLines.push(
4255
+ "event: content_block_delta",
4256
+ `data: {"type":"content_block_delta","index":${toolData.blockIndex},"delta":{"type":"input_json_delta","partial_json":${JSON.stringify(chunk)}}}`,
4257
+ ""
4258
+ );
4259
+ this.logDebug("Sent input_json_delta", { blockIndex: toolData.blockIndex });
4260
+ }
4261
+ }
4262
+ coalesceContent(content) {
4263
+ if (!content) return void 0;
4264
+ if (typeof content === "string") return content;
4265
+ if (Array.isArray(content)) {
4266
+ return content.map((item) => {
4267
+ if (typeof item === "string") return item;
4268
+ if (typeof item?.text === "string") return item.text;
4269
+ if (typeof item?.content === "string") return item.content;
4270
+ return "";
4271
+ }).join("");
4272
+ }
4273
+ if (typeof content === "object" && typeof content.text === "string") {
4274
+ return content.text;
4275
+ }
4276
+ return void 0;
4277
+ }
4278
+ appendThinkingContent(content, state, sseLines) {
4279
+ if (!content) return;
4280
+ state.reasoningContent += content;
4281
+ if (!state.thinkingBlockStarted) {
4282
+ if (state.contentBlockStarted) {
4283
+ sseLines.push(
4284
+ "event: content_block_stop",
4285
+ 'data: {"type":"content_block_stop","index":0}',
4286
+ ""
4287
+ );
4288
+ state.contentBlockStarted = false;
4289
+ }
4290
+ sseLines.push(
4291
+ "event: content_block_start",
4292
+ 'data: {"type":"content_block_start","index":0,"content_block":{"type":"thinking","thinking":""}}',
4293
+ ""
4294
+ );
4295
+ state.thinkingBlockStarted = true;
4296
+ }
4297
+ sseLines.push(
4298
+ "event: content_block_delta",
4299
+ `data: {"type":"content_block_delta","index":0,"delta":{"type":"thinking_delta","thinking":"${this.escapeJsonString(content)}"}}`,
4300
+ ""
4301
+ );
3812
4302
  }
3813
- /**
3814
- * 更新配置
3815
- */
3816
- updateConfig(newConfig) {
3817
- this.config = { ...this.config, ...newConfig };
4303
+ appendTextContent(content, state, sseLines) {
4304
+ if (!content || content === "") return;
4305
+ if (state.thinkingBlockStarted && !state.contentBlockStarted) {
4306
+ sseLines.push(
4307
+ "event: content_block_stop",
4308
+ 'data: {"type":"content_block_stop","index":0}',
4309
+ "",
4310
+ "event: content_block_start",
4311
+ 'data: {"type":"content_block_start","index":1,"content_block":{"type":"text","text":""}}',
4312
+ ""
4313
+ );
4314
+ state.contentBlockStarted = true;
4315
+ } else if (!state.contentBlockStarted && !state.thinkingBlockStarted) {
4316
+ sseLines.push(
4317
+ "event: content_block_start",
4318
+ 'data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}',
4319
+ ""
4320
+ );
4321
+ state.contentBlockStarted = true;
4322
+ }
4323
+ state.textContent += content;
4324
+ const blockIndex = state.thinkingBlockStarted ? 1 : 0;
4325
+ sseLines.push(
4326
+ "event: content_block_delta",
4327
+ `data: {"type":"content_block_delta","index":${blockIndex},"delta":{"type":"text_delta","text":"${this.escapeJsonString(content)}"}}`,
4328
+ ""
4329
+ );
3818
4330
  }
3819
- /**
3820
- * 获取当前配置
3821
- */
3822
- getConfig() {
3823
- return { ...this.config };
4331
+ updateUsageFromChunk(chunk, state) {
4332
+ const usage = chunk?.usage || chunk?.response?.usage;
4333
+ if (!usage) return;
4334
+ if (typeof usage.prompt_tokens === "number") {
4335
+ state.usage.input_tokens = usage.prompt_tokens;
4336
+ }
4337
+ if (typeof usage.completion_tokens === "number") {
4338
+ state.usage.output_tokens = usage.completion_tokens;
4339
+ }
4340
+ if (typeof usage.input_tokens === "number") {
4341
+ state.usage.input_tokens = usage.input_tokens;
4342
+ }
4343
+ if (typeof usage.output_tokens === "number") {
4344
+ state.usage.output_tokens = usage.output_tokens;
4345
+ }
3824
4346
  }
3825
- /**
3826
- * 执行带验证的核心转换(同步版本)
3827
- * 为静态方法提供增强功能,但保持同步特性
3828
- */
3829
- performCoreConversionWithValidation(anthropicRequest) {
3830
- if (this.config.validation.enabled) {
3831
- try {
3832
- validateAnthropicRequest(anthropicRequest);
3833
- } catch (error) {
3834
- if (this.config.validation.strict) {
3835
- throw error;
3836
- } else {
3837
- const errorSummary = this.getValidationErrorSummary(error);
3838
- console.warn(`[A2ORequestAdapter] Input validation warning: ${errorSummary}. Details saved to logs.`);
3839
- }
3840
- }
4347
+ isResponsesEvent(chunk) {
4348
+ return typeof chunk?.type === "string" && chunk.type.startsWith("response.");
4349
+ }
4350
+ processResponsesEvent(event, state, sseLines) {
4351
+ this.updateUsageFromChunk(event, state);
4352
+ switch (event.type) {
4353
+ case "response.output_item.added":
4354
+ this.handleResponsesOutputItemAdded(event, state, sseLines);
4355
+ break;
4356
+ case "response.function_call_arguments.delta":
4357
+ this.handleResponsesFunctionArgumentsDelta(event, state, sseLines);
4358
+ break;
4359
+ case "response.function_call_arguments.done":
4360
+ case "response.output_item.done":
4361
+ this.handleResponsesFunctionArgumentsDone(event, state, sseLines);
4362
+ break;
4363
+ case "response.output_text.delta":
4364
+ case "response.text.delta":
4365
+ this.appendTextContent(this.extractResponsesTextDelta(event), state, sseLines);
4366
+ break;
4367
+ case "response.output_text.done":
4368
+ case "response.text.done":
4369
+ break;
4370
+ case "response.thinking.delta":
4371
+ this.appendThinkingContent(this.extractResponsesThinkingDelta(event), state, sseLines);
4372
+ break;
4373
+ default:
4374
+ break;
3841
4375
  }
3842
- let processedRequest = anthropicRequest;
3843
- if (this.config.healing.enabled) {
3844
- try {
3845
- processedRequest = this.applySyncHealing(anthropicRequest);
3846
- } catch (healingError) {
3847
- console.warn("[A2ORequestAdapter] Healing failed:", healingError);
4376
+ }
4377
+ resolveResponsesToolData(identifiers, state) {
4378
+ const aliases = [];
4379
+ if (identifiers.call_id) aliases.push(`responses_call_${identifiers.call_id}`);
4380
+ if (identifiers.item_id) aliases.push(`responses_item_${identifiers.item_id}`);
4381
+ if (typeof identifiers.output_index === "number") aliases.push(`responses_index_${identifiers.output_index}`);
4382
+ let toolData;
4383
+ for (const alias of aliases) {
4384
+ const existing = state.toolCallsMap.get(alias);
4385
+ if (existing) {
4386
+ toolData = existing;
4387
+ break;
3848
4388
  }
3849
4389
  }
3850
- const result = this.performBasicConversion(processedRequest, true);
3851
- if (this.config.validation.enabled) {
3852
- try {
3853
- validateOpenAIRequest(result);
3854
- } catch (error) {
3855
- if (this.config.validation.strict) {
3856
- throw error;
3857
- } else {
3858
- console.warn("[A2ORequestAdapter] Output validation warning:", error);
3859
- }
4390
+ if (!toolData) {
4391
+ const baseAlias = aliases[0] ?? `responses_auto_${++state.toolCallCounter}`;
4392
+ toolData = this.getOrCreateToolCallState(state, baseAlias);
4393
+ if (!aliases.length) {
4394
+ aliases.push(baseAlias);
3860
4395
  }
3861
4396
  }
3862
- return result;
4397
+ for (const alias of aliases) {
4398
+ this.registerToolCallAlias(state, alias, toolData);
4399
+ }
4400
+ return toolData;
3863
4401
  }
3864
- /**
3865
- * 执行基础转换逻辑(原有逻辑的提取)
3866
- */
3867
- performBasicConversion(anthropicRequest, skipValidation = false) {
3868
- if (!skipValidation && this.config.enableFormatValidation) {
3869
- FormatValidator.validateClaudeRequest(anthropicRequest);
4402
+ handleResponsesOutputItemAdded(event, state, sseLines) {
4403
+ const item = event?.item;
4404
+ if (!item) return;
4405
+ const itemType = item.type;
4406
+ if (itemType !== "function_call" && itemType !== "tool_call") {
4407
+ return;
3870
4408
  }
3871
- const openaiRequest = {
3872
- model: anthropicRequest.model,
3873
- messages: MessageConverter.convertMessages(anthropicRequest.messages, anthropicRequest.system),
3874
- max_tokens: anthropicRequest.max_tokens,
3875
- temperature: anthropicRequest.temperature,
3876
- stream: anthropicRequest.stream,
3877
- n: 1
3878
- };
3879
- if (anthropicRequest.tools && anthropicRequest.tools.length > 0) {
3880
- openaiRequest.tools = this.convertToolDefinitions(anthropicRequest.tools);
4409
+ const toolData = this.resolveResponsesToolData(
4410
+ { call_id: item.call_id ?? item.id, item_id: item.id, output_index: event.output_index },
4411
+ state
4412
+ );
4413
+ if (!toolData.id) {
4414
+ toolData.id = item.call_id || item.id || `call_${++state.toolCallCounter}`;
3881
4415
  }
3882
- const specialFields = ["_anthropic_protocol", "_rovo_tool_injected", "_routeResult"];
3883
- for (const field of specialFields) {
3884
- if (anthropicRequest[field] !== void 0) {
3885
- openaiRequest[field] = anthropicRequest[field];
3886
- }
4416
+ const name = item.name ?? item.function?.name ?? item.function_call?.name;
4417
+ if (name) {
4418
+ toolData.name = name;
3887
4419
  }
3888
- if (this.config.enableFormatValidation && !FormatValidator.validateOpenAIRequest(openaiRequest)) {
3889
- throw new Error("Generated OpenAI request format is invalid");
4420
+ if (typeof item.arguments === "string" && item.arguments.length > 0) {
4421
+ toolData.pendingChunks.push(item.arguments);
4422
+ }
4423
+ const started = this.maybeStartToolBlock(toolData, state, sseLines);
4424
+ if (started || toolData.blockStartSent) {
4425
+ this.flushPendingToolChunks(toolData, sseLines);
3890
4426
  }
3891
- return openaiRequest;
3892
4427
  }
3893
- /**
3894
- * 应用同步修复逻辑
3895
- * 简化版的修复,不依赖异步操作
3896
- */
3897
- applySyncHealing(request) {
3898
- const healedRequest = { ...request };
3899
- if (!healedRequest.max_tokens || healedRequest.max_tokens <= 0) {
3900
- healedRequest.max_tokens = 4096;
4428
+ handleResponsesFunctionArgumentsDelta(event, state, sseLines) {
4429
+ const toolData = this.resolveResponsesToolData(
4430
+ { call_id: event.call_id, item_id: event.item_id, output_index: event.output_index },
4431
+ state
4432
+ );
4433
+ if (!toolData.id && event.call_id) {
4434
+ toolData.id = event.call_id;
3901
4435
  }
3902
- if (!healedRequest.messages || !Array.isArray(healedRequest.messages)) {
3903
- throw new Error("Invalid messages array");
4436
+ const name = event.name ?? event.function_name ?? event.function?.name;
4437
+ if (name) {
4438
+ toolData.name = name;
3904
4439
  }
3905
- if (!healedRequest.model) {
3906
- healedRequest.model = "claude-sonnet-4";
4440
+ const argsChunk = this.extractArgumentsDelta(event);
4441
+ if (argsChunk) {
4442
+ toolData.pendingChunks.push(argsChunk);
3907
4443
  }
3908
- for (const message of healedRequest.messages) {
3909
- if (!message.role) {
3910
- message.role = "user";
4444
+ const started = this.maybeStartToolBlock(toolData, state, sseLines);
4445
+ if (started || toolData.blockStartSent) {
4446
+ this.flushPendingToolChunks(toolData, sseLines);
4447
+ }
4448
+ }
4449
+ handleResponsesFunctionArgumentsDone(event, state, sseLines) {
4450
+ const toolData = this.resolveResponsesToolData(
4451
+ { call_id: event.call_id, item_id: event.item_id, output_index: event.output_index },
4452
+ state
4453
+ );
4454
+ if (typeof event.arguments === "string" && event.arguments.length > 0) {
4455
+ toolData.pendingChunks.push(event.arguments);
4456
+ }
4457
+ const started = this.maybeStartToolBlock(toolData, state, sseLines);
4458
+ if (started || toolData.blockStartSent) {
4459
+ this.flushPendingToolChunks(toolData, sseLines);
4460
+ }
4461
+ if (toolData.blockStartSent && !toolData.blockStopSent && toolData.blockIndex !== void 0) {
4462
+ sseLines.push(
4463
+ "event: content_block_stop",
4464
+ `data: {"type":"content_block_stop","index":${toolData.blockIndex}}`,
4465
+ ""
4466
+ );
4467
+ toolData.blockStopSent = true;
4468
+ if (toolData.id && !state.completedToolCalls.includes(toolData.id)) {
4469
+ state.completedToolCalls.push(toolData.id);
4470
+ }
4471
+ this.logDebug("Sent content_block_stop", { toolName: toolData.name, blockIndex: toolData.blockIndex });
4472
+ }
4473
+ }
4474
+ extractResponsesTextDelta(event) {
4475
+ if (!event) return void 0;
4476
+ if (typeof event.delta === "string") return event.delta;
4477
+ if (event.delta && typeof event.delta.text === "string") return event.delta.text;
4478
+ if (typeof event.text === "string") return event.text;
4479
+ if (Array.isArray(event.output_text)) {
4480
+ return event.output_text.map((item) => item?.text ?? "").join("");
4481
+ }
4482
+ return void 0;
4483
+ }
4484
+ extractResponsesThinkingDelta(event) {
4485
+ if (!event) return void 0;
4486
+ if (typeof event.delta === "string") return event.delta;
4487
+ if (event.delta && typeof event.delta.thinking === "string") return event.delta.thinking;
4488
+ if (typeof event.text === "string") return event.text;
4489
+ return void 0;
4490
+ }
4491
+ extractArgumentsDelta(event) {
4492
+ if (!event) return void 0;
4493
+ if (typeof event.delta === "string") return event.delta;
4494
+ if (event.delta && typeof event.delta.arguments === "string") return event.delta.arguments;
4495
+ if (typeof event.arguments_delta === "string") return event.arguments_delta;
4496
+ if (typeof event.arguments === "string") return event.arguments;
4497
+ if (typeof event.partial_json === "string") return event.partial_json;
4498
+ return void 0;
4499
+ }
4500
+ /**
4501
+ * 在流结束时关闭所有未关闭的工具调用块
4502
+ */
4503
+ closeAllToolCallBlocks(state, sseLines) {
4504
+ const processed = /* @__PURE__ */ new Set();
4505
+ for (const toolData of state.toolCallsMap.values()) {
4506
+ if (processed.has(toolData)) continue;
4507
+ processed.add(toolData);
4508
+ if (!toolData.blockStartSent && toolData.pendingChunks.length > 0) {
4509
+ if (!toolData.name) {
4510
+ toolData.name = "unknown_tool";
4511
+ }
4512
+ const started = this.maybeStartToolBlock(toolData, state, sseLines);
4513
+ if (started) {
4514
+ this.flushPendingToolChunks(toolData, sseLines);
4515
+ }
3911
4516
  }
3912
- if (!message.content) {
3913
- message.content = "";
4517
+ if (toolData.blockStartSent && !toolData.blockStopSent && toolData.blockIndex !== void 0) {
4518
+ this.flushPendingToolChunks(toolData, sseLines);
4519
+ sseLines.push(
4520
+ "event: content_block_stop",
4521
+ `data: {"type":"content_block_stop","index":${toolData.blockIndex}}`,
4522
+ ""
4523
+ );
4524
+ toolData.blockStopSent = true;
4525
+ if (toolData.id && !state.completedToolCalls.includes(toolData.id)) {
4526
+ state.completedToolCalls.push(toolData.id);
4527
+ }
4528
+ this.logDebug("Sent content_block_stop", { toolName: toolData.name, blockIndex: toolData.blockIndex });
3914
4529
  }
3915
4530
  }
3916
- return healedRequest;
3917
4531
  }
3918
4532
  /**
3919
- * 获取验证错误详情
4533
+ * 添加最终事件 - 支持thinking+content双模式
3920
4534
  */
3921
- getValidationErrors(request, type) {
3922
- return FormatValidator.getValidationErrors(request, type);
4535
+ addFinalEvents(state, sseLines) {
4536
+ this.closeAllToolCallBlocks(state, sseLines);
4537
+ if (state.contentBlockStarted) {
4538
+ const blockIndex = state.thinkingBlockStarted ? 1 : 0;
4539
+ sseLines.push(
4540
+ "event: content_block_stop",
4541
+ `data: {"type":"content_block_stop","index":${blockIndex}}`,
4542
+ ""
4543
+ );
4544
+ } else if (state.thinkingBlockStarted) {
4545
+ sseLines.push(
4546
+ "event: content_block_stop",
4547
+ 'data: {"type":"content_block_stop","index":0}',
4548
+ ""
4549
+ );
4550
+ }
4551
+ const stopReason = state.completedToolCalls.length > 0 ? "tool_use" : "end_turn";
4552
+ const usagePayload = state.usage.input_tokens > 0 ? `{"input_tokens":${state.usage.input_tokens},"output_tokens":${state.usage.output_tokens}}` : `{"output_tokens":${state.usage.output_tokens}}`;
4553
+ sseLines.push(
4554
+ "event: message_delta",
4555
+ `data: {"type":"message_delta","delta":{"stop_reason":"${stopReason}","stop_sequence":null},"usage":${usagePayload}}`,
4556
+ "",
4557
+ "event: message_stop",
4558
+ 'data: {"type":"message_stop"}',
4559
+ ""
4560
+ );
3923
4561
  }
3924
4562
  /**
3925
- * 生成简洁的验证错误摘要
4563
+ * 构建标准响应格式
3926
4564
  */
3927
- getValidationErrorSummary(error) {
3928
- if (error?.issues?.length > 0) {
3929
- const invalidEnums = error.issues.filter((i) => i.code === "invalid_enum_value");
3930
- const missingFields = error.issues.filter((i) => i.code === "invalid_type");
3931
- const summary = [];
3932
- if (invalidEnums.length > 0) {
3933
- const first = invalidEnums[0];
3934
- summary.push(`invalid_${first.path?.join(".")}: '${first.received}'`);
3935
- }
3936
- if (missingFields.length > 0) {
3937
- summary.push(`${missingFields.length} missing fields`);
4565
+ buildStandardResponse(openaiStream) {
4566
+ const state = this.createConversionState();
4567
+ const lines = openaiStream.split("\n");
4568
+ const noopSseLines = [];
4569
+ for (const line of lines) {
4570
+ if (line.startsWith("data:")) {
4571
+ const dataLine = line.startsWith("data: ") ? line.substring(6) : line.substring(5);
4572
+ if (dataLine.trim() === "[DONE]") break;
4573
+ try {
4574
+ const chunk = JSON.parse(dataLine);
4575
+ noopSseLines.length = 0;
4576
+ this.processStreamChunk(chunk, state, noopSseLines);
4577
+ } catch (error) {
4578
+ }
3938
4579
  }
3939
- return summary.slice(0, 2).join(", ") + (error.issues.length > 5 ? ` (+${error.issues.length - 5} more)` : "");
3940
4580
  }
3941
- return error.message || "Validation failed";
4581
+ const stopReason = state.completedToolCalls.length > 0 ? "tool_use" : "end_turn";
4582
+ return {
4583
+ id: `msg_${Date.now()}`,
4584
+ type: "message",
4585
+ role: "assistant",
4586
+ content: state.textContent ? [
4587
+ {
4588
+ type: "text",
4589
+ text: state.textContent
4590
+ }
4591
+ ] : [],
4592
+ model: "claude-3-sonnet-20240229",
4593
+ stop_reason: stopReason,
4594
+ stop_sequence: null,
4595
+ usage: state.usage
4596
+ };
3942
4597
  }
3943
- };
3944
- var A2ORequestAdapterStatic = {
3945
4598
  /**
3946
- * 转换Anthropic请求格式为OpenAI兼容格式(静态方法)
3947
- * 内部使用增强转换器,所有调用点自动获得增强功能
4599
+ * 创建转换状态对象
3948
4600
  */
3949
- convertAnthropicRequestToOpenAI: (anthropicRequest) => {
3950
- const adapter = new A2ORequestAdapter({
3951
- debugMode: false,
3952
- maxDescriptionLength: 100,
3953
- enableToolNameValidation: true,
3954
- enableFormatValidation: true,
3955
- validation: { enabled: true, strict: false },
3956
- healing: { enabled: true, maxAttempts: 2, enableCustomRules: true },
3957
- recovery: { enabled: false, maxRetries: 0, backoffMs: 1e3 },
3958
- monitoring: { enabled: false, logLevel: "none", enableMetrics: false }
3959
- });
3960
- try {
3961
- const result = adapter.performCoreConversionWithValidation(anthropicRequest);
3962
- return result;
3963
- } catch (error) {
3964
- console.warn(`[A2ORequestAdapterStatic] Enhanced conversion failed, using basic conversion: ${error?.message || error}`);
3965
- return adapter.performBasicConversion(anthropicRequest, true);
4601
+ createConversionState() {
4602
+ return {
4603
+ processedLines: 0,
4604
+ textContent: "",
4605
+ reasoningContent: "",
4606
+ toolCallsMap: /* @__PURE__ */ new Map(),
4607
+ completedToolCalls: [],
4608
+ allSSELines: [],
4609
+ errors: [],
4610
+ usage: {
4611
+ input_tokens: 0,
4612
+ output_tokens: 0
4613
+ },
4614
+ thinkingBlockStarted: false,
4615
+ contentBlockStarted: false,
4616
+ toolCallCounter: 0,
4617
+ nextToolBlockIndex: 1
4618
+ };
4619
+ }
4620
+ parseAnthropicSSEEvent(rawEvent) {
4621
+ const lines = rawEvent.split("\n");
4622
+ let eventType = null;
4623
+ const dataLines = [];
4624
+ for (const line of lines) {
4625
+ if (line.startsWith("event:")) {
4626
+ eventType = line.slice(6).trim();
4627
+ } else if (line.startsWith("data:")) {
4628
+ dataLines.push(line.slice(5).trim());
4629
+ }
3966
4630
  }
3967
- },
4631
+ const dataString = dataLines.join("\n");
4632
+ let data = null;
4633
+ if (dataString) {
4634
+ try {
4635
+ data = JSON.parse(dataString);
4636
+ } catch (error) {
4637
+ this.logDebug("Failed to parse Anthropic SSE JSON", { error });
4638
+ }
4639
+ }
4640
+ return { eventType, data };
4641
+ }
4642
+ extractTextFromAnthropicDelta(data) {
4643
+ const delta = data?.delta;
4644
+ if (!delta) return null;
4645
+ if (typeof delta.text === "string") {
4646
+ return delta.text;
4647
+ }
4648
+ if (delta.type === "text_delta" && typeof delta.text === "string") {
4649
+ return delta.text;
4650
+ }
4651
+ return null;
4652
+ }
4653
+ mapAnthropicStopReasonToOpenAI(reason) {
4654
+ switch (reason) {
4655
+ case "max_tokens":
4656
+ return "length";
4657
+ case "tool_use":
4658
+ return "tool_calls";
4659
+ case "stop_sequence":
4660
+ case "end_turn":
4661
+ default:
4662
+ return "stop";
4663
+ }
4664
+ }
4665
+ buildOpenAIStreamChunk(model, content, finishReason = null) {
4666
+ return {
4667
+ id: `chatcmpl-${Date.now()}`,
4668
+ object: "chat.completion.chunk",
4669
+ created: Math.floor(Date.now() / 1e3),
4670
+ model,
4671
+ choices: [{
4672
+ index: 0,
4673
+ delta: content ? { content } : {},
4674
+ finish_reason: finishReason
4675
+ }]
4676
+ };
4677
+ }
3968
4678
  /**
3969
- * 转换OpenAI响应格式为Claude兼容格式(静态方法)
3970
- * 内部使用增强转换器
4679
+ * 转换消息格式
3971
4680
  */
3972
- convertOpenAIResponseToClaude: (openaiResponse) => {
3973
- const adapter = new A2ORequestAdapter({
3974
- debugMode: false,
3975
- maxDescriptionLength: 100,
3976
- enableToolNameValidation: true,
3977
- enableFormatValidation: true,
3978
- validation: { enabled: true, strict: false },
3979
- healing: { enabled: true, maxAttempts: 2, enableCustomRules: true },
3980
- recovery: { enabled: false, maxRetries: 0, backoffMs: 1e3 },
3981
- monitoring: { enabled: false, logLevel: "none", enableMetrics: false }
3982
- });
3983
- return adapter.convertOpenAIResponseToClaude(openaiResponse);
3984
- },
4681
+ convertMessages(messages) {
4682
+ return messages.map((msg) => ({
4683
+ role: msg.role,
4684
+ content: msg.content
4685
+ }));
4686
+ }
4687
+ /**
4688
+ * 映射Anthropic模型到OpenAI模型
4689
+ */
4690
+ mapAnthropicModelToOpenAI(model) {
4691
+ const supportedModels = [
4692
+ "glm-4.5",
4693
+ "kimi-k2",
4694
+ "deepseek-v3.1",
4695
+ "deepseek-r1",
4696
+ "deepseek-v3",
4697
+ "qwen3-32b",
4698
+ "qwen3-coder",
4699
+ "qwen3-235b",
4700
+ "tstars2.0"
4701
+ ];
4702
+ if (supportedModels.includes(model)) {
4703
+ return model;
4704
+ }
4705
+ const mapping = {
4706
+ "claude-3-sonnet-20240229": "glm-4.5",
4707
+ "claude-3-haiku-20240307": "kimi-k2",
4708
+ "claude-3-opus-20240229": "deepseek-v3.1"
4709
+ };
4710
+ return mapping[model] || "glm-4.5";
4711
+ }
3985
4712
  /**
3986
- * 验证Claude请求格式(静态方法)
4713
+ * 检查请求是否包含图片内容
3987
4714
  */
3988
- validateClaudeRequest: (request) => {
3989
- return FormatValidator.validateClaudeRequest(request);
3990
- },
4715
+ hasImageContent(request) {
4716
+ return request.messages.some(
4717
+ (msg) => Array.isArray(msg.content) && msg.content.some((content) => content?.type === "image")
4718
+ );
4719
+ }
3991
4720
  /**
3992
- * 验证OpenAI请求格式(静态方法)
4721
+ * 转义JSON字符串
3993
4722
  */
3994
- validateOpenAIRequest: (request) => {
3995
- return FormatValidator.validateOpenAIRequest(request);
3996
- },
4723
+ escapeJsonString(str) {
4724
+ return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
4725
+ }
3997
4726
  /**
3998
- * 获取支持的工具列表(静态方法)
4727
+ * 获取初始SSE事件(message_start + ping)
3999
4728
  */
4000
- getSupportedTools: () => {
4001
- return [];
4002
- },
4729
+ getInitialSSEEvents(modelName = "claude-sonnet-4", messageId = `msg_${Date.now()}_${Math.random().toString(36).substring(2, 8)}`) {
4730
+ return [
4731
+ "event: message_start",
4732
+ `data: {"type":"message_start","message":{"id":"${messageId}","type":"message","role":"assistant","model":"${modelName}","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":0,"output_tokens":0}}}`,
4733
+ "",
4734
+ "event: ping",
4735
+ 'data: {"type":"ping"}',
4736
+ ""
4737
+ ];
4738
+ }
4003
4739
  /**
4004
- * 检查工具是否支持(静态方法)
4740
+ * 增量转换单个OpenAI数据块为Anthropic SSE事件
4741
+ * 用于逐个处理流式数据片段
4005
4742
  */
4006
- isToolSupported: (_toolName) => {
4007
- return true;
4008
- },
4743
+ convertIncrementalChunk(openaiDataLine, state) {
4744
+ const logger = this.config.logger;
4745
+ const sseEvents = [];
4746
+ state.processedLines += 1;
4747
+ if (openaiDataLine.trim() === "[DONE]") {
4748
+ this.addFinalEvents(state, sseEvents);
4749
+ state.allSSELines.push(...sseEvents);
4750
+ return sseEvents;
4751
+ }
4752
+ try {
4753
+ const chunk = JSON.parse(openaiDataLine);
4754
+ this.processStreamChunk(chunk, state, sseEvents);
4755
+ if (sseEvents.length > 0) {
4756
+ state.allSSELines.push(...sseEvents);
4757
+ }
4758
+ return sseEvents;
4759
+ } catch (error) {
4760
+ if (this.config.debugMode) {
4761
+ logger.warn("Failed to parse OpenAI stream chunk in convertIncrementalChunk", {
4762
+ line: openaiDataLine.substring(0, 200),
4763
+ error: error instanceof Error ? error.message : String(error)
4764
+ });
4765
+ }
4766
+ state.errors.push({
4767
+ error: error instanceof Error ? error.message : String(error),
4768
+ raw: openaiDataLine
4769
+ });
4770
+ return [];
4771
+ }
4772
+ }
4009
4773
  /**
4010
- * 获取工具映射(静态方法,已弃用)
4774
+ * 暴露内部状态创建方法,供外部增量处理流程使用。
4011
4775
  */
4012
- getToolMapping: (claudeToolName) => {
4013
- return claudeToolName;
4776
+ createIncrementalState() {
4777
+ return this.createConversionState();
4014
4778
  }
4015
4779
  };
4016
4780
 
@@ -4151,15 +4915,36 @@ var ToolCallProcessor = class _ToolCallProcessor {
4151
4915
  * 处理增量工具调用
4152
4916
  */
4153
4917
  static processIncrementalToolCalls(toolCalls, state, sseLines) {
4918
+ const debugEnabled = process.env.AI_PROTOCOL_DEBUG === "true";
4919
+ if (debugEnabled) {
4920
+ console.debug("[ToolProcessor] processIncrementalToolCalls called with:", JSON.stringify(toolCalls, null, 2));
4921
+ }
4154
4922
  for (const toolCall of toolCalls) {
4155
4923
  const toolId = toolCall.id;
4156
4924
  const toolName = toolCall.function?.name;
4157
4925
  const toolArgs = toolCall.function?.arguments;
4926
+ if (debugEnabled) {
4927
+ console.debug("[ToolProcessor] Processing tool call:", {
4928
+ toolId,
4929
+ toolName,
4930
+ hasArgs: !!toolArgs
4931
+ });
4932
+ }
4158
4933
  if (toolName && toolId && !state.toolCallsMap.has(toolId)) {
4934
+ if (debugEnabled) {
4935
+ console.debug("[ToolProcessor] Starting new tool call:", toolName);
4936
+ }
4159
4937
  _ToolCallProcessor.processToolCallStart(toolId, toolName, state, sseLines);
4160
4938
  }
4161
4939
  if (toolArgs) {
4940
+ if (debugEnabled) {
4941
+ console.debug("[ToolProcessor] Processing tool args, calling processToolArgs");
4942
+ }
4162
4943
  _ToolCallProcessor.processToolArgs(toolId, toolArgs, state, sseLines);
4944
+ } else if (toolName && toolId) {
4945
+ _ToolCallProcessor.processToolArgs(toolId, "", state, sseLines);
4946
+ } else {
4947
+ console.warn("\u26A0\uFE0F\u26A0\uFE0F\u26A0\uFE0F [ToolProcessor] No tool args to process! This will result in empty input!");
4163
4948
  }
4164
4949
  }
4165
4950
  }
@@ -4167,15 +4952,36 @@ var ToolCallProcessor = class _ToolCallProcessor {
4167
4952
  * 处理工具调用
4168
4953
  */
4169
4954
  static processBatchToolCalls(toolCalls, state, sseLines) {
4955
+ const debugEnabled = process.env.AI_PROTOCOL_DEBUG === "true";
4956
+ if (debugEnabled) {
4957
+ console.debug("[ToolProcessor] processBatchToolCalls called with:", JSON.stringify(toolCalls, null, 2));
4958
+ }
4170
4959
  for (const toolCall of toolCalls) {
4171
4960
  const toolId = toolCall.id;
4172
4961
  const toolName = toolCall.function?.name;
4173
4962
  const toolArgs = toolCall.function?.arguments;
4963
+ if (debugEnabled) {
4964
+ console.debug("[ToolProcessor] Processing batch tool call:", {
4965
+ toolId,
4966
+ toolName,
4967
+ hasArgs: !!toolArgs
4968
+ });
4969
+ }
4174
4970
  if (toolName && toolId && !state.toolCallsMap.has(toolId)) {
4971
+ if (debugEnabled) {
4972
+ console.debug("[ToolProcessor] Starting new batch tool call:", toolName);
4973
+ }
4175
4974
  _ToolCallProcessor.processToolCallStart(toolId, toolName, state, sseLines);
4176
4975
  }
4177
4976
  if (toolArgs) {
4977
+ if (debugEnabled) {
4978
+ console.debug("[ToolProcessor] Processing batch tool args, calling processToolArgs");
4979
+ }
4178
4980
  _ToolCallProcessor.processToolArgs(toolId, toolArgs, state, sseLines);
4981
+ } else if (toolName && toolId) {
4982
+ _ToolCallProcessor.processToolArgs(toolId, "", state, sseLines);
4983
+ } else {
4984
+ console.warn("\u26A0\uFE0F\u26A0\uFE0F\u26A0\uFE0F [ToolProcessor] No batch tool args to process! This will result in empty input!");
4179
4985
  }
4180
4986
  }
4181
4987
  }
@@ -4373,6 +5179,247 @@ function generateMessageId() {
4373
5179
  return `msg_${Date.now()}_${Math.random().toString(36).substring(2, 8)}`;
4374
5180
  }
4375
5181
 
5182
+ // src/core/o2a-sse-adapter/stream-converter.ts
5183
+ var StreamConverter = class {
5184
+ constructor(adapter, options = {}) {
5185
+ this.buffer = "";
5186
+ this.adapter = adapter;
5187
+ this.options = {
5188
+ bufferTimeout: 5e3,
5189
+ errorRecovery: true,
5190
+ maxRetries: 3,
5191
+ debug: false,
5192
+ ...options
5193
+ };
5194
+ this.state = this.adapter.createIncrementalState();
5195
+ this.stats = {
5196
+ chunksProcessed: 0,
5197
+ eventsGenerated: 0,
5198
+ errors: 0,
5199
+ retries: 0,
5200
+ startTime: Date.now(),
5201
+ lastUpdateTime: Date.now(),
5202
+ bufferSize: 0
5203
+ };
5204
+ if (this.options.debug) {
5205
+ console.log("[StreamConverter] \u5DF2\u521D\u59CB\u5316\uFF0C\u914D\u7F6E:", this.options);
5206
+ }
5207
+ }
5208
+ /**
5209
+ * 获取初始事件
5210
+ */
5211
+ getInitialEvents() {
5212
+ const events = this.adapter.getInitialSSEEvents(
5213
+ this.options.modelName,
5214
+ this.options.messageId
5215
+ );
5216
+ this.stats.eventsGenerated += events.length;
5217
+ this.stats.lastUpdateTime = Date.now();
5218
+ if (this.options.debug) {
5219
+ console.log("[StreamConverter] \u751F\u6210\u521D\u59CB\u4E8B\u4EF6:", events.length, "\u4E2A");
5220
+ }
5221
+ return events;
5222
+ }
5223
+ /**
5224
+ * 处理单个数据块
5225
+ */
5226
+ processChunk(chunk) {
5227
+ this.stats.chunksProcessed++;
5228
+ this.stats.lastUpdateTime = Date.now();
5229
+ if (this.options.debug) {
5230
+ console.log("[StreamConverter] \u5904\u7406\u6570\u636E\u5757:", chunk.substring(0, 100) + "...");
5231
+ }
5232
+ try {
5233
+ const events = this.processBufferedData(chunk);
5234
+ this.stats.eventsGenerated += events.length;
5235
+ if (this.options.onChunkProcessed) {
5236
+ this.options.onChunkProcessed(chunk, events);
5237
+ }
5238
+ return events;
5239
+ } catch (error) {
5240
+ return this.handleChunkError(error, chunk);
5241
+ }
5242
+ }
5243
+ /**
5244
+ * 结束流处理
5245
+ */
5246
+ finalize() {
5247
+ if (this.options.debug) {
5248
+ console.log("[StreamConverter] \u7ED3\u675F\u6D41\u5904\u7406\uFF0C\u7F13\u51B2\u533A\u5927\u5C0F:", this.buffer.length);
5249
+ }
5250
+ let events = [];
5251
+ if (this.buffer.trim()) {
5252
+ console.warn("[StreamConverter] \u7F13\u51B2\u533A\u4E2D\u6709\u672A\u5904\u7406\u6570\u636E\uFF0C\u5F3A\u5236\u5904\u7406:", this.buffer);
5253
+ events = this.processIncompleteBuffer();
5254
+ }
5255
+ try {
5256
+ const finalEvents = this.adapter.convertIncrementalChunk("[DONE]", this.state);
5257
+ events.push(...finalEvents);
5258
+ this.stats.eventsGenerated += finalEvents.length;
5259
+ } catch (error) {
5260
+ console.error("[StreamConverter] \u5904\u7406\u7ED3\u675F\u4E8B\u4EF6\u5931\u8D25:", error);
5261
+ }
5262
+ this.clearBufferTimeout();
5263
+ this.stats.lastUpdateTime = Date.now();
5264
+ if (this.options.debug) {
5265
+ console.log("[StreamConverter] \u6D41\u5904\u7406\u5B8C\u6210\uFF0C\u7EDF\u8BA1\u4FE1\u606F:", this.stats);
5266
+ }
5267
+ return events;
5268
+ }
5269
+ /**
5270
+ * 获取当前状态
5271
+ */
5272
+ getState() {
5273
+ return { ...this.state };
5274
+ }
5275
+ /**
5276
+ * 重置状态
5277
+ */
5278
+ reset() {
5279
+ this.state = this.adapter.createIncrementalState();
5280
+ this.buffer = "";
5281
+ this.clearBufferTimeout();
5282
+ this.stats = {
5283
+ chunksProcessed: 0,
5284
+ eventsGenerated: 0,
5285
+ errors: 0,
5286
+ retries: 0,
5287
+ startTime: Date.now(),
5288
+ lastUpdateTime: Date.now(),
5289
+ bufferSize: 0
5290
+ };
5291
+ if (this.options.debug) {
5292
+ console.log("[StreamConverter] \u72B6\u6001\u5DF2\u91CD\u7F6E");
5293
+ }
5294
+ }
5295
+ /**
5296
+ * 获取统计信息
5297
+ */
5298
+ getStats() {
5299
+ return {
5300
+ ...this.stats,
5301
+ bufferSize: this.buffer.length
5302
+ };
5303
+ }
5304
+ /**
5305
+ * 处理缓冲的数据
5306
+ */
5307
+ processBufferedData(newChunk) {
5308
+ this.buffer += newChunk;
5309
+ this.stats.bufferSize = this.buffer.length;
5310
+ const lines = this.buffer.split("\n");
5311
+ this.buffer = lines.pop() || "";
5312
+ const events = [];
5313
+ for (const line of lines) {
5314
+ if (line.startsWith("data:")) {
5315
+ const jsonStr = line.slice(5).trim();
5316
+ if (jsonStr && jsonStr !== "[DONE]") {
5317
+ const lineEvents = this.processDataLine(jsonStr);
5318
+ events.push(...lineEvents);
5319
+ } else if (jsonStr === "[DONE]") {
5320
+ const finalEvents = this.adapter.convertIncrementalChunk("[DONE]", this.state);
5321
+ events.push(...finalEvents);
5322
+ }
5323
+ }
5324
+ }
5325
+ this.resetBufferTimeout();
5326
+ return events;
5327
+ }
5328
+ /**
5329
+ * 处理单行数据
5330
+ */
5331
+ processDataLine(jsonStr, attempt = 0) {
5332
+ try {
5333
+ const chunkEvents = this.adapter.convertIncrementalChunk(jsonStr, this.state);
5334
+ if (this.options.debug && chunkEvents.length > 0) {
5335
+ console.log("[StreamConverter] \u751F\u6210\u4E8B\u4EF6:", chunkEvents.length, "\u4E2A");
5336
+ }
5337
+ return chunkEvents;
5338
+ } catch (error) {
5339
+ if (this.options.errorRecovery && attempt < (this.options.maxRetries || 3)) {
5340
+ console.warn(`[StreamConverter] \u5904\u7406\u6570\u636E\u884C\u5931\u8D25\uFF0C\u91CD\u8BD5 ${attempt + 1}/${this.options.maxRetries}:`, error);
5341
+ this.stats.retries++;
5342
+ return this.processDataLine(jsonStr, attempt + 1);
5343
+ }
5344
+ this.stats.errors++;
5345
+ console.error("[StreamConverter] \u5904\u7406\u6570\u636E\u884C\u6700\u7EC8\u5931\u8D25:", error, "Data:", jsonStr);
5346
+ if (this.options.onError) {
5347
+ this.options.onError(error, {
5348
+ chunk: jsonStr,
5349
+ state: this.state,
5350
+ attempt,
5351
+ totalRetries: this.stats.retries
5352
+ });
5353
+ }
5354
+ return [];
5355
+ }
5356
+ }
5357
+ /**
5358
+ * 处理块错误
5359
+ */
5360
+ handleChunkError(error, chunk) {
5361
+ this.stats.errors++;
5362
+ if (this.options.debug) {
5363
+ console.error("[StreamConverter] \u5757\u5904\u7406\u9519\u8BEF:", error.message);
5364
+ }
5365
+ if (!this.options.errorRecovery) {
5366
+ throw error;
5367
+ }
5368
+ this.state.errors.push(`Chunk processing error: ${error.message}`);
5369
+ if (this.options.onError) {
5370
+ this.options.onError(error, {
5371
+ chunk,
5372
+ state: this.state,
5373
+ totalRetries: this.stats.retries
5374
+ });
5375
+ }
5376
+ return [];
5377
+ }
5378
+ /**
5379
+ * 处理不完整的缓冲区数据
5380
+ */
5381
+ processIncompleteBuffer() {
5382
+ if (!this.buffer.trim()) {
5383
+ return [];
5384
+ }
5385
+ console.warn("[StreamConverter] \u5904\u7406\u4E0D\u5B8C\u6574\u7F13\u51B2\u533A\u6570\u636E:", this.buffer);
5386
+ if (this.buffer.startsWith("data:")) {
5387
+ const jsonStr = this.buffer.slice(5).trim();
5388
+ if (jsonStr) {
5389
+ return this.processDataLine(jsonStr);
5390
+ }
5391
+ }
5392
+ return [];
5393
+ }
5394
+ /**
5395
+ * 重置缓冲区超时
5396
+ */
5397
+ resetBufferTimeout() {
5398
+ this.clearBufferTimeout();
5399
+ if (this.options.bufferTimeout && this.options.bufferTimeout > 0) {
5400
+ this.bufferTimeout = setTimeout(() => {
5401
+ if (this.buffer.trim()) {
5402
+ console.warn("[StreamConverter] \u7F13\u51B2\u533A\u8D85\u65F6\uFF0C\u5F3A\u5236\u5904\u7406\u6570\u636E:", this.buffer);
5403
+ const events = this.processIncompleteBuffer();
5404
+ this.buffer = "";
5405
+ if (events.length > 0 && this.options.onChunkProcessed) {
5406
+ this.options.onChunkProcessed("TIMEOUT_FLUSH", events);
5407
+ }
5408
+ }
5409
+ }, this.options.bufferTimeout);
5410
+ }
5411
+ }
5412
+ /**
5413
+ * 清理缓冲区超时
5414
+ */
5415
+ clearBufferTimeout() {
5416
+ if (this.bufferTimeout) {
5417
+ clearTimeout(this.bufferTimeout);
5418
+ this.bufferTimeout = void 0;
5419
+ }
5420
+ }
5421
+ };
5422
+
4376
5423
  // src/core/o2a-sse-adapter/adapter.ts
4377
5424
  var O2ASSEAdapter = class {
4378
5425
  constructor(debugMode = false, config = {}) {
@@ -4544,6 +5591,17 @@ var O2ASSEAdapter = class {
4544
5591
  }
4545
5592
  try {
4546
5593
  const data = JSON.parse(dataContent);
5594
+ if ((data.choices?.length === 0 || !data.choices) && data.prompt_filter_results) {
5595
+ if (this.debugMode) {
5596
+ console.warn("\u26A0\uFE0F [O2ASSEAdapter] \u68C0\u6D4B\u5230Azure\u5185\u5BB9\u8FC7\u6EE4\u5668\u54CD\u5E94:", data.prompt_filter_results);
5597
+ }
5598
+ StreamingStateManager.processTextContent(
5599
+ `\u9519\u8BEF\uFF1A\u5185\u5BB9\u8FC7\u6EE4\u5668\u62E6\u622A\u4E86\u8BF7\u6C42\u3002\u8BF7\u68C0\u67E5\u8F93\u5165\u5185\u5BB9\u662F\u5426\u7B26\u5408\u4F7F\u7528\u653F\u7B56\u3002`,
5600
+ state,
5601
+ sseLines
5602
+ );
5603
+ break;
5604
+ }
4547
5605
  const choice = data.choices?.[0];
4548
5606
  const delta = choice?.delta;
4549
5607
  if (!delta) {
@@ -4587,6 +5645,19 @@ var O2ASSEAdapter = class {
4587
5645
  processNonStreamingResponse(data, state, sseLines) {
4588
5646
  const choice = data.choices?.[0];
4589
5647
  if (!choice) {
5648
+ if (data.prompt_filter_results || data.choices?.length === 0) {
5649
+ const errorMsg = "Azure\u5185\u5BB9\u8FC7\u6EE4\u5668\u62E6\u622A\u4E86\u8BF7\u6C42";
5650
+ const filterDetails = data.prompt_filter_results ? JSON.stringify(data.prompt_filter_results).substring(0, 500) : "choices\u4E3A\u7A7A";
5651
+ if (this.debugMode) {
5652
+ console.warn(`\u26A0\uFE0F [O2ASSEAdapter] ${errorMsg}:`, filterDetails);
5653
+ }
5654
+ StreamingStateManager.processTextContent(
5655
+ `\u9519\u8BEF\uFF1A\u5185\u5BB9\u8FC7\u6EE4\u5668\u62E6\u622A\u4E86\u8BF7\u6C42\u3002\u8BF7\u68C0\u67E5\u8F93\u5165\u5185\u5BB9\u662F\u5426\u7B26\u5408\u4F7F\u7528\u653F\u7B56\u3002`,
5656
+ state,
5657
+ sseLines
5658
+ );
5659
+ return;
5660
+ }
4590
5661
  if (this.debugMode) {
4591
5662
  console.warn("\u26A0\uFE0F [O2ASSEAdapter] \u975E\u6D41\u5F0F\u54CD\u5E94\u6CA1\u6709choices\u6570\u636E");
4592
5663
  }
@@ -4627,6 +5698,101 @@ var O2ASSEAdapter = class {
4627
5698
  validateClaudeSSE(sseContent) {
4628
5699
  return FormatValidator2.validateClaudeSSE(sseContent);
4629
5700
  }
5701
+ /**
5702
+ * 将 OpenAI Response 流直接转换为 Anthropic SSE 流
5703
+ * 这是新增的核心流式处理方法,支持实时转换
5704
+ */
5705
+ convertResponseStream(openaiResponse, options = {}) {
5706
+ if (!openaiResponse.body) {
5707
+ throw new Error("Response body is null or undefined");
5708
+ }
5709
+ return this.convertReadableStream(openaiResponse.body, options);
5710
+ }
5711
+ /**
5712
+ * 将 ReadableStream 转换为 Anthropic SSE 流
5713
+ */
5714
+ convertReadableStream(openaiStream, options = {}) {
5715
+ const converter = this.createStreamConverter(options);
5716
+ const decoder = new TextDecoder();
5717
+ return new ReadableStream({
5718
+ async start(controller) {
5719
+ if (options.debug) {
5720
+ console.log("[O2ASSEAdapter] \u5F00\u59CB\u6D41\u5F0F\u8F6C\u6362\uFF0C\u914D\u7F6E:", options);
5721
+ }
5722
+ try {
5723
+ const initialEvents = converter.getInitialEvents();
5724
+ for (const event of initialEvents) {
5725
+ controller.enqueue(event);
5726
+ }
5727
+ } catch (error) {
5728
+ console.error("[O2ASSEAdapter] \u521D\u59CB\u5316\u5931\u8D25:", error);
5729
+ controller.error(error);
5730
+ return;
5731
+ }
5732
+ const reader = openaiStream.getReader();
5733
+ try {
5734
+ while (true) {
5735
+ const { done, value } = await reader.read();
5736
+ if (done) {
5737
+ try {
5738
+ const finalEvents = converter.finalize();
5739
+ for (const event of finalEvents) {
5740
+ controller.enqueue(event);
5741
+ }
5742
+ if (options.debug) {
5743
+ console.log("[O2ASSEAdapter] \u6D41\u5F0F\u8F6C\u6362\u5B8C\u6210\uFF0C\u7EDF\u8BA1:", converter.getStats());
5744
+ }
5745
+ } catch (error) {
5746
+ console.error("[O2ASSEAdapter] \u7ED3\u675F\u5904\u7406\u5931\u8D25:", error);
5747
+ }
5748
+ break;
5749
+ }
5750
+ const chunk = decoder.decode(value, { stream: true });
5751
+ try {
5752
+ const events = converter.processChunk(chunk);
5753
+ for (const event of events) {
5754
+ controller.enqueue(event);
5755
+ }
5756
+ } catch (error) {
5757
+ console.error("[O2ASSEAdapter] \u5757\u5904\u7406\u5931\u8D25:", error);
5758
+ if (options.errorRecovery === false) {
5759
+ controller.error(error);
5760
+ return;
5761
+ }
5762
+ if (options.onError) {
5763
+ options.onError(error, {
5764
+ chunk,
5765
+ state: converter.getState()
5766
+ });
5767
+ }
5768
+ }
5769
+ }
5770
+ } catch (error) {
5771
+ console.error("[O2ASSEAdapter] \u6D41\u5904\u7406\u5931\u8D25:", error);
5772
+ if (options.onError) {
5773
+ options.onError(error, {
5774
+ chunk: "",
5775
+ state: converter.getState()
5776
+ });
5777
+ }
5778
+ controller.error(error);
5779
+ } finally {
5780
+ controller.close();
5781
+ }
5782
+ }
5783
+ });
5784
+ }
5785
+ /**
5786
+ * 创建流式转换器实例
5787
+ * 提供更精细的流处理控制
5788
+ */
5789
+ createStreamConverter(options = {}) {
5790
+ return new StreamConverter(this, {
5791
+ modelName: options.modelName || this.config.defaultModel,
5792
+ debug: options.debug || this.debugMode,
5793
+ ...options
5794
+ });
5795
+ }
4630
5796
  /**
4631
5797
  * 应用增强功能到SSE转换
4632
5798
  * 包括输入验证、输出修复等
@@ -4698,15 +5864,56 @@ var O2ASSEAdapterStatic = {
4698
5864
  validateClaudeSSE: (sseContent) => {
4699
5865
  const adapter = new O2ASSEAdapter(false);
4700
5866
  return adapter.validateClaudeSSE(sseContent);
5867
+ },
5868
+ /**
5869
+ * 转换 Response 流为 Anthropic SSE(静态方法)
5870
+ * 新增:直接处理 Response 对象的流式转换
5871
+ */
5872
+ convertResponseStream: (openaiResponse, options = {}) => {
5873
+ const adapter = new O2ASSEAdapter(options.debug || false, {
5874
+ defaultModel: options.modelName || "claude-sonnet-4",
5875
+ generateUniqueMessageId: !options.messageId,
5876
+ errorDataMaxLength: 500
5877
+ });
5878
+ return adapter.convertResponseStream(openaiResponse, options);
5879
+ },
5880
+ /**
5881
+ * 转换 ReadableStream 为 Anthropic SSE(静态方法)
5882
+ * 新增:处理任意 ReadableStream<Uint8Array> 的流式转换
5883
+ */
5884
+ convertReadableStream: (openaiStream, options = {}) => {
5885
+ const adapter = new O2ASSEAdapter(options.debug || false, {
5886
+ defaultModel: options.modelName || "claude-sonnet-4",
5887
+ generateUniqueMessageId: !options.messageId,
5888
+ errorDataMaxLength: 500
5889
+ });
5890
+ return adapter.convertReadableStream(openaiStream, options);
5891
+ },
5892
+ /**
5893
+ * 创建流式转换器(静态方法)
5894
+ * 新增:提供更精细的流处理控制
5895
+ */
5896
+ createStreamConverter: (options = {}) => {
5897
+ const adapter = new O2ASSEAdapter(options.debug || false, {
5898
+ defaultModel: options.modelName || "claude-sonnet-4",
5899
+ generateUniqueMessageId: !options.messageId,
5900
+ errorDataMaxLength: 500
5901
+ });
5902
+ return adapter.createStreamConverter(options);
4701
5903
  }
4702
5904
  };
4703
5905
 
4704
5906
  // src/core/standard/standard-protocol-adapter.ts
4705
5907
  var StandardProtocolAdapter = class {
4706
5908
  constructor(options = {}) {
4707
- this.debugMode = options.debugMode || false;
5909
+ this.debugMode = options.debugMode ?? process.env.AI_PROTOCOL_DEBUG === "true";
4708
5910
  this.sseAdapter = new O2ASSEAdapter(this.debugMode);
4709
5911
  }
5912
+ logDebug(message, meta) {
5913
+ if (this.debugMode) {
5914
+ console.debug(message, meta ?? "");
5915
+ }
5916
+ }
4710
5917
  /**
4711
5918
  * 转换Anthropic请求为OpenAI请求格式
4712
5919
  * @param anthropicRequest - Anthropic格式的请求
@@ -4730,7 +5937,7 @@ var StandardProtocolAdapter = class {
4730
5937
  */
4731
5938
  convertFromStreamToStandard(openaiRawStream, modelName, messageId) {
4732
5939
  if (this.debugMode) {
4733
- console.log("\u{1F504} [StandardProtocolAdapter] convertFromStreamToStandard \u5F00\u59CB\u5904\u7406:", {
5940
+ this.logDebug("\u{1F504} [StandardProtocolAdapter] convertFromStreamToStandard \u5F00\u59CB\u5904\u7406:", {
4734
5941
  rawStreamLength: openaiRawStream.length,
4735
5942
  modelName,
4736
5943
  messageId,
@@ -4739,14 +5946,14 @@ var StandardProtocolAdapter = class {
4739
5946
  }
4740
5947
  const sseResult = this.sseAdapter.convertToClaudeSSE(openaiRawStream, modelName, messageId);
4741
5948
  if (this.debugMode) {
4742
- console.log("\u{1F504} [StandardProtocolAdapter] SSE\u8F6C\u6362\u5B8C\u6210:", {
5949
+ this.logDebug("\u{1F504} [StandardProtocolAdapter] SSE\u8F6C\u6362\u5B8C\u6210:", {
4743
5950
  sseResultLength: sseResult.length,
4744
5951
  ssePreview: sseResult.substring(0, 500)
4745
5952
  });
4746
5953
  }
4747
5954
  const standardResponse = this.extractStandardResponseFromSSE(sseResult, modelName, messageId);
4748
5955
  if (this.debugMode) {
4749
- console.log("\u{1F504} [StandardProtocolAdapter] \u6807\u51C6\u54CD\u5E94\u63D0\u53D6\u5B8C\u6210:", {
5956
+ this.logDebug("\u{1F504} [StandardProtocolAdapter] \u6807\u51C6\u54CD\u5E94\u63D0\u53D6\u5B8C\u6210:", {
4750
5957
  contentLength: standardResponse.content.length,
4751
5958
  usage: standardResponse.usage,
4752
5959
  stopReason: standardResponse.stop_reason
@@ -4761,7 +5968,7 @@ var StandardProtocolAdapter = class {
4761
5968
  const lines = sseContent.split("\n");
4762
5969
  const finalMessageId = messageId || generateMessageId();
4763
5970
  if (this.debugMode) {
4764
- console.log("\u{1F50D} [StandardProtocolAdapter] extractStandardResponseFromSSE \u5F00\u59CB\u89E3\u6790:", {
5971
+ this.logDebug("\u{1F50D} [StandardProtocolAdapter] extractStandardResponseFromSSE \u5F00\u59CB\u89E3\u6790:", {
4765
5972
  totalLines: lines.length,
4766
5973
  messageId: finalMessageId
4767
5974
  });
@@ -4781,6 +5988,8 @@ var StandardProtocolAdapter = class {
4781
5988
  };
4782
5989
  let currentTextContent = "";
4783
5990
  const toolCalls = /* @__PURE__ */ new Map();
5991
+ const toolInputBuffers = /* @__PURE__ */ new Map();
5992
+ const indexToToolId = /* @__PURE__ */ new Map();
4784
5993
  let processedDataLines = 0;
4785
5994
  for (const line of lines) {
4786
5995
  if (line.startsWith("data: ")) {
@@ -4793,24 +6002,74 @@ var StandardProtocolAdapter = class {
4793
6002
  if (data.type === "content_block_start") {
4794
6003
  const contentBlock = data.content_block;
4795
6004
  if (contentBlock.type === "tool_use") {
6005
+ const toolIndex = data.index;
4796
6006
  toolCalls.set(contentBlock.id, {
4797
6007
  type: "tool_use",
4798
6008
  id: contentBlock.id,
4799
6009
  name: contentBlock.name,
4800
6010
  input: contentBlock.input || {}
6011
+ // 初始为空对象,稍后会被更新
6012
+ });
6013
+ toolInputBuffers.set(toolIndex, "");
6014
+ indexToToolId.set(toolIndex, contentBlock.id);
6015
+ console.log("\u{1F527}\u{1F527}\u{1F527} [StandardProtocolAdapter] \u6DFB\u52A0\u5DE5\u5177\u8C03\u7528:", {
6016
+ index: toolIndex,
6017
+ toolId: contentBlock.id,
6018
+ name: contentBlock.name,
6019
+ indexToToolIdSize: indexToToolId.size
4801
6020
  });
4802
- if (this.debugMode) {
4803
- console.log("\u{1F527} [StandardProtocolAdapter] \u6DFB\u52A0\u5DE5\u5177\u8C03\u7528:", contentBlock);
4804
- }
4805
6021
  }
4806
6022
  }
4807
6023
  if (data.type === "content_block_delta" && data.delta?.type === "text_delta") {
4808
6024
  currentTextContent += data.delta.text;
4809
6025
  if (this.debugMode && currentTextContent.length % 50 === 0) {
4810
- console.log(`\u{1F4DD} [StandardProtocolAdapter] \u7D2F\u79EF\u6587\u672C\u5185\u5BB9 (${currentTextContent.length}\u5B57\u7B26):`, currentTextContent.substring(currentTextContent.length - 20));
6026
+ this.logDebug(`\u{1F4DD} [StandardProtocolAdapter] \u7D2F\u79EF\u6587\u672C\u5185\u5BB9 (${currentTextContent.length}\u5B57\u7B26)`, currentTextContent.substring(currentTextContent.length - 20));
4811
6027
  }
4812
6028
  }
4813
6029
  if (data.type === "content_block_delta" && data.delta?.type === "input_json_delta") {
6030
+ const toolIndex = data.index;
6031
+ const toolId = indexToToolId.get(toolIndex);
6032
+ if (this.debugMode) {
6033
+ this.logDebug(`\u{1F527}\u{1F527}\u{1F527} [StandardProtocolAdapter] \u68C0\u6D4B\u5230input_json_delta\u4E8B\u4EF6\uFF01`, {
6034
+ toolIndex,
6035
+ toolId: toolId || "NOT_FOUND",
6036
+ delta: data.delta.partial_json
6037
+ });
6038
+ }
6039
+ if (toolId) {
6040
+ const currentBuffer = toolInputBuffers.get(toolIndex) || "";
6041
+ const newBuffer = currentBuffer + data.delta.partial_json;
6042
+ toolInputBuffers.set(toolIndex, newBuffer);
6043
+ if (this.debugMode) {
6044
+ this.logDebug(`\u{1F527} [StandardProtocolAdapter] \u7D2F\u79EF\u5DE5\u5177\u53C2\u6570 (index=${toolIndex}, id=${toolId})`, {
6045
+ bufferLength: newBuffer.length
6046
+ });
6047
+ }
6048
+ } else {
6049
+ console.warn(`\u26A0\uFE0F [StandardProtocolAdapter] \u627E\u4E0D\u5230toolId for index=${toolIndex}`);
6050
+ }
6051
+ }
6052
+ if (data.type === "content_block_stop") {
6053
+ const toolIndex = data.index;
6054
+ const toolId = indexToToolId.get(toolIndex);
6055
+ if (toolId) {
6056
+ const jsonBuffer = toolInputBuffers.get(toolIndex);
6057
+ const tool = toolCalls.get(toolId);
6058
+ if (jsonBuffer && tool) {
6059
+ try {
6060
+ const parsedInput = JSON.parse(jsonBuffer);
6061
+ tool.input = parsedInput;
6062
+ if (this.debugMode) {
6063
+ this.logDebug(`\u2705 [StandardProtocolAdapter] \u5DE5\u5177\u53C2\u6570\u89E3\u6790\u5B8C\u6210 (index=${toolIndex}, id=${toolId})`, parsedInput);
6064
+ }
6065
+ } catch (parseError) {
6066
+ console.warn(`\u26A0\uFE0F [StandardProtocolAdapter] \u5DE5\u5177\u53C2\u6570JSON\u89E3\u6790\u5931\u8D25 (index=${toolIndex}, id=${toolId}):`, {
6067
+ buffer: jsonBuffer,
6068
+ error: parseError
6069
+ });
6070
+ }
6071
+ }
6072
+ }
4814
6073
  }
4815
6074
  if (data.type === "message_delta") {
4816
6075
  if (data.delta?.stop_reason) {
@@ -4819,7 +6078,7 @@ var StandardProtocolAdapter = class {
4819
6078
  if (data.usage) {
4820
6079
  response.usage = data.usage;
4821
6080
  if (this.debugMode) {
4822
- console.log("\u{1F4CA} [StandardProtocolAdapter] \u66F4\u65B0usage\u4FE1\u606F:", data.usage);
6081
+ this.logDebug("\u{1F4CA} [StandardProtocolAdapter] \u66F4\u65B0usage\u4FE1\u606F:", data.usage);
4823
6082
  }
4824
6083
  }
4825
6084
  }
@@ -4839,7 +6098,7 @@ var StandardProtocolAdapter = class {
4839
6098
  }
4840
6099
  response.content.push(...Array.from(toolCalls.values()));
4841
6100
  if (this.debugMode) {
4842
- console.log("\u2705 [StandardProtocolAdapter] \u6807\u51C6\u54CD\u5E94\u6784\u5EFA\u5B8C\u6210:", {
6101
+ this.logDebug("\u2705 [StandardProtocolAdapter] \u6807\u51C6\u54CD\u5E94\u6784\u5EFA\u5B8C\u6210:", {
4843
6102
  contentCount: response.content.length,
4844
6103
  textLength: currentTextContent.length,
4845
6104
  toolCallsCount: toolCalls.size,
@@ -5699,6 +6958,7 @@ export {
5699
6958
  createAnthropicSDK,
5700
6959
  createOpenAISDK,
5701
6960
  createValidator,
6961
+ downloadImageAsBase64,
5702
6962
  errorRecovery,
5703
6963
  getAllHealingStrategies,
5704
6964
  getGlobalLogger,
@@ -5708,6 +6968,8 @@ export {
5708
6968
  healO2ARequest,
5709
6969
  healO2AResponse,
5710
6970
  healingValidate,
6971
+ isBase64DataUri,
6972
+ isExternalUrl,
5711
6973
  isRecoverable,
5712
6974
  protocolHealer,
5713
6975
  safeValidate,