@earendil-works/pi-durable 1.1.0 → 1.2.0
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/CHANGELOG.md +40 -0
- package/README.md +147 -11
- package/dist/codemode/codemode.d.ts +173 -0
- package/dist/codemode/codemode.d.ts.map +1 -0
- package/dist/codemode/codemode.js +886 -0
- package/dist/codemode/codemode.js.map +1 -0
- package/dist/codemode/describe.d.ts +38 -0
- package/dist/codemode/describe.d.ts.map +1 -0
- package/dist/codemode/describe.js +119 -0
- package/dist/codemode/describe.js.map +1 -0
- package/dist/codemode/index.d.ts +3 -0
- package/dist/codemode/index.d.ts.map +1 -0
- package/dist/codemode/index.js +3 -0
- package/dist/codemode/index.js.map +1 -0
- package/dist/codemode/models.d.ts +28 -0
- package/dist/codemode/models.d.ts.map +1 -0
- package/dist/codemode/models.js +231 -0
- package/dist/codemode/models.js.map +1 -0
- package/dist/codemode/output.d.ts +41 -0
- package/dist/codemode/output.d.ts.map +1 -0
- package/dist/codemode/output.js +167 -0
- package/dist/codemode/output.js.map +1 -0
- package/dist/env/index.d.ts +42 -0
- package/dist/env/index.d.ts.map +1 -1
- package/dist/env/index.js.map +1 -1
- package/dist/errors.d.ts +20 -3
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +34 -5
- package/dist/errors.js.map +1 -1
- package/dist/harness/agent.d.ts +5 -2
- package/dist/harness/agent.d.ts.map +1 -1
- package/dist/harness/agent.js +69 -25
- package/dist/harness/agent.js.map +1 -1
- package/dist/harness/compaction.d.ts +6 -1
- package/dist/harness/compaction.d.ts.map +1 -1
- package/dist/harness/compaction.js +10 -7
- package/dist/harness/compaction.js.map +1 -1
- package/dist/harness/events.d.ts +35 -15
- package/dist/harness/events.d.ts.map +1 -1
- package/dist/harness/events.js +77 -23
- package/dist/harness/events.js.map +1 -1
- package/dist/harness/generation.d.ts +2 -0
- package/dist/harness/generation.d.ts.map +1 -1
- package/dist/harness/generation.js +31 -8
- package/dist/harness/generation.js.map +1 -1
- package/dist/harness/harness.d.ts +7 -0
- package/dist/harness/harness.d.ts.map +1 -1
- package/dist/harness/harness.js +53 -26
- package/dist/harness/harness.js.map +1 -1
- package/dist/harness/inbox.d.ts +16 -4
- package/dist/harness/inbox.d.ts.map +1 -1
- package/dist/harness/inbox.js +26 -3
- package/dist/harness/inbox.js.map +1 -1
- package/dist/harness/live.d.ts +74 -17
- package/dist/harness/live.d.ts.map +1 -1
- package/dist/harness/live.js +66 -14
- package/dist/harness/live.js.map +1 -1
- package/dist/harness/scheduler.d.ts +18 -5
- package/dist/harness/scheduler.d.ts.map +1 -1
- package/dist/harness/scheduler.js +696 -163
- package/dist/harness/scheduler.js.map +1 -1
- package/dist/harness/submissions.d.ts +7 -4
- package/dist/harness/submissions.d.ts.map +1 -1
- package/dist/harness/submissions.js +32 -13
- package/dist/harness/submissions.js.map +1 -1
- package/dist/harness/task-graph.d.ts +1 -1
- package/dist/harness/task-graph.d.ts.map +1 -1
- package/dist/harness/task-graph.js +15 -10
- package/dist/harness/task-graph.js.map +1 -1
- package/dist/harness/tool.d.ts +45 -5
- package/dist/harness/tool.d.ts.map +1 -1
- package/dist/harness/tool.js +377 -62
- package/dist/harness/tool.js.map +1 -1
- package/dist/harness/types.d.ts +153 -13
- package/dist/harness/types.d.ts.map +1 -1
- package/dist/harness/types.js.map +1 -1
- package/dist/harness/util.d.ts +6 -1
- package/dist/harness/util.d.ts.map +1 -1
- package/dist/harness/util.js +5 -2
- package/dist/harness/util.js.map +1 -1
- package/dist/harness/view.d.ts +6 -2
- package/dist/harness/view.d.ts.map +1 -1
- package/dist/harness/view.js +22 -12
- package/dist/harness/view.js.map +1 -1
- package/dist/images/cloudflare.d.ts +4 -0
- package/dist/images/cloudflare.d.ts.map +1 -0
- package/dist/images/cloudflare.js +9 -0
- package/dist/images/cloudflare.js.map +1 -0
- package/dist/images/exif.d.ts +3 -0
- package/dist/images/exif.d.ts.map +1 -0
- package/dist/images/exif.js +71 -0
- package/dist/images/exif.js.map +1 -0
- package/dist/images/index.d.ts +18 -0
- package/dist/images/index.d.ts.map +1 -0
- package/dist/images/index.js +125 -0
- package/dist/images/index.js.map +1 -0
- package/dist/images/node-worker.d.ts +17 -0
- package/dist/images/node-worker.d.ts.map +1 -0
- package/dist/images/node-worker.js +16 -0
- package/dist/images/node-worker.js.map +1 -0
- package/dist/images/node.d.ts +21 -0
- package/dist/images/node.d.ts.map +1 -0
- package/dist/images/node.js +121 -0
- package/dist/images/node.js.map +1 -0
- package/dist/index.d.ts +6 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/search.d.ts +13 -0
- package/dist/search.d.ts.map +1 -0
- package/dist/search.js +85 -0
- package/dist/search.js.map +1 -0
- package/dist/session/observation.d.ts +10 -3
- package/dist/session/observation.d.ts.map +1 -1
- package/dist/session/observation.js +24 -34
- package/dist/session/observation.js.map +1 -1
- package/dist/session/session.d.ts +68 -5
- package/dist/session/session.d.ts.map +1 -1
- package/dist/session/session.js +461 -49
- package/dist/session/session.js.map +1 -1
- package/dist/session/transaction.d.ts.map +1 -1
- package/dist/session/transaction.js +1 -0
- package/dist/session/transaction.js.map +1 -1
- package/dist/storage/jsonl/storage.d.ts +4 -0
- package/dist/storage/jsonl/storage.d.ts.map +1 -1
- package/dist/storage/jsonl/storage.js +2 -1
- package/dist/storage/jsonl/storage.js.map +1 -1
- package/dist/storage/memory.d.ts.map +1 -1
- package/dist/storage/memory.js +8 -10
- package/dist/storage/memory.js.map +1 -1
- package/dist/storage/scan.d.ts.map +1 -1
- package/dist/storage/scan.js +5 -4
- package/dist/storage/scan.js.map +1 -1
- package/dist/storage/sqlite/storage.d.ts.map +1 -1
- package/dist/storage/sqlite/storage.js +10 -12
- package/dist/storage/sqlite/storage.js.map +1 -1
- package/dist/testing/env-conformance.d.ts.map +1 -1
- package/dist/testing/env-conformance.js +100 -0
- package/dist/testing/env-conformance.js.map +1 -1
- package/dist/testing/runner.d.ts +1 -1
- package/dist/testing/runner.d.ts.map +1 -1
- package/dist/testing/runner.js.map +1 -1
- package/dist/testing/storage-conformance.d.ts.map +1 -1
- package/dist/testing/storage-conformance.js +3 -2
- package/dist/testing/storage-conformance.js.map +1 -1
- package/dist/testing/types.d.ts +2 -0
- package/dist/testing/types.d.ts.map +1 -1
- package/dist/testing/types.js.map +1 -1
- package/dist/tool-search/index.d.ts +2 -0
- package/dist/tool-search/index.d.ts.map +1 -0
- package/dist/tool-search/index.js +2 -0
- package/dist/tool-search/index.js.map +1 -0
- package/dist/tool-search/tool-search.d.ts +23 -0
- package/dist/tool-search/tool-search.d.ts.map +1 -0
- package/dist/tool-search/tool-search.js +84 -0
- package/dist/tool-search/tool-search.js.map +1 -0
- package/dist/tools/bash.d.ts +10 -2
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash.js +27 -9
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/edit.js +1 -1
- package/dist/tools/edit.js.map +1 -1
- package/dist/tools/image-processor.d.ts +47 -0
- package/dist/tools/image-processor.d.ts.map +1 -0
- package/dist/tools/image-processor.js +30 -0
- package/dist/tools/image-processor.js.map +1 -0
- package/dist/tools/image.d.ts +8 -0
- package/dist/tools/image.d.ts.map +1 -1
- package/dist/tools/image.js +47 -0
- package/dist/tools/image.js.map +1 -1
- package/dist/tools/index.d.ts +25 -2
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +13 -5
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/read.d.ts +10 -2
- package/dist/tools/read.d.ts.map +1 -1
- package/dist/tools/read.js +87 -28
- package/dist/tools/read.js.map +1 -1
- package/dist/tools/write.js +1 -1
- package/dist/tools/write.js.map +1 -1
- package/dist/types.d.ts +59 -5
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +43 -4
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { Type } from "typebox";
|
|
2
|
+
import { defineTool } from "../harness/define.js";
|
|
3
|
+
/** Reserved names of code mode's internal tools; they are never listed to scripts or the model. */
|
|
4
|
+
export const CODEMODE_INTERNAL_PREFIX = "pi.codemode.";
|
|
5
|
+
export const CLASSIFY_TOOL = `${CODEMODE_INTERNAL_PREFIX}models.classify`;
|
|
6
|
+
export const GENERATE_IMAGES_TOOL = `${CODEMODE_INTERNAL_PREFIX}models.generateImages`;
|
|
7
|
+
/** The global a script calls, by internal tool. */
|
|
8
|
+
export const MODEL_CALLS = {
|
|
9
|
+
"models.classify": CLASSIFY_TOOL,
|
|
10
|
+
"models.generateImages": GENERATE_IMAGES_TOOL,
|
|
11
|
+
};
|
|
12
|
+
/** The catalog functions, host globals of the `models` namespace. */
|
|
13
|
+
export const CATALOG_GLOBALS = [
|
|
14
|
+
"models.getModelsOfType",
|
|
15
|
+
"models.getAvailableOfType",
|
|
16
|
+
"models.getModelOfType",
|
|
17
|
+
];
|
|
18
|
+
/** `models.classify()` and `models.generateImages()` calls one script may have in flight; the rest wait. */
|
|
19
|
+
export const MAX_CONCURRENT_MODEL_CALLS = 4;
|
|
20
|
+
const MODEL_TYPES = new Set(["chat", "image", "classifier"]);
|
|
21
|
+
export const MODELS_DESCRIPTION = [
|
|
22
|
+
"`models` (all functions return promises):",
|
|
23
|
+
'- `models.getModelsOfType(type, provider?)`, `models.getAvailableOfType(type, provider?)` (configured auth only), `models.getModelOfType(type, provider, id)`: the catalog; `type` is "chat", "image", or "classifier".',
|
|
24
|
+
'- `models.classify(model, { state: { ... }, images?: [{ type: "image", data, mimeType }], questions: { <id>: { type: "choice", instructions, criteria: { <label>: <meaning> } } | { type: "score", instructions, criteria: [<lowest level>, ..., <highest level>] } | { type: "bool", instructions, criteria: { true: <meaning>, false: <meaning> } } } })` resolves to `{ answers: { <id>: ... }, stopReason, errorMessage?, usage? }`.',
|
|
25
|
+
'- `models.generateImages(model, { input: [{ type: "text", text: <prompt> }, ...optional { type: "image", data, mimeType }] })` resolves to `{ output: [image blocks], stopReason, errorMessage?, usage? }`; show each image with `image(block)`.',
|
|
26
|
+
`At most ${MAX_CONCURRENT_MODEL_CALLS} classify/generateImages calls run at once; more wait.`,
|
|
27
|
+
].join("\n");
|
|
28
|
+
function isRecord(value) {
|
|
29
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
30
|
+
}
|
|
31
|
+
/** A script value in an error message: `undefined`, `a string`, `an array`, or its keys (`{ prompt }`). */
|
|
32
|
+
function describeValue(value) {
|
|
33
|
+
if (value === undefined || value === null)
|
|
34
|
+
return String(value);
|
|
35
|
+
if (Array.isArray(value))
|
|
36
|
+
return value.length === 0 ? "an empty array" : "an array";
|
|
37
|
+
if (typeof value === "object") {
|
|
38
|
+
const keys = Object.keys(value);
|
|
39
|
+
if (keys.length === 0)
|
|
40
|
+
return "{}";
|
|
41
|
+
return `{ ${keys.slice(0, 6).join(", ")}${keys.length > 6 ? ", ..." : ""} }`;
|
|
42
|
+
}
|
|
43
|
+
return typeof value === "string" ? "a string" : `a ${typeof value}`;
|
|
44
|
+
}
|
|
45
|
+
/** `an image`, `a classifier`. */
|
|
46
|
+
function withArticle(word) {
|
|
47
|
+
return `${/^[aeiou]/.test(word) ? "an" : "a"} ${word}`;
|
|
48
|
+
}
|
|
49
|
+
function toModelType(value) {
|
|
50
|
+
if (typeof value === "string" && MODEL_TYPES.has(value))
|
|
51
|
+
return value;
|
|
52
|
+
throw new Error(`Unknown model type ${JSON.stringify(value)}. Use "chat", "image", or "classifier".`);
|
|
53
|
+
}
|
|
54
|
+
function toProvider(value) {
|
|
55
|
+
if (value === undefined || value === null)
|
|
56
|
+
return undefined;
|
|
57
|
+
if (typeof value !== "string")
|
|
58
|
+
throw new Error("provider must be a string");
|
|
59
|
+
return value;
|
|
60
|
+
}
|
|
61
|
+
/** Catalog entry for scripts. `headers` is dropped because configured headers can carry credentials. */
|
|
62
|
+
function toModelInfo(model) {
|
|
63
|
+
const info = { ...model };
|
|
64
|
+
delete info.headers;
|
|
65
|
+
return JSON.parse(JSON.stringify(info));
|
|
66
|
+
}
|
|
67
|
+
/** The catalog functions: what a script's call of `name` with `args` (the argument list) resolves to. */
|
|
68
|
+
export async function catalogCall(name, args, models, context) {
|
|
69
|
+
const list = Array.isArray(args) ? args : [];
|
|
70
|
+
const [type, provider, id] = list;
|
|
71
|
+
if (name === "models.getModelsOfType")
|
|
72
|
+
return models.getModelsOfType(toModelType(type), toProvider(provider)).map(toModelInfo);
|
|
73
|
+
if (name === "models.getAvailableOfType") {
|
|
74
|
+
const available = await models.getAvailableOfType(toModelType(type), toProvider(provider), {
|
|
75
|
+
...(context.abortSignal === undefined ? {} : { signal: context.abortSignal }),
|
|
76
|
+
});
|
|
77
|
+
return available.map(toModelInfo);
|
|
78
|
+
}
|
|
79
|
+
if (typeof provider !== "string" || typeof id !== "string") {
|
|
80
|
+
throw new Error(`models.getModelOfType(type, provider, id) expects three strings, got (${list.map(describeValue).join(", ")}). The provider and the id are separate arguments, for example models.getModelOfType("classifier", "typesafe", "jev-latest").`);
|
|
81
|
+
}
|
|
82
|
+
const model = models.getModelOfType(toModelType(type), provider, id);
|
|
83
|
+
return model === undefined ? undefined : toModelInfo(model);
|
|
84
|
+
}
|
|
85
|
+
const CLASSIFIER_CONTEXT_SHAPE = '{ state: { ... }, images?: [{ type: "image", data: <base64>, mimeType }], questions: { <id>: { type: "choice", instructions, criteria: { <label>: <meaning> } } | { type: "score", instructions, criteria: [<lowest level>, ..., <highest level>] } | { type: "bool", instructions, criteria: { true: <meaning>, false: <meaning> } } } }';
|
|
86
|
+
/** Check a script's classifier context, so mistakes fail with the expected shape instead of a provider error. */
|
|
87
|
+
function checkClassifierContext(context) {
|
|
88
|
+
const fail = (problem) => new Error(`models.classify() ${problem}. Expected context: ${CLASSIFIER_CONTEXT_SHAPE}.`);
|
|
89
|
+
if (!isRecord(context))
|
|
90
|
+
throw fail(`expects a context object as its second argument, got ${describeValue(context)}`);
|
|
91
|
+
if (!isRecord(context.state))
|
|
92
|
+
throw fail(`context.state must be an object, got ${describeValue(context.state)}`);
|
|
93
|
+
const { images } = context;
|
|
94
|
+
if (images !== undefined) {
|
|
95
|
+
if (!Array.isArray(images))
|
|
96
|
+
throw fail(`context.images must be an array, got ${describeValue(images)}`);
|
|
97
|
+
images.forEach((image, index) => {
|
|
98
|
+
if (!isRecord(image) ||
|
|
99
|
+
image.type !== "image" ||
|
|
100
|
+
typeof image.data !== "string" ||
|
|
101
|
+
typeof image.mimeType !== "string") {
|
|
102
|
+
throw fail(`context.images[${index}] must be an image block, got ${describeValue(image)}`);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
const { questions } = context;
|
|
107
|
+
if (!isRecord(questions) || Object.keys(questions).length === 0) {
|
|
108
|
+
throw fail(`context.questions must map question IDs to questions, got ${describeValue(questions)}`);
|
|
109
|
+
}
|
|
110
|
+
const isStrings = (values) => values.length > 0 && values.every((value) => typeof value === "string");
|
|
111
|
+
for (const [id, question] of Object.entries(questions)) {
|
|
112
|
+
const at = `context.questions.${id}`;
|
|
113
|
+
if (!isRecord(question))
|
|
114
|
+
throw fail(`${at} must be a question object, got ${describeValue(question)}`);
|
|
115
|
+
if (typeof question.instructions !== "string")
|
|
116
|
+
throw fail(`${at}.instructions must be a string`);
|
|
117
|
+
const { criteria } = question;
|
|
118
|
+
if (question.type === "choice") {
|
|
119
|
+
if (!isRecord(criteria) || !isStrings(Object.values(criteria))) {
|
|
120
|
+
throw fail(`${at} is a "choice" question, so criteria must map each label to its meaning`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
else if (question.type === "score") {
|
|
124
|
+
if (!Array.isArray(criteria) || !isStrings(criteria)) {
|
|
125
|
+
throw fail(`${at} is a "score" question, so criteria must list the levels as strings, lowest first`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
else if (question.type === "bool") {
|
|
129
|
+
if (!isRecord(criteria) || typeof criteria.true !== "string" || typeof criteria.false !== "string") {
|
|
130
|
+
throw fail(`${at} is a "bool" question, so criteria must be { true: string, false: string }`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
throw fail(`${at}.type must be "choice", "score", or "bool", got ${JSON.stringify(question.type)}`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return context;
|
|
138
|
+
}
|
|
139
|
+
/** Check a script's image context, so mistakes such as `{ prompt }` fail with the expected shape. */
|
|
140
|
+
function checkImagesContext(context) {
|
|
141
|
+
const fail = (problem) => new Error(`models.generateImages() ${problem}. Expected context: { input: [{ type: "text", text: <prompt> }, ...optional { type: "image", data: <base64>, mimeType } references] }.`);
|
|
142
|
+
if (!isRecord(context))
|
|
143
|
+
throw fail(`expects a context object as its second argument, got ${describeValue(context)}`);
|
|
144
|
+
const { input } = context;
|
|
145
|
+
if (!Array.isArray(input) || input.length === 0) {
|
|
146
|
+
throw fail(`context.input must be a non-empty array of blocks, got ${describeValue(input)}`);
|
|
147
|
+
}
|
|
148
|
+
input.forEach((block, index) => {
|
|
149
|
+
if (isRecord(block) && block.type === "text" && typeof block.text === "string")
|
|
150
|
+
return;
|
|
151
|
+
if (isRecord(block) &&
|
|
152
|
+
block.type === "image" &&
|
|
153
|
+
typeof block.data === "string" &&
|
|
154
|
+
typeof block.mimeType === "string")
|
|
155
|
+
return;
|
|
156
|
+
throw fail(`context.input[${index}] must be a text or image block, got ${describeValue(block)}`);
|
|
157
|
+
});
|
|
158
|
+
return context;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Resolve the script's model by provider and id only, so a script-supplied baseUrl or headers never receive the
|
|
162
|
+
* credentials, with errors that say what to do.
|
|
163
|
+
*/
|
|
164
|
+
function resolveModel(name, type, model, models) {
|
|
165
|
+
const listHint = `List the ${type} models you can use with models.getAvailableOfType("${type}").`;
|
|
166
|
+
if (!isRecord(model) || typeof model.provider !== "string" || typeof model.id !== "string") {
|
|
167
|
+
const undefinedHint = model === undefined || model === null
|
|
168
|
+
? " models.getModelOfType() returns undefined for an unknown provider or id."
|
|
169
|
+
: "";
|
|
170
|
+
throw new Error(`${name}() expects ${withArticle(type)} model as its first argument, got ${describeValue(model)}.${undefinedHint} ${listHint}`);
|
|
171
|
+
}
|
|
172
|
+
const { provider, id } = model;
|
|
173
|
+
const resolved = models.getModelOfType(type, provider, id);
|
|
174
|
+
if (resolved !== undefined)
|
|
175
|
+
return resolved;
|
|
176
|
+
const actualType = [...MODEL_TYPES].find((other) => other !== type && models.getModelOfType(other, provider, id) !== undefined);
|
|
177
|
+
throw new Error(actualType
|
|
178
|
+
? `"${provider}/${id}" is ${withArticle(actualType)} model, not ${withArticle(type)} model. ${listHint}`
|
|
179
|
+
: `Unknown ${type} model "${provider}/${id}". ${listHint}`);
|
|
180
|
+
}
|
|
181
|
+
const ModelCallParameters = Type.Object({ model: Type.Unknown(), context: Type.Unknown() });
|
|
182
|
+
/** The result of a model call as the tool returns it: the whole result for the script, usage counted by the Harness. */
|
|
183
|
+
function modelResult(label, result, value) {
|
|
184
|
+
const ok = result.stopReason === "stop";
|
|
185
|
+
return {
|
|
186
|
+
output: [
|
|
187
|
+
{
|
|
188
|
+
type: "text",
|
|
189
|
+
text: ok ? `${label} done` : `${label} ${result.stopReason}: ${result.errorMessage ?? ""}`,
|
|
190
|
+
},
|
|
191
|
+
],
|
|
192
|
+
structuredOutput: JSON.parse(JSON.stringify(value)),
|
|
193
|
+
...(ok ? {} : { isError: true }),
|
|
194
|
+
...(result.usage === undefined ? {} : { usage: result.usage }),
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
/** The internal tools behind `models.classify()` and `models.generateImages()`. */
|
|
198
|
+
export function createModelTools() {
|
|
199
|
+
const classify = defineTool({
|
|
200
|
+
name: CLASSIFY_TOOL,
|
|
201
|
+
description: "models.classify() of code mode scripts.",
|
|
202
|
+
parameters: ModelCallParameters,
|
|
203
|
+
structuredOutputSchema: Type.Unknown(),
|
|
204
|
+
callers: ["tools"],
|
|
205
|
+
execute: async (args, api, context) => {
|
|
206
|
+
const model = resolveModel("models.classify", "classifier", args.model, api.models);
|
|
207
|
+
const checked = checkClassifierContext(args.context);
|
|
208
|
+
const result = await api.models.classify(model, checked, {
|
|
209
|
+
...(context.abortSignal === undefined ? {} : { signal: context.abortSignal }),
|
|
210
|
+
});
|
|
211
|
+
return modelResult(`classify ${model.provider}/${model.id}`, result, result);
|
|
212
|
+
},
|
|
213
|
+
});
|
|
214
|
+
const generateImages = defineTool({
|
|
215
|
+
name: GENERATE_IMAGES_TOOL,
|
|
216
|
+
description: "models.generateImages() of code mode scripts.",
|
|
217
|
+
parameters: ModelCallParameters,
|
|
218
|
+
structuredOutputSchema: Type.Unknown(),
|
|
219
|
+
callers: ["tools"],
|
|
220
|
+
execute: async (args, api, context) => {
|
|
221
|
+
const model = resolveModel("models.generateImages", "image", args.model, api.models);
|
|
222
|
+
const checked = checkImagesContext(args.context);
|
|
223
|
+
const result = await api.models.generateImages(model, checked, {
|
|
224
|
+
...(context.abortSignal === undefined ? {} : { signal: context.abortSignal }),
|
|
225
|
+
});
|
|
226
|
+
return modelResult(`generateImages ${model.provider}/${model.id}`, result, result);
|
|
227
|
+
},
|
|
228
|
+
});
|
|
229
|
+
return [classify, generateImages];
|
|
230
|
+
}
|
|
231
|
+
//# sourceMappingURL=models.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"models.js","sourceRoot":"","sources":["../../src/codemode/models.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `models.*` for code mode scripts, copied from the coding agent's code mode: the model catalog, and classifier and\n * image model calls.\n *\n * The catalog functions are host globals, journaled like any global. `models.classify()` and `models.generateImages()`\n * are paid effects, so each runs as an internal tool (`callers: [\"tools\"]`, a reserved name, hidden from scripts'\n * tool listings): a nested durable call with its own task, live row, abort, usage accounting, and replay policy. The\n * code mode tool turns a script's call of the global into a call of that tool.\n */\nimport type { Context, JsonValue } from \"@earendil-works/chord\";\nimport type {\n\tAnyModel,\n\tClassifierContext,\n\tImagesContext,\n\tModels,\n\tModelType,\n\tModelTypeMap,\n\tUsage,\n} from \"@earendil-works/pi-ai\";\nimport { Type } from \"typebox\";\nimport { defineTool } from \"../harness/define.ts\";\nimport type { ToolRegistration } from \"../harness/types.ts\";\n\n/** Reserved names of code mode's internal tools; they are never listed to scripts or the model. */\nexport const CODEMODE_INTERNAL_PREFIX = \"pi.codemode.\";\nexport const CLASSIFY_TOOL = `${CODEMODE_INTERNAL_PREFIX}models.classify`;\nexport const GENERATE_IMAGES_TOOL = `${CODEMODE_INTERNAL_PREFIX}models.generateImages`;\n\n/** The global a script calls, by internal tool. */\nexport const MODEL_CALLS: Readonly<Record<string, string>> = {\n\t\"models.classify\": CLASSIFY_TOOL,\n\t\"models.generateImages\": GENERATE_IMAGES_TOOL,\n};\n\n/** The catalog functions, host globals of the `models` namespace. */\nexport const CATALOG_GLOBALS = [\n\t\"models.getModelsOfType\",\n\t\"models.getAvailableOfType\",\n\t\"models.getModelOfType\",\n] as const;\n\n/** `models.classify()` and `models.generateImages()` calls one script may have in flight; the rest wait. */\nexport const MAX_CONCURRENT_MODEL_CALLS = 4;\n\nconst MODEL_TYPES: ReadonlySet<string> = new Set<ModelType>([\"chat\", \"image\", \"classifier\"]);\n\nexport const MODELS_DESCRIPTION = [\n\t\"`models` (all functions return promises):\",\n\t'- `models.getModelsOfType(type, provider?)`, `models.getAvailableOfType(type, provider?)` (configured auth only), `models.getModelOfType(type, provider, id)`: the catalog; `type` is \"chat\", \"image\", or \"classifier\".',\n\t'- `models.classify(model, { state: { ... }, images?: [{ type: \"image\", data, mimeType }], questions: { <id>: { type: \"choice\", instructions, criteria: { <label>: <meaning> } } | { type: \"score\", instructions, criteria: [<lowest level>, ..., <highest level>] } | { type: \"bool\", instructions, criteria: { true: <meaning>, false: <meaning> } } } })` resolves to `{ answers: { <id>: ... }, stopReason, errorMessage?, usage? }`.',\n\t'- `models.generateImages(model, { input: [{ type: \"text\", text: <prompt> }, ...optional { type: \"image\", data, mimeType }] })` resolves to `{ output: [image blocks], stopReason, errorMessage?, usage? }`; show each image with `image(block)`.',\n\t`At most ${MAX_CONCURRENT_MODEL_CALLS} classify/generateImages calls run at once; more wait.`,\n].join(\"\\n\");\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/** A script value in an error message: `undefined`, `a string`, `an array`, or its keys (`{ prompt }`). */\nfunction describeValue(value: unknown): string {\n\tif (value === undefined || value === null) return String(value);\n\tif (Array.isArray(value)) return value.length === 0 ? \"an empty array\" : \"an array\";\n\tif (typeof value === \"object\") {\n\t\tconst keys = Object.keys(value);\n\t\tif (keys.length === 0) return \"{}\";\n\t\treturn `{ ${keys.slice(0, 6).join(\", \")}${keys.length > 6 ? \", ...\" : \"\"} }`;\n\t}\n\treturn typeof value === \"string\" ? \"a string\" : `a ${typeof value}`;\n}\n\n/** `an image`, `a classifier`. */\nfunction withArticle(word: string): string {\n\treturn `${/^[aeiou]/.test(word) ? \"an\" : \"a\"} ${word}`;\n}\n\nfunction toModelType(value: unknown): ModelType {\n\tif (typeof value === \"string\" && MODEL_TYPES.has(value)) return value as ModelType;\n\tthrow new Error(`Unknown model type ${JSON.stringify(value)}. Use \"chat\", \"image\", or \"classifier\".`);\n}\n\nfunction toProvider(value: unknown): string | undefined {\n\tif (value === undefined || value === null) return undefined;\n\tif (typeof value !== \"string\") throw new Error(\"provider must be a string\");\n\treturn value;\n}\n\n/** Catalog entry for scripts. `headers` is dropped because configured headers can carry credentials. */\nfunction toModelInfo(model: AnyModel): JsonValue {\n\tconst info: Record<string, unknown> = { ...model };\n\tdelete info.headers;\n\treturn JSON.parse(JSON.stringify(info)) as JsonValue;\n}\n\n/** The catalog functions: what a script's call of `name` with `args` (the argument list) resolves to. */\nexport async function catalogCall(\n\tname: string,\n\targs: unknown,\n\tmodels: Models,\n\tcontext: Context,\n): Promise<JsonValue | undefined> {\n\tconst list = Array.isArray(args) ? args : [];\n\tconst [type, provider, id] = list;\n\tif (name === \"models.getModelsOfType\")\n\t\treturn models.getModelsOfType(toModelType(type), toProvider(provider)).map(toModelInfo);\n\tif (name === \"models.getAvailableOfType\") {\n\t\tconst available = await models.getAvailableOfType(toModelType(type), toProvider(provider), {\n\t\t\t...(context.abortSignal === undefined ? {} : { signal: context.abortSignal }),\n\t\t});\n\t\treturn available.map(toModelInfo);\n\t}\n\tif (typeof provider !== \"string\" || typeof id !== \"string\") {\n\t\tthrow new Error(\n\t\t\t`models.getModelOfType(type, provider, id) expects three strings, got (${list.map(describeValue).join(\", \")}). The provider and the id are separate arguments, for example models.getModelOfType(\"classifier\", \"typesafe\", \"jev-latest\").`,\n\t\t);\n\t}\n\tconst model = models.getModelOfType(toModelType(type), provider, id);\n\treturn model === undefined ? undefined : toModelInfo(model);\n}\n\nconst CLASSIFIER_CONTEXT_SHAPE =\n\t'{ state: { ... }, images?: [{ type: \"image\", data: <base64>, mimeType }], questions: { <id>: { type: \"choice\", instructions, criteria: { <label>: <meaning> } } | { type: \"score\", instructions, criteria: [<lowest level>, ..., <highest level>] } | { type: \"bool\", instructions, criteria: { true: <meaning>, false: <meaning> } } } }';\n\n/** Check a script's classifier context, so mistakes fail with the expected shape instead of a provider error. */\nfunction checkClassifierContext(context: unknown): ClassifierContext {\n\tconst fail = (problem: string) =>\n\t\tnew Error(`models.classify() ${problem}. Expected context: ${CLASSIFIER_CONTEXT_SHAPE}.`);\n\tif (!isRecord(context)) throw fail(`expects a context object as its second argument, got ${describeValue(context)}`);\n\tif (!isRecord(context.state)) throw fail(`context.state must be an object, got ${describeValue(context.state)}`);\n\tconst { images } = context;\n\tif (images !== undefined) {\n\t\tif (!Array.isArray(images)) throw fail(`context.images must be an array, got ${describeValue(images)}`);\n\t\timages.forEach((image: unknown, index) => {\n\t\t\tif (\n\t\t\t\t!isRecord(image) ||\n\t\t\t\timage.type !== \"image\" ||\n\t\t\t\ttypeof image.data !== \"string\" ||\n\t\t\t\ttypeof image.mimeType !== \"string\"\n\t\t\t) {\n\t\t\t\tthrow fail(`context.images[${index}] must be an image block, got ${describeValue(image)}`);\n\t\t\t}\n\t\t});\n\t}\n\tconst { questions } = context;\n\tif (!isRecord(questions) || Object.keys(questions).length === 0) {\n\t\tthrow fail(`context.questions must map question IDs to questions, got ${describeValue(questions)}`);\n\t}\n\tconst isStrings = (values: unknown[]) => values.length > 0 && values.every((value) => typeof value === \"string\");\n\tfor (const [id, question] of Object.entries(questions)) {\n\t\tconst at = `context.questions.${id}`;\n\t\tif (!isRecord(question)) throw fail(`${at} must be a question object, got ${describeValue(question)}`);\n\t\tif (typeof question.instructions !== \"string\") throw fail(`${at}.instructions must be a string`);\n\t\tconst { criteria } = question;\n\t\tif (question.type === \"choice\") {\n\t\t\tif (!isRecord(criteria) || !isStrings(Object.values(criteria))) {\n\t\t\t\tthrow fail(`${at} is a \"choice\" question, so criteria must map each label to its meaning`);\n\t\t\t}\n\t\t} else if (question.type === \"score\") {\n\t\t\tif (!Array.isArray(criteria) || !isStrings(criteria)) {\n\t\t\t\tthrow fail(`${at} is a \"score\" question, so criteria must list the levels as strings, lowest first`);\n\t\t\t}\n\t\t} else if (question.type === \"bool\") {\n\t\t\tif (!isRecord(criteria) || typeof criteria.true !== \"string\" || typeof criteria.false !== \"string\") {\n\t\t\t\tthrow fail(`${at} is a \"bool\" question, so criteria must be { true: string, false: string }`);\n\t\t\t}\n\t\t} else {\n\t\t\tthrow fail(`${at}.type must be \"choice\", \"score\", or \"bool\", got ${JSON.stringify(question.type)}`);\n\t\t}\n\t}\n\treturn context as unknown as ClassifierContext;\n}\n\n/** Check a script's image context, so mistakes such as `{ prompt }` fail with the expected shape. */\nfunction checkImagesContext(context: unknown): ImagesContext {\n\tconst fail = (problem: string) =>\n\t\tnew Error(\n\t\t\t`models.generateImages() ${problem}. Expected context: { input: [{ type: \"text\", text: <prompt> }, ...optional { type: \"image\", data: <base64>, mimeType } references] }.`,\n\t\t);\n\tif (!isRecord(context)) throw fail(`expects a context object as its second argument, got ${describeValue(context)}`);\n\tconst { input } = context;\n\tif (!Array.isArray(input) || input.length === 0) {\n\t\tthrow fail(`context.input must be a non-empty array of blocks, got ${describeValue(input)}`);\n\t}\n\tinput.forEach((block: unknown, index) => {\n\t\tif (isRecord(block) && block.type === \"text\" && typeof block.text === \"string\") return;\n\t\tif (\n\t\t\tisRecord(block) &&\n\t\t\tblock.type === \"image\" &&\n\t\t\ttypeof block.data === \"string\" &&\n\t\t\ttypeof block.mimeType === \"string\"\n\t\t)\n\t\t\treturn;\n\t\tthrow fail(`context.input[${index}] must be a text or image block, got ${describeValue(block)}`);\n\t});\n\treturn context as unknown as ImagesContext;\n}\n\n/**\n * Resolve the script's model by provider and id only, so a script-supplied baseUrl or headers never receive the\n * credentials, with errors that say what to do.\n */\nfunction resolveModel<TType extends \"classifier\" | \"image\">(\n\tname: string,\n\ttype: TType,\n\tmodel: unknown,\n\tmodels: Models,\n): ModelTypeMap[TType] {\n\tconst listHint = `List the ${type} models you can use with models.getAvailableOfType(\"${type}\").`;\n\tif (!isRecord(model) || typeof model.provider !== \"string\" || typeof model.id !== \"string\") {\n\t\tconst undefinedHint =\n\t\t\tmodel === undefined || model === null\n\t\t\t\t? \" models.getModelOfType() returns undefined for an unknown provider or id.\"\n\t\t\t\t: \"\";\n\t\tthrow new Error(\n\t\t\t`${name}() expects ${withArticle(type)} model as its first argument, got ${describeValue(model)}.${undefinedHint} ${listHint}`,\n\t\t);\n\t}\n\tconst { provider, id } = model;\n\tconst resolved = models.getModelOfType(type, provider, id);\n\tif (resolved !== undefined) return resolved;\n\tconst actualType = [...MODEL_TYPES].find(\n\t\t(other) => other !== type && models.getModelOfType(other as ModelType, provider, id) !== undefined,\n\t);\n\tthrow new Error(\n\t\tactualType\n\t\t\t? `\"${provider}/${id}\" is ${withArticle(actualType)} model, not ${withArticle(type)} model. ${listHint}`\n\t\t\t: `Unknown ${type} model \"${provider}/${id}\". ${listHint}`,\n\t);\n}\n\nconst ModelCallParameters = Type.Object({ model: Type.Unknown(), context: Type.Unknown() });\n\n/** The result of a model call as the tool returns it: the whole result for the script, usage counted by the Harness. */\nfunction modelResult(\n\tlabel: string,\n\tresult: { stopReason: string; errorMessage?: string; usage?: Usage },\n\tvalue: unknown,\n) {\n\tconst ok = result.stopReason === \"stop\";\n\treturn {\n\t\toutput: [\n\t\t\t{\n\t\t\t\ttype: \"text\" as const,\n\t\t\t\ttext: ok ? `${label} done` : `${label} ${result.stopReason}: ${result.errorMessage ?? \"\"}`,\n\t\t\t},\n\t\t],\n\t\tstructuredOutput: JSON.parse(JSON.stringify(value)) as JsonValue,\n\t\t...(ok ? {} : { isError: true }),\n\t\t...(result.usage === undefined ? {} : { usage: result.usage }),\n\t};\n}\n\n/** The internal tools behind `models.classify()` and `models.generateImages()`. */\nexport function createModelTools(): ToolRegistration[] {\n\tconst classify = defineTool({\n\t\tname: CLASSIFY_TOOL,\n\t\tdescription: \"models.classify() of code mode scripts.\",\n\t\tparameters: ModelCallParameters,\n\t\tstructuredOutputSchema: Type.Unknown(),\n\t\tcallers: [\"tools\"],\n\t\texecute: async (args, api, context) => {\n\t\t\tconst model = resolveModel(\"models.classify\", \"classifier\", args.model, api.models);\n\t\t\tconst checked = checkClassifierContext(args.context);\n\t\t\tconst result = await api.models.classify(model, checked, {\n\t\t\t\t...(context.abortSignal === undefined ? {} : { signal: context.abortSignal }),\n\t\t\t});\n\t\t\treturn modelResult(`classify ${model.provider}/${model.id}`, result, result);\n\t\t},\n\t});\n\tconst generateImages = defineTool({\n\t\tname: GENERATE_IMAGES_TOOL,\n\t\tdescription: \"models.generateImages() of code mode scripts.\",\n\t\tparameters: ModelCallParameters,\n\t\tstructuredOutputSchema: Type.Unknown(),\n\t\tcallers: [\"tools\"],\n\t\texecute: async (args, api, context) => {\n\t\t\tconst model = resolveModel(\"models.generateImages\", \"image\", args.model, api.models);\n\t\t\tconst checked = checkImagesContext(args.context);\n\t\t\tconst result = await api.models.generateImages(model, checked, {\n\t\t\t\t...(context.abortSignal === undefined ? {} : { signal: context.abortSignal }),\n\t\t\t});\n\t\t\treturn modelResult(`generateImages ${model.provider}/${model.id}`, result, result);\n\t\t},\n\t});\n\treturn [classify as unknown as ToolRegistration, generateImages as unknown as ToolRegistration];\n}\n"]}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A script's result as the model reads it, copied from the coding agent's code mode: a "Script completed/failed"
|
|
3
|
+
* header with the wall time, the script's text items (`==> text N/M <==` when there are several), its `console` lines
|
|
4
|
+
* in one `<console_output>` block, the returned value, and on failure the error with the calls already made. Text past
|
|
5
|
+
* the token budget is cut to its start and end, with the complete text in a temp file; images are saved to temp files
|
|
6
|
+
* too, with their path next to them, because a script has no other way to hand their bytes on.
|
|
7
|
+
*/
|
|
8
|
+
import type { ImageContent, TextContent } from "@earendil-works/pi-ai";
|
|
9
|
+
import type { CodemodeResult } from "@earendil-works/pi-codemode/portable";
|
|
10
|
+
type Item = TextContent | ImageContent;
|
|
11
|
+
/** Writes a file of the result under a fixed name; its path, or why it could not be written. */
|
|
12
|
+
export type OutputFile = (name: string, data: string | Uint8Array) => Promise<{
|
|
13
|
+
path: string;
|
|
14
|
+
} | {
|
|
15
|
+
error: string;
|
|
16
|
+
}>;
|
|
17
|
+
/** The tool's output limits, which the Harness enforces by cutting without a spill file: results stay inside them. */
|
|
18
|
+
type Limits = {
|
|
19
|
+
readonly maxBytes: number;
|
|
20
|
+
readonly maxLines: number;
|
|
21
|
+
};
|
|
22
|
+
/** Default token budget for script output. */
|
|
23
|
+
export declare const DEFAULT_MAX_OUTPUT_TOKENS = 10000;
|
|
24
|
+
/** The tool result's content for a script's result, and the path of the complete text when it was cut. */
|
|
25
|
+
export declare function formatResult(result: CodemodeResult, options: {
|
|
26
|
+
/** Images `models.generateImages()` returned; when the script showed none, the result says so. */
|
|
27
|
+
readonly generatedImages: number;
|
|
28
|
+
readonly wallMs: number;
|
|
29
|
+
readonly maxOutputTokens: number;
|
|
30
|
+
readonly calls: readonly {
|
|
31
|
+
name: string;
|
|
32
|
+
status: string;
|
|
33
|
+
}[];
|
|
34
|
+
readonly limits: Limits;
|
|
35
|
+
readonly write: OutputFile;
|
|
36
|
+
}): Promise<{
|
|
37
|
+
content: Item[];
|
|
38
|
+
fullOutputPath?: string;
|
|
39
|
+
}>;
|
|
40
|
+
export {};
|
|
41
|
+
//# sourceMappingURL=output.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../../src/codemode/output.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,KAAK,EAAsB,cAAc,EAAE,MAAM,sCAAsC,CAAC;AAE/F,KAAK,IAAI,GAAG,WAAW,GAAG,YAAY,CAAC;AAEvC,gGAAgG;AAChG,MAAM,MAAM,UAAU,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU,KAAK,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAEpH,sHAAsH;AACtH,KAAK,MAAM,GAAG;IAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAIvE,8CAA8C;AAC9C,eAAO,MAAM,yBAAyB,QAAS,CAAC;AAwJhD,0GAA0G;AAC1G,wBAAsB,YAAY,CACjC,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE;IACR,kGAAkG;IAClG,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,KAAK,EAAE,SAAS;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC5D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;CAC3B,GACC,OAAO,CAAC;IAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAoBvD"}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/** Lines of the result besides the script's text: the header, markers, notes. */
|
|
2
|
+
const LINE_MARGIN = 100;
|
|
3
|
+
/** Default token budget for script output. */
|
|
4
|
+
export const DEFAULT_MAX_OUTPUT_TOKENS = 10_000;
|
|
5
|
+
/** Characters per token when estimating. */
|
|
6
|
+
const CHARS_PER_TOKEN = 4;
|
|
7
|
+
/** File extensions of the image types `image()` accepts. */
|
|
8
|
+
const IMAGE_EXTENSIONS = {
|
|
9
|
+
"image/png": ".png",
|
|
10
|
+
"image/jpeg": ".jpg",
|
|
11
|
+
"image/gif": ".gif",
|
|
12
|
+
"image/webp": ".webp",
|
|
13
|
+
};
|
|
14
|
+
/** Like the script's `text()`: strings as is, other values as compact JSON. */
|
|
15
|
+
function valueText(value) {
|
|
16
|
+
if (typeof value === "string")
|
|
17
|
+
return value;
|
|
18
|
+
return JSON.stringify(value) ?? String(value);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Lay out the script's output so the model can tell items apart: with more than one text item, each starts with a
|
|
22
|
+
* `==> text N/M <==` line; `console` lines follow all other output in one `<console_output>` block.
|
|
23
|
+
*/
|
|
24
|
+
function formatItems(output) {
|
|
25
|
+
const total = output.filter((item) => item.type === "text" && !item.console).length;
|
|
26
|
+
const items = [];
|
|
27
|
+
const consoleLines = [];
|
|
28
|
+
let index = 0;
|
|
29
|
+
for (const item of output) {
|
|
30
|
+
if (item.type === "image")
|
|
31
|
+
items.push(item);
|
|
32
|
+
else if (item.console)
|
|
33
|
+
consoleLines.push(item.text);
|
|
34
|
+
else {
|
|
35
|
+
index++;
|
|
36
|
+
items.push({ type: "text", text: total > 1 ? `==> text ${index}/${total} <==\n${item.text}` : item.text });
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (consoleLines.length > 0) {
|
|
40
|
+
items.push({ type: "text", text: `<console_output>\n${consoleLines.join("\n")}\n</console_output>` });
|
|
41
|
+
}
|
|
42
|
+
return items;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Join adjacent text items into one, each part starting on its own line. The separator check looks at the last
|
|
46
|
+
* non-empty part, not the joined text: reading the end of a growing concatenation makes V8 copy it on every item, which
|
|
47
|
+
* is quadratic.
|
|
48
|
+
*/
|
|
49
|
+
function joinAdjacentText(items) {
|
|
50
|
+
const joined = [];
|
|
51
|
+
let parts = [];
|
|
52
|
+
let lastPart = "";
|
|
53
|
+
const flush = () => {
|
|
54
|
+
if (parts.length > 0)
|
|
55
|
+
joined.push({ type: "text", text: parts.join("") });
|
|
56
|
+
parts = [];
|
|
57
|
+
lastPart = "";
|
|
58
|
+
};
|
|
59
|
+
for (const item of items) {
|
|
60
|
+
if (item.type !== "text") {
|
|
61
|
+
flush();
|
|
62
|
+
joined.push(item);
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (lastPart !== "" && !lastPart.endsWith("\n")) {
|
|
66
|
+
parts.push("\n");
|
|
67
|
+
lastPart = "\n";
|
|
68
|
+
}
|
|
69
|
+
parts.push(item.text);
|
|
70
|
+
if (item.text !== "")
|
|
71
|
+
lastPart = item.text;
|
|
72
|
+
}
|
|
73
|
+
flush();
|
|
74
|
+
return joined;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Apply the token budget: when the combined text exceeds it, the text items become one item that keeps the start and
|
|
78
|
+
* end of the text, and images follow it. The complete text is written to a temp file.
|
|
79
|
+
*/
|
|
80
|
+
async function truncate(items, maxTokens, limits, write) {
|
|
81
|
+
const texts = items.filter((item) => item.type === "text").map((item) => item.text);
|
|
82
|
+
const combined = texts.join("\n");
|
|
83
|
+
// Within the limits too: a UTF-16 unit is at most 3 UTF-8 bytes, and the rest of the result is a few short lines.
|
|
84
|
+
const budget = Math.min(maxTokens * CHARS_PER_TOKEN, Math.floor(limits.maxBytes / 4));
|
|
85
|
+
const lineBudget = Math.floor((limits.maxLines - LINE_MARGIN) / 2);
|
|
86
|
+
const lines = combined.split("\n").length;
|
|
87
|
+
if (texts.length === 0 || (combined.length <= budget && lines <= 2 * lineBudget))
|
|
88
|
+
return { items: [...items] };
|
|
89
|
+
const headChars = Math.floor(budget / 2);
|
|
90
|
+
const tailChars = budget - headChars;
|
|
91
|
+
const head = combined.slice(0, headChars).split("\n").slice(0, lineBudget).join("\n");
|
|
92
|
+
const tail = tailChars > 0 ? combined.slice(-tailChars).split("\n").slice(-lineBudget).join("\n") : "";
|
|
93
|
+
const removed = combined.length - head.length - tail.length;
|
|
94
|
+
let text = `Warning: truncated output (original token count: ${Math.ceil(combined.length / CHARS_PER_TOKEN)})\nTotal output lines: ${combined.split("\n").length}\n\n${head}…${Math.ceil(removed / CHARS_PER_TOKEN)} tokens truncated…${tail}`;
|
|
95
|
+
const spilled = await write("output.txt", combined);
|
|
96
|
+
text +=
|
|
97
|
+
"path" in spilled
|
|
98
|
+
? `\n\n[Full output: ${spilled.path} (read with offset/limit)]`
|
|
99
|
+
: `\n\n[Could not save the full output: ${spilled.error}]`;
|
|
100
|
+
return {
|
|
101
|
+
items: [{ type: "text", text }, ...items.filter((item) => item.type === "image")],
|
|
102
|
+
...("path" in spilled ? { fullOutputPath: spilled.path } : {}),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
/** Save each image to a file and put a text item with its path before it; an image shown twice is saved once. */
|
|
106
|
+
async function saveImages(items, write) {
|
|
107
|
+
const labels = new Map();
|
|
108
|
+
const label = async ({ data, mimeType }, index) => {
|
|
109
|
+
const bytes = Uint8Array.from(atob(data), (char) => char.charCodeAt(0));
|
|
110
|
+
const kind = `${mimeType}, ${bytes.length} bytes`;
|
|
111
|
+
// A failed write must not discard the result of a script whose calls already ran: it becomes part of the label.
|
|
112
|
+
const saved = await write(`image-${index}${IMAGE_EXTENSIONS[mimeType] ?? ".bin"}`, bytes);
|
|
113
|
+
return "path" in saved
|
|
114
|
+
? `[Image saved to ${saved.path} (${kind})]`
|
|
115
|
+
: `[Image (${kind}) could not be saved: ${saved.error}]`;
|
|
116
|
+
};
|
|
117
|
+
const result = await Promise.all(items.map(async (item) => {
|
|
118
|
+
if (item.type !== "image")
|
|
119
|
+
return [item];
|
|
120
|
+
let pending = labels.get(item.data);
|
|
121
|
+
if (pending === undefined) {
|
|
122
|
+
pending = label(item, labels.size + 1);
|
|
123
|
+
labels.set(item.data, pending);
|
|
124
|
+
}
|
|
125
|
+
return [{ type: "text", text: await pending }, item];
|
|
126
|
+
}));
|
|
127
|
+
return result.flat();
|
|
128
|
+
}
|
|
129
|
+
function formatError(result, calls) {
|
|
130
|
+
const { error } = result;
|
|
131
|
+
const head = error.kind === "script"
|
|
132
|
+
? (error.stack ?? `${error.name ?? "Error"}: ${error.message}`)
|
|
133
|
+
: error.kind === "timeout"
|
|
134
|
+
? `Script timed out: ${error.message}`
|
|
135
|
+
: error.kind === "aborted"
|
|
136
|
+
? `Script aborted: ${error.message}`
|
|
137
|
+
: `Script sandbox failed: ${error.message}`;
|
|
138
|
+
const summary = calls.length === 0
|
|
139
|
+
? "No tool calls were made."
|
|
140
|
+
: `Tool calls made before the failure (they are not undone): ${calls.map((call) => `${call.name} (${call.status})`).join(", ")}`;
|
|
141
|
+
return `${head}\n\n${summary}`;
|
|
142
|
+
}
|
|
143
|
+
/** The tool result's content for a script's result, and the path of the complete text when it was cut. */
|
|
144
|
+
export async function formatResult(result, options) {
|
|
145
|
+
const output = [...result.output];
|
|
146
|
+
if (result.ok && result.value !== undefined)
|
|
147
|
+
output.push({ type: "text", text: valueText(result.value) });
|
|
148
|
+
const items = formatItems(output);
|
|
149
|
+
if (!result.ok)
|
|
150
|
+
items.push({ type: "text", text: `Script error:\n${formatError(result, options.calls)}` });
|
|
151
|
+
const generated = options.generatedImages;
|
|
152
|
+
if (generated > 0 && !items.some((item) => item.type === "image")) {
|
|
153
|
+
items.push({
|
|
154
|
+
type: "text",
|
|
155
|
+
text: `Note: models.generateImages() returned ${generated} image${generated === 1 ? "" : "s"} that the script did not show. Show each image block of result.output with image(block).`,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
const truncated = await truncate(joinAdjacentText(items), options.maxOutputTokens, options.limits, options.write);
|
|
159
|
+
// After truncation, which joins the text items and moves images after them, so each path stays next to its image.
|
|
160
|
+
const content = joinAdjacentText(await saveImages(truncated.items, options.write));
|
|
161
|
+
const header = `${result.ok ? "Script completed" : "Script failed"}\nWall time ${(options.wallMs / 1000).toFixed(1)} seconds\nOutput:\n`;
|
|
162
|
+
return {
|
|
163
|
+
content: [{ type: "text", text: header }, ...content],
|
|
164
|
+
...(truncated.fullOutputPath === undefined ? {} : { fullOutputPath: truncated.fullOutputPath }),
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.js","sourceRoot":"","sources":["../../src/codemode/output.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * A script's result as the model reads it, copied from the coding agent's code mode: a \"Script completed/failed\"\n * header with the wall time, the script's text items (`==> text N/M <==` when there are several), its `console` lines\n * in one `<console_output>` block, the returned value, and on failure the error with the calls already made. Text past\n * the token budget is cut to its start and end, with the complete text in a temp file; images are saved to temp files\n * too, with their path next to them, because a script has no other way to hand their bytes on.\n */\nimport type { ImageContent, TextContent } from \"@earendil-works/pi-ai\";\nimport type { CodemodeOutputItem, CodemodeResult } from \"@earendil-works/pi-codemode/portable\";\n\ntype Item = TextContent | ImageContent;\n\n/** Writes a file of the result under a fixed name; its path, or why it could not be written. */\nexport type OutputFile = (name: string, data: string | Uint8Array) => Promise<{ path: string } | { error: string }>;\n\n/** The tool's output limits, which the Harness enforces by cutting without a spill file: results stay inside them. */\ntype Limits = { readonly maxBytes: number; readonly maxLines: number };\n/** Lines of the result besides the script's text: the header, markers, notes. */\nconst LINE_MARGIN = 100;\n\n/** Default token budget for script output. */\nexport const DEFAULT_MAX_OUTPUT_TOKENS = 10_000;\n/** Characters per token when estimating. */\nconst CHARS_PER_TOKEN = 4;\n\n/** File extensions of the image types `image()` accepts. */\nconst IMAGE_EXTENSIONS: Record<string, string> = {\n\t\"image/png\": \".png\",\n\t\"image/jpeg\": \".jpg\",\n\t\"image/gif\": \".gif\",\n\t\"image/webp\": \".webp\",\n};\n\n/** Like the script's `text()`: strings as is, other values as compact JSON. */\nfunction valueText(value: unknown): string {\n\tif (typeof value === \"string\") return value;\n\treturn JSON.stringify(value) ?? String(value);\n}\n\n/**\n * Lay out the script's output so the model can tell items apart: with more than one text item, each starts with a\n * `==> text N/M <==` line; `console` lines follow all other output in one `<console_output>` block.\n */\nfunction formatItems(output: readonly CodemodeOutputItem[]): Item[] {\n\tconst total = output.filter((item) => item.type === \"text\" && !item.console).length;\n\tconst items: Item[] = [];\n\tconst consoleLines: string[] = [];\n\tlet index = 0;\n\tfor (const item of output) {\n\t\tif (item.type === \"image\") items.push(item);\n\t\telse if (item.console) consoleLines.push(item.text);\n\t\telse {\n\t\t\tindex++;\n\t\t\titems.push({ type: \"text\", text: total > 1 ? `==> text ${index}/${total} <==\\n${item.text}` : item.text });\n\t\t}\n\t}\n\tif (consoleLines.length > 0) {\n\t\titems.push({ type: \"text\", text: `<console_output>\\n${consoleLines.join(\"\\n\")}\\n</console_output>` });\n\t}\n\treturn items;\n}\n\n/**\n * Join adjacent text items into one, each part starting on its own line. The separator check looks at the last\n * non-empty part, not the joined text: reading the end of a growing concatenation makes V8 copy it on every item, which\n * is quadratic.\n */\nfunction joinAdjacentText(items: readonly Item[]): Item[] {\n\tconst joined: Item[] = [];\n\tlet parts: string[] = [];\n\tlet lastPart = \"\";\n\tconst flush = () => {\n\t\tif (parts.length > 0) joined.push({ type: \"text\", text: parts.join(\"\") });\n\t\tparts = [];\n\t\tlastPart = \"\";\n\t};\n\tfor (const item of items) {\n\t\tif (item.type !== \"text\") {\n\t\t\tflush();\n\t\t\tjoined.push(item);\n\t\t\tcontinue;\n\t\t}\n\t\tif (lastPart !== \"\" && !lastPart.endsWith(\"\\n\")) {\n\t\t\tparts.push(\"\\n\");\n\t\t\tlastPart = \"\\n\";\n\t\t}\n\t\tparts.push(item.text);\n\t\tif (item.text !== \"\") lastPart = item.text;\n\t}\n\tflush();\n\treturn joined;\n}\n\n/**\n * Apply the token budget: when the combined text exceeds it, the text items become one item that keeps the start and\n * end of the text, and images follow it. The complete text is written to a temp file.\n */\nasync function truncate(\n\titems: readonly Item[],\n\tmaxTokens: number,\n\tlimits: Limits,\n\twrite: OutputFile,\n): Promise<{ items: Item[]; fullOutputPath?: string }> {\n\tconst texts = items.filter((item): item is TextContent => item.type === \"text\").map((item) => item.text);\n\tconst combined = texts.join(\"\\n\");\n\t// Within the limits too: a UTF-16 unit is at most 3 UTF-8 bytes, and the rest of the result is a few short lines.\n\tconst budget = Math.min(maxTokens * CHARS_PER_TOKEN, Math.floor(limits.maxBytes / 4));\n\tconst lineBudget = Math.floor((limits.maxLines - LINE_MARGIN) / 2);\n\tconst lines = combined.split(\"\\n\").length;\n\tif (texts.length === 0 || (combined.length <= budget && lines <= 2 * lineBudget)) return { items: [...items] };\n\tconst headChars = Math.floor(budget / 2);\n\tconst tailChars = budget - headChars;\n\tconst head = combined.slice(0, headChars).split(\"\\n\").slice(0, lineBudget).join(\"\\n\");\n\tconst tail = tailChars > 0 ? combined.slice(-tailChars).split(\"\\n\").slice(-lineBudget).join(\"\\n\") : \"\";\n\tconst removed = combined.length - head.length - tail.length;\n\tlet text = `Warning: truncated output (original token count: ${Math.ceil(combined.length / CHARS_PER_TOKEN)})\\nTotal output lines: ${combined.split(\"\\n\").length}\\n\\n${head}…${Math.ceil(removed / CHARS_PER_TOKEN)} tokens truncated…${tail}`;\n\tconst spilled = await write(\"output.txt\", combined);\n\ttext +=\n\t\t\"path\" in spilled\n\t\t\t? `\\n\\n[Full output: ${spilled.path} (read with offset/limit)]`\n\t\t\t: `\\n\\n[Could not save the full output: ${spilled.error}]`;\n\treturn {\n\t\titems: [{ type: \"text\", text }, ...items.filter((item) => item.type === \"image\")],\n\t\t...(\"path\" in spilled ? { fullOutputPath: spilled.path } : {}),\n\t};\n}\n\n/** Save each image to a file and put a text item with its path before it; an image shown twice is saved once. */\nasync function saveImages(items: readonly Item[], write: OutputFile): Promise<Item[]> {\n\tconst labels = new Map<string, Promise<string>>();\n\tconst label = async ({ data, mimeType }: ImageContent, index: number): Promise<string> => {\n\t\tconst bytes = Uint8Array.from(atob(data), (char) => char.charCodeAt(0));\n\t\tconst kind = `${mimeType}, ${bytes.length} bytes`;\n\t\t// A failed write must not discard the result of a script whose calls already ran: it becomes part of the label.\n\t\tconst saved = await write(`image-${index}${IMAGE_EXTENSIONS[mimeType] ?? \".bin\"}`, bytes);\n\t\treturn \"path\" in saved\n\t\t\t? `[Image saved to ${saved.path} (${kind})]`\n\t\t\t: `[Image (${kind}) could not be saved: ${saved.error}]`;\n\t};\n\tconst result = await Promise.all(\n\t\titems.map(async (item): Promise<Item[]> => {\n\t\t\tif (item.type !== \"image\") return [item];\n\t\t\tlet pending = labels.get(item.data);\n\t\t\tif (pending === undefined) {\n\t\t\t\tpending = label(item, labels.size + 1);\n\t\t\t\tlabels.set(item.data, pending);\n\t\t\t}\n\t\t\treturn [{ type: \"text\", text: await pending }, item];\n\t\t}),\n\t);\n\treturn result.flat();\n}\n\nfunction formatError(\n\tresult: Extract<CodemodeResult, { ok: false }>,\n\tcalls: readonly { name: string; status: string }[],\n) {\n\tconst { error } = result;\n\tconst head =\n\t\terror.kind === \"script\"\n\t\t\t? (error.stack ?? `${error.name ?? \"Error\"}: ${error.message}`)\n\t\t\t: error.kind === \"timeout\"\n\t\t\t\t? `Script timed out: ${error.message}`\n\t\t\t\t: error.kind === \"aborted\"\n\t\t\t\t\t? `Script aborted: ${error.message}`\n\t\t\t\t\t: `Script sandbox failed: ${error.message}`;\n\tconst summary =\n\t\tcalls.length === 0\n\t\t\t? \"No tool calls were made.\"\n\t\t\t: `Tool calls made before the failure (they are not undone): ${calls.map((call) => `${call.name} (${call.status})`).join(\", \")}`;\n\treturn `${head}\\n\\n${summary}`;\n}\n\n/** The tool result's content for a script's result, and the path of the complete text when it was cut. */\nexport async function formatResult(\n\tresult: CodemodeResult,\n\toptions: {\n\t\t/** Images `models.generateImages()` returned; when the script showed none, the result says so. */\n\t\treadonly generatedImages: number;\n\t\treadonly wallMs: number;\n\t\treadonly maxOutputTokens: number;\n\t\treadonly calls: readonly { name: string; status: string }[];\n\t\treadonly limits: Limits;\n\t\treadonly write: OutputFile;\n\t},\n): Promise<{ content: Item[]; fullOutputPath?: string }> {\n\tconst output = [...result.output];\n\tif (result.ok && result.value !== undefined) output.push({ type: \"text\", text: valueText(result.value) });\n\tconst items = formatItems(output);\n\tif (!result.ok) items.push({ type: \"text\", text: `Script error:\\n${formatError(result, options.calls)}` });\n\tconst generated = options.generatedImages;\n\tif (generated > 0 && !items.some((item) => item.type === \"image\")) {\n\t\titems.push({\n\t\t\ttype: \"text\",\n\t\t\ttext: `Note: models.generateImages() returned ${generated} image${generated === 1 ? \"\" : \"s\"} that the script did not show. Show each image block of result.output with image(block).`,\n\t\t});\n\t}\n\tconst truncated = await truncate(joinAdjacentText(items), options.maxOutputTokens, options.limits, options.write);\n\t// After truncation, which joins the text items and moves images after them, so each path stays next to its image.\n\tconst content = joinAdjacentText(await saveImages(truncated.items, options.write));\n\tconst header = `${result.ok ? \"Script completed\" : \"Script failed\"}\\nWall time ${(options.wallMs / 1000).toFixed(1)} seconds\\nOutput:\\n`;\n\treturn {\n\t\tcontent: [{ type: \"text\", text: header }, ...content],\n\t\t...(truncated.fullOutputPath === undefined ? {} : { fullOutputPath: truncated.fullOutputPath }),\n\t};\n}\n"]}
|
package/dist/env/index.d.ts
CHANGED
|
@@ -125,6 +125,41 @@ export interface DirReader {
|
|
|
125
125
|
}, FileError>>;
|
|
126
126
|
close(context: Context): Promise<void>;
|
|
127
127
|
}
|
|
128
|
+
/** What `FileSystem.listFiles` lists. */
|
|
129
|
+
export interface ListFilesOptions {
|
|
130
|
+
/**
|
|
131
|
+
* Include names starting with `.` (on Windows also files with the hidden attribute); default true. Entries named
|
|
132
|
+
* `.git` are never listed.
|
|
133
|
+
*/
|
|
134
|
+
hidden?: boolean;
|
|
135
|
+
/**
|
|
136
|
+
* Inside a git work tree, skip what git ignores: the `.gitignore` files of the directory, its subdirectories and its
|
|
137
|
+
* parents, `.git/info/exclude`, and the global excludes file; default true. Unlike `git ls-files`, a tracked file that
|
|
138
|
+
* matches an ignore rule is skipped too. Outside work trees, `.gitignore` files have no effect.
|
|
139
|
+
*/
|
|
140
|
+
gitignore?: boolean;
|
|
141
|
+
/** Names of entries to skip with everything below them, such as `node_modules`. */
|
|
142
|
+
exclude?: readonly string[];
|
|
143
|
+
/** Most entries to list; default 50 000. */
|
|
144
|
+
maxEntries?: number;
|
|
145
|
+
/** Most bytes of the listing: each entry's UTF-8 path plus two; default 4 MiB, at most 15.9 MiB. */
|
|
146
|
+
maxBytes?: number;
|
|
147
|
+
/** Time after which the listing stops; default 10 000 ms. */
|
|
148
|
+
timeoutMs?: number;
|
|
149
|
+
}
|
|
150
|
+
/** One entry of a `FileListing`. */
|
|
151
|
+
export interface FileListEntry {
|
|
152
|
+
/** Relative to the listed directory, with `/` separators on every system. */
|
|
153
|
+
path: string;
|
|
154
|
+
/** A symbolic link is listed as such and not followed. */
|
|
155
|
+
kind: FileKind;
|
|
156
|
+
}
|
|
157
|
+
export interface FileListing {
|
|
158
|
+
/** Depth first, parents before their entries, siblings in byte order of their names. */
|
|
159
|
+
entries: FileListEntry[];
|
|
160
|
+
/** A bound in `ListFilesOptions` ended the listing early. */
|
|
161
|
+
truncated: boolean;
|
|
162
|
+
}
|
|
128
163
|
/** Portable filesystem capability. Operations return failures rather than throwing. */
|
|
129
164
|
export interface FileSystem {
|
|
130
165
|
/**
|
|
@@ -158,6 +193,13 @@ export interface FileSystem {
|
|
|
158
193
|
renameFile(sourcePath: string, destinationPath: string, context: Context): Promise<Result<void, FileError>>;
|
|
159
194
|
fileInfo(path: string, context: Context): Promise<Result<FileInfo, FileError>>;
|
|
160
195
|
listDir(path: string, context: Context): Promise<Result<FileInfo[], FileError>>;
|
|
196
|
+
/**
|
|
197
|
+
* Optional: the files, directories and symbolic links below a directory, at any depth, for indexes such as file
|
|
198
|
+
* completion. Symbolic links are not followed; other kinds of files are not listed; entries that cannot be read are
|
|
199
|
+
* skipped. See `ListFilesOptions` for hidden and ignored files and for the bounds. Fails with `not_supported` when
|
|
200
|
+
* the environment cannot list right now (an older remote daemon), so callers fall back to their own walk.
|
|
201
|
+
*/
|
|
202
|
+
listFiles?(path: string, options: ListFilesOptions | undefined, context: Context): Promise<Result<FileListing, FileError>>;
|
|
161
203
|
openDirReader(path: string, context: Context): Promise<Result<DirReader, FileError>>;
|
|
162
204
|
/**
|
|
163
205
|
* Report changes to files and directories, for hosts that load resources from the environment. When the returned
|