soka 0.0.1.beta2

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 (45) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.rubocop.yml +365 -0
  4. data/CHANGELOG.md +31 -0
  5. data/CLAUDE.md +213 -0
  6. data/LICENSE +21 -0
  7. data/README.md +650 -0
  8. data/Rakefile +10 -0
  9. data/examples/1_basic.rb +94 -0
  10. data/examples/2_event_handling.rb +120 -0
  11. data/examples/3_memory.rb +182 -0
  12. data/examples/4_hooks.rb +140 -0
  13. data/examples/5_error_handling.rb +85 -0
  14. data/examples/6_retry.rb +164 -0
  15. data/examples/7_tool_conditional.rb +180 -0
  16. data/examples/8_multi_provider.rb +112 -0
  17. data/lib/soka/agent.rb +130 -0
  18. data/lib/soka/agent_tool.rb +146 -0
  19. data/lib/soka/agent_tools/params_validator.rb +139 -0
  20. data/lib/soka/agents/dsl_methods.rb +140 -0
  21. data/lib/soka/agents/hook_manager.rb +68 -0
  22. data/lib/soka/agents/llm_builder.rb +32 -0
  23. data/lib/soka/agents/retry_handler.rb +74 -0
  24. data/lib/soka/agents/tool_builder.rb +78 -0
  25. data/lib/soka/configuration.rb +60 -0
  26. data/lib/soka/engines/base.rb +67 -0
  27. data/lib/soka/engines/concerns/prompt_template.rb +130 -0
  28. data/lib/soka/engines/concerns/response_processor.rb +103 -0
  29. data/lib/soka/engines/react.rb +136 -0
  30. data/lib/soka/engines/reasoning_context.rb +92 -0
  31. data/lib/soka/llm.rb +85 -0
  32. data/lib/soka/llms/anthropic.rb +124 -0
  33. data/lib/soka/llms/base.rb +114 -0
  34. data/lib/soka/llms/concerns/response_parser.rb +47 -0
  35. data/lib/soka/llms/concerns/streaming_handler.rb +78 -0
  36. data/lib/soka/llms/gemini.rb +106 -0
  37. data/lib/soka/llms/openai.rb +97 -0
  38. data/lib/soka/memory.rb +83 -0
  39. data/lib/soka/result.rb +136 -0
  40. data/lib/soka/test_helpers.rb +162 -0
  41. data/lib/soka/thoughts_memory.rb +112 -0
  42. data/lib/soka/version.rb +5 -0
  43. data/lib/soka.rb +49 -0
  44. data/sig/soka.rbs +4 -0
  45. metadata +158 -0
