soka 0.0.4 → 0.0.6
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/.rubocop.yml +1 -1
- data/CHANGELOG.md +46 -0
- data/CLAUDE.md +19 -4
- data/README.md +28 -22
- data/examples/1_basic.rb +0 -2
- data/examples/2_event_handling.rb +0 -1
- data/examples/3_memory.rb +0 -2
- data/lib/soka/agent.rb +3 -3
- data/lib/soka/agent_tool.rb +10 -16
- data/lib/soka/agents/dsl_methods.rb +1 -7
- data/lib/soka/agents/hook_manager.rb +4 -6
- data/lib/soka/configuration.rb +2 -3
- data/lib/soka/engines/concerns/response_parser.rb +9 -21
- data/lib/soka/engines/concerns/response_processor.rb +12 -1
- data/lib/soka/engines/prompts/base.rb +103 -0
- data/lib/soka/engines/prompts/format_helpers.rb +43 -0
- data/lib/soka/engines/prompts/instructions.rb +86 -0
- data/lib/soka/engines/prompts/workflow_rules.rb +98 -0
- data/lib/soka/engines/prompts.rb +13 -0
- data/lib/soka/engines/react.rb +6 -7
- data/lib/soka/engines/reasoning_context.rb +0 -12
- data/lib/soka/llm.rb +4 -4
- data/lib/soka/llms/anthropic.rb +17 -21
- data/lib/soka/llms/base.rb +1 -2
- data/lib/soka/llms/gemini.rb +2 -3
- data/lib/soka/llms/openai.rb +42 -36
- data/lib/soka/result.rb +1 -8
- data/lib/soka/version.rb +1 -1
- data/lib/soka.rb +1 -0
- metadata +26 -5
- data/lib/soka/engines/concerns/prompt_template.rb +0 -121
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 07c69024bab6c364c20dfdea14e2a9b2dddc6248c6c3c741c1d917fdad0e31b2
|
4
|
+
data.tar.gz: f89d4e6f71c833f340dda97dbb281d21b7ff586aa8df3de0ebcda419dc947576
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e10b7c4eb7428b4eec647d26c767af0a9fd87a38f639b5925c12784a3feaad4f9ef2abb907e2eb73e46a271305c14a8d75dca37c0d559758bf5f64d43faf5c1e
|
7
|
+
data.tar.gz: b34810a3566fd70bddbd0b0922f321c2a7a9aecf2b914b2cf08d613e0f3c5ee9aedb0a8e2ae4d8b4cf8b8551db4b2f2d1ff14d4d47b10f0f0b9b825a595e5854
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -7,6 +7,52 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
7
7
|
|
8
8
|
## [Unreleased]
|
9
9
|
|
10
|
+
## [0.0.6] - 2025-08-12
|
11
|
+
|
12
|
+
### Features
|
13
|
+
- feat: add OpenAI Responses API support with reasoning (8c81be8)
|
14
|
+
- feat: enhance ReAct prompt template with clearer instructions (d371c12)
|
15
|
+
- feat(gemini): add thinkingConfig to generationConfig (8e44e63)
|
16
|
+
|
17
|
+
### Bug Fixes
|
18
|
+
- fix(openai): update default model name from gpt-5-nano to gpt-5-mini (1e5971f)
|
19
|
+
- fix: update Final_Answer tag to FinalAnswer across codebase (feb6071)
|
20
|
+
|
21
|
+
### Documentation
|
22
|
+
- docs: add model testing results and update OpenAI model names (f83b987)
|
23
|
+
- docs: add problem solving guidelines for AI assistants (0e99e5f)
|
24
|
+
|
25
|
+
### Code Refactoring
|
26
|
+
- refactor: reorganize prompt templates into modular structure (0c10e21)
|
27
|
+
- refactor: improve format reminder in response processor (a3de6a5)
|
28
|
+
- refactor: remove configurable timeout option (7e7e7c3)
|
29
|
+
- refactor: reorganize Anthropic LLM class structure (4fc4f90)
|
30
|
+
- refactor: extract format helpers from prompt template (d371c12)
|
31
|
+
- refactor: simplify AgentTool type mapping with constant (c609949)
|
32
|
+
- refactor: standardize Action tag to use single-line JSON format (5ac06f0)
|
33
|
+
- refactor(context): simplify iteration handling and improve logging (3e65643)
|
34
|
+
- refactor(llms): remove max_tokens parameter from configurations (d7a1fc8)
|
35
|
+
|
36
|
+
### Performance
|
37
|
+
- perf: replace standard JSON parser with Oj for better performance (3fc969f)
|
38
|
+
|
39
|
+
### Chores
|
40
|
+
- chore: update gitignore and documentation (8dc5102)
|
41
|
+
- chore: adjust HTTP client timeout settings (38f47a7)
|
42
|
+
- chore: update .gitignore to include .serena (90e3d41)
|
43
|
+
|
44
|
+
## [0.0.5] - 2025-08-05
|
45
|
+
|
46
|
+
### Bug Fixes
|
47
|
+
- fix: remove confidence_score from examples and hook manager (12d9e8b)
|
48
|
+
|
49
|
+
### Tests
|
50
|
+
- test: fix Hash#inspect format compatibility for Ruby 3.4 (c9f1a31)
|
51
|
+
|
52
|
+
### Chores
|
53
|
+
- chore: update CI workflows to test multiple Ruby versions (d2e1b9a)
|
54
|
+
- chore: downgrade minimum Ruby version to 3.1 (8a3d7f0)
|
55
|
+
|
10
56
|
## [0.0.4] - 2025-08-04
|
11
57
|
|
12
58
|
### Features
|
data/CLAUDE.md
CHANGED
@@ -65,7 +65,6 @@ class MyAgent < Soka::Agent
|
|
65
65
|
provider :gemini # Choose AI provider
|
66
66
|
model 'gemini-2.5-flash-lite' # Specify model
|
67
67
|
max_iterations 10 # Limit reasoning cycles
|
68
|
-
timeout 30 # Request timeout
|
69
68
|
|
70
69
|
# Define tools
|
71
70
|
tool :my_tool, 'Description' do
|
@@ -256,7 +255,6 @@ Soka.setup do |config|
|
|
256
255
|
|
257
256
|
config.performance do |perf|
|
258
257
|
perf.max_iterations = 10
|
259
|
-
perf.timeout = 30
|
260
258
|
end
|
261
259
|
end
|
262
260
|
```
|
@@ -274,7 +272,6 @@ ANTHROPIC_API_KEY=your_api_key
|
|
274
272
|
|
275
273
|
### Performance Tips
|
276
274
|
- Set appropriate `max_iterations` to prevent infinite loops
|
277
|
-
- Use `timeout` to handle slow API responses
|
278
275
|
- Implement caching for frequently used tools
|
279
276
|
- Consider memory limits for long conversations
|
280
277
|
|
@@ -306,7 +303,7 @@ end
|
|
306
303
|
```ruby
|
307
304
|
allow(agent).to receive(:llm).and_return(mock_llm)
|
308
305
|
allow(mock_llm).to receive(:chat).and_return(
|
309
|
-
double(content: '<Thought>Test</Thought><
|
306
|
+
double(content: '<Thought>Test</Thought><FinalAnswer>Done</FinalAnswer>')
|
310
307
|
)
|
311
308
|
```
|
312
309
|
|
@@ -352,6 +349,13 @@ The `examples/` directory contains 10 comprehensive examples:
|
|
352
349
|
- Remove duplicate code
|
353
350
|
- Avoid over-abstraction
|
354
351
|
|
352
|
+
### Ruby Code Organization (Important for AI Assistants)
|
353
|
+
- **Always organize Ruby class methods with public methods first, then private methods**
|
354
|
+
- Place all public methods at the beginning of the class
|
355
|
+
- Place all private methods at the end after a single `private` keyword
|
356
|
+
- Never mix public and private sections
|
357
|
+
- Avoid using `public` keyword explicitly unless switching back from private
|
358
|
+
|
355
359
|
### Code Documentation
|
356
360
|
- **When adjusting code, add comments and documentation to methods**
|
357
361
|
- Use YARD format comments
|
@@ -363,6 +367,17 @@ The `examples/` directory contains 10 comprehensive examples:
|
|
363
367
|
- Document all public APIs
|
364
368
|
- Keep README and CLAUDE.md updated
|
365
369
|
|
370
|
+
### Problem Solving Approach (Important for AI Assistants)
|
371
|
+
- **Always solve problems directly - never avoid issues or use incorrect workarounds**
|
372
|
+
- Face technical challenges head-on without shortcuts or workarounds
|
373
|
+
- Ensure solutions align with architecture design and best practices
|
374
|
+
- Avoid temporary patches; pursue fundamental solutions
|
375
|
+
- **When facing difficult problems or needing more information, provide current status and issues, then pause for discussion before implementation**
|
376
|
+
- Analyze and explain the situation when encountering complex problems
|
377
|
+
- Clearly describe difficulties encountered and information needed
|
378
|
+
- Wait for confirmation and discussion before proceeding with implementation
|
379
|
+
- Maintain transparent communication without assuming or guessing requirements
|
380
|
+
|
366
381
|
## 🤝 Contributing
|
367
382
|
|
368
383
|
[Contributing Guidelines]
|
data/README.md
CHANGED
@@ -175,7 +175,6 @@ Soka.setup do |config|
|
|
175
175
|
# Performance Configuration
|
176
176
|
config.performance do |perf|
|
177
177
|
perf.max_iterations = 10 # ReAct max iterations
|
178
|
-
perf.timeout = 30 # API call timeout (seconds)
|
179
178
|
end
|
180
179
|
|
181
180
|
# Default tools
|
@@ -226,7 +225,6 @@ class WeatherAgent < Soka::Agent
|
|
226
225
|
provider :gemini
|
227
226
|
model 'gemini-2.5-flash-lite'
|
228
227
|
max_iterations 10
|
229
|
-
timeout 30
|
230
228
|
|
231
229
|
# Register tools
|
232
230
|
tool SearchTool
|
@@ -314,7 +312,7 @@ result = agent.run('What is the weather in Tokyo today?')
|
|
314
312
|
# Result object provides rich information
|
315
313
|
puts result.final_answer # Final answer
|
316
314
|
puts result.iterations # Number of iterations used
|
317
|
-
puts result.status # :success, :failed, :
|
315
|
+
puts result.status # :success, :failed, :max_iterations_reached
|
318
316
|
puts result.execution_time # Execution time (if recorded)
|
319
317
|
|
320
318
|
# Check execution status
|
@@ -322,8 +320,6 @@ if result.successful?
|
|
322
320
|
puts "Success: #{result.final_answer}"
|
323
321
|
elsif result.failed?
|
324
322
|
puts "Failed: #{result.error}"
|
325
|
-
elsif result.timeout?
|
326
|
-
puts "Execution timeout"
|
327
323
|
elsif result.max_iterations_reached?
|
328
324
|
puts "Max iterations reached"
|
329
325
|
end
|
@@ -445,12 +441,11 @@ Soka uses a tagged ReAct format:
|
|
445
441
|
```xml
|
446
442
|
<Thought>I need to search for weather information in Tokyo</Thought>
|
447
443
|
<Action>
|
448
|
-
|
449
|
-
Parameters: {"query": "Tokyo weather", "location": "Japan"}
|
444
|
+
{"tool": "search", "parameters": {"query": "Tokyo weather", "location": "Japan"}}
|
450
445
|
</Action>
|
451
446
|
<Observation>Tokyo today: Sunny, temperature 28°C, humidity 65%</Observation>
|
452
447
|
<Thought>I have obtained the weather information and can answer the user now</Thought>
|
453
|
-
<
|
448
|
+
<FinalAnswer>Today in Tokyo it's sunny with a temperature of 28°C and humidity of 65%.</FinalAnswer>
|
454
449
|
```
|
455
450
|
|
456
451
|
### Result Object Structure
|
@@ -460,7 +455,7 @@ Parameters: {"query": "Tokyo weather", "location": "Japan"}
|
|
460
455
|
result.input # User input
|
461
456
|
result.thoughts # Array of thinking steps
|
462
457
|
result.final_answer # Final answer
|
463
|
-
result.status # Status (:success, :failed, :
|
458
|
+
result.status # Status (:success, :failed, :max_iterations_reached)
|
464
459
|
result.error # Error message (if any)
|
465
460
|
result.execution_time # Execution time (seconds)
|
466
461
|
result.iterations # Number of iterations
|
@@ -477,7 +472,7 @@ result.iterations # Number of iterations
|
|
477
472
|
}
|
478
473
|
],
|
479
474
|
final_answer: "Final answer",
|
480
|
-
status: :success, # :success, :failed, :
|
475
|
+
status: :success, # :success, :failed, :max_iterations_reached
|
481
476
|
error: nil, # Error message (if any)
|
482
477
|
execution_time: 1.23, # Execution time (seconds)
|
483
478
|
iterations: 2, # Number of iterations
|
@@ -663,7 +658,7 @@ ruby examples/1_basic.rb
|
|
663
658
|
- Default model: `gemini-2.5-flash-lite`
|
664
659
|
|
665
660
|
#### OpenAI
|
666
|
-
- Models: `gpt-
|
661
|
+
- Models: `gpt-5`, `gpt-5-mini`, `gpt-5-nano`
|
667
662
|
- Environment variable: `OPENAI_API_KEY`
|
668
663
|
- Features: Streaming support, powerful reasoning
|
669
664
|
|
@@ -672,6 +667,24 @@ ruby examples/1_basic.rb
|
|
672
667
|
- Environment variable: `ANTHROPIC_API_KEY`
|
673
668
|
- Features: Long context support, excellent code understanding
|
674
669
|
|
670
|
+
### Model Testing Results
|
671
|
+
|
672
|
+
Based on extensive testing with the Soka framework, here are the recommended models for optimal performance:
|
673
|
+
|
674
|
+
| Provider | ✅ Recommended Models | ⚠️ Not Recommended | Notes |
|
675
|
+
|----------|----------------------|-------------------|--------|
|
676
|
+
| **Gemini** | `gemini-2.5-flash-lite` ⭐<br>`gemini-2.5-pro` | `gemini-2.5-flash` | **gemini-2.5-flash-lite**: Fast, cost-effective, with good performance<br>**Avoid gemini-2.5-flash**: Often skips thinking process when using tools, directly jumps to tool usage |
|
677
|
+
| **OpenAI** | `gpt-5` <br>`gpt-5-mini` ⭐ | `gpt-5-nano` | **gpt-5-mini**: Good balance of price, speed, and effectiveness<br>**Avoid gpt-5-nano**: Can enter infinite tool-calling loops, fails to complete tasks |
|
678
|
+
| **Claude** | *To be tested* | - | Testing in progress |
|
679
|
+
|
680
|
+
⭐ = Best choice for most use cases
|
681
|
+
|
682
|
+
#### Testing Insights
|
683
|
+
|
684
|
+
- **Thinking Process**: Models marked as "Not Recommended" often fail to properly follow the ReAct pattern, either skipping the thinking phase or getting stuck in loops
|
685
|
+
- **Cost-Performance**: `gemini-2.5-flash-lite` and `gpt-5-mini` offer the best balance for most applications
|
686
|
+
- **Reliability**: Recommended models consistently complete tasks without entering error states
|
687
|
+
|
675
688
|
### Configuration Options
|
676
689
|
|
677
690
|
| Option | Type | Default | Description |
|
@@ -682,7 +695,6 @@ ruby examples/1_basic.rb
|
|
682
695
|
| `ai.instructions` | String | nil | Custom agent instructions |
|
683
696
|
| `ai.think_in` | String | `"en"` | Thinking language |
|
684
697
|
| `performance.max_iterations` | Integer | 10 | Max iterations |
|
685
|
-
| `performance.timeout` | Integer | 30 | Timeout (seconds) |
|
686
698
|
|
687
699
|
### Tool Parameter Validation
|
688
700
|
|
@@ -696,8 +708,8 @@ ruby examples/1_basic.rb
|
|
696
708
|
## Performance Optimization
|
697
709
|
|
698
710
|
1. **Use appropriate models**:
|
699
|
-
- Simple tasks: `gemini-2.5-flash-lite` or `gpt-
|
700
|
-
- Complex reasoning: `gemini-2.5-pro` or `gpt-
|
711
|
+
- Simple tasks: `gemini-2.5-flash-lite` or `gpt-5-mini` or `claude-3-5-haiku-latest`
|
712
|
+
- Complex reasoning: `gemini-2.5-pro` or `gpt-5` or `claude-sonnet-4-0`
|
701
713
|
|
702
714
|
2. **Control iterations**:
|
703
715
|
```ruby
|
@@ -715,13 +727,7 @@ ruby examples/1_basic.rb
|
|
715
727
|
```
|
716
728
|
Solution: Ensure correct environment variable is set or provide API key in configuration
|
717
729
|
|
718
|
-
2. **
|
719
|
-
```
|
720
|
-
Soka::LLMError: Request timed out
|
721
|
-
```
|
722
|
-
Solution: Increase timeout or use a faster model
|
723
|
-
|
724
|
-
3. **Max Iterations Reached**
|
730
|
+
2. **Max Iterations Reached**
|
725
731
|
```
|
726
732
|
Status: max_iterations_reached
|
727
733
|
```
|
@@ -795,7 +801,7 @@ This project is licensed under the MIT License. See the [LICENSE](LICENSE) file
|
|
795
801
|
## Acknowledgments
|
796
802
|
|
797
803
|
- Thanks to the [ReAct paper](https://arxiv.org/abs/2210.03629) for the theoretical foundation
|
798
|
-
- Thanks to the [Regent](https://github.com/
|
804
|
+
- Thanks to the [Regent](https://github.com/alchaplinsky/regent) project for architectural inspiration
|
799
805
|
- Thanks to all contributors for their efforts
|
800
806
|
|
801
807
|
---
|
data/examples/1_basic.rb
CHANGED
@@ -15,7 +15,6 @@ Soka.setup do |config|
|
|
15
15
|
|
16
16
|
config.performance do |perf|
|
17
17
|
perf.max_iterations = 5
|
18
|
-
perf.timeout = 30
|
19
18
|
end
|
20
19
|
end
|
21
20
|
|
@@ -90,5 +89,4 @@ puts '-' * 50
|
|
90
89
|
|
91
90
|
result = agent.run('What time is it now?')
|
92
91
|
puts "✅ Answer: #{result.final_answer}"
|
93
|
-
puts "📊 Confidence: #{(result.confidence_score * 100).round(1)}%"
|
94
92
|
puts "⏱️ Iterations: #{result.iterations}"
|
data/examples/3_memory.rb
CHANGED
@@ -17,7 +17,6 @@ Soka.setup do |config|
|
|
17
17
|
|
18
18
|
config.performance do |perf|
|
19
19
|
perf.max_iterations = 10
|
20
|
-
perf.timeout = 30
|
21
20
|
end
|
22
21
|
end
|
23
22
|
|
@@ -136,7 +135,6 @@ result = agent_with_memory.run(
|
|
136
135
|
"Please calculate 15 * 8, and remember this calculation result with key 'first_calc'"
|
137
136
|
)
|
138
137
|
puts "Agent: #{result.final_answer}"
|
139
|
-
puts "Confidence: #{(result.confidence_score * 100).round(1)}%"
|
140
138
|
puts "Iterations: #{result.iterations}"
|
141
139
|
puts '-' * 50
|
142
140
|
|
data/lib/soka/agent.rb
CHANGED
@@ -17,7 +17,6 @@ module Soka
|
|
17
17
|
# @param engine [Class] The engine class to use (defaults to Engine::React)
|
18
18
|
# @param options [Hash] Configuration options
|
19
19
|
# @option options [Integer] :max_iterations Maximum iterations for reasoning
|
20
|
-
# @option options [Integer] :timeout Timeout in seconds for operations
|
21
20
|
# @option options [Symbol] :provider LLM provider override
|
22
21
|
# @option options [String] :model LLM model override
|
23
22
|
# @option options [String] :api_key LLM API key override
|
@@ -44,8 +43,9 @@ module Soka
|
|
44
43
|
# Apply performance-related configuration
|
45
44
|
# @param options [Hash] Configuration options
|
46
45
|
def apply_performance_config(options)
|
47
|
-
@max_iterations = options.fetch(:max_iterations)
|
48
|
-
|
46
|
+
@max_iterations = options.fetch(:max_iterations) do
|
47
|
+
self.class._max_iterations || Soka.configuration.performance.max_iterations
|
48
|
+
end
|
49
49
|
end
|
50
50
|
|
51
51
|
# Apply behavior-related configuration
|
data/lib/soka/agent_tool.rb
CHANGED
@@ -5,6 +5,15 @@ module Soka
|
|
5
5
|
class AgentTool
|
6
6
|
include AgentTools::ParamsValidator
|
7
7
|
|
8
|
+
TYPE_MAPPING = {
|
9
|
+
'String' => 'string',
|
10
|
+
'Integer' => 'integer', 'Fixnum' => 'integer', 'Bignum' => 'integer',
|
11
|
+
'Float' => 'number', 'Numeric' => 'number',
|
12
|
+
'TrueClass' => 'boolean', 'FalseClass' => 'boolean', 'Boolean' => 'boolean',
|
13
|
+
'Array' => 'array',
|
14
|
+
'Hash' => 'object', 'Object' => 'object'
|
15
|
+
}.freeze
|
16
|
+
|
8
17
|
# Handles parameter definitions for tools
|
9
18
|
class ParamsDefinition
|
10
19
|
attr_reader :required_params, :optional_params, :validations
|
@@ -109,22 +118,7 @@ module Soka
|
|
109
118
|
end
|
110
119
|
|
111
120
|
def type_to_json_type(ruby_type)
|
112
|
-
|
113
|
-
end
|
114
|
-
|
115
|
-
def type_mapping
|
116
|
-
@type_mapping ||= build_type_mapping
|
117
|
-
end
|
118
|
-
|
119
|
-
def build_type_mapping
|
120
|
-
{
|
121
|
-
'String' => 'string',
|
122
|
-
'Integer' => 'integer', 'Fixnum' => 'integer', 'Bignum' => 'integer',
|
123
|
-
'Float' => 'number', 'Numeric' => 'number',
|
124
|
-
'TrueClass' => 'boolean', 'FalseClass' => 'boolean', 'Boolean' => 'boolean',
|
125
|
-
'Array' => 'array',
|
126
|
-
'Hash' => 'object', 'Object' => 'object'
|
127
|
-
}.freeze
|
121
|
+
TYPE_MAPPING[ruby_type.to_s] || 'string'
|
128
122
|
end
|
129
123
|
end
|
130
124
|
|
@@ -10,7 +10,7 @@ module Soka
|
|
10
10
|
|
11
11
|
# Class methods for DSL
|
12
12
|
module ClassMethods
|
13
|
-
attr_accessor :_provider, :_model, :_api_key, :_max_iterations, :
|
13
|
+
attr_accessor :_provider, :_model, :_api_key, :_max_iterations, :_tools, :_retry_config, :_hooks,
|
14
14
|
:_instructions, :_think_in
|
15
15
|
|
16
16
|
def inherited(subclass)
|
@@ -44,12 +44,6 @@ module Soka
|
|
44
44
|
@_max_iterations = num
|
45
45
|
end
|
46
46
|
|
47
|
-
# Define timeout for the agent
|
48
|
-
# @param duration [Integer] The timeout duration in seconds
|
49
|
-
def timeout(duration)
|
50
|
-
@_timeout = duration
|
51
|
-
end
|
52
|
-
|
53
47
|
# Define custom instructions (system prompt) for the agent
|
54
48
|
# @param text_or_method [String, Symbol] The custom instructions/system prompt or method name
|
55
49
|
# @example Using a string
|
@@ -10,12 +10,12 @@ module Soka
|
|
10
10
|
# @param hook_type [Symbol] The type of hook (:before_action, :after_action, :on_error)
|
11
11
|
# @param args [Array] Arguments to pass to the hook methods
|
12
12
|
# @return [Object, nil] The result from on_error hooks, or nil
|
13
|
-
def run_hooks(hook_type,
|
13
|
+
def run_hooks(hook_type, ...)
|
14
14
|
return unless self.class._hooks && self.class._hooks[hook_type]
|
15
15
|
|
16
16
|
self.class._hooks[hook_type].each do |method_name|
|
17
17
|
if respond_to?(method_name, true)
|
18
|
-
result = send(method_name,
|
18
|
+
result = send(method_name, ...)
|
19
19
|
return result if hook_type == :on_error && result
|
20
20
|
end
|
21
21
|
end
|
@@ -32,8 +32,7 @@ module Soka
|
|
32
32
|
thoughts: engine_result.thoughts,
|
33
33
|
final_answer: engine_result.final_answer,
|
34
34
|
status: engine_result.status,
|
35
|
-
error: engine_result.error
|
36
|
-
confidence_score: engine_result.confidence_score
|
35
|
+
error: engine_result.error
|
37
36
|
)
|
38
37
|
end
|
39
38
|
|
@@ -59,8 +58,7 @@ module Soka
|
|
59
58
|
thoughts: [],
|
60
59
|
final_answer: nil,
|
61
60
|
status: :failed,
|
62
|
-
error: error.message
|
63
|
-
confidence_score: 0.0
|
61
|
+
error: error.message
|
64
62
|
)
|
65
63
|
end
|
66
64
|
end
|
data/lib/soka/configuration.rb
CHANGED
@@ -5,7 +5,7 @@ module Soka
|
|
5
5
|
class Configuration
|
6
6
|
# AI provider configuration
|
7
7
|
class AIConfig
|
8
|
-
attr_accessor :provider, :model, :api_key
|
8
|
+
attr_accessor :provider, :model, :api_key
|
9
9
|
|
10
10
|
def initialize
|
11
11
|
@provider = :gemini
|
@@ -15,11 +15,10 @@ module Soka
|
|
15
15
|
|
16
16
|
# Performance-related configuration
|
17
17
|
class PerformanceConfig
|
18
|
-
attr_accessor :max_iterations
|
18
|
+
attr_accessor :max_iterations
|
19
19
|
|
20
20
|
def initialize
|
21
21
|
@max_iterations = 10
|
22
|
-
@timeout = 30
|
23
22
|
end
|
24
23
|
end
|
25
24
|
|
@@ -15,7 +15,7 @@ module Soka
|
|
15
15
|
{
|
16
16
|
thoughts: extract_tagged_content(text, 'Thought'),
|
17
17
|
actions: extract_actions(text),
|
18
|
-
final_answer: extract_tagged_content(text, '
|
18
|
+
final_answer: extract_tagged_content(text, 'FinalAnswer').first
|
19
19
|
}
|
20
20
|
end
|
21
21
|
|
@@ -29,30 +29,18 @@ module Soka
|
|
29
29
|
action_blocks.filter_map { |block| parse_action_block(block[0]) }
|
30
30
|
end
|
31
31
|
|
32
|
+
# Parse action block from LLM response in JSON format
|
33
|
+
# @param content [String] The action block content containing JSON
|
34
|
+
# @return [Hash, nil] The parsed action with tool and params
|
32
35
|
def parse_action_block(content)
|
33
36
|
content = content.strip
|
34
|
-
|
35
|
-
params_match = content.match(/Parameters:\s*(.+)/m)
|
37
|
+
action_json = Oj.load(content, symbol_keys: true)
|
36
38
|
|
37
|
-
return unless
|
39
|
+
return nil unless action_json[:tool]
|
38
40
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
{ tool: tool_name, params: params }
|
44
|
-
end
|
45
|
-
|
46
|
-
# Parse JSON parameters from action block
|
47
|
-
# @param params_json [String] The JSON string to parse
|
48
|
-
# @return [Hash] The parsed parameters as a hash with symbol keys
|
49
|
-
def parse_json_params(params_json)
|
50
|
-
# Clean up the JSON string - remove any trailing commas or whitespace
|
51
|
-
cleaned_json = params_json.strip.gsub(/,\s*}/, '}').gsub(/,\s*\]/, ']')
|
52
|
-
JSON.parse(cleaned_json, symbolize_names: true)
|
53
|
-
rescue JSON::ParserError
|
54
|
-
# Return empty hash to continue when JSON parsing fails
|
55
|
-
{}
|
41
|
+
{ tool: action_json[:tool], params: action_json[:parameters] || {} }
|
42
|
+
rescue Oj::ParseError
|
43
|
+
nil
|
56
44
|
end
|
57
45
|
end
|
58
46
|
end
|
@@ -86,16 +86,27 @@ module Soka
|
|
86
86
|
# @param context [ReasoningContext] The reasoning context
|
87
87
|
# @param content [String] The raw response content
|
88
88
|
def handle_no_action(context, content)
|
89
|
+
# Important: We still need to add the message to maintain conversation flow
|
90
|
+
# but we should NOT add a new thought since the response was incomplete
|
89
91
|
context.add_message(role: 'assistant', content: content)
|
90
92
|
context.add_message(
|
91
93
|
role: 'user',
|
92
|
-
content:
|
94
|
+
content: format_reminder
|
93
95
|
)
|
94
96
|
end
|
95
97
|
|
96
98
|
def format_observation(observation)
|
97
99
|
"<Observation>#{observation}</Observation>"
|
98
100
|
end
|
101
|
+
|
102
|
+
def format_reminder
|
103
|
+
# Strict mode (default)
|
104
|
+
'CRITICAL: Only responses with proper <Thought>, <Action>, and <FinalAnswer> tags will be processed. ' \
|
105
|
+
'Other formats will fail.'
|
106
|
+
# Normal mode (alternative)
|
107
|
+
# 'Structure your response using these required tags: <Thought> for reasoning, ' \
|
108
|
+
# '<Action> for tool usage, and <FinalAnswer> for conclusions.'
|
109
|
+
end
|
99
110
|
end
|
100
111
|
end
|
101
112
|
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Soka
|
4
|
+
module Engines
|
5
|
+
module Prompts
|
6
|
+
# Base module for prompt generation
|
7
|
+
module Base
|
8
|
+
private
|
9
|
+
|
10
|
+
def system_prompt
|
11
|
+
# Use custom instructions if provided, otherwise use default ReAct prompt
|
12
|
+
if custom_instructions
|
13
|
+
combine_with_react_format(custom_instructions).strip
|
14
|
+
else
|
15
|
+
default_react_prompt.strip
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def default_react_prompt
|
20
|
+
tools_description = format_tools_description(tools)
|
21
|
+
|
22
|
+
<<~PROMPT
|
23
|
+
You are an AI assistant that uses the ReAct (Reasoning and Acting) framework to solve problems step by step.
|
24
|
+
|
25
|
+
You have access to the following tools:
|
26
|
+
#{tools_description}
|
27
|
+
|
28
|
+
#{format_instructions}
|
29
|
+
PROMPT
|
30
|
+
end
|
31
|
+
|
32
|
+
def combine_with_react_format(instructions)
|
33
|
+
tools_description = format_tools_description(tools)
|
34
|
+
|
35
|
+
<<~PROMPT
|
36
|
+
#{react_base_prompt}
|
37
|
+
|
38
|
+
#{react_framework_structure}
|
39
|
+
|
40
|
+
You have access to the following tools:
|
41
|
+
#{tools_description}
|
42
|
+
|
43
|
+
#{format_instructions}
|
44
|
+
|
45
|
+
#{custom_instructions_section(instructions)}
|
46
|
+
PROMPT
|
47
|
+
end
|
48
|
+
|
49
|
+
def react_base_prompt
|
50
|
+
'You are an AI assistant that uses the ReAct (Reasoning and Acting) framework to solve problems step by step.'
|
51
|
+
end
|
52
|
+
|
53
|
+
def react_framework_structure
|
54
|
+
<<~STRUCTURE
|
55
|
+
🔧 REACT FRAMEWORK STRUCTURE:
|
56
|
+
You MUST use XML-style tags to structure your response. Each tag has a specific purpose:
|
57
|
+
- <Thought>: Your first-person reasoning (max 30 words)
|
58
|
+
- <Action>: Tool invocation with JSON parameters
|
59
|
+
- <Observation>: Tool results (provided by system)
|
60
|
+
- <FinalAnswer>: Your complete solution (direct & concise)
|
61
|
+
|
62
|
+
These tags are MANDATORY and define the ReAct workflow. The system parses these tags to understand your reasoning process.
|
63
|
+
STRUCTURE
|
64
|
+
end
|
65
|
+
|
66
|
+
def custom_instructions_section(instructions)
|
67
|
+
<<~SECTION
|
68
|
+
📋 CUSTOM BEHAVIOR INSTRUCTIONS:
|
69
|
+
═══════════════════════════════════════════
|
70
|
+
The following instructions apply to the CONTENT within your XML tags, NOT the ReAct structure itself:
|
71
|
+
|
72
|
+
#{instructions}
|
73
|
+
|
74
|
+
⚠️ IMPORTANT: These custom instructions modify HOW you think and respond within the tags,
|
75
|
+
but DO NOT change the requirement to use the XML tag structure for ReAct.
|
76
|
+
═══════════════════════════════════════════
|
77
|
+
SECTION
|
78
|
+
end
|
79
|
+
|
80
|
+
def format_instructions
|
81
|
+
thinking_instruction = build_thinking_instruction(think_in)
|
82
|
+
iteration_limit = build_iteration_limit_warning
|
83
|
+
|
84
|
+
<<~INSTRUCTIONS
|
85
|
+
#{iteration_limit}
|
86
|
+
|
87
|
+
🎯 MANDATORY XML TAG STRUCTURE:
|
88
|
+
You MUST use XML-style tags to wrap your content. This is NOT optional.
|
89
|
+
The system relies on these tags to parse and understand your reasoning.
|
90
|
+
|
91
|
+
#{thinking_instruction}
|
92
|
+
|
93
|
+
#{step_format_example}
|
94
|
+
|
95
|
+
#{action_format_rules}
|
96
|
+
|
97
|
+
#{final_answer_critical_format}
|
98
|
+
INSTRUCTIONS
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|