smart_prompt 0.5.0 → 0.5.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.
@@ -0,0 +1,559 @@
1
+ # Anthropic Claude Examples for SmartPrompt
2
+
3
+ This document provides comprehensive examples for using Anthropic's Claude models with SmartPrompt. Claude offers powerful capabilities including advanced reasoning, multimodal understanding (text + images), tool calling, and streaming responses.
4
+
5
+ ## Table of Contents
6
+
7
+ 1. [Setup and Configuration](#setup-and-configuration)
8
+ 2. [Basic Chat Examples](#basic-chat-examples)
9
+ 3. [Multimodal Examples (Vision)](#multimodal-examples-vision)
10
+ 4. [Tool Calling Examples](#tool-calling-examples)
11
+ 5. [Streaming Response Examples](#streaming-response-examples)
12
+ 6. [Advanced Usage](#advanced-usage)
13
+ 7. [Best Practices](#best-practices)
14
+
15
+ ## Setup and Configuration
16
+
17
+ ### 1. Install Dependencies
18
+
19
+ Ensure you have the `anthropic` gem installed:
20
+
21
+ ```ruby
22
+ gem 'anthropic', '~> 1.14'
23
+ ```
24
+
25
+ ### 2. Set API Key
26
+
27
+ Set your Anthropic API key as an environment variable:
28
+
29
+ ```bash
30
+ export ANTHROPIC_API_KEY='your-api-key-here'
31
+ ```
32
+
33
+ ### 3. Configure SmartPrompt
34
+
35
+ Create or update `config/anthropic_config.yml`:
36
+
37
+ ```yaml
38
+ adapters:
39
+ anthropic: "AnthropicAdapter"
40
+
41
+ llms:
42
+ claude:
43
+ adapter: "anthropic"
44
+ api_key: ENV["ANTHROPIC_API_KEY"]
45
+ model: "claude-3-5-sonnet-20241022"
46
+ temperature: 0.7
47
+ max_tokens: 4096
48
+
49
+ claude_haiku:
50
+ adapter: "anthropic"
51
+ api_key: ENV["ANTHROPIC_API_KEY"]
52
+ model: "claude-3-5-haiku-20241022"
53
+ temperature: 0.7
54
+ max_tokens: 4096
55
+
56
+ claude_opus:
57
+ adapter: "anthropic"
58
+ api_key: ENV["ANTHROPIC_API_KEY"]
59
+ model: "claude-3-opus-20240229"
60
+ temperature: 0.7
61
+ max_tokens: 4096
62
+ ```
63
+
64
+ ## Basic Chat Examples
65
+
66
+ ### Simple Question-Answer
67
+
68
+ ```ruby
69
+ require 'smart_prompt'
70
+
71
+ engine = SmartPrompt::Engine.new('config/anthropic_config.yml')
72
+
73
+ SmartPrompt.define_worker :simple_chat do
74
+ use "claude"
75
+ sys_msg("You are a helpful AI assistant.")
76
+ prompt(params[:message])
77
+ send_msg
78
+ end
79
+
80
+ response = engine.call_worker(:simple_chat, {
81
+ message: "What is the capital of France?"
82
+ })
83
+ puts response
84
+ ```
85
+
86
+ **Run the example:**
87
+ ```bash
88
+ ruby examples/anthropic_basic_chat.rb
89
+ ```
90
+
91
+ ### Multi-turn Conversation
92
+
93
+ ```ruby
94
+ SmartPrompt.define_worker :conversation do
95
+ use "claude"
96
+ sys_msg("You are a knowledgeable history teacher.")
97
+ prompt(params[:message], with_history: true)
98
+ send_msg
99
+ end
100
+
101
+ # First message
102
+ response1 = engine.call_worker(:conversation, {
103
+ message: "Who was the first president of the United States?"
104
+ })
105
+
106
+ # Follow-up message (maintains context)
107
+ response2 = engine.call_worker(:conversation, {
108
+ message: "What were his major accomplishments?"
109
+ })
110
+ ```
111
+
112
+ ### Using Different Models
113
+
114
+ ```ruby
115
+ # Claude 3.5 Sonnet - Best balance of intelligence and speed
116
+ SmartPrompt.define_worker :sonnet_chat do
117
+ use "claude"
118
+ model "claude-3-5-sonnet-20241022"
119
+ prompt(params[:message])
120
+ send_msg
121
+ end
122
+
123
+ # Claude 3.5 Haiku - Fastest, most cost-effective
124
+ SmartPrompt.define_worker :haiku_chat do
125
+ use "claude_haiku"
126
+ model "claude-3-5-haiku-20241022"
127
+ prompt(params[:message])
128
+ send_msg
129
+ end
130
+
131
+ # Claude 3 Opus - Highest quality for complex tasks
132
+ SmartPrompt.define_worker :opus_chat do
133
+ use "claude_opus"
134
+ model "claude-3-opus-20240229"
135
+ prompt(params[:message])
136
+ send_msg
137
+ end
138
+ ```
139
+
140
+ ### Temperature Control
141
+
142
+ ```ruby
143
+ # Creative writing (high temperature)
144
+ SmartPrompt.define_worker :creative_chat do
145
+ use "claude_creative" # temperature: 0.9
146
+ sys_msg("You are a creative writer.")
147
+ prompt("Write a creative tagline for a coffee shop.")
148
+ send_msg
149
+ end
150
+
151
+ # Precise analysis (low temperature)
152
+ SmartPrompt.define_worker :precise_chat do
153
+ use "claude_precise" # temperature: 0.3
154
+ sys_msg("You are a precise analyst.")
155
+ prompt("Analyze this data and provide key insights.")
156
+ send_msg
157
+ end
158
+ ```
159
+
160
+ ## Multimodal Examples (Vision)
161
+
162
+ Claude can analyze images alongside text. Supported formats: JPEG, PNG, GIF, WebP (up to 5MB).
163
+
164
+ ### Analyze Image from URL
165
+
166
+ ```ruby
167
+ SmartPrompt.define_worker :image_analyzer do
168
+ use "claude"
169
+ sys_msg("You are an expert at analyzing images.")
170
+ prompt(params[:message])
171
+ send_msg
172
+ end
173
+
174
+ response = engine.call_worker(:image_analyzer, {
175
+ message: [
176
+ { type: "text", text: "What do you see in this image?" },
177
+ { type: "image_url", image_url: "https://example.com/image.jpg" }
178
+ ]
179
+ })
180
+ ```
181
+
182
+ **Run the example:**
183
+ ```bash
184
+ ruby examples/anthropic_multimodal.rb
185
+ ```
186
+
187
+ ### Analyze Local Image (Base64)
188
+
189
+ ```ruby
190
+ require 'base64'
191
+
192
+ # Read and encode image
193
+ image_data = File.binread("./path/to/image.jpg")
194
+ base64_image = Base64.strict_encode64(image_data)
195
+ data_url = "data:image/jpeg;base64,#{base64_image}"
196
+
197
+ response = engine.call_worker(:image_analyzer, {
198
+ message: [
199
+ { type: "text", text: "Describe this image in detail." },
200
+ { type: "image_url", image_url: data_url }
201
+ ]
202
+ })
203
+ ```
204
+
205
+ ### Compare Multiple Images
206
+
207
+ ```ruby
208
+ response = engine.call_worker(:image_analyzer, {
209
+ message: [
210
+ { type: "text", text: "Compare these two images." },
211
+ { type: "image_url", image_url: "https://example.com/image1.jpg" },
212
+ { type: "image_url", image_url: "https://example.com/image2.jpg" }
213
+ ]
214
+ })
215
+ ```
216
+
217
+ ### OCR and Text Extraction
218
+
219
+ ```ruby
220
+ SmartPrompt.define_worker :ocr_extractor do
221
+ use "claude"
222
+ sys_msg("You are an expert at reading text from images.")
223
+ prompt(params[:message])
224
+ send_msg
225
+ end
226
+
227
+ response = engine.call_worker(:ocr_extractor, {
228
+ message: [
229
+ { type: "text", text: "Extract all text from this image." },
230
+ { type: "image_url", image_url: "https://example.com/document.jpg" }
231
+ ]
232
+ })
233
+ ```
234
+
235
+ ### Product Image Analysis
236
+
237
+ ```ruby
238
+ SmartPrompt.define_worker :product_analyzer do
239
+ use "claude"
240
+ sys_msg("You are a product analyst for e-commerce.")
241
+ prompt(params[:message])
242
+ send_msg
243
+ end
244
+
245
+ response = engine.call_worker(:product_analyzer, {
246
+ message: [
247
+ { type: "text", text: "Analyze this product image and provide:\n1. Product description\n2. Key features\n3. Suggested title" },
248
+ { type: "image_url", image_url: "https://example.com/product.jpg" }
249
+ ]
250
+ })
251
+ ```
252
+
253
+ ## Tool Calling Examples
254
+
255
+ Claude can use external tools/functions to perform actions or retrieve information.
256
+
257
+ ### Simple Weather Tool
258
+
259
+ ```ruby
260
+ weather_tool = [
261
+ {
262
+ type: "function",
263
+ function: {
264
+ name: "get_weather",
265
+ description: "Get the current weather for a location",
266
+ parameters: {
267
+ type: "object",
268
+ properties: {
269
+ location: {
270
+ type: "string",
271
+ description: "The city and state, e.g. San Francisco, CA"
272
+ },
273
+ unit: {
274
+ type: "string",
275
+ enum: ["celsius", "fahrenheit"],
276
+ description: "Temperature unit"
277
+ }
278
+ },
279
+ required: ["location"]
280
+ }
281
+ }
282
+ }
283
+ ]
284
+
285
+ SmartPrompt.define_worker :weather_assistant do
286
+ use "claude"
287
+ sys_msg("You are a weather assistant. Use the get_weather tool.")
288
+ prompt(params[:message])
289
+ params.merge(tools: weather_tool)
290
+ send_msg
291
+ end
292
+
293
+ response = engine.call_worker(:weather_assistant, {
294
+ message: "What's the weather in Tokyo?"
295
+ })
296
+ ```
297
+
298
+ **Run the example:**
299
+ ```bash
300
+ ruby examples/anthropic_tool_calling.rb
301
+ ```
302
+
303
+ ### Multiple Calculator Tools
304
+
305
+ ```ruby
306
+ calculator_tools = [
307
+ {
308
+ type: "function",
309
+ function: {
310
+ name: "add",
311
+ description: "Add two numbers",
312
+ parameters: {
313
+ type: "object",
314
+ properties: {
315
+ a: { type: "number" },
316
+ b: { type: "number" }
317
+ },
318
+ required: ["a", "b"]
319
+ }
320
+ }
321
+ },
322
+ {
323
+ type: "function",
324
+ function: {
325
+ name: "multiply",
326
+ description: "Multiply two numbers",
327
+ parameters: {
328
+ type: "object",
329
+ properties: {
330
+ a: { type: "number" },
331
+ b: { type: "number" }
332
+ },
333
+ required: ["a", "b"]
334
+ }
335
+ }
336
+ }
337
+ ]
338
+
339
+ SmartPrompt.define_worker :calculator do
340
+ use "claude"
341
+ sys_msg("You are a calculator assistant.")
342
+ prompt(params[:message])
343
+ params.merge(tools: calculator_tools)
344
+ send_msg
345
+ end
346
+ ```
347
+
348
+ ### Database Query Tool
349
+
350
+ ```ruby
351
+ database_tool = [
352
+ {
353
+ type: "function",
354
+ function: {
355
+ name: "query_database",
356
+ description: "Query the customer database",
357
+ parameters: {
358
+ type: "object",
359
+ properties: {
360
+ query_type: {
361
+ type: "string",
362
+ enum: ["customer_info", "order_history", "product_details"]
363
+ },
364
+ customer_id: { type: "string" }
365
+ },
366
+ required: ["query_type"]
367
+ }
368
+ }
369
+ }
370
+ ]
371
+ ```
372
+
373
+ ### Complex Tool with Nested Parameters
374
+
375
+ ```ruby
376
+ search_tool = [
377
+ {
378
+ type: "function",
379
+ function: {
380
+ name: "search_products",
381
+ description: "Search products with filters",
382
+ parameters: {
383
+ type: "object",
384
+ properties: {
385
+ query: { type: "string" },
386
+ filters: {
387
+ type: "object",
388
+ properties: {
389
+ category: { type: "string" },
390
+ price_range: {
391
+ type: "object",
392
+ properties: {
393
+ min: { type: "number" },
394
+ max: { type: "number" }
395
+ }
396
+ },
397
+ in_stock: { type: "boolean" }
398
+ }
399
+ },
400
+ sort_by: {
401
+ type: "string",
402
+ enum: ["price_asc", "price_desc", "popularity"]
403
+ }
404
+ },
405
+ required: ["query"]
406
+ }
407
+ }
408
+ }
409
+ ]
410
+ ```
411
+
412
+ ## Streaming Response Examples
413
+
414
+ Streaming provides better user experience by showing responses as they're generated.
415
+
416
+ ### Basic Streaming
417
+
418
+ ```ruby
419
+ SmartPrompt.define_worker :streaming_chat do
420
+ use "claude"
421
+ sys_msg("You are a helpful assistant.")
422
+ prompt(params[:message])
423
+ send_msg
424
+ end
425
+
426
+ engine.call_worker_by_stream(:streaming_chat, {
427
+ message: "Tell me a story about a brave knight."
428
+ }) do |chunk, bytesize|
429
+ if chunk.is_a?(Hash) && chunk["type"] == "content_block_delta"
430
+ text = chunk.dig("delta", "text")
431
+ print text if text
432
+ end
433
+ end
434
+ ```
435
+
436
+ **Run the example:**
437
+ ```bash
438
+ ruby examples/anthropic_streaming.rb
439
+ ```
440
+
441
+ ### Handling Different Event Types
442
+
443
+ ```ruby
444
+ engine.call_worker_by_stream(:streaming_chat, {
445
+ message: "Explain photosynthesis."
446
+ }) do |chunk, bytesize|
447
+ if chunk.is_a?(Hash)
448
+ case chunk["type"]
449
+ when "message_start"
450
+ # Message started
451
+ puts "[Starting response...]"
452
+ when "content_block_start"
453
+ # Content block started
454
+ when "content_block_delta"
455
+ # Incremental text
456
+ text = chunk.dig("delta", "text")
457
+ print text if text
458
+ when "content_block_stop"
459
+ # Content block finished
460
+ when "message_stop"
461
+ # Message complete
462
+ puts "\n[Response complete]"
463
+ end
464
+ end
465
+ end
466
+ ```
467
+
468
+ ### Streaming with Progress Tracking
469
+
470
+ ```ruby
471
+ char_count = 0
472
+
473
+ engine.call_worker_by_stream(:streaming_chat, {
474
+ message: "Write a Python function for Fibonacci."
475
+ }) do |chunk, bytesize|
476
+ if chunk.is_a?(Hash) && chunk["type"] == "content_block_delta"
477
+ text = chunk.dig("delta", "text")
478
+ if text
479
+ print text
480
+ char_count += text.length
481
+ end
482
+ end
483
+ end
484
+
485
+ puts "\n[Total characters: #{char_count}]"
486
+ ```
487
+
488
+ ### Streaming with Error Handling
489
+
490
+ ```ruby
491
+ begin
492
+ engine.call_worker_by_stream(:streaming_chat, {
493
+ message: "What are the benefits of exercise?"
494
+ }) do |chunk, bytesize|
495
+ if chunk.is_a?(Hash) && chunk["type"] == "content_block_delta"
496
+ text = chunk.dig("delta", "text")
497
+ print text if text
498
+ end
499
+ end
500
+ puts "\n[Stream completed successfully]"
501
+ rescue SmartPrompt::LLMAPIError => e
502
+ puts "\n[Error: #{e.message}]"
503
+ end
504
+ ```
505
+
506
+ ## Advanced Usage
507
+
508
+ ### Custom Endpoint (Proxy or Private Deployment)
509
+
510
+ ```yaml
511
+ llms:
512
+ claude_custom:
513
+ adapter: "anthropic"
514
+ api_key: ENV["ANTHROPIC_API_KEY"]
515
+ url: "https://your-custom-endpoint.com"
516
+ model: "claude-3-5-sonnet-20241022"
517
+ ```
518
+
519
+ ### Combining Multimodal and Tool Calling
520
+
521
+ ```ruby
522
+ tools = [
523
+ {
524
+ type: "function",
525
+ function: {
526
+ name: "identify_object",
527
+ description: "Identify objects in the image",
528
+ parameters: {
529
+ type: "object",
530
+ properties: {
531
+ object_name: { type: "string" }
532
+ },
533
+ required: ["object_name"]
534
+ }
535
+ }
536
+ }
537
+ ]
538
+
539
+ SmartPrompt.define_worker :multimodal_tools do
540
+ use "claude"
541
+ sys_msg("You can analyze images and use tools.")
542
+ prompt(params[:message])
543
+ params.merge(tools: tools)
544
+ send_msg
545
+ end
546
+
547
+ response = engine.call_worker(:multimodal_tools, {
548
+ message: [
549
+ { type: "text", text: "What objects do you see? Use the identify_object tool." },
550
+ { type: "image_url", image_url: "https://example.com/scene.jpg" }
551
+ ]
552
+ })
553
+ ```
554
+
555
+ ### Conversation with Mixed Content
556
+
557
+ ```ruby
558
+ SmartPrompt.define_worker :mixed_conversation do
559
+ use "cla
@@ -0,0 +1,155 @@
1
+ # Conversation Integration with HistoryManager - Implementation Summary
2
+
3
+ ## Overview
4
+ Successfully integrated the HistoryManager with the Conversation class to provide session-based history management while maintaining backward compatibility with the existing `with_history` parameter.
5
+
6
+ ## Changes Made
7
+
8
+ ### 1. Conversation Class (`lib/smart_prompt/conversation.rb`)
9
+
10
+ #### Modified `initialize` method:
11
+ - Added optional `session_id` parameter
12
+ - Added `@session_id` and `@use_history_manager` instance variables
13
+
14
+ #### Modified `history_messages` method:
15
+ - Now checks if HistoryManager is available and being used
16
+ - If using HistoryManager, retrieves messages from the session and converts them to hash format
17
+ - Falls back to old implementation (`@engine.history_messages`) if HistoryManager is not available
18
+
19
+ #### Modified `add_message` method:
20
+ - When `with_history` is true and HistoryManager is available:
21
+ - Sets `@use_history_manager` flag to true
22
+ - Generates a default session ID if none exists
23
+ - Adds message to HistoryManager session
24
+ - Falls back to old implementation if HistoryManager is not available
25
+ - Always adds message to local `@messages` array for current conversation
26
+
27
+ #### Added `generate_default_session_id` private method:
28
+ - Generates unique session IDs in format: `default_{timestamp}_{random}`
29
+
30
+ ### 2. Engine Class (`lib/smart_prompt/engine.rb`)
31
+
32
+ #### Added `history_manager` attribute reader:
33
+ - Exposes HistoryManager instance to other components
34
+
35
+ #### Modified `initialize` method:
36
+ - Added `@history_manager = nil` initialization
37
+
38
+ #### Modified `load_config` method:
39
+ - Checks for `history` configuration section
40
+ - Initializes HistoryManager if configuration is present
41
+ - Logs initialization success
42
+
43
+ ### 3. Worker Class (`lib/smart_prompt/worker.rb`)
44
+
45
+ #### Modified `execute` method:
46
+ - Generates default session ID when:
47
+ - `with_history` is true
48
+ - No `session_id` is provided in params
49
+ - HistoryManager is available
50
+ - Session ID format: `worker_{worker_name}_{timestamp}`
51
+ - Passes session ID to Conversation constructor
52
+
53
+ #### Modified `execute_by_stream` method:
54
+ - Same session ID generation logic as `execute`
55
+ - Ensures streaming workers also support session management
56
+
57
+ ## Backward Compatibility
58
+
59
+ ### Maintained Features:
60
+ 1. **`with_history` parameter**: Still works as before
61
+ 2. **Old history implementation**: Falls back automatically when HistoryManager is not configured
62
+ 3. **Existing worker definitions**: No changes required to existing workers
63
+ 4. **API compatibility**: All existing methods maintain their signatures
64
+
65
+ ### Migration Path:
66
+ 1. **Without configuration**: System uses old `@engine.history_messages` array
67
+ 2. **With configuration**: System automatically uses HistoryManager
68
+ 3. **Explicit session IDs**: Can be provided via `session_id` parameter for fine-grained control
69
+
70
+ ## Configuration Example
71
+
72
+ To enable HistoryManager, add to your config YAML:
73
+
74
+ ```yaml
75
+ history:
76
+ cache_size: 100
77
+ session_defaults:
78
+ max_messages: 50
79
+ max_tokens: 2000
80
+ persistence:
81
+ enabled: true
82
+ storage_path: "./history_data"
83
+ ```
84
+
85
+ ## Usage Examples
86
+
87
+ ### 1. Basic Usage with Auto-Generated Session:
88
+ ```ruby
89
+ conversation = SmartPrompt::Conversation.new(engine)
90
+ conversation.add_message({ role: "user", content: "Hello" }, true)
91
+ # Session ID is auto-generated
92
+ ```
93
+
94
+ ### 2. Explicit Session ID:
95
+ ```ruby
96
+ conversation = SmartPrompt::Conversation.new(engine, nil, "my_session_123")
97
+ conversation.add_message({ role: "user", content: "Hello" }, true)
98
+ # Uses "my_session_123"
99
+ ```
100
+
101
+ ### 3. Worker with Default Session:
102
+ ```ruby
103
+ engine.call_worker("my_worker", with_history: true)
104
+ # Session ID: "worker_my_worker_{timestamp}"
105
+ ```
106
+
107
+ ### 4. Worker with Custom Session:
108
+ ```ruby
109
+ engine.call_worker("my_worker", with_history: true, session_id: "custom_session")
110
+ # Uses "custom_session"
111
+ ```
112
+
113
+ ## Testing
114
+
115
+ Created comprehensive integration tests:
116
+
117
+ ### `test/conversation_integration_test.rb`:
118
+ - Tests Conversation with HistoryManager
119
+ - Tests Conversation without HistoryManager (backward compatibility)
120
+ - Tests backward compatibility with `with_history` parameter
121
+ - Tests default session creation
122
+ - Tests session isolation between conversations
123
+ - Tests message format conversion
124
+
125
+ ### `test/worker_history_integration_test.rb`:
126
+ - Tests worker with default session creation
127
+ - Tests worker with explicit session ID
128
+ - Tests worker without history
129
+ - Tests multiple worker calls to same session
130
+ - Tests worker session isolation
131
+
132
+ All tests pass successfully, confirming:
133
+ - ✅ Integration works correctly
134
+ - ✅ Backward compatibility is maintained
135
+ - ✅ Session isolation is enforced
136
+ - ✅ Default session creation works
137
+ - ✅ Existing tests continue to pass
138
+
139
+ ## Requirements Validated
140
+
141
+ This implementation satisfies the following requirements from the spec:
142
+
143
+ - **Requirement 5.1**: Supports existing `with_history: true` parameter
144
+ - **Requirement 5.2**: Creates default session for workers when no session ID provided
145
+ - **Requirement 5.4**: Maintains backward compatibility with existing API
146
+
147
+ ## Next Steps
148
+
149
+ The integration is complete and ready for use. The next task in the implementation plan is:
150
+
151
+ **Task 14**: Integrate with Engine class
152
+ - Add HistoryManager initialization to Engine ✅ (Already done)
153
+ - Add history configuration loading from YAML ✅ (Already done)
154
+ - Expose history_manager accessor ✅ (Already done)
155
+ - Add deprecation warning for old history_messages (Optional)