soka 0.0.3 → 0.0.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b99b5c66b841d01fa4ac0955e3924d5057787027827bdd9794094304ad218fae
4
- data.tar.gz: 4343a63fd6057865f9fd03ac7de05e3a12ca1e2ef6e8b4fe2b35e4c088e7cb1a
3
+ metadata.gz: 63bed0cf9c1546d9269d2a3118ece5d9b601c9b98f12aac3399d259d952ce870
4
+ data.tar.gz: 22f07f6f2ff084169a9645b1b290968528f1686c97cef898b584228edac9d432
5
5
  SHA512:
6
- metadata.gz: 633bf8b6bece348f71a53ca68229b710cbb0437372eaac0ca8d7b3e1a83baea6f9a31ed3d4cc68267f6cafcd6bbb5aefc28327212cacbc74d1d15437c0faf5ec
7
- data.tar.gz: 3c5e8939f02d9ad9804e053b57ae22227c00662a4a57d35b4685e63a980534412c81441d75a46939d94e288b122084f7f612022c95e20ed506c1162114c25d1b
6
+ metadata.gz: b68243391052669708b953a4cbfd45741070c2305a421741287aebcfc934b93fcc133da9a8137471deb874ee09aaa3e1a4776f308aeb58176ee0d2aa1367f0e9
7
+ data.tar.gz: 22a2225e56bc4ef89dfdb130b15c0dcf3254a6ddb4d3c4e9913861e1b264078adc51db545d6c0c9a421eb9d50f46034031ca9d21781a8be59400e8048d119cf3
data/CHANGELOG.md CHANGED
@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.0.4] - 2025-08-04
11
+
12
+ ### Features
13
+ - feat: add dynamic instructions via method support (e428631)
14
+
15
+ ### Documentation
16
+ - docs: update README with dynamic instructions feature (b3f2d81)
17
+
10
18
  ## [0.0.3] - 2025-08-03
11
19
 
12
20
  ### Features
data/README.md CHANGED
@@ -148,7 +148,7 @@ class ConversationsController < ApplicationController
148
148
  def create
149
149
  agent = CustomerSupportAgent.new
150
150
  result = agent.run(params[:message])
