@anythingai/teleprompt 0.1.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/LICENSE +21 -0
- package/README.md +157 -0
- package/dist/index.cjs +95 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +76 -0
- package/dist/index.d.ts +76 -0
- package/dist/index.js +95 -0
- package/dist/index.js.map +1 -0
- package/dist/testing.cjs +20 -0
- package/dist/testing.cjs.map +1 -0
- package/dist/testing.d.cts +26 -0
- package/dist/testing.d.ts +26 -0
- package/dist/testing.js +20 -0
- package/dist/testing.js.map +1 -0
- package/dist/types-DXgCksVT.d.cts +34 -0
- package/dist/types-DXgCksVT.d.ts +34 -0
- package/package.json +74 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Create Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# teleprompt
|
|
2
|
+
|
|
3
|
+
Compose LLM system prompts from discrete sections instead of monolithic template literals.
|
|
4
|
+
|
|
5
|
+
Conditional logic, variants, and prompt changes stay co-located with their content. Adding a new flag is one section in one file, not a boolean threaded through 15 function signatures.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @anythingai/teleprompt
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { PromptBuilder, type PromptContext, type PromptSection } from '@anythingai/teleprompt';
|
|
15
|
+
|
|
16
|
+
// Define your context shape
|
|
17
|
+
type MyFlags = { webSearchEnabled: boolean };
|
|
18
|
+
type MyVars = { assistantName: string };
|
|
19
|
+
type MyContext = PromptContext<MyFlags, MyVars>;
|
|
20
|
+
|
|
21
|
+
// Sections are objects with an id and a render function
|
|
22
|
+
const identity: PromptSection<MyContext> = {
|
|
23
|
+
id: 'identity',
|
|
24
|
+
render: (ctx) => `You are ${ctx.vars.assistantName}, a helpful AI assistant.`,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// This is a static section with no context dependencies
|
|
28
|
+
const guidelines: PromptSection<MyContext> = {
|
|
29
|
+
id: 'guidelines',
|
|
30
|
+
render: () => `# Guidelines
|
|
31
|
+
- Be concise and direct.
|
|
32
|
+
- Cite sources when making factual claims.
|
|
33
|
+
- Ask for clarification when a request is ambiguous.`,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// Conditional logic lives in the section, not threaded through function signatures
|
|
37
|
+
const webSearch: PromptSection<MyContext> = {
|
|
38
|
+
id: 'web-search',
|
|
39
|
+
when: (ctx) => ctx.flags.webSearchEnabled,
|
|
40
|
+
render: () => `You have access to web search. Use it when the user asks about
|
|
41
|
+
current events or information that may have changed after your training cutoff.`,
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// Compose and build
|
|
45
|
+
const prompt = new PromptBuilder<MyContext>()
|
|
46
|
+
.use(identity)
|
|
47
|
+
.use(guidelines)
|
|
48
|
+
.use(webSearch)
|
|
49
|
+
.build({
|
|
50
|
+
flags: { webSearchEnabled: true },
|
|
51
|
+
vars: { assistantName: 'Daniel' },
|
|
52
|
+
});
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Sections
|
|
56
|
+
|
|
57
|
+
A section has an `id`, a `render` function, and optionally a `when` guard:
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
const citation: PromptSection<MyContext> = {
|
|
61
|
+
id: 'citation',
|
|
62
|
+
when: (ctx) => ctx.flags.citationEnabled, // excluded when false
|
|
63
|
+
render: () => 'Always include citations with links when referencing external sources.',
|
|
64
|
+
};
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Sections render in the order you call `.use()`. To reorder, change the call order.
|
|
68
|
+
|
|
69
|
+
## Forking
|
|
70
|
+
|
|
71
|
+
Create variants without duplicating prompt code:
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
const base = new PromptBuilder<MyContext>()
|
|
75
|
+
.use(identity)
|
|
76
|
+
.use(guidelines)
|
|
77
|
+
.use(tone);
|
|
78
|
+
|
|
79
|
+
// Customer support agent — adds escalation rules
|
|
80
|
+
const supportAgent = base.fork()
|
|
81
|
+
.use(escalationPolicy)
|
|
82
|
+
.use(ticketFormat);
|
|
83
|
+
|
|
84
|
+
// Code assistant — swaps guidelines, drops tone
|
|
85
|
+
const codeAssistant = base.fork()
|
|
86
|
+
.without(guidelines)
|
|
87
|
+
.without(tone)
|
|
88
|
+
.use(codingGuidelines)
|
|
89
|
+
.use(outputFormat);
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Each fork is independent. Modifying one doesn't affect the others.
|
|
93
|
+
|
|
94
|
+
## Context
|
|
95
|
+
|
|
96
|
+
Sections receive a typed context with boolean flags and arbitrary variables:
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
type MyFlags = {
|
|
100
|
+
webSearchEnabled: boolean;
|
|
101
|
+
citationEnabled: boolean;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
type MyVars = {
|
|
105
|
+
assistantName: string;
|
|
106
|
+
language: string;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
type MyContext = PromptContext<MyFlags, MyVars>;
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
You build the context once and pass it to `.build(ctx)`. Every section receives the same object — no threading booleans through function signatures.
|
|
113
|
+
|
|
114
|
+
## Builder API
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
new PromptBuilder<MyContext>()
|
|
118
|
+
// append a section (replaces if same id exists)
|
|
119
|
+
.use(section)
|
|
120
|
+
|
|
121
|
+
// remove by section object or string id
|
|
122
|
+
.without(section)
|
|
123
|
+
|
|
124
|
+
// check existence by section object or string id
|
|
125
|
+
.has(section)
|
|
126
|
+
|
|
127
|
+
// list all section ids
|
|
128
|
+
.ids()
|
|
129
|
+
|
|
130
|
+
// independent copy
|
|
131
|
+
.fork()
|
|
132
|
+
|
|
133
|
+
// render to string
|
|
134
|
+
.build(ctx)
|
|
135
|
+
|
|
136
|
+
// render + debug info: { included: string[], excluded: string[] }
|
|
137
|
+
.buildWithMeta(ctx)
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Testing
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
import { mockContext, renderSection } from '@anythingai/teleprompt/testing';
|
|
144
|
+
|
|
145
|
+
// Render a section in isolation
|
|
146
|
+
const output = renderSection(webSearch, { flags: { webSearchEnabled: true } });
|
|
147
|
+
expect(output).toContain('web search');
|
|
148
|
+
|
|
149
|
+
// Assert on prompt structure
|
|
150
|
+
const { included, excluded } = builder.buildWithMeta(ctx);
|
|
151
|
+
expect(included).toContain('web-search');
|
|
152
|
+
expect(excluded).toContain('citation');
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## License
|
|
156
|
+
|
|
157
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); var _class;// src/builder.ts
|
|
2
|
+
var PromptBuilder = (_class = class _PromptBuilder {constructor() { _class.prototype.__init.call(this); }
|
|
3
|
+
__init() {this.sections = []}
|
|
4
|
+
/**
|
|
5
|
+
* Add a section to the prompt.
|
|
6
|
+
*
|
|
7
|
+
* If a section with the same `id` already exists, it is replaced.
|
|
8
|
+
* This makes `.use()` idempotent — you can safely call it multiple
|
|
9
|
+
* times with the same section without creating duplicates.
|
|
10
|
+
*/
|
|
11
|
+
use(section) {
|
|
12
|
+
const existingIdx = this.sections.findIndex((s) => s.id === section.id);
|
|
13
|
+
if (existingIdx >= 0) {
|
|
14
|
+
this.sections[existingIdx] = section;
|
|
15
|
+
} else {
|
|
16
|
+
this.sections.push(section);
|
|
17
|
+
}
|
|
18
|
+
return this;
|
|
19
|
+
}
|
|
20
|
+
/** Remove a section. Accepts an id string or a section object. */
|
|
21
|
+
without(ref) {
|
|
22
|
+
const id = typeof ref === "string" ? ref : ref.id;
|
|
23
|
+
this.sections = this.sections.filter((s) => s.id !== id);
|
|
24
|
+
return this;
|
|
25
|
+
}
|
|
26
|
+
/** Check if a section exists. Accepts an id string or a section object. */
|
|
27
|
+
has(ref) {
|
|
28
|
+
const id = typeof ref === "string" ? ref : ref.id;
|
|
29
|
+
return this.sections.some((s) => s.id === id);
|
|
30
|
+
}
|
|
31
|
+
/** Get the ids of all registered sections (in insertion order). */
|
|
32
|
+
ids() {
|
|
33
|
+
return this.sections.map((s) => s.id);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Create an independent copy of this builder.
|
|
37
|
+
*
|
|
38
|
+
* Use this to create mode-specific or model-specific variants
|
|
39
|
+
* without mutating the base builder.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```ts
|
|
43
|
+
* const base = new PromptBuilder().use(a).use(b);
|
|
44
|
+
* const variant = base.fork().use(c); // base is unchanged
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
fork() {
|
|
48
|
+
const forked = new _PromptBuilder();
|
|
49
|
+
forked.sections = [...this.sections];
|
|
50
|
+
return forked;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Build the final prompt string.
|
|
54
|
+
*
|
|
55
|
+
* 1. Filters out sections whose `when` guard returns false
|
|
56
|
+
* 2. Renders each section
|
|
57
|
+
* 3. Filters out empty strings
|
|
58
|
+
* 4. Joins with separator and trims
|
|
59
|
+
*/
|
|
60
|
+
build(ctx) {
|
|
61
|
+
return this.buildWithMeta(ctx).prompt;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Build the prompt and return metadata about which sections were
|
|
65
|
+
* included/excluded. Useful for debugging and logging.
|
|
66
|
+
*
|
|
67
|
+
* A section is "excluded" if its `when` guard returns false.
|
|
68
|
+
* A section is "included" only if it passes the guard and renders
|
|
69
|
+
* a non-empty string.
|
|
70
|
+
*/
|
|
71
|
+
buildWithMeta(ctx) {
|
|
72
|
+
const included = [];
|
|
73
|
+
const excluded = [];
|
|
74
|
+
const rendered = this.sections.filter((s) => {
|
|
75
|
+
if (s.when && !s.when(ctx)) {
|
|
76
|
+
excluded.push(s.id);
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
return true;
|
|
80
|
+
}).map((s) => {
|
|
81
|
+
const output = s.render(ctx);
|
|
82
|
+
if (output) {
|
|
83
|
+
included.push(s.id);
|
|
84
|
+
} else {
|
|
85
|
+
excluded.push(s.id);
|
|
86
|
+
}
|
|
87
|
+
return output;
|
|
88
|
+
}).filter(Boolean).join("\n\n").trim();
|
|
89
|
+
return { prompt: rendered, included, excluded };
|
|
90
|
+
}
|
|
91
|
+
}, _class);
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
exports.PromptBuilder = PromptBuilder;
|
|
95
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/asurve/dev/teleprompt/dist/index.cjs","../src/builder.ts"],"names":[],"mappings":"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","file":"/Users/asurve/dev/teleprompt/dist/index.cjs","sourcesContent":[null,"import type { PromptContext, PromptSection } from './types';\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<\n TCtx extends PromptContext<Record<string, boolean>, Record<string, unknown>>,\n> {\n private sections: PromptSection<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 existingIdx = this.sections.findIndex((s) => s.id === section.id);\n if (existingIdx >= 0) {\n this.sections[existingIdx] = section;\n } else {\n this.sections.push(section);\n }\n return this;\n }\n\n /** Remove a section. Accepts an id string or a section object. */\n without(ref: string | { id: string }): this {\n const id = typeof ref === 'string' ? ref : ref.id;\n this.sections = this.sections.filter((s) => s.id !== id);\n return this;\n }\n\n /** Check if a section exists. Accepts an id string or a section object. */\n has(ref: string | { id: string }): boolean {\n const id = typeof ref === 'string' ? ref : ref.id;\n return this.sections.some((s) => s.id === id);\n }\n\n /** Get the ids of all registered sections (in insertion order). */\n ids(): string[] {\n return this.sections.map((s) => s.id);\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.sections = [...this.sections];\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 build(ctx: TCtx): string {\n return this.buildWithMeta(ctx).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(ctx: TCtx): {\n prompt: string;\n included: string[];\n excluded: string[];\n } {\n const included: string[] = [];\n const excluded: string[] = [];\n\n const rendered = this.sections\n .filter((s) => {\n if (s.when && !s.when(ctx)) {\n excluded.push(s.id);\n return false;\n }\n return true;\n })\n .map((s) => {\n const output = s.render(ctx);\n if (output) {\n included.push(s.id);\n } else {\n excluded.push(s.id);\n }\n return output;\n })\n .filter(Boolean)\n .join('\\n\\n')\n .trim();\n\n return { prompt: rendered, included, excluded };\n }\n}\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { P as PromptContext, a as PromptSection } from './types-DXgCksVT.cjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Declarative, composable prompt builder.
|
|
5
|
+
*
|
|
6
|
+
* Prompts are composed from discrete {@link PromptSection}s that are
|
|
7
|
+
* independently testable pure functions. The builder handles ordering,
|
|
8
|
+
* conditional inclusion, and final assembly.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* const prompt = new PromptBuilder()
|
|
13
|
+
* .use(identitySection)
|
|
14
|
+
* .use(rulesSection)
|
|
15
|
+
* .use(featureSection)
|
|
16
|
+
* .build(ctx);
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
declare class PromptBuilder<TCtx extends PromptContext<Record<string, boolean>, Record<string, unknown>>> {
|
|
20
|
+
private sections;
|
|
21
|
+
/**
|
|
22
|
+
* Add a section to the prompt.
|
|
23
|
+
*
|
|
24
|
+
* If a section with the same `id` already exists, it is replaced.
|
|
25
|
+
* This makes `.use()` idempotent — you can safely call it multiple
|
|
26
|
+
* times with the same section without creating duplicates.
|
|
27
|
+
*/
|
|
28
|
+
use(section: PromptSection<TCtx>): this;
|
|
29
|
+
/** Remove a section. Accepts an id string or a section object. */
|
|
30
|
+
without(ref: string | {
|
|
31
|
+
id: string;
|
|
32
|
+
}): this;
|
|
33
|
+
/** Check if a section exists. Accepts an id string or a section object. */
|
|
34
|
+
has(ref: string | {
|
|
35
|
+
id: string;
|
|
36
|
+
}): boolean;
|
|
37
|
+
/** Get the ids of all registered sections (in insertion order). */
|
|
38
|
+
ids(): string[];
|
|
39
|
+
/**
|
|
40
|
+
* Create an independent copy of this builder.
|
|
41
|
+
*
|
|
42
|
+
* Use this to create mode-specific or model-specific variants
|
|
43
|
+
* without mutating the base builder.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```ts
|
|
47
|
+
* const base = new PromptBuilder().use(a).use(b);
|
|
48
|
+
* const variant = base.fork().use(c); // base is unchanged
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
fork(): PromptBuilder<TCtx>;
|
|
52
|
+
/**
|
|
53
|
+
* Build the final prompt string.
|
|
54
|
+
*
|
|
55
|
+
* 1. Filters out sections whose `when` guard returns false
|
|
56
|
+
* 2. Renders each section
|
|
57
|
+
* 3. Filters out empty strings
|
|
58
|
+
* 4. Joins with separator and trims
|
|
59
|
+
*/
|
|
60
|
+
build(ctx: TCtx): string;
|
|
61
|
+
/**
|
|
62
|
+
* Build the prompt and return metadata about which sections were
|
|
63
|
+
* included/excluded. Useful for debugging and logging.
|
|
64
|
+
*
|
|
65
|
+
* A section is "excluded" if its `when` guard returns false.
|
|
66
|
+
* A section is "included" only if it passes the guard and renders
|
|
67
|
+
* a non-empty string.
|
|
68
|
+
*/
|
|
69
|
+
buildWithMeta(ctx: TCtx): {
|
|
70
|
+
prompt: string;
|
|
71
|
+
included: string[];
|
|
72
|
+
excluded: string[];
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export { PromptBuilder, PromptContext, PromptSection };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { P as PromptContext, a as PromptSection } from './types-DXgCksVT.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Declarative, composable prompt builder.
|
|
5
|
+
*
|
|
6
|
+
* Prompts are composed from discrete {@link PromptSection}s that are
|
|
7
|
+
* independently testable pure functions. The builder handles ordering,
|
|
8
|
+
* conditional inclusion, and final assembly.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* const prompt = new PromptBuilder()
|
|
13
|
+
* .use(identitySection)
|
|
14
|
+
* .use(rulesSection)
|
|
15
|
+
* .use(featureSection)
|
|
16
|
+
* .build(ctx);
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
declare class PromptBuilder<TCtx extends PromptContext<Record<string, boolean>, Record<string, unknown>>> {
|
|
20
|
+
private sections;
|
|
21
|
+
/**
|
|
22
|
+
* Add a section to the prompt.
|
|
23
|
+
*
|
|
24
|
+
* If a section with the same `id` already exists, it is replaced.
|
|
25
|
+
* This makes `.use()` idempotent — you can safely call it multiple
|
|
26
|
+
* times with the same section without creating duplicates.
|
|
27
|
+
*/
|
|
28
|
+
use(section: PromptSection<TCtx>): this;
|
|
29
|
+
/** Remove a section. Accepts an id string or a section object. */
|
|
30
|
+
without(ref: string | {
|
|
31
|
+
id: string;
|
|
32
|
+
}): this;
|
|
33
|
+
/** Check if a section exists. Accepts an id string or a section object. */
|
|
34
|
+
has(ref: string | {
|
|
35
|
+
id: string;
|
|
36
|
+
}): boolean;
|
|
37
|
+
/** Get the ids of all registered sections (in insertion order). */
|
|
38
|
+
ids(): string[];
|
|
39
|
+
/**
|
|
40
|
+
* Create an independent copy of this builder.
|
|
41
|
+
*
|
|
42
|
+
* Use this to create mode-specific or model-specific variants
|
|
43
|
+
* without mutating the base builder.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```ts
|
|
47
|
+
* const base = new PromptBuilder().use(a).use(b);
|
|
48
|
+
* const variant = base.fork().use(c); // base is unchanged
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
fork(): PromptBuilder<TCtx>;
|
|
52
|
+
/**
|
|
53
|
+
* Build the final prompt string.
|
|
54
|
+
*
|
|
55
|
+
* 1. Filters out sections whose `when` guard returns false
|
|
56
|
+
* 2. Renders each section
|
|
57
|
+
* 3. Filters out empty strings
|
|
58
|
+
* 4. Joins with separator and trims
|
|
59
|
+
*/
|
|
60
|
+
build(ctx: TCtx): string;
|
|
61
|
+
/**
|
|
62
|
+
* Build the prompt and return metadata about which sections were
|
|
63
|
+
* included/excluded. Useful for debugging and logging.
|
|
64
|
+
*
|
|
65
|
+
* A section is "excluded" if its `when` guard returns false.
|
|
66
|
+
* A section is "included" only if it passes the guard and renders
|
|
67
|
+
* a non-empty string.
|
|
68
|
+
*/
|
|
69
|
+
buildWithMeta(ctx: TCtx): {
|
|
70
|
+
prompt: string;
|
|
71
|
+
included: string[];
|
|
72
|
+
excluded: string[];
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export { PromptBuilder, PromptContext, PromptSection };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// src/builder.ts
|
|
2
|
+
var PromptBuilder = class _PromptBuilder {
|
|
3
|
+
sections = [];
|
|
4
|
+
/**
|
|
5
|
+
* Add a section to the prompt.
|
|
6
|
+
*
|
|
7
|
+
* If a section with the same `id` already exists, it is replaced.
|
|
8
|
+
* This makes `.use()` idempotent — you can safely call it multiple
|
|
9
|
+
* times with the same section without creating duplicates.
|
|
10
|
+
*/
|
|
11
|
+
use(section) {
|
|
12
|
+
const existingIdx = this.sections.findIndex((s) => s.id === section.id);
|
|
13
|
+
if (existingIdx >= 0) {
|
|
14
|
+
this.sections[existingIdx] = section;
|
|
15
|
+
} else {
|
|
16
|
+
this.sections.push(section);
|
|
17
|
+
}
|
|
18
|
+
return this;
|
|
19
|
+
}
|
|
20
|
+
/** Remove a section. Accepts an id string or a section object. */
|
|
21
|
+
without(ref) {
|
|
22
|
+
const id = typeof ref === "string" ? ref : ref.id;
|
|
23
|
+
this.sections = this.sections.filter((s) => s.id !== id);
|
|
24
|
+
return this;
|
|
25
|
+
}
|
|
26
|
+
/** Check if a section exists. Accepts an id string or a section object. */
|
|
27
|
+
has(ref) {
|
|
28
|
+
const id = typeof ref === "string" ? ref : ref.id;
|
|
29
|
+
return this.sections.some((s) => s.id === id);
|
|
30
|
+
}
|
|
31
|
+
/** Get the ids of all registered sections (in insertion order). */
|
|
32
|
+
ids() {
|
|
33
|
+
return this.sections.map((s) => s.id);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Create an independent copy of this builder.
|
|
37
|
+
*
|
|
38
|
+
* Use this to create mode-specific or model-specific variants
|
|
39
|
+
* without mutating the base builder.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```ts
|
|
43
|
+
* const base = new PromptBuilder().use(a).use(b);
|
|
44
|
+
* const variant = base.fork().use(c); // base is unchanged
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
fork() {
|
|
48
|
+
const forked = new _PromptBuilder();
|
|
49
|
+
forked.sections = [...this.sections];
|
|
50
|
+
return forked;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Build the final prompt string.
|
|
54
|
+
*
|
|
55
|
+
* 1. Filters out sections whose `when` guard returns false
|
|
56
|
+
* 2. Renders each section
|
|
57
|
+
* 3. Filters out empty strings
|
|
58
|
+
* 4. Joins with separator and trims
|
|
59
|
+
*/
|
|
60
|
+
build(ctx) {
|
|
61
|
+
return this.buildWithMeta(ctx).prompt;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Build the prompt and return metadata about which sections were
|
|
65
|
+
* included/excluded. Useful for debugging and logging.
|
|
66
|
+
*
|
|
67
|
+
* A section is "excluded" if its `when` guard returns false.
|
|
68
|
+
* A section is "included" only if it passes the guard and renders
|
|
69
|
+
* a non-empty string.
|
|
70
|
+
*/
|
|
71
|
+
buildWithMeta(ctx) {
|
|
72
|
+
const included = [];
|
|
73
|
+
const excluded = [];
|
|
74
|
+
const rendered = this.sections.filter((s) => {
|
|
75
|
+
if (s.when && !s.when(ctx)) {
|
|
76
|
+
excluded.push(s.id);
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
return true;
|
|
80
|
+
}).map((s) => {
|
|
81
|
+
const output = s.render(ctx);
|
|
82
|
+
if (output) {
|
|
83
|
+
included.push(s.id);
|
|
84
|
+
} else {
|
|
85
|
+
excluded.push(s.id);
|
|
86
|
+
}
|
|
87
|
+
return output;
|
|
88
|
+
}).filter(Boolean).join("\n\n").trim();
|
|
89
|
+
return { prompt: rendered, included, excluded };
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
export {
|
|
93
|
+
PromptBuilder
|
|
94
|
+
};
|
|
95
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/builder.ts"],"sourcesContent":["import type { PromptContext, PromptSection } from './types';\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<\n TCtx extends PromptContext<Record<string, boolean>, Record<string, unknown>>,\n> {\n private sections: PromptSection<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 existingIdx = this.sections.findIndex((s) => s.id === section.id);\n if (existingIdx >= 0) {\n this.sections[existingIdx] = section;\n } else {\n this.sections.push(section);\n }\n return this;\n }\n\n /** Remove a section. Accepts an id string or a section object. */\n without(ref: string | { id: string }): this {\n const id = typeof ref === 'string' ? ref : ref.id;\n this.sections = this.sections.filter((s) => s.id !== id);\n return this;\n }\n\n /** Check if a section exists. Accepts an id string or a section object. */\n has(ref: string | { id: string }): boolean {\n const id = typeof ref === 'string' ? ref : ref.id;\n return this.sections.some((s) => s.id === id);\n }\n\n /** Get the ids of all registered sections (in insertion order). */\n ids(): string[] {\n return this.sections.map((s) => s.id);\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.sections = [...this.sections];\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 build(ctx: TCtx): string {\n return this.buildWithMeta(ctx).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(ctx: TCtx): {\n prompt: string;\n included: string[];\n excluded: string[];\n } {\n const included: string[] = [];\n const excluded: string[] = [];\n\n const rendered = this.sections\n .filter((s) => {\n if (s.when && !s.when(ctx)) {\n excluded.push(s.id);\n return false;\n }\n return true;\n })\n .map((s) => {\n const output = s.render(ctx);\n if (output) {\n included.push(s.id);\n } else {\n excluded.push(s.id);\n }\n return output;\n })\n .filter(Boolean)\n .join('\\n\\n')\n .trim();\n\n return { prompt: rendered, included, excluded };\n }\n}\n"],"mappings":";AAkBO,IAAM,gBAAN,MAAM,eAEX;AAAA,EACQ,WAAkC,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAS3C,IAAI,SAAoC;AACtC,UAAM,cAAc,KAAK,SAAS,UAAU,CAAC,MAAM,EAAE,OAAO,QAAQ,EAAE;AACtE,QAAI,eAAe,GAAG;AACpB,WAAK,SAAS,WAAW,IAAI;AAAA,IAC/B,OAAO;AACL,WAAK,SAAS,KAAK,OAAO;AAAA,IAC5B;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,QAAQ,KAAoC;AAC1C,UAAM,KAAK,OAAO,QAAQ,WAAW,MAAM,IAAI;AAC/C,SAAK,WAAW,KAAK,SAAS,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE;AACvD,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,IAAI,KAAuC;AACzC,UAAM,KAAK,OAAO,QAAQ,WAAW,MAAM,IAAI;AAC/C,WAAO,KAAK,SAAS,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE;AAAA,EAC9C;AAAA;AAAA,EAGA,MAAgB;AACd,WAAO,KAAK,SAAS,IAAI,CAAC,MAAM,EAAE,EAAE;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,OAA4B;AAC1B,UAAM,SAAS,IAAI,eAAoB;AACvC,WAAO,WAAW,CAAC,GAAG,KAAK,QAAQ;AACnC,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,KAAmB;AACvB,WAAO,KAAK,cAAc,GAAG,EAAE;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,cAAc,KAIZ;AACA,UAAM,WAAqB,CAAC;AAC5B,UAAM,WAAqB,CAAC;AAE5B,UAAM,WAAW,KAAK,SACnB,OAAO,CAAC,MAAM;AACb,UAAI,EAAE,QAAQ,CAAC,EAAE,KAAK,GAAG,GAAG;AAC1B,iBAAS,KAAK,EAAE,EAAE;AAClB,eAAO;AAAA,MACT;AACA,aAAO;AAAA,IACT,CAAC,EACA,IAAI,CAAC,MAAM;AACV,YAAM,SAAS,EAAE,OAAO,GAAG;AAC3B,UAAI,QAAQ;AACV,iBAAS,KAAK,EAAE,EAAE;AAAA,MACpB,OAAO;AACL,iBAAS,KAAK,EAAE,EAAE;AAAA,MACpB;AACA,aAAO;AAAA,IACT,CAAC,EACA,OAAO,OAAO,EACd,KAAK,MAAM,EACX,KAAK;AAER,WAAO,EAAE,QAAQ,UAAU,UAAU,SAAS;AAAA,EAChD;AACF;","names":[]}
|
package/dist/testing.cjs
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true});// src/testing.ts
|
|
2
|
+
function mockContext(overrides) {
|
|
3
|
+
return {
|
|
4
|
+
flags: {},
|
|
5
|
+
vars: {},
|
|
6
|
+
...overrides
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
function renderSection(section, contextOverrides) {
|
|
10
|
+
const ctx = mockContext(contextOverrides);
|
|
11
|
+
if (section.when && !section.when(ctx)) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
return section.render(ctx);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
exports.mockContext = mockContext; exports.renderSection = renderSection;
|
|
20
|
+
//# sourceMappingURL=testing.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/asurve/dev/teleprompt/dist/testing.cjs","../src/testing.ts"],"names":[],"mappings":"AAAA;ACYO,SAAS,WAAA,CAGd,SAAA,EAAiF;AACjF,EAAA,OAAO;AAAA,IACL,KAAA,EAAO,CAAC,CAAA;AAAA,IACR,IAAA,EAAM,CAAC,CAAA;AAAA,IACP,GAAG;AAAA,EACL,CAAA;AACF;AAYO,SAAS,aAAA,CAEd,OAAA,EAA8B,gBAAA,EAAiD;AAC/E,EAAA,MAAM,IAAA,EAAM,WAAA,CAAY,gBAAgB,CAAA;AACxC,EAAA,GAAA,CAAI,OAAA,CAAQ,KAAA,GAAQ,CAAC,OAAA,CAAQ,IAAA,CAAK,GAAG,CAAA,EAAG;AACtC,IAAA,OAAO,IAAA;AAAA,EACT;AACA,EAAA,OAAO,OAAA,CAAQ,MAAA,CAAO,GAAG,CAAA;AAC3B;AD1BA;AACE;AACA;AACF,yEAAC","file":"/Users/asurve/dev/teleprompt/dist/testing.cjs","sourcesContent":[null,"import type { PromptContext, PromptSection } from './types';\n\n/**\n * Create a minimal PromptContext for testing.\n * All fields have sensible defaults that can be overridden.\n *\n * @example\n * ```ts\n * const ctx = mockContext({ flags: { myFlag: true } });\n * const output = mySection.render(ctx);\n * ```\n */\nexport function mockContext<\n TFlags extends Record<string, boolean> = Record<string, boolean>,\n TVars extends Record<string, unknown> = Record<string, unknown>,\n>(overrides?: Partial<PromptContext<TFlags, TVars>>): PromptContext<TFlags, TVars> {\n return {\n flags: {} as TFlags,\n vars: {} as TVars,\n ...overrides,\n };\n}\n\n/**\n * Render a single section in isolation with a mock context.\n * Respects the section's `when` guard — returns `null` if excluded.\n *\n * @example\n * ```ts\n * const output = renderSection(mySection, { flags: { enabled: true } });\n * expect(output).toContain('expected text');\n * ```\n */\nexport function renderSection<\n TCtx extends PromptContext<Record<string, boolean>, Record<string, unknown>>,\n>(section: PromptSection<TCtx>, contextOverrides?: Partial<TCtx>): string | null {\n const ctx = mockContext(contextOverrides) as TCtx;\n if (section.when && !section.when(ctx)) {\n return null;\n }\n return section.render(ctx);\n}\n"]}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { P as PromptContext, a as PromptSection } from './types-DXgCksVT.cjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Create a minimal PromptContext for testing.
|
|
5
|
+
* All fields have sensible defaults that can be overridden.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* const ctx = mockContext({ flags: { myFlag: true } });
|
|
10
|
+
* const output = mySection.render(ctx);
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
declare function mockContext<TFlags extends Record<string, boolean> = Record<string, boolean>, TVars extends Record<string, unknown> = Record<string, unknown>>(overrides?: Partial<PromptContext<TFlags, TVars>>): PromptContext<TFlags, TVars>;
|
|
14
|
+
/**
|
|
15
|
+
* Render a single section in isolation with a mock context.
|
|
16
|
+
* Respects the section's `when` guard — returns `null` if excluded.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* const output = renderSection(mySection, { flags: { enabled: true } });
|
|
21
|
+
* expect(output).toContain('expected text');
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
declare function renderSection<TCtx extends PromptContext<Record<string, boolean>, Record<string, unknown>>>(section: PromptSection<TCtx>, contextOverrides?: Partial<TCtx>): string | null;
|
|
25
|
+
|
|
26
|
+
export { mockContext, renderSection };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { P as PromptContext, a as PromptSection } from './types-DXgCksVT.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Create a minimal PromptContext for testing.
|
|
5
|
+
* All fields have sensible defaults that can be overridden.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* const ctx = mockContext({ flags: { myFlag: true } });
|
|
10
|
+
* const output = mySection.render(ctx);
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
declare function mockContext<TFlags extends Record<string, boolean> = Record<string, boolean>, TVars extends Record<string, unknown> = Record<string, unknown>>(overrides?: Partial<PromptContext<TFlags, TVars>>): PromptContext<TFlags, TVars>;
|
|
14
|
+
/**
|
|
15
|
+
* Render a single section in isolation with a mock context.
|
|
16
|
+
* Respects the section's `when` guard — returns `null` if excluded.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* const output = renderSection(mySection, { flags: { enabled: true } });
|
|
21
|
+
* expect(output).toContain('expected text');
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
declare function renderSection<TCtx extends PromptContext<Record<string, boolean>, Record<string, unknown>>>(section: PromptSection<TCtx>, contextOverrides?: Partial<TCtx>): string | null;
|
|
25
|
+
|
|
26
|
+
export { mockContext, renderSection };
|
package/dist/testing.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// src/testing.ts
|
|
2
|
+
function mockContext(overrides) {
|
|
3
|
+
return {
|
|
4
|
+
flags: {},
|
|
5
|
+
vars: {},
|
|
6
|
+
...overrides
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
function renderSection(section, contextOverrides) {
|
|
10
|
+
const ctx = mockContext(contextOverrides);
|
|
11
|
+
if (section.when && !section.when(ctx)) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
return section.render(ctx);
|
|
15
|
+
}
|
|
16
|
+
export {
|
|
17
|
+
mockContext,
|
|
18
|
+
renderSection
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=testing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/testing.ts"],"sourcesContent":["import type { PromptContext, PromptSection } from './types';\n\n/**\n * Create a minimal PromptContext for testing.\n * All fields have sensible defaults that can be overridden.\n *\n * @example\n * ```ts\n * const ctx = mockContext({ flags: { myFlag: true } });\n * const output = mySection.render(ctx);\n * ```\n */\nexport function mockContext<\n TFlags extends Record<string, boolean> = Record<string, boolean>,\n TVars extends Record<string, unknown> = Record<string, unknown>,\n>(overrides?: Partial<PromptContext<TFlags, TVars>>): PromptContext<TFlags, TVars> {\n return {\n flags: {} as TFlags,\n vars: {} as TVars,\n ...overrides,\n };\n}\n\n/**\n * Render a single section in isolation with a mock context.\n * Respects the section's `when` guard — returns `null` if excluded.\n *\n * @example\n * ```ts\n * const output = renderSection(mySection, { flags: { enabled: true } });\n * expect(output).toContain('expected text');\n * ```\n */\nexport function renderSection<\n TCtx extends PromptContext<Record<string, boolean>, Record<string, unknown>>,\n>(section: PromptSection<TCtx>, contextOverrides?: Partial<TCtx>): string | null {\n const ctx = mockContext(contextOverrides) as TCtx;\n if (section.when && !section.when(ctx)) {\n return null;\n }\n return section.render(ctx);\n}\n"],"mappings":";AAYO,SAAS,YAGd,WAAiF;AACjF,SAAO;AAAA,IACL,OAAO,CAAC;AAAA,IACR,MAAM,CAAC;AAAA,IACP,GAAG;AAAA,EACL;AACF;AAYO,SAAS,cAEd,SAA8B,kBAAiD;AAC/E,QAAM,MAAM,YAAY,gBAAgB;AACxC,MAAI,QAAQ,QAAQ,CAAC,QAAQ,KAAK,GAAG,GAAG;AACtC,WAAO;AAAA,EACT;AACA,SAAO,QAAQ,OAAO,GAAG;AAC3B;","names":[]}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The context passed to every section's `when` and `render` functions.
|
|
3
|
+
*
|
|
4
|
+
* @typeParam TFlags - Shape of the boolean flags object
|
|
5
|
+
* @typeParam TVars - Shape of the runtime variables object
|
|
6
|
+
*/
|
|
7
|
+
interface PromptContext<TFlags extends Record<string, boolean>, TVars extends Record<string, unknown>> {
|
|
8
|
+
/** Boolean flags that control which sections are included and how they render */
|
|
9
|
+
flags: TFlags;
|
|
10
|
+
/** Runtime data sections can read from (model, mode, integrations, etc.) */
|
|
11
|
+
vars: TVars;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* A single composable unit of prompt content.
|
|
15
|
+
*
|
|
16
|
+
* @typeParam TCtx - The prompt context type this section operates on
|
|
17
|
+
*/
|
|
18
|
+
interface PromptSection<TCtx extends PromptContext<Record<string, boolean>, Record<string, unknown>>> {
|
|
19
|
+
/** Unique identifier. Used by `use()`, `without()`, and `buildWithMeta()`. */
|
|
20
|
+
id: string;
|
|
21
|
+
/**
|
|
22
|
+
* Guard function. Return `false` to exclude this section from the output.
|
|
23
|
+
* If omitted, the section is always included.
|
|
24
|
+
*/
|
|
25
|
+
when?: (ctx: TCtx) => boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Render the section content. Receives the full prompt context.
|
|
28
|
+
* Return an empty string to include nothing (different from `when: false`
|
|
29
|
+
* which removes the section entirely including any separator).
|
|
30
|
+
*/
|
|
31
|
+
render: (ctx: TCtx) => string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export type { PromptContext as P, PromptSection as a };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The context passed to every section's `when` and `render` functions.
|
|
3
|
+
*
|
|
4
|
+
* @typeParam TFlags - Shape of the boolean flags object
|
|
5
|
+
* @typeParam TVars - Shape of the runtime variables object
|
|
6
|
+
*/
|
|
7
|
+
interface PromptContext<TFlags extends Record<string, boolean>, TVars extends Record<string, unknown>> {
|
|
8
|
+
/** Boolean flags that control which sections are included and how they render */
|
|
9
|
+
flags: TFlags;
|
|
10
|
+
/** Runtime data sections can read from (model, mode, integrations, etc.) */
|
|
11
|
+
vars: TVars;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* A single composable unit of prompt content.
|
|
15
|
+
*
|
|
16
|
+
* @typeParam TCtx - The prompt context type this section operates on
|
|
17
|
+
*/
|
|
18
|
+
interface PromptSection<TCtx extends PromptContext<Record<string, boolean>, Record<string, unknown>>> {
|
|
19
|
+
/** Unique identifier. Used by `use()`, `without()`, and `buildWithMeta()`. */
|
|
20
|
+
id: string;
|
|
21
|
+
/**
|
|
22
|
+
* Guard function. Return `false` to exclude this section from the output.
|
|
23
|
+
* If omitted, the section is always included.
|
|
24
|
+
*/
|
|
25
|
+
when?: (ctx: TCtx) => boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Render the section content. Receives the full prompt context.
|
|
28
|
+
* Return an empty string to include nothing (different from `when: false`
|
|
29
|
+
* which removes the section entirely including any separator).
|
|
30
|
+
*/
|
|
31
|
+
render: (ctx: TCtx) => string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export type { PromptContext as P, PromptSection as a };
|
package/package.json
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@anythingai/teleprompt",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Composable, declarative prompt builder for LLM system prompts",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"sideEffects": false,
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"import": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"default": "./dist/index.js"
|
|
15
|
+
},
|
|
16
|
+
"require": {
|
|
17
|
+
"types": "./dist/index.d.cts",
|
|
18
|
+
"default": "./dist/index.cjs"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"./testing": {
|
|
22
|
+
"import": {
|
|
23
|
+
"types": "./dist/testing.d.ts",
|
|
24
|
+
"default": "./dist/testing.js"
|
|
25
|
+
},
|
|
26
|
+
"require": {
|
|
27
|
+
"types": "./dist/testing.d.cts",
|
|
28
|
+
"default": "./dist/testing.cjs"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"dist",
|
|
34
|
+
"README.md",
|
|
35
|
+
"LICENSE"
|
|
36
|
+
],
|
|
37
|
+
"keywords": [
|
|
38
|
+
"prompt",
|
|
39
|
+
"llm",
|
|
40
|
+
"ai",
|
|
41
|
+
"system-prompt",
|
|
42
|
+
"prompt-builder",
|
|
43
|
+
"prompt-composition",
|
|
44
|
+
"composable"
|
|
45
|
+
],
|
|
46
|
+
"license": "MIT",
|
|
47
|
+
"repository": {
|
|
48
|
+
"type": "git",
|
|
49
|
+
"url": "https://github.com/Create-Inc/teleprompt.git"
|
|
50
|
+
},
|
|
51
|
+
"engines": {
|
|
52
|
+
"node": ">=20"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@arethetypeswrong/cli": "^0.17.0",
|
|
56
|
+
"@biomejs/biome": "^1.9.0",
|
|
57
|
+
"publint": "^0.3.0",
|
|
58
|
+
"tsup": "^8.4.0",
|
|
59
|
+
"tsx": "^4.21.0",
|
|
60
|
+
"typescript": "^5.7.0",
|
|
61
|
+
"vitest": "^3.0.0"
|
|
62
|
+
},
|
|
63
|
+
"scripts": {
|
|
64
|
+
"build": "tsup",
|
|
65
|
+
"check": "pnpm lint && pnpm typecheck && pnpm test && pnpm publint && pnpm attw",
|
|
66
|
+
"lint": "biome check .",
|
|
67
|
+
"lint:fix": "biome check --write .",
|
|
68
|
+
"typecheck": "tsc --noEmit",
|
|
69
|
+
"test": "vitest run",
|
|
70
|
+
"test:watch": "vitest",
|
|
71
|
+
"publint": "publint",
|
|
72
|
+
"attw": "attw --pack . --profile node16"
|
|
73
|
+
}
|
|
74
|
+
}
|