@anythingai/teleprompt 0.2.0 → 0.3.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/dist/index.cjs +132 -169
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -66
- package/dist/index.d.ts +9 -66
- package/dist/index.js +132 -169
- package/dist/index.js.map +1 -1
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +4 -23
- package/dist/testing.d.ts +4 -23
- package/dist/testing.js.map +1 -1
- package/dist/types-ow-XbrO7.d.cts +12 -0
- package/dist/types-ow-XbrO7.d.ts +12 -0
- package/llms.txt +183 -0
- package/package.json +19 -19
- package/dist/types--sRnfw6Q.d.cts +0 -34
- package/dist/types--sRnfw6Q.d.ts +0 -34
package/dist/index.cjs
CHANGED
|
@@ -1,25 +1,7 @@
|
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class;// src/builder.ts
|
|
2
|
-
function assertNever(value) {
|
|
3
|
-
throw new Error(`Unexpected format: ${value}`);
|
|
4
|
-
}
|
|
5
|
-
function isSection(node) {
|
|
6
|
-
return "render" in node;
|
|
7
|
-
}
|
|
8
|
-
function isGroup(node) {
|
|
9
|
-
return "children" in node;
|
|
10
|
-
}
|
|
11
|
-
function isOneOf(node) {
|
|
12
|
-
return "candidates" in node;
|
|
13
|
-
}
|
|
14
2
|
var PromptBuilder = (_class = class _PromptBuilder {constructor() { _class.prototype.__init.call(this); }
|
|
15
3
|
__init() {this.nodes = []}
|
|
16
|
-
/**
|
|
17
|
-
* Add a section to the prompt.
|
|
18
|
-
*
|
|
19
|
-
* If a section with the same `id` already exists, it is replaced.
|
|
20
|
-
* This makes `.use()` idempotent — you can safely call it multiple
|
|
21
|
-
* times with the same section without creating duplicates.
|
|
22
|
-
*/
|
|
4
|
+
/** Replaces an existing section with the same id. */
|
|
23
5
|
use(section2) {
|
|
24
6
|
const idx = this.nodes.findIndex((n) => !isOneOf(n) && n.id === section2.id);
|
|
25
7
|
if (idx >= 0) {
|
|
@@ -30,8 +12,7 @@ var PromptBuilder = (_class = class _PromptBuilder {constructor() { _class.proto
|
|
|
30
12
|
return this;
|
|
31
13
|
}
|
|
32
14
|
/**
|
|
33
|
-
*
|
|
34
|
-
* a non-empty string wins — the rest are excluded.
|
|
15
|
+
* First candidate that renders a non-empty string wins.
|
|
35
16
|
*
|
|
36
17
|
* @example
|
|
37
18
|
* ```ts
|
|
@@ -43,9 +24,7 @@ var PromptBuilder = (_class = class _PromptBuilder {constructor() { _class.proto
|
|
|
43
24
|
return this;
|
|
44
25
|
}
|
|
45
26
|
/**
|
|
46
|
-
*
|
|
47
|
-
* wrapped in `<id>...</id>`. In `text` format, groups are transparent
|
|
48
|
-
* and children render as if they were top-level sections.
|
|
27
|
+
* In `xml` format, wraps children in `<id>` tags. Transparent in `text` format.
|
|
49
28
|
*
|
|
50
29
|
* @example
|
|
51
30
|
* ```ts
|
|
@@ -67,199 +46,183 @@ var PromptBuilder = (_class = class _PromptBuilder {constructor() { _class.proto
|
|
|
67
46
|
}
|
|
68
47
|
return this;
|
|
69
48
|
}
|
|
70
|
-
/**
|
|
49
|
+
/** Searches recursively into groups and oneOf candidates. */
|
|
71
50
|
without(ref) {
|
|
72
51
|
const id = typeof ref === "string" ? ref : ref.id;
|
|
73
|
-
this.nodes =
|
|
52
|
+
this.nodes = removeNode(this.nodes, id);
|
|
74
53
|
return this;
|
|
75
54
|
}
|
|
76
|
-
/**
|
|
55
|
+
/** Searches recursively into groups and oneOf candidates. */
|
|
77
56
|
has(ref) {
|
|
78
57
|
const id = typeof ref === "string" ? ref : ref.id;
|
|
79
|
-
return
|
|
58
|
+
return hasNode(this.nodes, id);
|
|
80
59
|
}
|
|
81
|
-
/** Get all ids (sections, groups, and oneOf candidates) in order. */
|
|
82
60
|
ids() {
|
|
83
|
-
return
|
|
61
|
+
return collectIds(this.nodes);
|
|
84
62
|
}
|
|
85
63
|
/**
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
* Use this to create mode-specific or model-specific variants
|
|
89
|
-
* without mutating the base builder.
|
|
64
|
+
* Creates an independent copy. Modifications to the fork don't affect the original.
|
|
90
65
|
*
|
|
91
66
|
* @example
|
|
92
67
|
* ```ts
|
|
93
68
|
* const base = new PromptBuilder().use(a).use(b);
|
|
94
|
-
* const variant = base.fork().use(c);
|
|
69
|
+
* const variant = base.fork().use(c);
|
|
95
70
|
* ```
|
|
96
71
|
*/
|
|
97
72
|
fork() {
|
|
98
73
|
const forked = new _PromptBuilder();
|
|
99
|
-
forked.nodes =
|
|
74
|
+
forked.nodes = deepCopy(this.nodes);
|
|
100
75
|
return forked;
|
|
101
76
|
}
|
|
102
|
-
/**
|
|
103
|
-
* Build the final prompt string.
|
|
104
|
-
*
|
|
105
|
-
* 1. Filters out sections whose `when` guard returns false
|
|
106
|
-
* 2. Renders each section
|
|
107
|
-
* 3. Filters out empty strings
|
|
108
|
-
* 4. Joins with separator and trims
|
|
109
|
-
*
|
|
110
|
-
* Pass `{ format: 'xml' }` to wrap each section in `<id>` tags.
|
|
111
|
-
*/
|
|
112
77
|
build(ctx, options) {
|
|
113
78
|
return this.buildWithMeta(ctx, options).prompt;
|
|
114
79
|
}
|
|
115
|
-
/**
|
|
116
|
-
* Build the prompt and return metadata about which sections were
|
|
117
|
-
* included/excluded. Useful for debugging and logging.
|
|
118
|
-
*
|
|
119
|
-
* A section is "excluded" if its `when` guard returns false.
|
|
120
|
-
* A section is "included" only if it passes the guard and renders
|
|
121
|
-
* a non-empty string.
|
|
122
|
-
*/
|
|
80
|
+
/** Like `build`, but also returns which section ids were included/excluded. */
|
|
123
81
|
buildWithMeta(ctx, options) {
|
|
124
82
|
const included = [];
|
|
125
83
|
const excluded = [];
|
|
126
84
|
const format = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _ => _.format]), () => ( "text"));
|
|
127
|
-
const parts =
|
|
85
|
+
const parts = renderNodes(this.nodes, ctx, format, included, excluded);
|
|
128
86
|
const prompt = parts.join("\n\n").trim();
|
|
129
87
|
return { prompt, included, excluded };
|
|
130
88
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
89
|
+
}, _class);
|
|
90
|
+
function isSection(node) {
|
|
91
|
+
return "render" in node;
|
|
92
|
+
}
|
|
93
|
+
function isGroup(node) {
|
|
94
|
+
return "children" in node;
|
|
95
|
+
}
|
|
96
|
+
function isOneOf(node) {
|
|
97
|
+
return "candidates" in node;
|
|
98
|
+
}
|
|
99
|
+
function assertNever(value) {
|
|
100
|
+
throw new Error(`Unexpected format: ${value}`);
|
|
101
|
+
}
|
|
102
|
+
function formatSection(id, content, format) {
|
|
103
|
+
switch (format) {
|
|
104
|
+
case "text":
|
|
105
|
+
return content;
|
|
106
|
+
case "xml":
|
|
107
|
+
return `<${id}>
|
|
108
|
+
${content}
|
|
109
|
+
</${id}>`;
|
|
110
|
+
default:
|
|
111
|
+
return assertNever(format);
|
|
147
112
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
113
|
+
}
|
|
114
|
+
function formatGroup(id, childParts, format) {
|
|
115
|
+
switch (format) {
|
|
116
|
+
case "text":
|
|
117
|
+
return childParts;
|
|
118
|
+
case "xml":
|
|
119
|
+
return [`<${id}>
|
|
120
|
+
${childParts.join("\n\n")}
|
|
121
|
+
</${id}>`];
|
|
122
|
+
default:
|
|
123
|
+
return assertNever(format);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
function renderSection(section2, ctx, format, parts, included, excluded) {
|
|
127
|
+
if (section2.when && !section2.when(ctx)) {
|
|
159
128
|
excluded.push(section2.id);
|
|
160
129
|
return false;
|
|
161
130
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
continue;
|
|
168
|
-
}
|
|
169
|
-
if (this.renderSection(candidate, ctx, format, parts, included, excluded)) {
|
|
170
|
-
found = true;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
131
|
+
const output = section2.render(ctx);
|
|
132
|
+
if (output) {
|
|
133
|
+
included.push(section2.id);
|
|
134
|
+
parts.push(formatSection(section2.id, output, format));
|
|
135
|
+
return true;
|
|
173
136
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
default:
|
|
184
|
-
return assertNever(format);
|
|
137
|
+
excluded.push(section2.id);
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
function renderOneOf(node, ctx, format, parts, included, excluded) {
|
|
141
|
+
let found = false;
|
|
142
|
+
for (const candidate of node.candidates) {
|
|
143
|
+
if (found) {
|
|
144
|
+
excluded.push(candidate.id);
|
|
145
|
+
continue;
|
|
185
146
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
* Wrap a group's rendered children according to the output format.
|
|
189
|
-
* Returns an array — in text mode the children are spread inline,
|
|
190
|
-
* in xml mode they are joined and wrapped in a single entry.
|
|
191
|
-
*/
|
|
192
|
-
formatGroup(id, childParts, format) {
|
|
193
|
-
switch (format) {
|
|
194
|
-
case "text":
|
|
195
|
-
return childParts;
|
|
196
|
-
case "xml":
|
|
197
|
-
return [`<${id}>
|
|
198
|
-
${childParts.join("\n\n")}
|
|
199
|
-
</${id}>`];
|
|
200
|
-
default:
|
|
201
|
-
return assertNever(format);
|
|
147
|
+
if (renderSection(candidate, ctx, format, parts, included, excluded)) {
|
|
148
|
+
found = true;
|
|
202
149
|
}
|
|
203
150
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
}
|
|
151
|
+
}
|
|
152
|
+
function renderNodes(nodes, ctx, format, included, excluded) {
|
|
153
|
+
const parts = [];
|
|
154
|
+
for (const node of nodes) {
|
|
155
|
+
if (isSection(node)) {
|
|
156
|
+
renderSection(node, ctx, format, parts, included, excluded);
|
|
157
|
+
} else if (isGroup(node)) {
|
|
158
|
+
const childParts = renderNodes(node.children, ctx, format, included, excluded);
|
|
159
|
+
if (childParts.length > 0) {
|
|
160
|
+
parts.push(...formatGroup(node.id, childParts, format));
|
|
215
161
|
}
|
|
162
|
+
} else {
|
|
163
|
+
renderOneOf(node, ctx, format, parts, included, excluded);
|
|
216
164
|
}
|
|
217
|
-
return false;
|
|
218
165
|
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
if (
|
|
231
|
-
result.push({ candidates: remaining });
|
|
232
|
-
}
|
|
166
|
+
return parts;
|
|
167
|
+
}
|
|
168
|
+
function hasNode(nodes, id) {
|
|
169
|
+
for (const node of nodes) {
|
|
170
|
+
if (isSection(node)) {
|
|
171
|
+
if (node.id === id) return true;
|
|
172
|
+
} else if (isGroup(node)) {
|
|
173
|
+
if (node.id === id) return true;
|
|
174
|
+
if (hasNode(node.children, id)) return true;
|
|
175
|
+
} else {
|
|
176
|
+
for (const c of node.candidates) {
|
|
177
|
+
if (c.id === id) return true;
|
|
233
178
|
}
|
|
234
179
|
}
|
|
235
|
-
return result;
|
|
236
180
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
function removeNode(nodes, id) {
|
|
184
|
+
const result = [];
|
|
185
|
+
for (const n of nodes) {
|
|
186
|
+
if (isSection(n)) {
|
|
187
|
+
if (n.id !== id) result.push(n);
|
|
188
|
+
} else if (isGroup(n)) {
|
|
189
|
+
if (n.id !== id) {
|
|
190
|
+
result.push({ ...n, children: removeNode(n.children, id) });
|
|
191
|
+
}
|
|
192
|
+
} else {
|
|
193
|
+
const remaining = n.candidates.filter((c) => c.id !== id);
|
|
194
|
+
if (remaining.length > 0) {
|
|
195
|
+
result.push({ candidates: remaining });
|
|
247
196
|
}
|
|
248
197
|
}
|
|
249
|
-
return ids;
|
|
250
198
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
199
|
+
return result;
|
|
200
|
+
}
|
|
201
|
+
function collectIds(nodes) {
|
|
202
|
+
const ids = [];
|
|
203
|
+
for (const node of nodes) {
|
|
204
|
+
if (isSection(node)) {
|
|
205
|
+
ids.push(node.id);
|
|
206
|
+
} else if (isGroup(node)) {
|
|
207
|
+
ids.push(node.id);
|
|
208
|
+
ids.push(...collectIds(node.children));
|
|
209
|
+
} else {
|
|
210
|
+
ids.push(...node.candidates.map((c) => c.id));
|
|
211
|
+
}
|
|
261
212
|
}
|
|
262
|
-
|
|
213
|
+
return ids;
|
|
214
|
+
}
|
|
215
|
+
function deepCopy(nodes) {
|
|
216
|
+
return nodes.map((n) => {
|
|
217
|
+
if (isGroup(n)) {
|
|
218
|
+
return { ...n, children: deepCopy(n.children) };
|
|
219
|
+
}
|
|
220
|
+
if (isOneOf(n)) {
|
|
221
|
+
return { candidates: [...n.candidates] };
|
|
222
|
+
}
|
|
223
|
+
return n;
|
|
224
|
+
});
|
|
225
|
+
}
|
|
263
226
|
|
|
264
227
|
// src/section.ts
|
|
265
228
|
function section(id, render) {
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/asurve/dev/teleprompt/dist/index.cjs","../src/builder.ts","../src/section.ts"],"names":["section"],"mappings":"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","file":"/Users/asurve/dev/teleprompt/dist/index.cjs","sourcesContent":[null,"import type { PromptContext, PromptSection } from './types';\n\nfunction assertNever(value: never): never {\n throw new Error(`Unexpected format: ${value}`);\n}\n\n/**\n * A named group of prompt nodes, rendered as an XML wrapper\n * in `xml` format and transparently in `text` format.\n */\ninterface PromptGroup<TCtx extends PromptContext> {\n id: string;\n children: PromptNode<TCtx>[];\n}\n\n/** Mutually exclusive sections — the first candidate that renders wins. */\ninterface PromptOneOf<TCtx extends PromptContext> {\n candidates: PromptSection<TCtx>[];\n}\n\ntype PromptNode<TCtx extends PromptContext> =\n | PromptSection<TCtx>\n | PromptGroup<TCtx>\n | PromptOneOf<TCtx>;\n\nfunction isSection<TCtx extends PromptContext>(\n node: PromptNode<TCtx>,\n): node is PromptSection<TCtx> {\n return 'render' in node;\n}\n\nfunction isGroup<TCtx extends PromptContext>(node: PromptNode<TCtx>): node is PromptGroup<TCtx> {\n return 'children' in node;\n}\n\nfunction isOneOf<TCtx extends PromptContext>(node: PromptNode<TCtx>): node is PromptOneOf<TCtx> {\n return 'candidates' in node;\n}\n\n/** Supported output formats for prompt rendering. */\nexport type PromptFormat = 'text' | 'xml';\n\n/** Options for {@link PromptBuilder.build} and {@link PromptBuilder.buildWithMeta}. */\nexport interface BuildOptions {\n /**\n * Output format.\n *\n * - `'text'` (default) — sections joined with `\\n\\n`, groups are transparent.\n * - `'xml'` — each section wrapped in `<id>...</id>` tags, groups\n * wrapped in `<id>...</id>` containing their children.\n */\n format?: PromptFormat;\n}\n\n/**\n * Declarative, composable prompt builder.\n *\n * Prompts are composed from discrete {@link PromptSection}s that are\n * independently testable pure functions. The builder handles ordering,\n * conditional inclusion, and final assembly.\n *\n * @example\n * ```ts\n * const prompt = new PromptBuilder()\n * .use(identitySection)\n * .use(rulesSection)\n * .use(featureSection)\n * .build(ctx);\n * ```\n */\nexport class PromptBuilder<TCtx extends PromptContext = PromptContext> {\n private nodes: PromptNode<TCtx>[] = [];\n\n /**\n * Add a section to the prompt.\n *\n * If a section with the same `id` already exists, it is replaced.\n * This makes `.use()` idempotent — you can safely call it multiple\n * times with the same section without creating duplicates.\n */\n use(section: PromptSection<TCtx>): this {\n const idx = this.nodes.findIndex((n) => !isOneOf(n) && n.id === section.id);\n if (idx >= 0) {\n this.nodes[idx] = section;\n } else {\n this.nodes.push(section);\n }\n return this;\n }\n\n /**\n * Add mutually exclusive sections. The first candidate that renders\n * a non-empty string wins — the rest are excluded.\n *\n * @example\n * ```ts\n * builder.useOneOf(activeTasks, noActiveTasks)\n * ```\n */\n useOneOf(...candidates: PromptSection<TCtx>[]): this {\n this.nodes.push({ candidates });\n return this;\n }\n\n /**\n * Add a named group of sections. In `xml` format, children are\n * wrapped in `<id>...</id>`. In `text` format, groups are transparent\n * and children render as if they were top-level sections.\n *\n * @example\n * ```ts\n * builder.group('tools', b => b\n * .use(bashSection)\n * .use(gitSection)\n * )\n * ```\n */\n group(id: string, configure: (builder: PromptBuilder<TCtx>) => void): this {\n const inner = new PromptBuilder<TCtx>();\n configure(inner);\n const group: PromptGroup<TCtx> = { id, children: inner.nodes };\n const idx = this.nodes.findIndex((n) => !isOneOf(n) && n.id === id);\n if (idx >= 0) {\n this.nodes[idx] = group;\n } else {\n this.nodes.push(group);\n }\n return this;\n }\n\n /** Remove a section or group. Accepts an id string or an object with `id`. Searches recursively into groups and oneOf candidates. */\n without(ref: string | { id: string }): this {\n const id = typeof ref === 'string' ? ref : ref.id;\n this.nodes = this.removeNode(this.nodes, id);\n return this;\n }\n\n /** Check if a section or group exists. Accepts an id string or an object with `id`. Searches recursively into groups and oneOf candidates. */\n has(ref: string | { id: string }): boolean {\n const id = typeof ref === 'string' ? ref : ref.id;\n return this.hasNode(this.nodes, id);\n }\n\n /** Get all ids (sections, groups, and oneOf candidates) in order. */\n ids(): string[] {\n return this.collectIds(this.nodes);\n }\n\n /**\n * Create an independent copy of this builder.\n *\n * Use this to create mode-specific or model-specific variants\n * without mutating the base builder.\n *\n * @example\n * ```ts\n * const base = new PromptBuilder().use(a).use(b);\n * const variant = base.fork().use(c); // base is unchanged\n * ```\n */\n fork(): PromptBuilder<TCtx> {\n const forked = new PromptBuilder<TCtx>();\n forked.nodes = this.deepCopy(this.nodes);\n return forked;\n }\n\n /**\n * Build the final prompt string.\n *\n * 1. Filters out sections whose `when` guard returns false\n * 2. Renders each section\n * 3. Filters out empty strings\n * 4. Joins with separator and trims\n *\n * Pass `{ format: 'xml' }` to wrap each section in `<id>` tags.\n */\n build(ctx: TCtx, options?: BuildOptions): string {\n return this.buildWithMeta(ctx, options).prompt;\n }\n\n /**\n * Build the prompt and return metadata about which sections were\n * included/excluded. Useful for debugging and logging.\n *\n * A section is \"excluded\" if its `when` guard returns false.\n * A section is \"included\" only if it passes the guard and renders\n * a non-empty string.\n */\n buildWithMeta(\n ctx: TCtx,\n options?: BuildOptions,\n ): {\n prompt: string;\n included: string[];\n excluded: string[];\n } {\n const included: string[] = [];\n const excluded: string[] = [];\n const format = options?.format ?? 'text';\n\n const parts = this.renderNodes(this.nodes, ctx, format, included, excluded);\n const prompt = parts.join('\\n\\n').trim();\n\n return { prompt, included, excluded };\n }\n\n // --- Private ---\n\n private renderNodes(\n nodes: PromptNode<TCtx>[],\n ctx: TCtx,\n format: PromptFormat,\n included: string[],\n excluded: string[],\n ): string[] {\n const parts: string[] = [];\n\n for (const node of nodes) {\n if (isSection(node)) {\n this.renderSection(node, ctx, format, parts, included, excluded);\n } else if (isGroup(node)) {\n const childParts = this.renderNodes(node.children, ctx, format, included, excluded);\n if (childParts.length > 0) {\n parts.push(...this.formatGroup(node.id, childParts, format));\n }\n } else {\n this.renderOneOf(node, ctx, format, parts, included, excluded);\n }\n }\n\n return parts;\n }\n\n private renderSection(\n section: PromptSection<TCtx>,\n ctx: TCtx,\n format: PromptFormat,\n parts: string[],\n included: string[],\n excluded: string[],\n ): boolean {\n if (section.when && !section.when(ctx)) {\n excluded.push(section.id);\n return false;\n }\n const output = section.render(ctx);\n if (output) {\n included.push(section.id);\n parts.push(this.formatSection(section.id, output, format));\n return true;\n }\n excluded.push(section.id);\n return false;\n }\n\n private renderOneOf(\n node: PromptOneOf<TCtx>,\n ctx: TCtx,\n format: PromptFormat,\n parts: string[],\n included: string[],\n excluded: string[],\n ): void {\n let found = false;\n for (const candidate of node.candidates) {\n if (found) {\n excluded.push(candidate.id);\n continue;\n }\n if (this.renderSection(candidate, ctx, format, parts, included, excluded)) {\n found = true;\n }\n }\n }\n\n /** Wrap a single section's rendered content according to the output format. */\n private formatSection(id: string, content: string, format: PromptFormat): string {\n switch (format) {\n case 'text':\n return content;\n case 'xml':\n return `<${id}>\\n${content}\\n</${id}>`;\n default:\n return assertNever(format);\n }\n }\n\n /**\n * Wrap a group's rendered children according to the output format.\n * Returns an array — in text mode the children are spread inline,\n * in xml mode they are joined and wrapped in a single entry.\n */\n private formatGroup(id: string, childParts: string[], format: PromptFormat): string[] {\n switch (format) {\n case 'text':\n return childParts;\n case 'xml':\n return [`<${id}>\\n${childParts.join('\\n\\n')}\\n</${id}>`];\n default:\n return assertNever(format);\n }\n }\n\n private hasNode(nodes: PromptNode<TCtx>[], id: string): boolean {\n for (const node of nodes) {\n if (isSection(node)) {\n if (node.id === id) return true;\n } else if (isGroup(node)) {\n if (node.id === id) return true;\n if (this.hasNode(node.children, id)) return true;\n } else {\n for (const c of node.candidates) {\n if (c.id === id) return true;\n }\n }\n }\n return false;\n }\n\n private removeNode(nodes: PromptNode<TCtx>[], id: string): PromptNode<TCtx>[] {\n const result: PromptNode<TCtx>[] = [];\n for (const n of nodes) {\n if (isSection(n)) {\n if (n.id !== id) result.push(n);\n } else if (isGroup(n)) {\n if (n.id !== id) {\n result.push({ ...n, children: this.removeNode(n.children, id) });\n }\n } else {\n const remaining = n.candidates.filter((c) => c.id !== id);\n if (remaining.length > 0) {\n result.push({ candidates: remaining });\n }\n }\n }\n return result;\n }\n\n private collectIds(nodes: PromptNode<TCtx>[]): string[] {\n const ids: string[] = [];\n for (const node of nodes) {\n if (isSection(node)) {\n ids.push(node.id);\n } else if (isGroup(node)) {\n ids.push(node.id);\n ids.push(...this.collectIds(node.children));\n } else {\n ids.push(...node.candidates.map((c) => c.id));\n }\n }\n return ids;\n }\n\n private deepCopy(nodes: PromptNode<TCtx>[]): PromptNode<TCtx>[] {\n return nodes.map((n) => {\n if (isGroup(n)) {\n return { ...n, children: this.deepCopy(n.children) };\n }\n if (isOneOf(n)) {\n return { candidates: [...n.candidates] };\n }\n return n;\n });\n }\n}\n","import type { PromptContext, PromptSection } from './types';\n\n/**\n * Create a prompt section. Return a string to include, `null` to exclude.\n *\n * @example\n * ```ts\n * // Static\n * section('identity', () => 'You are Coworker.')\n *\n * // Conditional — return null to exclude\n * section('prod', (ctx: SandboxCtx) => {\n * if (ctx.vars.prodContext == null) return null;\n * return `## Prod\\n\\n${ctx.vars.prodContext}`;\n * })\n * ```\n */\nexport function section<TCtx extends PromptContext = PromptContext>(\n id: string,\n render: (ctx: TCtx) => string | null,\n): PromptSection<TCtx> {\n return {\n id,\n render: (ctx) => render(ctx) ?? '',\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["/Users/asurve/dev/teleprompt/main/dist/index.cjs","../src/builder.ts","../src/section.ts"],"names":["section"],"mappings":"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","file":"/Users/asurve/dev/teleprompt/main/dist/index.cjs","sourcesContent":[null,"import type { PromptContext, PromptSection } from './types';\n\nexport type PromptFormat = 'text' | 'xml';\n\nexport interface BuildOptions {\n /**\n * - `'text'` (default) — sections joined with `\\n\\n`, groups are transparent.\n * - `'xml'` — each section wrapped in `<id>...</id>` tags, groups\n * wrapped in `<id>...</id>` containing their children.\n */\n format?: PromptFormat;\n}\n\n/**\n * @example\n * ```ts\n * const prompt = new PromptBuilder()\n * .use(identitySection)\n * .use(rulesSection)\n * .use(featureSection)\n * .build(ctx);\n * ```\n */\nexport class PromptBuilder<TCtx extends PromptContext = PromptContext> {\n private nodes: PromptNode<TCtx>[] = [];\n\n /** Replaces an existing section with the same id. */\n use(section: PromptSection<TCtx>): this {\n const idx = this.nodes.findIndex((n) => !isOneOf(n) && n.id === section.id);\n if (idx >= 0) {\n this.nodes[idx] = section;\n } else {\n this.nodes.push(section);\n }\n return this;\n }\n\n /**\n * First candidate that renders a non-empty string wins.\n *\n * @example\n * ```ts\n * builder.useOneOf(activeTasks, noActiveTasks)\n * ```\n */\n useOneOf(...candidates: PromptSection<TCtx>[]): this {\n this.nodes.push({ candidates });\n return this;\n }\n\n /**\n * In `xml` format, wraps children in `<id>` tags. Transparent in `text` format.\n *\n * @example\n * ```ts\n * builder.group('tools', b => b\n * .use(bashSection)\n * .use(gitSection)\n * )\n * ```\n */\n group(id: string, configure: (builder: PromptBuilder<TCtx>) => void): this {\n const inner = new PromptBuilder<TCtx>();\n configure(inner);\n const group: PromptGroup<TCtx> = { id, children: inner.nodes };\n const idx = this.nodes.findIndex((n) => !isOneOf(n) && n.id === id);\n if (idx >= 0) {\n this.nodes[idx] = group;\n } else {\n this.nodes.push(group);\n }\n return this;\n }\n\n /** Searches recursively into groups and oneOf candidates. */\n without(ref: string | { id: string }): this {\n const id = typeof ref === 'string' ? ref : ref.id;\n this.nodes = removeNode(this.nodes, id);\n return this;\n }\n\n /** Searches recursively into groups and oneOf candidates. */\n has(ref: string | { id: string }): boolean {\n const id = typeof ref === 'string' ? ref : ref.id;\n return hasNode(this.nodes, id);\n }\n\n ids(): string[] {\n return collectIds(this.nodes);\n }\n\n /**\n * Creates an independent copy. Modifications to the fork don't affect the original.\n *\n * @example\n * ```ts\n * const base = new PromptBuilder().use(a).use(b);\n * const variant = base.fork().use(c);\n * ```\n */\n fork(): PromptBuilder<TCtx> {\n const forked = new PromptBuilder<TCtx>();\n forked.nodes = deepCopy(this.nodes);\n return forked;\n }\n\n build(ctx: TCtx, options?: BuildOptions): string {\n return this.buildWithMeta(ctx, options).prompt;\n }\n\n /** Like `build`, but also returns which section ids were included/excluded. */\n buildWithMeta(\n ctx: TCtx,\n options?: BuildOptions,\n ): {\n prompt: string;\n included: string[];\n excluded: string[];\n } {\n const included: string[] = [];\n const excluded: string[] = [];\n const format = options?.format ?? 'text';\n\n const parts = renderNodes(this.nodes, ctx, format, included, excluded);\n const prompt = parts.join('\\n\\n').trim();\n\n return { prompt, included, excluded };\n }\n}\n\ninterface PromptGroup<TCtx extends PromptContext> {\n id: string;\n children: PromptNode<TCtx>[];\n}\n\ninterface PromptOneOf<TCtx extends PromptContext> {\n candidates: PromptSection<TCtx>[];\n}\n\ntype PromptNode<TCtx extends PromptContext> =\n | PromptSection<TCtx>\n | PromptGroup<TCtx>\n | PromptOneOf<TCtx>;\n\nfunction isSection<TCtx extends PromptContext>(\n node: PromptNode<TCtx>,\n): node is PromptSection<TCtx> {\n return 'render' in node;\n}\n\nfunction isGroup<TCtx extends PromptContext>(node: PromptNode<TCtx>): node is PromptGroup<TCtx> {\n return 'children' in node;\n}\n\nfunction isOneOf<TCtx extends PromptContext>(node: PromptNode<TCtx>): node is PromptOneOf<TCtx> {\n return 'candidates' in node;\n}\n\nfunction assertNever(value: never): never {\n throw new Error(`Unexpected format: ${value}`);\n}\n\nfunction formatSection(id: string, content: string, format: PromptFormat): string {\n switch (format) {\n case 'text':\n return content;\n case 'xml':\n return `<${id}>\\n${content}\\n</${id}>`;\n default:\n return assertNever(format);\n }\n}\n\nfunction formatGroup(id: string, childParts: string[], format: PromptFormat): string[] {\n switch (format) {\n case 'text':\n return childParts;\n case 'xml':\n return [`<${id}>\\n${childParts.join('\\n\\n')}\\n</${id}>`];\n default:\n return assertNever(format);\n }\n}\n\nfunction renderSection<TCtx extends PromptContext>(\n section: PromptSection<TCtx>,\n ctx: TCtx,\n format: PromptFormat,\n parts: string[],\n included: string[],\n excluded: string[],\n): boolean {\n if (section.when && !section.when(ctx)) {\n excluded.push(section.id);\n return false;\n }\n const output = section.render(ctx);\n if (output) {\n included.push(section.id);\n parts.push(formatSection(section.id, output, format));\n return true;\n }\n excluded.push(section.id);\n return false;\n}\n\nfunction renderOneOf<TCtx extends PromptContext>(\n node: PromptOneOf<TCtx>,\n ctx: TCtx,\n format: PromptFormat,\n parts: string[],\n included: string[],\n excluded: string[],\n): void {\n let found = false;\n for (const candidate of node.candidates) {\n if (found) {\n excluded.push(candidate.id);\n continue;\n }\n if (renderSection(candidate, ctx, format, parts, included, excluded)) {\n found = true;\n }\n }\n}\n\nfunction renderNodes<TCtx extends PromptContext>(\n nodes: PromptNode<TCtx>[],\n ctx: TCtx,\n format: PromptFormat,\n included: string[],\n excluded: string[],\n): string[] {\n const parts: string[] = [];\n\n for (const node of nodes) {\n if (isSection(node)) {\n renderSection(node, ctx, format, parts, included, excluded);\n } else if (isGroup(node)) {\n const childParts = renderNodes(node.children, ctx, format, included, excluded);\n if (childParts.length > 0) {\n parts.push(...formatGroup(node.id, childParts, format));\n }\n } else {\n renderOneOf(node, ctx, format, parts, included, excluded);\n }\n }\n\n return parts;\n}\n\nfunction hasNode<TCtx extends PromptContext>(nodes: PromptNode<TCtx>[], id: string): boolean {\n for (const node of nodes) {\n if (isSection(node)) {\n if (node.id === id) return true;\n } else if (isGroup(node)) {\n if (node.id === id) return true;\n if (hasNode(node.children, id)) return true;\n } else {\n for (const c of node.candidates) {\n if (c.id === id) return true;\n }\n }\n }\n return false;\n}\n\nfunction removeNode<TCtx extends PromptContext>(\n nodes: PromptNode<TCtx>[],\n id: string,\n): PromptNode<TCtx>[] {\n const result: PromptNode<TCtx>[] = [];\n for (const n of nodes) {\n if (isSection(n)) {\n if (n.id !== id) result.push(n);\n } else if (isGroup(n)) {\n if (n.id !== id) {\n result.push({ ...n, children: removeNode(n.children, id) });\n }\n } else {\n const remaining = n.candidates.filter((c) => c.id !== id);\n if (remaining.length > 0) {\n result.push({ candidates: remaining });\n }\n }\n }\n return result;\n}\n\nfunction collectIds<TCtx extends PromptContext>(nodes: PromptNode<TCtx>[]): string[] {\n const ids: string[] = [];\n for (const node of nodes) {\n if (isSection(node)) {\n ids.push(node.id);\n } else if (isGroup(node)) {\n ids.push(node.id);\n ids.push(...collectIds(node.children));\n } else {\n ids.push(...node.candidates.map((c) => c.id));\n }\n }\n return ids;\n}\n\nfunction deepCopy<TCtx extends PromptContext>(nodes: PromptNode<TCtx>[]): PromptNode<TCtx>[] {\n return nodes.map((n) => {\n if (isGroup(n)) {\n return { ...n, children: deepCopy(n.children) };\n }\n if (isOneOf(n)) {\n return { candidates: [...n.candidates] };\n }\n return n;\n });\n}\n","import type { PromptContext, PromptSection } from './types';\n\n/**\n * Create a prompt section. Return a string to include, `null` to exclude.\n *\n * @example\n * ```ts\n * section('identity', () => 'You are Coworker.')\n *\n * section('prod', (ctx: SandboxCtx) => {\n * if (ctx.vars.prodContext == null) return null;\n * return `## Prod\\n\\n${ctx.vars.prodContext}`;\n * })\n * ```\n */\nexport function section<TCtx extends PromptContext = PromptContext>(\n id: string,\n render: (ctx: TCtx) => string | null,\n): PromptSection<TCtx> {\n return {\n id,\n render: (ctx) => render(ctx) ?? '',\n };\n}\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-
import { P as PromptContext, a as PromptSection } from './types
|
|
1
|
+
import { P as PromptContext, a as PromptSection } from './types-ow-XbrO7.cjs';
|
|
2
2
|
|
|
3
|
-
/** Supported output formats for prompt rendering. */
|
|
4
3
|
type PromptFormat = 'text' | 'xml';
|
|
5
|
-
/** Options for {@link PromptBuilder.build} and {@link PromptBuilder.buildWithMeta}. */
|
|
6
4
|
interface BuildOptions {
|
|
7
5
|
/**
|
|
8
|
-
* Output format.
|
|
9
|
-
*
|
|
10
6
|
* - `'text'` (default) — sections joined with `\n\n`, groups are transparent.
|
|
11
7
|
* - `'xml'` — each section wrapped in `<id>...</id>` tags, groups
|
|
12
8
|
* wrapped in `<id>...</id>` containing their children.
|
|
@@ -14,12 +10,6 @@ interface BuildOptions {
|
|
|
14
10
|
format?: PromptFormat;
|
|
15
11
|
}
|
|
16
12
|
/**
|
|
17
|
-
* Declarative, composable prompt builder.
|
|
18
|
-
*
|
|
19
|
-
* Prompts are composed from discrete {@link PromptSection}s that are
|
|
20
|
-
* independently testable pure functions. The builder handles ordering,
|
|
21
|
-
* conditional inclusion, and final assembly.
|
|
22
|
-
*
|
|
23
13
|
* @example
|
|
24
14
|
* ```ts
|
|
25
15
|
* const prompt = new PromptBuilder()
|
|
@@ -31,17 +21,10 @@ interface BuildOptions {
|
|
|
31
21
|
*/
|
|
32
22
|
declare class PromptBuilder<TCtx extends PromptContext = PromptContext> {
|
|
33
23
|
private nodes;
|
|
34
|
-
/**
|
|
35
|
-
* Add a section to the prompt.
|
|
36
|
-
*
|
|
37
|
-
* If a section with the same `id` already exists, it is replaced.
|
|
38
|
-
* This makes `.use()` idempotent — you can safely call it multiple
|
|
39
|
-
* times with the same section without creating duplicates.
|
|
40
|
-
*/
|
|
24
|
+
/** Replaces an existing section with the same id. */
|
|
41
25
|
use(section: PromptSection<TCtx>): this;
|
|
42
26
|
/**
|
|
43
|
-
*
|
|
44
|
-
* a non-empty string wins — the rest are excluded.
|
|
27
|
+
* First candidate that renders a non-empty string wins.
|
|
45
28
|
*
|
|
46
29
|
* @example
|
|
47
30
|
* ```ts
|
|
@@ -50,9 +33,7 @@ declare class PromptBuilder<TCtx extends PromptContext = PromptContext> {
|
|
|
50
33
|
*/
|
|
51
34
|
useOneOf(...candidates: PromptSection<TCtx>[]): this;
|
|
52
35
|
/**
|
|
53
|
-
*
|
|
54
|
-
* wrapped in `<id>...</id>`. In `text` format, groups are transparent
|
|
55
|
-
* and children render as if they were top-level sections.
|
|
36
|
+
* In `xml` format, wraps children in `<id>` tags. Transparent in `text` format.
|
|
56
37
|
*
|
|
57
38
|
* @example
|
|
58
39
|
* ```ts
|
|
@@ -63,68 +44,32 @@ declare class PromptBuilder<TCtx extends PromptContext = PromptContext> {
|
|
|
63
44
|
* ```
|
|
64
45
|
*/
|
|
65
46
|
group(id: string, configure: (builder: PromptBuilder<TCtx>) => void): this;
|
|
66
|
-
/**
|
|
47
|
+
/** Searches recursively into groups and oneOf candidates. */
|
|
67
48
|
without(ref: string | {
|
|
68
49
|
id: string;
|
|
69
50
|
}): this;
|
|
70
|
-
/**
|
|
51
|
+
/** Searches recursively into groups and oneOf candidates. */
|
|
71
52
|
has(ref: string | {
|
|
72
53
|
id: string;
|
|
73
54
|
}): boolean;
|
|
74
|
-
/** Get all ids (sections, groups, and oneOf candidates) in order. */
|
|
75
55
|
ids(): string[];
|
|
76
56
|
/**
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
* Use this to create mode-specific or model-specific variants
|
|
80
|
-
* without mutating the base builder.
|
|
57
|
+
* Creates an independent copy. Modifications to the fork don't affect the original.
|
|
81
58
|
*
|
|
82
59
|
* @example
|
|
83
60
|
* ```ts
|
|
84
61
|
* const base = new PromptBuilder().use(a).use(b);
|
|
85
|
-
* const variant = base.fork().use(c);
|
|
62
|
+
* const variant = base.fork().use(c);
|
|
86
63
|
* ```
|
|
87
64
|
*/
|
|
88
65
|
fork(): PromptBuilder<TCtx>;
|
|
89
|
-
/**
|
|
90
|
-
* Build the final prompt string.
|
|
91
|
-
*
|
|
92
|
-
* 1. Filters out sections whose `when` guard returns false
|
|
93
|
-
* 2. Renders each section
|
|
94
|
-
* 3. Filters out empty strings
|
|
95
|
-
* 4. Joins with separator and trims
|
|
96
|
-
*
|
|
97
|
-
* Pass `{ format: 'xml' }` to wrap each section in `<id>` tags.
|
|
98
|
-
*/
|
|
99
66
|
build(ctx: TCtx, options?: BuildOptions): string;
|
|
100
|
-
/**
|
|
101
|
-
* Build the prompt and return metadata about which sections were
|
|
102
|
-
* included/excluded. Useful for debugging and logging.
|
|
103
|
-
*
|
|
104
|
-
* A section is "excluded" if its `when` guard returns false.
|
|
105
|
-
* A section is "included" only if it passes the guard and renders
|
|
106
|
-
* a non-empty string.
|
|
107
|
-
*/
|
|
67
|
+
/** Like `build`, but also returns which section ids were included/excluded. */
|
|
108
68
|
buildWithMeta(ctx: TCtx, options?: BuildOptions): {
|
|
109
69
|
prompt: string;
|
|
110
70
|
included: string[];
|
|
111
71
|
excluded: string[];
|
|
112
72
|
};
|
|
113
|
-
private renderNodes;
|
|
114
|
-
private renderSection;
|
|
115
|
-
private renderOneOf;
|
|
116
|
-
/** Wrap a single section's rendered content according to the output format. */
|
|
117
|
-
private formatSection;
|
|
118
|
-
/**
|
|
119
|
-
* Wrap a group's rendered children according to the output format.
|
|
120
|
-
* Returns an array — in text mode the children are spread inline,
|
|
121
|
-
* in xml mode they are joined and wrapped in a single entry.
|
|
122
|
-
*/
|
|
123
|
-
private formatGroup;
|
|
124
|
-
private hasNode;
|
|
125
|
-
private removeNode;
|
|
126
|
-
private collectIds;
|
|
127
|
-
private deepCopy;
|
|
128
73
|
}
|
|
129
74
|
|
|
130
75
|
/**
|
|
@@ -132,10 +77,8 @@ declare class PromptBuilder<TCtx extends PromptContext = PromptContext> {
|
|
|
132
77
|
*
|
|
133
78
|
* @example
|
|
134
79
|
* ```ts
|
|
135
|
-
* // Static
|
|
136
80
|
* section('identity', () => 'You are Coworker.')
|
|
137
81
|
*
|
|
138
|
-
* // Conditional — return null to exclude
|
|
139
82
|
* section('prod', (ctx: SandboxCtx) => {
|
|
140
83
|
* if (ctx.vars.prodContext == null) return null;
|
|
141
84
|
* return `## Prod\n\n${ctx.vars.prodContext}`;
|