ruby_llm_swarm 1.9.1

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 (154) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +175 -0
  4. data/lib/generators/ruby_llm/chat_ui/chat_ui_generator.rb +187 -0
  5. data/lib/generators/ruby_llm/chat_ui/templates/controllers/chats_controller.rb.tt +39 -0
  6. data/lib/generators/ruby_llm/chat_ui/templates/controllers/messages_controller.rb.tt +24 -0
  7. data/lib/generators/ruby_llm/chat_ui/templates/controllers/models_controller.rb.tt +14 -0
  8. data/lib/generators/ruby_llm/chat_ui/templates/jobs/chat_response_job.rb.tt +12 -0
  9. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/_chat.html.erb.tt +16 -0
  10. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/_form.html.erb.tt +29 -0
  11. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/index.html.erb.tt +16 -0
  12. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/new.html.erb.tt +11 -0
  13. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/show.html.erb.tt +23 -0
  14. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_content.html.erb.tt +1 -0
  15. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_form.html.erb.tt +21 -0
  16. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_message.html.erb.tt +13 -0
  17. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_tool_calls.html.erb.tt +7 -0
  18. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/create.turbo_stream.erb.tt +9 -0
  19. data/lib/generators/ruby_llm/chat_ui/templates/views/models/_model.html.erb.tt +16 -0
  20. data/lib/generators/ruby_llm/chat_ui/templates/views/models/index.html.erb.tt +28 -0
  21. data/lib/generators/ruby_llm/chat_ui/templates/views/models/show.html.erb.tt +18 -0
  22. data/lib/generators/ruby_llm/generator_helpers.rb +194 -0
  23. data/lib/generators/ruby_llm/install/install_generator.rb +106 -0
  24. data/lib/generators/ruby_llm/install/templates/add_references_to_chats_tool_calls_and_messages_migration.rb.tt +9 -0
  25. data/lib/generators/ruby_llm/install/templates/chat_model.rb.tt +3 -0
  26. data/lib/generators/ruby_llm/install/templates/create_chats_migration.rb.tt +7 -0
  27. data/lib/generators/ruby_llm/install/templates/create_messages_migration.rb.tt +16 -0
  28. data/lib/generators/ruby_llm/install/templates/create_models_migration.rb.tt +45 -0
  29. data/lib/generators/ruby_llm/install/templates/create_tool_calls_migration.rb.tt +20 -0
  30. data/lib/generators/ruby_llm/install/templates/initializer.rb.tt +12 -0
  31. data/lib/generators/ruby_llm/install/templates/message_model.rb.tt +4 -0
  32. data/lib/generators/ruby_llm/install/templates/model_model.rb.tt +3 -0
  33. data/lib/generators/ruby_llm/install/templates/tool_call_model.rb.tt +3 -0
  34. data/lib/generators/ruby_llm/upgrade_to_v1_7/templates/migration.rb.tt +145 -0
  35. data/lib/generators/ruby_llm/upgrade_to_v1_7/upgrade_to_v1_7_generator.rb +124 -0
  36. data/lib/generators/ruby_llm/upgrade_to_v1_9/templates/add_v1_9_message_columns.rb.tt +15 -0
  37. data/lib/generators/ruby_llm/upgrade_to_v1_9/upgrade_to_v1_9_generator.rb +49 -0
  38. data/lib/ruby_llm/active_record/acts_as.rb +174 -0
  39. data/lib/ruby_llm/active_record/acts_as_legacy.rb +384 -0
  40. data/lib/ruby_llm/active_record/chat_methods.rb +350 -0
  41. data/lib/ruby_llm/active_record/message_methods.rb +81 -0
  42. data/lib/ruby_llm/active_record/model_methods.rb +84 -0
  43. data/lib/ruby_llm/aliases.json +295 -0
  44. data/lib/ruby_llm/aliases.rb +38 -0
  45. data/lib/ruby_llm/attachment.rb +220 -0
  46. data/lib/ruby_llm/chat.rb +816 -0
  47. data/lib/ruby_llm/chunk.rb +6 -0
  48. data/lib/ruby_llm/configuration.rb +78 -0
  49. data/lib/ruby_llm/connection.rb +126 -0
  50. data/lib/ruby_llm/content.rb +73 -0
  51. data/lib/ruby_llm/context.rb +29 -0
  52. data/lib/ruby_llm/embedding.rb +29 -0
  53. data/lib/ruby_llm/error.rb +84 -0
  54. data/lib/ruby_llm/image.rb +49 -0
  55. data/lib/ruby_llm/message.rb +86 -0
  56. data/lib/ruby_llm/mime_type.rb +71 -0
  57. data/lib/ruby_llm/model/info.rb +111 -0
  58. data/lib/ruby_llm/model/modalities.rb +22 -0
  59. data/lib/ruby_llm/model/pricing.rb +48 -0
  60. data/lib/ruby_llm/model/pricing_category.rb +46 -0
  61. data/lib/ruby_llm/model/pricing_tier.rb +33 -0
  62. data/lib/ruby_llm/model.rb +7 -0
  63. data/lib/ruby_llm/models.json +33198 -0
  64. data/lib/ruby_llm/models.rb +231 -0
  65. data/lib/ruby_llm/models_schema.json +168 -0
  66. data/lib/ruby_llm/moderation.rb +56 -0
  67. data/lib/ruby_llm/provider.rb +243 -0
  68. data/lib/ruby_llm/providers/anthropic/capabilities.rb +134 -0
  69. data/lib/ruby_llm/providers/anthropic/chat.rb +125 -0
  70. data/lib/ruby_llm/providers/anthropic/content.rb +44 -0
  71. data/lib/ruby_llm/providers/anthropic/embeddings.rb +20 -0
  72. data/lib/ruby_llm/providers/anthropic/media.rb +92 -0
  73. data/lib/ruby_llm/providers/anthropic/models.rb +63 -0
  74. data/lib/ruby_llm/providers/anthropic/streaming.rb +45 -0
  75. data/lib/ruby_llm/providers/anthropic/tools.rb +109 -0
  76. data/lib/ruby_llm/providers/anthropic.rb +36 -0
  77. data/lib/ruby_llm/providers/bedrock/capabilities.rb +167 -0
  78. data/lib/ruby_llm/providers/bedrock/chat.rb +63 -0
  79. data/lib/ruby_llm/providers/bedrock/media.rb +61 -0
  80. data/lib/ruby_llm/providers/bedrock/models.rb +98 -0
  81. data/lib/ruby_llm/providers/bedrock/signing.rb +831 -0
  82. data/lib/ruby_llm/providers/bedrock/streaming/base.rb +51 -0
  83. data/lib/ruby_llm/providers/bedrock/streaming/content_extraction.rb +71 -0
  84. data/lib/ruby_llm/providers/bedrock/streaming/message_processing.rb +67 -0
  85. data/lib/ruby_llm/providers/bedrock/streaming/payload_processing.rb +80 -0
  86. data/lib/ruby_llm/providers/bedrock/streaming/prelude_handling.rb +78 -0
  87. data/lib/ruby_llm/providers/bedrock/streaming.rb +18 -0
  88. data/lib/ruby_llm/providers/bedrock.rb +82 -0
  89. data/lib/ruby_llm/providers/deepseek/capabilities.rb +130 -0
  90. data/lib/ruby_llm/providers/deepseek/chat.rb +16 -0
  91. data/lib/ruby_llm/providers/deepseek.rb +30 -0
  92. data/lib/ruby_llm/providers/gemini/capabilities.rb +281 -0
  93. data/lib/ruby_llm/providers/gemini/chat.rb +454 -0
  94. data/lib/ruby_llm/providers/gemini/embeddings.rb +37 -0
  95. data/lib/ruby_llm/providers/gemini/images.rb +47 -0
  96. data/lib/ruby_llm/providers/gemini/media.rb +112 -0
  97. data/lib/ruby_llm/providers/gemini/models.rb +40 -0
  98. data/lib/ruby_llm/providers/gemini/streaming.rb +61 -0
  99. data/lib/ruby_llm/providers/gemini/tools.rb +198 -0
  100. data/lib/ruby_llm/providers/gemini/transcription.rb +116 -0
  101. data/lib/ruby_llm/providers/gemini.rb +37 -0
  102. data/lib/ruby_llm/providers/gpustack/chat.rb +27 -0
  103. data/lib/ruby_llm/providers/gpustack/media.rb +46 -0
  104. data/lib/ruby_llm/providers/gpustack/models.rb +90 -0
  105. data/lib/ruby_llm/providers/gpustack.rb +34 -0
  106. data/lib/ruby_llm/providers/mistral/capabilities.rb +155 -0
  107. data/lib/ruby_llm/providers/mistral/chat.rb +24 -0
  108. data/lib/ruby_llm/providers/mistral/embeddings.rb +33 -0
  109. data/lib/ruby_llm/providers/mistral/models.rb +48 -0
  110. data/lib/ruby_llm/providers/mistral.rb +32 -0
  111. data/lib/ruby_llm/providers/ollama/chat.rb +27 -0
  112. data/lib/ruby_llm/providers/ollama/media.rb +46 -0
  113. data/lib/ruby_llm/providers/ollama/models.rb +36 -0
  114. data/lib/ruby_llm/providers/ollama.rb +30 -0
  115. data/lib/ruby_llm/providers/openai/capabilities.rb +299 -0
  116. data/lib/ruby_llm/providers/openai/chat.rb +88 -0
  117. data/lib/ruby_llm/providers/openai/embeddings.rb +33 -0
  118. data/lib/ruby_llm/providers/openai/images.rb +38 -0
  119. data/lib/ruby_llm/providers/openai/media.rb +81 -0
  120. data/lib/ruby_llm/providers/openai/models.rb +39 -0
  121. data/lib/ruby_llm/providers/openai/moderation.rb +34 -0
  122. data/lib/ruby_llm/providers/openai/streaming.rb +46 -0
  123. data/lib/ruby_llm/providers/openai/tools.rb +98 -0
  124. data/lib/ruby_llm/providers/openai/transcription.rb +70 -0
  125. data/lib/ruby_llm/providers/openai.rb +44 -0
  126. data/lib/ruby_llm/providers/openai_responses.rb +395 -0
  127. data/lib/ruby_llm/providers/openrouter/models.rb +73 -0
  128. data/lib/ruby_llm/providers/openrouter.rb +26 -0
  129. data/lib/ruby_llm/providers/perplexity/capabilities.rb +137 -0
  130. data/lib/ruby_llm/providers/perplexity/chat.rb +16 -0
  131. data/lib/ruby_llm/providers/perplexity/models.rb +42 -0
  132. data/lib/ruby_llm/providers/perplexity.rb +48 -0
  133. data/lib/ruby_llm/providers/vertexai/chat.rb +14 -0
  134. data/lib/ruby_llm/providers/vertexai/embeddings.rb +32 -0
  135. data/lib/ruby_llm/providers/vertexai/models.rb +130 -0
  136. data/lib/ruby_llm/providers/vertexai/streaming.rb +14 -0
  137. data/lib/ruby_llm/providers/vertexai/transcription.rb +16 -0
  138. data/lib/ruby_llm/providers/vertexai.rb +55 -0
  139. data/lib/ruby_llm/railtie.rb +35 -0
  140. data/lib/ruby_llm/responses_session.rb +77 -0
  141. data/lib/ruby_llm/stream_accumulator.rb +101 -0
  142. data/lib/ruby_llm/streaming.rb +153 -0
  143. data/lib/ruby_llm/tool.rb +209 -0
  144. data/lib/ruby_llm/tool_call.rb +22 -0
  145. data/lib/ruby_llm/tool_executors.rb +125 -0
  146. data/lib/ruby_llm/transcription.rb +35 -0
  147. data/lib/ruby_llm/utils.rb +91 -0
  148. data/lib/ruby_llm/version.rb +5 -0
  149. data/lib/ruby_llm.rb +140 -0
  150. data/lib/tasks/models.rake +525 -0
  151. data/lib/tasks/release.rake +67 -0
  152. data/lib/tasks/ruby_llm.rake +15 -0
  153. data/lib/tasks/vcr.rake +92 -0
  154. metadata +346 -0
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators'
4
+ require 'rails/generators/active_record'
5
+ require_relative '../generator_helpers'
6
+
7
+ module RubyLLM
8
+ module Generators
9
+ # Generator to upgrade existing RubyLLM apps to v1.7 with new Rails-like API
10
+ class UpgradeToV17Generator < Rails::Generators::Base
11
+ include Rails::Generators::Migration
12
+ include RubyLLM::Generators::GeneratorHelpers
13
+
14
+ namespace 'ruby_llm:upgrade_to_v1_7'
15
+ source_root File.expand_path('templates', __dir__)
16
+
17
+ # Override source_paths to include install templates
18
+ def self.source_paths
19
+ [
20
+ File.expand_path('templates', __dir__),
21
+ File.expand_path('../install/templates', __dir__)
22
+ ]
23
+ end
24
+
25
+ argument :model_mappings, type: :array, default: [], banner: 'chat:ChatName message:MessageName ...'
26
+
27
+ desc 'Upgrades existing RubyLLM apps to v1.7 with new Rails-like API\n' \
28
+ 'Usage: rails g ruby_llm:upgrade_to_v1_7 [chat:ChatName] [message:MessageName] ...'
29
+
30
+ def self.next_migration_number(dirname)
31
+ ::ActiveRecord::Generators::Base.next_migration_number(dirname)
32
+ end
33
+
34
+ def create_migration_file
35
+ @model_table_already_existed = table_exists?(table_name_for(model_model_name))
36
+
37
+ # First check if models table exists, if not create it
38
+ unless @model_table_already_existed
39
+ migration_template 'create_models_migration.rb.tt',
40
+ "db/migrate/create_#{table_name_for(model_model_name)}.rb",
41
+ migration_version: migration_version,
42
+ model_model_name: model_model_name
43
+
44
+ sleep 1 # Ensure different timestamp
45
+ end
46
+
47
+ migration_template 'migration.rb.tt',
48
+ 'db/migrate/migrate_to_ruby_llm_model_references.rb',
49
+ migration_version: migration_version,
50
+ chat_model_name: chat_model_name,
51
+ message_model_name: message_model_name,
52
+ tool_call_model_name: tool_call_model_name,
53
+ model_model_name: model_model_name,
54
+ model_table_already_existed: @model_table_already_existed
55
+ end
56
+
57
+ def create_model_file
58
+ create_namespace_modules
59
+
60
+ template 'model_model.rb.tt', "app/models/#{model_model_name.underscore}.rb"
61
+ end
62
+
63
+ def update_existing_models
64
+ update_model_acts_as(chat_model_name, 'acts_as_chat', acts_as_chat_declaration)
65
+ update_model_acts_as(message_model_name, 'acts_as_message', acts_as_message_declaration)
66
+ update_model_acts_as(tool_call_model_name, 'acts_as_tool_call', acts_as_tool_call_declaration)
67
+ end
68
+
69
+ def update_initializer
70
+ initializer_path = 'config/initializers/ruby_llm.rb'
71
+
72
+ unless File.exist?(initializer_path)
73
+ say_status :warning, 'No initializer found. Creating one...', :yellow
74
+ template 'initializer.rb.tt', initializer_path
75
+ return
76
+ end
77
+
78
+ initializer_content = File.read(initializer_path)
79
+
80
+ return if initializer_content.include?('config.use_new_acts_as')
81
+
82
+ inject_into_file initializer_path, before: /^end/ do
83
+ lines = ["\n # Enable the new Rails-like API", ' config.use_new_acts_as = true']
84
+ lines << " config.model_registry_class = \"#{model_model_name}\"" if model_model_name != 'Model'
85
+ lines << "\n"
86
+ lines.join("\n")
87
+ end
88
+ end
89
+
90
+ def show_next_steps
91
+ say_status :success, 'Upgrade prepared!', :green
92
+ say <<~INSTRUCTIONS
93
+
94
+ Next steps:
95
+ 1. Review the generated migrations
96
+ 2. Run: rails db:migrate
97
+ 3. Update your code to use the new API: #{chat_model_name}.create! now has the same signature as RubyLLM.chat
98
+
99
+ ⚠️ If you get "undefined method 'acts_as_model'" during migration:
100
+ Add this to config/application.rb BEFORE your Application class:
101
+
102
+ RubyLLM.configure do |config|
103
+ config.use_new_acts_as = true
104
+ end
105
+
106
+ 📚 See the full migration guide: https://rubyllm.com/upgrading-to-1-7/
107
+
108
+ INSTRUCTIONS
109
+ end
110
+
111
+ private
112
+
113
+ def update_model_acts_as(model_name, old_acts_as, new_acts_as)
114
+ model_path = "app/models/#{model_name.underscore}.rb"
115
+ return unless File.exist?(Rails.root.join(model_path))
116
+
117
+ content = File.read(Rails.root.join(model_path))
118
+ return unless content.match?(/^\s*#{old_acts_as}/)
119
+
120
+ gsub_file model_path, /^\s*#{old_acts_as}.*$/, " #{new_acts_as}"
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,15 @@
1
+ class AddRubyLlmV19Columns < ActiveRecord::Migration<%= migration_version %>
2
+ def change
3
+ unless column_exists?(:<%= message_table_name %>, :cached_tokens)
4
+ add_column :<%= message_table_name %>, :cached_tokens, :integer
5
+ end
6
+
7
+ unless column_exists?(:<%= message_table_name %>, :cache_creation_tokens)
8
+ add_column :<%= message_table_name %>, :cache_creation_tokens, :integer
9
+ end
10
+
11
+ unless column_exists?(:<%= message_table_name %>, :content_raw)
12
+ add_column :<%= message_table_name %>, :content_raw, :json
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators'
4
+ require 'rails/generators/active_record'
5
+ require_relative '../generator_helpers'
6
+
7
+ module RubyLLM
8
+ module Generators
9
+ # Generator to add v1.9 columns (cached tokens + raw content support) to existing apps.
10
+ class UpgradeToV19Generator < Rails::Generators::Base
11
+ include Rails::Generators::Migration
12
+ include RubyLLM::Generators::GeneratorHelpers
13
+
14
+ namespace 'ruby_llm:upgrade_to_v1_9'
15
+ source_root File.expand_path('templates', __dir__)
16
+
17
+ argument :model_mappings, type: :array, default: [], banner: 'message:MessageName'
18
+
19
+ desc 'Adds cached token columns and raw content storage fields introduced in v1.9.0'
20
+
21
+ def self.next_migration_number(dirname)
22
+ ::ActiveRecord::Generators::Base.next_migration_number(dirname)
23
+ end
24
+
25
+ def create_migration_file
26
+ parse_model_mappings
27
+
28
+ migration_template 'add_v1_9_message_columns.rb.tt',
29
+ 'db/migrate/add_ruby_llm_v1_9_columns.rb',
30
+ migration_version: migration_version,
31
+ message_table_name: message_table_name
32
+ end
33
+
34
+ def show_next_steps
35
+ say_status :success, 'Upgrade prepared!', :green
36
+ say <<~INSTRUCTIONS
37
+
38
+ Next steps:
39
+ 1. Review the generated migration
40
+ 2. Run: rails db:migrate
41
+ 3. Restart your application server
42
+
43
+ 📚 See the v1.9.0 release notes for details on cached token tracking and raw content support.
44
+
45
+ INSTRUCTIONS
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,174 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyLLM
4
+ module ActiveRecord
5
+ # Adds chat and message persistence capabilities to ActiveRecord models.
6
+ module ActsAs
7
+ extend ActiveSupport::Concern
8
+
9
+ # When ActsAs is included, ensure models are loaded from database
10
+ def self.included(base)
11
+ super
12
+ # Monkey-patch Models to use database when ActsAs is active
13
+ RubyLLM::Models.class_eval do
14
+ def self.load_models
15
+ read_from_database
16
+ rescue StandardError => e
17
+ RubyLLM.logger.debug "Failed to load models from database: #{e.message}, falling back to JSON"
18
+ read_from_json
19
+ end
20
+
21
+ def self.read_from_database
22
+ model_class = RubyLLM.config.model_registry_class
23
+ model_class = model_class.constantize if model_class.is_a?(String)
24
+ model_class.all.map(&:to_llm)
25
+ end
26
+
27
+ def load_from_database!
28
+ @models = self.class.read_from_database
29
+ end
30
+ end
31
+ end
32
+
33
+ class_methods do # rubocop:disable Metrics/BlockLength
34
+ def acts_as_chat(messages: :messages, message_class: nil, messages_foreign_key: nil, # rubocop:disable Metrics/ParameterLists
35
+ model: :model, model_class: nil, model_foreign_key: nil)
36
+ include RubyLLM::ActiveRecord::ChatMethods
37
+
38
+ class_attribute :messages_association_name, :model_association_name, :message_class, :model_class
39
+
40
+ self.messages_association_name = messages
41
+ self.model_association_name = model
42
+ self.message_class = (message_class || messages.to_s.classify).to_s
43
+ self.model_class = (model_class || model.to_s.classify).to_s
44
+
45
+ has_many messages,
46
+ -> { order(created_at: :asc) },
47
+ class_name: self.message_class,
48
+ foreign_key: messages_foreign_key,
49
+ dependent: :destroy
50
+
51
+ belongs_to model,
52
+ class_name: self.model_class,
53
+ foreign_key: model_foreign_key,
54
+ optional: true
55
+
56
+ delegate :add_message, to: :to_llm
57
+
58
+ define_method :messages_association do
59
+ send(messages_association_name)
60
+ end
61
+
62
+ define_method :model_association do
63
+ send(model_association_name)
64
+ end
65
+
66
+ define_method :'model_association=' do |value|
67
+ send("#{model_association_name}=", value)
68
+ end
69
+ end
70
+
71
+ def acts_as_model(chats: :chats, chat_class: nil, chats_foreign_key: nil)
72
+ include RubyLLM::ActiveRecord::ModelMethods
73
+
74
+ class_attribute :chats_association_name, :chat_class
75
+
76
+ self.chats_association_name = chats
77
+ self.chat_class = (chat_class || chats.to_s.classify).to_s
78
+
79
+ validates :model_id, presence: true, uniqueness: { scope: :provider }
80
+ validates :provider, presence: true
81
+ validates :name, presence: true
82
+
83
+ has_many chats, class_name: self.chat_class, foreign_key: chats_foreign_key
84
+
85
+ define_method :chats_association do
86
+ send(chats_association_name)
87
+ end
88
+ end
89
+
90
+ def acts_as_message(chat: :chat, chat_class: nil, chat_foreign_key: nil, touch_chat: false, # rubocop:disable Metrics/ParameterLists
91
+ tool_calls: :tool_calls, tool_call_class: nil, tool_calls_foreign_key: nil,
92
+ model: :model, model_class: nil, model_foreign_key: nil)
93
+ include RubyLLM::ActiveRecord::MessageMethods
94
+
95
+ class_attribute :chat_association_name, :tool_calls_association_name, :model_association_name,
96
+ :chat_class, :tool_call_class, :model_class
97
+
98
+ self.chat_association_name = chat
99
+ self.tool_calls_association_name = tool_calls
100
+ self.model_association_name = model
101
+ self.chat_class = (chat_class || chat.to_s.classify).to_s
102
+ self.tool_call_class = (tool_call_class || tool_calls.to_s.classify).to_s
103
+ self.model_class = (model_class || model.to_s.classify).to_s
104
+
105
+ belongs_to chat,
106
+ class_name: self.chat_class,
107
+ foreign_key: chat_foreign_key,
108
+ touch: touch_chat
109
+
110
+ has_many tool_calls,
111
+ class_name: self.tool_call_class,
112
+ foreign_key: tool_calls_foreign_key,
113
+ dependent: :destroy
114
+
115
+ belongs_to :parent_tool_call,
116
+ class_name: self.tool_call_class,
117
+ foreign_key: ActiveSupport::Inflector.foreign_key(tool_calls.to_s.singularize),
118
+ optional: true
119
+
120
+ has_many :tool_results,
121
+ through: tool_calls,
122
+ source: :result,
123
+ class_name: name
124
+
125
+ belongs_to model,
126
+ class_name: self.model_class,
127
+ foreign_key: model_foreign_key,
128
+ optional: true
129
+
130
+ delegate :tool_call?, :tool_result?, to: :to_llm
131
+
132
+ define_method :chat_association do
133
+ send(chat_association_name)
134
+ end
135
+
136
+ define_method :tool_calls_association do
137
+ send(tool_calls_association_name)
138
+ end
139
+
140
+ define_method :model_association do
141
+ send(model_association_name)
142
+ end
143
+ end
144
+
145
+ def acts_as_tool_call(message: :message, message_class: nil, message_foreign_key: nil, # rubocop:disable Metrics/ParameterLists
146
+ result: :result, result_class: nil, result_foreign_key: nil)
147
+ class_attribute :message_association_name, :result_association_name, :message_class, :result_class
148
+
149
+ self.message_association_name = message
150
+ self.result_association_name = result
151
+ self.message_class = (message_class || message.to_s.classify).to_s
152
+ self.result_class = (result_class || self.message_class).to_s
153
+
154
+ belongs_to message,
155
+ class_name: self.message_class,
156
+ foreign_key: message_foreign_key
157
+
158
+ has_one result,
159
+ class_name: self.result_class,
160
+ foreign_key: result_foreign_key,
161
+ dependent: :nullify
162
+
163
+ define_method :message_association do
164
+ send(message_association_name)
165
+ end
166
+
167
+ define_method :result_association do
168
+ send(result_association_name)
169
+ end
170
+ end
171
+ end
172
+ end
173
+ end
174
+ end