llm.rb 15.0.2 → 15.1.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/CHANGELOG.md +161 -1
- data/README.md +134 -85
- data/bin/llm.rb +38 -3
- data/data/bedrock.json +250 -0
- data/data/deepinfra.json +162 -0
- data/data/deepseek.json +50 -0
- data/data/google.json +19 -21
- data/data/openrouter.json +14280 -0
- data/data/xai.json +28 -16
- data/docs/deepdive/advanced/context.md +8 -6
- data/docs/deepdive/fundamentals/agents.md +11 -10
- data/docs/deepdive/fundamentals/stream.md +4 -4
- data/lib/llm/active_record.rb +1 -1
- data/lib/llm/agent.rb +6 -0
- data/lib/llm/context.rb +28 -13
- data/lib/llm/cost.rb +13 -0
- data/lib/llm/function/fork/task.rb +14 -10
- data/lib/llm/provider.rb +29 -8
- data/lib/llm/providers/anthropic.rb +1 -1
- data/lib/llm/providers/bedrock/models.rb +2 -2
- data/lib/llm/providers/bedrock.rb +1 -1
- data/lib/llm/providers/google.rb +1 -1
- data/lib/llm/providers/ollama.rb +1 -1
- data/lib/llm/providers/openai/responses.rb +2 -1
- data/lib/llm/providers/openai.rb +1 -1
- data/lib/llm/providers/openrouter.rb +87 -0
- data/lib/llm/repl/buffer.rb +20 -5
- data/lib/llm/repl/input.rb +5 -4
- data/lib/llm/repl/markdown/table.rb +5 -2
- data/lib/llm/repl/node.rb +26 -1
- data/lib/llm/repl/stream.rb +30 -3
- data/lib/llm/repl/window.rb +7 -7
- data/lib/llm/repl.rb +1 -0
- data/lib/llm/skill.rb +7 -1
- data/lib/llm/stream.rb +8 -3
- data/lib/llm/tools/git.rb +2 -2
- data/lib/llm/tools/mkdir.rb +2 -2
- data/lib/llm/tools/rg.rb +2 -2
- data/lib/llm/tools/ruby.rb +2 -2
- data/lib/llm/tools/shell.rb +2 -2
- data/lib/llm/tools/utils.rb +1 -1
- data/lib/llm/transport/curb.rb +5 -3
- data/lib/llm/transport/http.rb +5 -2
- data/lib/llm/transport/persistent_http.rb +6 -4
- data/lib/llm/transport/utils.rb +8 -6
- data/lib/llm/version.rb +1 -1
- data/lib/llm.rb +16 -1
- data/llm.gemspec +2 -1
- metadata +19 -3
data/lib/llm/repl/node.rb
CHANGED
|
@@ -6,6 +6,30 @@ class LLM::Repl
|
|
|
6
6
|
# of text and optional curses attributes.
|
|
7
7
|
# @api private
|
|
8
8
|
class Node
|
|
9
|
+
##
|
|
10
|
+
# Returns the display width of the given text, counting wide
|
|
11
|
+
# characters such as emoji as two columns.
|
|
12
|
+
# @param [#to_s] text
|
|
13
|
+
# @return [Integer]
|
|
14
|
+
def self.width(text)
|
|
15
|
+
Unicode::DisplayWidth.of(text.to_s)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
##
|
|
19
|
+
# Slices the given text so the returned piece has a display width
|
|
20
|
+
# of at most `width` columns.
|
|
21
|
+
# @param [#to_s] text
|
|
22
|
+
# @param [Integer] width
|
|
23
|
+
# @return [String]
|
|
24
|
+
def self.slice(text, width)
|
|
25
|
+
out = +""
|
|
26
|
+
text.to_s.each_char do |char|
|
|
27
|
+
break if self.width(out + char) > width
|
|
28
|
+
out << char
|
|
29
|
+
end
|
|
30
|
+
out
|
|
31
|
+
end
|
|
32
|
+
|
|
9
33
|
##
|
|
10
34
|
# @return [String]
|
|
11
35
|
attr_reader :text
|
|
@@ -24,9 +48,10 @@ class LLM::Repl
|
|
|
24
48
|
end
|
|
25
49
|
|
|
26
50
|
##
|
|
51
|
+
# Returns the display width of the text
|
|
27
52
|
# @return [Integer]
|
|
28
53
|
def size
|
|
29
|
-
@text
|
|
54
|
+
self.class.width(@text)
|
|
30
55
|
end
|
|
31
56
|
alias_method :length, :size
|
|
32
57
|
|
data/lib/llm/repl/stream.rb
CHANGED
|
@@ -17,6 +17,7 @@ class LLM::Repl
|
|
|
17
17
|
# @return [LLM::Repl::Stream]
|
|
18
18
|
def initialize(repl, queue)
|
|
19
19
|
@repl = repl
|
|
20
|
+
@agent = @repl.agent
|
|
20
21
|
@_queue = queue
|
|
21
22
|
@buffer = +""
|
|
22
23
|
@tools = {}
|
|
@@ -39,6 +40,14 @@ class LLM::Repl
|
|
|
39
40
|
@_queue.push [:status, [" ", lambda, " • #{tool.name}(#{format_args(tool)})"]]
|
|
40
41
|
end
|
|
41
42
|
|
|
43
|
+
##
|
|
44
|
+
# @param [Exception] ex
|
|
45
|
+
# @param [Integer] attempt
|
|
46
|
+
# @return [void]
|
|
47
|
+
def on_retry(ex, attempt)
|
|
48
|
+
@_queue.push [:status, retry_error(ex, attempt)]
|
|
49
|
+
end
|
|
50
|
+
|
|
42
51
|
##
|
|
43
52
|
# @param [LLM::Function] tool
|
|
44
53
|
# @param [LLM::Function::Return] result
|
|
@@ -56,12 +65,30 @@ class LLM::Repl
|
|
|
56
65
|
|
|
57
66
|
private
|
|
58
67
|
|
|
68
|
+
##
|
|
69
|
+
# @return [LLM::Agent]
|
|
70
|
+
def agent
|
|
71
|
+
@agent
|
|
72
|
+
end
|
|
73
|
+
|
|
59
74
|
##
|
|
60
75
|
# @return [LLM::Repl::Node]
|
|
61
76
|
def lambda
|
|
62
77
|
Node.new("λ", Curses::A_BOLD | Color.red)
|
|
63
78
|
end
|
|
64
79
|
|
|
80
|
+
##
|
|
81
|
+
# @return [LLM::Repl::Node]
|
|
82
|
+
def retry_error(ex, attempt)
|
|
83
|
+
err = case ex.class.to_s
|
|
84
|
+
when "LLM::RateLimitError", "LLM::InsufficientQuotaError" then "Rate limited"
|
|
85
|
+
when "Net::ReadTimeout", "Net::OpenTimeout" then "Timed out"
|
|
86
|
+
else "#{ex.class}"
|
|
87
|
+
end
|
|
88
|
+
[Node.new(" 🔁 "),
|
|
89
|
+
Node.new("#{err} • attempt #{attempt} of #{agent.retry_budget}", Color.red | Curses::A_BOLD)]
|
|
90
|
+
end
|
|
91
|
+
|
|
65
92
|
##
|
|
66
93
|
# Formats tool arguments as compact key: value pairs
|
|
67
94
|
# suitable for the status line. Strings are quoted and
|
|
@@ -82,7 +109,7 @@ class LLM::Repl
|
|
|
82
109
|
props = props.sort_by { tool.params.properties[_1].index }
|
|
83
110
|
props = props.filter_map { args[_1] ? "#{_1}: #{format_value(args[_1])}" : nil }
|
|
84
111
|
result = props.join(", ")
|
|
85
|
-
result
|
|
112
|
+
Node.width(result) > max ? "#{Node.slice(result, max - 1)}…" : result
|
|
86
113
|
end
|
|
87
114
|
|
|
88
115
|
##
|
|
@@ -92,7 +119,7 @@ class LLM::Repl
|
|
|
92
119
|
def format_value(value, max: 18)
|
|
93
120
|
case value
|
|
94
121
|
when String
|
|
95
|
-
value
|
|
122
|
+
Node.width(value) > max ? "#{Node.slice(value, max)}…".inspect : value.inspect
|
|
96
123
|
when Array
|
|
97
124
|
items = value.take(2).map { format_value(_1, max: 10) }
|
|
98
125
|
items << "…" if value.size > 2
|
|
@@ -103,7 +130,7 @@ class LLM::Repl
|
|
|
103
130
|
"nil"
|
|
104
131
|
else
|
|
105
132
|
str = value.inspect
|
|
106
|
-
str
|
|
133
|
+
Node.width(str) > max ? "#{Node.slice(str, max)}…" : str
|
|
107
134
|
end
|
|
108
135
|
end
|
|
109
136
|
end
|
data/lib/llm/repl/window.rb
CHANGED
|
@@ -140,11 +140,11 @@ class LLM::Repl
|
|
|
140
140
|
remaining = buffer.width - width
|
|
141
141
|
break if remaining <= 0
|
|
142
142
|
text, attrs = chunk.text.to_s, chunk.attrs
|
|
143
|
-
clipped = text
|
|
143
|
+
clipped = Node.slice(text, remaining)
|
|
144
144
|
Curses.attron(attrs) if attrs
|
|
145
145
|
Curses.addstr(clipped)
|
|
146
146
|
Curses.attroff(attrs) if attrs
|
|
147
|
-
width += clipped
|
|
147
|
+
width += Node.width(clipped)
|
|
148
148
|
end
|
|
149
149
|
end
|
|
150
150
|
last_drawn = offset + rows.size
|
|
@@ -163,10 +163,10 @@ class LLM::Repl
|
|
|
163
163
|
Curses.clrtoeol
|
|
164
164
|
status.nodes.each { addnode(_1) }
|
|
165
165
|
context = status.context_bar
|
|
166
|
-
Curses.setpos(y, [(columns - context
|
|
166
|
+
Curses.setpos(y, [(columns - Node.width(context)) / 2, 0].max)
|
|
167
167
|
Curses.addstr(context)
|
|
168
168
|
cost = status.cost.to_s
|
|
169
|
-
Curses.setpos(y, [columns - cost
|
|
169
|
+
Curses.setpos(y, [columns - Node.width(cost), 0].max)
|
|
170
170
|
Curses.addstr(cost)
|
|
171
171
|
end
|
|
172
172
|
|
|
@@ -178,9 +178,9 @@ class LLM::Repl
|
|
|
178
178
|
def draw_meta(offset:)
|
|
179
179
|
y = Curses.lines - offset
|
|
180
180
|
fill_row(y) do
|
|
181
|
-
addnode(status.cwd, status.cwd.text
|
|
182
|
-
Curses.setpos(y, [columns - status.model.
|
|
183
|
-
addnode(status.model, status.model.text
|
|
181
|
+
addnode(status.cwd, Node.slice(status.cwd.text, columns))
|
|
182
|
+
Curses.setpos(y, [columns - status.model.size, 0].max)
|
|
183
|
+
addnode(status.model, Node.slice(status.model.text, columns))
|
|
184
184
|
end
|
|
185
185
|
end
|
|
186
186
|
|
data/lib/llm/repl.rb
CHANGED
data/lib/llm/skill.rb
CHANGED
|
@@ -35,6 +35,11 @@ module LLM
|
|
|
35
35
|
# @return [String]
|
|
36
36
|
attr_reader :description
|
|
37
37
|
|
|
38
|
+
##
|
|
39
|
+
# Returns the model the skill will use.
|
|
40
|
+
# @return [String, nil]
|
|
41
|
+
attr_reader :model
|
|
42
|
+
|
|
38
43
|
##
|
|
39
44
|
# Returns the skill instructions.
|
|
40
45
|
# @return [String]
|
|
@@ -90,7 +95,7 @@ module LLM
|
|
|
90
95
|
stream = ctx.stream
|
|
91
96
|
stream.on_skill_call(self)
|
|
92
97
|
agent = self.agent(ctx)
|
|
93
|
-
res = agent.talk("Solve the user's query.")
|
|
98
|
+
res = agent.talk("Solve the user's query.", model: model || ctx.model)
|
|
94
99
|
stream.on_skill_return(agent, self, res)
|
|
95
100
|
{content: res.content}
|
|
96
101
|
end
|
|
@@ -156,6 +161,7 @@ module LLM
|
|
|
156
161
|
@frontmatter = LLM::Object.from(YAML.safe_load(match[1]) || {})
|
|
157
162
|
@name = @frontmatter.name || @name
|
|
158
163
|
@description = @frontmatter.description || @description
|
|
164
|
+
@model = @frontmatter.model || nil
|
|
159
165
|
@instructions = match[2]
|
|
160
166
|
@inherit_tools, @tools = parse_tools(@frontmatter.tools)
|
|
161
167
|
end
|
data/lib/llm/stream.rb
CHANGED
|
@@ -185,12 +185,17 @@ module LLM
|
|
|
185
185
|
end
|
|
186
186
|
|
|
187
187
|
##
|
|
188
|
-
# Called when a request is
|
|
189
|
-
#
|
|
188
|
+
# Called when a request is retried because it was rate limited or
|
|
189
|
+
# timed out.
|
|
190
|
+
# @param [Exception] error
|
|
191
|
+
# The error that caused the retry
|
|
192
|
+
# @param [Integer] attempt
|
|
193
|
+
# The one-based retry attempt number
|
|
190
194
|
# @return [nil]
|
|
191
|
-
def
|
|
195
|
+
def on_retry(error, attempt)
|
|
192
196
|
nil
|
|
193
197
|
end
|
|
198
|
+
alias_method :on_rate_limit, :on_retry
|
|
194
199
|
|
|
195
200
|
##
|
|
196
201
|
# Called before a skill's subagent runs.
|
data/lib/llm/tools/git.rb
CHANGED
data/lib/llm/tools/mkdir.rb
CHANGED
data/lib/llm/tools/rg.rb
CHANGED
data/lib/llm/tools/ruby.rb
CHANGED
data/lib/llm/tools/shell.rb
CHANGED
data/lib/llm/tools/utils.rb
CHANGED
|
@@ -8,7 +8,7 @@ class LLM::Tool
|
|
|
8
8
|
# Wait for a command to finish, or abort
|
|
9
9
|
# with an error when it exceeds the
|
|
10
10
|
# specified timeout.
|
|
11
|
-
# @param [Test::
|
|
11
|
+
# @param [Test::Command] command
|
|
12
12
|
# @param [Integer] timeout
|
|
13
13
|
# @return [void]
|
|
14
14
|
def wait(command:, timeout:)
|
data/lib/llm/transport/curb.rb
CHANGED
|
@@ -28,12 +28,14 @@ class LLM::Transport
|
|
|
28
28
|
# @param [String] host
|
|
29
29
|
# @param [Integer] port
|
|
30
30
|
# @param [Integer] timeout
|
|
31
|
+
# @param [Integer] connect_timeout
|
|
31
32
|
# @param [Boolean] ssl
|
|
32
33
|
# @return [LLM::Transport::Curb]
|
|
33
|
-
def initialize(host:, port:, timeout:, ssl:)
|
|
34
|
+
def initialize(host:, port:, timeout:, connect_timeout:, ssl:)
|
|
34
35
|
@host = host
|
|
35
36
|
@port = port
|
|
36
37
|
@timeout = timeout
|
|
38
|
+
@connect_timeout = connect_timeout
|
|
37
39
|
@ssl = ssl
|
|
38
40
|
@base_uri = URI("#{ssl ? "https" : "http"}://#{host}:#{port}/")
|
|
39
41
|
@monitor = Monitor.new
|
|
@@ -112,13 +114,13 @@ class LLM::Transport
|
|
|
112
114
|
|
|
113
115
|
private
|
|
114
116
|
|
|
115
|
-
attr_reader :host, :port, :timeout, :ssl, :base_uri
|
|
117
|
+
attr_reader :host, :port, :timeout, :connect_timeout, :ssl, :base_uri
|
|
116
118
|
|
|
117
119
|
def build_easy(request)
|
|
118
120
|
LLM.require "curb" unless defined?(::Curl)
|
|
119
121
|
easy = ::Curl::Easy.new(request_url(request))
|
|
120
122
|
easy.timeout = timeout
|
|
121
|
-
easy.connect_timeout =
|
|
123
|
+
easy.connect_timeout = connect_timeout
|
|
122
124
|
request.headers.each { |k, v| easy.headers[k] = v }
|
|
123
125
|
easy.follow_location = true
|
|
124
126
|
easy.ssl_verify_peer = false if !ssl
|
data/lib/llm/transport/http.rb
CHANGED
|
@@ -20,12 +20,14 @@ class LLM::Transport
|
|
|
20
20
|
# @param [String] host
|
|
21
21
|
# @param [Integer] port
|
|
22
22
|
# @param [Integer] timeout
|
|
23
|
+
# @param [Integer] connect_timeout
|
|
23
24
|
# @param [Boolean] ssl
|
|
24
25
|
# @return [LLM::Transport::HTTP]
|
|
25
|
-
def initialize(host:, port:, timeout:, ssl:)
|
|
26
|
+
def initialize(host:, port:, timeout:, connect_timeout:, ssl:)
|
|
26
27
|
@host = host
|
|
27
28
|
@port = port
|
|
28
29
|
@timeout = timeout
|
|
30
|
+
@connect_timeout = connect_timeout
|
|
29
31
|
@ssl = ssl
|
|
30
32
|
@base_uri = URI("#{ssl ? "https" : "http"}://#{host}:#{port}/")
|
|
31
33
|
@monitor = Monitor.new
|
|
@@ -93,10 +95,11 @@ class LLM::Transport
|
|
|
93
95
|
|
|
94
96
|
private
|
|
95
97
|
|
|
96
|
-
attr_reader :host, :port, :timeout, :ssl, :base_uri
|
|
98
|
+
attr_reader :host, :port, :timeout, :connect_timeout, :ssl, :base_uri
|
|
97
99
|
|
|
98
100
|
def client
|
|
99
101
|
client = Net::HTTP.new(host, port)
|
|
102
|
+
client.open_timeout = connect_timeout
|
|
100
103
|
client.read_timeout = timeout
|
|
101
104
|
client.use_ssl = ssl
|
|
102
105
|
client
|
|
@@ -32,12 +32,14 @@ class LLM::Transport
|
|
|
32
32
|
# @param [String] host
|
|
33
33
|
# @param [Integer] port
|
|
34
34
|
# @param [Integer] timeout
|
|
35
|
+
# @param [Integer] connect_timeout
|
|
35
36
|
# @param [Boolean] ssl
|
|
36
37
|
# @return [LLM::Transport::PersistentHTTP]
|
|
37
|
-
def initialize(host:, port:, timeout:, ssl:)
|
|
38
|
+
def initialize(host:, port:, timeout:, connect_timeout:, ssl:)
|
|
38
39
|
@host = host
|
|
39
40
|
@port = port
|
|
40
41
|
@timeout = timeout
|
|
42
|
+
@connect_timeout = connect_timeout
|
|
41
43
|
@ssl = ssl
|
|
42
44
|
@base_uri = URI("#{ssl ? "https" : "http"}://#{host}:#{port}/")
|
|
43
45
|
@monitor = Monitor.new
|
|
@@ -100,7 +102,7 @@ class LLM::Transport
|
|
|
100
102
|
|
|
101
103
|
private
|
|
102
104
|
|
|
103
|
-
attr_reader :host, :port, :timeout, :ssl, :base_uri
|
|
105
|
+
attr_reader :host, :port, :timeout, :connect_timeout, :ssl, :base_uri
|
|
104
106
|
|
|
105
107
|
def client
|
|
106
108
|
self.class.lock do
|
|
@@ -110,14 +112,14 @@ class LLM::Transport
|
|
|
110
112
|
LLM.require "net/http/persistent" unless defined?(Net::HTTP::Persistent)
|
|
111
113
|
client = Net::HTTP::Persistent.new(name: self.class.name)
|
|
112
114
|
client.read_timeout = timeout
|
|
113
|
-
client.open_timeout =
|
|
115
|
+
client.open_timeout = connect_timeout
|
|
114
116
|
self.class.registry[client_id] = client
|
|
115
117
|
end
|
|
116
118
|
end
|
|
117
119
|
end
|
|
118
120
|
|
|
119
121
|
def client_id
|
|
120
|
-
"#{host}:#{port}:#{timeout}:#{ssl}"
|
|
122
|
+
"#{host}:#{port}:#{timeout}:#{connect_timeout}:#{ssl}"
|
|
121
123
|
end
|
|
122
124
|
|
|
123
125
|
def close_socket(http)
|
data/lib/llm/transport/utils.rb
CHANGED
|
@@ -24,18 +24,19 @@ class LLM::Transport
|
|
|
24
24
|
# @param [String] host
|
|
25
25
|
# @param [Integer] port
|
|
26
26
|
# @param [Integer, nil] timeout
|
|
27
|
+
# @param [Integer] connect_timeout
|
|
27
28
|
# @param [Boolean] ssl
|
|
28
29
|
# @param [Boolean] persistent
|
|
29
30
|
# @param [LLM::Transport, Class, Symbol, nil] transport
|
|
30
31
|
# @return [LLM::Transport]
|
|
31
|
-
def resolve_transport(host:, port:, timeout:, ssl:, persistent:, transport:)
|
|
32
|
+
def resolve_transport(host:, port:, timeout:, connect_timeout: 5, ssl:, persistent:, transport:)
|
|
32
33
|
if transport.nil?
|
|
33
|
-
default_transport(host:, port:, timeout:, ssl:, persistent:)
|
|
34
|
+
default_transport(host:, port:, timeout:, connect_timeout:, ssl:, persistent:)
|
|
34
35
|
elsif Class === transport && transport <= LLM::Transport
|
|
35
|
-
transport.new(host:, port:, timeout:, ssl:)
|
|
36
|
+
transport.new(host:, port:, timeout:, connect_timeout:, ssl:)
|
|
36
37
|
elsif Symbol === transport
|
|
37
38
|
transport = LLM::Transport.public_send(transport)
|
|
38
|
-
transport.new(host:, port:, timeout:, ssl:)
|
|
39
|
+
transport.new(host:, port:, timeout:, connect_timeout:, ssl:)
|
|
39
40
|
else
|
|
40
41
|
transport
|
|
41
42
|
end
|
|
@@ -49,12 +50,13 @@ class LLM::Transport
|
|
|
49
50
|
# @param [String] host
|
|
50
51
|
# @param [Integer] port
|
|
51
52
|
# @param [Integer, nil] timeout
|
|
53
|
+
# @param [Integer] connect_timeout
|
|
52
54
|
# @param [Boolean] ssl
|
|
53
55
|
# @param [Boolean] persistent
|
|
54
56
|
# @return [LLM::Transport]
|
|
55
|
-
def default_transport(host:, port:, timeout:, ssl:, persistent:)
|
|
57
|
+
def default_transport(host:, port:, timeout:, connect_timeout:, ssl:, persistent:)
|
|
56
58
|
target = persistent ? LLM::Transport::PersistentHTTP : LLM::Transport::HTTP
|
|
57
|
-
target.new(host:, port:, timeout:, ssl:)
|
|
59
|
+
target.new(host:, port:, timeout:, connect_timeout:, ssl:)
|
|
58
60
|
end
|
|
59
61
|
end
|
|
60
62
|
end
|
data/lib/llm/version.rb
CHANGED
data/lib/llm.rb
CHANGED
|
@@ -77,7 +77,9 @@ module LLM
|
|
|
77
77
|
# @raise [LLM::LoadError]
|
|
78
78
|
# When the dependency cannot be loaded
|
|
79
79
|
def self.require(name, version = nil)
|
|
80
|
-
names = {"xchan" => "xchan.rb",
|
|
80
|
+
names = { "xchan" => "xchan.rb",
|
|
81
|
+
"net/http/persistent" => "net-http-persistent",
|
|
82
|
+
"unicode/display_width" => "unicode-display_width" }
|
|
81
83
|
gem(names[name] || name, version) if version
|
|
82
84
|
super(name)
|
|
83
85
|
rescue ::LoadError
|
|
@@ -260,6 +262,19 @@ module LLM
|
|
|
260
262
|
end
|
|
261
263
|
end
|
|
262
264
|
|
|
265
|
+
##
|
|
266
|
+
# @param key (see LLM::OpenRouter#initialize)
|
|
267
|
+
# @param host (see LLM::OpenRouter#initialize)
|
|
268
|
+
# @return (see LLM::OpenRouter#initialize)
|
|
269
|
+
def openrouter(key: UNDEFINED, **)
|
|
270
|
+
lock(:require) { require_relative "llm/providers/openrouter" unless defined?(LLM::OpenRouter) }
|
|
271
|
+
if key == UNDEFINED
|
|
272
|
+
LLM::OpenRouter.new(key: key(name: __method__), **)
|
|
273
|
+
else
|
|
274
|
+
LLM::OpenRouter.new(key:, **)
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
|
|
263
278
|
##
|
|
264
279
|
# @param key (see LLM::Alibaba#initialize)
|
|
265
280
|
# @param host (see LLM::Alibaba#initialize)
|
data/llm.gemspec
CHANGED
|
@@ -53,7 +53,7 @@ DESCRIPTION
|
|
|
53
53
|
spec.add_development_dependency "yard", "~> 0.9.37"
|
|
54
54
|
spec.add_development_dependency "redcarpet", "~> 3.6"
|
|
55
55
|
spec.add_development_dependency "webrick", "~> 1.8"
|
|
56
|
-
spec.add_development_dependency "test-cmd.rb", "~>
|
|
56
|
+
spec.add_development_dependency "test-cmd.rb", "~> 2.1"
|
|
57
57
|
spec.add_development_dependency "rake", "~> 13.0"
|
|
58
58
|
spec.add_development_dependency "rspec", "~> 3.0"
|
|
59
59
|
spec.add_development_dependency "standard", "~> 1.50"
|
|
@@ -71,4 +71,5 @@ DESCRIPTION
|
|
|
71
71
|
spec.add_development_dependency "curb", "~> 1.3"
|
|
72
72
|
spec.add_development_dependency "curses", "~> 1.6"
|
|
73
73
|
spec.add_development_dependency "kramdown", "~> 2.5"
|
|
74
|
+
spec.add_development_dependency "unicode-display_width", "~> 3.2"
|
|
74
75
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: llm.rb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 15.0
|
|
4
|
+
version: 15.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Robert Gleeson
|
|
@@ -73,14 +73,14 @@ dependencies:
|
|
|
73
73
|
requirements:
|
|
74
74
|
- - "~>"
|
|
75
75
|
- !ruby/object:Gem::Version
|
|
76
|
-
version:
|
|
76
|
+
version: '2.1'
|
|
77
77
|
type: :development
|
|
78
78
|
prerelease: false
|
|
79
79
|
version_requirements: !ruby/object:Gem::Requirement
|
|
80
80
|
requirements:
|
|
81
81
|
- - "~>"
|
|
82
82
|
- !ruby/object:Gem::Version
|
|
83
|
-
version:
|
|
83
|
+
version: '2.1'
|
|
84
84
|
- !ruby/object:Gem::Dependency
|
|
85
85
|
name: rake
|
|
86
86
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -319,6 +319,20 @@ dependencies:
|
|
|
319
319
|
- - "~>"
|
|
320
320
|
- !ruby/object:Gem::Version
|
|
321
321
|
version: '2.5'
|
|
322
|
+
- !ruby/object:Gem::Dependency
|
|
323
|
+
name: unicode-display_width
|
|
324
|
+
requirement: !ruby/object:Gem::Requirement
|
|
325
|
+
requirements:
|
|
326
|
+
- - "~>"
|
|
327
|
+
- !ruby/object:Gem::Version
|
|
328
|
+
version: '3.2'
|
|
329
|
+
type: :development
|
|
330
|
+
prerelease: false
|
|
331
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
332
|
+
requirements:
|
|
333
|
+
- - "~>"
|
|
334
|
+
- !ruby/object:Gem::Version
|
|
335
|
+
version: '3.2'
|
|
322
336
|
description: |
|
|
323
337
|
llm.rb is an advanced runtime for building agentic AI applications on CRuby.
|
|
324
338
|
It has zero runtime dependencies by default, it supports concurrent and parallel
|
|
@@ -346,6 +360,7 @@ files:
|
|
|
346
360
|
- data/mistral.json
|
|
347
361
|
- data/moonshot.json
|
|
348
362
|
- data/openai.json
|
|
363
|
+
- data/openrouter.json
|
|
349
364
|
- data/xai.json
|
|
350
365
|
- data/zai.json
|
|
351
366
|
- docs/deepdive.md
|
|
@@ -551,6 +566,7 @@ files:
|
|
|
551
566
|
- lib/llm/providers/openai/schema.rb
|
|
552
567
|
- lib/llm/providers/openai/stream_parser.rb
|
|
553
568
|
- lib/llm/providers/openai/vector_stores.rb
|
|
569
|
+
- lib/llm/providers/openrouter.rb
|
|
554
570
|
- lib/llm/providers/xai.rb
|
|
555
571
|
- lib/llm/providers/xai/images.rb
|
|
556
572
|
- lib/llm/providers/zai.rb
|