@n0ts123/mcplink-core 0.0.11 → 0.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,10 +1,7 @@
1
1
  import { Client } from '@modelcontextprotocol/sdk/client/index.js';
2
2
  import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
3
3
  import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
4
- import { generateText, streamText } from 'ai';
5
- import { z } from 'zod';
6
- export { createOpenAI } from '@ai-sdk/openai';
7
- export { createAnthropic } from '@ai-sdk/anthropic';
4
+ import axios from 'axios';
8
5
 
9
6
  // src/MCPManager.ts
10
7
  var RECONNECT_ERROR_KEYWORDS = [
@@ -325,1514 +322,244 @@ var MCPManager = class {
325
322
  this.servers.delete(id);
326
323
  }
327
324
  };
328
-
329
- // src/types.ts
330
- var MCPLinkEventType = /* @__PURE__ */ ((MCPLinkEventType2) => {
331
- MCPLinkEventType2["THINKING_START"] = "thinking_start";
332
- MCPLinkEventType2["THINKING_DELTA"] = "thinking_delta";
333
- MCPLinkEventType2["THINKING_END"] = "thinking_end";
334
- MCPLinkEventType2["TEXT_START"] = "text_start";
335
- MCPLinkEventType2["TEXT_DELTA"] = "text_delta";
336
- MCPLinkEventType2["TEXT_END"] = "text_end";
337
- MCPLinkEventType2["TOOL_CALL_START"] = "tool_call_start";
338
- MCPLinkEventType2["TOOL_CALL_DELTA"] = "tool_call_delta";
339
- MCPLinkEventType2["TOOL_EXECUTING"] = "tool_executing";
340
- MCPLinkEventType2["TOOL_RESULT"] = "tool_result";
341
- MCPLinkEventType2["IMMEDIATE_RESULT"] = "immediate_result";
342
- MCPLinkEventType2["ITERATION_START"] = "iteration_start";
343
- MCPLinkEventType2["ITERATION_END"] = "iteration_end";
344
- MCPLinkEventType2["COMPLETE"] = "complete";
345
- MCPLinkEventType2["ERROR"] = "error";
346
- return MCPLinkEventType2;
347
- })(MCPLinkEventType || {});
348
-
349
- // src/Agent.ts
350
- var DEFAULT_SYSTEM_PROMPT = `\u4F60\u662F\u4E00\u4E2A\u4E13\u4E1A\u3001\u53CB\u597D\u7684\u667A\u80FD\u52A9\u624B\u3002
351
-
352
- ## \u56DE\u590D\u8981\u6C42
353
- - \u7B80\u6D01\u6E05\u6670\uFF0C\u91CD\u70B9\u7A81\u51FA
354
- - \u7528\u5217\u8868\u5448\u73B0\u5173\u952E\u4FE1\u606F
355
- - \u8BED\u6C14\u793C\u8C8C\u81EA\u7136\uFF0C\u50CF\u4E13\u4E1A\u52A9\u624B
356
- - \u6709\u7ED3\u8BBA\u65F6\u76F4\u63A5\u7ED9\u51FA\uFF0C\u9700\u8981\u8865\u5145\u4FE1\u606F\u65F6\u7B80\u5355\u8BE2\u95EE`;
357
- var DEFAULT_THINKING_PHASE_PROMPT = `
358
- ---
359
- \u8FD9\u662F\u4F60\u7684\u5185\u5FC3\u72EC\u767D\uFF0C\u7528\u6237\u770B\u4E0D\u5230\u3002
360
-
361
- \u5224\u65AD\u5F53\u524D\u72B6\u6001\uFF1A\u6211\u62FF\u5230\u4E86\u4EC0\u4E48\uFF1F\u4EFB\u52A1\u5B8C\u6210\u4E86\u5417\uFF1F\u8FD8\u9700\u8981\u67E5\u4EC0\u4E48\uFF1F
362
-
363
- \u91CD\u8981\uFF1A\u8FD9\u91CC\u53EA\u662F\u601D\u8003\u5224\u65AD\uFF0C\u4E0D\u8981\u5728\u8FD9\u91CC\u5199\u56DE\u590D\u5185\u5BB9\uFF08\u56DE\u590D\u662F\u4E0B\u4E00\u6B65\u7684\u4E8B\uFF09\u3002
364
- ---`;
365
- var Agent = class {
366
- model;
367
- mcpManager;
368
- systemPrompt;
369
- maxIterations;
370
- immediateResultMatchers;
371
- parallelToolCalls;
372
- enableThinkingPhase;
373
- thinkingPhasePrompt;
374
- thinkingMaxTokens;
375
- constructor(model, mcpManager, options = {}) {
376
- this.model = model;
377
- this.mcpManager = mcpManager;
378
- this.systemPrompt = options.systemPrompt || DEFAULT_SYSTEM_PROMPT;
379
- this.maxIterations = options.maxIterations || 10;
380
- this.immediateResultMatchers = options.immediateResultMatchers || [];
381
- this.parallelToolCalls = options.parallelToolCalls ?? true;
382
- this.enableThinkingPhase = options.enableThinkingPhase ?? false;
383
- this.thinkingPhasePrompt = options.thinkingPhasePrompt || DEFAULT_THINKING_PHASE_PROMPT;
384
- this.thinkingMaxTokens = options.thinkingMaxTokens ?? 1e3;
325
+ var HttpClient = class {
326
+ client;
327
+ constructor() {
328
+ this.client = axios.create({
329
+ timeout: 12e4
330
+ });
385
331
  }
386
332
  /**
387
- * 生成工具描述文本(用于思考阶段)
333
+ * 非流式请求
388
334
  */
389
- generateToolsDescription(tools) {
390
- if (tools.length === 0) {
391
- return "\u5F53\u524D\u6CA1\u6709\u53EF\u7528\u7684\u5DE5\u5177\u3002";
392
- }
393
- let description = "";
394
- for (const tool of tools) {
395
- description += `### ${tool.name}
396
- `;
397
- description += `\u63CF\u8FF0: ${tool.description}
398
- `;
399
- if (tool.inputSchema.properties) {
400
- description += `\u53C2\u6570:
401
- `;
402
- for (const [key, prop] of Object.entries(tool.inputSchema.properties)) {
403
- const propInfo = prop;
404
- const required = tool.inputSchema.required?.includes(key) ? "\u5FC5\u586B" : "\u53EF\u9009";
405
- description += ` - ${key} (${propInfo.type || "any"}, ${required}): ${propInfo.description || ""}
406
- `;
407
- }
335
+ async chat(aiConfig, adapter, messages, tools) {
336
+ const body = adapter.buildRequestBody(aiConfig, messages, tools);
337
+ const headers = { ...adapter.getHeaders(aiConfig), ...aiConfig.headers };
338
+ const response = await this.client.post(
339
+ adapter.getEndpoint(aiConfig.baseURL),
340
+ body,
341
+ {
342
+ headers,
343
+ timeout: aiConfig.timeout || 12e4
408
344
  }
409
- description += "\n";
410
- }
411
- return description;
345
+ );
346
+ return adapter.parseResponse(response.data);
412
347
  }
413
348
  /**
414
- * 摘要化工具返回结果(用于思考阶段,避免 AI 直接格式化输出数据)
415
- * @param toolName 工具名称
416
- * @param result 工具返回的结果
417
- * @returns 摘要字符串
349
+ * 流式请求 - SSE
418
350
  */
419
- summarizeToolResult(toolName, result) {
420
- let count = 0;
421
- let resultObj = result;
422
- if (typeof result === "string") {
423
- try {
424
- resultObj = JSON.parse(result);
425
- } catch {
426
- return `[\u5DE5\u5177 ${toolName} \u8FD4\u56DE\u4E86\u6570\u636E]`;
351
+ async *streamChat(aiConfig, adapter, messages, tools) {
352
+ const body = adapter.buildRequestBody(aiConfig, messages, tools);
353
+ const streamBody = { ...body, stream: true };
354
+ const headers = { ...adapter.getHeaders(aiConfig), ...aiConfig.headers };
355
+ const response = await this.client.post(
356
+ adapter.getEndpoint(aiConfig.baseURL),
357
+ streamBody,
358
+ {
359
+ headers,
360
+ timeout: aiConfig.timeout || 12e4,
361
+ responseType: "stream"
427
362
  }
428
- }
429
- if (Array.isArray(resultObj)) {
430
- count = resultObj.length;
431
- } else if (typeof resultObj === "object" && resultObj !== null) {
432
- const obj = resultObj;
433
- for (const key of ["data", "list", "items", "records", "results"]) {
434
- if (Array.isArray(obj[key])) {
435
- count = obj[key].length;
436
- break;
437
- }
363
+ );
364
+ const stream = response.data;
365
+ for await (const event of this.parseSSE(stream)) {
366
+ const parsed = adapter.parseStreamChunk(event);
367
+ if (parsed) {
368
+ yield parsed;
438
369
  }
439
370
  }
440
- if (count > 0) {
441
- return `[\u5DE5\u5177 ${toolName} \u8FD4\u56DE\u4E86\u6570\u636E\uFF0C\u5305\u542B ${count} \u6761\u8BB0\u5F55]`;
442
- }
443
- return `[\u5DE5\u5177 ${toolName} \u8FD4\u56DE\u4E86\u6570\u636E]`;
444
371
  }
445
372
  /**
446
- * 检查工具返回结果是否匹配即时结果匹配器
447
- * @param result 工具返回的结果
448
- * @returns 如果匹配返回 true,否则返回 false
373
+ * 解析 SSE 流
449
374
  */
450
- matchImmediateResult(result) {
451
- const debug = process.env.DEBUG_MCPLINK === "true";
452
- if (!this.immediateResultMatchers.length) {
453
- if (debug) console.log("[MCPLink] \u26A0\uFE0F \u672A\u914D\u7F6E\u5373\u65F6\u7ED3\u679C\u5339\u914D\u5668");
454
- return false;
455
- }
456
- let resultObj = null;
457
- if (typeof result === "string") {
458
- try {
459
- const parsed = JSON.parse(result);
460
- if (typeof parsed === "object" && parsed !== null) {
461
- resultObj = parsed;
462
- if (debug) console.log("[MCPLink] \u{1F50D} \u89E3\u6790\u5DE5\u5177\u7ED3\u679C\u4E3A\u5BF9\u8C61:", Object.keys(parsed));
375
+ async *parseSSE(stream) {
376
+ let buffer = "";
377
+ for await (const chunk of stream) {
378
+ buffer += chunk.toString();
379
+ const lines = buffer.split("\n");
380
+ buffer = lines.pop() || "";
381
+ for (const line of lines) {
382
+ const trimmed = line.trim();
383
+ if (trimmed.startsWith("data: ")) {
384
+ const data = trimmed.slice(6);
385
+ if (data === "[DONE]") {
386
+ return;
387
+ }
388
+ if (data) {
389
+ yield data;
390
+ }
463
391
  }
464
- } catch {
465
- if (debug) console.log("[MCPLink] \u26A0\uFE0F \u5DE5\u5177\u7ED3\u679C\u4E0D\u662F\u6709\u6548 JSON");
466
392
  }
467
- } else if (typeof result === "object" && result !== null) {
468
- resultObj = result;
469
- if (debug) console.log("[MCPLink] \u{1F50D} \u5DE5\u5177\u7ED3\u679C\u662F\u5BF9\u8C61:", Object.keys(result));
470
- }
471
- if (!resultObj) {
472
- if (debug) console.log("[MCPLink] \u26A0\uFE0F \u65E0\u6CD5\u89E3\u6790\u5DE5\u5177\u7ED3\u679C\u4E3A\u5BF9\u8C61");
473
- return false;
474
393
  }
475
- for (const matcher of this.immediateResultMatchers) {
476
- let matched = true;
477
- for (const [key, value] of Object.entries(matcher)) {
478
- if (resultObj[key] !== value) {
479
- matched = false;
480
- break;
481
- }
482
- }
483
- if (matched) {
484
- if (debug) console.log("[MCPLink] \u2705 \u5373\u65F6\u7ED3\u679C\u5339\u914D\u6210\u529F:", JSON.stringify(matcher));
485
- return true;
394
+ if (buffer.trim().startsWith("data: ")) {
395
+ const data = buffer.trim().slice(6);
396
+ if (data && data !== "[DONE]") {
397
+ yield data;
486
398
  }
487
399
  }
488
- if (debug) console.log("[MCPLink] \u274C \u5373\u65F6\u7ED3\u679C\u672A\u5339\u914D\uFF0C\u671F\u671B:", JSON.stringify(this.immediateResultMatchers), "\u5B9E\u9645:", JSON.stringify(resultObj));
489
- return false;
490
- }
491
- /**
492
- * 将 MCP 工具转换为 Vercel AI SDK 格式
493
- */
494
- convertMCPToolsToAITools(mcpTools) {
495
- const tools = {};
496
- for (const mcpTool of mcpTools) {
497
- const zodSchema = this.jsonSchemaToZod(mcpTool.inputSchema);
498
- tools[mcpTool.name] = {
499
- description: mcpTool.description,
500
- parameters: zodSchema
501
- };
502
- }
503
- return tools;
504
400
  }
505
- /**
506
- * JSON Schema 到 Zod 的完整递归转换
507
- * 支持嵌套对象、对象数组、枚举等所有常见类型
508
- */
509
- jsonSchemaToZod(schema) {
510
- return this.convertSchemaToZod(schema, schema.required || []);
511
- }
512
- /**
513
- * 递归转换 JSON Schema 节点为 Zod 类型
514
- */
515
- convertSchemaToZod(schema, parentRequired = [], key) {
516
- const type = schema.type;
517
- const description = schema.description;
518
- const enumValues = schema.enum;
519
- let zodType;
520
- if (enumValues && enumValues.length > 0) {
521
- if (enumValues.every((v) => typeof v === "string")) {
522
- zodType = z.enum(enumValues);
523
- } else if (enumValues.every((v) => typeof v === "number")) {
524
- const literals = enumValues.map((v) => z.literal(v));
525
- zodType = literals.length === 1 ? literals[0] : z.union([literals[0], literals[1], ...literals.slice(2)]);
526
- } else {
527
- zodType = z.unknown();
528
- }
529
- } else {
530
- switch (type) {
531
- case "string":
532
- zodType = z.string();
533
- break;
534
- case "number":
535
- zodType = z.number();
536
- break;
537
- case "integer":
538
- zodType = z.number().int();
539
- break;
540
- case "boolean":
541
- zodType = z.boolean();
542
- break;
543
- case "null":
544
- zodType = z.null();
545
- break;
546
- case "object": {
547
- const properties = schema.properties;
548
- const required = schema.required || [];
549
- if (properties) {
550
- const shape = {};
551
- for (const [propKey, propSchema] of Object.entries(properties)) {
552
- let propZod = this.convertSchemaToZod(propSchema, required, propKey);
553
- if (!required.includes(propKey)) {
554
- propZod = propZod.optional();
555
- }
556
- shape[propKey] = propZod;
557
- }
558
- zodType = z.object(shape);
559
- } else {
560
- zodType = z.record(z.unknown());
561
- }
562
- break;
563
- }
564
- case "array": {
565
- const items = schema.items;
566
- if (items) {
567
- const itemsRequired = items.required || [];
568
- zodType = z.array(this.convertSchemaToZod(items, itemsRequired));
569
- } else {
570
- zodType = z.array(z.unknown());
571
- }
572
- break;
573
- }
574
- default:
575
- zodType = z.unknown();
401
+ };
402
+
403
+ // src/adapters/openai.ts
404
+ var OpenAIAdapter = class {
405
+ name = "openai";
406
+ /**
407
+ * 构建请求体
408
+ * 完全开放:除必要字段外,其他参数原封不动传递
409
+ */
410
+ buildRequestBody(config, messages, tools) {
411
+ const { baseURL, apiKey, model, headers, timeout, ...customParams } = config;
412
+ const openaiMessages = messages.map((msg) => this.convertMessage(msg));
413
+ const openaiTools = tools?.map((tool) => ({
414
+ type: "function",
415
+ function: {
416
+ name: tool.name,
417
+ description: tool.description,
418
+ parameters: tool.parameters
576
419
  }
420
+ }));
421
+ const body = {
422
+ model,
423
+ messages: openaiMessages,
424
+ // 用户自定义参数(包括 enable_thinking 等)
425
+ ...customParams
426
+ };
427
+ if (openaiTools && openaiTools.length > 0) {
428
+ body.tools = openaiTools;
577
429
  }
578
- if (description) {
579
- zodType = zodType.describe(description);
580
- }
581
- return zodType;
430
+ return body;
582
431
  }
583
432
  /**
584
- * 执行对话
433
+ * 获取请求头
585
434
  */
586
- async chat(userMessage, callbacks) {
587
- const startTime = Date.now();
588
- const toolCallRecords = [];
589
- let totalPromptTokens = 0;
590
- let totalCompletionTokens = 0;
591
- const messages = [
592
- { role: "system", content: this.systemPrompt },
593
- { role: "user", content: userMessage }
594
- ];
595
- const mcpTools = this.mcpManager.getAllTools();
596
- const tools = this.convertMCPToolsToAITools(mcpTools);
597
- let iteration = 0;
598
- let finalContent = "";
599
- while (iteration < this.maxIterations) {
600
- iteration++;
601
- callbacks?.onIterationStart?.(iteration);
602
- const response = await generateText({
603
- model: this.model,
604
- messages,
605
- tools: Object.keys(tools).length > 0 ? tools : void 0,
606
- maxSteps: 1
607
- // 每次只执行一步,方便我们控制流程
608
- });
609
- if (response.usage) {
610
- totalPromptTokens += response.usage.promptTokens;
611
- totalCompletionTokens += response.usage.completionTokens;
612
- }
613
- const toolCalls = response.toolCalls || [];
614
- if (toolCalls.length === 0) {
615
- finalContent = response.text || "";
616
- callbacks?.onTextDelta?.(finalContent);
617
- callbacks?.onIterationEnd?.(iteration);
618
- break;
619
- }
620
- const toolResults = [];
621
- for (const toolCall of toolCalls) {
622
- const toolName = toolCall.toolName;
623
- const toolArgs = toolCall.args;
624
- const toolCallId = toolCall.toolCallId;
625
- callbacks?.onToolCallStart?.(toolName, toolArgs);
626
- const toolStartTime = Date.now();
627
- let result;
628
- let isError = false;
629
- try {
630
- result = await this.mcpManager.callTool(toolName, toolArgs);
631
- } catch (error) {
632
- result = error instanceof Error ? error.message : String(error);
633
- isError = true;
634
- }
635
- const duration2 = Date.now() - toolStartTime;
636
- callbacks?.onToolResult?.(toolName, result, duration2);
637
- toolResults.push({
638
- toolCallId,
639
- toolName,
640
- result,
641
- isError,
642
- duration: duration2
643
- });
644
- toolCallRecords.push({
645
- name: toolName,
646
- arguments: toolArgs,
647
- result,
648
- duration: duration2
649
- });
650
- }
651
- messages.push({
652
- role: "assistant",
653
- content: [
654
- { type: "text", text: response.text || "" },
655
- ...toolCalls.map((tc) => ({
656
- type: "tool-call",
657
- toolCallId: tc.toolCallId,
658
- toolName: tc.toolName,
659
- args: tc.args
660
- }))
661
- ]
662
- });
663
- for (const tr of toolResults) {
664
- messages.push({
665
- role: "tool",
666
- content: [
667
- {
668
- type: "tool-result",
669
- toolCallId: tr.toolCallId,
670
- toolName: tr.toolName,
671
- result: tr.result
672
- }
673
- ]
674
- });
675
- }
676
- callbacks?.onIterationEnd?.(iteration);
677
- }
678
- const duration = Date.now() - startTime;
435
+ getHeaders(config) {
679
436
  return {
680
- content: finalContent,
681
- toolCalls: toolCallRecords,
682
- messages: messages.map((m) => ({
683
- role: m.role,
684
- content: typeof m.content === "string" ? m.content : JSON.stringify(m.content)
685
- })),
686
- usage: {
687
- promptTokens: totalPromptTokens,
688
- completionTokens: totalCompletionTokens,
689
- totalTokens: totalPromptTokens + totalCompletionTokens
690
- },
691
- iterations: iteration,
692
- duration
437
+ "Content-Type": "application/json",
438
+ "Authorization": `Bearer ${config.apiKey}`
693
439
  };
694
440
  }
695
441
  /**
696
- * 将 UserMessage 转换为 Vercel AI SDK 的消息内容格式
442
+ * 获取请求端点
697
443
  */
698
- convertUserMessageToContent(message) {
699
- if (typeof message === "string") {
700
- return message;
701
- }
702
- return message.map((part) => {
703
- switch (part.type) {
704
- case "text":
705
- return { type: "text", text: part.text };
706
- case "image":
707
- return { type: "image", image: part.image, mimeType: part.mimeType };
708
- case "file":
709
- return { type: "file", data: part.data, mimeType: part.mimeType };
710
- default:
711
- return { type: "text", text: "" };
712
- }
713
- });
444
+ getEndpoint(baseURL) {
445
+ const normalized = baseURL.replace(/\/$/, "");
446
+ return `${normalized}/chat/completions`;
714
447
  }
715
448
  /**
716
- * 从 UserMessage 提取纯文本内容(用于日志等)
449
+ * 解析非流式响应
717
450
  */
718
- extractTextFromMessage(message) {
719
- if (typeof message === "string") {
720
- return message;
721
- }
722
- return message.filter((part) => part.type === "text").map((part) => part.text).join("\n");
451
+ parseResponse(data) {
452
+ const response = data;
453
+ const choice = response.choices[0];
454
+ const message = choice?.message;
455
+ const toolCalls = message?.tool_calls?.map((tc) => ({
456
+ id: tc.id,
457
+ name: tc.function.name,
458
+ arguments: JSON.parse(tc.function.arguments)
459
+ }));
460
+ return {
461
+ content: message?.content || "",
462
+ toolCalls: toolCalls?.length ? toolCalls : void 0
463
+ };
723
464
  }
724
465
  /**
725
- * 流式对话 - 返回事件生成器
726
- * @param userMessage 用户消息(支持字符串或多模态数组)
727
- * @param options 可选参数
728
- * @param options.allowedTools 允许使用的工具名称列表,为空或不传则使用所有工具
729
- * @param options.history 历史消息列表
466
+ * 解析流式数据块
730
467
  */
731
- async *chatStream(userMessage, options) {
732
- const startTime = Date.now();
733
- const toolCallRecords = [];
734
- const messages = [{ role: "system", content: this.systemPrompt }];
735
- if (options?.history && options.history.length > 0) {
736
- for (const msg of options.history) {
737
- messages.push({
738
- role: msg.role,
739
- content: msg.content
740
- });
741
- }
742
- }
743
- const userContent = this.convertUserMessageToContent(userMessage);
744
- messages.push({ role: "user", content: userContent });
745
- let mcpTools = this.mcpManager.getAllTools();
746
- if (options?.allowedTools && options.allowedTools.length > 0) {
747
- mcpTools = mcpTools.filter((tool) => options.allowedTools.includes(tool.name));
748
- }
749
- const tools = this.convertMCPToolsToAITools(mcpTools);
750
- const hasTools = Object.keys(tools).length > 0;
751
- let iteration = 0;
752
- while (iteration < this.maxIterations) {
753
- iteration++;
754
- yield {
755
- type: "iteration_start" /* ITERATION_START */,
756
- timestamp: Date.now(),
757
- data: { iteration, maxIterations: this.maxIterations }
758
- };
759
- if (this.enableThinkingPhase && hasTools) {
760
- yield {
761
- type: "thinking_start" /* THINKING_START */,
762
- timestamp: Date.now(),
763
- data: {}
764
- };
765
- const toolsDescription = this.generateToolsDescription(mcpTools);
766
- const thinkingSystemPrompt = `## \u4F60\u7684\u89D2\u8272
767
- \u4F60\u73B0\u5728\u662F\u300C\u5185\u90E8\u601D\u8003\u8005\u300D\uFF0C\u8FD9\u6BB5\u601D\u8003\u7528\u6237\u770B\u4E0D\u5230\u3002
768
- \u4F60\u7684\u4EFB\u52A1\u662F\uFF1A\u5206\u6790\u5F53\u524D\u72B6\u6001\uFF0C\u5224\u65AD\u4E0B\u4E00\u6B65\u8BE5\u505A\u4EC0\u4E48\u3002
769
-
770
- ## \u53C2\u8003\u4FE1\u606F\uFF08\u53EF\u80FD\u5305\u542B\u91CD\u8981\u914D\u7F6E\u5982\u8BA4\u8BC1\u4FE1\u606F\uFF09
771
- ${this.systemPrompt}
772
-
773
- ## \u53EF\u7528\u5DE5\u5177
774
- ${toolsDescription}
775
- ${this.thinkingPhasePrompt}`;
776
- const summarizedMessages = messages.slice(1).map((msg) => {
777
- if (msg.role === "tool" && Array.isArray(msg.content)) {
778
- const summarizedContent = msg.content.map((item) => {
779
- if (item.type === "tool-result") {
780
- return {
781
- ...item,
782
- result: this.summarizeToolResult(item.toolName, item.result)
783
- };
784
- }
785
- return item;
786
- });
787
- return { ...msg, content: summarizedContent };
788
- }
789
- return msg;
790
- });
791
- const thinkingMessages = [
792
- {
793
- role: "system",
794
- content: thinkingSystemPrompt
795
- },
796
- ...summarizedMessages
797
- ];
798
- const thinkingStream = streamText({
799
- model: this.model,
800
- messages: thinkingMessages,
801
- maxTokens: this.thinkingMaxTokens
802
- // 不传 tools,强制 AI 输出文本思考
803
- });
804
- let thinkingContent = "";
805
- for await (const chunk of thinkingStream.fullStream) {
806
- if (chunk.type === "text-delta") {
807
- thinkingContent += chunk.textDelta;
808
- yield {
809
- type: "thinking_delta" /* THINKING_DELTA */,
810
- timestamp: Date.now(),
811
- data: { content: chunk.textDelta }
812
- };
813
- }
814
- }
815
- yield {
816
- type: "thinking_end" /* THINKING_END */,
817
- timestamp: Date.now(),
818
- data: {}
819
- };
820
- if (thinkingContent) {
821
- messages.push({
822
- role: "assistant",
823
- content: `[\u5185\u90E8\u51B3\u7B56]
824
- ${thinkingContent}`
825
- });
826
- }
827
- }
828
- const stream = streamText({
829
- model: this.model,
830
- messages,
831
- tools: hasTools ? tools : void 0,
832
- maxSteps: 1
833
- });
834
- let fullText = "";
835
- let reasoningText = "";
836
- const toolCalls = [];
837
- let currentToolCall = null;
838
- let hasStartedText = false;
839
- let hasStartedReasoning = false;
840
- const sentToolCallStarts = /* @__PURE__ */ new Set();
841
- let isInsideThinkTag = false;
842
- let textBuffer = "";
843
- for await (const chunk of stream.fullStream) {
844
- switch (chunk.type) {
845
- case "reasoning":
846
- if (!hasStartedReasoning) {
847
- hasStartedReasoning = true;
848
- yield {
849
- type: "thinking_start" /* THINKING_START */,
850
- timestamp: Date.now(),
851
- data: {}
852
- };
853
- }
854
- reasoningText += chunk.textDelta;
855
- yield {
856
- type: "thinking_delta" /* THINKING_DELTA */,
857
- timestamp: Date.now(),
858
- data: { content: chunk.textDelta }
859
- };
860
- break;
861
- case "text-delta":
862
- const delta = chunk.textDelta;
863
- textBuffer += delta;
864
- if (!isInsideThinkTag) {
865
- const thinkStartMatch = textBuffer.match(/<think>/i);
866
- if (thinkStartMatch) {
867
- const beforeThink = textBuffer.substring(0, thinkStartMatch.index);
868
- if (beforeThink.trim()) {
869
- if (!hasStartedText) {
870
- hasStartedText = true;
871
- yield {
872
- type: "text_start" /* TEXT_START */,
873
- timestamp: Date.now(),
874
- data: {}
875
- };
876
- }
877
- fullText += beforeThink;
878
- yield {
879
- type: "text_delta" /* TEXT_DELTA */,
880
- timestamp: Date.now(),
881
- data: { content: beforeThink }
882
- };
883
- }
884
- isInsideThinkTag = true;
885
- if (!hasStartedReasoning) {
886
- hasStartedReasoning = true;
887
- yield {
888
- type: "thinking_start" /* THINKING_START */,
889
- timestamp: Date.now(),
890
- data: {}
891
- };
892
- }
893
- textBuffer = textBuffer.substring(thinkStartMatch.index + 7);
894
- } else if (!textBuffer.includes("<")) {
895
- if (hasStartedReasoning && !hasStartedText) {
896
- yield {
897
- type: "thinking_end" /* THINKING_END */,
898
- timestamp: Date.now(),
899
- data: {}
900
- };
901
- }
902
- if (!hasStartedText) {
903
- hasStartedText = true;
904
- yield {
905
- type: "text_start" /* TEXT_START */,
906
- timestamp: Date.now(),
907
- data: {}
908
- };
909
- }
910
- fullText += textBuffer;
911
- yield {
912
- type: "text_delta" /* TEXT_DELTA */,
913
- timestamp: Date.now(),
914
- data: { content: textBuffer }
915
- };
916
- textBuffer = "";
917
- }
918
- } else {
919
- const thinkEndMatch = textBuffer.match(/<\/think>/i);
920
- if (thinkEndMatch) {
921
- const thinkContent = textBuffer.substring(0, thinkEndMatch.index);
922
- if (thinkContent) {
923
- reasoningText += thinkContent;
924
- yield {
925
- type: "thinking_delta" /* THINKING_DELTA */,
926
- timestamp: Date.now(),
927
- data: { content: thinkContent }
928
- };
929
- }
930
- yield {
931
- type: "thinking_end" /* THINKING_END */,
932
- timestamp: Date.now(),
933
- data: {}
934
- };
935
- isInsideThinkTag = false;
936
- textBuffer = textBuffer.substring(thinkEndMatch.index + 8);
937
- } else if (!textBuffer.includes("<")) {
938
- reasoningText += textBuffer;
939
- yield {
940
- type: "thinking_delta" /* THINKING_DELTA */,
941
- timestamp: Date.now(),
942
- data: { content: textBuffer }
943
- };
944
- textBuffer = "";
945
- }
946
- }
947
- break;
948
- case "tool-call":
949
- if (!sentToolCallStarts.has(chunk.toolCallId)) {
950
- yield {
951
- type: "tool_call_start" /* TOOL_CALL_START */,
952
- timestamp: Date.now(),
953
- data: {
954
- toolName: chunk.toolName,
955
- toolCallId: chunk.toolCallId,
956
- toolArgs: chunk.args
957
- }
958
- };
959
- sentToolCallStarts.add(chunk.toolCallId);
960
- toolCalls.push({
961
- toolCallId: chunk.toolCallId,
962
- toolName: chunk.toolName,
963
- args: chunk.args
964
- });
965
- }
966
- break;
967
- case "tool-call-streaming-start":
968
- currentToolCall = {
969
- toolCallId: chunk.toolCallId,
970
- toolName: chunk.toolName,
971
- argsText: ""
972
- };
973
- if (!sentToolCallStarts.has(chunk.toolCallId)) {
974
- yield {
975
- type: "tool_call_start" /* TOOL_CALL_START */,
976
- timestamp: Date.now(),
977
- data: {
978
- toolName: chunk.toolName,
979
- toolCallId: chunk.toolCallId
980
- }
981
- };
982
- sentToolCallStarts.add(chunk.toolCallId);
983
- }
984
- break;
985
- case "tool-call-delta":
986
- if (currentToolCall) {
987
- currentToolCall.argsText += chunk.argsTextDelta;
988
- yield {
989
- type: "tool_call_delta" /* TOOL_CALL_DELTA */,
990
- timestamp: Date.now(),
991
- data: {
992
- toolCallId: currentToolCall.toolCallId,
993
- argsTextDelta: chunk.argsTextDelta
994
- }
995
- };
996
- }
997
- break;
998
- case "finish":
999
- if (textBuffer) {
1000
- if (isInsideThinkTag) {
1001
- reasoningText += textBuffer;
1002
- yield {
1003
- type: "thinking_delta" /* THINKING_DELTA */,
1004
- timestamp: Date.now(),
1005
- data: { content: textBuffer }
1006
- };
1007
- } else {
1008
- if (!hasStartedText) {
1009
- hasStartedText = true;
1010
- yield {
1011
- type: "text_start" /* TEXT_START */,
1012
- timestamp: Date.now(),
1013
- data: {}
1014
- };
1015
- }
1016
- fullText += textBuffer;
1017
- yield {
1018
- type: "text_delta" /* TEXT_DELTA */,
1019
- timestamp: Date.now(),
1020
- data: { content: textBuffer }
1021
- };
1022
- }
1023
- textBuffer = "";
1024
- }
1025
- if (isInsideThinkTag || hasStartedReasoning && !hasStartedText) {
1026
- yield {
1027
- type: "thinking_end" /* THINKING_END */,
1028
- timestamp: Date.now(),
1029
- data: {}
1030
- };
1031
- isInsideThinkTag = false;
1032
- }
1033
- if (hasStartedText) {
1034
- yield {
1035
- type: "text_end" /* TEXT_END */,
1036
- timestamp: Date.now(),
1037
- data: {}
1038
- };
1039
- }
1040
- break;
1041
- case "error":
1042
- yield {
1043
- type: "error" /* ERROR */,
1044
- timestamp: Date.now(),
1045
- data: { error: chunk.error }
1046
- };
1047
- break;
1048
- }
1049
- }
1050
- if (toolCalls.length === 0) {
1051
- yield {
1052
- type: "iteration_end" /* ITERATION_END */,
1053
- timestamp: Date.now(),
1054
- data: { iteration }
1055
- };
1056
- break;
1057
- }
1058
- const toolResults = [];
1059
- for (const toolCall of toolCalls) {
1060
- yield {
1061
- type: "tool_executing" /* TOOL_EXECUTING */,
1062
- timestamp: Date.now(),
1063
- data: {
1064
- toolName: toolCall.toolName,
1065
- toolCallId: toolCall.toolCallId,
1066
- toolArgs: toolCall.args
1067
- }
1068
- };
1069
- }
1070
- let hasImmediateResult = false;
1071
- if (this.parallelToolCalls && toolCalls.length > 1) {
1072
- const executePromises = toolCalls.map(async (toolCall) => {
1073
- const toolStartTime = Date.now();
1074
- let result;
1075
- let isError = false;
1076
- try {
1077
- result = await this.mcpManager.callTool(toolCall.toolName, toolCall.args);
1078
- } catch (error) {
1079
- result = error instanceof Error ? error.message : String(error);
1080
- isError = true;
1081
- }
1082
- const duration = Date.now() - toolStartTime;
468
+ parseStreamChunk(line) {
469
+ try {
470
+ const data = JSON.parse(line);
471
+ const choice = data.choices[0];
472
+ const delta = choice?.delta;
473
+ if (delta?.content) {
474
+ return { type: "text", content: delta.content };
475
+ }
476
+ if (delta?.tool_calls) {
477
+ const tc = delta.tool_calls[0];
478
+ if (tc.id && tc.function?.name) {
1083
479
  return {
1084
- toolCallId: toolCall.toolCallId,
1085
- toolName: toolCall.toolName,
1086
- args: toolCall.args,
1087
- result,
1088
- isError,
1089
- duration
1090
- };
1091
- });
1092
- const results = await Promise.all(executePromises);
1093
- for (const r of results) {
1094
- yield {
1095
- type: "tool_result" /* TOOL_RESULT */,
1096
- timestamp: Date.now(),
1097
- data: {
1098
- toolName: r.toolName,
1099
- toolResult: r.result,
1100
- toolCallId: r.toolCallId,
1101
- duration: r.duration,
1102
- isError: r.isError
480
+ type: "tool_call",
481
+ toolCall: {
482
+ id: tc.id,
483
+ name: tc.function.name,
484
+ arguments: tc.function.arguments ? JSON.parse(tc.function.arguments) : {}
1103
485
  }
1104
486
  };
1105
- if (!r.isError && this.matchImmediateResult(r.result)) {
1106
- hasImmediateResult = true;
1107
- yield {
1108
- type: "immediate_result" /* IMMEDIATE_RESULT */,
1109
- timestamp: Date.now(),
1110
- data: {
1111
- toolName: r.toolName,
1112
- toolCallId: r.toolCallId,
1113
- immediateResult: r.result
1114
- }
1115
- };
1116
- }
1117
- toolResults.push({
1118
- toolCallId: r.toolCallId,
1119
- toolName: r.toolName,
1120
- result: r.result,
1121
- isError: r.isError,
1122
- duration: r.duration
1123
- });
1124
- toolCallRecords.push({
1125
- name: r.toolName,
1126
- arguments: r.args,
1127
- result: r.result,
1128
- duration: r.duration
1129
- });
1130
- }
1131
- } else {
1132
- for (const toolCall of toolCalls) {
1133
- const toolName = toolCall.toolName;
1134
- const toolArgs = toolCall.args;
1135
- const toolCallId = toolCall.toolCallId;
1136
- const toolStartTime = Date.now();
1137
- let result;
1138
- let isError = false;
1139
- try {
1140
- result = await this.mcpManager.callTool(toolName, toolArgs);
1141
- } catch (error) {
1142
- result = error instanceof Error ? error.message : String(error);
1143
- isError = true;
1144
- }
1145
- const duration = Date.now() - toolStartTime;
1146
- yield {
1147
- type: "tool_result" /* TOOL_RESULT */,
1148
- timestamp: Date.now(),
1149
- data: {
1150
- toolName,
1151
- toolResult: result,
1152
- toolCallId,
1153
- duration,
1154
- isError
1155
- }
1156
- };
1157
- if (!isError && this.matchImmediateResult(result)) {
1158
- hasImmediateResult = true;
1159
- yield {
1160
- type: "immediate_result" /* IMMEDIATE_RESULT */,
1161
- timestamp: Date.now(),
1162
- data: {
1163
- toolName,
1164
- toolCallId,
1165
- immediateResult: result
1166
- }
1167
- };
1168
- }
1169
- toolResults.push({
1170
- toolCallId,
1171
- toolName,
1172
- result,
1173
- isError,
1174
- duration
1175
- });
1176
- toolCallRecords.push({
1177
- name: toolName,
1178
- arguments: toolArgs,
1179
- result,
1180
- duration
1181
- });
1182
- }
1183
- }
1184
- if (hasImmediateResult) {
1185
- yield {
1186
- type: "iteration_end" /* ITERATION_END */,
1187
- timestamp: Date.now(),
1188
- data: { iteration }
1189
- };
1190
- break;
1191
- }
1192
- messages.push({
1193
- role: "assistant",
1194
- content: [
1195
- ...fullText ? [{ type: "text", text: fullText }] : [],
1196
- ...toolCalls.map((tc) => ({
1197
- type: "tool-call",
1198
- toolCallId: tc.toolCallId,
1199
- toolName: tc.toolName,
1200
- args: tc.args
1201
- }))
1202
- ]
1203
- });
1204
- for (const tr of toolResults) {
1205
- messages.push({
1206
- role: "tool",
1207
- content: [
1208
- {
1209
- type: "tool-result",
1210
- toolCallId: tr.toolCallId,
1211
- toolName: tr.toolName,
1212
- result: tr.result
1213
- }
1214
- ]
1215
- });
1216
- }
1217
- yield {
1218
- type: "iteration_end" /* ITERATION_END */,
1219
- timestamp: Date.now(),
1220
- data: { iteration }
1221
- };
1222
- }
1223
- const totalDuration = Date.now() - startTime;
1224
- yield {
1225
- type: "complete" /* COMPLETE */,
1226
- timestamp: Date.now(),
1227
- data: {
1228
- totalIterations: iteration,
1229
- totalDuration
1230
- }
1231
- };
1232
- }
1233
- };
1234
- var PromptBasedAgent = class {
1235
- model;
1236
- mcpManager;
1237
- systemPrompt;
1238
- maxIterations;
1239
- immediateResultMatchers;
1240
- parallelToolCalls;
1241
- // PromptBasedAgent 本身通过 prompt 实现思考,此配置保留以保持接口一致
1242
- enableThinkingPhase;
1243
- constructor(model, mcpManager, options = {}) {
1244
- this.model = model;
1245
- this.mcpManager = mcpManager;
1246
- this.systemPrompt = options.systemPrompt || "";
1247
- this.maxIterations = options.maxIterations || 10;
1248
- this.immediateResultMatchers = options.immediateResultMatchers || [];
1249
- this.parallelToolCalls = options.parallelToolCalls ?? true;
1250
- this.enableThinkingPhase = options.enableThinkingPhase ?? false;
1251
- }
1252
- /**
1253
- * 检查工具返回结果是否匹配即时结果匹配器
1254
- * @param result 工具返回的结果
1255
- * @returns 如果匹配返回 true,否则返回 false
1256
- */
1257
- matchImmediateResult(result) {
1258
- const debug = process.env.DEBUG_MCPLINK === "true";
1259
- if (!this.immediateResultMatchers.length) {
1260
- if (debug) console.log("[MCPLink] \u26A0\uFE0F \u672A\u914D\u7F6E\u5373\u65F6\u7ED3\u679C\u5339\u914D\u5668");
1261
- return false;
1262
- }
1263
- let resultObj = null;
1264
- if (typeof result === "string") {
1265
- try {
1266
- const parsed = JSON.parse(result);
1267
- if (typeof parsed === "object" && parsed !== null) {
1268
- resultObj = parsed;
1269
- if (debug) console.log("[MCPLink] \u{1F50D} \u89E3\u6790\u5DE5\u5177\u7ED3\u679C\u4E3A\u5BF9\u8C61:", Object.keys(parsed));
1270
- }
1271
- } catch {
1272
- if (debug) console.log("[MCPLink] \u26A0\uFE0F \u5DE5\u5177\u7ED3\u679C\u4E0D\u662F\u6709\u6548 JSON");
1273
- }
1274
- } else if (typeof result === "object" && result !== null) {
1275
- resultObj = result;
1276
- if (debug) console.log("[MCPLink] \u{1F50D} \u5DE5\u5177\u7ED3\u679C\u662F\u5BF9\u8C61:", Object.keys(result));
1277
- }
1278
- if (!resultObj) {
1279
- if (debug) console.log("[MCPLink] \u26A0\uFE0F \u65E0\u6CD5\u89E3\u6790\u5DE5\u5177\u7ED3\u679C\u4E3A\u5BF9\u8C61");
1280
- return false;
1281
- }
1282
- for (const matcher of this.immediateResultMatchers) {
1283
- let matched = true;
1284
- for (const [key, value] of Object.entries(matcher)) {
1285
- if (resultObj[key] !== value) {
1286
- matched = false;
1287
- break;
1288
487
  }
1289
488
  }
1290
- if (matched) {
1291
- if (debug) console.log("[MCPLink] \u2705 \u5373\u65F6\u7ED3\u679C\u5339\u914D\u6210\u529F:", JSON.stringify(matcher));
1292
- return true;
489
+ if (choice?.finish_reason) {
490
+ return { type: "done" };
1293
491
  }
492
+ return null;
493
+ } catch {
494
+ return null;
1294
495
  }
1295
- if (debug) console.log("[MCPLink] \u274C \u5373\u65F6\u7ED3\u679C\u672A\u5339\u914D\uFF0C\u671F\u671B:", JSON.stringify(this.immediateResultMatchers), "\u5B9E\u9645:", JSON.stringify(resultObj));
1296
- return false;
1297
496
  }
1298
497
  /**
1299
- * 生成工具列表描述
498
+ * 转换消息格式
1300
499
  */
1301
- generateToolsDescription(tools) {
1302
- if (tools.length === 0) {
1303
- return "\u5F53\u524D\u6CA1\u6709\u53EF\u7528\u7684\u5DE5\u5177\u3002";
1304
- }
1305
- let description = "";
1306
- for (const tool of tools) {
1307
- description += `### ${tool.name}
1308
- `;
1309
- description += `\u63CF\u8FF0: ${tool.description}
1310
- `;
1311
- description += `\u53C2\u6570: ${JSON.stringify(tool.inputSchema, null, 2)}
1312
-
1313
- `;
1314
- }
1315
- return description;
1316
- }
1317
- /**
1318
- * 内置系统提示词 - 强调格式约束
1319
- */
1320
- BUILT_IN_PROMPT = `
1321
- ## \u5DE5\u5177\u8C03\u7528\u683C\u5F0F\uFF08\u5FC5\u987B\u4E25\u683C\u9075\u5B88\uFF09
1322
-
1323
- \u5F53\u4F60\u9700\u8981\u83B7\u53D6\u6570\u636E\u6216\u6267\u884C\u64CD\u4F5C\u65F6\uFF0C**\u53EA\u80FD**\u4F7F\u7528\u4EE5\u4E0B\u683C\u5F0F\uFF1A
1324
-
1325
- <tool_call>
1326
- {"name": "\u5DE5\u5177\u540D\u79F0", "arguments": {"\u53C2\u6570\u540D": "\u503C"}}
1327
- </tool_call>
1328
-
1329
- ### \u5DE5\u4F5C\u6D41\u7A0B
1330
- 1. \u5206\u6790\u7528\u6237\u9700\u6C42
1331
- 2. \u5982\u9700\u6570\u636E\uFF0C\u8F93\u51FA <tool_call>...</tool_call> \u540E**\u7ACB\u5373\u505C\u6B62**
1332
- 3. \u7CFB\u7EDF\u4F1A\u6267\u884C\u5DE5\u5177\u5E76\u8FD4\u56DE\u771F\u5B9E\u7ED3\u679C
1333
- 4. \u6536\u5230\u7ED3\u679C\u540E\uFF0C\u7528\u4E2D\u6587\u6574\u7406\u56DE\u590D\u7528\u6237
1334
-
1335
- ### \u4E25\u683C\u7981\u6B62
1336
- - \u274C \u81EA\u5DF1\u7F16\u5199\u5DE5\u5177\u8FD4\u56DE\u7ED3\u679C\uFF08\u5982 \`\u7ED3\u679C:{...}\` \u6216 \`{"code":200...}\`\uFF09
1337
- - \u274C \u6A21\u62DF\u5DE5\u5177\u8C03\u7528\uFF08\u5982 \`RPCCall:\`\u3001\`FunctionCall:\`\uFF09
1338
- - \u274C \u5728\u6CA1\u6709\u771F\u5B9E\u5DE5\u5177\u7ED3\u679C\u7684\u60C5\u51B5\u4E0B\u7F16\u9020\u6570\u636E
1339
- - \u274C \u4E00\u6B21\u8F93\u51FA\u4E2D\u540C\u65F6\u5305\u542B\u5DE5\u5177\u8C03\u7528\u548C\u6700\u7EC8\u56DE\u590D
1340
-
1341
- ### \u6B63\u786E\u793A\u4F8B
1342
- \u7528\u6237: "\u67E5\u8BE2\u6211\u7684\u8BA2\u5355"
1343
- \u4F60\u7684\u8F93\u51FA:
1344
- <tool_call>
1345
- {"name": "get_orders", "arguments": {"token": "xxx"}}
1346
- </tool_call>
1347
-
1348
- \uFF08\u7136\u540E\u505C\u6B62\uFF0C\u7B49\u5F85\u7CFB\u7EDF\u8FD4\u56DE\u771F\u5B9E\u7ED3\u679C\uFF09
1349
-
1350
- ### \u56DE\u590D\u683C\u5F0F
1351
- - \u4F7F\u7528\u4E2D\u6587
1352
- - \u4F7F\u7528 Markdown \u683C\u5F0F\u7F8E\u5316\u8F93\u51FA
1353
- - \u5217\u8868\u6570\u636E\u6BCF\u9879\u72EC\u5360\u4E00\u884C
1354
- `;
1355
- /**
1356
- * 构建完整的系统提示词
1357
- */
1358
- buildSystemPrompt(tools) {
1359
- const toolsDescription = this.generateToolsDescription(tools);
1360
- const userPrompt = this.systemPrompt || "\u4F60\u662F\u4E00\u4E2A\u667A\u80FD\u52A9\u624B\u3002";
1361
- return `${userPrompt}
1362
-
1363
- ## \u53EF\u7528\u5DE5\u5177
1364
- ${toolsDescription}
1365
- ${this.BUILT_IN_PROMPT}`;
1366
- }
1367
- /**
1368
- * 解析工具调用
1369
- */
1370
- parseToolCall(text) {
1371
- const tagMatch = text.match(/<tool_call>\s*([\s\S]*?)\s*<\/tool_call>/i);
1372
- if (tagMatch) {
1373
- try {
1374
- const json = JSON.parse(tagMatch[1].trim());
1375
- if (json.name) return { name: json.name, arguments: json.arguments || {} };
1376
- } catch {
1377
- }
1378
- }
1379
- const codeMatch = text.match(/```(?:json)?\s*\n?\s*(\{[\s\S]*?"name"[\s\S]*?\})\s*\n?\s*```/i);
1380
- if (codeMatch) {
1381
- try {
1382
- const json = JSON.parse(codeMatch[1].trim());
1383
- if (json.name) return { name: json.name, arguments: json.arguments || {} };
1384
- } catch {
1385
- }
1386
- }
1387
- const jsonMatch = text.match(/\{\s*"name"\s*:\s*"([^"]+)"[\s\S]*?"arguments"\s*:\s*(\{[\s\S]*?\})\s*\}/i);
1388
- if (jsonMatch) {
1389
- try {
1390
- const fullMatch = jsonMatch[0];
1391
- const json = JSON.parse(fullMatch);
1392
- if (json.name) return { name: json.name, arguments: json.arguments || {} };
1393
- } catch {
1394
- }
1395
- }
1396
- return null;
1397
- }
1398
- /**
1399
- * 智能压缩历史消息
1400
- * - 用户消息完整保留
1401
- * - AI 回复保留关键信息(ID、名称、数量、价格等)
1402
- * - 去除冗长的 JSON 原始数据
1403
- */
1404
- compressHistory(history) {
1405
- const MAX_USER_MESSAGE_LENGTH = 500;
1406
- const MAX_ASSISTANT_MESSAGE_LENGTH = 1500;
1407
- const recentHistory = history.slice(-20);
1408
- return recentHistory.map((msg) => {
1409
- if (msg.role === "user") {
1410
- if (msg.content.length <= MAX_USER_MESSAGE_LENGTH) {
1411
- return msg;
1412
- }
1413
- return {
1414
- role: msg.role,
1415
- content: msg.content.slice(0, MAX_USER_MESSAGE_LENGTH) + "..."
1416
- };
1417
- }
1418
- let content = msg.content;
1419
- content = content.replace(/```json\n[\s\S]*?\n```/g, "[\u5DE5\u5177\u8FD4\u56DE\u6570\u636E]");
1420
- content = content.replace(/## .*?\(原始JSON\)[\s\S]*?(?=##|$)/g, "");
1421
- if (content.length > MAX_ASSISTANT_MESSAGE_LENGTH) {
1422
- const tableMatch = content.match(/\|[\s\S]*?\|/g);
1423
- if (tableMatch) {
1424
- const tables = tableMatch.join("\n");
1425
- if (tables.length < MAX_ASSISTANT_MESSAGE_LENGTH) {
1426
- content = content.slice(0, MAX_ASSISTANT_MESSAGE_LENGTH - tables.length) + "\n" + tables;
500
+ convertMessage(msg) {
501
+ if (msg.role !== "tool") {
502
+ const result = {
503
+ role: msg.role,
504
+ content: msg.content
505
+ };
506
+ if (msg.role === "assistant" && msg.toolCalls) {
507
+ result.tool_calls = msg.toolCalls.map((tc) => ({
508
+ id: tc.id,
509
+ type: "function",
510
+ function: {
511
+ name: tc.name,
512
+ arguments: JSON.stringify(tc.arguments)
1427
513
  }
1428
- }
1429
- content = content.slice(0, MAX_ASSISTANT_MESSAGE_LENGTH) + "...";
1430
- }
1431
- return { role: msg.role, content: content.trim() || msg.content.slice(0, 500) };
1432
- });
1433
- }
1434
- /**
1435
- * 将 UserMessage 转换为 Vercel AI SDK 的消息内容格式
1436
- */
1437
- convertUserMessageToContent(message) {
1438
- if (typeof message === "string") {
1439
- return message;
1440
- }
1441
- return message.map((part) => {
1442
- switch (part.type) {
1443
- case "text":
1444
- return { type: "text", text: part.text };
1445
- case "image":
1446
- return { type: "image", image: part.image, mimeType: part.mimeType };
1447
- case "file":
1448
- return { type: "file", data: part.data, mimeType: part.mimeType };
1449
- default:
1450
- return { type: "text", text: "" };
1451
- }
1452
- });
1453
- }
1454
- /**
1455
- * 从 UserMessage 提取纯文本内容(用于日志等)
1456
- */
1457
- extractTextFromMessage(message) {
1458
- if (typeof message === "string") {
1459
- return message;
1460
- }
1461
- return message.filter((part) => part.type === "text").map((part) => part.text).join("\n");
1462
- }
1463
- /**
1464
- * 流式对话(支持多模态消息)
1465
- */
1466
- async *chatStream(userMessage, options) {
1467
- const startTime = Date.now();
1468
- let mcpTools = this.mcpManager.getAllTools();
1469
- if (options?.allowedTools?.length) {
1470
- mcpTools = mcpTools.filter((t) => options.allowedTools.includes(t.name));
1471
- }
1472
- const messages = [
1473
- { role: "system", content: this.buildSystemPrompt(mcpTools) }
1474
- ];
1475
- if (options?.history?.length) {
1476
- const compressedHistory = this.compressHistory(options.history);
1477
- console.log(`[PromptBasedAgent] \u{1F4DA} \u5386\u53F2\u6D88\u606F: ${options.history.length} \u6761 -> \u538B\u7F29\u540E: ${compressedHistory.length} \u6761`);
1478
- for (const msg of compressedHistory) {
1479
- messages.push({ role: msg.role, content: msg.content });
514
+ }));
1480
515
  }
516
+ return result;
1481
517
  }
1482
- const userContent = this.convertUserMessageToContent(userMessage);
1483
- messages.push({ role: "user", content: userContent });
1484
- const textPreview = this.extractTextFromMessage(userMessage);
1485
- console.log(`[PromptBasedAgent] \u{1F4DD} \u7528\u6237\u6D88\u606F: "${textPreview.slice(0, 50)}${textPreview.length > 50 ? "..." : ""}"`);
1486
- console.log(`[PromptBasedAgent] \u{1F4CA} \u603B\u6D88\u606F\u6570: ${messages.length}`);
1487
- let iteration = 0;
1488
- while (iteration < this.maxIterations) {
1489
- iteration++;
1490
- yield {
1491
- type: "iteration_start" /* ITERATION_START */,
1492
- timestamp: Date.now(),
1493
- data: { iteration, maxIterations: this.maxIterations }
518
+ if (msg.toolResults && msg.toolResults.length > 0) {
519
+ const tr = msg.toolResults[0];
520
+ return {
521
+ role: "tool",
522
+ tool_call_id: tr.toolCallId,
523
+ content: typeof tr.result === "string" ? tr.result : JSON.stringify(tr.result)
1494
524
  };
1495
- console.log(`[PromptBasedAgent] \u{1F916} \u8C03\u7528\u6A21\u578B\uFF0C\u8FED\u4EE3 ${iteration}/${this.maxIterations}...`);
1496
- const modelStartTime = Date.now();
1497
- const stream = streamText({
1498
- model: this.model,
1499
- messages,
1500
- // 设置请求超时
1501
- experimental_telemetry: {
1502
- isEnabled: false
1503
- // 禁用遥测以减少开销
1504
- }
1505
- });
1506
- let fullResponse = "";
1507
- let buffer = "";
1508
- let inThinking = false;
1509
- let inToolCall = false;
1510
- let thinkingStarted = false;
1511
- let thinkingEnded = false;
1512
- let textStarted = false;
1513
- let firstChunkReceived = false;
1514
- const FIRST_CHUNK_TIMEOUT = 12e4;
1515
- let timeoutId = null;
1516
- new Promise((_, reject) => {
1517
- timeoutId = setTimeout(() => {
1518
- reject(new Error(`\u6A21\u578B\u54CD\u5E94\u8D85\u65F6 (${FIRST_CHUNK_TIMEOUT / 1e3}\u79D2\u65E0\u54CD\u5E94)`));
1519
- }, FIRST_CHUNK_TIMEOUT);
1520
- });
1521
- try {
1522
- for await (const chunk of stream.fullStream) {
1523
- if (!firstChunkReceived) {
1524
- firstChunkReceived = true;
1525
- if (timeoutId) {
1526
- clearTimeout(timeoutId);
1527
- timeoutId = null;
1528
- }
1529
- console.log(`[PromptBasedAgent] \u26A1 \u9996\u4E2A chunk \u5230\u8FBE\uFF0C\u8017\u65F6: ${Date.now() - modelStartTime}ms`);
1530
- }
1531
- if (chunk.type === "reasoning") {
1532
- if (!thinkingStarted) {
1533
- thinkingStarted = true;
1534
- yield { type: "thinking_start" /* THINKING_START */, timestamp: Date.now(), data: {} };
1535
- }
1536
- if (chunk.textDelta) {
1537
- yield { type: "thinking_delta" /* THINKING_DELTA */, timestamp: Date.now(), data: { content: chunk.textDelta } };
1538
- }
1539
- continue;
1540
- }
1541
- if (chunk.type === "text-delta") {
1542
- const delta = chunk.textDelta;
1543
- buffer += delta;
1544
- fullResponse += delta;
1545
- while (buffer.length > 0) {
1546
- if (!inThinking && !inToolCall) {
1547
- const thinkStart = buffer.indexOf("<think>");
1548
- if (thinkStart !== -1) {
1549
- if (thinkStart > 0) {
1550
- const before = buffer.substring(0, thinkStart);
1551
- if (before.trim() && thinkingEnded) {
1552
- if (!textStarted) {
1553
- textStarted = true;
1554
- yield { type: "text_start" /* TEXT_START */, timestamp: Date.now(), data: {} };
1555
- }
1556
- yield { type: "text_delta" /* TEXT_DELTA */, timestamp: Date.now(), data: { content: before } };
1557
- }
1558
- }
1559
- inThinking = true;
1560
- if (!thinkingStarted) {
1561
- thinkingStarted = true;
1562
- yield { type: "thinking_start" /* THINKING_START */, timestamp: Date.now(), data: {} };
1563
- }
1564
- buffer = buffer.substring(thinkStart + 7);
1565
- continue;
1566
- }
1567
- const toolStart = buffer.indexOf("<tool_call>");
1568
- if (toolStart !== -1) {
1569
- if (toolStart > 0) {
1570
- const before = buffer.substring(0, toolStart);
1571
- if (before.trim() && thinkingEnded) {
1572
- if (!textStarted) {
1573
- textStarted = true;
1574
- yield { type: "text_start" /* TEXT_START */, timestamp: Date.now(), data: {} };
1575
- }
1576
- yield { type: "text_delta" /* TEXT_DELTA */, timestamp: Date.now(), data: { content: before } };
1577
- }
1578
- }
1579
- inToolCall = true;
1580
- buffer = buffer.substring(toolStart + 11);
1581
- continue;
1582
- }
1583
- if (!buffer.includes("<")) {
1584
- if (buffer.trim() && (thinkingEnded || !thinkingStarted)) {
1585
- if (!textStarted) {
1586
- textStarted = true;
1587
- yield { type: "text_start" /* TEXT_START */, timestamp: Date.now(), data: {} };
1588
- }
1589
- yield { type: "text_delta" /* TEXT_DELTA */, timestamp: Date.now(), data: { content: buffer } };
1590
- }
1591
- buffer = "";
1592
- }
1593
- break;
1594
- }
1595
- if (inThinking) {
1596
- const thinkEnd = buffer.indexOf("</think>");
1597
- if (thinkEnd !== -1) {
1598
- const content = buffer.substring(0, thinkEnd);
1599
- if (content) {
1600
- yield { type: "thinking_delta" /* THINKING_DELTA */, timestamp: Date.now(), data: { content } };
1601
- }
1602
- yield { type: "thinking_end" /* THINKING_END */, timestamp: Date.now(), data: {} };
1603
- thinkingEnded = true;
1604
- inThinking = false;
1605
- buffer = buffer.substring(thinkEnd + 8);
1606
- continue;
1607
- }
1608
- if (buffer.length > 10 && !buffer.includes("<")) {
1609
- const safe = buffer.substring(0, buffer.length - 10);
1610
- yield { type: "thinking_delta" /* THINKING_DELTA */, timestamp: Date.now(), data: { content: safe } };
1611
- buffer = buffer.substring(safe.length);
1612
- }
1613
- break;
1614
- }
1615
- if (inToolCall) {
1616
- const toolEnd = buffer.indexOf("</tool_call>");
1617
- if (toolEnd !== -1) {
1618
- inToolCall = false;
1619
- buffer = buffer.substring(toolEnd + 12);
1620
- continue;
1621
- }
1622
- break;
1623
- }
1624
- break;
1625
- }
1626
- }
1627
- if (chunk.type === "finish") {
1628
- if (buffer.trim()) {
1629
- if (inThinking) {
1630
- yield { type: "thinking_delta" /* THINKING_DELTA */, timestamp: Date.now(), data: { content: buffer } };
1631
- yield { type: "thinking_end" /* THINKING_END */, timestamp: Date.now(), data: {} };
1632
- thinkingEnded = true;
1633
- } else if (!inToolCall) {
1634
- if (!textStarted) {
1635
- textStarted = true;
1636
- yield { type: "text_start" /* TEXT_START */, timestamp: Date.now(), data: {} };
1637
- }
1638
- yield { type: "text_delta" /* TEXT_DELTA */, timestamp: Date.now(), data: { content: buffer } };
1639
- }
1640
- }
1641
- if (textStarted) {
1642
- yield { type: "text_end" /* TEXT_END */, timestamp: Date.now(), data: {} };
1643
- }
1644
- }
1645
- }
1646
- } finally {
1647
- if (timeoutId) {
1648
- clearTimeout(timeoutId);
1649
- }
1650
- }
1651
- const toolCall = this.parseToolCall(fullResponse);
1652
- if (toolCall) {
1653
- const toolCallId = `tool-${Date.now()}`;
1654
- yield {
1655
- type: "tool_call_start" /* TOOL_CALL_START */,
1656
- timestamp: Date.now(),
1657
- data: { toolName: toolCall.name, toolCallId, toolArgs: toolCall.arguments }
1658
- };
1659
- yield {
1660
- type: "tool_executing" /* TOOL_EXECUTING */,
1661
- timestamp: Date.now(),
1662
- data: { toolName: toolCall.name, toolCallId, toolArgs: toolCall.arguments }
1663
- };
1664
- const toolStartTime = Date.now();
1665
- let result;
1666
- let isError = false;
1667
- try {
1668
- result = await this.mcpManager.callTool(toolCall.name, toolCall.arguments);
1669
- } catch (error) {
1670
- result = error instanceof Error ? error.message : String(error);
1671
- isError = true;
1672
- }
1673
- const duration = Date.now() - toolStartTime;
1674
- yield {
1675
- type: "tool_result" /* TOOL_RESULT */,
1676
- timestamp: Date.now(),
1677
- data: { toolName: toolCall.name, toolResult: result, toolCallId, duration, isError }
1678
- };
1679
- if (!isError && this.matchImmediateResult(result)) {
1680
- yield {
1681
- type: "immediate_result" /* IMMEDIATE_RESULT */,
1682
- timestamp: Date.now(),
1683
- data: {
1684
- toolName: toolCall.name,
1685
- toolCallId,
1686
- immediateResult: result
1687
- }
1688
- };
1689
- yield { type: "iteration_end" /* ITERATION_END */, timestamp: Date.now(), data: { iteration } };
1690
- yield {
1691
- type: "complete" /* COMPLETE */,
1692
- timestamp: Date.now(),
1693
- data: { totalDuration: Date.now() - startTime, totalIterations: iteration }
1694
- };
1695
- return;
1696
- }
1697
- messages.push({ role: "assistant", content: fullResponse });
1698
- const resultStr = typeof result === "string" ? result : JSON.stringify(result, null, 2);
1699
- messages.push({
1700
- role: "user",
1701
- content: `\u5DE5\u5177 ${toolCall.name} \u8FD4\u56DE\u7ED3\u679C\uFF1A
1702
- ${resultStr}
1703
-
1704
- \u8BF7\u6839\u636E\u7ED3\u679C\u7528\u4E2D\u6587\u56DE\u590D\u7528\u6237\u3002`
1705
- });
1706
- yield { type: "iteration_end" /* ITERATION_END */, timestamp: Date.now(), data: { iteration } };
1707
- continue;
1708
- }
1709
- console.log(`[PromptBasedAgent] \u2705 \u6A21\u578B\u54CD\u5E94\u5B8C\u6210\uFF0C\u8017\u65F6: ${Date.now() - modelStartTime}ms\uFF0C\u54CD\u5E94\u957F\u5EA6: ${fullResponse.length}`);
1710
- if (!textStarted && fullResponse.trim()) {
1711
- let cleanText = fullResponse.replace(/<think>[\s\S]*?<\/think>/gi, "").replace(/<tool_call>[\s\S]*?<\/tool_call>/gi, "").trim();
1712
- if (cleanText) {
1713
- yield { type: "text_start" /* TEXT_START */, timestamp: Date.now(), data: {} };
1714
- yield { type: "text_delta" /* TEXT_DELTA */, timestamp: Date.now(), data: { content: cleanText } };
1715
- yield { type: "text_end" /* TEXT_END */, timestamp: Date.now(), data: {} };
1716
- }
1717
- }
1718
- yield { type: "iteration_end" /* ITERATION_END */, timestamp: Date.now(), data: { iteration } };
1719
- break;
1720
525
  }
1721
- yield {
1722
- type: "complete" /* COMPLETE */,
1723
- timestamp: Date.now(),
1724
- data: { totalDuration: Date.now() - startTime, totalIterations: iteration }
526
+ return {
527
+ role: "user",
528
+ content: msg.content
1725
529
  };
1726
530
  }
1727
531
  };
532
+ var openaiAdapter = new OpenAIAdapter();
1728
533
 
1729
534
  // src/MCPLink.ts
1730
- var NATIVE_FUNCTION_CALLING_PATTERNS = [
1731
- // OpenAI GPT 系列 - 支持原生 function calling
1732
- /^gpt/i,
1733
- // OpenAI o1/o3 需要特殊处理,暂用 PromptBased
1734
- // /^o1/i,
1735
- // /^o3/i,
1736
- // Anthropic Claude - 支持原生 function calling
1737
- /^claude/i,
1738
- // Google Gemini 稳定版 - 支持原生 function calling
1739
- // 注意:gemini-*-preview/thinking 版本需要特殊处理,不在此列表
1740
- /^gemini-[\d.]+-flash$/i,
1741
- /^gemini-[\d.]+-pro$/i,
1742
- /^gemini-pro$/i,
1743
- /^gemini-flash$/i,
1744
- // Mistral - 支持原生 function calling
1745
- /^mistral/i,
1746
- /^mixtral/i,
1747
- // Cohere Command-R - 支持原生 function calling
1748
- /^command-r/i
1749
- ];
1750
- var PROMPT_BASED_PATTERNS = [
1751
- // DeepSeek(不支持原生 function calling)
1752
- /deepseek/i,
1753
- // OpenAI o1/o3 思考模型
1754
- /^o1/i,
1755
- /^o3/i,
1756
- // Gemini 思考/预览版本 - 需要 thought_signature,暂用 PromptBased
1757
- /gemini.*preview/i,
1758
- /gemini.*thinking/i,
1759
- /gemini.*exp/i,
1760
- // 开源模型(大多数不支持原生 function calling)
1761
- /^llama/i,
1762
- /^phi-/i,
1763
- /^qwen/i,
1764
- /^yi-/i,
1765
- /^glm/i,
1766
- /^baichuan/i
1767
- ];
1768
- function detectNativeToolSupport(modelId) {
1769
- console.log(`[MCPLink] \u{1F50D} \u68C0\u6D4B\u6A21\u578B: "${modelId}"`);
1770
- for (const pattern of PROMPT_BASED_PATTERNS) {
1771
- if (pattern.test(modelId)) {
1772
- console.log(`[MCPLink] \u2705 Model "${modelId}" -> PromptBasedAgent (matched: ${pattern})`);
1773
- return false;
1774
- }
1775
- }
1776
- for (const pattern of NATIVE_FUNCTION_CALLING_PATTERNS) {
1777
- if (pattern.test(modelId)) {
1778
- console.log(`[MCPLink] \u2705 Model "${modelId}" -> Agent (\u539F\u751F\u6A21\u5F0F, matched: ${pattern})`);
1779
- return true;
1780
- }
1781
- }
1782
- console.log(`[MCPLink] \u26A0\uFE0F Model "${modelId}" -> PromptBasedAgent (\u672A\u77E5\u6A21\u578B\uFF0C\u9ED8\u8BA4)`);
1783
- return false;
1784
- }
1785
535
  var MCPLink = class {
1786
- model;
1787
- mcpManager;
1788
- agent;
1789
- promptBasedAgent;
1790
536
  config;
537
+ mcpManager;
538
+ httpClient;
539
+ adapter;
1791
540
  initialized = false;
1792
- detectedNativeSupport;
1793
541
  constructor(config) {
1794
542
  this.config = config;
1795
- this.model = config.model;
1796
543
  this.mcpManager = new MCPManager();
544
+ this.httpClient = new HttpClient();
545
+ if (typeof config.adapter === "string") {
546
+ this.adapter = this.getAdapterByType(config.adapter);
547
+ } else if (config.adapter) {
548
+ this.adapter = config.adapter;
549
+ } else {
550
+ this.adapter = openaiAdapter;
551
+ }
1797
552
  if (config.mcpServers) {
1798
553
  for (const [id, serverConfig] of Object.entries(config.mcpServers)) {
1799
554
  this.mcpManager.addServer(id, serverConfig);
1800
555
  }
1801
556
  }
1802
- this.agent = new Agent(this.model, this.mcpManager, {
1803
- systemPrompt: config.systemPrompt,
1804
- maxIterations: config.maxIterations,
1805
- immediateResultMatchers: config.immediateResultMatchers,
1806
- parallelToolCalls: config.parallelToolCalls,
1807
- enableThinkingPhase: config.enableThinkingPhase,
1808
- thinkingPhasePrompt: config.thinkingPhasePrompt,
1809
- thinkingMaxTokens: config.thinkingMaxTokens
1810
- });
1811
- this.promptBasedAgent = new PromptBasedAgent(this.model, this.mcpManager, {
1812
- systemPrompt: config.systemPrompt,
1813
- maxIterations: config.maxIterations,
1814
- immediateResultMatchers: config.immediateResultMatchers,
1815
- parallelToolCalls: config.parallelToolCalls,
1816
- enableThinkingPhase: config.enableThinkingPhase,
1817
- thinkingPhasePrompt: config.thinkingPhasePrompt,
1818
- thinkingMaxTokens: config.thinkingMaxTokens
1819
- });
1820
- if (config.usePromptBasedTools === true) {
1821
- this.detectedNativeSupport = false;
1822
- } else if (config.usePromptBasedTools === false) {
1823
- this.detectedNativeSupport = true;
1824
- } else {
1825
- const modelNameToCheck = config.modelName || config.model.modelId;
1826
- this.detectedNativeSupport = detectNativeToolSupport(modelNameToCheck);
1827
- }
1828
557
  }
1829
558
  /**
1830
559
  * 初始化 - 连接所有 MCP 服务器
1831
560
  */
1832
561
  async initialize() {
1833
- if (this.initialized) {
1834
- return;
1835
- }
562
+ if (this.initialized) return;
1836
563
  await this.mcpManager.startAll();
1837
564
  this.initialized = true;
1838
565
  }
@@ -1844,132 +571,349 @@ var MCPLink = class {
1844
571
  this.initialized = false;
1845
572
  }
1846
573
  /**
1847
- * 发起对话
1848
- */
1849
- async chat(message, callbacks) {
574
+ * 对话 - 极简设计
575
+ *
576
+ * 流程:
577
+ * 1. 发送消息给 AI
578
+ * 2. AI 返回文本/工具调用
579
+ * 3. 如果有工具调用,执行 MCP 工具
580
+ * 4. 返回结果(包括新的消息历史,用户可选择是否继续)
581
+ *
582
+ * 用户需要自己:
583
+ * - 维护 messages 历史
584
+ * - 处理流式响应(通过 onStream)
585
+ * - 决定是否继续迭代(如果返回了 toolCalls)
586
+ */
587
+ async chat(options) {
1850
588
  if (!this.initialized) {
1851
589
  await this.initialize();
1852
590
  }
1853
- return this.agent.chat(message, callbacks);
591
+ const startTime = Date.now();
592
+ const maxIterations = this.config.maxIterations ?? 10;
593
+ let iterations = 0;
594
+ let messages = [...options.messages];
595
+ const mcpTools = this.mcpManager.getAllTools();
596
+ const tools = mcpTools.map((t) => ({
597
+ name: t.name,
598
+ description: t.description,
599
+ parameters: t.inputSchema
600
+ }));
601
+ while (iterations < maxIterations) {
602
+ iterations++;
603
+ const response = options.stream !== false ? await this.streamChat(messages, tools, options.onStream) : await this.singleChat(messages, tools);
604
+ if (!response.toolCalls || response.toolCalls.length === 0) {
605
+ messages.push({
606
+ role: "assistant",
607
+ content: response.content
608
+ });
609
+ return {
610
+ content: response.content,
611
+ messages,
612
+ iterations,
613
+ duration: Date.now() - startTime
614
+ };
615
+ }
616
+ const assistantMessage = {
617
+ role: "assistant",
618
+ content: response.content,
619
+ toolCalls: response.toolCalls
620
+ };
621
+ const toolResults = [];
622
+ for (const tc of response.toolCalls) {
623
+ let result;
624
+ let isError = false;
625
+ try {
626
+ result = await this.mcpManager.callTool(tc.name, tc.arguments);
627
+ } catch (error) {
628
+ result = error instanceof Error ? error.message : String(error);
629
+ isError = true;
630
+ }
631
+ toolResults.push({
632
+ toolCallId: tc.id,
633
+ toolName: tc.name,
634
+ result,
635
+ isError
636
+ });
637
+ }
638
+ messages.push(assistantMessage);
639
+ messages.push({
640
+ role: "tool",
641
+ content: "",
642
+ toolResults
643
+ });
644
+ }
645
+ return {
646
+ content: messages[messages.length - 1]?.content || "",
647
+ messages,
648
+ iterations,
649
+ duration: Date.now() - startTime
650
+ };
651
+ }
652
+ /**
653
+ * 单次 AI 调用(非流式)
654
+ */
655
+ async singleChat(messages, tools) {
656
+ const response = await this.httpClient.chat(
657
+ this.config.ai,
658
+ this.adapter,
659
+ messages,
660
+ tools
661
+ );
662
+ return {
663
+ content: response.content,
664
+ toolCalls: response.toolCalls
665
+ };
666
+ }
667
+ /**
668
+ * 流式 AI 调用
669
+ */
670
+ async streamChat(messages, tools, onStream) {
671
+ let content = "";
672
+ const toolCalls = [];
673
+ for await (const event of this.httpClient.streamChat(
674
+ this.config.ai,
675
+ this.adapter,
676
+ messages,
677
+ tools
678
+ )) {
679
+ const shouldContinue = onStream?.(event);
680
+ if (shouldContinue === false) {
681
+ break;
682
+ }
683
+ switch (event.type) {
684
+ case "text":
685
+ content += event.content;
686
+ break;
687
+ case "tool_call":
688
+ toolCalls.push(event.toolCall);
689
+ break;
690
+ case "error":
691
+ throw event.error;
692
+ }
693
+ }
694
+ return {
695
+ content,
696
+ toolCalls: toolCalls.length ? toolCalls : void 0
697
+ };
1854
698
  }
1855
699
  /**
1856
- * 流式对话
1857
- * @param message 用户消息(支持字符串或多模态数组)
1858
- * @param options 可选参数
1859
- * @param options.allowedTools 允许使用的工具名称列表
1860
- * @param options.history 历史消息列表
700
+ * 流式对话 - 公共方法
701
+ * 直接发起流式请求并返回结果
1861
702
  */
1862
- async *chatStream(message, options) {
703
+ async chatStream(messages, onStream) {
1863
704
  if (!this.initialized) {
1864
705
  await this.initialize();
1865
706
  }
1866
- if (this.detectedNativeSupport) {
1867
- yield* this.agent.chatStream(message, options);
1868
- } else {
1869
- yield* this.promptBasedAgent.chatStream(message, options);
707
+ const startTime = Date.now();
708
+ const mcpTools = this.mcpManager.getAllTools();
709
+ const tools = mcpTools.map((t) => ({
710
+ name: t.name,
711
+ description: t.description,
712
+ parameters: t.inputSchema
713
+ }));
714
+ const response = await this.streamChat(messages, tools, onStream);
715
+ if (response.toolCalls && response.toolCalls.length > 0) {
716
+ const toolResults = [];
717
+ for (const tc of response.toolCalls) {
718
+ let result;
719
+ let isError = false;
720
+ try {
721
+ result = await this.mcpManager.callTool(tc.name, tc.arguments);
722
+ } catch (error) {
723
+ result = error instanceof Error ? error.message : String(error);
724
+ isError = true;
725
+ }
726
+ toolResults.push({
727
+ toolCallId: tc.id,
728
+ toolName: tc.name,
729
+ result,
730
+ isError
731
+ });
732
+ }
733
+ const assistantMessage = {
734
+ role: "assistant",
735
+ content: response.content,
736
+ toolCalls: response.toolCalls
737
+ };
738
+ messages.push(assistantMessage);
739
+ messages.push({
740
+ role: "tool",
741
+ content: "",
742
+ toolResults
743
+ });
744
+ return {
745
+ content: response.content,
746
+ messages,
747
+ iterations: 1,
748
+ duration: Date.now() - startTime
749
+ };
1870
750
  }
751
+ messages.push({
752
+ role: "assistant",
753
+ content: response.content
754
+ });
755
+ return {
756
+ content: response.content,
757
+ messages,
758
+ iterations: 1,
759
+ duration: Date.now() - startTime
760
+ };
1871
761
  }
1872
762
  /**
1873
- * 获取当前使用的模式
763
+ * 根据类型获取适配器
1874
764
  */
1875
- getToolCallingMode() {
1876
- return this.detectedNativeSupport ? "native" : "prompt-based";
765
+ getAdapterByType(type) {
766
+ switch (type) {
767
+ case "openai":
768
+ return openaiAdapter;
769
+ default:
770
+ throw new Error(`Unknown adapter type: ${type}`);
771
+ }
1877
772
  }
1878
773
  // ============ MCP 服务器管理 ============
1879
- /**
1880
- * 添加 MCP 服务器
1881
- */
1882
774
  addMCPServer(id, config) {
1883
775
  this.mcpManager.addServer(id, config);
1884
776
  }
1885
- /**
1886
- * 移除 MCP 服务器
1887
- */
1888
777
  async removeMCPServer(id) {
1889
778
  await this.mcpManager.removeServer(id);
1890
779
  }
1891
- /**
1892
- * 启动指定 MCP 服务器
1893
- */
1894
780
  async startMCPServer(id) {
1895
781
  await this.mcpManager.startServer(id);
1896
782
  }
1897
- /**
1898
- * 停止指定 MCP 服务器
1899
- */
1900
783
  async stopMCPServer(id) {
1901
784
  await this.mcpManager.stopServer(id);
1902
785
  }
1903
- /**
1904
- * 获取所有 MCP 服务器状态
1905
- */
1906
786
  getMCPServerStatuses() {
1907
787
  return this.mcpManager.getServerStatuses();
1908
788
  }
1909
- /**
1910
- * 获取所有可用工具
1911
- */
1912
789
  getTools() {
1913
790
  return this.mcpManager.getAllTools();
1914
791
  }
1915
- /**
1916
- * 手动调用工具
1917
- */
1918
792
  async callTool(toolName, args) {
1919
793
  return this.mcpManager.callTool(toolName, args);
1920
794
  }
1921
- // ============ 配置管理 ============
1922
- /**
1923
- * 更新系统提示词
1924
- */
1925
- setSystemPrompt(prompt) {
1926
- this.config.systemPrompt = prompt;
1927
- this.agent = new Agent(this.model, this.mcpManager, {
1928
- systemPrompt: prompt,
1929
- maxIterations: this.config.maxIterations,
1930
- immediateResultMatchers: this.config.immediateResultMatchers,
1931
- parallelToolCalls: this.config.parallelToolCalls,
1932
- enableThinkingPhase: this.config.enableThinkingPhase,
1933
- thinkingPhasePrompt: this.config.thinkingPhasePrompt,
1934
- thinkingMaxTokens: this.config.thinkingMaxTokens
1935
- });
1936
- this.promptBasedAgent = new PromptBasedAgent(this.model, this.mcpManager, {
1937
- systemPrompt: prompt,
1938
- maxIterations: this.config.maxIterations,
1939
- immediateResultMatchers: this.config.immediateResultMatchers,
1940
- parallelToolCalls: this.config.parallelToolCalls,
1941
- enableThinkingPhase: this.config.enableThinkingPhase,
1942
- thinkingPhasePrompt: this.config.thinkingPhasePrompt,
1943
- thinkingMaxTokens: this.config.thinkingMaxTokens
1944
- });
795
+ };
796
+
797
+ // src/standard-stream.ts
798
+ async function* toStandardStream(rawStream, options) {
799
+ let iteration = 0;
800
+ options.maxIterations ?? 10;
801
+ const startTime = Date.now();
802
+ let hasTextStarted = false;
803
+ const pendingToolCalls = /* @__PURE__ */ new Map();
804
+ for await (const event of rawStream) {
805
+ const shouldContinue = options.onRawEvent?.(event);
806
+ if (shouldContinue === false) break;
807
+ switch (event.type) {
808
+ case "text":
809
+ if (!hasTextStarted) {
810
+ hasTextStarted = true;
811
+ yield { type: "text_start" };
812
+ }
813
+ if (event.content) {
814
+ yield { type: "text_delta", content: event.content };
815
+ }
816
+ break;
817
+ case "tool_call": {
818
+ if (hasTextStarted) {
819
+ hasTextStarted = false;
820
+ yield { type: "text_end" };
821
+ }
822
+ const tc = event.toolCall;
823
+ pendingToolCalls.set(tc.id, { ...tc, argsBuffer: "" });
824
+ yield {
825
+ type: "tool_call_start",
826
+ toolCallId: tc.id,
827
+ toolName: tc.name,
828
+ toolArgs: tc.arguments
829
+ };
830
+ yield {
831
+ type: "tool_executing",
832
+ toolCallId: tc.id,
833
+ toolName: tc.name
834
+ };
835
+ const toolStartTime = Date.now();
836
+ let result;
837
+ let isError = false;
838
+ try {
839
+ result = await options.executeTool(tc.name, tc.arguments);
840
+ } catch (error) {
841
+ result = error instanceof Error ? error.message : String(error);
842
+ isError = true;
843
+ }
844
+ const duration = Date.now() - toolStartTime;
845
+ yield {
846
+ type: "tool_result",
847
+ toolCallId: tc.id,
848
+ toolName: tc.name,
849
+ toolResult: result,
850
+ duration,
851
+ isError
852
+ };
853
+ pendingToolCalls.delete(tc.id);
854
+ break;
855
+ }
856
+ case "done":
857
+ if (hasTextStarted) {
858
+ hasTextStarted = false;
859
+ yield { type: "text_end" };
860
+ }
861
+ break;
862
+ case "error":
863
+ yield { type: "error", error: event.error };
864
+ return;
865
+ }
1945
866
  }
1946
- /**
1947
- * 更新 AI 模型
1948
- */
1949
- setModel(model) {
1950
- this.model = model;
1951
- this.config.model = model;
1952
- this.agent = new Agent(this.model, this.mcpManager, {
1953
- systemPrompt: this.config.systemPrompt,
1954
- maxIterations: this.config.maxIterations,
1955
- immediateResultMatchers: this.config.immediateResultMatchers,
1956
- parallelToolCalls: this.config.parallelToolCalls,
1957
- enableThinkingPhase: this.config.enableThinkingPhase,
1958
- thinkingPhasePrompt: this.config.thinkingPhasePrompt,
1959
- thinkingMaxTokens: this.config.thinkingMaxTokens
1960
- });
1961
- this.promptBasedAgent = new PromptBasedAgent(this.model, this.mcpManager, {
1962
- systemPrompt: this.config.systemPrompt,
1963
- maxIterations: this.config.maxIterations,
1964
- immediateResultMatchers: this.config.immediateResultMatchers,
1965
- parallelToolCalls: this.config.parallelToolCalls,
1966
- enableThinkingPhase: this.config.enableThinkingPhase,
1967
- thinkingPhasePrompt: this.config.thinkingPhasePrompt,
1968
- thinkingMaxTokens: this.config.thinkingMaxTokens
1969
- });
867
+ if (hasTextStarted) {
868
+ yield { type: "text_end" };
1970
869
  }
1971
- };
870
+ yield {
871
+ type: "complete",
872
+ totalIterations: iteration,
873
+ totalDuration: Date.now() - startTime
874
+ };
875
+ }
876
+ async function collectStandardResponse(stream) {
877
+ const response = {
878
+ content: "",
879
+ toolCalls: [],
880
+ iterations: 0,
881
+ duration: 0
882
+ };
883
+ const toolCallMap = /* @__PURE__ */ new Map();
884
+ for await (const event of stream) {
885
+ switch (event.type) {
886
+ case "text_delta":
887
+ response.content += event.content;
888
+ break;
889
+ case "tool_call_start": {
890
+ const tc = {
891
+ id: event.toolCallId,
892
+ name: event.toolName,
893
+ arguments: event.toolArgs
894
+ };
895
+ response.toolCalls.push(tc);
896
+ toolCallMap.set(event.toolCallId, tc);
897
+ break;
898
+ }
899
+ case "tool_result": {
900
+ const tc = toolCallMap.get(event.toolCallId);
901
+ if (tc) {
902
+ tc.result = event.toolResult;
903
+ tc.duration = event.duration;
904
+ tc.isError = event.isError;
905
+ }
906
+ break;
907
+ }
908
+ case "complete":
909
+ response.iterations = event.totalIterations;
910
+ response.duration = event.totalDuration;
911
+ break;
912
+ }
913
+ }
914
+ return response;
915
+ }
1972
916
 
1973
- export { Agent, DEFAULT_SYSTEM_PROMPT, DEFAULT_THINKING_PHASE_PROMPT, MCPLink, MCPLinkEventType, MCPManager, PromptBasedAgent };
917
+ export { HttpClient, MCPLink, MCPManager, OpenAIAdapter, collectStandardResponse, openaiAdapter, toStandardStream };
1974
918
  //# sourceMappingURL=index.js.map
1975
919
  //# sourceMappingURL=index.js.map