prompt_manager 0.5.7 → 0.5.8
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/COMMITS.md +196 -0
- data/README.md +485 -203
- data/docs/.keep +0 -0
- data/docs/advanced/custom-keywords.md +421 -0
- data/docs/advanced/dynamic-directives.md +535 -0
- data/docs/advanced/performance.md +612 -0
- data/docs/advanced/search-integration.md +635 -0
- data/docs/api/configuration.md +355 -0
- data/docs/api/directive-processor.md +431 -0
- data/docs/api/prompt-class.md +354 -0
- data/docs/api/storage-adapters.md +462 -0
- data/docs/assets/favicon.ico +1 -0
- data/docs/assets/logo.svg +24 -0
- data/docs/core-features/comments.md +48 -0
- data/docs/core-features/directive-processing.md +38 -0
- data/docs/core-features/erb-integration.md +68 -0
- data/docs/core-features/error-handling.md +197 -0
- data/docs/core-features/parameter-history.md +76 -0
- data/docs/core-features/parameterized-prompts.md +500 -0
- data/docs/core-features/shell-integration.md +79 -0
- data/docs/development/architecture.md +544 -0
- data/docs/development/contributing.md +425 -0
- data/docs/development/roadmap.md +234 -0
- data/docs/development/testing.md +822 -0
- data/docs/examples/advanced.md +523 -0
- data/docs/examples/basic.md +688 -0
- data/docs/examples/real-world.md +776 -0
- data/docs/examples.md +337 -0
- data/docs/getting-started/basic-concepts.md +318 -0
- data/docs/getting-started/installation.md +97 -0
- data/docs/getting-started/quick-start.md +256 -0
- data/docs/index.md +230 -0
- data/docs/migration/v0.9.0.md +459 -0
- data/docs/migration/v1.0.0.md +591 -0
- data/docs/storage/activerecord-adapter.md +348 -0
- data/docs/storage/custom-adapters.md +176 -0
- data/docs/storage/filesystem-adapter.md +236 -0
- data/docs/storage/overview.md +427 -0
- data/examples/advanced_integrations.rb +52 -0
- data/examples/prompts_dir/advanced_demo.txt +79 -0
- data/examples/prompts_dir/directive_example.json +1 -0
- data/examples/prompts_dir/directive_example.txt +8 -0
- data/examples/prompts_dir/todo.json +1 -1
- data/improvement_plan.md +996 -0
- data/lib/prompt_manager/storage/file_system_adapter.rb +8 -2
- data/lib/prompt_manager/version.rb +1 -1
- data/mkdocs.yml +146 -0
- data/prompt_manager_logo.png +0 -0
- metadata +46 -3
- data/LICENSE.txt +0 -21
@@ -0,0 +1,431 @@
|
|
1
|
+
# Directive Processor API Reference
|
2
|
+
|
3
|
+
The Directive Processor handles special instructions in prompts that begin with `//` and enable powerful prompt composition capabilities.
|
4
|
+
|
5
|
+
## Core Classes
|
6
|
+
|
7
|
+
### `PromptManager::DirectiveProcessor`
|
8
|
+
|
9
|
+
The main class responsible for processing directives within prompts.
|
10
|
+
|
11
|
+
#### Constructor
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
processor = PromptManager::DirectiveProcessor.new(
|
15
|
+
storage: storage_adapter,
|
16
|
+
max_depth: 10,
|
17
|
+
timeout: 30
|
18
|
+
)
|
19
|
+
```
|
20
|
+
|
21
|
+
**Parameters:**
|
22
|
+
- `storage` (Storage::Base): Storage adapter for resolving includes
|
23
|
+
- `max_depth` (Integer): Maximum include depth to prevent circular references (default: 10)
|
24
|
+
- `timeout` (Numeric): Processing timeout in seconds (default: 30)
|
25
|
+
|
26
|
+
#### Instance Methods
|
27
|
+
|
28
|
+
##### `process(content, context = {})`
|
29
|
+
|
30
|
+
Processes all directives in the given content.
|
31
|
+
|
32
|
+
**Parameters:**
|
33
|
+
- `content` (String): The prompt content containing directives
|
34
|
+
- `context` (Hash): Processing context and variables
|
35
|
+
|
36
|
+
**Returns:** String - Processed content with directives resolved
|
37
|
+
|
38
|
+
**Raises:**
|
39
|
+
- `PromptManager::DirectiveProcessingError` - If directive processing fails
|
40
|
+
- `PromptManager::CircularIncludeError` - If circular includes are detected
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
processor = PromptManager::DirectiveProcessor.new(storage: adapter)
|
44
|
+
result = processor.process("//include header.txt\nHello World!")
|
45
|
+
```
|
46
|
+
|
47
|
+
##### `register_directive(name, handler)`
|
48
|
+
|
49
|
+
Registers a custom directive handler.
|
50
|
+
|
51
|
+
**Parameters:**
|
52
|
+
- `name` (String): Directive name (without //)
|
53
|
+
- `handler` (Proc): Handler that processes the directive
|
54
|
+
|
55
|
+
```ruby
|
56
|
+
processor.register_directive('timestamp') do |args, context|
|
57
|
+
Time.current.strftime('%Y-%m-%d %H:%M:%S')
|
58
|
+
end
|
59
|
+
```
|
60
|
+
|
61
|
+
## Built-in Directives
|
62
|
+
|
63
|
+
### `//include` (alias: `//import`)
|
64
|
+
|
65
|
+
Includes content from another prompt file.
|
66
|
+
|
67
|
+
**Syntax:**
|
68
|
+
```text
|
69
|
+
//include path/to/file.txt
|
70
|
+
//include [VARIABLE_PATH].txt
|
71
|
+
//import common/header.txt
|
72
|
+
```
|
73
|
+
|
74
|
+
**Features:**
|
75
|
+
- Parameter substitution in paths: `//include templates/[TEMPLATE_TYPE].txt`
|
76
|
+
- Relative and absolute path resolution
|
77
|
+
- Circular include detection
|
78
|
+
- Nested include support
|
79
|
+
|
80
|
+
**Examples:**
|
81
|
+
|
82
|
+
```text
|
83
|
+
# Basic include
|
84
|
+
//include common/header.txt
|
85
|
+
|
86
|
+
# With parameter substitution
|
87
|
+
//include templates/[EMAIL_TYPE].txt
|
88
|
+
|
89
|
+
# Nested directory structure
|
90
|
+
//include emails/marketing/[CAMPAIGN_TYPE]/template.txt
|
91
|
+
```
|
92
|
+
|
93
|
+
### `//set`
|
94
|
+
|
95
|
+
Sets variables for use within the current prompt.
|
96
|
+
|
97
|
+
**Syntax:**
|
98
|
+
```text
|
99
|
+
//set VARIABLE_NAME value
|
100
|
+
//set CURRENT_DATE <%= Date.today %>
|
101
|
+
```
|
102
|
+
|
103
|
+
**Examples:**
|
104
|
+
|
105
|
+
```text
|
106
|
+
//set COMPANY_NAME Acme Corporation
|
107
|
+
//set SUPPORT_EMAIL support@[COMPANY_DOMAIN]
|
108
|
+
//set GREETING Hello [CUSTOMER_NAME]
|
109
|
+
|
110
|
+
Your message: [GREETING]
|
111
|
+
Contact us: [SUPPORT_EMAIL]
|
112
|
+
```
|
113
|
+
|
114
|
+
### `//if` / `//endif`
|
115
|
+
|
116
|
+
Conditional content inclusion.
|
117
|
+
|
118
|
+
**Syntax:**
|
119
|
+
```text
|
120
|
+
//if CONDITION
|
121
|
+
content to include if condition is true
|
122
|
+
//endif
|
123
|
+
```
|
124
|
+
|
125
|
+
**Examples:**
|
126
|
+
|
127
|
+
```text
|
128
|
+
//if [USER_TYPE] == 'premium'
|
129
|
+
🌟 Premium features are available!
|
130
|
+
//endif
|
131
|
+
|
132
|
+
//if [ORDER_TOTAL] > 100
|
133
|
+
🚚 Free shipping applied!
|
134
|
+
//endif
|
135
|
+
```
|
136
|
+
|
137
|
+
## Custom Directive Development
|
138
|
+
|
139
|
+
### Simple Directive Handler
|
140
|
+
|
141
|
+
```ruby
|
142
|
+
# Register a simple directive
|
143
|
+
processor.register_directive('upper') do |args, context|
|
144
|
+
args.upcase
|
145
|
+
end
|
146
|
+
|
147
|
+
# Usage in prompt:
|
148
|
+
# //upper hello world
|
149
|
+
# Result: HELLO WORLD
|
150
|
+
```
|
151
|
+
|
152
|
+
### Complex Directive Handler
|
153
|
+
|
154
|
+
```ruby
|
155
|
+
# Register directive with parameter processing
|
156
|
+
processor.register_directive('format_currency') do |args, context|
|
157
|
+
amount, currency = args.split(',').map(&:strip)
|
158
|
+
formatted_amount = sprintf('%.2f', amount.to_f)
|
159
|
+
|
160
|
+
case currency.downcase
|
161
|
+
when 'usd', '$'
|
162
|
+
"$#{formatted_amount}"
|
163
|
+
when 'eur', '€'
|
164
|
+
"€#{formatted_amount}"
|
165
|
+
else
|
166
|
+
"#{formatted_amount} #{currency}"
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
# Usage in prompt:
|
171
|
+
# //format_currency [ORDER_TOTAL], USD
|
172
|
+
# Result: $123.45
|
173
|
+
```
|
174
|
+
|
175
|
+
### Directive with Context Access
|
176
|
+
|
177
|
+
```ruby
|
178
|
+
processor.register_directive('user_greeting') do |args, context|
|
179
|
+
user_name = context.dig(:parameters, :user_name) || 'Guest'
|
180
|
+
time_of_day = Time.current.hour < 12 ? 'morning' : 'afternoon'
|
181
|
+
|
182
|
+
"Good #{time_of_day}, #{user_name}!"
|
183
|
+
end
|
184
|
+
|
185
|
+
# Usage in prompt:
|
186
|
+
# //user_greeting
|
187
|
+
# Result: Good morning, Alice!
|
188
|
+
```
|
189
|
+
|
190
|
+
## Error Handling
|
191
|
+
|
192
|
+
### Directive Processing Errors
|
193
|
+
|
194
|
+
```ruby
|
195
|
+
begin
|
196
|
+
result = processor.process(content)
|
197
|
+
rescue PromptManager::DirectiveProcessingError => e
|
198
|
+
puts "Directive error at line #{e.line_number}: #{e.message}"
|
199
|
+
puts "Directive: #{e.directive}"
|
200
|
+
rescue PromptManager::CircularIncludeError => e
|
201
|
+
puts "Circular include detected: #{e.include_chain.join(' -> ')}"
|
202
|
+
end
|
203
|
+
```
|
204
|
+
|
205
|
+
### Custom Error Handling
|
206
|
+
|
207
|
+
```ruby
|
208
|
+
processor.register_directive('safe_include') do |args, context|
|
209
|
+
begin
|
210
|
+
storage.read(args)
|
211
|
+
rescue PromptManager::PromptNotFoundError
|
212
|
+
"<!-- Template #{args} not found -->"
|
213
|
+
end
|
214
|
+
end
|
215
|
+
```
|
216
|
+
|
217
|
+
## Advanced Features
|
218
|
+
|
219
|
+
### Conditional Directives
|
220
|
+
|
221
|
+
```ruby
|
222
|
+
processor.register_directive('feature_flag') do |args, context|
|
223
|
+
feature_name, content = args.split(':', 2)
|
224
|
+
|
225
|
+
if FeatureFlag.enabled?(feature_name)
|
226
|
+
processor.process(content.strip, context)
|
227
|
+
else
|
228
|
+
''
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
# Usage:
|
233
|
+
# //feature_flag new_ui: Welcome to our new interface!
|
234
|
+
```
|
235
|
+
|
236
|
+
### Loop Directives
|
237
|
+
|
238
|
+
```ruby
|
239
|
+
processor.register_directive('foreach') do |args, context|
|
240
|
+
array_name, template = args.split(':', 2)
|
241
|
+
array_data = context.dig(:parameters, array_name.to_sym) || []
|
242
|
+
|
243
|
+
array_data.map.with_index do |item, index|
|
244
|
+
item_context = context.merge(
|
245
|
+
parameters: context[:parameters].merge(
|
246
|
+
item: item,
|
247
|
+
index: index,
|
248
|
+
first: index == 0,
|
249
|
+
last: index == array_data.length - 1
|
250
|
+
)
|
251
|
+
)
|
252
|
+
processor.process(template.strip, item_context)
|
253
|
+
end.join("\n")
|
254
|
+
end
|
255
|
+
|
256
|
+
# Usage:
|
257
|
+
# //foreach items: - [ITEM.NAME]: $[ITEM.PRICE]
|
258
|
+
```
|
259
|
+
|
260
|
+
### Template Inheritance
|
261
|
+
|
262
|
+
```ruby
|
263
|
+
class TemplateInheritanceProcessor < PromptManager::DirectiveProcessor
|
264
|
+
def initialize(**options)
|
265
|
+
super(**options)
|
266
|
+
register_built_in_directives
|
267
|
+
end
|
268
|
+
|
269
|
+
private
|
270
|
+
|
271
|
+
def register_built_in_directives
|
272
|
+
register_directive('extends') do |args, context|
|
273
|
+
parent_content = storage.read(args)
|
274
|
+
context[:parent_content] = parent_content
|
275
|
+
'' # Don't include anything at this point
|
276
|
+
end
|
277
|
+
|
278
|
+
register_directive('block') do |args, context|
|
279
|
+
block_name, content = args.split(':', 2)
|
280
|
+
context[:blocks] ||= {}
|
281
|
+
context[:blocks][block_name] = content.strip
|
282
|
+
'' # Blocks are processed later
|
283
|
+
end
|
284
|
+
|
285
|
+
register_directive('yield') do |args, context|
|
286
|
+
block_name = args.strip
|
287
|
+
context.dig(:blocks, block_name) || ''
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
def process(content, context = {})
|
292
|
+
# First pass: extract blocks and parent template
|
293
|
+
super(content, context)
|
294
|
+
|
295
|
+
# Second pass: process parent template with blocks
|
296
|
+
if context[:parent_content]
|
297
|
+
super(context[:parent_content], context)
|
298
|
+
else
|
299
|
+
super(content, context)
|
300
|
+
end
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
# Usage:
|
305
|
+
# child.txt:
|
306
|
+
# //extends parent.txt
|
307
|
+
# //block content: This is child content
|
308
|
+
# //block title: Child Page
|
309
|
+
|
310
|
+
# parent.txt:
|
311
|
+
# <h1>//yield title</h1>
|
312
|
+
# <div>//yield content</div>
|
313
|
+
```
|
314
|
+
|
315
|
+
## Configuration
|
316
|
+
|
317
|
+
### Global Configuration
|
318
|
+
|
319
|
+
```ruby
|
320
|
+
PromptManager.configure do |config|
|
321
|
+
config.directive_processor_class = CustomDirectiveProcessor
|
322
|
+
config.max_include_depth = 5
|
323
|
+
config.directive_timeout = 60
|
324
|
+
end
|
325
|
+
```
|
326
|
+
|
327
|
+
### Custom Processor
|
328
|
+
|
329
|
+
```ruby
|
330
|
+
class CustomDirectiveProcessor < PromptManager::DirectiveProcessor
|
331
|
+
def initialize(**options)
|
332
|
+
super(**options)
|
333
|
+
register_custom_directives
|
334
|
+
end
|
335
|
+
|
336
|
+
private
|
337
|
+
|
338
|
+
def register_custom_directives
|
339
|
+
register_directive('env') { |args, context| ENV[args] }
|
340
|
+
register_directive('random') { |args, context| rand(args.to_i) }
|
341
|
+
register_directive('uuid') { |args, context| SecureRandom.uuid }
|
342
|
+
end
|
343
|
+
end
|
344
|
+
```
|
345
|
+
|
346
|
+
## Performance Optimization
|
347
|
+
|
348
|
+
### Caching Directive Results
|
349
|
+
|
350
|
+
```ruby
|
351
|
+
class CachedDirectiveProcessor < PromptManager::DirectiveProcessor
|
352
|
+
def initialize(**options)
|
353
|
+
super(**options)
|
354
|
+
@directive_cache = {}
|
355
|
+
end
|
356
|
+
|
357
|
+
def register_directive(name, &handler)
|
358
|
+
cached_handler = lambda do |args, context|
|
359
|
+
cache_key = "#{name}:#{args}:#{context.hash}"
|
360
|
+
|
361
|
+
@directive_cache[cache_key] ||= handler.call(args, context)
|
362
|
+
end
|
363
|
+
|
364
|
+
super(name, &cached_handler)
|
365
|
+
end
|
366
|
+
|
367
|
+
def clear_cache
|
368
|
+
@directive_cache.clear
|
369
|
+
end
|
370
|
+
end
|
371
|
+
```
|
372
|
+
|
373
|
+
### Parallel Processing
|
374
|
+
|
375
|
+
```ruby
|
376
|
+
class ParallelDirectiveProcessor < PromptManager::DirectiveProcessor
|
377
|
+
def process_includes(content, context)
|
378
|
+
includes = extract_includes(content)
|
379
|
+
|
380
|
+
# Process includes in parallel
|
381
|
+
results = Parallel.map(includes) do |include_directive|
|
382
|
+
process_single_include(include_directive, context)
|
383
|
+
end
|
384
|
+
|
385
|
+
# Replace includes with results
|
386
|
+
replace_includes(content, includes, results)
|
387
|
+
end
|
388
|
+
end
|
389
|
+
```
|
390
|
+
|
391
|
+
## Testing Directives
|
392
|
+
|
393
|
+
### RSpec Examples
|
394
|
+
|
395
|
+
```ruby
|
396
|
+
describe 'Custom Directive' do
|
397
|
+
let(:processor) { PromptManager::DirectiveProcessor.new(storage: storage) }
|
398
|
+
let(:storage) { instance_double(PromptManager::Storage::Base) }
|
399
|
+
|
400
|
+
before do
|
401
|
+
processor.register_directive('test') do |args, context|
|
402
|
+
"processed: #{args}"
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
it 'processes custom directive' do
|
407
|
+
content = "//test hello world"
|
408
|
+
result = processor.process(content)
|
409
|
+
|
410
|
+
expect(result).to eq "processed: hello world"
|
411
|
+
end
|
412
|
+
|
413
|
+
it 'handles directive errors gracefully' do
|
414
|
+
processor.register_directive('error') { |args, context| raise 'test error' }
|
415
|
+
|
416
|
+
expect {
|
417
|
+
processor.process("//error test")
|
418
|
+
}.to raise_error(PromptManager::DirectiveProcessingError)
|
419
|
+
end
|
420
|
+
end
|
421
|
+
```
|
422
|
+
|
423
|
+
## Best Practices
|
424
|
+
|
425
|
+
1. **Error Handling**: Always handle errors gracefully in directive handlers
|
426
|
+
2. **Performance**: Cache expensive operations in directive handlers
|
427
|
+
3. **Security**: Validate and sanitize directive arguments
|
428
|
+
4. **Documentation**: Document custom directive syntax and behavior
|
429
|
+
5. **Testing**: Write comprehensive tests for custom directives
|
430
|
+
6. **Naming**: Use descriptive names for custom directives
|
431
|
+
7. **Context**: Use context parameter to access prompt rendering state
|