@keo-ai/axiom 0.2.1 → 0.2.3
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/README.md +42 -0
- package/dist/function_call_loop/loop.d.ts +2 -0
- package/dist/function_call_loop/loop.js +19 -1
- package/dist/function_call_loop/provider.js +45 -55
- package/dist/function_call_loop/stream.js +17 -4
- package/dist/function_call_loop/types.d.ts +5 -0
- package/dist/llm_provider/bailian.js +18 -14
- package/dist/llm_provider/index.d.ts +4 -0
- package/dist/llm_provider/types.d.ts +3 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -177,9 +177,15 @@ for await (const chunk of LLM.streamPredict({ model: 'qwen-max', prompt: '讲个
|
|
|
177
177
|
if (chunk.type === 'reasoning') {
|
|
178
178
|
process.stdout.write(chunk.delta); // 推理过程
|
|
179
179
|
}
|
|
180
|
+
if (chunk.type === 'finish' && chunk.usage) {
|
|
181
|
+
console.log('Token usage:', chunk.usage);
|
|
182
|
+
// { promptTokens, completionTokens, totalTokens, cachedPromptTokens? }
|
|
183
|
+
}
|
|
180
184
|
}
|
|
181
185
|
```
|
|
182
186
|
|
|
187
|
+
> 💡 流式调用自动启用 `stream_options: { include_usage: true }`,`finish` 事件携带完整的 token 消耗统计。usage 可能与 `finish_reason` 在同一个 SSE chunk,也可能在独立的 chunk(`choices: []`)中返回,两种情况均已兼容。
|
|
188
|
+
|
|
183
189
|
### 模型列表
|
|
184
190
|
|
|
185
191
|
当前支持的模型(通过 `Model` 类型枚举):
|
|
@@ -328,6 +334,9 @@ for await (const chunk of stream) {
|
|
|
328
334
|
break;
|
|
329
335
|
case 'turn_end':
|
|
330
336
|
console.log(`\n--- Turn ${chunk.turn} End ---`);
|
|
337
|
+
if (chunk.usage) {
|
|
338
|
+
console.log(`Token usage: prompt=${chunk.usage.promptTokens}, completion=${chunk.usage.completionTokens}`);
|
|
339
|
+
}
|
|
331
340
|
break;
|
|
332
341
|
}
|
|
333
342
|
}
|
|
@@ -366,6 +375,8 @@ console.log('Turns:', result!.turns);
|
|
|
366
375
|
| `tool_result` | tool 执行完成 | `callId`, `toolName`, `content`, `status`, `turn` |
|
|
367
376
|
| `turn_end` | 一轮结束(tool 全部执行完或 content 直接返回) | `turn`, `usage?` |
|
|
368
377
|
|
|
378
|
+
> 💡 `turn_end` 的 `usage` 字段携带该轮 LLM 调用的 token 消耗统计(`promptTokens`、`completionTokens`、`totalTokens`、`cachedPromptTokens?`)。流式请求自动启用 `stream_options: { include_usage: true }`,确保 API 返回 usage 数据。
|
|
379
|
+
|
|
369
380
|
#### 流式 vs 非流式的选择
|
|
370
381
|
|
|
371
382
|
| 场景 | 推荐方式 |
|
|
@@ -462,6 +473,37 @@ for await (const chunk of stream) {
|
|
|
462
473
|
|
|
463
474
|
**向后兼容**:返回普通值(非 `ToolResult`)时行为完全不变,仍按原有逻辑 `JSON.stringify` 后透给 LLM。
|
|
464
475
|
|
|
476
|
+
#### 用 ToolResult 终止 Loop(end tool)
|
|
477
|
+
|
|
478
|
+
当某个 tool 的执行结果就是最终答案、不需要再调 LLM 时,可以设置 `endLoop: true`:
|
|
479
|
+
|
|
480
|
+
```ts
|
|
481
|
+
const finalAnswerTool = {
|
|
482
|
+
name: 'get_answer',
|
|
483
|
+
description: 'Get final answer',
|
|
484
|
+
parameters: { /* ... */ },
|
|
485
|
+
execute: async (args) => {
|
|
486
|
+
const answer = await computeAnswer(args);
|
|
487
|
+
return {
|
|
488
|
+
forLLM: '已找到最终答案',
|
|
489
|
+
forFrontend: answer,
|
|
490
|
+
endLoop: true,
|
|
491
|
+
finalContent: answer.summary,
|
|
492
|
+
};
|
|
493
|
+
},
|
|
494
|
+
};
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
| 字段 | 说明 |
|
|
498
|
+
|---|---|
|
|
499
|
+
| `endLoop` | 为 `true` 时,该 tool 执行完成后直接结束 Loop,不再进入下一轮 LLM 调用 |
|
|
500
|
+
| `finalContent` | 可选。设置后作为 `LoopResult.finalContent` 返回;不设置则使用 `forLLM` |
|
|
501
|
+
|
|
502
|
+
**行为规则**:
|
|
503
|
+
- `endLoop` 仅在 execute 成功返回 `ToolResult` 时生效
|
|
504
|
+
- 若一轮并行调用中有多个 tool 都设置 `endLoop`,取第一个完成的结果作为最终输出
|
|
505
|
+
- `pendingApproval` 的优先级高于 `endLoop`:未审批时先返回 `pendingApproval`
|
|
506
|
+
|
|
465
507
|
### Tool 审批拦截
|
|
466
508
|
|
|
467
509
|
对于敏感操作(转账、删除数据等),可以给 tool 配置 `approval`,Loop 会在执行前暂停并返回 `pendingApproval`:
|
|
@@ -32,7 +32,7 @@ const DEFAULT_BAILIAN_BASE_URL = 'https://dashscope.aliyuncs.com/compatible-mode
|
|
|
32
32
|
const DEFAULT_BAILIAN_MODEL = 'qwen-max';
|
|
33
33
|
function runLoop(config) {
|
|
34
34
|
return __awaiter(this, void 0, void 0, function* () {
|
|
35
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
35
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
36
36
|
const llmCaller = (_a = config.llmCaller) !== null && _a !== void 0 ? _a : (0, provider_1.createLLMCaller)({
|
|
37
37
|
apiKey: (_b = process.env.BAILIAN_API_KEY) !== null && _b !== void 0 ? _b : '',
|
|
38
38
|
baseUrl: (_c = process.env.BAILIAN_BASE_URL) !== null && _c !== void 0 ? _c : DEFAULT_BAILIAN_BASE_URL,
|
|
@@ -157,6 +157,18 @@ function runLoop(config) {
|
|
|
157
157
|
totalUsage: computeTotalUsage(),
|
|
158
158
|
};
|
|
159
159
|
}
|
|
160
|
+
// 检查是否有 tool 要求结束 Loop
|
|
161
|
+
const endLoopResult = parallelResults.find((r) => r.endLoop);
|
|
162
|
+
if (endLoopResult === null || endLoopResult === void 0 ? void 0 : endLoopResult.endLoop) {
|
|
163
|
+
return {
|
|
164
|
+
messages,
|
|
165
|
+
harness: harness.getAll(),
|
|
166
|
+
finalContent: (_h = endLoopResult.finalContent) !== null && _h !== void 0 ? _h : endLoopResult.content,
|
|
167
|
+
turns: turn,
|
|
168
|
+
usageHistory,
|
|
169
|
+
totalUsage: computeTotalUsage(),
|
|
170
|
+
};
|
|
171
|
+
}
|
|
160
172
|
// 第八步:回到第一步继续下一轮
|
|
161
173
|
}
|
|
162
174
|
// Loop 被终止(Turn Policy 或硬限制)
|
|
@@ -399,10 +411,14 @@ function executeToolsInParallel(config, harness, toolCalls, visibleToolNames, ha
|
|
|
399
411
|
};
|
|
400
412
|
let content;
|
|
401
413
|
let frontendData;
|
|
414
|
+
let endLoop;
|
|
415
|
+
let finalContent;
|
|
402
416
|
if (status === 'success') {
|
|
403
417
|
if (isToolResult(result)) {
|
|
404
418
|
content = result.forLLM;
|
|
405
419
|
frontendData = result.forFrontend;
|
|
420
|
+
endLoop = result.endLoop;
|
|
421
|
+
finalContent = result.finalContent;
|
|
406
422
|
}
|
|
407
423
|
else {
|
|
408
424
|
content =
|
|
@@ -418,6 +434,8 @@ function executeToolsInParallel(config, harness, toolCalls, visibleToolNames, ha
|
|
|
418
434
|
toolCallId: callId,
|
|
419
435
|
content,
|
|
420
436
|
frontendData,
|
|
437
|
+
endLoop,
|
|
438
|
+
finalContent,
|
|
421
439
|
record: finalRecord,
|
|
422
440
|
events: [endEvent],
|
|
423
441
|
pendingApproval: typeof result === 'object' &&
|
|
@@ -118,6 +118,7 @@ function createLLMCaller(options) {
|
|
|
118
118
|
tools: tools.length > 0 ? toOpenAITools(tools) : undefined,
|
|
119
119
|
reasoning_effort: callOptions === null || callOptions === void 0 ? void 0 : callOptions.reasoningEffort,
|
|
120
120
|
stream: true,
|
|
121
|
+
stream_options: { include_usage: true },
|
|
121
122
|
});
|
|
122
123
|
const url = `${options.baseUrl}/chat/completions`;
|
|
123
124
|
let response;
|
|
@@ -148,7 +149,27 @@ function createLLMCaller(options) {
|
|
|
148
149
|
const toolCallAccumulators = [];
|
|
149
150
|
let fullContent = '';
|
|
150
151
|
let fullReasoningContent = '';
|
|
151
|
-
|
|
152
|
+
// 累积 usage:OpenAI 兼容 API 在 stream_options.include_usage 开启时,
|
|
153
|
+
// usage 可能与 finish_reason 在同一个 chunk,也可能在独立的 chunk(choices: [])中返回。
|
|
154
|
+
// 统一在此收集,流结束后合并到 finish 事件中 yield。
|
|
155
|
+
let streamUsage;
|
|
156
|
+
let hasFinishReason = false;
|
|
157
|
+
function buildToolCalls() {
|
|
158
|
+
const toolCalls = [];
|
|
159
|
+
for (const acc of toolCallAccumulators) {
|
|
160
|
+
if (acc.id && acc.type && acc.function.name) {
|
|
161
|
+
toolCalls.push({
|
|
162
|
+
id: acc.id,
|
|
163
|
+
type: acc.type,
|
|
164
|
+
function: {
|
|
165
|
+
name: acc.function.name,
|
|
166
|
+
arguments: acc.function.arguments,
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return toolCalls;
|
|
172
|
+
}
|
|
152
173
|
try {
|
|
153
174
|
while (true) {
|
|
154
175
|
const { done, value } = yield __await(reader.read());
|
|
@@ -171,7 +192,16 @@ function createLLMCaller(options) {
|
|
|
171
192
|
catch (_g) {
|
|
172
193
|
continue;
|
|
173
194
|
}
|
|
174
|
-
|
|
195
|
+
// 从任意 chunk 中收集 usage(包括 choices 为空的独立 usage chunk)
|
|
196
|
+
if (parsed.usage) {
|
|
197
|
+
streamUsage = {
|
|
198
|
+
promptTokens: parsed.usage.prompt_tokens,
|
|
199
|
+
completionTokens: parsed.usage.completion_tokens,
|
|
200
|
+
totalTokens: parsed.usage.total_tokens,
|
|
201
|
+
cachedPromptTokens: (_c = parsed.usage.prompt_tokens_details) === null || _c === void 0 ? void 0 : _c.cached_tokens,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
const choice = (_d = parsed.choices) === null || _d === void 0 ? void 0 : _d[0];
|
|
175
205
|
if (!choice)
|
|
176
206
|
continue;
|
|
177
207
|
const delta = choice.delta;
|
|
@@ -199,69 +229,29 @@ function createLLMCaller(options) {
|
|
|
199
229
|
acc.id = tcDelta.id;
|
|
200
230
|
if (tcDelta.type)
|
|
201
231
|
acc.type = tcDelta.type;
|
|
202
|
-
if ((
|
|
232
|
+
if ((_e = tcDelta.function) === null || _e === void 0 ? void 0 : _e.name) {
|
|
203
233
|
acc.function.name = tcDelta.function.name;
|
|
204
234
|
}
|
|
205
|
-
if ((
|
|
235
|
+
if ((_f = tcDelta.function) === null || _f === void 0 ? void 0 : _f.arguments) {
|
|
206
236
|
acc.function.arguments += tcDelta.function.arguments;
|
|
207
237
|
}
|
|
208
238
|
}
|
|
209
239
|
}
|
|
210
|
-
//
|
|
240
|
+
// 标记流结束(不立即 yield finish,等 usage 收集完毕)
|
|
211
241
|
if (choice.finish_reason) {
|
|
212
|
-
|
|
213
|
-
for (const acc of toolCallAccumulators) {
|
|
214
|
-
if (acc.id && acc.type && acc.function.name) {
|
|
215
|
-
toolCalls.push({
|
|
216
|
-
id: acc.id,
|
|
217
|
-
type: acc.type,
|
|
218
|
-
function: {
|
|
219
|
-
name: acc.function.name,
|
|
220
|
-
arguments: acc.function.arguments,
|
|
221
|
-
},
|
|
222
|
-
});
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
yield yield __await({
|
|
226
|
-
type: 'finish',
|
|
227
|
-
content: fullContent || null,
|
|
228
|
-
reasoningContent: fullReasoningContent || undefined,
|
|
229
|
-
tool_calls: toolCalls.length > 0 ? toolCalls : undefined,
|
|
230
|
-
usage: parsed.usage
|
|
231
|
-
? {
|
|
232
|
-
promptTokens: parsed.usage.prompt_tokens,
|
|
233
|
-
completionTokens: parsed.usage.completion_tokens,
|
|
234
|
-
totalTokens: parsed.usage.total_tokens,
|
|
235
|
-
cachedPromptTokens: (_f = parsed.usage.prompt_tokens_details) === null || _f === void 0 ? void 0 : _f.cached_tokens,
|
|
236
|
-
}
|
|
237
|
-
: undefined,
|
|
238
|
-
});
|
|
239
|
-
hasYieldedFinish = true;
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
// 兜底:如果流正常结束但没有收到 finish_reason,也 yield 一个 finish
|
|
244
|
-
if (!hasYieldedFinish) {
|
|
245
|
-
const toolCalls = [];
|
|
246
|
-
for (const acc of toolCallAccumulators) {
|
|
247
|
-
if (acc.id && acc.type && acc.function.name) {
|
|
248
|
-
toolCalls.push({
|
|
249
|
-
id: acc.id,
|
|
250
|
-
type: acc.type,
|
|
251
|
-
function: {
|
|
252
|
-
name: acc.function.name,
|
|
253
|
-
arguments: acc.function.arguments,
|
|
254
|
-
},
|
|
255
|
-
});
|
|
242
|
+
hasFinishReason = true;
|
|
256
243
|
}
|
|
257
244
|
}
|
|
258
|
-
yield yield __await({
|
|
259
|
-
type: 'finish',
|
|
260
|
-
content: fullContent || null,
|
|
261
|
-
reasoningContent: fullReasoningContent || undefined,
|
|
262
|
-
tool_calls: toolCalls.length > 0 ? toolCalls : undefined,
|
|
263
|
-
});
|
|
264
245
|
}
|
|
246
|
+
// 流结束,统一 yield finish 事件(确保包含累积的 usage)
|
|
247
|
+
const toolCalls = buildToolCalls();
|
|
248
|
+
yield yield __await({
|
|
249
|
+
type: 'finish',
|
|
250
|
+
content: fullContent || null,
|
|
251
|
+
reasoningContent: fullReasoningContent || undefined,
|
|
252
|
+
tool_calls: toolCalls.length > 0 ? toolCalls : undefined,
|
|
253
|
+
usage: streamUsage,
|
|
254
|
+
});
|
|
265
255
|
}
|
|
266
256
|
finally {
|
|
267
257
|
reader.releaseLock();
|
|
@@ -54,7 +54,7 @@ function buildInitialMessages(config) {
|
|
|
54
54
|
function runLoopStream(config) {
|
|
55
55
|
return __asyncGenerator(this, arguments, function* runLoopStream_1() {
|
|
56
56
|
var _a, e_1, _b, _c;
|
|
57
|
-
var _d, _e, _f, _g, _h, _j, _k;
|
|
57
|
+
var _d, _e, _f, _g, _h, _j, _k, _l;
|
|
58
58
|
const llmCaller = (_d = config.llmCaller) !== null && _d !== void 0 ? _d : (0, provider_1.createLLMCaller)({
|
|
59
59
|
apiKey: (_e = process.env.BAILIAN_API_KEY) !== null && _e !== void 0 ? _e : '',
|
|
60
60
|
baseUrl: (_f = process.env.BAILIAN_BASE_URL) !== null && _f !== void 0 ? _f : DEFAULT_BAILIAN_BASE_URL,
|
|
@@ -135,9 +135,9 @@ function runLoopStream(config) {
|
|
|
135
135
|
reasoningEffort: config.reasoningEffort,
|
|
136
136
|
});
|
|
137
137
|
try {
|
|
138
|
-
for (var
|
|
138
|
+
for (var _m = true, stream_1 = (e_1 = void 0, __asyncValues(stream)), stream_1_1; stream_1_1 = yield __await(stream_1.next()), _a = stream_1_1.done, !_a; _m = true) {
|
|
139
139
|
_c = stream_1_1.value;
|
|
140
|
-
|
|
140
|
+
_m = false;
|
|
141
141
|
const chunk = _c;
|
|
142
142
|
if (chunk.type === 'content') {
|
|
143
143
|
fullContent += chunk.delta;
|
|
@@ -155,7 +155,7 @@ function runLoopStream(config) {
|
|
|
155
155
|
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
156
156
|
finally {
|
|
157
157
|
try {
|
|
158
|
-
if (!
|
|
158
|
+
if (!_m && !_a && (_b = stream_1.return)) yield __await(_b.call(stream_1));
|
|
159
159
|
}
|
|
160
160
|
finally { if (e_1) throw e_1.error; }
|
|
161
161
|
}
|
|
@@ -234,6 +234,19 @@ function runLoopStream(config) {
|
|
|
234
234
|
totalUsage: computeTotalUsage(),
|
|
235
235
|
});
|
|
236
236
|
}
|
|
237
|
+
// 检查是否有 tool 要求结束 Loop
|
|
238
|
+
const endLoopResult = parallelResults.find((r) => r.endLoop);
|
|
239
|
+
if (endLoopResult === null || endLoopResult === void 0 ? void 0 : endLoopResult.endLoop) {
|
|
240
|
+
yield yield __await({ type: 'turn_end', turn, usage: finishChunk.usage });
|
|
241
|
+
return yield __await({
|
|
242
|
+
messages,
|
|
243
|
+
harness: harness.getAll(),
|
|
244
|
+
finalContent: (_l = endLoopResult.finalContent) !== null && _l !== void 0 ? _l : endLoopResult.content,
|
|
245
|
+
turns: turn,
|
|
246
|
+
usageHistory,
|
|
247
|
+
totalUsage: computeTotalUsage(),
|
|
248
|
+
});
|
|
249
|
+
}
|
|
237
250
|
yield yield __await({ type: 'turn_end', turn, usage: finishChunk.usage });
|
|
238
251
|
// 第八步:回到第一步继续下一轮
|
|
239
252
|
}
|
|
@@ -113,10 +113,15 @@ export interface ApprovalConfig {
|
|
|
113
113
|
* Tool 执行结果的分流包装。
|
|
114
114
|
* 当 execute 返回此类型时,forLLM 进入 messages[] 给 LLM 上下文,
|
|
115
115
|
* forFrontend 通过 SSE tool_result 事件透给前端渲染。
|
|
116
|
+
* 设置 endLoop 为 true 可让 Loop 在执行完该 tool 后直接终止,不再调用 LLM。
|
|
116
117
|
*/
|
|
117
118
|
export interface ToolResult {
|
|
118
119
|
readonly forLLM: string;
|
|
119
120
|
readonly forFrontend?: unknown;
|
|
121
|
+
/** 为 true 时,tool 执行完成后直接结束 Loop,不再进入下一轮 LLM 调用 */
|
|
122
|
+
readonly endLoop?: boolean;
|
|
123
|
+
/** endLoop 为 true 时,作为 LoopResult.finalContent 返回;不设置则使用 forLLM */
|
|
124
|
+
readonly finalContent?: string;
|
|
120
125
|
}
|
|
121
126
|
/**
|
|
122
127
|
* 注册到 Loop 中的 Tool。
|
|
@@ -103,7 +103,7 @@ class BailianProvider {
|
|
|
103
103
|
return __asyncGenerator(this, arguments, function* stream_1() {
|
|
104
104
|
var _a, _b, _c;
|
|
105
105
|
const url = `${this.config.baseUrl}/chat/completions`;
|
|
106
|
-
const body = this.adaptRequest(Object.assign(Object.assign({}, this.buildRequestBody(request)), { stream: true }));
|
|
106
|
+
const body = this.adaptRequest(Object.assign(Object.assign({}, this.buildRequestBody(request)), { stream: true, stream_options: { include_usage: true } }));
|
|
107
107
|
let response;
|
|
108
108
|
try {
|
|
109
109
|
response = yield __await(fetch(url, {
|
|
@@ -128,6 +128,10 @@ class BailianProvider {
|
|
|
128
128
|
const reader = response.body.getReader();
|
|
129
129
|
const decoder = new TextDecoder();
|
|
130
130
|
let buffer = '';
|
|
131
|
+
// 累积 usage:OpenAI 兼容 API 在 stream_options.include_usage 开启时,
|
|
132
|
+
// usage 可能与 finish_reason 在同一个 chunk,也可能在独立的 chunk(choices: [])中返回。
|
|
133
|
+
// 统一在此收集,流结束后一次性 yield finish 事件。
|
|
134
|
+
let streamUsage;
|
|
131
135
|
try {
|
|
132
136
|
while (true) {
|
|
133
137
|
const { done, value } = yield __await(reader.read());
|
|
@@ -150,7 +154,16 @@ class BailianProvider {
|
|
|
150
154
|
catch (_d) {
|
|
151
155
|
continue;
|
|
152
156
|
}
|
|
153
|
-
|
|
157
|
+
// 从任意 chunk 中收集 usage(包括 choices 为空的独立 usage chunk)
|
|
158
|
+
if (parsed.usage) {
|
|
159
|
+
streamUsage = {
|
|
160
|
+
promptTokens: parsed.usage.prompt_tokens,
|
|
161
|
+
completionTokens: parsed.usage.completion_tokens,
|
|
162
|
+
totalTokens: parsed.usage.total_tokens,
|
|
163
|
+
cachedPromptTokens: (_b = parsed.usage.prompt_tokens_details) === null || _b === void 0 ? void 0 : _b.cached_tokens,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
const choice = (_c = parsed.choices) === null || _c === void 0 ? void 0 : _c[0];
|
|
154
167
|
if (!choice)
|
|
155
168
|
continue;
|
|
156
169
|
const delta = choice.delta;
|
|
@@ -160,20 +173,9 @@ class BailianProvider {
|
|
|
160
173
|
if (delta.content) {
|
|
161
174
|
yield yield __await({ type: 'content', delta: delta.content });
|
|
162
175
|
}
|
|
163
|
-
if (choice.finish_reason && parsed.usage) {
|
|
164
|
-
yield yield __await({
|
|
165
|
-
type: 'finish',
|
|
166
|
-
usage: {
|
|
167
|
-
promptTokens: parsed.usage.prompt_tokens,
|
|
168
|
-
completionTokens: parsed.usage.completion_tokens,
|
|
169
|
-
totalTokens: parsed.usage.total_tokens,
|
|
170
|
-
cachedPromptTokens: (_c = parsed.usage.prompt_tokens_details) === null || _c === void 0 ? void 0 : _c.cached_tokens,
|
|
171
|
-
},
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
176
|
}
|
|
175
177
|
}
|
|
176
|
-
yield yield __await({ type: 'finish' });
|
|
178
|
+
yield yield __await({ type: 'finish', usage: streamUsage });
|
|
177
179
|
}
|
|
178
180
|
finally {
|
|
179
181
|
reader.releaseLock();
|
|
@@ -194,6 +196,8 @@ class BailianProvider {
|
|
|
194
196
|
response_format: request.responseFormat
|
|
195
197
|
? { type: request.responseFormat === 'json' ? 'json_object' : 'text' }
|
|
196
198
|
: undefined,
|
|
199
|
+
stream: request.stream,
|
|
200
|
+
stream_options: request.streamOptions,
|
|
197
201
|
};
|
|
198
202
|
}
|
|
199
203
|
}
|
|
@@ -35,6 +35,10 @@ export interface OpenAIChatRequest {
|
|
|
35
35
|
type: 'text' | 'json_object';
|
|
36
36
|
};
|
|
37
37
|
stream?: boolean;
|
|
38
|
+
/** 流式请求时是否在最后一个 chunk 中返回 usage 信息。OpenAI 兼容 API 默认不返回,需显式开启 */
|
|
39
|
+
stream_options?: {
|
|
40
|
+
include_usage: boolean;
|
|
41
|
+
};
|
|
38
42
|
reasoning_effort?: 'low' | 'medium' | 'high';
|
|
39
43
|
/** 百炼等平台的扩展参数 */
|
|
40
44
|
extra_body?: Record<string, unknown>;
|
|
@@ -14,6 +14,9 @@ export interface LLMRequest {
|
|
|
14
14
|
readonly maxTokens?: number;
|
|
15
15
|
readonly topP?: number;
|
|
16
16
|
readonly stream?: boolean;
|
|
17
|
+
readonly streamOptions?: {
|
|
18
|
+
include_usage: boolean;
|
|
19
|
+
};
|
|
17
20
|
readonly model?: string;
|
|
18
21
|
readonly responseFormat?: 'text' | 'json';
|
|
19
22
|
}
|