ruby_llm 0.1.0.pre8 → 0.1.0.pre10
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 +31 -25
- data/lib/ruby_llm/chat.rb +8 -7
- data/lib/ruby_llm/tool.rb +61 -71
- data/lib/ruby_llm/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7d37a2d7434c6fba64700425cb86e29a6d04108f72e89e2a51273581adacb2a8
|
4
|
+
data.tar.gz: 45021416cdf96bf2ffdac2917a689515d519014d51054c6a4fe02bc60343685d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7ba9769ef1b6763e983540a02f42139bd4063bbdfb03b5999e33d78db82a46cb09b58778406b575f7b4d66105a42db8973c0a96939706ad80697aca01623a7dc
|
7
|
+
data.tar.gz: 0027621e9fbb2e2261eb4d2d8ef44672813cf9e0a5b523d8b850ca2ea1c52614671dbf914ca55bbe3cd5d029b163b4ac7308221f63afa4dae20230ce9ee88f4c
|
data/README.md
CHANGED
@@ -39,7 +39,7 @@ chat.ask "What's the best way to learn Ruby?"
|
|
39
39
|
|
40
40
|
## Available Models
|
41
41
|
|
42
|
-
RubyLLM gives you access to the latest models from multiple providers
|
42
|
+
RubyLLM gives you access to the latest models from multiple providers:
|
43
43
|
|
44
44
|
```ruby
|
45
45
|
# List all available models
|
@@ -79,49 +79,55 @@ puts "Conversation used #{last_message.input_tokens} input tokens and #{last_mes
|
|
79
79
|
|
80
80
|
## Using Tools
|
81
81
|
|
82
|
-
Give
|
82
|
+
Give Claude some Ruby superpowers by letting it call your code. Simply create a tool class:
|
83
83
|
|
84
84
|
```ruby
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
param :expression,
|
89
|
-
|
85
|
+
class CalculatorTool < RubyLLM::Tool
|
86
|
+
description "Performs arithmetic calculations"
|
87
|
+
|
88
|
+
param :expression,
|
89
|
+
type: :string,
|
90
|
+
required: true,
|
91
|
+
desc: "A mathematical expression to evaluate (e.g. '2 + 2')"
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def execute(args)
|
90
96
|
eval(args[:expression]).to_s
|
91
|
-
rescue => e
|
92
|
-
{ error: "Invalid expression: #{e.message}" }
|
93
97
|
end
|
94
98
|
end
|
99
|
+
```
|
95
100
|
|
96
|
-
|
97
|
-
chat = RubyLLM.chat.with_tool calculator
|
101
|
+
Then use it in your conversations:
|
98
102
|
|
99
|
-
|
103
|
+
```ruby
|
104
|
+
chat = RubyLLM.chat.with_tool(CalculatorTool)
|
105
|
+
|
106
|
+
# Claude will automatically use the calculator when appropriate
|
100
107
|
chat.ask "What's 2+2?"
|
101
|
-
# => "
|
108
|
+
# => "Let me calculate that for you. The result is 4."
|
102
109
|
|
103
|
-
chat.ask "and
|
104
|
-
# => "
|
110
|
+
chat.ask "If I have 3 apples and multiply them by 5, how many do I have?"
|
111
|
+
# => "Let me help you calculate that. 3 × 5 = 15, so you would have 15 apples."
|
105
112
|
|
106
113
|
# Add multiple tools
|
107
|
-
chat.with_tools
|
114
|
+
chat.with_tools(CalculatorTool, WeatherTool, DatabaseTool)
|
108
115
|
```
|
109
116
|
|
110
|
-
Tools let you seamlessly integrate Ruby code with AI capabilities.
|
117
|
+
Tools let you seamlessly integrate your Ruby code with AI capabilities. The model will automatically decide when to use your tools and handle the results appropriately.
|
111
118
|
|
112
|
-
|
113
|
-
|
114
|
-
RubyLLM gives you easy access to model capabilities:
|
119
|
+
Need to debug a tool? RubyLLM automatically logs all tool calls and their results when debug logging is enabled:
|
115
120
|
|
116
121
|
```ruby
|
117
|
-
|
122
|
+
ENV['RUBY_LLM_DEBUG'] = 'true'
|
118
123
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
model.supports_json_mode # => true
|
124
|
+
chat.ask "What's 123 * 456?"
|
125
|
+
# D, -- RubyLLM: Tool calculator called with: {"expression" => "123 * 456"}
|
126
|
+
# D, -- RubyLLM: Tool calculator returned: "56088"
|
123
127
|
```
|
124
128
|
|
129
|
+
Create tools for anything - database queries, API calls, custom business logic - and let Claude use them naturally in conversation.
|
130
|
+
|
125
131
|
## Coming Soon
|
126
132
|
|
127
133
|
- Rails integration for seamless database and Active Record support
|
data/lib/ruby_llm/chat.rb
CHANGED
@@ -26,7 +26,8 @@ module RubyLLM
|
|
26
26
|
def with_tool(tool)
|
27
27
|
raise Error, "Model #{@model.id} doesn't support function calling" unless @model.supports_functions
|
28
28
|
|
29
|
-
|
29
|
+
tool_instance = tool.is_a?(Class) ? tool.to_tool : tool
|
30
|
+
@tools[tool_instance.name.to_sym] = tool_instance
|
30
31
|
self
|
31
32
|
end
|
32
33
|
|
@@ -45,14 +46,14 @@ module RubyLLM
|
|
45
46
|
response = @provider.complete(messages, tools: @tools, model: @model.id, &block)
|
46
47
|
|
47
48
|
if response.tool_call?
|
48
|
-
handle_tool_calls response
|
49
|
+
handle_tool_calls response, &block
|
49
50
|
else
|
50
51
|
add_message response
|
51
52
|
response
|
52
53
|
end
|
53
54
|
end
|
54
55
|
|
55
|
-
def handle_tool_calls(response)
|
56
|
+
def handle_tool_calls(response, &block)
|
56
57
|
add_message response
|
57
58
|
|
58
59
|
response.tool_calls.each_value do |tool_call|
|
@@ -60,11 +61,11 @@ module RubyLLM
|
|
60
61
|
add_tool_result tool_call.id, result if result
|
61
62
|
end
|
62
63
|
|
63
|
-
complete
|
64
|
+
complete(&block)
|
64
65
|
end
|
65
66
|
|
66
67
|
def execute_tool(tool_call)
|
67
|
-
tool = tools[tool_call.name]
|
68
|
+
tool = tools[tool_call.name.to_sym]
|
68
69
|
args = tool_call.arguments
|
69
70
|
tool.call(args)
|
70
71
|
end
|
@@ -85,8 +86,8 @@ module RubyLLM
|
|
85
86
|
|
86
87
|
def ensure_valid_tools
|
87
88
|
tools.each_key do |name|
|
88
|
-
unless name.is_a?(
|
89
|
-
raise Error, 'Tools should be of the format {<name>: <RubyLLM::Tool>}'
|
89
|
+
unless name.is_a?(Symbol) && tools[name].is_a?(RubyLLM::Tool)
|
90
|
+
raise Error, 'Tools should be of the format {<name.to_sym>: <RubyLLM::Tool>}'
|
90
91
|
end
|
91
92
|
end
|
92
93
|
end
|
data/lib/ruby_llm/tool.rb
CHANGED
@@ -1,100 +1,90 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# lib/ruby_llm/tool.rb
|
3
4
|
module RubyLLM
|
4
5
|
class Tool
|
5
|
-
class
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
@
|
10
|
-
@type = type
|
11
|
-
@description = description
|
12
|
-
@required = required
|
6
|
+
class << self
|
7
|
+
def description(text = nil)
|
8
|
+
return @description unless text
|
9
|
+
|
10
|
+
@description = text
|
13
11
|
end
|
14
12
|
|
15
|
-
def
|
16
|
-
|
17
|
-
|
18
|
-
|
13
|
+
def param(name, type:, desc: nil, required: true)
|
14
|
+
param = Parameter.new(
|
15
|
+
name,
|
16
|
+
type: type.to_s,
|
17
|
+
description: desc,
|
19
18
|
required: required
|
20
|
-
|
19
|
+
)
|
20
|
+
parameters[name] = param
|
21
21
|
end
|
22
|
-
end
|
23
22
|
|
24
|
-
|
25
|
-
|
26
|
-
@tool = tool
|
23
|
+
def parameters
|
24
|
+
@parameters ||= {}
|
27
25
|
end
|
28
26
|
|
29
|
-
def
|
30
|
-
|
31
|
-
|
27
|
+
def name
|
28
|
+
super
|
29
|
+
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
30
|
+
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
31
|
+
.downcase
|
32
|
+
.delete_suffix('_tool')
|
32
33
|
end
|
33
34
|
|
34
|
-
def
|
35
|
-
|
36
|
-
self
|
37
|
-
end
|
35
|
+
def to_tool
|
36
|
+
tool_instance = new
|
38
37
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
end
|
43
|
-
end
|
38
|
+
def tool_instance.name
|
39
|
+
self.class.name
|
40
|
+
end
|
44
41
|
|
45
|
-
|
42
|
+
def tool_instance.description
|
43
|
+
self.class.description
|
44
|
+
end
|
46
45
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
builder.instance_eval(&block)
|
51
|
-
tool
|
52
|
-
end
|
46
|
+
def tool_instance.parameters
|
47
|
+
self.class.parameters
|
48
|
+
end
|
53
49
|
|
54
|
-
|
55
|
-
|
56
|
-
@parameters = {}
|
50
|
+
tool_instance
|
51
|
+
end
|
57
52
|
end
|
58
53
|
|
59
54
|
def call(args)
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
result
|
68
|
-
rescue StandardError => e
|
69
|
-
RubyLLM.logger.error "Tool #{name}(#{args.inspect}) failed with error #{e.message}"
|
70
|
-
{ error: e.message }
|
71
|
-
end
|
55
|
+
RubyLLM.logger.debug "Tool #{name} called with: #{args.inspect}"
|
56
|
+
result = execute(args.transform_keys(&:to_sym))
|
57
|
+
RubyLLM.logger.debug "Tool #{name} returned: #{result.inspect}"
|
58
|
+
result
|
59
|
+
rescue StandardError => e
|
60
|
+
RubyLLM.logger.error "Tool #{name} failed with error: #{e.message}"
|
61
|
+
{ error: e.message }
|
72
62
|
end
|
73
63
|
|
74
|
-
|
75
|
-
def from_method(method, description: nil)
|
76
|
-
define(method.name.to_s) do
|
77
|
-
description description if description
|
78
|
-
|
79
|
-
method.parameters.each do |type, name|
|
80
|
-
param name, required: (type == :req)
|
81
|
-
end
|
64
|
+
private
|
82
65
|
|
83
|
-
|
84
|
-
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|
66
|
+
def execute(args)
|
67
|
+
raise NotImplementedError, 'Subclasses must implement #execute'
|
88
68
|
end
|
69
|
+
end
|
89
70
|
|
90
|
-
|
71
|
+
# Using the existing Parameter class from Tool.rb
|
72
|
+
class Parameter
|
73
|
+
attr_reader :name, :type, :description, :required
|
91
74
|
|
92
|
-
def
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
75
|
+
def initialize(name, type: 'string', description: nil, required: true)
|
76
|
+
@name = name
|
77
|
+
@type = type
|
78
|
+
@description = description
|
79
|
+
@required = required
|
80
|
+
end
|
81
|
+
|
82
|
+
def to_h
|
83
|
+
{
|
84
|
+
type: type,
|
85
|
+
description: description,
|
86
|
+
required: required
|
87
|
+
}.compact
|
98
88
|
end
|
99
89
|
end
|
100
90
|
end
|
data/lib/ruby_llm/version.rb
CHANGED