llm.rb 12.4.0 → 12.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cd29af290d188d501b82ef31aae35258d7b6987b14f51bdce68dfbcc3bdca671
4
- data.tar.gz: 4def59348a5f33bd66248e654f75de05adc7bd8d188686c9464bcfca12a61dcb
3
+ metadata.gz: 665281b1c81541ff2f9621138cee510e473dfbda2c7dcff7d1a324e5fe558c89
4
+ data.tar.gz: c3ee596e41885038dc831ef6459da330d1984fea0125a9dbc5aa7d0ff8e21061
5
5
  SHA512:
6
- metadata.gz: da29e5f492f1130249d5db09ce098aff34d6805d16ed1d8e6df120a3038c4e013dc4dc95a16fcf39dd68f5eacc813715a41c1de7b61befa28206914a26c1aa32
7
- data.tar.gz: 310c5dab18e187fd8aabd66c363e192a0b556b7f26efd2d7c553416b60782921da94feec04b834549b023ac5f3064c74e411aa32e5981782b237aabe9938d1fb
6
+ metadata.gz: cecf7fa0a2b1c6aff010c537b6741c74e537104eb9a67e3c2cd2840d06963040834c87263f71f6a1f815a4be831efa7740b2c03eb3f5fde9bbb274be4ee69c5c
7
+ data.tar.gz: 8b695e7c1656bf427bb095a257e954437b5b2dfc46e4d22349eb91dab9981eac1c5fc6b46bca89ecfec14986d386a0fba6303ef4dcf39a82345d3a136e944092
data/CHANGELOG.md CHANGED
@@ -15,8 +15,248 @@
15
15
 
16
16
  ## What's next
17
17
 
18
+ Changes since `v12.5.0`.
19
+
20
+ ## v12.5.0
21
+
18
22
  Changes since `v12.4.0`.
19
23
 
