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,544 @@
|
|
1
|
+
# Architecture
|
2
|
+
|
3
|
+
PromptManager follows a modular, layered architecture designed for flexibility, extensibility, and maintainability.
|
4
|
+
|
5
|
+
## High-Level Architecture
|
6
|
+
|
7
|
+
```mermaid
|
8
|
+
graph TB
|
9
|
+
subgraph "Application Layer"
|
10
|
+
APP[Your Application]
|
11
|
+
CLI[CLI Tools]
|
12
|
+
WEB[Web Interface]
|
13
|
+
end
|
14
|
+
|
15
|
+
subgraph "Core Layer"
|
16
|
+
PROMPT[PromptManager::Prompt]
|
17
|
+
DP[DirectiveProcessor]
|
18
|
+
ERB[ERB Engine]
|
19
|
+
PARAM[Parameter Manager]
|
20
|
+
end
|
21
|
+
|
22
|
+
subgraph "Storage Layer"
|
23
|
+
SA[Storage Adapter Interface]
|
24
|
+
FS[FileSystemAdapter]
|
25
|
+
AR[ActiveRecordAdapter]
|
26
|
+
CUSTOM[Custom Adapters]
|
27
|
+
end
|
28
|
+
|
29
|
+
subgraph "Data Layer"
|
30
|
+
FILES[File System]
|
31
|
+
DB[Database]
|
32
|
+
API[External APIs]
|
33
|
+
end
|
34
|
+
|
35
|
+
APP --> PROMPT
|
36
|
+
CLI --> PROMPT
|
37
|
+
WEB --> PROMPT
|
38
|
+
|
39
|
+
PROMPT --> DP
|
40
|
+
PROMPT --> ERB
|
41
|
+
PROMPT --> PARAM
|
42
|
+
PROMPT --> SA
|
43
|
+
|
44
|
+
SA --> FS
|
45
|
+
SA --> AR
|
46
|
+
SA --> CUSTOM
|
47
|
+
|
48
|
+
FS --> FILES
|
49
|
+
AR --> DB
|
50
|
+
CUSTOM --> API
|
51
|
+
```
|
52
|
+
|
53
|
+
## Core Components
|
54
|
+
|
55
|
+
### 1. Prompt Class
|
56
|
+
|
57
|
+
The central `PromptManager::Prompt` class orchestrates all functionality:
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
class PromptManager::Prompt
|
61
|
+
attr_accessor :id, :parameters, :context
|
62
|
+
attr_reader :keywords, :directives
|
63
|
+
|
64
|
+
def initialize(options = {})
|
65
|
+
@id = options[:id]
|
66
|
+
@context = options[:context] || []
|
67
|
+
@directives_processor = options[:directives_processor]
|
68
|
+
@erb_flag = options[:erb_flag] || false
|
69
|
+
@envar_flag = options[:envar_flag] || false
|
70
|
+
end
|
71
|
+
|
72
|
+
def to_s
|
73
|
+
# Processing pipeline
|
74
|
+
text = load_raw_text
|
75
|
+
text = process_directives(text)
|
76
|
+
text = process_erb(text) if @erb_flag
|
77
|
+
text = substitute_envars(text) if @envar_flag
|
78
|
+
text = substitute_parameters(text)
|
79
|
+
text
|
80
|
+
end
|
81
|
+
end
|
82
|
+
```
|
83
|
+
|
84
|
+
### 2. Storage Adapter Pattern
|
85
|
+
|
86
|
+
Storage adapters implement a common interface:
|
87
|
+
|
88
|
+
```mermaid
|
89
|
+
classDiagram
|
90
|
+
class StorageAdapter {
|
91
|
+
<<interface>>
|
92
|
+
+get(id) String, Hash
|
93
|
+
+save(id, text, params) void
|
94
|
+
+delete(id) void
|
95
|
+
+list() Array~String~
|
96
|
+
+search(query) Array~String~
|
97
|
+
}
|
98
|
+
|
99
|
+
class FileSystemAdapter {
|
100
|
+
+prompts_dir Path
|
101
|
+
+prompt_extension String
|
102
|
+
+params_extension String
|
103
|
+
+search_proc Proc
|
104
|
+
+get(id) String, Hash
|
105
|
+
+save(id, text, params) void
|
106
|
+
}
|
107
|
+
|
108
|
+
class ActiveRecordAdapter {
|
109
|
+
+model Class
|
110
|
+
+id_column Symbol
|
111
|
+
+text_column Symbol
|
112
|
+
+parameters_column Symbol
|
113
|
+
+get(id) String, Hash
|
114
|
+
+save(id, text, params) void
|
115
|
+
}
|
116
|
+
|
117
|
+
StorageAdapter <|-- FileSystemAdapter
|
118
|
+
StorageAdapter <|-- ActiveRecordAdapter
|
119
|
+
```
|
120
|
+
|
121
|
+
### 3. Processing Pipeline
|
122
|
+
|
123
|
+
The prompt processing follows a well-defined pipeline:
|
124
|
+
|
125
|
+
```mermaid
|
126
|
+
graph LR
|
127
|
+
A[Raw Text] --> B[Parse Comments]
|
128
|
+
B --> C[Extract Keywords]
|
129
|
+
C --> D[Process Directives]
|
130
|
+
D --> E[Apply ERB]
|
131
|
+
E --> F[Substitute Envars]
|
132
|
+
F --> G[Replace Parameters]
|
133
|
+
G --> H[Final Text]
|
134
|
+
|
135
|
+
subgraph "Error Handling"
|
136
|
+
I[Validation]
|
137
|
+
J[Error Recovery]
|
138
|
+
end
|
139
|
+
|
140
|
+
C --> I
|
141
|
+
D --> I
|
142
|
+
E --> I
|
143
|
+
F --> I
|
144
|
+
G --> I
|
145
|
+
I --> J
|
146
|
+
```
|
147
|
+
|
148
|
+
## Design Patterns
|
149
|
+
|
150
|
+
### 1. Adapter Pattern
|
151
|
+
|
152
|
+
Storage adapters use the Adapter pattern to provide a consistent interface across different storage backends:
|
153
|
+
|
154
|
+
```ruby
|
155
|
+
# Common interface
|
156
|
+
module StorageAdapter
|
157
|
+
def get(id)
|
158
|
+
raise NotImplementedError
|
159
|
+
end
|
160
|
+
|
161
|
+
def save(id, text, parameters)
|
162
|
+
raise NotImplementedError
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
# Specific implementations
|
167
|
+
class FileSystemAdapter
|
168
|
+
include StorageAdapter
|
169
|
+
|
170
|
+
def get(id)
|
171
|
+
text = File.read(prompt_path(id))
|
172
|
+
params = JSON.parse(File.read(params_path(id)))
|
173
|
+
[text, params]
|
174
|
+
end
|
175
|
+
end
|
176
|
+
```
|
177
|
+
|
178
|
+
### 2. Strategy Pattern
|
179
|
+
|
180
|
+
Directive processing uses the Strategy pattern for different directive types:
|
181
|
+
|
182
|
+
```ruby
|
183
|
+
class DirectiveProcessor
|
184
|
+
def process_directive(directive, prompt)
|
185
|
+
case directive
|
186
|
+
when /^\/\/include (.+)$/
|
187
|
+
IncludeStrategy.new.process($1, prompt)
|
188
|
+
when /^\/\/import (.+)$/
|
189
|
+
ImportStrategy.new.process($1, prompt)
|
190
|
+
else
|
191
|
+
raise "Unknown directive: #{directive}"
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
```
|
196
|
+
|
197
|
+
### 3. Template Method Pattern
|
198
|
+
|
199
|
+
The prompt generation uses Template Method pattern:
|
200
|
+
|
201
|
+
```ruby
|
202
|
+
class Prompt
|
203
|
+
def to_s
|
204
|
+
template_method
|
205
|
+
end
|
206
|
+
|
207
|
+
private
|
208
|
+
|
209
|
+
def template_method
|
210
|
+
text = load_text # Hook 1
|
211
|
+
text = preprocess(text) # Hook 2
|
212
|
+
text = process(text) # Hook 3
|
213
|
+
text = postprocess(text) # Hook 4
|
214
|
+
text
|
215
|
+
end
|
216
|
+
|
217
|
+
# Hooks can be overridden by subclasses
|
218
|
+
def preprocess(text); text; end
|
219
|
+
def process(text); substitute_parameters(text); end
|
220
|
+
def postprocess(text); text; end
|
221
|
+
end
|
222
|
+
```
|
223
|
+
|
224
|
+
### 4. Configuration Object Pattern
|
225
|
+
|
226
|
+
Storage adapters use configuration objects for flexible setup:
|
227
|
+
|
228
|
+
```ruby
|
229
|
+
class FileSystemAdapter
|
230
|
+
attr_reader :config
|
231
|
+
|
232
|
+
def self.config(&block)
|
233
|
+
@config ||= Configuration.new
|
234
|
+
block.call(@config) if block_given?
|
235
|
+
self
|
236
|
+
end
|
237
|
+
|
238
|
+
class Configuration
|
239
|
+
attr_accessor :prompts_dir, :prompt_extension, :params_extension
|
240
|
+
|
241
|
+
def initialize
|
242
|
+
@prompts_dir = nil
|
243
|
+
@prompt_extension = '.txt'
|
244
|
+
@params_extension = '.json'
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
```
|
249
|
+
|
250
|
+
## Data Flow
|
251
|
+
|
252
|
+
### 1. Prompt Loading
|
253
|
+
|
254
|
+
```mermaid
|
255
|
+
sequenceDiagram
|
256
|
+
participant App as Application
|
257
|
+
participant P as Prompt
|
258
|
+
participant SA as StorageAdapter
|
259
|
+
participant FS as FileSystem
|
260
|
+
|
261
|
+
App->>P: new(id: 'greeting')
|
262
|
+
P->>SA: get('greeting')
|
263
|
+
SA->>FS: read greeting.txt
|
264
|
+
FS-->>SA: raw text
|
265
|
+
SA->>FS: read greeting.json
|
266
|
+
FS-->>SA: parameters
|
267
|
+
SA-->>P: text, parameters
|
268
|
+
P-->>App: Prompt instance
|
269
|
+
```
|
270
|
+
|
271
|
+
### 2. Prompt Processing
|
272
|
+
|
273
|
+
```mermaid
|
274
|
+
sequenceDiagram
|
275
|
+
participant App as Application
|
276
|
+
participant P as Prompt
|
277
|
+
participant DP as DirectiveProcessor
|
278
|
+
participant ERB as ERB Engine
|
279
|
+
|
280
|
+
App->>P: to_s()
|
281
|
+
P->>P: extract_keywords()
|
282
|
+
P->>DP: process_directives(text)
|
283
|
+
DP-->>P: processed text
|
284
|
+
P->>ERB: process_erb(text)
|
285
|
+
ERB-->>P: templated text
|
286
|
+
P->>P: substitute_parameters()
|
287
|
+
P-->>App: final text
|
288
|
+
```
|
289
|
+
|
290
|
+
### 3. Parameter Management
|
291
|
+
|
292
|
+
```mermaid
|
293
|
+
graph TD
|
294
|
+
A[Set Parameters] --> B{New Format?}
|
295
|
+
B -->|v0.3.0+| C[Store as Array]
|
296
|
+
B -->|Legacy| D[Convert to Array]
|
297
|
+
C --> E[Parameter History]
|
298
|
+
D --> E
|
299
|
+
E --> F[Get Latest Value]
|
300
|
+
E --> G[Get Full History]
|
301
|
+
F --> H[Parameter Substitution]
|
302
|
+
G --> I[UI Dropdowns]
|
303
|
+
```
|
304
|
+
|
305
|
+
## Extension Points
|
306
|
+
|
307
|
+
The architecture provides several extension points for customization:
|
308
|
+
|
309
|
+
### 1. Custom Storage Adapters
|
310
|
+
|
311
|
+
```ruby
|
312
|
+
class RedisAdapter
|
313
|
+
include PromptManager::StorageAdapter
|
314
|
+
|
315
|
+
def initialize(redis_client)
|
316
|
+
@redis = redis_client
|
317
|
+
end
|
318
|
+
|
319
|
+
def get(id)
|
320
|
+
text = @redis.get("prompt:#{id}:text")
|
321
|
+
params_json = @redis.get("prompt:#{id}:params")
|
322
|
+
params = JSON.parse(params_json || '{}')
|
323
|
+
[text, params]
|
324
|
+
end
|
325
|
+
end
|
326
|
+
```
|
327
|
+
|
328
|
+
### 2. Custom Directive Processors
|
329
|
+
|
330
|
+
```ruby
|
331
|
+
class CustomDirectiveProcessor < PromptManager::DirectiveProcessor
|
332
|
+
def process_directive(directive, prompt)
|
333
|
+
case directive
|
334
|
+
when /^\/\/model (.+)$/
|
335
|
+
@model = $1
|
336
|
+
"" # Remove directive from output
|
337
|
+
when /^\/\/temperature (.+)$/
|
338
|
+
@temperature = $1.to_f
|
339
|
+
""
|
340
|
+
else
|
341
|
+
super # Delegate to parent
|
342
|
+
end
|
343
|
+
end
|
344
|
+
end
|
345
|
+
```
|
346
|
+
|
347
|
+
### 3. Custom Keyword Patterns
|
348
|
+
|
349
|
+
```ruby
|
350
|
+
# Support multiple keyword formats
|
351
|
+
class MultiPatternPrompt < PromptManager::Prompt
|
352
|
+
PATTERNS = [
|
353
|
+
/(\[[A-Z _|]+\])/, # [KEYWORD]
|
354
|
+
/(\{\{[a-z_]+\}\})/, # {{keyword}}
|
355
|
+
/(:[a-z_]+)/ # :keyword
|
356
|
+
]
|
357
|
+
|
358
|
+
def extract_keywords(text)
|
359
|
+
keywords = []
|
360
|
+
PATTERNS.each do |pattern|
|
361
|
+
keywords.concat(text.scan(pattern).flatten)
|
362
|
+
end
|
363
|
+
keywords.uniq
|
364
|
+
end
|
365
|
+
end
|
366
|
+
```
|
367
|
+
|
368
|
+
## Performance Considerations
|
369
|
+
|
370
|
+
### 1. Lazy Loading
|
371
|
+
|
372
|
+
```ruby
|
373
|
+
class Prompt
|
374
|
+
def keywords
|
375
|
+
@keywords ||= extract_keywords(@raw_text)
|
376
|
+
end
|
377
|
+
|
378
|
+
def raw_text
|
379
|
+
@raw_text ||= storage_adapter.get(@id).first
|
380
|
+
end
|
381
|
+
end
|
382
|
+
```
|
383
|
+
|
384
|
+
### 2. Caching
|
385
|
+
|
386
|
+
```ruby
|
387
|
+
class CachingStorageAdapter
|
388
|
+
def initialize(adapter, cache_store = {})
|
389
|
+
@adapter = adapter
|
390
|
+
@cache = cache_store
|
391
|
+
end
|
392
|
+
|
393
|
+
def get(id)
|
394
|
+
@cache[id] ||= @adapter.get(id)
|
395
|
+
end
|
396
|
+
end
|
397
|
+
```
|
398
|
+
|
399
|
+
### 3. Batch Operations
|
400
|
+
|
401
|
+
```ruby
|
402
|
+
class BatchProcessor
|
403
|
+
def process_prompts(prompt_ids)
|
404
|
+
# Load all prompts at once
|
405
|
+
prompts_data = storage_adapter.get_batch(prompt_ids)
|
406
|
+
|
407
|
+
# Process in parallel
|
408
|
+
results = prompts_data.map do |id, (text, params)|
|
409
|
+
Thread.new { process_single_prompt(id, text, params) }
|
410
|
+
end.map(&:value)
|
411
|
+
|
412
|
+
results
|
413
|
+
end
|
414
|
+
end
|
415
|
+
```
|
416
|
+
|
417
|
+
## Error Handling Architecture
|
418
|
+
|
419
|
+
```mermaid
|
420
|
+
graph TD
|
421
|
+
A[Operation] --> B{Success?}
|
422
|
+
B -->|Yes| C[Return Result]
|
423
|
+
B -->|No| D[Classify Error]
|
424
|
+
D --> E{Error Type}
|
425
|
+
E -->|Storage| F[StorageError]
|
426
|
+
E -->|Parameter| G[ParameterError]
|
427
|
+
E -->|Configuration| H[ConfigurationError]
|
428
|
+
E -->|Processing| I[ProcessingError]
|
429
|
+
|
430
|
+
F --> J[Log & Report]
|
431
|
+
G --> J
|
432
|
+
H --> J
|
433
|
+
I --> J
|
434
|
+
|
435
|
+
J --> K{Recoverable?}
|
436
|
+
K -->|Yes| L[Apply Recovery Strategy]
|
437
|
+
K -->|No| M[Propagate Error]
|
438
|
+
|
439
|
+
L --> A
|
440
|
+
```
|
441
|
+
|
442
|
+
### Error Hierarchy
|
443
|
+
|
444
|
+
```ruby
|
445
|
+
module PromptManager
|
446
|
+
class Error < StandardError; end
|
447
|
+
|
448
|
+
class StorageError < Error
|
449
|
+
attr_reader :operation, :id
|
450
|
+
|
451
|
+
def initialize(message, operation: nil, id: nil)
|
452
|
+
super(message)
|
453
|
+
@operation = operation
|
454
|
+
@id = id
|
455
|
+
end
|
456
|
+
end
|
457
|
+
|
458
|
+
class ParameterError < Error
|
459
|
+
attr_reader :missing_params, :invalid_params
|
460
|
+
end
|
461
|
+
|
462
|
+
class ConfigurationError < Error
|
463
|
+
attr_reader :setting, :value
|
464
|
+
end
|
465
|
+
|
466
|
+
class ProcessingError < Error
|
467
|
+
attr_reader :stage, :directive
|
468
|
+
end
|
469
|
+
end
|
470
|
+
```
|
471
|
+
|
472
|
+
## Security Considerations
|
473
|
+
|
474
|
+
### 1. Parameter Sanitization
|
475
|
+
|
476
|
+
```ruby
|
477
|
+
class SecurePrompt < Prompt
|
478
|
+
def substitute_parameters(text)
|
479
|
+
safe_params = sanitize_parameters(@parameters)
|
480
|
+
super(text, safe_params)
|
481
|
+
end
|
482
|
+
|
483
|
+
private
|
484
|
+
|
485
|
+
def sanitize_parameters(params)
|
486
|
+
params.transform_values do |value|
|
487
|
+
# Remove potential injection attacks
|
488
|
+
value.gsub(/[<>'"&]/, '')
|
489
|
+
end
|
490
|
+
end
|
491
|
+
end
|
492
|
+
```
|
493
|
+
|
494
|
+
### 2. File System Security
|
495
|
+
|
496
|
+
```ruby
|
497
|
+
class SecureFileSystemAdapter < FileSystemAdapter
|
498
|
+
def prompt_path(id)
|
499
|
+
# Prevent directory traversal
|
500
|
+
sanitized_id = id.gsub(/[^a-zA-Z0-9_-]/, '')
|
501
|
+
raise SecurityError, "Invalid prompt ID" if sanitized_id != id
|
502
|
+
|
503
|
+
File.join(@config.prompts_dir, "#{sanitized_id}.txt")
|
504
|
+
end
|
505
|
+
end
|
506
|
+
```
|
507
|
+
|
508
|
+
## Testing Architecture
|
509
|
+
|
510
|
+
The architecture supports comprehensive testing at multiple levels:
|
511
|
+
|
512
|
+
### 1. Unit Tests
|
513
|
+
- Individual component testing
|
514
|
+
- Mock storage adapters for isolation
|
515
|
+
- Parameter validation testing
|
516
|
+
|
517
|
+
### 2. Integration Tests
|
518
|
+
- End-to-end prompt processing
|
519
|
+
- Multiple storage adapter combinations
|
520
|
+
- Error handling scenarios
|
521
|
+
|
522
|
+
### 3. Performance Tests
|
523
|
+
- Large prompt collections
|
524
|
+
- Concurrent access patterns
|
525
|
+
- Memory usage optimization
|
526
|
+
|
527
|
+
## Future Architecture Goals
|
528
|
+
|
529
|
+
### 1. Plugin System
|
530
|
+
- Dynamic directive loading
|
531
|
+
- Community-contributed processors
|
532
|
+
- Runtime plugin management
|
533
|
+
|
534
|
+
### 2. Distributed Storage
|
535
|
+
- Multi-node prompt storage
|
536
|
+
- Replication and consistency
|
537
|
+
- Fault tolerance
|
538
|
+
|
539
|
+
### 3. Real-time Updates
|
540
|
+
- Live prompt editing
|
541
|
+
- Change notification system
|
542
|
+
- Collaborative editing support
|
543
|
+
|
544
|
+
This architecture provides a solid foundation for current needs while remaining flexible enough to support future enhancements and extensions.
|