smart_prompt 0.4.4 → 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.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +16 -0
  3. data/README.cn.md +305 -11
  4. data/README.md +309 -11
  5. data/Rakefile +10 -1
  6. data/config/anthropic_config.yml +151 -0
  7. data/config/image_generation_config.yml +22 -0
  8. data/config/multimodal_config.yml +85 -0
  9. data/config/sensenova_config.yml +63 -0
  10. data/config/zhipu_config.yml +73 -0
  11. data/docs/ANTHROPIC_EXAMPLES.md +559 -0
  12. data/docs/CONVERSATION_INTEGRATION_SUMMARY.md +155 -0
  13. data/docs/HISTORY_EXAMPLES_README.md +533 -0
  14. data/docs/HISTORY_MANAGEMENT_GUIDE.md +797 -0
  15. data/docs/MONITORING_GUIDE.md +278 -0
  16. data/docs/MULTIMODAL_README.md +265 -0
  17. data/docs/RELEVANCE_BASED_STRATEGY_IMPLEMENTATION.md +124 -0
  18. data/docs/STT_README.md +302 -0
  19. data/docs/TTS_README.md +303 -0
  20. data/docs/VIDEO_GENERATION_README.md +246 -0
  21. data/docs/delete_files_list.md +124 -0
  22. data/examples/anthropic_basic_chat.rb +143 -0
  23. data/examples/anthropic_example.rb +232 -0
  24. data/examples/anthropic_multimodal.rb +212 -0
  25. data/examples/anthropic_streaming.rb +312 -0
  26. data/examples/anthropic_tool_calling.rb +393 -0
  27. data/examples/automatic_cleanup_example.rb +109 -0
  28. data/examples/history_management_examples.rb +522 -0
  29. data/examples/image_generation_example.rb +130 -0
  30. data/examples/monitoring_example.rb +121 -0
  31. data/examples/multimodal_example.rb +63 -0
  32. data/examples/relevance_based_strategy_example.rb +87 -0
  33. data/examples/sensenova_example.rb +129 -0
  34. data/examples/stt_example.rb +287 -0
  35. data/examples/tts_example.rb +244 -0
  36. data/examples/video_generation_example.rb +189 -0
  37. data/examples/zhipu_example.rb +151 -0
  38. data/lib/smart_prompt/anthropic_adapter.rb +407 -298
  39. data/lib/smart_prompt/compression_engine.rb +201 -0
  40. data/lib/smart_prompt/context_strategy.rb +22 -0
  41. data/lib/smart_prompt/conversation.rb +47 -4
  42. data/lib/smart_prompt/engine.rb +29 -2
  43. data/lib/smart_prompt/history_manager.rb +596 -0
  44. data/lib/smart_prompt/hybrid_strategy.rb +222 -0
  45. data/lib/smart_prompt/image_generation_adapter.rb +297 -0
  46. data/lib/smart_prompt/lru_cache.rb +133 -0
  47. data/lib/smart_prompt/message.rb +57 -0
  48. data/lib/smart_prompt/multimodal_adapter.rb +277 -0
  49. data/lib/smart_prompt/persistence_layer.rb +197 -0
  50. data/lib/smart_prompt/relevance_based_strategy.rb +221 -0
  51. data/lib/smart_prompt/sensenova_adapter.rb +410 -0
  52. data/lib/smart_prompt/session.rb +140 -0
  53. data/lib/smart_prompt/sliding_window_strategy.rb +100 -0
  54. data/lib/smart_prompt/stt_adapter.rb +381 -0
  55. data/lib/smart_prompt/summary_based_strategy.rb +152 -0
  56. data/lib/smart_prompt/token_counter.rb +74 -0
  57. data/lib/smart_prompt/tts_adapter.rb +403 -0
  58. data/lib/smart_prompt/version.rb +1 -1
  59. data/lib/smart_prompt/video_generation_adapter.rb +330 -0
  60. data/lib/smart_prompt/worker.rb +28 -3
  61. data/lib/smart_prompt/zhipu_adapter.rb +616 -0
  62. data/lib/smart_prompt.rb +21 -0
  63. data/workers/history_management_examples.rb +407 -0
  64. data/workers/image_generation_workers.rb +119 -0
  65. data/workers/multimodal_workers.rb +110 -0
  66. data/workers/sensenova_workers.rb +62 -0
  67. data/workers/stt_workers.rb +195 -0
  68. data/workers/tts_workers.rb +388 -0
  69. data/workers/video_generation_workers.rb +264 -0
  70. data/workers/zhipu_workers.rb +113 -0
  71. metadata +88 -1
