@cloudbase/ai 3.3.14-beta.1 → 3.6.4-alpha.1

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