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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +18 -2
- data/README.cn.md +55 -4
- data/README.md +55 -4
- data/docs/ANTHROPIC_EXAMPLES.md +559 -0
- data/docs/CONVERSATION_INTEGRATION_SUMMARY.md +155 -0
- data/docs/HISTORY_EXAMPLES_README.md +533 -0
- data/docs/HISTORY_MANAGEMENT_GUIDE.md +797 -0
- data/docs/MONITORING_GUIDE.md +278 -0
- data/docs/MULTIMODAL_README.md +265 -0
- data/docs/RELEVANCE_BASED_STRATEGY_IMPLEMENTATION.md +124 -0
- data/docs/STT_README.md +302 -0
- data/docs/TTS_README.md +303 -0
- data/docs/VIDEO_GENERATION_README.md +246 -0
- data/docs/delete_files_list.md +124 -0
- data/lib/smart_prompt/anthropic_adapter.rb +167 -140
- data/lib/smart_prompt/conversation.rb +195 -42
- data/lib/smart_prompt/engine.rb +20 -10
- data/lib/smart_prompt/openai_adapter.rb +25 -1
- data/lib/smart_prompt/version.rb +1 -1
- data/lib/smart_prompt/worker.rb +5 -2
- data/lib/smart_prompt.rb +2 -1
- metadata +33 -22
|
@@ -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)
|