24
+ This release extends the REPL command system with typed parameters, a
25
+ built-in `/help` command, command aliases (`/quit`), and cancellation
26
+ via the 'Esc' key.
27
+
28
+ The default HTTP timeout is increased to 15 minutes (900s) to better
29
+ accommodate reasoning models and large structured outputs.
30
+ `LLM::Agent#deserialize` and `LLM::Agent#restore` now return `self` for
31
+ method chaining, and `LLM::Buffer#pop` is added for tail-end message
32
+ removal.
33
+
34
+ Tool resolution gains a fallback to the global `LLM::Function` registry
35
+ before raising `LLM::NoSuchToolError`, and `pending_functions` aliases
36
+ are added on both contexts and agents for a consistent interface.
37
+
38
+ Several REPL bugs are fixed including parameter state leakage across
39
+ turns and invalid tool-call error routing.
40
+
41
+ Model metadata is refreshed across all providers with new Anthropic,
42
+ OpenAI, Google, DeepInfra, DeepSeek, and xAI model entries.
43
+
44
+ ### Add
45
+
46
+ #### Buffer & function internals
47
+
48
+ * **buffer: add `LLM::Buffer#pop`** <br>
49
+ Add `LLM::Buffer#pop` for removing the last message from the tail
50
+ of the buffer, complementing the existing `#<<` and array-style
51
+ message management.
52
+
53
+ * **function: add registry fallback for tool resolution** <br>
54
+ When resolving tool calls from a message, if the tool is not found
55
+ in the available tools list, it now also looks up the global
56
+ `LLM::Function` registry via `LLM::Function.find_by_name` before
57
+ creating a placeholder function. This improves tool resolution for
58
+ tools that are registered globally but not passed directly through
59
+ the request tool set.
60
+
61
+ #### Consistent `pending_functions` aliases
62
+
63
+ * **context: alias `LLM::Context#functions` as `LLM::Context#pending_functions`** <br>
64
+ Add `LLM::Context#pending_functions` as an alias for `LLM::Context#functions`,
65
+ so callers that prefer the more descriptive `pending_functions` name can use
66
+ it instead of `functions` when checking for unresolved tool work.
67
+
68
+ * **agent: alias `LLM::Agent#functions` as `LLM::Agent#pending_functions`** <br>
69
+ Add `LLM::Agent#pending_functions` as an alias for `LLM::Agent#functions`,
70
+ matching the same alias on `LLM::Context`, so callers have a consistent
71
+ `pending_functions` interface across both contexts and agents.
72
+
73
+ #### REPL command system
74
+
75
+ * **repl: extend command system with parameter support** <br>
76
+ Commands can now declare typed parameters using the `parameter`
77
+ DSL, modelled after `LLM::Tool` and `LLM::Schema` conventions.
78
+ Parameters can be marked as required with `required %i[...]`,
79
+ and values are type-checked before being passed to `call`.
80
+ Argument parsing is handled by the repl: arguments are split
81
+ from the input string and assigned to parameters by position.
82
+
83
+ ```ruby
84
+ class Greeter < LLM::Command
85
+ name "greet"
86
+ description "Greets the given name"
87
+ parameter :name, String, "The person's name"
88
+ required %i[name]
89
+
90
+ def call(name:)
91
+ write("Welcome #{name}!\n")
92
+ end
93
+ end
94
+ ```
95
+
96
+ * **repl: add `help` command** <br>
97
+ Add `LLM::Repl::Help` as a new built-in command, registered
98
+ automatically via the command registry. Typing `/help` shows
99
+ the `help` command's own name, description, and parameters,
100
+ while `/help <name>` shows details for a specific command,
101
+ including its parameters and whether each is required or
102
+ optional. Unknown command names produce an error message.
103
+
104
+ ```ruby
105
+ class Help < Command
106
+ name "help"
107
+ description "show help for a given command"
108
+ parameter :name, String, "The name of a command"
109
+
110
+ def call(name: nil)
111
+ if name.nil?
112
+ write("\n#{self.class.help}\n\n")
113
+ elsif command = LLM::Command.find_by(name:)
114
+ write("\n#{command.help}\n\n")
115
+ else
116
+ write "\nNo help for #{name} was found" \
117
+ "\nThat command doesn't exist.\n\n"
118
+ end
119
+ end
120
+ end
121
+ ```
122
+
123
+ * **repl: add support for command aliases** <br>
124
+ Commands can now be aliased by creating a subclass of another
125
+ command (with `LLM::Command` as an indirect ancestor). The
126
+ first alias introduced is `/quit` as an alias of `/exit`.
127
+
128
+ ```ruby
129
+ class Quit < Command::Exit
130
+ name "quit"
131
+ end
132
+ ```
133
+
134
+ * **repl: add `Command::Parameter#optional?`** <br>
135
+ Parameters now expose an `#optional?` method that returns `true`
136
+ when a parameter has not been marked as required, making it
137
+ possible to query parameter optionality programmatically.
138
+
139
+ * **repl: add `LLM::Repl::Command#write`** <br>
140
+ Commands can now write output to the transcript via the `write`
141
+ method. Commands also receive a reference to the active repl
142
+ through their `#initialize` method, making it possible to
143
+ interact with the repl window from within a command.
144
+
145
+ * **repl: display command errors in the curses UI** <br>
146
+ Commands invoked with too few arguments now display an error
147
+ message — `command(<name>): too few arguments` — directly in
148
+ the curses transcript area, giving immediate feedback instead
149
+ of silently failing.
150
+
151
+ * **repl: add `LLM::Command` convenience constant** <br>
152
+ Add `LLM::Command = LLM::Repl::Command` as a shorter alias,
153
+ available once `"llm/repl"` is required.
154
+
155
+ #### Misc
156
+
157
+ * **repl: implement cancellation with the 'Esc' key** <br>
158
+ The curses-based REPL now supports cancelling an active model
159
+ request by pressing the 'Esc' key. When a request is in progress,
160
+ the status line shows `thinking • Esc to cancel`, and pressing
161
+ Esc calls `LLM::Agent#cancel!` to interrupt the request. The
162
+ transcript displays `request cancelled!` to confirm the
163
+ cancellation.
164
+
165
+ ### Change
166
+
167
+ #### Misc
168
+
169
+ * **provider: increase default timeout to 900s** <br>
170
+ The default HTTP timeout for all providers has been increased from
171
+ 180 to 900 seconds (15 minutes) to better accommodate long-running
172
+ requests such as reasoning models and large structured outputs.
173
+
174
+ * **agent: `deserialize` and `restore` return `self`** <br>
175
+ `LLM::Agent#deserialize` and `LLM::Agent#restore` now return `self`
176
+ (the agent instance) instead of forwarding the context's return
177
+ value, enabling method chaining after restoring agent state.
178
+
179
+ * **context: discard all messages from a cancelled turn** <br>
180
+ When `LLM::Context#cancel!` is called, all messages added during
181
+ that turn are now discarded via `Buffer#slice!`, preventing edge
182
+ cases where dangling tool calls between turns caused repeated
183
+ cancellation loops. The `#repair!` method now handles tool call
184
+ cancellations on the next turn instead of mutating the conversation
185
+ buffer directly at cancellation time.
186
+
187
+ * **stream: drop the `error` argument from `on_tool_call`** <br>
188
+ The `on_tool_call` callback no longer accepts an `error` argument.
189
+ Previously, stream parsers passed both a tool and an optional error,
190
+ requiring boilerplate like `if error; queue << error; end` in every
191
+ callback. Error handling is now pushed directly onto the stream queue
192
+ inside each provider's stream parser, so `on_tool_call(tool)` is the
193
+ only signature. The REPL stream and base `LLM::Stream` class have
194
+ been updated accordingly.
195
+
196
+ #### REPL internals
197
+
198
+ * **repl: pass the repl instance to command constructors** <br>
199
+ `LLM::Repl::Command` subclasses now receive the active repl
200
+ instance via `initialize(repl)`, enabling commands to write
201
+ to the transcript and interact with the repl window.
202
+
203
+ * **repl: `Command#write` prefixes messages with the command name** <br>
204
+ The `#write` method now prefixes output with `command(<name>): `
205
+ so command messages are consistent with the `user:` and `agent:`
206
+ labels in the transcript. The prefix can be customised with the
207
+ `who:` keyword argument, or set to `who: nil` to disable it
208
+ entirely.
209
+
210
+ ### Fix
211
+
212
+ #### Misc
213
+
214
+ * **function: avoid silent skip of tools not found in available tools** <br>
215
+ When a model calls a tool that is not present in the available tools
216
+ list, instead of silently skipping the tool call (via `next`), a
217
+ `LLM::NoSuchToolError` is now raised so the model receives feedback
218
+ about the invalid tool call and can correct course.
219
+ <br><br>
220
+ An additional fallback to the global `LLM::Function` registry is
221
+ tried before raising, so globally registered tools are still
222
+ resolved even when not in the per-request tool set.
223
+
224
+ #### REPL bugs
225
+
226
+ * **repl: don't persist parameter state between turns** <br>
227
+ Parameter state (such as `Parameter#value`) was leaking across
228
+ turns because the same parameter objects were being mutated
229
+ in place. A duplicate set of parameters is now created for each
230
+ turn, keeping the original parameter definitions intact and
231
+ preventing stale state from carrying over.
232
+
233
+ * **repl: reply with error when given an invalid tool** <br>
234
+ When the model tries to call a tool that does not exist, the
235
+ error is now pushed onto the stream queue so the model can
236
+ see the error and correct course, instead of silently dropping
237
+ the invalid tool call and leaving it to `Context#repair` to
238
+ remove it from history.
239
+
240
+ * **repl: fix save of initial runtime state** <br>
241
+ Fix a bug in `LLM::Repl#configure` where a non-existent path
242
+ argument was treated as no path at all, preventing the initial
243
+ runtime state from being saved after the first turn. The correct
244
+ behavior is to create the file so it can be written to after
245
+ the first turn completes.
246
+
247
+ ### Refresh
248
+
249
+ * **Refresh model metadata across all providers** <br>
250
+ Update model listings, pricing, capabilities, reasoning options,
251
+ modality support, context limits, and release dates across all
252
+ provider registries (Anthropic, AWS Bedrock, DeepInfra, DeepSeek,
253
+ Google, Mistral, OpenAI, xAI, and ZAI). Notable changes include
254
+ Anthropic claude-opus-4-8 and claude-sonnet-4-6 additions with
255
+ effort-based reasoning, OpenAI gpt-5.6-sol/terra/luna and
256
+ gpt-5-codex additions, Google gemini-3-pro-preview and
257
+ gemini-3-flash-preview additions, DeepInfra Qwen3.5 and DeepSeek
258
+ V4 model additions, and updated xAI Grok model entries.
259
+
20
260
  ## v12.4.0
