soka 0.0.5 → 0.0.7

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: eb5c0a25160f948f0514c8297166706adef7b1d1921854e95cedef2466b561f0
4
- data.tar.gz: afe45e214ea6077f81d3d21a56474213db820124d1bc46652a960d6a8c524e59
3
+ metadata.gz: 484709d85e7835138e502ed9a795257f46289df28f7f3fef31c3e048b62cdc20
4
+ data.tar.gz: 74fa32be0296709abe5dd18480a6fabb6ae9dd0f17aa608a7e1e9f51f3934b37
5
5
  SHA512:
6
- metadata.gz: 687b999469d40246dd5c224ec433d634089b81ee8f46d3ba85b6fa469317f2e1d1b65fba7e196905bee7060f41ab73423d054bce86ee432a3126f6868121dbe4
7
- data.tar.gz: fc52a74769adc232e881019aad8b3d4edc9f72e60a42bc2ecf3178f06591f24a7f9a38830f4c98a0f0048783a8b42ac5854a1e8caf0aac6a5baadbb8357d2d77
6
+ metadata.gz: 7665b62bd029b2b29c3cba25c42326160176b5426435fce0e37e4c7929afa33acacb29a5efb431f480efb7e94960d9406feb3467c30b8f284f7ef94cf8e8e5c7
7
+ data.tar.gz: 1af86f19c9d89d156826e676816908f8c56a443586281a744c75a3ba9f0f1c461989a223c82e79204042801147f81710cbb5224059d988de984f18a595c95aac
data/CHANGELOG.md CHANGED
@@ -7,6 +7,48 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.0.7] - 2025-08-15
11
+
12
+ ### Features
13
+ - feat: add execution time tracking to ReAct engine (eea5efb)
14
+
15
+ ### Code Refactoring
16
+ - refactor: reduce thought tag word limit for better conciseness (7b3290c)
17
+
18
+ ## [0.0.6] - 2025-08-12
19
+
20
+ ### Features
21
+ - feat: add OpenAI Responses API support with reasoning (8c81be8)
22
+ - feat: enhance ReAct prompt template with clearer instructions (d371c12)
23
+ - feat(gemini): add thinkingConfig to generationConfig (8e44e63)
24
+
25
+ ### Bug Fixes
26
+ - fix(openai): update default model name from gpt-5-nano to gpt-5-mini (1e5971f)
27
+ - fix: update Final_Answer tag to FinalAnswer across codebase (feb6071)
28
+
29
+ ### Documentation
30
+ - docs: add model testing results and update OpenAI model names (f83b987)
31
+ - docs: add problem solving guidelines for AI assistants (0e99e5f)
32
+
33
+ ### Code Refactoring
34
+ - refactor: reorganize prompt templates into modular structure (0c10e21)
35
+ - refactor: improve format reminder in response processor (a3de6a5)
36
+ - refactor: remove configurable timeout option (7e7e7c3)
37
+ - refactor: reorganize Anthropic LLM class structure (4fc4f90)
38
+ - refactor: extract format helpers from prompt template (d371c12)
39
+ - refactor: simplify AgentTool type mapping with constant (c609949)
40
+ - refactor: standardize Action tag to use single-line JSON format (5ac06f0)
41
+ - refactor(context): simplify iteration handling and improve logging (3e65643)
42
+ - refactor(llms): remove max_tokens parameter from configurations (d7a1fc8)
43
+
44
+ ### Performance
45
+ - perf: replace standard JSON parser with Oj for better performance (3fc969f)
46
+
47
+ ### Chores
48
+ - chore: update gitignore and documentation (8dc5102)
49
+ - chore: adjust HTTP client timeout settings (38f47a7)
50
+ - chore: update .gitignore to include .serena (90e3d41)
51
+
10
52
  ## [0.0.5] - 2025-08-05
11
53
 
12
54
  ### Bug Fixes
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><Final_Answer>Done</Final_Answer>')
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, :timeout, :max_iterations_reached
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
- Tool: search
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
- <Final_Answer>Today in Tokyo it's sunny with a temperature of 28°C and humidity of 65%.</Final_Answer>
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, :timeout, :max_iterations_reached)
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, :timeout, :max_iterations_reached
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-4.1`, `gpt-4.1-mini`, `gpt-4.1-nano`
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-4.1-mini` or `claude-3-5-haiku-latest`
700
- - Complex reasoning: `gemini-2.5-pro` or `gpt-4.1` or `claude-sonnet-4-0`
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. **Timeout Error**
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/alextwoods/regent) project for architectural inspiration
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
 
@@ -16,7 +16,6 @@ Soka.setup do |config|
16
16
 
17
17
  config.performance do |perf|
18
18
  perf.max_iterations = 5
19
- perf.timeout = 30
20
19
  end
21
20
  end
22
21
 
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
 
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) { self.class._max_iterations } || 10
48
- @timeout = options.fetch(:timeout) { self.class._timeout } || 30
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
@@ -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
- type_mapping[ruby_type.to_s] || 'string'
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, :_timeout, :_tools, :_retry_config, :_hooks,
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
@@ -32,7 +32,8 @@ 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
35
+ error: engine_result.error,
36
+ execution_time: engine_result.execution_time
36
37
  )
37
38
  end
38
39
 
@@ -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, :fallback_provider, :fallback_model, :fallback_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, :timeout
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, 'Final_Answer').first
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
- tool_match = content.match(/Tool:\s*(.+)/)
35
- params_match = content.match(/Parameters:\s*(.+)/m)
37
+ action_json = Oj.load(content, symbol_keys: true)
36
38
 
37
- return unless tool_match && params_match
39
+ return nil unless action_json[:tool]
38
40
 
39
- tool_name = tool_match[1].strip
40
- params_json = params_match[1].strip
41
- params = parse_json_params(params_json)
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: 'Please follow the exact format with <Thought>, <Action>, and <Final_Answer> tags.'
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 20 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
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Soka
4
+ module Engines
5
+ module Prompts
6
+ # Helper methods for formatting prompt components
7
+ module FormatHelpers
8
+ private
9
+
10
+ # Format tools description for prompt
11
+ # @param tools [Array] The tools available
12
+ # @return [String] Formatted tools description
13
+ def format_tools_description(tools)
14
+ return 'No tools available.' if tools.empty?
15
+
16
+ tools.map do |tool|
17
+ schema = tool.class.to_h
18
+ params_desc = format_parameters(schema[:parameters])
19
+
20
+ "- #{schema[:name]}: #{schema[:description]}\n Parameters: \n#{params_desc}"
21
+ end.join("\n")
22
+ end
23
+
24
+ # Format parameters for tool description
25
+ # @param params_schema [Hash] The parameters schema
26
+ # @return [String] Formatted parameters description
27
+ def format_parameters(params_schema)
28
+ return 'none' if params_schema[:properties].empty?
29
+
30
+ properties = params_schema[:properties].map do |name, config|
31
+ required = params_schema[:required].include?(name.to_s) ? '(required)' : '(optional)'
32
+ type = config[:type]
33
+ desc = config[:description]
34
+
35
+ " - #{name} #{required} [#{type}] - #{desc}"
36
+ end
37
+
38
+ properties.join("\n")
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end