@perstack/runtime 0.0.61 → 0.0.63
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 +87 -59
- package/dist/src/index.d.ts +427 -386
- package/dist/src/index.js +776 -404
- package/dist/src/index.js.map +1 -1
- package/package.json +3 -3
package/dist/src/index.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync } from 'fs';
|
|
2
|
+
import { readFile, mkdir, writeFile } from 'fs/promises';
|
|
3
|
+
import path2 from 'path';
|
|
4
|
+
import { jobSchema, runSettingSchema, checkpointSchema, knownModels, stopRunByDelegate, stopRunByInteractiveTool, resolveToolResults, attemptCompletion, callDelegate, callInteractiveTool, stopRunByExceededMaxSteps, continueToNextStep, retry, completeRun, callTools, startRun, resumeToolCalls, finishAllToolCalls, startGeneration, finishToolCall, runParamsSchema, createRuntimeEvent } from '@perstack/core';
|
|
1
5
|
import { createAmazonBedrock } from '@ai-sdk/amazon-bedrock';
|
|
2
6
|
import { createAnthropic } from '@ai-sdk/anthropic';
|
|
3
7
|
import { createAzure } from '@ai-sdk/azure';
|
|
@@ -5,11 +9,8 @@ import { createDeepSeek } from '@ai-sdk/deepseek';
|
|
|
5
9
|
import { createGoogleGenerativeAI } from '@ai-sdk/google';
|
|
6
10
|
import { createVertex } from '@ai-sdk/google-vertex';
|
|
7
11
|
import { createOpenAI } from '@ai-sdk/openai';
|
|
8
|
-
import { knownModels, stopRunByDelegate, stopRunByInteractiveTool, resolveThought, attemptCompletion, resolvePdfFile, resolveImageFile, resolveToolResult, stopRunByExceededMaxSteps, continueToNextStep, retry, completeRun, callDelegate, callInteractiveTool, callTool, startRun, startGeneration, finishToolCall, runParamsSchema, createRuntimeEvent, checkpointSchema, runSettingSchema } from '@perstack/core';
|
|
9
12
|
import { createOllama } from 'ollama-ai-provider-v2';
|
|
10
13
|
import { createId } from '@paralleldrive/cuid2';
|
|
11
|
-
import { readFile, readdir, mkdir, writeFile } from 'fs/promises';
|
|
12
|
-
import path from 'path';
|
|
13
14
|
import { setup, assign, createActor } from 'xstate';
|
|
14
15
|
import { generateText, tool, jsonSchema } from 'ai';
|
|
15
16
|
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
@@ -21,7 +22,208 @@ import { ApiV1Client } from '@perstack/api-client/v1';
|
|
|
21
22
|
|
|
22
23
|
// package.json
|
|
23
24
|
var package_default = {
|
|
24
|
-
version: "0.0.
|
|
25
|
+
version: "0.0.63"};
|
|
26
|
+
function getJobsDir() {
|
|
27
|
+
return `${process.cwd()}/perstack/jobs`;
|
|
28
|
+
}
|
|
29
|
+
function getJobDir(jobId) {
|
|
30
|
+
return `${getJobsDir()}/${jobId}`;
|
|
31
|
+
}
|
|
32
|
+
function storeJob(job) {
|
|
33
|
+
const jobDir = getJobDir(job.id);
|
|
34
|
+
if (!existsSync(jobDir)) {
|
|
35
|
+
mkdirSync(jobDir, { recursive: true });
|
|
36
|
+
}
|
|
37
|
+
const jobPath = path2.resolve(jobDir, "job.json");
|
|
38
|
+
writeFileSync(jobPath, JSON.stringify(job, null, 2));
|
|
39
|
+
}
|
|
40
|
+
function retrieveJob(jobId) {
|
|
41
|
+
const jobDir = getJobDir(jobId);
|
|
42
|
+
const jobPath = path2.resolve(jobDir, "job.json");
|
|
43
|
+
if (!existsSync(jobPath)) {
|
|
44
|
+
return void 0;
|
|
45
|
+
}
|
|
46
|
+
const content = readFileSync(jobPath, "utf-8");
|
|
47
|
+
return jobSchema.parse(JSON.parse(content));
|
|
48
|
+
}
|
|
49
|
+
function getAllJobs() {
|
|
50
|
+
const jobsDir = getJobsDir();
|
|
51
|
+
if (!existsSync(jobsDir)) {
|
|
52
|
+
return [];
|
|
53
|
+
}
|
|
54
|
+
const jobDirNames = readdirSync(jobsDir, { withFileTypes: true }).filter((dir) => dir.isDirectory()).map((dir) => dir.name);
|
|
55
|
+
if (jobDirNames.length === 0) {
|
|
56
|
+
return [];
|
|
57
|
+
}
|
|
58
|
+
const jobs = [];
|
|
59
|
+
for (const jobDirName of jobDirNames) {
|
|
60
|
+
const jobPath = path2.resolve(jobsDir, jobDirName, "job.json");
|
|
61
|
+
if (!existsSync(jobPath)) {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
const content = readFileSync(jobPath, "utf-8");
|
|
66
|
+
jobs.push(jobSchema.parse(JSON.parse(content)));
|
|
67
|
+
} catch {
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return jobs.sort((a, b) => b.startedAt - a.startedAt);
|
|
71
|
+
}
|
|
72
|
+
function createInitialJob(jobId, expertKey, maxSteps) {
|
|
73
|
+
return {
|
|
74
|
+
id: jobId,
|
|
75
|
+
status: "running",
|
|
76
|
+
coordinatorExpertKey: expertKey,
|
|
77
|
+
totalSteps: 0,
|
|
78
|
+
maxSteps,
|
|
79
|
+
usage: {
|
|
80
|
+
inputTokens: 0,
|
|
81
|
+
outputTokens: 0,
|
|
82
|
+
reasoningTokens: 0,
|
|
83
|
+
totalTokens: 0,
|
|
84
|
+
cachedInputTokens: 0
|
|
85
|
+
},
|
|
86
|
+
startedAt: Date.now()
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
async function createDefaultFileSystem() {
|
|
90
|
+
const fs = await import('fs');
|
|
91
|
+
const fsPromises = await import('fs/promises');
|
|
92
|
+
return {
|
|
93
|
+
existsSync: fs.existsSync,
|
|
94
|
+
mkdir: async (p, options) => {
|
|
95
|
+
await fsPromises.mkdir(p, options);
|
|
96
|
+
},
|
|
97
|
+
readFile: fsPromises.readFile,
|
|
98
|
+
writeFile: fsPromises.writeFile
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function defaultGetRunDir(jobId, runId) {
|
|
102
|
+
return `${process.cwd()}/perstack/jobs/${jobId}/runs/${runId}`;
|
|
103
|
+
}
|
|
104
|
+
async function storeRunSetting(setting, fs, getRunDir = defaultGetRunDir) {
|
|
105
|
+
const fileSystem = fs ?? await createDefaultFileSystem();
|
|
106
|
+
const runDir = getRunDir(setting.jobId, setting.runId);
|
|
107
|
+
if (fileSystem.existsSync(runDir)) {
|
|
108
|
+
const runSettingPath = path2.resolve(runDir, "run-setting.json");
|
|
109
|
+
const runSetting = runSettingSchema.parse(
|
|
110
|
+
JSON.parse(await fileSystem.readFile(runSettingPath, "utf-8"))
|
|
111
|
+
);
|
|
112
|
+
runSetting.updatedAt = Date.now();
|
|
113
|
+
await fileSystem.writeFile(runSettingPath, JSON.stringify(runSetting), "utf-8");
|
|
114
|
+
} else {
|
|
115
|
+
await fileSystem.mkdir(runDir, { recursive: true });
|
|
116
|
+
await fileSystem.writeFile(
|
|
117
|
+
path2.resolve(runDir, "run-setting.json"),
|
|
118
|
+
JSON.stringify(setting),
|
|
119
|
+
"utf-8"
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
function getAllRuns() {
|
|
124
|
+
const jobsDir = getJobsDir();
|
|
125
|
+
if (!existsSync(jobsDir)) {
|
|
126
|
+
return [];
|
|
127
|
+
}
|
|
128
|
+
const jobDirNames = readdirSync(jobsDir, { withFileTypes: true }).filter((dir) => dir.isDirectory()).map((dir) => dir.name);
|
|
129
|
+
if (jobDirNames.length === 0) {
|
|
130
|
+
return [];
|
|
131
|
+
}
|
|
132
|
+
const runs = [];
|
|
133
|
+
for (const jobDirName of jobDirNames) {
|
|
134
|
+
const runsDir = path2.resolve(jobsDir, jobDirName, "runs");
|
|
135
|
+
if (!existsSync(runsDir)) {
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
const runDirNames = readdirSync(runsDir, { withFileTypes: true }).filter((dir) => dir.isDirectory()).map((dir) => dir.name);
|
|
139
|
+
for (const runDirName of runDirNames) {
|
|
140
|
+
const runSettingPath = path2.resolve(runsDir, runDirName, "run-setting.json");
|
|
141
|
+
if (!existsSync(runSettingPath)) {
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
try {
|
|
145
|
+
const content = readFileSync(runSettingPath, "utf-8");
|
|
146
|
+
runs.push(runSettingSchema.parse(JSON.parse(content)));
|
|
147
|
+
} catch {
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return runs.sort((a, b) => b.updatedAt - a.updatedAt);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// src/default-store.ts
|
|
155
|
+
function getCheckpointDir(jobId) {
|
|
156
|
+
return `${getJobDir(jobId)}/checkpoints`;
|
|
157
|
+
}
|
|
158
|
+
function getCheckpointPath(jobId, checkpointId) {
|
|
159
|
+
return `${getCheckpointDir(jobId)}/${checkpointId}.json`;
|
|
160
|
+
}
|
|
161
|
+
async function defaultRetrieveCheckpoint(jobId, checkpointId) {
|
|
162
|
+
const checkpointPath = getCheckpointPath(jobId, checkpointId);
|
|
163
|
+
if (!existsSync(checkpointPath)) {
|
|
164
|
+
throw new Error(`checkpoint not found: ${checkpointId}`);
|
|
165
|
+
}
|
|
166
|
+
const checkpoint = await readFile(checkpointPath, "utf8");
|
|
167
|
+
return checkpointSchema.parse(JSON.parse(checkpoint));
|
|
168
|
+
}
|
|
169
|
+
async function defaultStoreCheckpoint(checkpoint) {
|
|
170
|
+
const { id, jobId } = checkpoint;
|
|
171
|
+
const checkpointDir = getCheckpointDir(jobId);
|
|
172
|
+
await mkdir(checkpointDir, { recursive: true });
|
|
173
|
+
await writeFile(getCheckpointPath(jobId, id), JSON.stringify(checkpoint));
|
|
174
|
+
}
|
|
175
|
+
async function defaultStoreEvent(event) {
|
|
176
|
+
const { timestamp, jobId, runId, stepNumber, type } = event;
|
|
177
|
+
const runDir = defaultGetRunDir(jobId, runId);
|
|
178
|
+
const eventPath = `${runDir}/event-${timestamp}-${stepNumber}-${type}.json`;
|
|
179
|
+
await mkdir(runDir, { recursive: true });
|
|
180
|
+
await writeFile(eventPath, JSON.stringify(event));
|
|
181
|
+
}
|
|
182
|
+
function getCheckpointsByJobId(jobId) {
|
|
183
|
+
const checkpointDir = getCheckpointDir(jobId);
|
|
184
|
+
if (!existsSync(checkpointDir)) {
|
|
185
|
+
return [];
|
|
186
|
+
}
|
|
187
|
+
const files = readdirSync(checkpointDir).filter((file) => file.endsWith(".json"));
|
|
188
|
+
const checkpoints = [];
|
|
189
|
+
for (const file of files) {
|
|
190
|
+
try {
|
|
191
|
+
const content = readFileSync(path2.resolve(checkpointDir, file), "utf-8");
|
|
192
|
+
checkpoints.push(checkpointSchema.parse(JSON.parse(content)));
|
|
193
|
+
} catch {
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return checkpoints.sort((a, b) => a.stepNumber - b.stepNumber);
|
|
197
|
+
}
|
|
198
|
+
function getEventsByRun(jobId, runId) {
|
|
199
|
+
const runDir = defaultGetRunDir(jobId, runId);
|
|
200
|
+
if (!existsSync(runDir)) {
|
|
201
|
+
return [];
|
|
202
|
+
}
|
|
203
|
+
return readdirSync(runDir).filter((file) => file.startsWith("event-")).map((file) => {
|
|
204
|
+
const [_, timestamp, stepNumber, type] = file.split(".")[0].split("-");
|
|
205
|
+
return { timestamp: Number(timestamp), stepNumber: Number(stepNumber), type };
|
|
206
|
+
}).sort((a, b) => a.stepNumber - b.stepNumber);
|
|
207
|
+
}
|
|
208
|
+
function getEventContents(jobId, runId, maxStepNumber) {
|
|
209
|
+
const runDir = defaultGetRunDir(jobId, runId);
|
|
210
|
+
if (!existsSync(runDir)) {
|
|
211
|
+
return [];
|
|
212
|
+
}
|
|
213
|
+
const eventFiles = readdirSync(runDir).filter((file) => file.startsWith("event-")).map((file) => {
|
|
214
|
+
const [_, timestamp, step, type] = file.split(".")[0].split("-");
|
|
215
|
+
return { file, timestamp: Number(timestamp), stepNumber: Number(step), type };
|
|
216
|
+
}).filter((e) => maxStepNumber === void 0 || e.stepNumber <= maxStepNumber).sort((a, b) => a.timestamp - b.timestamp);
|
|
217
|
+
const events = [];
|
|
218
|
+
for (const { file } of eventFiles) {
|
|
219
|
+
try {
|
|
220
|
+
const content = readFileSync(path2.resolve(runDir, file), "utf-8");
|
|
221
|
+
events.push(JSON.parse(content));
|
|
222
|
+
} catch {
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return events;
|
|
226
|
+
}
|
|
25
227
|
function getModel(modelId, providerConfig) {
|
|
26
228
|
switch (providerConfig.providerName) {
|
|
27
229
|
case "anthropic": {
|
|
@@ -142,6 +344,7 @@ function sumUsage(a, b) {
|
|
|
142
344
|
function createInitialCheckpoint(checkpointId, params) {
|
|
143
345
|
return {
|
|
144
346
|
id: checkpointId,
|
|
347
|
+
jobId: params.jobId,
|
|
145
348
|
runId: params.runId,
|
|
146
349
|
expert: {
|
|
147
350
|
key: params.expertKey,
|
|
@@ -185,6 +388,7 @@ function buildDelegationReturnState(currentSetting, resultCheckpoint, parentChec
|
|
|
185
388
|
interactiveToolCallResult: {
|
|
186
389
|
toolCallId,
|
|
187
390
|
toolName,
|
|
391
|
+
skillName: `delegate/${resultCheckpoint.expert.key}`,
|
|
188
392
|
text: delegateText.text
|
|
189
393
|
}
|
|
190
394
|
}
|
|
@@ -192,16 +396,19 @@ function buildDelegationReturnState(currentSetting, resultCheckpoint, parentChec
|
|
|
192
396
|
checkpoint: {
|
|
193
397
|
...parentCheckpoint,
|
|
194
398
|
stepNumber: resultCheckpoint.stepNumber,
|
|
195
|
-
usage: resultCheckpoint.usage
|
|
399
|
+
usage: resultCheckpoint.usage,
|
|
400
|
+
pendingToolCalls: parentCheckpoint.pendingToolCalls,
|
|
401
|
+
partialToolResults: parentCheckpoint.partialToolResults
|
|
196
402
|
}
|
|
197
403
|
};
|
|
198
404
|
}
|
|
199
405
|
function buildDelegateToState(currentSetting, resultCheckpoint, currentExpert) {
|
|
200
406
|
const { delegateTo } = resultCheckpoint;
|
|
201
|
-
if (!delegateTo) {
|
|
407
|
+
if (!delegateTo || delegateTo.length === 0) {
|
|
202
408
|
throw new Error("delegateTo is required for buildDelegateToState");
|
|
203
409
|
}
|
|
204
|
-
const
|
|
410
|
+
const firstDelegation = delegateTo[0];
|
|
411
|
+
const { expert, toolCallId, toolName, query } = firstDelegation;
|
|
205
412
|
return {
|
|
206
413
|
setting: {
|
|
207
414
|
...currentSetting,
|
|
@@ -229,73 +436,12 @@ function buildDelegateToState(currentSetting, resultCheckpoint, currentExpert) {
|
|
|
229
436
|
toolName,
|
|
230
437
|
checkpointId: resultCheckpoint.id
|
|
231
438
|
},
|
|
232
|
-
usage: resultCheckpoint.usage
|
|
439
|
+
usage: resultCheckpoint.usage,
|
|
440
|
+
pendingToolCalls: void 0,
|
|
441
|
+
partialToolResults: void 0
|
|
233
442
|
}
|
|
234
443
|
};
|
|
235
444
|
}
|
|
236
|
-
async function createDefaultFileSystem() {
|
|
237
|
-
const fs = await import('fs');
|
|
238
|
-
const fsPromises = await import('fs/promises');
|
|
239
|
-
return {
|
|
240
|
-
existsSync: fs.existsSync,
|
|
241
|
-
mkdir: async (p, options) => {
|
|
242
|
-
await fsPromises.mkdir(p, options);
|
|
243
|
-
},
|
|
244
|
-
readFile: fsPromises.readFile,
|
|
245
|
-
writeFile: fsPromises.writeFile
|
|
246
|
-
};
|
|
247
|
-
}
|
|
248
|
-
function defaultGetRunDir(runId) {
|
|
249
|
-
return `${process.cwd()}/perstack/runs/${runId}`;
|
|
250
|
-
}
|
|
251
|
-
async function storeRunSetting(setting, fs, getRunDir = defaultGetRunDir) {
|
|
252
|
-
const fileSystem = fs ?? await createDefaultFileSystem();
|
|
253
|
-
const runDir = getRunDir(setting.runId);
|
|
254
|
-
if (fileSystem.existsSync(runDir)) {
|
|
255
|
-
const runSettingPath = path.resolve(runDir, "run-setting.json");
|
|
256
|
-
const runSetting = runSettingSchema.parse(
|
|
257
|
-
JSON.parse(await fileSystem.readFile(runSettingPath, "utf-8"))
|
|
258
|
-
);
|
|
259
|
-
runSetting.updatedAt = Date.now();
|
|
260
|
-
await fileSystem.writeFile(runSettingPath, JSON.stringify(runSetting), "utf-8");
|
|
261
|
-
} else {
|
|
262
|
-
await fileSystem.mkdir(runDir, { recursive: true });
|
|
263
|
-
await fileSystem.writeFile(
|
|
264
|
-
path.resolve(runDir, "run-setting.json"),
|
|
265
|
-
JSON.stringify(setting),
|
|
266
|
-
"utf-8"
|
|
267
|
-
);
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
// src/default-store.ts
|
|
272
|
-
async function defaultRetrieveCheckpoint(runId, checkpointId) {
|
|
273
|
-
const runDir = defaultGetRunDir(runId);
|
|
274
|
-
const checkpointFiles = await readdir(runDir, { withFileTypes: true }).then(
|
|
275
|
-
(files) => files.filter((file) => file.isFile() && file.name.startsWith("checkpoint-"))
|
|
276
|
-
);
|
|
277
|
-
const checkpointFile = checkpointFiles.find((file) => file.name.endsWith(`-${checkpointId}.json`));
|
|
278
|
-
if (!checkpointFile) {
|
|
279
|
-
throw new Error(`checkpoint not found: ${runId} ${checkpointId}`);
|
|
280
|
-
}
|
|
281
|
-
const checkpointPath = `${runDir}/${checkpointFile.name}`;
|
|
282
|
-
const checkpoint = await readFile(checkpointPath, "utf8");
|
|
283
|
-
return checkpointSchema.parse(JSON.parse(checkpoint));
|
|
284
|
-
}
|
|
285
|
-
async function defaultStoreCheckpoint(checkpoint, timestamp) {
|
|
286
|
-
const { id, runId, stepNumber } = checkpoint;
|
|
287
|
-
const runDir = defaultGetRunDir(runId);
|
|
288
|
-
const checkpointPath = `${runDir}/checkpoint-${timestamp}-${stepNumber}-${id}.json`;
|
|
289
|
-
await mkdir(runDir, { recursive: true });
|
|
290
|
-
await writeFile(checkpointPath, JSON.stringify(checkpoint));
|
|
291
|
-
}
|
|
292
|
-
async function defaultStoreEvent(event) {
|
|
293
|
-
const { timestamp, runId, stepNumber, type } = event;
|
|
294
|
-
const runDir = defaultGetRunDir(runId);
|
|
295
|
-
const eventPath = `${runDir}/event-${timestamp}-${stepNumber}-${type}.json`;
|
|
296
|
-
await mkdir(runDir, { recursive: true });
|
|
297
|
-
await writeFile(eventPath, JSON.stringify(event));
|
|
298
|
-
}
|
|
299
445
|
var RunEventEmitter = class {
|
|
300
446
|
listeners = [];
|
|
301
447
|
subscribe(listener) {
|
|
@@ -328,9 +474,11 @@ var BaseSkillManager = class {
|
|
|
328
474
|
skill;
|
|
329
475
|
interactiveSkill;
|
|
330
476
|
expert;
|
|
477
|
+
_jobId;
|
|
331
478
|
_runId;
|
|
332
479
|
_eventListener;
|
|
333
|
-
constructor(runId, eventListener) {
|
|
480
|
+
constructor(jobId, runId, eventListener) {
|
|
481
|
+
this._jobId = jobId;
|
|
334
482
|
this._runId = runId;
|
|
335
483
|
this._eventListener = eventListener;
|
|
336
484
|
}
|
|
@@ -381,8 +529,8 @@ var DelegateSkillManager = class extends BaseSkillManager {
|
|
|
381
529
|
type = "delegate";
|
|
382
530
|
lazyInit = false;
|
|
383
531
|
expert;
|
|
384
|
-
constructor(expert, runId, eventListener) {
|
|
385
|
-
super(runId, eventListener);
|
|
532
|
+
constructor(expert, jobId, runId, eventListener) {
|
|
533
|
+
super(jobId, runId, eventListener);
|
|
386
534
|
this.name = expert.name;
|
|
387
535
|
this.expert = expert;
|
|
388
536
|
}
|
|
@@ -414,8 +562,8 @@ var InteractiveSkillManager = class extends BaseSkillManager {
|
|
|
414
562
|
type = "interactive";
|
|
415
563
|
lazyInit = false;
|
|
416
564
|
interactiveSkill;
|
|
417
|
-
constructor(interactiveSkill, runId, eventListener) {
|
|
418
|
-
super(runId, eventListener);
|
|
565
|
+
constructor(interactiveSkill, jobId, runId, eventListener) {
|
|
566
|
+
super(jobId, runId, eventListener);
|
|
419
567
|
this.name = interactiveSkill.name;
|
|
420
568
|
this.interactiveSkill = interactiveSkill;
|
|
421
569
|
}
|
|
@@ -441,8 +589,8 @@ var McpSkillManager = class extends BaseSkillManager {
|
|
|
441
589
|
skill;
|
|
442
590
|
_mcpClient;
|
|
443
591
|
_env;
|
|
444
|
-
constructor(skill, env, runId, eventListener) {
|
|
445
|
-
super(runId, eventListener);
|
|
592
|
+
constructor(skill, env, jobId, runId, eventListener) {
|
|
593
|
+
super(jobId, runId, eventListener);
|
|
446
594
|
this.name = skill.name;
|
|
447
595
|
this.skill = skill;
|
|
448
596
|
this._env = env;
|
|
@@ -481,7 +629,7 @@ var McpSkillManager = class extends BaseSkillManager {
|
|
|
481
629
|
const startTime = Date.now();
|
|
482
630
|
const { command, args } = this._getCommandArgs(skill);
|
|
483
631
|
if (this._eventListener) {
|
|
484
|
-
const event = createRuntimeEvent("skillStarting", this._runId, {
|
|
632
|
+
const event = createRuntimeEvent("skillStarting", this._jobId, this._runId, {
|
|
485
633
|
skillName: skill.name,
|
|
486
634
|
command,
|
|
487
635
|
args
|
|
@@ -492,7 +640,7 @@ var McpSkillManager = class extends BaseSkillManager {
|
|
|
492
640
|
if (transport.stderr) {
|
|
493
641
|
transport.stderr.on("data", (chunk) => {
|
|
494
642
|
if (this._eventListener) {
|
|
495
|
-
const event = createRuntimeEvent("skillStderr", this._runId, {
|
|
643
|
+
const event = createRuntimeEvent("skillStderr", this._jobId, this._runId, {
|
|
496
644
|
skillName: skill.name,
|
|
497
645
|
message: chunk.toString().trim()
|
|
498
646
|
});
|
|
@@ -505,7 +653,7 @@ var McpSkillManager = class extends BaseSkillManager {
|
|
|
505
653
|
const connectTime = Date.now();
|
|
506
654
|
if (this._eventListener) {
|
|
507
655
|
const serverInfo = this._mcpClient.getServerVersion();
|
|
508
|
-
const event = createRuntimeEvent("skillConnected", this._runId, {
|
|
656
|
+
const event = createRuntimeEvent("skillConnected", this._jobId, this._runId, {
|
|
509
657
|
skillName: skill.name,
|
|
510
658
|
serverInfo: serverInfo ? { name: serverInfo.name, version: serverInfo.version } : void 0,
|
|
511
659
|
connectDurationMs: connectTime - connectStartTime,
|
|
@@ -541,7 +689,7 @@ var McpSkillManager = class extends BaseSkillManager {
|
|
|
541
689
|
if (this._mcpClient) {
|
|
542
690
|
await this._mcpClient.close();
|
|
543
691
|
if (this._eventListener && this.skill) {
|
|
544
|
-
const event = createRuntimeEvent("skillDisconnected", this._runId, {
|
|
692
|
+
const event = createRuntimeEvent("skillDisconnected", this._jobId, this._runId, {
|
|
545
693
|
skillName: this.skill.name
|
|
546
694
|
});
|
|
547
695
|
this._eventListener(event);
|
|
@@ -644,8 +792,8 @@ async function initSkillManagersWithCleanup(managers, allManagers) {
|
|
|
644
792
|
throw firstRejected.reason;
|
|
645
793
|
}
|
|
646
794
|
}
|
|
647
|
-
async function getSkillManagers(expert, experts, setting, eventListener) {
|
|
648
|
-
const { perstackBaseSkillCommand, env, runId } = setting;
|
|
795
|
+
async function getSkillManagers(expert, experts, setting, eventListener, options) {
|
|
796
|
+
const { perstackBaseSkillCommand, env, jobId, runId } = setting;
|
|
649
797
|
const { skills } = expert;
|
|
650
798
|
if (!skills["@perstack/base"]) {
|
|
651
799
|
throw new Error("Base skill is not defined");
|
|
@@ -672,20 +820,22 @@ async function getSkillManagers(expert, experts, setting, eventListener) {
|
|
|
672
820
|
return skill;
|
|
673
821
|
});
|
|
674
822
|
const mcpSkillManagers = mcpSkills.map((skill) => {
|
|
675
|
-
const manager = new McpSkillManager(skill, env, runId, eventListener);
|
|
823
|
+
const manager = new McpSkillManager(skill, env, jobId, runId, eventListener);
|
|
676
824
|
allManagers.push(manager);
|
|
677
825
|
return manager;
|
|
678
826
|
});
|
|
679
827
|
await initSkillManagersWithCleanup(mcpSkillManagers, allManagers);
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
const
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
828
|
+
if (!options?.isDelegatedRun) {
|
|
829
|
+
const interactiveSkills = Object.values(skills).filter(
|
|
830
|
+
(skill) => skill.type === "interactiveSkill"
|
|
831
|
+
);
|
|
832
|
+
const interactiveSkillManagers = interactiveSkills.map((interactiveSkill) => {
|
|
833
|
+
const manager = new InteractiveSkillManager(interactiveSkill, jobId, runId, eventListener);
|
|
834
|
+
allManagers.push(manager);
|
|
835
|
+
return manager;
|
|
836
|
+
});
|
|
837
|
+
await initSkillManagersWithCleanup(interactiveSkillManagers, allManagers);
|
|
838
|
+
}
|
|
689
839
|
const delegateSkillManagers = [];
|
|
690
840
|
for (const delegateExpertName of expert.delegates) {
|
|
691
841
|
const delegate = experts[delegateExpertName];
|
|
@@ -694,7 +844,7 @@ async function getSkillManagers(expert, experts, setting, eventListener) {
|
|
|
694
844
|
})));
|
|
695
845
|
throw new Error(`Delegate expert "${delegateExpertName}" not found in experts`);
|
|
696
846
|
}
|
|
697
|
-
const manager = new DelegateSkillManager(delegate, runId, eventListener);
|
|
847
|
+
const manager = new DelegateSkillManager(delegate, jobId, runId, eventListener);
|
|
698
848
|
allManagers.push(manager);
|
|
699
849
|
delegateSkillManagers.push(manager);
|
|
700
850
|
}
|
|
@@ -735,37 +885,58 @@ async function getToolSet(skillManagers) {
|
|
|
735
885
|
}
|
|
736
886
|
|
|
737
887
|
// src/states/calling-delegate.ts
|
|
888
|
+
async function getToolType(toolName, skillManagers) {
|
|
889
|
+
const skillManager = await getSkillManagerByToolName(skillManagers, toolName);
|
|
890
|
+
return skillManager.type;
|
|
891
|
+
}
|
|
738
892
|
async function callingDelegateLogic({
|
|
739
893
|
setting,
|
|
740
894
|
checkpoint,
|
|
741
895
|
step,
|
|
742
896
|
skillManagers
|
|
743
897
|
}) {
|
|
744
|
-
if (!step.
|
|
745
|
-
throw new Error("No tool
|
|
746
|
-
}
|
|
747
|
-
const
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
898
|
+
if (!step.pendingToolCalls || step.pendingToolCalls.length === 0) {
|
|
899
|
+
throw new Error("No pending tool calls found");
|
|
900
|
+
}
|
|
901
|
+
const toolCallTypes = await Promise.all(
|
|
902
|
+
step.pendingToolCalls.map(async (tc) => ({
|
|
903
|
+
toolCall: tc,
|
|
904
|
+
type: await getToolType(tc.toolName, skillManagers)
|
|
905
|
+
}))
|
|
906
|
+
);
|
|
907
|
+
const delegateToolCalls = toolCallTypes.filter((t) => t.type === "delegate").map((t) => t.toolCall);
|
|
908
|
+
const nonDelegateToolCalls = toolCallTypes.filter((t) => t.type !== "delegate").map((t) => t.toolCall);
|
|
909
|
+
if (delegateToolCalls.length === 0) {
|
|
910
|
+
throw new Error("No delegate tool calls found");
|
|
911
|
+
}
|
|
912
|
+
const delegations = await Promise.all(
|
|
913
|
+
delegateToolCalls.map(async (tc) => {
|
|
914
|
+
const skillManager = await getSkillManagerByToolName(skillManagers, tc.toolName);
|
|
915
|
+
if (!skillManager.expert) {
|
|
916
|
+
throw new Error(`Delegation error: skill manager "${tc.toolName}" not found`);
|
|
917
|
+
}
|
|
918
|
+
if (!tc.args || !tc.args.query || typeof tc.args.query !== "string") {
|
|
919
|
+
throw new Error(`Delegation error: query is undefined for ${tc.toolName}`);
|
|
920
|
+
}
|
|
921
|
+
return {
|
|
760
922
|
expert: {
|
|
761
923
|
key: skillManager.expert.key,
|
|
762
924
|
name: skillManager.expert.name,
|
|
763
925
|
version: skillManager.expert.version
|
|
764
926
|
},
|
|
765
|
-
toolCallId: id,
|
|
766
|
-
toolName,
|
|
767
|
-
query: args.query
|
|
768
|
-
}
|
|
927
|
+
toolCallId: tc.id,
|
|
928
|
+
toolName: tc.toolName,
|
|
929
|
+
query: tc.args.query
|
|
930
|
+
};
|
|
931
|
+
})
|
|
932
|
+
);
|
|
933
|
+
return stopRunByDelegate(setting, checkpoint, {
|
|
934
|
+
checkpoint: {
|
|
935
|
+
...checkpoint,
|
|
936
|
+
status: "stoppedByDelegate",
|
|
937
|
+
delegateTo: delegations,
|
|
938
|
+
pendingToolCalls: nonDelegateToolCalls.length > 0 ? nonDelegateToolCalls : void 0,
|
|
939
|
+
partialToolResults: step.partialToolResults
|
|
769
940
|
},
|
|
770
941
|
step: {
|
|
771
942
|
...step,
|
|
@@ -778,10 +949,17 @@ async function callingInteractiveToolLogic({
|
|
|
778
949
|
checkpoint,
|
|
779
950
|
step
|
|
780
951
|
}) {
|
|
952
|
+
if (!step.pendingToolCalls || step.pendingToolCalls.length === 0) {
|
|
953
|
+
throw new Error("No pending tool calls found");
|
|
954
|
+
}
|
|
955
|
+
const currentToolCall = step.pendingToolCalls[0];
|
|
956
|
+
const remainingToolCalls = step.pendingToolCalls.slice(1);
|
|
781
957
|
return stopRunByInteractiveTool(setting, checkpoint, {
|
|
782
958
|
checkpoint: {
|
|
783
959
|
...checkpoint,
|
|
784
|
-
status: "stoppedByInteractiveTool"
|
|
960
|
+
status: "stoppedByInteractiveTool",
|
|
961
|
+
pendingToolCalls: [currentToolCall, ...remainingToolCalls],
|
|
962
|
+
partialToolResults: step.partialToolResults
|
|
785
963
|
},
|
|
786
964
|
step: {
|
|
787
965
|
...step,
|
|
@@ -789,37 +967,150 @@ async function callingInteractiveToolLogic({
|
|
|
789
967
|
}
|
|
790
968
|
});
|
|
791
969
|
}
|
|
970
|
+
function hasRemainingTodos(toolResult) {
|
|
971
|
+
const firstPart = toolResult.result[0];
|
|
972
|
+
if (!firstPart || firstPart.type !== "textPart") {
|
|
973
|
+
return false;
|
|
974
|
+
}
|
|
975
|
+
try {
|
|
976
|
+
const parsed = JSON.parse(firstPart.text);
|
|
977
|
+
return Array.isArray(parsed.remainingTodos) && parsed.remainingTodos.length > 0;
|
|
978
|
+
} catch {
|
|
979
|
+
return false;
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
function isFileInfo(value) {
|
|
983
|
+
return typeof value === "object" && value !== null && "path" in value && "mimeType" in value && "size" in value && typeof value.path === "string" && typeof value.mimeType === "string" && typeof value.size === "number";
|
|
984
|
+
}
|
|
985
|
+
async function processFileToolResult(toolResult, toolName) {
|
|
986
|
+
const processedContents = [];
|
|
987
|
+
for (const part of toolResult.result) {
|
|
988
|
+
if (part.type !== "textPart") {
|
|
989
|
+
processedContents.push(part);
|
|
990
|
+
continue;
|
|
991
|
+
}
|
|
992
|
+
let fileInfo;
|
|
993
|
+
try {
|
|
994
|
+
const parsed = JSON.parse(part.text);
|
|
995
|
+
if (isFileInfo(parsed)) {
|
|
996
|
+
fileInfo = parsed;
|
|
997
|
+
}
|
|
998
|
+
} catch {
|
|
999
|
+
processedContents.push(part);
|
|
1000
|
+
continue;
|
|
1001
|
+
}
|
|
1002
|
+
if (!fileInfo) {
|
|
1003
|
+
processedContents.push(part);
|
|
1004
|
+
continue;
|
|
1005
|
+
}
|
|
1006
|
+
const { path: path4, mimeType } = fileInfo;
|
|
1007
|
+
try {
|
|
1008
|
+
const buffer = await readFile(path4);
|
|
1009
|
+
if (toolName === "readImageFile") {
|
|
1010
|
+
processedContents.push({
|
|
1011
|
+
type: "imageInlinePart",
|
|
1012
|
+
id: part.id,
|
|
1013
|
+
encodedData: buffer.toString("base64"),
|
|
1014
|
+
mimeType
|
|
1015
|
+
});
|
|
1016
|
+
} else {
|
|
1017
|
+
processedContents.push({
|
|
1018
|
+
type: "fileInlinePart",
|
|
1019
|
+
id: part.id,
|
|
1020
|
+
encodedData: buffer.toString("base64"),
|
|
1021
|
+
mimeType
|
|
1022
|
+
});
|
|
1023
|
+
}
|
|
1024
|
+
} catch (error) {
|
|
1025
|
+
processedContents.push({
|
|
1026
|
+
type: "textPart",
|
|
1027
|
+
id: part.id,
|
|
1028
|
+
text: `Failed to read file "${path4}": ${error instanceof Error ? error.message : String(error)}`
|
|
1029
|
+
});
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
return { ...toolResult, result: processedContents };
|
|
1033
|
+
}
|
|
1034
|
+
async function executeMcpToolCall(toolCall, skillManagers) {
|
|
1035
|
+
const skillManager = await getSkillManagerByToolName(skillManagers, toolCall.toolName);
|
|
1036
|
+
if (skillManager.type !== "mcp") {
|
|
1037
|
+
throw new Error(`Incorrect SkillType, required MCP, got ${skillManager.type}`);
|
|
1038
|
+
}
|
|
1039
|
+
const result = await skillManager.callTool(toolCall.toolName, toolCall.args);
|
|
1040
|
+
const toolResult = {
|
|
1041
|
+
id: toolCall.id,
|
|
1042
|
+
skillName: toolCall.skillName,
|
|
1043
|
+
toolName: toolCall.toolName,
|
|
1044
|
+
result
|
|
1045
|
+
};
|
|
1046
|
+
if (toolCall.toolName === "readPdfFile" || toolCall.toolName === "readImageFile") {
|
|
1047
|
+
return processFileToolResult(toolResult, toolCall.toolName);
|
|
1048
|
+
}
|
|
1049
|
+
return toolResult;
|
|
1050
|
+
}
|
|
1051
|
+
async function getToolType2(toolCall, skillManagers) {
|
|
1052
|
+
const skillManager = await getSkillManagerByToolName(skillManagers, toolCall.toolName);
|
|
1053
|
+
return skillManager.type;
|
|
1054
|
+
}
|
|
792
1055
|
async function callingToolLogic({
|
|
793
1056
|
setting,
|
|
794
1057
|
checkpoint,
|
|
795
1058
|
step,
|
|
796
1059
|
skillManagers
|
|
797
1060
|
}) {
|
|
798
|
-
|
|
799
|
-
|
|
1061
|
+
const pendingToolCalls = step.pendingToolCalls ?? step.toolCalls ?? [];
|
|
1062
|
+
if (pendingToolCalls.length === 0) {
|
|
1063
|
+
throw new Error("No tool calls found");
|
|
800
1064
|
}
|
|
801
|
-
const
|
|
802
|
-
const
|
|
803
|
-
|
|
804
|
-
|
|
1065
|
+
const toolResults = step.toolResults ? [...step.toolResults] : [];
|
|
1066
|
+
const attemptCompletionTool = pendingToolCalls.find(
|
|
1067
|
+
(tc) => tc.skillName === "@perstack/base" && tc.toolName === "attemptCompletion"
|
|
1068
|
+
);
|
|
1069
|
+
if (attemptCompletionTool) {
|
|
1070
|
+
const toolResult = await executeMcpToolCall(attemptCompletionTool, skillManagers);
|
|
1071
|
+
if (hasRemainingTodos(toolResult)) {
|
|
1072
|
+
return resolveToolResults(setting, checkpoint, { toolResults: [toolResult] });
|
|
1073
|
+
}
|
|
1074
|
+
return attemptCompletion(setting, checkpoint, { toolResult });
|
|
1075
|
+
}
|
|
1076
|
+
const toolCallTypes = await Promise.all(
|
|
1077
|
+
pendingToolCalls.map(async (tc) => ({
|
|
1078
|
+
toolCall: tc,
|
|
1079
|
+
type: await getToolType2(tc, skillManagers)
|
|
1080
|
+
}))
|
|
1081
|
+
);
|
|
1082
|
+
const mcpToolCalls = toolCallTypes.filter((t) => t.type === "mcp").map((t) => t.toolCall);
|
|
1083
|
+
const delegateToolCalls = toolCallTypes.filter((t) => t.type === "delegate").map((t) => t.toolCall);
|
|
1084
|
+
const interactiveToolCalls = toolCallTypes.filter((t) => t.type === "interactive").map((t) => t.toolCall);
|
|
1085
|
+
if (mcpToolCalls.length > 0) {
|
|
1086
|
+
const mcpResults = await Promise.all(
|
|
1087
|
+
mcpToolCalls.map((tc) => executeMcpToolCall(tc, skillManagers))
|
|
1088
|
+
);
|
|
1089
|
+
toolResults.push(...mcpResults);
|
|
1090
|
+
}
|
|
1091
|
+
if (delegateToolCalls.length > 0) {
|
|
1092
|
+
step.partialToolResults = toolResults;
|
|
1093
|
+
step.pendingToolCalls = [...delegateToolCalls, ...interactiveToolCalls];
|
|
1094
|
+
return callDelegate(setting, checkpoint, {
|
|
1095
|
+
newMessage: checkpoint.messages[checkpoint.messages.length - 1],
|
|
1096
|
+
toolCalls: delegateToolCalls,
|
|
1097
|
+
usage: step.usage
|
|
1098
|
+
});
|
|
805
1099
|
}
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
}
|
|
818
|
-
if (toolName === "readImageFile") {
|
|
819
|
-
return resolveImageFile(setting, checkpoint, { toolResult });
|
|
820
|
-
}
|
|
1100
|
+
if (interactiveToolCalls.length > 0) {
|
|
1101
|
+
const interactiveToolCall = interactiveToolCalls[0];
|
|
1102
|
+
if (!interactiveToolCall) {
|
|
1103
|
+
throw new Error("No interactive tool call found");
|
|
1104
|
+
}
|
|
1105
|
+
step.partialToolResults = toolResults;
|
|
1106
|
+
step.pendingToolCalls = interactiveToolCalls;
|
|
1107
|
+
return callInteractiveTool(setting, checkpoint, {
|
|
1108
|
+
newMessage: checkpoint.messages[checkpoint.messages.length - 1],
|
|
1109
|
+
toolCall: interactiveToolCall,
|
|
1110
|
+
usage: step.usage
|
|
1111
|
+
});
|
|
821
1112
|
}
|
|
822
|
-
return
|
|
1113
|
+
return resolveToolResults(setting, checkpoint, { toolResults });
|
|
823
1114
|
}
|
|
824
1115
|
async function finishingStepLogic({
|
|
825
1116
|
setting,
|
|
@@ -1035,9 +1326,12 @@ function toolResultPartToCoreToolResultPart(part) {
|
|
|
1035
1326
|
output: { type: "text", value: contents[0].text }
|
|
1036
1327
|
};
|
|
1037
1328
|
}
|
|
1038
|
-
const contentValue = contents.map(
|
|
1039
|
-
(content
|
|
1040
|
-
|
|
1329
|
+
const contentValue = contents.map((content) => {
|
|
1330
|
+
if (content.type === "textPart") {
|
|
1331
|
+
return { type: "text", text: content.text };
|
|
1332
|
+
}
|
|
1333
|
+
return { type: "media", data: content.encodedData, mediaType: content.mimeType };
|
|
1334
|
+
});
|
|
1041
1335
|
return {
|
|
1042
1336
|
type: "tool-result",
|
|
1043
1337
|
toolCallId: part.toolCallId,
|
|
@@ -1052,21 +1346,21 @@ async function generatingRunResultLogic({
|
|
|
1052
1346
|
checkpoint,
|
|
1053
1347
|
step
|
|
1054
1348
|
}) {
|
|
1055
|
-
if (!step.
|
|
1056
|
-
throw new Error("No tool
|
|
1349
|
+
if (!step.toolCalls || !step.toolResults || step.toolResults.length === 0) {
|
|
1350
|
+
throw new Error("No tool calls or tool results found");
|
|
1057
1351
|
}
|
|
1058
|
-
const
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
{
|
|
1352
|
+
const toolResultParts = step.toolResults.map((toolResult) => {
|
|
1353
|
+
const toolCall = step.toolCalls?.find((tc) => tc.id === toolResult.id);
|
|
1354
|
+
return {
|
|
1062
1355
|
type: "toolResultPart",
|
|
1063
|
-
toolCallId: id,
|
|
1064
|
-
toolName,
|
|
1065
|
-
contents: result.filter(
|
|
1066
|
-
(part) => part.type === "textPart" || part.type === "imageInlinePart"
|
|
1356
|
+
toolCallId: toolResult.id,
|
|
1357
|
+
toolName: toolCall?.toolName ?? toolResult.toolName,
|
|
1358
|
+
contents: toolResult.result.filter(
|
|
1359
|
+
(part) => part.type === "textPart" || part.type === "imageInlinePart" || part.type === "fileInlinePart"
|
|
1067
1360
|
)
|
|
1068
|
-
}
|
|
1069
|
-
|
|
1361
|
+
};
|
|
1362
|
+
});
|
|
1363
|
+
const toolMessage = createToolMessage(toolResultParts);
|
|
1070
1364
|
const model = getModel(setting.model, setting.providerConfig);
|
|
1071
1365
|
const { messages } = checkpoint;
|
|
1072
1366
|
let generationResult;
|
|
@@ -1103,13 +1397,48 @@ async function generatingRunResultLogic({
|
|
|
1103
1397
|
step: {
|
|
1104
1398
|
...step,
|
|
1105
1399
|
newMessages: [...step.newMessages, ...newMessages],
|
|
1106
|
-
finishedAt:
|
|
1400
|
+
finishedAt: Date.now(),
|
|
1107
1401
|
usage: sumUsage(step.usage, usage)
|
|
1108
1402
|
},
|
|
1109
1403
|
text,
|
|
1110
1404
|
usage
|
|
1111
1405
|
});
|
|
1112
1406
|
}
|
|
1407
|
+
async function classifyToolCalls(toolCalls, skillManagers) {
|
|
1408
|
+
return Promise.all(
|
|
1409
|
+
toolCalls.map(async (tc) => {
|
|
1410
|
+
const skillManager = await getSkillManagerByToolName(skillManagers, tc.toolName);
|
|
1411
|
+
return {
|
|
1412
|
+
toolCallId: tc.toolCallId,
|
|
1413
|
+
toolName: tc.toolName,
|
|
1414
|
+
input: tc.input,
|
|
1415
|
+
skillManager
|
|
1416
|
+
};
|
|
1417
|
+
})
|
|
1418
|
+
);
|
|
1419
|
+
}
|
|
1420
|
+
function sortToolCallsByPriority(toolCalls) {
|
|
1421
|
+
const priority = { mcp: 0, delegate: 1, interactive: 2 };
|
|
1422
|
+
return [...toolCalls].sort(
|
|
1423
|
+
(a, b) => (priority[a.skillManager.type] ?? 99) - (priority[b.skillManager.type] ?? 99)
|
|
1424
|
+
);
|
|
1425
|
+
}
|
|
1426
|
+
function buildToolCallParts(toolCalls) {
|
|
1427
|
+
return toolCalls.map((tc) => ({
|
|
1428
|
+
type: "toolCallPart",
|
|
1429
|
+
toolCallId: tc.toolCallId,
|
|
1430
|
+
toolName: tc.toolName,
|
|
1431
|
+
args: tc.input
|
|
1432
|
+
}));
|
|
1433
|
+
}
|
|
1434
|
+
function buildToolCalls(toolCalls) {
|
|
1435
|
+
return toolCalls.map((tc) => ({
|
|
1436
|
+
id: tc.toolCallId,
|
|
1437
|
+
skillName: tc.skillManager.name,
|
|
1438
|
+
toolName: tc.toolName,
|
|
1439
|
+
args: tc.input
|
|
1440
|
+
}));
|
|
1441
|
+
}
|
|
1113
1442
|
async function generatingToolCallLogic({
|
|
1114
1443
|
setting,
|
|
1115
1444
|
checkpoint,
|
|
@@ -1141,8 +1470,7 @@ async function generatingToolCallLogic({
|
|
|
1141
1470
|
}
|
|
1142
1471
|
const usage = usageFromGenerateTextResult(result);
|
|
1143
1472
|
const { text, toolCalls, finishReason } = result;
|
|
1144
|
-
|
|
1145
|
-
if (!toolCall) {
|
|
1473
|
+
if (toolCalls.length === 0) {
|
|
1146
1474
|
const reason = JSON.stringify({
|
|
1147
1475
|
error: "Error: No tool call generated",
|
|
1148
1476
|
message: "You must generate a tool call. Try again."
|
|
@@ -1153,42 +1481,26 @@ async function generatingToolCallLogic({
|
|
|
1153
1481
|
usage
|
|
1154
1482
|
});
|
|
1155
1483
|
}
|
|
1156
|
-
const
|
|
1157
|
-
|
|
1158
|
-
type: "toolCallPart",
|
|
1159
|
-
toolCallId: toolCall.toolCallId,
|
|
1160
|
-
toolName: toolCall.toolName,
|
|
1161
|
-
args: toolCall.input
|
|
1162
|
-
}
|
|
1163
|
-
];
|
|
1164
|
-
if (text) {
|
|
1165
|
-
contents.push({
|
|
1166
|
-
type: "textPart",
|
|
1167
|
-
text
|
|
1168
|
-
});
|
|
1169
|
-
}
|
|
1170
|
-
const skillManager = await getSkillManagerByToolName(skillManagers, toolCall.toolName);
|
|
1171
|
-
const eventPayload = {
|
|
1172
|
-
newMessage: createExpertMessage(contents),
|
|
1173
|
-
toolCall: {
|
|
1174
|
-
id: toolCall.toolCallId,
|
|
1175
|
-
skillName: skillManager.name,
|
|
1176
|
-
toolName: toolCall.toolName,
|
|
1177
|
-
args: toolCall.input
|
|
1178
|
-
},
|
|
1179
|
-
usage
|
|
1180
|
-
};
|
|
1484
|
+
const classified = await classifyToolCalls(toolCalls, skillManagers);
|
|
1485
|
+
const sorted = sortToolCallsByPriority(classified);
|
|
1181
1486
|
if (finishReason === "tool-calls" || finishReason === "stop") {
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1487
|
+
const toolCallParts = buildToolCallParts(sorted);
|
|
1488
|
+
const contents = [...toolCallParts];
|
|
1489
|
+
if (text) {
|
|
1490
|
+
contents.push({ type: "textPart", text });
|
|
1491
|
+
}
|
|
1492
|
+
const allToolCalls = buildToolCalls(sorted);
|
|
1493
|
+
return callTools(setting, checkpoint, {
|
|
1494
|
+
newMessage: createExpertMessage(contents),
|
|
1495
|
+
toolCalls: allToolCalls,
|
|
1496
|
+
usage
|
|
1497
|
+
});
|
|
1190
1498
|
}
|
|
1191
1499
|
if (finishReason === "length") {
|
|
1500
|
+
const firstToolCall = sorted[0];
|
|
1501
|
+
if (!firstToolCall) {
|
|
1502
|
+
throw new Error("No tool call found");
|
|
1503
|
+
}
|
|
1192
1504
|
const reason = JSON.stringify({
|
|
1193
1505
|
error: "Error: Tool call generation failed",
|
|
1194
1506
|
message: "Generation length exceeded. Try again."
|
|
@@ -1199,27 +1511,36 @@ async function generatingToolCallLogic({
|
|
|
1199
1511
|
createExpertMessage([
|
|
1200
1512
|
{
|
|
1201
1513
|
type: "toolCallPart",
|
|
1202
|
-
toolCallId:
|
|
1203
|
-
toolName:
|
|
1204
|
-
args:
|
|
1514
|
+
toolCallId: firstToolCall.toolCallId,
|
|
1515
|
+
toolName: firstToolCall.toolName,
|
|
1516
|
+
args: firstToolCall.input
|
|
1205
1517
|
}
|
|
1206
1518
|
]),
|
|
1207
1519
|
createToolMessage([
|
|
1208
1520
|
{
|
|
1209
1521
|
type: "toolResultPart",
|
|
1210
|
-
toolCallId:
|
|
1211
|
-
toolName:
|
|
1522
|
+
toolCallId: firstToolCall.toolCallId,
|
|
1523
|
+
toolName: firstToolCall.toolName,
|
|
1212
1524
|
contents: [{ type: "textPart", text: reason }]
|
|
1213
1525
|
}
|
|
1214
1526
|
])
|
|
1215
1527
|
],
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1528
|
+
toolCalls: [
|
|
1529
|
+
{
|
|
1530
|
+
id: firstToolCall.toolCallId,
|
|
1531
|
+
skillName: firstToolCall.skillManager.name,
|
|
1532
|
+
toolName: firstToolCall.toolName,
|
|
1533
|
+
args: firstToolCall.input
|
|
1534
|
+
}
|
|
1535
|
+
],
|
|
1536
|
+
toolResults: [
|
|
1537
|
+
{
|
|
1538
|
+
id: firstToolCall.toolCallId,
|
|
1539
|
+
skillName: firstToolCall.skillManager.name,
|
|
1540
|
+
toolName: firstToolCall.toolName,
|
|
1541
|
+
result: [{ type: "textPart", id: createId(), text: reason }]
|
|
1542
|
+
}
|
|
1543
|
+
],
|
|
1223
1544
|
usage
|
|
1224
1545
|
});
|
|
1225
1546
|
}
|
|
@@ -1348,18 +1669,24 @@ async function initLogic({
|
|
|
1348
1669
|
if (!setting.input.interactiveToolCallResult) {
|
|
1349
1670
|
throw new Error("Interactive tool call result is undefined");
|
|
1350
1671
|
}
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1672
|
+
const { toolCallId, toolName, skillName, text } = setting.input.interactiveToolCallResult;
|
|
1673
|
+
const pendingToolCalls = checkpoint.pendingToolCalls ?? [];
|
|
1674
|
+
const newToolResult = {
|
|
1675
|
+
id: toolCallId,
|
|
1676
|
+
skillName,
|
|
1677
|
+
toolName,
|
|
1678
|
+
result: [{ type: "textPart", id: createId(), text }]
|
|
1679
|
+
};
|
|
1680
|
+
const updatedPartialResults = [...checkpoint.partialToolResults ?? [], newToolResult];
|
|
1681
|
+
const updatedPendingToolCalls = pendingToolCalls.filter((tc) => tc.id !== toolCallId);
|
|
1682
|
+
const updatedCheckpoint = {
|
|
1683
|
+
...checkpoint,
|
|
1684
|
+
partialToolResults: updatedPartialResults,
|
|
1685
|
+
pendingToolCalls: updatedPendingToolCalls.length > 0 ? updatedPendingToolCalls : void 0
|
|
1686
|
+
};
|
|
1687
|
+
return startRun(setting, updatedCheckpoint, {
|
|
1688
|
+
initialCheckpoint: updatedCheckpoint,
|
|
1689
|
+
inputMessages: []
|
|
1363
1690
|
});
|
|
1364
1691
|
}
|
|
1365
1692
|
default:
|
|
@@ -1376,116 +1703,27 @@ async function preparingForStepLogic({
|
|
|
1376
1703
|
setting,
|
|
1377
1704
|
checkpoint
|
|
1378
1705
|
}) {
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
setting,
|
|
1385
|
-
checkpoint,
|
|
1386
|
-
step
|
|
1387
|
-
}) {
|
|
1388
|
-
if (!step.toolCall || !step.toolResult) {
|
|
1389
|
-
throw new Error("No tool call or tool result found");
|
|
1390
|
-
}
|
|
1391
|
-
const { id, toolName } = step.toolCall;
|
|
1392
|
-
const { result } = step.toolResult;
|
|
1393
|
-
const textParts = result.filter((part) => part.type === "textPart");
|
|
1394
|
-
const files = [];
|
|
1395
|
-
for (const textPart of textParts) {
|
|
1396
|
-
let imageInfo;
|
|
1397
|
-
try {
|
|
1398
|
-
imageInfo = JSON.parse(textPart.text);
|
|
1399
|
-
} catch {
|
|
1400
|
-
files.push({
|
|
1401
|
-
type: "textPart",
|
|
1402
|
-
text: textPart.text
|
|
1403
|
-
});
|
|
1404
|
-
continue;
|
|
1405
|
-
}
|
|
1406
|
-
const { path: path2, mimeType, size } = imageInfo;
|
|
1407
|
-
try {
|
|
1408
|
-
const buffer = await readFile(path2);
|
|
1409
|
-
files.push({
|
|
1410
|
-
type: "imageInlinePart",
|
|
1411
|
-
encodedData: buffer.toString("base64"),
|
|
1412
|
-
mimeType
|
|
1413
|
-
});
|
|
1414
|
-
} catch (error) {
|
|
1415
|
-
files.push({
|
|
1416
|
-
type: "textPart",
|
|
1417
|
-
text: `Failed to read image file "${path2}": ${error instanceof Error ? error.message : String(error)}`
|
|
1418
|
-
});
|
|
1419
|
-
}
|
|
1706
|
+
if (checkpoint.pendingToolCalls && checkpoint.pendingToolCalls.length > 0) {
|
|
1707
|
+
return resumeToolCalls(setting, checkpoint, {
|
|
1708
|
+
pendingToolCalls: checkpoint.pendingToolCalls,
|
|
1709
|
+
partialToolResults: checkpoint.partialToolResults ?? []
|
|
1710
|
+
});
|
|
1420
1711
|
}
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
}
|
|
1434
|
-
async function resolvingPdfFileLogic({
|
|
1435
|
-
setting,
|
|
1436
|
-
checkpoint,
|
|
1437
|
-
step
|
|
1438
|
-
}) {
|
|
1439
|
-
if (!step.toolCall || !step.toolResult) {
|
|
1440
|
-
throw new Error("No tool call or tool result found");
|
|
1441
|
-
}
|
|
1442
|
-
const { id, toolName } = step.toolCall;
|
|
1443
|
-
const { result } = step.toolResult;
|
|
1444
|
-
const textParts = result.filter((part) => part.type === "textPart");
|
|
1445
|
-
const files = [];
|
|
1446
|
-
for (const textPart of textParts) {
|
|
1447
|
-
let pdfInfo;
|
|
1448
|
-
try {
|
|
1449
|
-
pdfInfo = JSON.parse(textPart.text);
|
|
1450
|
-
} catch {
|
|
1451
|
-
files.push({
|
|
1452
|
-
type: "textPart",
|
|
1453
|
-
text: textPart.text
|
|
1454
|
-
});
|
|
1455
|
-
continue;
|
|
1456
|
-
}
|
|
1457
|
-
const { path: path2, mimeType, size } = pdfInfo;
|
|
1458
|
-
try {
|
|
1459
|
-
const buffer = await readFile(path2);
|
|
1460
|
-
files.push({
|
|
1461
|
-
type: "fileInlinePart",
|
|
1462
|
-
encodedData: buffer.toString("base64"),
|
|
1463
|
-
mimeType
|
|
1464
|
-
});
|
|
1465
|
-
} catch (error) {
|
|
1466
|
-
files.push({
|
|
1467
|
-
type: "textPart",
|
|
1468
|
-
text: `Failed to read PDF file "${path2}": ${error instanceof Error ? error.message : String(error)}`
|
|
1469
|
-
});
|
|
1470
|
-
}
|
|
1712
|
+
if (checkpoint.partialToolResults && checkpoint.partialToolResults.length > 0) {
|
|
1713
|
+
const toolResultParts = checkpoint.partialToolResults.map((tr) => ({
|
|
1714
|
+
type: "toolResultPart",
|
|
1715
|
+
toolCallId: tr.id,
|
|
1716
|
+
toolName: tr.toolName,
|
|
1717
|
+
contents: tr.result.filter(
|
|
1718
|
+
(part) => part.type === "textPart" || part.type === "imageInlinePart" || part.type === "fileInlinePart"
|
|
1719
|
+
)
|
|
1720
|
+
}));
|
|
1721
|
+
return finishAllToolCalls(setting, checkpoint, {
|
|
1722
|
+
newMessages: [createToolMessage(toolResultParts)]
|
|
1723
|
+
});
|
|
1471
1724
|
}
|
|
1472
|
-
return
|
|
1473
|
-
|
|
1474
|
-
createToolMessage([
|
|
1475
|
-
{
|
|
1476
|
-
type: "toolResultPart",
|
|
1477
|
-
toolCallId: id,
|
|
1478
|
-
toolName,
|
|
1479
|
-
contents: [
|
|
1480
|
-
{
|
|
1481
|
-
type: "textPart",
|
|
1482
|
-
text: "User uploads PDF file as follows."
|
|
1483
|
-
}
|
|
1484
|
-
]
|
|
1485
|
-
}
|
|
1486
|
-
]),
|
|
1487
|
-
createUserMessage(files)
|
|
1488
|
-
]
|
|
1725
|
+
return startGeneration(setting, checkpoint, {
|
|
1726
|
+
messages: checkpoint.messages
|
|
1489
1727
|
});
|
|
1490
1728
|
}
|
|
1491
1729
|
async function resolvingToolResultLogic({
|
|
@@ -1493,24 +1731,22 @@ async function resolvingToolResultLogic({
|
|
|
1493
1731
|
checkpoint,
|
|
1494
1732
|
step
|
|
1495
1733
|
}) {
|
|
1496
|
-
if (!step.
|
|
1497
|
-
throw new Error("No tool
|
|
1734
|
+
if (!step.toolCalls || !step.toolResults || step.toolResults.length === 0) {
|
|
1735
|
+
throw new Error("No tool calls or tool results found");
|
|
1498
1736
|
}
|
|
1499
|
-
const
|
|
1500
|
-
|
|
1737
|
+
const toolResultParts = step.toolResults.map((toolResult) => {
|
|
1738
|
+
const toolCall = step.toolCalls?.find((tc) => tc.id === toolResult.id);
|
|
1739
|
+
return {
|
|
1740
|
+
type: "toolResultPart",
|
|
1741
|
+
toolCallId: toolResult.id,
|
|
1742
|
+
toolName: toolCall?.toolName ?? toolResult.toolName,
|
|
1743
|
+
contents: toolResult.result.filter(
|
|
1744
|
+
(part) => part.type === "textPart" || part.type === "imageInlinePart" || part.type === "fileInlinePart"
|
|
1745
|
+
)
|
|
1746
|
+
};
|
|
1747
|
+
});
|
|
1501
1748
|
return finishToolCall(setting, checkpoint, {
|
|
1502
|
-
newMessages: [
|
|
1503
|
-
createToolMessage([
|
|
1504
|
-
{
|
|
1505
|
-
type: "toolResultPart",
|
|
1506
|
-
toolCallId: id,
|
|
1507
|
-
toolName,
|
|
1508
|
-
contents: result.filter(
|
|
1509
|
-
(part) => part.type === "textPart" || part.type === "imageInlinePart"
|
|
1510
|
-
)
|
|
1511
|
-
}
|
|
1512
|
-
])
|
|
1513
|
-
]
|
|
1749
|
+
newMessages: [createToolMessage(toolResultParts)]
|
|
1514
1750
|
});
|
|
1515
1751
|
}
|
|
1516
1752
|
|
|
@@ -1552,7 +1788,9 @@ var runtimeStateMachine = setup({
|
|
|
1552
1788
|
checkpoint: ({ context, event }) => ({
|
|
1553
1789
|
...context.checkpoint,
|
|
1554
1790
|
status: "proceeding",
|
|
1555
|
-
messages: [...context.checkpoint.messages, ...event.inputMessages]
|
|
1791
|
+
messages: [...context.checkpoint.messages, ...event.inputMessages],
|
|
1792
|
+
pendingToolCalls: event.initialCheckpoint.pendingToolCalls,
|
|
1793
|
+
partialToolResults: event.initialCheckpoint.partialToolResults
|
|
1556
1794
|
}),
|
|
1557
1795
|
step: ({ context, event }) => ({
|
|
1558
1796
|
...context.step,
|
|
@@ -1575,6 +1813,38 @@ var runtimeStateMachine = setup({
|
|
|
1575
1813
|
startedAt: event.timestamp
|
|
1576
1814
|
})
|
|
1577
1815
|
})
|
|
1816
|
+
},
|
|
1817
|
+
resumeToolCalls: {
|
|
1818
|
+
target: "CallingTool",
|
|
1819
|
+
actions: assign({
|
|
1820
|
+
step: ({ context, event }) => ({
|
|
1821
|
+
stepNumber: context.checkpoint.stepNumber,
|
|
1822
|
+
inputMessages: context.step.inputMessages ?? [],
|
|
1823
|
+
newMessages: context.step.newMessages,
|
|
1824
|
+
toolCalls: context.step.toolCalls,
|
|
1825
|
+
toolResults: event.partialToolResults,
|
|
1826
|
+
pendingToolCalls: event.pendingToolCalls,
|
|
1827
|
+
usage: context.step.usage,
|
|
1828
|
+
startedAt: context.step.startedAt
|
|
1829
|
+
})
|
|
1830
|
+
})
|
|
1831
|
+
},
|
|
1832
|
+
finishAllToolCalls: {
|
|
1833
|
+
target: "FinishingStep",
|
|
1834
|
+
actions: assign({
|
|
1835
|
+
checkpoint: ({ context, event }) => ({
|
|
1836
|
+
...context.checkpoint,
|
|
1837
|
+
messages: [...context.checkpoint.messages, ...event.newMessages],
|
|
1838
|
+
pendingToolCalls: void 0,
|
|
1839
|
+
partialToolResults: void 0
|
|
1840
|
+
}),
|
|
1841
|
+
step: ({ context, event }) => ({
|
|
1842
|
+
...context.step,
|
|
1843
|
+
newMessages: [...context.step.newMessages, ...event.newMessages],
|
|
1844
|
+
toolResults: context.checkpoint.partialToolResults,
|
|
1845
|
+
pendingToolCalls: void 0
|
|
1846
|
+
})
|
|
1847
|
+
})
|
|
1578
1848
|
}
|
|
1579
1849
|
}
|
|
1580
1850
|
},
|
|
@@ -1591,13 +1861,13 @@ var runtimeStateMachine = setup({
|
|
|
1591
1861
|
step: ({ context, event }) => ({
|
|
1592
1862
|
...context.step,
|
|
1593
1863
|
newMessages: event.newMessages,
|
|
1594
|
-
|
|
1595
|
-
|
|
1864
|
+
toolCalls: event.toolCalls,
|
|
1865
|
+
toolResults: event.toolResults,
|
|
1596
1866
|
usage: sumUsage(context.step.usage, event.usage)
|
|
1597
1867
|
})
|
|
1598
1868
|
})
|
|
1599
1869
|
},
|
|
1600
|
-
|
|
1870
|
+
callTools: {
|
|
1601
1871
|
target: "CallingTool",
|
|
1602
1872
|
actions: assign({
|
|
1603
1873
|
checkpoint: ({ context, event }) => ({
|
|
@@ -1609,7 +1879,7 @@ var runtimeStateMachine = setup({
|
|
|
1609
1879
|
step: ({ context, event }) => ({
|
|
1610
1880
|
...context.step,
|
|
1611
1881
|
newMessages: [event.newMessage],
|
|
1612
|
-
|
|
1882
|
+
toolCalls: event.toolCalls,
|
|
1613
1883
|
usage: sumUsage(context.step.usage, event.usage)
|
|
1614
1884
|
})
|
|
1615
1885
|
})
|
|
@@ -1626,7 +1896,7 @@ var runtimeStateMachine = setup({
|
|
|
1626
1896
|
step: ({ context, event }) => ({
|
|
1627
1897
|
...context.step,
|
|
1628
1898
|
newMessages: [event.newMessage],
|
|
1629
|
-
|
|
1899
|
+
toolCalls: [event.toolCall],
|
|
1630
1900
|
usage: sumUsage(context.step.usage, event.usage)
|
|
1631
1901
|
})
|
|
1632
1902
|
})
|
|
@@ -1643,7 +1913,7 @@ var runtimeStateMachine = setup({
|
|
|
1643
1913
|
step: ({ context, event }) => ({
|
|
1644
1914
|
...context.step,
|
|
1645
1915
|
newMessages: [event.newMessage],
|
|
1646
|
-
|
|
1916
|
+
toolCalls: event.toolCalls,
|
|
1647
1917
|
usage: sumUsage(context.step.usage, event.usage)
|
|
1648
1918
|
})
|
|
1649
1919
|
})
|
|
@@ -1652,12 +1922,13 @@ var runtimeStateMachine = setup({
|
|
|
1652
1922
|
},
|
|
1653
1923
|
CallingTool: {
|
|
1654
1924
|
on: {
|
|
1655
|
-
|
|
1925
|
+
resolveToolResults: {
|
|
1656
1926
|
target: "ResolvingToolResult",
|
|
1657
1927
|
actions: assign({
|
|
1658
1928
|
step: ({ context, event }) => ({
|
|
1659
1929
|
...context.step,
|
|
1660
|
-
|
|
1930
|
+
toolResults: event.toolResults,
|
|
1931
|
+
pendingToolCalls: void 0
|
|
1661
1932
|
})
|
|
1662
1933
|
})
|
|
1663
1934
|
},
|
|
@@ -1666,34 +1937,40 @@ var runtimeStateMachine = setup({
|
|
|
1666
1937
|
actions: assign({
|
|
1667
1938
|
step: ({ context, event }) => ({
|
|
1668
1939
|
...context.step,
|
|
1669
|
-
|
|
1940
|
+
toolResults: [event.toolResult]
|
|
1670
1941
|
})
|
|
1671
1942
|
})
|
|
1672
1943
|
},
|
|
1673
|
-
|
|
1674
|
-
target: "
|
|
1944
|
+
attemptCompletion: {
|
|
1945
|
+
target: "GeneratingRunResult",
|
|
1675
1946
|
actions: assign({
|
|
1676
1947
|
step: ({ context, event }) => ({
|
|
1677
1948
|
...context.step,
|
|
1678
|
-
|
|
1949
|
+
toolResults: [event.toolResult]
|
|
1679
1950
|
})
|
|
1680
1951
|
})
|
|
1681
1952
|
},
|
|
1682
|
-
|
|
1683
|
-
target: "
|
|
1953
|
+
callDelegate: {
|
|
1954
|
+
target: "CallingDelegate",
|
|
1684
1955
|
actions: assign({
|
|
1685
|
-
step: ({ context
|
|
1956
|
+
step: ({ context }) => ({
|
|
1686
1957
|
...context.step,
|
|
1687
|
-
|
|
1958
|
+
toolCalls: context.step.toolCalls,
|
|
1959
|
+
toolResults: context.step.toolResults,
|
|
1960
|
+
pendingToolCalls: context.step.pendingToolCalls,
|
|
1961
|
+
partialToolResults: context.step.partialToolResults
|
|
1688
1962
|
})
|
|
1689
1963
|
})
|
|
1690
1964
|
},
|
|
1691
|
-
|
|
1692
|
-
target: "
|
|
1965
|
+
callInteractiveTool: {
|
|
1966
|
+
target: "CallingInteractiveTool",
|
|
1693
1967
|
actions: assign({
|
|
1694
|
-
step: ({ context
|
|
1968
|
+
step: ({ context }) => ({
|
|
1695
1969
|
...context.step,
|
|
1696
|
-
|
|
1970
|
+
toolCalls: context.step.toolCalls,
|
|
1971
|
+
toolResults: context.step.toolResults,
|
|
1972
|
+
pendingToolCalls: context.step.pendingToolCalls,
|
|
1973
|
+
partialToolResults: context.step.partialToolResults
|
|
1697
1974
|
})
|
|
1698
1975
|
})
|
|
1699
1976
|
}
|
|
@@ -1733,40 +2010,6 @@ var runtimeStateMachine = setup({
|
|
|
1733
2010
|
}
|
|
1734
2011
|
}
|
|
1735
2012
|
},
|
|
1736
|
-
ResolvingPdfFile: {
|
|
1737
|
-
on: {
|
|
1738
|
-
finishToolCall: {
|
|
1739
|
-
target: "FinishingStep",
|
|
1740
|
-
actions: assign({
|
|
1741
|
-
checkpoint: ({ context, event }) => ({
|
|
1742
|
-
...context.checkpoint,
|
|
1743
|
-
messages: [...context.checkpoint.messages, ...event.newMessages]
|
|
1744
|
-
}),
|
|
1745
|
-
step: ({ context, event }) => ({
|
|
1746
|
-
...context.step,
|
|
1747
|
-
newMessages: [...context.step.newMessages, ...event.newMessages]
|
|
1748
|
-
})
|
|
1749
|
-
})
|
|
1750
|
-
}
|
|
1751
|
-
}
|
|
1752
|
-
},
|
|
1753
|
-
ResolvingImageFile: {
|
|
1754
|
-
on: {
|
|
1755
|
-
finishToolCall: {
|
|
1756
|
-
target: "FinishingStep",
|
|
1757
|
-
actions: assign({
|
|
1758
|
-
checkpoint: ({ context, event }) => ({
|
|
1759
|
-
...context.checkpoint,
|
|
1760
|
-
messages: [...context.checkpoint.messages, ...event.newMessages]
|
|
1761
|
-
}),
|
|
1762
|
-
step: ({ context, event }) => ({
|
|
1763
|
-
...context.step,
|
|
1764
|
-
newMessages: [...context.step.newMessages, ...event.newMessages]
|
|
1765
|
-
})
|
|
1766
|
-
})
|
|
1767
|
-
}
|
|
1768
|
-
}
|
|
1769
|
-
},
|
|
1770
2013
|
GeneratingRunResult: {
|
|
1771
2014
|
on: {
|
|
1772
2015
|
retry: {
|
|
@@ -1780,8 +2023,8 @@ var runtimeStateMachine = setup({
|
|
|
1780
2023
|
step: ({ context, event }) => ({
|
|
1781
2024
|
...context.step,
|
|
1782
2025
|
newMessages: event.newMessages,
|
|
1783
|
-
|
|
1784
|
-
|
|
2026
|
+
toolCalls: event.toolCalls,
|
|
2027
|
+
toolResults: event.toolResults,
|
|
1785
2028
|
usage: sumUsage(context.step.usage, event.usage)
|
|
1786
2029
|
})
|
|
1787
2030
|
})
|
|
@@ -1863,8 +2106,6 @@ var StateMachineLogics = {
|
|
|
1863
2106
|
CallingTool: callingToolLogic,
|
|
1864
2107
|
ResolvingToolResult: resolvingToolResultLogic,
|
|
1865
2108
|
ResolvingThought: resolvingThoughtLogic,
|
|
1866
|
-
ResolvingPdfFile: resolvingPdfFileLogic,
|
|
1867
|
-
ResolvingImageFile: resolvingImageFileLogic,
|
|
1868
2109
|
GeneratingRunResult: generatingRunResultLogic,
|
|
1869
2110
|
CallingInteractiveTool: callingInteractiveToolLogic,
|
|
1870
2111
|
CallingDelegate: callingDelegateLogic,
|
|
@@ -1903,7 +2144,7 @@ async function executeStateMachine(params) {
|
|
|
1903
2144
|
} else {
|
|
1904
2145
|
const event = await StateMachineLogics[runState.value](runState.context);
|
|
1905
2146
|
if ("checkpoint" in event) {
|
|
1906
|
-
await storeCheckpoint(event.checkpoint
|
|
2147
|
+
await storeCheckpoint(event.checkpoint);
|
|
1907
2148
|
}
|
|
1908
2149
|
await eventEmitter.emit(event);
|
|
1909
2150
|
if (shouldContinueRun) {
|
|
@@ -1992,10 +2233,15 @@ async function run(runInput, options) {
|
|
|
1992
2233
|
const contextWindow = getContextWindow(setting.providerConfig.providerName, setting.model);
|
|
1993
2234
|
const getRunDir = options?.getRunDir ?? defaultGetRunDir;
|
|
1994
2235
|
await storeRunSetting(setting, options?.fileSystem, getRunDir);
|
|
2236
|
+
let job = retrieveJob(setting.jobId) ?? createInitialJob(setting.jobId, setting.expertKey, setting.maxSteps);
|
|
2237
|
+
if (job.status !== "running") {
|
|
2238
|
+
job = { ...job, status: "running", finishedAt: void 0 };
|
|
2239
|
+
}
|
|
2240
|
+
storeJob(job);
|
|
1995
2241
|
while (true) {
|
|
1996
2242
|
const { expertToRun, experts } = await setupExperts(setting, options?.resolveExpertToRun);
|
|
1997
2243
|
if (options?.eventListener) {
|
|
1998
|
-
const initEvent = createRuntimeEvent("initializeRuntime", setting.runId, {
|
|
2244
|
+
const initEvent = createRuntimeEvent("initializeRuntime", setting.jobId, setting.runId, {
|
|
1999
2245
|
runtimeVersion: package_default.version,
|
|
2000
2246
|
expertName: expertToRun.name,
|
|
2001
2247
|
experts: Object.keys(experts),
|
|
@@ -2013,9 +2259,11 @@ async function run(runInput, options) {
|
|
|
2013
2259
|
expertToRun,
|
|
2014
2260
|
experts,
|
|
2015
2261
|
setting,
|
|
2016
|
-
options?.eventListener
|
|
2262
|
+
options?.eventListener,
|
|
2263
|
+
{ isDelegatedRun: !!checkpoint?.delegatedBy }
|
|
2017
2264
|
);
|
|
2018
2265
|
const initialCheckpoint = checkpoint ? createNextStepCheckpoint(createId(), checkpoint) : createInitialCheckpoint(createId(), {
|
|
2266
|
+
jobId: setting.jobId,
|
|
2019
2267
|
runId: setting.runId,
|
|
2020
2268
|
expertKey: setting.expertKey,
|
|
2021
2269
|
expert: expertToRun,
|
|
@@ -2030,11 +2278,21 @@ async function run(runInput, options) {
|
|
|
2030
2278
|
storeCheckpoint,
|
|
2031
2279
|
shouldContinueRun: options?.shouldContinueRun
|
|
2032
2280
|
});
|
|
2281
|
+
job = {
|
|
2282
|
+
...job,
|
|
2283
|
+
totalSteps: runResultCheckpoint.stepNumber,
|
|
2284
|
+
usage: runResultCheckpoint.usage
|
|
2285
|
+
};
|
|
2033
2286
|
switch (runResultCheckpoint.status) {
|
|
2034
2287
|
case "completed": {
|
|
2288
|
+
if (options?.returnOnDelegationComplete) {
|
|
2289
|
+
storeJob(job);
|
|
2290
|
+
return runResultCheckpoint;
|
|
2291
|
+
}
|
|
2035
2292
|
if (runResultCheckpoint.delegatedBy) {
|
|
2293
|
+
storeJob(job);
|
|
2036
2294
|
const parentCheckpoint = await retrieveCheckpoint(
|
|
2037
|
-
setting.
|
|
2295
|
+
setting.jobId,
|
|
2038
2296
|
runResultCheckpoint.delegatedBy.checkpointId
|
|
2039
2297
|
);
|
|
2040
2298
|
const result = buildDelegationReturnState(setting, runResultCheckpoint, parentCheckpoint);
|
|
@@ -2042,21 +2300,80 @@ async function run(runInput, options) {
|
|
|
2042
2300
|
checkpoint = result.checkpoint;
|
|
2043
2301
|
break;
|
|
2044
2302
|
}
|
|
2303
|
+
storeJob({ ...job, status: "completed", finishedAt: Date.now() });
|
|
2045
2304
|
return runResultCheckpoint;
|
|
2046
2305
|
}
|
|
2047
2306
|
case "stoppedByInteractiveTool": {
|
|
2307
|
+
storeJob({ ...job, status: "stoppedByInteractiveTool" });
|
|
2048
2308
|
return runResultCheckpoint;
|
|
2049
2309
|
}
|
|
2050
2310
|
case "stoppedByDelegate": {
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2311
|
+
storeJob(job);
|
|
2312
|
+
const { delegateTo } = runResultCheckpoint;
|
|
2313
|
+
if (!delegateTo || delegateTo.length === 0) {
|
|
2314
|
+
throw new Error("No delegations found in checkpoint");
|
|
2315
|
+
}
|
|
2316
|
+
if (delegateTo.length === 1) {
|
|
2317
|
+
const result = buildDelegateToState(setting, runResultCheckpoint, expertToRun);
|
|
2318
|
+
setting = result.setting;
|
|
2319
|
+
checkpoint = result.checkpoint;
|
|
2320
|
+
break;
|
|
2321
|
+
}
|
|
2322
|
+
const firstDelegation = delegateTo[0];
|
|
2323
|
+
const remainingDelegations = delegateTo.slice(1);
|
|
2324
|
+
const [firstResult, ...restResults] = await Promise.all(
|
|
2325
|
+
delegateTo.map(
|
|
2326
|
+
(delegation) => runDelegate(delegation, setting, runResultCheckpoint, expertToRun, options)
|
|
2327
|
+
)
|
|
2328
|
+
);
|
|
2329
|
+
const allResults = [firstResult, ...restResults];
|
|
2330
|
+
const aggregatedUsage = allResults.reduce(
|
|
2331
|
+
(acc, result) => sumUsage(acc, result.deltaUsage),
|
|
2332
|
+
runResultCheckpoint.usage
|
|
2333
|
+
);
|
|
2334
|
+
const maxStepNumber = Math.max(...allResults.map((r) => r.stepNumber));
|
|
2335
|
+
const restToolResults = restResults.map((result) => ({
|
|
2336
|
+
id: result.toolCallId,
|
|
2337
|
+
skillName: `delegate/${result.expertKey}`,
|
|
2338
|
+
toolName: result.toolName,
|
|
2339
|
+
result: [{ type: "textPart", id: createId(), text: result.text }]
|
|
2340
|
+
}));
|
|
2341
|
+
const processedToolCallIds = new Set(remainingDelegations.map((d) => d.toolCallId));
|
|
2342
|
+
const remainingToolCalls = runResultCheckpoint.pendingToolCalls?.filter(
|
|
2343
|
+
(tc) => !processedToolCallIds.has(tc.id) && tc.id !== firstDelegation.toolCallId
|
|
2344
|
+
);
|
|
2345
|
+
setting = {
|
|
2346
|
+
...setting,
|
|
2347
|
+
expertKey: expertToRun.key,
|
|
2348
|
+
input: {
|
|
2349
|
+
interactiveToolCallResult: {
|
|
2350
|
+
toolCallId: firstResult.toolCallId,
|
|
2351
|
+
toolName: firstResult.toolName,
|
|
2352
|
+
skillName: `delegate/${firstResult.expertKey}`,
|
|
2353
|
+
text: firstResult.text
|
|
2354
|
+
}
|
|
2355
|
+
}
|
|
2356
|
+
};
|
|
2357
|
+
checkpoint = {
|
|
2358
|
+
...runResultCheckpoint,
|
|
2359
|
+
status: "stoppedByDelegate",
|
|
2360
|
+
delegateTo: void 0,
|
|
2361
|
+
stepNumber: maxStepNumber,
|
|
2362
|
+
usage: aggregatedUsage,
|
|
2363
|
+
pendingToolCalls: remainingToolCalls?.length ? remainingToolCalls : void 0,
|
|
2364
|
+
partialToolResults: [
|
|
2365
|
+
...runResultCheckpoint.partialToolResults ?? [],
|
|
2366
|
+
...restToolResults
|
|
2367
|
+
]
|
|
2368
|
+
};
|
|
2054
2369
|
break;
|
|
2055
2370
|
}
|
|
2056
2371
|
case "stoppedByExceededMaxSteps": {
|
|
2372
|
+
storeJob({ ...job, status: "stoppedByMaxSteps", finishedAt: Date.now() });
|
|
2057
2373
|
return runResultCheckpoint;
|
|
2058
2374
|
}
|
|
2059
2375
|
case "stoppedByError": {
|
|
2376
|
+
storeJob({ ...job, status: "stoppedByError", finishedAt: Date.now() });
|
|
2060
2377
|
return runResultCheckpoint;
|
|
2061
2378
|
}
|
|
2062
2379
|
default:
|
|
@@ -2073,10 +2390,65 @@ function getEventListener(options) {
|
|
|
2073
2390
|
listener(event);
|
|
2074
2391
|
};
|
|
2075
2392
|
}
|
|
2393
|
+
async function runDelegate(delegation, parentSetting, parentCheckpoint, parentExpert, options) {
|
|
2394
|
+
const { expert, toolCallId, toolName, query } = delegation;
|
|
2395
|
+
const delegateRunId = createId();
|
|
2396
|
+
const delegateSetting = {
|
|
2397
|
+
...parentSetting,
|
|
2398
|
+
runId: delegateRunId,
|
|
2399
|
+
expertKey: expert.key,
|
|
2400
|
+
input: { text: query }
|
|
2401
|
+
};
|
|
2402
|
+
const delegateCheckpoint = {
|
|
2403
|
+
id: createId(),
|
|
2404
|
+
jobId: parentSetting.jobId,
|
|
2405
|
+
runId: delegateRunId,
|
|
2406
|
+
status: "init",
|
|
2407
|
+
stepNumber: parentCheckpoint.stepNumber,
|
|
2408
|
+
messages: [],
|
|
2409
|
+
expert: {
|
|
2410
|
+
key: expert.key,
|
|
2411
|
+
name: expert.name,
|
|
2412
|
+
version: expert.version
|
|
2413
|
+
},
|
|
2414
|
+
delegatedBy: {
|
|
2415
|
+
expert: {
|
|
2416
|
+
key: parentExpert.key,
|
|
2417
|
+
name: parentExpert.name,
|
|
2418
|
+
version: parentExpert.version
|
|
2419
|
+
},
|
|
2420
|
+
toolCallId,
|
|
2421
|
+
toolName,
|
|
2422
|
+
checkpointId: parentCheckpoint.id
|
|
2423
|
+
},
|
|
2424
|
+
usage: createEmptyUsage(),
|
|
2425
|
+
contextWindow: parentCheckpoint.contextWindow
|
|
2426
|
+
};
|
|
2427
|
+
const resultCheckpoint = await run(
|
|
2428
|
+
{ setting: delegateSetting, checkpoint: delegateCheckpoint },
|
|
2429
|
+
{ ...options, returnOnDelegationComplete: true }
|
|
2430
|
+
);
|
|
2431
|
+
const lastMessage = resultCheckpoint.messages[resultCheckpoint.messages.length - 1];
|
|
2432
|
+
if (!lastMessage || lastMessage.type !== "expertMessage") {
|
|
2433
|
+
throw new Error("Delegation error: delegation result message is incorrect");
|
|
2434
|
+
}
|
|
2435
|
+
const textPart = lastMessage.contents.find((c) => c.type === "textPart");
|
|
2436
|
+
if (!textPart || textPart.type !== "textPart") {
|
|
2437
|
+
throw new Error("Delegation error: delegation result message does not contain text");
|
|
2438
|
+
}
|
|
2439
|
+
return {
|
|
2440
|
+
toolCallId,
|
|
2441
|
+
toolName,
|
|
2442
|
+
expertKey: expert.key,
|
|
2443
|
+
text: textPart.text,
|
|
2444
|
+
stepNumber: resultCheckpoint.stepNumber,
|
|
2445
|
+
deltaUsage: resultCheckpoint.usage
|
|
2446
|
+
};
|
|
2447
|
+
}
|
|
2076
2448
|
|
|
2077
2449
|
// src/index.ts
|
|
2078
2450
|
var runtimeVersion = package_default.version;
|
|
2079
2451
|
|
|
2080
|
-
export { StateMachineLogics, calculateContextWindowUsage, getContextWindow, getModel, defaultGetRunDir as getRunDir, run, runtimeStateMachine, runtimeVersion };
|
|
2452
|
+
export { StateMachineLogics, calculateContextWindowUsage, createInitialJob, getAllJobs, getAllRuns, getCheckpointDir, getCheckpointPath, getCheckpointsByJobId, getContextWindow, getEventContents, getEventsByRun, getJobDir, getJobsDir, getModel, defaultGetRunDir as getRunDir, retrieveJob, run, runtimeStateMachine, runtimeVersion, storeJob };
|
|
2081
2453
|
//# sourceMappingURL=index.js.map
|
|
2082
2454
|
//# sourceMappingURL=index.js.map
|