21
261
 
22
262
  Changes since `v12.3.1`.
data/README.md CHANGED
@@ -141,6 +141,8 @@ instance. The session inherits the agent's model, tools,
141
141
  skills, and instructions.
142
142
 
143
143
  ```ruby
144
+ require "llm"
145
+
144
146
  llm = LLM.deepseek(key: ENV["KEY"])
145
147
  agent = LLM::Agent.new(llm)
146
148
  agent.repl
@@ -153,6 +155,8 @@ is read from and written to. This lets you resume a
153
155
  conversation across REPL sessions.
154
156
 
155
157
  ```ruby
158
+ require "llm"
159
+
156
160
  llm = LLM.deepseek(key: ENV["KEY"])
157
161
  agent = LLM::Agent.new(llm)
158
162
  agent.repl(path: "session.json")
@@ -169,6 +173,8 @@ llm.rb. They power the agents that can be found in the
169
173
  [agents/](agents/) directory.
170
174
 
171
175
  ```ruby
176
+ require "llm"
177
+
172
178
  llm = LLM.deepseek(key: ENV["KEY"])
173
179
  agent = LLM::Agent.new(llm)
174
180
  agent.repl(tools: [Debugger])
@@ -192,6 +198,8 @@ The `skills` option lets you load extra skill directories
192
198
  without attaching them to an agent permanently.
193
199
 
194
200
  ```ruby
