smart_prompt 0.4.4 → 0.5.0

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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -10
  3. data/README.cn.md +307 -64
  4. data/README.md +311 -64
  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/examples/anthropic_basic_chat.rb +143 -0
  12. data/examples/anthropic_example.rb +232 -0
  13. data/examples/anthropic_multimodal.rb +212 -0
  14. data/examples/anthropic_streaming.rb +312 -0
  15. data/examples/anthropic_tool_calling.rb +393 -0
  16. data/examples/automatic_cleanup_example.rb +109 -0
  17. data/examples/history_management_examples.rb +522 -0
  18. data/examples/image_generation_example.rb +130 -0
  19. data/examples/monitoring_example.rb +121 -0
  20. data/examples/multimodal_example.rb +63 -0
  21. data/examples/relevance_based_strategy_example.rb +87 -0
  22. data/examples/sensenova_example.rb +129 -0
  23. data/examples/stt_example.rb +287 -0
  24. data/examples/tts_example.rb +244 -0
  25. data/examples/video_generation_example.rb +189 -0
  26. data/examples/zhipu_example.rb +151 -0
  27. data/lib/smart_prompt/anthropic_adapter.rb +363 -281
  28. data/lib/smart_prompt/compression_engine.rb +201 -0
  29. data/lib/smart_prompt/context_strategy.rb +22 -0
  30. data/lib/smart_prompt/conversation.rb +81 -191
  31. data/lib/smart_prompt/engine.rb +36 -19
  32. data/lib/smart_prompt/history_manager.rb +596 -0
  33. data/lib/smart_prompt/hybrid_strategy.rb +222 -0
  34. data/lib/smart_prompt/image_generation_adapter.rb +297 -0
  35. data/lib/smart_prompt/lru_cache.rb +133 -0
  36. data/lib/smart_prompt/message.rb +57 -0
  37. data/lib/smart_prompt/multimodal_adapter.rb +277 -0
  38. data/lib/smart_prompt/openai_adapter.rb +1 -25
  39. data/lib/smart_prompt/persistence_layer.rb +197 -0
  40. data/lib/smart_prompt/relevance_based_strategy.rb +221 -0
  41. data/lib/smart_prompt/sensenova_adapter.rb +410 -0
  42. data/lib/smart_prompt/session.rb +140 -0
  43. data/lib/smart_prompt/sliding_window_strategy.rb +100 -0
  44. data/lib/smart_prompt/stt_adapter.rb +381 -0
  45. data/lib/smart_prompt/summary_based_strategy.rb +152 -0
  46. data/lib/smart_prompt/token_counter.rb +74 -0
  47. data/lib/smart_prompt/tts_adapter.rb +403 -0
  48. data/lib/smart_prompt/version.rb +1 -1
  49. data/lib/smart_prompt/video_generation_adapter.rb +330 -0
  50. data/lib/smart_prompt/worker.rb +25 -3
  51. data/lib/smart_prompt/zhipu_adapter.rb +616 -0
  52. data/lib/smart_prompt.rb +22 -2
  53. data/workers/history_management_examples.rb +407 -0
  54. data/workers/image_generation_workers.rb +119 -0
  55. data/workers/multimodal_workers.rb +110 -0
  56. data/workers/sensenova_workers.rb +62 -0
  57. data/workers/stt_workers.rb +195 -0
  58. data/workers/tts_workers.rb +388 -0
  59. data/workers/video_generation_workers.rb +264 -0
  60. data/workers/zhipu_workers.rb +113 -0
  61. metadata +84 -8
