@perstack/runtime 0.0.48 → 0.0.50
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 +35 -0
- package/dist/src/index.d.ts +492 -3299
- package/dist/src/index.js +520 -458
- package/dist/src/index.js.map +1 -1
- package/package.json +4 -3
package/dist/src/index.js
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
import { createAmazonBedrock } from '@ai-sdk/amazon-bedrock';
|
|
2
2
|
import { createAnthropic } from '@ai-sdk/anthropic';
|
|
3
3
|
import { createAzure } from '@ai-sdk/azure';
|
|
4
|
+
import { createDeepSeek } from '@ai-sdk/deepseek';
|
|
4
5
|
import { createGoogleGenerativeAI } from '@ai-sdk/google';
|
|
5
6
|
import { createVertex } from '@ai-sdk/google-vertex';
|
|
6
7
|
import { createOpenAI } from '@ai-sdk/openai';
|
|
7
8
|
import { knownModels, stopRunByDelegate, stopRunByInteractiveTool, resolveThought, attemptCompletion, resolvePdfFile, resolveImageFile, resolveToolResult, stopRunByExceededMaxSteps, continueToNextStep, retry, completeRun, callDelegate, callInteractiveTool, callTool, startRun, startGeneration, finishToolCall, runParamsSchema, createRuntimeEvent, checkpointSchema } from '@perstack/core';
|
|
8
9
|
import { createOllama } from 'ollama-ai-provider-v2';
|
|
9
|
-
import { existsSync } from 'fs';
|
|
10
|
-
import { readFile, writeFile, mkdir, readdir } from 'fs/promises';
|
|
11
|
-
import path from 'path';
|
|
12
10
|
import { createId } from '@paralleldrive/cuid2';
|
|
11
|
+
import { readFile, readdir, mkdir, writeFile } from 'fs/promises';
|
|
12
|
+
import path from 'path';
|
|
13
13
|
import { setup, assign, createActor } from 'xstate';
|
|
14
|
-
import {
|
|
14
|
+
import { generateText, tool, jsonSchema } from 'ai';
|
|
15
15
|
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
16
16
|
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
|
|
17
17
|
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
18
18
|
import { McpError } from '@modelcontextprotocol/sdk/types.js';
|
|
19
|
-
import { generateText, tool, jsonSchema } from 'ai';
|
|
20
19
|
import { dedent } from 'ts-dedent';
|
|
20
|
+
import { ApiV1Client } from '@perstack/api-client/v1';
|
|
21
21
|
|
|
22
22
|
// package.json
|
|
23
23
|
var package_default = {
|
|
24
|
-
version: "0.0.
|
|
24
|
+
version: "0.0.50"};
|
|
25
25
|
function getModel(modelId, providerConfig) {
|
|
26
26
|
switch (providerConfig.providerName) {
|
|
27
27
|
case "anthropic": {
|
|
@@ -87,6 +87,14 @@ function getModel(modelId, providerConfig) {
|
|
|
87
87
|
});
|
|
88
88
|
return vertex(modelId);
|
|
89
89
|
}
|
|
90
|
+
case "deepseek": {
|
|
91
|
+
const deepseek = createDeepSeek({
|
|
92
|
+
apiKey: providerConfig.apiKey,
|
|
93
|
+
baseURL: providerConfig.baseUrl,
|
|
94
|
+
headers: providerConfig.headers
|
|
95
|
+
});
|
|
96
|
+
return deepseek(modelId);
|
|
97
|
+
}
|
|
90
98
|
}
|
|
91
99
|
}
|
|
92
100
|
function getContextWindow(providerName, modelId) {
|
|
@@ -96,8 +104,167 @@ function getContextWindow(providerName, modelId) {
|
|
|
96
104
|
function calculateContextWindowUsage(usage, contextWindow) {
|
|
97
105
|
return (usage.inputTokens + usage.cachedInputTokens + usage.outputTokens) / contextWindow;
|
|
98
106
|
}
|
|
107
|
+
|
|
108
|
+
// src/usage.ts
|
|
109
|
+
function createEmptyUsage() {
|
|
110
|
+
return {
|
|
111
|
+
inputTokens: 0,
|
|
112
|
+
outputTokens: 0,
|
|
113
|
+
reasoningTokens: 0,
|
|
114
|
+
totalTokens: 0,
|
|
115
|
+
cachedInputTokens: 0
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
function usageFromGenerateTextResult(result) {
|
|
119
|
+
return {
|
|
120
|
+
inputTokens: result.usage.inputTokens || 0,
|
|
121
|
+
outputTokens: result.usage.outputTokens || 0,
|
|
122
|
+
reasoningTokens: result.usage.reasoningTokens || 0,
|
|
123
|
+
totalTokens: result.usage.totalTokens || 0,
|
|
124
|
+
cachedInputTokens: result.usage.cachedInputTokens || 0
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function sumUsage(a, b) {
|
|
128
|
+
return {
|
|
129
|
+
inputTokens: a.inputTokens + b.inputTokens,
|
|
130
|
+
outputTokens: a.outputTokens + b.outputTokens,
|
|
131
|
+
reasoningTokens: a.reasoningTokens + b.reasoningTokens,
|
|
132
|
+
totalTokens: a.totalTokens + b.totalTokens,
|
|
133
|
+
cachedInputTokens: a.cachedInputTokens + b.cachedInputTokens
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// src/checkpoint-helpers.ts
|
|
138
|
+
function createInitialCheckpoint(checkpointId, params) {
|
|
139
|
+
return {
|
|
140
|
+
id: checkpointId,
|
|
141
|
+
runId: params.runId,
|
|
142
|
+
expert: {
|
|
143
|
+
key: params.expertKey,
|
|
144
|
+
name: params.expert.name,
|
|
145
|
+
version: params.expert.version
|
|
146
|
+
},
|
|
147
|
+
stepNumber: 1,
|
|
148
|
+
status: "init",
|
|
149
|
+
messages: [],
|
|
150
|
+
usage: createEmptyUsage(),
|
|
151
|
+
contextWindow: params.contextWindow,
|
|
152
|
+
contextWindowUsage: params.contextWindow ? 0 : void 0
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
function createNextStepCheckpoint(checkpointId, checkpoint) {
|
|
156
|
+
return {
|
|
157
|
+
...checkpoint,
|
|
158
|
+
id: checkpointId,
|
|
159
|
+
stepNumber: checkpoint.stepNumber + 1
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
function buildDelegationReturnState(currentSetting, resultCheckpoint, parentCheckpoint) {
|
|
163
|
+
const { messages, delegatedBy } = resultCheckpoint;
|
|
164
|
+
if (!delegatedBy) {
|
|
165
|
+
throw new Error("delegatedBy is required for buildDelegationReturnState");
|
|
166
|
+
}
|
|
167
|
+
const delegateResultMessage = messages[messages.length - 1];
|
|
168
|
+
if (!delegateResultMessage || delegateResultMessage.type !== "expertMessage") {
|
|
169
|
+
throw new Error("Delegation error: delegation result message is incorrect");
|
|
170
|
+
}
|
|
171
|
+
const delegateText = delegateResultMessage.contents.find((content) => content.type === "textPart");
|
|
172
|
+
if (!delegateText) {
|
|
173
|
+
throw new Error("Delegation error: delegation result message does not contain a text");
|
|
174
|
+
}
|
|
175
|
+
const { expert, toolCallId, toolName } = delegatedBy;
|
|
176
|
+
return {
|
|
177
|
+
setting: {
|
|
178
|
+
...currentSetting,
|
|
179
|
+
expertKey: expert.key,
|
|
180
|
+
input: {
|
|
181
|
+
interactiveToolCallResult: {
|
|
182
|
+
toolCallId,
|
|
183
|
+
toolName,
|
|
184
|
+
text: delegateText.text
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
checkpoint: {
|
|
189
|
+
...parentCheckpoint,
|
|
190
|
+
stepNumber: resultCheckpoint.stepNumber,
|
|
191
|
+
usage: resultCheckpoint.usage
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
function buildDelegateToState(currentSetting, resultCheckpoint, currentExpert) {
|
|
196
|
+
const { delegateTo } = resultCheckpoint;
|
|
197
|
+
if (!delegateTo) {
|
|
198
|
+
throw new Error("delegateTo is required for buildDelegateToState");
|
|
199
|
+
}
|
|
200
|
+
const { expert, toolCallId, toolName, query } = delegateTo;
|
|
201
|
+
return {
|
|
202
|
+
setting: {
|
|
203
|
+
...currentSetting,
|
|
204
|
+
expertKey: expert.key,
|
|
205
|
+
input: {
|
|
206
|
+
text: query
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
checkpoint: {
|
|
210
|
+
...resultCheckpoint,
|
|
211
|
+
status: "init",
|
|
212
|
+
messages: [],
|
|
213
|
+
expert: {
|
|
214
|
+
key: expert.key,
|
|
215
|
+
name: expert.name,
|
|
216
|
+
version: expert.version
|
|
217
|
+
},
|
|
218
|
+
delegatedBy: {
|
|
219
|
+
expert: {
|
|
220
|
+
key: currentExpert.key,
|
|
221
|
+
name: currentExpert.name,
|
|
222
|
+
version: currentExpert.version
|
|
223
|
+
},
|
|
224
|
+
toolCallId,
|
|
225
|
+
toolName,
|
|
226
|
+
checkpointId: resultCheckpoint.id
|
|
227
|
+
},
|
|
228
|
+
usage: resultCheckpoint.usage
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
async function createDefaultFileSystem() {
|
|
233
|
+
const fs = await import('fs');
|
|
234
|
+
const fsPromises = await import('fs/promises');
|
|
235
|
+
return {
|
|
236
|
+
existsSync: fs.existsSync,
|
|
237
|
+
mkdir: async (p, options) => {
|
|
238
|
+
await fsPromises.mkdir(p, options);
|
|
239
|
+
},
|
|
240
|
+
readFile: fsPromises.readFile,
|
|
241
|
+
writeFile: fsPromises.writeFile
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
function defaultGetRunDir(runId) {
|
|
245
|
+
return `${process.cwd()}/perstack/runs/${runId}`;
|
|
246
|
+
}
|
|
247
|
+
async function storeRunSetting(setting, fs, getRunDir = defaultGetRunDir) {
|
|
248
|
+
const fileSystem = fs ?? await createDefaultFileSystem();
|
|
249
|
+
const runDir = getRunDir(setting.runId);
|
|
250
|
+
if (fileSystem.existsSync(runDir)) {
|
|
251
|
+
const runSettingPath = path.resolve(runDir, "run-setting.json");
|
|
252
|
+
const runSetting = JSON.parse(await fileSystem.readFile(runSettingPath, "utf-8"));
|
|
253
|
+
runSetting.updatedAt = Date.now();
|
|
254
|
+
await fileSystem.writeFile(runSettingPath, JSON.stringify(runSetting), "utf-8");
|
|
255
|
+
} else {
|
|
256
|
+
await fileSystem.mkdir(runDir, { recursive: true });
|
|
257
|
+
await fileSystem.writeFile(
|
|
258
|
+
path.resolve(runDir, "run-setting.json"),
|
|
259
|
+
JSON.stringify(setting),
|
|
260
|
+
"utf-8"
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// src/default-store.ts
|
|
99
266
|
async function defaultRetrieveCheckpoint(runId, checkpointId) {
|
|
100
|
-
const runDir =
|
|
267
|
+
const runDir = defaultGetRunDir(runId);
|
|
101
268
|
const checkpointFiles = await readdir(runDir, { withFileTypes: true }).then(
|
|
102
269
|
(files) => files.filter((file) => file.isFile() && file.name.startsWith("checkpoint-"))
|
|
103
270
|
);
|
|
@@ -111,14 +278,14 @@ async function defaultRetrieveCheckpoint(runId, checkpointId) {
|
|
|
111
278
|
}
|
|
112
279
|
async function defaultStoreCheckpoint(checkpoint, timestamp) {
|
|
113
280
|
const { id, runId, stepNumber } = checkpoint;
|
|
114
|
-
const runDir =
|
|
281
|
+
const runDir = defaultGetRunDir(runId);
|
|
115
282
|
const checkpointPath = `${runDir}/checkpoint-${timestamp}-${stepNumber}-${id}.json`;
|
|
116
283
|
await mkdir(runDir, { recursive: true });
|
|
117
284
|
await writeFile(checkpointPath, JSON.stringify(checkpoint));
|
|
118
285
|
}
|
|
119
286
|
async function defaultStoreEvent(event) {
|
|
120
287
|
const { timestamp, runId, stepNumber, type } = event;
|
|
121
|
-
const runDir =
|
|
288
|
+
const runDir = defaultGetRunDir(runId);
|
|
122
289
|
const eventPath = `${runDir}/event-${timestamp}-${stepNumber}-${type}.json`;
|
|
123
290
|
await mkdir(runDir, { recursive: true });
|
|
124
291
|
await writeFile(eventPath, JSON.stringify(event));
|
|
@@ -138,76 +305,20 @@ var RunEventEmitter = class {
|
|
|
138
305
|
}
|
|
139
306
|
}
|
|
140
307
|
};
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}
|
|
145
|
-
const client = new ApiV1Client({
|
|
146
|
-
baseUrl: clientOptions.perstackApiBaseUrl,
|
|
147
|
-
apiKey: clientOptions.perstackApiKey
|
|
148
|
-
});
|
|
149
|
-
const { expert } = await client.registry.experts.get({ expertKey });
|
|
150
|
-
experts[expertKey] = toRuntimeExpert(expert);
|
|
151
|
-
return experts[expertKey];
|
|
152
|
-
}
|
|
153
|
-
function toRuntimeExpert(expert) {
|
|
154
|
-
const skills = Object.fromEntries(
|
|
155
|
-
Object.entries(expert.skills).map(([name, skill]) => {
|
|
156
|
-
switch (skill.type) {
|
|
157
|
-
case "mcpStdioSkill":
|
|
158
|
-
return [name, { ...skill, name }];
|
|
159
|
-
case "mcpSseSkill":
|
|
160
|
-
return [name, { ...skill, name }];
|
|
161
|
-
case "interactiveSkill":
|
|
162
|
-
return [name, { ...skill, name }];
|
|
163
|
-
default: {
|
|
164
|
-
throw new Error(`Unknown skill type: ${skill.type}`);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
})
|
|
168
|
-
);
|
|
169
|
-
return { ...expert, skills };
|
|
170
|
-
}
|
|
171
|
-
var SkillManager = class {
|
|
308
|
+
|
|
309
|
+
// src/skill-manager/base.ts
|
|
310
|
+
var BaseSkillManager = class {
|
|
172
311
|
_toolDefinitions = [];
|
|
173
312
|
_initialized = false;
|
|
174
313
|
_initializing;
|
|
175
|
-
name;
|
|
176
|
-
type;
|
|
177
|
-
lazyInit;
|
|
178
314
|
skill;
|
|
179
315
|
interactiveSkill;
|
|
180
316
|
expert;
|
|
181
|
-
_mcpClient;
|
|
182
|
-
_params;
|
|
183
|
-
_env;
|
|
184
317
|
_runId;
|
|
185
318
|
_eventListener;
|
|
186
|
-
constructor(
|
|
187
|
-
this._params = params;
|
|
319
|
+
constructor(runId, eventListener) {
|
|
188
320
|
this._runId = runId;
|
|
189
321
|
this._eventListener = eventListener;
|
|
190
|
-
this.type = params.type;
|
|
191
|
-
switch (params.type) {
|
|
192
|
-
case "mcp":
|
|
193
|
-
this.name = params.skill.name;
|
|
194
|
-
this.skill = params.skill;
|
|
195
|
-
this._env = params.env;
|
|
196
|
-
this.lazyInit = this.skill.type === "mcpStdioSkill" && this.skill.lazyInit && this.skill.name !== "@perstack/base";
|
|
197
|
-
break;
|
|
198
|
-
case "interactive":
|
|
199
|
-
this.name = params.interactiveSkill.name;
|
|
200
|
-
this.interactiveSkill = params.interactiveSkill;
|
|
201
|
-
this._env = {};
|
|
202
|
-
this.lazyInit = false;
|
|
203
|
-
break;
|
|
204
|
-
case "delegate":
|
|
205
|
-
this.name = params.expert.name;
|
|
206
|
-
this.expert = params.expert;
|
|
207
|
-
this._env = {};
|
|
208
|
-
this.lazyInit = false;
|
|
209
|
-
break;
|
|
210
|
-
}
|
|
211
322
|
}
|
|
212
323
|
async init() {
|
|
213
324
|
if (this._initialized) {
|
|
@@ -232,75 +343,146 @@ var SkillManager = class {
|
|
|
232
343
|
return this._initialized;
|
|
233
344
|
}
|
|
234
345
|
async _performInit() {
|
|
235
|
-
|
|
236
|
-
case "mcp": {
|
|
237
|
-
await this._initMcpSkill(this._params);
|
|
238
|
-
break;
|
|
239
|
-
}
|
|
240
|
-
case "interactive": {
|
|
241
|
-
await this._initInteractiveSkill(this._params);
|
|
242
|
-
break;
|
|
243
|
-
}
|
|
244
|
-
case "delegate": {
|
|
245
|
-
await this._initDelegate(this._params);
|
|
246
|
-
break;
|
|
247
|
-
}
|
|
248
|
-
}
|
|
346
|
+
await this._doInit();
|
|
249
347
|
this._initialized = true;
|
|
250
348
|
this._initializing = void 0;
|
|
251
349
|
}
|
|
252
|
-
async
|
|
253
|
-
if (this.isInitialized()) {
|
|
254
|
-
throw new Error(`Skill ${
|
|
350
|
+
async getToolDefinitions() {
|
|
351
|
+
if (!this.isInitialized() && !this.lazyInit) {
|
|
352
|
+
throw new Error(`Skill ${this.name} is not initialized`);
|
|
353
|
+
}
|
|
354
|
+
if (!this.isInitialized() && this.lazyInit) {
|
|
355
|
+
return [];
|
|
255
356
|
}
|
|
357
|
+
return this._filterTools(this._toolDefinitions);
|
|
358
|
+
}
|
|
359
|
+
_filterTools(tools) {
|
|
360
|
+
return tools;
|
|
361
|
+
}
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
// src/skill-manager/delegate.ts
|
|
365
|
+
var DelegateSkillManager = class extends BaseSkillManager {
|
|
366
|
+
name;
|
|
367
|
+
type = "delegate";
|
|
368
|
+
lazyInit = false;
|
|
369
|
+
expert;
|
|
370
|
+
constructor(expert, runId, eventListener) {
|
|
371
|
+
super(runId, eventListener);
|
|
372
|
+
this.name = expert.name;
|
|
373
|
+
this.expert = expert;
|
|
374
|
+
}
|
|
375
|
+
async _doInit() {
|
|
376
|
+
this._toolDefinitions = [
|
|
377
|
+
{
|
|
378
|
+
skillName: this.expert.name,
|
|
379
|
+
name: this.expert.name.split("/").pop() ?? this.expert.name,
|
|
380
|
+
description: this.expert.description,
|
|
381
|
+
inputSchema: {
|
|
382
|
+
type: "object",
|
|
383
|
+
properties: { query: { type: "string" } },
|
|
384
|
+
required: ["query"]
|
|
385
|
+
},
|
|
386
|
+
interactive: false
|
|
387
|
+
}
|
|
388
|
+
];
|
|
389
|
+
}
|
|
390
|
+
async close() {
|
|
391
|
+
}
|
|
392
|
+
async callTool(_toolName, _input) {
|
|
393
|
+
return [];
|
|
394
|
+
}
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
// src/skill-manager/interactive.ts
|
|
398
|
+
var InteractiveSkillManager = class extends BaseSkillManager {
|
|
399
|
+
name;
|
|
400
|
+
type = "interactive";
|
|
401
|
+
lazyInit = false;
|
|
402
|
+
interactiveSkill;
|
|
403
|
+
constructor(interactiveSkill, runId, eventListener) {
|
|
404
|
+
super(runId, eventListener);
|
|
405
|
+
this.name = interactiveSkill.name;
|
|
406
|
+
this.interactiveSkill = interactiveSkill;
|
|
407
|
+
}
|
|
408
|
+
async _doInit() {
|
|
409
|
+
this._toolDefinitions = Object.values(this.interactiveSkill.tools).map((tool2) => ({
|
|
410
|
+
skillName: this.interactiveSkill.name,
|
|
411
|
+
name: tool2.name,
|
|
412
|
+
description: tool2.description,
|
|
413
|
+
inputSchema: JSON.parse(tool2.inputJsonSchema),
|
|
414
|
+
interactive: true
|
|
415
|
+
}));
|
|
416
|
+
}
|
|
417
|
+
async close() {
|
|
418
|
+
}
|
|
419
|
+
async callTool(_toolName, _input) {
|
|
420
|
+
return [];
|
|
421
|
+
}
|
|
422
|
+
};
|
|
423
|
+
var McpSkillManager = class extends BaseSkillManager {
|
|
424
|
+
name;
|
|
425
|
+
type = "mcp";
|
|
426
|
+
lazyInit;
|
|
427
|
+
skill;
|
|
428
|
+
_mcpClient;
|
|
429
|
+
_env;
|
|
430
|
+
constructor(skill, env, runId, eventListener) {
|
|
431
|
+
super(runId, eventListener);
|
|
432
|
+
this.name = skill.name;
|
|
433
|
+
this.skill = skill;
|
|
434
|
+
this._env = env;
|
|
435
|
+
this.lazyInit = skill.type === "mcpStdioSkill" && skill.lazyInit && skill.name !== "@perstack/base";
|
|
436
|
+
}
|
|
437
|
+
async _doInit() {
|
|
256
438
|
this._mcpClient = new Client({
|
|
257
|
-
name: `${
|
|
439
|
+
name: `${this.skill.name}-mcp-client`,
|
|
258
440
|
version: "1.0.0"
|
|
259
441
|
});
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
}
|
|
265
|
-
const env = {};
|
|
266
|
-
const { requiredEnv } = params.skill;
|
|
267
|
-
for (const envName of requiredEnv) {
|
|
268
|
-
if (!this._env[envName]) {
|
|
269
|
-
throw new Error(`Skill ${params.skill.name} requires environment variable ${envName}`);
|
|
270
|
-
}
|
|
271
|
-
env[envName] = this._env[envName];
|
|
272
|
-
}
|
|
273
|
-
const { command, args } = this._getCommandArgs(params.skill);
|
|
274
|
-
const transport = new StdioClientTransport({ command, args, env, stderr: "ignore" });
|
|
275
|
-
await this._mcpClient.connect(transport);
|
|
276
|
-
if (this._eventListener) {
|
|
277
|
-
const serverInfo = this._mcpClient.getServerVersion();
|
|
278
|
-
const event = createRuntimeEvent("skillConnected", this._runId, {
|
|
279
|
-
skillName: params.skill.name,
|
|
280
|
-
serverInfo: serverInfo ? { name: serverInfo.name, version: serverInfo.version } : void 0
|
|
281
|
-
});
|
|
282
|
-
this._eventListener(event);
|
|
283
|
-
}
|
|
284
|
-
break;
|
|
285
|
-
}
|
|
286
|
-
case "mcpSseSkill": {
|
|
287
|
-
if (!params.skill.endpoint) {
|
|
288
|
-
throw new Error(`Skill ${params.skill.name} has no endpoint`);
|
|
289
|
-
}
|
|
290
|
-
const transport = new SSEClientTransport(new URL(params.skill.endpoint));
|
|
291
|
-
await this._mcpClient.connect(transport);
|
|
292
|
-
break;
|
|
293
|
-
}
|
|
442
|
+
if (this.skill.type === "mcpStdioSkill") {
|
|
443
|
+
await this._initStdio(this.skill);
|
|
444
|
+
} else {
|
|
445
|
+
await this._initSse(this.skill);
|
|
294
446
|
}
|
|
295
447
|
const { tools } = await this._mcpClient.listTools();
|
|
296
448
|
this._toolDefinitions = tools.map((tool2) => ({
|
|
297
|
-
skillName:
|
|
449
|
+
skillName: this.skill.name,
|
|
298
450
|
name: tool2.name,
|
|
299
451
|
description: tool2.description,
|
|
300
452
|
inputSchema: tool2.inputSchema,
|
|
301
453
|
interactive: false
|
|
302
454
|
}));
|
|
303
455
|
}
|
|
456
|
+
async _initStdio(skill) {
|
|
457
|
+
if (!skill.command) {
|
|
458
|
+
throw new Error(`Skill ${skill.name} has no command`);
|
|
459
|
+
}
|
|
460
|
+
const env = {};
|
|
461
|
+
for (const envName of skill.requiredEnv) {
|
|
462
|
+
if (!this._env[envName]) {
|
|
463
|
+
throw new Error(`Skill ${skill.name} requires environment variable ${envName}`);
|
|
464
|
+
}
|
|
465
|
+
env[envName] = this._env[envName];
|
|
466
|
+
}
|
|
467
|
+
const { command, args } = this._getCommandArgs(skill);
|
|
468
|
+
const transport = new StdioClientTransport({ command, args, env, stderr: "ignore" });
|
|
469
|
+
await this._mcpClient.connect(transport);
|
|
470
|
+
if (this._eventListener) {
|
|
471
|
+
const serverInfo = this._mcpClient.getServerVersion();
|
|
472
|
+
const event = createRuntimeEvent("skillConnected", this._runId, {
|
|
473
|
+
skillName: skill.name,
|
|
474
|
+
serverInfo: serverInfo ? { name: serverInfo.name, version: serverInfo.version } : void 0
|
|
475
|
+
});
|
|
476
|
+
this._eventListener(event);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
async _initSse(skill) {
|
|
480
|
+
if (!skill.endpoint) {
|
|
481
|
+
throw new Error(`Skill ${skill.name} has no endpoint`);
|
|
482
|
+
}
|
|
483
|
+
const transport = new SSEClientTransport(new URL(skill.endpoint));
|
|
484
|
+
await this._mcpClient.connect(transport);
|
|
485
|
+
}
|
|
304
486
|
_getCommandArgs(skill) {
|
|
305
487
|
const { name, command, packageName, args } = skill;
|
|
306
488
|
if (!packageName && (!args || args.length === 0)) {
|
|
@@ -317,36 +499,6 @@ var SkillManager = class {
|
|
|
317
499
|
}
|
|
318
500
|
return { command, args: newArgs };
|
|
319
501
|
}
|
|
320
|
-
async _initInteractiveSkill(params) {
|
|
321
|
-
if (this.isInitialized()) {
|
|
322
|
-
throw new Error(`Skill ${params.interactiveSkill.name} is already initialized`);
|
|
323
|
-
}
|
|
324
|
-
this._toolDefinitions = Object.values(params.interactiveSkill.tools).map((tool2) => ({
|
|
325
|
-
skillName: params.interactiveSkill.name,
|
|
326
|
-
name: tool2.name,
|
|
327
|
-
description: tool2.description,
|
|
328
|
-
inputSchema: JSON.parse(tool2.inputJsonSchema),
|
|
329
|
-
interactive: true
|
|
330
|
-
}));
|
|
331
|
-
}
|
|
332
|
-
async _initDelegate(params) {
|
|
333
|
-
if (this.isInitialized()) {
|
|
334
|
-
throw new Error(`Skill ${params.expert.name} is already initialized`);
|
|
335
|
-
}
|
|
336
|
-
this._toolDefinitions = [
|
|
337
|
-
{
|
|
338
|
-
skillName: params.expert.name,
|
|
339
|
-
name: params.expert.name.split("/").pop() ?? params.expert.name,
|
|
340
|
-
description: params.expert.description,
|
|
341
|
-
inputSchema: {
|
|
342
|
-
type: "object",
|
|
343
|
-
properties: { query: { type: "string" } },
|
|
344
|
-
required: ["query"]
|
|
345
|
-
},
|
|
346
|
-
interactive: false
|
|
347
|
-
}
|
|
348
|
-
];
|
|
349
|
-
}
|
|
350
502
|
async close() {
|
|
351
503
|
if (this._mcpClient) {
|
|
352
504
|
await this._mcpClient.close();
|
|
@@ -358,42 +510,23 @@ var SkillManager = class {
|
|
|
358
510
|
}
|
|
359
511
|
}
|
|
360
512
|
}
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
if (!this.isInitialized() && this.lazyInit) {
|
|
366
|
-
return [];
|
|
367
|
-
}
|
|
368
|
-
if (this._params.type === "mcp") {
|
|
369
|
-
const omit = this._params.skill.omit ?? [];
|
|
370
|
-
const pick = this._params.skill.pick ?? [];
|
|
371
|
-
return this._toolDefinitions.filter((tool2) => omit.length > 0 ? !omit.includes(tool2.name) : true).filter((tool2) => pick.length > 0 ? pick.includes(tool2.name) : true);
|
|
372
|
-
}
|
|
373
|
-
return this._toolDefinitions;
|
|
513
|
+
_filterTools(tools) {
|
|
514
|
+
const omit = this.skill.omit ?? [];
|
|
515
|
+
const pick = this.skill.pick ?? [];
|
|
516
|
+
return tools.filter((tool2) => omit.length > 0 ? !omit.includes(tool2.name) : true).filter((tool2) => pick.length > 0 ? pick.includes(tool2.name) : true);
|
|
374
517
|
}
|
|
375
518
|
async callTool(toolName, input) {
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
} catch (error) {
|
|
388
|
-
return this._handleToolError(error, toolName);
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
case "interactive": {
|
|
392
|
-
return [];
|
|
393
|
-
}
|
|
394
|
-
case "delegate": {
|
|
395
|
-
return [];
|
|
396
|
-
}
|
|
519
|
+
if (!this.isInitialized() || !this._mcpClient) {
|
|
520
|
+
throw new Error(`${this.name} is not initialized`);
|
|
521
|
+
}
|
|
522
|
+
try {
|
|
523
|
+
const result = await this._mcpClient.callTool({
|
|
524
|
+
name: toolName,
|
|
525
|
+
arguments: input
|
|
526
|
+
});
|
|
527
|
+
return this._convertToolResult(result, toolName, input);
|
|
528
|
+
} catch (error) {
|
|
529
|
+
return this._handleToolError(error, toolName);
|
|
397
530
|
}
|
|
398
531
|
}
|
|
399
532
|
_handleToolError(error, toolName) {
|
|
@@ -462,79 +595,68 @@ var SkillManager = class {
|
|
|
462
595
|
throw new Error(`Unsupported resource type: ${JSON.stringify(resource)}`);
|
|
463
596
|
}
|
|
464
597
|
};
|
|
598
|
+
|
|
599
|
+
// src/skill-manager/helpers.ts
|
|
600
|
+
async function initSkillManagersWithCleanup(managers, allManagers) {
|
|
601
|
+
const results = await Promise.allSettled(managers.map((m) => m.init()));
|
|
602
|
+
const firstRejected = results.find((r) => r.status === "rejected");
|
|
603
|
+
if (firstRejected) {
|
|
604
|
+
await Promise.all(allManagers.map((m) => m.close().catch(() => {
|
|
605
|
+
})));
|
|
606
|
+
throw firstRejected.reason;
|
|
607
|
+
}
|
|
608
|
+
}
|
|
465
609
|
async function getSkillManagers(expert, experts, setting, eventListener) {
|
|
466
610
|
const { perstackBaseSkillCommand, env, runId } = setting;
|
|
467
|
-
const skillManagers = {};
|
|
468
611
|
const { skills } = expert;
|
|
469
612
|
if (!skills["@perstack/base"]) {
|
|
470
613
|
throw new Error("Base skill is not defined");
|
|
471
614
|
}
|
|
472
|
-
const
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
skill.args = overrideArgs;
|
|
485
|
-
skill.lazyInit = false;
|
|
615
|
+
const allManagers = [];
|
|
616
|
+
const mcpSkills = Object.values(skills).filter(
|
|
617
|
+
(skill) => skill.type === "mcpStdioSkill" || skill.type === "mcpSseSkill"
|
|
618
|
+
);
|
|
619
|
+
for (const skill of mcpSkills) {
|
|
620
|
+
if (perstackBaseSkillCommand && skill.type === "mcpStdioSkill") {
|
|
621
|
+
const matchesBaseByPackage = skill.command === "npx" && skill.packageName === "@perstack/base";
|
|
622
|
+
const matchesBaseByArgs = skill.command === "npx" && Array.isArray(skill.args) && skill.args.includes("@perstack/base");
|
|
623
|
+
if (matchesBaseByPackage || matchesBaseByArgs) {
|
|
624
|
+
const [overrideCommand, ...overrideArgs] = perstackBaseSkillCommand;
|
|
625
|
+
if (!overrideCommand) {
|
|
626
|
+
throw new Error("perstackBaseSkillCommand must have at least one element");
|
|
486
627
|
}
|
|
628
|
+
skill.command = overrideCommand;
|
|
629
|
+
skill.packageName = void 0;
|
|
630
|
+
skill.args = overrideArgs;
|
|
631
|
+
skill.lazyInit = false;
|
|
487
632
|
}
|
|
488
|
-
|
|
489
|
-
{
|
|
490
|
-
type: "mcp",
|
|
491
|
-
skill,
|
|
492
|
-
env
|
|
493
|
-
},
|
|
494
|
-
runId,
|
|
495
|
-
eventListener
|
|
496
|
-
);
|
|
497
|
-
await skillManager.init();
|
|
498
|
-
return skillManager;
|
|
499
|
-
})
|
|
500
|
-
);
|
|
501
|
-
const interactiveSkillManagers = await Promise.all(
|
|
502
|
-
Object.values(skills).filter((skill) => skill.type === "interactiveSkill").map(async (interactiveSkill) => {
|
|
503
|
-
const skillManager = new SkillManager(
|
|
504
|
-
{
|
|
505
|
-
type: "interactive",
|
|
506
|
-
interactiveSkill
|
|
507
|
-
},
|
|
508
|
-
runId,
|
|
509
|
-
eventListener
|
|
510
|
-
);
|
|
511
|
-
await skillManager.init();
|
|
512
|
-
return skillManager;
|
|
513
|
-
})
|
|
514
|
-
);
|
|
515
|
-
const delegateSkillManagers = await Promise.all(
|
|
516
|
-
expert.delegates.map(async (delegateExpertName) => {
|
|
517
|
-
const delegate = experts[delegateExpertName];
|
|
518
|
-
const skillManager = new SkillManager(
|
|
519
|
-
{
|
|
520
|
-
type: "delegate",
|
|
521
|
-
expert: delegate
|
|
522
|
-
},
|
|
523
|
-
runId,
|
|
524
|
-
eventListener
|
|
525
|
-
);
|
|
526
|
-
await skillManager.init();
|
|
527
|
-
return skillManager;
|
|
528
|
-
})
|
|
529
|
-
);
|
|
530
|
-
for (const skillManager of mcpSkillManagers) {
|
|
531
|
-
skillManagers[skillManager.name] = skillManager;
|
|
532
|
-
}
|
|
533
|
-
for (const skillManager of interactiveSkillManagers) {
|
|
534
|
-
skillManagers[skillManager.name] = skillManager;
|
|
633
|
+
}
|
|
535
634
|
}
|
|
536
|
-
|
|
537
|
-
|
|
635
|
+
const mcpSkillManagers = mcpSkills.map((skill) => {
|
|
636
|
+
const manager = new McpSkillManager(skill, env, runId, eventListener);
|
|
637
|
+
allManagers.push(manager);
|
|
638
|
+
return manager;
|
|
639
|
+
});
|
|
640
|
+
await initSkillManagersWithCleanup(mcpSkillManagers, allManagers);
|
|
641
|
+
const interactiveSkills = Object.values(skills).filter(
|
|
642
|
+
(skill) => skill.type === "interactiveSkill"
|
|
643
|
+
);
|
|
644
|
+
const interactiveSkillManagers = interactiveSkills.map((interactiveSkill) => {
|
|
645
|
+
const manager = new InteractiveSkillManager(interactiveSkill, runId, eventListener);
|
|
646
|
+
allManagers.push(manager);
|
|
647
|
+
return manager;
|
|
648
|
+
});
|
|
649
|
+
await initSkillManagersWithCleanup(interactiveSkillManagers, allManagers);
|
|
650
|
+
const delegateSkillManagers = expert.delegates.map((delegateExpertName) => {
|
|
651
|
+
const delegate = experts[delegateExpertName];
|
|
652
|
+
const manager = new DelegateSkillManager(delegate, runId, eventListener);
|
|
653
|
+
allManagers.push(manager);
|
|
654
|
+
return manager;
|
|
655
|
+
});
|
|
656
|
+
await initSkillManagersWithCleanup(delegateSkillManagers, allManagers);
|
|
657
|
+
const skillManagers = {};
|
|
658
|
+
for (const manager of allManagers) {
|
|
659
|
+
skillManagers[manager.name] = manager;
|
|
538
660
|
}
|
|
539
661
|
return skillManagers;
|
|
540
662
|
}
|
|
@@ -603,7 +725,7 @@ async function callingDelegateLogic({
|
|
|
603
725
|
},
|
|
604
726
|
step: {
|
|
605
727
|
...step,
|
|
606
|
-
finishedAt:
|
|
728
|
+
finishedAt: Date.now()
|
|
607
729
|
}
|
|
608
730
|
});
|
|
609
731
|
}
|
|
@@ -860,54 +982,23 @@ function toolCallPartToCoreToolCallPart(part) {
|
|
|
860
982
|
};
|
|
861
983
|
}
|
|
862
984
|
function toolResultPartToCoreToolResultPart(part) {
|
|
863
|
-
const
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
]
|
|
876
|
-
};
|
|
985
|
+
const { contents } = part;
|
|
986
|
+
if (contents.length === 1 && contents[0].type === "textPart") {
|
|
987
|
+
return {
|
|
988
|
+
type: "tool-result",
|
|
989
|
+
toolCallId: part.toolCallId,
|
|
990
|
+
toolName: part.toolName,
|
|
991
|
+
output: { type: "text", value: contents[0].text }
|
|
992
|
+
};
|
|
993
|
+
}
|
|
994
|
+
const contentValue = contents.map(
|
|
995
|
+
(content) => content.type === "textPart" ? { type: "text", text: content.text } : { type: "media", data: content.encodedData, mediaType: content.mimeType }
|
|
996
|
+
);
|
|
877
997
|
return {
|
|
878
998
|
type: "tool-result",
|
|
879
999
|
toolCallId: part.toolCallId,
|
|
880
1000
|
toolName: part.toolName,
|
|
881
|
-
output
|
|
882
|
-
};
|
|
883
|
-
}
|
|
884
|
-
|
|
885
|
-
// src/usage.ts
|
|
886
|
-
function createEmptyUsage() {
|
|
887
|
-
return {
|
|
888
|
-
inputTokens: 0,
|
|
889
|
-
outputTokens: 0,
|
|
890
|
-
reasoningTokens: 0,
|
|
891
|
-
totalTokens: 0,
|
|
892
|
-
cachedInputTokens: 0
|
|
893
|
-
};
|
|
894
|
-
}
|
|
895
|
-
function usageFromGenerateTextResult(result) {
|
|
896
|
-
return {
|
|
897
|
-
inputTokens: result.usage.inputTokens || 0,
|
|
898
|
-
outputTokens: result.usage.outputTokens || 0,
|
|
899
|
-
reasoningTokens: result.usage.reasoningTokens || 0,
|
|
900
|
-
totalTokens: result.usage.totalTokens || 0,
|
|
901
|
-
cachedInputTokens: result.usage.cachedInputTokens || 0
|
|
902
|
-
};
|
|
903
|
-
}
|
|
904
|
-
function sumUsage(a, b) {
|
|
905
|
-
return {
|
|
906
|
-
inputTokens: a.inputTokens + b.inputTokens,
|
|
907
|
-
outputTokens: a.outputTokens + b.outputTokens,
|
|
908
|
-
reasoningTokens: a.reasoningTokens + b.reasoningTokens,
|
|
909
|
-
totalTokens: a.totalTokens + b.totalTokens,
|
|
910
|
-
cachedInputTokens: a.cachedInputTokens + b.cachedInputTokens
|
|
1001
|
+
output: { type: "content", value: contentValue }
|
|
911
1002
|
};
|
|
912
1003
|
}
|
|
913
1004
|
|
|
@@ -995,7 +1086,6 @@ async function generatingToolCallLogic({
|
|
|
995
1086
|
});
|
|
996
1087
|
} catch (error) {
|
|
997
1088
|
if (error instanceof Error) {
|
|
998
|
-
console.error(error);
|
|
999
1089
|
const reason = JSON.stringify({ error: error.name, message: error.message });
|
|
1000
1090
|
return retry(setting, checkpoint, {
|
|
1001
1091
|
reason,
|
|
@@ -1729,6 +1819,114 @@ var StateMachineLogics = {
|
|
|
1729
1819
|
FinishingStep: finishingStepLogic
|
|
1730
1820
|
};
|
|
1731
1821
|
|
|
1822
|
+
// src/execute-state-machine.ts
|
|
1823
|
+
async function executeStateMachine(params) {
|
|
1824
|
+
const {
|
|
1825
|
+
setting,
|
|
1826
|
+
initialCheckpoint,
|
|
1827
|
+
eventListener,
|
|
1828
|
+
skillManagers,
|
|
1829
|
+
eventEmitter,
|
|
1830
|
+
storeCheckpoint,
|
|
1831
|
+
shouldContinueRun
|
|
1832
|
+
} = params;
|
|
1833
|
+
const runActor = createActor(runtimeStateMachine, {
|
|
1834
|
+
input: {
|
|
1835
|
+
setting,
|
|
1836
|
+
initialCheckpoint,
|
|
1837
|
+
eventListener,
|
|
1838
|
+
skillManagers
|
|
1839
|
+
}
|
|
1840
|
+
});
|
|
1841
|
+
return new Promise((resolve, reject) => {
|
|
1842
|
+
runActor.subscribe(async (runState) => {
|
|
1843
|
+
try {
|
|
1844
|
+
if (runState.value === "Stopped") {
|
|
1845
|
+
const { checkpoint, skillManagers: skillManagers2 } = runState.context;
|
|
1846
|
+
if (!checkpoint) {
|
|
1847
|
+
throw new Error("Checkpoint is undefined");
|
|
1848
|
+
}
|
|
1849
|
+
await closeSkillManagers(skillManagers2);
|
|
1850
|
+
resolve(checkpoint);
|
|
1851
|
+
} else {
|
|
1852
|
+
const event = await StateMachineLogics[runState.value](runState.context);
|
|
1853
|
+
if ("checkpoint" in event) {
|
|
1854
|
+
await storeCheckpoint(event.checkpoint, event.timestamp);
|
|
1855
|
+
}
|
|
1856
|
+
await eventEmitter.emit(event);
|
|
1857
|
+
if (shouldContinueRun) {
|
|
1858
|
+
const shouldContinue = await shouldContinueRun(
|
|
1859
|
+
runState.context.setting,
|
|
1860
|
+
runState.context.checkpoint,
|
|
1861
|
+
runState.context.step
|
|
1862
|
+
);
|
|
1863
|
+
if (!shouldContinue) {
|
|
1864
|
+
runActor.stop();
|
|
1865
|
+
await closeSkillManagers(runState.context.skillManagers);
|
|
1866
|
+
resolve(runState.context.checkpoint);
|
|
1867
|
+
return;
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
runActor.send(event);
|
|
1871
|
+
}
|
|
1872
|
+
} catch (error) {
|
|
1873
|
+
await closeSkillManagers(skillManagers).catch(() => {
|
|
1874
|
+
});
|
|
1875
|
+
reject(error);
|
|
1876
|
+
}
|
|
1877
|
+
});
|
|
1878
|
+
runActor.start();
|
|
1879
|
+
});
|
|
1880
|
+
}
|
|
1881
|
+
async function resolveExpertToRun(expertKey, experts, clientOptions) {
|
|
1882
|
+
if (experts[expertKey]) {
|
|
1883
|
+
return experts[expertKey];
|
|
1884
|
+
}
|
|
1885
|
+
const client = new ApiV1Client({
|
|
1886
|
+
baseUrl: clientOptions.perstackApiBaseUrl,
|
|
1887
|
+
apiKey: clientOptions.perstackApiKey
|
|
1888
|
+
});
|
|
1889
|
+
const { expert } = await client.registry.experts.get({ expertKey });
|
|
1890
|
+
experts[expertKey] = toRuntimeExpert(expert);
|
|
1891
|
+
return experts[expertKey];
|
|
1892
|
+
}
|
|
1893
|
+
function toRuntimeExpert(expert) {
|
|
1894
|
+
const skills = Object.fromEntries(
|
|
1895
|
+
Object.entries(expert.skills).map(([name, skill]) => {
|
|
1896
|
+
switch (skill.type) {
|
|
1897
|
+
case "mcpStdioSkill":
|
|
1898
|
+
return [name, { ...skill, name }];
|
|
1899
|
+
case "mcpSseSkill":
|
|
1900
|
+
return [name, { ...skill, name }];
|
|
1901
|
+
case "interactiveSkill":
|
|
1902
|
+
return [name, { ...skill, name }];
|
|
1903
|
+
default: {
|
|
1904
|
+
throw new Error(`Unknown skill type: ${skill.type}`);
|
|
1905
|
+
}
|
|
1906
|
+
}
|
|
1907
|
+
})
|
|
1908
|
+
);
|
|
1909
|
+
return { ...expert, skills };
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1912
|
+
// src/setup-experts.ts
|
|
1913
|
+
async function setupExperts(setting, resolveExpertToRun2 = resolveExpertToRun) {
|
|
1914
|
+
const { expertKey } = setting;
|
|
1915
|
+
const experts = { ...setting.experts };
|
|
1916
|
+
const clientOptions = {
|
|
1917
|
+
perstackApiBaseUrl: setting.perstackApiBaseUrl,
|
|
1918
|
+
perstackApiKey: setting.perstackApiKey
|
|
1919
|
+
};
|
|
1920
|
+
const expertToRun = await resolveExpertToRun2(expertKey, experts, clientOptions);
|
|
1921
|
+
for (const delegateName of expertToRun.delegates) {
|
|
1922
|
+
const delegate = await resolveExpertToRun2(delegateName, experts, clientOptions);
|
|
1923
|
+
if (!delegate) {
|
|
1924
|
+
throw new Error(`Delegate ${delegateName} not found`);
|
|
1925
|
+
}
|
|
1926
|
+
}
|
|
1927
|
+
return { expertToRun, experts };
|
|
1928
|
+
}
|
|
1929
|
+
|
|
1732
1930
|
// src/runtime.ts
|
|
1733
1931
|
async function run(runInput, options) {
|
|
1734
1932
|
const runParams = runParamsSchema.parse(runInput);
|
|
@@ -1739,15 +1937,10 @@ async function run(runInput, options) {
|
|
|
1739
1937
|
eventEmitter.subscribe(eventListener);
|
|
1740
1938
|
let { setting, checkpoint } = runParams;
|
|
1741
1939
|
const contextWindow = getContextWindow(setting.providerConfig.providerName, setting.model);
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
throw new Error(`Workspace path must be absolute: ${setting.workspace}`);
|
|
1745
|
-
}
|
|
1746
|
-
process.chdir(setting.workspace);
|
|
1747
|
-
}
|
|
1748
|
-
await storeRunSetting(setting);
|
|
1940
|
+
const getRunDir = options?.getRunDir ?? defaultGetRunDir;
|
|
1941
|
+
await storeRunSetting(setting, options?.fileSystem, getRunDir);
|
|
1749
1942
|
while (true) {
|
|
1750
|
-
const { expertToRun, experts } = await setupExperts(setting);
|
|
1943
|
+
const { expertToRun, experts } = await setupExperts(setting, options?.resolveExpertToRun);
|
|
1751
1944
|
if (options?.eventListener) {
|
|
1752
1945
|
const initEvent = createRuntimeEvent("initializeRuntime", setting.runId, {
|
|
1753
1946
|
runtimeVersion: package_default.version,
|
|
@@ -1769,102 +1962,31 @@ async function run(runInput, options) {
|
|
|
1769
1962
|
setting,
|
|
1770
1963
|
options?.eventListener
|
|
1771
1964
|
);
|
|
1772
|
-
const
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
},
|
|
1778
|
-
initialCheckpoint: checkpoint ? {
|
|
1779
|
-
...checkpoint,
|
|
1780
|
-
id: createId(),
|
|
1781
|
-
stepNumber: checkpoint.stepNumber + 1
|
|
1782
|
-
} : {
|
|
1783
|
-
id: createId(),
|
|
1784
|
-
runId: setting.runId,
|
|
1785
|
-
expert: {
|
|
1786
|
-
key: setting.expertKey,
|
|
1787
|
-
name: expertToRun.name,
|
|
1788
|
-
version: expertToRun.version
|
|
1789
|
-
},
|
|
1790
|
-
stepNumber: 1,
|
|
1791
|
-
status: "init",
|
|
1792
|
-
messages: [],
|
|
1793
|
-
usage: createEmptyUsage(),
|
|
1794
|
-
contextWindow,
|
|
1795
|
-
contextWindowUsage: contextWindow ? 0 : void 0
|
|
1796
|
-
},
|
|
1797
|
-
eventListener,
|
|
1798
|
-
skillManagers
|
|
1799
|
-
}
|
|
1965
|
+
const initialCheckpoint = checkpoint ? createNextStepCheckpoint(createId(), checkpoint) : createInitialCheckpoint(createId(), {
|
|
1966
|
+
runId: setting.runId,
|
|
1967
|
+
expertKey: setting.expertKey,
|
|
1968
|
+
expert: expertToRun,
|
|
1969
|
+
contextWindow
|
|
1800
1970
|
});
|
|
1801
|
-
const runResultCheckpoint = await
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
await closeSkillManagers(skillManagers2);
|
|
1810
|
-
resolve(checkpoint2);
|
|
1811
|
-
} else {
|
|
1812
|
-
const event = await StateMachineLogics[runState.value](runState.context);
|
|
1813
|
-
if ("checkpoint" in event) {
|
|
1814
|
-
await storeCheckpoint(event.checkpoint, event.timestamp);
|
|
1815
|
-
}
|
|
1816
|
-
await eventEmitter.emit(event);
|
|
1817
|
-
if (options?.shouldContinueRun) {
|
|
1818
|
-
const shouldContinue = await options.shouldContinueRun(
|
|
1819
|
-
runState.context.setting,
|
|
1820
|
-
runState.context.checkpoint,
|
|
1821
|
-
runState.context.step
|
|
1822
|
-
);
|
|
1823
|
-
if (!shouldContinue) {
|
|
1824
|
-
runActor.stop();
|
|
1825
|
-
resolve(runState.context.checkpoint);
|
|
1826
|
-
return;
|
|
1827
|
-
}
|
|
1828
|
-
}
|
|
1829
|
-
runActor.send(event);
|
|
1830
|
-
}
|
|
1831
|
-
} catch (error) {
|
|
1832
|
-
reject(error);
|
|
1833
|
-
}
|
|
1834
|
-
});
|
|
1835
|
-
runActor.start();
|
|
1971
|
+
const runResultCheckpoint = await executeStateMachine({
|
|
1972
|
+
setting: { ...setting, experts },
|
|
1973
|
+
initialCheckpoint,
|
|
1974
|
+
eventListener,
|
|
1975
|
+
skillManagers,
|
|
1976
|
+
eventEmitter,
|
|
1977
|
+
storeCheckpoint,
|
|
1978
|
+
shouldContinueRun: options?.shouldContinueRun
|
|
1836
1979
|
});
|
|
1837
1980
|
switch (runResultCheckpoint.status) {
|
|
1838
1981
|
case "completed": {
|
|
1839
1982
|
if (runResultCheckpoint.delegatedBy) {
|
|
1840
|
-
const
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
if (delegateResultMessage.type !== "expertMessage") {
|
|
1844
|
-
throw new Error("Delegation error: delegation result message is incorrect");
|
|
1845
|
-
}
|
|
1846
|
-
const delegateText = delegateResultMessage.contents.find(
|
|
1847
|
-
(content) => content.type === "textPart"
|
|
1983
|
+
const parentCheckpoint = await retrieveCheckpoint(
|
|
1984
|
+
setting.runId,
|
|
1985
|
+
runResultCheckpoint.delegatedBy.checkpointId
|
|
1848
1986
|
);
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
setting = {
|
|
1853
|
-
...setting,
|
|
1854
|
-
expertKey: expert.key,
|
|
1855
|
-
input: {
|
|
1856
|
-
interactiveToolCallResult: {
|
|
1857
|
-
toolCallId,
|
|
1858
|
-
toolName,
|
|
1859
|
-
text: delegateText.text
|
|
1860
|
-
}
|
|
1861
|
-
}
|
|
1862
|
-
};
|
|
1863
|
-
checkpoint = {
|
|
1864
|
-
...await retrieveCheckpoint(setting.runId, checkpointId),
|
|
1865
|
-
stepNumber: runResultCheckpoint.stepNumber,
|
|
1866
|
-
usage: runResultCheckpoint.usage
|
|
1867
|
-
};
|
|
1987
|
+
const result = buildDelegationReturnState(setting, runResultCheckpoint, parentCheckpoint);
|
|
1988
|
+
setting = result.setting;
|
|
1989
|
+
checkpoint = result.checkpoint;
|
|
1868
1990
|
break;
|
|
1869
1991
|
}
|
|
1870
1992
|
return runResultCheckpoint;
|
|
@@ -1873,38 +1995,9 @@ async function run(runInput, options) {
|
|
|
1873
1995
|
return runResultCheckpoint;
|
|
1874
1996
|
}
|
|
1875
1997
|
case "stoppedByDelegate": {
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
const { expert, toolCallId, toolName, query } = runResultCheckpoint.delegateTo;
|
|
1880
|
-
setting = {
|
|
1881
|
-
...setting,
|
|
1882
|
-
expertKey: expert.key,
|
|
1883
|
-
input: {
|
|
1884
|
-
text: query
|
|
1885
|
-
}
|
|
1886
|
-
};
|
|
1887
|
-
checkpoint = {
|
|
1888
|
-
...runResultCheckpoint,
|
|
1889
|
-
status: "init",
|
|
1890
|
-
messages: [],
|
|
1891
|
-
expert: {
|
|
1892
|
-
key: expert.key,
|
|
1893
|
-
name: expert.name,
|
|
1894
|
-
version: expert.version
|
|
1895
|
-
},
|
|
1896
|
-
delegatedBy: {
|
|
1897
|
-
expert: {
|
|
1898
|
-
key: expertToRun.key,
|
|
1899
|
-
name: expertToRun.name,
|
|
1900
|
-
version: expertToRun.version
|
|
1901
|
-
},
|
|
1902
|
-
toolCallId,
|
|
1903
|
-
toolName,
|
|
1904
|
-
checkpointId: runResultCheckpoint.id
|
|
1905
|
-
},
|
|
1906
|
-
usage: runResultCheckpoint.usage
|
|
1907
|
-
};
|
|
1998
|
+
const result = buildDelegateToState(setting, runResultCheckpoint, expertToRun);
|
|
1999
|
+
setting = result.setting;
|
|
2000
|
+
checkpoint = result.checkpoint;
|
|
1908
2001
|
break;
|
|
1909
2002
|
}
|
|
1910
2003
|
case "stoppedByExceededMaxSteps": {
|
|
@@ -1918,22 +2011,6 @@ async function run(runInput, options) {
|
|
|
1918
2011
|
}
|
|
1919
2012
|
}
|
|
1920
2013
|
}
|
|
1921
|
-
async function setupExperts(setting) {
|
|
1922
|
-
const { expertKey } = setting;
|
|
1923
|
-
const experts = { ...setting.experts };
|
|
1924
|
-
const clientOptions = {
|
|
1925
|
-
perstackApiBaseUrl: setting.perstackApiBaseUrl,
|
|
1926
|
-
perstackApiKey: setting.perstackApiKey
|
|
1927
|
-
};
|
|
1928
|
-
const expertToRun = await resolveExpertToRun(expertKey, experts, clientOptions);
|
|
1929
|
-
for (const delegateName of expertToRun.delegates) {
|
|
1930
|
-
const delegate = await resolveExpertToRun(delegateName, experts, clientOptions);
|
|
1931
|
-
if (!delegate) {
|
|
1932
|
-
throw new Error(`Delegate ${delegateName} not found`);
|
|
1933
|
-
}
|
|
1934
|
-
}
|
|
1935
|
-
return { expertToRun, experts };
|
|
1936
|
-
}
|
|
1937
2014
|
function getEventListener(options) {
|
|
1938
2015
|
const listener = options?.eventListener ?? ((e) => console.log(JSON.stringify(e)));
|
|
1939
2016
|
return async (event) => {
|
|
@@ -1943,25 +2020,10 @@ function getEventListener(options) {
|
|
|
1943
2020
|
listener(event);
|
|
1944
2021
|
};
|
|
1945
2022
|
}
|
|
1946
|
-
async function storeRunSetting(setting) {
|
|
1947
|
-
const runDir = getRunDir(setting.runId);
|
|
1948
|
-
if (existsSync(runDir)) {
|
|
1949
|
-
const runSettingPath = path.resolve(runDir, "run-setting.json");
|
|
1950
|
-
const runSetting = JSON.parse(await readFile(runSettingPath, "utf-8"));
|
|
1951
|
-
runSetting.updatedAt = Date.now();
|
|
1952
|
-
await writeFile(runSettingPath, JSON.stringify(runSetting), "utf-8");
|
|
1953
|
-
} else {
|
|
1954
|
-
await mkdir(runDir, { recursive: true });
|
|
1955
|
-
await writeFile(path.resolve(runDir, "run-setting.json"), JSON.stringify(setting), "utf-8");
|
|
1956
|
-
}
|
|
1957
|
-
}
|
|
1958
|
-
function getRunDir(runId) {
|
|
1959
|
-
return `${process.cwd()}/perstack/runs/${runId}`;
|
|
1960
|
-
}
|
|
1961
2023
|
|
|
1962
2024
|
// src/index.ts
|
|
1963
2025
|
var runtimeVersion = package_default.version;
|
|
1964
2026
|
|
|
1965
|
-
export { StateMachineLogics, calculateContextWindowUsage, getContextWindow, getModel, getRunDir, run, runtimeStateMachine, runtimeVersion };
|
|
2027
|
+
export { StateMachineLogics, calculateContextWindowUsage, getContextWindow, getModel, defaultGetRunDir as getRunDir, run, runtimeStateMachine, runtimeVersion };
|
|
1966
2028
|
//# sourceMappingURL=index.js.map
|
|
1967
2029
|
//# sourceMappingURL=index.js.map
|