@cloudbase/ai 3.6.2 → 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 (41) hide show
  1. package/dist/miniprogram/index.js +1 -1
  2. package/dist/miniprogram/index.js.LICENSE.txt +2 -2
  3. package/package.json +8 -4
  4. package/.eslintignore +0 -2
  5. package/.eslintrc.js +0 -16
  6. package/src/AI.ts +0 -191
  7. package/src/bot/index.ts +0 -278
  8. package/src/bot/types.ts +0 -165
  9. package/src/errors.ts +0 -23
  10. package/src/eventsource_parser/index.ts +0 -2
  11. package/src/eventsource_parser/parse.ts +0 -169
  12. package/src/eventsource_parser/stream.ts +0 -35
  13. package/src/eventsource_parser/types.ts +0 -90
  14. package/src/index.ts +0 -165
  15. package/src/models/Ark/index.ts +0 -94
  16. package/src/models/Ark/type.ts +0 -82
  17. package/src/models/DashScope/index.ts +0 -97
  18. package/src/models/DashScope/type.ts +0 -76
  19. package/src/models/DeepSeek/index.ts +0 -80
  20. package/src/models/Default/index.ts +0 -70
  21. package/src/models/HunYuan/index.ts +0 -94
  22. package/src/models/HunYuan/type.ts +0 -85
  23. package/src/models/HunYuan/util.ts +0 -60
  24. package/src/models/HunYuanBeta/index.ts +0 -83
  25. package/src/models/HunYuanExp/index.ts +0 -80
  26. package/src/models/HunYuanOpen/index.ts +0 -83
  27. package/src/models/Moonshot/index.ts +0 -95
  28. package/src/models/Moonshot/type.ts +0 -82
  29. package/src/models/Yi/index.ts +0 -100
  30. package/src/models/Yi/type.ts +0 -77
  31. package/src/models/ZhiPu/index.ts +0 -104
  32. package/src/models/ZhiPu/type.ts +0 -71
  33. package/src/models/image-model.ts +0 -100
  34. package/src/models/index.ts +0 -41
  35. package/src/models/model.ts +0 -425
  36. package/src/type.ts +0 -362
  37. package/src/utils.ts +0 -198
  38. package/tsconfig.esm.json +0 -16
  39. package/tsconfig.json +0 -16
  40. package/webpack/web.prod.js +0 -100
  41. package/webpack/webpack.miniprogram.js +0 -24
@@ -1,100 +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
- import { InvalidParamsError } from '../errors'
12
-
13
- interface HunyuanImageGenerateFn {
14
- (input: HunyuanARGenerateImageInput): Promise<HunyuanARGenerateImageOutput>
15
- (input: HunyuanGenerateImageInput): Promise<HunyuanGenerateImageOutput>
16
- }
17
-
18
- type GenerateImageFn<TProvider> = TProvider extends 'hunyuan-image'
19
- ? HunyuanImageGenerateFn
20
- : (input: unknown) => Promise<unknown>
21
-
22
- export class DefaultImageModel<TProvider extends string> {
23
- public defaultGenerateImageSubUrl = 'images/ar/generations'
24
-
25
- public generateImageSubUrlConfig: Record<string, Array<[RegExp, string]>> = {
26
- 'hunyuan-image': [
27
- [/^hunyuan-image(-v[12](\..*)?)?$/, 'images/generations'],
28
- ],
29
- }
30
-
31
- /** 生成图片 */
32
- public generateImage: GenerateImageFn<TProvider>
33
-
34
- constructor(
35
- private req: ModelReq,
36
- public baseUrl: string,
37
- public provider: TProvider,
38
- ) {
39
- this.generateImage = this.generateImageImpl.bind(this) as GenerateImageFn<TProvider>
40
- }
41
-
42
- /** generateImage 的实际实现 */
43
- private async generateImageImpl(input: HunyuanARGenerateImageInput | HunyuanGenerateImageInput,): Promise<HunyuanARGenerateImageOutput | HunyuanGenerateImageOutput> {
44
- // 当模型为 HY-Image-v3.0-I2I-ToB-v1.0.1 且 provider 为空或为 hunyuan-image 时校验参数
45
- if (
46
- typeof input === 'object' && input && 'model' in input && input.model === 'HY-Image-v3.0-I2I-ToB-v1.0.1' && (this.provider === '' || this.provider === 'hunyuan-image')
47
- ) {
48
- const typedInput = input
49
- const imageUrls = typedInput.image_urls as string[] | undefined
50
- const images = typedInput.images as string[] | undefined
51
-
52
- if (imageUrls && imageUrls.length > 1) {
53
- throw new InvalidParamsError(
54
- '当模型为 HY-Image-v3.0-I2I-ToB-v1.0.1 时,image_urls 最多只能有 1 个元素',
55
- 'image_urls',
56
- )
57
- }
58
- if (images && images.length > 1) {
59
- throw new InvalidParamsError(
60
- '当模型为 HY-Image-v3.0-I2I-ToB-v1.0.1 时,images 最多只能有 1 个元素',
61
- 'images',
62
- )
63
- }
64
- }
65
-
66
- let subUrl = this.defaultGenerateImageSubUrl
67
-
68
- if (typeof input === 'object' && input && 'model' in input && typeof input.model === 'string') {
69
- const newSubUrl = this.getGenerateImageSubUrl(this.provider, input.model)
70
- if (newSubUrl) subUrl = newSubUrl
71
- }
72
-
73
- const res = (await this.req({
74
- url: this.getGenerateUrl(subUrl),
75
- data: input,
76
- stream: false,
77
- })) as HunyuanARGenerateImageOutput | HunyuanGenerateImageOutput
78
-
79
- return res
80
- }
81
-
82
- private getGenerateUrl(subUrl: string) {
83
- return `${this.baseUrl}/${this.provider}/${subUrl}`
84
- }
85
-
86
- /**
87
- * 根据 provider 与 model 解析生成图片的子路径。
88
- * 按配置顺序用 RegExp 匹配,命中即返回对应 subUrl。
89
- */
90
- private getGenerateImageSubUrl(provider: string, model: string): string | undefined {
91
- const rules = this.generateImageSubUrlConfig[provider]
92
- if (!rules) return undefined
93
-
94
- for (const [pattern, subUrl] of rules) {
95
- if (pattern.test(model)) return subUrl
96
- }
97
-
98
- return undefined
99
- }
100
- }
@@ -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
- }