@@ -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."
@@ -0,0 +1,109 @@
1
+ #!/usr/bin/env ruby
2
+ # Example demonstrating automatic cleanup functionality in HistoryManager
3
+
4
+ require_relative '../lib/smart_prompt'
5
+
6
+ puts "=== Automatic Cleanup Example ==="
7
+ puts
8
+
9
+ # Example 1: Manual cleanup with TTL
10
+ puts "1. Manual cleanup with TTL"
11
+ puts "-" * 50
12
+
13
+ manager = SmartPrompt::HistoryManager.new(
14
+ cleanup: {
15
+ auto_cleanup: false, # Manual cleanup
16
+ session_ttl: 5, # 5 seconds TTL
17
+ cleanup_interval: 10
18
+ }
19
+ )
20
+
21
+ # Create some sessions
22
+ manager.add_message("session1", { role: "user", content: "Message in session 1" })
23
+ manager.add_message("session2", { role: "user", content: "Message in session 2" })
24
+
25
+ puts "Created 2 sessions"
26
+ puts "Active sessions: #{manager.session_ids.join(', ')}"
27
+ puts
28
+
29
+ # Wait for sessions to expire
30
+ puts "Waiting 6 seconds for sessions to expire..."
31
+ sleep(6)
32
+
33
+ # Manually trigger cleanup
34
+ expired = manager.cleanup_expired_sessions
35
+ puts "Cleaned up #{expired.length} expired sessions: #{expired.join(', ')}"
36
+ puts "Active sessions: #{manager.session_ids.join(', ')}"
37
+ puts
38
+
39
+ manager.shutdown
40
+
41
+ # Example 2: Automatic cleanup with background thread
42
+ puts "\n2. Automatic cleanup with background thread"
43
+ puts "-" * 50
44
+
45
+ manager2 = SmartPrompt::HistoryManager.new(
46
+ cleanup: {
47
+ auto_cleanup: true, # Automatic cleanup enabled
48
+ session_ttl: 3, # 3 seconds TTL
49
+ cleanup_interval: 2 # Check every 2 seconds
50
+ }
51
+ )
52
+
53
+ # Create a session
54
+ manager2.add_message("auto_session", { role: "user", content: "This will be auto-cleaned" })
55
+ puts "Created session: auto_session"
56
+ puts "Active sessions: #{manager2.session_ids.join(', ')}"
57
+ puts
58
+
59
+ # Wait for automatic cleanup to occur
60
+ puts "Waiting 5 seconds for automatic cleanup..."
61
+ sleep(5)
62
+
63
+ puts "Active sessions after automatic cleanup: #{manager2.session_ids.join(', ')}"
64
+ puts
65
+
66
+ manager2.shutdown
67
+
68
+ # Example 3: Custom cleanup callback
69
+ puts "\n3. Custom cleanup callback"
70
+ puts "-" * 50
71
+
72
+ # Custom callback that cleans up sessions with more than 3 messages
73
+ custom_callback = lambda do |session, age|
74
+ # Cleanup if session has more than 3 messages OR is older than 10 seconds
75
+ session.message_count > 3 || age > 10
76
+ end
77
+
78
+ manager3 = SmartPrompt::HistoryManager.new(
79
+ cleanup: {
80
+ auto_cleanup: false,
81
+ session_ttl: 100, # Long TTL, callback will decide
82
+ cleanup_callback: custom_callback
83
+ }
84
+ )
85
+
86
+ # Create sessions with different message counts
87
+ manager3.add_message("small_session", { role: "user", content: "Message 1" })
88
+ manager3.add_message("small_session", { role: "user", content: "Message 2" })
89
+
90
+ manager3.add_message("large_session", { role: "user", content: "Message 1" })
91
+ manager3.add_message("large_session", { role: "user", content: "Message 2" })
92
+ manager3.add_message("large_session", { role: "user", content: "Message 3" })
93
+ manager3.add_message("large_session", { role: "user", content: "Message 4" })
94
+ manager3.add_message("large_session", { role: "user", content: "Message 5" })
95
+
96
+ puts "Created 2 sessions:"
97
+ puts " - small_session: 2 messages"
98
+ puts " - large_session: 5 messages"
99
+ puts
100
+
101
+ # Trigger cleanup with custom callback
102
+ expired = manager3.cleanup_expired_sessions
103
+ puts "Custom callback cleaned up: #{expired.join(', ')}"
104
+ puts "Remaining sessions: #{manager3.session_ids.join(', ')}"
105
+ puts
106
+
107
+ manager3.shutdown
108
+
109
+ puts "\n=== Example Complete ==="