151
-
151
+
152
152
  render json: {
153
153
  answer: result.final_answer,
154
154
  status: result.status
@@ -240,10 +240,10 @@ class WeatherAgent < Soka::Agent
240
240
 
241
241
  # Custom tool (functional) - requires description as second parameter
242
242
  tool :get_weather, "Get weather for a location"
243
-
243
+
244
244
  # Custom instructions (optional)
245
245
  instructions "You are a weather expert. Provide detailed weather information."
246
-
246
+
247
247
  # Thinking language (optional)
248
248
  think_in 'en' # Default is 'en'
249
249
 
@@ -376,8 +376,8 @@ Customize your agent's personality and response style:
376
376
  ```ruby
377
377
  class FriendlyAgent < Soka::Agent
378
378
  provider :gemini
379
-
380
- # Define personality at class level
379
+
380
+ # Method 1: Static string instructions
381
381
  instructions <<~PROMPT
382
382
  You are a friendly, helpful assistant.
383
383
  Use casual language and be encouraging.
@@ -385,7 +385,28 @@ class FriendlyAgent < Soka::Agent
385
385
  PROMPT
386
386
  end
387
387
 
388
- # Or override at runtime
388
+ # Method 2: Dynamic instructions using method
389
+ class DynamicAgent < Soka::Agent
390
+ provider :gemini
391
+
392
+ # Reference a method for dynamic instructions
393
+ instructions :generate_instructions
394
+
395
+ private
396
+
397
+ def generate_instructions
398
+ hour = Time.now.hour
399
+ mood = hour < 12 ? 'cheerful morning' : 'relaxed afternoon'
400
+
401
+ <<~PROMPT
402
+ You are a #{mood} assistant.
403
+ Current Time: #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}
404
+ Adjust your tone based on the time of day.
405
+ PROMPT
406
+ end
407
+ end
408
+
409
+ # Method 3: Override at runtime
389
410
  agent = FriendlyAgent.new(
390
411
  instructions: 'Be more formal and professional.'
391
412
  )
@@ -399,7 +420,7 @@ Optimize reasoning for specific languages:
399
420
  ```ruby
400
421
  class GlobalAgent < Soka::Agent
401
422
  provider :gemini
402
-
423
+
403
424
  # Set default thinking language
404
425
  think_in 'zh-TW' # Think in Traditional Chinese
405
426
  end
@@ -611,7 +632,14 @@ Shows how to customize agent personality:
611
632
  - Creating different agent personalities
612
633
  - Use cases for different domains
613
634
 
614
- ### 10. Multilingual Thinking (`examples/10_think_in_languages.rb`)
635
+ ### 10. Dynamic Instructions (`examples/10_dynamic_instructions.rb`)
636
+ Demonstrates dynamic instruction generation:
637
+ - Using methods to generate instructions dynamically
638
+ - Time-based instruction changes
639
+ - Environment-based configuration
640
+ - Session-based personality switching
641
+
642
+ ### 11. Multilingual Thinking (`examples/11_think_in_languages.rb`)
615
643
  Demonstrates language-specific reasoning:
616
644
  - Setting thinking language with `think_in`
617
645
  - Class-level vs instance-level configuration
@@ -0,0 +1,248 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'soka'
6
+ require 'dotenv/load'
7
+ require 'dentaku'
8
+
9
+ # Example 10: Dynamic Instructions via Method
10
+ #
11
+ # This example demonstrates how to use a method to generate instructions dynamically.
12
+ # Instead of static string instructions, you can define a method that returns
13
+ # instructions based on runtime conditions.
14
+ #
15
+ # Key concepts:
16
+ # - Using a Symbol to reference an instructions method
17
+ # - Dynamic instruction generation based on context
18
+ # - Maintaining compatibility with string-based instructions
19
+
20
+ # Example 1: Time-aware Assistant
21
+ # Instructions change based on time of day
22
+ class TimeAwareAssistant < Soka::Agent
23
+ provider :gemini
24
+ model 'gemini-2.5-flash-lite'
25
+
26
+ # Use a method to generate instructions
27
+ instructions :generate_time_based_instructions
28
+
29
+ class GreetingTool < Soka::AgentTool
30
+ desc 'Generate appropriate greetings'
31
+
32
+ params do
33
+ requires :name, String, desc: 'Name of the person to greet'
34
+ end
35
+
36
+ def call(name:)
37
+ "Greeting generated for #{name}"
38
+ end
39
+ end
40
+
41
+ tool GreetingTool
42
+
43
+ private
44
+
45
+ def generate_time_based_instructions
46
+ hour = Time.now.hour
47
+
48
+ base_instructions = if hour < 12
49
+ 'You are a cheerful morning assistant who is energetic and motivating.'
50
+ elsif hour < 17
51
+ 'You are a focused afternoon assistant who is professional and efficient.'
52
+ else
53
+ 'You are a relaxed evening assistant who is calm and supportive.'
54
+ end
55
+
56
+ <<~INSTRUCTIONS
57
+ #{base_instructions}
58
+ Current time: #{Time.now.strftime('%H:%M')}
59
+
60
+ You must:
61
+ - Acknowledge the time of day in your responses
62
+ - Adjust your tone based on the time period
63
+ - Be helpful while matching the appropriate energy level
64
+ INSTRUCTIONS
65
+ end
66
+ end
67
+
68
+ # Example 2: Environment-based Assistant
69
+ # Instructions change based on environment variables
70
+ class EnvironmentAwareAssistant < Soka::Agent
71
+ provider :gemini
72
+ model 'gemini-2.5-flash-lite'
73
+
74
+ # Use a method for environment-based instructions
75
+ instructions :build_environment_instructions
76
+
77
+ class CalculatorTool < Soka::AgentTool
78
+ desc 'Performs calculations'
79
+
80
+ params do
81
+ requires :expression, String, desc: 'Math expression'
82
+ end
83
+
84
+ def call(expression:)
85
+ calculator = Dentaku::Calculator.new
86
+ result = calculator.evaluate(expression)
87
+ "Result: #{expression} = #{result}"
88
+ rescue StandardError => e
89
+ "Error: #{e.message}"
90
+ end
91
+ end
92
+
93
+ tool CalculatorTool
94
+
95
+ private
96
+
97
+ def build_environment_instructions
98
+ user_level = ENV['USER_LEVEL'] || 'beginner'
99
+ debug_mode = ENV['DEBUG'] == 'true'
100
+
101
+ style = case user_level
102
+ when 'expert'
103
+ 'Use technical terminology and assume deep knowledge. Be concise.'
104
+ when 'intermediate'
105
+ 'Balance technical accuracy with clear explanations. Provide context when needed.'
106
+ else
107
+ 'Use simple language and explain concepts thoroughly. Avoid jargon.'
108
+ end
109
+
110
+ debug_info = if debug_mode
111
+ "\n- Include detailed technical information in responses"
112
+ else
113
+ "\n- Keep responses focused on the essential information"
114
+ end
115
+
116
+ <<~INSTRUCTIONS
117
+ You are an AI assistant adapting to the user's expertise level.
118
+
119
+ Communication style: #{style}
120
+ User level: #{user_level}
121
+ Debug mode: #{debug_mode}
122
+
123
+ Guidelines:#{debug_info}
124
+ - Adapt your responses to the #{user_level} level
125
+ - Maintain accuracy while adjusting complexity
126
+ INSTRUCTIONS
127
+ end
128
+ end
129
+
130
+ # Example 3: Session-based Assistant
131
+ # Instructions change based on a session ID
132
+ class SessionAssistant < Soka::Agent
133
+ provider :gemini
134
+ model 'gemini-2.5-flash-lite'
135
+
136
+ # Use method for session-based instructions
137
+ instructions :generate_session_instructions
138
+
139
+ class InfoTool < Soka::AgentTool
140
+ desc 'Provides information'
141
+
142
+ params do
143
+ requires :query, String, desc: 'Information query'
144
+ end
145
+
146
+ def call(query:)
147
+ "Information about: #{query}"
148
+ end
149
+ end
150
+
151
+ tool InfoTool
152
+
153
+ private
154
+
155
+ def generate_session_instructions
156
+ # Simulate different session types based on time
157
+ session_type = Time.now.min % 3
158
+
159
+ personality = case session_type
160
+ when 0
161
+ 'You are a formal, professional assistant.'
162
+ when 1
163
+ 'You are a friendly, casual assistant.'
164
+ else
165
+ 'You are an educational, patient assistant.'
166
+ end
167
+
168
+ <<~INSTRUCTIONS
169
+ #{personality}
170
+ Session ID: #{Time.now.to_i}
171
+
172
+ Guidelines:
173
+ - Maintain consistent personality throughout the session
174
+ - Provide helpful and accurate information
175
+ - Adapt your tone to match the session style
176
+ INSTRUCTIONS
177
+ end
178
+ end
179
+
180
+ # Run examples
181
+ puts '=== Time-Aware Assistant Example ==='
182
+ time_assistant = TimeAwareAssistant.new
183
+
184
+ time_assistant.run('Please greet John appropriately for the current time') do |event|
185
+ case event.type
186
+ when :thought
187
+ puts "⏰ Thinking: #{event.content}"
188
+ when :final_answer
189
+ puts "📝 Response: #{event.content}"
190
+ end
191
+ end
192
+
193
+ puts "\n=== Environment-based Assistant (Beginner) Example ==="
194
+ ENV['USER_LEVEL'] = 'beginner'
195
+ ENV['DEBUG'] = 'false'
196
+
197
+ beginner_assistant = EnvironmentAwareAssistant.new
198
+
199
+ beginner_assistant.run('Calculate 15% of 200') do |event|
200
+ case event.type
201
+ when :thought
202
+ puts "🎓 Thinking: #{event.content}"
203
+ when :final_answer
204
+ puts "📚 Response: #{event.content}"
205
+ end
206
+ end
207
+
208
+ puts "\n=== Environment-based Assistant (Expert) Example ==="
209
+ ENV['USER_LEVEL'] = 'expert'
210
+ ENV['DEBUG'] = 'true'
211
+
212
+ expert_assistant = EnvironmentAwareAssistant.new
213
+
214
+ expert_assistant.run('Calculate 15% of 200') do |event|
215
+ case event.type
216
+ when :final_answer
217
+ puts "💼 Response: #{event.content}"
218
+ end
219
+ end
220
+
221
+ puts "\n=== Session Assistant Example ==="
222
+ session_assistant = SessionAssistant.new
223
+
224
+ session_assistant.run('Tell me about Ruby') do |event|
225
+ case event.type
226
+ when :thought
227
+ puts "🔧 Thinking: #{event.content}"
228
+ when :final_answer
229
+ puts "🖥️ Response: #{event.content}"
230
+ end
231
+ end
232
+
233
+ puts "\n=== Mixed: String and Method Instructions ==="
234
+ # You can still use string instructions
235
+ class StringInstructionsAgent < Soka::Agent
236
+ provider :gemini
237
+ model 'gemini-2.5-flash-lite'
238
+
239
+ instructions 'You are a helpful assistant that always responds concisely.'
240
+ end
241
+
242
+ puts "String instructions work as before."
243
+
244
+ # And override at runtime
245
+ TimeAwareAssistant.new(
246
+ instructions: 'Override with a custom string instruction at runtime.'
247
+ )
248
+ puts "Runtime overrides still work with both string and method-based instructions."
data/lib/soka/agent.rb CHANGED
@@ -51,7 +51,7 @@ module Soka
51
51
  # Apply behavior-related configuration
52
52
  # @param options [Hash] Configuration options
53
53
  def apply_behavior_config(options)
54
- @instructions = options.fetch(:instructions) { self.class._instructions }
54
+ @instructions = options.fetch(:instructions) { resolve_instructions }
55
55
  @think_in = options.fetch(:think_in) { self.class._think_in } || 'en'
56
56
  end
57
57
 
@@ -86,6 +86,35 @@ module Soka
86
86
  end
87
87
  end
88
88
 
89
+ # Resolve instructions from class configuration
90
+ # @return [String, nil] The resolved instructions
91
+ def resolve_instructions
92
+ return nil unless self.class._instructions
93
+
94
+ case self.class._instructions
95
+ when Symbol
96
+ # Call the method if it's a symbol
97
+ send(self.class._instructions) if respond_to?(self.class._instructions, true)
98
+ else
99
+ # Return string or any other value directly
100
+ self.class._instructions
101
+ end
102
+ end
103
+
104
+ # Resolve current instructions (may be dynamic)
105
+ # @return [String, nil] The current instructions
106
+ def resolve_current_instructions
107
+ case @instructions
108
+ when Symbol
109
+ send(@instructions) if respond_to?(@instructions, true)
110
+ when Proc
111
+ @instructions.call
112
+ else
113
+ # Return string or any other value directly
114
+ @instructions
115
+ end
116
+ end
117
+
89
118
  # Validate the input is not empty
90
119
  # @param input [String] The input to validate
91
120
  # @raise [ArgumentError] If input is empty
@@ -115,7 +144,7 @@ module Soka
115
144
  engine_instance = @engine.new(self, @tools,
116
145
  llm: @llm,
117
146
  max_iterations: @max_iterations,
118
- custom_instructions: @instructions,
147
+ custom_instructions: resolve_current_instructions,
119
148
  think_in: @think_in)
120
149
  with_retry { engine_instance.reason(input, &) }
121
150
  end
@@ -51,9 +51,13 @@ module Soka
51
51
  end
52
52
 
53
53
  # Define custom instructions (system prompt) for the agent
54
- # @param text [String] The custom instructions/system prompt
55
- def instructions(text)
56
- @_instructions = text
54
+ # @param text_or_method [String, Symbol] The custom instructions/system prompt or method name
55
+ # @example Using a string
56
+ # instructions "You are a helpful assistant"
57
+ # @example Using a method
58
+ # instructions :generate_instructions
59
+ def instructions(text_or_method)
60
+ @_instructions = text_or_method
57
61
  end
58
62
 
59
63
  # Define thinking language for the agent
data/lib/soka/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Soka
4
- VERSION = '0.0.3'
4
+ VERSION = '0.0.4'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: soka
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - jiunjiun
@@ -53,7 +53,8 @@ files:
53
53
  - LICENSE
54
54
  - README.md
55
55
  - Rakefile
56
- - examples/10_think_in_languages.rb
56
+ - examples/10_dynamic_instructions.rb
57
+ - examples/11_think_in_languages.rb
57
58
  - examples/1_basic.rb
58
59
  - examples/2_event_handling.rb
59
60
  - examples/3_memory.rb