201
+ require "llm"
202
+
195
203
  llm = LLM.deepseek(key: ENV["KEY"])
196
204
  agent = LLM::Agent.new(llm)
197
205
  agent.repl(skills: [__dir__])
@@ -205,11 +213,39 @@ the tracer associated with an instance of
205
213
  [`LLM::Agent`](https://r.uby.dev/api-docs/llm.rb/LLM/Agent.html).
206
214
 
207
215
  ```ruby
208
- llm = LLM.deepseek(key: ENV["KEY"])
209
- agent = LLM::Agent.new(llm, tracer: LLM.logger(llm, path: "agent.log"))
216
+ require "llm"
217
+
218
+ llm = LLM.deepseek(key: ENV["KEY"])
219
+ tracer = LLM.logger(llm, path: "agent.log")
220
+ agent = LLM::Agent.new(llm, tracer:)
210
221
  agent.repl(tracer: true, tools: [Debugger])
211
222
  ```
212
223
 
224
+ ##### REPL: Commands
225
+
226
+ Commands are recognized by a `/` prefix and are backed by the
227
+ [`LLM::Repl::Command`](https://r.uby.dev/api-docs/llm.rb/LLM/Repl/Command.html)
228
+ class, which can be subclassed to add custom commands. Once you
229
+ create a subclass, it is automatically added to the repl. A command
230
+ can have zero or more parameters, and all parameters are presumed
231
+ to be a String (at least for now).
232
+
233
+ ```ruby
234
+ require "llm"
235
+ require "llm/repl"
236
+
237
+ class Greeter < LLM::Command
238
+ name "greet"
239
+ description "Greets the given name"
240
+ parameter :name, String, "The person's name"
241
+ required %i[name]
242
+
243
+ def call(name:)
244
+ write("Welcome #{name}!\n")
245
+ end
246
+ end
247
+ ```
248
+
213
249
  ##### REPL: Input
214
250
 
215
251
  The input area supports several keyboard shortcuts:
@@ -227,12 +263,6 @@ The input area supports several keyboard shortcuts:
227
263
  | `Up / Down` | Scroll the transcript |
228
264
  | `/exit` | Leave the REPL |
229
265
 
230
- ##### REPL: Commands
231
-
232
- Commands are recognized by a `/` prefix and are backed by the
233
- [`LLM::Repl::Command`](https://r.uby.dev/api-docs/llm.rb/LLM/Repl/Command.html)
234
- class, which can be subclassed to add custom commands.
235
-
236
266
  #### LLM::MCP
237
267
 
238
268
  The Model Context Protocol (MCP) has first-class support