@opentiny/next-sdk 0.2.3 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,5 @@
1
1
  import { streamText, stepCountIs, generateText, StreamTextResult } from 'ai'
2
- import {
3
- experimental_MCPClientConfig as MCPClientConfig,
4
- experimental_createMCPClient as createMCPClient
5
- } from '@ai-sdk/mcp'
2
+ import { MCPClientConfig, createMCPClient } from '@ai-sdk/mcp'
6
3
  import type { ToolSet } from 'ai'
7
4
  import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'
8
5
  import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js'
@@ -10,7 +7,7 @@ import type { IAgentModelProviderOption, McpServerConfig } from './type'
10
7
  import { ProviderV2 } from '@ai-sdk/provider'
11
8
  import { OpenAIProvider } from '@ai-sdk/openai'
12
9
  import { createOpenAI } from '@ai-sdk/openai'
13
- import { createDeepSeek } from '@ai-sdk/deepseek'
10
+ import { createDeepSeek, DeepSeekProvider } from '@ai-sdk/deepseek'
14
11
  import { ExtensionClientTransport } from '../transport/ExtensionClientTransport'
15
12
  import { MessageChannelTransport } from '@opentiny/next'
16
13
  import { WebMcpClient } from '../WebMcpClient'
@@ -30,7 +27,7 @@ type ChatMethodFn = typeof streamText | typeof generateText
30
27
  * @returns 暴露了 chat, chatStream方法
31
28
  */
