@perstack/runtime 0.0.23 → 0.0.25
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/dist/index.d.ts +642 -1937
- package/dist/index.js +485 -1213
- package/dist/index.js.map +1 -1
- package/package.json +18 -13
package/dist/index.js
CHANGED
|
@@ -1,828 +1,81 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
import { createId } from
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
import {
|
|
18
|
-
import
|
|
19
|
-
import {
|
|
20
|
-
var DefaultModel = "claude-4-sonnet-20250514";
|
|
21
|
-
var AnthropicModels = [
|
|
22
|
-
"claude-4-opus-20250514",
|
|
23
|
-
"claude-4-sonnet-20250514",
|
|
24
|
-
"claude-3-7-sonnet-20250219",
|
|
25
|
-
"claude-3-5-sonnet-latest",
|
|
26
|
-
"claude-3-5-sonnet-20241022",
|
|
27
|
-
"claude-3-5-sonnet-20240620",
|
|
28
|
-
"claude-3-5-haiku-latest",
|
|
29
|
-
"claude-3-5-haiku-20241022"
|
|
30
|
-
// "claude-3-opus-latest", - No pdf support
|
|
31
|
-
// "claude-3-opus-20240229", - No pdf support
|
|
32
|
-
// "claude-3-sonnet-20240229", - No pdf support
|
|
33
|
-
// "claude-3-haiku-20240307", - No pdf support
|
|
34
|
-
];
|
|
35
|
-
var GoogleModels = [
|
|
36
|
-
"gemini-1.5-flash",
|
|
37
|
-
"gemini-1.5-flash-latest",
|
|
38
|
-
"gemini-1.5-flash-001",
|
|
39
|
-
"gemini-1.5-flash-002",
|
|
40
|
-
"gemini-1.5-flash-8b",
|
|
41
|
-
"gemini-1.5-flash-8b-latest",
|
|
42
|
-
"gemini-1.5-flash-8b-001",
|
|
43
|
-
"gemini-1.5-pro",
|
|
44
|
-
"gemini-1.5-pro-latest",
|
|
45
|
-
"gemini-1.5-pro-001",
|
|
46
|
-
"gemini-1.5-pro-002",
|
|
47
|
-
"gemini-2.0-flash",
|
|
48
|
-
"gemini-2.0-flash-001",
|
|
49
|
-
"gemini-2.0-flash-live-001",
|
|
50
|
-
"gemini-2.0-flash-lite",
|
|
51
|
-
"gemini-2.0-pro-exp-02-05",
|
|
52
|
-
"gemini-2.0-flash-thinking-exp-01-21",
|
|
53
|
-
"gemini-2.0-flash-exp",
|
|
54
|
-
"gemini-2.5-pro",
|
|
55
|
-
"gemini-2.5-flash",
|
|
56
|
-
"gemini-2.5-flash-lite",
|
|
57
|
-
"gemini-2.5-pro-exp-03-25",
|
|
58
|
-
"gemini-2.5-flash-preview-04-17"
|
|
59
|
-
];
|
|
60
|
-
var OpenAIModels = [
|
|
61
|
-
"o4-mini",
|
|
62
|
-
"o4-mini-2025-04-16",
|
|
63
|
-
"o3",
|
|
64
|
-
"o3-2025-04-16",
|
|
65
|
-
"o3-mini",
|
|
66
|
-
"o3-mini-2025-01-31",
|
|
67
|
-
"o1",
|
|
68
|
-
"o1-2024-12-17",
|
|
69
|
-
"o1-mini",
|
|
70
|
-
"o1-mini-2024-09-12",
|
|
71
|
-
// "o1-preview", - No tool support
|
|
72
|
-
// "o1-preview-2024-09-12", - No tool support
|
|
73
|
-
"gpt-4.5-preview",
|
|
74
|
-
"gpt-4.5-preview-2025-02-27",
|
|
75
|
-
"gpt-4.1",
|
|
76
|
-
"gpt-4.1-2025-04-14",
|
|
77
|
-
"gpt-4.1-mini",
|
|
78
|
-
"gpt-4.1-mini-2025-04-14",
|
|
79
|
-
"gpt-4.1-nano",
|
|
80
|
-
"gpt-4.1-nano-2025-04-14",
|
|
81
|
-
"gpt-4o",
|
|
82
|
-
"gpt-4o-2024-05-13",
|
|
83
|
-
"gpt-4o-2024-08-06",
|
|
84
|
-
"gpt-4o-2024-11-20",
|
|
85
|
-
"gpt-4o-audio-preview",
|
|
86
|
-
"gpt-4o-audio-preview-2024-10-01",
|
|
87
|
-
"gpt-4o-audio-preview-2024-12-17",
|
|
88
|
-
"gpt-4o-search-preview",
|
|
89
|
-
"gpt-4o-search-preview-2025-03-11",
|
|
90
|
-
"gpt-4o-mini-search-preview",
|
|
91
|
-
"gpt-4o-mini-search-preview-2025-03-11",
|
|
92
|
-
"gpt-4o-mini",
|
|
93
|
-
"gpt-4o-mini-2024-07-18"
|
|
94
|
-
// "gpt-4-turbo", - Legacy model
|
|
95
|
-
// "gpt-4-turbo-2024-04-09", - Legacy model
|
|
96
|
-
// "gpt-4-turbo-preview", - Legacy model
|
|
97
|
-
// "gpt-4-0125-preview", - No image input support
|
|
98
|
-
// "gpt-4-1106-preview", - No image input support
|
|
99
|
-
// "gpt-4", - No image input support
|
|
100
|
-
// "gpt-4-0613", - No image input support
|
|
101
|
-
// "gpt-3.5-turbo-0125", - Legacy model
|
|
102
|
-
// "gpt-3.5-turbo", - Legacy model
|
|
103
|
-
// "gpt-3.5-turbo-1106", - Legacy model
|
|
104
|
-
// "chatgpt-4o-latest", - No tool support
|
|
105
|
-
];
|
|
106
|
-
var SupportedModels = Object.fromEntries(
|
|
107
|
-
[...AnthropicModels, ...GoogleModels, ...OpenAIModels].map((model) => [
|
|
108
|
-
model,
|
|
109
|
-
{ default: model === DefaultModel, model }
|
|
110
|
-
])
|
|
111
|
-
);
|
|
112
|
-
function getDefaultModelName() {
|
|
113
|
-
const model = Object.values(SupportedModels).find((model2) => model2.default);
|
|
114
|
-
if (!model) {
|
|
115
|
-
throw new Error("No default model found");
|
|
116
|
-
}
|
|
117
|
-
return model.model;
|
|
118
|
-
}
|
|
119
|
-
function getModel(modelId) {
|
|
120
|
-
const unwrappedModelId = modelId ?? getDefaultModelName();
|
|
121
|
-
if (AnthropicModels.includes(unwrappedModelId)) {
|
|
122
|
-
return anthropic(unwrappedModelId);
|
|
123
|
-
}
|
|
124
|
-
if (GoogleModels.includes(unwrappedModelId)) {
|
|
125
|
-
return google(unwrappedModelId);
|
|
126
|
-
}
|
|
127
|
-
if (OpenAIModels.includes(unwrappedModelId)) {
|
|
128
|
-
return openai(unwrappedModelId);
|
|
129
|
-
}
|
|
130
|
-
throw new Error(`Unsupported model: ${unwrappedModelId}`);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// src/schemas/messages.ts
|
|
134
|
-
import { z } from "zod";
|
|
135
|
-
var BasePartSchema = z.object({
|
|
136
|
-
id: z.string()
|
|
137
|
-
});
|
|
138
|
-
var TextPartSchema = BasePartSchema.extend({
|
|
139
|
-
type: z.literal("textPart"),
|
|
140
|
-
text: z.string()
|
|
141
|
-
});
|
|
142
|
-
var ImageUrlPartSchema = BasePartSchema.extend({
|
|
143
|
-
type: z.literal("imageUrlPart"),
|
|
144
|
-
url: z.string().url(),
|
|
145
|
-
mimeType: z.string()
|
|
146
|
-
});
|
|
147
|
-
var ImageInlinePartSchema = BasePartSchema.extend({
|
|
148
|
-
type: z.literal("imageInlinePart"),
|
|
149
|
-
encodedData: z.string(),
|
|
150
|
-
mimeType: z.string()
|
|
151
|
-
});
|
|
152
|
-
var ImageBinaryPartSchema = BasePartSchema.extend({
|
|
153
|
-
type: z.literal("imageBinaryPart"),
|
|
154
|
-
data: z.string(),
|
|
155
|
-
mimeType: z.string()
|
|
156
|
-
});
|
|
157
|
-
var FileUrlPartSchema = BasePartSchema.extend({
|
|
158
|
-
type: z.literal("fileUrlPart"),
|
|
159
|
-
url: z.string().url(),
|
|
160
|
-
mimeType: z.string()
|
|
161
|
-
});
|
|
162
|
-
var FileInlinePartSchema = BasePartSchema.extend({
|
|
163
|
-
type: z.literal("fileInlinePart"),
|
|
164
|
-
encodedData: z.string(),
|
|
165
|
-
mimeType: z.string()
|
|
166
|
-
});
|
|
167
|
-
var FileBinaryPartSchema = BasePartSchema.extend({
|
|
168
|
-
type: z.literal("fileBinaryPart"),
|
|
169
|
-
data: z.string(),
|
|
170
|
-
mimeType: z.string()
|
|
171
|
-
});
|
|
172
|
-
var ToolCallPartSchema = BasePartSchema.extend({
|
|
173
|
-
type: z.literal("toolCallPart"),
|
|
174
|
-
toolCallId: z.string(),
|
|
175
|
-
toolName: z.string(),
|
|
176
|
-
args: z.unknown()
|
|
177
|
-
});
|
|
178
|
-
var ToolResultPartSchema = BasePartSchema.extend({
|
|
179
|
-
type: z.literal("toolResultPart"),
|
|
180
|
-
toolCallId: z.string(),
|
|
181
|
-
toolName: z.string(),
|
|
182
|
-
contents: z.array(z.union([TextPartSchema, ImageInlinePartSchema])),
|
|
183
|
-
isError: z.boolean().optional()
|
|
184
|
-
});
|
|
185
|
-
var BaseMessageSchema = z.object({
|
|
186
|
-
id: z.string()
|
|
187
|
-
});
|
|
188
|
-
var InstructionMessageSchema = BaseMessageSchema.extend({
|
|
189
|
-
type: z.literal("instructionMessage"),
|
|
190
|
-
contents: z.array(TextPartSchema),
|
|
191
|
-
cache: z.boolean().optional()
|
|
192
|
-
});
|
|
193
|
-
var UserMessageSchema = BaseMessageSchema.extend({
|
|
194
|
-
type: z.literal("userMessage"),
|
|
195
|
-
contents: z.array(
|
|
196
|
-
z.union([
|
|
197
|
-
TextPartSchema,
|
|
198
|
-
ImageUrlPartSchema,
|
|
199
|
-
ImageInlinePartSchema,
|
|
200
|
-
ImageBinaryPartSchema,
|
|
201
|
-
FileUrlPartSchema,
|
|
202
|
-
FileInlinePartSchema,
|
|
203
|
-
FileBinaryPartSchema
|
|
204
|
-
])
|
|
205
|
-
),
|
|
206
|
-
cache: z.boolean().optional()
|
|
207
|
-
});
|
|
208
|
-
var ExpertMessageSchema = BaseMessageSchema.extend({
|
|
209
|
-
type: z.literal("expertMessage"),
|
|
210
|
-
contents: z.array(z.union([TextPartSchema, ToolCallPartSchema])),
|
|
211
|
-
cache: z.boolean().optional()
|
|
212
|
-
});
|
|
213
|
-
var ToolMessageSchema = BaseMessageSchema.extend({
|
|
214
|
-
type: z.literal("toolMessage"),
|
|
215
|
-
contents: z.array(ToolResultPartSchema),
|
|
216
|
-
cache: z.boolean().optional()
|
|
217
|
-
});
|
|
218
|
-
var MessageSchema = z.union([
|
|
219
|
-
InstructionMessageSchema,
|
|
220
|
-
UserMessageSchema,
|
|
221
|
-
ExpertMessageSchema,
|
|
222
|
-
ToolMessageSchema
|
|
223
|
-
]);
|
|
224
|
-
|
|
225
|
-
// src/schemas/runtime.ts
|
|
226
|
-
var expertNameRegex = /^(@[a-z0-9][a-z0-9_-]*\/)?[a-z0-9][a-z0-9_-]*$/;
|
|
227
|
-
var versionRegex = /^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-[\w.-]+)?(?:\+[\w.-]+)?$/;
|
|
228
|
-
var tagNameRegex = /^[a-z0-9][a-z0-9_-]*$/;
|
|
229
|
-
var expertKeyRegex = /^((?:@[a-z0-9][a-z0-9_\.-]*\/)?[a-z0-9][a-z0-9_\.-]*)(?:@((?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-[\w.-]+)?(?:\+[\w.-]+)?)|@([a-z0-9][a-z0-9_\.-]*))?$/;
|
|
230
|
-
var skillNameRegex = /^[a-z0-9][a-z0-9._-]*$/;
|
|
231
|
-
var maxNameLength = 214;
|
|
232
|
-
function parseExpertKey(expertKey) {
|
|
233
|
-
const match = expertKey.match(expertKeyRegex);
|
|
234
|
-
if (!match) {
|
|
235
|
-
throw new Error(`Invalid expert key format: ${expertKey}`);
|
|
236
|
-
}
|
|
237
|
-
const [key, name, version, tag] = match;
|
|
238
|
-
return {
|
|
239
|
-
key,
|
|
240
|
-
name,
|
|
241
|
-
version,
|
|
242
|
-
tag
|
|
243
|
-
};
|
|
244
|
-
}
|
|
245
|
-
var McpStdioSkillSchema = z2.object({
|
|
246
|
-
type: z2.literal("mcpStdioSkill"),
|
|
247
|
-
name: z2.string(),
|
|
248
|
-
description: z2.string().optional(),
|
|
249
|
-
rule: z2.string().optional(),
|
|
250
|
-
pick: z2.array(z2.string()).optional().default([]),
|
|
251
|
-
omit: z2.array(z2.string()).optional().default([]),
|
|
252
|
-
command: z2.string(),
|
|
253
|
-
packageName: z2.string().optional(),
|
|
254
|
-
args: z2.array(z2.string()).optional().default([]),
|
|
255
|
-
requiredEnv: z2.array(z2.string()).optional().default([])
|
|
256
|
-
});
|
|
257
|
-
var McpSseSkillSchema = z2.object({
|
|
258
|
-
type: z2.literal("mcpSseSkill"),
|
|
259
|
-
name: z2.string(),
|
|
260
|
-
description: z2.string().optional(),
|
|
261
|
-
rule: z2.string().optional(),
|
|
262
|
-
pick: z2.array(z2.string()).optional().default([]),
|
|
263
|
-
omit: z2.array(z2.string()).optional().default([]),
|
|
264
|
-
endpoint: z2.string()
|
|
265
|
-
});
|
|
266
|
-
var InteractiveToolSchema = z2.object({
|
|
267
|
-
name: z2.string(),
|
|
268
|
-
description: z2.string().optional(),
|
|
269
|
-
inputJsonSchema: z2.string()
|
|
270
|
-
});
|
|
271
|
-
var InteractiveSkillSchema = z2.object({
|
|
272
|
-
type: z2.literal("interactiveSkill"),
|
|
273
|
-
name: z2.string(),
|
|
274
|
-
description: z2.string().optional(),
|
|
275
|
-
rule: z2.string().optional(),
|
|
276
|
-
tools: z2.record(z2.string(), InteractiveToolSchema.omit({ name: true })).transform((tools) => {
|
|
277
|
-
return Object.fromEntries(
|
|
278
|
-
Object.entries(tools).map(([key, toolWithoutName]) => [
|
|
279
|
-
key,
|
|
280
|
-
InteractiveToolSchema.parse({ ...toolWithoutName, name: key })
|
|
281
|
-
])
|
|
282
|
-
);
|
|
283
|
-
})
|
|
284
|
-
});
|
|
285
|
-
var ExpertSchema = z2.object({
|
|
286
|
-
key: z2.string().regex(expertKeyRegex).min(1),
|
|
287
|
-
name: z2.string().regex(expertNameRegex).min(1).max(maxNameLength),
|
|
288
|
-
version: z2.string().regex(versionRegex),
|
|
289
|
-
description: z2.string().min(1).max(1024 * 2).optional(),
|
|
290
|
-
instruction: z2.string().min(1).max(1024 * 20),
|
|
291
|
-
skills: z2.record(
|
|
292
|
-
z2.string(),
|
|
293
|
-
z2.discriminatedUnion("type", [
|
|
294
|
-
McpStdioSkillSchema.omit({ name: true }),
|
|
295
|
-
McpSseSkillSchema.omit({ name: true }),
|
|
296
|
-
InteractiveSkillSchema.omit({ name: true })
|
|
297
|
-
])
|
|
298
|
-
).optional().default({
|
|
299
|
-
"@perstack/base": {
|
|
300
|
-
type: "mcpStdioSkill",
|
|
301
|
-
description: "Base skill",
|
|
302
|
-
command: "npx",
|
|
303
|
-
args: ["-y", "@perstack/base"],
|
|
304
|
-
pick: [],
|
|
305
|
-
omit: [],
|
|
306
|
-
requiredEnv: []
|
|
307
|
-
}
|
|
308
|
-
}).transform((skills) => {
|
|
309
|
-
return Object.fromEntries(
|
|
310
|
-
Object.entries(skills).map(([key, skillWithoutName]) => [
|
|
311
|
-
key,
|
|
312
|
-
z2.discriminatedUnion("type", [
|
|
313
|
-
McpStdioSkillSchema,
|
|
314
|
-
McpSseSkillSchema,
|
|
315
|
-
InteractiveSkillSchema
|
|
316
|
-
]).parse({ ...skillWithoutName, name: key })
|
|
317
|
-
])
|
|
318
|
-
);
|
|
319
|
-
}),
|
|
320
|
-
delegates: z2.array(z2.string().regex(expertKeyRegex).min(1)).optional().default([]),
|
|
321
|
-
tags: z2.array(z2.string().regex(tagNameRegex).min(1)).optional().default([])
|
|
322
|
-
});
|
|
323
|
-
var UsageSchema = z2.object({
|
|
324
|
-
promptTokens: z2.number(),
|
|
325
|
-
completionTokens: z2.number(),
|
|
326
|
-
totalTokens: z2.number(),
|
|
327
|
-
cacheCreationInputTokens: z2.number(),
|
|
328
|
-
cacheReadInputTokens: z2.number()
|
|
329
|
-
});
|
|
330
|
-
var CheckpointStatusSchema = z2.enum([
|
|
331
|
-
"init",
|
|
332
|
-
"proceeding",
|
|
333
|
-
"completed",
|
|
334
|
-
"stoppedByInteractiveTool",
|
|
335
|
-
"stoppedByDelegate",
|
|
336
|
-
"stoppedByExceededMaxSteps",
|
|
337
|
-
"stoppedByError"
|
|
338
|
-
]);
|
|
339
|
-
var CheckpointSchema = z2.object({
|
|
340
|
-
id: z2.string(),
|
|
341
|
-
runId: z2.string(),
|
|
342
|
-
status: CheckpointStatusSchema,
|
|
343
|
-
stepNumber: z2.number(),
|
|
344
|
-
messages: z2.array(MessageSchema),
|
|
345
|
-
expert: z2.object({
|
|
346
|
-
key: z2.string(),
|
|
347
|
-
name: z2.string(),
|
|
348
|
-
version: z2.string()
|
|
349
|
-
}),
|
|
350
|
-
delegateTo: z2.object({
|
|
351
|
-
expert: z2.object({
|
|
352
|
-
key: z2.string(),
|
|
353
|
-
name: z2.string(),
|
|
354
|
-
version: z2.string()
|
|
355
|
-
}),
|
|
356
|
-
toolCallId: z2.string(),
|
|
357
|
-
toolName: z2.string(),
|
|
358
|
-
query: z2.string()
|
|
359
|
-
}).optional(),
|
|
360
|
-
delegatedBy: z2.object({
|
|
361
|
-
expert: z2.object({
|
|
362
|
-
key: z2.string(),
|
|
363
|
-
name: z2.string(),
|
|
364
|
-
version: z2.string()
|
|
365
|
-
}),
|
|
366
|
-
toolCallId: z2.string(),
|
|
367
|
-
toolName: z2.string(),
|
|
368
|
-
checkpointId: z2.string()
|
|
369
|
-
}).optional(),
|
|
370
|
-
usage: UsageSchema
|
|
371
|
-
});
|
|
372
|
-
var RunParamsSchema = z2.object({
|
|
373
|
-
setting: z2.object({
|
|
374
|
-
runId: z2.string().optional().transform((value) => value ?? createId()),
|
|
375
|
-
expertKey: z2.string().regex(expertKeyRegex).min(1),
|
|
376
|
-
input: z2.object({
|
|
377
|
-
text: z2.string().optional(),
|
|
378
|
-
interactiveToolCallResult: z2.object({
|
|
379
|
-
toolCallId: z2.string(),
|
|
380
|
-
toolName: z2.string(),
|
|
381
|
-
text: z2.string()
|
|
382
|
-
}).optional()
|
|
383
|
-
}),
|
|
384
|
-
experts: z2.record(z2.string().regex(expertKeyRegex).min(1), ExpertSchema.omit({ key: true })).optional().default({}).transform(
|
|
385
|
-
(experts) => Object.fromEntries(
|
|
386
|
-
Object.entries(experts).map(([key, expertWithoutKey]) => [
|
|
387
|
-
key,
|
|
388
|
-
ExpertSchema.parse({
|
|
389
|
-
...expertWithoutKey,
|
|
390
|
-
key
|
|
391
|
-
})
|
|
392
|
-
])
|
|
393
|
-
)
|
|
394
|
-
),
|
|
395
|
-
model: z2.string().optional().default(getDefaultModelName()),
|
|
396
|
-
temperature: z2.number().min(0).max(1).optional().default(0.3),
|
|
397
|
-
maxSteps: z2.number().min(1).optional(),
|
|
398
|
-
maxRetries: z2.number().min(0).optional().default(5),
|
|
399
|
-
timeout: z2.number().min(0).optional().default(1e3 * 60),
|
|
400
|
-
workspace: z2.string().optional(),
|
|
401
|
-
startedAt: z2.number().optional().default(Date.now()),
|
|
402
|
-
updatedAt: z2.number().optional().default(Date.now())
|
|
403
|
-
}),
|
|
404
|
-
checkpoint: CheckpointSchema.optional()
|
|
405
|
-
});
|
|
406
|
-
function createEvent(type) {
|
|
407
|
-
return (setting, checkpoint, data) => {
|
|
408
|
-
return {
|
|
409
|
-
type,
|
|
410
|
-
id: createId(),
|
|
411
|
-
expertKey: checkpoint.expert.key,
|
|
412
|
-
timestamp: Date.now(),
|
|
413
|
-
runId: setting.runId,
|
|
414
|
-
stepNumber: checkpoint.stepNumber,
|
|
415
|
-
...data
|
|
416
|
-
};
|
|
417
|
-
};
|
|
418
|
-
}
|
|
419
|
-
var startRun = createEvent("startRun");
|
|
420
|
-
var startGeneration = createEvent("startGeneration");
|
|
421
|
-
var retry = createEvent("retry");
|
|
422
|
-
var callTool = createEvent("callTool");
|
|
423
|
-
var callInteractiveTool = createEvent("callInteractiveTool");
|
|
424
|
-
var callDelegate = createEvent("callDelegate");
|
|
425
|
-
var resolveToolResult = createEvent("resolveToolResult");
|
|
426
|
-
var resolveThought = createEvent("resolveThought");
|
|
427
|
-
var resolvePdfFile = createEvent("resolvePdfFile");
|
|
428
|
-
var resolveImageFile = createEvent("resolveImageFile");
|
|
429
|
-
var attemptCompletion = createEvent("attemptCompletion");
|
|
430
|
-
var finishToolCall = createEvent("finishToolCall");
|
|
431
|
-
var completeRun = createEvent("completeRun");
|
|
432
|
-
var stopRunByInteractiveTool = createEvent("stopRunByInteractiveTool");
|
|
433
|
-
var stopRunByDelegate = createEvent("stopRunByDelegate");
|
|
434
|
-
var stopRunByExceededMaxSteps = createEvent("stopRunByExceededMaxSteps");
|
|
435
|
-
var continueToNextStep = createEvent("continueToNextStep");
|
|
436
|
-
|
|
437
|
-
// src/default-store.ts
|
|
438
|
-
async function defaultRetrieveCheckpoint(runId, checkpointId) {
|
|
439
|
-
const runDir = getRunDir(runId);
|
|
440
|
-
const checkpointFiles = await readdir(runDir, { withFileTypes: true }).then(
|
|
441
|
-
(files) => files.filter((file) => file.isFile() && file.name.startsWith("checkpoint-"))
|
|
442
|
-
);
|
|
443
|
-
const checkpointFile = checkpointFiles.find((file) => file.name.endsWith(`-${checkpointId}.json`));
|
|
444
|
-
if (!checkpointFile) {
|
|
445
|
-
throw new Error(`checkpoint not found: ${runId} ${checkpointId}`);
|
|
446
|
-
}
|
|
447
|
-
const checkpointPath = `${runDir}/${checkpointFile.name}`;
|
|
448
|
-
const checkpoint = await readFile(checkpointPath, "utf8");
|
|
449
|
-
return CheckpointSchema.parse(JSON.parse(checkpoint));
|
|
450
|
-
}
|
|
451
|
-
async function defaultStoreCheckpoint(checkpoint, timestamp) {
|
|
452
|
-
const { id, runId, stepNumber } = checkpoint;
|
|
453
|
-
const runDir = getRunDir(runId);
|
|
454
|
-
const checkpointPath = `${runDir}/checkpoint-${timestamp}-${stepNumber}-${id}.json`;
|
|
455
|
-
await mkdir(runDir, { recursive: true });
|
|
456
|
-
await writeFile(checkpointPath, JSON.stringify(checkpoint, null, 2));
|
|
457
|
-
}
|
|
458
|
-
async function defaultStoreEvent(event) {
|
|
459
|
-
const { timestamp, runId, stepNumber, type } = event;
|
|
460
|
-
const runDir = getRunDir(runId);
|
|
461
|
-
const eventPath = `${runDir}/event-${timestamp}-${stepNumber}-${type}.json`;
|
|
462
|
-
await mkdir(runDir, { recursive: true });
|
|
463
|
-
await writeFile(eventPath, JSON.stringify(event, null, 2));
|
|
464
|
-
}
|
|
1
|
+
import { setup, assign, createActor } from 'xstate';
|
|
2
|
+
import { createAmazonBedrock } from '@ai-sdk/amazon-bedrock';
|
|
3
|
+
import { createAnthropic } from '@ai-sdk/anthropic';
|
|
4
|
+
import { createAzure } from '@ai-sdk/azure';
|
|
5
|
+
import { createGoogleGenerativeAI } from '@ai-sdk/google';
|
|
6
|
+
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';
|
|
8
|
+
import { createOllama } from 'ollama-ai-provider-v2';
|
|
9
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
10
|
+
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
|
|
11
|
+
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
12
|
+
import { McpError } from '@modelcontextprotocol/sdk/types.js';
|
|
13
|
+
import { createId } from '@paralleldrive/cuid2';
|
|
14
|
+
import { generateText, tool, jsonSchema } from 'ai';
|
|
15
|
+
import { dedent } from 'ts-dedent';
|
|
16
|
+
import { readFile, writeFile, mkdir, readdir } from 'fs/promises';
|
|
17
|
+
import { existsSync } from 'fs';
|
|
18
|
+
import path from 'path';
|
|
19
|
+
import { ApiV1Client } from '@perstack/api-client/v1';
|
|
465
20
|
|
|
466
|
-
//
|
|
467
|
-
|
|
468
|
-
name
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
".": "./src/index.ts"
|
|
476
|
-
},
|
|
477
|
-
publishConfig: {
|
|
478
|
-
exports: {
|
|
479
|
-
".": "./dist/index.js"
|
|
480
|
-
},
|
|
481
|
-
types: "./dist/index.d.ts"
|
|
482
|
-
},
|
|
483
|
-
files: [
|
|
484
|
-
"dist"
|
|
485
|
-
],
|
|
486
|
-
scripts: {
|
|
487
|
-
clean: "rm -rf dist",
|
|
488
|
-
dev: "tsup --watch --config ../../tsup.config.ts",
|
|
489
|
-
build: "npm run clean && tsup --config ../../tsup.config.ts",
|
|
490
|
-
prepublishOnly: "npm run clean && npm run build"
|
|
491
|
-
},
|
|
492
|
-
dependencies: {
|
|
493
|
-
"@ai-sdk/anthropic": "^2.0.1",
|
|
494
|
-
"@ai-sdk/google": "^2.0.2",
|
|
495
|
-
"@ai-sdk/openai": "^2.0.3",
|
|
496
|
-
"@modelcontextprotocol/sdk": "^1.17.1",
|
|
497
|
-
"@paralleldrive/cuid2": "^2.2.2",
|
|
498
|
-
ai: "^5.0.4",
|
|
499
|
-
"smol-toml": "^1.4.1",
|
|
500
|
-
"ts-dedent": "^2.2.0",
|
|
501
|
-
xstate: "^5.20.1",
|
|
502
|
-
zod: "^4.0.14"
|
|
503
|
-
},
|
|
504
|
-
devDependencies: {
|
|
505
|
-
"@tsconfig/node22": "^22.0.2",
|
|
506
|
-
"@types/node": "^24.2.0",
|
|
507
|
-
tsup: "^8.5.0",
|
|
508
|
-
typescript: "^5.9.2",
|
|
509
|
-
vitest: "^3.2.4"
|
|
510
|
-
},
|
|
511
|
-
engines: {
|
|
512
|
-
node: ">=22.0.0"
|
|
513
|
-
}
|
|
514
|
-
};
|
|
515
|
-
|
|
516
|
-
// src/events/default-event-listener.ts
|
|
517
|
-
var log = console.info;
|
|
518
|
-
var debug = console.debug;
|
|
519
|
-
var header = (e) => {
|
|
520
|
-
const t = (/* @__PURE__ */ new Date()).toISOString();
|
|
521
|
-
const stepNumber = e.stepNumber;
|
|
522
|
-
const key = e.expertKey;
|
|
523
|
-
return `${t} ${stepNumber} ${key}`;
|
|
524
|
-
};
|
|
525
|
-
async function defaultEventListener(e) {
|
|
526
|
-
await defaultStoreEvent(e);
|
|
527
|
-
switch (e.type) {
|
|
528
|
-
case "startRun": {
|
|
529
|
-
log(`${header(e)} Perstack@${package_default.version} started`);
|
|
530
|
-
const { inputMessages } = e;
|
|
531
|
-
for (const message of inputMessages) {
|
|
532
|
-
if (message.type === "userMessage") {
|
|
533
|
-
logUserMessage(message);
|
|
534
|
-
} else if (message.type === "toolMessage") {
|
|
535
|
-
logToolMessage(message);
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
break;
|
|
539
|
-
}
|
|
540
|
-
case "startGeneration": {
|
|
541
|
-
log(`${header(e)} Generating tool call`);
|
|
542
|
-
break;
|
|
543
|
-
}
|
|
544
|
-
case "retry": {
|
|
545
|
-
logUsage(e);
|
|
546
|
-
log(`${header(e)} Retrying tool call generation`);
|
|
547
|
-
break;
|
|
548
|
-
}
|
|
549
|
-
case "callTool": {
|
|
550
|
-
logUsage(e);
|
|
551
|
-
log(`${header(e)} Calling tool`);
|
|
552
|
-
if (e.toolCall.skillName === "@perstack/base") {
|
|
553
|
-
switch (e.toolCall.toolName) {
|
|
554
|
-
case "think": {
|
|
555
|
-
const thought = e.toolCall.args.thought;
|
|
556
|
-
log(`${header(e)} Thought Updated:`);
|
|
557
|
-
debug(thought);
|
|
558
|
-
break;
|
|
559
|
-
}
|
|
560
|
-
case "readPdfFile": {
|
|
561
|
-
const path2 = e.toolCall.args.path;
|
|
562
|
-
log(`${header(e)} Reading PDF: ${path2}`);
|
|
563
|
-
break;
|
|
564
|
-
}
|
|
565
|
-
case "readImageFile": {
|
|
566
|
-
const path2 = e.toolCall.args.path;
|
|
567
|
-
log(`${header(e)} Reading Image: ${path2}`);
|
|
568
|
-
break;
|
|
569
|
-
}
|
|
570
|
-
default: {
|
|
571
|
-
log(`${header(e)} Tool: ${e.toolCall.skillName}/${e.toolCall.toolName}`);
|
|
572
|
-
debug(`${header(e)} Args: ${JSON.stringify(e.toolCall.args, null, 2)}`);
|
|
573
|
-
break;
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
} else {
|
|
577
|
-
log(`${header(e)} Tool: ${e.toolCall.skillName}/${e.toolCall.toolName}`);
|
|
578
|
-
debug(`${header(e)} Args: ${JSON.stringify(e.toolCall.args, null, 2)}`);
|
|
579
|
-
}
|
|
580
|
-
break;
|
|
581
|
-
}
|
|
582
|
-
case "callInteractiveTool": {
|
|
583
|
-
logUsage(e);
|
|
584
|
-
log(`${header(e)} Calling interactive tool`);
|
|
585
|
-
log(`${header(e)} Tool: ${e.toolCall.skillName}/${e.toolCall.toolName}`);
|
|
586
|
-
debug(`${header(e)} Args: ${JSON.stringify(e.toolCall.args, null, 2)}`);
|
|
587
|
-
break;
|
|
588
|
-
}
|
|
589
|
-
case "callDelegate": {
|
|
590
|
-
logUsage(e);
|
|
591
|
-
log(`${header(e)} Calling delegate`);
|
|
592
|
-
log(`${header(e)} Tool: ${e.toolCall.toolName}`);
|
|
593
|
-
debug(`${header(e)} Args: ${JSON.stringify(e.toolCall.args, null, 2)}`);
|
|
594
|
-
break;
|
|
595
|
-
}
|
|
596
|
-
case "resolveToolResult": {
|
|
597
|
-
log(`${header(e)} Resolved Tool Result`);
|
|
598
|
-
if (e.toolResult.skillName === "@perstack/base") {
|
|
599
|
-
switch (e.toolResult.toolName) {
|
|
600
|
-
case "todo": {
|
|
601
|
-
const text = e.toolResult.result.find((r) => r.type === "textPart")?.text;
|
|
602
|
-
const { todos } = JSON.parse(text ?? "{}");
|
|
603
|
-
log(`${header(e)} Todo:`);
|
|
604
|
-
for (const todo of todos) {
|
|
605
|
-
debug(`${todo.completed ? "[x]" : "[ ]"} ${todo.id}: ${todo.title}`);
|
|
606
|
-
}
|
|
607
|
-
break;
|
|
608
|
-
}
|
|
609
|
-
default: {
|
|
610
|
-
log(`${header(e)} Tool: ${e.toolResult.skillName}/${e.toolResult.toolName}`);
|
|
611
|
-
debug(`${header(e)} Result: ${JSON.stringify(e.toolResult.result, null, 2)}`);
|
|
612
|
-
break;
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
} else {
|
|
616
|
-
log(`${header(e)} Tool: ${e.toolResult.skillName}/${e.toolResult.toolName}`);
|
|
617
|
-
debug(`${header(e)} Result: ${JSON.stringify(e.toolResult.result, null, 2)}`);
|
|
618
|
-
}
|
|
619
|
-
break;
|
|
620
|
-
}
|
|
621
|
-
case "resolveThought": {
|
|
622
|
-
log(`${header(e)} Resolved Thought:`, e.toolResult);
|
|
623
|
-
break;
|
|
624
|
-
}
|
|
625
|
-
case "resolvePdfFile": {
|
|
626
|
-
log(`${header(e)} Resolved PDF:`, e.toolResult);
|
|
627
|
-
break;
|
|
628
|
-
}
|
|
629
|
-
case "resolveImageFile": {
|
|
630
|
-
log(`${header(e)} Resolved Image:`, e.toolResult);
|
|
631
|
-
break;
|
|
632
|
-
}
|
|
633
|
-
case "attemptCompletion": {
|
|
634
|
-
log(`${header(e)} Attempting completion`);
|
|
635
|
-
break;
|
|
636
|
-
}
|
|
637
|
-
case "completeRun": {
|
|
638
|
-
logUsage(e);
|
|
639
|
-
log(`${header(e)} Completing run`);
|
|
640
|
-
debug(`${header(e)} Result:`, e.text);
|
|
641
|
-
break;
|
|
642
|
-
}
|
|
643
|
-
case "stopRunByInteractiveTool": {
|
|
644
|
-
log(`${header(e)} Stopping run by interactive tool`);
|
|
645
|
-
break;
|
|
646
|
-
}
|
|
647
|
-
case "stopRunByDelegate": {
|
|
648
|
-
log(`${header(e)} Stopping run by delegate`);
|
|
649
|
-
break;
|
|
650
|
-
}
|
|
651
|
-
case "stopRunByExceededMaxSteps": {
|
|
652
|
-
log(`${header(e)} Stopping run by exceeded max steps`);
|
|
653
|
-
break;
|
|
21
|
+
// src/runtime-state-machine.ts
|
|
22
|
+
function getModel(modelId, providerConfig) {
|
|
23
|
+
switch (providerConfig.name) {
|
|
24
|
+
case "anthropic": {
|
|
25
|
+
const anthropic = createAnthropic({
|
|
26
|
+
apiKey: providerConfig.apiKey,
|
|
27
|
+
baseURL: providerConfig.baseUrl
|
|
28
|
+
});
|
|
29
|
+
return anthropic(modelId);
|
|
654
30
|
}
|
|
655
|
-
case "
|
|
656
|
-
|
|
657
|
-
|
|
31
|
+
case "google": {
|
|
32
|
+
const google = createGoogleGenerativeAI({
|
|
33
|
+
apiKey: providerConfig.apiKey,
|
|
34
|
+
baseURL: providerConfig.baseUrl
|
|
35
|
+
});
|
|
36
|
+
return google(modelId);
|
|
37
|
+
}
|
|
38
|
+
case "openai": {
|
|
39
|
+
const openai = createOpenAI({
|
|
40
|
+
apiKey: providerConfig.apiKey,
|
|
41
|
+
baseURL: providerConfig.baseUrl,
|
|
42
|
+
organization: providerConfig.organization,
|
|
43
|
+
project: providerConfig.project
|
|
44
|
+
});
|
|
45
|
+
return openai(modelId);
|
|
658
46
|
}
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
for (const content of contents) {
|
|
665
|
-
if (content.type === "textPart") {
|
|
666
|
-
log(`${t} User: ${content.text}`);
|
|
667
|
-
} else if (content.type === "imageUrlPart") {
|
|
668
|
-
log(`${t} User: ${content.url}`);
|
|
669
|
-
} else if (content.type === "imageInlinePart") {
|
|
670
|
-
log(`${t} User: Inline image`);
|
|
671
|
-
} else if (content.type === "imageBinaryPart") {
|
|
672
|
-
log(`${t} User: Binary image`);
|
|
673
|
-
} else if (content.type === "fileUrlPart") {
|
|
674
|
-
log(`${t} User: ${content.url}`);
|
|
675
|
-
} else if (content.type === "fileInlinePart") {
|
|
676
|
-
log(`${t} User: Inline file`);
|
|
677
|
-
} else if (content.type === "fileBinaryPart") {
|
|
678
|
-
log(`${t} User: Binary file`);
|
|
47
|
+
case "ollama": {
|
|
48
|
+
const ollama = createOllama({
|
|
49
|
+
baseURL: providerConfig.baseUrl
|
|
50
|
+
});
|
|
51
|
+
return ollama(modelId);
|
|
679
52
|
}
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
}
|
|
53
|
+
case "azure-openai": {
|
|
54
|
+
const azure = createAzure({
|
|
55
|
+
apiKey: providerConfig.apiKey,
|
|
56
|
+
resourceName: providerConfig.resourceName,
|
|
57
|
+
apiVersion: providerConfig.apiVersion
|
|
58
|
+
});
|
|
59
|
+
return azure(modelId);
|
|
60
|
+
}
|
|
61
|
+
case "amazon-bedrock": {
|
|
62
|
+
const amazonBedrock = createAmazonBedrock({
|
|
63
|
+
accessKeyId: providerConfig.accessKeyId,
|
|
64
|
+
secretAccessKey: providerConfig.secretAccessKey,
|
|
65
|
+
region: providerConfig.region,
|
|
66
|
+
sessionToken: providerConfig.sessionToken
|
|
67
|
+
});
|
|
68
|
+
return amazonBedrock(modelId);
|
|
695
69
|
}
|
|
696
70
|
}
|
|
697
71
|
}
|
|
698
|
-
function
|
|
699
|
-
const
|
|
700
|
-
|
|
701
|
-
`Out: ${e.usage.completionTokens.toLocaleString()}`,
|
|
702
|
-
`Total: ${e.usage.totalTokens.toLocaleString()}`,
|
|
703
|
-
`Cache-read: ${e.usage.cacheReadInputTokens.toLocaleString()}`,
|
|
704
|
-
`Cache-write: ${e.usage.cacheCreationInputTokens.toLocaleString()}`
|
|
705
|
-
].join(", ");
|
|
706
|
-
log(`${header(e)} Tokens usage: ${usageByGeneration}`);
|
|
707
|
-
const usageByRun = [
|
|
708
|
-
`In: ${e.usage.promptTokens.toLocaleString()}`,
|
|
709
|
-
`Out: ${e.usage.completionTokens.toLocaleString()}`,
|
|
710
|
-
`Total: ${e.usage.totalTokens.toLocaleString()}`,
|
|
711
|
-
`Cache-read: ${e.usage.cacheReadInputTokens.toLocaleString()}`,
|
|
712
|
-
`Cache-write: ${e.usage.cacheCreationInputTokens.toLocaleString()}`
|
|
713
|
-
].join(", ");
|
|
714
|
-
log(`${header(e)} Total usage: ${usageByRun}`);
|
|
72
|
+
function getContextWindow(providerName, modelId) {
|
|
73
|
+
const modelConfig = knownModels.find((model) => model.provider === providerName)?.models.find((model) => model.name === modelId);
|
|
74
|
+
return modelConfig?.contextWindow;
|
|
715
75
|
}
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
import { createId as createId2 } from "@paralleldrive/cuid2";
|
|
719
|
-
var RunEventEmitter = class {
|
|
720
|
-
listeners = [];
|
|
721
|
-
subscribe(listener) {
|
|
722
|
-
this.listeners.push(listener);
|
|
723
|
-
}
|
|
724
|
-
async emit(event) {
|
|
725
|
-
for (const listener of this.listeners) {
|
|
726
|
-
await listener({
|
|
727
|
-
...event,
|
|
728
|
-
id: createId2(),
|
|
729
|
-
timestamp: Date.now()
|
|
730
|
-
});
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
};
|
|
734
|
-
|
|
735
|
-
// src/schemas/api-registry.ts
|
|
736
|
-
import { z as z3 } from "zod";
|
|
737
|
-
var RegistryV1ExpertSchema = z3.object({
|
|
738
|
-
name: z3.string(),
|
|
739
|
-
version: z3.string(),
|
|
740
|
-
minRuntimeVersion: z3.string(),
|
|
741
|
-
description: z3.string(),
|
|
742
|
-
instruction: z3.string(),
|
|
743
|
-
skills: z3.record(
|
|
744
|
-
z3.string(),
|
|
745
|
-
z3.discriminatedUnion("type", [
|
|
746
|
-
McpStdioSkillSchema.omit({ name: true }),
|
|
747
|
-
McpSseSkillSchema.omit({ name: true }),
|
|
748
|
-
InteractiveSkillSchema.omit({ name: true })
|
|
749
|
-
])
|
|
750
|
-
),
|
|
751
|
-
delegates: z3.array(z3.string()),
|
|
752
|
-
tags: z3.array(z3.string()),
|
|
753
|
-
status: z3.string(),
|
|
754
|
-
owner: z3.object({ name: z3.string() }),
|
|
755
|
-
createdAt: z3.iso.datetime(),
|
|
756
|
-
updatedAt: z3.iso.datetime()
|
|
757
|
-
});
|
|
758
|
-
var RegistryV1ExpertsGetResponseSchema = z3.object({
|
|
759
|
-
data: z3.object({
|
|
760
|
-
expert: RegistryV1ExpertSchema
|
|
761
|
-
})
|
|
762
|
-
});
|
|
763
|
-
|
|
764
|
-
// src/api-clinent.ts
|
|
765
|
-
var Perstack = class {
|
|
766
|
-
baseUrl;
|
|
767
|
-
registry;
|
|
768
|
-
apiKey;
|
|
769
|
-
constructor(params) {
|
|
770
|
-
this.baseUrl = params?.baseUrl ?? process.env.PERSTACK_API_BASE_URL ?? "https://api.perstack.ai";
|
|
771
|
-
this.apiKey = params?.apiKey ?? process.env.PERSTACK_API_KEY;
|
|
772
|
-
this.registry = new RegistryClientV1(this);
|
|
773
|
-
}
|
|
774
|
-
};
|
|
775
|
-
var RegistryClientV1 = class {
|
|
776
|
-
client;
|
|
777
|
-
endpoint;
|
|
778
|
-
constructor(client) {
|
|
779
|
-
this.client = client;
|
|
780
|
-
this.endpoint = "/api/registry/v1/experts";
|
|
781
|
-
}
|
|
782
|
-
async get(expertKey) {
|
|
783
|
-
const safeExpertKey = encodeURIComponent(expertKey);
|
|
784
|
-
const url = new URL(`${this.endpoint}/${safeExpertKey}`, this.client.baseUrl);
|
|
785
|
-
const headers = {
|
|
786
|
-
"Content-Type": "application/json",
|
|
787
|
-
Authorization: `Bearer ${this.client.apiKey}`
|
|
788
|
-
};
|
|
789
|
-
const result = await fetch(url.toString(), { headers });
|
|
790
|
-
if (!result.ok) {
|
|
791
|
-
throw new Error(
|
|
792
|
-
`Registry returned non-200 status code: ${result.status}, reason: ${result.statusText}`
|
|
793
|
-
);
|
|
794
|
-
}
|
|
795
|
-
const json = await result.json();
|
|
796
|
-
return RegistryV1ExpertsGetResponseSchema.parse(json);
|
|
797
|
-
}
|
|
798
|
-
};
|
|
799
|
-
|
|
800
|
-
// src/resolve-expert-to-run.ts
|
|
801
|
-
async function resolveExpertToRun(expertKey, experts) {
|
|
802
|
-
if (experts[expertKey]) {
|
|
803
|
-
return experts[expertKey];
|
|
804
|
-
}
|
|
805
|
-
const client = new Perstack();
|
|
806
|
-
const { data } = await client.registry.get(expertKey);
|
|
807
|
-
const expert = ExpertSchema.parse({
|
|
808
|
-
...data.expert,
|
|
809
|
-
key: expertKey
|
|
810
|
-
});
|
|
811
|
-
experts[expertKey] = expert;
|
|
812
|
-
return expert;
|
|
76
|
+
function calculateContextWindowUsage(usage, contextWindow) {
|
|
77
|
+
return (usage.inputTokens + usage.cachedInputTokens + usage.outputTokens) / contextWindow;
|
|
813
78
|
}
|
|
814
|
-
|
|
815
|
-
// src/runtime-state-machine.ts
|
|
816
|
-
import { assign, setup } from "xstate";
|
|
817
|
-
|
|
818
|
-
// src/skill-manager.ts
|
|
819
|
-
import { Client as McpClient } from "@modelcontextprotocol/sdk/client/index.js";
|
|
820
|
-
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
821
|
-
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
822
|
-
import { McpError } from "@modelcontextprotocol/sdk/types.js";
|
|
823
|
-
import { createId as createId3 } from "@paralleldrive/cuid2";
|
|
824
|
-
import { jsonSchema, tool } from "ai";
|
|
825
|
-
import { ZodError } from "zod";
|
|
826
79
|
var SkillManager = class {
|
|
827
80
|
_toolDefinitions = [];
|
|
828
81
|
_initialized = false;
|
|
@@ -833,6 +86,7 @@ var SkillManager = class {
|
|
|
833
86
|
expert;
|
|
834
87
|
_mcpClient;
|
|
835
88
|
_params;
|
|
89
|
+
_env;
|
|
836
90
|
constructor(params) {
|
|
837
91
|
this._params = params;
|
|
838
92
|
this.type = params.type;
|
|
@@ -840,14 +94,17 @@ var SkillManager = class {
|
|
|
840
94
|
case "mcp":
|
|
841
95
|
this.name = params.skill.name;
|
|
842
96
|
this.skill = params.skill;
|
|
97
|
+
this._env = params.env;
|
|
843
98
|
break;
|
|
844
99
|
case "interactive":
|
|
845
100
|
this.name = params.interactiveSkill.name;
|
|
846
101
|
this.interactiveSkill = params.interactiveSkill;
|
|
102
|
+
this._env = {};
|
|
847
103
|
break;
|
|
848
104
|
case "delegate":
|
|
849
105
|
this.name = params.expert.name;
|
|
850
106
|
this.expert = params.expert;
|
|
107
|
+
this._env = {};
|
|
851
108
|
break;
|
|
852
109
|
}
|
|
853
110
|
}
|
|
@@ -868,7 +125,7 @@ var SkillManager = class {
|
|
|
868
125
|
}
|
|
869
126
|
}
|
|
870
127
|
async _initMcpSkill(params) {
|
|
871
|
-
this._mcpClient = new
|
|
128
|
+
this._mcpClient = new Client({
|
|
872
129
|
name: `${params.skill.name}-mcp-client`,
|
|
873
130
|
version: "1.0.0"
|
|
874
131
|
});
|
|
@@ -877,14 +134,16 @@ var SkillManager = class {
|
|
|
877
134
|
if (!params.skill.command) {
|
|
878
135
|
throw new Error(`Skill ${params.skill.name} has no command`);
|
|
879
136
|
}
|
|
137
|
+
const env = {};
|
|
138
|
+
const { requiredEnv } = params.skill;
|
|
139
|
+
for (const envName of requiredEnv) {
|
|
140
|
+
if (!this._env[envName]) {
|
|
141
|
+
throw new Error(`Skill ${params.skill.name} requires environment variable ${envName}`);
|
|
142
|
+
}
|
|
143
|
+
env[envName] = this._env[envName];
|
|
144
|
+
}
|
|
880
145
|
const { command, args } = this._getCommandArgs(params.skill);
|
|
881
|
-
const transport = new StdioClientTransport({
|
|
882
|
-
command,
|
|
883
|
-
args,
|
|
884
|
-
env: Object.fromEntries(
|
|
885
|
-
Object.entries(process.env).filter(([_, value]) => value !== void 0 && value !== "").map(([key, value]) => [key, value])
|
|
886
|
-
)
|
|
887
|
-
});
|
|
146
|
+
const transport = new StdioClientTransport({ command, args, env });
|
|
888
147
|
await this._mcpClient.connect(transport);
|
|
889
148
|
break;
|
|
890
149
|
}
|
|
@@ -995,13 +254,10 @@ var SkillManager = class {
|
|
|
995
254
|
{
|
|
996
255
|
type: "textPart",
|
|
997
256
|
text: `Error calling tool ${toolName}: ${error.message}`,
|
|
998
|
-
id:
|
|
257
|
+
id: createId()
|
|
999
258
|
}
|
|
1000
259
|
];
|
|
1001
260
|
}
|
|
1002
|
-
if (error instanceof ZodError) {
|
|
1003
|
-
return [{ type: "textPart", text: `Invalid tool call arguments: ${error}`, id: createId3() }];
|
|
1004
|
-
}
|
|
1005
261
|
throw error;
|
|
1006
262
|
}
|
|
1007
263
|
_convertToolResult(result, toolName, input) {
|
|
@@ -1010,7 +266,7 @@ var SkillManager = class {
|
|
|
1010
266
|
{
|
|
1011
267
|
type: "textPart",
|
|
1012
268
|
text: `Tool ${toolName} returned nothing with arguments: ${JSON.stringify(input)}`,
|
|
1013
|
-
id:
|
|
269
|
+
id: createId()
|
|
1014
270
|
}
|
|
1015
271
|
];
|
|
1016
272
|
}
|
|
@@ -1020,9 +276,9 @@ var SkillManager = class {
|
|
|
1020
276
|
switch (part.type) {
|
|
1021
277
|
case "text":
|
|
1022
278
|
if (!part.text || part.text === "") {
|
|
1023
|
-
return { type: "textPart", text: "Error: No content", id:
|
|
279
|
+
return { type: "textPart", text: "Error: No content", id: createId() };
|
|
1024
280
|
}
|
|
1025
|
-
return { type: "textPart", text: part.text, id:
|
|
281
|
+
return { type: "textPart", text: part.text, id: createId() };
|
|
1026
282
|
case "image":
|
|
1027
283
|
if (!part.data || !part.mimeType) {
|
|
1028
284
|
throw new Error("Image part must have both data and mimeType");
|
|
@@ -1031,7 +287,7 @@ var SkillManager = class {
|
|
|
1031
287
|
type: "imageInlinePart",
|
|
1032
288
|
encodedData: part.data,
|
|
1033
289
|
mimeType: part.mimeType,
|
|
1034
|
-
id:
|
|
290
|
+
id: createId()
|
|
1035
291
|
};
|
|
1036
292
|
case "resource":
|
|
1037
293
|
if (!part.resource) {
|
|
@@ -1045,39 +301,36 @@ var SkillManager = class {
|
|
|
1045
301
|
throw new Error(`Resource ${JSON.stringify(resource)} has no mimeType`);
|
|
1046
302
|
}
|
|
1047
303
|
if (resource.text && typeof resource.text === "string") {
|
|
1048
|
-
return { type: "textPart", text: resource.text, id:
|
|
304
|
+
return { type: "textPart", text: resource.text, id: createId() };
|
|
1049
305
|
}
|
|
1050
306
|
if (resource.blob && typeof resource.blob === "string") {
|
|
1051
307
|
return {
|
|
1052
308
|
type: "fileInlinePart",
|
|
1053
309
|
encodedData: resource.blob,
|
|
1054
310
|
mimeType: resource.mimeType,
|
|
1055
|
-
id:
|
|
311
|
+
id: createId()
|
|
1056
312
|
};
|
|
1057
313
|
}
|
|
1058
314
|
throw new Error(`Unsupported resource type: ${JSON.stringify(resource)}`);
|
|
1059
315
|
}
|
|
1060
316
|
};
|
|
1061
|
-
async function getSkillManagers(expert, experts) {
|
|
317
|
+
async function getSkillManagers(expert, experts, setting) {
|
|
318
|
+
const { perstackBaseSkillArgs, env } = setting;
|
|
1062
319
|
const skillManagers = {};
|
|
1063
320
|
const { skills } = expert;
|
|
1064
321
|
if (!skills["@perstack/base"]) {
|
|
1065
|
-
|
|
1066
|
-
type: "mcpStdioSkill",
|
|
1067
|
-
name: "@perstack/base",
|
|
1068
|
-
description: "The base skill for Perstack",
|
|
1069
|
-
pick: [],
|
|
1070
|
-
omit: [],
|
|
1071
|
-
command: "npx",
|
|
1072
|
-
args: ["-y", "@perstack/base"],
|
|
1073
|
-
requiredEnv: []
|
|
1074
|
-
};
|
|
322
|
+
throw new Error("Base skill is not defined");
|
|
1075
323
|
}
|
|
1076
324
|
const mcpSkillManagers = await Promise.all(
|
|
1077
325
|
Object.values(skills).filter((skill) => skill.type === "mcpStdioSkill" || skill.type === "mcpSseSkill").map(async (skill) => {
|
|
326
|
+
if (perstackBaseSkillArgs && skill.type === "mcpStdioSkill" && skill.name === "@perstack/base") {
|
|
327
|
+
skill.packageName = void 0;
|
|
328
|
+
skill.args = ["-y", ...perstackBaseSkillArgs];
|
|
329
|
+
}
|
|
1078
330
|
const skillManager = new SkillManager({
|
|
1079
331
|
type: "mcp",
|
|
1080
|
-
skill
|
|
332
|
+
skill,
|
|
333
|
+
env
|
|
1081
334
|
});
|
|
1082
335
|
await skillManager.init();
|
|
1083
336
|
return skillManager;
|
|
@@ -1152,12 +405,12 @@ async function callingDelegateLogic({
|
|
|
1152
405
|
step,
|
|
1153
406
|
skillManagers
|
|
1154
407
|
}) {
|
|
1155
|
-
if (!step
|
|
408
|
+
if (!step.toolCall) {
|
|
1156
409
|
throw new Error("No tool call found");
|
|
1157
410
|
}
|
|
1158
411
|
const { id, toolName, args } = step.toolCall;
|
|
1159
412
|
const skillManager = await getSkillManagerByToolName(skillManagers, toolName);
|
|
1160
|
-
if (!skillManager
|
|
413
|
+
if (!skillManager.expert) {
|
|
1161
414
|
throw new Error(`Delegation error: skill manager "${toolName}" not found`);
|
|
1162
415
|
}
|
|
1163
416
|
if (!args || !args.query || typeof args.query !== "string") {
|
|
@@ -1184,8 +437,6 @@ async function callingDelegateLogic({
|
|
|
1184
437
|
}
|
|
1185
438
|
});
|
|
1186
439
|
}
|
|
1187
|
-
|
|
1188
|
-
// src/states/calling-interactive-tool.ts
|
|
1189
440
|
async function callingInteractiveToolLogic({
|
|
1190
441
|
setting,
|
|
1191
442
|
checkpoint,
|
|
@@ -1202,15 +453,13 @@ async function callingInteractiveToolLogic({
|
|
|
1202
453
|
}
|
|
1203
454
|
});
|
|
1204
455
|
}
|
|
1205
|
-
|
|
1206
|
-
// src/states/calling-tool.ts
|
|
1207
456
|
async function callingToolLogic({
|
|
1208
457
|
setting,
|
|
1209
458
|
checkpoint,
|
|
1210
459
|
step,
|
|
1211
460
|
skillManagers
|
|
1212
461
|
}) {
|
|
1213
|
-
if (!step
|
|
462
|
+
if (!step.toolCall) {
|
|
1214
463
|
throw new Error("No tool call found");
|
|
1215
464
|
}
|
|
1216
465
|
const { id, skillName, toolName, args } = step.toolCall;
|
|
@@ -1236,9 +485,6 @@ async function callingToolLogic({
|
|
|
1236
485
|
}
|
|
1237
486
|
return resolveToolResult(setting, checkpoint, { toolResult });
|
|
1238
487
|
}
|
|
1239
|
-
|
|
1240
|
-
// src/states/finishing-step.ts
|
|
1241
|
-
import { createId as createId4 } from "@paralleldrive/cuid2";
|
|
1242
488
|
async function finishingStepLogic({
|
|
1243
489
|
setting,
|
|
1244
490
|
checkpoint,
|
|
@@ -1266,26 +512,19 @@ async function finishingStepLogic({
|
|
|
1266
512
|
},
|
|
1267
513
|
nextCheckpoint: {
|
|
1268
514
|
...checkpoint,
|
|
1269
|
-
id:
|
|
515
|
+
id: createId(),
|
|
1270
516
|
stepNumber: checkpoint.stepNumber + 1
|
|
1271
517
|
}
|
|
1272
518
|
});
|
|
1273
519
|
}
|
|
1274
|
-
|
|
1275
|
-
// src/states/generating-run-result.ts
|
|
1276
|
-
import { generateText } from "ai";
|
|
1277
|
-
|
|
1278
|
-
// src/messages/message.ts
|
|
1279
|
-
import { createId as createId5 } from "@paralleldrive/cuid2";
|
|
1280
|
-
import { dedent } from "ts-dedent";
|
|
1281
520
|
function createUserMessage(contents) {
|
|
1282
521
|
return {
|
|
1283
522
|
type: "userMessage",
|
|
1284
523
|
contents: contents.map((part) => ({
|
|
1285
524
|
...part,
|
|
1286
|
-
id:
|
|
525
|
+
id: createId()
|
|
1287
526
|
})),
|
|
1288
|
-
id:
|
|
527
|
+
id: createId()
|
|
1289
528
|
};
|
|
1290
529
|
}
|
|
1291
530
|
function createExpertMessage(contents) {
|
|
@@ -1293,9 +532,9 @@ function createExpertMessage(contents) {
|
|
|
1293
532
|
type: "expertMessage",
|
|
1294
533
|
contents: contents.map((part) => ({
|
|
1295
534
|
...part,
|
|
1296
|
-
id:
|
|
535
|
+
id: createId()
|
|
1297
536
|
})),
|
|
1298
|
-
id:
|
|
537
|
+
id: createId()
|
|
1299
538
|
};
|
|
1300
539
|
}
|
|
1301
540
|
function createToolMessage(contents) {
|
|
@@ -1305,11 +544,11 @@ function createToolMessage(contents) {
|
|
|
1305
544
|
...part,
|
|
1306
545
|
contents: part.contents.map((part2) => ({
|
|
1307
546
|
...part2,
|
|
1308
|
-
id:
|
|
547
|
+
id: createId()
|
|
1309
548
|
})),
|
|
1310
|
-
id:
|
|
549
|
+
id: createId()
|
|
1311
550
|
})),
|
|
1312
|
-
id:
|
|
551
|
+
id: createId()
|
|
1313
552
|
};
|
|
1314
553
|
}
|
|
1315
554
|
function messageToCoreMessage(message) {
|
|
@@ -1476,36 +715,29 @@ function toolResultPartToCoreToolResultPart(part) {
|
|
|
1476
715
|
// src/usage.ts
|
|
1477
716
|
function createEmptyUsage() {
|
|
1478
717
|
return {
|
|
1479
|
-
|
|
1480
|
-
|
|
718
|
+
inputTokens: 0,
|
|
719
|
+
outputTokens: 0,
|
|
720
|
+
reasoningTokens: 0,
|
|
1481
721
|
totalTokens: 0,
|
|
1482
|
-
|
|
1483
|
-
cacheReadInputTokens: 0
|
|
722
|
+
cachedInputTokens: 0
|
|
1484
723
|
};
|
|
1485
724
|
}
|
|
1486
725
|
function usageFromGenerateTextResult(result) {
|
|
1487
|
-
let cacheCreationInputTokens = 0;
|
|
1488
|
-
let cacheReadInputTokens = 0;
|
|
1489
|
-
if (result.providerMetadata?.anthropic) {
|
|
1490
|
-
const anthropicMetadata = result.providerMetadata.anthropic;
|
|
1491
|
-
cacheCreationInputTokens = anthropicMetadata.cacheCreationInputTokens || 0;
|
|
1492
|
-
cacheReadInputTokens = anthropicMetadata.cacheReadInputTokens || 0;
|
|
1493
|
-
}
|
|
1494
726
|
return {
|
|
1495
|
-
|
|
1496
|
-
|
|
727
|
+
inputTokens: result.usage.inputTokens || 0,
|
|
728
|
+
outputTokens: result.usage.outputTokens || 0,
|
|
729
|
+
reasoningTokens: result.usage.reasoningTokens || 0,
|
|
1497
730
|
totalTokens: result.usage.totalTokens || 0,
|
|
1498
|
-
|
|
1499
|
-
cacheReadInputTokens
|
|
731
|
+
cachedInputTokens: result.usage.cachedInputTokens || 0
|
|
1500
732
|
};
|
|
1501
733
|
}
|
|
1502
734
|
function sumUsage(a, b) {
|
|
1503
735
|
return {
|
|
1504
|
-
|
|
1505
|
-
|
|
736
|
+
inputTokens: a.inputTokens + b.inputTokens,
|
|
737
|
+
outputTokens: a.outputTokens + b.outputTokens,
|
|
738
|
+
reasoningTokens: a.reasoningTokens + b.reasoningTokens,
|
|
1506
739
|
totalTokens: a.totalTokens + b.totalTokens,
|
|
1507
|
-
|
|
1508
|
-
cacheReadInputTokens: a.cacheReadInputTokens + b.cacheReadInputTokens
|
|
740
|
+
cachedInputTokens: a.cachedInputTokens + b.cachedInputTokens
|
|
1509
741
|
};
|
|
1510
742
|
}
|
|
1511
743
|
|
|
@@ -1515,7 +747,7 @@ async function generatingRunResultLogic({
|
|
|
1515
747
|
checkpoint,
|
|
1516
748
|
step
|
|
1517
749
|
}) {
|
|
1518
|
-
if (!step
|
|
750
|
+
if (!step.toolCall || !step.toolResult) {
|
|
1519
751
|
throw new Error("No tool call or tool result found");
|
|
1520
752
|
}
|
|
1521
753
|
const { id, toolName } = step.toolCall;
|
|
@@ -1530,7 +762,7 @@ async function generatingRunResultLogic({
|
|
|
1530
762
|
)
|
|
1531
763
|
}
|
|
1532
764
|
]);
|
|
1533
|
-
const model = getModel(setting.model);
|
|
765
|
+
const model = getModel(setting.model, setting.providerConfig);
|
|
1534
766
|
const { messages } = checkpoint;
|
|
1535
767
|
let generationResult;
|
|
1536
768
|
try {
|
|
@@ -1543,17 +775,11 @@ async function generatingRunResultLogic({
|
|
|
1543
775
|
});
|
|
1544
776
|
} catch (error) {
|
|
1545
777
|
if (error instanceof Error) {
|
|
778
|
+
const reason = JSON.stringify({ error: error.name, message: error.message });
|
|
1546
779
|
return retry(setting, checkpoint, {
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
{
|
|
1551
|
-
type: "textPart",
|
|
1552
|
-
text: JSON.stringify({ error: error.name, message: error.message })
|
|
1553
|
-
}
|
|
1554
|
-
])
|
|
1555
|
-
],
|
|
1556
|
-
usage: checkpoint.usage
|
|
780
|
+
reason,
|
|
781
|
+
newMessages: [toolMessage, createUserMessage([{ type: "textPart", text: reason }])],
|
|
782
|
+
usage: createEmptyUsage()
|
|
1557
783
|
});
|
|
1558
784
|
}
|
|
1559
785
|
throw error;
|
|
@@ -1566,6 +792,7 @@ async function generatingRunResultLogic({
|
|
|
1566
792
|
...checkpoint,
|
|
1567
793
|
messages: [...messages, ...newMessages],
|
|
1568
794
|
usage: sumUsage(checkpoint.usage, usage),
|
|
795
|
+
contextWindowUsage: checkpoint.contextWindow ? calculateContextWindowUsage(usage, checkpoint.contextWindow) : void 0,
|
|
1569
796
|
status: "completed"
|
|
1570
797
|
},
|
|
1571
798
|
step: {
|
|
@@ -1578,20 +805,16 @@ async function generatingRunResultLogic({
|
|
|
1578
805
|
usage
|
|
1579
806
|
});
|
|
1580
807
|
}
|
|
1581
|
-
|
|
1582
|
-
// src/states/generating-tool-call.ts
|
|
1583
|
-
import { createId as createId6 } from "@paralleldrive/cuid2";
|
|
1584
|
-
import { generateText as generateText2 } from "ai";
|
|
1585
808
|
async function generatingToolCallLogic({
|
|
1586
809
|
setting,
|
|
1587
810
|
checkpoint,
|
|
1588
811
|
skillManagers
|
|
1589
812
|
}) {
|
|
1590
813
|
const { messages } = checkpoint;
|
|
1591
|
-
const model = getModel(setting.model);
|
|
814
|
+
const model = getModel(setting.model, setting.providerConfig);
|
|
1592
815
|
let result;
|
|
1593
816
|
try {
|
|
1594
|
-
result = await
|
|
817
|
+
result = await generateText({
|
|
1595
818
|
model,
|
|
1596
819
|
messages: messages.map(messageToCoreMessage),
|
|
1597
820
|
temperature: setting.temperature,
|
|
@@ -1602,16 +825,12 @@ async function generatingToolCallLogic({
|
|
|
1602
825
|
});
|
|
1603
826
|
} catch (error) {
|
|
1604
827
|
if (error instanceof Error) {
|
|
828
|
+
console.error(error);
|
|
829
|
+
const reason = JSON.stringify({ error: error.name, message: error.message });
|
|
1605
830
|
return retry(setting, checkpoint, {
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
type: "textPart",
|
|
1610
|
-
text: JSON.stringify({ error: error.name, message: error.message })
|
|
1611
|
-
}
|
|
1612
|
-
])
|
|
1613
|
-
],
|
|
1614
|
-
usage: checkpoint.usage
|
|
831
|
+
reason,
|
|
832
|
+
newMessages: [createUserMessage([{ type: "textPart", text: reason }])],
|
|
833
|
+
usage: createEmptyUsage()
|
|
1615
834
|
});
|
|
1616
835
|
}
|
|
1617
836
|
throw error;
|
|
@@ -1620,18 +839,13 @@ async function generatingToolCallLogic({
|
|
|
1620
839
|
const { text, toolCalls, finishReason } = result;
|
|
1621
840
|
const toolCall = toolCalls[0];
|
|
1622
841
|
if (!toolCall) {
|
|
842
|
+
const reason = JSON.stringify({
|
|
843
|
+
error: "Error: No tool call generated",
|
|
844
|
+
message: "You must generate a tool call. Try again."
|
|
845
|
+
});
|
|
1623
846
|
return retry(setting, checkpoint, {
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
{
|
|
1627
|
-
type: "textPart",
|
|
1628
|
-
text: JSON.stringify({
|
|
1629
|
-
error: "Error: No tool call generated",
|
|
1630
|
-
message: "You must generate a tool call. Try again."
|
|
1631
|
-
})
|
|
1632
|
-
}
|
|
1633
|
-
])
|
|
1634
|
-
],
|
|
847
|
+
reason,
|
|
848
|
+
newMessages: [createUserMessage([{ type: "textPart", text: reason }])],
|
|
1635
849
|
usage
|
|
1636
850
|
});
|
|
1637
851
|
}
|
|
@@ -1660,7 +874,7 @@ async function generatingToolCallLogic({
|
|
|
1660
874
|
},
|
|
1661
875
|
usage
|
|
1662
876
|
};
|
|
1663
|
-
if (finishReason === "tool-calls") {
|
|
877
|
+
if (finishReason === "tool-calls" || finishReason === "stop") {
|
|
1664
878
|
switch (skillManager.type) {
|
|
1665
879
|
case "mcp":
|
|
1666
880
|
return callTool(setting, checkpoint, eventPayload);
|
|
@@ -1671,7 +885,12 @@ async function generatingToolCallLogic({
|
|
|
1671
885
|
}
|
|
1672
886
|
}
|
|
1673
887
|
if (finishReason === "length") {
|
|
888
|
+
const reason = JSON.stringify({
|
|
889
|
+
error: "Error: Tool call generation failed",
|
|
890
|
+
message: "Generation length exceeded. Try again."
|
|
891
|
+
});
|
|
1674
892
|
return retry(setting, checkpoint, {
|
|
893
|
+
reason,
|
|
1675
894
|
newMessages: [
|
|
1676
895
|
createExpertMessage([
|
|
1677
896
|
{
|
|
@@ -1686,15 +905,7 @@ async function generatingToolCallLogic({
|
|
|
1686
905
|
type: "toolResultPart",
|
|
1687
906
|
toolCallId: toolCall.toolCallId,
|
|
1688
907
|
toolName: toolCall.toolName,
|
|
1689
|
-
contents: [
|
|
1690
|
-
{
|
|
1691
|
-
type: "textPart",
|
|
1692
|
-
text: JSON.stringify({
|
|
1693
|
-
error: "Error: Tool call generation failed",
|
|
1694
|
-
message: "Generation length exceeded. Try again."
|
|
1695
|
-
})
|
|
1696
|
-
}
|
|
1697
|
-
]
|
|
908
|
+
contents: [{ type: "textPart", text: reason }]
|
|
1698
909
|
}
|
|
1699
910
|
])
|
|
1700
911
|
],
|
|
@@ -1703,27 +914,14 @@ async function generatingToolCallLogic({
|
|
|
1703
914
|
id: toolCall.toolCallId,
|
|
1704
915
|
skillName: skillManager.name,
|
|
1705
916
|
toolName: toolCall.toolName,
|
|
1706
|
-
result: [
|
|
1707
|
-
{
|
|
1708
|
-
type: "textPart",
|
|
1709
|
-
id: createId6(),
|
|
1710
|
-
text: JSON.stringify({
|
|
1711
|
-
error: "Error: Tool call generation failed",
|
|
1712
|
-
message: "Generation length exceeded. Try again."
|
|
1713
|
-
})
|
|
1714
|
-
}
|
|
1715
|
-
]
|
|
917
|
+
result: [{ type: "textPart", id: createId(), text: reason }]
|
|
1716
918
|
},
|
|
1717
919
|
usage
|
|
1718
920
|
});
|
|
1719
921
|
}
|
|
1720
922
|
throw new Error(`Unexpected finish reason: ${finishReason}`);
|
|
1721
923
|
}
|
|
1722
|
-
|
|
1723
|
-
// src/messages/instruction-message.ts
|
|
1724
|
-
import { createId as createId7 } from "@paralleldrive/cuid2";
|
|
1725
|
-
import { dedent as dedent2 } from "ts-dedent";
|
|
1726
|
-
var metaInstruction = dedent2`
|
|
924
|
+
var metaInstruction = dedent`
|
|
1727
925
|
IMPORTANT:
|
|
1728
926
|
You are NOT an "interactive" AI agent.
|
|
1729
927
|
From the start of the agent loop until the completion of the task,
|
|
@@ -1741,33 +939,32 @@ var metaInstruction = dedent2`
|
|
|
1741
939
|
5. Notify Task Completion: Call the attemptCompletion tool to inform the user when the task is complete
|
|
1742
940
|
6. Generate Final Results: Produce a final result that clearly describes each task you performed, step by step
|
|
1743
941
|
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
-
|
|
1749
|
-
-
|
|
1750
|
-
-
|
|
1751
|
-
-
|
|
1752
|
-
-
|
|
1753
|
-
-
|
|
1754
|
-
-
|
|
942
|
+
Conditions for ending the agent loop:
|
|
943
|
+
If any of the following apply, **immediately call the attemptCompletion tool**.
|
|
944
|
+
When the agent loop must end, calling any tool other than attemptCompletion is highly dangerous.
|
|
945
|
+
Under all circumstances, strictly follow this rule.
|
|
946
|
+
- When the task is complete
|
|
947
|
+
- When the user's request is outside your expertise
|
|
948
|
+
- When the user makes an unethical request
|
|
949
|
+
- When the user's request is unintelligible
|
|
950
|
+
- When the request may pose security risks
|
|
951
|
+
- When the request may harm or inconvenience others
|
|
952
|
+
- When the request contains inappropriate or sexual content
|
|
1755
953
|
|
|
1756
954
|
Rules for requests outside your area of expertise:
|
|
1757
955
|
- Tell your area of expertise to the user in final results
|
|
1758
|
-
- In the Final Result, you can show the user how to create an expert that they might need; This can be done by running the \`parrot new\` (no arguments required) command.
|
|
1759
956
|
|
|
1760
|
-
|
|
1761
|
-
-
|
|
1762
|
-
-
|
|
1763
|
-
-
|
|
957
|
+
General approach and output quality rules:
|
|
958
|
+
- The user recognizes you as an expert in this domain
|
|
959
|
+
- As an expert, execute each step with thorough reasoning and high confidence
|
|
960
|
+
- As an expert, deliver output exceeding the user's expectations
|
|
1764
961
|
|
|
1765
962
|
Environment information:
|
|
1766
963
|
- Current time is ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
1767
964
|
- Current working directory is ${process.cwd()}
|
|
1768
965
|
`;
|
|
1769
966
|
function createInstructionMessage(expert, experts) {
|
|
1770
|
-
const instruction =
|
|
967
|
+
const instruction = dedent`
|
|
1771
968
|
You are Perstack, an AI expert that tackles tasks requested by users by utilizing all available tools.
|
|
1772
969
|
|
|
1773
970
|
(The following information describes your nature and role as an AI, the mechanisms of the AI system, and other meta-cognitive aspects.)
|
|
@@ -1795,12 +992,12 @@ function createInstructionMessage(expert, experts) {
|
|
|
1795
992
|
type: "instructionMessage",
|
|
1796
993
|
contents: [
|
|
1797
994
|
{
|
|
1798
|
-
id:
|
|
995
|
+
id: createId(),
|
|
1799
996
|
type: "textPart",
|
|
1800
997
|
text: instruction
|
|
1801
998
|
}
|
|
1802
999
|
],
|
|
1803
|
-
id:
|
|
1000
|
+
id: createId(),
|
|
1804
1001
|
cache: true
|
|
1805
1002
|
};
|
|
1806
1003
|
}
|
|
@@ -1809,7 +1006,7 @@ function getSkillRules(expert) {
|
|
|
1809
1006
|
if (!skill.rule) {
|
|
1810
1007
|
return acc;
|
|
1811
1008
|
}
|
|
1812
|
-
return
|
|
1009
|
+
return dedent`
|
|
1813
1010
|
${acc}
|
|
1814
1011
|
|
|
1815
1012
|
"${skill.name}" skill rules:
|
|
@@ -1823,7 +1020,7 @@ function getDelegateRules(expert, experts) {
|
|
|
1823
1020
|
if (!delegate) {
|
|
1824
1021
|
return acc;
|
|
1825
1022
|
}
|
|
1826
|
-
return
|
|
1023
|
+
return dedent`
|
|
1827
1024
|
${acc}
|
|
1828
1025
|
|
|
1829
1026
|
About "${delegate.name}":
|
|
@@ -1847,33 +1044,12 @@ async function initLogic({
|
|
|
1847
1044
|
return startRun(setting, checkpoint, {
|
|
1848
1045
|
initialCheckpoint: checkpoint,
|
|
1849
1046
|
inputMessages: [
|
|
1850
|
-
createInstructionMessage(expert, experts),
|
|
1851
|
-
createUserMessage([{ type: "textPart", text: setting.input.text }])
|
|
1852
|
-
]
|
|
1853
|
-
});
|
|
1854
|
-
}
|
|
1855
|
-
case "proceeding": {
|
|
1856
|
-
if (!setting.input.text) {
|
|
1857
|
-
throw new Error("Input message is undefined");
|
|
1858
|
-
}
|
|
1859
|
-
return startRun(setting, checkpoint, {
|
|
1860
|
-
initialCheckpoint: checkpoint,
|
|
1861
|
-
inputMessages: [createUserMessage([{ type: "textPart", text: setting.input.text }])]
|
|
1862
|
-
});
|
|
1863
|
-
}
|
|
1864
|
-
case "stoppedByDelegate": {
|
|
1865
|
-
if (!setting.input.interactiveToolCallResult) {
|
|
1866
|
-
throw new Error("Interactive tool call result is undefined");
|
|
1867
|
-
}
|
|
1868
|
-
return startRun(setting, checkpoint, {
|
|
1869
|
-
initialCheckpoint: checkpoint,
|
|
1870
|
-
inputMessages: [
|
|
1871
|
-
createUserMessage([
|
|
1872
|
-
{ type: "textPart", text: setting.input.interactiveToolCallResult.text }
|
|
1873
|
-
])
|
|
1047
|
+
createInstructionMessage(expert, experts),
|
|
1048
|
+
createUserMessage([{ type: "textPart", text: setting.input.text }])
|
|
1874
1049
|
]
|
|
1875
1050
|
});
|
|
1876
1051
|
}
|
|
1052
|
+
case "stoppedByDelegate":
|
|
1877
1053
|
case "stoppedByInteractiveTool": {
|
|
1878
1054
|
if (!setting.input.interactiveToolCallResult) {
|
|
1879
1055
|
throw new Error("Interactive tool call result is undefined");
|
|
@@ -1893,11 +1069,15 @@ async function initLogic({
|
|
|
1893
1069
|
});
|
|
1894
1070
|
}
|
|
1895
1071
|
default:
|
|
1896
|
-
|
|
1072
|
+
if (!setting.input.text) {
|
|
1073
|
+
throw new Error("Input message is undefined");
|
|
1074
|
+
}
|
|
1075
|
+
return startRun(setting, checkpoint, {
|
|
1076
|
+
initialCheckpoint: checkpoint,
|
|
1077
|
+
inputMessages: [createUserMessage([{ type: "textPart", text: setting.input.text }])]
|
|
1078
|
+
});
|
|
1897
1079
|
}
|
|
1898
1080
|
}
|
|
1899
|
-
|
|
1900
|
-
// src/states/preparing-for-step.ts
|
|
1901
1081
|
async function preparingForStepLogic({
|
|
1902
1082
|
setting,
|
|
1903
1083
|
checkpoint
|
|
@@ -1906,15 +1086,12 @@ async function preparingForStepLogic({
|
|
|
1906
1086
|
messages: checkpoint.messages
|
|
1907
1087
|
});
|
|
1908
1088
|
}
|
|
1909
|
-
|
|
1910
|
-
// src/states/resolving-image-file.ts
|
|
1911
|
-
import { readFile as readFile2 } from "fs/promises";
|
|
1912
1089
|
async function resolvingImageFileLogic({
|
|
1913
1090
|
setting,
|
|
1914
1091
|
checkpoint,
|
|
1915
1092
|
step
|
|
1916
1093
|
}) {
|
|
1917
|
-
if (!step
|
|
1094
|
+
if (!step.toolCall || !step.toolResult) {
|
|
1918
1095
|
throw new Error("No tool call or tool result found");
|
|
1919
1096
|
}
|
|
1920
1097
|
const { id, toolName } = step.toolCall;
|
|
@@ -1933,7 +1110,7 @@ async function resolvingImageFileLogic({
|
|
|
1933
1110
|
continue;
|
|
1934
1111
|
}
|
|
1935
1112
|
const { path: path2, mimeType, size } = imageInfo;
|
|
1936
|
-
const file = await
|
|
1113
|
+
const file = await readFile(path2).then((buffer) => ({
|
|
1937
1114
|
encodedData: buffer.toString("base64"),
|
|
1938
1115
|
mimeType,
|
|
1939
1116
|
size
|
|
@@ -1957,15 +1134,12 @@ async function resolvingImageFileLogic({
|
|
|
1957
1134
|
]
|
|
1958
1135
|
});
|
|
1959
1136
|
}
|
|
1960
|
-
|
|
1961
|
-
// src/states/resolving-pdf-file.ts
|
|
1962
|
-
import { readFile as readFile3 } from "fs/promises";
|
|
1963
1137
|
async function resolvingPdfFileLogic({
|
|
1964
1138
|
setting,
|
|
1965
1139
|
checkpoint,
|
|
1966
1140
|
step
|
|
1967
1141
|
}) {
|
|
1968
|
-
if (!step
|
|
1142
|
+
if (!step.toolCall || !step.toolResult) {
|
|
1969
1143
|
throw new Error("No tool call or tool result found");
|
|
1970
1144
|
}
|
|
1971
1145
|
const { id, toolName } = step.toolCall;
|
|
@@ -1984,7 +1158,7 @@ async function resolvingPdfFileLogic({
|
|
|
1984
1158
|
continue;
|
|
1985
1159
|
}
|
|
1986
1160
|
const { path: path2, mimeType, size } = pdfInfo;
|
|
1987
|
-
const file = await
|
|
1161
|
+
const file = await readFile(path2).then((buffer) => ({
|
|
1988
1162
|
encodedData: buffer.toString("base64"),
|
|
1989
1163
|
mimeType,
|
|
1990
1164
|
size
|
|
@@ -2014,14 +1188,12 @@ async function resolvingPdfFileLogic({
|
|
|
2014
1188
|
]
|
|
2015
1189
|
});
|
|
2016
1190
|
}
|
|
2017
|
-
|
|
2018
|
-
// src/states/resolving-tool-result.ts
|
|
2019
1191
|
async function resolvingToolResultLogic({
|
|
2020
1192
|
setting,
|
|
2021
1193
|
checkpoint,
|
|
2022
1194
|
step
|
|
2023
1195
|
}) {
|
|
2024
|
-
if (!step
|
|
1196
|
+
if (!step.toolCall || !step.toolResult) {
|
|
2025
1197
|
throw new Error("No tool call or tool result found");
|
|
2026
1198
|
}
|
|
2027
1199
|
const { id, toolName } = step.toolCall;
|
|
@@ -2077,17 +1249,14 @@ var runtimeStateMachine = setup({
|
|
|
2077
1249
|
startRun: {
|
|
2078
1250
|
target: "PreparingForStep",
|
|
2079
1251
|
actions: assign({
|
|
2080
|
-
checkpoint: ({ event }) => ({
|
|
2081
|
-
...
|
|
2082
|
-
|
|
2083
|
-
|
|
1252
|
+
checkpoint: ({ context, event }) => ({
|
|
1253
|
+
...context.checkpoint,
|
|
1254
|
+
status: "proceeding",
|
|
1255
|
+
messages: [...context.checkpoint.messages, ...event.inputMessages]
|
|
2084
1256
|
}),
|
|
2085
|
-
step: ({ event }) => ({
|
|
2086
|
-
|
|
2087
|
-
inputMessages: event.inputMessages
|
|
2088
|
-
newMessages: [],
|
|
2089
|
-
usage: createEmptyUsage(),
|
|
2090
|
-
startedAt: event.timestamp
|
|
1257
|
+
step: ({ context, event }) => ({
|
|
1258
|
+
...context.step,
|
|
1259
|
+
inputMessages: event.inputMessages
|
|
2091
1260
|
})
|
|
2092
1261
|
})
|
|
2093
1262
|
}
|
|
@@ -2100,7 +1269,7 @@ var runtimeStateMachine = setup({
|
|
|
2100
1269
|
actions: assign({
|
|
2101
1270
|
step: ({ context, event }) => ({
|
|
2102
1271
|
stepNumber: context.checkpoint.stepNumber,
|
|
2103
|
-
inputMessages: context.step.inputMessages,
|
|
1272
|
+
inputMessages: context.step.inputMessages ?? [],
|
|
2104
1273
|
newMessages: [],
|
|
2105
1274
|
usage: createEmptyUsage(),
|
|
2106
1275
|
startedAt: event.timestamp
|
|
@@ -2134,7 +1303,8 @@ var runtimeStateMachine = setup({
|
|
|
2134
1303
|
checkpoint: ({ context, event }) => ({
|
|
2135
1304
|
...context.checkpoint,
|
|
2136
1305
|
messages: [...context.checkpoint.messages, event.newMessage],
|
|
2137
|
-
usage: sumUsage(context.checkpoint.usage, event.usage)
|
|
1306
|
+
usage: sumUsage(context.checkpoint.usage, event.usage),
|
|
1307
|
+
contextWindowUsage: context.checkpoint.contextWindow ? calculateContextWindowUsage(event.usage, context.checkpoint.contextWindow) : void 0
|
|
2138
1308
|
}),
|
|
2139
1309
|
step: ({ context, event }) => ({
|
|
2140
1310
|
...context.step,
|
|
@@ -2150,7 +1320,8 @@ var runtimeStateMachine = setup({
|
|
|
2150
1320
|
checkpoint: ({ context, event }) => ({
|
|
2151
1321
|
...context.checkpoint,
|
|
2152
1322
|
messages: [...context.checkpoint.messages, event.newMessage],
|
|
2153
|
-
usage: sumUsage(context.checkpoint.usage, event.usage)
|
|
1323
|
+
usage: sumUsage(context.checkpoint.usage, event.usage),
|
|
1324
|
+
contextWindowUsage: context.checkpoint.contextWindow ? calculateContextWindowUsage(event.usage, context.checkpoint.contextWindow) : void 0
|
|
2154
1325
|
}),
|
|
2155
1326
|
step: ({ context, event }) => ({
|
|
2156
1327
|
...context.step,
|
|
@@ -2166,7 +1337,8 @@ var runtimeStateMachine = setup({
|
|
|
2166
1337
|
checkpoint: ({ context, event }) => ({
|
|
2167
1338
|
...context.checkpoint,
|
|
2168
1339
|
messages: [...context.checkpoint.messages, event.newMessage],
|
|
2169
|
-
usage: sumUsage(context.checkpoint.usage, event.usage)
|
|
1340
|
+
usage: sumUsage(context.checkpoint.usage, event.usage),
|
|
1341
|
+
contextWindowUsage: context.checkpoint.contextWindow ? calculateContextWindowUsage(event.usage, context.checkpoint.contextWindow) : void 0
|
|
2170
1342
|
}),
|
|
2171
1343
|
step: ({ context, event }) => ({
|
|
2172
1344
|
...context.step,
|
|
@@ -2318,7 +1490,10 @@ var runtimeStateMachine = setup({
|
|
|
2318
1490
|
target: "Stopped",
|
|
2319
1491
|
actions: assign({
|
|
2320
1492
|
checkpoint: ({ event }) => event.checkpoint,
|
|
2321
|
-
step: ({ event }) =>
|
|
1493
|
+
step: ({ event }) => ({
|
|
1494
|
+
...event.step,
|
|
1495
|
+
inputMessages: void 0
|
|
1496
|
+
})
|
|
2322
1497
|
})
|
|
2323
1498
|
}
|
|
2324
1499
|
}
|
|
@@ -2329,7 +1504,10 @@ var runtimeStateMachine = setup({
|
|
|
2329
1504
|
target: "Stopped",
|
|
2330
1505
|
actions: assign({
|
|
2331
1506
|
checkpoint: ({ event }) => event.checkpoint,
|
|
2332
|
-
step: ({ event }) =>
|
|
1507
|
+
step: ({ event }) => ({
|
|
1508
|
+
...event.step,
|
|
1509
|
+
inputMessages: void 0
|
|
1510
|
+
})
|
|
2333
1511
|
})
|
|
2334
1512
|
}
|
|
2335
1513
|
}
|
|
@@ -2340,7 +1518,10 @@ var runtimeStateMachine = setup({
|
|
|
2340
1518
|
target: "Stopped",
|
|
2341
1519
|
actions: assign({
|
|
2342
1520
|
checkpoint: ({ event }) => event.checkpoint,
|
|
2343
|
-
step: ({ event }) =>
|
|
1521
|
+
step: ({ event }) => ({
|
|
1522
|
+
...event.step,
|
|
1523
|
+
inputMessages: void 0
|
|
1524
|
+
})
|
|
2344
1525
|
})
|
|
2345
1526
|
}
|
|
2346
1527
|
}
|
|
@@ -2389,75 +1570,272 @@ var StateMachineLogics = {
|
|
|
2389
1570
|
CallingDelegate: callingDelegateLogic,
|
|
2390
1571
|
FinishingStep: finishingStepLogic
|
|
2391
1572
|
};
|
|
1573
|
+
async function defaultRetrieveCheckpoint(runId, checkpointId) {
|
|
1574
|
+
const runDir = getRunDir(runId);
|
|
1575
|
+
const checkpointFiles = await readdir(runDir, { withFileTypes: true }).then(
|
|
1576
|
+
(files) => files.filter((file) => file.isFile() && file.name.startsWith("checkpoint-"))
|
|
1577
|
+
);
|
|
1578
|
+
const checkpointFile = checkpointFiles.find((file) => file.name.endsWith(`-${checkpointId}.json`));
|
|
1579
|
+
if (!checkpointFile) {
|
|
1580
|
+
throw new Error(`checkpoint not found: ${runId} ${checkpointId}`);
|
|
1581
|
+
}
|
|
1582
|
+
const checkpointPath = `${runDir}/${checkpointFile.name}`;
|
|
1583
|
+
const checkpoint = await readFile(checkpointPath, "utf8");
|
|
1584
|
+
return checkpointSchema.parse(JSON.parse(checkpoint));
|
|
1585
|
+
}
|
|
1586
|
+
async function defaultStoreCheckpoint(checkpoint, timestamp) {
|
|
1587
|
+
const { id, runId, stepNumber } = checkpoint;
|
|
1588
|
+
const runDir = getRunDir(runId);
|
|
1589
|
+
const checkpointPath = `${runDir}/checkpoint-${timestamp}-${stepNumber}-${id}.json`;
|
|
1590
|
+
await mkdir(runDir, { recursive: true });
|
|
1591
|
+
await writeFile(checkpointPath, JSON.stringify(checkpoint, null, 2));
|
|
1592
|
+
}
|
|
1593
|
+
async function defaultStoreEvent(event) {
|
|
1594
|
+
const { timestamp, runId, stepNumber, type } = event;
|
|
1595
|
+
const runDir = getRunDir(runId);
|
|
1596
|
+
const eventPath = `${runDir}/event-${timestamp}-${stepNumber}-${type}.json`;
|
|
1597
|
+
await mkdir(runDir, { recursive: true });
|
|
1598
|
+
await writeFile(eventPath, JSON.stringify(event, null, 2));
|
|
1599
|
+
}
|
|
2392
1600
|
|
|
2393
|
-
//
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
}
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
1601
|
+
// package.json
|
|
1602
|
+
var package_default = {
|
|
1603
|
+
version: "0.0.25"};
|
|
1604
|
+
|
|
1605
|
+
// src/events/default-event-listener.ts
|
|
1606
|
+
var log = console.info;
|
|
1607
|
+
var debug = console.debug;
|
|
1608
|
+
var header = (e) => {
|
|
1609
|
+
const t = (/* @__PURE__ */ new Date()).toISOString();
|
|
1610
|
+
const stepNumber = e.stepNumber;
|
|
1611
|
+
const key = e.expertKey;
|
|
1612
|
+
return `${t} ${stepNumber} ${key}`;
|
|
1613
|
+
};
|
|
1614
|
+
async function defaultEventListener(e) {
|
|
1615
|
+
await defaultStoreEvent(e);
|
|
1616
|
+
switch (e.type) {
|
|
1617
|
+
case "startRun": {
|
|
1618
|
+
log(`${header(e)} Perstack@${package_default.version} started`);
|
|
1619
|
+
break;
|
|
1620
|
+
}
|
|
1621
|
+
case "startGeneration": {
|
|
1622
|
+
log(`${header(e)} Generating tool call`);
|
|
1623
|
+
break;
|
|
1624
|
+
}
|
|
1625
|
+
case "retry": {
|
|
1626
|
+
log(`${header(e)} Retrying tool call generation`);
|
|
1627
|
+
debug(e.reason);
|
|
1628
|
+
break;
|
|
1629
|
+
}
|
|
1630
|
+
case "callTool": {
|
|
1631
|
+
log(`${header(e)} Calling tool`);
|
|
1632
|
+
if (e.toolCall.skillName === "@perstack/base") {
|
|
1633
|
+
switch (e.toolCall.toolName) {
|
|
1634
|
+
case "think": {
|
|
1635
|
+
const thought = e.toolCall.args.thought;
|
|
1636
|
+
log(`${header(e)} Thought Updated:`);
|
|
1637
|
+
debug(thought);
|
|
1638
|
+
break;
|
|
1639
|
+
}
|
|
1640
|
+
case "readPdfFile": {
|
|
1641
|
+
const path2 = e.toolCall.args.path;
|
|
1642
|
+
log(`${header(e)} Reading PDF: ${path2}`);
|
|
1643
|
+
break;
|
|
1644
|
+
}
|
|
1645
|
+
case "readImageFile": {
|
|
1646
|
+
const path2 = e.toolCall.args.path;
|
|
1647
|
+
log(`${header(e)} Reading Image: ${path2}`);
|
|
1648
|
+
break;
|
|
1649
|
+
}
|
|
1650
|
+
default: {
|
|
1651
|
+
log(`${header(e)} Tool: ${e.toolCall.skillName}/${e.toolCall.toolName}`);
|
|
1652
|
+
debug(`${header(e)} Args: ${JSON.stringify(e.toolCall.args, null, 2)}`);
|
|
1653
|
+
break;
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
} else {
|
|
1657
|
+
log(`${header(e)} Tool: ${e.toolCall.skillName}/${e.toolCall.toolName}`);
|
|
1658
|
+
debug(`${header(e)} Args: ${JSON.stringify(e.toolCall.args, null, 2)}`);
|
|
1659
|
+
}
|
|
1660
|
+
break;
|
|
1661
|
+
}
|
|
1662
|
+
case "callInteractiveTool": {
|
|
1663
|
+
log(`${header(e)} Calling interactive tool`);
|
|
1664
|
+
log(`${header(e)} Tool: ${e.toolCall.skillName}/${e.toolCall.toolName}`);
|
|
1665
|
+
debug(`${header(e)} Args: ${JSON.stringify(e.toolCall.args, null, 2)}`);
|
|
1666
|
+
break;
|
|
1667
|
+
}
|
|
1668
|
+
case "callDelegate": {
|
|
1669
|
+
log(`${header(e)} Calling delegate`);
|
|
1670
|
+
log(`${header(e)} Tool: ${e.toolCall.toolName}`);
|
|
1671
|
+
debug(`${header(e)} Args: ${JSON.stringify(e.toolCall.args, null, 2)}`);
|
|
1672
|
+
break;
|
|
1673
|
+
}
|
|
1674
|
+
case "resolveToolResult": {
|
|
1675
|
+
log(`${header(e)} Resolved Tool Result`);
|
|
1676
|
+
if (e.toolResult.skillName === "@perstack/base") {
|
|
1677
|
+
switch (e.toolResult.toolName) {
|
|
1678
|
+
case "todo": {
|
|
1679
|
+
const text = e.toolResult.result.find((r) => r.type === "textPart")?.text;
|
|
1680
|
+
const { todos } = JSON.parse(text ?? "{}");
|
|
1681
|
+
log(`${header(e)} Todo:`);
|
|
1682
|
+
for (const todo of todos) {
|
|
1683
|
+
debug(`${todo.completed ? "[x]" : "[ ]"} ${todo.id}: ${todo.title}`);
|
|
1684
|
+
}
|
|
1685
|
+
break;
|
|
1686
|
+
}
|
|
1687
|
+
default: {
|
|
1688
|
+
log(`${header(e)} Tool: ${e.toolResult.skillName}/${e.toolResult.toolName}`);
|
|
1689
|
+
debug(`${header(e)} Result: ${JSON.stringify(e.toolResult.result, null, 2)}`);
|
|
1690
|
+
break;
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
} else {
|
|
1694
|
+
log(`${header(e)} Tool: ${e.toolResult.skillName}/${e.toolResult.toolName}`);
|
|
1695
|
+
debug(`${header(e)} Result: ${JSON.stringify(e.toolResult.result, null, 2)}`);
|
|
1696
|
+
}
|
|
1697
|
+
break;
|
|
1698
|
+
}
|
|
1699
|
+
case "resolveThought": {
|
|
1700
|
+
log(`${header(e)} Resolved Thought:`, e.toolResult);
|
|
1701
|
+
break;
|
|
1702
|
+
}
|
|
1703
|
+
case "resolvePdfFile": {
|
|
1704
|
+
log(`${header(e)} Resolved PDF:`, e.toolResult);
|
|
1705
|
+
break;
|
|
1706
|
+
}
|
|
1707
|
+
case "resolveImageFile": {
|
|
1708
|
+
log(`${header(e)} Resolved Image:`, e.toolResult);
|
|
1709
|
+
break;
|
|
1710
|
+
}
|
|
1711
|
+
case "attemptCompletion": {
|
|
1712
|
+
log(`${header(e)} Attempting completion`);
|
|
1713
|
+
break;
|
|
1714
|
+
}
|
|
1715
|
+
case "completeRun": {
|
|
1716
|
+
logUsage(e);
|
|
1717
|
+
log(`${header(e)} Completing run`);
|
|
1718
|
+
debug(`${header(e)} Result:`, e.text);
|
|
1719
|
+
break;
|
|
1720
|
+
}
|
|
1721
|
+
case "stopRunByInteractiveTool": {
|
|
1722
|
+
logUsage(e);
|
|
1723
|
+
log(`${header(e)} Stopping run by interactive tool`);
|
|
1724
|
+
break;
|
|
1725
|
+
}
|
|
1726
|
+
case "stopRunByDelegate": {
|
|
1727
|
+
logUsage(e);
|
|
1728
|
+
log(`${header(e)} Stopping run by delegate`);
|
|
1729
|
+
break;
|
|
1730
|
+
}
|
|
1731
|
+
case "stopRunByExceededMaxSteps": {
|
|
1732
|
+
logUsage(e);
|
|
1733
|
+
log(`${header(e)} Stopping run by exceeded max steps`);
|
|
1734
|
+
break;
|
|
1735
|
+
}
|
|
1736
|
+
case "continueToNextStep": {
|
|
1737
|
+
logUsage(e);
|
|
1738
|
+
log(`${header(e)} Continuing to next step`);
|
|
1739
|
+
if (e.checkpoint.contextWindowUsage) {
|
|
1740
|
+
log(`${header(e)} Context window usage: ${e.checkpoint.contextWindowUsage.toFixed(2)}%`);
|
|
1741
|
+
}
|
|
1742
|
+
break;
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1745
|
+
}
|
|
1746
|
+
function logUsage(e) {
|
|
1747
|
+
const usageByStep = [
|
|
1748
|
+
`In: ${e.step.usage.inputTokens.toLocaleString()}`,
|
|
1749
|
+
`Reasoning: ${e.step.usage.reasoningTokens.toLocaleString()}`,
|
|
1750
|
+
`Out: ${e.step.usage.outputTokens.toLocaleString()}`,
|
|
1751
|
+
`Total: ${e.step.usage.totalTokens.toLocaleString()}`,
|
|
1752
|
+
`Cache-read: ${e.step.usage.cachedInputTokens.toLocaleString()}`
|
|
1753
|
+
].join(", ");
|
|
1754
|
+
const usageByRun = [
|
|
1755
|
+
`In: ${e.checkpoint.usage.inputTokens.toLocaleString()}`,
|
|
1756
|
+
`Reasoning: ${e.checkpoint.usage.reasoningTokens.toLocaleString()}`,
|
|
1757
|
+
`Out: ${e.checkpoint.usage.outputTokens.toLocaleString()}`,
|
|
1758
|
+
`Total: ${e.checkpoint.usage.totalTokens.toLocaleString()}`,
|
|
1759
|
+
`Cache-read: ${e.checkpoint.usage.cachedInputTokens.toLocaleString()}`
|
|
1760
|
+
].join(", ");
|
|
1761
|
+
log(`${header(e)} Tokens usage by step: ${usageByStep}`);
|
|
1762
|
+
log(`${header(e)} Tokens usage by run: ${usageByRun}`);
|
|
1763
|
+
}
|
|
1764
|
+
var RunEventEmitter = class {
|
|
1765
|
+
listeners = [];
|
|
1766
|
+
subscribe(listener) {
|
|
1767
|
+
this.listeners.push(listener);
|
|
1768
|
+
}
|
|
1769
|
+
async emit(event) {
|
|
1770
|
+
for (const listener of this.listeners) {
|
|
1771
|
+
await listener({
|
|
1772
|
+
...event,
|
|
1773
|
+
id: createId(),
|
|
1774
|
+
timestamp: Date.now()
|
|
1775
|
+
});
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
};
|
|
1779
|
+
async function resolveExpertToRun(expertKey, experts, clientOptions) {
|
|
1780
|
+
console.log(experts);
|
|
1781
|
+
if (experts[expertKey]) {
|
|
1782
|
+
return experts[expertKey];
|
|
1783
|
+
}
|
|
1784
|
+
const client = new ApiV1Client({
|
|
1785
|
+
baseUrl: clientOptions.perstackApiBaseUrl,
|
|
1786
|
+
apiKey: clientOptions.perstackApiKey
|
|
1787
|
+
});
|
|
1788
|
+
const { expert } = await client.registry.experts.get({ expertKey });
|
|
1789
|
+
experts[expertKey] = toRuntimeExpert(expert);
|
|
1790
|
+
return experts[expertKey];
|
|
1791
|
+
}
|
|
1792
|
+
function toRuntimeExpert(expert) {
|
|
1793
|
+
const skills = Object.fromEntries(
|
|
1794
|
+
Object.entries(expert.skills).map(([name, skill]) => {
|
|
1795
|
+
switch (skill.type) {
|
|
1796
|
+
case "mcpStdioSkill":
|
|
1797
|
+
return [
|
|
1798
|
+
name,
|
|
1799
|
+
{
|
|
1800
|
+
...skill,
|
|
1801
|
+
name
|
|
1802
|
+
}
|
|
1803
|
+
];
|
|
1804
|
+
case "mcpSseSkill":
|
|
1805
|
+
return [
|
|
1806
|
+
name,
|
|
1807
|
+
{
|
|
1808
|
+
...skill,
|
|
1809
|
+
name
|
|
1810
|
+
}
|
|
1811
|
+
];
|
|
1812
|
+
case "interactiveSkill":
|
|
1813
|
+
return [
|
|
1814
|
+
name,
|
|
1815
|
+
{
|
|
1816
|
+
...skill,
|
|
1817
|
+
name
|
|
1818
|
+
}
|
|
1819
|
+
];
|
|
1820
|
+
}
|
|
2445
1821
|
})
|
|
2446
|
-
)
|
|
2447
|
-
|
|
1822
|
+
);
|
|
1823
|
+
return {
|
|
1824
|
+
...expert,
|
|
1825
|
+
skills
|
|
1826
|
+
};
|
|
1827
|
+
}
|
|
2448
1828
|
|
|
2449
1829
|
// src/runtime.ts
|
|
2450
1830
|
async function run(runInput, options) {
|
|
2451
|
-
const runParams =
|
|
1831
|
+
const runParams = runParamsSchema.parse(runInput);
|
|
2452
1832
|
const eventListener = options?.eventListener ?? defaultEventListener;
|
|
2453
1833
|
const retrieveCheckpoint = options?.retrieveCheckpoint ?? defaultRetrieveCheckpoint;
|
|
2454
1834
|
const storeCheckpoint = options?.storeCheckpoint ?? defaultStoreCheckpoint;
|
|
2455
1835
|
const eventEmitter = new RunEventEmitter();
|
|
2456
1836
|
eventEmitter.subscribe(eventListener);
|
|
2457
1837
|
let { setting, checkpoint } = runParams;
|
|
2458
|
-
|
|
2459
|
-
checkpoint.status = "proceeding";
|
|
2460
|
-
}
|
|
1838
|
+
const contextWindow = getContextWindow(setting.providerConfig.name, setting.model);
|
|
2461
1839
|
if (setting.workspace) {
|
|
2462
1840
|
if (!path.isAbsolute(setting.workspace)) {
|
|
2463
1841
|
throw new Error(`Workspace path must be absolute: ${setting.workspace}`);
|
|
@@ -2468,7 +1846,7 @@ async function run(runInput, options) {
|
|
|
2468
1846
|
while (true) {
|
|
2469
1847
|
const { expertToRun, experts } = await setupExperts(setting);
|
|
2470
1848
|
printRunSetting(expertToRun.name, experts, setting);
|
|
2471
|
-
const skillManagers = await getSkillManagers(expertToRun, experts);
|
|
1849
|
+
const skillManagers = await getSkillManagers(expertToRun, experts, setting);
|
|
2472
1850
|
const runActor = createActor(runtimeStateMachine, {
|
|
2473
1851
|
input: {
|
|
2474
1852
|
setting: {
|
|
@@ -2477,10 +1855,10 @@ async function run(runInput, options) {
|
|
|
2477
1855
|
},
|
|
2478
1856
|
initialCheckpoint: checkpoint ? {
|
|
2479
1857
|
...checkpoint,
|
|
2480
|
-
id:
|
|
1858
|
+
id: createId(),
|
|
2481
1859
|
stepNumber: checkpoint.stepNumber + 1
|
|
2482
1860
|
} : {
|
|
2483
|
-
id:
|
|
1861
|
+
id: createId(),
|
|
2484
1862
|
runId: setting.runId,
|
|
2485
1863
|
expert: {
|
|
2486
1864
|
key: setting.expertKey,
|
|
@@ -2490,7 +1868,9 @@ async function run(runInput, options) {
|
|
|
2490
1868
|
stepNumber: 1,
|
|
2491
1869
|
status: "init",
|
|
2492
1870
|
messages: [],
|
|
2493
|
-
usage: createEmptyUsage()
|
|
1871
|
+
usage: createEmptyUsage(),
|
|
1872
|
+
contextWindow,
|
|
1873
|
+
contextWindowUsage: contextWindow ? 0 : void 0
|
|
2494
1874
|
},
|
|
2495
1875
|
eventListener,
|
|
2496
1876
|
skillManagers
|
|
@@ -2619,9 +1999,13 @@ async function run(runInput, options) {
|
|
|
2619
1999
|
async function setupExperts(setting) {
|
|
2620
2000
|
const { expertKey } = setting;
|
|
2621
2001
|
const experts = { ...setting.experts };
|
|
2622
|
-
const
|
|
2002
|
+
const clientOptions = {
|
|
2003
|
+
perstackApiBaseUrl: setting.perstackApiBaseUrl,
|
|
2004
|
+
perstackApiKey: setting.perstackApiKey
|
|
2005
|
+
};
|
|
2006
|
+
const expertToRun = await resolveExpertToRun(expertKey, experts, clientOptions);
|
|
2623
2007
|
for (const delegateName of expertToRun.delegates) {
|
|
2624
|
-
const delegate = await resolveExpertToRun(delegateName, experts);
|
|
2008
|
+
const delegate = await resolveExpertToRun(delegateName, experts, clientOptions);
|
|
2625
2009
|
if (!delegate) {
|
|
2626
2010
|
throw new Error(`Delegate ${delegateName} not found`);
|
|
2627
2011
|
}
|
|
@@ -2650,12 +2034,12 @@ async function storeRunSetting(setting) {
|
|
|
2650
2034
|
const runDir = getRunDir(setting.runId);
|
|
2651
2035
|
if (existsSync(runDir)) {
|
|
2652
2036
|
const runSettingPath = path.resolve(runDir, "run-setting.json");
|
|
2653
|
-
const runSetting = JSON.parse(await
|
|
2037
|
+
const runSetting = JSON.parse(await readFile(runSettingPath, "utf-8"));
|
|
2654
2038
|
runSetting.updatedAt = Date.now();
|
|
2655
|
-
await
|
|
2039
|
+
await writeFile(runSettingPath, JSON.stringify(runSetting, null, 2), "utf-8");
|
|
2656
2040
|
} else {
|
|
2657
|
-
await
|
|
2658
|
-
await
|
|
2041
|
+
await mkdir(runDir, { recursive: true });
|
|
2042
|
+
await writeFile(
|
|
2659
2043
|
path.resolve(runDir, "run-setting.json"),
|
|
2660
2044
|
JSON.stringify(setting, null, 2),
|
|
2661
2045
|
"utf-8"
|
|
@@ -2665,119 +2049,7 @@ async function storeRunSetting(setting) {
|
|
|
2665
2049
|
function getRunDir(runId) {
|
|
2666
2050
|
return `${process.cwd()}/perstack/runs/${runId}`;
|
|
2667
2051
|
}
|
|
2668
|
-
async function parseConfig(config) {
|
|
2669
|
-
const toml = TOML.parse(config ?? "");
|
|
2670
|
-
return PerstackConfigSchema.parse(toml);
|
|
2671
|
-
}
|
|
2672
2052
|
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
var RunInputSchema = z5.object({
|
|
2676
|
-
expertKey: z5.string(),
|
|
2677
|
-
query: z5.string(),
|
|
2678
|
-
options: z5.object({
|
|
2679
|
-
config: z5.string().optional(),
|
|
2680
|
-
model: z5.string().optional(),
|
|
2681
|
-
temperature: z5.string().optional().transform((value) => {
|
|
2682
|
-
if (value === void 0) {
|
|
2683
|
-
return void 0;
|
|
2684
|
-
}
|
|
2685
|
-
const parsedValue = Number.parseFloat(value);
|
|
2686
|
-
if (Number.isNaN(parsedValue)) {
|
|
2687
|
-
return void 0;
|
|
2688
|
-
}
|
|
2689
|
-
return parsedValue;
|
|
2690
|
-
}),
|
|
2691
|
-
maxSteps: z5.string().optional().transform((value) => {
|
|
2692
|
-
if (value === void 0) {
|
|
2693
|
-
return void 0;
|
|
2694
|
-
}
|
|
2695
|
-
const parsedValue = Number.parseInt(value);
|
|
2696
|
-
if (Number.isNaN(parsedValue)) {
|
|
2697
|
-
return void 0;
|
|
2698
|
-
}
|
|
2699
|
-
return parsedValue;
|
|
2700
|
-
}),
|
|
2701
|
-
maxRetries: z5.string().optional().transform((value) => {
|
|
2702
|
-
if (value === void 0) {
|
|
2703
|
-
return void 0;
|
|
2704
|
-
}
|
|
2705
|
-
const parsedValue = Number.parseInt(value);
|
|
2706
|
-
if (Number.isNaN(parsedValue)) {
|
|
2707
|
-
return void 0;
|
|
2708
|
-
}
|
|
2709
|
-
return parsedValue;
|
|
2710
|
-
}),
|
|
2711
|
-
timeout: z5.string().optional().transform((value) => {
|
|
2712
|
-
if (value === void 0) {
|
|
2713
|
-
return void 0;
|
|
2714
|
-
}
|
|
2715
|
-
const parsedValue = Number.parseInt(value);
|
|
2716
|
-
if (Number.isNaN(parsedValue)) {
|
|
2717
|
-
return void 0;
|
|
2718
|
-
}
|
|
2719
|
-
return parsedValue;
|
|
2720
|
-
}),
|
|
2721
|
-
runId: z5.string().optional()
|
|
2722
|
-
})
|
|
2723
|
-
});
|
|
2724
|
-
export {
|
|
2725
|
-
CheckpointSchema,
|
|
2726
|
-
CheckpointStatusSchema,
|
|
2727
|
-
ExpertMessageSchema,
|
|
2728
|
-
ExpertSchema,
|
|
2729
|
-
FileBinaryPartSchema,
|
|
2730
|
-
FileInlinePartSchema,
|
|
2731
|
-
FileUrlPartSchema,
|
|
2732
|
-
ImageBinaryPartSchema,
|
|
2733
|
-
ImageInlinePartSchema,
|
|
2734
|
-
ImageUrlPartSchema,
|
|
2735
|
-
InstructionMessageSchema,
|
|
2736
|
-
InteractiveSkillSchema,
|
|
2737
|
-
InteractiveToolSchema,
|
|
2738
|
-
McpSseSkillSchema,
|
|
2739
|
-
McpStdioSkillSchema,
|
|
2740
|
-
MessageSchema,
|
|
2741
|
-
PerstackConfigSchema,
|
|
2742
|
-
RegistryV1ExpertsGetResponseSchema,
|
|
2743
|
-
RunInputSchema,
|
|
2744
|
-
RunParamsSchema,
|
|
2745
|
-
StateMachineLogics,
|
|
2746
|
-
TextPartSchema,
|
|
2747
|
-
ToolCallPartSchema,
|
|
2748
|
-
ToolMessageSchema,
|
|
2749
|
-
ToolResultPartSchema,
|
|
2750
|
-
UsageSchema,
|
|
2751
|
-
UserMessageSchema,
|
|
2752
|
-
attemptCompletion,
|
|
2753
|
-
callDelegate,
|
|
2754
|
-
callInteractiveTool,
|
|
2755
|
-
callTool,
|
|
2756
|
-
completeRun,
|
|
2757
|
-
continueToNextStep,
|
|
2758
|
-
createEvent,
|
|
2759
|
-
defaultEventListener,
|
|
2760
|
-
expertKeyRegex,
|
|
2761
|
-
expertNameRegex,
|
|
2762
|
-
finishToolCall,
|
|
2763
|
-
getRunDir,
|
|
2764
|
-
maxNameLength,
|
|
2765
|
-
parseConfig,
|
|
2766
|
-
parseExpertKey,
|
|
2767
|
-
resolveImageFile,
|
|
2768
|
-
resolvePdfFile,
|
|
2769
|
-
resolveThought,
|
|
2770
|
-
resolveToolResult,
|
|
2771
|
-
retry,
|
|
2772
|
-
run,
|
|
2773
|
-
runtimeStateMachine,
|
|
2774
|
-
skillNameRegex,
|
|
2775
|
-
startGeneration,
|
|
2776
|
-
startRun,
|
|
2777
|
-
stopRunByDelegate,
|
|
2778
|
-
stopRunByExceededMaxSteps,
|
|
2779
|
-
stopRunByInteractiveTool,
|
|
2780
|
-
tagNameRegex,
|
|
2781
|
-
versionRegex
|
|
2782
|
-
};
|
|
2053
|
+
export { StateMachineLogics, calculateContextWindowUsage, defaultEventListener, getContextWindow, getModel, getRunDir, run, runtimeStateMachine };
|
|
2054
|
+
//# sourceMappingURL=index.js.map
|
|
2783
2055
|
//# sourceMappingURL=index.js.map
|