@keo-ai/axiom 0.1.2 → 0.1.4

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.
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Function Call Loop — Function Call 引擎。
3
+ *
4
+ * 核心能力:
5
+ * - 每次 tool 执行向上层回传执行前、执行后两个事件
6
+ * - 维护跨轮次的执行历史(Harness),tool 之间能互相看见
7
+ * - 每次调 LLM 之前动态决定暴露哪些 tool
8
+ * - 支持自定义上下文压缩
9
+ * - 每轮全局策略判断,决定继续执行还是终止降级
10
+ * - 单轮支持多个 tool 并行执行(Plan and Execute)
11
+ * - 系统 Prompt 完全由外部注入
12
+ */
13
+ export { runLoopWithModel } from './loop';
14
+ export type { Message, ToolCall, ToolDefinition, HarnessRecord, HarnessRecordStatus, ToolExecutionStartEvent, ToolExecutionEndEvent, LoopEvent, ToolDiscoverResult, ToolExecuteContext, Tool, ApprovalConfig, TurnPolicyResult, TurnPolicy, CompressionConfig, LLMCaller, LLMCallOptions, LoopConfig, LoopResult, PendingApprovalInfo, } from './types';
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ /**
3
+ * Function Call Loop — Function Call 引擎。
4
+ *
5
+ * 核心能力:
6
+ * - 每次 tool 执行向上层回传执行前、执行后两个事件
7
+ * - 维护跨轮次的执行历史(Harness),tool 之间能互相看见
8
+ * - 每次调 LLM 之前动态决定暴露哪些 tool
9
+ * - 支持自定义上下文压缩
10
+ * - 每轮全局策略判断,决定继续执行还是终止降级
11
+ * - 单轮支持多个 tool 并行执行(Plan and Execute)
12
+ * - 系统 Prompt 完全由外部注入
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.runLoopWithModel = void 0;
16
+ var loop_1 = require("./loop");
17
+ Object.defineProperty(exports, "runLoopWithModel", { enumerable: true, get: function () { return loop_1.runLoopWithModel; } });
@@ -0,0 +1,23 @@
1
+ import type { LoopConfig, LoopResult } from './types';
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>;
@@ -0,0 +1,472 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.runLoop = runLoop;
13
+ exports.runLoopWithModel = runLoopWithModel;
14
+ const harness_1 = require("./harness");
15
+ const provider_1 = require("./provider");
16
+ /**
17
+ * 运行 Function Call Loop。
18
+ * 按设计文档的完整流程执行,支持生命周期事件、Harness、Tool 发现、
19
+ * 上下文压缩、Turn Policy 和并行执行。
20
+ *
21
+ * @param config - Loop 配置
22
+ * @returns Loop 执行结果
23
+ */
24
+ function buildInitialMessages(config) {
25
+ return [...config.messages];
26
+ }
27
+ function runLoop(config) {
28
+ return __awaiter(this, void 0, void 0, function* () {
29
+ var _a, _b, _c;
30
+ const harness = new harness_1.Harness();
31
+ const messages = buildInitialMessages(config);
32
+ let turn = 0;
33
+ const signal = config.signal;
34
+ while (true) {
35
+ // 检查取消信号
36
+ if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
37
+ break;
38
+ }
39
+ // 第一步:轮次加一
40
+ turn++;
41
+ // 第二步:全局轮次策略
42
+ const decision = yield runTurnPolicy(config, harness, turn);
43
+ if (decision.message) {
44
+ messages.push({ role: 'assistant', content: decision.message });
45
+ break;
46
+ }
47
+ if (decision.injectMessage) {
48
+ messages.push({ role: 'system', content: decision.injectMessage });
49
+ }
50
+ // 硬兜底检查
51
+ if (config.maxTurns !== undefined && turn >= config.maxTurns) {
52
+ messages.push({
53
+ role: 'assistant',
54
+ content: (_a = config.terminateMessage) !== null && _a !== void 0 ? _a : 'Max turns reached.',
55
+ });
56
+ break;
57
+ }
58
+ // 第三步:Tool 发现
59
+ const discoverResult = yield discoverTools(config, harness);
60
+ const visibleTools = discoverResult.filter((t) => t.visible);
61
+ const toolDefinitions = visibleTools.map((t) => ({
62
+ type: 'function',
63
+ function: {
64
+ name: t.name,
65
+ description: t.description,
66
+ parameters: t.parameters,
67
+ },
68
+ }));
69
+ const visibleToolNames = new Set(visibleTools.map((t) => t.name));
70
+ // 第四步:上下文压缩
71
+ const messagesForLLM = yield compressMessages(config, messages, turn);
72
+ // 第五步:调用 LLM
73
+ const llmResponse = yield config.llmCaller.call(messagesForLLM, toolDefinitions, {
74
+ model: config.model,
75
+ temperature: config.temperature,
76
+ maxTokens: config.maxTokens,
77
+ topP: config.topP,
78
+ reasoningEffort: config.reasoningEffort,
79
+ });
80
+ // 第六步:检查 LLM 回复
81
+ if (!llmResponse.tool_calls || llmResponse.tool_calls.length === 0) {
82
+ messages.push({
83
+ role: 'assistant',
84
+ content: (_b = llmResponse.content) !== null && _b !== void 0 ? _b : '',
85
+ });
86
+ return {
87
+ messages,
88
+ harness: harness.getAll(),
89
+ finalContent: llmResponse.content,
90
+ turns: turn,
91
+ };
92
+ }
93
+ // 有 tool_calls,将 assistant message 加入 Messages
94
+ messages.push({
95
+ role: 'assistant',
96
+ content: (_c = llmResponse.content) !== null && _c !== void 0 ? _c : '',
97
+ tool_calls: llmResponse.tool_calls,
98
+ });
99
+ // 第七步:并行执行 tool
100
+ const harnessSnapshot = harness.snapshot();
101
+ const parallelResults = yield executeToolsInParallel(config, harness, llmResponse.tool_calls, visibleToolNames, harnessSnapshot, turn);
102
+ // 将 tool result messages 加入 Messages
103
+ for (const result of parallelResults) {
104
+ messages.push({
105
+ role: 'tool',
106
+ content: result.content,
107
+ tool_call_id: result.toolCallId,
108
+ });
109
+ }
110
+ // 检查是否有待审批的 tool
111
+ const pending = parallelResults.find((r) => r.pendingApproval);
112
+ if (pending === null || pending === void 0 ? void 0 : pending.pendingApproval) {
113
+ return {
114
+ messages,
115
+ harness: harness.getAll(),
116
+ finalContent: null,
117
+ turns: turn,
118
+ pendingApproval: {
119
+ ticketId: pending.pendingApproval.ticketId,
120
+ toolName: pending.pendingApproval.toolName,
121
+ args: pending.pendingApproval.args,
122
+ callId: pending.toolCallId,
123
+ },
124
+ };
125
+ }
126
+ // 第八步:回到第一步继续下一轮
127
+ }
128
+ // Loop 被终止(Turn Policy 或硬限制)
129
+ const lastMessage = messages[messages.length - 1];
130
+ return {
131
+ messages,
132
+ harness: harness.getAll(),
133
+ finalContent: (lastMessage === null || lastMessage === void 0 ? void 0 : lastMessage.role) === 'assistant' ? lastMessage.content : null,
134
+ turns: turn,
135
+ };
136
+ });
137
+ }
138
+ /**
139
+ * 执行 Turn Policy。
140
+ */
141
+ function runTurnPolicy(config, harness, turn) {
142
+ return __awaiter(this, void 0, void 0, function* () {
143
+ var _a, _b;
144
+ if (config.turnPolicy) {
145
+ try {
146
+ return yield config.turnPolicy(harness.getAll(), turn, config.metadata);
147
+ }
148
+ catch (_c) {
149
+ return {};
150
+ }
151
+ }
152
+ // 默认策略
153
+ const maxTurns = (_a = config.maxTurns) !== null && _a !== void 0 ? _a : Infinity;
154
+ if (turn < maxTurns - 1) {
155
+ return {};
156
+ }
157
+ if (turn === maxTurns - 1) {
158
+ return {
159
+ injectMessage: config.warningMessage,
160
+ };
161
+ }
162
+ return {
163
+ message: (_b = config.terminateMessage) !== null && _b !== void 0 ? _b : 'Max turns reached.',
164
+ };
165
+ });
166
+ }
167
+ /**
168
+ * Tool 发现。
169
+ */
170
+ function discoverTools(config, harness) {
171
+ return __awaiter(this, void 0, void 0, function* () {
172
+ const results = [];
173
+ for (const tool of config.tools) {
174
+ if (tool.discover) {
175
+ try {
176
+ const result = yield tool.discover(harness.getAll(), config.metadata);
177
+ results.push(result);
178
+ }
179
+ catch (_a) {
180
+ // discover 抛异常 → 视为不可见
181
+ results.push({
182
+ name: tool.name,
183
+ description: tool.description,
184
+ parameters: tool.parameters,
185
+ visible: false,
186
+ });
187
+ }
188
+ }
189
+ else {
190
+ // 没有 discover 函数 → 使用静态配置,可见
191
+ results.push({
192
+ name: tool.name,
193
+ description: tool.description,
194
+ parameters: tool.parameters,
195
+ visible: true,
196
+ });
197
+ }
198
+ }
199
+ return results;
200
+ });
201
+ }
202
+ /**
203
+ * 将 Messages 按轮次分组(不含初始 system/user 消息)。
204
+ * 一轮 = 一个 assistant message + 其后的所有 tool messages。
205
+ */
206
+ function groupMessagesByRound(messages) {
207
+ const rounds = [];
208
+ let i = 0;
209
+ // 跳过初始消息(system 和 user)
210
+ while (i < messages.length && messages[i].role !== 'assistant') {
211
+ i++;
212
+ }
213
+ // 从第一个 assistant 开始分组
214
+ while (i < messages.length) {
215
+ const round = [messages[i]];
216
+ i++;
217
+ while (i < messages.length && messages[i].role !== 'assistant') {
218
+ round.push(messages[i]);
219
+ i++;
220
+ }
221
+ rounds.push(round);
222
+ }
223
+ return rounds;
224
+ }
225
+ /**
226
+ * 上下文压缩。
227
+ */
228
+ function compressMessages(config, messages, _turn) {
229
+ return __awaiter(this, void 0, void 0, function* () {
230
+ if (!config.compression) {
231
+ return messages;
232
+ }
233
+ const firstAssistantIndex = messages.findIndex((m) => m.role === 'assistant');
234
+ if (firstAssistantIndex === -1) {
235
+ return messages;
236
+ }
237
+ const initialMessages = messages.slice(0, firstAssistantIndex);
238
+ const restMessages = messages.slice(firstAssistantIndex);
239
+ const rounds = groupMessagesByRound(restMessages);
240
+ if (rounds.length <= config.compression.keepRounds) {
241
+ return messages;
242
+ }
243
+ const keepCount = config.compression.keepRounds;
244
+ const oldRounds = rounds.slice(0, rounds.length - keepCount);
245
+ const recentRounds = rounds.slice(rounds.length - keepCount);
246
+ try {
247
+ const compressed = yield config.compression.compress(oldRounds);
248
+ return [...initialMessages, ...compressed, ...recentRounds.flat()];
249
+ }
250
+ catch (_a) {
251
+ // compress 抛异常 → 视为不压缩
252
+ return messages;
253
+ }
254
+ });
255
+ }
256
+ /**
257
+ * 并行执行 tool。
258
+ */
259
+ function executeToolsInParallel(config, harness, toolCalls, visibleToolNames, harnessSnapshot, turn) {
260
+ return __awaiter(this, void 0, void 0, function* () {
261
+ var _a, _b, _c, _d, _e, _f, _g;
262
+ const toolMap = new Map(config.tools.map((t) => [t.name, t]));
263
+ const signal = config.signal;
264
+ const promises = toolCalls.map((toolCall) => __awaiter(this, void 0, void 0, function* () {
265
+ const toolName = toolCall.function.name;
266
+ const callId = toolCall.id;
267
+ const rawArgs = toolCall.function.arguments;
268
+ let args;
269
+ try {
270
+ args = JSON.parse(rawArgs);
271
+ }
272
+ catch (_a) {
273
+ args = rawArgs;
274
+ }
275
+ const globalIndex = harness.length;
276
+ const pendingRecord = (0, harness_1.createPendingRecord)({
277
+ id: callId,
278
+ turn,
279
+ globalIndex,
280
+ toolName,
281
+ args,
282
+ rawArgs,
283
+ });
284
+ // 执行前事件
285
+ const startEvent = {
286
+ type: 'execution:start',
287
+ callId,
288
+ toolName,
289
+ args,
290
+ turn,
291
+ };
292
+ if (config.onEvent) {
293
+ try {
294
+ yield config.onEvent(startEvent);
295
+ }
296
+ catch (_b) {
297
+ // 事件回调异常不应阻塞主流程
298
+ }
299
+ }
300
+ let status = 'pending';
301
+ let result;
302
+ let error;
303
+ // 检查 tool 是否可见
304
+ if (!visibleToolNames.has(toolName)) {
305
+ status = 'error';
306
+ error = `Tool "${toolName}" is not visible in this turn`;
307
+ }
308
+ else {
309
+ const tool = toolMap.get(toolName);
310
+ if (!tool) {
311
+ status = 'error';
312
+ error = `Tool "${toolName}" not found`;
313
+ }
314
+ else {
315
+ const context = {
316
+ harness: harnessSnapshot,
317
+ turn,
318
+ metadata: config.metadata,
319
+ };
320
+ try {
321
+ if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
322
+ status = 'cancelled';
323
+ }
324
+ else if (tool.approval) {
325
+ const ticket = yield tool.approval.createTicket(args, context);
326
+ if (ticket.approved) {
327
+ // 已审批,直接执行
328
+ result = yield tool.execute(args, context);
329
+ status = 'success';
330
+ }
331
+ else {
332
+ // 未审批,暂停 Loop
333
+ result = { __type: 'PENDING_APPROVAL', ticketId: ticket.ticketId };
334
+ status = 'success';
335
+ }
336
+ }
337
+ else {
338
+ result = yield tool.execute(args, context);
339
+ status = 'success';
340
+ }
341
+ }
342
+ catch (e) {
343
+ status = 'error';
344
+ error = e instanceof Error ? e.message : String(e);
345
+ }
346
+ }
347
+ }
348
+ const finalRecord = (0, harness_1.finalizeRecord)(pendingRecord, status, result, error);
349
+ // 执行后事件
350
+ const endEvent = {
351
+ type: 'execution:end',
352
+ callId,
353
+ toolName,
354
+ status: finalRecord.status,
355
+ durationMs: finalRecord.durationMs,
356
+ result: finalRecord.result,
357
+ error: finalRecord.error,
358
+ turn,
359
+ };
360
+ const content = status === 'success'
361
+ ? typeof result === 'object' && result !== null
362
+ ? JSON.stringify(result)
363
+ : String(result !== null && result !== void 0 ? result : '')
364
+ : error !== null && error !== void 0 ? error : 'Execution failed';
365
+ return {
366
+ toolCallId: callId,
367
+ content,
368
+ record: finalRecord,
369
+ events: [endEvent],
370
+ pendingApproval: typeof result === 'object' &&
371
+ result !== null &&
372
+ '__type' in result &&
373
+ result.__type === 'PENDING_APPROVAL'
374
+ ? {
375
+ ticketId: result.ticketId,
376
+ toolName,
377
+ args,
378
+ }
379
+ : undefined,
380
+ };
381
+ }));
382
+ const settled = yield Promise.allSettled(promises);
383
+ // 收集结果,按完成顺序追加到 Harness
384
+ const results = [];
385
+ for (const item of settled) {
386
+ if (item.status === 'fulfilled') {
387
+ results.push(item.value);
388
+ }
389
+ else {
390
+ // Promise 自身抛异常(极罕见,通常已被内部 try-catch 处理)
391
+ const failedCall = toolCalls[settled.indexOf(item)];
392
+ const fallbackRecord = (0, harness_1.createPendingRecord)({
393
+ id: (_a = failedCall === null || failedCall === void 0 ? void 0 : failedCall.id) !== null && _a !== void 0 ? _a : `unknown-${Date.now()}`,
394
+ turn,
395
+ globalIndex: harness.length,
396
+ toolName: (_b = failedCall === null || failedCall === void 0 ? void 0 : failedCall.function.name) !== null && _b !== void 0 ? _b : 'unknown',
397
+ args: failedCall === null || failedCall === void 0 ? void 0 : failedCall.function.arguments,
398
+ rawArgs: (_c = failedCall === null || failedCall === void 0 ? void 0 : failedCall.function.arguments) !== null && _c !== void 0 ? _c : '',
399
+ });
400
+ const finalRecord = (0, harness_1.finalizeRecord)(fallbackRecord, 'error', undefined, item.reason instanceof Error ? item.reason.message : String(item.reason));
401
+ results.push({
402
+ toolCallId: (_d = failedCall === null || failedCall === void 0 ? void 0 : failedCall.id) !== null && _d !== void 0 ? _d : '',
403
+ content: (_e = finalRecord.error) !== null && _e !== void 0 ? _e : 'Execution failed',
404
+ record: finalRecord,
405
+ events: [
406
+ {
407
+ type: 'execution:end',
408
+ callId: (_f = failedCall === null || failedCall === void 0 ? void 0 : failedCall.id) !== null && _f !== void 0 ? _f : '',
409
+ toolName: (_g = failedCall === null || failedCall === void 0 ? void 0 : failedCall.function.name) !== null && _g !== void 0 ? _g : 'unknown',
410
+ status: 'error',
411
+ durationMs: finalRecord.durationMs,
412
+ error: finalRecord.error,
413
+ turn,
414
+ },
415
+ ],
416
+ });
417
+ }
418
+ }
419
+ // 按完成顺序追加 HarnessRecord,并发出事件
420
+ for (const result of results) {
421
+ harness.append(result.record);
422
+ for (const event of result.events) {
423
+ if (config.onEvent) {
424
+ try {
425
+ yield config.onEvent(event);
426
+ }
427
+ catch (_h) {
428
+ // 事件回调异常不应阻塞主流程
429
+ }
430
+ }
431
+ }
432
+ }
433
+ return results;
434
+ });
435
+ }
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
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Function Call Loop 独立的 LLM Provider 实现。
3
+ * 兼容 OpenAI 协议,不依赖 predict 模块。
4
+ */
5
+ import type { Message, ToolCall, ToolDefinition, LLMCallOptions } from './types';
6
+ export interface ProviderOptions {
7
+ readonly apiKey: string;
8
+ readonly baseUrl: string;
9
+ readonly defaultModel: string;
10
+ }
11
+ /**
12
+ * 基于 OpenAI 协议的 LLM 调用器。
13
+ */
14
+ export declare function createLLMCaller(options: ProviderOptions): {
15
+ call(messages: ReadonlyArray<Message>, tools: ReadonlyArray<ToolDefinition>, callOptions?: LLMCallOptions): Promise<{
16
+ readonly content: string | null;
17
+ readonly tool_calls?: ReadonlyArray<ToolCall>;
18
+ }>;
19
+ };
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.createLLMCaller = createLLMCaller;
13
+ /**
14
+ * 基于 OpenAI 协议的 LLM 调用器。
15
+ */
16
+ function createLLMCaller(options) {
17
+ return {
18
+ call(messages, tools, callOptions) {
19
+ return __awaiter(this, void 0, void 0, function* () {
20
+ var _a, _b, _c;
21
+ const url = `${options.baseUrl}/chat/completions`;
22
+ const body = {
23
+ model: (_a = callOptions === null || callOptions === void 0 ? void 0 : callOptions.model) !== null && _a !== void 0 ? _a : options.defaultModel,
24
+ messages: messages.map((m) => {
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
+ }),
40
+ temperature: callOptions === null || callOptions === void 0 ? void 0 : callOptions.temperature,
41
+ max_tokens: callOptions === null || callOptions === void 0 ? void 0 : callOptions.maxTokens,
42
+ top_p: callOptions === null || callOptions === void 0 ? void 0 : callOptions.topP,
43
+ tools: tools.length > 0
44
+ ? tools.map((t) => ({
45
+ type: t.type,
46
+ function: {
47
+ name: t.function.name,
48
+ description: t.function.description,
49
+ parameters: t.function.parameters,
50
+ },
51
+ }))
52
+ : undefined,
53
+ };
54
+ let response;
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
+ }
77
+ return {
78
+ content: choice.message.content,
79
+ tool_calls: (_c = choice.message.tool_calls) === null || _c === void 0 ? void 0 : _c.map((tc) => ({
80
+ id: tc.id,
81
+ type: tc.type,
82
+ function: {
83
+ name: tc.function.name,
84
+ arguments: tc.function.arguments,
85
+ },
86
+ })),
87
+ };
88
+ });
89
+ },
90
+ };
91
+ }