@cloudbase/ai 2.8.20-beta.0 → 2.8.22-beta.0

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.
Files changed (106) hide show
  1. package/dist/cjs/AI.d.ts +27 -0
  2. package/dist/cjs/AI.js +154 -0
  3. package/dist/cjs/index.d.ts +13 -38
  4. package/dist/cjs/index.js +9 -73
  5. package/dist/cjs/models/Ark/index.d.ts +4 -18
  6. package/dist/cjs/models/Ark/index.js +45 -82
  7. package/dist/cjs/models/Ark/type.d.ts +52 -19
  8. package/dist/cjs/models/Ark/type.js +1 -1
  9. package/dist/cjs/models/DashScope/index.d.ts +4 -18
  10. package/dist/cjs/models/DashScope/index.js +45 -82
  11. package/dist/cjs/models/DashScope/type.d.ts +55 -24
  12. package/dist/cjs/models/DashScope/type.js +1 -1
  13. package/dist/cjs/models/HunYuan/index.d.ts +4 -19
  14. package/dist/cjs/models/HunYuan/index.js +47 -107
  15. package/dist/cjs/models/HunYuan/type.d.ts +68 -48
  16. package/dist/cjs/models/HunYuan/type.js +1 -1
  17. package/dist/cjs/models/HunYuan/util.d.ts +4 -0
  18. package/dist/cjs/models/HunYuan/util.js +58 -0
  19. package/dist/cjs/models/HunYuanBeta/index.d.ts +4 -19
  20. package/dist/cjs/models/HunYuanBeta/index.js +42 -110
  21. package/dist/cjs/models/Moonshot/index.d.ts +4 -18
  22. package/dist/cjs/models/Moonshot/index.js +45 -82
  23. package/dist/cjs/models/Moonshot/type.d.ts +63 -9
  24. package/dist/cjs/models/Moonshot/type.js +1 -1
  25. package/dist/cjs/models/Yi/index.d.ts +4 -18
  26. package/dist/cjs/models/Yi/index.js +48 -82
  27. package/dist/cjs/models/Yi/type.d.ts +41 -15
  28. package/dist/cjs/models/Yi/type.js +1 -1
  29. package/dist/cjs/models/ZhiPu/index.d.ts +4 -18
  30. package/dist/cjs/models/ZhiPu/index.js +59 -87
  31. package/dist/cjs/models/ZhiPu/type.d.ts +19 -19
  32. package/dist/cjs/models/ZhiPu/type.js +1 -1
  33. package/dist/cjs/models/index.d.ts +16 -15
  34. package/dist/cjs/models/index.js +26 -23
  35. package/dist/cjs/models/model.d.ts +38 -0
  36. package/dist/cjs/models/model.js +481 -0
  37. package/dist/cjs/type.d.ts +76 -15
  38. package/dist/cjs/type.js +6 -12
  39. package/dist/cjs/utils.d.ts +9 -1
  40. package/dist/cjs/utils.js +46 -2
  41. package/dist/esm/AI.d.ts +27 -0
  42. package/dist/esm/AI.js +128 -0
  43. package/dist/esm/index.d.ts +13 -38
  44. package/dist/esm/index.js +4 -70
  45. package/dist/esm/models/Ark/index.d.ts +4 -18
  46. package/dist/esm/models/Ark/index.js +45 -82
  47. package/dist/esm/models/Ark/type.d.ts +52 -19
  48. package/dist/esm/models/Ark/type.js +1 -1
  49. package/dist/esm/models/DashScope/index.d.ts +4 -18
  50. package/dist/esm/models/DashScope/index.js +45 -82
  51. package/dist/esm/models/DashScope/type.d.ts +55 -24
  52. package/dist/esm/models/DashScope/type.js +1 -1
  53. package/dist/esm/models/HunYuan/index.d.ts +4 -19
  54. package/dist/esm/models/HunYuan/index.js +47 -107
  55. package/dist/esm/models/HunYuan/type.d.ts +68 -48
  56. package/dist/esm/models/HunYuan/type.js +1 -1
  57. package/dist/esm/models/HunYuan/util.d.ts +4 -0
  58. package/dist/esm/models/HunYuan/util.js +53 -0
  59. package/dist/esm/models/HunYuanBeta/index.d.ts +4 -19
  60. package/dist/esm/models/HunYuanBeta/index.js +42 -110
  61. package/dist/esm/models/Moonshot/index.d.ts +4 -18
  62. package/dist/esm/models/Moonshot/index.js +45 -82
  63. package/dist/esm/models/Moonshot/type.d.ts +63 -9
  64. package/dist/esm/models/Moonshot/type.js +1 -1
  65. package/dist/esm/models/Yi/index.d.ts +4 -18
  66. package/dist/esm/models/Yi/index.js +48 -82
  67. package/dist/esm/models/Yi/type.d.ts +41 -15
  68. package/dist/esm/models/Yi/type.js +1 -1
  69. package/dist/esm/models/ZhiPu/index.d.ts +4 -18
  70. package/dist/esm/models/ZhiPu/index.js +59 -87
  71. package/dist/esm/models/ZhiPu/type.d.ts +19 -19
  72. package/dist/esm/models/ZhiPu/type.js +1 -1
  73. package/dist/esm/models/index.d.ts +16 -15
  74. package/dist/esm/models/index.js +17 -16
  75. package/dist/esm/models/model.d.ts +38 -0
  76. package/dist/esm/models/model.js +478 -0
  77. package/dist/esm/type.d.ts +76 -15
  78. package/dist/esm/type.js +5 -11
  79. package/dist/esm/utils.d.ts +9 -1
  80. package/dist/esm/utils.js +41 -1
  81. package/package.json +3 -3
  82. package/src/AI.ts +77 -0
  83. package/src/index.ts +3 -60
  84. package/src/models/Ark/index.ts +52 -54
  85. package/src/models/Ark/type.ts +60 -19
  86. package/src/models/DashScope/index.ts +56 -55
  87. package/src/models/DashScope/type.ts +63 -21
  88. package/src/models/HunYuan/index.ts +44 -67
  89. package/src/models/HunYuan/type.ts +68 -46
  90. package/src/models/HunYuan/util.ts +59 -0
  91. package/src/models/HunYuanBeta/index.ts +41 -75
  92. package/src/models/Moonshot/index.ts +52 -54
  93. package/src/models/Moonshot/type.ts +61 -7
  94. package/src/models/Yi/index.ts +59 -55
  95. package/src/models/Yi/type.ts +47 -19
  96. package/src/models/ZhiPu/index.ts +60 -52
  97. package/src/models/ZhiPu/type.ts +20 -9
  98. package/src/models/index.ts +25 -15
  99. package/src/models/model.ts +415 -0
  100. package/src/type.ts +100 -13
  101. package/src/utils.ts +53 -1
  102. package/dist/cjs/models/HunYuanBeta/type.d.ts +0 -52
  103. package/dist/cjs/models/HunYuanBeta/type.js +0 -3
  104. package/dist/esm/models/HunYuanBeta/type.d.ts +0 -52
  105. package/dist/esm/models/HunYuanBeta/type.js +0 -2
  106. package/src/models/HunYuanBeta/type.ts +0 -61
