@uicast/core 0.0.1-beta.2 → 0.0.1
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 +3 -13
- package/README.md +120 -1
- package/dist/build-elements-by-key.d.ts +13 -0
- package/dist/build-elements-by-key.js +39 -0
- package/dist/constants.d.ts +2 -0
- package/dist/constants.js +4 -0
- package/dist/create-component-definition.d.ts +26 -0
- package/dist/create-component-definition.js +74 -0
- package/dist/entry-error.d.ts +41 -31
- package/dist/entry-error.js +49 -20
- package/dist/entry-shape.d.ts +7 -0
- package/dist/entry-shape.js +88 -0
- package/dist/expr/evaluate.d.ts +4 -6
- package/dist/expr/evaluate.js +36 -46
- package/dist/expr/extract-deps.d.ts +3 -1
- package/dist/expr/extract-deps.js +20 -20
- package/dist/expr/plan-step-waves.d.ts +8 -0
- package/dist/expr/plan-step-waves.js +45 -0
- package/dist/index.d.ts +7 -8
- package/dist/index.js +5 -9
- package/dist/internal.d.ts +12 -0
- package/dist/internal.js +13 -0
- package/dist/json-schema.d.ts +58 -0
- package/dist/json-schema.js +73 -0
- package/dist/prompt/format.d.ts +5 -0
- package/dist/prompt/format.js +22 -0
- package/dist/prompt/get-common-instructions-partial-prompt.d.ts +20 -10
- package/dist/prompt/get-common-instructions-partial-prompt.js +11 -9
- package/dist/prompt/get-components-partial-prompt.d.ts +20 -28
- package/dist/prompt/get-components-partial-prompt.js +122 -123
- package/dist/prompt/get-edit-request-prompt.d.ts +16 -16
- package/dist/prompt/get-edit-request-prompt.js +11 -18
- package/dist/prompt/get-error-recovery-prompt.d.ts +25 -14
- package/dist/prompt/get-error-recovery-prompt.js +17 -14
- package/dist/prompt/get-expressions-partial-prompt.d.ts +19 -20
- package/dist/prompt/get-expressions-partial-prompt.js +11 -21
- package/dist/prompt/get-functions-partial-prompt.d.ts +16 -16
- package/dist/prompt/get-functions-partial-prompt.js +52 -40
- package/dist/prompt/get-scope-partial-prompt.d.ts +17 -17
- package/dist/prompt/get-scope-partial-prompt.js +21 -29
- package/dist/prompt/index.js +0 -2
- package/dist/prompt/json-schema-to-ts.d.ts +6 -0
- package/dist/prompt/json-schema-to-ts.js +146 -0
- package/dist/prompt/md/EXPRESSIONS.json +1 -1
- package/dist/prompt/md/INSTRUCTIONS.json +1 -1
- package/dist/prompt/shared-types.d.ts +5 -0
- package/dist/prompt/shared-types.js +82 -0
- package/dist/scope/create-proxy-scope.d.ts +52 -17
- package/dist/scope/create-proxy-scope.js +280 -86
- package/dist/scope/parse-scope.d.ts +2 -20
- package/dist/scope/parse-scope.js +10 -35
- package/dist/scope/parse-set-address.d.ts +16 -0
- package/dist/scope/parse-set-address.js +52 -0
- package/dist/stream-json-lines.d.ts +14 -0
- package/dist/stream-json-lines.js +59 -0
- package/dist/types.d.ts +81 -46
- package/dist/types.js +6 -9
- package/dist/url-policy.d.ts +45 -0
- package/dist/url-policy.js +252 -0
- package/package.json +18 -16
- package/dist/def/create-component-definition.d.ts +0 -9
- package/dist/def/create-component-definition.js +0 -10
- package/dist/expr/analyze.d.ts +0 -5
- package/dist/expr/analyze.js +0 -189
- package/dist/expr/ast-utils.d.ts +0 -4
- package/dist/expr/ast-utils.js +0 -23
- package/dist/expr/globals.d.ts +0 -3
- package/dist/expr/globals.js +0 -111
- package/dist/expr/safer-eval.d.ts +0 -21
- package/dist/expr/safer-eval.js +0 -170
- package/dist/expr/validate.d.ts +0 -7
- package/dist/expr/validate.js +0 -109
- package/dist/prompt-utils/json-schema-to-ts.d.ts +0 -73
- package/dist/prompt-utils/json-schema-to-ts.js +0 -199
- package/dist/stream/stream-json-lines.d.ts +0 -1
- package/dist/stream/stream-json-lines.js +0 -94
- package/dist/utils/utils.d.ts +0 -13
- package/dist/utils/utils.js +0 -58
|
@@ -1,134 +1,133 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
? Object.assign(Object.assign({}, jsonSchema), { description: undefined }) : jsonSchema;
|
|
11
|
-
/**
|
|
12
|
-
* Render an object schema's properties as an indented, described bullet list:
|
|
13
|
-
*
|
|
14
|
-
* - count?: number — The number to display
|
|
15
|
-
*
|
|
16
|
-
* Each field's type comes from `JSONSchemaToTs`; its `description` (when set)
|
|
17
|
-
* follows after an em-dash. A non-object schema, or one without properties,
|
|
18
|
-
* yields `[]`. Shared by a component's props and a typed event's options — both
|
|
19
|
-
* are "object schema → described fields".
|
|
20
|
-
*/
|
|
21
|
-
const describeFields = (jsonSchema, indent) => {
|
|
22
|
-
var _a;
|
|
23
|
-
const schema = jsonSchema;
|
|
24
|
-
if (!(schema === null || schema === void 0 ? void 0 : schema.properties))
|
|
25
|
-
return [];
|
|
26
|
-
const required = (_a = schema.required) !== null && _a !== void 0 ? _a : [];
|
|
27
|
-
return Object.entries(schema.properties).map(([name, field]) => {
|
|
1
|
+
import { isSchemaObject, resolvePointer, specToJSONSchema } from "../json-schema.js";
|
|
2
|
+
import { resolveUrlPolicy, schemaHasUrlFormat } from "../url-policy.js";
|
|
3
|
+
import { dashTail, joinSections, noteSection, stripRootAnnotations, unwrapParens } from "./format.js";
|
|
4
|
+
import { jsonSchemaToTs } from "./json-schema-to-ts.js";
|
|
5
|
+
import { collectSharedTypes } from "./shared-types.js";
|
|
6
|
+
const typeOf = (schema, namedRefs) => jsonSchemaToTs(stripRootAnnotations(schema), { namedRefs });
|
|
7
|
+
const describeFields = (schema, indent, namedRefs) => {
|
|
8
|
+
const required = schema.required ?? [];
|
|
9
|
+
return Object.entries(schema.properties ?? {}).map(([name, field]) => {
|
|
28
10
|
const optional = required.includes(name) ? "" : "?";
|
|
29
|
-
|
|
30
|
-
|
|
11
|
+
// The engine applies defaults before render, so the default is what omitting the field means.
|
|
12
|
+
const fallback = "default" in field ? ` = ${JSON.stringify(field.default)}` : "";
|
|
13
|
+
return `${indent}- ${name}${optional}: ${typeOf(field, namedRefs)}${fallback}${dashTail(field.description)}`;
|
|
31
14
|
});
|
|
32
15
|
};
|
|
16
|
+
// The props object whose fields list one per line: the schema itself, or what a root `$ref` names.
|
|
17
|
+
const propsObject = (schema) => {
|
|
18
|
+
if (schema.properties)
|
|
19
|
+
return schema;
|
|
20
|
+
if (typeof schema.$ref !== "string")
|
|
21
|
+
return null;
|
|
22
|
+
const target = resolvePointer(schema.$ref, schema);
|
|
23
|
+
return isSchemaObject(target) && target.properties ? target : null;
|
|
24
|
+
};
|
|
25
|
+
// Any other shape (a union, an intersection, a record) prints as one type, so the model still sees it.
|
|
26
|
+
const describeProps = (schema, namedRefs) => {
|
|
27
|
+
const object = propsObject(schema);
|
|
28
|
+
if (!object)
|
|
29
|
+
return [` Props: ${unwrapParens(typeOf(schema, namedRefs))}`];
|
|
30
|
+
const fields = describeFields(object, " ", namedRefs);
|
|
31
|
+
return fields.length ? [" Props:", ...fields] : [];
|
|
32
|
+
};
|
|
33
|
+
// What a URL prop may hold. A predicate cannot be described, so it prints nothing: say it in `note`.
|
|
34
|
+
const describeUrlProps = (policy) => {
|
|
35
|
+
if (typeof policy === "function")
|
|
36
|
+
return "";
|
|
37
|
+
const { allowRelative, allowSameOrigin, hosts, allowDataImages, origin } = resolveUrlPolicy(policy);
|
|
38
|
+
const allowed = [
|
|
39
|
+
allowRelative && "- relative URL: `/a`, `a/b`, `?q=1`, `#x`",
|
|
40
|
+
allowSameOrigin && `- absolute URL on ${origin ? `\`${origin}\`` : "this site"}`,
|
|
41
|
+
hosts.length > 0 && `- http or https URL on ${hosts.map((host) => `\`${host}\``).join(", ")}`,
|
|
42
|
+
"- `mailto:`, `tel:` or `sms:` link",
|
|
43
|
+
allowDataImages && "- `data:` image, not SVG",
|
|
44
|
+
].filter(Boolean);
|
|
45
|
+
return `## URL Props\n\nURL-format props (\`format uri\`, \`format uri-reference\`) take only:\n${allowed.join("\n")}\n\nOther URLs fail element.`;
|
|
46
|
+
};
|
|
47
|
+
// A payload with an `id` converts to a root `$ref` into `$defs`; the def's key is the event's name.
|
|
48
|
+
const eventName = (schema) => {
|
|
49
|
+
const name = schema.$ref?.startsWith("#/$defs/") ? schema.$ref.slice("#/$defs/".length) : undefined;
|
|
50
|
+
return name && schema.$defs?.[name] ? name : undefined;
|
|
51
|
+
};
|
|
52
|
+
// A callback payload with an `id` is a common event; two different payloads under one `id` throw.
|
|
53
|
+
const commonEvents = (defs) => {
|
|
54
|
+
const events = new Map();
|
|
55
|
+
for (const def of defs) {
|
|
56
|
+
for (const spec of Object.values(def.callbacks ?? {})) {
|
|
57
|
+
const schema = specToJSONSchema(spec);
|
|
58
|
+
const name = eventName(schema);
|
|
59
|
+
if (!name)
|
|
60
|
+
continue;
|
|
61
|
+
const fingerprint = JSON.stringify(schema);
|
|
62
|
+
const known = events.get(name);
|
|
63
|
+
if (known && known.fingerprint !== fingerprint) {
|
|
64
|
+
throw new Error(`Two callbacks declare the event "${name}" with different payloads — a shared \`id\` must name one shape.`);
|
|
65
|
+
}
|
|
66
|
+
if (!known)
|
|
67
|
+
events.set(name, { schema, fingerprint });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return new Map([...events].map(([id, { schema }]) => [id, schema]));
|
|
71
|
+
};
|
|
33
72
|
/**
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* `# Component Details`, one entry per visible def: its name + description, a
|
|
37
|
-
* `Props:` list (each prop's type + description) and an `Event handlers:` list
|
|
38
|
-
* (each handler's signature + description, a typed event's options described one
|
|
39
|
-
* level deeper). Types come from `JSONSchemaToTs`; every description rides after
|
|
40
|
-
* an em-dash, so nothing the author wrote on a prop, option, or handler is lost.
|
|
73
|
+
* The component menu, from your definitions. A payload with an `id` prints once, under `## Common Events`; a URL
|
|
74
|
+
* prop adds `## URL Props`. Throws on a duplicate name, or on two different payloads with one `id`.
|
|
41
75
|
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
* payload is rendered once as a named type under `# Common Events`, and a
|
|
45
|
-
* component whose callback is one of them references it as `evt: <$id>` instead
|
|
46
|
-
* of re-inlining the payload — killing the per-component duplication. A callback
|
|
47
|
-
* is matched by its `$id`, so it stays library-agnostic (anything that emits
|
|
48
|
-
* `$id` participates); non-common callbacks render inline as before.
|
|
76
|
+
* @example
|
|
77
|
+
* getComponentsPartialPrompt({ definitions: defs });
|
|
49
78
|
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
* caller passes whatever def set + common events it exposes.
|
|
53
|
-
*
|
|
54
|
-
* Mirrors `getFunctionsPartialPrompt` — same `# Available X` / `# X Details`
|
|
55
|
-
* two-section shape.
|
|
56
|
-
*
|
|
57
|
-
* Throws on a duplicate component `name`, a common event whose schema has no
|
|
58
|
-
* `$id`, or a duplicate common event `$id`.
|
|
79
|
+
* @example
|
|
80
|
+
* getComponentsPartialPrompt({ definitions: [...defs, BadgeDef], urlPolicy: { hosts: ["cdn.example.com"] } });
|
|
59
81
|
*/
|
|
60
|
-
export function getComponentsPartialPrompt({ definitions: defs,
|
|
61
|
-
const
|
|
62
|
-
for (const
|
|
63
|
-
if (
|
|
64
|
-
throw new Error(`Duplicate component name: "${
|
|
65
|
-
|
|
66
|
-
seen.add(def.name);
|
|
67
|
-
}
|
|
68
|
-
// Index the common events by `$id`, rendering each once. A missing `$id`
|
|
69
|
-
// would leave the handler unmatchable (it would silently inline on every
|
|
70
|
-
// component), so it's a hard error — as is a duplicate id, which would make
|
|
71
|
-
// the per-component reference ambiguous.
|
|
72
|
-
const commonIds = new Set();
|
|
73
|
-
const commonLines = [];
|
|
74
|
-
for (const schema of commonEvents) {
|
|
75
|
-
const jsonSchema = toJSONSchema(schema);
|
|
76
|
-
const id = readId(jsonSchema);
|
|
77
|
-
if (!id) {
|
|
78
|
-
throw new Error('Common event schema is missing a JSON Schema `$id` (add e.g. .meta({ $id: "onClick" }))');
|
|
79
|
-
}
|
|
80
|
-
if (commonIds.has(id)) {
|
|
81
|
-
throw new Error(`Duplicate common event id: "${id}"`);
|
|
82
|
-
}
|
|
83
|
-
commonIds.add(id);
|
|
84
|
-
const ts = JSONSchemaToTs(stripRootDescription(jsonSchema));
|
|
85
|
-
const description = jsonSchema.description;
|
|
86
|
-
commonLines.push(`- ${id}: ${ts}${description ? ` — ${description}` : ""}`);
|
|
82
|
+
export function getComponentsPartialPrompt({ definitions: defs, urlPolicy, note }) {
|
|
83
|
+
const names = new Set();
|
|
84
|
+
for (const { name } of defs) {
|
|
85
|
+
if (names.has(name))
|
|
86
|
+
throw new Error(`Duplicate component name: "${name}"`);
|
|
87
|
+
names.add(name);
|
|
87
88
|
}
|
|
88
89
|
const visible = defs.filter((def) => !def.hidden);
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
//
|
|
114
|
-
const
|
|
115
|
-
if (
|
|
116
|
-
return [` - ${
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
` - ${
|
|
120
|
-
];
|
|
90
|
+
// A heading with nothing under it is dropped.
|
|
91
|
+
if (visible.length === 0)
|
|
92
|
+
return noteSection(note);
|
|
93
|
+
// One registry for the block: a `$def` shared by an event payload and a prop prints once.
|
|
94
|
+
const shared = collectSharedTypes();
|
|
95
|
+
const events = commonEvents(visible);
|
|
96
|
+
const eventLines = [...events].flatMap(([name, schema]) => {
|
|
97
|
+
const refs = shared.add(schema);
|
|
98
|
+
const payload = schema.$defs?.[name];
|
|
99
|
+
const tail = dashTail(payload.description);
|
|
100
|
+
const fields = describeFields(payload, " ", refs);
|
|
101
|
+
return fields.length ? [`- ${name}${tail}`, ...fields] : [`- ${name}: ${typeOf(payload, refs)}${tail}`];
|
|
102
|
+
});
|
|
103
|
+
let hasUrlProps = false;
|
|
104
|
+
const details = visible.map(({ name, description, props, callbacks }) => {
|
|
105
|
+
const propsSchema = specToJSONSchema(props);
|
|
106
|
+
hasUrlProps ||= schemaHasUrlFormat(propsSchema);
|
|
107
|
+
const propLines = describeProps(propsSchema, shared.add(propsSchema));
|
|
108
|
+
const handlers = Object.entries(callbacks ?? {}).flatMap(([callback, spec]) => {
|
|
109
|
+
const schema = specToJSONSchema(spec);
|
|
110
|
+
const refs = shared.add(schema);
|
|
111
|
+
const event = eventName(schema);
|
|
112
|
+
if (event && events.has(event))
|
|
113
|
+
return [` - ${callback}(evt: ${event})`];
|
|
114
|
+
// The description documents the handler, not its `evt`.
|
|
115
|
+
const tail = dashTail(schema.description);
|
|
116
|
+
if (schema.type === "null")
|
|
117
|
+
return [` - ${callback}()${tail}`];
|
|
118
|
+
const fields = describeFields(schema, " ", refs);
|
|
119
|
+
if (fields.length)
|
|
120
|
+
return [` - ${callback}(evt)${tail}`, ...fields];
|
|
121
|
+
return [` - ${callback}(evt: ${typeOf(schema, refs)})${tail}`];
|
|
121
122
|
});
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
.join("\n
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
"\n\n# Component Details\n\n" +
|
|
133
|
-
detail);
|
|
123
|
+
return [
|
|
124
|
+
`- ${name} — ${description}`,
|
|
125
|
+
...propLines,
|
|
126
|
+
...(handlers.length ? [" Event handlers:", ...handlers] : []),
|
|
127
|
+
].join("\n");
|
|
128
|
+
});
|
|
129
|
+
// After the details, so every hoisted definition is in.
|
|
130
|
+
const sharedLines = shared.lines();
|
|
131
|
+
const block = joinSections(`# Available Components\n\n${visible.map((def) => def.name).join(", ")}`, eventLines.length > 0 && `## Common Events\n\n${eventLines.join("\n")}`, `## Component Details\n\n${details.join("\n\n")}`, sharedLines.length > 0 && `## Shared Types\n\n${sharedLines.join("\n")}`, hasUrlProps && describeUrlProps(urlPolicy));
|
|
132
|
+
return joinSections(block.trim(), noteSection(note));
|
|
134
133
|
}
|
|
@@ -1,23 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options for `getEditRequestPrompt`.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* const options: EditRequestPromptOptions = { request: "Add a total row.", missingKeys: ["totals"] };
|
|
6
|
+
*/
|
|
1
7
|
export type EditRequestPromptOptions = {
|
|
2
|
-
/** The
|
|
8
|
+
/** The change the user asked for, in their words. */
|
|
3
9
|
request: string;
|
|
4
|
-
/**
|
|
5
|
-
* Keys referenced as children in the replayed tree but never emitted —
|
|
6
|
-
* typically the tail of an earlier response that was cut off. Listing them
|
|
7
|
-
* tells the model those elements do NOT exist, so it re-emits them instead
|
|
8
|
-
* of keeping them by reference.
|
|
9
|
-
*/
|
|
10
|
+
/** Keys named in `children` but never emitted, as after a cut-off response. The model is told to emit them. */
|
|
10
11
|
missingKeys?: string[];
|
|
11
12
|
};
|
|
12
13
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* the change request plus the delta convention (re-emit a key to replace its
|
|
16
|
-
* subtree — the Partial Replacement rule the system prompt already teaches).
|
|
17
|
-
* The model then emits only the elements that change instead of the whole
|
|
18
|
-
* page — cheaper and faster, since output tokens dominate both.
|
|
14
|
+
* A user message asking the model to change a page it made, by emitting only the entries that change. One turn's
|
|
15
|
+
* content, not part of the system prompt.
|
|
19
16
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
17
|
+
* @example
|
|
18
|
+
* const messages = [
|
|
19
|
+
* { role: "assistant", content: stored.map((entry) => JSON.stringify(entry)).join("\n") },
|
|
20
|
+
* { role: "user", content: getEditRequestPrompt({ request: "Make the total large." }) },
|
|
21
|
+
* ];
|
|
22
22
|
*/
|
|
23
|
-
export declare function getEditRequestPrompt({ request, missingKeys
|
|
23
|
+
export declare function getEditRequestPrompt({ request, missingKeys }: EditRequestPromptOptions): string;
|
|
@@ -1,22 +1,15 @@
|
|
|
1
|
+
import { joinSections } from "./format.js";
|
|
1
2
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* the change request plus the delta convention (re-emit a key to replace its
|
|
5
|
-
* subtree — the Partial Replacement rule the system prompt already teaches).
|
|
6
|
-
* The model then emits only the elements that change instead of the whole
|
|
7
|
-
* page — cheaper and faster, since output tokens dominate both.
|
|
3
|
+
* A user message asking the model to change a page it made, by emitting only the entries that change. One turn's
|
|
4
|
+
* content, not part of the system prompt.
|
|
8
5
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
6
|
+
* @example
|
|
7
|
+
* const messages = [
|
|
8
|
+
* { role: "assistant", content: stored.map((entry) => JSON.stringify(entry)).join("\n") },
|
|
9
|
+
* { role: "user", content: getEditRequestPrompt({ request: "Make the total large." }) },
|
|
10
|
+
* ];
|
|
11
11
|
*/
|
|
12
|
-
export function getEditRequestPrompt({ request, missingKeys = []
|
|
13
|
-
return
|
|
14
|
-
|
|
15
|
-
"Update the previously generated page above. Emit ONLY the elements that change: re-emit an existing key to replace that element (its new `children` array defines the new subtree — children you still reference are kept as-is, so do not re-emit them), use fresh keys for new elements, and do not re-emit unchanged elements. Never re-emit the whole page. `seed` on re-emitted keys does NOT re-run — initialize any NEW state via the `seed` of a newly-keyed element instead.",
|
|
16
|
-
missingKeys.length > 0
|
|
17
|
-
? `Warning: the page above references child keys that were never emitted (the earlier response was cut off): ${missingKeys.join(", ")}. These elements do NOT exist — emit them now, or re-emit their parents with corrected children.`
|
|
18
|
-
: "",
|
|
19
|
-
]
|
|
20
|
-
.filter(Boolean)
|
|
21
|
-
.join("\n\n");
|
|
12
|
+
export function getEditRequestPrompt({ request, missingKeys = [] }) {
|
|
13
|
+
return joinSections(request, "Change page above. Emit ONLY entries that change: re-emit key to replace its entry (new `children` define its subtree; listed old children stay, don't re-emit them); new keys for new entries. Never re-emit whole page. Re-emitted `seed` doesn't run again: new state goes in new key's `seed`.", missingKeys.length > 0 &&
|
|
14
|
+
`Warning: page lists keys never emitted (earlier response cut off): ${missingKeys.join(", ")}. They do NOT exist: emit them, or re-emit their parents with fixed \`children\`.`);
|
|
22
15
|
}
|
|
@@ -1,24 +1,35 @@
|
|
|
1
|
+
import { type EntryErrorReason } from "../entry-error.js";
|
|
2
|
+
/**
|
|
3
|
+
* One failed element, for `getErrorRecoveryPrompt`.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* const failure: RenderFailure = { key: "chart", message: error.message, reason: error.reason };
|
|
7
|
+
*/
|
|
1
8
|
export type RenderFailure = {
|
|
2
|
-
/** The
|
|
9
|
+
/** The failed element's key. */
|
|
3
10
|
key: string;
|
|
4
|
-
/**
|
|
11
|
+
/** What went wrong, e.g. the error's `message`. */
|
|
5
12
|
message: string;
|
|
13
|
+
/** Adds what this kind of failure means to the line; a reason whose fault is `"environment"` adds nothing. */
|
|
14
|
+
reason?: EntryErrorReason;
|
|
6
15
|
};
|
|
16
|
+
/**
|
|
17
|
+
* Options for `getErrorRecoveryPrompt`.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* const options: ErrorRecoveryPromptOptions = { failures: [{ key: "chart", message: error.message }] };
|
|
21
|
+
*/
|
|
7
22
|
export type ErrorRecoveryPromptOptions = {
|
|
8
|
-
/**
|
|
23
|
+
/** One per failed element. */
|
|
9
24
|
failures: RenderFailure[];
|
|
10
25
|
};
|
|
11
26
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* it corrected. Deliberately surface-neutral — it states the failures and the
|
|
15
|
-
* fix request, while the output conventions come from the surface's own
|
|
16
|
-
* prompt: on a page surface the host passes this through its edit pipeline
|
|
17
|
-
* (replayed document + `getEditRequestPrompt`, which carries the delta
|
|
18
|
-
* convention), on a chat surface it goes out as a plain user message and the
|
|
19
|
-
* model replies with a corrected fence.
|
|
27
|
+
* A user message naming the elements that failed, so the model sends fixed ones under the same keys. One turn's
|
|
28
|
+
* content, not part of the system prompt.
|
|
20
29
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
30
|
+
* @example
|
|
31
|
+
* getErrorRecoveryPrompt({
|
|
32
|
+
* failures: [{ key: "chart", message: "chartData.map is not a function", reason: "expression-runtime" }],
|
|
33
|
+
* });
|
|
23
34
|
*/
|
|
24
|
-
export declare function getErrorRecoveryPrompt({ failures
|
|
35
|
+
export declare function getErrorRecoveryPrompt({ failures }: ErrorRecoveryPromptOptions): string;
|
|
@@ -1,23 +1,26 @@
|
|
|
1
|
+
import { FAULT_BY_REASON, REASON_DESCRIPTIONS } from "../entry-error.js";
|
|
1
2
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* it corrected. Deliberately surface-neutral — it states the failures and the
|
|
5
|
-
* fix request, while the output conventions come from the surface's own
|
|
6
|
-
* prompt: on a page surface the host passes this through its edit pipeline
|
|
7
|
-
* (replayed document + `getEditRequestPrompt`, which carries the delta
|
|
8
|
-
* convention), on a chat surface it goes out as a plain user message and the
|
|
9
|
-
* model replies with a corrected fence.
|
|
3
|
+
* A user message naming the elements that failed, so the model sends fixed ones under the same keys. One turn's
|
|
4
|
+
* content, not part of the system prompt.
|
|
10
5
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
6
|
+
* @example
|
|
7
|
+
* getErrorRecoveryPrompt({
|
|
8
|
+
* failures: [{ key: "chart", message: "chartData.map is not a function", reason: "expression-runtime" }],
|
|
9
|
+
* });
|
|
13
10
|
*/
|
|
14
|
-
export function getErrorRecoveryPrompt({ failures
|
|
11
|
+
export function getErrorRecoveryPrompt({ failures }) {
|
|
15
12
|
const list = failures
|
|
16
|
-
.map((
|
|
13
|
+
.map(({ key, message, reason }) => {
|
|
14
|
+
const line = `- Entry \`${key}\`: ${message}`;
|
|
15
|
+
// Environment faults are not the model's to fix.
|
|
16
|
+
if (!reason || FAULT_BY_REASON[reason] === "environment")
|
|
17
|
+
return line;
|
|
18
|
+
return `${line} (${reason} — ${REASON_DESCRIPTIONS[reason]})`;
|
|
19
|
+
})
|
|
17
20
|
.join("\n");
|
|
18
21
|
return [
|
|
19
|
-
"
|
|
22
|
+
"Rendered UI has errors:",
|
|
20
23
|
list,
|
|
21
|
-
"
|
|
24
|
+
"Re-emit each failed entry fixed, same `key`. Fix cause: when it comes from another entry (like `seed` elsewhere), fix that entry too.",
|
|
22
25
|
].join("\n\n");
|
|
23
26
|
}
|
|
@@ -1,24 +1,23 @@
|
|
|
1
|
-
/** No options yet — the type is reserved so the signature can grow without breaking callers. */
|
|
2
|
-
export type ExpressionsPromptOptions = Record<string, never>;
|
|
3
1
|
/**
|
|
4
|
-
*
|
|
5
|
-
* the generation LLM follows when authoring `expr` values (props, hidden,
|
|
6
|
-
* seed, callbacks). Catalog-/app-agnostic: it documents only the evaluator
|
|
7
|
-
* surface (allowed syntax, the `scopes` / `evt` context variables), not any
|
|
8
|
-
* specific host functions.
|
|
2
|
+
* Options for `getExpressionsPartialPrompt`.
|
|
9
3
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
4
|
+
* @example
|
|
5
|
+
* const options: ExpressionsPromptOptions = { maxLength: 500 };
|
|
6
|
+
*/
|
|
7
|
+
export type ExpressionsPromptOptions = {
|
|
8
|
+
/** The evaluator's `maxSourceLength`: the longest expression, in characters. Default 1000. */
|
|
9
|
+
maxLength?: number;
|
|
10
|
+
/** Your text, appended verbatim as a trailing `## Note`. */
|
|
11
|
+
note?: string;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* The `# JavaScript Expressions` block: the language's rules, its globals and a few idioms. The globals and the
|
|
15
|
+
* default length come from `@uicast/expr` itself.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* getExpressionsPartialPrompt();
|
|
16
19
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* system prompt. The closing line refers to host functions "listed below",
|
|
20
|
-
* so the app should join its function-list partial (e.g.
|
|
21
|
-
* `getFunctionsPartialPrompt({ functions })`) as a later section. core
|
|
22
|
-
* ships the pieces; the app owns the assembly.
|
|
20
|
+
* @example
|
|
21
|
+
* getExpressionsPartialPrompt({ maxLength: 500 }); // with new Evaluator({ maxSourceLength: 500 })
|
|
23
22
|
*/
|
|
24
|
-
export declare function getExpressionsPartialPrompt(
|
|
23
|
+
export declare function getExpressionsPartialPrompt({ maxLength, note, }?: ExpressionsPromptOptions): string;
|
|
@@ -1,27 +1,17 @@
|
|
|
1
|
-
import { ALLOWED_GLOBALS } from "
|
|
1
|
+
import { ALLOWED_GLOBALS, DEFAULT_MAX_SOURCE_LENGTH } from "@uicast/expr/internal";
|
|
2
|
+
import { joinSections, noteSection } from "./format.js";
|
|
2
3
|
import EXPRESSIONS from "./md/EXPRESSIONS.json" with { type: "json" };
|
|
3
4
|
/**
|
|
4
|
-
* The `# JavaScript Expressions` block
|
|
5
|
-
*
|
|
6
|
-
* seed, callbacks). Catalog-/app-agnostic: it documents only the evaluator
|
|
7
|
-
* surface (allowed syntax, the `scopes` / `evt` context variables), not any
|
|
8
|
-
* specific host functions.
|
|
5
|
+
* The `# JavaScript Expressions` block: the language's rules, its globals and a few idioms. The globals and the
|
|
6
|
+
* default length come from `@uicast/expr` itself.
|
|
9
7
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* after edits). The doc carries a visible `🔴ALLOWED_GLOBALS🔴` slot that this
|
|
13
|
-
* builder fills from `globals.ts` (the single source of truth for the sandbox
|
|
14
|
-
* allow-list) so the prompt can never drift from what the evaluator actually
|
|
15
|
-
* permits.
|
|
8
|
+
* @example
|
|
9
|
+
* getExpressionsPartialPrompt();
|
|
16
10
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* system prompt. The closing line refers to host functions "listed below",
|
|
20
|
-
* so the app should join its function-list partial (e.g.
|
|
21
|
-
* `getFunctionsPartialPrompt({ functions })`) as a later section. core
|
|
22
|
-
* ships the pieces; the app owns the assembly.
|
|
11
|
+
* @example
|
|
12
|
+
* getExpressionsPartialPrompt({ maxLength: 500 }); // with new Evaluator({ maxSourceLength: 500 })
|
|
23
13
|
*/
|
|
24
|
-
export function getExpressionsPartialPrompt(
|
|
25
|
-
const
|
|
26
|
-
return
|
|
14
|
+
export function getExpressionsPartialPrompt({ maxLength = DEFAULT_MAX_SOURCE_LENGTH, note, } = {}) {
|
|
15
|
+
const language = EXPRESSIONS.replace("🔴ALLOWED_GLOBALS🔴", ALLOWED_GLOBALS.join(", ")).replace("🔴MAX_LENGTH🔴", String(maxLength));
|
|
16
|
+
return joinSections(language.trim(), noteSection(note));
|
|
27
17
|
}
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
import type { StandardToolV0 } from "
|
|
1
|
+
import type { StandardToolV0 } from "@uicast/expr";
|
|
2
|
+
/**
|
|
3
|
+
* Options for `getFunctionsPartialPrompt`.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* const options: FunctionsPromptOptions = { functions: tools, note: "Amounts are in cents." };
|
|
7
|
+
*/
|
|
2
8
|
export type FunctionsPromptOptions = {
|
|
3
|
-
/**
|
|
9
|
+
/** Your host functions: the array the evaluator binds. An empty one adds nothing. */
|
|
4
10
|
functions: StandardToolV0[];
|
|
11
|
+
/** Your text, appended verbatim as this section's trailing `## Note`. */
|
|
12
|
+
note?: string;
|
|
5
13
|
};
|
|
6
14
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* the tool's Zod schemas:
|
|
11
|
-
*
|
|
12
|
-
* - updateRows({ sheet: string, ... }) => void: <description>
|
|
13
|
-
*
|
|
14
|
-
* A tool with no `inputSchema` renders `name()`; one with no `outputSchema`
|
|
15
|
-
* renders `=> void`.
|
|
15
|
+
* The `# Available Functions` block: a call signature per host function, from its schemas. A tool with no
|
|
16
|
+
* `outputSchema` prints `=> unknown`: undeclared, not empty. Throws on a duplicate name, a name that is not an
|
|
17
|
+
* identifier, or a reserved one: `scopes`, `evt`, `currentValue` or a global such as `Math`.
|
|
16
18
|
*
|
|
17
|
-
*
|
|
18
|
-
* (
|
|
19
|
-
* surrounding prompt it needs (extra calling conventions, examples, …).
|
|
20
|
-
* Mirrors `getComponentsPartialPrompt` — same two-section shape.
|
|
19
|
+
* @example
|
|
20
|
+
* getFunctionsPartialPrompt({ functions: tools }); // the same array as new Evaluator({ functions: tools })
|
|
21
21
|
*/
|
|
22
|
-
export declare function getFunctionsPartialPrompt({ functions
|
|
22
|
+
export declare function getFunctionsPartialPrompt({ functions, note }: FunctionsPromptOptions): string;
|