32
29
  export class AgentModelProvider {
33
- llm: ProviderV2 | OpenAIProvider
30
+ llm: ProviderV2 | OpenAIProvider | DeepSeekProvider
34
31
  /** 当前mcpServers对象集合。键为服务器名称,值为 McpServerConfig 或任意的 MCPTransport
35
32
  * 参考: https://ai-sdk.dev/docs/ai-sdk-core/tools-and-tool-calling#initializing-an-mcp-client */
36
33
  mcpServers: Record<string, McpServerConfig> = {}
@@ -48,14 +45,6 @@ export class AgentModelProvider {
48
45
  responseMessages: any[] = []
49
46
  /** 是否使用 ReAct 模式(通过提示词而非 function calling 进行工具调用) */
50
47
  useReActMode: boolean = false
51
- /** 流结束后,返回token细节的事件 */
52
- onUsage?: (usage: {
53
- cachedInputTokens: number
54
- inputTokens: number
55
- reasoningTokens: number
56
- outputTokens: number
57
- totalTokens: number
58
- }) => void
59
48
 
60
49
  constructor({ llmConfig, mcpServers }: IAgentModelProviderOption) {
61
50
  if (!llmConfig) {
@@ -69,7 +58,7 @@ export class AgentModelProvider {
69
58
  this.llm = llmConfig.llm
70
59
  } else if (llmConfig.providerType) {
71
60
  const providerType = llmConfig.providerType
72
- let providerFn: (options: any) => ProviderV2 | OpenAIProvider
61
+ let providerFn: (options: any) => ProviderV2 | OpenAIProvider | DeepSeekProvider
73
62
 
74
63
  if (typeof providerType === 'string') {
75
64
  providerFn = AIProviderFactories[providerType]
@@ -599,6 +588,10 @@ export class AgentModelProvider {
599
588
  // 配置:最多保留的图片数量
600
589
  const maxImages = (options as any).maxImages ?? 3
601
590
 
591
+ // 模拟开启流
592
+ controller.enqueue({ type: 'start' })
593
+ controller.enqueue({ type: 'start-step' })
594
+
602
595
  try {
603
596
  while (stepCount < maxSteps) {
604
597
  stepCount++
@@ -618,7 +611,7 @@ export class AgentModelProvider {
618
611
  messages: messagesForModel
619
612
  })
620
613
 
621
- // 收集流式输出
614
+ // 1、直接问,收集流式输出
622
615
  let assistantText = ''
623
616
  for await (const part of result.fullStream) {
624
617
  if (part.type === 'text-delta') {
@@ -630,8 +623,10 @@ export class AgentModelProvider {
630
623
  })
631
624
  } else if (part.type === 'text-start') {
632
625
  controller.enqueue({ type: 'text-start' })
633
- } else if (part.type === 'text-end') {
626
+ } else if (part.type === 'text-end' || part.type === 'finish-step' || part.type === 'finish') {
634
627
  // 暂时不关闭,等待检查是否有工具调用
628
+ } else if (part.type === 'start' || part.type === 'start-step') {
629
+ // 后面每一轮的start 也要忽略
635
630
  } else {
636
631
  // 转发其他类型的事件
637
632
  controller.enqueue(part)
@@ -644,12 +639,14 @@ export class AgentModelProvider {
644
639
  allUserMessages.push(assistantMsg)
645
640
  fullMessageHistory.push(assistantMsg)
646
641
 
647
- // 解析工具调用
642
+ // 2、解析工具调用
648
643
  const action = parseReActAction(accumulatedText, tools)
649
644
 
650
645
  if (!action) {
651
646
  // 没有工具调用,结束流
652
647
  controller.enqueue({ type: 'text-end' })
648
+ controller.enqueue({ type: 'finish-step' })
649
+ controller.enqueue({ type: 'finish' })
653
650
  controller.close()
654
651
  self.responseMessages = fullMessageHistory
655
652
  // 触发 onFinish 回调
@@ -661,13 +658,15 @@ export class AgentModelProvider {
661
658
  if (action.toolName === 'computer' && action.arguments?.action === 'terminate') {
662
659
  // 视为对话结束
663
660
  controller.enqueue({ type: 'text-end' })
661
+ controller.enqueue({ type: 'finish-step' })
662
+ controller.enqueue({ type: 'finish' })
664
663
  controller.close()
665
664
  self.responseMessages = fullMessageHistory
666
665
  streamCompleteResolver({ messages: fullMessageHistory })
667
666
  return
668
667
  }
669
668
 
670
- // 发送工具调用开始事件(符合 tiny-robot 格式)
669
+ // 3、是工具调用,发送工具调用开始事件(符合 tiny-robot 格式)
671
670
  const toolCallId = `react-${Date.now()}`
672
671
  controller.enqueue({
673
672
  type: 'tool-input-start',
@@ -682,8 +681,19 @@ export class AgentModelProvider {
682
681
  id: toolCallId,
683
682
  delta: argsString
684
683
  })
684
+ controller.enqueue({
685
+ type: 'tool-input-end',
686
+ id: toolCallId
687
+ })
688
+
689
+ // 4、执行工具调用
690
+ controller.enqueue({
691
+ type: 'tool-call',
692
+ toolCallId: toolCallId,
693
+ toolName: action.toolName,
694
+ input: action.arguments
695
+ })
685
696
 
686
- // 执行工具调用
687
697
  const toolResult = await self._executeReActToolCall(action.toolName, action.arguments, tools)
688
698
 
689
699
  // 如果结果包含 screenshot,先提取出来,避免 JSON stringify 导致过大
@@ -715,48 +725,56 @@ export class AgentModelProvider {
715
725
  } else {
716
726
  observationText = JSON.stringify(resultData)
717
727
  }
718
- } else {
719
- observationText = `工具执行失败 - ${toolResult.error}`
720
- }
721
-
722
- // 统一使用 XML 格式的 Observation,如果有截图,添加验证提示
723
- let finalObservation = `<tool_response>\n${observationText}\n</tool_response>`
724
-
725
- if (screenshot) {
726
- finalObservation += `\n请检查截图以确认操作是否成功。如果成功,请继续下一步;如果失败,请重试。`
727
- }
728
-
729
- // 发送工具结果(符合 tiny-robot 格式,给 UI 展示用的,不包含 base64 防止卡顿)
730
- controller.enqueue({
731
- type: 'tool-result',
732
- toolCallId: toolCallId,
733
- result: finalObservation
734
- })
735
728
 
736
- // 添加工具结果到消息历史(ReAct 模式下,工具结果作为 user 消息添加)
737
- const observationMessage = screenshot
738
- ? {
739
- role: 'user',
740
- content: [
741
- { type: 'text', text: finalObservation },
742
- { type: 'image', image: screenshot }
743
- ]
744
- }
745
- : {
746
- role: 'user',
747
- content: finalObservation
748
- }
729
+ // 统一使用 XML 格式的 Observation,如果有截图,添加验证提示
730
+ let finalObservation = `<tool_response>\n${observationText}\n</tool_response>`
749
731
 
750
- // 添加到所有消息和完整历史
751
- allUserMessages.push(observationMessage)
752
- fullMessageHistory.push(observationMessage)
732
+ if (screenshot) {
733
+ finalObservation += `\n请检查截图以确认操作是否成功。如果成功,请继续下一步;如果失败,请重试。`
734
+ }
753
735
 
754
- // 重置累积文本,准备下一轮
755
- accumulatedText = ''
736
+ // 发送工具结果(符合 tiny-robot 格式,给 UI 展示用的,不包含 base64 防止卡顿)
737
+ controller.enqueue({
738
+ type: 'tool-result',
739
+ toolCallId: toolCallId,
740
+ result: finalObservation
741
+ })
742
+
743
+ // 添加工具结果到消息历史(ReAct 模式下,工具结果作为 user 消息添加)
744
+ const observationMessage = screenshot
745
+ ? {
746
+ role: 'user',
747
+ content: [
748
+ { type: 'text', text: finalObservation },
749
+ { type: 'image', image: screenshot }
750
+ ]
751
+ }
752
+ : {
753
+ role: 'user',
754
+ content: finalObservation
755
+ }
756
+
757
+ // 添加到所有消息和完整历史
758
+ allUserMessages.push(observationMessage)
759
+ fullMessageHistory.push(observationMessage)
760
+
761
+ // 重置累积文本,准备下一轮
762
+ accumulatedText = ''
763
+ } else {
764
+ observationText = `工具执行失败 - ${toolResult.error}`
765
+ controller.enqueue({
766
+ type: 'tool-error',
767
+ toolCallId: toolCallId,
768
+ input: action.arguments,
769
+ error: { message: observationText }
770
+ })
771
+ }
756
772
  }
757
773
 
758
774
  // 达到最大步数
759
775
  controller.enqueue({ type: 'text-end' })
776
+ controller.enqueue({ type: 'finish-step' })
777
+ controller.enqueue({ type: 'finish' })
760
778
  controller.close()
761
779
  self.responseMessages = fullMessageHistory
762
780
  // 触发 onFinish 回调
@@ -819,7 +837,7 @@ export class AgentModelProvider {
819
837
  const result = chatMethod(chatOptions)
820
838
 
821
839
  // 缓存 ai-sdk 的多轮对话的消息
822
- ;(result as StreamTextResult<ToolSet, unknown>)?.response?.then((res: any) => {
840
+ ;(result as any)?.response?.then((res: any) => {
823
841
  // 检查 res.messages 的第一条消息是否是 user 消息
824
842
  // 如果不是,且有 lastUserMessage,则先添加 lastUserMessage
825
843
  const firstMessage = res.messages?.[0]
@@ -830,11 +848,6 @@ export class AgentModelProvider {
830
848
  this.responseMessages.push(...res.messages)
831
849
  })
832
850
 
833
- // 读取使用量
834
- result?.usage?.then((usage: any) => {
835
- this.onUsage?.(usage)
836
- })
837
-
838
851
  return result
839
852
  }
840
853
 
package/agent/type.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { ProviderV2 } from '@ai-sdk/provider'
2
- import type { experimental_MCPClientConfig as MCPClientConfig } from '@ai-sdk/mcp'
2
+ import type { MCPClientConfig } from '@ai-sdk/mcp'
3
3
 
4
4
  // 从 MCPClientConfig 中提取 transport 类型
5
5
  export type MCPTransport = MCPClientConfig['transport']
@@ -1,4 +1,4 @@
1
- import { dynamicTool, jsonSchema, Tool, ToolCallOptions, ToolSet } from 'ai'
1
+ import { dynamicTool, jsonSchema, Tool, ToolExecutionOptions, ToolSet } from 'ai'
2
2
  import { WebMcpClient } from '../../WebMcpClient'
3
3
 
4
4
  /**
@@ -13,7 +13,7 @@ export const getAISDKTools = async (client: WebMcpClient): Promise<ToolSet> => {
13
13
  const listToolsResult = await client.listTools()
14
14
 
15
15
  for (const { name, description, inputSchema } of listToolsResult.tools) {
16
- const execute = async (args: any, options: ToolCallOptions): Promise<any> => {
16
+ const execute = async (args: any, options: ToolExecutionOptions): Promise<any> => {
17
17
  return client.callTool({ name, arguments: args }, { signal: options?.abortSignal })
18
18
  }
19
19
 
@@ -2,7 +2,7 @@ import { streamText, generateText } from 'ai';
2
2
  import { IAgentModelProviderOption, McpServerConfig } from './type';
3
3
  import { ProviderV2 } from '@ai-sdk/provider';
4
4
  import { OpenAIProvider, createOpenAI } from '@ai-sdk/openai';
5
- import { createDeepSeek } from '@ai-sdk/deepseek';
5
+ import { createDeepSeek, DeepSeekProvider } from '@ai-sdk/deepseek';
6
6
  import { WebMcpClient } from '../WebMcpClient';
7
7
 
8
8
  export declare const AIProviderFactories: {
@@ -14,7 +14,7 @@ export declare const AIProviderFactories: {
14
14
  * @returns 暴露了 chat, chatStream方法
15
15
  */
16
16
  export declare class AgentModelProvider {
17
- llm: ProviderV2 | OpenAIProvider;
17
+ llm: ProviderV2 | OpenAIProvider | DeepSeekProvider;
18
18
  /** 当前mcpServers对象集合。键为服务器名称,值为 McpServerConfig 或任意的 MCPTransport
19
19
  * 参考: https://ai-sdk.dev/docs/ai-sdk-core/tools-and-tool-calling#initializing-an-mcp-client */
20
20
  mcpServers: Record<string, McpServerConfig>;
@@ -32,14 +32,6 @@ export declare class AgentModelProvider {
32
32
  responseMessages: any[];
33
33
  /** 是否使用 ReAct 模式(通过提示词而非 function calling 进行工具调用) */
34
34
  useReActMode: boolean;
35
- /** 流结束后,返回token细节的事件 */
36
- onUsage?: (usage: {
37
- cachedInputTokens: number;
38
- inputTokens: number;
39
- reasoningTokens: number;
40
- outputTokens: number;
41
- totalTokens: number;
42
- }) => void;
43
35
  constructor({ llmConfig, mcpServers }: IAgentModelProviderOption);
44
36
  /** 创建一个 ai-sdk的 mcpClient, 创建失败则返回 null */
45
37
  private _createOneClient;
@@ -58,7 +50,7 @@ export declare class AgentModelProvider {
58
50
  /** 全量更新所有的 mcpServers */
59
51
  updateMcpServers(mcpServers?: Record<string, McpServerConfig>): Promise<void>;
60
52
  /** 插入一个新的mcpServer,如果已经存在则返回false */
61
- insertMcpServer(serverName: string, mcpServer: McpServerConfig): Promise<false | WebMcpClient | import('@ai-sdk/mcp').experimental_MCPClient | null>;
53
+ insertMcpServer(serverName: string, mcpServer: McpServerConfig): Promise<false | WebMcpClient | import('@ai-sdk/mcp').MCPClient | null>;
62
54
  /** 通过服务器名称删除mcpServer: mcpServers mcpClients mcpTools ignoreToolnames */
63
55
  removeMcpServer(serverName: string): Promise<void>;
64
56
  /** 创建临时允许调用的tools集合 */
@@ -1,5 +1,5 @@
1
1
  import { ProviderV2 } from '@ai-sdk/provider';
2
- import { experimental_MCPClientConfig as MCPClientConfig } from '@ai-sdk/mcp';
2
+ import { MCPClientConfig } from '@ai-sdk/mcp';
3
3
 
4
4
  export type MCPTransport = MCPClientConfig['transport'];
5
5
  type ProviderFactory = 'openai' | 'deepseek' | ((options: any) => ProviderV2);
package/dist/index.d.ts CHANGED
@@ -26,3 +26,4 @@ export { AgentModelProvider } from './agent/AgentModelProvider';
26
26
  export { getAISDKTools } from './agent/utils/getAISDKTools';
27
27
  export { QrCode, type QrCodeOption } from './remoter/QrCode';
28
28
  export type * from './agent/type';
29
+ export { getSkillOverviews, formatSkillsForSystemPrompt, getSkillMdPaths, getSkillMdContent, getMainSkillPaths, getMainSkillPathByName, parseSkillFrontMatter, createSkillTools, type SkillMeta, type SkillToolsSet } from './skills/index';