dify_llm 1.8.2 → 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.
- checksums.yaml +4 -4
- data/README.md +8 -3
- data/lib/generators/ruby_llm/generator_helpers.rb +31 -10
- data/lib/generators/ruby_llm/install/templates/create_messages_migration.rb.tt +3 -0
- data/lib/generators/ruby_llm/install/templates/create_models_migration.rb.tt +5 -0
- data/lib/generators/ruby_llm/install/templates/create_tool_calls_migration.rb.tt +7 -1
- data/lib/generators/ruby_llm/upgrade_to_v1_7/upgrade_to_v1_7_generator.rb +1 -1
- data/lib/generators/ruby_llm/upgrade_to_v1_9/templates/add_v1_9_message_columns.rb.tt +15 -0
- data/lib/generators/ruby_llm/upgrade_to_v1_9/upgrade_to_v1_9_generator.rb +49 -0
- data/lib/ruby_llm/active_record/acts_as.rb +22 -24
- data/lib/ruby_llm/active_record/chat_methods.rb +41 -13
- data/lib/ruby_llm/active_record/message_methods.rb +11 -2
- data/lib/ruby_llm/active_record/model_methods.rb +1 -1
- data/lib/ruby_llm/aliases.json +61 -32
- data/lib/ruby_llm/attachment.rb +44 -13
- data/lib/ruby_llm/chat.rb +13 -2
- data/lib/ruby_llm/configuration.rb +6 -1
- data/lib/ruby_llm/connection.rb +3 -3
- data/lib/ruby_llm/content.rb +23 -0
- data/lib/ruby_llm/message.rb +11 -6
- data/lib/ruby_llm/model/info.rb +4 -0
- data/lib/ruby_llm/models.json +9649 -8211
- data/lib/ruby_llm/models.rb +14 -22
- data/lib/ruby_llm/provider.rb +23 -1
- data/lib/ruby_llm/providers/anthropic/chat.rb +22 -3
- data/lib/ruby_llm/providers/anthropic/content.rb +44 -0
- data/lib/ruby_llm/providers/anthropic/media.rb +3 -2
- data/lib/ruby_llm/providers/anthropic/models.rb +15 -0
- data/lib/ruby_llm/providers/anthropic/streaming.rb +2 -0
- data/lib/ruby_llm/providers/anthropic/tools.rb +20 -18
- data/lib/ruby_llm/providers/bedrock/media.rb +2 -1
- data/lib/ruby_llm/providers/bedrock/streaming/content_extraction.rb +15 -0
- data/lib/ruby_llm/providers/bedrock/streaming/payload_processing.rb +2 -0
- data/lib/ruby_llm/providers/dify/chat.rb +16 -5
- data/lib/ruby_llm/providers/gemini/chat.rb +352 -69
- data/lib/ruby_llm/providers/gemini/media.rb +59 -1
- data/lib/ruby_llm/providers/gemini/tools.rb +146 -25
- data/lib/ruby_llm/providers/gemini/transcription.rb +116 -0
- data/lib/ruby_llm/providers/gemini.rb +2 -1
- data/lib/ruby_llm/providers/gpustack/media.rb +1 -0
- data/lib/ruby_llm/providers/ollama/media.rb +1 -0
- data/lib/ruby_llm/providers/openai/chat.rb +7 -2
- data/lib/ruby_llm/providers/openai/media.rb +2 -1
- data/lib/ruby_llm/providers/openai/streaming.rb +7 -2
- data/lib/ruby_llm/providers/openai/tools.rb +26 -6
- data/lib/ruby_llm/providers/openai/transcription.rb +70 -0
- data/lib/ruby_llm/providers/openai.rb +1 -0
- data/lib/ruby_llm/providers/vertexai/transcription.rb +16 -0
- data/lib/ruby_llm/providers/vertexai.rb +11 -11
- data/lib/ruby_llm/railtie.rb +24 -22
- data/lib/ruby_llm/stream_accumulator.rb +10 -4
- data/lib/ruby_llm/tool.rb +126 -0
- data/lib/ruby_llm/transcription.rb +35 -0
- data/lib/ruby_llm/utils.rb +46 -0
- data/lib/ruby_llm/version.rb +1 -1
- data/lib/ruby_llm.rb +7 -0
- metadata +24 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d85ea7861e7fdc76f629a92f9aa4768d61d8ad6ad069d540c35943356ab622cc
|
|
4
|
+
data.tar.gz: f684857d9908d597c2f913e0f57fad58e71bab4be9698e29dee95ec49c571f74
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 71a889822eeb0a384e5dd9639eb92db1dc1d9da7d6da02fcc97000596affdb023ae32d9bebc341bf6810050b2146198fdb90af544d99a8d70a2b84be05dfe87e
|
|
7
|
+
data.tar.gz: 912691efa352436b5cc6ba1d1b5fbf0890e48591399b5c6e98f44a7d6c2721f2164ae1acebe906d48f987ce0f4cb90ff261cef66c35e7f8982133c31c9bfebfb
|
data/README.md
CHANGED
|
@@ -18,7 +18,7 @@ Battle tested at [<picture><source media="(prefers-color-scheme: dark)" srcset="
|
|
|
18
18
|
</div>
|
|
19
19
|
|
|
20
20
|
> [!NOTE]
|
|
21
|
-
> Using RubyLLM
|
|
21
|
+
> Using RubyLLM? [Share your story](https://tally.so/r/3Na02p)! Takes 5 minutes.
|
|
22
22
|
|
|
23
23
|
---
|
|
24
24
|
|
|
@@ -69,6 +69,11 @@ RubyLLM.paint "a sunset over mountains in watercolor style"
|
|
|
69
69
|
RubyLLM.embed "Ruby is elegant and expressive"
|
|
70
70
|
```
|
|
71
71
|
|
|
72
|
+
```ruby
|
|
73
|
+
# Transcribe audio to text
|
|
74
|
+
RubyLLM.transcribe "meeting.wav"
|
|
75
|
+
```
|
|
76
|
+
|
|
72
77
|
```ruby
|
|
73
78
|
# Moderate content for safety
|
|
74
79
|
RubyLLM.moderate "Check if this text is safe"
|
|
@@ -107,10 +112,10 @@ response = chat.with_schema(ProductSchema).ask "Analyze this product", with: "pr
|
|
|
107
112
|
|
|
108
113
|
* **Chat:** Conversational AI with `RubyLLM.chat`
|
|
109
114
|
* **Vision:** Analyze images and videos
|
|
110
|
-
* **Audio:** Transcribe and understand speech
|
|
115
|
+
* **Audio:** Transcribe and understand speech with `RubyLLM.transcribe`
|
|
111
116
|
* **Documents:** Extract from PDFs, CSVs, JSON, any file type
|
|
112
117
|
* **Image generation:** Create images with `RubyLLM.paint`
|
|
113
|
-
* **Embeddings:**
|
|
118
|
+
* **Embeddings:** Generate embeddings with `RubyLLM.embed`
|
|
114
119
|
* **Moderation:** Content safety with `RubyLLM.moderate`
|
|
115
120
|
* **Tools:** Let AI call your Ruby methods
|
|
116
121
|
* **Structured output:** JSON schemas that just work
|
|
@@ -52,8 +52,10 @@ module RubyLLM
|
|
|
52
52
|
def acts_as_chat_declaration
|
|
53
53
|
params = []
|
|
54
54
|
|
|
55
|
-
add_association_params(params, :messages, message_table_name, message_model_name,
|
|
56
|
-
|
|
55
|
+
add_association_params(params, :messages, message_table_name, message_model_name,
|
|
56
|
+
owner_table: chat_table_name, plural: true)
|
|
57
|
+
add_association_params(params, :model, model_table_name, model_model_name,
|
|
58
|
+
owner_table: chat_table_name)
|
|
57
59
|
|
|
58
60
|
"acts_as_chat#{" #{params.join(', ')}" if params.any?}"
|
|
59
61
|
end
|
|
@@ -61,9 +63,12 @@ module RubyLLM
|
|
|
61
63
|
def acts_as_message_declaration
|
|
62
64
|
params = []
|
|
63
65
|
|
|
64
|
-
add_association_params(params, :chat, chat_table_name, chat_model_name
|
|
65
|
-
|
|
66
|
-
add_association_params(params, :
|
|
66
|
+
add_association_params(params, :chat, chat_table_name, chat_model_name,
|
|
67
|
+
owner_table: message_table_name)
|
|
68
|
+
add_association_params(params, :tool_calls, tool_call_table_name, tool_call_model_name,
|
|
69
|
+
owner_table: message_table_name, plural: true)
|
|
70
|
+
add_association_params(params, :model, model_table_name, model_model_name,
|
|
71
|
+
owner_table: message_table_name)
|
|
67
72
|
|
|
68
73
|
"acts_as_message#{" #{params.join(', ')}" if params.any?}"
|
|
69
74
|
end
|
|
@@ -71,7 +76,8 @@ module RubyLLM
|
|
|
71
76
|
def acts_as_model_declaration
|
|
72
77
|
params = []
|
|
73
78
|
|
|
74
|
-
add_association_params(params, :chats, chat_table_name, chat_model_name,
|
|
79
|
+
add_association_params(params, :chats, chat_table_name, chat_model_name,
|
|
80
|
+
owner_table: model_table_name, plural: true)
|
|
75
81
|
|
|
76
82
|
"acts_as_model#{" #{params.join(', ')}" if params.any?}"
|
|
77
83
|
end
|
|
@@ -79,7 +85,8 @@ module RubyLLM
|
|
|
79
85
|
def acts_as_tool_call_declaration
|
|
80
86
|
params = []
|
|
81
87
|
|
|
82
|
-
add_association_params(params, :message, message_table_name, message_model_name
|
|
88
|
+
add_association_params(params, :message, message_table_name, message_model_name,
|
|
89
|
+
owner_table: tool_call_table_name)
|
|
83
90
|
|
|
84
91
|
"acts_as_tool_call#{" #{params.join(', ')}" if params.any?}"
|
|
85
92
|
end
|
|
@@ -120,6 +127,12 @@ module RubyLLM
|
|
|
120
127
|
false
|
|
121
128
|
end
|
|
122
129
|
|
|
130
|
+
def mysql?
|
|
131
|
+
::ActiveRecord::Base.connection.adapter_name.downcase.include?('mysql')
|
|
132
|
+
rescue StandardError
|
|
133
|
+
false
|
|
134
|
+
end
|
|
135
|
+
|
|
123
136
|
def table_exists?(table_name)
|
|
124
137
|
::ActiveRecord::Base.connection.table_exists?(table_name)
|
|
125
138
|
rescue StandardError
|
|
@@ -128,13 +141,21 @@ module RubyLLM
|
|
|
128
141
|
|
|
129
142
|
private
|
|
130
143
|
|
|
131
|
-
def add_association_params(params, default_assoc, table_name, model_name, plural: false)
|
|
144
|
+
def add_association_params(params, default_assoc, table_name, model_name, owner_table:, plural: false) # rubocop:disable Metrics/ParameterLists
|
|
132
145
|
assoc = plural ? table_name.to_sym : table_name.singularize.to_sym
|
|
133
146
|
|
|
134
|
-
|
|
147
|
+
default_foreign_key = "#{default_assoc}_id"
|
|
148
|
+
# has_many/has_one: foreign key is on the associated table pointing back to owner
|
|
149
|
+
# belongs_to: foreign key is on the owner table pointing to associated table
|
|
150
|
+
foreign_key = if plural || default_assoc.to_s.pluralize == default_assoc.to_s # has_many or has_one
|
|
151
|
+
"#{owner_table.singularize}_id"
|
|
152
|
+
else # belongs_to
|
|
153
|
+
"#{table_name.singularize}_id"
|
|
154
|
+
end
|
|
135
155
|
|
|
136
|
-
params << "#{default_assoc}: :#{assoc}"
|
|
156
|
+
params << "#{default_assoc}: :#{assoc}" if assoc != default_assoc
|
|
137
157
|
params << "#{default_assoc.to_s.singularize}_class: '#{model_name}'" if model_name != assoc.to_s.classify
|
|
158
|
+
params << "#{default_assoc}_foreign_key: :#{foreign_key}" if foreign_key != default_foreign_key
|
|
138
159
|
end
|
|
139
160
|
|
|
140
161
|
# Convert namespaced model names to proper table names
|
|
@@ -3,8 +3,11 @@ class Create<%= message_model_name.gsub('::', '').pluralize %> < ActiveRecord::M
|
|
|
3
3
|
create_table :<%= message_table_name %> do |t|
|
|
4
4
|
t.string :role, null: false
|
|
5
5
|
t.text :content
|
|
6
|
+
t.json :content_raw
|
|
6
7
|
t.integer :input_tokens
|
|
7
8
|
t.integer :output_tokens
|
|
9
|
+
t.integer :cached_tokens
|
|
10
|
+
t.integer :cache_creation_tokens
|
|
8
11
|
t.timestamps
|
|
9
12
|
end
|
|
10
13
|
|
|
@@ -14,6 +14,11 @@ class Create<%= model_model_name.gsub('::', '').pluralize %> < ActiveRecord::Mig
|
|
|
14
14
|
t.jsonb :capabilities, default: []
|
|
15
15
|
t.jsonb :pricing, default: {}
|
|
16
16
|
t.jsonb :metadata, default: {}
|
|
17
|
+
<% elsif mysql? %>
|
|
18
|
+
t.json :modalities
|
|
19
|
+
t.json :capabilities
|
|
20
|
+
t.json :pricing
|
|
21
|
+
t.json :metadata
|
|
17
22
|
<% else %>
|
|
18
23
|
t.json :modalities, default: {}
|
|
19
24
|
t.json :capabilities, default: []
|
|
@@ -4,7 +4,13 @@ class Create<%= tool_call_model_name.gsub('::', '').pluralize %> < ActiveRecord:
|
|
|
4
4
|
create_table :<%= tool_call_table_name %> do |t|
|
|
5
5
|
t.string :tool_call_id, null: false
|
|
6
6
|
t.string :name, null: false
|
|
7
|
-
|
|
7
|
+
<% if postgresql? %>
|
|
8
|
+
t.jsonb :arguments, default: {}
|
|
9
|
+
<% elsif mysql? %>
|
|
10
|
+
t.json :arguments
|
|
11
|
+
<% else %>
|
|
12
|
+
t.json :arguments, default: {}
|
|
13
|
+
<% end %>
|
|
8
14
|
t.timestamps
|
|
9
15
|
end
|
|
10
16
|
|
|
@@ -9,7 +9,7 @@ module RubyLLM
|
|
|
9
9
|
# Generator to upgrade existing RubyLLM apps to v1.7 with new Rails-like API
|
|
10
10
|
class UpgradeToV17Generator < Rails::Generators::Base
|
|
11
11
|
include Rails::Generators::Migration
|
|
12
|
-
include RubyLLM::GeneratorHelpers
|
|
12
|
+
include RubyLLM::Generators::GeneratorHelpers
|
|
13
13
|
|
|
14
14
|
namespace 'ruby_llm:upgrade_to_v1_7'
|
|
15
15
|
source_root File.expand_path('templates', __dir__)
|
|
@@ -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
|
|
@@ -11,28 +11,28 @@ module RubyLLM
|
|
|
11
11
|
super
|
|
12
12
|
# Monkey-patch Models to use database when ActsAs is active
|
|
13
13
|
RubyLLM::Models.class_eval do
|
|
14
|
-
def load_models
|
|
14
|
+
def self.load_models
|
|
15
15
|
read_from_database
|
|
16
16
|
rescue StandardError => e
|
|
17
17
|
RubyLLM.logger.debug "Failed to load models from database: #{e.message}, falling back to JSON"
|
|
18
18
|
read_from_json
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
def
|
|
22
|
-
@models = read_from_database
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def read_from_database
|
|
21
|
+
def self.read_from_database
|
|
26
22
|
model_class = RubyLLM.config.model_registry_class
|
|
27
23
|
model_class = model_class.constantize if model_class.is_a?(String)
|
|
28
24
|
model_class.all.map(&:to_llm)
|
|
29
25
|
end
|
|
26
|
+
|
|
27
|
+
def load_from_database!
|
|
28
|
+
@models = self.class.read_from_database
|
|
29
|
+
end
|
|
30
30
|
end
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
class_methods do # rubocop:disable Metrics/BlockLength
|
|
34
|
-
def acts_as_chat(messages: :messages, message_class: nil,
|
|
35
|
-
model: :model, model_class: nil)
|
|
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
36
|
include RubyLLM::ActiveRecord::ChatMethods
|
|
37
37
|
|
|
38
38
|
class_attribute :messages_association_name, :model_association_name, :message_class, :model_class
|
|
@@ -45,12 +45,12 @@ module RubyLLM
|
|
|
45
45
|
has_many messages,
|
|
46
46
|
-> { order(created_at: :asc) },
|
|
47
47
|
class_name: self.message_class,
|
|
48
|
-
foreign_key:
|
|
48
|
+
foreign_key: messages_foreign_key,
|
|
49
49
|
dependent: :destroy
|
|
50
50
|
|
|
51
51
|
belongs_to model,
|
|
52
52
|
class_name: self.model_class,
|
|
53
|
-
foreign_key:
|
|
53
|
+
foreign_key: model_foreign_key,
|
|
54
54
|
optional: true
|
|
55
55
|
|
|
56
56
|
delegate :add_message, to: :to_llm
|
|
@@ -68,7 +68,7 @@ module RubyLLM
|
|
|
68
68
|
end
|
|
69
69
|
end
|
|
70
70
|
|
|
71
|
-
def acts_as_model(chats: :chats, chat_class: nil)
|
|
71
|
+
def acts_as_model(chats: :chats, chat_class: nil, chats_foreign_key: nil)
|
|
72
72
|
include RubyLLM::ActiveRecord::ModelMethods
|
|
73
73
|
|
|
74
74
|
class_attribute :chats_association_name, :chat_class
|
|
@@ -80,18 +80,16 @@ module RubyLLM
|
|
|
80
80
|
validates :provider, presence: true
|
|
81
81
|
validates :name, presence: true
|
|
82
82
|
|
|
83
|
-
has_many chats,
|
|
84
|
-
class_name: self.chat_class,
|
|
85
|
-
foreign_key: ActiveSupport::Inflector.foreign_key(table_name.singularize)
|
|
83
|
+
has_many chats, class_name: self.chat_class, foreign_key: chats_foreign_key
|
|
86
84
|
|
|
87
85
|
define_method :chats_association do
|
|
88
86
|
send(chats_association_name)
|
|
89
87
|
end
|
|
90
88
|
end
|
|
91
89
|
|
|
92
|
-
def acts_as_message(chat: :chat, chat_class: nil, touch_chat: false, # rubocop:disable Metrics/ParameterLists
|
|
93
|
-
tool_calls: :tool_calls, tool_call_class: nil,
|
|
94
|
-
model: :model, model_class: nil)
|
|
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)
|
|
95
93
|
include RubyLLM::ActiveRecord::MessageMethods
|
|
96
94
|
|
|
97
95
|
class_attribute :chat_association_name, :tool_calls_association_name, :model_association_name,
|
|
@@ -106,12 +104,12 @@ module RubyLLM
|
|
|
106
104
|
|
|
107
105
|
belongs_to chat,
|
|
108
106
|
class_name: self.chat_class,
|
|
109
|
-
foreign_key:
|
|
107
|
+
foreign_key: chat_foreign_key,
|
|
110
108
|
touch: touch_chat
|
|
111
109
|
|
|
112
110
|
has_many tool_calls,
|
|
113
111
|
class_name: self.tool_call_class,
|
|
114
|
-
foreign_key:
|
|
112
|
+
foreign_key: tool_calls_foreign_key,
|
|
115
113
|
dependent: :destroy
|
|
116
114
|
|
|
117
115
|
belongs_to :parent_tool_call,
|
|
@@ -126,7 +124,7 @@ module RubyLLM
|
|
|
126
124
|
|
|
127
125
|
belongs_to model,
|
|
128
126
|
class_name: self.model_class,
|
|
129
|
-
foreign_key:
|
|
127
|
+
foreign_key: model_foreign_key,
|
|
130
128
|
optional: true
|
|
131
129
|
|
|
132
130
|
delegate :tool_call?, :tool_result?, to: :to_llm
|
|
@@ -144,8 +142,8 @@ module RubyLLM
|
|
|
144
142
|
end
|
|
145
143
|
end
|
|
146
144
|
|
|
147
|
-
def acts_as_tool_call(message: :message, message_class: nil,
|
|
148
|
-
result: :result, result_class: nil)
|
|
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)
|
|
149
147
|
class_attribute :message_association_name, :result_association_name, :message_class, :result_class
|
|
150
148
|
|
|
151
149
|
self.message_association_name = message
|
|
@@ -155,11 +153,11 @@ module RubyLLM
|
|
|
155
153
|
|
|
156
154
|
belongs_to message,
|
|
157
155
|
class_name: self.message_class,
|
|
158
|
-
foreign_key:
|
|
156
|
+
foreign_key: message_foreign_key
|
|
159
157
|
|
|
160
158
|
has_one result,
|
|
161
159
|
class_name: self.result_class,
|
|
162
|
-
foreign_key:
|
|
160
|
+
foreign_key: result_foreign_key,
|
|
163
161
|
dependent: :nullify
|
|
164
162
|
|
|
165
163
|
define_method :message_association do
|
|
@@ -174,8 +174,16 @@ module RubyLLM
|
|
|
174
174
|
end
|
|
175
175
|
|
|
176
176
|
def create_user_message(content, with: nil)
|
|
177
|
-
|
|
177
|
+
content_text, attachments, content_raw = prepare_content_for_storage(content)
|
|
178
|
+
|
|
179
|
+
message_record = messages_association.build(role: :user)
|
|
180
|
+
message_record.content = content_text
|
|
181
|
+
message_record.content_raw = content_raw if message_record.respond_to?(:content_raw=)
|
|
182
|
+
message_record.save!
|
|
183
|
+
|
|
178
184
|
persist_content(message_record, with) if with.present?
|
|
185
|
+
persist_content(message_record, attachments) if attachments.present?
|
|
186
|
+
|
|
179
187
|
message_record
|
|
180
188
|
end
|
|
181
189
|
|
|
@@ -235,28 +243,25 @@ module RubyLLM
|
|
|
235
243
|
@message = messages_association.create!(role: :assistant, content: '')
|
|
236
244
|
end
|
|
237
245
|
|
|
238
|
-
|
|
246
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
|
247
|
+
def persist_message_completion(message)
|
|
239
248
|
return unless message
|
|
240
249
|
|
|
241
250
|
tool_call_id = find_tool_call_id(message.tool_call_id) if message.tool_call_id
|
|
242
251
|
|
|
243
252
|
transaction do
|
|
244
|
-
|
|
245
|
-
attachments_to_persist = nil
|
|
246
|
-
|
|
247
|
-
if content.is_a?(RubyLLM::Content)
|
|
248
|
-
attachments_to_persist = content.attachments if content.attachments.any?
|
|
249
|
-
content = content.text
|
|
250
|
-
elsif content.is_a?(Hash) || content.is_a?(Array)
|
|
251
|
-
content = content.to_json
|
|
252
|
-
end
|
|
253
|
+
content_text, attachments_to_persist, content_raw = prepare_content_for_storage(message.content)
|
|
253
254
|
|
|
254
255
|
attrs = {
|
|
255
256
|
role: message.role,
|
|
256
|
-
content:
|
|
257
|
+
content: content_text,
|
|
257
258
|
input_tokens: message.input_tokens,
|
|
258
259
|
output_tokens: message.output_tokens
|
|
259
260
|
}
|
|
261
|
+
attrs[:cached_tokens] = message.cached_tokens if @message.has_attribute?(:cached_tokens)
|
|
262
|
+
if @message.has_attribute?(:cache_creation_tokens)
|
|
263
|
+
attrs[:cache_creation_tokens] = message.cache_creation_tokens
|
|
264
|
+
end
|
|
260
265
|
|
|
261
266
|
# Add model association dynamically
|
|
262
267
|
attrs[self.class.model_association_name] = model_association
|
|
@@ -266,12 +271,15 @@ module RubyLLM
|
|
|
266
271
|
attrs[parent_tool_call_assoc.foreign_key] = tool_call_id
|
|
267
272
|
end
|
|
268
273
|
|
|
269
|
-
@message.
|
|
274
|
+
@message.assign_attributes(attrs)
|
|
275
|
+
@message.content_raw = content_raw if @message.respond_to?(:content_raw=)
|
|
276
|
+
@message.save!
|
|
270
277
|
|
|
271
278
|
persist_content(@message, attachments_to_persist) if attachments_to_persist
|
|
272
279
|
persist_tool_calls(message.tool_calls) if message.tool_calls.present?
|
|
273
280
|
end
|
|
274
281
|
end
|
|
282
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
|
275
283
|
|
|
276
284
|
def persist_tool_calls(tool_calls)
|
|
277
285
|
tool_calls.each_value do |tool_call|
|
|
@@ -331,6 +339,26 @@ module RubyLLM
|
|
|
331
339
|
RubyLLM.logger.warn "Failed to process attachment #{source}: #{e.message}"
|
|
332
340
|
nil
|
|
333
341
|
end
|
|
342
|
+
|
|
343
|
+
def prepare_content_for_storage(content)
|
|
344
|
+
attachments = nil
|
|
345
|
+
content_raw = nil
|
|
346
|
+
content_text = content
|
|
347
|
+
|
|
348
|
+
case content
|
|
349
|
+
when RubyLLM::Content::Raw
|
|
350
|
+
content_raw = content.value
|
|
351
|
+
content_text = nil
|
|
352
|
+
when RubyLLM::Content
|
|
353
|
+
attachments = content.attachments if content.attachments.any?
|
|
354
|
+
content_text = content.text
|
|
355
|
+
when Hash, Array
|
|
356
|
+
content_raw = content
|
|
357
|
+
content_text = nil
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
[content_text, attachments, content_raw]
|
|
361
|
+
end
|
|
334
362
|
end
|
|
335
363
|
end
|
|
336
364
|
end
|
|
@@ -11,6 +11,9 @@ module RubyLLM
|
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def to_llm
|
|
14
|
+
cached = has_attribute?(:cached_tokens) ? self[:cached_tokens] : nil
|
|
15
|
+
cache_creation = has_attribute?(:cache_creation_tokens) ? self[:cache_creation_tokens] : nil
|
|
16
|
+
|
|
14
17
|
RubyLLM::Message.new(
|
|
15
18
|
role: role.to_sym,
|
|
16
19
|
content: extract_content,
|
|
@@ -18,6 +21,8 @@ module RubyLLM
|
|
|
18
21
|
tool_call_id: extract_tool_call_id,
|
|
19
22
|
input_tokens: input_tokens,
|
|
20
23
|
output_tokens: output_tokens,
|
|
24
|
+
cached_tokens: cached,
|
|
25
|
+
cache_creation_tokens: cache_creation,
|
|
21
26
|
model_id: model_association&.model_id
|
|
22
27
|
)
|
|
23
28
|
end
|
|
@@ -42,9 +47,13 @@ module RubyLLM
|
|
|
42
47
|
end
|
|
43
48
|
|
|
44
49
|
def extract_content
|
|
45
|
-
return
|
|
50
|
+
return RubyLLM::Content::Raw.new(content_raw) if has_attribute?(:content_raw) && content_raw.present?
|
|
51
|
+
|
|
52
|
+
content_value = self[:content]
|
|
53
|
+
|
|
54
|
+
return content_value unless respond_to?(:attachments) && attachments.attached?
|
|
46
55
|
|
|
47
|
-
RubyLLM::Content.new(
|
|
56
|
+
RubyLLM::Content.new(content_value).tap do |content_obj|
|
|
48
57
|
@_tempfiles = []
|
|
49
58
|
|
|
50
59
|
attachments.each do |attachment|
|
|
@@ -77,7 +77,7 @@ module RubyLLM
|
|
|
77
77
|
delegate :supports?, :supports_vision?, :supports_functions?, :type,
|
|
78
78
|
:input_price_per_million, :output_price_per_million,
|
|
79
79
|
:function_calling?, :structured_output?, :batch?,
|
|
80
|
-
:reasoning?, :citations?, :streaming?,
|
|
80
|
+
:reasoning?, :citations?, :streaming?, :provider_class,
|
|
81
81
|
to: :to_llm
|
|
82
82
|
end
|
|
83
83
|
end
|