ruby_llm 0.1.0.pre17 → 0.1.0.pre19

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: 899bee99690875afeb0d8bb83b654cf05ccaa4db7006971eb122e7188e91f6b7
4
- data.tar.gz: a050cc712573b90bc2d2d1012f40c1866be940e001b6d2aaff6089834bf89584
3
+ metadata.gz: 230db2439975e0515e1f128a286222c06de3de1b7eff0b77631bc9e4fa2df338
4
+ data.tar.gz: 25846959e0c3d2c411d82825077b4ff112bef0aea7910e87f5b3336fd58f8dca
5
5
  SHA512:
6
- metadata.gz: f18a0c1094c407d7690151825f1c0cc557e2621fda75569c052ab9d396a46b7201e8aa3ba643a42da3d1b52e2a8a95216d315f66632cd6cc4de1fee0a372cfb1
7
- data.tar.gz: 312856c0e943e93933e33e492d1e599732b6714a527cf83ac3d5b271d50ecd004064ae7aec5b5671e5dca2c83dba3e458794f0a87c0c12f19c27357a89b9ad61
6
+ metadata.gz: b082f5426e1c0d4ef426df99c5cca9463943b525e113b11aa9d60b799ebe6b0ae77fcc347a06d922b98316b402f8eb9a6a716ddd5e45c26ef86458eb4405d17c
7
+ data.tar.gz: c8a8a42ef3e0e8eb676fae0a28a26103c0dd1ff66be9753bb13d448f3114fb05d862fe78eee5f7599b6e2edd28a8d78c4e1fcd64ca87e163cecf1725f73351ff
data/README.md CHANGED
@@ -82,7 +82,7 @@ Need vector embeddings for your text? RubyLLM makes it simple:
82
82
 
83
83
  ```ruby
84
84
  # Get embeddings with the default model
85
- vector = RubyLLM.embed(text: "Hello, world!")
85
+ vector = RubyLLM.embed "Hello, world!"
86
86
 
87
87
  # Use a specific model
88
88
  vector = RubyLLM.embed(
@@ -191,18 +191,32 @@ end
191
191
  class CreateMessages < ActiveRecord::Migration[8.0]
192
192
  def change
193
193
  create_table :messages do |t|
194
- t.references :chat
194
+ t.references :chat, null: false
195
195
  t.string :role
196
196
  t.text :content
197
- t.json :tool_calls, default: {}
198
- t.string :tool_call_id
197
+ t.string :model_id
199
198
  t.integer :input_tokens
200
199
  t.integer :output_tokens
201
- t.string :model_id
200
+ t.references :tool_call
202
201
  t.timestamps
203
202
  end
204
203
  end
205
204
  end
205
+
206
+ # db/migrate/YYYYMMDDHHMMSS_create_tool_calls.rb
207
+ class CreateToolCalls < ActiveRecord::Migration[8.0]
208
+ def change
209
+ create_table :tool_calls do |t|
210
+ t.references :message, null: false
211
+ t.string :tool_call_id, null: false
212
+ t.string :name, null: false
213
+ t.jsonb :arguments, default: {}
214
+ t.timestamps
215
+ end
216
+
217
+ add_index :tool_calls, :tool_call_id
218
+ end
219
+ end
206
220
  ```
207
221
 
208
222
  Then in your models:
@@ -218,6 +232,10 @@ end
218
232
  class Message < ApplicationRecord
219
233
  acts_as_message
220
234
  end
