@riotprompt/riotprompt 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.
Files changed (129) hide show
  1. package/.gitcarve/config.yaml +10 -0
  2. package/.gitcarve/context/content.md +11 -0
  3. package/.markdown-doctest-setup.mjs +23 -0
  4. package/.nvmrc +1 -0
  5. package/LICENSE +190 -0
  6. package/README.md +513 -0
  7. package/dist/builder.cjs +152 -0
  8. package/dist/builder.cjs.map +1 -0
  9. package/dist/builder.d.ts +37 -0
  10. package/dist/builder.js +148 -0
  11. package/dist/builder.js.map +1 -0
  12. package/dist/chat.cjs +26 -0
  13. package/dist/chat.cjs.map +1 -0
  14. package/dist/chat.d.ts +14 -0
  15. package/dist/chat.js +21 -0
  16. package/dist/chat.js.map +1 -0
  17. package/dist/constants.cjs +34 -0
  18. package/dist/constants.cjs.map +1 -0
  19. package/dist/constants.d.ts +13 -0
  20. package/dist/constants.js +23 -0
  21. package/dist/constants.js.map +1 -0
  22. package/dist/formatter.cjs +139 -0
  23. package/dist/formatter.cjs.map +1 -0
  24. package/dist/formatter.d.ts +88 -0
  25. package/dist/formatter.js +131 -0
  26. package/dist/formatter.js.map +1 -0
  27. package/dist/items/content.cjs +14 -0
  28. package/dist/items/content.cjs.map +1 -0
  29. package/dist/items/content.d.ts +3 -0
  30. package/dist/items/content.js +10 -0
  31. package/dist/items/content.js.map +1 -0
  32. package/dist/items/context.cjs +13 -0
  33. package/dist/items/context.cjs.map +1 -0
  34. package/dist/items/context.d.ts +3 -0
  35. package/dist/items/context.js +9 -0
  36. package/dist/items/context.js.map +1 -0
  37. package/dist/items/instruction.cjs +13 -0
  38. package/dist/items/instruction.cjs.map +1 -0
  39. package/dist/items/instruction.d.ts +3 -0
  40. package/dist/items/instruction.js +9 -0
  41. package/dist/items/instruction.js.map +1 -0
  42. package/dist/items/parameters.cjs +53 -0
  43. package/dist/items/parameters.cjs.map +1 -0
  44. package/dist/items/parameters.d.ts +5 -0
  45. package/dist/items/parameters.js +47 -0
  46. package/dist/items/parameters.js.map +1 -0
  47. package/dist/items/section.cjs +120 -0
  48. package/dist/items/section.cjs.map +1 -0
  49. package/dist/items/section.d.ts +33 -0
  50. package/dist/items/section.js +115 -0
  51. package/dist/items/section.js.map +1 -0
  52. package/dist/items/trait.cjs +13 -0
  53. package/dist/items/trait.cjs.map +1 -0
  54. package/dist/items/trait.d.ts +3 -0
  55. package/dist/items/trait.js +9 -0
  56. package/dist/items/trait.js.map +1 -0
  57. package/dist/items/weighted.cjs +27 -0
  58. package/dist/items/weighted.cjs.map +1 -0
  59. package/dist/items/weighted.d.ts +24 -0
  60. package/dist/items/weighted.js +22 -0
  61. package/dist/items/weighted.js.map +1 -0
  62. package/dist/loader.cjs +167 -0
  63. package/dist/loader.cjs.map +1 -0
  64. package/dist/loader.d.ts +35 -0
  65. package/dist/loader.js +161 -0
  66. package/dist/loader.js.map +1 -0
  67. package/dist/logger.cjs +51 -0
  68. package/dist/logger.cjs.map +1 -0
  69. package/dist/logger.d.ts +11 -0
  70. package/dist/logger.js +46 -0
  71. package/dist/logger.js.map +1 -0
  72. package/dist/override.cjs +109 -0
  73. package/dist/override.cjs.map +1 -0
  74. package/dist/override.d.ts +31 -0
  75. package/dist/override.js +105 -0
  76. package/dist/override.js.map +1 -0
  77. package/dist/parse/markdown.cjs +114 -0
  78. package/dist/parse/markdown.cjs.map +1 -0
  79. package/dist/parse/markdown.d.ts +3 -0
  80. package/dist/parse/markdown.js +110 -0
  81. package/dist/parse/markdown.js.map +1 -0
  82. package/dist/parse/text.cjs +33 -0
  83. package/dist/parse/text.cjs.map +1 -0
  84. package/dist/parse/text.d.ts +3 -0
  85. package/dist/parse/text.js +29 -0
  86. package/dist/parse/text.js.map +1 -0
  87. package/dist/parser.cjs +99 -0
  88. package/dist/parser.cjs.map +1 -0
  89. package/dist/parser.d.ts +21 -0
  90. package/dist/parser.js +75 -0
  91. package/dist/parser.js.map +1 -0
  92. package/dist/prompt.cjs +15 -0
  93. package/dist/prompt.cjs.map +1 -0
  94. package/dist/prompt.d.ts +16 -0
  95. package/dist/prompt.js +11 -0
  96. package/dist/prompt.js.map +1 -0
  97. package/dist/riotprompt.cjs +1359 -0
  98. package/dist/riotprompt.cjs.map +1 -0
  99. package/dist/riotprompt.d.ts +25 -0
  100. package/dist/riotprompt.js +21 -0
  101. package/dist/riotprompt.js.map +1 -0
  102. package/dist/util/general.cjs +52 -0
  103. package/dist/util/general.cjs.map +1 -0
  104. package/dist/util/general.d.ts +4 -0
  105. package/dist/util/general.js +47 -0
  106. package/dist/util/general.js.map +1 -0
  107. package/dist/util/markdown.cjs +115 -0
  108. package/dist/util/markdown.cjs.map +1 -0
  109. package/dist/util/markdown.d.ts +7 -0
  110. package/dist/util/markdown.js +111 -0
  111. package/dist/util/markdown.js.map +1 -0
  112. package/dist/util/storage.cjs +155 -0
  113. package/dist/util/storage.cjs.map +1 -0
  114. package/dist/util/storage.d.ts +32 -0
  115. package/dist/util/storage.js +132 -0
  116. package/dist/util/storage.js.map +1 -0
  117. package/dist/util/text.cjs +42 -0
  118. package/dist/util/text.cjs.map +1 -0
  119. package/dist/util/text.d.ts +1 -0
  120. package/dist/util/text.js +38 -0
  121. package/dist/util/text.js.map +1 -0
  122. package/docs/loader.md +237 -0
  123. package/docs/override.md +323 -0
  124. package/docs/parser.md +130 -0
  125. package/eslint.config.mjs +82 -0
  126. package/nodemon.json +14 -0
  127. package/package.json +72 -0
  128. package/vite.config.ts +114 -0
  129. package/vitest.config.ts +25 -0