data/README.md ADDED
@@ -0,0 +1,650 @@
1
+ # Soka
2
+
3
+ <p align="center">
4
+ <strong>Ruby AI Agent Framework based on ReAct Pattern</strong>
5
+ </p>
6
+
7
+ <p align="center">
8
+ <a href="#features">Features</a> •
9
+ <a href="#installation">Installation</a> •
10
+ <a href="#quick-start">Quick Start</a> •
11
+ <a href="#advanced-features">Advanced Features</a> •
12
+ <a href="#api-documentation">API Documentation</a> •
13
+ <a href="#examples">Examples</a> •
14
+ <a href="#contributing">Contributing</a>
15
+ </p>
16
+
17
+ Soka is a Ruby AI Agent framework based on the ReAct (Reasoning and Acting) pattern, supporting multiple AI providers, offering an object-oriented tool system and intelligent memory management. It enables you to quickly build intelligent agents that handle complex reasoning and action tasks.
18
+
19
+ ## Features
20
+
21
+ - 🤖 **Multi AI Provider Support**: Google Gemini, OpenAI, Anthropic
22
+ - 🛠️ **Object-Oriented Tool System**: Grape API-like parameter definition and validation
23
+ - 🧠 **Intelligent Memory Management**: Conversation history and thought process recording
24
+ - 🔄 **ReAct Reasoning Pattern**: Tagged thought-action-observation loop
25
+ - ⚡ **Flexible Configuration System**: Global and instance-level configuration options
26
+ - 🔁 **Error Handling and Retry**: Built-in exponential backoff retry mechanism
27
+ - 🧪 **Test Friendly**: Complete test helper tools
28
+ - 📝 **Full Type Support**: Using dry-rb ecosystem
29
+ - 🚀 **Modular Design**: Easy to extend and maintain
30
+ - 💾 **Built-in Caching Mechanism**: Improve performance and save costs
31
+
32
+ ## Installation
33
+
34
+ Add the following to your Gemfile:
35
+
36
+ ```ruby
37
+ gem 'soka'
38
+ ```
39
+
40
+ Then execute:
41
+
42
+ ```bash
43
+ bundle install
44
+ ```
45
+
46
+ Or install directly:
47
+
48
+ ```bash
49
+ gem install soka
50
+ ```
51
+
52
+ ## Quick Start
53
+
54
+ ### 1. Set up API Key
55
+
56
+ ```bash
57
+ # Method 1: Environment variable
58
+ export GEMINI_API_KEY="your-api-key"
59
+
60
+ # Method 2: Create .env file
61
+ echo "GEMINI_API_KEY=your-api-key" > .env
62
+ ```
63
+
64
+ Get API Keys:
65
+ - [Google AI Studio](https://aistudio.google.com/app/apikey)
66
+ - [OpenAI Platform](https://platform.openai.com/api-keys)
67
+ - [Anthropic Console](https://console.anthropic.com/settings/keys)
68
+
69
+ ### 2. Basic Usage
70
+
71
+ ```ruby
72
+ require 'soka'
73
+
74
+ # Create a simple time tool
75
+ class TimeTool < Soka::AgentTool
76
+ desc "Get current time"
77
+
78
+ def call
79
+ Time.now.strftime('%Y-%m-%d %H:%M:%S')
80
+ end
81
+ end
82
+
83
+ # Create Agent
84
+ class SimpleAgent < Soka::Agent
85
+ tool TimeTool
86
+ end
87
+
88
+ # Execute
89
+ agent = SimpleAgent.new
90
+ result = agent.run("What time is it?")
91
+ puts result.final_answer
92
+ ```
93
+
94
+ ### 3. Run Examples
95
+
96
+ ```bash
97
+ # Run full example (API key required)
98
+ ruby examples/1_basic.rb
99
+ ```
100
+
101
+ ## Core Concepts
102
+
103
+ ### Global Configuration
104
+
105
+ ```ruby
106
+ Soka.setup do |config|
107
+ # AI Configuration
108
+ config.ai do |ai|
109
+ ai.provider = :gemini # :gemini, :openai, :anthropic
110
+ ai.model = 'gemini-2.5-flash-lite'
111
+ ai.api_key = ENV['GEMINI_API_KEY']
112
+ end
113
+
114
+ # Performance Configuration
115
+ config.performance do |perf|
116
+ perf.max_iterations = 10 # ReAct max iterations
117
+ perf.timeout = 30 # API call timeout (seconds)
118
+ end
119
+
120
+ # Default tools
121
+ config.tools = [SearchTool, TimeTool]
122
+ end
123
+ ```
124
+
125
+ ### Defining Tools
126
+
127
+ Tools are functional modules that Agents can use:
128
+
129
+ ```ruby
130
+ class SearchTool < Soka::AgentTool
131
+ desc "Search the web for information"
132
+
133
+ params do
134
+ requires :query, String, desc: "The query to search for"
135
+ optional :location, String, desc: "Location context", default: "Global"
136
+
137
+ # Parameter validation
138
+ validates :query, presence: true, length: { minimum: 1, maximum: 500 }
139
+ validates :location, inclusion: { in: %w[Global US Europe Asia] }, allow_nil: true
140
+ end
141
+
142
+ def call(query:, location: "Global")
143
+ # Actual search logic
144
+ perform_search(query, location)
145
+ rescue => e
146
+ { error: e.message, tool: self.class.name }
147
+ end
148
+
149
+ private
150
+
151
+ def perform_search(query, location)
152
+ # Here you can call real search APIs
153
+ "Search results for #{query} in #{location}..."
154
+ end
155
+ end
156
+ ```
157
+
158
+ ### Defining Agents
159
+
160
+ Agents are the entities that perform ReAct reasoning:
161
+
162
+ ```ruby
163
+ class WeatherAgent < Soka::Agent
164
+ # AI settings (override global settings)
165
+ provider :gemini
166
+ model 'gemini-2.5-flash-lite'
167
+ max_iterations 10
168
+ timeout 30
169
+
170
+ # Register tools
171
+ tool SearchTool
172
+ tool TimeTool
173
+
174
+ # Conditional tool registration
175
+ tool CalculatorTool, if: -> { ENV['ENABLE_CALCULATOR'] == 'true' }
176
+
177
+ # Batch registration
178
+ tools SearchTool, TimeTool, WeatherTool
179
+
180
+ # Custom tool (functional) - requires description as second parameter
181
+ tool :get_weather, "Get weather for a location"
182
+
183
+ # Lifecycle hooks
184
+ before_action :track_action
185
+ after_action :update_metrics
186
+ on_error :handle_error
187
+
188
+ private
189
+
190
+ # Method implementation for functional tool
191
+ # Note: This is currently experimental and not fully implemented
192
+ def get_weather(location:)
193
+ "#{location} is currently sunny, temperature 25°C"
194
+ end
195
+
196
+ def track_action(action)
197
+ # Track action execution
198
+ @action_count ||= 0
199
+ @action_count += 1
200
+ end
201
+
202
+ def update_metrics(result)
203
+ # Update metrics
204
+ # metrics.record(result)
205
+ end
206
+
207
+ def handle_error(error, context)
208
+ # Handle errors
209
+ :continue # or :stop to interrupt execution
210
+ end
211
+ end
212
+ ```
213
+
214
+ ### Using Agents
215
+
216
+ #### Block Mode (Real-time Feedback)
217
+
218
+ Suitable for scenarios that need to display the execution process:
219
+
220
+ ```ruby
221
+ agent = WeatherAgent.new
222
+
223
+ agent.run('What is the weather in Tokyo today?') do |event|
224
+ case event.type
225
+ when :thought
226
+ puts "💭 Thinking: #{event.content}"
227
+ when :action
228
+ puts "🔧 Action: Using tool #{event.content[:tool]}"
229
+ when :observation
230
+ puts "👀 Observation: #{event.content}"
231
+ when :final_answer
232
+ puts "✅ Answer: #{event.content}"
233
+ when :error
234
+ puts "❌ Error: #{event.content}"
235
+ end
236
+ end
237
+ ```
238
+
239
+ #### Direct Mode (Get Result)
240
+
241
+ Suitable for scenarios that only need the final result:
242
+
243
+ ```ruby
244
+ agent = WeatherAgent.new
245
+ result = agent.run('What is the weather in Tokyo today?')
246
+
247
+ # Result object provides rich information
248
+ puts result.final_answer # Final answer
249
+ puts result.confidence_score # Confidence score (0.0-1.0)
250
+ puts result.iterations # Number of iterations used
251
+ puts result.status # :success, :failed, :timeout, :max_iterations_reached
252
+ puts result.execution_time # Execution time (if recorded)
253
+
254
+ # Check execution status
255
+ if result.successful?
256
+ puts "Success: #{result.final_answer}"
257
+ elsif result.failed?
258
+ puts "Failed: #{result.error}"
259
+ elsif result.timeout?
260
+ puts "Execution timeout"
261
+ elsif result.max_iterations_reached?
262
+ puts "Max iterations reached"
263
+ end
264
+ ```
265
+
266
+ ### Memory Management
267
+
268
+ #### Basic Conversation Memory
269
+
270
+ ```ruby
271
+ # Initialize Agent with history
272
+ memory = [
273
+ { role: 'user', content: 'My name is John' },
274
+ { role: 'assistant', content: 'Hello John! Nice to meet you.' }
275
+ ]
276
+
277
+ agent = WeatherAgent.new(memory: memory)
278
+ result = agent.run('What is my name?')
279
+ # => "Your name is John."
280
+
281
+ # Memory updates automatically
282
+ puts agent.memory
283
+ # <Soka::Memory> [
284
+ # { role: 'user', content: 'My name is John' },
285
+ # { role: 'assistant', content: 'Hello John! Nice to meet you.' },
286
+ # { role: 'user', content: 'What is my name?' },
287
+ # { role: 'assistant', content: 'Your name is John.' }
288
+ # ]
289
+ ```
290
+
291
+ #### Thought Process Memory
292
+
293
+ ```ruby
294
+ # View complete thought process
295
+ puts agent.thoughts_memory
296
+ # <Soka::ThoughtsMemory> (3 sessions, 2 successful, 1 failed, avg confidence: 0.82, avg iterations: 2.3)
297
+
298
+ # Get detailed information for specific session
299
+ last_session = agent.thoughts_memory.last_session
300
+ puts last_session[:thoughts] # All thinking steps
301
+ puts last_session[:confidence_score] # Confidence score for that execution
302
+ ```
303
+
304
+ ## Advanced Features
305
+
306
+ ### ReAct Flow Format
307
+
308
+ Soka uses a tagged ReAct format:
309
+
310
+ ```xml
311
+ <Thought>I need to search for weather information in Tokyo</Thought>
312
+ <Action>
313
+ Tool: search
314
+ Parameters: {"query": "Tokyo weather", "location": "Japan"}
315
+ </Action>
316
+ <Observation>Tokyo today: Sunny, temperature 28°C, humidity 65%</Observation>
317
+ <Thought>I have obtained the weather information and can answer the user now</Thought>
318
+ <Final_Answer>Today in Tokyo it's sunny with a temperature of 28°C and humidity of 65%.</Final_Answer>
319
+ ```
320
+
321
+ ### Result Object Structure
322
+
323
+ ```ruby
324
+ # Result object attributes
325
+ result.input # User input
326
+ result.thoughts # Array of thinking steps
327
+ result.final_answer # Final answer
328
+ result.confidence_score # Confidence score (0.0-1.0)
329
+ result.status # Status (:success, :failed, :timeout, :max_iterations_reached)
330
+ result.error # Error message (if any)
331
+ result.execution_time # Execution time (seconds)
332
+ result.iterations # Number of iterations
333
+
334
+ # Complete structure
335
+ {
336
+ input: "User input",
337
+ thoughts: [
338
+ {
339
+ step: 1,
340
+ thought: "Thinking content",
341
+ action: { tool: "search", params: { query: "..." } },
342
+ observation: "Observation result"
343
+ }
344
+ ],
345
+ final_answer: "Final answer",
346
+ confidence_score: 0.85, # Calculated based on iterations
347
+ status: :success, # :success, :failed, :timeout, :max_iterations_reached
348
+ error: nil, # Error message (if any)
349
+ execution_time: 1.23, # Execution time (seconds)
350
+ iterations: 2, # Number of iterations
351
+ created_at: Time # Creation time
352
+ }
353
+ ```
354
+
355
+ ### Test Support
356
+
357
+ Soka provides complete test helper tools:
358
+
359
+ ```ruby
360
+ RSpec.describe WeatherAgent do
361
+ include Soka::TestHelpers
362
+
363
+ it "answers weather questions" do
364
+ # Mock AI response
365
+ mock_ai_response({
366
+ thoughts: [
367
+ {
368
+ step: 1,
369
+ thought: "Need to search for weather information",
370
+ action: { tool: "search", params: { query: "Tokyo weather" } },
371
+ observation: "Tokyo is sunny today"
372
+ }
373
+ ],
374
+ final_answer: "Tokyo is sunny today."
375
+ })
376
+
377
+ # Mock tool response
378
+ mock_tool_response(SearchTool, "Tokyo is sunny today")
379
+
380
+ agent = described_class.new
381
+ result = agent.run("What's the weather in Tokyo?")
382
+
383
+ expect(result).to be_successful
384
+ expect(result.final_answer).to include("sunny")
385
+ expect(result).to have_thoughts_count(1)
386
+ expect(result).to have_confidence_score_above(0.8)
387
+ end
388
+
389
+ it "handles tool errors gracefully" do
390
+ allow_tool_to_fail(SearchTool, StandardError.new("API error"))
391
+
392
+ agent = described_class.new
393
+ result = agent.run("Search test")
394
+
395
+ expect(result).to be_failed
396
+ expect(result.error).to include("API error")
397
+ end
398
+ end
399
+ ```
400
+
401
+ ### Custom Engines
402
+
403
+ You can implement your own reasoning engine:
404
+
405
+ ```ruby
406
+ class CustomEngine < Soka::Engines::Base
407
+ def reason(task, &block)
408
+ # Implement custom reasoning logic
409
+ context = Soka::Engines::ReasoningContext.new(
410
+ task: task,
411
+ event_handler: block,
412
+ max_iterations: max_iterations
413
+ )
414
+
415
+ # Use emit_event to send events
416
+ emit_event(:thought, "Starting reasoning...", &block)
417
+
418
+ # Perform reasoning...
419
+
420
+ # Return result (using Struct)
421
+ Soka::Engines::React::ReasonResult.new(
422
+ input: task,
423
+ thoughts: thoughts,
424
+ final_answer: answer,
425
+ status: :success,
426
+ confidence_score: calculate_confidence_score(thoughts, :success)
427
+ )
428
+ end
429
+ end
430
+
431
+ # Use custom engine
432
+ agent = MyAgent.new(engine: CustomEngine)
433
+ ```
434
+
435
+ ## Examples
436
+
437
+ The `examples/` directory contains several examples demonstrating different features of Soka, ordered from basic to advanced:
438
+
439
+ ### 1. Basic Example (`examples/1_basic.rb`)
440
+ Demonstrates the fundamental usage of Soka with simple tools:
441
+ - Creating basic tools (SearchTool, TimeTool)
442
+ - Setting up an agent
443
+ - Running queries with event handling
444
+ - Direct result access
445
+
446
+ ### 2. Event Handling (`examples/2_event_handling.rb`)
447
+ Shows how to handle real-time events during agent execution:
448
+ - Event-based response handling
449
+ - Different event types (thought, action, observation, final_answer)
450
+ - Multi-step task processing
451
+ - Direct result mode vs event mode
452
+
453
+ ### 3. Memory Management (`examples/3_memory.rb`)
454
+ Illustrates memory features and conversation context:
455
+ - Using Soka::Memory for conversation history
456
+ - Array format for initial memory
457
+ - Tool-based memory storage and recall
458
+ - Accessing complete conversation history
459
+ - Viewing thinking processes
460
+
461
+ ### 4. Lifecycle Hooks (`examples/4_hooks.rb`)
462
+ Demonstrates lifecycle hooks for monitoring and control:
463
+ - `before_action` for pre-processing
464
+ - `after_action` for post-processing
465
+ - `on_error` for error handling
466
+ - Tracking agent activity and metrics
467
+
468
+ ### 5. Error Handling (`examples/5_error_handling.rb`)
469
+ Shows robust error handling mechanisms:
470
+ - Tool errors and agent-level errors
471
+ - Using `on_error` hooks
472
+ - Continuing execution after errors
473
+ - Error result inspection
474
+
475
+ ### 6. Retry Mechanisms (`examples/6_retry.rb`)
476
+ Demonstrates retry strategies for reliability:
477
+ - Handling transient failures
478
+ - Exponential backoff
479
+ - Rate limiting scenarios
480
+ - Configuring retry behavior
481
+
482
+ ### 7. Conditional Tools (`examples/7_tool_conditional.rb`)
483
+ Shows dynamic tool loading based on conditions:
484
+ - Environment-based tool loading
485
+ - Role-based access control
486
+ - Feature flag integration
487
+ - Time-based availability
488
+
489
+ ### 8. Multi-Provider Support (`examples/8_multi_provider.rb`)
490
+ Demonstrates using different AI providers:
491
+ - Configuring Gemini, OpenAI, and Anthropic
492
+ - Provider-specific features
493
+ - Comparing outputs across models
494
+ - Cost optimization strategies
495
+
496
+ To run any example:
497
+ ```bash
498
+ # Make sure you have the required API keys in your .env file
499
+ ruby examples/1_basic.rb
500
+ ```
501
+
502
+ ## API Documentation
503
+
504
+ ### Supported AI Providers
505
+
506
+ #### Google Gemini
507
+ - Models: `gemini-2.5-pro`, `gemini-2.5-flash`, `gemini-2.5-flash-lite`
508
+ - Environment variable: `GEMINI_API_KEY`
509
+ - Features: Fast response, cost-effective
510
+ - Default model: `gemini-2.5-flash-lite`
511
+
512
+ #### OpenAI
513
+ - Models: `gpt-4.1`, `gpt-4.1-mini`, `gpt-4.1-nano`
514
+ - Environment variable: `OPENAI_API_KEY`
515
+ - Features: Streaming support, powerful reasoning
516
+
517
+ #### Anthropic
518
+ - Models: `claude-opus-4-0`, `claude-sonnet-4-0`, `claude-3-5-haiku-latest`
519
+ - Environment variable: `ANTHROPIC_API_KEY`
520
+ - Features: Long context support, excellent code understanding
521
+
522
+ ### Configuration Options
523
+
524
+ | Option | Type | Default | Description |
525
+ |--------|------|---------|-------------|
526
+ | `ai.provider` | Symbol | `:gemini` | AI provider |
527
+ | `ai.model` | String | `"gemini-2.5-flash-lite"` | Model to use |
528
+ | `ai.api_key` | String | nil | API key |
529
+ | `performance.max_iterations` | Integer | 10 | Max iterations |
530
+ | `performance.timeout` | Integer | 30 | Timeout (seconds) |
531
+
532
+ ### Tool Parameter Validation
533
+
534
+ | Validator | Options | Description |
535
+ |-----------|---------|-------------|
536
+ | `presence` | `true/false` | Value cannot be empty |
537
+ | `length` | `minimum`, `maximum` | String length limits |
538
+ | `inclusion` | `in`, `allow_nil` | Value must be in specified list |
539
+ | `format` | `with` | Match regular expression |
540
+
541
+ ## Performance Optimization
542
+
543
+ 1. **Use appropriate models**:
544
+ - Simple tasks: `gemini-2.5-flash-lite` or `gpt-4.1-mini` or `claude-3-5-haiku-latest`
545
+ - Complex reasoning: `gemini-2.5-pro` or `gpt-4.1` or `claude-sonnet-4-0`
546
+
547
+ 2. **Control iterations**:
548
+ ```ruby
549
+ agent = MyAgent.new(max_iterations: 5) # Limit iterations
550
+ ```
551
+
552
+
553
+ ## Troubleshooting
554
+
555
+ ### Common Issues
556
+
557
+ 1. **API Key Error**
558
+ ```
559
+ Soka::LLMError: API key is required
560
+ ```
561
+ Solution: Ensure correct environment variable is set or provide API key in configuration
562
+
563
+ 2. **Timeout Error**
564
+ ```
565
+ Soka::LLMError: Request timed out
566
+ ```
567
+ Solution: Increase timeout or use a faster model
568
+
569
+ 3. **Max Iterations Reached**
570
+ ```
571
+ Status: max_iterations_reached
572
+ ```
573
+ Solution: Simplify the problem or increase `max_iterations`
574
+
575
+ ### Debugging Tips
576
+
577
+ ```ruby
578
+ # Adjust max iterations
579
+ Soka.configure do |c|
580
+ c.performance.max_iterations = 20
581
+ end
582
+
583
+ # Use block mode to see execution process
584
+ agent.run(query) do |event|
585
+ p event # Print all events
586
+ end
587
+
588
+ # Inspect thought process
589
+ result = agent.run(query)
590
+ result.thoughts.each do |thought|
591
+ puts "Step #{thought[:step]}: #{thought[:thought]}"
592
+ puts "Action: #{thought[:action]}" if thought[:action]
593
+ puts "Observation: #{thought[:observation]}" if thought[:observation]
594
+ end
595
+ ```
596
+
597
+ ## Development
598
+
599
+ ```bash
600
+ # Install dependencies
601
+ bundle install
602
+
603
+ # Run tests
604
+ bundle exec rspec
605
+
606
+ # Run Rubocop
607
+ bundle exec rubocop
608
+
609
+ # Open interactive console
610
+ bin/console
611
+
612
+ # Create new version
613
+ # 1. Update lib/soka/version.rb
614
+ # 2. Update CHANGELOG.md
615
+ # 3. Commit changes
616
+ # 4. Create tag
617
+ bundle exec rake release
618
+ ```
619
+
620
+ ## Contributing
621
+
622
+ We welcome all forms of contributions!
623
+
624
+ 1. Fork the project
625
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
626
+ 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
627
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
628
+ 5. Open a Pull Request
629
+
630
+ Please ensure:
631
+ - Add appropriate tests
632
+ - Update relevant documentation
633
+ - Follow existing code style
634
+ - Pass Rubocop checks
635
+
636
+ ## License
637
+
638
+ This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
639
+
640
+ ## Acknowledgments
641
+
642
+ - Thanks to the [ReAct paper](https://arxiv.org/abs/2210.03629) for the theoretical foundation
643
+ - Thanks to the [Regent](https://github.com/alextwoods/regent) project for architectural inspiration
644
+ - Thanks to all contributors for their efforts
645
+
646
+ ---
647
+
648
+ <p align="center">
649
+ Made with ❤️ in Taiwan
650
+ </p>
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+ require 'rubocop/rake_task'
6
+
7
+ RSpec::Core::RakeTask.new(:spec)
8
+ RuboCop::RakeTask.new
9
+
10
+ task default: %i[spec rubocop]