235
+
236
+ class ToolCall < ApplicationRecord
237
+ acts_as_tool_call
238
+ end
221
239
  ```
222
240
 
223
241
  That's it! Now you can use chats straight from your models:
@@ -8,13 +8,16 @@ module RubyLLM
8
8
  module ActsAs
9
9
  extend ActiveSupport::Concern
10
10
 
11
- class_methods do
12
- def acts_as_chat(message_class: 'Message') # rubocop:disable Metrics/MethodLength
11
+ class_methods do # rubocop:disable Metrics/BlockLength
12
+ def acts_as_chat(message_class: 'Message', tool_call_class: 'ToolCall') # rubocop:disable Metrics/MethodLength
13
13
  include ChatMethods
14
14
 
15
+ @message_class = message_class.to_s
16
+ @tool_call_class = tool_call_class.to_s
17
+
15
18
  has_many :messages,
16
19
  -> { order(created_at: :asc) },
17
- class_name: message_class.to_s,
20
+ class_name: @message_class,
18
21
  dependent: :destroy
19
22
 
20
23
  delegate :complete,
@@ -28,13 +31,35 @@ module RubyLLM
28
31
  to: :to_llm
29
32
  end
30
33
 
31
- def acts_as_message(chat_class: 'Chat')
34
+ def acts_as_message(chat_class: 'Chat', tool_call_class: 'ToolCall') # rubocop:disable Metrics/MethodLength
32
35
  include MessageMethods
33
36
 
34
- belongs_to :chat, class_name: chat_class.to_s
37
+ @chat_class = chat_class.to_s
38
+ @tool_call_class = tool_call_class.to_s
39
+
40
+ belongs_to :chat, class_name: @chat_class
41
+ has_many :tool_calls, class_name: @tool_call_class, dependent: :destroy
42
+
43
+ belongs_to :parent_tool_call,
44
+ class_name: @tool_call_class,
45
+ foreign_key: 'tool_call_id',
46
+ optional: true,
47
+ inverse_of: :result
35
48
 
36
49
  delegate :tool_call?, :tool_result?, :tool_results, to: :to_llm
37
50
  end
51
+
52
+ def acts_as_tool_call(message_class: 'Message')
53
+ @message_class = message_class.to_s
54
+
55
+ belongs_to :message, class_name: @message_class
56
+
57
+ has_one :result,
58
+ class_name: @message_class,
59
+ foreign_key: 'tool_call_id',
60
+ inverse_of: :parent_tool_call,
61
+ dependent: :nullify
62
+ end
38
63
  end
39
64
  end
40
65
 
@@ -43,6 +68,10 @@ module RubyLLM
43
68
  module ChatMethods
44
69
  extend ActiveSupport::Concern
45
70
 
71
+ class_methods do
72
+ attr_reader :tool_call_class
73
+ end
74
+
46
75
  def to_llm
47
76
  chat = RubyLLM.chat(model: model_id)
48
77
 
@@ -73,18 +102,32 @@ module RubyLLM
73
102
  )
74
103
  end
75
104
 
76
- def persist_message_completion(message)
105
+ def persist_message_completion(message) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
77
106
  return unless message
78
107
 
79
- messages.last.update!(
80
- role: message.role,
81
- content: message.content,
82
- model_id: message.model_id,
83
- tool_calls: message.tool_calls,
84
- tool_call_id: message.tool_call_id,
85
- input_tokens: message.input_tokens,
86
- output_tokens: message.output_tokens
87
- )
108
+ if message.tool_call_id
109
+ tool_call_id = self.class.tool_call_class.constantize.find_by(tool_call_id: message.tool_call_id).id
110
+ end
111
+
112
+ transaction do
113
+ messages.last.update!(
114
+ role: message.role,
115
+ content: message.content,
116
+ model_id: message.model_id,
117
+ tool_call_id: tool_call_id,
118
+ input_tokens: message.input_tokens,
119
+ output_tokens: message.output_tokens
120
+ )
121
+ persist_tool_calls(message.tool_calls) if message.tool_calls.present?
122
+ end
123
+ end
124
+
125
+ def persist_tool_calls(tool_calls)
126
+ tool_calls.each_value do |tool_call|
127
+ attributes = tool_call.to_h
128
+ attributes[:tool_call_id] = attributes.delete(:id)
129
+ messages.last.tool_calls.create!(**attributes)
130
+ end
88
131
  end
89
132
  end
90
133
 
@@ -97,13 +140,30 @@ module RubyLLM
97
140
  RubyLLM::Message.new(
98
141
  role: role.to_sym,
99
142
  content: content,
100
- tool_calls: tool_calls,
101
- tool_call_id: tool_call_id,
143
+ tool_calls: extract_tool_calls,
144
+ tool_call_id: extract_tool_call_id,
102
145
  input_tokens: input_tokens,
103
146
  output_tokens: output_tokens,
104
147
  model_id: model_id
105
148
  )
106
149
  end
150
+
151
+ def extract_tool_calls
152
+ tool_calls.to_h do |tool_call|
153
+ [
154
+ tool_call.tool_call_id,
155
+ RubyLLM::ToolCall.new(
156
+ id: tool_call.tool_call_id,
157
+ name: tool_call.name,
158
+ arguments: tool_call.arguments
159
+ )
160
+ ]
161
+ end
162
+ end
163
+
164
+ def extract_tool_call_id
165
+ parent_tool_call&.tool_call_id
166
+ end
107
167
  end
108
168
  end
109
169
  end
data/lib/ruby_llm/chat.rb CHANGED
@@ -97,8 +97,10 @@ module RubyLLM
97
97
 
98
98
  def handle_tool_calls(response, &block)
99
99
  response.tool_calls.each_value do |tool_call|
100
+ @on[:new_message]&.call
100
101
  result = execute_tool tool_call
101
- add_tool_result tool_call.id, result if result
102
+ message = add_tool_result tool_call.id, result
103
+ @on[:end_message]&.call(message)
102
104
  end
103
105
 
104
106
  complete(&block)
@@ -19,5 +19,13 @@ module RubyLLM
19
19
  @name = name
20
20
  @arguments = arguments
21
21
  end
22
+
23
+ def to_h
24
+ {
25
+ id: @id,
26
+ name: @name,
27
+ arguments: @arguments
28
+ }
29
+ end
22
30
  end
23
31
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RubyLLM
4
- VERSION = '0.1.0.pre17'
4
+ VERSION = '0.1.0.pre19'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_llm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0.pre17
4
+ version: 0.1.0.pre19
5
5
  platform: ruby
6
6
  authors:
7
7
  - Carmine Paolino