ruby_llm 2.0.0.rc4 → 2.0.0
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 +4 -4
- data/lib/generators/ruby_llm/generator_helpers.rb +4 -1
- data/lib/generators/ruby_llm/install/templates/create_chats_migration.rb.tt +1 -1
- data/lib/generators/ruby_llm/install/templates/create_messages_migration.rb.tt +1 -1
- data/lib/generators/ruby_llm/install/templates/create_ruby_llm_records_migration.rb.tt +5 -5
- data/lib/generators/ruby_llm/upgrade/templates/prepare_v2_upgrade.rb.tt +2 -2
- data/lib/ruby_llm/active_record/attachment_helpers.rb +81 -3
- data/lib/ruby_llm/active_record/chat_methods.rb +11 -8
- data/lib/ruby_llm/agent.rb +17 -17
- data/lib/ruby_llm/chat.rb +15 -15
- data/lib/ruby_llm/error.rb +1 -1
- data/lib/ruby_llm/message.rb +15 -1
- data/lib/ruby_llm/protocol.rb +36 -13
- data/lib/ruby_llm/protocols/vertexai/research.rb +8 -8
- data/lib/ruby_llm/provider.rb +4 -4
- data/lib/ruby_llm/providers/openrouter/chat.rb +4 -0
- data/lib/ruby_llm/research_job.rb +4 -4
- data/lib/ruby_llm/server_tool_call.rb +2 -2
- data/lib/ruby_llm/tools/{server_tools.rb → provider_tools.rb} +7 -7
- data/lib/ruby_llm/version.rb +1 -1
- data/lib/ruby_llm.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 42dd9dfdb910f8e5df4eb6114ed59586a13e4a51797173d3f3bdb13b6d59c28b
|
|
4
|
+
data.tar.gz: fa4e0aa374c915ab5e97146bcacb438fc2c3fca0c029ed51c1aea6c934769da8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3e5489b6fdd071de5c4281c9cc8eff9d345dd10876cb6773144873d2e58c3ec400109d208706d4c04c12d93328f5864f839907521fd751accc29aa9a5a753602
|
|
7
|
+
data.tar.gz: 19c33af936f0626262ad3e469c955b98aee8599b15fe088ba745e31584f91ebbf166cabc981bef4bef526f6e6c96800aebe07dc133ca732313ca58598aa6273f
|
data/README.md
CHANGED
|
@@ -34,7 +34,7 @@ Build with the models you want. Move between hosted and local providers without
|
|
|
34
34
|
|
|
35
35
|
## Start with one line. Add files, tools, and agents
|
|
36
36
|
|
|
37
|
-
These examples use **2.0.0
|
|
37
|
+
These examples use **2.0.0**. Follow [Getting Started](https://rubyllm.com/getting-started/) to install it and configure the providers you want to try. For 1.x, use the [1.x docs](https://rubyllm.com/v1/).
|
|
38
38
|
|
|
39
39
|
```ruby
|
|
40
40
|
# Just ask
|
|
@@ -167,7 +167,7 @@ Agents, workflows, RAG, images, audio, and video. Built in, with usage tracking
|
|
|
167
167
|
* **Tools:** Let AI call your Ruby methods
|
|
168
168
|
* **Tool approval:** Park a run until a human approves with `requires_approval`
|
|
169
169
|
* **The agentic loop:** Drive it yourself with `ask_later`, `step`, and `complete?`
|
|
170
|
-
* **
|
|
170
|
+
* **Provider tools:** Web search, code execution, and MCP connectors with `with_provider_tools`
|
|
171
171
|
* **Agents:** Reusable assistants with `RubyLLM::Agent`
|
|
172
172
|
* **Prompt templates:** ERB prompts in `app/prompts`, rendered with `RubyLLM.render_prompt`
|
|
173
173
|
* **Workflows:** Correlate multi-agent runs in your telemetry with `RubyLLM.workflow`
|
|
@@ -189,10 +189,10 @@ Agents, workflows, RAG, images, audio, and video. Built in, with usage tracking
|
|
|
189
189
|
|
|
190
190
|
## Installation
|
|
191
191
|
|
|
192
|
-
Install
|
|
192
|
+
Install RubyLLM 2.0:
|
|
193
193
|
|
|
194
194
|
```bash
|
|
195
|
-
bundle add ruby_llm --version 2.0.0
|
|
195
|
+
bundle add ruby_llm --version 2.0.0
|
|
196
196
|
```
|
|
197
197
|
|
|
198
198
|
Configure a provider in your script, or in `config/initializers/ruby_llm.rb` in Rails:
|
|
@@ -138,7 +138,10 @@ module RubyLLM
|
|
|
138
138
|
end
|
|
139
139
|
|
|
140
140
|
def reference_type
|
|
141
|
-
Rails.application.
|
|
141
|
+
application = Rails.application if Rails.respond_to?(:application)
|
|
142
|
+
return :bigint unless application
|
|
143
|
+
|
|
144
|
+
application.config.generators.options.dig(:active_record, :primary_key_type) || :bigint
|
|
142
145
|
end
|
|
143
146
|
|
|
144
147
|
def create_migration_class_name(table_name)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
class <%= create_migration_class_name(chat_table_name) %> < ActiveRecord::Migration<%= migration_version %>
|
|
2
2
|
def change
|
|
3
|
-
create_table :<%= chat_table_name %> do |t|
|
|
3
|
+
create_table :<%= chat_table_name %>, id: :<%= reference_type %> do |t|
|
|
4
4
|
t.references :ruby_llm_model, null: false, foreign_key: { to_table: :ruby_llm_models }, type: :<%= reference_type %>
|
|
5
5
|
t.boolean :cancelled, null: false, default: false
|
|
6
6
|
t.timestamps
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
class <%= create_migration_class_name(message_table_name) %> < ActiveRecord::Migration<%= migration_version %>
|
|
2
2
|
def change
|
|
3
|
-
create_table :<%= message_table_name %> do |t|
|
|
3
|
+
create_table :<%= message_table_name %>, id: :<%= reference_type %> do |t|
|
|
4
4
|
t.references :<%= chat_table_name.singularize %>, null: false, foreign_key: { to_table: :<%= chat_table_name %> }, type: :<%= reference_type %>
|
|
5
5
|
t.string :role, null: false
|
|
6
6
|
t.text :content
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
class
|
|
1
|
+
class <%= create_migration_class_name('ruby_llm_records') %> < ActiveRecord::Migration<%= migration_version %>
|
|
2
2
|
def change
|
|
3
|
-
create_table :ruby_llm_models do |t|
|
|
3
|
+
create_table :ruby_llm_models, id: :<%= reference_type %> do |t|
|
|
4
4
|
t.string :model_id, null: false
|
|
5
5
|
t.string :name, null: false
|
|
6
6
|
t.string :provider, null: false
|
|
@@ -37,7 +37,7 @@ class CreateRubyLlmRecords < ActiveRecord::Migration<%= migration_version %>
|
|
|
37
37
|
<% end -%>
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
-
create_table :ruby_llm_tool_calls do |t|
|
|
40
|
+
create_table :ruby_llm_tool_calls, id: :<%= reference_type %> do |t|
|
|
41
41
|
t.references :message, polymorphic: true, null: false, type: :<%= reference_type %>, index: false
|
|
42
42
|
t.references :result, polymorphic: true, type: :<%= reference_type %>, index: false
|
|
43
43
|
t.string :tool_call_id, null: false
|
|
@@ -60,7 +60,7 @@ class CreateRubyLlmRecords < ActiveRecord::Migration<%= migration_version %>
|
|
|
60
60
|
t.index :name
|
|
61
61
|
end
|
|
62
62
|
|
|
63
|
-
create_table :ruby_llm_usages do |t|
|
|
63
|
+
create_table :ruby_llm_usages, id: :<%= reference_type %> do |t|
|
|
64
64
|
t.references :chat, polymorphic: true, null: false, type: :<%= reference_type %>, index: false
|
|
65
65
|
t.references :message, polymorphic: true, type: :<%= reference_type %>, index: false
|
|
66
66
|
t.string :operation, null: false
|
|
@@ -87,7 +87,7 @@ class CreateRubyLlmRecords < ActiveRecord::Migration<%= migration_version %>
|
|
|
87
87
|
t.check_constraint "status IN (<%= usage_statuses_sql %>)"
|
|
88
88
|
end
|
|
89
89
|
|
|
90
|
-
create_table :ruby_llm_batches do |t|
|
|
90
|
+
create_table :ruby_llm_batches, id: :<%= reference_type %> do |t|
|
|
91
91
|
t.string :provider_batch_id, null: false
|
|
92
92
|
t.string :provider, null: false
|
|
93
93
|
t.string :status, null: false
|
|
@@ -382,7 +382,7 @@ class <%= prepare_migration_class_name %> < ActiveRecord::Migration<%= migration
|
|
|
382
382
|
|
|
383
383
|
def create_usages
|
|
384
384
|
unless table_exists?(:ruby_llm_usages)
|
|
385
|
-
create_table :ruby_llm_usages do |table|
|
|
385
|
+
create_table :ruby_llm_usages, id: :<%= reference_type %> do |table|
|
|
386
386
|
table.references :chat, polymorphic: true, null: false,
|
|
387
387
|
type: reference_type_for(:<%= chat_table_name %>), index: false
|
|
388
388
|
table.references :message, polymorphic: true,
|
|
@@ -417,7 +417,7 @@ class <%= prepare_migration_class_name %> < ActiveRecord::Migration<%= migration
|
|
|
417
417
|
|
|
418
418
|
def create_batches
|
|
419
419
|
unless table_exists?(:ruby_llm_batches)
|
|
420
|
-
create_table :ruby_llm_batches do |table|
|
|
420
|
+
create_table :ruby_llm_batches, id: :<%= reference_type %> do |table|
|
|
421
421
|
table.string :provider_batch_id, null: false
|
|
422
422
|
table.string :provider, null: false
|
|
423
423
|
table.string :status, null: false
|
|
@@ -58,18 +58,96 @@ module RubyLLM
|
|
|
58
58
|
end
|
|
59
59
|
|
|
60
60
|
def plain_text_content(content_value)
|
|
61
|
+
return action_text_plain_text(content_value) if action_text_content?(content_value)
|
|
61
62
|
return content_value.to_plain_text if content_value.respond_to?(:to_plain_text)
|
|
62
63
|
|
|
63
64
|
content_value
|
|
64
65
|
end
|
|
65
66
|
|
|
66
67
|
def action_text_attachment_sources(content_value)
|
|
67
|
-
return [] unless
|
|
68
|
+
return [] unless action_text_content?(content_value)
|
|
68
69
|
|
|
70
|
+
action_text_attachables(content_value).flat_map do |attachable|
|
|
71
|
+
action_text_attachable_sources(attachable)
|
|
72
|
+
end.compact
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def action_text_content?(content_value)
|
|
76
|
+
content_value.respond_to?(:body) && action_text_body?(content_value.body)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def preload_action_text_embeds(messages)
|
|
80
|
+
return unless defined?(ActionText::Attachment)
|
|
81
|
+
|
|
82
|
+
rich_texts = messages.filter_map do |message|
|
|
83
|
+
rich_text = message.rich_text_content
|
|
84
|
+
body = rich_text&.body
|
|
85
|
+
rich_text if action_text_body?(body) && action_text_embeds?(body)
|
|
86
|
+
end
|
|
87
|
+
return if rich_texts.empty?
|
|
88
|
+
|
|
89
|
+
::ActiveRecord::Associations::Preloader.new(
|
|
90
|
+
records: rich_texts,
|
|
91
|
+
associations: { embeds_attachments: :blob }
|
|
92
|
+
).call
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def action_text_embeds?(body)
|
|
96
|
+
body.fragment.find_all(ActionText::Attachment.tag_name).any? { |node| action_text_blob_id(node) }
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def action_text_body?(body)
|
|
100
|
+
defined?(ActionText::Attachment) && body.respond_to?(:fragment) && body.respond_to?(:attachables) &&
|
|
101
|
+
body.respond_to?(:sanitize_content_attachment)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def action_text_plain_text(content_value)
|
|
105
|
+
body = content_value.body
|
|
106
|
+
preloaded_blobs = action_text_preloaded_blobs(content_value).index_by { |blob| blob.id.to_s }
|
|
107
|
+
rendered = body.fragment.replace(ActionText::Attachment.tag_name) do |node|
|
|
108
|
+
if node.key?('content')
|
|
109
|
+
sanitized_content = body.sanitize_content_attachment(node.remove_attribute('content').to_s)
|
|
110
|
+
node['content'] = sanitized_content if sanitized_content.present?
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
attachable = action_text_attachable_for_node(node, preloaded_blobs)
|
|
114
|
+
ActionText::Attachment.from_node(node, attachable).to_plain_text
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
ActionText::Content.new(rendered, canonicalize: false).fragment.to_plain_text
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def action_text_attachables(content_value)
|
|
69
121
|
body = content_value.body
|
|
70
|
-
|
|
122
|
+
preloaded_blobs = action_text_preloaded_blobs(content_value).index_by { |blob| blob.id.to_s }
|
|
123
|
+
body.fragment.find_all(ActionText::Attachment.tag_name).map do |node|
|
|
124
|
+
action_text_attachable_for_node(node, preloaded_blobs)
|
|
125
|
+
end
|
|
126
|
+
end
|
|
71
127
|
|
|
72
|
-
|
|
128
|
+
def action_text_attachable_for_node(node, preloaded_blobs)
|
|
129
|
+
preloaded_blobs[action_text_blob_id(node)] || ActionText::Attachable.from_node(node)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def action_text_blob_id(node)
|
|
133
|
+
sgid = node['sgid']
|
|
134
|
+
gid = SignedGlobalID.parse(sgid, for: ActionText::Attachable::LOCATOR_NAME) if sgid
|
|
135
|
+
return unless gid && gid.app == GlobalID.app && gid.model_name == ActiveStorage::Blob.name
|
|
136
|
+
|
|
137
|
+
gid.model_id.to_s
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def action_text_preloaded_blobs(content_value)
|
|
141
|
+
return [] unless content_value.class.respond_to?(:reflect_on_association) &&
|
|
142
|
+
content_value.class.reflect_on_association(:embeds_attachments)
|
|
143
|
+
|
|
144
|
+
association = content_value.association(:embeds_attachments)
|
|
145
|
+
return [] unless association.loaded?
|
|
146
|
+
|
|
147
|
+
content_value.embeds_attachments.filter_map do |attachment|
|
|
148
|
+
blob_association = attachment.association(:blob)
|
|
149
|
+
blob_association.target if blob_association.loaded?
|
|
150
|
+
end
|
|
73
151
|
end
|
|
74
152
|
|
|
75
153
|
def action_text_attachable_sources(attachable)
|
|
@@ -212,7 +212,7 @@ module RubyLLM
|
|
|
212
212
|
# RubyLLM::Chat. Each behaves exactly as documented on RubyLLM::Chat,
|
|
213
213
|
# then returns the record so calls chain.
|
|
214
214
|
CHAINABLE_CHAT_DELEGATES = %i[
|
|
215
|
-
with_tools with_tool_options
|
|
215
|
+
with_tools with_tool_options with_provider_tools with_fallbacks with_temperature
|
|
216
216
|
with_max_output_tokens with_thinking with_citations with_caching
|
|
217
217
|
with_end_user with_compaction
|
|
218
218
|
with_provider_options with_headers with_schema
|
|
@@ -233,10 +233,10 @@ module RubyLLM
|
|
|
233
233
|
# Applies Chat#with_tool_options and returns this record.
|
|
234
234
|
|
|
235
235
|
##
|
|
236
|
-
# :method:
|
|
237
|
-
# :call-seq:
|
|
236
|
+
# :method: with_provider_tools
|
|
237
|
+
# :call-seq: with_provider_tools(*tools, **tools_with_options)
|
|
238
238
|
#
|
|
239
|
-
# Applies Chat#
|
|
239
|
+
# Applies Chat#with_provider_tools and returns this record.
|
|
240
240
|
|
|
241
241
|
##
|
|
242
242
|
# :method: with_fallbacks
|
|
@@ -407,10 +407,10 @@ module RubyLLM
|
|
|
407
407
|
# Delegates to Chat#schema. See that method for arguments and return values.
|
|
408
408
|
|
|
409
409
|
##
|
|
410
|
-
# :method:
|
|
411
|
-
# :call-seq:
|
|
410
|
+
# :method: provider_tools
|
|
411
|
+
# :call-seq: provider_tools
|
|
412
412
|
#
|
|
413
|
-
# Delegates to Chat#
|
|
413
|
+
# Delegates to Chat#provider_tools. See that method for arguments and return values.
|
|
414
414
|
|
|
415
415
|
##
|
|
416
416
|
# :method: temperature
|
|
@@ -449,7 +449,7 @@ module RubyLLM
|
|
|
449
449
|
|
|
450
450
|
PASSTHROUGH_CHAT_DELEGATES = %i[
|
|
451
451
|
caching citations compaction concurrency end_user fallbacks headers max_output_tokens provider_options
|
|
452
|
-
schema
|
|
452
|
+
schema provider_tools temperature thinking tool_options tools
|
|
453
453
|
add_completion count_tokens each render
|
|
454
454
|
].freeze
|
|
455
455
|
|
|
@@ -778,8 +778,11 @@ module RubyLLM
|
|
|
778
778
|
|
|
779
779
|
associations = %i[ruby_llm_tool_calls ruby_llm_parent_tool_call ruby_llm_usages]
|
|
780
780
|
associations << { attachments_attachments: :blob } if attachment_association?(assoc.klass)
|
|
781
|
+
rich_text_reflection = assoc.klass.reflect_on_association(:rich_text_content)
|
|
782
|
+
associations << :rich_text_content if rich_text_reflection
|
|
781
783
|
|
|
782
784
|
::ActiveRecord::Associations::Preloader.new(records: messages, associations: associations).call
|
|
785
|
+
preload_action_text_embeds(messages) if rich_text_reflection
|
|
783
786
|
messages
|
|
784
787
|
end
|
|
785
788
|
|
data/lib/ruby_llm/agent.rb
CHANGED
|
@@ -44,7 +44,7 @@ module RubyLLM
|
|
|
44
44
|
DUPED_INHERITED_CONFIG = {
|
|
45
45
|
:@chat_kwargs => {},
|
|
46
46
|
:@tools => [],
|
|
47
|
-
:@
|
|
47
|
+
:@provider_tools => [],
|
|
48
48
|
:@tool_options => {},
|
|
49
49
|
:@caching => nil,
|
|
50
50
|
:@compaction => nil,
|
|
@@ -66,7 +66,7 @@ module RubyLLM
|
|
|
66
66
|
|
|
67
67
|
# Chat methods that return the wrapped chat so calls can be chained there.
|
|
68
68
|
CHAINABLE_CHAT_DELEGATES = %i[
|
|
69
|
-
with_instructions with_tools
|
|
69
|
+
with_instructions with_tools with_provider_tools with_tool_options with_model
|
|
70
70
|
with_temperature with_max_output_tokens with_thinking with_citations
|
|
71
71
|
with_end_user with_compaction with_caching with_context with_provider_options
|
|
72
72
|
with_headers with_schema with_fallbacks
|
|
@@ -77,7 +77,7 @@ module RubyLLM
|
|
|
77
77
|
|
|
78
78
|
# Chat values and operations whose return values pass through unchanged.
|
|
79
79
|
PASSTHROUGH_CHAT_DELEGATES = %i[
|
|
80
|
-
model provider messages tools
|
|
80
|
+
model provider messages tools provider_tools tool_options provider_options headers schema concurrency
|
|
81
81
|
caching citations compaction context end_user fallbacks thinking temperature max_output_tokens
|
|
82
82
|
each complete? cancelled? awaiting_approval? pending_approvals
|
|
83
83
|
add_message add_completion tokens cost render
|
|
@@ -150,17 +150,17 @@ module RubyLLM
|
|
|
150
150
|
end
|
|
151
151
|
|
|
152
152
|
# Enables provider-executed tools for chats this agent builds, applied
|
|
153
|
-
# via Chat#
|
|
153
|
+
# via Chat#with_provider_tools. Accepts the same aliases, options, and
|
|
154
154
|
# raw Hashes; a block defers evaluation until the chat is built.
|
|
155
155
|
# Called with no arguments, returns the declared entries.
|
|
156
156
|
#
|
|
157
|
-
#
|
|
158
|
-
#
|
|
157
|
+
# provider_tools :web_search
|
|
158
|
+
# provider_tools web_search: { allowed_domains: ["ruby-lang.org"] }
|
|
159
159
|
#
|
|
160
|
-
def
|
|
161
|
-
return @
|
|
160
|
+
def provider_tools(*tools, **tools_with_options, &block)
|
|
161
|
+
return @provider_tools || [] if tools.empty? && tools_with_options.empty? && !block_given?
|
|
162
162
|
|
|
163
|
-
@
|
|
163
|
+
@provider_tools = block_given? ? block : RubyLLM::Tools::ProviderTools.normalize(tools, tools_with_options)
|
|
164
164
|
end
|
|
165
165
|
|
|
166
166
|
# Adds system instructions for chats this agent builds. Accepts a string,
|
|
@@ -667,8 +667,8 @@ module RubyLLM
|
|
|
667
667
|
options = evaluate(tool_options, runtime)
|
|
668
668
|
chat.with_tool_options(**options) if options && !options.empty?
|
|
669
669
|
|
|
670
|
-
|
|
671
|
-
chat.
|
|
670
|
+
provider_tools_to_apply = Array(evaluate(provider_tools, runtime)).compact
|
|
671
|
+
chat.with_provider_tools(*provider_tools_to_apply) if provider_tools_to_apply.any?
|
|
672
672
|
end
|
|
673
673
|
|
|
674
674
|
def apply_passthrough_options(chat)
|
|
@@ -873,10 +873,10 @@ module RubyLLM
|
|
|
873
873
|
# Delegates to Chat#with_tools. See that method for arguments and return values.
|
|
874
874
|
|
|
875
875
|
##
|
|
876
|
-
# :method:
|
|
877
|
-
# :call-seq:
|
|
876
|
+
# :method: with_provider_tools
|
|
877
|
+
# :call-seq: with_provider_tools(*tools, **tools_with_options)
|
|
878
878
|
#
|
|
879
|
-
# Delegates to Chat#
|
|
879
|
+
# Delegates to Chat#with_provider_tools. See that method for arguments and return values.
|
|
880
880
|
|
|
881
881
|
##
|
|
882
882
|
# :method: with_tool_options
|
|
@@ -1053,10 +1053,10 @@ module RubyLLM
|
|
|
1053
1053
|
# Delegates to Chat#tools. See that method for arguments and return values.
|
|
1054
1054
|
|
|
1055
1055
|
##
|
|
1056
|
-
# :method:
|
|
1057
|
-
# :call-seq:
|
|
1056
|
+
# :method: provider_tools
|
|
1057
|
+
# :call-seq: provider_tools
|
|
1058
1058
|
#
|
|
1059
|
-
# Delegates to Chat#
|
|
1059
|
+
# Delegates to Chat#provider_tools. See that method for arguments and return values.
|
|
1060
1060
|
|
|
1061
1061
|
##
|
|
1062
1062
|
# :method: tool_options
|
data/lib/ruby_llm/chat.rb
CHANGED
|
@@ -42,9 +42,9 @@ module RubyLLM
|
|
|
42
42
|
# The registered tools, as a Hash of tool name Symbols to Tool instances.
|
|
43
43
|
attr_reader :tools
|
|
44
44
|
|
|
45
|
-
# The
|
|
45
|
+
# The provider tools enabled with #with_provider_tools, as an array of
|
|
46
46
|
# normalized entry Hashes.
|
|
47
|
-
attr_reader :
|
|
47
|
+
attr_reader :provider_tools
|
|
48
48
|
|
|
49
49
|
# Extra request options set with #with_provider_options, expressed in
|
|
50
50
|
# the provider's request vocabulary.
|
|
@@ -119,7 +119,7 @@ module RubyLLM
|
|
|
119
119
|
@messages = []
|
|
120
120
|
@usage_entries = []
|
|
121
121
|
@tools = {}
|
|
122
|
-
@
|
|
122
|
+
@provider_tools = []
|
|
123
123
|
@tool_prefs = { choice: nil, calls: nil }
|
|
124
124
|
@concurrency = normalize_tool_concurrency(@config.tool_concurrency)
|
|
125
125
|
@provider_options = {}
|
|
@@ -354,10 +354,10 @@ module RubyLLM
|
|
|
354
354
|
# no alias for yet work without a gem update. Entries add to any tools
|
|
355
355
|
# enabled earlier; pass +nil+ to clear them all. Returns +self+.
|
|
356
356
|
#
|
|
357
|
-
# chat.
|
|
358
|
-
# chat.
|
|
359
|
-
# chat.
|
|
360
|
-
# chat.
|
|
357
|
+
# chat.with_provider_tools(:web_search)
|
|
358
|
+
# chat.with_provider_tools(:web_search, :code_execution)
|
|
359
|
+
# chat.with_provider_tools(web_search: { allowed_domains: ["ruby-lang.org"] })
|
|
360
|
+
# chat.with_provider_tools({ type: "web_search_20260318", name: "web_search" })
|
|
361
361
|
#
|
|
362
362
|
# The tool steps the model ran come back on
|
|
363
363
|
# Message#server_tool_calls, citations from search tools on
|
|
@@ -365,14 +365,14 @@ module RubyLLM
|
|
|
365
365
|
# <tt>message.tokens.server_tool_use</tt>.
|
|
366
366
|
#
|
|
367
367
|
# Raises UnsupportedServerToolError at request time when the provider
|
|
368
|
-
# has no
|
|
369
|
-
def
|
|
368
|
+
# has no provider-tool support or does not define a requested alias.
|
|
369
|
+
def with_provider_tools(*tools, **tools_with_options)
|
|
370
370
|
if tools == [nil] && tools_with_options.empty?
|
|
371
|
-
@
|
|
371
|
+
@provider_tools = []
|
|
372
372
|
return self
|
|
373
373
|
end
|
|
374
374
|
|
|
375
|
-
@
|
|
375
|
+
@provider_tools += RubyLLM::Tools::ProviderTools.normalize(tools, tools_with_options)
|
|
376
376
|
self
|
|
377
377
|
end
|
|
378
378
|
|
|
@@ -742,7 +742,7 @@ module RubyLLM
|
|
|
742
742
|
# chat.with_instructions("Be terse.").with_tools(Weather)
|
|
743
743
|
# chat.count_tokens("What's the weather in Berlin?")
|
|
744
744
|
#
|
|
745
|
-
#
|
|
745
|
+
# Provider tools, provider_options, compaction, and before_request hooks
|
|
746
746
|
# are not included. Raises Error when the provider has no token counting
|
|
747
747
|
# endpoint.
|
|
748
748
|
def count_tokens(message = nil)
|
|
@@ -849,7 +849,7 @@ module RubyLLM
|
|
|
849
849
|
@provider.render(
|
|
850
850
|
preprocessed_messages,
|
|
851
851
|
tools: @tools,
|
|
852
|
-
|
|
852
|
+
provider_tools: @provider_tools,
|
|
853
853
|
tool_prefs: @tool_prefs,
|
|
854
854
|
temperature: @temperature,
|
|
855
855
|
max_output_tokens: @max_output_tokens,
|
|
@@ -1009,7 +1009,7 @@ module RubyLLM
|
|
|
1009
1009
|
input_messages: messages.dup,
|
|
1010
1010
|
message_count: messages.size,
|
|
1011
1011
|
tools: tools.keys,
|
|
1012
|
-
|
|
1012
|
+
provider_tools: provider_tools,
|
|
1013
1013
|
tool_choice: tool_prefs[:choice],
|
|
1014
1014
|
tool_call_limit: tool_prefs[:calls],
|
|
1015
1015
|
temperature: @temperature,
|
|
@@ -1147,7 +1147,7 @@ module RubyLLM
|
|
|
1147
1147
|
@provider.complete(
|
|
1148
1148
|
preprocessed_messages,
|
|
1149
1149
|
tools: @tools,
|
|
1150
|
-
|
|
1150
|
+
provider_tools: @provider_tools,
|
|
1151
1151
|
tool_prefs: @tool_prefs,
|
|
1152
1152
|
temperature: @temperature,
|
|
1153
1153
|
max_output_tokens: @max_output_tokens,
|
data/lib/ruby_llm/error.rb
CHANGED
|
@@ -83,7 +83,7 @@ module RubyLLM
|
|
|
83
83
|
end
|
|
84
84
|
end
|
|
85
85
|
|
|
86
|
-
# Raised when Chat#
|
|
86
|
+
# Raised when Chat#with_provider_tools is used with a provider RubyLLM has
|
|
87
87
|
# no server-tool support for, or with an alias the provider's protocol
|
|
88
88
|
# does not define.
|
|
89
89
|
class UnsupportedServerToolError < Error; end
|
data/lib/ruby_llm/message.rb
CHANGED
|
@@ -64,7 +64,7 @@ module RubyLLM
|
|
|
64
64
|
|
|
65
65
|
# The provider-executed tool steps in this response, as an array of
|
|
66
66
|
# ServerToolCall objects. Empty unless the chat enabled tools with
|
|
67
|
-
# Chat#
|
|
67
|
+
# Chat#with_provider_tools and the model used one.
|
|
68
68
|
attr_reader :server_tool_calls
|
|
69
69
|
|
|
70
70
|
# The provider-shaped content blocks of this assistant message, kept
|
|
@@ -129,6 +129,14 @@ module RubyLLM
|
|
|
129
129
|
dup.tap { |message| message.instance_variable_set(:@attachments, wrapped) }
|
|
130
130
|
end
|
|
131
131
|
|
|
132
|
+
def without_thinking # :nodoc:
|
|
133
|
+
dup.tap do |message|
|
|
134
|
+
message.instance_variable_set(:@thinking, nil)
|
|
135
|
+
message.instance_variable_set(:@raw_reasoning, nil)
|
|
136
|
+
message.instance_variable_set(:@tool_calls, tool_calls_without_thought_signatures)
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
132
140
|
# Returns +true+ if the assistant requested one or more tool calls,
|
|
133
141
|
# +false+ otherwise.
|
|
134
142
|
def tool_call?
|
|
@@ -276,6 +284,12 @@ module RubyLLM
|
|
|
276
284
|
end
|
|
277
285
|
end
|
|
278
286
|
|
|
287
|
+
def tool_calls_without_thought_signatures
|
|
288
|
+
tool_calls&.transform_values do |call|
|
|
289
|
+
call.dup.tap { |copy| copy.thought_signature = nil }
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
|
|
279
293
|
def coerce_thinking(thinking, signature)
|
|
280
294
|
case thinking
|
|
281
295
|
when nil, Thinking then thinking
|
data/lib/ruby_llm/protocol.rb
CHANGED
|
@@ -96,13 +96,13 @@ module RubyLLM
|
|
|
96
96
|
|
|
97
97
|
def complete(messages, tools:, temperature:, provider_options: {}, headers: {}, schema: nil, thinking: nil,
|
|
98
98
|
max_output_tokens: nil, citations: false, caching: nil, tool_prefs: nil, before_request: [],
|
|
99
|
-
usage_recorder: nil,
|
|
100
|
-
resolution =
|
|
99
|
+
usage_recorder: nil, provider_tools: [], compaction: nil, end_user: nil, &)
|
|
100
|
+
resolution = resolve_provider_tools_for_request(provider_tools)
|
|
101
101
|
headers = resolution.headers.merge(headers) if resolution
|
|
102
102
|
headers = apply_compaction_headers(headers, compaction) if compaction
|
|
103
103
|
payload = render(
|
|
104
104
|
messages, tools:, tool_prefs:, temperature:, max_output_tokens:, provider_options:, schema:, thinking:,
|
|
105
|
-
citations:, caching:, compaction:, end_user:, before_request:,
|
|
105
|
+
citations:, caching:, compaction:, end_user:, before_request:, provider_tools:,
|
|
106
106
|
stream: block_given?
|
|
107
107
|
)
|
|
108
108
|
|
|
@@ -122,7 +122,7 @@ module RubyLLM
|
|
|
122
122
|
|
|
123
123
|
def render(messages, tools:, temperature:, provider_options: {}, schema: nil, thinking: nil,
|
|
124
124
|
max_output_tokens: nil, citations: false, caching: nil, tool_prefs: nil, before_request: [],
|
|
125
|
-
stream: false,
|
|
125
|
+
stream: false, provider_tools: [], compaction: nil, end_user: nil)
|
|
126
126
|
payload = render_payload(
|
|
127
127
|
messages,
|
|
128
128
|
tools: tools,
|
|
@@ -139,7 +139,7 @@ module RubyLLM
|
|
|
139
139
|
payload = apply_end_user(payload, end_user) if end_user
|
|
140
140
|
payload = apply_compaction(payload, compaction) if compaction
|
|
141
141
|
payload = Support::Utils.deep_merge(payload, provider_options)
|
|
142
|
-
payload =
|
|
142
|
+
payload = apply_provider_tools(payload, provider_tools)
|
|
143
143
|
apply_before_request_hooks(payload, before_request)
|
|
144
144
|
rescue NotImplementedError
|
|
145
145
|
raise Error, "#{@provider.name} doesn't support chat"
|
|
@@ -188,8 +188,8 @@ module RubyLLM
|
|
|
188
188
|
end
|
|
189
189
|
|
|
190
190
|
# The alias table mapping portable server tool names to this protocol's
|
|
191
|
-
# wire format. Protocols with
|
|
192
|
-
# +nil+ means the protocol has no
|
|
191
|
+
# wire format. Protocols with provider-tool support override this;
|
|
192
|
+
# +nil+ means the protocol has no provider-tool support at all.
|
|
193
193
|
def server_tool_aliases
|
|
194
194
|
nil
|
|
195
195
|
end
|
|
@@ -471,6 +471,7 @@ module RubyLLM
|
|
|
471
471
|
end
|
|
472
472
|
|
|
473
473
|
def preprocess_message(message)
|
|
474
|
+
return message.without_thinking if foreign_thinking?(message)
|
|
474
475
|
return message unless auto_upload_large_files?
|
|
475
476
|
return message unless message.role == :user
|
|
476
477
|
return message if message.attachments.empty?
|
|
@@ -483,21 +484,43 @@ module RubyLLM
|
|
|
483
484
|
|
|
484
485
|
private
|
|
485
486
|
|
|
486
|
-
|
|
487
|
+
# A thinking signature is opaque to every provider but the one that
|
|
488
|
+
# issued it, so a message another provider produced replays without
|
|
489
|
+
# its thinking. A message with no known producer replays as it is.
|
|
490
|
+
def foreign_thinking?(message)
|
|
491
|
+
return false unless message.role == :assistant && carries_thinking?(message)
|
|
492
|
+
|
|
493
|
+
producer = producer_slug(message)
|
|
494
|
+
!producer.nil? && producer != @provider.slug
|
|
495
|
+
end
|
|
496
|
+
|
|
497
|
+
def carries_thinking?(message)
|
|
498
|
+
return true if message.thinking || message.raw_reasoning
|
|
499
|
+
|
|
500
|
+
message.tool_call? && message.tool_calls.each_value.any?(&:thought_signature)
|
|
501
|
+
end
|
|
502
|
+
|
|
503
|
+
# Only a usage entry names the producer: a model id alone can belong
|
|
504
|
+
# to several providers.
|
|
505
|
+
def producer_slug(message)
|
|
506
|
+
message.ruby_llm_usage_entries.reverse.find(&:succeeded?)&.provider
|
|
507
|
+
end
|
|
508
|
+
|
|
509
|
+
def resolve_provider_tools_for_request(entries)
|
|
487
510
|
return nil if entries.nil? || entries.empty?
|
|
488
511
|
|
|
489
512
|
aliases = server_tool_aliases
|
|
490
513
|
unless aliases
|
|
491
514
|
raise UnsupportedServerToolError,
|
|
492
|
-
"#{@provider.name} has no
|
|
515
|
+
"#{@provider.name} has no provider-tool support through RubyLLM yet. " \
|
|
493
516
|
'Request options in the provider vocabulary can be set with with_provider_options.'
|
|
494
517
|
end
|
|
495
518
|
|
|
496
|
-
RubyLLM::Tools::
|
|
519
|
+
RubyLLM::Tools::ProviderTools.resolve(entries, aliases: aliases, owner: @provider.name)
|
|
497
520
|
end
|
|
498
521
|
|
|
499
|
-
def
|
|
500
|
-
resolution =
|
|
522
|
+
def apply_provider_tools(payload, entries)
|
|
523
|
+
resolution = resolve_provider_tools_for_request(entries)
|
|
501
524
|
return payload unless resolution
|
|
502
525
|
|
|
503
526
|
payload = Support::Utils.deep_merge(payload, resolution.payload) unless resolution.payload.empty?
|
|
@@ -505,7 +528,7 @@ module RubyLLM
|
|
|
505
528
|
payload
|
|
506
529
|
end
|
|
507
530
|
|
|
508
|
-
#
|
|
531
|
+
# Provider tools join function tools in the payload's tools array. The
|
|
509
532
|
# entry shape comes from the alias table or the caller's raw Hash.
|
|
510
533
|
def merge_server_tool_entries(payload, entries)
|
|
511
534
|
payload[:tools] = Array(payload[:tools]) + entries
|
|
@@ -21,9 +21,9 @@ module RubyLLM
|
|
|
21
21
|
|
|
22
22
|
def server_tool_aliases = SERVER_TOOL_ALIASES
|
|
23
23
|
|
|
24
|
-
def create_research_job(prompt, agent:, with: nil,
|
|
24
|
+
def create_research_job(prompt, agent:, with: nil, provider_tools: nil, provider_options: {})
|
|
25
25
|
validate_research_agent(agent)
|
|
26
|
-
payload = render_research_payload(prompt, agent:, with:,
|
|
26
|
+
payload = render_research_payload(prompt, agent:, with:, provider_tools:, provider_options:)
|
|
27
27
|
response = @connection.post(research_url, payload, idempotent: false)
|
|
28
28
|
parse_research_job(response, agent:)
|
|
29
29
|
end
|
|
@@ -71,20 +71,20 @@ module RubyLLM
|
|
|
71
71
|
raise ArgumentError, 'Vertex AI research requires the supported Deep Research agent ID'
|
|
72
72
|
end
|
|
73
73
|
|
|
74
|
-
def render_research_payload(prompt, agent:, with:,
|
|
74
|
+
def render_research_payload(prompt, agent:, with:, provider_tools:, provider_options:)
|
|
75
75
|
raise ArgumentError, 'Research requires a nonempty prompt' unless prompt.is_a?(String) && !prompt.empty?
|
|
76
76
|
|
|
77
77
|
attachments = Attachment.wrap(with, config: @config)
|
|
78
78
|
validate_research_attachments(attachments)
|
|
79
79
|
payload = { agent:, input: render_interaction_content(prompt, attachments), background: true, stream: false }
|
|
80
|
-
payload[:tools] = [] unless
|
|
80
|
+
payload[:tools] = [] unless provider_tools.nil?
|
|
81
81
|
options = render_research_options(provider_options)
|
|
82
|
-
entries = if
|
|
83
|
-
RubyLLM::Tools::
|
|
82
|
+
entries = if provider_tools.is_a?(Hash)
|
|
83
|
+
RubyLLM::Tools::ProviderTools.normalize([], provider_tools)
|
|
84
84
|
else
|
|
85
|
-
RubyLLM::Tools::
|
|
85
|
+
RubyLLM::Tools::ProviderTools.normalize(Array(provider_tools), {})
|
|
86
86
|
end
|
|
87
|
-
|
|
87
|
+
apply_provider_tools(payload, entries).merge(options)
|
|
88
88
|
end
|
|
89
89
|
|
|
90
90
|
def validate_research_attachments(attachments)
|
data/lib/ruby_llm/provider.rb
CHANGED
|
@@ -130,13 +130,13 @@ module RubyLLM
|
|
|
130
130
|
|
|
131
131
|
def complete(messages, tools:, temperature:, model:, provider_options: {}, headers: {}, schema: nil, # :nodoc:
|
|
132
132
|
max_output_tokens: nil, thinking: nil, citations: false, caching: nil, tool_prefs: nil,
|
|
133
|
-
protocol: nil, before_request: [], usage_recorder: nil,
|
|
133
|
+
protocol: nil, before_request: [], usage_recorder: nil, provider_tools: [],
|
|
134
134
|
compaction: nil, end_user: nil, &)
|
|
135
135
|
protocol_class = resolve_protocol(protocol, model, tools:, schema:, thinking:, tool_prefs:, citations:)
|
|
136
136
|
protocol_class.new(self, model).complete(
|
|
137
137
|
messages,
|
|
138
138
|
tools: tools,
|
|
139
|
-
|
|
139
|
+
provider_tools: provider_tools,
|
|
140
140
|
tool_prefs: tool_prefs,
|
|
141
141
|
temperature: temperature,
|
|
142
142
|
max_output_tokens: max_output_tokens,
|
|
@@ -166,12 +166,12 @@ module RubyLLM
|
|
|
166
166
|
|
|
167
167
|
def render(messages, tools:, temperature:, model:, provider_options: {}, schema: nil, thinking: nil, # :nodoc:
|
|
168
168
|
max_output_tokens: nil, citations: false, caching: nil, tool_prefs: nil, protocol: nil,
|
|
169
|
-
before_request: [],
|
|
169
|
+
before_request: [], provider_tools: [], compaction: nil, end_user: nil)
|
|
170
170
|
protocol_class = resolve_protocol(protocol, model, tools:, schema:, thinking:, tool_prefs:, citations:)
|
|
171
171
|
protocol_class.new(self, model).render(
|
|
172
172
|
messages,
|
|
173
173
|
tools: tools,
|
|
174
|
-
|
|
174
|
+
provider_tools: provider_tools,
|
|
175
175
|
tool_prefs: tool_prefs,
|
|
176
176
|
temperature: temperature,
|
|
177
177
|
max_output_tokens: max_output_tokens,
|
|
@@ -44,6 +44,10 @@ module RubyLLM
|
|
|
44
44
|
payload = super
|
|
45
45
|
payload.delete(:reasoning_effort)
|
|
46
46
|
strip_schema_strict(payload)
|
|
47
|
+
if tool_prefs&.dig(:choice) == :none
|
|
48
|
+
payload.delete(:tools)
|
|
49
|
+
payload.delete(:parallel_tool_calls)
|
|
50
|
+
end
|
|
47
51
|
|
|
48
52
|
reasoning = build_reasoning(thinking)
|
|
49
53
|
payload[:reasoning] = reasoning if reasoning
|
|
@@ -67,15 +67,15 @@ module RubyLLM
|
|
|
67
67
|
|
|
68
68
|
# Submits one research task without waiting. +provider:+ and +agent:+
|
|
69
69
|
# are required; +with:+ attaches documents or images where supported.
|
|
70
|
-
# +
|
|
71
|
-
# and their options, as on Chat#
|
|
72
|
-
def self.research_later(prompt, provider:, agent:, with: nil,
|
|
70
|
+
# +provider_tools:+ accepts an array of aliases or a Hash of aliases
|
|
71
|
+
# and their options, as on Chat#with_provider_tools.
|
|
72
|
+
def self.research_later(prompt, provider:, agent:, with: nil, provider_tools: nil,
|
|
73
73
|
context: nil, provider_options: {}, metadata: nil)
|
|
74
74
|
config = context&.config || RubyLLM.config
|
|
75
75
|
instance = Provider.resolve!(provider).new(config)
|
|
76
76
|
payload = { provider: instance.slug, agent:, prompt:, metadata: }
|
|
77
77
|
RubyLLM.instrument('research_job.ruby_llm', payload, config:) do |event|
|
|
78
|
-
job = instance.research_later(prompt, agent:, with:,
|
|
78
|
+
job = instance.research_later(prompt, agent:, with:, provider_tools:, provider_options:)
|
|
79
79
|
event[:job_id] = job.id
|
|
80
80
|
event[:status] = job.status
|
|
81
81
|
job
|
|
@@ -5,9 +5,9 @@ module RubyLLM
|
|
|
5
5
|
# response: a web search the model ran, a code execution, or the results
|
|
6
6
|
# block a provider returned for one. They appear on
|
|
7
7
|
# Message#server_tool_calls when a chat enables tools with
|
|
8
|
-
# Chat#
|
|
8
|
+
# Chat#with_provider_tools.
|
|
9
9
|
#
|
|
10
|
-
# response = chat.
|
|
10
|
+
# response = chat.with_provider_tools(:web_search).ask "What changed in Ruby 3.5?"
|
|
11
11
|
# response.server_tool_calls.map(&:type) # => ["server_tool_use", "web_search_tool_result"]
|
|
12
12
|
#
|
|
13
13
|
# RubyLLM does not model each tool's result schema. #raw always holds the
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
module RubyLLM
|
|
4
4
|
module Tools # :nodoc:
|
|
5
|
-
# Normalizes and resolves the
|
|
6
|
-
# Chat#
|
|
5
|
+
# Normalizes and resolves the provider tools a chat enabled with
|
|
6
|
+
# Chat#with_provider_tools. Protocols declare an alias table mapping portable
|
|
7
7
|
# names such as +:web_search+ to their wire format; raw Hashes pass through
|
|
8
8
|
# verbatim so new provider tools work without a gem update. :nodoc:
|
|
9
|
-
module
|
|
10
|
-
# The result of resolving a chat's
|
|
9
|
+
module ProviderTools # :nodoc: all
|
|
10
|
+
# The result of resolving a chat's provider tools against a protocol's
|
|
11
11
|
# alias table: tool entries for the payload's tools slot, extra payload
|
|
12
12
|
# fields to merge, and request headers to add.
|
|
13
13
|
class Resolution
|
|
@@ -61,7 +61,7 @@ module RubyLLM
|
|
|
61
61
|
|
|
62
62
|
module_function
|
|
63
63
|
|
|
64
|
-
# Normalizes
|
|
64
|
+
# Normalizes with_provider_tools arguments into entry hashes:
|
|
65
65
|
# {name:, options:} for aliases and {raw:} for verbatim tool specs.
|
|
66
66
|
def normalize(tools, tools_with_options)
|
|
67
67
|
entries = tools.compact.map { |tool| normalize_positional(tool) }
|
|
@@ -74,13 +74,13 @@ module RubyLLM
|
|
|
74
74
|
when Hash then normalized_entry?(tool) ? tool : { raw: tool }
|
|
75
75
|
else
|
|
76
76
|
raise ArgumentError,
|
|
77
|
-
"
|
|
77
|
+
"Provider tools must be Symbols or Hashes, got #{tool.class}. " \
|
|
78
78
|
'Pass :web_search for a portable alias or the provider\'s tool definition as a Hash.'
|
|
79
79
|
end
|
|
80
80
|
end
|
|
81
81
|
|
|
82
82
|
# Entries that already went through normalize (an Agent's declared
|
|
83
|
-
#
|
|
83
|
+
# provider_tools, for example) pass through unchanged.
|
|
84
84
|
def normalized_entry?(hash)
|
|
85
85
|
hash.key?(:raw) ? hash.size == 1 : hash.size == 2 && hash.key?(:name) && hash.key?(:options)
|
|
86
86
|
end
|
data/lib/ruby_llm/version.rb
CHANGED
data/lib/ruby_llm.rb
CHANGED
|
@@ -75,7 +75,7 @@ loader.setup
|
|
|
75
75
|
#
|
|
76
76
|
# Subclass Tool and implement +execute+ to give the model an application
|
|
77
77
|
# action. Tool.requires_approval pauses execution for a human decision;
|
|
78
|
-
# Chat#approve and Chat#deny record it. Chat#
|
|
78
|
+
# Chat#approve and Chat#deny record it. Chat#with_provider_tools enables
|
|
79
79
|
# provider-executed tools such as web search, code execution, and remote MCP.
|
|
80
80
|
# Their calls appear as ServerToolCall values, with Citation values for sources.
|
|
81
81
|
#
|
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: 2.0.0
|
|
4
|
+
version: 2.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Carmine Paolino
|
|
@@ -589,7 +589,7 @@ files:
|
|
|
589
589
|
- lib/ruby_llm/tokens.rb
|
|
590
590
|
- lib/ruby_llm/tool.rb
|
|
591
591
|
- lib/ruby_llm/tool_call.rb
|
|
592
|
-
- lib/ruby_llm/tools/
|
|
592
|
+
- lib/ruby_llm/tools/provider_tools.rb
|
|
593
593
|
- lib/ruby_llm/transcription.rb
|
|
594
594
|
- lib/ruby_llm/transcription/wav_audio.rb
|
|
595
595
|
- lib/ruby_llm/transcription_chunk.rb
|
|
@@ -617,7 +617,7 @@ metadata:
|
|
|
617
617
|
funding_uri: https://github.com/sponsors/crmne
|
|
618
618
|
rubygems_mfa_required: 'true'
|
|
619
619
|
post_install_message: |
|
|
620
|
-
RubyLLM 2.0.0
|
|
620
|
+
RubyLLM 2.0.0
|
|
621
621
|
|
|
622
622
|
2.0 renames several APIs and changes what message content returns. Coming
|
|
623
623
|
from 1.x? Read the upgrade guide before you boot:
|