@@ -0,0 +1,312 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require './lib/smart_prompt'
5
+
6
+ # Example: Streaming Responses with Anthropic Claude
7
+ # This example demonstrates how to use Claude's streaming capabilities
8
+
9
+ puts "=" * 60
10
+ puts "Anthropic Claude - Streaming Response Example"
11
+ puts "=" * 60
12
+
13
+ # Initialize the engine with Anthropic configuration
14
+ engine = SmartPrompt::Engine.new('config/anthropic_config.yml')
15
+
16
+ # Example 1: Basic Streaming
17
+ puts "\n1. Basic Streaming Response"
18
+ puts "-" * 60
19
+
20
+ SmartPrompt.define_worker :streaming_chat do
21
+ use "claude"
22
+ sys_msg("You are a helpful assistant.")
23
+ prompt(params[:message])
24
+ send_msg
25
+ end
26
+
27
+ print "User: Tell me a short story about a brave knight.\n"
28
+ print "Claude (streaming): "
29
+
30
+ engine.call_worker_by_stream(:streaming_chat, {
31
+ message: "Tell me a short story about a brave knight."
32
+ }) do |chunk, bytesize|
33
+ # Handle Anthropic streaming format
34
+ if chunk.is_a?(Hash)
35
+ if chunk["type"] == "content_block_delta"
36
+ text = chunk.dig("delta", "text")
37
+ print text if text
38
+ end
39
+ end
40
+ end
41
+
42
+ puts "\n"
43
+
44
+ # Example 2: Streaming with Different Event Types
45
+ puts "\n2. Streaming with Event Type Handling"
46
+ puts "-" * 60
47
+
48
+ SmartPrompt.define_worker :detailed_streaming do
49
+ use "claude"
50
+ sys_msg("You are a knowledgeable teacher.")
51
+ prompt(params[:message])
52
+ send_msg
53
+ end
54
+
55
+ print "User: Explain how photosynthesis works.\n"
56
+ print "Claude: "
57
+
58
+ message_started = false
59
+ content_started = false
60
+
61
+ engine.call_worker_by_stream(:detailed_streaming, {
62
+ message: "Explain how photosynthesis works in 3-4 sentences."
63
+ }) do |chunk, bytesize|
64
+ if chunk.is_a?(Hash)
65
+ case chunk["type"]
66
+ when "message_start"
67
+ message_started = true
68
+ # Message metadata available in chunk["message"]
69
+ when "content_block_start"
70
+ content_started = true
71
+ # Content block started
72
+ when "content_block_delta"
73
+ text = chunk.dig("delta", "text")
74
+ print text if text
75
+ when "content_block_stop"
76
+ # Content block finished
77
+ when "message_delta"
78
+ # Message metadata update (e.g., stop_reason)
79
+ when "message_stop"
80
+ # Message completely finished
81
+ end
82
+ end
83
+ end
84
+
85
+ puts "\n"
86
+
87
+ # Example 3: Streaming Long-form Content
88
+ puts "\n3. Streaming Long-form Content"
89
+ puts "-" * 60
90
+
91
+ SmartPrompt.define_worker :long_form_streaming do
92
+ use "claude"
93
+ sys_msg("You are a creative writer who writes engaging stories.")
94
+ prompt(params[:message])
95
+ send_msg
96
+ end
97
+
98
+ print "User: Write a detailed story about a robot learning to paint.\n"
99
+ print "Claude: "
100
+
101
+ total_chars = 0
102
+
103
+ engine.call_worker_by_stream(:long_form_streaming, {
104
+ message: "Write a detailed story (about 200 words) about a robot learning to paint."
105
+ }) do |chunk, bytesize|
106
+ if chunk.is_a?(Hash) && chunk["type"] == "content_block_delta"
107
+ text = chunk.dig("delta", "text")
108
+ if text
109
+ print text
110
+ total_chars += text.length
111
+ end
112
+ end
113
+ end
114
+
115
+ puts "\n\n[Total characters streamed: #{total_chars}]"
116
+
117
+ # Example 4: Streaming with Progress Indicator
118
+ puts "\n4. Streaming with Progress Indicator"
119
+ puts "-" * 60
120
+
121
+ SmartPrompt.define_worker :progress_streaming do
122
+ use "claude"
123
+ sys_msg("You are a helpful coding assistant.")
124
+ prompt(params[:message])
125
+ send_msg
126
+ end
127
+
128
+ print "User: Write a Python function to calculate Fibonacci numbers.\n"
129
+ print "Claude: "
130
+
131
+ char_count = 0
132
+ last_dot_time = Time.now
133
+
134
+ engine.call_worker_by_stream(:progress_streaming, {
135
+ message: "Write a Python function to calculate Fibonacci numbers with comments."
136
+ }) do |chunk, bytesize|
137
+ if chunk.is_a?(Hash) && chunk["type"] == "content_block_delta"
138
+ text = chunk.dig("delta", "text")
139
+ if text
140
+ print text
141
+ char_count += text.length
142
+
143
+ # Print a progress indicator every 50 characters
144
+ if char_count % 50 == 0 && Time.now - last_dot_time > 0.5
145
+ # This is just for demonstration
146
+ last_dot_time = Time.now
147
+ end
148
+ end
149
+ end
150
+ end
151
+
152
+ puts "\n"
153
+
154
+ # Example 5: Streaming with Error Handling
155
+ puts "\n5. Streaming with Error Handling"
156
+ puts "-" * 60
157
+
158
+ SmartPrompt.define_worker :safe_streaming do
159
+ use "claude"
160
+ sys_msg("You are a helpful assistant.")
161
+ prompt(params[:message])
162
+ send_msg
163
+ end
164
+
165
+ print "User: What are the benefits of exercise?\n"
166
+ print "Claude: "
167
+
168
+ begin
169
+ engine.call_worker_by_stream(:safe_streaming, {
170
+ message: "What are the benefits of exercise? List 5 benefits."
171
+ }) do |chunk, bytesize|
172
+ if chunk.is_a?(Hash) && chunk["type"] == "content_block_delta"
173
+ text = chunk.dig("delta", "text")
174
+ print text if text
175
+ end
176
+ end
177
+ puts "\n[Stream completed successfully]"
178
+ rescue SmartPrompt::LLMAPIError => e
179
+ puts "\n[Error during streaming: #{e.message}]"
180
+ rescue StandardError => e
181
+ puts "\n[Unexpected error: #{e.message}]"
182
+ end
183
+
184
+ puts ""
185
+
186
+ # Example 6: Streaming vs Non-Streaming Comparison
187
+ puts "\n6. Streaming vs Non-Streaming Comparison"
188
+ puts "-" * 60
189
+
190
+ question = "Explain the theory of relativity in simple terms."
191
+
192
+ # Non-streaming (traditional)
193
+ puts "Non-streaming mode:"
194
+ print "User: #{question}\n"
195
+ print "Claude: "
196
+
197
+ start_time = Time.now
198
+
199
+ SmartPrompt.define_worker :non_streaming_chat do
200
+ use "claude"
201
+ sys_msg("You are a physics teacher.")
202
+ prompt(params[:message])
203
+ send_msg
204
+ end
205
+
206
+ response = engine.call_worker(:non_streaming_chat, { message: question })
207
+ end_time = Time.now
208
+
209
+ puts response
210
+ puts "[Response received in #{(end_time - start_time).round(2)} seconds]\n"
211
+
212
+ # Streaming mode
213
+ puts "\nStreaming mode:"
214
+ print "User: #{question}\n"
215
+ print "Claude: "
216
+
217
+ start_time = Time.now
218
+ first_chunk_time = nil
219
+
220
+ SmartPrompt.define_worker :streaming_comparison do
221
+ use "claude"
222
+ sys_msg("You are a physics teacher.")
223
+ prompt(params[:message])
224
+ send_msg
225
+ end
226
+
227
+ engine.call_worker_by_stream(:streaming_comparison, { message: question }) do |chunk, bytesize|
228
+ if chunk.is_a?(Hash) && chunk["type"] == "content_block_delta"
229
+ first_chunk_time ||= Time.now
230
+ text = chunk.dig("delta", "text")
231
+ print text if text
232
+ end
233
+ end
234
+
235
+ end_time = Time.now
236
+
237
+ puts "\n[First chunk in #{(first_chunk_time - start_time).round(2)} seconds]"
238
+ puts "[Total time: #{(end_time - start_time).round(2)} seconds]\n"
239
+
240
+ # Example 7: Streaming with Different Models
241
+ puts "\n7. Streaming with Different Claude Models"
242
+ puts "-" * 60
243
+
244
+ question = "What is machine learning?"
245
+
246
+ # Claude 3.5 Sonnet
247
+ puts "Claude 3.5 Sonnet (streaming):"
248
+ print "User: #{question}\n"
249
+ print "Claude: "
250
+
251
+ SmartPrompt.define_worker :sonnet_streaming do
252
+ use "claude"
253
+ model "claude-3-5-sonnet-20241022"
254
+ prompt(params[:message])
255
+ send_msg
256
+ end
257
+
258
+ engine.call_worker_by_stream(:sonnet_streaming, { message: question }) do |chunk, bytesize|
259
+ if chunk.is_a?(Hash) && chunk["type"] == "content_block_delta"
260
+ text = chunk.dig("delta", "text")
261
+ print text if text
262
+ end
263
+ end
264
+
265
+ puts "\n"
266
+
267
+ # Claude 3.5 Haiku (faster)
268
+ puts "\nClaude 3.5 Haiku (streaming):"
269
+ print "User: #{question}\n"
270
+ print "Claude: "
271
+
272
+ SmartPrompt.define_worker :haiku_streaming do
273
+ use "claude_haiku"
274
+ model "claude-3-5-haiku-20241022"
275
+ prompt(params[:message])
276
+ send_msg
277
+ end
278
+
279
+ engine.call_worker_by_stream(:haiku_streaming, { message: question }) do |chunk, bytesize|
280
+ if chunk.is_a?(Hash) && chunk["type"] == "content_block_delta"
281
+ text = chunk.dig("delta", "text")
282
+ print text if text
283
+ end
284
+ end
285
+
286
+ puts "\n"
287
+
288
+ # Example 8: Streaming Best Practices
289
+ puts "\n8. Streaming Best Practices"
290
+ puts "-" * 60
291
+
292
+ puts "Best Practices for Streaming with Claude:"
293
+ puts "1. Use streaming for long-form content to improve perceived latency"
294
+ puts "2. Handle different event types appropriately:"
295
+ puts " - message_start: Initialize UI/state"
296
+ puts " - content_block_delta: Display incremental text"
297
+ puts " - message_stop: Finalize and clean up"
298
+ puts "3. Implement proper error handling for network issues"
299
+ puts "4. Consider buffering small chunks for smoother display"
300
+ puts "5. Show loading indicators before first chunk arrives"
301
+ puts "6. Use streaming for better user experience in chat applications"
302
+ puts "7. Non-streaming is better for short responses or batch processing"
303
+ puts "8. Test streaming behavior with different network conditions"
304
+ puts "9. Implement timeout handling for stalled streams"
305
+ puts "10. Consider token usage - streaming doesn't reduce costs\n"
306
+
307
+ puts "\n" + "=" * 60
308
+ puts "Streaming examples completed!"
309
+ puts "=" * 60
310
+ puts "\nNote: Streaming provides a better user experience by showing"
311
+ puts "responses as they're generated, reducing perceived latency."
312
+ puts "The total time and token usage is similar to non-streaming mode."
@@ -0,0 +1,393 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require './lib/smart_prompt'
5
+ require 'json'
6
+
7
+ # Example: Tool Calling (Function Calling) with Anthropic Claude
8
+ # This example demonstrates how to use Claude with external tools/functions
9
+
10
+ puts "=" * 60
11
+ puts "Anthropic Claude - Tool Calling Example"
12
+ puts "=" * 60
13
+
14
+ # Initialize the engine with Anthropic configuration
15
+ engine = SmartPrompt::Engine.new('config/anthropic_config.yml')
16
+
17
+ # Example 1: Simple Weather Tool
18
+ puts "\n1. Simple Weather Tool"
19
+ puts "-" * 60
20
+
21
+ # Define the weather tool
22
+ weather_tool = [
23
+ {
24
+ type: "function",
25
+ function: {
26
+ name: "get_weather",
27
+ description: "Get the current weather for a specific location",
28
+ parameters: {
29
+ type: "object",
30
+ properties: {
31
+ location: {
32
+ type: "string",
33
+ description: "The city and state, e.g. San Francisco, CA"
34
+ },
35
+ unit: {
36
+ type: "string",
37
+ enum: ["celsius", "fahrenheit"],
38
+ description: "The temperature unit to use"
39
+ }
40
+ },
41
+ required: ["location"]
42
+ }
43
+ }
44
+ }
45
+ ]
46
+
47
+ SmartPrompt.define_worker :weather_assistant do
48
+ use "claude"
49
+ sys_msg("You are a helpful weather assistant. Use the get_weather tool when users ask about weather.")
50
+ prompt(params[:message])
51
+ params.merge(tools: weather_tool)
52
+ send_msg
53
+ end
54
+
55
+ response = engine.call_worker(:weather_assistant, {
56
+ message: "What's the weather like in Tokyo?"
57
+ })
58
+ puts "User: What's the weather like in Tokyo?"
59
+ puts "Claude: #{response}\n"
60
+
61
+ # Example 2: Multiple Tools - Calculator
62
+ puts "\n2. Calculator Tools"
63
+ puts "-" * 60
64
+
65
+ calculator_tools = [
66
+ {
67
+ type: "function",
68
+ function: {
69
+ name: "add",
70
+ description: "Add two numbers together",
71
+ parameters: {
72
+ type: "object",
73
+ properties: {
74
+ a: { type: "number", description: "First number" },
75
+ b: { type: "number", description: "Second number" }
76
+ },
77
+ required: ["a", "b"]
78
+ }
79
+ }
80
+ },
81
+ {
82
+ type: "function",
83
+ function: {
84
+ name: "multiply",
85
+ description: "Multiply two numbers",
86
+ parameters: {
87
+ type: "object",
88
+ properties: {
89
+ a: { type: "number", description: "First number" },
90
+ b: { type: "number", description: "Second number" }
91
+ },
92
+ required: ["a", "b"]
93
+ }
94
+ }
95
+ },
96
+ {
97
+ type: "function",
98
+ function: {
99
+ name: "divide",
100
+ description: "Divide two numbers",
101
+ parameters: {
102
+ type: "object",
103
+ properties: {
104
+ a: { type: "number", description: "Numerator" },
105
+ b: { type: "number", description: "Denominator" }
106
+ },
107
+ required: ["a", "b"]
108
+ }
109
+ }
110
+ }
111
+ ]
112
+
113
+ SmartPrompt.define_worker :calculator_assistant do
114
+ use "claude"
115
+ sys_msg("You are a helpful calculator assistant. Use the available math tools to help users with calculations.")
116
+ prompt(params[:message])
117
+ params.merge(tools: calculator_tools)
118
+ send_msg
119
+ end
120
+
121
+ response = engine.call_worker(:calculator_assistant, {
122
+ message: "What is 15 multiplied by 23, then divided by 5?"
123
+ })
124
+ puts "User: What is 15 multiplied by 23, then divided by 5?"
125
+ puts "Claude: #{response}\n"
126
+
127
+ # Example 3: Database Query Tool
128
+ puts "\n3. Database Query Tool"
129
+ puts "-" * 60
130
+
131
+ database_tools = [
132
+ {
133
+ type: "function",
134
+ function: {
135
+ name: "query_database",
136
+ description: "Query the customer database for information",
137
+ parameters: {
138
+ type: "object",
139
+ properties: {
140
+ query_type: {
141
+ type: "string",
142
+ enum: ["customer_info", "order_history", "product_details"],
143
+ description: "The type of query to perform"
144
+ },
145
+ customer_id: {
146
+ type: "string",
147
+ description: "The customer ID to query"
148
+ },
149
+ filters: {
150
+ type: "object",
151
+ description: "Additional filters for the query"
152
+ }
153
+ },
154
+ required: ["query_type"]
155
+ }
156
+ }
157
+ }
158
+ ]
159
+
160
+ SmartPrompt.define_worker :database_assistant do
161
+ use "claude"
162
+ sys_msg("You are a customer service assistant with access to the customer database. Use the query_database tool to help users.")
163
+ prompt(params[:message])
164
+ params.merge(tools: database_tools)
165
+ send_msg
166
+ end
167
+
168
+ response = engine.call_worker(:database_assistant, {
169
+ message: "Can you look up the order history for customer ID 12345?"
170
+ })
171
+ puts "User: Can you look up the order history for customer ID 12345?"
172
+ puts "Claude: #{response}\n"
173
+
174
+ # Example 4: File Operations Tool
175
+ puts "\n4. File Operations Tool"
176
+ puts "-" * 60
177
+
178
+ file_tools = [
179
+ {
180
+ type: "function",
181
+ function: {
182
+ name: "read_file",
183
+ description: "Read the contents of a file",
184
+ parameters: {
185
+ type: "object",
186
+ properties: {
187
+ file_path: {
188
+ type: "string",
189
+ description: "The path to the file to read"
190
+ }
191
+ },
192
+ required: ["file_path"]
193
+ }
194
+ }
195
+ },
196
+ {
197
+ type: "function",
198
+ function: {
199
+ name: "write_file",
200
+ description: "Write content to a file",
201
+ parameters: {
202
+ type: "object",
203
+ properties: {
204
+ file_path: {
205
+ type: "string",
206
+ description: "The path to the file to write"
207
+ },
208
+ content: {
209
+ type: "string",
210
+ description: "The content to write to the file"
211
+ }
212
+ },
213
+ required: ["file_path", "content"]
214
+ }
215
+ }
216
+ },
217
+ {
218
+ type: "function",
219
+ function: {
220
+ name: "list_files",
221
+ description: "List files in a directory",
222
+ parameters: {
223
+ type: "object",
224
+ properties: {
225
+ directory: {
226
+ type: "string",
227
+ description: "The directory path to list files from"
228
+ }
229
+ },
230
+ required: ["directory"]
231
+ }
232
+ }
233
+ }
234
+ ]
235
+
236
+ SmartPrompt.define_worker :file_assistant do
237
+ use "claude"
238
+ sys_msg("You are a helpful file management assistant. Use the available file tools to help users manage their files.")
239
+ prompt(params[:message])
240
+ params.merge(tools: file_tools)
241
+ send_msg
242
+ end
243
+
244
+ response = engine.call_worker(:file_assistant, {
245
+ message: "Can you list all files in the /documents folder?"
246
+ })
247
+ puts "User: Can you list all files in the /documents folder?"
248
+ puts "Claude: #{response}\n"
249
+
250
+ # Example 5: API Integration Tool
251
+ puts "\n5. API Integration Tool"
252
+ puts "-" * 60
253
+
254
+ api_tools = [
255
+ {
256
+ type: "function",
257
+ function: {
258
+ name: "fetch_stock_price",
259
+ description: "Fetch the current stock price for a given ticker symbol",
260
+ parameters: {
261
+ type: "object",
262
+ properties: {
263
+ ticker: {
264
+ type: "string",
265
+ description: "The stock ticker symbol (e.g., AAPL, GOOGL)"
266
+ }
267
+ },
268
+ required: ["ticker"]
269
+ }
270
+ }
271
+ },
272
+ {
273
+ type: "function",
274
+ function: {
275
+ name: "fetch_news",
276
+ description: "Fetch recent news articles about a company or topic",
277
+ parameters: {
278
+ type: "object",
279
+ properties: {
280
+ query: {
281
+ type: "string",
282
+ description: "The search query for news articles"
283
+ },
284
+ limit: {
285
+ type: "number",
286
+ description: "Maximum number of articles to return"
287
+ }
288
+ },
289
+ required: ["query"]
290
+ }
291
+ }
292
+ }
293
+ ]
294
+
295
+ SmartPrompt.define_worker :market_assistant do
296
+ use "claude"
297
+ sys_msg("You are a financial market assistant. Use the available tools to provide stock prices and news.")
298
+ prompt(params[:message])
299
+ params.merge(tools: api_tools)
300
+ send_msg
301
+ end
302
+
303
+ response = engine.call_worker(:market_assistant, {
304
+ message: "What's the current price of Apple stock and any recent news about the company?"
305
+ })
306
+ puts "User: What's the current price of Apple stock and any recent news about the company?"
307
+ puts "Claude: #{response}\n"
308
+
309
+ # Example 6: Complex Tool with Nested Parameters
310
+ puts "\n6. Complex Tool with Nested Parameters"
311
+ puts "-" * 60
312
+
313
+ search_tool = [
314
+ {
315
+ type: "function",
316
+ function: {
317
+ name: "search_products",
318
+ description: "Search for products in the catalog with advanced filters",
319
+ parameters: {
320
+ type: "object",
321
+ properties: {
322
+ query: {
323
+ type: "string",
324
+ description: "The search query"
325
+ },
326
+ filters: {
327
+ type: "object",
328
+ properties: {
329
+ category: {
330
+ type: "string",
331
+ description: "Product category"
332
+ },
333
+ price_range: {
334
+ type: "object",
335
+ properties: {
336
+ min: { type: "number", description: "Minimum price" },
337
+ max: { type: "number", description: "Maximum price" }
338
+ }
339
+ },
340
+ in_stock: {
341
+ type: "boolean",
342
+ description: "Only show in-stock items"
343
+ }
344
+ }
345
+ },
346
+ sort_by: {
347
+ type: "string",
348
+ enum: ["price_asc", "price_desc", "popularity", "newest"],
349
+ description: "How to sort the results"
350
+ }
351
+ },
352
+ required: ["query"]
353
+ }
354
+ }
355
+ }
356
+ ]
357
+
358
+ SmartPrompt.define_worker :shopping_assistant do
359
+ use "claude"
360
+ sys_msg("You are a helpful shopping assistant. Use the search_products tool to help users find products.")
361
+ prompt(params[:message])
362
+ params.merge(tools: search_tool)
363
+ send_msg
364
+ end
365
+
366
+ response = engine.call_worker(:shopping_assistant, {
367
+ message: "Find me laptops under $1000 that are in stock, sorted by popularity"
368
+ })
369
+ puts "User: Find me laptops under $1000 that are in stock, sorted by popularity"
370
+ puts "Claude: #{response}\n"
371
+
372
+ # Example 7: Tool Calling Best Practices
373
+ puts "\n7. Tool Calling Best Practices"
374
+ puts "-" * 60
375
+
376
+ puts "Best Practices for Tool Calling with Claude:"
377
+ puts "1. Provide clear, descriptive function names"
378
+ puts "2. Write detailed descriptions for functions and parameters"
379
+ puts "3. Use appropriate parameter types (string, number, boolean, object, array)"
380
+ puts "4. Mark required parameters explicitly"
381
+ puts "5. Use enums for parameters with limited valid values"
382
+ puts "6. Include examples in parameter descriptions when helpful"
383
+ puts "7. Keep tool definitions focused and single-purpose"
384
+ puts "8. Test tools with various user queries"
385
+ puts "9. Handle tool errors gracefully in your implementation"
386
+ puts "10. Consider tool execution order for complex workflows\n"
387
+
388
+ puts "\n" + "=" * 60
389
+ puts "Tool calling examples completed!"
390
+ puts "=" * 60
391
+ puts "\nNote: These examples show tool definitions. In a real application,"
392
+ puts "you would implement the actual tool functions and handle the tool"
393
+ puts "calls returned by Claude to execute the requested operations."