ruby_llm 0.1.0.pre12 → 0.1.0.pre13
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/.rubocop.yml +4 -1
- data/README.md +13 -11
- data/lib/ruby_llm/chat.rb +15 -5
- data/lib/ruby_llm/message.rb +1 -1
- data/lib/ruby_llm/provider.rb +2 -2
- data/lib/ruby_llm/providers/anthropic.rb +73 -28
- data/lib/ruby_llm/providers/openai.rb +1 -1
- data/lib/ruby_llm/version.rb +1 -1
- data/ruby_llm.gemspec +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: 325d22d60cd9f0a4157670442830fe2473e8e46879ee672d0e56207f6b3b8e72
|
4
|
+
data.tar.gz: 355be22334837f40ac717974058d4cceb4d46dd13e36a808c11410a9c4624dc0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a7ea97dbe171c7f62df442134b613c84ec277dffcdf1e86fafeed14a95c7ba2545b950d454153606e205f13ffd5a79a9e6e8158d1f053be51a915bbfb03d4806
|
7
|
+
data.tar.gz: bb20ec1351e7597550283b7b7b2b1dff50a26ceba1b123b5fabd8636150f2fae5eb935dac51153448895b7782cdbb85575c25a671064928a8c83465489bea6b9
|
data/.rubocop.yml
CHANGED
data/README.md
CHANGED
@@ -79,15 +79,14 @@ puts "Conversation used #{last_message.input_tokens} input tokens and #{last_mes
|
|
79
79
|
|
80
80
|
## Using Tools
|
81
81
|
|
82
|
-
Give
|
82
|
+
Give your AI assistants access to your Ruby code by creating tool classes that do one thing well:
|
83
83
|
|
84
84
|
```ruby
|
85
|
-
class
|
85
|
+
class Calculator < RubyLLM::Tool
|
86
86
|
description "Performs arithmetic calculations"
|
87
87
|
|
88
88
|
param :expression,
|
89
89
|
type: :string,
|
90
|
-
required: true,
|
91
90
|
desc: "A mathematical expression to evaluate (e.g. '2 + 2')"
|
92
91
|
|
93
92
|
def execute(expression:)
|
@@ -95,11 +94,10 @@ class CalculatorTool < RubyLLM::Tool
|
|
95
94
|
end
|
96
95
|
end
|
97
96
|
|
98
|
-
class
|
97
|
+
class Search < RubyLLM::Tool
|
99
98
|
description "Searches documents by similarity"
|
100
99
|
|
101
100
|
param :query,
|
102
|
-
type: :string,
|
103
101
|
desc: "The search query"
|
104
102
|
|
105
103
|
param :limit,
|
@@ -107,12 +105,12 @@ class DocumentSearchTool < RubyLLM::Tool
|
|
107
105
|
desc: "Number of results to return",
|
108
106
|
required: false
|
109
107
|
|
110
|
-
def initialize(
|
111
|
-
@
|
108
|
+
def initialize(repo:)
|
109
|
+
@repo = repo
|
112
110
|
end
|
113
111
|
|
114
112
|
def execute(query:, limit: 5)
|
115
|
-
@
|
113
|
+
@repo.similarity_search(query, limit:)
|
116
114
|
end
|
117
115
|
end
|
118
116
|
```
|
@@ -121,11 +119,15 @@ Then use them in your conversations:
|
|
121
119
|
|
122
120
|
```ruby
|
123
121
|
# Simple tools just work
|
124
|
-
chat = RubyLLM.chat.with_tool
|
122
|
+
chat = RubyLLM.chat.with_tool Calculator
|
125
123
|
|
126
124
|
# Tools with dependencies are just regular Ruby objects
|
127
|
-
search =
|
128
|
-
chat.with_tools
|
125
|
+
search = Search.new repo: Document
|
126
|
+
chat.with_tools search, CalculatorTool
|
127
|
+
|
128
|
+
# Need more control? Configure as needed
|
129
|
+
chat.with_model('claude-3-5-sonnet-20241022')
|
130
|
+
.with_temperature(0.9)
|
129
131
|
|
130
132
|
chat.ask "What's 2+2?"
|
131
133
|
# => "Let me calculate that for you. The result is 4."
|
data/lib/ruby_llm/chat.rb
CHANGED
@@ -10,6 +10,7 @@ module RubyLLM
|
|
10
10
|
model_id = model || RubyLLM.config.default_model
|
11
11
|
@model = Models.find model_id
|
12
12
|
@provider = Models.provider_for model_id
|
13
|
+
@temperature = 0.7
|
13
14
|
@messages = []
|
14
15
|
@tools = {}
|
15
16
|
|
@@ -26,7 +27,7 @@ module RubyLLM
|
|
26
27
|
def with_tool(tool)
|
27
28
|
raise Error, "Model #{@model.id} doesn't support function calling" unless @model.supports_functions
|
28
29
|
|
29
|
-
tool_instance = tool.is_a?(Class) ? tool.
|
30
|
+
tool_instance = tool.is_a?(Class) ? tool.new : tool
|
30
31
|
@tools[tool_instance.name.to_sym] = tool_instance
|
31
32
|
self
|
32
33
|
end
|
@@ -36,6 +37,17 @@ module RubyLLM
|
|
36
37
|
self
|
37
38
|
end
|
38
39
|
|
40
|
+
def with_model(model_id)
|
41
|
+
@model = Models.find model_id
|
42
|
+
@provider = Models.provider_for model_id
|
43
|
+
self
|
44
|
+
end
|
45
|
+
|
46
|
+
def with_temperature(temperature)
|
47
|
+
@temperature = temperature
|
48
|
+
self
|
49
|
+
end
|
50
|
+
|
39
51
|
def each(&block)
|
40
52
|
messages.each(&block)
|
41
53
|
end
|
@@ -43,19 +55,17 @@ module RubyLLM
|
|
43
55
|
private
|
44
56
|
|
45
57
|
def complete(&block)
|
46
|
-
response = @provider.complete
|
58
|
+
response = @provider.complete messages, tools: @tools, temperature: @temperature, model: @model.id, &block
|
47
59
|
|
60
|
+
add_message response
|
48
61
|
if response.tool_call?
|
49
62
|
handle_tool_calls response, &block
|
50
63
|
else
|
51
|
-
add_message response
|
52
64
|
response
|
53
65
|
end
|
54
66
|
end
|
55
67
|
|
56
68
|
def handle_tool_calls(response, &block)
|
57
|
-
add_message response
|
58
|
-
|
59
69
|
response.tool_calls.each_value do |tool_call|
|
60
70
|
result = execute_tool tool_call
|
61
71
|
add_tool_result tool_call.id, result if result
|
data/lib/ruby_llm/message.rb
CHANGED
data/lib/ruby_llm/provider.rb
CHANGED
@@ -7,8 +7,8 @@ module RubyLLM
|
|
7
7
|
end
|
8
8
|
|
9
9
|
module InstanceMethods
|
10
|
-
def complete(messages, tools
|
11
|
-
payload = build_payload messages, tools, model: model, stream: block_given?
|
10
|
+
def complete(messages, tools:, temperature:, model:, &block)
|
11
|
+
payload = build_payload messages, tools: tools, temperature: temperature, model: model, stream: block_given?
|
12
12
|
|
13
13
|
if block_given?
|
14
14
|
stream_response payload, &block
|
@@ -26,7 +26,7 @@ module RubyLLM
|
|
26
26
|
'/v1/models'
|
27
27
|
end
|
28
28
|
|
29
|
-
def build_payload(messages, tools
|
29
|
+
def build_payload(messages, tools:, temperature:, model:, stream: false)
|
30
30
|
{
|
31
31
|
model: model,
|
32
32
|
messages: format_messages(messages),
|
@@ -34,7 +34,7 @@ module RubyLLM
|
|
34
34
|
stream: stream,
|
35
35
|
max_tokens: RubyLLM.models.find(model).max_tokens
|
36
36
|
}.tap do |payload|
|
37
|
-
payload[:tools] = tools.map { |t| function_for(t) } if tools.any?
|
37
|
+
payload[:tools] = tools.values.map { |t| function_for(t) } if tools.any?
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
@@ -42,26 +42,26 @@ module RubyLLM
|
|
42
42
|
data = response.body
|
43
43
|
content_blocks = data['content'] || []
|
44
44
|
|
45
|
-
|
45
|
+
text_blocks = content_blocks.select { |c| c['type'] == 'text' }
|
46
|
+
text_content = text_blocks.map { |c| c['text'] }.join('')
|
47
|
+
|
46
48
|
tool_use = content_blocks.find { |c| c['type'] == 'tool_use' }
|
47
49
|
|
48
50
|
if tool_use
|
49
51
|
Message.new(
|
50
52
|
role: :assistant,
|
51
53
|
content: text_content,
|
52
|
-
tool_calls:
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
}
|
57
|
-
]
|
54
|
+
tool_calls: parse_tool_calls(tool_use),
|
55
|
+
input_tokens: data.dig('usage', 'input_tokens'),
|
56
|
+
output_tokens: data.dig('usage', 'output_tokens'),
|
57
|
+
model_id: data['model']
|
58
58
|
)
|
59
59
|
else
|
60
60
|
Message.new(
|
61
61
|
role: :assistant,
|
62
62
|
content: text_content,
|
63
|
-
input_tokens: data
|
64
|
-
output_tokens: data
|
63
|
+
input_tokens: data.dig('usage', 'input_tokens'),
|
64
|
+
output_tokens: data.dig('usage', 'output_tokens'),
|
65
65
|
model_id: data['model']
|
66
66
|
)
|
67
67
|
end
|
@@ -90,18 +90,44 @@ module RubyLLM
|
|
90
90
|
|
91
91
|
def handle_stream(&block)
|
92
92
|
to_json_stream do |data|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
93
|
+
if data['type'] == 'content_block_delta' && data.dig('delta', 'type') == 'input_json_delta'
|
94
|
+
block.call(
|
95
|
+
Chunk.new(
|
96
|
+
role: :assistant,
|
97
|
+
model_id: data.dig('message', 'model'),
|
98
|
+
content: data.dig('delta', 'text'),
|
99
|
+
input_tokens: data.dig('message', 'usage', 'input_tokens'),
|
100
|
+
output_tokens: data.dig('message', 'usage', 'output_tokens') || data.dig('usage', 'output_tokens'),
|
101
|
+
tool_calls: { nil => ToolCall.new(id: nil, name: nil, arguments: data.dig('delta', 'partial_json')) }
|
102
|
+
)
|
100
103
|
)
|
101
|
-
|
104
|
+
else
|
105
|
+
block.call(
|
106
|
+
Chunk.new(
|
107
|
+
role: :assistant,
|
108
|
+
model_id: data.dig('message', 'model'),
|
109
|
+
content: data.dig('delta', 'text'),
|
110
|
+
input_tokens: data.dig('message', 'usage', 'input_tokens'),
|
111
|
+
output_tokens: data.dig('message', 'usage', 'output_tokens') || data.dig('usage', 'output_tokens'),
|
112
|
+
tool_calls: parse_tool_calls(data['content_block'])
|
113
|
+
)
|
114
|
+
)
|
115
|
+
end
|
102
116
|
end
|
103
117
|
end
|
104
118
|
|
119
|
+
def parse_tool_calls(content_block)
|
120
|
+
return nil unless content_block && content_block['type'] == 'tool_use'
|
121
|
+
|
122
|
+
{
|
123
|
+
content_block['id'] => ToolCall.new(
|
124
|
+
id: content_block['id'],
|
125
|
+
name: content_block['name'],
|
126
|
+
arguments: content_block['input']
|
127
|
+
)
|
128
|
+
}
|
129
|
+
end
|
130
|
+
|
105
131
|
def function_for(tool)
|
106
132
|
{
|
107
133
|
name: tool.name,
|
@@ -116,16 +142,31 @@ module RubyLLM
|
|
116
142
|
|
117
143
|
def format_messages(messages)
|
118
144
|
messages.map do |msg|
|
119
|
-
if msg.
|
145
|
+
if msg.tool_call?
|
120
146
|
{
|
121
|
-
role:
|
147
|
+
role: 'assistant',
|
148
|
+
content: [
|
149
|
+
{
|
150
|
+
type: 'text',
|
151
|
+
text: msg.content
|
152
|
+
},
|
153
|
+
{
|
154
|
+
type: 'tool_use',
|
155
|
+
id: msg.tool_calls.values.first.id,
|
156
|
+
name: msg.tool_calls.values.first.name,
|
157
|
+
input: msg.tool_calls.values.first.arguments
|
158
|
+
}
|
159
|
+
]
|
160
|
+
}
|
161
|
+
elsif msg.tool_result?
|
162
|
+
{
|
163
|
+
role: 'user',
|
122
164
|
content: [
|
123
165
|
{
|
124
166
|
type: 'tool_result',
|
125
|
-
tool_use_id: msg.
|
126
|
-
content: msg.
|
127
|
-
|
128
|
-
}.compact
|
167
|
+
tool_use_id: msg.tool_call_id,
|
168
|
+
content: msg.content
|
169
|
+
}
|
129
170
|
]
|
130
171
|
}
|
131
172
|
else
|
@@ -139,19 +180,23 @@ module RubyLLM
|
|
139
180
|
|
140
181
|
def convert_role(role)
|
141
182
|
case role
|
183
|
+
when :tool then 'user'
|
142
184
|
when :user then 'user'
|
143
185
|
else 'assistant'
|
144
186
|
end
|
145
187
|
end
|
146
188
|
|
147
189
|
def clean_parameters(parameters)
|
148
|
-
parameters.transform_values do |
|
149
|
-
|
190
|
+
parameters.transform_values do |param|
|
191
|
+
{
|
192
|
+
type: param.type,
|
193
|
+
description: param.description
|
194
|
+
}.compact
|
150
195
|
end
|
151
196
|
end
|
152
197
|
|
153
198
|
def required_parameters(parameters)
|
154
|
-
parameters.select { |_,
|
199
|
+
parameters.select { |_, param| param.required }.keys
|
155
200
|
end
|
156
201
|
end
|
157
202
|
end
|
data/lib/ruby_llm/version.rb
CHANGED
data/ruby_llm.gemspec
CHANGED
@@ -15,7 +15,7 @@ Gem::Specification.new do |spec|
|
|
15
15
|
' works.'
|
16
16
|
spec.homepage = 'https://github.com/crmne/ruby_llm'
|
17
17
|
spec.license = 'MIT'
|
18
|
-
spec.required_ruby_version = Gem::Requirement.new('>=
|
18
|
+
spec.required_ruby_version = Gem::Requirement.new('>= 3.1.0')
|
19
19
|
|
20
20
|
spec.metadata['homepage_uri'] = spec.homepage
|
21
21
|
spec.metadata['source_code_uri'] = spec.homepage
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby_llm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.0.
|
4
|
+
version: 0.1.0.pre13
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Carmine Paolino
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-02-
|
11
|
+
date: 2025-02-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: event_stream_parser
|
@@ -380,7 +380,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
380
380
|
requirements:
|
381
381
|
- - ">="
|
382
382
|
- !ruby/object:Gem::Version
|
383
|
-
version:
|
383
|
+
version: 3.1.0
|
384
384
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
385
385
|
requirements:
|
386
386
|
- - ">="
|