package/README.md ADDED
@@ -0,0 +1,513 @@
1
+ # riotprompt
2
+
3
+ A structured prompt engineering library for LLMs - because you have better things to do than worry about prompt formatting.
4
+
5
+ > "I don't wanna hear it, know you're full of sh*t" - Minor Threat
6
+
7
+ ## Table of Contents
8
+ - [Why riotprompt?](#why-riotprompt)
9
+ - [Installation](#installation)
10
+ - [Basic Usage](#basic-usage)
11
+ - [Core Concepts](#core-concepts)
12
+ - [WeightedText](#weightedtext)
13
+ - [Prompt Structure Elements](#prompt-structure-elements)
14
+ - [Sections](#sections)
15
+ - [Advanced Usage](#advanced-usage)
16
+ - [Creating Sections](#creating-sections)
17
+ - [Setting Section and Item Weights](#setting-section-and-item-weights)
18
+ - [Using Parameters for Customization](#using-parameters-for-customization)
19
+ - [Parsing Markdown for Section Creation](#parsing-markdown-for-section-creation)
20
+ - [Overriding Prompt Content](#overriding-prompt-content)
21
+ - [Building Prompts](#building-prompts)
22
+ - [Manipulating Section Contents](#manipulating-section-contents)
23
+ - [Using the riotprompt Loader for File-Based Prompts](#using-the-riotprompt-loader-for-file-based-prompts)
24
+ - [Customizing Format Options](#customizing-format-options)
25
+ - [Model Support](#model-support)
26
+ - [Why the Name?](#why-the-name)
27
+ - [Contributing](#contributing)
28
+ - [License](#license)
29
+
30
+ ## Why riotprompt?
31
+
32
+ Tired of spending hours crafting and formatting the perfect LLM prompt? riotprompt provides a structured way to organize your prompts, allowing you to focus on the content rather than the formatting.
33
+
34
+ riotprompt helps you:
35
+ - Organize prompt elements into logical categories (instructions, content, context)
36
+ - Create reusable persona definitions with traits and instructions
37
+ - Group related items into sections
38
+ - Format everything consistently for different LLM models
39
+
40
+ ## Installation
41
+
42
+ ```bash
43
+ npm install @riotprompt
44
+ ```
45
+
46
+ ## Basic Usage
47
+
48
+ ```js
49
+ import { createSection, createPrompt, Formatter, Section, Instruction } from '@riotprompt';
50
+
51
+ // Create a new prompt
52
+ const section: Section<Instruction> = createSection<Instruction>({ title: "Instructions" });
53
+
54
+ // Add instructions
55
+ section.add("Answer in a concise manner");
56
+ section.add("Provide code examples when appropriate");
57
+
58
+ // Verify parts of the output
59
+ console.log('Number of instructions:', section.items.length);
60
+ // Output: Number of instructions: 2
61
+
62
+ // Formatting a Section using Tags
63
+ const formatterTags = Formatter.create();
64
+ const formattedTags = formatterTags.format(section);
65
+ console.log(formattedTags);
66
+ // Output: <Instructions>
67
+ // Answer in a concise manner
68
+ //
69
+ // Provide code examples when appropriate
70
+ // </Instructions>
71
+
72
+ // Formatting a Section using Markdown
73
+ const formatterMarkdown = Formatter.create({ formatOptions: { sectionSeparator: "markdown" }});
74
+ const formattedMarkdown = formatterMarkdown.format(section)
75
+ console.log(formattedMarkdown);
76
+ // Output: # Instructions
77
+ //
78
+ // Answer in a concise manner
79
+ //
80
+ // Provide code examples when appropriate
81
+
82
+
83
+ ```
84
+
85
+ ## Core Concepts
86
+
87
+ riotprompt is built around several key concepts:
88
+
89
+ ### WeightedText
90
+
91
+ The base type for all prompt elements. Each element has:
92
+ - `text`: The actual content
93
+ - `weight`: Optional weight value (for potential future ranking/prioritization)
94
+
95
+ ### Prompt Structure Elements
96
+
97
+ riotprompt organizes prompts into four main categories:
98
+
99
+ 1. **Personas**: Define who the LLM should be
100
+ - `name`: The persona's identifier
101
+ - `traits`: Characteristics the persona should embody (e.g., "You are a developer working on a project who needs to create a commit message")
102
+ - `instructions`: Specific guidance for the persona
103
+
104
+ 2. **Instructions**: Tell the LLM how to respond
105
+ - General guidelines for response format, tone, etc.
106
+
107
+ 3. **Content**: What the LLM should respond to
108
+ - The actual query or task
109
+
110
+ 4. **Context**: Provide background information
111
+ - Additional context that helps the LLM understand the request
112
+
113
+ ### Sections
114
+
115
+ Groups related items together:
116
+ - `title`: Section name
117
+ - `items`: Collection of related elements
118
+
119
+ ## Advanced Usage
120
+
121
+ ### Creating Sections
122
+
123
+ ```js
124
+ import { createSection, Formatter, Section, Instruction, Context } from '@riotprompt';
125
+
126
+ // Create a section for coding best practices
127
+ const instructions: Section<Instruction> = createSection<Instruction>({ title: "Instructions" });
128
+ instructions.add("Follow DRY (Don't Repeat Yourself) principles");
129
+ instructions.add("Write readable code with clear variable names");
130
+ instructions.add("Add comments for complex logic");
131
+
132
+ const writerPersona: Section<Instruction> = createSection<Instruction>({ title: "Writer Persona" });
133
+ writerPersona.add("You are an amazingly talented writer who is awesome.");
134
+
135
+ const literatureContext: Section<Context> = createSection<Context>({ title: "Literature Context" });
136
+ literatureContext.add("Here is the full text of a really long book.");
137
+ ```
138
+
139
+ ### Setting Section and Item Weights
140
+
141
+ riotprompt allows you to assign weights to sections and individual items within those sections. This can be useful for future enhancements where prompt elements might be prioritized or selected based on their weight.
142
+
143
+ You can define `weight` for the section itself and a default `itemWeight` for items added to that section using `SectionOptions`. Additionally, `parameters` can be defined at the section level and will be passed down to items added to that section.
144
+
145
+ ```js
146
+ import { createSection, Formatter, Section, Instruction } from '@riotprompt';
147
+
148
+ // Create a section with specific weights and parameters
149
+ const weightedSection: Section<Instruction> = createSection<Instruction>({
150
+ title: "Weighted Topics",
151
+ weight: 10, // Weight for the entire section
152
+ itemWeight: 5, // Default weight for items in this section
153
+ parameters: { topic: "advanced" } // Parameters passed to items
154
+ });
155
+
156
+ // Items added to this section will inherit the itemWeight and parameters
157
+ // unless overridden individually.
158
+ weightedSection.add("Discuss {{topic}} caching strategies");
159
+ weightedSection.add("Explain {{topic}} database indexing", { weight: 7 }); // Override itemWeight
160
+ ```
161
+
162
+ ### Using Parameters for Customization
163
+
164
+ riotprompt supports dynamic content in your prompts through the use of parameters. Parameters allow you to define placeholders in your prompt text (e.g., `{{variable}}`) and replace them with specific values when the prompt is created or formatted. This is a simple yet powerful way to customize prompts for different scenarios without altering the core structure.
165
+
166
+ Parameters can be passed when creating a prompt, a persona, or a section. They can also be supplied directly when adding individual items like instructions, content, or context if those items are strings with placeholders.
167
+
168
+ ```js
169
+ import { createSection, createParameters, Formatter, Section, Instruction, Parameters } from '@riotprompt';
170
+
171
+ const parameters: Parameters = createParameters({
172
+ "targetLanguage": "Spanish",
173
+ })
174
+
175
+ const instructions: Section<Instruction> = createSection({ title: "Instructions", parameters });
176
+ instructions.add("Translate the following text to {{targetLanguage}}.");
177
+
178
+ const formatter = Formatter.create({ formatOptions: { sectionSeparator: "markdown" }});
179
+ const formatted = formatter.format(instructions);
180
+ console.log(formatted);
181
+ // Output: # Instructions
182
+ // Translate the following text to Spanish
183
+ //
184
+ ```
185
+
186
+ ### Parsing Markdown for Section Creation
187
+
188
+ riotprompt can simplify the process of structuring your prompts by parsing Markdown content. When you provide Markdown text, riotprompt can automatically convert Markdown headers (e.g., `# Title`, `## Subtitle`) into `Section` objects. The text of the header becomes the title of the `Section`.
189
+
190
+ This allows you to draft complex prompt structures in a familiar Markdown format and then easily import them into riotprompt. For instance, a document like this:
191
+
192
+ ```markdown
193
+ # Main Topic
194
+ Some general instructions or content.
195
+
196
+ ## Sub-Topic 1
197
+ Details about the first sub-topic.
198
+
199
+ ### Sub-Sub-Topic A
200
+ Further details.
201
+
202
+ ## Sub-Topic 2
203
+ Details about the second sub-topic.
204
+ ```
205
+
206
+ Could be parsed into a main section titled "Main Topic" containing text and two sub-sections: "Sub-Topic 1" (which itself contains a nested section "Sub-Sub-Topic A") and "Sub-Topic 2". The content under each header would become items within the respective sections.
207
+
208
+ ```js
209
+ import { Parser, Formatter } from '@riotprompt';
210
+
211
+ // Markdown content with sections
212
+ const markdownContent = `
213
+ # Instructions
214
+ Follow these guidelines when writing code.
215
+
216
+ ## Best Practices
217
+ - Keep functions small and focused
218
+ - Use meaningful variable names
219
+
220
+ ## Documentation
221
+ - Comment complex logic
222
+ - Document public APIs thoroughly
223
+ `;
224
+
225
+ // Parse the Markdown into a Section structure
226
+ const parser = Parser.create();
227
+
228
+ const parsedSection = parser.parse(markdownContent);
229
+
230
+ // Now you can manipulate the parsed sections
231
+ const bestPracticesSection = parsedSection.items[1]; // Accessing the "Best Practices" section
232
+ bestPracticesSection.add("- Write tests for your code");
233
+
234
+ // Format the resulting section structure
235
+ const formatter = Formatter.create();
236
+ const formattedPrompt = formatter.format(parsedSection);
237
+ console.log(formattedPrompt);
238
+ /* Output:
239
+ <Instructions>
240
+ Follow these guidelines when writing code.
241
+
242
+ <section title="Best Practices">
243
+ - Keep functions small and focused
244
+ - Use meaningful variable names
245
+ - Write tests for your code
246
+ </section>
247
+
248
+ <section title="Documentation">
249
+ - Comment complex logic
250
+ - Document public APIs thoroughly
251
+ </section>
252
+ </Instructions>
253
+ */
254
+ ```
255
+
256
+ For more information, see the [riotprompt Parser Documentation](docs/parser.md)
257
+
258
+ ### Overriding Prompt Content
259
+
260
+ riotprompt's Override utility allows you to customize or replace parts of a prompt without altering the original prompt files. This is particularly useful in larger applications where you have default prompt templates but want to adjust certain sections for specific use cases, users, or environments. By using overrides, you can maintain a clean separation between core prompt content and custom modifications.
261
+
262
+ The override system works by looking for specially-named files in an override directory that correspond to your prompt files. You can completely override a section (replace it entirely), prepend content (insert before the original), or append content (insert after the original). This is all done through a simple file naming convention where files with the same name fully override, files ending in `-pre.md` prepend content, and files ending in `-post.md` append content.
263
+
264
+ For more information, see the [riotprompt Override Documentation](docs/override.md)
265
+
266
+ ### Building Prompts
267
+
268
+ The riotprompt library provides a powerful Builder pattern for constructing complex prompts programmatically. The Builder allows you to assemble prompts from various sources including files, directories, and inline content.
269
+
270
+ #### Using the Builder
271
+
272
+ The Builder provides a fluent interface for assembling prompts from various sources:
273
+
274
+ <!-- skip-example -->
275
+ ```js
276
+ import { Builder } from '@riotprompt';
277
+
278
+ // Create a builder instance
279
+ const builder = Builder.create({
280
+ basePath: './prompts', // Base directory for prompt files
281
+ overridePath: './overrides', // Optional directory for override files
282
+ overrides: true, // Whether to apply overrides
283
+ parameters: { role: 'expert' } // Optional parameters for substitution
284
+ });
285
+
286
+ // Build a prompt from various sources
287
+ const prompt: Prompt = await builder
288
+ .addPersonaPath('personas/developer.md')
289
+ .addInstructionPath('instructions/code-review.md')
290
+ .loadContext(['./context/people', './context/projects'])
291
+ .addContent('Here is some code I want you to look at.')
292
+ .build();
293
+
294
+ // Format and use the prompt with your LLM API
295
+ ```
296
+
297
+ #### Builder Methods
298
+
299
+ The Builder supports the following methods:
300
+
301
+ - **`addPersonaPath(path)`**: Load a persona from a file
302
+ - **`addContextPath(path)`**: Load context from a file
303
+ - **`addInstructionPath(path)`**: Load instructions from a file
304
+ - **`addContentPath(path)`**: Load content from a file
305
+ - **`addContent(text)`**: Add content directly as a string
306
+ - **`addContext(text)`**: Add context directly as a string
307
+ - **`loadContext(directories)`**: Load context from multiple directories
308
+ - **`loadContent(directories)`**: Load content from multiple directories
309
+ - **`build()`**: Assemble the final prompt
310
+
311
+ All methods return the builder instance for chaining, and the `build()` method returns a Promise that resolves to a `Prompt` object.
312
+
313
+ #### Loading from Directories
314
+
315
+ The Builder can load content from entire directories:
316
+
317
+ <!-- skip-example -->
318
+ ```js
319
+ import { Builder } from '@riotprompt/builder';
320
+
321
+ const builder = Builder.create({
322
+ basePath: './prompts',
323
+ });
324
+
325
+ // Load all files from specific directories
326
+ const prompt = await builder
327
+ .loadContext(['context/user', 'context/project'])
328
+ .loadContent(['content/queries'])
329
+ .build();
330
+ ```
331
+
332
+ ### Manipulating Section Contents
333
+
334
+ Once you have a `Section` object, whether created directly, through Markdown parsing, or as part of a `riotprompt` instance (e.g., `prompt.instructionsSection`), you have several methods to manage its contents. These methods allow for dynamic construction and modification of your prompt structure.
335
+
336
+ The `Section` interface provides the following methods for item manipulation:
337
+
338
+ - **`add(item: T | Section<T> | string, options?: WeightedOptions): Section<T>`**
339
+ Appends a new item or a nested section to the end of the section's item list. If a string is provided, it's typically converted into an appropriate `WeightedText` object (e.g., `Instruction`, `ContentText`).
340
+ ```typescript
341
+ mySection.add("New item at the end");
342
+ const nestedSection = createSection("Nested");
343
+ mySection.add(nestedSection);
344
+ ```
345
+
346
+ - **`append(item: T | Section<T> | string, options?: WeightedOptions): Section<T>`**
347
+ Alias for `add`. Appends an item or nested section to the end.
348
+ ```typescript
349
+ mySection.append("Another item at the end");
350
+ ```
351
+
352
+ - **`prepend(item: T | Section<T> | string, options?: WeightedOptions): Section<T>`**
353
+ Adds a new item or a nested section to the beginning of the section's item list.
354
+ ```typescript
355
+ mySection.prepend("Item at the very beginning");
356
+ ```
357
+
358
+ - **`insert(index: number, item: T | Section<T> | string, options?: WeightedOptions): Section<T>`**
359
+ Inserts an item or nested section at a specific zero-based `index` within the item list.
360
+ ```typescript
361
+ mySection.insert(1, "Item at index 1"); // Inserts after the first item
362
+ ```
363
+
364
+ - **`replace(index: number, item: T | Section<T> | string, options?: WeightedOptions): Section<T>`**
365
+ Replaces the item at the specified `index` with a new item or nested section.
366
+ ```typescript
367
+ mySection.replace(0, "Replaced first item");
368
+ ```
369
+
370
+ - **`remove(index: number): Section<T>`**
371
+ Removes the item at the specified `index` from the item list.
372
+ ```typescript
373
+ mySection.remove(0); // Removes the first item
374
+ ```
375
+
376
+ These methods return the `Section` instance itself, allowing for fluent chaining of operations:
377
+
378
+ ```js
379
+ import { createSection, Formatter, Section, Instruction } from '@riotprompt';
380
+
381
+ const mySection: Section<Instruction> = createSection({ title: "Example" });
382
+
383
+ mySection
384
+ .add("First item")
385
+ .prepend("Actually, this is first")
386
+ .insert(1, "This goes second")
387
+ .remove(2); // Removes "First item"
388
+
389
+ const formatter = Formatter.create({ formatOptions: { sectionSeparator: "markdown" }})
390
+ const formatted = formatter.format( mySection );
391
+ console.log( formatted );
392
+ // Output: # Example
393
+ //
394
+ // Actually, this is first
395
+ //
396
+ // This goes second
397
+ ```
398
+ ```
399
+
400
+ ### Using the riotprompt Loader for File-Based Prompts
401
+
402
+ riotprompt provides a Loader utility that allows you to load prompt templates from external files. This is particularly useful when you want to:
403
+
404
+ - Store complex prompts as separate files
405
+ - Share prompt templates across different parts of your application
406
+ - Keep your prompt content separate from your application code
407
+
408
+ The Loader supports various file formats and can automatically parse the content into the appropriate Section structures.
409
+
410
+ The Loader works seamlessly with the Parser to convert structured content into riotprompt's internal representation, allowing you to focus on writing clear prompts rather than managing their implementation details.
411
+
412
+ For more documentation on the Loader, see the [riotprompt Loader Documentation](docs/loader.md)
413
+
414
+ ### Customizing Format Options
415
+
416
+ riotprompt supports various formatting styles to organize your prompt elements:
417
+
418
+ #### Available Formatting Options
419
+
420
+ - **areaSeparator**: Determines how major areas (Instructions, Content, Context) are formatted
421
+ - `"tag"`: Uses XML-style tags `<instructions>...</instructions>`
422
+ - `"markdown"`: Uses markdown headers `#### Instructions`
423
+
424
+ - **sectionSeparator**: Determines how sections within areas are formatted
425
+ - `"tag"`: Uses XML-style tags `<section title="Best Practices">...</section>`
426
+ - `"markdown"`: Uses markdown subheaders `#### Section : Best Practices`
427
+
428
+ #### Examples of Different Separator Styles
429
+
430
+ Here's how the same prompt would be formatted using different separator styles:
431
+
432
+ **Tag Style (Default)**
433
+
434
+ ```
435
+ <instructions>
436
+ Answer in a concise manner
437
+ Provide code examples when appropriate
438
+
439
+ <section title="Best Practices">
440
+ Follow DRY (Don't Repeat Yourself) principles
441
+ Write readable code with clear variable names
442
+ Add comments for complex logic
443
+ </section>
444
+ </instructions>
445
+
446
+ <contents>
447
+ Explain how promises work in JavaScript
448
+ </contents>
449
+
450
+ <context>
451
+ This is for a beginner JavaScript tutorial
452
+ </context>
453
+ ```
454
+
455
+ **Markdown Style**
456
+
457
+ ```
458
+ #### Instructions
459
+
460
+ Answer in a concise manner
461
+ Provide code examples when appropriate
462
+
463
+ #### Section : Best Practices
464
+
465
+ Follow DRY (Don't Repeat Yourself) principles
466
+ Write readable code with clear variable names
467
+ Add comments for complex logic
468
+
469
+ #### Contents
470
+
471
+ Explain how promises work in JavaScript
472
+
473
+ #### Context
474
+
475
+ This is for a beginner JavaScript tutorial
476
+ ```
477
+
478
+ Different LLM providers have different recommendations for prompt formatting:
479
+
480
+ - **Anthropic (Claude)** generally recommends using XML-style tags to clearly delineate sections of prompts
481
+ - **OpenAI (GPT)** models work well with both markdown-style formatting and XML tags
482
+
483
+ The field of prompt engineering is rapidly evolving, with new research and best practices emerging regularly. riotprompt's flexible formatting system allows you to adapt to these changes without rewriting your prompts entirely.
484
+
485
+ By separating the structure of your prompt (instructions, context, content) from its formatting, riotprompt makes it easier to experiment with different formatting approaches to find what works best for your specific use case and model.
486
+
487
+
488
+ ## Model Support
489
+
490
+ The initial version of riotprompt is designed specifically for the OpenAI API with models such as:
491
+ - gpt-4o
492
+ - gpt-4o-mini
493
+ - o1-mini
494
+ - o1
495
+ - o1-preview
496
+ - o1-pro
497
+ - o3-mini
498
+
499
+ Future versions will be expanded to support other LLM providers and their respective models.
500
+
501
+ The formatter automatically adapts the prompt structure based on the model's requirements (e.g., using system messages for models that support them).
502
+
503
+ ## Why the Name?
504
+
505
+ Why not? Who are you to even ask that question?
506
+
507
+ ## Contributing
508
+
509
+ Contributions are welcome! Please feel free to submit a Pull Request.
510
+
511
+ ## License
512
+
513
+ Apache 2.0
@@ -0,0 +1,152 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4
+
5
+ const path = require('path');
6
+ const zod = require('zod');
7
+ const parameters = require('./items/parameters.cjs');
8
+ const section = require('./items/section.cjs');
9
+ const logger = require('./logger.cjs');
10
+ require('./items/weighted.cjs');
11
+ const prompt = require('./prompt.cjs');
12
+ require('./formatter.cjs');
13
+ const parser = require('./parser.cjs');
14
+ const loader = require('./loader.cjs');
15
+ const override = require('./override.cjs');
16
+
17
+ const OptionSchema = zod.z.object({
18
+ logger: zod.z.any().optional().default(logger.DEFAULT_LOGGER),
19
+ basePath: zod.z.string(),
20
+ overridePath: zod.z.string().optional().default("./"),
21
+ overrides: zod.z.boolean().optional().default(false),
22
+ parameters: parameters.ParametersSchema.optional().default({})
23
+ });
24
+ const create = (builderOptions)=>{
25
+ const options = OptionSchema.parse(builderOptions);
26
+ const logger$1 = logger.wrapLogger(options.logger, 'Builder');
27
+ const parser$1 = parser.create({
28
+ logger: logger$1
29
+ });
30
+ const override$1 = override.create({
31
+ logger: logger$1,
32
+ configDir: options.overridePath || "./",
33
+ overrides: options.overrides || false
34
+ });
35
+ const loader$1 = loader.create({
36
+ logger: logger$1
37
+ });
38
+ const personaSection = section.create({
39
+ title: "Persona"
40
+ });
41
+ const contextSection = section.create({
42
+ title: "Context"
43
+ });
44
+ const instructionSection = section.create({
45
+ title: "Instruction"
46
+ });
47
+ const contentSection = section.create({
48
+ title: "Content"
49
+ });
50
+ const parameters = options.parameters;
51
+ const instance = {};
52
+ const loadOptions = (sectionOptions = {})=>{
53
+ const currentOptions = section.SectionOptionsSchema.parse(sectionOptions);
54
+ return {
55
+ ...currentOptions,
56
+ parameters: {
57
+ ...parameters,
58
+ ...currentOptions.parameters
59
+ }
60
+ };
61
+ };
62
+ const loadDirectories = async (directories, sectionOptions = {})=>{
63
+ const currentOptions = loadOptions(sectionOptions);
64
+ logger$1.debug("Loading directories", directories);
65
+ const sections = await loader$1.load(directories, currentOptions);
66
+ return sections;
67
+ };
68
+ const loadContext = async (contextDirectories, sectionOptions = {})=>{
69
+ const currentOptions = loadOptions(sectionOptions);
70
+ logger$1.debug('Loading context', contextDirectories);
71
+ const context = await loadDirectories(contextDirectories, currentOptions);
72
+ contextSection.add(context);
73
+ return instance;
74
+ };
75
+ instance.loadContext = loadContext;
76
+ const loadContent = async (contentDirectories, sectionOptions = {})=>{
77
+ const currentOptions = loadOptions(sectionOptions);
78
+ const content = await loadDirectories(contentDirectories, currentOptions);
79
+ contentSection.add(content);
80
+ return instance;
81
+ };
82
+ instance.loadContent = loadContent;
83
+ const loadPath = async (contentPath, sectionOptions = {})=>{
84
+ const currentOptions = loadOptions(sectionOptions);
85
+ const defaultPath = path.join(options.basePath, contentPath);
86
+ const section = await parser$1.parseFile(defaultPath, currentOptions);
87
+ const overrideSection = await override$1.customize(contentPath, section, currentOptions);
88
+ return overrideSection;
89
+ };
90
+ const addPersonaPath = async (contentPath, sectionOptions = {})=>{
91
+ const currentOptions = loadOptions(sectionOptions);
92
+ const persona = await loadPath(contentPath, currentOptions);
93
+ personaSection.add(persona);
94
+ return instance;
95
+ };
96
+ instance.addPersonaPath = addPersonaPath;
97
+ const addContextPath = async (contentPath, sectionOptions = {})=>{
98
+ logger$1.debug("Adding context path", contentPath);
99
+ const currentOptions = loadOptions(sectionOptions);
100
+ const context = await loadPath(contentPath, currentOptions);
101
+ contextSection.add(context);
102
+ return instance;
103
+ };
104
+ instance.addContextPath = addContextPath;
105
+ const addInstructionPath = async (contentPath, sectionOptions = {})=>{
106
+ logger$1.debug("Adding instruction path", contentPath);
107
+ const currentOptions = loadOptions(sectionOptions);
108
+ const instruction = await loadPath(contentPath, currentOptions);
109
+ instructionSection.add(instruction);
110
+ return instance;
111
+ };
112
+ instance.addInstructionPath = addInstructionPath;
113
+ const addContentPath = async (contentPath, sectionOptions = {})=>{
114
+ logger$1.debug("Adding content path", contentPath);
115
+ const currentOptions = loadOptions(sectionOptions);
116
+ const content = await loadPath(contentPath, currentOptions);
117
+ contentSection.add(content);
118
+ return instance;
119
+ };
120
+ instance.addContentPath = addContentPath;
121
+ const addContent = async (content, sectionOptions = {})=>{
122
+ logger$1.debug("Adding content", typeof content);
123
+ const currentOptions = loadOptions(sectionOptions);
124
+ const parsedContentSection = parser$1.parse(content, currentOptions);
125
+ contentSection.add(parsedContentSection);
126
+ return instance;
127
+ };
128
+ instance.addContent = addContent;
129
+ const addContext = async (context, sectionOptions = {})=>{
130
+ logger$1.debug("Adding context", typeof context);
131
+ const currentOptions = loadOptions(sectionOptions);
132
+ const parsedContextSection = parser$1.parse(context, currentOptions);
133
+ contextSection.add(parsedContextSection);
134
+ return instance;
135
+ };
136
+ instance.addContext = addContext;
137
+ const build = async ()=>{
138
+ logger$1.debug("Building prompt", {});
139
+ const prompt$1 = prompt.create({
140
+ persona: personaSection,
141
+ contexts: contextSection,
142
+ instructions: instructionSection,
143
+ contents: contentSection
144
+ });
145
+ return prompt$1;
146
+ };
147
+ instance.build = build;
148
+ return instance;
149
+ };
150
+
151
+ exports.create = create;
152
+ //# sourceMappingURL=builder.cjs.map