@keo-ai/axiom 0.1.4 → 0.1.6
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 +29 -28
- package/dist/function_call_loop/index.d.ts +3 -3
- package/dist/function_call_loop/index.js +3 -3
- package/dist/function_call_loop/loop.d.ts +0 -21
- package/dist/function_call_loop/loop.js +33 -57
- package/dist/function_call_loop/provider.d.ts +2 -2
- package/dist/function_call_loop/provider.js +38 -53
- package/dist/function_call_loop/types.d.ts +9 -6
- package/dist/index.d.ts +4 -3
- package/dist/index.js +3 -3
- package/dist/llm_provider/bailian.d.ts +28 -0
- package/dist/llm_provider/bailian.js +175 -0
- package/dist/llm_provider/index.d.ts +73 -0
- package/dist/llm_provider/index.js +62 -0
- package/dist/llm_provider/llm.d.ts +32 -0
- package/dist/llm_provider/llm.js +2 -0
- package/dist/llm_provider/models.d.ts +14 -0
- package/dist/llm_provider/models.js +18 -0
- package/dist/llm_provider/types.d.ts +51 -0
- package/dist/llm_provider/types.js +2 -0
- package/dist/predict/config.d.ts +2 -2
- package/dist/predict/index.d.ts +6 -5
- package/dist/predict/index.js +2 -2
- package/dist/predict/llm.d.ts +3 -33
- package/dist/predict/llm.js +1 -1
- package/dist/predict/models.d.ts +2 -14
- package/dist/predict/models.js +2 -14
- package/dist/predict/predict.d.ts +1 -1
- package/dist/predict/predict.js +1 -1
- package/dist/predict/types.d.ts +1 -51
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -179,6 +179,7 @@ for await (const chunk of LLM.streamPredict({ model: 'qwen3.7-max', prompt: '讲
|
|
|
179
179
|
- `deepseek-v4-pro`
|
|
180
180
|
- `deepseek-v4-flash`
|
|
181
181
|
- `kimi-k2.6`
|
|
182
|
+
- `glm-5.1`
|
|
182
183
|
- `qwen-vl-plus`
|
|
183
184
|
|
|
184
185
|
> 目前所有模型均路由到百炼 Provider。后续接入其他厂商时,通过 `MODEL_REGISTRY` 扩展映射即可。
|
|
@@ -213,9 +214,7 @@ Function Call Loop 是 Axiom 的底层 Function Call 引擎。它负责把 **LLM
|
|
|
213
214
|
|
|
214
215
|
### 快速开始
|
|
215
216
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
像 predict 模块一样,设置环境变量后一行调用:
|
|
217
|
+
设置环境变量后一行调用:
|
|
219
218
|
|
|
220
219
|
```bash
|
|
221
220
|
export BAILIAN_API_KEY="your-api-key"
|
|
@@ -224,12 +223,12 @@ export BAILIAN_API_KEY="your-api-key"
|
|
|
224
223
|
```ts
|
|
225
224
|
import { FunctionCallLoop } from '@keo-ai/axiom';
|
|
226
225
|
|
|
227
|
-
const result = await FunctionCallLoop.
|
|
226
|
+
const result = await FunctionCallLoop.runLoop({
|
|
228
227
|
messages: [
|
|
229
228
|
{ role: 'system', content: 'You are a helpful assistant.' },
|
|
230
229
|
{ role: 'user', content: 'What is the weather in Beijing?' },
|
|
231
230
|
],
|
|
232
|
-
model: '
|
|
231
|
+
model: 'qwen3.7-max',
|
|
233
232
|
temperature: 0.7,
|
|
234
233
|
maxTokens: 2048,
|
|
235
234
|
tools: [
|
|
@@ -243,7 +242,7 @@ const result = await FunctionCallLoop.runLoopWithModel({
|
|
|
243
242
|
},
|
|
244
243
|
required: ['city'],
|
|
245
244
|
},
|
|
246
|
-
execute: async (args) => {
|
|
245
|
+
execute: async (args, context) => {
|
|
247
246
|
return { temperature: 25, condition: 'Sunny' };
|
|
248
247
|
},
|
|
249
248
|
},
|
|
@@ -266,7 +265,7 @@ console.log('Harness:', result.harness);
|
|
|
266
265
|
Loop 每处理一个 tool call,按顺序抛出两个事件:
|
|
267
266
|
|
|
268
267
|
```ts
|
|
269
|
-
const result = await FunctionCallLoop.
|
|
268
|
+
const result = await FunctionCallLoop.runLoop({
|
|
270
269
|
// ...
|
|
271
270
|
onEvent: (event) => {
|
|
272
271
|
if (event.type === 'execution:start') {
|
|
@@ -315,7 +314,7 @@ console.log(result.harness);
|
|
|
315
314
|
对于敏感操作(转账、删除数据等),可以给 tool 配置 `approval`,Loop 会在执行前暂停并返回 `pendingApproval`:
|
|
316
315
|
|
|
317
316
|
```ts
|
|
318
|
-
const result = await FunctionCallLoop.
|
|
317
|
+
const result = await FunctionCallLoop.runLoop({
|
|
319
318
|
messages: [
|
|
320
319
|
{ role: 'system', content: 'You are a helpful assistant.' },
|
|
321
320
|
{ role: 'user', content: 'Transfer 1000 to Alice' },
|
|
@@ -333,7 +332,7 @@ const result = await FunctionCallLoop.runLoopWithModel({
|
|
|
333
332
|
return { ticketId: ticket.id };
|
|
334
333
|
},
|
|
335
334
|
},
|
|
336
|
-
execute: async (args) => {
|
|
335
|
+
execute: async (args, context) => {
|
|
337
336
|
// 审批通过后才会执行到这里
|
|
338
337
|
return await doTransfer(args);
|
|
339
338
|
},
|
|
@@ -357,7 +356,7 @@ if (result.pendingApproval) {
|
|
|
357
356
|
```ts
|
|
358
357
|
const record = await db.findByTicketId(ticketId);
|
|
359
358
|
|
|
360
|
-
const result = await FunctionCallLoop.
|
|
359
|
+
const result = await FunctionCallLoop.runLoop({
|
|
361
360
|
messages: record.messages,
|
|
362
361
|
metadata: { userId: 'u-123', orgId: 'o-456' },
|
|
363
362
|
tools: [
|
|
@@ -375,7 +374,7 @@ const result = await FunctionCallLoop.runLoopWithModel({
|
|
|
375
374
|
return { ticketId: ticket.id };
|
|
376
375
|
},
|
|
377
376
|
},
|
|
378
|
-
execute: async (args) => await doTransfer(args),
|
|
377
|
+
execute: async (args, context) => await doTransfer(args),
|
|
379
378
|
},
|
|
380
379
|
],
|
|
381
380
|
});
|
|
@@ -406,7 +405,7 @@ const tool = {
|
|
|
406
405
|
visible: isAdmin,
|
|
407
406
|
};
|
|
408
407
|
},
|
|
409
|
-
execute: async (args) => { /* ... */ },
|
|
408
|
+
execute: async (args, context) => { /* ... */ },
|
|
410
409
|
};
|
|
411
410
|
```
|
|
412
411
|
|
|
@@ -419,17 +418,17 @@ const tool = {
|
|
|
419
418
|
|
|
420
419
|
### Turn Policy(全局轮次策略)
|
|
421
420
|
|
|
422
|
-
每轮开始时,Loop 先执行 Turn Policy
|
|
421
|
+
每轮开始时,Loop 先执行 Turn Policy,可注入一条 system 消息干预本轮:
|
|
423
422
|
|
|
424
423
|
```ts
|
|
425
|
-
const result = await FunctionCallLoop.
|
|
424
|
+
const result = await FunctionCallLoop.runLoop({
|
|
426
425
|
// ...
|
|
427
426
|
turnPolicy: async (harness, turn, metadata) => {
|
|
428
|
-
//
|
|
427
|
+
// 连续失败时注入提醒
|
|
429
428
|
const failCount = harness.filter((r) => r.status === 'error').length;
|
|
430
429
|
if (failCount >= 3) {
|
|
431
430
|
return {
|
|
432
|
-
|
|
431
|
+
injectMessage: 'Previous calls failed, please try a different approach.',
|
|
433
432
|
};
|
|
434
433
|
}
|
|
435
434
|
return {};
|
|
@@ -443,21 +442,20 @@ const result = await FunctionCallLoop.runLoopWithModel({
|
|
|
443
442
|
|---|---|
|
|
444
443
|
| `turn < maxTurns - 1` | 继续执行 |
|
|
445
444
|
| `turn === maxTurns - 1` | 注入 `warningMessage`,继续执行 |
|
|
446
|
-
| `turn >= maxTurns` |
|
|
445
|
+
| `turn >= maxTurns` | 交由硬兜底处理 |
|
|
447
446
|
|
|
448
447
|
| 边界情况 | 行为 |
|
|
449
448
|
|---|---|
|
|
450
|
-
| `turnPolicy` 抛异常 |
|
|
451
|
-
| 返回 `terminate` | 向 Messages 塞入 `fallbackMessage`,结束 Loop,不再调 LLM |
|
|
449
|
+
| `turnPolicy` 抛异常 | 视为无注入,继续执行 |
|
|
452
450
|
|
|
453
|
-
###
|
|
451
|
+
### 上下文 compact
|
|
454
452
|
|
|
455
453
|
控制给 LLM 的历史消息长度,只动 Messages,不动 Harness:
|
|
456
454
|
|
|
457
455
|
```ts
|
|
458
|
-
const result = await FunctionCallLoop.
|
|
456
|
+
const result = await FunctionCallLoop.runLoop({
|
|
459
457
|
// ...
|
|
460
|
-
|
|
458
|
+
compact: {
|
|
461
459
|
keepRounds: 3, // 最近 3 轮保持完整
|
|
462
460
|
compress: async (oldRounds) => {
|
|
463
461
|
// oldRounds: 需要压缩的轮次数组,每轮是一个 Message 数组
|
|
@@ -475,7 +473,7 @@ const result = await FunctionCallLoop.runLoopWithModel({
|
|
|
475
473
|
|
|
476
474
|
| 规则 | 说明 |
|
|
477
475
|
|---|---|
|
|
478
|
-
|
|
|
476
|
+
| compact 只影响 Messages | Harness 始终完整保留 |
|
|
479
477
|
| 系统消息和初始输入不参与压缩 | 始终保留 |
|
|
480
478
|
| `compress` 抛异常 | 视为不压缩,使用原始 Messages 继续执行 |
|
|
481
479
|
|
|
@@ -486,7 +484,7 @@ const result = await FunctionCallLoop.runLoopWithModel({
|
|
|
486
484
|
```ts
|
|
487
485
|
const controller = new AbortController();
|
|
488
486
|
|
|
489
|
-
const promise = FunctionCallLoop.
|
|
487
|
+
const promise = FunctionCallLoop.runLoop({
|
|
490
488
|
// ...
|
|
491
489
|
signal: controller.signal,
|
|
492
490
|
});
|
|
@@ -499,7 +497,7 @@ const result = await promise;
|
|
|
499
497
|
|
|
500
498
|
### 配置项
|
|
501
499
|
|
|
502
|
-
#### `
|
|
500
|
+
#### `runLoop` 配置
|
|
503
501
|
|
|
504
502
|
**对话入口**
|
|
505
503
|
|
|
@@ -512,9 +510,10 @@ const result = await promise;
|
|
|
512
510
|
| 配置项 | 类型 | 必填 | 说明 |
|
|
513
511
|
|---|---|---|---|
|
|
514
512
|
| `tools` | `Tool[]` | ✅ | 注册的 tool 列表 |
|
|
513
|
+
| `llmCaller` | `LLMCaller` | — | 自定义 LLM 调用器。未设置时从环境变量自动创建 |
|
|
515
514
|
| `maxTurns` | `number` | — | 最大轮次,默认无限制 |
|
|
516
515
|
| `turnPolicy` | `TurnPolicy` | — | 自定义轮次策略 |
|
|
517
|
-
| `
|
|
516
|
+
| `compact` | `CompactConfig` | — | 上下文 compact 配置 |
|
|
518
517
|
| `metadata` | `unknown` | — | 传递给 Tool 和 Turn Policy 的元数据 |
|
|
519
518
|
| `warningMessage` | `string` | — | 默认策略在 `maxTurns - 1` 时注入的告警文本 |
|
|
520
519
|
| `terminateMessage` | `string` | — | 默认策略在 `maxTurns` 时注入的终止文本 |
|
|
@@ -525,11 +524,12 @@ const result = await promise;
|
|
|
525
524
|
|
|
526
525
|
| 配置项 | 类型 | 必填 | 说明 |
|
|
527
526
|
|---|---|---|---|
|
|
528
|
-
| `model` | `
|
|
527
|
+
| `model` | `Model` | — | 模型枚举值。默认从 `BAILIAN_DEFAULT_MODEL` 环境变量读取,否则 `qwen-max` |
|
|
529
528
|
| `maxTokens` | `number` | — | 单次 LLM 调用的最大输出 token 数 |
|
|
530
529
|
| `temperature` | `number` | — | 采样温度,范围 0~2 |
|
|
531
530
|
| `topP` | `number` | — | 核采样概率阈值,范围 0~1 |
|
|
532
531
|
| `reasoningEffort` | `'low' \| 'medium' \| 'high'` | — | 推理深度。仅部分模型支持(如 o1、o3) |
|
|
532
|
+
| `responseFormat` | `'text' \| 'json'` | — | 响应格式。`json` 时模型输出会被内部 `JSON.parse`,解析结果存到 `parsedContent` |
|
|
533
533
|
|
|
534
534
|
### 返回结果
|
|
535
535
|
|
|
@@ -537,8 +537,9 @@ const result = await promise;
|
|
|
537
537
|
interface LoopResult {
|
|
538
538
|
messages: Message[]; // 完整的对话历史
|
|
539
539
|
harness: HarnessRecord[]; // 执行历史
|
|
540
|
-
finalContent: string | null; //
|
|
540
|
+
finalContent: string | null; // 最终回复内容(原始字符串)
|
|
541
541
|
turns: number; // 实际执行轮数
|
|
542
|
+
parsedContent?: unknown; // 仅当 responseFormat === 'json' 时存在,JSON.parse 后的结果
|
|
542
543
|
}
|
|
543
544
|
```
|
|
544
545
|
|
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
* - 每次 tool 执行向上层回传执行前、执行后两个事件
|
|
6
6
|
* - 维护跨轮次的执行历史(Harness),tool 之间能互相看见
|
|
7
7
|
* - 每次调 LLM 之前动态决定暴露哪些 tool
|
|
8
|
-
* -
|
|
8
|
+
* - 支持自定义上下文 compact
|
|
9
9
|
* - 每轮全局策略判断,决定继续执行还是终止降级
|
|
10
10
|
* - 单轮支持多个 tool 并行执行(Plan and Execute)
|
|
11
11
|
* - 系统 Prompt 完全由外部注入
|
|
12
12
|
*/
|
|
13
|
-
export {
|
|
14
|
-
export type { Message, ToolCall, ToolDefinition, HarnessRecord, HarnessRecordStatus, ToolExecutionStartEvent, ToolExecutionEndEvent, LoopEvent, ToolDiscoverResult, ToolExecuteContext, Tool, ApprovalConfig, TurnPolicyResult, TurnPolicy,
|
|
13
|
+
export { runLoop } from './loop';
|
|
14
|
+
export type { Message, ToolCall, ToolDefinition, HarnessRecord, HarnessRecordStatus, ToolExecutionStartEvent, ToolExecutionEndEvent, LoopEvent, ToolDiscoverResult, ToolExecuteContext, Tool, ApprovalConfig, TurnPolicyResult, TurnPolicy, CompactConfig, LLMCaller, LLMCallOptions, LoopConfig, LoopResult, PendingApprovalInfo, } from './types';
|
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
* - 每次 tool 执行向上层回传执行前、执行后两个事件
|
|
7
7
|
* - 维护跨轮次的执行历史(Harness),tool 之间能互相看见
|
|
8
8
|
* - 每次调 LLM 之前动态决定暴露哪些 tool
|
|
9
|
-
* -
|
|
9
|
+
* - 支持自定义上下文 compact
|
|
10
10
|
* - 每轮全局策略判断,决定继续执行还是终止降级
|
|
11
11
|
* - 单轮支持多个 tool 并行执行(Plan and Execute)
|
|
12
12
|
* - 系统 Prompt 完全由外部注入
|
|
13
13
|
*/
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.
|
|
15
|
+
exports.runLoop = void 0;
|
|
16
16
|
var loop_1 = require("./loop");
|
|
17
|
-
Object.defineProperty(exports, "
|
|
17
|
+
Object.defineProperty(exports, "runLoop", { enumerable: true, get: function () { return loop_1.runLoop; } });
|
|
@@ -1,23 +1,2 @@
|
|
|
1
1
|
import type { LoopConfig, LoopResult } from './types';
|
|
2
2
|
export declare function runLoop(config: LoopConfig): Promise<LoopResult>;
|
|
3
|
-
/**
|
|
4
|
-
* 便捷入口:直接指定模型运行 Function Call Loop。
|
|
5
|
-
* 内部自动创建 LLM Caller,无需手动实现 llmCaller.call。
|
|
6
|
-
*
|
|
7
|
-
* 环境变量配置:
|
|
8
|
-
* - `BAILIAN_API_KEY` — 必填
|
|
9
|
-
* - `BAILIAN_BASE_URL` — 可选,默认百炼兼容地址
|
|
10
|
-
* - `BAILIAN_DEFAULT_MODEL` — 可选,默认 `qwen-max`
|
|
11
|
-
*
|
|
12
|
-
* @example
|
|
13
|
-
* ```ts
|
|
14
|
-
* const result = await runLoopWithModel({
|
|
15
|
-
* messages: [
|
|
16
|
-
* { role: 'system', content: 'You are helpful' },
|
|
17
|
-
* { role: 'user', content: 'What is the weather?' },
|
|
18
|
-
* ],
|
|
19
|
-
* tools: [...],
|
|
20
|
-
* });
|
|
21
|
-
* ```
|
|
22
|
-
*/
|
|
23
|
-
export declare function runLoopWithModel(config: Omit<LoopConfig, 'llmCaller'>): Promise<LoopResult>;
|
|
@@ -10,7 +10,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.runLoop = runLoop;
|
|
13
|
-
exports.runLoopWithModel = runLoopWithModel;
|
|
14
13
|
const harness_1 = require("./harness");
|
|
15
14
|
const provider_1 = require("./provider");
|
|
16
15
|
/**
|
|
@@ -24,9 +23,19 @@ const provider_1 = require("./provider");
|
|
|
24
23
|
function buildInitialMessages(config) {
|
|
25
24
|
return [...config.messages];
|
|
26
25
|
}
|
|
26
|
+
const DEFAULT_BAILIAN_BASE_URL = 'https://dashscope.aliyuncs.com/compatible-mode/v1';
|
|
27
|
+
const DEFAULT_BAILIAN_MODEL = 'qwen-max';
|
|
27
28
|
function runLoop(config) {
|
|
28
29
|
return __awaiter(this, void 0, void 0, function* () {
|
|
29
|
-
var _a, _b, _c;
|
|
30
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
31
|
+
const llmCaller = (_a = config.llmCaller) !== null && _a !== void 0 ? _a : (0, provider_1.createLLMCaller)({
|
|
32
|
+
apiKey: (_b = process.env.BAILIAN_API_KEY) !== null && _b !== void 0 ? _b : '',
|
|
33
|
+
baseUrl: (_c = process.env.BAILIAN_BASE_URL) !== null && _c !== void 0 ? _c : DEFAULT_BAILIAN_BASE_URL,
|
|
34
|
+
defaultModel: (_d = process.env.BAILIAN_DEFAULT_MODEL) !== null && _d !== void 0 ? _d : DEFAULT_BAILIAN_MODEL,
|
|
35
|
+
});
|
|
36
|
+
if (!config.llmCaller && !process.env.BAILIAN_API_KEY) {
|
|
37
|
+
throw new Error('BAILIAN_API_KEY environment variable is not set.');
|
|
38
|
+
}
|
|
30
39
|
const harness = new harness_1.Harness();
|
|
31
40
|
const messages = buildInitialMessages(config);
|
|
32
41
|
let turn = 0;
|
|
@@ -40,10 +49,6 @@ function runLoop(config) {
|
|
|
40
49
|
turn++;
|
|
41
50
|
// 第二步:全局轮次策略
|
|
42
51
|
const decision = yield runTurnPolicy(config, harness, turn);
|
|
43
|
-
if (decision.message) {
|
|
44
|
-
messages.push({ role: 'assistant', content: decision.message });
|
|
45
|
-
break;
|
|
46
|
-
}
|
|
47
52
|
if (decision.injectMessage) {
|
|
48
53
|
messages.push({ role: 'system', content: decision.injectMessage });
|
|
49
54
|
}
|
|
@@ -51,7 +56,7 @@ function runLoop(config) {
|
|
|
51
56
|
if (config.maxTurns !== undefined && turn >= config.maxTurns) {
|
|
52
57
|
messages.push({
|
|
53
58
|
role: 'assistant',
|
|
54
|
-
content: (
|
|
59
|
+
content: (_e = config.terminateMessage) !== null && _e !== void 0 ? _e : 'Max turns reached.',
|
|
55
60
|
});
|
|
56
61
|
break;
|
|
57
62
|
}
|
|
@@ -70,30 +75,40 @@ function runLoop(config) {
|
|
|
70
75
|
// 第四步:上下文压缩
|
|
71
76
|
const messagesForLLM = yield compressMessages(config, messages, turn);
|
|
72
77
|
// 第五步:调用 LLM
|
|
73
|
-
const llmResponse = yield
|
|
78
|
+
const llmResponse = yield llmCaller.call(messagesForLLM, toolDefinitions, {
|
|
74
79
|
model: config.model,
|
|
75
80
|
temperature: config.temperature,
|
|
76
81
|
maxTokens: config.maxTokens,
|
|
77
82
|
topP: config.topP,
|
|
78
83
|
reasoningEffort: config.reasoningEffort,
|
|
84
|
+
responseFormat: config.responseFormat,
|
|
79
85
|
});
|
|
80
86
|
// 第六步:检查 LLM 回复
|
|
81
87
|
if (!llmResponse.tool_calls || llmResponse.tool_calls.length === 0) {
|
|
82
88
|
messages.push({
|
|
83
89
|
role: 'assistant',
|
|
84
|
-
content: (
|
|
90
|
+
content: (_f = llmResponse.content) !== null && _f !== void 0 ? _f : '',
|
|
85
91
|
});
|
|
86
|
-
|
|
92
|
+
const result = {
|
|
87
93
|
messages,
|
|
88
94
|
harness: harness.getAll(),
|
|
89
95
|
finalContent: llmResponse.content,
|
|
90
96
|
turns: turn,
|
|
91
97
|
};
|
|
98
|
+
if (config.responseFormat === 'json' && llmResponse.content) {
|
|
99
|
+
try {
|
|
100
|
+
Object.assign(result, { parsedContent: JSON.parse(llmResponse.content) });
|
|
101
|
+
}
|
|
102
|
+
catch (_h) {
|
|
103
|
+
// JSON 解析失败时保持 parsedContent 未定义,finalContent 仍为原始字符串
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return result;
|
|
92
107
|
}
|
|
93
108
|
// 有 tool_calls,将 assistant message 加入 Messages
|
|
94
109
|
messages.push({
|
|
95
110
|
role: 'assistant',
|
|
96
|
-
content: (
|
|
111
|
+
content: (_g = llmResponse.content) !== null && _g !== void 0 ? _g : '',
|
|
97
112
|
tool_calls: llmResponse.tool_calls,
|
|
98
113
|
});
|
|
99
114
|
// 第七步:并行执行 tool
|
|
@@ -140,12 +155,12 @@ function runLoop(config) {
|
|
|
140
155
|
*/
|
|
141
156
|
function runTurnPolicy(config, harness, turn) {
|
|
142
157
|
return __awaiter(this, void 0, void 0, function* () {
|
|
143
|
-
var _a
|
|
158
|
+
var _a;
|
|
144
159
|
if (config.turnPolicy) {
|
|
145
160
|
try {
|
|
146
161
|
return yield config.turnPolicy(harness.getAll(), turn, config.metadata);
|
|
147
162
|
}
|
|
148
|
-
catch (
|
|
163
|
+
catch (_b) {
|
|
149
164
|
return {};
|
|
150
165
|
}
|
|
151
166
|
}
|
|
@@ -159,9 +174,7 @@ function runTurnPolicy(config, harness, turn) {
|
|
|
159
174
|
injectMessage: config.warningMessage,
|
|
160
175
|
};
|
|
161
176
|
}
|
|
162
|
-
return {
|
|
163
|
-
message: (_b = config.terminateMessage) !== null && _b !== void 0 ? _b : 'Max turns reached.',
|
|
164
|
-
};
|
|
177
|
+
return {};
|
|
165
178
|
});
|
|
166
179
|
}
|
|
167
180
|
/**
|
|
@@ -227,7 +240,7 @@ function groupMessagesByRound(messages) {
|
|
|
227
240
|
*/
|
|
228
241
|
function compressMessages(config, messages, _turn) {
|
|
229
242
|
return __awaiter(this, void 0, void 0, function* () {
|
|
230
|
-
if (!config.
|
|
243
|
+
if (!config.compact) {
|
|
231
244
|
return messages;
|
|
232
245
|
}
|
|
233
246
|
const firstAssistantIndex = messages.findIndex((m) => m.role === 'assistant');
|
|
@@ -237,14 +250,14 @@ function compressMessages(config, messages, _turn) {
|
|
|
237
250
|
const initialMessages = messages.slice(0, firstAssistantIndex);
|
|
238
251
|
const restMessages = messages.slice(firstAssistantIndex);
|
|
239
252
|
const rounds = groupMessagesByRound(restMessages);
|
|
240
|
-
if (rounds.length <= config.
|
|
253
|
+
if (rounds.length <= config.compact.keepRounds) {
|
|
241
254
|
return messages;
|
|
242
255
|
}
|
|
243
|
-
const keepCount = config.
|
|
256
|
+
const keepCount = config.compact.keepRounds;
|
|
244
257
|
const oldRounds = rounds.slice(0, rounds.length - keepCount);
|
|
245
258
|
const recentRounds = rounds.slice(rounds.length - keepCount);
|
|
246
259
|
try {
|
|
247
|
-
const compressed = yield config.
|
|
260
|
+
const compressed = yield config.compact.compress(oldRounds);
|
|
248
261
|
return [...initialMessages, ...compressed, ...recentRounds.flat()];
|
|
249
262
|
}
|
|
250
263
|
catch (_a) {
|
|
@@ -433,40 +446,3 @@ function executeToolsInParallel(config, harness, toolCalls, visibleToolNames, ha
|
|
|
433
446
|
return results;
|
|
434
447
|
});
|
|
435
448
|
}
|
|
436
|
-
const DEFAULT_BAILIAN_BASE_URL = 'https://dashscope.aliyuncs.com/compatible-mode/v1';
|
|
437
|
-
const DEFAULT_BAILIAN_MODEL = 'qwen-max';
|
|
438
|
-
/**
|
|
439
|
-
* 便捷入口:直接指定模型运行 Function Call Loop。
|
|
440
|
-
* 内部自动创建 LLM Caller,无需手动实现 llmCaller.call。
|
|
441
|
-
*
|
|
442
|
-
* 环境变量配置:
|
|
443
|
-
* - `BAILIAN_API_KEY` — 必填
|
|
444
|
-
* - `BAILIAN_BASE_URL` — 可选,默认百炼兼容地址
|
|
445
|
-
* - `BAILIAN_DEFAULT_MODEL` — 可选,默认 `qwen-max`
|
|
446
|
-
*
|
|
447
|
-
* @example
|
|
448
|
-
* ```ts
|
|
449
|
-
* const result = await runLoopWithModel({
|
|
450
|
-
* messages: [
|
|
451
|
-
* { role: 'system', content: 'You are helpful' },
|
|
452
|
-
* { role: 'user', content: 'What is the weather?' },
|
|
453
|
-
* ],
|
|
454
|
-
* tools: [...],
|
|
455
|
-
* });
|
|
456
|
-
* ```
|
|
457
|
-
*/
|
|
458
|
-
function runLoopWithModel(config) {
|
|
459
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
460
|
-
var _a, _b;
|
|
461
|
-
const apiKey = process.env.BAILIAN_API_KEY;
|
|
462
|
-
if (!apiKey) {
|
|
463
|
-
throw new Error('BAILIAN_API_KEY environment variable is not set.');
|
|
464
|
-
}
|
|
465
|
-
const llmCaller = (0, provider_1.createLLMCaller)({
|
|
466
|
-
apiKey,
|
|
467
|
-
baseUrl: (_a = process.env.BAILIAN_BASE_URL) !== null && _a !== void 0 ? _a : DEFAULT_BAILIAN_BASE_URL,
|
|
468
|
-
defaultModel: (_b = process.env.BAILIAN_DEFAULT_MODEL) !== null && _b !== void 0 ? _b : DEFAULT_BAILIAN_MODEL,
|
|
469
|
-
});
|
|
470
|
-
return runLoop(Object.assign(Object.assign({}, config), { llmCaller }));
|
|
471
|
-
});
|
|
472
|
-
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Function Call Loop
|
|
3
|
-
*
|
|
2
|
+
* Function Call Loop 的 LLM Provider 适配器。
|
|
3
|
+
* 基于 predict 模块的 `callChatCompletions` 构建,共用底层 OpenAI 协议 HTTP 层。
|
|
4
4
|
*/
|
|
5
5
|
import type { Message, ToolCall, ToolDefinition, LLMCallOptions } from './types';
|
|
6
6
|
export interface ProviderOptions {
|
|
@@ -10,6 +10,35 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.createLLMCaller = createLLMCaller;
|
|
13
|
+
const llm_provider_1 = require("../llm_provider");
|
|
14
|
+
function toOpenAIMessages(messages) {
|
|
15
|
+
return messages.map((m) => {
|
|
16
|
+
var _a;
|
|
17
|
+
return ({
|
|
18
|
+
role: m.role,
|
|
19
|
+
content: m.content,
|
|
20
|
+
tool_calls: (_a = m.tool_calls) === null || _a === void 0 ? void 0 : _a.map((tc) => ({
|
|
21
|
+
id: tc.id,
|
|
22
|
+
type: tc.type,
|
|
23
|
+
function: {
|
|
24
|
+
name: tc.function.name,
|
|
25
|
+
arguments: tc.function.arguments,
|
|
26
|
+
},
|
|
27
|
+
})),
|
|
28
|
+
tool_call_id: m.tool_call_id,
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
function toOpenAITools(tools) {
|
|
33
|
+
return tools.map((t) => ({
|
|
34
|
+
type: t.type,
|
|
35
|
+
function: {
|
|
36
|
+
name: t.function.name,
|
|
37
|
+
description: t.function.description,
|
|
38
|
+
parameters: t.function.parameters,
|
|
39
|
+
},
|
|
40
|
+
}));
|
|
41
|
+
}
|
|
13
42
|
/**
|
|
14
43
|
* 基于 OpenAI 协议的 LLM 调用器。
|
|
15
44
|
*/
|
|
@@ -17,66 +46,22 @@ function createLLMCaller(options) {
|
|
|
17
46
|
return {
|
|
18
47
|
call(messages, tools, callOptions) {
|
|
19
48
|
return __awaiter(this, void 0, void 0, function* () {
|
|
20
|
-
var _a, _b
|
|
21
|
-
const
|
|
22
|
-
const body = {
|
|
49
|
+
var _a, _b;
|
|
50
|
+
const data = yield (0, llm_provider_1.callChatCompletions)(options.baseUrl, options.apiKey, {
|
|
23
51
|
model: (_a = callOptions === null || callOptions === void 0 ? void 0 : callOptions.model) !== null && _a !== void 0 ? _a : options.defaultModel,
|
|
24
|
-
messages: messages
|
|
25
|
-
var _a;
|
|
26
|
-
return ({
|
|
27
|
-
role: m.role,
|
|
28
|
-
content: m.content,
|
|
29
|
-
tool_calls: (_a = m.tool_calls) === null || _a === void 0 ? void 0 : _a.map((tc) => ({
|
|
30
|
-
id: tc.id,
|
|
31
|
-
type: tc.type,
|
|
32
|
-
function: {
|
|
33
|
-
name: tc.function.name,
|
|
34
|
-
arguments: tc.function.arguments,
|
|
35
|
-
},
|
|
36
|
-
})),
|
|
37
|
-
tool_call_id: m.tool_call_id,
|
|
38
|
-
});
|
|
39
|
-
}),
|
|
52
|
+
messages: toOpenAIMessages(messages),
|
|
40
53
|
temperature: callOptions === null || callOptions === void 0 ? void 0 : callOptions.temperature,
|
|
41
54
|
max_tokens: callOptions === null || callOptions === void 0 ? void 0 : callOptions.maxTokens,
|
|
42
55
|
top_p: callOptions === null || callOptions === void 0 ? void 0 : callOptions.topP,
|
|
43
|
-
tools: tools.length > 0
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
function: {
|
|
47
|
-
name: t.function.name,
|
|
48
|
-
description: t.function.description,
|
|
49
|
-
parameters: t.function.parameters,
|
|
50
|
-
},
|
|
51
|
-
}))
|
|
56
|
+
tools: tools.length > 0 ? toOpenAITools(tools) : undefined,
|
|
57
|
+
response_format: (callOptions === null || callOptions === void 0 ? void 0 : callOptions.responseFormat)
|
|
58
|
+
? { type: callOptions.responseFormat === 'json' ? 'json_object' : 'text' }
|
|
52
59
|
: undefined,
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
try {
|
|
56
|
-
response = yield fetch(url, {
|
|
57
|
-
method: 'POST',
|
|
58
|
-
headers: {
|
|
59
|
-
'Content-Type': 'application/json',
|
|
60
|
-
Authorization: `Bearer ${options.apiKey}`,
|
|
61
|
-
},
|
|
62
|
-
body: JSON.stringify(body),
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
catch (cause) {
|
|
66
|
-
throw new Error(`[llm] ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
67
|
-
}
|
|
68
|
-
if (!response.ok) {
|
|
69
|
-
const text = yield response.text();
|
|
70
|
-
throw new Error(`[llm] HTTP ${response.status}: ${text}`);
|
|
71
|
-
}
|
|
72
|
-
const data = (yield response.json());
|
|
73
|
-
const choice = (_b = data.choices) === null || _b === void 0 ? void 0 : _b[0];
|
|
74
|
-
if (!choice) {
|
|
75
|
-
throw new Error('[llm] No choice in response');
|
|
76
|
-
}
|
|
60
|
+
}, 'llm');
|
|
61
|
+
const choice = data.choices[0];
|
|
77
62
|
return {
|
|
78
63
|
content: choice.message.content,
|
|
79
|
-
tool_calls: (
|
|
64
|
+
tool_calls: (_b = choice.message.tool_calls) === null || _b === void 0 ? void 0 : _b.map((tc) => ({
|
|
80
65
|
id: tc.id,
|
|
81
66
|
type: tc.type,
|
|
82
67
|
function: {
|
|
@@ -121,8 +121,6 @@ export interface Tool {
|
|
|
121
121
|
* Turn Policy 决策结果。
|
|
122
122
|
*/
|
|
123
123
|
export interface TurnPolicyResult {
|
|
124
|
-
/** 终止 Loop,此消息作为 assistant 回复写入历史 */
|
|
125
|
-
readonly message?: string;
|
|
126
124
|
/** 继续执行前作为 system 消息注入 */
|
|
127
125
|
readonly injectMessage?: string;
|
|
128
126
|
}
|
|
@@ -133,7 +131,7 @@ export type TurnPolicy = (harness: ReadonlyArray<HarnessRecord>, turn: number, m
|
|
|
133
131
|
/**
|
|
134
132
|
* 上下文压缩配置。
|
|
135
133
|
*/
|
|
136
|
-
export interface
|
|
134
|
+
export interface CompactConfig {
|
|
137
135
|
readonly keepRounds: number;
|
|
138
136
|
readonly compress: (messages: ReadonlyArray<ReadonlyArray<Message>>) => Message[] | Promise<Message[]>;
|
|
139
137
|
}
|
|
@@ -146,6 +144,7 @@ export interface LLMCallOptions {
|
|
|
146
144
|
readonly maxTokens?: number;
|
|
147
145
|
readonly topP?: number;
|
|
148
146
|
readonly reasoningEffort?: 'low' | 'medium' | 'high';
|
|
147
|
+
readonly responseFormat?: 'text' | 'json';
|
|
149
148
|
}
|
|
150
149
|
/**
|
|
151
150
|
* LLM 调用接口。由外部注入,Loop 内部不绑定具体 Provider。
|
|
@@ -163,10 +162,10 @@ export interface LoopConfig {
|
|
|
163
162
|
/** 初始消息列表,直接作为对话起点 */
|
|
164
163
|
readonly messages: ReadonlyArray<Message>;
|
|
165
164
|
readonly tools: ReadonlyArray<Tool>;
|
|
166
|
-
readonly llmCaller
|
|
165
|
+
readonly llmCaller?: LLMCaller;
|
|
167
166
|
readonly maxTurns?: number;
|
|
168
167
|
readonly turnPolicy?: TurnPolicy;
|
|
169
|
-
readonly
|
|
168
|
+
readonly compact?: CompactConfig;
|
|
170
169
|
readonly metadata?: unknown;
|
|
171
170
|
/** 告警文本:当 turnPolicy 未自定义且当前轮次等于 maxTurns - 1 时塞入 Messages */
|
|
172
171
|
readonly warningMessage?: string;
|
|
@@ -177,7 +176,7 @@ export interface LoopConfig {
|
|
|
177
176
|
/** 取消信号 */
|
|
178
177
|
readonly signal?: AbortSignal;
|
|
179
178
|
/** 模型名称。未设置时从环境变量或 Provider 默认值读取 */
|
|
180
|
-
readonly model?:
|
|
179
|
+
readonly model?: import('../llm_provider/models').Model;
|
|
181
180
|
/** 采样温度,范围 0~2 */
|
|
182
181
|
readonly temperature?: number;
|
|
183
182
|
/** 单次 LLM 调用的最大输出 token 数 */
|
|
@@ -186,6 +185,8 @@ export interface LoopConfig {
|
|
|
186
185
|
readonly topP?: number;
|
|
187
186
|
/** 推理深度。仅部分模型支持 */
|
|
188
187
|
readonly reasoningEffort?: 'low' | 'medium' | 'high';
|
|
188
|
+
/** 响应格式。默认 text */
|
|
189
|
+
readonly responseFormat?: 'text' | 'json';
|
|
189
190
|
}
|
|
190
191
|
/**
|
|
191
192
|
* 待审批信息。
|
|
@@ -205,4 +206,6 @@ export interface LoopResult {
|
|
|
205
206
|
readonly finalContent: string | null;
|
|
206
207
|
readonly turns: number;
|
|
207
208
|
readonly pendingApproval?: PendingApprovalInfo;
|
|
209
|
+
/** 当 responseFormat === 'json' 时,finalContent 经 JSON.parse 后的结果 */
|
|
210
|
+
readonly parsedContent?: unknown;
|
|
208
211
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -26,9 +26,10 @@ export type { LLMProvider, PredictorOptions } from './predict';
|
|
|
26
26
|
export { Predictor } from './predict';
|
|
27
27
|
/** 百炼 Provider 实现 */
|
|
28
28
|
export { BailianProvider } from './predict';
|
|
29
|
-
/**
|
|
30
|
-
export { MODEL_REGISTRY } from './
|
|
31
|
-
export type { Model
|
|
29
|
+
/** 模型注册表与枚举,定义模型与 Provider 的映射关系 */
|
|
30
|
+
export { MODEL_REGISTRY } from './llm_provider';
|
|
31
|
+
export type { Model } from './llm_provider';
|
|
32
|
+
export type { PredictConfig, PredictWithMessagesConfig } from './predict';
|
|
32
33
|
/** 向量检索(Embedding + pgvector + Rerank) */
|
|
33
34
|
export { EmbeddingSearch, embed } from './embedding_search';
|
|
34
35
|
export type { EmbeddingSearchConfig, SearchResult } from './embedding_search';
|
package/dist/index.js
CHANGED
|
@@ -63,9 +63,9 @@ Object.defineProperty(exports, "Predictor", { enumerable: true, get: function ()
|
|
|
63
63
|
/** 百炼 Provider 实现 */
|
|
64
64
|
var predict_3 = require("./predict");
|
|
65
65
|
Object.defineProperty(exports, "BailianProvider", { enumerable: true, get: function () { return predict_3.BailianProvider; } });
|
|
66
|
-
/**
|
|
67
|
-
var
|
|
68
|
-
Object.defineProperty(exports, "MODEL_REGISTRY", { enumerable: true, get: function () { return
|
|
66
|
+
/** 模型注册表与枚举,定义模型与 Provider 的映射关系 */
|
|
67
|
+
var llm_provider_1 = require("./llm_provider");
|
|
68
|
+
Object.defineProperty(exports, "MODEL_REGISTRY", { enumerable: true, get: function () { return llm_provider_1.MODEL_REGISTRY; } });
|
|
69
69
|
/** 向量检索(Embedding + pgvector + Rerank) */
|
|
70
70
|
var embedding_search_1 = require("./embedding_search");
|
|
71
71
|
Object.defineProperty(exports, "EmbeddingSearch", { enumerable: true, get: function () { return embedding_search_1.EmbeddingSearch; } });
|