@@ -1,15 +1,45 @@
1
- import { type ParsedEvent } from '../../eventsource_parser'
2
1
  import {
3
2
  createAsyncIterable,
4
3
  TransformStream,
5
- TextDecoderStream,
6
- createEventSourceParserTransformStream,
7
4
  toPolyfillReadable,
5
+ intoStandardStream,
6
+ isToolCallAssistantMessage,
8
7
  } from '../../utils'
9
- import type { ZhiPuGenerateTextOutput, ZhiPuStreamTextOutput } from './type'
10
- import type { ChatModel, StreamTextResult, ModelReq, BaseChatModelInput } from '../../type'
8
+ import type { ZhiPuGenerateTextOutput, ZhiPuInputData, ZhiPuStreamTextOutput } from './type'
9
+ import type {
10
+ ModelReq,
11
+ BaseChatModelInput,
12
+ SimpleChatModel,
13
+ DoStreamOutput,
14
+ BaseDoStreamOutputChunk,
15
+ DoGenerateOutput,
16
+ } from '../../type'
11
17
 
12
- export class ZhiPuModel implements ChatModel {
18
+ function processInput(input: BaseChatModelInput): ZhiPuInputData {
19
+ const { messages, model, temperature, tool_choice, tools, top_p } = input
20
+
21
+ const processToolChoice = () => {
22
+ if (tool_choice && tool_choice !== 'auto') {
23
+ console.warn('`tool_choice` is not \'auto\'')
24
+ }
25
+ return tool_choice as any
26
+ }
27
+
28
+ return {
29
+ ...input,
30
+ messages,
31
+ model,
32
+ temperature,
33
+ tool_choice: processToolChoice(),
34
+ tools: tools?.map(tool => ({
35
+ ...tool,
36
+ function: { ...tool.function, parameters: JSON.parse(tool.function.parameters) },
37
+ })),
38
+ top_p,
39
+ }
40
+ }
41
+
42
+ export class ZhiPuSimpleModel implements SimpleChatModel {
13
43
  public subUrl = 'zhipu/api/paas/v4/chat/completions'
14
44
  constructor(private req: ModelReq, public baseUrl: string, subUrl?: string) {
15
45
  if (subUrl != null) {
@@ -21,7 +51,8 @@ export class ZhiPuModel implements ChatModel {
21
51
  return `${this.baseUrl}/${this.subUrl}`
22
52
  }
23
53
 
24
- async generateText(data: BaseChatModelInput) {
54
+ public async doGenerate(_data: BaseChatModelInput): Promise<DoGenerateOutput> {
55
+ const data = processInput(_data)
25
56
  const res = (await this.req({
26
57
  url: this.url,
27
58
  data: {
@@ -30,14 +61,13 @@ export class ZhiPuModel implements ChatModel {
30
61
  },
31
62
  stream: false,
32
63
  })) as ZhiPuGenerateTextOutput
33
- return {
34
- text: (res?.choices?.[0]?.message?.content as string) ?? '',
35
- rawResponse: res,
36
- }
64
+ return { ...res, rawResponse: res }
37
65
  }
38
66
 
39
- async streamText(data: BaseChatModelInput) {
40
- const stream = await this.req({
67
+ public async doStream(_data: BaseChatModelInput): Promise<DoStreamOutput> {
68
+ const data = processInput(_data)
69
+ let isToolCall: null | boolean = null
70
+ const _stream = await this.req({
41
71
  url: this.url,
42
72
  data: {
43
73
  ...data,
@@ -45,50 +75,28 @@ export class ZhiPuModel implements ChatModel {
45
75
  },
46
76
  stream: true,
47
77
  })
48
- return new ZhiPuModelStreamResult(stream)
49
- }
50
- }
51
-
52
- class ZhiPuModelStreamResult implements StreamTextResult {
53
- private _eventSourceStream: ReadableStream<ParsedEvent>
54
-
55
- constructor(_stream: ReadableStream<Uint8Array>) {
56
78
  const stream = toPolyfillReadable(_stream) as typeof _stream
57
- this._eventSourceStream = stream
58
- .pipeThrough(new TextDecoderStream())
59
- .pipeThrough(createEventSourceParserTransformStream())
60
- }
61
-
62
- private get teeedStream() {
63
- const [s1, s2] = this._eventSourceStream.tee()
64
- this._eventSourceStream = s2
65
- return s1
66
- }
67
79
 
68
- get eventSourceStream() {
69
- return createAsyncIterable(this.teeedStream)
70
- }
71
-
72
- get dataStream() {
73
- return createAsyncIterable(this.eventSourceStream.pipeThrough(new TransformStream<ParsedEvent, ZhiPuStreamTextOutput>({
80
+ const zhipuStream = intoStandardStream<ZhiPuStreamTextOutput>(stream)
81
+ const streamWithRaw = zhipuStream.pipeThrough(new TransformStream<ZhiPuStreamTextOutput, BaseDoStreamOutputChunk & { rawResponse?: any }>({
74
82
  transform(chunk, controller) {
75
- try {
76
- const data = JSON.parse(chunk.data) as ZhiPuStreamTextOutput
77
- controller.enqueue(data)
78
- } catch (e) {
79
- if (chunk.data !== '[DONE]') {
80
- console.warn('Error when transforming event source data to json', e)
83
+ const newChoices = chunk.choices.map((choice) => {
84
+ const message = choice.delta
85
+ if (isToolCall == null) isToolCall = isToolCallAssistantMessage(message)
86
+ if (isToolCall) {
87
+ return {
88
+ ...choice,
89
+ finish_reason: 'tool_calls' as const,
90
+ delta: message,
91
+ }
81
92
  }
82
- }
93
+ return choice
94
+ })
95
+ const newChunk = { ...chunk, choices: newChoices }
96
+ controller.enqueue({ ...newChunk, rawResponse: chunk })
83
97
  },
84
- }),),)
85
- }
98
+ }),)
86
99
 
87
- get textStream() {
88
- return createAsyncIterable(this.dataStream.pipeThrough(new TransformStream<ZhiPuStreamTextOutput, string>({
89
- transform(chunk, controller) {
90
- controller.enqueue(chunk?.choices?.[0]?.delta?.content ?? '')
91
- },
92
- }),),)
100
+ return createAsyncIterable(streamWithRaw)
93
101
  }
94
102
  }
@@ -1,3 +1,5 @@
1
+ import { ChatModelMessage } from '../../type'
2
+
1
3
  type SystemMessage = {
2
4
  role: 'system'
3
5
  content: string
@@ -13,8 +15,8 @@ type AssistantMessage = {
13
15
  content?: string
14
16
  tool_calls?: Array<{
15
17
  id: string
16
- type: 'web_search' | 'retrieval' | 'function'
17
- function?: { name: string; auguments: string }
18
+ type: string
19
+ function?: { name: string; arguments: string }
18
20
  }>
19
21
  }
20
22
 
@@ -36,25 +38,34 @@ export type ZhiPuInputData = {
36
38
  max_tokens?: number
37
39
  stop?: Array<string>
38
40
  tools?: Array<{
39
- type: 'web_search' | 'retrieval' | 'function'
41
+ type: string
40
42
  function: { name: string; description: string; parameters: object }
41
- retrieval: { knowledge_id: string; prompt_template?: string; parameters: object }
42
- web_search: { enable?: boolean; search_query?: string; search_result?: boolean }
43
43
  }>
44
44
  tool_choice?: 'auto'
45
45
  user_id?: string
46
46
  }
47
47
 
48
- // todo: more precise
49
48
  export type ZhiPuGenerateTextOutput = {
50
49
  choices?: Array<{
51
- message?: { content?: string }
50
+ finish_reason?: 'stop' | 'tool_calls' | 'length' | 'sensitive' | 'network_error'
51
+ message?: ChatModelMessage
52
52
  }>
53
+ usage?: {
54
+ completion_tokens: number
55
+ prompt_tokens: number
56
+ total_tokens: number
57
+ }
53
58
  }
54
59
 
55
- // todo: more precise
56
60
  export type ZhiPuStreamTextOutput = {
57
61
  choices?: Array<{
58
- delta?: { content?: string }
62
+ index: number
63
+ finish_reason?: 'stop' | 'tool_calls' | 'length' | 'sensitive' | 'network_error'
64
+ delta?: ChatModelMessage
59
65
  }>
66
+ usage?: {
67
+ completion_tokens: number
68
+ prompt_tokens: number
69
+ total_tokens: number
70
+ }
60
71
  }
@@ -1,19 +1,29 @@
1
- import { HunYuanModel } from './HunYuan'
2
- import { ArkModel } from './Ark'
3
- import { DashScopeModel } from './DashScope'
4
- import { YiModel } from './Yi'
5
- import { MoonshotModel } from './Moonshot'
6
- import { ZhiPuModel } from './ZhiPu'
7
- import { HunYuanBetaModel } from './HunYuanBeta'
1
+ import { ZhiPuSimpleModel } from './ZhiPu/index'
2
+ import { HunYuanBetaSimpleModel } from './HunYuanBeta/index'
3
+ import { HunYuanSimpleModel } from './HunYuan/index'
4
+ import { ArkSimpleModel } from './Ark/index'
5
+ import { DSSimpleModel } from './DashScope/index'
6
+ import { YiSimpleModel } from './Yi/index'
7
+ import { MoonshotSimpleModel } from './Moonshot/index'
8
8
 
9
9
  export const MODELS = {
10
- hunyuan: HunYuanModel,
11
- 'hunyuan-beta': HunYuanBetaModel,
12
- ark: ArkModel,
13
- dashscope: DashScopeModel,
14
- '01-ai': YiModel,
15
- moonshot: MoonshotModel,
16
- zhipu: ZhiPuModel,
10
+ hunyuan: HunYuanSimpleModel,
11
+ 'hunyuan-beta': HunYuanBetaSimpleModel,
12
+ ark: ArkSimpleModel,
13
+ dashscope: DSSimpleModel,
14
+ '01-ai': YiSimpleModel,
15
+ moonshot: MoonshotSimpleModel,
16
+ zhipu: ZhiPuSimpleModel,
17
17
  }
18
18
 
19
- export { HunYuanModel, HunYuanBetaModel, ArkModel, DashScopeModel, YiModel, MoonshotModel, ZhiPuModel }
19
+ export {
20
+ ZhiPuSimpleModel,
21
+ HunYuanBetaSimpleModel,
22
+ HunYuanSimpleModel,
23
+ ArkSimpleModel,
24
+ DSSimpleModel,
25
+ YiSimpleModel,
26
+ MoonshotSimpleModel,
27
+ }
28
+
29
+ export { ReactModel, toolMap } from './model'
@@ -0,0 +1,415 @@
1
+ import {
2
+ createAsyncIterable,
3
+ TransformStream,
4
+ createPromise,
5
+ isToolCallAssistantMessage,
6
+ functionToolToModelTool,
7
+ } from '../utils'
8
+ import {
9
+ BaseChatModelInput,
10
+ BaseDoStreamOutputChunk,
11
+ DoStreamOutput,
12
+ DoGenerateOutput,
13
+ SimpleChatModel,
14
+ ToolCall,
15
+ ChatModelMessage,
16
+ AsyncIterableReadableStream,
17
+ Usage,
18
+ ToolCallAssistantMessage,
19
+ ModelTool,
20
+ FunctionTool,
21
+ } from '../type'
22
+
23
+ type ReactModelInput = ReactProps & Omit<BaseChatModelInput, 'tools'> & { tools?: Array<ModelTool | FunctionTool> }
24
+
25
+ interface IOnStepFinish {
26
+ messages: Array<ChatModelMessage>
27
+ text?: string
28
+ toolCall?: ToolCall
29
+ toolResult?: unknown
30
+ finishReason?: string
31
+ stepUsage?: Usage
32
+ totalUsage?: Usage
33
+ }
34
+
35
+ interface ReactProps {
36
+ maxSteps?: number
37
+ onStepFinish?: (prop: IOnStepFinish) => unknown
38
+ abortSignal?: AbortSignal // TODO: 实现 abortSignal
39
+ }
40
+
41
+ function processInput(obj: ReactModelInput): [ReactProps, BaseChatModelInput] {
42
+ const { onStepFinish, abortSignal, maxSteps, ...b } = obj
43
+
44
+ if (maxSteps != null && maxSteps < 1) {
45
+ throw new Error('`maxSteps` muse be greater than 0.')
46
+ }
47
+
48
+ return [
49
+ { onStepFinish, abortSignal, maxSteps },
50
+ {
51
+ ...b,
52
+ tools: b.tools?.map((tool) => {
53
+ if ('fn' in tool) {
54
+ return functionToolToModelTool(tool)
55
+ }
56
+ return tool
57
+ }),
58
+ },
59
+ ]
60
+ }
61
+
62
+ export class ReactModel {
63
+ constructor(private model: SimpleChatModel) {}
64
+
65
+ public async generateText(_input: ReactModelInput): Promise<{
66
+ text: string
67
+ messages: Array<ChatModelMessage>
68
+ usage: Usage
69
+ rawResponses: Array<unknown>
70
+ error?: any
71
+ }> {
72
+ const rawResponses = []
73
+ const totalUsage: Usage = { completion_tokens: 0, prompt_tokens: 0, total_tokens: 0 }
74
+
75
+ const [{ onStepFinish, maxSteps = 10 }, input] = processInput(_input)
76
+
77
+ const doGenerate = () => this.model.doGenerate(input) // 后续代码会直接对 input.messages 原地修改,这里一直用同一个对象就行
78
+ let currentRes = await doGenerate()
79
+ let currentStep = 1
80
+ currentRes.rawResponse && rawResponses.push(currentRes.rawResponse)
81
+
82
+ let toolCall: ToolCall | null = null
83
+
84
+ // TODO: 一次对话有多个 tool call? 目前没有这种现象,暂时不处理
85
+ while (currentStep < maxSteps && (toolCall = getToolCallFromGenerate(currentRes)) != null) {
86
+ const stepUsage = createSolidUsage(currentRes.usage)
87
+ addToUsage(totalUsage, stepUsage)
88
+
89
+ // 当判断需要工具调用时
90
+ try {
91
+ const toolCallResult = await callTool(toolCall) // 调用
92
+
93
+ const choice = currentRes.choices[0] // getToolCallFromGenerate 保证了 choice 肯定存在
94
+
95
+ await onStepFinish?.({
96
+ finishReason: choice.finish_reason,
97
+ messages: input.messages.slice(),
98
+ text: choice.message.content,
99
+ toolCall,
100
+ toolResult: toolCallResult,
101
+ stepUsage,
102
+ totalUsage: Object.assign({}, totalUsage),
103
+ })
104
+
105
+ pushNewMessages(input.messages, choice.message as ToolCallAssistantMessage, toolCallResult) // 用调用结果修改最新的消息
106
+
107
+ currentRes = await doGenerate() // 循环对话
108
+ currentRes.rawResponse && rawResponses.push(currentRes.rawResponse)
109
+ currentStep += 1
110
+ } catch (e) {
111
+ return {
112
+ text: '',
113
+ messages: input.messages,
114
+ usage: totalUsage,
115
+ error: e,
116
+ rawResponses,
117
+ }
118
+ }
119
+ }
120
+
121
+ const lastChoice = currentRes?.choices?.[0]
122
+ const lastMessage = lastChoice?.message
123
+
124
+ const text = lastMessage?.content ?? ''
125
+ const messages = lastMessage ? [...input.messages, lastMessage] : input.messages
126
+
127
+ const stepUsage = createSolidUsage(currentRes.usage)
128
+ addToUsage(totalUsage, stepUsage)
129
+
130
+ await onStepFinish?.({
131
+ finishReason: lastChoice.finish_reason,
132
+ messages: messages.slice(),
133
+ text,
134
+ toolCall: getToolCallFromGenerate(currentRes),
135
+ toolResult: null,
136
+ stepUsage,
137
+ totalUsage: Object.assign({}, totalUsage),
138
+ })
139
+
140
+ return {
141
+ text,
142
+ messages,
143
+ usage: totalUsage,
144
+ rawResponses,
145
+ }
146
+ }
147
+
148
+ public async streamText(_input: ReactModelInput): Promise<{
149
+ dataStream: DoStreamOutput
150
+ textStream: AsyncIterableReadableStream<string>
151
+ messages: Promise<Array<ChatModelMessage>>
152
+ usage: Promise<Usage>
153
+ error?: any
154
+ }> {
155
+ const totalUsage: Usage = { completion_tokens: 0, prompt_tokens: 0, total_tokens: 0 }
156
+
157
+ const [{ onStepFinish, maxSteps = 10 }, input] = processInput(_input)
158
+ const doStream = () => this.model.doStream(input) // 后续代码会直接对 input.messages 原地修改,这里一直用同一个对象就行
159
+ let currentRes = await doStream()
160
+ const currentStep = 1
161
+ let readResult: { message: ToolCallAssistantMessage; usage: Usage } | null = null
162
+
163
+ const readCurrentStream = () => {
164
+ const [oldStream, newStream] = currentRes.tee()
165
+ currentRes = createAsyncIterable(oldStream)
166
+ return readFunctionCallStream(newStream)
167
+ }
168
+
169
+ // TODO: 一次对话有多个 tool call? 目前没有这种现象,暂时不处理
170
+ // 这里和 generateText 不太一样,除了解析出 toolCall 外,还需要从流中构造出其他完整的信息
171
+ while (currentStep < maxSteps && (readResult = await readCurrentStream()) != null) {
172
+ const { message: assistantMessage, usage: stepUsage } = readResult
173
+ addToUsage(totalUsage, stepUsage)
174
+
175
+ // 当判断需要工具调用时
176
+ const toolCall = assistantMessage.tool_calls?.[0] // 这个 toolCall 一定存在
177
+ try {
178
+ const toolCallResult = await callTool(toolCall) // 调用
179
+
180
+ await onStepFinish?.({
181
+ finishReason: 'tool_calls',
182
+ messages: input.messages.slice(),
183
+ text: assistantMessage.content,
184
+ toolCall,
185
+ toolResult: toolCallResult,
186
+ stepUsage,
187
+ totalUsage: Object.assign({}, totalUsage),
188
+ })
189
+
190
+ pushNewMessages(input.messages, assistantMessage, toolCallResult) // 用调用结果修改最新的消息
191
+ currentRes = await doStream() // 循环对话
192
+ } catch (e) {
193
+ const [s1, s2] = currentRes.tee()
194
+ return {
195
+ messages: Promise.resolve(input.messages),
196
+ dataStream: createAsyncIterable(s1),
197
+ textStream: createAsyncIterable(s2.pipeThrough(new TransformStream({
198
+ transform(chunk, controller) {
199
+ const str = chunk?.choices?.[0]?.delta?.content
200
+ if (typeof str === 'string') controller.enqueue(str)
201
+ },
202
+ }),),),
203
+ usage: Promise.resolve(totalUsage),
204
+ error: e,
205
+ }
206
+ }
207
+ }
208
+
209
+ /**
210
+ * 最后返回时,有几种情况:
211
+ * 1. 没超 maxStep 无工具调用
212
+ * 2. 超了 maxStep 有工具调用
213
+ * 3. 超了 maxStep 无工具调用
214
+ * never. 没超 maxStep,有工具调用,这时候会进到上面的 while 循环 block 中处理
215
+ *
216
+ * 其中 1. 3. 可以合并,没有工具调用就应该直接返回,无论 maxStep
217
+ *
218
+ * 所以合并为:
219
+ * 1. 无工具调用
220
+ * 2. 有工具调用,但是超过 maxStep
221
+ *
222
+ * 这两种情况都没进到 while 循环 block 中处理
223
+ * 我们需要 a. 塞 message b. 算 Usage c. 调用 onStepFinish
224
+ */
225
+
226
+ readResult = await readCurrentStream()
227
+
228
+ if (readResult) {
229
+ // 情况 2 有工具调用,但是超过 maxStep
230
+ const { message, usage } = readResult
231
+ addToUsage(totalUsage, usage)
232
+
233
+ const messages = [...input.messages, message]
234
+
235
+ onStepFinish({
236
+ messages: messages.slice(),
237
+ finishReason: 'tool_call',
238
+ stepUsage: usage,
239
+ text: message.content,
240
+ toolCall: message.tool_calls[0],
241
+ totalUsage: Object.assign({}, totalUsage),
242
+ })
243
+
244
+ const [s1, s2] = currentRes.tee()
245
+ return {
246
+ messages: Promise.resolve([...input.messages, message]),
247
+ dataStream: createAsyncIterable(s1),
248
+ textStream: createAsyncIterable(s2.pipeThrough(new TransformStream({
249
+ transform(chunk, controller) {
250
+ const str = chunk?.choices?.[0]?.delta?.content
251
+ if (typeof str === 'string') controller.enqueue(str)
252
+ },
253
+ }),),),
254
+ usage: Promise.resolve(totalUsage),
255
+ }
256
+ }
257
+ // 情况 1 无工具调用
258
+ const messagePromise = createPromise<Array<ChatModelMessage>>()
259
+ const usagePromise = createPromise<Usage>()
260
+
261
+ const message: ChatModelMessage = {
262
+ role: 'assistant',
263
+ content: '',
264
+ }
265
+ let finishReason = ''
266
+ const stepUsage: Usage = { completion_tokens: 0, prompt_tokens: 0, total_tokens: 0 }
267
+
268
+ const originStream = currentRes.pipeThrough(new TransformStream({
269
+ transform(chunk, controller) {
270
+ // 不改变 chunk 内容,只是拦截下内容拼最后的结果
271
+ const content = chunk?.choices?.[0]?.delta?.content
272
+ if (typeof content === 'string') {
273
+ message.content += content
274
+ }
275
+
276
+ const reason = chunk?.choices?.[0]?.finish_reason
277
+ if (reason) finishReason = reason
278
+
279
+ // TODO: 不同大模型的 stream usage 格式不一样,后续可能要调整.
280
+ // hunyuan 每个 chunk 都会有 usage,逐步增加,以最后一个的为准;
281
+ // zhipu 最后 chunk 会有 usage;
282
+ if (chunk?.usage?.completion_tokens) stepUsage.completion_tokens = chunk.usage.completion_tokens
283
+ if (chunk?.usage?.prompt_tokens) stepUsage.prompt_tokens = chunk.usage.prompt_tokens
284
+ if (chunk?.usage?.total_tokens) stepUsage.total_tokens = chunk.usage.total_tokens
285
+
286
+ controller.enqueue(chunk)
287
+ },
288
+ flush() {
289
+ messagePromise.res([...input.messages, message])
290
+ addToUsage(totalUsage, stepUsage)
291
+ usagePromise.res(Object.assign({}, totalUsage))
292
+ onStepFinish?.({
293
+ messages: [...input.messages, message],
294
+ finishReason,
295
+ text: message.content,
296
+ stepUsage,
297
+ totalUsage: Object.assign({}, totalUsage),
298
+ })
299
+ },
300
+ }),)
301
+
302
+ const [s1, s2] = originStream.tee()
303
+
304
+ return {
305
+ messages: messagePromise.promise,
306
+ dataStream: createAsyncIterable(s1),
307
+ textStream: createAsyncIterable(s2.pipeThrough(new TransformStream({
308
+ transform(chunk, controller) {
309
+ const content = chunk?.choices?.[0]?.delta?.content
310
+ if (typeof content === 'string') {
311
+ controller.enqueue(content)
312
+ }
313
+ },
314
+ }),),),
315
+ usage: usagePromise.promise,
316
+ }
317
+ }
318
+ }
319
+
320
+ function getToolCallFromGenerate(output: DoGenerateOutput) {
321
+ const choice = output?.choices?.[0]
322
+
323
+ if (!choice) return null
324
+
325
+ const { finish_reason, message } = choice
326
+
327
+ if (finish_reason !== 'tool_calls') return null
328
+ if (!message) return null
329
+ if (!isToolCallAssistantMessage(message)) return null
330
+
331
+ return message.tool_calls[0]
332
+ }
333
+
334
+ function pushNewMessages(
335
+ messages: Array<ChatModelMessage>,
336
+ assistantMessage: ToolCallAssistantMessage,
337
+ toolCallResult: unknown,
338
+ ) {
339
+ messages.push(assistantMessage, {
340
+ role: 'tool',
341
+ tool_call_id: assistantMessage.tool_calls[0].id,
342
+ content: JSON.stringify(toolCallResult),
343
+ })
344
+ }
345
+
346
+ async function readFunctionCallStream(stream: ReadableStream<BaseDoStreamOutputChunk>,): Promise<{ message: ToolCallAssistantMessage; usage: Usage } | null> {
347
+ const stepUsage: Usage = { completion_tokens: 0, prompt_tokens: 0, total_tokens: 0 }
348
+ const aStream = createAsyncIterable(stream)
349
+
350
+ const retToolCall: ToolCall = {
351
+ id: '',
352
+ function: {
353
+ name: '',
354
+ arguments: '',
355
+ },
356
+ type: '',
357
+ }
358
+
359
+ const retMessage: ToolCallAssistantMessage = {
360
+ role: 'assistant',
361
+ content: '',
362
+ tool_calls: [retToolCall],
363
+ }
364
+
365
+ for await (const chunk of aStream) {
366
+ const choice = chunk?.choices[0]
367
+ if (!choice) return null
368
+
369
+ const { finish_reason, delta } = choice
370
+
371
+ if (finish_reason !== 'tool_calls') return null
372
+ if (!delta) continue
373
+
374
+ if (delta.content) retMessage.content += delta.content
375
+
376
+ if (!('tool_calls' in delta)) continue
377
+ const toolCall = delta?.tool_calls?.[0]
378
+ if (toolCall?.id) retToolCall.id = toolCall.id
379
+ if (toolCall?.type) retToolCall.type = toolCall.type
380
+ if (toolCall?.function?.name) retToolCall.function.name = toolCall.function.name
381
+ if (toolCall?.function?.arguments) retToolCall.function.arguments += toolCall.function.arguments
382
+
383
+ // TODO: 不同大模型的 stream usage 格式不一样,后续可能要调整.
384
+ // hunyuan 每个 chunk 都会有 usage,逐步增加,以最后一个的为准;
385
+ // zhipu 最后 chunk 会有 usage;
386
+ if (chunk?.usage?.completion_tokens) stepUsage.completion_tokens = chunk.usage.completion_tokens
387
+ if (chunk?.usage?.prompt_tokens) stepUsage.prompt_tokens = chunk.usage.prompt_tokens
388
+ if (chunk?.usage?.total_tokens) stepUsage.total_tokens = chunk.usage.total_tokens
389
+ }
390
+
391
+ return {
392
+ message: retMessage,
393
+ usage: stepUsage,
394
+ }
395
+ }
396
+
397
+ export const toolMap = new Map<string, CallableFunction>()
398
+
399
+ function callTool(toolCall: ToolCall) {
400
+ return toolMap.get(toolCall.function.name)(JSON.parse(toolCall.function.arguments))
401
+ }
402
+
403
+ function createSolidUsage(usage?: Partial<Usage>): Usage {
404
+ return {
405
+ completion_tokens: usage?.completion_tokens ?? 0,
406
+ prompt_tokens: usage?.prompt_tokens ?? 0,
407
+ total_tokens: usage?.total_tokens ?? 0,
408
+ }
409
+ }
410
+
411
+ function addToUsage(targetUsage: Usage, sourceUsage: Usage) {
412
+ targetUsage.completion_tokens += sourceUsage.completion_tokens
413
+ targetUsage.prompt_tokens += sourceUsage.prompt_tokens
414
+ targetUsage.total_tokens += sourceUsage.total_tokens
415
+ }