@perstack/runtime 0.0.37 → 0.0.39
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 +201 -0
- package/dist/src/index.d.ts +25 -18
- package/dist/src/index.js +213 -311
- package/dist/src/index.js.map +1 -1
- package/package.json +17 -17
package/dist/src/index.js
CHANGED
|
@@ -1,24 +1,25 @@
|
|
|
1
|
-
import { setup, assign, createActor } from 'xstate';
|
|
2
1
|
import { createAmazonBedrock } from '@ai-sdk/amazon-bedrock';
|
|
3
2
|
import { createAnthropic } from '@ai-sdk/anthropic';
|
|
4
3
|
import { createAzure } from '@ai-sdk/azure';
|
|
5
4
|
import { createGoogleGenerativeAI } from '@ai-sdk/google';
|
|
6
5
|
import { createOpenAI } from '@ai-sdk/openai';
|
|
7
|
-
import { knownModels, stopRunByDelegate, stopRunByInteractiveTool, resolveThought, attemptCompletion, resolvePdfFile, resolveImageFile, resolveToolResult, stopRunByExceededMaxSteps, continueToNextStep, retry, completeRun, callDelegate, callInteractiveTool, callTool, startRun, startGeneration, finishToolCall, runParamsSchema, checkpointSchema } from '@perstack/core';
|
|
6
|
+
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
7
|
import { createOllama } from 'ollama-ai-provider-v2';
|
|
8
|
+
import { existsSync } from 'fs';
|
|
9
|
+
import { readFile, writeFile, mkdir, readdir } from 'fs/promises';
|
|
10
|
+
import path from 'path';
|
|
11
|
+
import { createId } from '@paralleldrive/cuid2';
|
|
12
|
+
import { setup, assign, createActor } from 'xstate';
|
|
13
|
+
import { ApiV1Client } from '@perstack/api-client/v1';
|
|
9
14
|
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
10
15
|
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
|
|
11
16
|
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
12
17
|
import { McpError } from '@modelcontextprotocol/sdk/types.js';
|
|
13
|
-
import { createId } from '@paralleldrive/cuid2';
|
|
14
18
|
import { generateText, tool, jsonSchema } from 'ai';
|
|
15
19
|
import { dedent } from 'ts-dedent';
|
|
16
|
-
import {
|
|
17
|
-
import { existsSync } from 'fs';
|
|
18
|
-
import path from 'path';
|
|
19
|
-
import { ApiV1Client } from '@perstack/api-client/v1';
|
|
20
|
+
import { createRequire } from 'module';
|
|
20
21
|
|
|
21
|
-
// src/
|
|
22
|
+
// src/model.ts
|
|
22
23
|
function getModel(modelId, providerConfig) {
|
|
23
24
|
switch (providerConfig.name) {
|
|
24
25
|
case "anthropic": {
|
|
@@ -76,39 +77,142 @@ function getContextWindow(providerName, modelId) {
|
|
|
76
77
|
function calculateContextWindowUsage(usage, contextWindow) {
|
|
77
78
|
return (usage.inputTokens + usage.cachedInputTokens + usage.outputTokens) / contextWindow;
|
|
78
79
|
}
|
|
80
|
+
async function defaultRetrieveCheckpoint(runId, checkpointId) {
|
|
81
|
+
const runDir = getRunDir(runId);
|
|
82
|
+
const checkpointFiles = await readdir(runDir, { withFileTypes: true }).then(
|
|
83
|
+
(files) => files.filter((file) => file.isFile() && file.name.startsWith("checkpoint-"))
|
|
84
|
+
);
|
|
85
|
+
const checkpointFile = checkpointFiles.find((file) => file.name.endsWith(`-${checkpointId}.json`));
|
|
86
|
+
if (!checkpointFile) {
|
|
87
|
+
throw new Error(`checkpoint not found: ${runId} ${checkpointId}`);
|
|
88
|
+
}
|
|
89
|
+
const checkpointPath = `${runDir}/${checkpointFile.name}`;
|
|
90
|
+
const checkpoint = await readFile(checkpointPath, "utf8");
|
|
91
|
+
return checkpointSchema.parse(JSON.parse(checkpoint));
|
|
92
|
+
}
|
|
93
|
+
async function defaultStoreCheckpoint(checkpoint, timestamp) {
|
|
94
|
+
const { id, runId, stepNumber } = checkpoint;
|
|
95
|
+
const runDir = getRunDir(runId);
|
|
96
|
+
const checkpointPath = `${runDir}/checkpoint-${timestamp}-${stepNumber}-${id}.json`;
|
|
97
|
+
await mkdir(runDir, { recursive: true });
|
|
98
|
+
await writeFile(checkpointPath, JSON.stringify(checkpoint));
|
|
99
|
+
}
|
|
100
|
+
async function defaultStoreEvent(event) {
|
|
101
|
+
const { timestamp, runId, stepNumber, type } = event;
|
|
102
|
+
const runDir = getRunDir(runId);
|
|
103
|
+
const eventPath = `${runDir}/event-${timestamp}-${stepNumber}-${type}.json`;
|
|
104
|
+
await mkdir(runDir, { recursive: true });
|
|
105
|
+
await writeFile(eventPath, JSON.stringify(event));
|
|
106
|
+
}
|
|
107
|
+
var RunEventEmitter = class {
|
|
108
|
+
listeners = [];
|
|
109
|
+
subscribe(listener) {
|
|
110
|
+
this.listeners.push(listener);
|
|
111
|
+
}
|
|
112
|
+
async emit(event) {
|
|
113
|
+
for (const listener of this.listeners) {
|
|
114
|
+
await listener({
|
|
115
|
+
...event,
|
|
116
|
+
id: createId(),
|
|
117
|
+
timestamp: Date.now()
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
async function resolveExpertToRun(expertKey, experts, clientOptions) {
|
|
123
|
+
if (experts[expertKey]) {
|
|
124
|
+
return experts[expertKey];
|
|
125
|
+
}
|
|
126
|
+
const client = new ApiV1Client({
|
|
127
|
+
baseUrl: clientOptions.perstackApiBaseUrl,
|
|
128
|
+
apiKey: clientOptions.perstackApiKey
|
|
129
|
+
});
|
|
130
|
+
const { expert } = await client.registry.experts.get({ expertKey });
|
|
131
|
+
experts[expertKey] = toRuntimeExpert(expert);
|
|
132
|
+
return experts[expertKey];
|
|
133
|
+
}
|
|
134
|
+
function toRuntimeExpert(expert) {
|
|
135
|
+
const skills = Object.fromEntries(
|
|
136
|
+
Object.entries(expert.skills).map(([name, skill]) => {
|
|
137
|
+
switch (skill.type) {
|
|
138
|
+
case "mcpStdioSkill":
|
|
139
|
+
return [name, { ...skill, name }];
|
|
140
|
+
case "mcpSseSkill":
|
|
141
|
+
return [name, { ...skill, name }];
|
|
142
|
+
case "interactiveSkill":
|
|
143
|
+
return [name, { ...skill, name }];
|
|
144
|
+
default: {
|
|
145
|
+
throw new Error(`Unknown skill type: ${skill.type}`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
})
|
|
149
|
+
);
|
|
150
|
+
return { ...expert, skills };
|
|
151
|
+
}
|
|
79
152
|
var SkillManager = class {
|
|
80
153
|
_toolDefinitions = [];
|
|
81
154
|
_initialized = false;
|
|
155
|
+
_initializing;
|
|
82
156
|
name;
|
|
83
157
|
type;
|
|
158
|
+
lazyInit;
|
|
84
159
|
skill;
|
|
85
160
|
interactiveSkill;
|
|
86
161
|
expert;
|
|
87
162
|
_mcpClient;
|
|
88
163
|
_params;
|
|
89
164
|
_env;
|
|
90
|
-
|
|
165
|
+
_runId;
|
|
166
|
+
_eventListener;
|
|
167
|
+
constructor(params, runId, eventListener) {
|
|
91
168
|
this._params = params;
|
|
169
|
+
this._runId = runId;
|
|
170
|
+
this._eventListener = eventListener;
|
|
92
171
|
this.type = params.type;
|
|
93
172
|
switch (params.type) {
|
|
94
173
|
case "mcp":
|
|
95
174
|
this.name = params.skill.name;
|
|
96
175
|
this.skill = params.skill;
|
|
97
176
|
this._env = params.env;
|
|
177
|
+
this.lazyInit = this.skill.type === "mcpStdioSkill" && this.skill.lazyInit && this.skill.name !== "@perstack/base";
|
|
98
178
|
break;
|
|
99
179
|
case "interactive":
|
|
100
180
|
this.name = params.interactiveSkill.name;
|
|
101
181
|
this.interactiveSkill = params.interactiveSkill;
|
|
102
182
|
this._env = {};
|
|
183
|
+
this.lazyInit = false;
|
|
103
184
|
break;
|
|
104
185
|
case "delegate":
|
|
105
186
|
this.name = params.expert.name;
|
|
106
187
|
this.expert = params.expert;
|
|
107
188
|
this._env = {};
|
|
189
|
+
this.lazyInit = false;
|
|
108
190
|
break;
|
|
109
191
|
}
|
|
110
192
|
}
|
|
111
193
|
async init() {
|
|
194
|
+
if (this._initialized) {
|
|
195
|
+
throw new Error(`Skill ${this.name} is already initialized`);
|
|
196
|
+
}
|
|
197
|
+
if (this._initializing) {
|
|
198
|
+
throw new Error(`Skill ${this.name} is already initializing`);
|
|
199
|
+
}
|
|
200
|
+
const initPromise = this._performInit();
|
|
201
|
+
this._initializing = initPromise;
|
|
202
|
+
if (!this.lazyInit) {
|
|
203
|
+
try {
|
|
204
|
+
await initPromise;
|
|
205
|
+
} catch (error) {
|
|
206
|
+
this._initialized = false;
|
|
207
|
+
this._initializing = void 0;
|
|
208
|
+
throw error;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
isInitialized() {
|
|
213
|
+
return this._initialized;
|
|
214
|
+
}
|
|
215
|
+
async _performInit() {
|
|
112
216
|
switch (this._params.type) {
|
|
113
217
|
case "mcp": {
|
|
114
218
|
await this._initMcpSkill(this._params);
|
|
@@ -123,8 +227,13 @@ var SkillManager = class {
|
|
|
123
227
|
break;
|
|
124
228
|
}
|
|
125
229
|
}
|
|
230
|
+
this._initialized = true;
|
|
231
|
+
this._initializing = void 0;
|
|
126
232
|
}
|
|
127
233
|
async _initMcpSkill(params) {
|
|
234
|
+
if (this.isInitialized()) {
|
|
235
|
+
throw new Error(`Skill ${params.skill.name} is already initialized`);
|
|
236
|
+
}
|
|
128
237
|
this._mcpClient = new Client({
|
|
129
238
|
name: `${params.skill.name}-mcp-client`,
|
|
130
239
|
version: "1.0.0"
|
|
@@ -143,8 +252,16 @@ var SkillManager = class {
|
|
|
143
252
|
env[envName] = this._env[envName];
|
|
144
253
|
}
|
|
145
254
|
const { command, args } = this._getCommandArgs(params.skill);
|
|
146
|
-
const transport = new StdioClientTransport({ command, args, env });
|
|
255
|
+
const transport = new StdioClientTransport({ command, args, env, stderr: "ignore" });
|
|
147
256
|
await this._mcpClient.connect(transport);
|
|
257
|
+
if (this._eventListener) {
|
|
258
|
+
const serverInfo = this._mcpClient.getServerVersion();
|
|
259
|
+
const event = createRuntimeEvent("skillConnected", this._runId, {
|
|
260
|
+
skillName: params.skill.name,
|
|
261
|
+
serverInfo: serverInfo ? { name: serverInfo.name, version: serverInfo.version } : void 0
|
|
262
|
+
});
|
|
263
|
+
this._eventListener(event);
|
|
264
|
+
}
|
|
148
265
|
break;
|
|
149
266
|
}
|
|
150
267
|
case "mcpSseSkill": {
|
|
@@ -164,7 +281,6 @@ var SkillManager = class {
|
|
|
164
281
|
inputSchema: tool2.inputSchema,
|
|
165
282
|
interactive: false
|
|
166
283
|
}));
|
|
167
|
-
this._initialized = true;
|
|
168
284
|
}
|
|
169
285
|
_getCommandArgs(skill) {
|
|
170
286
|
const { name, command, packageName, args } = skill;
|
|
@@ -183,6 +299,9 @@ var SkillManager = class {
|
|
|
183
299
|
return { command, args: newArgs };
|
|
184
300
|
}
|
|
185
301
|
async _initInteractiveSkill(params) {
|
|
302
|
+
if (this.isInitialized()) {
|
|
303
|
+
throw new Error(`Skill ${params.interactiveSkill.name} is already initialized`);
|
|
304
|
+
}
|
|
186
305
|
this._toolDefinitions = Object.values(params.interactiveSkill.tools).map((tool2) => ({
|
|
187
306
|
skillName: params.interactiveSkill.name,
|
|
188
307
|
name: tool2.name,
|
|
@@ -190,9 +309,11 @@ var SkillManager = class {
|
|
|
190
309
|
inputSchema: JSON.parse(tool2.inputJsonSchema),
|
|
191
310
|
interactive: true
|
|
192
311
|
}));
|
|
193
|
-
this._initialized = true;
|
|
194
312
|
}
|
|
195
313
|
async _initDelegate(params) {
|
|
314
|
+
if (this.isInitialized()) {
|
|
315
|
+
throw new Error(`Skill ${params.expert.name} is already initialized`);
|
|
316
|
+
}
|
|
196
317
|
this._toolDefinitions = [
|
|
197
318
|
{
|
|
198
319
|
skillName: params.expert.name,
|
|
@@ -206,16 +327,24 @@ var SkillManager = class {
|
|
|
206
327
|
interactive: false
|
|
207
328
|
}
|
|
208
329
|
];
|
|
209
|
-
this._initialized = true;
|
|
210
330
|
}
|
|
211
331
|
async close() {
|
|
212
332
|
if (this._mcpClient) {
|
|
213
333
|
await this._mcpClient.close();
|
|
334
|
+
if (this._eventListener && this.skill) {
|
|
335
|
+
const event = createRuntimeEvent("skillDisconnected", this._runId, {
|
|
336
|
+
skillName: this.skill.name
|
|
337
|
+
});
|
|
338
|
+
this._eventListener(event);
|
|
339
|
+
}
|
|
214
340
|
}
|
|
215
341
|
}
|
|
216
342
|
async getToolDefinitions() {
|
|
217
|
-
if (!this.
|
|
218
|
-
throw new Error(
|
|
343
|
+
if (!this.isInitialized() && !this.lazyInit) {
|
|
344
|
+
throw new Error(`Skill ${this.name} is not initialized`);
|
|
345
|
+
}
|
|
346
|
+
if (!this.isInitialized() && this.lazyInit) {
|
|
347
|
+
return [];
|
|
219
348
|
}
|
|
220
349
|
if (this._params.type === "mcp") {
|
|
221
350
|
const omit = this._params.skill.omit ?? [];
|
|
@@ -227,7 +356,7 @@ var SkillManager = class {
|
|
|
227
356
|
async callTool(toolName, input) {
|
|
228
357
|
switch (this._params.type) {
|
|
229
358
|
case "mcp": {
|
|
230
|
-
if (!this._mcpClient) {
|
|
359
|
+
if (!this.isInitialized() || !this._mcpClient) {
|
|
231
360
|
throw new Error(`${this.name} is not initialized`);
|
|
232
361
|
}
|
|
233
362
|
try {
|
|
@@ -314,8 +443,8 @@ var SkillManager = class {
|
|
|
314
443
|
throw new Error(`Unsupported resource type: ${JSON.stringify(resource)}`);
|
|
315
444
|
}
|
|
316
445
|
};
|
|
317
|
-
async function getSkillManagers(expert, experts, setting) {
|
|
318
|
-
const { perstackBaseSkillCommand, env } = setting;
|
|
446
|
+
async function getSkillManagers(expert, experts, setting, eventListener) {
|
|
447
|
+
const { perstackBaseSkillCommand, env, runId } = setting;
|
|
319
448
|
const skillManagers = {};
|
|
320
449
|
const { skills } = expert;
|
|
321
450
|
if (!skills["@perstack/base"]) {
|
|
@@ -334,23 +463,32 @@ async function getSkillManagers(expert, experts, setting) {
|
|
|
334
463
|
skill.command = overrideCommand;
|
|
335
464
|
skill.packageName = void 0;
|
|
336
465
|
skill.args = overrideArgs;
|
|
466
|
+
skill.lazyInit = false;
|
|
337
467
|
}
|
|
338
468
|
}
|
|
339
|
-
const skillManager = new SkillManager(
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
469
|
+
const skillManager = new SkillManager(
|
|
470
|
+
{
|
|
471
|
+
type: "mcp",
|
|
472
|
+
skill,
|
|
473
|
+
env
|
|
474
|
+
},
|
|
475
|
+
runId,
|
|
476
|
+
eventListener
|
|
477
|
+
);
|
|
344
478
|
await skillManager.init();
|
|
345
479
|
return skillManager;
|
|
346
480
|
})
|
|
347
481
|
);
|
|
348
482
|
const interactiveSkillManagers = await Promise.all(
|
|
349
483
|
Object.values(skills).filter((skill) => skill.type === "interactiveSkill").map(async (interactiveSkill) => {
|
|
350
|
-
const skillManager = new SkillManager(
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
484
|
+
const skillManager = new SkillManager(
|
|
485
|
+
{
|
|
486
|
+
type: "interactive",
|
|
487
|
+
interactiveSkill
|
|
488
|
+
},
|
|
489
|
+
runId,
|
|
490
|
+
eventListener
|
|
491
|
+
);
|
|
354
492
|
await skillManager.init();
|
|
355
493
|
return skillManager;
|
|
356
494
|
})
|
|
@@ -358,10 +496,14 @@ async function getSkillManagers(expert, experts, setting) {
|
|
|
358
496
|
const delegateSkillManagers = await Promise.all(
|
|
359
497
|
expert.delegates.map(async (delegateExpertName) => {
|
|
360
498
|
const delegate = experts[delegateExpertName];
|
|
361
|
-
const skillManager = new SkillManager(
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
499
|
+
const skillManager = new SkillManager(
|
|
500
|
+
{
|
|
501
|
+
type: "delegate",
|
|
502
|
+
expert: delegate
|
|
503
|
+
},
|
|
504
|
+
runId,
|
|
505
|
+
eventListener
|
|
506
|
+
);
|
|
365
507
|
await skillManager.init();
|
|
366
508
|
return skillManager;
|
|
367
509
|
})
|
|
@@ -1579,266 +1721,20 @@ var StateMachineLogics = {
|
|
|
1579
1721
|
CallingDelegate: callingDelegateLogic,
|
|
1580
1722
|
FinishingStep: finishingStepLogic
|
|
1581
1723
|
};
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
(files) => files.filter((file) => file.isFile() && file.name.startsWith("checkpoint-"))
|
|
1586
|
-
);
|
|
1587
|
-
const checkpointFile = checkpointFiles.find((file) => file.name.endsWith(`-${checkpointId}.json`));
|
|
1588
|
-
if (!checkpointFile) {
|
|
1589
|
-
throw new Error(`checkpoint not found: ${runId} ${checkpointId}`);
|
|
1724
|
+
var getVersion = () => {
|
|
1725
|
+
if (typeof __RUNTIME_VERSION__ !== "undefined") {
|
|
1726
|
+
return __RUNTIME_VERSION__;
|
|
1590
1727
|
}
|
|
1591
|
-
const
|
|
1592
|
-
const
|
|
1593
|
-
return
|
|
1594
|
-
}
|
|
1595
|
-
async function defaultStoreCheckpoint(checkpoint, timestamp) {
|
|
1596
|
-
const { id, runId, stepNumber } = checkpoint;
|
|
1597
|
-
const runDir = getRunDir(runId);
|
|
1598
|
-
const checkpointPath = `${runDir}/checkpoint-${timestamp}-${stepNumber}-${id}.json`;
|
|
1599
|
-
await mkdir(runDir, { recursive: true });
|
|
1600
|
-
await writeFile(checkpointPath, JSON.stringify(checkpoint, null, 2));
|
|
1601
|
-
}
|
|
1602
|
-
async function defaultStoreEvent(event) {
|
|
1603
|
-
const { timestamp, runId, stepNumber, type } = event;
|
|
1604
|
-
const runDir = getRunDir(runId);
|
|
1605
|
-
const eventPath = `${runDir}/event-${timestamp}-${stepNumber}-${type}.json`;
|
|
1606
|
-
await mkdir(runDir, { recursive: true });
|
|
1607
|
-
await writeFile(eventPath, JSON.stringify(event, null, 2));
|
|
1608
|
-
}
|
|
1609
|
-
|
|
1610
|
-
// package.json
|
|
1611
|
-
var package_default = {
|
|
1612
|
-
version: "0.0.37"};
|
|
1613
|
-
|
|
1614
|
-
// src/events/default-event-listener.ts
|
|
1615
|
-
var log = console.info;
|
|
1616
|
-
var debug = console.debug;
|
|
1617
|
-
var header = (e) => {
|
|
1618
|
-
const t = (/* @__PURE__ */ new Date()).toISOString();
|
|
1619
|
-
const stepNumber = e.stepNumber;
|
|
1620
|
-
const key = e.expertKey;
|
|
1621
|
-
return `${t} ${stepNumber} ${key}`;
|
|
1728
|
+
const require2 = createRequire(import.meta.url);
|
|
1729
|
+
const pkg = require2("../package.json");
|
|
1730
|
+
return pkg.version;
|
|
1622
1731
|
};
|
|
1623
|
-
|
|
1624
|
-
await defaultStoreEvent(e);
|
|
1625
|
-
switch (e.type) {
|
|
1626
|
-
case "startRun": {
|
|
1627
|
-
log(`${header(e)} Perstack@${package_default.version} started`);
|
|
1628
|
-
break;
|
|
1629
|
-
}
|
|
1630
|
-
case "startGeneration": {
|
|
1631
|
-
log(`${header(e)} Generating tool call`);
|
|
1632
|
-
break;
|
|
1633
|
-
}
|
|
1634
|
-
case "retry": {
|
|
1635
|
-
log(`${header(e)} Retrying tool call generation`);
|
|
1636
|
-
debug(e.reason);
|
|
1637
|
-
break;
|
|
1638
|
-
}
|
|
1639
|
-
case "callTool": {
|
|
1640
|
-
log(`${header(e)} Calling tool`);
|
|
1641
|
-
if (e.toolCall.skillName === "@perstack/base") {
|
|
1642
|
-
switch (e.toolCall.toolName) {
|
|
1643
|
-
case "think": {
|
|
1644
|
-
const thought = e.toolCall.args.thought;
|
|
1645
|
-
log(`${header(e)} Thought Updated:`);
|
|
1646
|
-
debug(thought);
|
|
1647
|
-
break;
|
|
1648
|
-
}
|
|
1649
|
-
case "readPdfFile": {
|
|
1650
|
-
const path2 = e.toolCall.args.path;
|
|
1651
|
-
log(`${header(e)} Reading PDF: ${path2}`);
|
|
1652
|
-
break;
|
|
1653
|
-
}
|
|
1654
|
-
case "readImageFile": {
|
|
1655
|
-
const path2 = e.toolCall.args.path;
|
|
1656
|
-
log(`${header(e)} Reading Image: ${path2}`);
|
|
1657
|
-
break;
|
|
1658
|
-
}
|
|
1659
|
-
default: {
|
|
1660
|
-
log(`${header(e)} Tool: ${e.toolCall.skillName}/${e.toolCall.toolName}`);
|
|
1661
|
-
debug(`${header(e)} Args: ${JSON.stringify(e.toolCall.args, null, 2)}`);
|
|
1662
|
-
break;
|
|
1663
|
-
}
|
|
1664
|
-
}
|
|
1665
|
-
} else {
|
|
1666
|
-
log(`${header(e)} Tool: ${e.toolCall.skillName}/${e.toolCall.toolName}`);
|
|
1667
|
-
debug(`${header(e)} Args: ${JSON.stringify(e.toolCall.args, null, 2)}`);
|
|
1668
|
-
}
|
|
1669
|
-
break;
|
|
1670
|
-
}
|
|
1671
|
-
case "callInteractiveTool": {
|
|
1672
|
-
log(`${header(e)} Calling interactive tool`);
|
|
1673
|
-
log(`${header(e)} Tool: ${e.toolCall.skillName}/${e.toolCall.toolName}`);
|
|
1674
|
-
debug(`${header(e)} Args: ${JSON.stringify(e.toolCall.args, null, 2)}`);
|
|
1675
|
-
break;
|
|
1676
|
-
}
|
|
1677
|
-
case "callDelegate": {
|
|
1678
|
-
log(`${header(e)} Calling delegate`);
|
|
1679
|
-
log(`${header(e)} Tool: ${e.toolCall.toolName}`);
|
|
1680
|
-
debug(`${header(e)} Args: ${JSON.stringify(e.toolCall.args, null, 2)}`);
|
|
1681
|
-
break;
|
|
1682
|
-
}
|
|
1683
|
-
case "resolveToolResult": {
|
|
1684
|
-
log(`${header(e)} Resolved Tool Result`);
|
|
1685
|
-
if (e.toolResult.skillName === "@perstack/base") {
|
|
1686
|
-
switch (e.toolResult.toolName) {
|
|
1687
|
-
case "todo": {
|
|
1688
|
-
const text = e.toolResult.result.find((r) => r.type === "textPart")?.text;
|
|
1689
|
-
const { todos } = JSON.parse(text ?? "{}");
|
|
1690
|
-
log(`${header(e)} Todo:`);
|
|
1691
|
-
for (const todo of todos) {
|
|
1692
|
-
debug(`${todo.completed ? "[x]" : "[ ]"} ${todo.id}: ${todo.title}`);
|
|
1693
|
-
}
|
|
1694
|
-
break;
|
|
1695
|
-
}
|
|
1696
|
-
default: {
|
|
1697
|
-
log(`${header(e)} Tool: ${e.toolResult.skillName}/${e.toolResult.toolName}`);
|
|
1698
|
-
debug(`${header(e)} Result: ${JSON.stringify(e.toolResult.result, null, 2)}`);
|
|
1699
|
-
break;
|
|
1700
|
-
}
|
|
1701
|
-
}
|
|
1702
|
-
} else {
|
|
1703
|
-
log(`${header(e)} Tool: ${e.toolResult.skillName}/${e.toolResult.toolName}`);
|
|
1704
|
-
debug(`${header(e)} Result: ${JSON.stringify(e.toolResult.result, null, 2)}`);
|
|
1705
|
-
}
|
|
1706
|
-
break;
|
|
1707
|
-
}
|
|
1708
|
-
case "resolveThought": {
|
|
1709
|
-
log(`${header(e)} Resolved Thought:`, e.toolResult);
|
|
1710
|
-
break;
|
|
1711
|
-
}
|
|
1712
|
-
case "resolvePdfFile": {
|
|
1713
|
-
log(`${header(e)} Resolved PDF:`, e.toolResult);
|
|
1714
|
-
break;
|
|
1715
|
-
}
|
|
1716
|
-
case "resolveImageFile": {
|
|
1717
|
-
log(`${header(e)} Resolved Image:`, e.toolResult);
|
|
1718
|
-
break;
|
|
1719
|
-
}
|
|
1720
|
-
case "attemptCompletion": {
|
|
1721
|
-
log(`${header(e)} Attempting completion`);
|
|
1722
|
-
break;
|
|
1723
|
-
}
|
|
1724
|
-
case "completeRun": {
|
|
1725
|
-
logUsage(e);
|
|
1726
|
-
log(`${header(e)} Completing run`);
|
|
1727
|
-
debug(`${header(e)} Result:`, e.text);
|
|
1728
|
-
break;
|
|
1729
|
-
}
|
|
1730
|
-
case "stopRunByInteractiveTool": {
|
|
1731
|
-
logUsage(e);
|
|
1732
|
-
log(`${header(e)} Stopping run by interactive tool`);
|
|
1733
|
-
break;
|
|
1734
|
-
}
|
|
1735
|
-
case "stopRunByDelegate": {
|
|
1736
|
-
logUsage(e);
|
|
1737
|
-
log(`${header(e)} Stopping run by delegate`);
|
|
1738
|
-
break;
|
|
1739
|
-
}
|
|
1740
|
-
case "stopRunByExceededMaxSteps": {
|
|
1741
|
-
logUsage(e);
|
|
1742
|
-
log(`${header(e)} Stopping run by exceeded max steps`);
|
|
1743
|
-
break;
|
|
1744
|
-
}
|
|
1745
|
-
case "continueToNextStep": {
|
|
1746
|
-
logUsage(e);
|
|
1747
|
-
log(`${header(e)} Continuing to next step`);
|
|
1748
|
-
if (e.checkpoint.contextWindowUsage) {
|
|
1749
|
-
log(`${header(e)} Context window usage: ${e.checkpoint.contextWindowUsage.toFixed(2)}%`);
|
|
1750
|
-
}
|
|
1751
|
-
break;
|
|
1752
|
-
}
|
|
1753
|
-
}
|
|
1754
|
-
}
|
|
1755
|
-
function logUsage(e) {
|
|
1756
|
-
const usageByStep = [
|
|
1757
|
-
`In: ${e.step.usage.inputTokens.toLocaleString()}`,
|
|
1758
|
-
`Reasoning: ${e.step.usage.reasoningTokens.toLocaleString()}`,
|
|
1759
|
-
`Out: ${e.step.usage.outputTokens.toLocaleString()}`,
|
|
1760
|
-
`Total: ${e.step.usage.totalTokens.toLocaleString()}`,
|
|
1761
|
-
`Cache-read: ${e.step.usage.cachedInputTokens.toLocaleString()}`
|
|
1762
|
-
].join(", ");
|
|
1763
|
-
const usageByRun = [
|
|
1764
|
-
`In: ${e.checkpoint.usage.inputTokens.toLocaleString()}`,
|
|
1765
|
-
`Reasoning: ${e.checkpoint.usage.reasoningTokens.toLocaleString()}`,
|
|
1766
|
-
`Out: ${e.checkpoint.usage.outputTokens.toLocaleString()}`,
|
|
1767
|
-
`Total: ${e.checkpoint.usage.totalTokens.toLocaleString()}`,
|
|
1768
|
-
`Cache-read: ${e.checkpoint.usage.cachedInputTokens.toLocaleString()}`
|
|
1769
|
-
].join(", ");
|
|
1770
|
-
log(`${header(e)} Tokens usage by step: ${usageByStep}`);
|
|
1771
|
-
log(`${header(e)} Tokens usage by run: ${usageByRun}`);
|
|
1772
|
-
}
|
|
1773
|
-
var RunEventEmitter = class {
|
|
1774
|
-
listeners = [];
|
|
1775
|
-
subscribe(listener) {
|
|
1776
|
-
this.listeners.push(listener);
|
|
1777
|
-
}
|
|
1778
|
-
async emit(event) {
|
|
1779
|
-
for (const listener of this.listeners) {
|
|
1780
|
-
await listener({
|
|
1781
|
-
...event,
|
|
1782
|
-
id: createId(),
|
|
1783
|
-
timestamp: Date.now()
|
|
1784
|
-
});
|
|
1785
|
-
}
|
|
1786
|
-
}
|
|
1787
|
-
};
|
|
1788
|
-
async function resolveExpertToRun(expertKey, experts, clientOptions) {
|
|
1789
|
-
console.log(experts);
|
|
1790
|
-
if (experts[expertKey]) {
|
|
1791
|
-
return experts[expertKey];
|
|
1792
|
-
}
|
|
1793
|
-
const client = new ApiV1Client({
|
|
1794
|
-
baseUrl: clientOptions.perstackApiBaseUrl,
|
|
1795
|
-
apiKey: clientOptions.perstackApiKey
|
|
1796
|
-
});
|
|
1797
|
-
const { expert } = await client.registry.experts.get({ expertKey });
|
|
1798
|
-
experts[expertKey] = toRuntimeExpert(expert);
|
|
1799
|
-
return experts[expertKey];
|
|
1800
|
-
}
|
|
1801
|
-
function toRuntimeExpert(expert) {
|
|
1802
|
-
const skills = Object.fromEntries(
|
|
1803
|
-
Object.entries(expert.skills).map(([name, skill]) => {
|
|
1804
|
-
switch (skill.type) {
|
|
1805
|
-
case "mcpStdioSkill":
|
|
1806
|
-
return [
|
|
1807
|
-
name,
|
|
1808
|
-
{
|
|
1809
|
-
...skill,
|
|
1810
|
-
name
|
|
1811
|
-
}
|
|
1812
|
-
];
|
|
1813
|
-
case "mcpSseSkill":
|
|
1814
|
-
return [
|
|
1815
|
-
name,
|
|
1816
|
-
{
|
|
1817
|
-
...skill,
|
|
1818
|
-
name
|
|
1819
|
-
}
|
|
1820
|
-
];
|
|
1821
|
-
case "interactiveSkill":
|
|
1822
|
-
return [
|
|
1823
|
-
name,
|
|
1824
|
-
{
|
|
1825
|
-
...skill,
|
|
1826
|
-
name
|
|
1827
|
-
}
|
|
1828
|
-
];
|
|
1829
|
-
}
|
|
1830
|
-
})
|
|
1831
|
-
);
|
|
1832
|
-
return {
|
|
1833
|
-
...expert,
|
|
1834
|
-
skills
|
|
1835
|
-
};
|
|
1836
|
-
}
|
|
1732
|
+
var RUNTIME_VERSION = getVersion();
|
|
1837
1733
|
|
|
1838
1734
|
// src/runtime.ts
|
|
1839
1735
|
async function run(runInput, options) {
|
|
1840
1736
|
const runParams = runParamsSchema.parse(runInput);
|
|
1841
|
-
const eventListener = options
|
|
1737
|
+
const eventListener = getEventListener(options);
|
|
1842
1738
|
const retrieveCheckpoint = options?.retrieveCheckpoint ?? defaultRetrieveCheckpoint;
|
|
1843
1739
|
const storeCheckpoint = options?.storeCheckpoint ?? defaultStoreCheckpoint;
|
|
1844
1740
|
const eventEmitter = new RunEventEmitter();
|
|
@@ -1854,8 +1750,27 @@ async function run(runInput, options) {
|
|
|
1854
1750
|
await storeRunSetting(setting);
|
|
1855
1751
|
while (true) {
|
|
1856
1752
|
const { expertToRun, experts } = await setupExperts(setting);
|
|
1857
|
-
|
|
1858
|
-
|
|
1753
|
+
if (options?.eventListener) {
|
|
1754
|
+
const initEvent = createRuntimeEvent("initializeRuntime", setting.runId, {
|
|
1755
|
+
runtimeVersion: RUNTIME_VERSION,
|
|
1756
|
+
expertName: expertToRun.name,
|
|
1757
|
+
experts: Object.keys(experts),
|
|
1758
|
+
model: setting.model,
|
|
1759
|
+
temperature: setting.temperature,
|
|
1760
|
+
maxSteps: setting.maxSteps,
|
|
1761
|
+
maxRetries: setting.maxRetries,
|
|
1762
|
+
timeout: setting.timeout,
|
|
1763
|
+
query: setting.input.text,
|
|
1764
|
+
interactiveToolCall: setting.input.interactiveToolCallResult
|
|
1765
|
+
});
|
|
1766
|
+
options.eventListener(initEvent);
|
|
1767
|
+
}
|
|
1768
|
+
const skillManagers = await getSkillManagers(
|
|
1769
|
+
expertToRun,
|
|
1770
|
+
experts,
|
|
1771
|
+
setting,
|
|
1772
|
+
options?.eventListener
|
|
1773
|
+
);
|
|
1859
1774
|
const runActor = createActor(runtimeStateMachine, {
|
|
1860
1775
|
input: {
|
|
1861
1776
|
setting: {
|
|
@@ -2021,23 +1936,14 @@ async function setupExperts(setting) {
|
|
|
2021
1936
|
}
|
|
2022
1937
|
return { expertToRun, experts };
|
|
2023
1938
|
}
|
|
2024
|
-
function
|
|
2025
|
-
console.log(
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
console.log(`Timeout: ${setting.timeout}`);
|
|
2033
|
-
if (setting.input.text) {
|
|
2034
|
-
console.log(`Query: ${setting.input.text}`);
|
|
2035
|
-
}
|
|
2036
|
-
if (setting.input.interactiveToolCallResult) {
|
|
2037
|
-
console.log(`Tool: ${setting.input.interactiveToolCallResult.toolName}`);
|
|
2038
|
-
console.log(`Tool Call ID: ${setting.input.interactiveToolCallResult.toolCallId}`);
|
|
2039
|
-
console.log(`Tool Result: ${setting.input.interactiveToolCallResult.text}`);
|
|
2040
|
-
}
|
|
1939
|
+
function getEventListener(options) {
|
|
1940
|
+
const listener = options?.eventListener ?? ((e) => console.log(JSON.stringify(e)));
|
|
1941
|
+
return async (event) => {
|
|
1942
|
+
if ("stepNumber" in event) {
|
|
1943
|
+
await defaultStoreEvent(event);
|
|
1944
|
+
}
|
|
1945
|
+
listener(event);
|
|
1946
|
+
};
|
|
2041
1947
|
}
|
|
2042
1948
|
async function storeRunSetting(setting) {
|
|
2043
1949
|
const runDir = getRunDir(setting.runId);
|
|
@@ -2045,20 +1951,16 @@ async function storeRunSetting(setting) {
|
|
|
2045
1951
|
const runSettingPath = path.resolve(runDir, "run-setting.json");
|
|
2046
1952
|
const runSetting = JSON.parse(await readFile(runSettingPath, "utf-8"));
|
|
2047
1953
|
runSetting.updatedAt = Date.now();
|
|
2048
|
-
await writeFile(runSettingPath, JSON.stringify(runSetting
|
|
1954
|
+
await writeFile(runSettingPath, JSON.stringify(runSetting), "utf-8");
|
|
2049
1955
|
} else {
|
|
2050
1956
|
await mkdir(runDir, { recursive: true });
|
|
2051
|
-
await writeFile(
|
|
2052
|
-
path.resolve(runDir, "run-setting.json"),
|
|
2053
|
-
JSON.stringify(setting, null, 2),
|
|
2054
|
-
"utf-8"
|
|
2055
|
-
);
|
|
1957
|
+
await writeFile(path.resolve(runDir, "run-setting.json"), JSON.stringify(setting), "utf-8");
|
|
2056
1958
|
}
|
|
2057
1959
|
}
|
|
2058
1960
|
function getRunDir(runId) {
|
|
2059
1961
|
return `${process.cwd()}/perstack/runs/${runId}`;
|
|
2060
1962
|
}
|
|
2061
1963
|
|
|
2062
|
-
export { StateMachineLogics, calculateContextWindowUsage,
|
|
1964
|
+
export { RUNTIME_VERSION, StateMachineLogics, calculateContextWindowUsage, getContextWindow, getModel, getRunDir, run, runtimeStateMachine };
|
|
2063
1965
|
//# sourceMappingURL=index.js.map
|
|
2064
1966
|
//# sourceMappingURL=index.js.map
|