soka-rails 0.0.1.beta4

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.
data/DESIGN.md ADDED
@@ -0,0 +1,957 @@
1
+ # Soka Rails Design Document
2
+
3
+ ## 1. System Architecture Design
4
+
5
+ ### 1.1 Overall Architecture Diagram
6
+
7
+ ```
8
+ ┌─────────────────────────────────────────────────────────────┐
9
+ │ Rails Application │
10
+ ├─────────────────────────────────────────────────────────────┤
11
+ │ Soka Rails Layer │
12
+ │ ┌─────────────┐ ┌──────────────┐ ┌──────────────────┐ │
13
+ │ │ Railtie │ │ Generators │ │ Test Helpers │ │
14
+ │ └─────────────┘ └──────────────┘ └──────────────────┘ │
15
+ │ ┌─────────────┐ ┌──────────────┐ ┌──────────────────┐ │
16
+ │ │Configuration│ │ Agent Bridge │ │ Tool Bridge │ │
17
+ │ └─────────────┘ └──────────────┘ └──────────────────┘ │
18
+ ├─────────────────────────────────────────────────────────────┤
19
+ │ Soka Core Framework │
20
+ │ ┌─────────────┐ ┌──────────────┐ ┌──────────────────┐ │
21
+ │ │ Agent │ │ Tool │ │ ReAct Engine │ │
22
+ │ └─────────────┘ └──────────────┘ └──────────────────┘ │
23
+ └─────────────────────────────────────────────────────────────┘
24
+ ```
25
+
26
+ ### 1.2 Module Design
27
+
28
+ #### 1.2.1 Core Module Structure
29
+ ```ruby
30
+ module Soka
31
+ module Rails
32
+ # Version definition
33
+ VERSION = "0.0.1.beta1"
34
+
35
+ # Main components
36
+ class Railtie < ::Rails::Railtie; end
37
+ class Configuration; end
38
+ class Engine < ::Rails::Engine; end
39
+
40
+ # Bridge layer
41
+ module AgentBridge; end
42
+ module ToolBridge; end
43
+
44
+ # Test support
45
+ module TestHelpers; end
46
+
47
+ # Generator namespace
48
+ module Generators; end
49
+ end
50
+ end
51
+ ```
52
+
53
+ ## 2. Core Component Design
54
+
55
+ ### 2.1 Railtie Design (FR-001, FR-002)
56
+
57
+ ```ruby
58
+ # lib/soka/rails/railtie.rb
59
+ module Soka
60
+ module Rails
61
+ class Railtie < ::Rails::Railtie
62
+ # Autoload path configuration
63
+ initializer "soka.autoload_paths" do |app|
64
+ app.config.autoload_paths << app.root.join("app/soka")
65
+ app.config.eager_load_paths << app.root.join("app/soka")
66
+ end
67
+
68
+ # Load configuration
69
+ initializer "soka.load_configuration" do
70
+ config_file = ::Rails.root.join("config/initializers/soka.rb")
71
+ require config_file if config_file.exist?
72
+ end
73
+
74
+ # Setup Zeitwerk
75
+ initializer "soka.setup_zeitwerk" do
76
+ ::Rails.autoloaders.main.push_dir(
77
+ ::Rails.root.join("app/soka"),
78
+ namespace: Object
79
+ )
80
+ end
81
+
82
+ # Integrate Rails logger
83
+ initializer "soka.setup_logger" do
84
+ Soka.configure do |config|
85
+ config.logger = ::Rails.logger
86
+ end
87
+ end
88
+
89
+ # Development environment hot reload
90
+ if ::Rails.env.development?
91
+ config.to_prepare do
92
+ # Reload Soka related classes
93
+ Dir[::Rails.root.join("app/soka/**/*.rb")].each { |f| load f }
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
99
+ ```
100
+
101
+ ### 2.2 Configuration Design (FR-004, FR-010)
102
+
103
+ ```ruby
104
+ # lib/soka/rails/configuration.rb
105
+ module Soka
106
+ module Rails
107
+ class Configuration
108
+ attr_accessor :ai_provider, :ai_model, :ai_api_key
109
+ attr_accessor :max_iterations, :timeout
110
+
111
+ def initialize
112
+ # Use ENV.fetch to set default values
113
+ @ai_provider = ENV.fetch('SOKA_PROVIDER', :gemini).to_sym
114
+ @ai_model = ENV.fetch('SOKA_MODEL', 'gemini-2.0-flash-exp')
115
+ @ai_api_key = ENV.fetch('SOKA_API_KEY', nil)
116
+
117
+ # Performance settings
118
+ @max_iterations = ::Rails.env.production? ? 10 : 5
119
+ @timeout = 30.seconds
120
+ end
121
+
122
+ # DSL configuration methods
123
+ def ai
124
+ yield(AIConfig.new(self)) if block_given?
125
+ end
126
+
127
+ def performance
128
+ yield(PerformanceConfig.new(self)) if block_given?
129
+ end
130
+
131
+ # Internal configuration classes
132
+ class AIConfig
133
+ def initialize(config)
134
+ @config = config
135
+ end
136
+
137
+ def provider=(value)
138
+ @config.ai_provider = value
139
+ end
140
+
141
+ def model=(value)
142
+ @config.ai_model = value
143
+ end
144
+
145
+ def api_key=(value)
146
+ @config.ai_api_key = value
147
+ end
148
+ end
149
+
150
+ class PerformanceConfig
151
+ def initialize(config)
152
+ @config = config
153
+ end
154
+
155
+ def max_iterations=(value)
156
+ @config.max_iterations = value
157
+ end
158
+
159
+ def timeout=(value)
160
+ @config.timeout = value
161
+ end
162
+ end
163
+ end
164
+
165
+ # Global configuration methods
166
+ class << self
167
+ attr_writer :configuration
168
+
169
+ def configuration
170
+ @configuration ||= Configuration.new
171
+ end
172
+
173
+ def configure
174
+ yield(configuration)
175
+ end
176
+ end
177
+ end
178
+ end
179
+ ```
180
+
181
+ ### 2.3 ApplicationAgent Design (FR-006, FR-007)
182
+
183
+ ```ruby
184
+ # app/soka/agents/application_agent.rb
185
+ class ApplicationAgent < Soka::Agent
186
+ # Rails environment default configuration
187
+ if defined?(::Rails)
188
+ max_iterations ::Rails.env.production? ? 10 : 5
189
+ timeout 30.seconds
190
+ end
191
+
192
+ # Default tools
193
+ tool RailsInfoTool if defined?(RailsInfoTool)
194
+
195
+ # Rails integration hooks
196
+ before_action :log_agent_start
197
+ after_action :log_agent_complete
198
+ on_error :handle_agent_error
199
+
200
+ private
201
+
202
+ # Log Agent start execution
203
+ def log_agent_start(input)
204
+ ::Rails.logger.info "[Soka] #{self.class.name} started: #{input.truncate(100)}"
205
+ tag_request
206
+ end
207
+
208
+ # Log Agent completion
209
+ def log_agent_complete(result)
210
+ ::Rails.logger.info "[Soka] #{self.class.name} completed: #{result.status}"
211
+ cleanup_request_tags
212
+ end
213
+
214
+ # Error handling and tracking
215
+ def handle_agent_error(error, context)
216
+ ::Rails.logger.error "[Soka] #{self.class.name} error: #{error.message}"
217
+ ::Rails.logger.error error.backtrace.join("\n")
218
+
219
+ # Integrate Rails error tracking
220
+ if defined?(::Rails.error)
221
+ ::Rails.error.report(error,
222
+ context: {
223
+ agent: self.class.name,
224
+ input: context[:input],
225
+ iteration: context[:iteration]
226
+ }
227
+ )
228
+ end
229
+
230
+ :continue # Allow continuing execution
231
+ end
232
+
233
+ # Tag request for tracking
234
+ def tag_request
235
+ return unless defined?(::Rails.application.config.log_tags)
236
+
237
+ request_id = SecureRandom.uuid
238
+ Thread.current[:soka_request_id] = request_id
239
+ end
240
+
241
+ def cleanup_request_tags
242
+ Thread.current[:soka_request_id] = nil
243
+ end
244
+ end
245
+ ```
246
+
247
+ ### 2.4 ApplicationTool Design (FR-008)
248
+
249
+ ```ruby
250
+ # app/soka/tools/application_tool.rb
251
+ class ApplicationTool < Soka::AgentTool
252
+ # Rails specific helper methods
253
+
254
+ protected
255
+
256
+ # Safe parameter filtering
257
+ private
258
+ def safe_params(params)
259
+ ActionController::Parameters.new(params).permit!
260
+ end
261
+ end
262
+ ```
263
+
264
+ ### 2.5 RailsInfoTool Design (FR-009)
265
+
266
+ ```ruby
267
+ # app/soka/tools/rails_info_tool.rb
268
+ class RailsInfoTool < ApplicationTool
269
+ desc "Get Rails application information"
270
+
271
+ params do
272
+ requires :info_type, String,
273
+ desc: "Type of information to retrieve",
274
+ validates: {
275
+ inclusion: {
276
+ in: %w[routes version environment config]
277
+ }
278
+ }
279
+ end
280
+
281
+ def call(info_type:)
282
+ case info_type
283
+ when 'routes'
284
+ fetch_routes_info
285
+ when 'version'
286
+ fetch_version_info
287
+ when 'environment'
288
+ fetch_environment_info
289
+ when 'config'
290
+ fetch_safe_config_info
291
+ end
292
+ end
293
+
294
+ private
295
+
296
+ def fetch_routes_info
297
+ routes = ::Rails.application.routes.routes.map do |route|
298
+ next unless route.name.present?
299
+
300
+ {
301
+ name: route.name,
302
+ verb: route.verb,
303
+ path: route.path.spec.to_s,
304
+ controller: route.defaults[:controller],
305
+ action: route.defaults[:action]
306
+ }
307
+ end.compact
308
+
309
+ { routes: routes }
310
+ end
311
+
312
+ def fetch_version_info
313
+ {
314
+ rails_version: ::Rails.version,
315
+ ruby_version: RUBY_VERSION,
316
+ app_name: ::Rails.application.class.module_parent_name,
317
+ environment: ::Rails.env
318
+ }
319
+ end
320
+
321
+ def fetch_environment_info
322
+ {
323
+ rails_env: ::Rails.env,
324
+ time_zone: Time.zone.name,
325
+ host: ::Rails.application.config.hosts.first.to_s,
326
+ database: ActiveRecord::Base.connection.adapter_name
327
+ }
328
+ end
329
+
330
+ def fetch_safe_config_info
331
+ # Only return safe configuration values
332
+ safe_configs = {
333
+ time_zone: ::Rails.application.config.time_zone,
334
+ locale: I18n.locale.to_s,
335
+ default_locale: I18n.default_locale.to_s,
336
+ available_locales: I18n.available_locales.map(&:to_s),
337
+ eager_load: ::Rails.application.config.eager_load,
338
+ consider_all_requests_local: ::Rails.application.config.consider_all_requests_local,
339
+ force_ssl: ::Rails.application.config.force_ssl,
340
+ public_file_server_enabled: ::Rails.application.config.public_file_server.enabled
341
+ }
342
+
343
+ safe_configs
344
+ end
345
+ end
346
+ ```
347
+
348
+ ## 3. Generator Design (FR-003)
349
+
350
+ ### 3.1 Install Generator
351
+
352
+ ```ruby
353
+ # lib/generators/soka/install/install_generator.rb
354
+ module Soka
355
+ module Generators
356
+ class InstallGenerator < ::Rails::Generators::Base
357
+ source_root File.expand_path('templates', __dir__)
358
+
359
+ def create_initializer
360
+ template 'soka.rb', 'config/initializers/soka.rb'
361
+ end
362
+
363
+ def create_application_agent
364
+ template 'application_agent.rb', 'app/soka/agents/application_agent.rb'
365
+ end
366
+
367
+ def create_application_tool
368
+ template 'application_tool.rb', 'app/soka/tools/application_tool.rb'
369
+ end
370
+
371
+ def create_rails_info_tool
372
+ template 'rails_info_tool.rb', 'app/soka/tools/rails_info_tool.rb'
373
+ end
374
+
375
+ def add_soka_directory
376
+ empty_directory 'app/soka'
377
+ empty_directory 'app/soka/agents'
378
+ empty_directory 'app/soka/tools'
379
+ end
380
+
381
+ def display_post_install_message
382
+ say "\nSoka Rails has been successfully installed!", :green
383
+ say "\nNext steps:"
384
+ say " 1. Set your AI provider API key: SOKA_API_KEY=your_key"
385
+ say " 2. Create your first agent: rails generate soka:agent MyAgent"
386
+ say " 3. Create your first tool: rails generate soka:tool MyTool"
387
+ end
388
+ end
389
+ end
390
+ end
391
+ ```
392
+
393
+ ### 3.2 Agent Generator
394
+
395
+ ```ruby
396
+ # lib/generators/soka/agent/agent_generator.rb
397
+ module Soka
398
+ module Generators
399
+ class AgentGenerator < ::Rails::Generators::NamedBase
400
+ source_root File.expand_path('templates', __dir__)
401
+
402
+ argument :tools, type: :array, default: [], banner: "tool1 tool2"
403
+
404
+ def create_agent_file
405
+ @agent_class_name = class_name
406
+ @tools_list = tools
407
+
408
+ template 'agent.rb.tt',
409
+ File.join('app/soka/agents', class_path, "#{file_name}_agent.rb")
410
+ end
411
+
412
+ def create_test_file
413
+ @agent_class_name = class_name
414
+
415
+ template 'agent_spec.rb.tt',
416
+ File.join('spec/soka/agents', class_path, "#{file_name}_agent_spec.rb")
417
+ end
418
+ end
419
+ end
420
+ end
421
+ ```
422
+
423
+ ### 3.3 Tool Generator
424
+
425
+ ```ruby
426
+ # lib/generators/soka/tool/tool_generator.rb
427
+ module Soka
428
+ module Generators
429
+ class ToolGenerator < ::Rails::Generators::NamedBase
430
+ source_root File.expand_path('templates', __dir__)
431
+
432
+ argument :params, type: :array, default: [], banner: 'param1:type param2:type'
433
+
434
+ def create_tool_file
435
+ @tool_class_name = class_name
436
+ @params_list = parse_params
437
+
438
+ template 'tool.rb.tt',
439
+ File.join('app/soka/tools', class_path, "#{file_name}_tool.rb")
440
+ end
441
+
442
+ def create_test_file
443
+ @tool_class_name = class_name
444
+
445
+ template 'tool_spec.rb.tt',
446
+ File.join('spec/soka/tools', class_path, "#{file_name}_tool_spec.rb")
447
+ end
448
+
449
+ private
450
+
451
+ def parse_params
452
+ params.map do |param|
453
+ name, type = param.split(':')
454
+ { name: name, type: type || 'String' }
455
+ end
456
+ end
457
+ end
458
+ end
459
+ end
460
+ ```
461
+
462
+ ## 4. Test Support Design (FR-005)
463
+
464
+ ### 4.1 RSpec Test Helpers
465
+
466
+ ```ruby
467
+ # lib/soka/rails/test_helpers.rb
468
+ module Soka
469
+ module Rails
470
+ module TestHelpers
471
+ extend ActiveSupport::Concern
472
+
473
+ included do
474
+ let(:mock_llm) { instance_double(Soka::LLM) }
475
+ end
476
+
477
+ # Mock AI response
478
+ def mock_ai_response(response_attrs = {})
479
+ default_response = {
480
+ final_answer: "Mocked answer",
481
+ confidence_score: 0.95,
482
+ status: :completed,
483
+ iterations: 1,
484
+ thought_process: []
485
+ }
486
+
487
+ response = default_response.merge(response_attrs)
488
+
489
+ allow(Soka::LLM).to receive(:new).and_return(mock_llm)
490
+ allow(mock_llm).to receive(:chat).and_return(
491
+ OpenStruct.new(content: build_react_response(response))
492
+ )
493
+ end
494
+
495
+ # Mock tool execution
496
+ def mock_tool_execution(tool_class, result)
497
+ allow_any_instance_of(tool_class).to receive(:call).and_return(result)
498
+ end
499
+
500
+ # Build ReAct format response
501
+ def build_react_response(attrs)
502
+ thoughts = attrs[:thought_process].presence ||
503
+ ["Analyzing the request"]
504
+
505
+ response = thoughts.map { |t| "<Thought>#{t}</Thought>" }.join("\n")
506
+ response += "\n<Final_Answer>#{attrs[:final_answer]}</Final_Answer>"
507
+ response
508
+ end
509
+
510
+ # Agent test helper methods
511
+ def run_agent(agent, input, &block)
512
+ result = nil
513
+
514
+ if block_given?
515
+ result = agent.run(input, &block)
516
+ else
517
+ result = agent.run(input)
518
+ end
519
+
520
+ expect(result).to be_a(Struct)
521
+ result
522
+ end
523
+
524
+ # Event collector
525
+ def collect_agent_events(agent, input)
526
+ events = []
527
+
528
+ agent.run(input) do |event|
529
+ events << event
530
+ end
531
+
532
+ events
533
+ end
534
+
535
+ # Test configuration
536
+ def with_test_configuration
537
+ original_config = Soka::Rails.configuration.dup
538
+
539
+ Soka::Rails.configure do |config|
540
+ config.ai_provider = :mock
541
+ config.max_iterations = 3
542
+ config.timeout = 5.seconds
543
+ yield config if block_given?
544
+ end
545
+
546
+ yield
547
+ ensure
548
+ Soka::Rails.configuration = original_config
549
+ end
550
+ end
551
+ end
552
+ end
553
+ ```
554
+
555
+ ### 4.2 RSpec Configuration
556
+
557
+ ```ruby
558
+ # lib/soka/rails/rspec.rb
559
+ require 'soka/rails/test_helpers'
560
+
561
+ RSpec.configure do |config|
562
+ config.include Soka::Rails::TestHelpers, type: :agent
563
+ config.include Soka::Rails::TestHelpers, type: :tool
564
+
565
+ config.before(:each, type: :agent) do
566
+ # Reset Soka configuration
567
+ Soka.configuration = Soka::Configuration.new
568
+ end
569
+
570
+ config.before(:each, type: :tool) do
571
+ # Ensure tools are available
572
+ allow(Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new('test'))
573
+ end
574
+ end
575
+ ```
576
+
577
+ ## 5. Integration Points Design
578
+
579
+ ### 5.1 Controller Integration
580
+
581
+ ```ruby
582
+ # Example: Using in Controller
583
+ class ConversationsController < ApplicationController
584
+ def create
585
+ agent = CustomerSupportAgent.new(
586
+ memory: session_memory,
587
+ context: current_user_context
588
+ )
589
+
590
+ result = agent.run(conversation_params[:message])
591
+
592
+ session[:conversation_memory] = agent.memory.to_a
593
+
594
+ respond_to do |format|
595
+ format.json { render json: build_response(result) }
596
+ format.html { redirect_to conversation_path, notice: result.final_answer }
597
+ end
598
+ rescue Soka::Error => e
599
+ handle_soka_error(e)
600
+ end
601
+
602
+ private
603
+
604
+ def session_memory
605
+ Soka::Memory.new(session[:conversation_memory] || [])
606
+ end
607
+
608
+ def current_user_context
609
+ {
610
+ user_id: current_user.id,
611
+ user_name: current_user.name,
612
+ user_role: current_user.role
613
+ }
614
+ end
615
+
616
+ def build_response(result)
617
+ {
618
+ answer: result.final_answer,
619
+ confidence: result.confidence_score,
620
+ status: result.status,
621
+ metadata: {
622
+ iterations: result.iterations,
623
+ timestamp: Time.current
624
+ }
625
+ }
626
+ end
627
+
628
+ def handle_soka_error(error)
629
+ Rails.logger.error "[Soka Error] #{error.message}"
630
+ render json: { error: "AI processing error occurred" }, status: :internal_server_error
631
+ end
632
+ end
633
+ ```
634
+
635
+ ### 5.2 ActiveJob Integration
636
+
637
+ ```ruby
638
+ # Example: Background job integration
639
+ class ProcessConversationJob < ApplicationJob
640
+ queue_as :default
641
+
642
+ def perform(user_id, message)
643
+ user = User.find(user_id)
644
+ agent = CustomerSupportAgent.new(
645
+ memory: user.conversation_memory,
646
+ context: { user_id: user.id }
647
+ )
648
+
649
+ result = agent.run(message)
650
+
651
+ # Save result
652
+ user.conversations.create!(
653
+ message: message,
654
+ response: result.final_answer,
655
+ confidence: result.confidence_score,
656
+ metadata: {
657
+ iterations: result.iterations,
658
+ thought_process: result.thought_process
659
+ }
660
+ )
661
+
662
+ # Send notification
663
+ UserMailer.conversation_processed(user, result).deliver_later
664
+ end
665
+ end
666
+ ```
667
+
668
+ ### 5.3 ActionCable Integration
669
+
670
+ ```ruby
671
+ # Example: Real-time communication integration
672
+ class ConversationChannel < ApplicationCable::Channel
673
+ def subscribed
674
+ stream_from "conversation_#{params[:conversation_id]}"
675
+ end
676
+
677
+ def receive(data)
678
+ agent = CustomerSupportAgent.new
679
+
680
+ agent.run(data['message']) do |event|
681
+ ActionCable.server.broadcast(
682
+ "conversation_#{params[:conversation_id]}",
683
+ {
684
+ type: event.type,
685
+ content: event.content,
686
+ timestamp: Time.current
687
+ }
688
+ )
689
+ end
690
+ end
691
+ end
692
+ ```
693
+
694
+ ## 6. Performance Optimization Design
695
+
696
+ ### 6.1 Connection Pool Design
697
+
698
+ ```ruby
699
+ module Soka
700
+ module Rails
701
+ class ConnectionPool
702
+ include Singleton
703
+
704
+ def initialize
705
+ @pools = {}
706
+ @mutex = Mutex.new
707
+ end
708
+
709
+ def with_connection(provider, &block)
710
+ pool = get_pool(provider)
711
+ pool.with(&block)
712
+ end
713
+
714
+ private
715
+
716
+ def get_pool(provider)
717
+ @mutex.synchronize do
718
+ @pools[provider] ||= ConnectionPool.new(
719
+ size: pool_size,
720
+ timeout: pool_timeout
721
+ ) do
722
+ create_connection(provider)
723
+ end
724
+ end
725
+ end
726
+
727
+ def pool_size
728
+ ::Rails.env.production? ? 10 : 2
729
+ end
730
+
731
+ def pool_timeout
732
+ 5.seconds
733
+ end
734
+
735
+ def create_connection(provider)
736
+ Soka::LLM.new(provider: provider)
737
+ end
738
+ end
739
+ end
740
+ end
741
+ ```
742
+
743
+ ## 7. Error Handling Design
744
+
745
+ ### 7.1 Error Class Hierarchy
746
+
747
+ ```ruby
748
+ module Soka
749
+ module Rails
750
+ class Error < StandardError; end
751
+
752
+ class ConfigurationError < Error; end
753
+ class AgentError < Error; end
754
+ class ToolError < Error; end
755
+ class GeneratorError < Error; end
756
+
757
+ # Specific errors
758
+ class MissingApiKeyError < ConfigurationError
759
+ def initialize(provider)
760
+ super("Missing API key for provider: #{provider}")
761
+ end
762
+ end
763
+
764
+ class InvalidProviderError < ConfigurationError
765
+ def initialize(provider)
766
+ super("Invalid AI provider: #{provider}")
767
+ end
768
+ end
769
+
770
+ class AgentTimeoutError < AgentError
771
+ def initialize(timeout)
772
+ super("Agent execution timeout after #{timeout} seconds")
773
+ end
774
+ end
775
+ end
776
+ end
777
+ ```
778
+
779
+ ## 8. Security Design (NFR-003)
780
+
781
+ ### 8.1 API Key Management
782
+
783
+ ```ruby
784
+ module Soka
785
+ module Rails
786
+ module Security
787
+ class ApiKeyManager
788
+ def self.fetch_api_key(provider)
789
+ key = ENV.fetch("SOKA_#{provider.upcase}_API_KEY") do
790
+ ENV.fetch('SOKA_API_KEY', nil)
791
+ end
792
+
793
+ raise MissingApiKeyError.new(provider) if key.blank?
794
+
795
+ key
796
+ end
797
+
798
+ def self.validate_api_key(key)
799
+ return false if key.blank?
800
+ return false if key.length < 20
801
+ return false if key.include?(' ')
802
+
803
+ true
804
+ end
805
+ end
806
+ end
807
+ end
808
+ end
809
+ ```
810
+
811
+ ### 8.2 Parameter Filtering
812
+
813
+ ```ruby
814
+ module Soka
815
+ module Rails
816
+ module Security
817
+ module ParameterFiltering
818
+ extend ActiveSupport::Concern
819
+
820
+ FILTERED_PARAMS = %w[
821
+ api_key
822
+ password
823
+ secret
824
+ token
825
+ auth
826
+ ].freeze
827
+
828
+ def filter_sensitive_params(params)
829
+ filtered = params.deep_dup
830
+
831
+ FILTERED_PARAMS.each do |param|
832
+ filtered.deep_transform_keys! do |key|
833
+ if key.to_s.downcase.include?(param)
834
+ filtered[key] = '[FILTERED]'
835
+ end
836
+ end
837
+ end
838
+
839
+ filtered
840
+ end
841
+ end
842
+ end
843
+ end
844
+ end
845
+ ```
846
+
847
+ ## 9. Deployment Considerations
848
+
849
+ ### 9.1 Gem Structure
850
+
851
+ ```
852
+ soka-rails/
853
+ ├── lib/
854
+ │ ├── soka-rails.rb
855
+ │ ├── soka/
856
+ │ │ └── rails/
857
+ │ │ ├── version.rb
858
+ │ │ ├── railtie.rb
859
+ │ │ ├── configuration.rb
860
+ │ │ ├── test_helpers.rb
861
+ │ │ └── rspec.rb
862
+ │ └── generators/
863
+ │ └── soka/
864
+ │ ├── install/
865
+ │ ├── agent/
866
+ │ └── tool/
867
+ ├── app/
868
+ │ └── soka/
869
+ │ ├── agents/
870
+ │ │ └── application_agent.rb
871
+ │ └── tools/
872
+ │ ├── application_tool.rb
873
+ │ └── rails_info_tool.rb
874
+ ├── spec/
875
+ ├── Gemfile
876
+ ├── soka-rails.gemspec
877
+ ├── README.md
878
+ └── LICENSE
879
+ ```
880
+
881
+ ### 9.2 Gemspec Configuration
882
+
883
+ ```ruby
884
+ # frozen_string_literal: true
885
+
886
+ require_relative 'lib/soka/rails/version'
887
+
888
+ Gem::Specification.new do |spec|
889
+ spec.name = 'soka-rails'
890
+ spec.version = Soka::Rails::VERSION
891
+ spec.authors = ['jiunjiun']
892
+ spec.email = ['imjiunjiun@gmail.com']
893
+
894
+ spec.summary = 'Rails integration for Soka AI Agent Framework'
895
+ spec.description = 'Soka Rails provides seamless integration between the Soka AI Agent Framework ' \
896
+ 'and Ruby on Rails applications, following Rails conventions for easy adoption.'
897
+ spec.homepage = 'https://github.com/jiunjiun/soka-rails'
898
+ spec.license = 'MIT'
899
+ spec.required_ruby_version = '>= 3.4'
900
+
901
+ spec.metadata['homepage_uri'] = spec.homepage
902
+ spec.metadata['source_code_uri'] = 'https://github.com/jiunjiun/soka-rails'
903
+ spec.metadata['changelog_uri'] = 'https://github.com/jiunjiun/soka-rails/blob/main/CHANGELOG.md'
904
+ spec.metadata['rubygems_mfa_required'] = 'true'
905
+
906
+ # Specify which files should be added to the gem when it is released.
907
+ gemspec = File.basename(__FILE__)
908
+ spec.files = IO.popen(%w[git ls-files -z], chdir: __dir__, err: IO::NULL) do |ls|
909
+ ls.readlines("\x0", chomp: true).reject do |f|
910
+ (f == gemspec) ||
911
+ f.start_with?(*%w[bin/ test/ spec/ features/ .git .github/ appveyor Gemfile])
912
+ end
913
+ end
914
+
915
+ spec.require_paths = ['lib']
916
+
917
+ # Runtime dependencies
918
+ spec.add_dependency 'rails', '>= 7.0', '< 9.0'
919
+ spec.add_dependency 'soka', '~> 0.0.1'
920
+ spec.add_dependency 'zeitwerk', '~> 2.6'
921
+
922
+ # Development dependencies
923
+ spec.add_development_dependency 'rspec-rails', '~> 6.1'
924
+ spec.add_development_dependency 'rubocop', '~> 1.60'
925
+ spec.add_development_dependency 'rubocop-rails', '~> 2.23'
926
+ spec.add_development_dependency 'rubocop-rspec', '~> 2.25'
927
+ spec.add_development_dependency 'pry-rails', '~> 0.3'
928
+ spec.add_development_dependency 'yard', '~> 0.9'
929
+ end
930
+ ```
931
+
932
+ ## 10. Implementation Priority
933
+
934
+ ### Phase 1: Core Foundation (Week 1-2)
935
+ 1. Railtie implementation
936
+ 2. Configuration system
937
+ 3. Base Agent/Tool classes
938
+
939
+ ### Phase 2: Generator (Week 3-4)
940
+ 1. Install Generator
941
+ 2. Agent Generator
942
+ 3. Tool Generator
943
+
944
+ ### Phase 3: Test Support (Week 5-6)
945
+ 1. RSpec Test Helpers
946
+ 2. Mock system
947
+ 3. Test examples
948
+
949
+ ### Phase 4: Advanced Features (Week 7-8)
950
+ 1. Connection pool optimization
951
+ 2. Performance optimization
952
+ 3. Error handling improvements
953
+
954
+ ### Phase 5: Documentation & Release (Week 9-10)
955
+ 1. API documentation
956
+ 2. Usage guide
957
+ 3. Gem release preparation