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.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/COMMITS.md +196 -0
  4. data/README.md +485 -203
  5. data/docs/.keep +0 -0
  6. data/docs/advanced/custom-keywords.md +421 -0
  7. data/docs/advanced/dynamic-directives.md +535 -0
  8. data/docs/advanced/performance.md +612 -0
  9. data/docs/advanced/search-integration.md +635 -0
  10. data/docs/api/configuration.md +355 -0
  11. data/docs/api/directive-processor.md +431 -0
  12. data/docs/api/prompt-class.md +354 -0
  13. data/docs/api/storage-adapters.md +462 -0
  14. data/docs/assets/favicon.ico +1 -0
  15. data/docs/assets/logo.svg +24 -0
  16. data/docs/core-features/comments.md +48 -0
  17. data/docs/core-features/directive-processing.md +38 -0
  18. data/docs/core-features/erb-integration.md +68 -0
  19. data/docs/core-features/error-handling.md +197 -0
  20. data/docs/core-features/parameter-history.md +76 -0
  21. data/docs/core-features/parameterized-prompts.md +500 -0
  22. data/docs/core-features/shell-integration.md +79 -0
  23. data/docs/development/architecture.md +544 -0
  24. data/docs/development/contributing.md +425 -0
  25. data/docs/development/roadmap.md +234 -0
  26. data/docs/development/testing.md +822 -0
  27. data/docs/examples/advanced.md +523 -0
  28. data/docs/examples/basic.md +688 -0
  29. data/docs/examples/real-world.md +776 -0
  30. data/docs/examples.md +337 -0
  31. data/docs/getting-started/basic-concepts.md +318 -0
  32. data/docs/getting-started/installation.md +97 -0
  33. data/docs/getting-started/quick-start.md +256 -0
  34. data/docs/index.md +230 -0
  35. data/docs/migration/v0.9.0.md +459 -0
  36. data/docs/migration/v1.0.0.md +591 -0
  37. data/docs/storage/activerecord-adapter.md +348 -0
  38. data/docs/storage/custom-adapters.md +176 -0
  39. data/docs/storage/filesystem-adapter.md +236 -0
  40. data/docs/storage/overview.md +427 -0
  41. data/examples/advanced_integrations.rb +52 -0
  42. data/examples/prompts_dir/advanced_demo.txt +79 -0
  43. data/examples/prompts_dir/directive_example.json +1 -0
  44. data/examples/prompts_dir/directive_example.txt +8 -0
  45. data/examples/prompts_dir/todo.json +1 -1
  46. data/improvement_plan.md +996 -0
  47. data/lib/prompt_manager/storage/file_system_adapter.rb +8 -2
  48. data/lib/prompt_manager/version.rb +1 -1
  49. data/mkdocs.yml +146 -0
  50. data/prompt_manager_logo.png +0 -0
  51. metadata +46 -3
  52. data/LICENSE.txt +0 -21
