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,130 @@
1
+ # Image Generation Example for SmartPrompt
2
+ #
3
+ # Demonstrates calling SiliconFlow's image generation API
4
+ # (POST /v1/images/generations) through the ImageGenerationAdapter.
5
+ #
6
+ # Requires a valid SiliconFlow API key in the SILICONFLOW_API_KEY (or APIKey)
7
+ # environment variable.
8
+
9
+ require_relative "../lib/smart_prompt"
10
+
11
+ api_key = ENV["SILICONFLOW_API_KEY"] || ENV["APIKey"]
12
+
13
+ # Configuration for image generation capabilities.
14
+ config = {
15
+ "adapters" => {
16
+ "image_generation" => "ImageGenerationAdapter",
17
+ },
18
+ "llms" => {
19
+ "image_gen" => {
20
+ "adapter" => "image_generation",
21
+ "url" => "https://api.siliconflow.cn/v1/",
22
+ "api_key" => api_key,
23
+ # Kolors supports batch_size, guidance_scale and a range of image_size values.
24
+ "model" => "Kwai-Kolors/Kolors",
25
+ },
26
+ },
27
+ "default_llm" => "image_gen",
28
+ "template_path" => "./templates",
29
+ "worker_path" => "./workers",
30
+ "logger_file" => "./logs/smart_prompt.log",
31
+ }
32
+
33
+ # Write config to file
34
+ File.write("image_generation_config.yml", config.to_yaml)
35
+
36
+ # Initialize engine
37
+ engine = SmartPrompt::Engine.new("image_generation_config.yml")
38
+
39
+ puts "=== SmartPrompt Image Generation Demo ==="
40
+
41
+ unless api_key
42
+ puts "Note: SILICONFLOW_API_KEY is not set — the API calls below will fail at the network layer."
43
+ end
44
+
45
+ # Example 1: Simple text-to-image generation (Kolors)
46
+ puts "\n=== Example 1: Text-to-Image Generation ==="
47
+ begin
48
+ result = engine.call_worker(:image_generator, {
49
+ prompt: "A beautiful sunset over a mountain lake with pine trees, digital art style",
50
+ image_size: "1024x1024",
51
+ batch_size: 1,
52
+ save_to_file: true,
53
+ output_dir: "./generated_images",
54
+ filename_prefix: "sunset",
55
+ })
56
+
57
+ puts "Image generation successful!"
58
+ puts "Generated images: #{result[:images].size}"
59
+ puts "First image URL: #{result[:images].first[:url]}"
60
+ puts "Saved files: #{result[:saved_files]}"
61
+ rescue => e
62
+ puts "Error in image generation: #{e.message}"
63
+ end
64
+
65
+ # Example 2: Art generation with a specific style (more inference steps)
66
+ puts "\n=== Example 2: Artistic Image Generation ==="
67
+ begin
68
+ result = engine.call_worker(:art_generator, {
69
+ prompt: "A mystical forest with glowing mushrooms and fairies",
70
+ art_style: "fantasy art, detailed painting",
71
+ image_size: "960x1280",
72
+ num_inference_steps: 30,
73
+ guidance_scale: 7.5,
74
+ save_to_file: true,
75
+ output_dir: "./generated_images",
76
+ filename_prefix: "fantasy_forest",
77
+ })
78
+
79
+ puts "Art generation successful!"
80
+ puts "Generated art images: #{result[:images].size}"
81
+ puts "Saved files: #{result[:saved_files]}"
82
+ rescue => e
83
+ puts "Error in art generation: #{e.message}"
84
+ end
85
+
86
+ # Example 3: Product image generation (with a negative prompt)
87
+ puts "\n=== Example 3: Product Image Generation ==="
88
+ begin
89
+ result = engine.call_worker(:product_image_generator, {
90
+ prompt: "A modern smartphone on a marble surface",
91
+ image_size: "1024x1024",
92
+ negative_prompt: "text, watermark, logo",
93
+ save_to_file: true,
94
+ output_dir: "./generated_images",
95
+ filename_prefix: "smartphone",
96
+ })
97
+
98
+ puts "Product image generation successful!"
99
+ puts "Generated product images: #{result[:images].size}"
100
+ puts "Saved files: #{result[:saved_files]}"
101
+ rescue => e
102
+ puts "Error in product image generation: #{e.message}"
103
+ end
104
+
105
+ # Example 4: Direct adapter usage (without a worker)
106
+ puts "\n=== Example 4: Direct Adapter Usage ==="
107
+ begin
108
+ adapter = engine.llms["image_gen"]
109
+
110
+ images = adapter.generate_image(
111
+ "A cute robot reading a book in a cozy library",
112
+ image_size: "1024x1024",
113
+ seed: 42,
114
+ )
115
+
116
+ puts "Direct adapter usage successful!"
117
+ puts "Generated #{images.size} image(s)"
118
+ puts "First image URL: #{images.first[:url]}"
119
+
120
+ # Optionally save to disk
121
+ saved = adapter.save_image(images, "./generated_images", "robot")
122
+ puts "Saved files: #{saved}"
123
+ rescue => e
124
+ puts "Error in direct adapter usage: #{e.message}"
125
+ end
126
+
127
+ puts "\n=== All examples completed ==="
128
+
129
+ # Clean up
130
+ File.delete("image_generation_config.yml") if File.exist?("image_generation_config.yml")
@@ -0,0 +1,121 @@
1
+ #!/usr/bin/env ruby
2
+ # Example demonstrating the monitoring and logging features of HistoryManager
3
+
4
+ require_relative '../lib/smart_prompt'
5
+ require 'logger'
6
+
7
+ # Set up logger with INFO level
8
+ SmartPrompt.logger = Logger.new($stdout)
9
+ SmartPrompt.logger.level = Logger::INFO
10
+ SmartPrompt.logger.formatter = proc do |severity, datetime, progname, msg|
11
+ "[#{datetime.strftime('%H:%M:%S')}] #{severity}: #{msg}\n"
12
+ end
13
+
14
+ puts "=" * 80
15
+ puts "History Manager Monitoring Example"
16
+ puts "=" * 80
17
+ puts
18
+
19
+ # Create a HistoryManager with monitoring enabled
20
+ config = {
21
+ cache_size: 5,
22
+ session_defaults: {
23
+ max_messages: 10,
24
+ max_tokens: 1000
25
+ },
26
+ persistence: {
27
+ enabled: true,
28
+ storage_path: "./history_data_example",
29
+ async: false
30
+ },
31
+ monitoring: {
32
+ enabled: true,
33
+ log_level: :info
34
+ }
35
+ }
36
+
37
+ manager = SmartPrompt::HistoryManager.new(config)
38
+
39
+ puts "\n--- Creating Sessions and Adding Messages ---\n"
40
+
41
+ # Create first session
42
+ manager.add_message("user_123", { role: "system", content: "You are a helpful assistant." })
43
+ manager.add_message("user_123", { role: "user", content: "What is machine learning?" })
44
+ manager.add_message("user_123", { role: "assistant", content: "Machine learning is a subset of AI..." })
45
+
46
+ # Create second session
47
+ manager.add_message("user_456", { role: "user", content: "Hello!" })
48
+ manager.add_message("user_456", { role: "assistant", content: "Hi! How can I help you?" })
49
+
50
+ puts "\n--- Getting Session Statistics ---\n"
51
+
52
+ # Get statistics for a specific session
53
+ session_stats = manager.get_stats("user_123")
54
+ puts "Session user_123 statistics:"
55
+ puts " Messages: #{session_stats[:message_count]}"
56
+ puts " Tokens: #{session_stats[:total_tokens]}"
57
+ puts " Created: #{session_stats[:created_at]}"
58
+ puts
59
+
60
+ # Get system-wide statistics
61
+ system_stats = manager.get_stats
62
+ puts "System-wide statistics:"
63
+ puts " Active sessions: #{system_stats[:active_sessions]}"
64
+ puts " Total messages: #{system_stats[:total_messages]}"
65
+ puts " Total tokens: #{system_stats[:total_tokens]}"
66
+ puts " Messages per session (avg): #{system_stats[:messages_per_session_avg].round(2)}"
67
+ puts " Tokens per message (avg): #{system_stats[:tokens_per_message_avg].round(2)}"
68
+ puts " Cache hits: #{system_stats[:cache_hits]}"
69
+ puts " Cache misses: #{system_stats[:cache_misses]}"
70
+ puts " Cache hit rate: #{(system_stats[:cache_hit_rate] * 100).round(2)}%"
71
+ puts
72
+
73
+ puts "\n--- Exporting Metrics ---\n"
74
+
75
+ # Export metrics in different formats
76
+ puts "Prometheus format (first 10 lines):"
77
+ prometheus_metrics = manager.export_metrics(format: :prometheus)
78
+ puts prometheus_metrics.lines.first(10).join
79
+
80
+ puts "\nJSON format:"
81
+ json_metrics = manager.export_metrics(format: :json)
82
+ require 'json'
83
+ metrics_hash = JSON.parse(json_metrics)
84
+ puts JSON.pretty_generate(metrics_hash.slice(
85
+ 'active_sessions',
86
+ 'total_messages',
87
+ 'cache_hit_rate',
88
+ 'messages_per_session_avg'
89
+ ))
90
+
91
+ puts "\n--- Retrieving Context ---\n"
92
+
93
+ # Retrieve context with token limit
94
+ context = manager.get_context("user_123", 500)
95
+ puts "Retrieved #{context.count} messages from user_123 (within 500 token limit)"
96
+
97
+ puts "\n--- Searching Messages ---\n"
98
+
99
+ # Search for messages
100
+ results = manager.search_messages("user_123", "machine learning")
101
+ puts "Found #{results.count} messages containing 'machine learning'"
102
+
103
+ puts "\n--- Clearing Session ---\n"
104
+
105
+ # Clear a session (keeping system messages)
106
+ manager.clear_session("user_456", keep_system_messages: true)
107
+
108
+ puts "\n--- Final Statistics ---\n"
109
+
110
+ final_stats = manager.get_stats
111
+ puts "Final system statistics:"
112
+ puts " Active sessions: #{final_stats[:active_sessions]}"
113
+ puts " Total messages: #{final_stats[:total_messages]}"
114
+ puts " Sessions created: #{final_stats[:sessions_created]}"
115
+ puts " Sessions deleted: #{final_stats[:sessions_deleted]}"
116
+ puts " Messages added: #{final_stats[:messages_added]}"
117
+ puts " Context retrievals: #{final_stats[:context_retrievals]}"
118
+
119
+ # Cleanup
120
+ manager.shutdown
121
+ puts "\n--- Manager Shutdown Complete ---\n"
@@ -0,0 +1,63 @@
1
+ # Multimodal Example for SmartPrompt
2
+ # This example demonstrates how to use the new MultimodalAdapter
3
+
4
+ require_relative '../lib/smart_prompt'
5
+
6
+ # Configuration for multimodal capabilities
7
+ config = {
8
+ "adapters" => {
9
+ "multimodal" => "MultimodalAdapter"
10
+ },
11
+ "llms" => {
12
+ "qwen_vl" => {
13
+ "adapter" => "multimodal",
14
+ "url" => "https://api.siliconflow.cn/v1/",
15
+ "api_key" => ENV["SILICONFLOW_API_KEY"],
16
+ "default_model" => "Qwen/Qwen2.5-VL-7B-Instruct"
17
+ }
18
+ },
19
+ "default_llm" => "qwen_vl",
20
+ "template_path" => "./templates",
21
+ "worker_path" => "./workers",
22
+ "logger_file" => "./logs/smart_prompt.log"
23
+ }
24
+
25
+ # Write config to file
26
+ File.write('multimodal_config.yml', config.to_yaml)
27
+
28
+ # Initialize engine
29
+ engine = SmartPrompt::Engine.new('multimodal_config.yml')
30
+
31
+ # Example 1: Simple image analysis
32
+ puts "=== Example 1: Image Analysis ==="
33
+ result = engine.call_worker(:image_analyzer, {
34
+ image_url: "https://example.com/image.jpg",
35
+ question: "描述这张图片中的内容"
36
+ })
37
+ puts "Image Analysis Result: #{result}"
38
+
39
+ # Example 2: Video analysis
40
+ puts "\n=== Example 2: Video Analysis ==="
41
+ result = engine.call_worker(:video_analyzer, {
42
+ video_url: "https://example.com/video.mp4",
43
+ question: "这个视频的主要内容是什么?",
44
+ max_frames: 15,
45
+ fps: 2
46
+ })
47
+ puts "Video Analysis Result: #{result}"
48
+
49
+ # Example 3: Multiple images comparison
50
+ puts "\n=== Example 3: Multiple Images Comparison ==="
51
+ result = engine.call_worker(:multi_image_analyzer, {
52
+ image_urls: [
53
+ "https://example.com/image1.jpg",
54
+ "https://example.com/image2.jpg"
55
+ ],
56
+ question: "比较这两张图片的相似之处和不同之处"
57
+ })
58
+ puts "Multi-Image Analysis Result: #{result}"
59
+
60
+ puts "\n=== All examples completed successfully ==="
61
+
62
+ # Clean up
63
+ File.delete('multimodal_config.yml') if File.exist?('multimodal_config.yml')
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env ruby
2
+ # Example demonstrating the RelevanceBasedStrategy
3
+
4
+ require_relative '../lib/smart_prompt'
5
+
6
+ # Create a session with some conversation history
7
+ session = SmartPrompt::Session.new("demo_session", {})
8
+
9
+ # Add a diverse set of messages
10
+ session.add_message(role: "system", content: "You are a helpful AI assistant")
11
+ session.add_message(role: "user", content: "Tell me about machine learning")
12
+ session.add_message(role: "assistant", content: "Machine learning is a subset of artificial intelligence that enables systems to learn from data")
13
+ session.add_message(role: "user", content: "What are your favorite animals?")
14
+ session.add_message(role: "assistant", content: "I don't have personal preferences, but many people love cats and dogs")
15
+ session.add_message(role: "user", content: "How does deep learning work?")
16
+ session.add_message(role: "assistant", content: "Deep learning uses neural networks with multiple layers to learn hierarchical representations")
17
+ session.add_message(role: "user", content: "Tell me a joke")
18
+ session.add_message(role: "assistant", content: "Why did the programmer quit? Because they didn't get arrays!")
19
+ session.add_message(role: "user", content: "What is supervised learning?")
20
+ session.add_message(role: "assistant", content: "Supervised learning is when you train a model with labeled data")
21
+
22
+ puts "=" * 80
23
+ puts "RelevanceBasedStrategy Example"
24
+ puts "=" * 80
25
+ puts
26
+
27
+ # Create the strategy
28
+ strategy = SmartPrompt::RelevanceBasedStrategy.new(
29
+ top_k: 5,
30
+ recency_weight: 0.3,
31
+ relevance_weight: 0.7
32
+ )
33
+
34
+ # Current message is about neural networks
35
+ current_message = SmartPrompt::Message.new(
36
+ role: "user",
37
+ content: "Can you explain more about neural networks and how they relate to machine learning?"
38
+ )
39
+
40
+ puts "Current message: #{current_message.content}"
41
+ puts
42
+ puts "Total messages in session: #{session.message_count}"
43
+ puts
44
+
45
+ # Select relevant messages
46
+ messages = session.get_messages
47
+ selected = strategy.select_messages(messages, nil, current_message)
48
+
49
+ puts "Selected #{selected.length} most relevant messages:"
50
+ puts "-" * 80
51
+ selected.each_with_index do |msg, idx|
52
+ puts "#{idx + 1}. [#{msg.role}] #{msg.content}"
53
+ end
54
+ puts
55
+
56
+ # Demonstrate with token limit
57
+ puts "=" * 80
58
+ puts "With Token Limit (100 tokens)"
59
+ puts "=" * 80
60
+ selected_limited = strategy.select_messages(messages, 100, current_message)
61
+ total_tokens = selected_limited.sum { |m| m.token_count || 0 }
62
+
63
+ puts "Selected #{selected_limited.length} messages (#{total_tokens} tokens):"
64
+ puts "-" * 80
65
+ selected_limited.each_with_index do |msg, idx|
66
+ tokens = msg.token_count || 0
67
+ puts "#{idx + 1}. [#{msg.role}] (#{tokens} tokens) #{msg.content[0..60]}..."
68
+ end
69
+ puts
70
+
71
+ # Show compression recommendation
72
+ puts "=" * 80
73
+ puts "Compression Recommendation"
74
+ puts "=" * 80
75
+ should_compress = strategy.should_compress?(session)
76
+ puts "Should compress? #{should_compress}"
77
+ puts "Reason: Session has #{session.message_count} messages, threshold is #{5 * 3} messages"
78
+ puts
79
+
80
+ puts "=" * 80
81
+ puts "Strategy Configuration"
82
+ puts "=" * 80
83
+ puts "Top-k: 5"
84
+ puts "Recency weight: 0.3"
85
+ puts "Relevance weight: 0.7"
86
+ puts "Embedding service: Not configured (using keyword similarity)"
87
+ puts "=" * 80
@@ -0,0 +1,129 @@
1
+ # SenseNova (商汤 日日新) Example for SmartPrompt
2
+ #
3
+ # Demonstrates all four SenseNova model categories through one SenseNovaAdapter:
4
+ # 1. 商量 文本对话 (chat) — sync + streaming
5
+ # 2. 商量 图文多模态 (vision)
6
+ # 3. Cupido 向量模型 (embeddings)
7
+ # 4. 秒画 文生图 (text-to-image)
8
+ #
9
+ # Requires a valid SenseNova API key in the SENSENOVA_API_KEY environment variable
10
+ # (get one at https://platform.sensenova.cn/console) and the relevant models enabled.
11
+
12
+ require_relative "../lib/smart_prompt"
13
+
14
+ api_key = ENV["SENSENOVA_API_KEY"]
15
+
16
+ config = {
17
+ "adapters" => {
18
+ "sensenova" => "SenseNovaAdapter",
19
+ },
20
+ "llms" => {
21
+ "sensechat" => {
22
+ "adapter" => "sensenova",
23
+ "url" => "https://token.sensenova.cn/v1",
24
+ "api_key" => api_key,
25
+ "model" => "sensenova-6.7-flash-lite",
26
+ "temperature" => 0.7,
27
+ },
28
+ "sensevision" => {
29
+ "adapter" => "sensenova",
30
+ "url" => "https://token.sensenova.cn/v1",
31
+ "api_key" => api_key,
32
+ "model" => "sensenova-6.7-flash-lite",
33
+ },
34
+ "senseembedding" => {
35
+ "adapter" => "sensenova",
36
+ "url" => "https://api.sensenova.cn/compatible-mode/v2",
37
+ "embeddings_url" => "https://api.sensenova.cn/v1/llm/embeddings",
38
+ "api_key" => api_key,
39
+ "model" => "Cupido",
40
+ },
41
+ "senseimage" => {
42
+ "adapter" => "sensenova",
43
+ "url" => "https://token.sensenova.cn/v1",
44
+ "image_url" => "https://token.sensenova.cn/v1/images/generations",
45
+ "api_key" => api_key,
46
+ "model" => "sensenova-u1-fast",
47
+ },
48
+ },
49
+ "default_llm" => "sensechat",
50
+ "template_path" => "./templates",
51
+ "worker_path" => "./workers",
52
+ "logger_file" => "./logs/smart_prompt.log",
53
+ }
54
+
55
+ File.write("sensenova_config.yml", config.to_yaml)
56
+ engine = SmartPrompt::Engine.new("sensenova_config.yml")
57
+
58
+ puts "=== SmartPrompt SenseNova Demo ==="
59
+ unless api_key
60
+ puts "Note: SENSENOVA_API_KEY is not set — the API calls below will fail at the network layer."
61
+ end
62
+
63
+ # 1. Chat (sync)
64
+ puts "\n=== Example 1: 商量 文本对话 (sync) ==="
65
+ begin
66
+ result = engine.call_worker(:sensenova_chat, { prompt: "用一句话介绍商汤日日新大模型。" })
67
+ puts "Reply: #{result}"
68
+ rescue => e
69
+ puts "Error: #{e.message}"
70
+ end
71
+
72
+ # 2. Chat (streaming) — tokens are printed as they arrive.
73
+ puts "\n=== Example 2: 商量 文本对话 (streaming) ==="
74
+ begin
75
+ engine.call_worker_by_stream(:sensenova_chat, { prompt: "写两句关于春天的诗。" }) do |chunk, _|
76
+ if (delta = chunk.dig("choices", 0, "delta", "content"))
77
+ print delta
78
+ end
79
+ end
80
+ puts
81
+ rescue => e
82
+ puts "Error: #{e.message}"
83
+ end
84
+
85
+ # 3. Multimodal vision
86
+ puts "\n=== Example 3: 商量 图文多模态 ==="
87
+ begin
88
+ result = engine.call_worker(:sensenova_vision, {
89
+ image_url: "https://img0.baidu.com/it/u=3775751201,1094020238&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=615",
90
+ question: "图片里有什么?",
91
+ })
92
+ puts "Vision result: #{result}"
93
+ rescue => e
94
+ puts "Error: #{e.message}"
95
+ end
96
+
97
+ # 4. Embeddings (Cupido)
98
+ puts "\n=== Example 4: Cupido 向量模型 ==="
99
+ begin
100
+ vector = engine.call_worker(:sensenova_embed, { text: "商汤日日新大模型", length: 1024 })
101
+ puts "Embedding dim: #{vector.is_a?(Array) ? vector.size : vector} (first 5: #{vector.first(5) rescue vector})"
102
+ rescue => e
103
+ puts "Error: #{e.message}"
104
+ end
105
+
106
+ # 5. Text-to-image (秒画)
107
+ puts "\n=== Example 5: 秒画 文生图 ==="
108
+ begin
109
+ result = engine.call_worker(:sensenova_image, {
110
+ prompt: "一只在书房里读书的可爱机器人,温暖的光线,数字插画",
111
+ size: "2048x2048",
112
+ save_to_file: true,
113
+ output_dir: "./generated_images",
114
+ filename_prefix: "sensenova_robot",
115
+ })
116
+ if result.is_a?(Hash) && result[:images]
117
+ puts "Generated #{result[:images].size} image(s)"
118
+ puts "First image URL: #{result[:images].first[:url]}"
119
+ puts "Saved files: #{result[:saved_files]}"
120
+ else
121
+ puts "Result: #{result}"
122
+ end
123
+ rescue => e
124
+ puts "Error (image endpoint may need a live key to confirm): #{e.message}"
125
+ end
126
+
127
+ puts "\n=== All examples completed ==="
128
+
129
+ File.delete("sensenova_config.yml") if File.exist?("sensenova_config.yml")