@@ -0,0 +1,236 @@
1
+ # FileSystemAdapter
2
+
3
+ The FileSystemAdapter is the default storage adapter for PromptManager, storing prompts as files in a directory structure.
4
+
5
+ ## Overview
6
+
7
+ The FileSystemAdapter stores prompts as individual text files in a configurable directory. This is the simplest and most common storage method.
8
+
9
+ ## Configuration
10
+
11
+ ### Basic Setup
12
+
13
+ ```ruby
14
+ require 'prompt_manager'
15
+
16
+ # Use default filesystem storage (~/prompts_dir/)
17
+ prompt = PromptManager::Prompt.new(id: 'welcome_message')
18
+ ```
19
+
20
+ ### Custom Directory
21
+
22
+ ```ruby
23
+ PromptManager.configure do |config|
24
+ config.storage = PromptManager::Storage::FileSystemAdapter.new(
25
+ prompts_dir: '/path/to/your/prompts'
26
+ )
27
+ end
28
+ ```
29
+
30
+ ### Multiple Directories
31
+
32
+ ```ruby
33
+ # Search multiple directories in order
34
+ PromptManager.configure do |config|
35
+ config.storage = PromptManager::Storage::FileSystemAdapter.new(
36
+ prompts_dir: [
37
+ '/home/user/project_prompts',
38
+ '/shared/common_prompts',
39
+ '/system/default_prompts'
40
+ ]
41
+ )
42
+ end
43
+ ```
44
+
45
+ ## Directory Structure
46
+
47
+ ### Basic Structure
48
+
49
+ ```
50
+ prompts_dir/
51
+ ├── welcome_message.txt
52
+ ├── error_response.txt
53
+ ├── customer_service/
54
+ │ ├── greeting.txt
55
+ │ └── farewell.txt
56
+ └── templates/
57
+ ├── email_header.txt
58
+ └── email_footer.txt
59
+ ```
60
+
61
+ ### Organizing Prompts
62
+
63
+ ```ruby
64
+ # Access nested prompts using path separators
65
+ customer_greeting = PromptManager::Prompt.new(id: 'customer_service/greeting')
66
+ email_header = PromptManager::Prompt.new(id: 'templates/email_header')
67
+ ```
68
+
69
+ ## File Operations
70
+
71
+ ### Creating Prompts
72
+
73
+ ```ruby
74
+ # Prompts are created as .txt files
75
+ prompt = PromptManager::Prompt.new(id: 'new_prompt')
76
+ prompt.save("Your prompt content here...")
77
+ # Creates: prompts_dir/new_prompt.txt
78
+ ```
79
+
80
+ ### Reading Prompts
81
+
82
+ ```ruby
83
+ # Automatically reads from filesystem
84
+ prompt = PromptManager::Prompt.new(id: 'existing_prompt')
85
+ content = prompt.render
86
+ ```
87
+
88
+ ### Updating Prompts
89
+
90
+ ```ruby
91
+ # Modify the file directly or use save method
92
+ prompt = PromptManager::Prompt.new(id: 'existing_prompt')
93
+ prompt.save("Updated content...")
94
+ ```
95
+
96
+ ### Deleting Prompts
97
+
98
+ ```ruby
99
+ # Remove the prompt file
100
+ prompt = PromptManager::Prompt.new(id: 'old_prompt')
101
+ prompt.delete
102
+ ```
103
+
104
+ ## Advanced Features
105
+
106
+ ### File Extensions
107
+
108
+ The adapter supports different file extensions:
109
+
110
+ ```ruby
111
+ # These all work:
112
+ # - welcome.txt
113
+ # - welcome.md
114
+ # - welcome.prompt
115
+ # - welcome (no extension)
116
+
117
+ prompt = PromptManager::Prompt.new(id: 'welcome')
118
+ # Searches for welcome.txt, then welcome.md, etc.
119
+ ```
120
+
121
+ ### Directory Search Order
122
+
123
+ When using multiple directories, the adapter searches in order:
124
+
125
+ ```ruby
126
+ config.storage = PromptManager::Storage::FileSystemAdapter.new(
127
+ prompts_dir: [
128
+ './project_prompts', # First priority
129
+ '~/shared_prompts', # Second priority
130
+ '/system/prompts' # Last resort
131
+ ]
132
+ )
133
+ ```
134
+
135
+ ### Permissions and Security
136
+
137
+ ```ruby
138
+ # Set directory permissions
139
+ config.storage = PromptManager::Storage::FileSystemAdapter.new(
140
+ prompts_dir: '/secure/prompts',
141
+ file_mode: 0600, # Read/write for owner only
142
+ dir_mode: 0700 # Access for owner only
143
+ )
144
+ ```
145
+
146
+ ## Error Handling
147
+
148
+ ### Common Issues
149
+
150
+ ```ruby
151
+ begin
152
+ prompt = PromptManager::Prompt.new(id: 'missing_prompt')
153
+ rescue PromptManager::PromptNotFoundError => e
154
+ puts "Prompt file not found: #{e.message}"
155
+ end
156
+
157
+ begin
158
+ prompt.save("content")
159
+ rescue PromptManager::StorageError => e
160
+ puts "Cannot write file: #{e.message}"
161
+ # Check permissions, disk space, etc.
162
+ end
163
+ ```
164
+
165
+ ### File System Monitoring
166
+
167
+ ```ruby
168
+ # Watch for file changes (requires additional gems)
169
+ require 'listen'
170
+
171
+ listener = Listen.to('/path/to/prompts') do |modified, added, removed|
172
+ puts "Prompts changed: #{modified + added + removed}"
173
+ # Reload prompts if needed
174
+ end
175
+
176
+ listener.start
177
+ ```
178
+
179
+ ## Performance Considerations
180
+
181
+ ### Caching
182
+
183
+ ```ruby
184
+ # Enable file content caching
185
+ PromptManager.configure do |config|
186
+ config.cache_prompts = true
187
+ config.cache_ttl = 300 # 5 minutes
188
+ end
189
+ ```
190
+
191
+ ### Large Directories
192
+
193
+ For directories with many prompts:
194
+
195
+ ```ruby
196
+ # Use indexing for faster lookups
197
+ config.storage = PromptManager::Storage::FileSystemAdapter.new(
198
+ prompts_dir: '/large/prompt/directory',
199
+ enable_indexing: true,
200
+ index_file: '.prompt_index'
201
+ )
202
+ ```
203
+
204
+ ## Best Practices
205
+
206
+ 1. **Organize by Purpose**: Use subdirectories to group related prompts
207
+ 2. **Consistent Naming**: Use clear, descriptive prompt IDs
208
+ 3. **Version Control**: Store your prompts directory in git
209
+ 4. **Backup Strategy**: Regular backups of your prompts directory
210
+ 5. **File Permissions**: Secure sensitive prompts with appropriate permissions
211
+ 6. **Documentation**: Use comments in prompt files to document purpose and usage
212
+
213
+ ## Migration from Other Storage
214
+
215
+ ### From Database
216
+
217
+ ```ruby
218
+ # Export database prompts to filesystem
219
+ database_adapter.all_prompts.each do |prompt_id, content|
220
+ file_path = File.join(prompts_dir, "#{prompt_id}.txt")
221
+ File.write(file_path, content)
222
+ end
223
+ ```
224
+
225
+ ### Bulk Import
226
+
227
+ ```ruby
228
+ # Import multiple files
229
+ Dir.glob('/old/prompts/*.txt').each do |file_path|
230
+ prompt_id = File.basename(file_path, '.txt')
231
+ content = File.read(file_path)
232
+
233
+ prompt = PromptManager::Prompt.new(id: prompt_id)
234
+ prompt.save(content)
235
+ end
236
+ ```
@@ -0,0 +1,427 @@
1
+ # Storage Adapters Overview
2
+
3
+ Storage adapters are the backbone of PromptManager, providing the interface between your prompts and their persistence layer. They allow you to store prompts in various backends while maintaining a consistent API.
4
+
5
+ ## What are Storage Adapters?
6
+
7
+ Storage adapters implement a common interface that handles:
8
+
9
+ - **Loading** prompt text and parameters
10
+ - **Saving** changes back to storage
11
+ - **Listing** available prompts
12
+ - **Searching** through prompt collections
13
+ - **Managing** prompt metadata
14
+
15
+ Think of them as the "database drivers" for your prompts - they handle the specifics of where and how your prompts are stored.
16
+
17
+ ## Adapter Architecture
18
+
19
+ ```mermaid
20
+ graph TB
21
+ subgraph "Your Application"
22
+ APP[Application Code]
23
+ PROMPT[PromptManager::Prompt]
24
+ end
25
+
26
+ subgraph "Adapter Layer"
27
+ INTERFACE[Storage Adapter Interface]
28
+ FS[FileSystemAdapter]
29
+ AR[ActiveRecordAdapter]
30
+ CUSTOM[Custom Adapters]
31
+ end
32
+
33
+ subgraph "Storage Backends"
34
+ FILES[File System]
35
+ POSTGRES[PostgreSQL]
36
+ MYSQL[MySQL]
37
+ REDIS[Redis]
38
+ S3[AWS S3]
39
+ end
40
+
41
+ APP --> PROMPT
42
+ PROMPT --> INTERFACE
43
+ INTERFACE --> FS
44
+ INTERFACE --> AR
45
+ INTERFACE --> CUSTOM
46
+
47
+ FS --> FILES
48
+ AR --> POSTGRES
49
+ AR --> MYSQL
50
+ CUSTOM --> REDIS
51
+ CUSTOM --> S3
52
+ ```
53
+
54
+ ## Available Adapters
55
+
56
+ PromptManager includes two built-in adapters:
57
+
58
+ ### FileSystemAdapter
59
+ - **Best for**: Development, small teams, file-based workflows
60
+ - **Storage**: Local files (`.txt` for prompts, `.json` for parameters)
61
+ - **Features**: Simple setup, version control friendly, human-readable
62
+ - **Use cases**: Personal projects, documentation, prototype development
63
+
64
+ ### ActiveRecordAdapter
65
+ - **Best for**: Web applications, enterprise deployments, shared access
66
+ - **Storage**: Any database supported by ActiveRecord (PostgreSQL, MySQL, SQLite, etc.)
67
+ - **Features**: Transactions, concurrent access, advanced querying
68
+ - **Use cases**: Production applications, multi-user systems, API backends
69
+
70
+ ## Common Interface
71
+
72
+ All storage adapters implement the same core methods:
73
+
74
+ ```ruby
75
+ module StorageAdapterInterface
76
+ # Load a prompt's text and parameters
77
+ def get(id)
78
+ # Returns: [text_string, parameters_hash]
79
+ end
80
+
81
+ # Save a prompt's text and parameters
82
+ def save(id, text, parameters)
83
+ # Persists changes to storage
84
+ end
85
+
86
+ # Delete a prompt
87
+ def delete(id)
88
+ # Removes prompt from storage
89
+ end
90
+
91
+ # List all available prompt IDs
92
+ def list
93
+ # Returns: array of prompt ID strings
94
+ end
95
+
96
+ # Search for prompts (optional)
97
+ def search(query)
98
+ # Returns: array of matching prompt IDs
99
+ end
100
+ end
101
+ ```
102
+
103
+ ## Choosing an Adapter
104
+
105
+ ### Use FileSystemAdapter When:
106
+
107
+ ✅ **Starting a new project** - Simple setup and configuration
108
+ ✅ **Working solo or small team** - Easy to manage and understand
109
+ ✅ **Version control integration** - Files work well with Git
110
+ ✅ **Human-readable storage** - Can edit prompts directly
111
+ ✅ **Prototyping** - Quick to set up and iterate
112
+
113
+ ❌ **Avoid when**: High concurrency, complex querying, or web deployment
114
+
115
+ ### Use ActiveRecordAdapter When:
116
+
117
+ ✅ **Web applications** - Integrates with existing Rails/web apps
118
+ ✅ **Multi-user systems** - Handles concurrent access properly
119
+ ✅ **Complex queries** - SQL-based searching and filtering
120
+ ✅ **Production deployment** - Scalable and reliable
121
+ ✅ **Advanced features** - Transactions, migrations, backups
122
+
123
+ ❌ **Avoid when**: Simple scripts, file-based workflows, or development experimentation
124
+
125
+ ## Configuration Patterns
126
+
127
+ ### Global Configuration
128
+ Set the adapter once for your entire application:
129
+
130
+ ```ruby
131
+ # At application startup
132
+ PromptManager::Prompt.storage_adapter =
133
+ PromptManager::Storage::FileSystemAdapter.config do |config|
134
+ config.prompts_dir = Rails.root.join('prompts')
135
+ end.new
136
+ ```
137
+
138
+ ### Per-Instance Configuration
139
+ Use different adapters for different prompt types:
140
+
141
+ ```ruby
142
+ # Customer service prompts from filesystem
143
+ customer_service = PromptManager::Prompt.new(id: 'greeting')
144
+ customer_service.storage_adapter = filesystem_adapter
145
+
146
+ # System prompts from database
147
+ system_prompt = PromptManager::Prompt.new(id: 'api_template')
148
+ system_prompt.storage_adapter = activerecord_adapter
149
+ ```
150
+
151
+ ### Environment-Based Configuration
152
+ Different adapters for different environments:
153
+
154
+ ```ruby
155
+ adapter = case Rails.env
156
+ when 'development'
157
+ PromptManager::Storage::FileSystemAdapter.config do |config|
158
+ config.prompts_dir = 'prompts'
159
+ end.new
160
+ when 'production'
161
+ PromptManager::Storage::ActiveRecordAdapter.config do |config|
162
+ config.model = PromptModel
163
+ end.new
164
+ end
165
+
166
+ PromptManager::Prompt.storage_adapter = adapter
167
+ ```
168
+
169
+ ## Adapter Comparison
170
+
171
+ | Feature | FileSystemAdapter | ActiveRecordAdapter | Custom Adapters |
172
+ |---------|-------------------|---------------------|-----------------|
173
+ | **Setup Complexity** | Low | Medium | Varies |
174
+ | **Concurrent Access** | Limited | Excellent | Depends |
175
+ | **Search Performance** | Good with tools | Excellent | Varies |
176
+ | **Version Control** | Excellent | Poor | Varies |
177
+ | **Backup/Recovery** | File-based | Database tools | Depends |
178
+ | **Human Readable** | Yes | No | Depends |
179
+ | **Scalability** | Limited | High | Varies |
180
+ | **Query Complexity** | Limited | High | Varies |
181
+
182
+ ## Migration Between Adapters
183
+
184
+ You can migrate prompts between different adapters:
185
+
186
+ ```ruby
187
+ # Migration helper
188
+ class AdapterMigration
189
+ def self.migrate(from_adapter, to_adapter)
190
+ prompt_ids = from_adapter.list
191
+
192
+ prompt_ids.each do |id|
193
+ text, parameters = from_adapter.get(id)
194
+ to_adapter.save(id, text, parameters)
195
+ puts "Migrated: #{id}"
196
+ end
197
+ end
198
+ end
199
+
200
+ # Usage
201
+ old_adapter = FileSystemAdapter.new(prompts_dir: 'old_prompts')
202
+ new_adapter = ActiveRecordAdapter.new(model: PromptModel)
203
+
204
+ AdapterMigration.migrate(old_adapter, new_adapter)
205
+ ```
206
+
207
+ ## Performance Considerations
208
+
209
+ ### FileSystemAdapter Performance
210
+
211
+ **Strengths:**
212
+ - Fast individual prompt access
213
+ - No database overhead
214
+ - Works with external search tools (ripgrep, grep)
215
+
216
+ **Limitations:**
217
+ - Slow listing operations with many files
218
+ - No built-in indexing
219
+ - Limited concurrent write support
220
+
221
+ **Optimization tips:**
222
+ ```ruby
223
+ # Use custom search for better performance
224
+ adapter.config.search_proc = ->(query) {
225
+ `rg -l "#{query}" #{prompts_dir}`.split("\n").map { |f|
226
+ File.basename(f, '.txt')
227
+ }
228
+ }
229
+ ```
230
+
231
+ ### ActiveRecordAdapter Performance
232
+
233
+ **Strengths:**
234
+ - SQL-based querying and indexing
235
+ - Optimized for concurrent access
236
+ - Built-in caching support
237
+
238
+ **Limitations:**
239
+ - Database connection overhead
240
+ - More complex deployment
241
+
242
+ **Optimization tips:**
243
+ ```ruby
244
+ # Add database indexes
245
+ class CreatePrompts < ActiveRecord::Migration[7.0]
246
+ def change
247
+ create_table :prompts do |t|
248
+ t.string :name, null: false
249
+ t.text :content
250
+ t.json :parameters
251
+ t.timestamps
252
+ end
253
+
254
+ add_index :prompts, :name, unique: true
255
+ add_index :prompts, :created_at
256
+ # Add full-text search index for content
257
+ add_index :prompts, :content, using: :gin if adapter_name == 'PostgreSQL'
258
+ end
259
+ end
260
+ ```
261
+
262
+ ## Error Handling
263
+
264
+ All adapters should handle common error scenarios:
265
+
266
+ ```ruby
267
+ begin
268
+ prompt = PromptManager::Prompt.new(id: 'example')
269
+ text = prompt.to_s
270
+ rescue PromptManager::StorageError => e
271
+ case e.message
272
+ when /not found/
273
+ puts "Prompt doesn't exist: #{e.id}"
274
+ when /permission denied/
275
+ puts "Cannot access storage: #{e.message}"
276
+ when /connection failed/
277
+ puts "Storage backend unavailable: #{e.message}"
278
+ else
279
+ puts "Storage error: #{e.message}"
280
+ end
281
+ end
282
+ ```
283
+
284
+ ## Testing with Adapters
285
+
286
+ ### Test Adapter
287
+ For testing, create a simple in-memory adapter:
288
+
289
+ ```ruby
290
+ class TestAdapter
291
+ def initialize
292
+ @storage = {}
293
+ end
294
+
295
+ def get(id)
296
+ @storage[id] || raise("Prompt not found: #{id}")
297
+ end
298
+
299
+ def save(id, text, parameters)
300
+ @storage[id] = [text, parameters]
301
+ end
302
+
303
+ def list
304
+ @storage.keys
305
+ end
306
+ end
307
+
308
+ # In your tests
309
+ RSpec.describe "PromptManager" do
310
+ let(:adapter) { TestAdapter.new }
311
+
312
+ before do
313
+ PromptManager::Prompt.storage_adapter = adapter
314
+ adapter.save('test', 'Hello [NAME]', { '[NAME]' => 'World' })
315
+ end
316
+
317
+ it "processes prompts correctly" do
318
+ prompt = PromptManager::Prompt.new(id: 'test')
319
+ expect(prompt.to_s).to eq('Hello World')
320
+ end
321
+ end
322
+ ```
323
+
324
+ ### Adapter Testing
325
+ Test your custom adapters with a shared test suite:
326
+
327
+ ```ruby
328
+ # Shared examples for adapter testing
329
+ RSpec.shared_examples "storage adapter" do |adapter_factory|
330
+ let(:adapter) { adapter_factory.call }
331
+
332
+ it "saves and retrieves prompts" do
333
+ adapter.save('test', 'Hello [NAME]', { '[NAME]' => 'World' })
334
+ text, params = adapter.get('test')
335
+
336
+ expect(text).to eq('Hello [NAME]')
337
+ expect(params).to eq({ '[NAME]' => 'World' })
338
+ end
339
+
340
+ it "lists available prompts" do
341
+ adapter.save('test1', 'Content 1', {})
342
+ adapter.save('test2', 'Content 2', {})
343
+
344
+ expect(adapter.list).to contain_exactly('test1', 'test2')
345
+ end
346
+
347
+ it "raises error for missing prompts" do
348
+ expect { adapter.get('missing') }.to raise_error(PromptManager::StorageError)
349
+ end
350
+ end
351
+
352
+ # Test your adapters
353
+ RSpec.describe FileSystemAdapter do
354
+ include_examples "storage adapter", -> {
355
+ FileSystemAdapter.new(prompts_dir: temp_dir)
356
+ }
357
+ end
358
+ ```
359
+
360
+ ## Best Practices
361
+
362
+ ### 1. Configuration Management
363
+ ```ruby
364
+ # Use environment variables for sensitive config
365
+ database_config = {
366
+ host: ENV.fetch('DATABASE_HOST', 'localhost'),
367
+ password: ENV['DATABASE_PASSWORD'],
368
+ pool: ENV.fetch('DATABASE_POOL', 5).to_i
369
+ }
370
+ ```
371
+
372
+ ### 2. Error Recovery
373
+ ```ruby
374
+ class ResilientAdapter
375
+ def initialize(primary, fallback)
376
+ @primary = primary
377
+ @fallback = fallback
378
+ end
379
+
380
+ def get(id)
381
+ @primary.get(id)
382
+ rescue => e
383
+ Rails.logger.warn "Primary adapter failed: #{e.message}"
384
+ @fallback.get(id)
385
+ end
386
+ end
387
+ ```
388
+
389
+ ### 3. Caching Layer
390
+ ```ruby
391
+ class CachingAdapter
392
+ def initialize(adapter, cache_store = Rails.cache)
393
+ @adapter = adapter
394
+ @cache = cache_store
395
+ end
396
+
397
+ def get(id)
398
+ @cache.fetch("prompt:#{id}", expires_in: 1.hour) do
399
+ @adapter.get(id)
400
+ end
401
+ end
402
+ end
403
+ ```
404
+
405
+ ### 4. Monitoring and Logging
406
+ ```ruby
407
+ class InstrumentedAdapter
408
+ def initialize(adapter)
409
+ @adapter = adapter
410
+ end
411
+
412
+ def get(id)
413
+ start_time = Time.current
414
+ result = @adapter.get(id)
415
+ duration = Time.current - start_time
416
+
417
+ Rails.logger.info "Prompt loaded: #{id} (#{duration}s)"
418
+ result
419
+ end
420
+ end
421
+ ```
422
+
423
+ ## Next Steps
424
+
425
+ - **Learn specific adapters**: [FileSystemAdapter](filesystem-adapter.md) | [ActiveRecordAdapter](activerecord-adapter.md)
426
+ - **Build custom adapters**: [Custom Adapters Guide](custom-adapters.md)
427
+ - **See examples**: [Real-world Usage](../examples/real-world.md)
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/inline'
5
+
6
+ gemfile do
7
+ source 'https://rubygems.org'
8
+ gem 'prompt_manager'
9
+ gem 'ruby-openai'
10
+ gem 'tty-spinner'
11
+ end
12
+
13
+ require 'prompt_manager'
14
+ require 'openai'
15
+ require 'erb'
16
+ require 'time'
17
+ require 'tty-spinner'
18
+
19
+ # Configure PromptManager with filesystem adapter
20
+ PromptManager::Prompt.storage_adapter = PromptManager::Storage::FileSystemAdapter.config do |config|
21
+ config.prompts_dir = File.join(__dir__, 'prompts_dir')
22
+ end.new
23
+
24
+ # Configure OpenAI client
25
+ client = OpenAI::Client.new(
26
+ access_token: ENV['OPENAI_API_KEY']
27
+ )
28
+
29
+ # Get prompt instance and process with LLM
30
+ prompt = PromptManager::Prompt.new(
31
+ id: 'advanced_demo',
32
+ erb_flag: true,
33
+ envar_flag: true
34
+ )
35
+
36
+ spinner = TTY::Spinner.new("[:spinner] Waiting for response...")
37
+ spinner.auto_spin
38
+
39
+ response = client.chat(
40
+ parameters: {
41
+ model: 'gpt-4o-mini',
42
+ messages: [{ role: 'user', content: prompt.to_s }],
43
+ stream: proc do |chunk, _bytesize|
44
+ spinner.stop
45
+ content = chunk.dig("choices", 0, "delta", "content")
46
+ print content if content
47
+ $stdout.flush
48
+ end
49
+ }
50
+ )
51
+
52
+ puts