llm.rb 16.0.0.pre.6 → 16.0.0.pre.8
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 +129 -1919
- data/README.md +18 -8
- data/docs/deepdive/advanced/cancellation.md +40 -8
- data/docs/deepdive/features/database.md +28 -25
- data/docs/deepdive/fundamentals/agents.md +3 -3
- data/docs/deepdive/reference/tracer.md +9 -7
- data/lib/llm/active_record/acts_as_agent.rb +8 -1
- data/lib/llm/active_record/acts_as_llm.rb +8 -10
- data/lib/llm/active_record.rb +1 -2
- data/lib/llm/agent/interrupt.rb +77 -0
- data/lib/llm/agent.rb +32 -0
- data/lib/llm/context.rb +32 -3
- data/lib/llm/error.rb +13 -2
- data/lib/llm/function/async/task.rb +38 -17
- data/lib/llm/function/fiber/task.rb +98 -12
- data/lib/llm/function/fork/task.rb +19 -5
- data/lib/llm/function/thread/task.rb +31 -6
- data/lib/llm/sequel/plugin.rb +9 -12
- data/lib/llm/tracer.rb +6 -3
- data/lib/llm/version.rb +1 -1
- metadata +2 -1
data/CHANGELOG.md
CHANGED
|
@@ -13,6 +13,9 @@
|
|
|
13
13
|
> Changelog <br>
|
|
14
14
|
> [r.uby.dev](https://r.uby.dev) project
|
|
15
15
|
|
|
16
|
+
This file covers the v15 series and the unreleased changes that follow it.
|
|
17
|
+
Releases before v15 are kept in [changelog/old.md](changelog/old.md).
|
|
18
|
+
|
|
16
19
|
## What's next
|
|
17
20
|
|
|
18
21
|
### Breaking
|
|
@@ -23,251 +26,207 @@
|
|
|
23
26
|
|-----|-----|
|
|
24
27
|
| Ruby 3.3 or later | Ruby 3.4 or later |
|
|
25
28
|
| `record.messages` returns the messages the runtime holds | a `:jsonb` record returns a relation of `LLM::ActiveRecord::Message` rows |
|
|
29
|
+
| a `set_tracer` method on an ORM model | `set tracer:` in the block, or `tracer:` on the wrapper |
|
|
30
|
+
| a `:thread` or `:fiber` cancel that arrives before the tool starts is dropped | the cancel is held and raised once the tool starts |
|
|
31
|
+
| a cancel that arrives between two requests does nothing | the turn ends there, and the caller sees `LLM::Interrupt` |
|
|
32
|
+
| a bare `rescue`, or `rescue => ex`, catches a cancel | `LLM::Interrupt` is outside `StandardError`, so both forms pass it through |
|
|
26
33
|
|
|
27
34
|
* **drop Ruby 3.3 support** <br>
|
|
28
|
-
The gem now requires Ruby 3.4 or later
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
cancel that arrives before the tool starts is not delivered there, while
|
|
32
|
-
Ruby 3.4 and 4.0 both deliver it.
|
|
35
|
+
The gem now requires Ruby 3.4 or later. Ruby 3.3 cannot hold an
|
|
36
|
+
interrupt for a ractor-backed tool call, so a cancel can arrive
|
|
37
|
+
before the tool starts and never be delivered.
|
|
33
38
|
|
|
34
39
|
* **activerecord: read a `:jsonb` record's messages from the column** <br>
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
is
|
|
44
|
-
|
|
45
|
-
`
|
|
46
|
-
|
|
47
|
-
|
|
40
|
+
The `#messages` of a `:jsonb` record is now a relation over the stored
|
|
41
|
+
messages, so a conversation can be filtered and counted in SQL without a
|
|
42
|
+
provider or credentials. Before, every record loaded the runtime; reach
|
|
43
|
+
that again with `#messages!`, and order the relation by `position`
|
|
44
|
+
because it is unordered.
|
|
45
|
+
|
|
46
|
+
* **function: hold a cancel on `:thread` and `:fiber` instead of dropping it** <br>
|
|
47
|
+
A cancel that arrived before the tool started used to be dropped, and the call
|
|
48
|
+
ran to completion. It is now held and raised once the tool starts, so a caller
|
|
49
|
+
that cancelled a task it had not yet spawned sees
|
|
50
|
+
[`LLM::Interrupt`](https://r.uby.dev/api-docs/llm.rb/LLM/Interrupt.html) where
|
|
51
|
+
it used to see a return.
|
|
52
|
+
|
|
53
|
+
* **interrupt: reach a turn that is between its requests** <br>
|
|
54
|
+
A cancel that arrived between two requests used to do nothing, because
|
|
55
|
+
there was nothing in flight to close and nothing running to raise into.
|
|
56
|
+
It now ends the turn where it is, raising `LLM::Interrupt` into the
|
|
57
|
+
caller the turn runs under and announcing `scope: :agent` to the tracer
|
|
58
|
+
first. `LLM::Agent#run_loop` records that caller for as long as the turn
|
|
59
|
+
lasts, and it answers `interrupt!` through
|
|
60
|
+
[`LLM::Agent::Interrupt`](https://r.uby.dev/api-docs/llm.rb/LLM/Agent/Interrupt.html).
|
|
61
|
+
The raise lands in the thread or fiber the turn runs on, so it unwinds out
|
|
62
|
+
of the turn to whoever called it.
|
|
63
|
+
|
|
64
|
+
* **interrupt: move `LLM::Interrupt` outside `StandardError`** <br>
|
|
65
|
+
`LLM::Interrupt` is now an `Exception` rather than an `LLM::Error`, so a
|
|
66
|
+
bare `rescue` and a `rescue => ex` pass a cancel through instead of
|
|
67
|
+
swallowing it. Before, a broad rescue in a tool, a tracer, or an
|
|
68
|
+
application could eat an interrupt, and the turn looked like one that had
|
|
69
|
+
ignored its cancel. It is not a `SignalException`, since an interrupt has
|
|
70
|
+
to be catchable and a signal is a framework's own condition. A caller that
|
|
71
|
+
handles a cancel names `LLM::Interrupt`; `#wait` and the tool's
|
|
72
|
+
`on_interrupt` hook are unchanged, and a task still carries the interrupt
|
|
73
|
+
back to the caller rather than raising it inside its reactor.
|
|
48
74
|
|
|
49
75
|
### ActiveRecord
|
|
50
76
|
|
|
51
77
|
* **activerecord: add `messages!` for the runtime's own messages** <br>
|
|
52
78
|
[`LLM::ActiveRecord#messages!`](https://r.uby.dev/api-docs/llm.rb/LLM/ActiveRecord.html#messages!-instance_method)
|
|
53
|
-
returns the messages the runtime holds
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
answers to `#messages!` too, where it is the same call as `#messages`.
|
|
79
|
+
returns the messages the runtime holds, including state that has not been
|
|
80
|
+
saved, where `#messages` reads the column. Use it to reach the live
|
|
81
|
+
conversation of a `:jsonb` record. The Sequel plugin answers to
|
|
82
|
+
`#messages!` too, where it is the same call as `#messages`.
|
|
58
83
|
|
|
59
84
|
### Agent
|
|
60
85
|
|
|
61
86
|
* **agent: keep the instructions it injected in step** <br>
|
|
62
|
-
|
|
63
|
-
the instructions it injected up to date before a request goes out, so a
|
|
87
|
+
An agent now refreshes its instructions before each request, so a
|
|
64
88
|
conversation restored from saved state runs on the agent's current
|
|
65
|
-
instructions
|
|
66
|
-
|
|
67
|
-
never replaced, and the mark is written by
|
|
68
|
-
[`LLM::Message#to_h`](https://r.uby.dev/api-docs/llm.rb/LLM/Message.html#to_h-instance_method)
|
|
69
|
-
and read back on a restore, which is the conversation the refresh exists
|
|
70
|
-
for. Before this, the instructions were injected once and the message
|
|
71
|
-
stayed as it was for the life of the conversation.
|
|
89
|
+
instructions instead of the ones it was saved with. A message the caller
|
|
90
|
+
composed is never touched.
|
|
72
91
|
|
|
73
92
|
* **agent: identify its own instructions by the mark, not by the role** <br>
|
|
74
|
-
Fix a bug where
|
|
75
|
-
|
|
76
|
-
instructions the `user` role
|
|
77
|
-
|
|
78
|
-
turn. It now asks the provider which role instructions are given, and
|
|
79
|
-
identifies the message it wrote by its mark, because on Google the message
|
|
80
|
-
a caller seeds a conversation with is a user message too.
|
|
93
|
+
Fix a bug where an agent on Google added a fresh copy of its instructions
|
|
94
|
+
every turn, because it looked for a `system` message and Google gives
|
|
95
|
+
instructions the `user` role. It now marks the message it wrote and finds
|
|
96
|
+
it by that mark.
|
|
81
97
|
|
|
82
98
|
### Console
|
|
83
99
|
|
|
84
100
|
* **console: erase with the backspace key on OpenBSD** <br>
|
|
85
|
-
The console now erases
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
terminal's terminfo describes the key. All three erase. Before this only 127
|
|
89
|
-
did, so the backspace key did nothing on the OpenBSD console.
|
|
101
|
+
The console now erases for every code a backspace key sends: DEL (127),
|
|
102
|
+
^H (8), and curses's `KEY_BACKSPACE`. Before, only 127 worked, so
|
|
103
|
+
backspace did nothing on the OpenBSD console.
|
|
90
104
|
|
|
91
105
|
### Fix
|
|
92
106
|
|
|
93
107
|
* **fork: require xchan.rb `~> 0.24`** <br>
|
|
94
|
-
The `:fork`
|
|
95
|
-
instead of `~> 0.23`, and the gemspec's development dependency follows. An
|
|
96
|
-
application that uses `:fork` needs `xchan.rb` 0.24 or later.
|
|
108
|
+
The `:fork` strategy now needs `xchan.rb` 0.24 or later, up from 0.23.
|
|
97
109
|
|
|
98
110
|
### Function
|
|
99
111
|
|
|
100
112
|
* **function: deliver an interrupt to the tool, not to whatever is running** <br>
|
|
101
|
-
|
|
102
|
-
is the stretch of a call that an interrupt belongs to. The `:fork` and `:ractor`
|
|
103
|
-
strategies now raise
|
|
113
|
+
A cancel now raises
|
|
104
114
|
[`LLM::Interrupt`](https://r.uby.dev/api-docs/llm.rb/LLM/Interrupt.html) on the
|
|
105
|
-
thread
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
child process's or the ractor's main thread wherever that thread had got to.
|
|
115
|
+
thread running the tool, and only while it runs: arriving early, it is held
|
|
116
|
+
until the tool starts, so the tool's own `rescue` sees it; arriving late, it
|
|
117
|
+
is a no-op instead of throwing the result away. Applies to `:fork` and
|
|
118
|
+
`:ractor`.
|
|
110
119
|
|
|
111
120
|
* **function: answer a ractor-backed task once its ractor has gone** <br>
|
|
112
|
-
A `:ractor` tool's result is
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
the way
|
|
117
|
-
[`LLM::Function::Thread::Task#wait`](https://r.uby.dev/api-docs/llm.rb/LLM/Function/Thread/Task.html#wait-instance_method)
|
|
118
|
-
answers from the thread's value, and `alive?` answers `false` once the task
|
|
119
|
-
has been waited on. A cancel returns `nil` rather than raising, which is what
|
|
120
|
-
lets
|
|
121
|
-
[`LLM::Function::Ractor::Group#interrupt!`](https://r.uby.dev/api-docs/llm.rb/LLM/Function/Ractor/Group.html#interrupt!-instance_method)
|
|
122
|
-
reach every task it holds. Before this, a wait that arrived once the tool had
|
|
123
|
-
run raised `Ractor::ClosedError` or never came back, a cancel raised the same
|
|
124
|
-
error, and a group's cancel stopped at the first task that had already
|
|
125
|
-
returned, leaving the calls after it uncancelled.
|
|
121
|
+
A `:ractor` tool's result is now held by the task instead of asked of the
|
|
122
|
+
ractor that ran it, so a late wait is answered, a second wait comes from
|
|
123
|
+
memory, and a cancel returns `nil` instead of raising. A group's cancel now
|
|
124
|
+
reaches every task, not just the ones still running.
|
|
126
125
|
|
|
127
126
|
* **function: tell an `:async` tool it was cancelled** <br>
|
|
128
|
-
A cancel now raises `LLM::Interrupt` on the fiber the tool runs in,
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
reactor is stopped by whoever waits. Before this, the cancel pushed an
|
|
132
|
-
interrupt sentinel to the task's result queue and left the tool running, its
|
|
133
|
-
side effects happening and its result written to a queue nobody read, and the
|
|
134
|
-
reactor thread stayed alive until a group's `wait` stopped it.
|
|
127
|
+
A cancel now raises `LLM::Interrupt` on the fiber the tool runs in, so the
|
|
128
|
+
tool is told and can clean up. Before, the cancel was queued and the tool
|
|
129
|
+
kept running, its result written to a queue nobody read.
|
|
135
130
|
|
|
136
131
|
* **function: run the interrupt hook on every strategy** <br>
|
|
137
132
|
A tool that implements
|
|
138
133
|
[`LLM::Tool#on_interrupt`](https://r.uby.dev/api-docs/llm.rb/LLM/Tool.html#on_interrupt-instance_method)
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
134
|
+
is now told on every concurrency strategy. Before, `:async`, `:fork`, and
|
|
135
|
+
`:ractor` never ran it, and a class-backed tool ran it on no strategy at all.
|
|
136
|
+
|
|
137
|
+
* **function: hold a cancel that arrives before a thread or fiber starts** <br>
|
|
138
|
+
Fix a bug where a cancel that arrived before a `:thread` or `:fiber` tool
|
|
139
|
+
started was dropped, and the call ran to completion as if nobody had asked it
|
|
140
|
+
to stop. The cancel is now held on the task and raised once the body is live,
|
|
141
|
+
so the tool's `on_interrupt` hook runs and the caller sees `LLM::Interrupt`.
|
|
142
|
+
|
|
143
|
+
* **function: answer a task that has already answered** <br>
|
|
144
|
+
A second `#wait` on an `:async` or `:fork` task is answered from what the
|
|
145
|
+
first one took, which is what `Thread#value` does, so a task that has answered
|
|
146
|
+
answers again. Before, a second wait blocked on a queue nothing would fill, or
|
|
147
|
+
read a channel the task had already closed. An interrupt re-raises the same
|
|
148
|
+
exception rather than blocking.
|
|
150
149
|
|
|
151
150
|
### ORM
|
|
152
151
|
|
|
153
152
|
* **orm: save a conversation after each request, not once per turn** <br>
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
an
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
once, when the whole turn had finished, so an interrupted turn persisted
|
|
166
|
-
nothing.
|
|
153
|
+
A record-backed conversation is now written down as it goes, so an
|
|
154
|
+
interrupted turn can be continued instead of started over. Before, the
|
|
155
|
+
wrappers saved once when the whole turn finished, so an interrupted turn
|
|
156
|
+
persisted nothing.
|
|
157
|
+
|
|
158
|
+
* **orm: drop the `set_tracer` callback** <br>
|
|
159
|
+
The wrappers no longer resolve a tracer from a `set_tracer` method on the
|
|
160
|
+
model. Declare it where an agent's other defaults are declared, with
|
|
161
|
+
`set tracer:` in the block, or pass it to the wrapper as `tracer:`, which
|
|
162
|
+
is what a context has. A model that implements `set_tracer` today loses
|
|
163
|
+
its tracer silently.
|
|
167
164
|
|
|
168
165
|
### Prompt
|
|
169
166
|
|
|
170
167
|
* **prompt: let a message carry fields a caller attaches to it** <br>
|
|
171
168
|
[`LLM::Prompt#system`](https://r.uby.dev/api-docs/llm.rb/LLM/Prompt.html#system-instance_method),
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
along with its `#chat` alias, now take an `extra:` keyword, so a message a
|
|
176
|
-
caller composes can carry fields of its own. The runtime reads those fields
|
|
177
|
-
back rather than inferring provenance from a role, which is how an agent
|
|
178
|
-
tells the instructions it injected from a message the caller wrote.
|
|
169
|
+
`#user`, `#developer`, and `#talk` (and its `#chat` alias) now take an
|
|
170
|
+
`extra:` keyword, so a caller can tag a message with fields of its own. The
|
|
171
|
+
runtime reads those fields back instead of guessing provenance from the role.
|
|
179
172
|
|
|
180
173
|
### Provider
|
|
181
174
|
|
|
182
175
|
* **deepseek: support image attachments in chat completions** <br>
|
|
183
|
-
DeepSeek's vision models now
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
`LLM::Response` raises
|
|
176
|
+
DeepSeek's vision models now accept an image. A `:image_url` object is sent
|
|
177
|
+
as an `image_url` item, and a local image file as a base64 data URI.
|
|
178
|
+
Anything else is rejected with a reason: a remote file or an `LLM::Response`
|
|
179
|
+
raises
|
|
188
180
|
[`LLM::PromptError`](https://r.uby.dev/api-docs/llm.rb/LLM/PromptError.html)
|
|
189
|
-
because DeepSeek has no Files API,
|
|
190
|
-
|
|
191
|
-
only.
|
|
181
|
+
because DeepSeek has no Files API, and a non-image local file raises because
|
|
182
|
+
its models read images only.
|
|
192
183
|
|
|
193
184
|
### Registry
|
|
194
185
|
|
|
195
186
|
* **refresh model metadata** <br>
|
|
196
|
-
Update `data/` with current
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
`gpt-daybreak-blue-latest`, `gpt-daybreak-red-latest`, and `gpt-6.1-sol`, and
|
|
200
|
-
gives `gpt-6-astra` an `ultrafast` mode; Bedrock adds `openai.gpt-6-sol`,
|
|
201
|
-
`openai.gpt-6-luna`, `global.anthropic.claude-sonnet-5-5`,
|
|
202
|
-
`anthropic.claude-sonnet-5-5`, and the Grok 4.7 entries in both the global
|
|
203
|
-
and US regions; DeepInfra adds `tencent/Hy4-preview` and the Xiaomi MiMo
|
|
204
|
-
V2.6 Pro and V2.6 Flash models, and marks four older entries deprecated.
|
|
205
|
-
Bedrock also corrects the context and output limits of fifteen models, Google
|
|
206
|
-
lowers the limits of `gemini-2.5-computer-use-preview-10-2025` and
|
|
207
|
-
`gemini-3-pro-image`, and OpenRouter adds seven models, `openai/gpt-6.1-sol`
|
|
208
|
-
and `openai/gpt-6.1-sol-pro` among them, drops six, and reprices or corrects
|
|
209
|
-
the limits of many of the DeepSeek, Z.ai, Qwen, and Tencent entries.
|
|
187
|
+
Update `data/` with current listings, limits, and pricing. Alibaba, OpenAI,
|
|
188
|
+
Bedrock, DeepInfra, Google, and OpenRouter gain models and correct limits or
|
|
189
|
+
prices, and OpenRouter drops six entries.
|
|
210
190
|
|
|
211
191
|
### Schema
|
|
212
192
|
|
|
213
193
|
* **schema: apply every `parameter` option to the leaf** <br>
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
the range. A `default:` of `false` survives now as well, where the old code
|
|
220
|
-
discarded a false default, and `required: false` marks the parameter optional
|
|
221
|
-
rather than being ignored.
|
|
194
|
+
Every option given to `parameter` now reaches the schema, so `min:`, `max:`,
|
|
195
|
+
`multiple_of:`, and `const:` are no longer dropped, a `default: false`
|
|
196
|
+
survives, and `required: false` marks a parameter optional. Before, only
|
|
197
|
+
`required`, `default`, and `enum` were read, so a tool that declared a range
|
|
198
|
+
sent a plain number and lost it.
|
|
222
199
|
|
|
223
200
|
### Stream
|
|
224
201
|
|
|
225
202
|
* **stream: add `on_step`, called when a request completes** <br>
|
|
226
203
|
[`LLM::Stream#on_step`](https://r.uby.dev/api-docs/llm.rb/LLM/Stream.html#on_step-instance_method)
|
|
227
|
-
is a new callback,
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
checkpoint the conversation at, because a provider will accept it again
|
|
232
|
-
there. It fires once per successful request, so a request retried after a
|
|
233
|
-
rate limit or a timeout calls it when it lands and not once per attempt, and
|
|
234
|
-
it fires even when the stream is disabled.
|
|
204
|
+
is a new callback, fired once the prompt and response are in the
|
|
205
|
+
conversation. It marks where one request ends and the next begins, which is
|
|
206
|
+
where a stream can checkpoint, and it fires once per successful request even
|
|
207
|
+
when streaming is off.
|
|
235
208
|
|
|
236
209
|
### Tracers
|
|
237
210
|
|
|
238
211
|
* **tracer: report a request that failed** <br>
|
|
239
212
|
[`LLM::Tracer#on_request_error`](https://r.uby.dev/api-docs/llm.rb/LLM/Tracer.html#on_request_error-instance_method)
|
|
240
|
-
is now called
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
turned an error response into an exception, so a dropped connection or a
|
|
244
|
-
socket read that failed left the tracer holding a span it had opened and never
|
|
245
|
-
closed, which in a trace reads the same as a process that died. A failure that
|
|
246
|
-
surfaces as one of the transport's own error classes is reported the same way
|
|
247
|
-
unless the request was interrupted, and the exception the caller receives is
|
|
248
|
-
unchanged.
|
|
213
|
+
is now called whenever a request ends without a response, so a span is always
|
|
214
|
+
closed. Before, only provider errors that became exceptions were reported, so
|
|
215
|
+
a dropped connection left a span open, which reads like a process that died.
|
|
249
216
|
|
|
250
217
|
* **tracer: add `on_interrupt`, called when a request is interrupted** <br>
|
|
251
218
|
[`LLM::Tracer#on_interrupt`](https://r.uby.dev/api-docs/llm.rb/LLM/Tracer.html#on_interrupt-instance_method)
|
|
252
|
-
is a new hook, called with
|
|
253
|
-
`LLM::Interrupt` reaches the caller, so a tracer
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
delivered to whatever tracer happens to be bound, and a hook that raised would
|
|
259
|
-
replace the interrupt every caller is written against. A request is announced
|
|
260
|
-
with the span that `on_request_start` returned and its `request_id`; a tool
|
|
261
|
-
pass has neither, so both arrive as `nil`.
|
|
219
|
+
is a new hook, called with `scope: :request`, `:tool`, or `:agent` before
|
|
220
|
+
`LLM::Interrupt` reaches the caller, so a tracer can record an interrupted
|
|
221
|
+
turn. An `:agent` scope means the interrupt landed between a turn's
|
|
222
|
+
requests, where there was nothing more precise to interrupt. It does
|
|
223
|
+
nothing by default, since a raise here would replace the interrupt
|
|
224
|
+
callers are written against.
|
|
262
225
|
|
|
263
226
|
* **tracer: announce an interrupted tool phase once** <br>
|
|
264
227
|
[`LLM::Context#wait`](https://r.uby.dev/api-docs/llm.rb/LLM/Context.html#wait-instance_method)
|
|
265
|
-
now calls
|
|
266
|
-
per tool,
|
|
267
|
-
hears a single exception, so the announcement belongs to the phase rather than
|
|
268
|
-
to a tool. The hook runs before the caller is given the interrupt. A `:ractor`
|
|
269
|
-
call that answers `cancelled: true` instead of raising is not announced,
|
|
270
|
-
because the phase was not interrupted; one call was retired.
|
|
228
|
+
now calls `on_interrupt` once when an interrupt unwinds through it, rather
|
|
229
|
+
than once per tool, matching the single exception the caller receives.
|
|
271
230
|
|
|
272
231
|
## v15.5.0
|
|
273
232
|
|
|
@@ -1482,1752 +1441,3 @@ rate-limited requests, and a range of REPL and skills improvements.
|
|
|
1482
1441
|
Update `data/deepinfra.json` with current pricing for the DeepSeek
|
|
1483
1442
|
V4, DeepSeek-V3, DeepSeek-R1-0528, and Kimi-K3 models, and mark
|
|
1484
1443
|
`structured_output` support for one model.
|
|
1485
|
-
|
|
1486
|
-
## v14.0.0
|
|
1487
|
-
|
|
1488
|
-
Changes since `v13.1.0`.
|
|
1489
|
-
|
|
1490
|
-
This release replaces the `transformer=` setter with the new
|
|
1491
|
-
`LLM::Transformer` class hierarchy, refactors guards into the
|
|
1492
|
-
`LLM::Guard` superclass with per-tool-call interception, and replaces
|
|
1493
|
-
the agent `tool_attempts` parameter with the `tool_budget` class DSL.
|
|
1494
|
-
It also adds the Moonshot (Kimi) provider, the `LLM::Tool.set`
|
|
1495
|
-
bulk-assignment DSL, a `LLM::Function#return` shorthand, and a wide
|
|
1496
|
-
range of REPL improvements.
|
|
1497
|
-
|
|
1498
|
-
### Breaking
|
|
1499
|
-
|
|
1500
|
-
#### Migration
|
|
1501
|
-
|
|
1502
|
-
| Old | New |
|
|
1503
|
-
|-----|-----|
|
|
1504
|
-
| `ctx.transformer = MyTransformer` | `LLM::Context.new(transformer: MyTransformer)` |
|
|
1505
|
-
| `transformer.call(ctx, prompt, params)` | `transformer.call(message:, **opts)` |
|
|
1506
|
-
| `~/.llm.rb/session.json` (shared across providers) | `~/.llm.rb/<provider>/<uuid>.json` (scoped per provider and directory) |
|
|
1507
|
-
| `agent.talk(tool_attempts: 25)` | `set :tool_budget => 50` (disabled by default) |
|
|
1508
|
-
| `LLM::LoopGuard` | `LLM::Guard::Loop` |
|
|
1509
|
-
| `guard: true` / `ctx.guard = MyGuard` | `guard: MyGuard, guard_options: {}` |
|
|
1510
|
-
| `guard.call(ctx)` (warning string) | `guard.call(function:)` (`LLM::Function::Return` or nil) |
|
|
1511
|
-
| `LLM::GuardError` | `"guard_error"` |
|
|
1512
|
-
|
|
1513
|
-
* **replace the transformer setter with `LLM::Transformer`** <br>
|
|
1514
|
-
The previous `transformer=` setter and 3-argument
|
|
1515
|
-
`call(ctx, prompt, params)` interface on `LLM::Context` have been
|
|
1516
|
-
replaced by the new
|
|
1517
|
-
[`LLM::Transformer`](https://r.uby.dev/api-docs/llm.rb/LLM/Transformer.html)
|
|
1518
|
-
class interface. Configure a transformer class through `transformer:`
|
|
1519
|
-
and options through `transformer_options:` instead.
|
|
1520
|
-
|
|
1521
|
-
* **cli: scope session persistence per provider and directory** <br>
|
|
1522
|
-
`bin/llm.rb` no longer shares a single session file between providers.
|
|
1523
|
-
Each provider now has a `~/.llm.rb/<provider>.json` file that maps the
|
|
1524
|
-
current working directory to a UUID-scoped session file under
|
|
1525
|
-
`~/.llm.rb/<provider>/<uuid>.json`, so sessions are scoped to both the
|
|
1526
|
-
provider and the directory they were started in.
|
|
1527
|
-
|
|
1528
|
-
* **cli: harden the executable against bad inputs** <br>
|
|
1529
|
-
`bin/llm.rb` now prints an error message followed by the help menu and
|
|
1530
|
-
exits with status 1 when the `-p` switch is given without an argument or
|
|
1531
|
-
when an unknown option is passed. Previously unknown options produced a
|
|
1532
|
-
warning but the run continued. The session-file lookup also no longer
|
|
1533
|
-
rewrites `~/.llm.rb/<provider>.json` when it already exists.
|
|
1534
|
-
|
|
1535
|
-
* **agent: replace `tool_attempts` with the `tool_budget` class DSL** <br>
|
|
1536
|
-
[`LLM::Agent`](https://r.uby.dev/api-docs/llm.rb/LLM/Agent.html#tool_budget-class_method)
|
|
1537
|
-
replaces the `tool_attempts` parameter with a `tool_budget` class DSL
|
|
1538
|
-
(`tool_budget { 50 }`) that caps the number of tool calls allowed in a
|
|
1539
|
-
single turn. Once the budget is spent, the agent sends an in-band
|
|
1540
|
-
advisory message back through the model telling it to solve the problem
|
|
1541
|
-
with fewer tool calls.
|
|
1542
|
-
<br><br>
|
|
1543
|
-
The feature is now disabled by default; the old `tool_attempts`
|
|
1544
|
-
parameter defaulted to 25, which long-horizon agents could easily
|
|
1545
|
-
exhaust in a single turn.
|
|
1546
|
-
|
|
1547
|
-
* **guard: replace `LLM::LoopGuard` with the `LLM::Guard` class hierarchy** <br>
|
|
1548
|
-
[`LLM::Guard`](https://r.uby.dev/api-docs/llm.rb/LLM/Guard.html)
|
|
1549
|
-
is a new superclass for context-level supervisors, with
|
|
1550
|
-
[`LLM::Guard::Loop`](https://r.uby.dev/api-docs/llm.rb/LLM/Guard/Loop.html)
|
|
1551
|
-
(replacing `LLM::LoopGuard`) and
|
|
1552
|
-
[`LLM::Guard::Null`](https://r.uby.dev/api-docs/llm.rb/LLM/Guard/Null.html)
|
|
1553
|
-
as the built-in implementations.
|
|
1554
|
-
[`LLM::Context`](https://r.uby.dev/api-docs/llm.rb/LLM/Context.html)
|
|
1555
|
-
now accepts `guard:` (a guard class defaulting to `LLM::Guard::Null`)
|
|
1556
|
-
and `guard_options:` (a hash forwarded to the guard's `call` method),
|
|
1557
|
-
matching the transformer and compactor interfaces. The old boolean and
|
|
1558
|
-
hash forms of `guard` and the `guard=` setter are removed. `LLM::Agent`
|
|
1559
|
-
enables `LLM::Guard::Loop` by default.
|
|
1560
|
-
|
|
1561
|
-
* **guard: block individual tool calls instead of the whole batch** <br>
|
|
1562
|
-
[`LLM::Guard#call`](https://r.uby.dev/api-docs/llm.rb/LLM/Guard.html#call-instance_method)
|
|
1563
|
-
now receives the pending `function:` and returns an
|
|
1564
|
-
[`LLM::Function::Return`](https://r.uby.dev/api-docs/llm.rb/LLM/Function/Return.html)
|
|
1565
|
-
(or nil) instead of a warning string for the entire batch, so a guard
|
|
1566
|
-
can block a single tool call while the rest of the batch still
|
|
1567
|
-
executes. Custom guards that implemented the old `call(ctx)`
|
|
1568
|
-
warning-string interface must be updated to return a
|
|
1569
|
-
`LLM::Function::Return` instead.
|
|
1570
|
-
|
|
1571
|
-
* **errors: drop `LLM::GuardError`** <br>
|
|
1572
|
-
Remove `LLM::GuardError`. The constant was never raised as an
|
|
1573
|
-
exception; it only named the in-band error type for guarded tool
|
|
1574
|
-
returns. Guarded tool returns now use the string `"guard_error"` as
|
|
1575
|
-
their error type.
|
|
1576
|
-
|
|
1577
|
-
### Core
|
|
1578
|
-
|
|
1579
|
-
* **add `LLM::Provider#build_messages` for assembling outgoing messages** <br>
|
|
1580
|
-
[`LLM::Provider#build_messages`](https://r.uby.dev/api-docs/llm.rb/LLM/Provider.html#build_messages-instance_method)
|
|
1581
|
-
normalizes a prompt into `LLM::Message` objects and prepends the existing
|
|
1582
|
-
history, replacing the per-provider `build_complete_messages`
|
|
1583
|
-
implementation. The method is idempotent: prompts that are already
|
|
1584
|
-
[`LLM::Message`](https://r.uby.dev/api-docs/llm.rb/LLM/Message.html)
|
|
1585
|
-
instances or arrays of messages are returned as-is.
|
|
1586
|
-
|
|
1587
|
-
* **copy the `params` hash in `LLM::Context` and `LLM::Agent`** <br>
|
|
1588
|
-
[`LLM::Context`](https://r.uby.dev/api-docs/llm.rb/LLM/Context.html) and
|
|
1589
|
-
[`LLM::Agent`](https://r.uby.dev/api-docs/llm.rb/LLM/Agent.html) now copy
|
|
1590
|
-
the `params` hash in their constructors before mutating it, leaving the
|
|
1591
|
-
caller's hash untouched. Previously the constructors deleted keys from
|
|
1592
|
-
the caller's hash in place.
|
|
1593
|
-
|
|
1594
|
-
* **gemspec: ship the deepdive sub-files in the gem** <br>
|
|
1595
|
-
The gemspec now includes `resources/deepdive/*/*.md` in the gem
|
|
1596
|
-
package, so the full deepdive guide (fundamentals, advanced,
|
|
1597
|
-
protocols, and everything-else chapters) is available after
|
|
1598
|
-
installation.
|
|
1599
|
-
|
|
1600
|
-
* **add short aliases to `LLM::Cost`** <br>
|
|
1601
|
-
[`LLM::Cost`](https://r.uby.dev/api-docs/llm.rb/LLM/Cost.html) now
|
|
1602
|
-
offers short aliases for its cost accessors: `input`, `output`,
|
|
1603
|
-
`input_audio`, `output_audio`, `input_image`, `cache_read`,
|
|
1604
|
-
`cache_write`, and `reasoning`. Each alias matches the key used by
|
|
1605
|
-
`#to_h`, so `cost.input` reads the same value as `cost.input_costs`.
|
|
1606
|
-
|
|
1607
|
-
### Provider
|
|
1608
|
-
|
|
1609
|
-
* **add `LLM::Moonshot` for the Moonshot AI provider** <br>
|
|
1610
|
-
[`LLM::Moonshot`](https://r.uby.dev/api-docs/llm.rb/LLM/Moonshot.html)
|
|
1611
|
-
is a new provider that talks to
|
|
1612
|
-
[Moonshot AI](https://platform.moonshot.ai) through its
|
|
1613
|
-
OpenAI-compatible Kimi API. Create an instance with
|
|
1614
|
-
[`LLM.moonshot`](https://r.uby.dev/api-docs/llm.rb/LLM.html#moonshot-class_method),
|
|
1615
|
-
which accepts the same `key:`, `host:`, and `base_path:` options as the
|
|
1616
|
-
OpenAI provider. The provider defaults to the `kimi-k3` model and
|
|
1617
|
-
supports chat completions, streaming, tool calls, and structured output
|
|
1618
|
-
through the shared OpenAI-compatible path; image, audio, moderation,
|
|
1619
|
-
responses, and vector store endpoints raise `NotImplementedError`.
|
|
1620
|
-
Model metadata ships in `data/moonshot.json` for the registry.
|
|
1621
|
-
|
|
1622
|
-
### Transformer
|
|
1623
|
-
|
|
1624
|
-
* **add `LLM::Transformer` for rewriting messages before they reach the provider** <br>
|
|
1625
|
-
[`LLM::Transformer`](https://r.uby.dev/api-docs/llm.rb/LLM/Transformer.html)
|
|
1626
|
-
is a new superclass for message transformers. A transformer is bound to a
|
|
1627
|
-
context and rewrites a single message before it is sent to the provider,
|
|
1628
|
-
which makes it possible to redact personal information or rewrite any
|
|
1629
|
-
message before it goes out over the wire. Each subclass implements
|
|
1630
|
-
`call(message:, **opts)` and returns the message to send, either by
|
|
1631
|
-
mutating it in place or returning a new one.
|
|
1632
|
-
[`LLM::Transformer::Null`](https://r.uby.dev/api-docs/llm.rb/LLM/Transformer/Null.html)
|
|
1633
|
-
is a no-op transformer used as the default.
|
|
1634
|
-
|
|
1635
|
-
* **hook the transformer API into `LLM::Context`** <br>
|
|
1636
|
-
[`LLM::Context`](https://r.uby.dev/api-docs/llm.rb/LLM/Context.html) now
|
|
1637
|
-
accepts `transformer:` (a transformer class defaulting to
|
|
1638
|
-
`LLM::Transformer::Null`) and `transformer_options:` (a hash forwarded to
|
|
1639
|
-
the transformer's `call` method). The transformer runs on the most recent
|
|
1640
|
-
message in both chat and responses turns.
|
|
1641
|
-
[`LLM::Stream#on_transform`](https://r.uby.dev/api-docs/llm.rb/LLM/Stream.html#on_transform-instance_method)
|
|
1642
|
-
and
|
|
1643
|
-
[`LLM::Stream#on_transform_finish`](https://r.uby.dev/api-docs/llm.rb/LLM/Stream.html#on_transform_finish-instance_method)
|
|
1644
|
-
now receive the transformer instance as their single argument.
|
|
1645
|
-
|
|
1646
|
-
### Tool
|
|
1647
|
-
|
|
1648
|
-
* **add `LLM::Tool.set` for bulk-assigning tool properties** <br>
|
|
1649
|
-
[`LLM::Tool.set`](https://r.uby.dev/api-docs/llm.rb/LLM/Tool.html#set-class_method)
|
|
1650
|
-
accepts a hash of `name`, `description`, `parameters`, `required`, and
|
|
1651
|
-
`defaults` to configure a tool subclass in a single call. Parameters are
|
|
1652
|
-
defined as tuples of `[name, type, description, options]`, matching the
|
|
1653
|
-
same interface as the existing `parameter` DSL. Unknown keys raise
|
|
1654
|
-
`KeyError`.
|
|
1655
|
-
|
|
1656
|
-
### Function
|
|
1657
|
-
|
|
1658
|
-
* **add `LLM::Function#return` for building tool returns** <br>
|
|
1659
|
-
[`LLM::Function#return`](https://r.uby.dev/api-docs/llm.rb/LLM/Function.html#return-instance_method)
|
|
1660
|
-
returns an
|
|
1661
|
-
[`LLM::Function::Return`](https://r.uby.dev/api-docs/llm.rb/LLM/Function/Return.html)
|
|
1662
|
-
built from the function's own id and name, using the given hash as its
|
|
1663
|
-
value. It is a shorthand mainly useful inside a
|
|
1664
|
-
[`LLM::Guard`](https://r.uby.dev/api-docs/llm.rb/LLM/Guard.html)
|
|
1665
|
-
subclass and is defined via `define_method` because `return` is a Ruby
|
|
1666
|
-
keyword.
|
|
1667
|
-
|
|
1668
|
-
### Guard
|
|
1669
|
-
|
|
1670
|
-
* **run the guard for streamed tool calls** <br>
|
|
1671
|
-
Fix a gap where the guard was not consulted when a tool call was queued
|
|
1672
|
-
while a response was still streaming. The guard is now stamped onto the
|
|
1673
|
-
functions a context binds, so it runs wherever a task is spawned,
|
|
1674
|
-
including tool calls queued from a stream. A blocked call yields its
|
|
1675
|
-
`guard_error` return without executing.
|
|
1676
|
-
|
|
1677
|
-
### Agent
|
|
1678
|
-
|
|
1679
|
-
* **add `LLM::Agent#compacted?`** <br>
|
|
1680
|
-
[`LLM::Agent#compacted?`](https://r.uby.dev/api-docs/llm.rb/LLM/Agent.html#compacted%3F-instance_method)
|
|
1681
|
-
delegates to the wrapped
|
|
1682
|
-
[`LLM::Context#compacted?`](https://r.uby.dev/api-docs/llm.rb/LLM/Context.html#compacted%3F-instance_method)
|
|
1683
|
-
and reports whether the conversation has been compacted, so callers
|
|
1684
|
-
can detect when history was trimmed.
|
|
1685
|
-
|
|
1686
|
-
### Change
|
|
1687
|
-
|
|
1688
|
-
* **openai: default to `gpt-5.6-luna`** <br>
|
|
1689
|
-
The default OpenAI chat model has changed from `gpt-5.4-mini` to
|
|
1690
|
-
`gpt-5.6-luna`. The new model is OpenAI's fastest and most affordable
|
|
1691
|
-
option, matching the kind of default llm.rb aims for.
|
|
1692
|
-
|
|
1693
|
-
### Repl
|
|
1694
|
-
|
|
1695
|
-
* **show an unknown context state after `/compact`** <br>
|
|
1696
|
-
After running `/compact`, the REPL status line now renders `Context
|
|
1697
|
-
compacted` and the context-usage bar shows `???` instead of a percentage,
|
|
1698
|
-
because the used context is unknown until the next response.
|
|
1699
|
-
|
|
1700
|
-
* **land on a blank line after Ctrl+N at the end of history** <br>
|
|
1701
|
-
When recalling history with Ctrl+P and Ctrl+N, Ctrl+N at the last item
|
|
1702
|
-
now advances to a blank line so you can start typing new input, instead
|
|
1703
|
-
of staying stuck on the last item in history (the previous behavior).
|
|
1704
|
-
Recalling with Ctrl+P or Ctrl+N also no longer overwrites the input when
|
|
1705
|
-
there is no history to show.
|
|
1706
|
-
|
|
1707
|
-
* **restore history wrap for Ctrl+P and Ctrl+N** <br>
|
|
1708
|
-
Fix a regression where Ctrl+P and Ctrl+N recalled history text without
|
|
1709
|
-
reflowing it into rows, so recalled lines wider than the terminal were
|
|
1710
|
-
clipped. Recalled text now flows through the same word-wrap path as
|
|
1711
|
-
typed input and wraps at the terminal width.
|
|
1712
|
-
|
|
1713
|
-
* **restore Ctrl+D deletion across rows** <br>
|
|
1714
|
-
Fix a bug where Ctrl+D at the end of an input row was a no-op, so
|
|
1715
|
-
multiline input could not be joined by deleting a row break. Deleting
|
|
1716
|
-
at the end of a row now consumes the break and pulls the next row up,
|
|
1717
|
-
restoring the split space so merged words do not run together.
|
|
1718
|
-
|
|
1719
|
-
* **center the buffer with 20% gutters** <br>
|
|
1720
|
-
The curses-based REPL now centers
|
|
1721
|
-
[`LLM::Repl::Buffer`](https://r.uby.dev/api-docs/llm.rb/LLM/Repl/Buffer.html)
|
|
1722
|
-
in a content area that is 60% of the terminal width, with an unused 20%
|
|
1723
|
-
gutter on each side. The drawing area is based on the available rows and
|
|
1724
|
-
columns instead of a fixed 80-column width, and `Buffer#wrap` now
|
|
1725
|
-
hard-breaks words that overflow the width onto the next row, fixing a
|
|
1726
|
-
bug where a word could be cut off between rows.
|
|
1727
|
-
|
|
1728
|
-
* **apply markdown to previous messages** <br>
|
|
1729
|
-
The curses-based REPL now renders every message in the buffer with
|
|
1730
|
-
markdown styling, including messages that were already present when
|
|
1731
|
-
the session started or restored from disk. Previously only newly
|
|
1732
|
-
streamed responses were styled; older messages fell back to plain
|
|
1733
|
-
text.
|
|
1734
|
-
|
|
1735
|
-
* **add `LLM::Repl#sender` for the user label** <br>
|
|
1736
|
-
[`LLM::Repl#sender`](https://r.uby.dev/api-docs/llm.rb/LLM/Repl.html#sender-instance_method)
|
|
1737
|
-
returns the label used for user messages in the curses-based REPL. It
|
|
1738
|
-
defaults to `"You"` (previously `"user"`), and the buffer layout now
|
|
1739
|
-
places each label on its own line followed by the message content and a
|
|
1740
|
-
blank line.
|
|
1741
|
-
|
|
1742
|
-
* **add `LLM::Repl::Color` for coloring the curses UI** <br>
|
|
1743
|
-
Add
|
|
1744
|
-
[`LLM::Repl::Color`](https://r.uby.dev/api-docs/llm.rb/LLM/Repl/Color.html)
|
|
1745
|
-
as a new module that returns Curses color bitmasks. `Color.enable`
|
|
1746
|
-
initializes 8 color pairs, and methods like `Color.blue` return the
|
|
1747
|
-
corresponding `Curses.color_pair(X)` bitmask, which can be bitwise
|
|
1748
|
-
OR'ed with other attributes such as `Curses::A_BOLD`. User labels in
|
|
1749
|
-
the REPL are now rendered in blue instead of plain bold text.
|
|
1750
|
-
|
|
1751
|
-
* **split on words rather than characters** <br>
|
|
1752
|
-
`LLM::Repl::Buffer#wrap` now breaks text on word boundaries instead of
|
|
1753
|
-
wrapping one character at a time. A word that does not fit on the
|
|
1754
|
-
current row moves to the next, and only a single word longer than the
|
|
1755
|
-
whole width is hard-broken, so text is never clipped by the window.
|
|
1756
|
-
|
|
1757
|
-
* **render kramdown typographic symbols and smart quotes** <br>
|
|
1758
|
-
Fix a bug where certain character sequences such as `...` were not
|
|
1759
|
-
rendered at all in the curses-based REPL. Kramdown parses them into
|
|
1760
|
-
`:typographic_sym` and `:smart_quote` nodes, which previously fell
|
|
1761
|
-
through to the children clause and were dropped. The markdown renderer
|
|
1762
|
-
now maps them to their unicode equivalents: ellipsis, en and em
|
|
1763
|
-
dashes, guillemets, and single and double quotation marks.
|
|
1764
|
-
|
|
1765
|
-
* **apply colors to the markdown renderer** <br>
|
|
1766
|
-
The curses-based REPL now renders markdown with the `LLM::Repl::Color`
|
|
1767
|
-
palette: headers and strong text in white, code spans and code blocks
|
|
1768
|
-
in green, and links in underlined green, on the black background.
|
|
1769
|
-
Previously markdown styling used bold, underline, and reverse video
|
|
1770
|
-
attributes only.
|
|
1771
|
-
|
|
1772
|
-
* **wrap the input line at word boundaries** <br>
|
|
1773
|
-
The curses-based REPL input line now wraps words whole onto the next
|
|
1774
|
-
row at the terminal width instead of cutting them in half. A word that
|
|
1775
|
-
does not fit on the current row moves to the next row, and only a
|
|
1776
|
-
single word longer than the whole width is hard-broken, so typed text
|
|
1777
|
-
is never clipped by the window.
|
|
1778
|
-
|
|
1779
|
-
* **distinguish the connecting and thinking status bar phases** <br>
|
|
1780
|
-
The curses-based REPL status bar now shows `Connecting • Esc to
|
|
1781
|
-
cancel` while the model is establishing a connection, then switches
|
|
1782
|
-
to `Thinking • Esc to cancel` once a tool call or text fragment
|
|
1783
|
-
arrives on the stream. Active tool calls appear in the status bar
|
|
1784
|
-
with a lambda indicator.
|
|
1785
|
-
|
|
1786
|
-
* **add emoji to the status bar phases** <br>
|
|
1787
|
-
The curses-based REPL status bar now uses emoji to identify each
|
|
1788
|
-
phase at a glance: a globe (`🌐`) while the model is connecting, and
|
|
1789
|
-
a brain (`🧠`) while it is thinking. The text after the emoji still
|
|
1790
|
-
reads `Connecting • Esc to cancel` and `Thinking • Esc to cancel`
|
|
1791
|
-
respectively.
|
|
1792
|
-
|
|
1793
|
-
* **render the status bar with color and attributes** <br>
|
|
1794
|
-
The curses-based REPL status bar now supports colored and attributed
|
|
1795
|
-
status text, so the lambda indicator for active tool calls is drawn
|
|
1796
|
-
in bold red.
|
|
1797
|
-
|
|
1798
|
-
### Registry
|
|
1799
|
-
|
|
1800
|
-
* **refresh model metadata across providers** <br>
|
|
1801
|
-
Update `data/*.json` files with current provider model listings and
|
|
1802
|
-
pricing. Remove the deprecated Claude Opus 4.1 entries from the
|
|
1803
|
-
Anthropic registry, add `Qwen/Qwen3.8-Max` to DeepInfra, and add a
|
|
1804
|
-
`low` reasoning-effort option to DeepSeek.
|
|
1805
|
-
|
|
1806
|
-
## v13.1.0
|
|
1807
|
-
|
|
1808
|
-
Changes since `v13.0.0`.
|
|
1809
|
-
|
|
1810
|
-
This release adds `LLM::Agent` class DSL attributes (`path`, `description`),
|
|
1811
|
-
extends skills with file-path loading and the `tools: all` directive, adds new
|
|
1812
|
-
built-in tools (`LLM::Tool::Ruby`, `LLM::Tool::EditFile`), introduces the
|
|
1813
|
-
`LLM::Tracer::PrettyLogger` for human-readable tracing, renames `Transcript`
|
|
1814
|
-
to `Buffer` across the REPL, ships a `bin/llm.rb` CLI entry point, and fixes
|
|
1815
|
-
several agent and tool bugs around persistence, interruption, and naming.
|
|
1816
|
-
|
|
1817
|
-
### Core
|
|
1818
|
-
|
|
1819
|
-
* **add post install message with deepdive link** <br>
|
|
1820
|
-
The gemspec now includes a `post_install_message` that points users to
|
|
1821
|
-
the deepdive guide at `https://r.uby.dev/llm/deepdive` after installation,
|
|
1822
|
-
making it easier for new users to discover the project documentation.
|
|
1823
|
-
|
|
1824
|
-
### Agent
|
|
1825
|
-
|
|
1826
|
-
* **add `description` class DSL and instance method** <br>
|
|
1827
|
-
[`LLM::Agent`](https://r.uby.dev/api-docs/llm.rb/LLM/Agent.html) now
|
|
1828
|
-
has a `description` class DSL (`description "release engineer"`) and a
|
|
1829
|
-
corresponding `#description` instance method. The description is an
|
|
1830
|
-
optional self-documenting string that serves as a brief summary of the
|
|
1831
|
-
agent's purpose. It can be set via the class DSL,
|
|
1832
|
-
`LLM::Agent.set(description: ...)`, or `LLM::Agent.new(description: ...)`.
|
|
1833
|
-
|
|
1834
|
-
* **add `path` class DSL and instance method** <br>
|
|
1835
|
-
[`LLM::Agent`](https://r.uby.dev/api-docs/llm.rb/LLM/Agent.html) now
|
|
1836
|
-
has a `path` class DSL (`path "contexts/admin.json"`) and a
|
|
1837
|
-
corresponding `#path` instance method. When a path is set, the agent
|
|
1838
|
-
automatically restores its conversation history from that file on
|
|
1839
|
-
initialization and saves it back after each `talk` or `ask` turn,
|
|
1840
|
-
making session persistence across process restarts transparent.
|
|
1841
|
-
|
|
1842
|
-
### Skills
|
|
1843
|
-
|
|
1844
|
-
* **accept a path to a markdown file** <br>
|
|
1845
|
-
[`LLM::Skill.load`](https://r.uby.dev/api-docs/llm.rb/LLM/Skill.html#load-class_method)
|
|
1846
|
-
now accepts a path to a markdown file in addition to a directory path.
|
|
1847
|
-
When given a file path, the file is read directly instead of looking for
|
|
1848
|
-
a `SKILL.md` inside a directory. This makes it possible to load a single
|
|
1849
|
-
markdown file as a skill without placing it in a dedicated directory.
|
|
1850
|
-
|
|
1851
|
-
* **extend with `all` keyword for loading the full tool registry** <br>
|
|
1852
|
-
`LLM::Skill` now supports `tools: all` (or `tools: "*"`) in the frontmatter
|
|
1853
|
-
to load all tools from the global
|
|
1854
|
-
[`LLM::Tool.registry`](https://r.uby.dev/api-docs/llm.rb/LLM/Tool.html#registry-class_method).
|
|
1855
|
-
Previously, the `tools:` frontmatter only accepted `inherit`, an array of tool
|
|
1856
|
-
names, or nothing. The new `all` keyword makes it possible to give a skill
|
|
1857
|
-
access to every registered tool without listing them individually.
|
|
1858
|
-
|
|
1859
|
-
### Tools
|
|
1860
|
-
|
|
1861
|
-
* **add `LLM::Tool::Ruby` for executing Ruby code in a subprocess** <br>
|
|
1862
|
-
[`LLM::Tool::Ruby`](https://r.uby.dev/api-docs/llm.rb/LLM/Tool/Ruby.html)
|
|
1863
|
-
is a new built-in tool that runs a string of Ruby code in a separate
|
|
1864
|
-
Ruby process with a configurable timeout (default 15s). The code runs
|
|
1865
|
-
in an isolated address space unaware of its parent, making it useful
|
|
1866
|
-
for safe(ish) dynamic code execution. It must be required explicitly
|
|
1867
|
-
with `require "llm/tools/ruby"` and requires the `test-cmd.rb` gem.
|
|
1868
|
-
|
|
1869
|
-
* **rename `LLM::Tool::SwapText` to `LLM::Tool::EditFile`** <br>
|
|
1870
|
-
The `SwapText` tool has been renamed to
|
|
1871
|
-
[`LLM::Tool::EditFile`](https://r.uby.dev/api-docs/llm.rb/LLM/Tool/EditFile.html)
|
|
1872
|
-
to better match the naming of sibling tools (`ReadFile`, `WriteFile`).
|
|
1873
|
-
The old `require "llm/tools/swap_text"` path no longer exists; use
|
|
1874
|
-
`require "llm/tools/edit-file"` instead.
|
|
1875
|
-
|
|
1876
|
-
### Tracer
|
|
1877
|
-
|
|
1878
|
-
* **add `LLM::Tracer::PrettyLogger` for human-readable tracing** <br>
|
|
1879
|
-
[`LLM::Tracer::PrettyLogger`](https://r.uby.dev/api-docs/llm.rb/LLM/Tracer/PrettyLogger.html)
|
|
1880
|
-
is a new tracer that writes human-readable request and tool-call logs to a
|
|
1881
|
-
console or file. Unlike the structured JSON output of
|
|
1882
|
-
`LLM::Tracer::Logger`, the pretty logger emits single-line entries with
|
|
1883
|
-
inline context, making it easier to follow agent activity at a glance.
|
|
1884
|
-
It writes to `$stderr` by default and accepts an `io:` option for file
|
|
1885
|
-
output.
|
|
1886
|
-
|
|
1887
|
-
### Repl
|
|
1888
|
-
|
|
1889
|
-
* **rename `LLM::Repl::Transcript` to `LLM::Repl::Buffer`** <br>
|
|
1890
|
-
`LLM::Repl::Transcript` has been renamed to
|
|
1891
|
-
[`LLM::Repl::Buffer`](https://r.uby.dev/api-docs/llm.rb/LLM/Repl/Buffer.html)
|
|
1892
|
-
to better reflect its role as a conversation state manager. The old
|
|
1893
|
-
`start` and `finish` methods have been renamed to `open` and `close`
|
|
1894
|
-
respectively. The public accessor on
|
|
1895
|
-
[`LLM::Repl`](https://r.uby.dev/api-docs/llm.rb/LLM/Repl.html) has been
|
|
1896
|
-
renamed from `transcript` to `buffer`.
|
|
1897
|
-
|
|
1898
|
-
* **add `write_message` for formatted message writing** <br>
|
|
1899
|
-
[`LLM::Repl::Buffer#write_message`](https://r.uby.dev/api-docs/llm.rb/LLM/Repl/Buffer.html#write_message-instance_method)
|
|
1900
|
-
and
|
|
1901
|
-
[`LLM::Repl#write_message`](https://r.uby.dev/api-docs/llm.rb/LLM/Repl.html#write_message-instance_method)
|
|
1902
|
-
provide a convenience method that takes a username and content string,
|
|
1903
|
-
formatting the output with a bold `user:` label and a trailing newline.
|
|
1904
|
-
This is simpler than the equivalent sequence of `write` calls.
|
|
1905
|
-
|
|
1906
|
-
* **add `Command#write_message` and refactor `Command#write`** <br>
|
|
1907
|
-
[`LLM::Command#write_message`](https://r.uby.dev/api-docs/llm.rb/LLM/Repl/Command.html#write_message-instance_method)
|
|
1908
|
-
provides a convenience method that takes a username and content string,
|
|
1909
|
-
matching the same interface on `LLM::Repl` and `LLM::Buffer`. The
|
|
1910
|
-
`Command#write` method is now implemented on top of `write_message`,
|
|
1911
|
-
always prefixing output with `command(<name>): `. The `who:` keyword
|
|
1912
|
-
argument previously accepted by `write` has been removed; use
|
|
1913
|
-
`write_message` instead.
|
|
1914
|
-
|
|
1915
|
-
* **display pre-existing agent messages when the repl starts** <br>
|
|
1916
|
-
When
|
|
1917
|
-
[`LLM::Agent#repl`](https://r.uby.dev/api-docs/llm.rb/LLM/Agent.html#repl-instance_method)
|
|
1918
|
-
starts, any messages already in the agent's buffer are now rendered
|
|
1919
|
-
in the REPL window. Previously the REPL started with an empty
|
|
1920
|
-
transcript even when the agent carried prior conversation history,
|
|
1921
|
-
making it harder to resume a session. Tool-call and tool-return
|
|
1922
|
-
messages are skipped to avoid visual noise.
|
|
1923
|
-
|
|
1924
|
-
### CLI
|
|
1925
|
-
|
|
1926
|
-
* **add `bin/llm.rb` for launching the REPL from the command line** <br>
|
|
1927
|
-
A new executable script (`bin/llm.rb`) provides a convenient way to start
|
|
1928
|
-
an interactive REPL session directly from the terminal. It auto-detects
|
|
1929
|
-
the provider from environment variables like `OPENAI_API_KEY`, supports
|
|
1930
|
-
a `-p PROVIDER` flag for explicit provider selection, a `-t` flag for
|
|
1931
|
-
temporary (non-persistent) sessions, and `-h` for help. Sessions are
|
|
1932
|
-
automatically saved to `~/.llm.rb/` by default.
|
|
1933
|
-
|
|
1934
|
-
### Fix
|
|
1935
|
-
|
|
1936
|
-
* **agent: fix `path` restore on first run** <br>
|
|
1937
|
-
Fix a bug where [`LLM::Agent`](https://r.uby.dev/api-docs/llm.rb/LLM/Agent.html)
|
|
1938
|
-
called `@ctx.restore(path:)` even when the path's file did not exist.
|
|
1939
|
-
The fix checks `File.readable?(@path)` before attempting to restore,
|
|
1940
|
-
so the agent starts with a blank conversation on first use instead of
|
|
1941
|
-
failing with a file-not-found error.
|
|
1942
|
-
|
|
1943
|
-
* **tools: re-raise `LLM::Interrupt` to abort the turn** <br>
|
|
1944
|
-
[`LLM::Tool::Git`](https://r.uby.dev/api-docs/llm.rb/LLM/Tool/Git.html),
|
|
1945
|
-
[`LLM::Tool::Mkdir`](https://r.uby.dev/api-docs/llm.rb/LLM/Tool/Mkdir.html),
|
|
1946
|
-
[`LLM::Tool::Rg`](https://r.uby.dev/api-docs/llm.rb/LLM/Tool/Rg.html),
|
|
1947
|
-
[`LLM::Tool::Ruby`](https://r.uby.dev/api-docs/llm.rb/LLM/Tool/Ruby.html),
|
|
1948
|
-
and
|
|
1949
|
-
[`LLM::Tool::Shell`](https://r.uby.dev/api-docs/llm.rb/LLM/Tool/Shell.html)
|
|
1950
|
-
now re-raise `LLM::Interrupt` after killing their running command. The
|
|
1951
|
-
previous behavior rescued the interrupt and killed the child process but
|
|
1952
|
-
let the turn continue, which meant a cancelled tool call did not abort
|
|
1953
|
-
the conversation turn. Re-raising ensures the entire turn is interrupted.
|
|
1954
|
-
|
|
1955
|
-
* **tools: rescue `LLM::Interrupt` in shell-based tools** <br>
|
|
1956
|
-
[`LLM::Tool::Shell`](https://r.uby.dev/api-docs/llm.rb/LLM/Tool/Shell.html),
|
|
1957
|
-
[`LLM::Tool::Git`](https://r.uby.dev/api-docs/llm.rb/LLM/Tool/Git.html),
|
|
1958
|
-
[`LLM::Tool::Mkdir`](https://r.uby.dev/api-docs/llm.rb/LLM/Tool/Mkdir.html),
|
|
1959
|
-
and
|
|
1960
|
-
[`LLM::Tool::Rg`](https://r.uby.dev/api-docs/llm.rb/LLM/Tool/Rg.html)
|
|
1961
|
-
now rescue `LLM::Interrupt` and kill their running command, preventing
|
|
1962
|
-
orphaned child processes when a tool is interrupted during execution.
|
|
1963
|
-
|
|
1964
|
-
* **agent: fix default name derivation** <br>
|
|
1965
|
-
Fix a bug where `LLM::Agent` used without a subclass derived its default
|
|
1966
|
-
name as `"l-lm-agent"` instead of `"agent"`. The fix replaces the
|
|
1967
|
-
regex-based parameterization with a pattern that correctly handles
|
|
1968
|
-
single-word class names and multi-word namespaced names.
|
|
1969
|
-
|
|
1970
|
-
* **function: `#params` always returns an `LLM::Object`** <br>
|
|
1971
|
-
[`LLM::Function#params`](https://r.uby.dev/api-docs/llm.rb/LLM/Function.html#params-instance_method)
|
|
1972
|
-
now always returns an `LLM::Object` representing the function's parameter
|
|
1973
|
-
schema. Previously it returned `nil` when a function defined no parameters,
|
|
1974
|
-
forcing every caller to guard against `nil`. All provider adapters now use
|
|
1975
|
-
`fn.params.to_h` instead of `fn.params || {type: "object", properties: {}}`.
|
|
1976
|
-
|
|
1977
|
-
## v13.0.0
|
|
1978
|
-
|
|
1979
|
-
v13.0.0 is released under the MIT license. Commercial, personal,
|
|
1980
|
-
educational, and all other uses are permitted under the standard
|
|
1981
|
-
MIT terms.
|
|
1982
|
-
|
|
1983
|
-
Seven breaking changes. Concurrency strategies have been renamed
|
|
1984
|
-
(`:call` → `:sequential`, `:task` → `:async`), `spawn` is now
|
|
1985
|
-
`task`, and the `:async` strategy has been rebuilt from the ground
|
|
1986
|
-
up. It no longer blocks and now supports interruption. The compactor
|
|
1987
|
-
has been refactored into pluggable strategies. Interruption is now
|
|
1988
|
-
reliable across all six concurrency backends. The `functions` and
|
|
1989
|
-
`functions?` methods have been renamed to `pending_functions` and
|
|
1990
|
-
`pending_functions?`.
|
|
1991
|
-
|
|
1992
|
-
### Migration from v12.6.0
|
|
1993
|
-
|
|
1994
|
-
| Old | New |
|
|
1995
|
-
|-----|-----|
|
|
1996
|
-
| `fn.spawn(:call)` | `fn.task(:sequential)` |
|
|
1997
|
-
| `fn.spawn(:task)` | `fn.task(:async)` |
|
|
1998
|
-
| `ctx.wait(:call)` | `ctx.wait(:sequential)` |
|
|
1999
|
-
| `agent.concurrency :task` | `agent.concurrency :async` |
|
|
2000
|
-
| `LLM::Function::FiberGroup` | `LLM::Function::Fiber::Group` |
|
|
2001
|
-
| `LLM::Function::CallGroup` | `LLM::Function::Sequential::Group` |
|
|
2002
|
-
| `LLM::Function::TaskGroup` | `LLM::Function::Async::Group` |
|
|
2003
|
-
| `Compactor.new(model:, token_threshold:)` | `Compactor::Truncate.new(ctx)` |
|
|
2004
|
-
| `on_compaction(ctx, compactor)` | `on_compaction(compactor)` |
|
|
2005
|
-
| `ctx.functions` / `ctx.functions?` | `ctx.pending_functions` / `ctx.pending_functions?` |
|
|
2006
|
-
| `agent.functions` / `agent.functions?` | `agent.pending_functions` |
|
|
2007
|
-
|
|
2008
|
-
### Breaking
|
|
2009
|
-
|
|
2010
|
-
* **rename `LLM::Function#spawn` as `LLM::Function#task`** <br>
|
|
2011
|
-
[`LLM::Function#task`](https://r.uby.dev/api-docs/llm.rb/LLM/Function.html#task-instance_method)
|
|
2012
|
-
(previously `spawn`) now consistently returns a
|
|
2013
|
-
[`LLM::Function::Task`](https://r.uby.dev/api-docs/llm.rb/LLM/Function/Task.html)
|
|
2014
|
-
object that can be spawned, waited on, and passed to
|
|
2015
|
-
[`LLM::Function::Group`](https://r.uby.dev/api-docs/llm.rb/LLM/Function/Group.html).
|
|
2016
|
-
The old implementation alternated between spawning immediately or
|
|
2017
|
-
returning a raw thread or fiber.
|
|
2018
|
-
|
|
2019
|
-
* **rename concurrency strategies (`:call` → `:sequential`,**
|
|
2020
|
-
**`:task` → `:async`)** <br>
|
|
2021
|
-
The `:call` concurrency strategy is now `:sequential`, and the `:task`
|
|
2022
|
-
strategy is now `:async`.
|
|
2023
|
-
[`LLM::Agent.concurrency`](https://r.uby.dev/api-docs/llm.rb/LLM/Agent.html#concurrency-class_method),
|
|
2024
|
-
[`LLM::Context#wait`](https://r.uby.dev/api-docs/llm.rb/LLM/Context.html#wait-instance_method),
|
|
2025
|
-
`LLM::Function::Array#task`, and
|
|
2026
|
-
[`LLM::Function#task`](https://r.uby.dev/api-docs/llm.rb/LLM/Function.html#task-instance_method)
|
|
2027
|
-
all accept the new names. The old names raise `ArgumentError`.
|
|
2028
|
-
|
|
2029
|
-
* **rename group classes** <br>
|
|
2030
|
-
Group classes have been moved into their strategy's namespace:
|
|
2031
|
-
`FiberGroup` → `Fiber::Group`, `ThreadGroup` → `Thread::Group`,
|
|
2032
|
-
`CallGroup` → `Sequential::Group`, `TaskGroup` → `Async::Group`,
|
|
2033
|
-
`Fork::Group` → `Fork::Group`, `Ractor::Group` → `Ractor::Group`.
|
|
2034
|
-
|
|
2035
|
-
* **repurpose `LLM::Function::Task` as a task interface superclass** <br>
|
|
2036
|
-
`LLM::Function::Task` has been repurposed from a general-purpose class
|
|
2037
|
-
that tried to support multiple concurrency strategies into an abstract
|
|
2038
|
-
base class that defines the task interface. Individual strategies
|
|
2039
|
-
(`Sequential::Task`, `Thread::Task`, `Fiber::Task`, `Async::Task`,
|
|
2040
|
-
`Fork::Task`, `Ractor::Task`) now subclass it and implement
|
|
2041
|
-
`spawn`, `alive?`, `interrupt!`, and `wait`.
|
|
2042
|
-
|
|
2043
|
-
* **fix `:async` concurrency (now backed by a managed**
|
|
2044
|
-
**`LLM::Function::Async::Reactor` on a background thread)** <br>
|
|
2045
|
-
The `:async` strategy previously used `Async {}` which blocked the
|
|
2046
|
-
caller until all tasks completed and did not support interruption.
|
|
2047
|
-
The fix replaces it with a per-turn
|
|
2048
|
-
`LLM::Function::Async::Reactor` on a background thread. Work is
|
|
2049
|
-
submitted via `submit(&block)` and consumed by the reactor's event
|
|
2050
|
-
loop through a thread-safe `Queue`. `Async::Group` manages the
|
|
2051
|
-
reactor lifecycle and spawns tasks lazily on `wait`.
|
|
2052
|
-
<br><br>
|
|
2053
|
-
Interruption pushes [`LLM::Interrupt`](https://r.uby.dev/api-docs/llm.rb/LLM/Interrupt.html)
|
|
2054
|
-
to the task's result queue instead of using `Fiber#raise`, and
|
|
2055
|
-
results are bridged back to the caller through a second `Queue`.
|
|
2056
|
-
This work drove the broader refactor of strategy naming, the
|
|
2057
|
-
spawn/wait split, and the `Task` superclass. The `:async` strategy
|
|
2058
|
-
needed the same interface the other strategies already had.
|
|
2059
|
-
|
|
2060
|
-
* **compactor: refactor to strategy-based interface** <br>
|
|
2061
|
-
[`LLM::Compactor`](https://r.uby.dev/api-docs/llm.rb/LLM/Compactor.html)
|
|
2062
|
-
has been refactored from a single class that performed LLM-based
|
|
2063
|
-
summarization into a strategy-based superclass. Each subclass
|
|
2064
|
-
implements a different compaction strategy via `call(**opts)`. The old
|
|
2065
|
-
summarization approach (using `model:`, `token_threshold:`,
|
|
2066
|
-
`message_threshold:`, and `retention_window:` options) has been removed.
|
|
2067
|
-
The built-in
|
|
2068
|
-
[`LLM::Compactor::Truncate`](https://r.uby.dev/api-docs/llm.rb/LLM/Compactor/Truncate.html)
|
|
2069
|
-
strategy drops the oldest messages when the conversation exceeds a
|
|
2070
|
-
configured size.
|
|
2071
|
-
|
|
2072
|
-
* **rename `LLM::Context#{functions,functions?}` and `LLM::Agent#{functions,functions?}`** <br>
|
|
2073
|
-
[`LLM::Context#functions`](https://r.uby.dev/api-docs/llm.rb/LLM/Context.html#functions-instance_method)
|
|
2074
|
-
and
|
|
2075
|
-
[`LLM::Context#functions?`](https://r.uby.dev/api-docs/llm.rb/LLM/Context.html#functions%3F-instance_method)
|
|
2076
|
-
have been renamed to
|
|
2077
|
-
[`LLM::Context#pending_functions`](https://r.uby.dev/api-docs/llm.rb/LLM/Context.html#pending_functions-instance_method)
|
|
2078
|
-
and
|
|
2079
|
-
[`LLM::Context#pending_functions?`](https://r.uby.dev/api-docs/llm.rb/LLM/Context.html#pending_functions%3F-instance_method)
|
|
2080
|
-
respectively. The same rename applies to
|
|
2081
|
-
[`LLM::Agent#functions`](https://r.uby.dev/api-docs/llm.rb/LLM/Agent.html#functions-instance_method)
|
|
2082
|
-
(now
|
|
2083
|
-
[`LLM::Agent#pending_functions`](https://r.uby.dev/api-docs/llm.rb/LLM/Agent.html#pending_functions-instance_method)).
|
|
2084
|
-
The `pending_functions` name was already available as an alias in
|
|
2085
|
-
v12.5.0; this change removes the old `functions` name entirely.
|
|
2086
|
-
|
|
2087
|
-
### Core
|
|
2088
|
-
|
|
2089
|
-
* **extend `LLM.require` with an optional version argument** <br>
|
|
2090
|
-
`LLM.require` now accepts a second `version` parameter that is passed
|
|
2091
|
-
to `Kernel#gem` before loading, enabling version constraints for
|
|
2092
|
-
optional runtime dependencies. For example,
|
|
2093
|
-
`LLM.require "test-cmd.rb", "~> 2.2"` ensures a minimum gem version
|
|
2094
|
-
is available. This is used internally by the `Git`, `Rg`, `Mkdir`,
|
|
2095
|
-
and `Shell` tools to enforce compatibility with the `test-cmd.rb` gem.
|
|
2096
|
-
|
|
2097
|
-
### Compactor
|
|
2098
|
-
|
|
2099
|
-
* **add `Truncate` strategy for dropping oldest messages** <br>
|
|
2100
|
-
[`LLM::Compactor::Truncate`](https://r.uby.dev/api-docs/llm.rb/LLM/Compactor/Truncate.html)
|
|
2101
|
-
is a new built-in compaction strategy that drops the oldest messages
|
|
2102
|
-
when the conversation exceeds a configured size. It preserves tool
|
|
2103
|
-
call/return pairs so the algorithm never breaks in the middle of a
|
|
2104
|
-
sequence. Configured with `keep:` (default 64), it emits the standard
|
|
2105
|
-
`on_compaction` and `on_compaction_finish`
|
|
2106
|
-
[`LLM::Stream`](https://r.uby.dev/api-docs/llm.rb/LLM/Stream.html)
|
|
2107
|
-
lifecycle callbacks. No LLM call is made; the strategy is purely
|
|
2108
|
-
lossy but fast and requires no network.
|
|
2109
|
-
|
|
2110
|
-
* **raise when given an unparseable `keep:` value** <br>
|
|
2111
|
-
`LLM::Compactor::Truncate` now raises `ArgumentError` when the `keep:`
|
|
2112
|
-
parameter cannot be parsed as an integer or percentage string, instead
|
|
2113
|
-
of failing with an obscure error later during execution.
|
|
2114
|
-
|
|
2115
|
-
* **accept percentage string for the `keep:` parameter** <br>
|
|
2116
|
-
`LLM::Compactor::Truncate#call` now accepts a percentage string such
|
|
2117
|
-
as `"80%"` for the `keep:` parameter, which keeps approximately 80%
|
|
2118
|
-
of the most recent messages. Integer values continue to work as
|
|
2119
|
-
before. This makes it easy to trim proportionally rather than to an
|
|
2120
|
-
absolute number of messages.
|
|
2121
|
-
|
|
2122
|
-
* **add `Null` strategy for no-op compaction** <br>
|
|
2123
|
-
[`LLM::Compactor::Null`](https://r.uby.dev/api-docs/llm.rb/LLM/Compactor/Null.html)
|
|
2124
|
-
is a new built-in compaction strategy that does nothing. It is used as
|
|
2125
|
-
the default compactor when no strategy is configured on a context,
|
|
2126
|
-
ensuring the compactor interface is always present without requiring a
|
|
2127
|
-
separate nil check.
|
|
2128
|
-
|
|
2129
|
-
* **accept both `LLM::Agent` and `LLM::Context`** <br>
|
|
2130
|
-
[`LLM::Compactor#initialize`](https://r.uby.dev/api-docs/llm.rb/LLM/Compactor.html#initialize-instance_method)
|
|
2131
|
-
now accepts both `LLM::Agent` and `LLM::Context` instances. When given
|
|
2132
|
-
an agent, the internal context is unwrapped automatically, making the
|
|
2133
|
-
compactor API more flexible when working with agents.
|
|
2134
|
-
|
|
2135
|
-
#### Context integration
|
|
2136
|
-
|
|
2137
|
-
* **accept `compactor` and `compactor_options` parameters** <br>
|
|
2138
|
-
[`LLM::Context`](https://r.uby.dev/api-docs/llm.rb/LLM/Context.html)
|
|
2139
|
-
now accepts `compactor:` (a compactor class defaulting to
|
|
2140
|
-
[`LLM::Compactor::Null`](https://r.uby.dev/api-docs/llm.rb/LLM/Compactor/Null.html))
|
|
2141
|
-
and `compactor_options:` (a hash of options forwarded to the
|
|
2142
|
-
compactor's `call` method) parameters. The compactor is automatically
|
|
2143
|
-
invoked at the beginning of each `talk` turn. The previous `compactor=`
|
|
2144
|
-
setter has been removed in favour of constructor-driven configuration.
|
|
2145
|
-
|
|
2146
|
-
* **`on_compaction` and `on_compaction_finish` receive a single argument** <br>
|
|
2147
|
-
[`LLM::Stream#on_compaction`](https://r.uby.dev/api-docs/llm.rb/LLM/Stream.html#on_compaction-instance_method)
|
|
2148
|
-
and
|
|
2149
|
-
[`LLM::Stream#on_compaction_finish`](https://r.uby.dev/api-docs/llm.rb/LLM/Stream.html#on_compaction_finish-instance_method)
|
|
2150
|
-
now accept a single argument (the compactor instance) instead of two
|
|
2151
|
-
arguments (context and compactor). The context is still available via
|
|
2152
|
-
`LLM::Compactor#ctx`, so access to the context is not lost. This
|
|
2153
|
-
simplifies the callback interface for compaction lifecycle observers.
|
|
2154
|
-
|
|
2155
|
-
### Tools
|
|
2156
|
-
|
|
2157
|
-
* **add `LLM::Tool::Utils` module for shared command execution logic** <br>
|
|
2158
|
-
A new
|
|
2159
|
-
[`LLM::Tool::Utils`](https://r.uby.dev/api-docs/llm.rb/LLM/Tool/Utils.html)
|
|
2160
|
-
module provides shared `wait(command:, timeout:)` and `now` helper
|
|
2161
|
-
methods for tools that execute commands. Tools that include `Utils` can
|
|
2162
|
-
wait on a running command and automatically kill it when it exceeds the
|
|
2163
|
-
configured timeout, using `Process.clock_gettime` with `CLOCK_MONOTONIC`
|
|
2164
|
-
for precise timing. The module is used by both the `Shell` and `Rg`
|
|
2165
|
-
tools internally.
|
|
2166
|
-
|
|
2167
|
-
* **shell: add `timeout` parameter for command execution deadlines** <br>
|
|
2168
|
-
The
|
|
2169
|
-
[`LLM::Tool::Shell`](https://r.uby.dev/api-docs/llm.rb/LLM/Tool/Shell.html)
|
|
2170
|
-
tool now accepts a `timeout` parameter (default 60s) that automatically
|
|
2171
|
-
kills commands exceeding the specified time limit, preventing hung
|
|
2172
|
-
processes from blocking the agent indefinitely.
|
|
2173
|
-
|
|
2174
|
-
* **rg: add `timeout` parameter for search execution deadlines** <br>
|
|
2175
|
-
The
|
|
2176
|
-
[`LLM::Tool::Rg`](https://r.uby.dev/api-docs/llm.rb/LLM/Tool/Rg.html)
|
|
2177
|
-
tool now accepts a `timeout` parameter (default 5s) that automatically
|
|
2178
|
-
kills search commands exceeding the specified time limit, preventing
|
|
2179
|
-
long-running searches from blocking the agent indefinitely.
|
|
2180
|
-
|
|
2181
|
-
* **git: add `timeout` parameter for command execution deadlines** <br>
|
|
2182
|
-
The
|
|
2183
|
-
[`LLM::Tool::Git`](https://r.uby.dev/api-docs/llm.rb/LLM/Tool/Git.html)
|
|
2184
|
-
tool now accepts a `timeout` parameter (default 5s) that automatically
|
|
2185
|
-
kills git commands exceeding the specified time limit, preventing hung
|
|
2186
|
-
processes from blocking the agent indefinitely.
|
|
2187
|
-
|
|
2188
|
-
### Schema
|
|
2189
|
-
|
|
2190
|
-
* **properties are now ordered and support indifferent access** <br>
|
|
2191
|
-
[`LLM::Schema::Leaf`](https://r.uby.dev/api-docs/llm.rb/LLM/Schema/Leaf.html)
|
|
2192
|
-
tracks property definition order in a new `index` attribute, matching
|
|
2193
|
-
the convention already used by
|
|
2194
|
-
[`LLM::Command::Parameter`](https://r.uby.dev/api-docs/llm.rb/LLM/Command/Parameter.html).
|
|
2195
|
-
Internally, `@properties` is stored as an
|
|
2196
|
-
[`LLM::Object`](https://r.uby.dev/api-docs/llm.rb/LLM/Object.html)
|
|
2197
|
-
instead of a plain `Hash`, so lookups with both string and symbol keys
|
|
2198
|
-
work.
|
|
2199
|
-
|
|
2200
|
-
### Buffer
|
|
2201
|
-
|
|
2202
|
-
* **more array-like message management** <br>
|
|
2203
|
-
[`LLM::Buffer`](https://r.uby.dev/api-docs/llm.rb/LLM/Buffer.html)
|
|
2204
|
-
now exposes `first`, `reject!`, `select!`, `shift`, `clear`, `drop`,
|
|
2205
|
-
`take`, and `reverse`, making it easier to query and mutate
|
|
2206
|
-
`LLM::Context#messages` like an ordinary Array. `reject!` is aliased
|
|
2207
|
-
as `delete_if` for familiarity.
|
|
2208
|
-
|
|
2209
|
-
* **`last(nil)` no longer returns the last message** <br>
|
|
2210
|
-
`LLM::Buffer#last` now uses an internal `UNDEFINED` sentinel to
|
|
2211
|
-
distinguish between no argument (`last` returns the last message)
|
|
2212
|
-
and `nil` (`last(nil)` is treated as an argument). Previously `nil`
|
|
2213
|
-
was indistinguishable from no argument.
|
|
2214
|
-
|
|
2215
|
-
### Function
|
|
2216
|
-
|
|
2217
|
-
* **consolidate `call` and `call!` into one method** <br>
|
|
2218
|
-
The private `call!` method has been merged into the public
|
|
2219
|
-
[`LLM::Function#call`](https://r.uby.dev/api-docs/llm.rb/LLM/Function.html#call-instance_method).
|
|
2220
|
-
The separate `call!` method existed for tracer-scoping logic now
|
|
2221
|
-
handled directly inside `call`. All internal call sites now use
|
|
2222
|
-
`function.call` instead of `function.call!`.
|
|
2223
|
-
|
|
2224
|
-
* **add `LLM::Function::Group` as an abstract base class** <br>
|
|
2225
|
-
A new abstract base class
|
|
2226
|
-
([`LLM::Function::Group`](https://r.uby.dev/api-docs/llm.rb/LLM/Function/Group.html))
|
|
2227
|
-
defines the interface that all concurrency strategy groups must
|
|
2228
|
-
implement: `alive?`, `interrupt!`, and `wait`. Each strategy group
|
|
2229
|
-
(`Sequential::Group`, `Thread::Group`, `Fiber::Group`,
|
|
2230
|
-
`Async::Group`, `Fork::Group`, `Ractor::Group`) now subclasses
|
|
2231
|
-
this base.
|
|
2232
|
-
|
|
2233
|
-
* **split `spawn` and `wait` across all strategies** <br>
|
|
2234
|
-
`spawn` now starts execution without blocking, and `wait` collects
|
|
2235
|
-
the result. `on_tool_start` moved into each task's `spawn` so the
|
|
2236
|
-
tracer span covers execution rather than construction. Each task and
|
|
2237
|
-
group now exposes a public `spawn` method alongside the existing
|
|
2238
|
-
`wait`/`value` methods.
|
|
2239
|
-
|
|
2240
|
-
* **spawn tasks lazily in `Group#wait`** <br>
|
|
2241
|
-
All concurrency strategy groups (Fiber::Group, Fork::Group,
|
|
2242
|
-
Ractor::Group, Thread::Group) now automatically spawn their tasks
|
|
2243
|
-
when `wait` is called if they haven't been spawned yet, matching
|
|
2244
|
-
the existing `Async::Group` behavior. This makes the spawn/wait
|
|
2245
|
-
contract consistent across all six concurrency backends.
|
|
2246
|
-
|
|
2247
|
-
### Agent
|
|
2248
|
-
|
|
2249
|
-
* **add `name` class DSL and instance method** <br>
|
|
2250
|
-
[`LLM::Agent`](https://r.uby.dev/api-docs/llm.rb/LLM/Agent.html) now
|
|
2251
|
-
has a `name` class DSL (`name "admin"`) and a corresponding `#name`
|
|
2252
|
-
instance method. The name is resolved through the same lazy-resolution
|
|
2253
|
-
path as other agent attributes. It can be set via
|
|
2254
|
-
`LLM::Agent.set(name: ...)`, `LLM::Agent.new(name: ...)`, or the class
|
|
2255
|
-
DSL. When no name is given, a default is derived from the class name
|
|
2256
|
-
(e.g., `SystemAdmin` becomes `system-admin`). The REPL uses the name
|
|
2257
|
-
as the prompt label and transcript prefix, making it easier to
|
|
2258
|
-
distinguish multiple sessions.
|
|
2259
|
-
|
|
2260
|
-
### REPL
|
|
2261
|
-
|
|
2262
|
-
* **agent identity in the prompt** <br>
|
|
2263
|
-
[`LLM::Agent#repl`](https://r.uby.dev/api-docs/llm.rb/LLM/Agent.html#repl-instance_method)
|
|
2264
|
-
and
|
|
2265
|
-
[`LLM::Repl.new`](https://r.uby.dev/api-docs/llm.rb/LLM/Repl.html#initialize-instance_method)
|
|
2266
|
-
accept a `name:` parameter (defaulting to `LLM::Agent#name`) that sets
|
|
2267
|
-
the input prompt to `provider(name)> ` and labels transcript messages
|
|
2268
|
-
with the agent's name instead of a hardcoded `agent:`. Useful when
|
|
2269
|
-
running multiple sessions.
|
|
2270
|
-
|
|
2271
|
-
* **`/compact` command** <br>
|
|
2272
|
-
New built-in `/compact` command frees context window space by dropping
|
|
2273
|
-
the oldest messages via
|
|
2274
|
-
[`LLM::Compactor::Truncate`](https://r.uby.dev/api-docs/llm.rb/LLM/Compactor/Truncate.html).
|
|
2275
|
-
Supports both integer (`/compact 32`) and percentage (`/compact 75%`)
|
|
2276
|
-
arguments. Defaults to keeping the last 128 messages.
|
|
2277
|
-
|
|
2278
|
-
* **tab-completion for `/` commands** <br>
|
|
2279
|
-
Pressing Tab on an input line starting with `/` autocompletes the
|
|
2280
|
-
command name. Repeated Tab presses cycle through matching commands.
|
|
2281
|
-
Powered by
|
|
2282
|
-
[`LLM::Command.complete(str)`](https://r.uby.dev/api-docs/llm.rb/LLM/Command.html#complete-class_method)
|
|
2283
|
-
which is available outside the REPL too.
|
|
2284
|
-
|
|
2285
|
-
* **command system enhancements** <br>
|
|
2286
|
-
Commands can set parameter defaults in their `call` method signature
|
|
2287
|
-
(e.g., `def call(n: 128)`). Aliases like `/quit` now inherit their
|
|
2288
|
-
parent's description and parameters. Commands also have access to
|
|
2289
|
-
the active `agent` and `repl` via public readers.
|
|
2290
|
-
|
|
2291
|
-
* **tool argument sorting** <br>
|
|
2292
|
-
Tool parameters in the status bar are now displayed in definition
|
|
2293
|
-
order (using the new `index` attribute), regardless of the order
|
|
2294
|
-
the model returns them.
|
|
2295
|
-
|
|
2296
|
-
* **expanded markdown rendering** <br>
|
|
2297
|
-
The curses-based markdown renderer now handles lists (`<ul>`, `<ol>`),
|
|
2298
|
-
blockquotes, horizontal rules, hyperlinks (underline), images
|
|
2299
|
-
(`[image: alt text]`), and tables (aligned columns).
|
|
2300
|
-
|
|
2301
|
-
* **input improvements** <br>
|
|
2302
|
-
Ctrl+P and Ctrl+N walk through conversation history (user messages
|
|
2303
|
-
only, managed by
|
|
2304
|
-
[`LLM::Repl::Walker`](https://r.uby.dev/api-docs/llm.rb/LLM/Repl/Walker.html)).
|
|
2305
|
-
Page Up/Down scroll the transcript by a page. ENTER and BACKSPACE are
|
|
2306
|
-
now mapped to raw character codes from `Curses.getch` instead of
|
|
2307
|
-
`Curses::Key` constants.
|
|
2308
|
-
|
|
2309
|
-
### Object
|
|
2310
|
-
|
|
2311
|
-
* **preserve the original key name in `KeyError` messages** <br>
|
|
2312
|
-
`LLM::Object#fetch` now preserves the original key name when a key
|
|
2313
|
-
is not found, instead of raising `KeyError` with `key not found: nil`.
|
|
2314
|
-
The previous behavior occurred when the given key was not found in
|
|
2315
|
-
the stored hash, causing internal lookup to return `nil` and lose
|
|
2316
|
-
the original key reference.
|
|
2317
|
-
|
|
2318
|
-
### Registry
|
|
2319
|
-
|
|
2320
|
-
* **refresh model metadata across providers** <br>
|
|
2321
|
-
Update `data/*.json` files with current provider model listings and
|
|
2322
|
-
pricing. Mark several DeepInfra models as deprecated (`meta-llama/
|
|
2323
|
-
Meta-Llama-3.1-8B-Instruct`, `Qwen/Qwen1.5-110B-Chat`, and
|
|
2324
|
-
`mistralai/Mixtral-8x7B-Instruct-v0.1`). Correct xAI cache-read
|
|
2325
|
-
pricing from $0.50 to $0.30 per million input tokens.
|
|
2326
|
-
|
|
2327
|
-
### Fix
|
|
2328
|
-
|
|
2329
|
-
* **agent: fix default name resolution when name is not explicitly set** <br>
|
|
2330
|
-
Fix a bug where `LLM::Agent` derived its default name from
|
|
2331
|
-
`self.class` instead of `self`, causing the name to be `"class"`
|
|
2332
|
-
instead of a parameterized version of the actual class name
|
|
2333
|
-
(e.g., `"system-admin"` for `SystemAdmin`). The fix uses `self`
|
|
2334
|
-
directly, which correctly resolves the class name at the instance
|
|
2335
|
-
level.
|
|
2336
|
-
|
|
2337
|
-
* **function: make Fork::Task and Ractor::Task inherit LLM::Function::Task** <br>
|
|
2338
|
-
`LLM::Function::Fork::Task` and `LLM::Function::Ractor::Task` now
|
|
2339
|
-
explicitly subclass `LLM::Function::Task` and accept an options hash
|
|
2340
|
-
as their second argument, matching the constructor signature used
|
|
2341
|
-
by the other four task classes. The interface was already compatible
|
|
2342
|
-
but the inheritance was missing by mistake. It is now consistent across
|
|
2343
|
-
all six concurrency backends.
|
|
2344
|
-
|
|
2345
|
-
* **google: fix `stream` parameter leakage that broke the provider** <br>
|
|
2346
|
-
Fix a bug in the Google provider where `stream: stream.enabled?` was
|
|
2347
|
-
being merged into request parameters, causing API-level errors. The
|
|
2348
|
-
Google provider does not use a `stream` parameter. Streaming is
|
|
2349
|
-
controlled via the URL path (`streamGenerateContent` vs
|
|
2350
|
-
`generateContent`). The fix removes the leaked parameter and correctly
|
|
2351
|
-
routes streaming requests through the appropriate path.
|
|
2352
|
-
|
|
2353
|
-
* **fork: fix deadlock on xchan.rb channel** <br>
|
|
2354
|
-
Fix a deadlock in the `:fork` concurrency strategy where both the
|
|
2355
|
-
writer and reader could get stuck on the xchan channel, preventing the
|
|
2356
|
-
reader from draining the channel. The deadlock surfaced as an errno
|
|
2357
|
-
failure, especially with large tool returns. The fix requires xchan.rb
|
|
2358
|
-
v0.22.0 and uses the `SOCK_STREAM` socket type for communicating a
|
|
2359
|
-
tool's return value.
|
|
2360
|
-
|
|
2361
|
-
## v12.6.0
|
|
2362
|
-
|
|
2363
|
-
Changes since `v12.5.1`.
|
|
2364
|
-
|
|
2365
|
-
This release adds bulk defaults for tools and agents: `LLM::Tool.defaults`
|
|
2366
|
-
for setting parameter defaults and `LLM::Agent.set` for mass-assigning
|
|
2367
|
-
class-level defaults, both mirrored on ActiveRecord and Sequel agent models.
|
|
2368
|
-
|
|
2369
|
-
It also makes `LLM::Interrupt` reliable across every concurrency strategy
|
|
2370
|
-
(:thread, :call, :fiber, :task, :fork, and :ractor) so tool cancellation
|
|
2371
|
-
works consistently regardless of execution backend, and fixes a stale fiber
|
|
2372
|
-
reference in `LLM::Context#talk` that could prevent interruption after a
|
|
2373
|
-
prior call.
|
|
2374
|
-
|
|
2375
|
-
### Add
|
|
2376
|
-
|
|
2377
|
-
* **tool: add `defaults` method for setting parameter defaults** <br>
|
|
2378
|
-
Add `LLM::Tool.defaults(properties)` for bulk-setting default values
|
|
2379
|
-
on tool parameters, matching the same interface as `LLM::Schema.defaults`.
|
|
2380
|
-
Each key maps to a parameter name; unknown keys raise `KeyError`.
|
|
2381
|
-
|
|
2382
|
-
* **agent: add `set` method for bulk-assigning class-level defaults** <br>
|
|
2383
|
-
Add `LLM::Agent.set(properties)` for mass-assigning agent defaults
|
|
2384
|
-
from a Hash. Each key maps to a class-level accessor; unknown keys
|
|
2385
|
-
raise `KeyError`.
|
|
2386
|
-
|
|
2387
|
-
* **active_record: expose `set` on `acts_as_agent` models** <br>
|
|
2388
|
-
ActiveRecord models using `acts_as_agent` can call `set` to
|
|
2389
|
-
bulk-assign agent class-level defaults.
|
|
2390
|
-
|
|
2391
|
-
* **sequel: expose `set` on `plugin :agent` models** <br>
|
|
2392
|
-
Sequel models using `plugin :agent` can call `set` to bulk-assign
|
|
2393
|
-
agent class-level defaults.
|
|
2394
|
-
|
|
2395
|
-
### Fix
|
|
2396
|
-
|
|
2397
|
-
* **function: raise `LLM::Interrupt` on thread where tool is running** <br>
|
|
2398
|
-
On cancel, `LLM::Interrupt` is now raised on the thread that is
|
|
2399
|
-
running a tool. The tool can rescue `LLM::Interrupt` and gracefully
|
|
2400
|
-
terminate (e.g., clean up resources). The previous approach used
|
|
2401
|
-
`Thread#interrupt` which was less reliable. It did not interrupt a
|
|
2402
|
-
sleeping thread.
|
|
2403
|
-
|
|
2404
|
-
* **function: suppress thread exception reporting in `:thread` concurrency** <br>
|
|
2405
|
-
Threads spawned by the `:thread` concurrency strategy now have
|
|
2406
|
-
`report_on_exception` set to `false`, preventing noisy exception
|
|
2407
|
-
messages from appearing on stderr when a thread is interrupted
|
|
2408
|
-
during tool execution.
|
|
2409
|
-
|
|
2410
|
-
* **context: clear `@owner` after `talk` completes** <br>
|
|
2411
|
-
`LLM::Context#talk` now clears the `@owner` reference in an
|
|
2412
|
-
`ensure` block after the method completes, so `interrupt!` does
|
|
2413
|
-
not attempt to interrupt a stale fiber reference from a prior
|
|
2414
|
-
call.
|
|
2415
|
-
|
|
2416
|
-
* **function: raise `LLM::Interrupt` on thread waiting in `CallGroup#wait`** <br>
|
|
2417
|
-
When using `ctx.wait(:call)`, `LLM::Interrupt` is now raised on the
|
|
2418
|
-
thread executing the sequential tool wait. `CallGroup#wait` tracks the
|
|
2419
|
-
active thread and `interrupt!` raises `LLM::Interrupt` on it, enabling
|
|
2420
|
-
interruption of the `:call` concurrency strategy just like the existing
|
|
2421
|
-
`:thread` strategy.
|
|
2422
|
-
|
|
2423
|
-
* **function: raise `LLM::Interrupt` on fiber-backed tool tasks** <br>
|
|
2424
|
-
`LLM::Interrupt` is now raised on the active fiber via `Fiber#raise`
|
|
2425
|
-
when interrupting `:fiber`-concurrency tools.
|
|
2426
|
-
<br><br>
|
|
2427
|
-
`Task#interrupt!` now dispatches by task type: `Thread#raise` for
|
|
2428
|
-
threads, `Fiber#raise` for fibers. Making interruption reliable
|
|
2429
|
-
across all concurrency strategies.
|
|
2430
|
-
|
|
2431
|
-
* **function: raise `LLM::Interrupt` on fork-backed tool tasks** <br>
|
|
2432
|
-
`LLM::Interrupt` is now raised on the main thread of a fork child
|
|
2433
|
-
process via `Thread.main.raise(LLM::Interrupt)` when interrupting
|
|
2434
|
-
`:fork`-concurrency tools, and the fork `Task#wait` re-raises the
|
|
2435
|
-
interrupt on the parent side. Making interruption reliable across
|
|
2436
|
-
all concurrency strategies including `:fork`.
|
|
2437
|
-
|
|
2438
|
-
* **function: raise `LLM::Interrupt` on `Async::Task`-backed tool tasks** <br>
|
|
2439
|
-
`LLM::Interrupt` is now raised on the underlying fiber of an
|
|
2440
|
-
`Async::Task` via `Fiber#raise` when interrupting `:task`-concurrency
|
|
2441
|
-
tools. `Task#interrupt!` now detects `Async::Task` instances and
|
|
2442
|
-
dispatches to `Fiber#raise`, extending reliable interruption to the
|
|
2443
|
-
`:task` concurrency strategy under the Async runtime.
|
|
2444
|
-
|
|
2445
|
-
* **function: raise `LLM::Interrupt` on ractor-backed tool tasks** <br>
|
|
2446
|
-
`LLM::Interrupt` is now raised on the main thread inside a ractor
|
|
2447
|
-
via `Thread.main.raise(LLM::Interrupt)` when interrupting
|
|
2448
|
-
`:ractor`-concurrency tools. A listener thread inside the tool
|
|
2449
|
-
ractor waits for an interrupt message via `Ractor.receive` and
|
|
2450
|
-
raises `LLM::Interrupt` on the ractor's main thread.
|
|
2451
|
-
<br><br>
|
|
2452
|
-
`Task#interrupt!` delegates to the mailbox to send the interrupt
|
|
2453
|
-
message. Extending reliable interruption to the `:ractor`
|
|
2454
|
-
concurrency strategy.
|
|
2455
|
-
|
|
2456
|
-
## v12.5.1
|
|
2457
|
-
|
|
2458
|
-
Changes since `v12.5.0`.
|
|
2459
|
-
|
|
2460
|
-
This release reverts the global `LLM::Function` registry fallback for tool
|
|
2461
|
-
resolution that was added in v12.5.0.
|
|
2462
|
-
|
|
2463
|
-
### Change
|
|
2464
|
-
|
|
2465
|
-
* **function: remove the global registry fallback for tool resolution** <br>
|
|
2466
|
-
Remove the `LLM::Function.find_by_name` fallback that was added in
|
|
2467
|
-
v12.5.0 as an intermediate step between available-tools lookup and
|
|
2468
|
-
raising `LLM::NoSuchToolError`. Tool calls not found in the available
|
|
2469
|
-
tools list now go directly to `function_missing` (which raises
|
|
2470
|
-
`LLM::NoSuchToolError`) without first checking the global
|
|
2471
|
-
`LLM::Function` registry.
|
|
2472
|
-
|
|
2473
|
-
## v12.5.0
|
|
2474
|
-
|
|
2475
|
-
Changes since `v12.4.0`.
|
|
2476
|
-
|
|
2477
|
-
This release extends the REPL command system with typed parameters, a
|
|
2478
|
-
built-in `/help` command, command aliases (`/quit`), and cancellation
|
|
2479
|
-
via the 'Esc' key.
|
|
2480
|
-
|
|
2481
|
-
The default HTTP timeout is increased to 15 minutes (900s) to better
|
|
2482
|
-
accommodate reasoning models and large structured outputs.
|
|
2483
|
-
`LLM::Agent#deserialize` and `LLM::Agent#restore` now return `self` for
|
|
2484
|
-
method chaining, and `LLM::Buffer#pop` is added for tail-end message
|
|
2485
|
-
removal.
|
|
2486
|
-
|
|
2487
|
-
Tool resolution gains a fallback to the global `LLM::Function` registry
|
|
2488
|
-
before raising `LLM::NoSuchToolError`, and `pending_functions` aliases
|
|
2489
|
-
are added on both contexts and agents for a consistent interface.
|
|
2490
|
-
|
|
2491
|
-
Several REPL bugs are fixed including parameter state leakage across
|
|
2492
|
-
turns and invalid tool-call error routing.
|
|
2493
|
-
|
|
2494
|
-
Model metadata is refreshed across all providers with new Anthropic,
|
|
2495
|
-
OpenAI, Google, DeepInfra, DeepSeek, and xAI model entries.
|
|
2496
|
-
|
|
2497
|
-
### Add
|
|
2498
|
-
|
|
2499
|
-
#### Buffer & function internals
|
|
2500
|
-
|
|
2501
|
-
* **buffer: add `LLM::Buffer#pop`** <br>
|
|
2502
|
-
Add `LLM::Buffer#pop` for removing the last message from the tail
|
|
2503
|
-
of the buffer, complementing the existing `#<<` and array-style
|
|
2504
|
-
message management.
|
|
2505
|
-
|
|
2506
|
-
* **function: add registry fallback for tool resolution** <br>
|
|
2507
|
-
When resolving tool calls from a message, if the tool is not found
|
|
2508
|
-
in the available tools list, it now also looks up the global
|
|
2509
|
-
`LLM::Function` registry via `LLM::Function.find_by_name` before
|
|
2510
|
-
creating a placeholder function. This improves tool resolution for
|
|
2511
|
-
tools that are registered globally but not passed directly through
|
|
2512
|
-
the request tool set.
|
|
2513
|
-
|
|
2514
|
-
#### Consistent `pending_functions` aliases
|
|
2515
|
-
|
|
2516
|
-
* **context: alias `LLM::Context#functions` as `LLM::Context#pending_functions`** <br>
|
|
2517
|
-
Add `LLM::Context#pending_functions` as an alias for `LLM::Context#functions`,
|
|
2518
|
-
so callers that prefer the more descriptive `pending_functions` name can use
|
|
2519
|
-
it instead of `functions` when checking for unresolved tool work.
|
|
2520
|
-
|
|
2521
|
-
* **agent: alias `LLM::Agent#functions` as `LLM::Agent#pending_functions`** <br>
|
|
2522
|
-
Add `LLM::Agent#pending_functions` as an alias for `LLM::Agent#functions`,
|
|
2523
|
-
matching the same alias on `LLM::Context`, so callers have a consistent
|
|
2524
|
-
`pending_functions` interface across both contexts and agents.
|
|
2525
|
-
|
|
2526
|
-
#### REPL command system
|
|
2527
|
-
|
|
2528
|
-
* **repl: extend command system with parameter support** <br>
|
|
2529
|
-
Commands can now declare typed parameters using the `parameter`
|
|
2530
|
-
DSL, modelled after `LLM::Tool` and `LLM::Schema` conventions.
|
|
2531
|
-
Parameters can be marked as required with `required %i[...]`,
|
|
2532
|
-
and values are type-checked before being passed to `call`.
|
|
2533
|
-
Argument parsing is handled by the repl: arguments are split
|
|
2534
|
-
from the input string and assigned to parameters by position.
|
|
2535
|
-
|
|
2536
|
-
```ruby
|
|
2537
|
-
class Greeter < LLM::Command
|
|
2538
|
-
name "greet"
|
|
2539
|
-
description "Greets the given name"
|
|
2540
|
-
parameter :name, String, "The person's name"
|
|
2541
|
-
required %i[name]
|
|
2542
|
-
|
|
2543
|
-
def call(name:)
|
|
2544
|
-
write("Welcome #{name}!\n")
|
|
2545
|
-
end
|
|
2546
|
-
end
|
|
2547
|
-
```
|
|
2548
|
-
|
|
2549
|
-
* **repl: add `help` command** <br>
|
|
2550
|
-
Add `LLM::Repl::Help` as a new built-in command, registered
|
|
2551
|
-
automatically via the command registry. Typing `/help` shows
|
|
2552
|
-
the `help` command's own name, description, and parameters,
|
|
2553
|
-
while `/help <name>` shows details for a specific command,
|
|
2554
|
-
including its parameters and whether each is required or
|
|
2555
|
-
optional. Unknown command names produce an error message.
|
|
2556
|
-
|
|
2557
|
-
```ruby
|
|
2558
|
-
class Help < Command
|
|
2559
|
-
name "help"
|
|
2560
|
-
description "show help for a given command"
|
|
2561
|
-
parameter :name, String, "The name of a command"
|
|
2562
|
-
|
|
2563
|
-
def call(name: nil)
|
|
2564
|
-
if name.nil?
|
|
2565
|
-
write("\n#{self.class.help}\n\n")
|
|
2566
|
-
elsif command = LLM::Command.find_by(name:)
|
|
2567
|
-
write("\n#{command.help}\n\n")
|
|
2568
|
-
else
|
|
2569
|
-
write "\nNo help for #{name} was found" \
|
|
2570
|
-
"\nThat command doesn't exist.\n\n"
|
|
2571
|
-
end
|
|
2572
|
-
end
|
|
2573
|
-
end
|
|
2574
|
-
```
|
|
2575
|
-
|
|
2576
|
-
* **repl: add support for command aliases** <br>
|
|
2577
|
-
Commands can now be aliased by creating a subclass of another
|
|
2578
|
-
command (with `LLM::Command` as an indirect ancestor). The
|
|
2579
|
-
first alias introduced is `/quit` as an alias of `/exit`.
|
|
2580
|
-
|
|
2581
|
-
```ruby
|
|
2582
|
-
class Quit < Command::Exit
|
|
2583
|
-
name "quit"
|
|
2584
|
-
end
|
|
2585
|
-
```
|
|
2586
|
-
|
|
2587
|
-
* **repl: add `Command::Parameter#optional?`** <br>
|
|
2588
|
-
Parameters now expose an `#optional?` method that returns `true`
|
|
2589
|
-
when a parameter has not been marked as required, making it
|
|
2590
|
-
possible to query parameter optionality programmatically.
|
|
2591
|
-
|
|
2592
|
-
* **repl: add `LLM::Repl::Command#write`** <br>
|
|
2593
|
-
Commands can now write output to the transcript via the `write`
|
|
2594
|
-
method. Commands also receive a reference to the active repl
|
|
2595
|
-
through their `#initialize` method, making it possible to
|
|
2596
|
-
interact with the repl window from within a command.
|
|
2597
|
-
|
|
2598
|
-
* **repl: display command errors in the curses UI** <br>
|
|
2599
|
-
Commands invoked with too few arguments now display an error
|
|
2600
|
-
message: `command(<name>): too few arguments`. Displayed directly in
|
|
2601
|
-
the curses transcript area, giving immediate feedback instead
|
|
2602
|
-
of silently failing.
|
|
2603
|
-
|
|
2604
|
-
* **repl: add `LLM::Command` convenience constant** <br>
|
|
2605
|
-
Add `LLM::Command = LLM::Repl::Command` as a shorter alias,
|
|
2606
|
-
available once `"llm/repl"` is required.
|
|
2607
|
-
|
|
2608
|
-
#### Misc
|
|
2609
|
-
|
|
2610
|
-
* **repl: implement cancellation with the 'Esc' key** <br>
|
|
2611
|
-
The curses-based REPL now supports cancelling an active model
|
|
2612
|
-
request by pressing the 'Esc' key. When a request is in progress,
|
|
2613
|
-
the status line shows `thinking • Esc to cancel`, and pressing
|
|
2614
|
-
Esc calls `LLM::Agent#cancel!` to interrupt the request. The
|
|
2615
|
-
transcript displays `request cancelled!` to confirm the
|
|
2616
|
-
cancellation.
|
|
2617
|
-
|
|
2618
|
-
### Change
|
|
2619
|
-
|
|
2620
|
-
#### Misc
|
|
2621
|
-
|
|
2622
|
-
* **provider: increase default timeout to 900s** <br>
|
|
2623
|
-
The default HTTP timeout for all providers has been increased from
|
|
2624
|
-
180 to 900 seconds (15 minutes) to better accommodate long-running
|
|
2625
|
-
requests such as reasoning models and large structured outputs.
|
|
2626
|
-
|
|
2627
|
-
* **agent: `deserialize` and `restore` return `self`** <br>
|
|
2628
|
-
`LLM::Agent#deserialize` and `LLM::Agent#restore` now return `self`
|
|
2629
|
-
(the agent instance) instead of forwarding the context's return
|
|
2630
|
-
value, enabling method chaining after restoring agent state.
|
|
2631
|
-
|
|
2632
|
-
* **context: discard all messages from a cancelled turn** <br>
|
|
2633
|
-
When `LLM::Context#cancel!` is called, all messages added during
|
|
2634
|
-
that turn are now discarded via `Buffer#slice!`, preventing edge
|
|
2635
|
-
cases where dangling tool calls between turns caused repeated
|
|
2636
|
-
cancellation loops. The `#repair!` method now handles tool call
|
|
2637
|
-
cancellations on the next turn instead of mutating the conversation
|
|
2638
|
-
buffer directly at cancellation time.
|
|
2639
|
-
|
|
2640
|
-
* **stream: drop the `error` argument from `on_tool_call`** <br>
|
|
2641
|
-
The `on_tool_call` callback no longer accepts an `error` argument.
|
|
2642
|
-
Previously, stream parsers passed both a tool and an optional error,
|
|
2643
|
-
requiring boilerplate like `if error; queue << error; end` in every
|
|
2644
|
-
callback. Error handling is now pushed directly onto the stream queue
|
|
2645
|
-
inside each provider's stream parser, so `on_tool_call(tool)` is the
|
|
2646
|
-
only signature. The REPL stream and base `LLM::Stream` class have
|
|
2647
|
-
been updated accordingly.
|
|
2648
|
-
|
|
2649
|
-
#### REPL internals
|
|
2650
|
-
|
|
2651
|
-
* **repl: pass the repl instance to command constructors** <br>
|
|
2652
|
-
`LLM::Repl::Command` subclasses now receive the active repl
|
|
2653
|
-
instance via `initialize(repl)`, enabling commands to write
|
|
2654
|
-
to the transcript and interact with the repl window.
|
|
2655
|
-
|
|
2656
|
-
* **repl: `Command#write` prefixes messages with the command name** <br>
|
|
2657
|
-
The `#write` method now prefixes output with `command(<name>): `
|
|
2658
|
-
so command messages are consistent with the `user:` and `agent:`
|
|
2659
|
-
labels in the transcript. The prefix can be customised with the
|
|
2660
|
-
`who:` keyword argument, or set to `who: nil` to disable it
|
|
2661
|
-
entirely.
|
|
2662
|
-
|
|
2663
|
-
### Fix
|
|
2664
|
-
|
|
2665
|
-
#### Misc
|
|
2666
|
-
|
|
2667
|
-
* **function: avoid silent skip of tools not found in available tools** <br>
|
|
2668
|
-
When a model calls a tool that is not present in the available tools
|
|
2669
|
-
list, instead of silently skipping the tool call (via `next`), a
|
|
2670
|
-
`LLM::NoSuchToolError` is now raised so the model receives feedback
|
|
2671
|
-
about the invalid tool call and can correct course.
|
|
2672
|
-
<br><br>
|
|
2673
|
-
An additional fallback to the global `LLM::Function` registry is
|
|
2674
|
-
tried before raising, so globally registered tools are still
|
|
2675
|
-
resolved even when not in the per-request tool set.
|
|
2676
|
-
|
|
2677
|
-
#### REPL bugs
|
|
2678
|
-
|
|
2679
|
-
* **repl: don't persist parameter state between turns** <br>
|
|
2680
|
-
Parameter state (such as `Parameter#value`) was leaking across
|
|
2681
|
-
turns because the same parameter objects were being mutated
|
|
2682
|
-
in place. A duplicate set of parameters is now created for each
|
|
2683
|
-
turn, keeping the original parameter definitions intact and
|
|
2684
|
-
preventing stale state from carrying over.
|
|
2685
|
-
|
|
2686
|
-
* **repl: reply with error when given an invalid tool** <br>
|
|
2687
|
-
When the model tries to call a tool that does not exist, the
|
|
2688
|
-
error is now pushed onto the stream queue so the model can
|
|
2689
|
-
see the error and correct course, instead of silently dropping
|
|
2690
|
-
the invalid tool call and leaving it to `Context#repair` to
|
|
2691
|
-
remove it from history.
|
|
2692
|
-
|
|
2693
|
-
* **repl: fix save of initial runtime state** <br>
|
|
2694
|
-
Fix a bug in `LLM::Repl#configure` where a non-existent path
|
|
2695
|
-
argument was treated as no path at all, preventing the initial
|
|
2696
|
-
runtime state from being saved after the first turn. The correct
|
|
2697
|
-
behavior is to create the file so it can be written to after
|
|
2698
|
-
the first turn completes.
|
|
2699
|
-
|
|
2700
|
-
### Refresh
|
|
2701
|
-
|
|
2702
|
-
* **Refresh model metadata across all providers** <br>
|
|
2703
|
-
Update model listings, pricing, capabilities, reasoning options,
|
|
2704
|
-
modality support, context limits, and release dates across all
|
|
2705
|
-
provider registries (Anthropic, AWS Bedrock, DeepInfra, DeepSeek,
|
|
2706
|
-
Google, Mistral, OpenAI, xAI, and ZAI). Notable changes include
|
|
2707
|
-
Anthropic claude-opus-4-8 and claude-sonnet-4-6 additions with
|
|
2708
|
-
effort-based reasoning, OpenAI gpt-5.6-sol/terra/luna and
|
|
2709
|
-
gpt-5-codex additions, Google gemini-3-pro-preview and
|
|
2710
|
-
gemini-3-flash-preview additions, DeepInfra Qwen3.5 and DeepSeek
|
|
2711
|
-
V4 model additions, and updated xAI Grok model entries.
|
|
2712
|
-
|
|
2713
|
-
## v12.4.0
|
|
2714
|
-
|
|
2715
|
-
Changes since `v12.3.1`.
|
|
2716
|
-
|
|
2717
|
-
This release brings major improvements to the curses-based REPL
|
|
2718
|
-
(`LLM::Agent#repl`). The REPL now supports saving and restoring runtime
|
|
2719
|
-
state across sessions, automatic paste-mode detection for fast bulk input,
|
|
2720
|
-
a command system foundation with the `/exit` command, and several new
|
|
2721
|
-
keybindings (Ctrl+F, Ctrl+K, Ctrl+Y). Tool calls are rendered with a
|
|
2722
|
-
compact function-call syntax in the status bar.
|
|
2723
|
-
|
|
2724
|
-
Two new built-in tools: `LLM::Tool::Ls` and `LLM::Tool::Which` are
|
|
2725
|
-
available as opt-in additions for file listing and executable lookup.
|
|
2726
|
-
|
|
2727
|
-
Model metadata has been refreshed across providers, the REPL loop
|
|
2728
|
-
internals have been refactored to use `catch`/`throw` for cleaner command
|
|
2729
|
-
routing, and several bugs have been fixed including a tracer restoration
|
|
2730
|
-
issue in the agent ensure clause and a missing cursor in the REPL input
|
|
2731
|
-
area.
|
|
2732
|
-
|
|
2733
|
-
### Add
|
|
2734
|
-
|
|
2735
|
-
* **repl: allow runtime state to be saved and restored** <br>
|
|
2736
|
-
`LLM::Agent#repl` now accepts a `path:` option that serializes
|
|
2737
|
-
runtime state to the filesystem. When the path already exists,
|
|
2738
|
-
runtime state is restored when the read-eval-print loop starts.
|
|
2739
|
-
Otherwise the path is written after the first turn, making it
|
|
2740
|
-
possible to resume a session across process restarts.
|
|
2741
|
-
|
|
2742
|
-
* **repl: scroll to the bottom on submit** <br>
|
|
2743
|
-
The curses-based REPL now scrolls the transcript to the bottom when
|
|
2744
|
-
the user submits their input, so the latest response is visible
|
|
2745
|
-
without needing to scroll down manually.
|
|
2746
|
-
|
|
2747
|
-
* **repl: add Ctrl+F to move the cursor forward** <br>
|
|
2748
|
-
The curses-based REPL input now supports Ctrl+F to move the cursor
|
|
2749
|
-
forward by one column, matching common terminal editing conventions
|
|
2750
|
-
found in shells like `/bin/sh`.
|
|
2751
|
-
|
|
2752
|
-
* **repl: add Ctrl+K to erase from cursor to end of line** <br>
|
|
2753
|
-
The curses-based REPL input now supports Ctrl+K to erase all text
|
|
2754
|
-
from the cursor position to the end of the input buffer, matching
|
|
2755
|
-
common terminal editing conventions found in shells like `/bin/sh`.
|
|
2756
|
-
|
|
2757
|
-
* **repl: add Ctrl+Y to paste previously killed text** <br>
|
|
2758
|
-
The curses-based REPL input now supports Ctrl+Y to insert the most
|
|
2759
|
-
recently killed text (via Ctrl+K) at the current cursor position,
|
|
2760
|
-
matching the yank/paste convention found in shells like `/bin/sh`.
|
|
2761
|
-
The killed text is stored in an internal copy buffer so it can be
|
|
2762
|
-
pasted multiple times or at different cursor positions.
|
|
2763
|
-
|
|
2764
|
-
* **repl: add command system foundation** <br>
|
|
2765
|
-
Add `LLM::Repl::Command` as a new base class for REPL commands,
|
|
2766
|
-
along with the first built-in command `LLM::Repl::Command::Exit`
|
|
2767
|
-
which exits the read-eval-print loop via `throw(:exit)`.
|
|
2768
|
-
Commands are identified by a name and can be looked up through
|
|
2769
|
-
`Command.find_by`. This is the foundation for the `/` command
|
|
2770
|
-
syntax used in the REPL input line.
|
|
2771
|
-
|
|
2772
|
-
* **repl: connect the command system to user input** <br>
|
|
2773
|
-
The curses-based REPL now routes user input through the command
|
|
2774
|
-
system. Any input string beginning with `"/"` is matched against
|
|
2775
|
-
the command registry via `Command.find_by`, and the corresponding
|
|
2776
|
-
command is executed instead of being forwarded to the model.
|
|
2777
|
-
This makes built-in commands like `/exit` functional from the
|
|
2778
|
-
input line. Command arguments are not yet supported.
|
|
2779
|
-
|
|
2780
|
-
* **repl: add `LLM::Repl::Command.registry`** <br>
|
|
2781
|
-
Add `LLM::Repl::Command.registry` for auto-registering command
|
|
2782
|
-
subclasses. The `inherited` hook captures each new subclass and
|
|
2783
|
-
stores it in the registry, making it possible to enumerate all
|
|
2784
|
-
available commands at runtime. Built-in commands like Exit are
|
|
2785
|
-
automatically registered when the command file is loaded.
|
|
2786
|
-
|
|
2787
|
-
* **repl: detect and handle paste mode in the input line** <br>
|
|
2788
|
-
The curses-based REPL input now detects paste operations by tracking
|
|
2789
|
-
the rate at which characters arrive. A paste rate of ≤50ms is
|
|
2790
|
-
assumed to be a burst of characters that could only be explained by
|
|
2791
|
-
a paste. No human types that fast. Multiline pastes are supported
|
|
2792
|
-
through internal refactoring of the input handling logic.
|
|
2793
|
-
|
|
2794
|
-
* **repl: optimize paste mode rendering** <br>
|
|
2795
|
-
Track the paste state with an internal `@paste` variable and switch
|
|
2796
|
-
to a faster input path during paste operations. While in paste mode,
|
|
2797
|
-
the input buffer is drained via `Curses.getch`, bypassing the more
|
|
2798
|
-
expensive char-by-char render path used for ordinary interactive
|
|
2799
|
-
input. This makes pasting large amounts of text noticeably faster.
|
|
2800
|
-
|
|
2801
|
-
* **Add `LLM::Tool::Ls`** <br>
|
|
2802
|
-
Add a built-in tool for listing files and directories, with optional
|
|
2803
|
-
glob pattern filtering to narrow results. <br>
|
|
2804
|
-
It must be required explicitly with `require "llm/tools/ls"`.
|
|
2805
|
-
|
|
2806
|
-
* **Add `LLM::Tool::Which`** <br>
|
|
2807
|
-
Add a built-in tool for locating an executable on the system PATH.
|
|
2808
|
-
This lets an agent check whether a command is available before
|
|
2809
|
-
attempting to run it, avoiding failed subprocess calls. <br>
|
|
2810
|
-
It must be required explicitly with `require "llm/tools/which"`.
|
|
2811
|
-
|
|
2812
|
-
* **repl: render tool calls in a function-call syntax** <br>
|
|
2813
|
-
The curses-based REPL status bar now renders tool calls with a
|
|
2814
|
-
compact function-call syntax: `tool(key: value)` instead of
|
|
2815
|
-
`tool: name`. Strings are quoted and truncated, arrays show their
|
|
2816
|
-
first two elements, and hashes collapse to `{…}`, making it easier
|
|
2817
|
-
to see what arguments the model is passing. The `tool done` status
|
|
2818
|
-
message has been removed since the tool call itself conveys
|
|
2819
|
-
completion information.
|
|
2820
|
-
|
|
2821
|
-
### Change
|
|
2822
|
-
|
|
2823
|
-
* **Refresh model metadata** <br>
|
|
2824
|
-
Update model listings, pricing, and capabilities across providers.
|
|
2825
|
-
Fix GPT-5.6 model family names in the OpenAI registry (`gpt` to
|
|
2826
|
-
`gpt-sol`, `gpt-nano` to `gpt-luna`, `gpt-mini` to `gpt-terra`).
|
|
2827
|
-
Add OpenAI models (`gpt-5.6-luna`, `gpt-5.6-sol`, `gpt-5.6-terra`)
|
|
2828
|
-
to the AWS Bedrock registry. Update DeepInfra pricing for
|
|
2829
|
-
`DeepSeek-V3` and `Sky-T1-32B-Preview`. Fix Google model knowledge
|
|
2830
|
-
cutoff dates.
|
|
2831
|
-
|
|
2832
|
-
* **repl: control the loop with catch & throw** <br>
|
|
2833
|
-
The curses-based REPL input loop now uses `catch(:exit)` and
|
|
2834
|
-
`throw(:exit)` instead of returning the `:exit` symbol and
|
|
2835
|
-
breaking out of the loop. This enables the `/command` syntax
|
|
2836
|
-
without requiring an `:exit` return value to be propagated
|
|
2837
|
-
through a potentially deeply nested call path.
|
|
2838
|
-
|
|
2839
|
-
* **repl: replace Ctrl+D with shell-like delete-at-cursor** <br>
|
|
2840
|
-
The curses-based REPL input now treats Ctrl+D as a delete action
|
|
2841
|
-
that removes the character at the current cursor position, matching
|
|
2842
|
-
the shell/Emacs convention where Ctrl+D deletes the character under
|
|
2843
|
-
the cursor instead of signalling end-of-file. The previous Ctrl+D
|
|
2844
|
-
behaviour (exiting the REPL) is superseded by the `/exit` command.
|
|
2845
|
-
|
|
2846
|
-
* **repl: switch to 'Thinking' mode after tool return** <br>
|
|
2847
|
-
The curses-based REPL status line now switches to "Thinking" mode
|
|
2848
|
-
after a tool returns, so the user can see the agent is processing
|
|
2849
|
-
the tool result rather than showing a stale tool-call status.
|
|
2850
|
-
|
|
2851
|
-
### Fix
|
|
2852
|
-
|
|
2853
|
-
* **agent: fix a subtle typo in the ensure clause** <br>
|
|
2854
|
-
Fix a subtle typo in `LLM::Agent` where the deprecated `trace` local
|
|
2855
|
-
variable was given preference over `tracer` (the preferred local name)
|
|
2856
|
-
in an `ensure` clause. The `trace` local was supported for backward
|
|
2857
|
-
compatibility but the ensure clause still referenced `trace` instead of
|
|
2858
|
-
`tracer`, which meant the previous tracer was never restored when the
|
|
2859
|
-
REPL session ended.
|
|
2860
|
-
|
|
2861
|
-
* **repl: restore the cursor in the input area** <br>
|
|
2862
|
-
Remove the `Curses.curs_set(0)` call from the REPL redraw method,
|
|
2863
|
-
which was inadvertently hiding the cursor and making it impossible
|
|
2864
|
-
to see the current position in the input area. The input field is
|
|
2865
|
-
now always drawn at its full height so the cursor position is
|
|
2866
|
-
correctly maintained after each redraw.
|
|
2867
|
-
|
|
2868
|
-
## v12.3.1
|
|
2869
|
-
|
|
2870
|
-
Changes since `v12.3.0`.
|
|
2871
|
-
|
|
2872
|
-
This release fixes a flickering issue in the curses-based REPL redraw.
|
|
2873
|
-
The full-screen clear that caused visible flickering has been replaced
|
|
2874
|
-
with a targeted cursor-hide approach, and stale rows from a larger
|
|
2875
|
-
transcript are now explicitly cleared to prevent ghost text from
|
|
2876
|
-
lingering when the transcript shrinks.
|
|
2877
|
-
|
|
2878
|
-
### Fix
|
|
2879
|
-
|
|
2880
|
-
* **repl: fix redraw flicker** <br>
|
|
2881
|
-
Replace `Curses.clear` with `Curses.curs_set(0)` in the REPL redraw
|
|
2882
|
-
method to avoid a full screen clear that caused visible flickering
|
|
2883
|
-
during redraws. The drawing order is also adjusted so the status line
|
|
2884
|
-
is drawn before the divider, and stale rows left over from a larger
|
|
2885
|
-
transcript are now explicitly cleared to prevent ghost text from
|
|
2886
|
-
lingering when the transcript shrinks.
|
|
2887
|
-
|
|
2888
|
-
## v12.3.0
|
|
2889
|
-
|
|
2890
|
-
Changes since `v12.2.0`.
|
|
2891
|
-
|
|
2892
|
-
This release brings major improvements to the curses-based REPL
|
|
2893
|
-
(`LLM::Agent#repl`). The status line now shows a context-usage bar and
|
|
2894
|
-
running cost counter, the input field expands to three rows with
|
|
2895
|
-
full cursor navigation, model responses are rendered as styled markdown,
|
|
2896
|
-
and the UI stays responsive while the agent is working by running
|
|
2897
|
-
requests in a separate thread. A new `LLM::Stream::IO` and
|
|
2898
|
-
`LLM::Stream::Disabled` provide a uniform stream representation across
|
|
2899
|
-
all stream types.
|
|
2900
|
-
|
|
2901
|
-
Mistral OCR support is added for extracting text from images and
|
|
2902
|
-
documents via the `/v1/ocr` endpoint. The `skills:` and `tools:` options
|
|
2903
|
-
on `LLM::Agent#repl` let you attach additional tools or skill directories
|
|
2904
|
-
for the duration of a session. `LLM::Object#merge!` rounds out the
|
|
2905
|
-
in-place merge API, and a new `LLM.logger` convenience method creates
|
|
2906
|
-
tracer logger instances with less verbosity.
|
|
2907
|
-
|
|
2908
|
-
### Add
|
|
2909
|
-
|
|
2910
|
-
* **Add `LLM.logger` convenience method** <br>
|
|
2911
|
-
Add `LLM.logger(llm, ...)` as a shorter, less verbose way to create an
|
|
2912
|
-
`LLM::Tracer::Logger` instance. Takes a provider and optional keyword
|
|
2913
|
-
arguments forwarded to the logger constructor.
|
|
2914
|
-
|
|
2915
|
-
* **Add `skills:` option to `LLM::Agent#repl`** <br>
|
|
2916
|
-
`LLM::Agent#repl` now accepts a `skills:` keyword argument that attaches
|
|
2917
|
-
one or more skill directories (containing `SKILL.md`) for the duration of
|
|
2918
|
-
the repl session. Skills are loaded and converted to tools, combining with
|
|
2919
|
-
any tools already configured on the agent, and are discarded when the
|
|
2920
|
-
session ends.
|
|
2921
|
-
|
|
2922
|
-
* **Add `LLM::Provider#ocr` base method** <br>
|
|
2923
|
-
Add a base `ocr(...)` method to `LLM::Provider` that raises `NotImplementedError`
|
|
2924
|
-
by default, establishing a common interface for providers that support OCR
|
|
2925
|
-
(Optical Character Recognition) on images and documents.
|
|
2926
|
-
|
|
2927
|
-
* **Add Mistral OCR endpoint support** <br>
|
|
2928
|
-
The Mistral provider now supports OCR via its `/v1/ocr` endpoint. Call
|
|
2929
|
-
`mistral.ocr(image_url: ...)` for images or `mistral.ocr(document_url: ...)`
|
|
2930
|
-
for documents (e.g., PDFs). Returns an `LLM::Response` with extracted pages,
|
|
2931
|
-
markdown content, and structured block data.
|
|
2932
|
-
|
|
2933
|
-
* **Add `LLM::Object#merge!`** <br>
|
|
2934
|
-
Add `LLM::Object#merge!` for in-place merging of hash data into an
|
|
2935
|
-
`LLM::Object` instance, complementing the existing `#merge` method.
|
|
2936
|
-
|
|
2937
|
-
* **Add `LLM::Stream::IO` and `LLM::Stream::Disabled`** <br>
|
|
2938
|
-
`LLM::Stream::IO` wraps IO-like objects as stream targets, forwarding
|
|
2939
|
-
streamed content via `#<<`. `LLM::Stream::Disabled` represents an explicitly
|
|
2940
|
-
disabled stream with no-op callbacks.
|
|
2941
|
-
|
|
2942
|
-
This is part of an internal refactoring that lets all stream values: IO
|
|
2943
|
-
objects, `true`, `false`, `nil`, and `LLM::Stream` instances themselves
|
|
2944
|
-
be represented by the same `LLM::Stream` interface via the new
|
|
2945
|
-
`LLM::Stream.try` factory method.
|
|
2946
|
-
|
|
2947
|
-
Before this change the codebase had to perform ad-hoc type checks
|
|
2948
|
-
(e.g. `if LLM::Stream === stream`) scattered throughout. After this
|
|
2949
|
-
change all stream handling goes through a single uniform path, and
|
|
2950
|
-
providers check `#enabled?` to decide whether to request streaming
|
|
2951
|
-
from the API.
|
|
2952
|
-
|
|
2953
|
-
### Change
|
|
2954
|
-
|
|
2955
|
-
* **repl: rename `trace:` to `tracer:`** <br>
|
|
2956
|
-
The `trace:` keyword argument in `LLM::Agent#repl` has been renamed to
|
|
2957
|
-
`tracer:` for consistency with the rest of the codebase. The old `trace:`
|
|
2958
|
-
name still works with a deprecation warning.
|
|
2959
|
-
|
|
2960
|
-
* **repl: add context-usage bar and cost counter to the status line** <br>
|
|
2961
|
-
The curses-based REPL status line now shows a small progress bar that
|
|
2962
|
-
indicates how much of the model's context window remains as a percentage,
|
|
2963
|
-
alongside a running cost estimate rendered on the right side of the status
|
|
2964
|
-
line. The input line has been updated to show the provider name as a prefix.
|
|
2965
|
-
Estimates are best-effort and depend on registry pricing data (see `data/`).
|
|
2966
|
-
|
|
2967
|
-
* **repl: keep the UI responsive while a request is in progress** <br>
|
|
2968
|
-
The curses-based REPL now spawns the agent request in a separate thread
|
|
2969
|
-
and communicates streamed output through a queue, so the curses UI stays
|
|
2970
|
-
responsive during model processing. Users can continue to scroll through
|
|
2971
|
-
the transcript while the agent is working.
|
|
2972
|
-
|
|
2973
|
-
* **repl: style transcript rows as structured data with bold labels** <br>
|
|
2974
|
-
The curses-based REPL transcript now stores rows as structured data with
|
|
2975
|
-
style metadata instead of plain strings, enabling bold rendering of the
|
|
2976
|
-
`user:` and `agent:` labels for improved readability during interactive
|
|
2977
|
-
sessions.
|
|
2978
|
-
|
|
2979
|
-
* **repl: render a small subset of markdown** <br>
|
|
2980
|
-
The curses-based REPL now renders model responses as styled markdown.
|
|
2981
|
-
Headers and strong text render in bold, emphasis renders in underline,
|
|
2982
|
-
and code spans and blocks are highlighted with inverted colors. Streaming
|
|
2983
|
-
content is buffered and re-rendered on each tick so the transcript reads
|
|
2984
|
-
cleanly as the agent responds. Requires the optional `kramdown` gem.
|
|
2985
|
-
|
|
2986
|
-
* **repl: add cursor LEFT/RIGHT movement to the input line** <br>
|
|
2987
|
-
The curses-based REPL input now supports cursor movement with the left
|
|
2988
|
-
and right arrow keys, enabling in-place text editing before submitting
|
|
2989
|
-
a prompt. The cursor position is tracked visually and moves backwards
|
|
2990
|
-
on left-arrow and forwards on right-arrow.
|
|
2991
|
-
|
|
2992
|
-
* **repl: add Ctrl+A and Ctrl+E keybindings to the input line** <br>
|
|
2993
|
-
The curses-based REPL input now supports Ctrl+A to jump the cursor to
|
|
2994
|
-
the start of the input line and Ctrl+E to jump it to the end, matching
|
|
2995
|
-
common terminal editing conventions.
|
|
2996
|
-
|
|
2997
|
-
* **repl: add `tools:` option to `LLM::Agent#repl`** <br>
|
|
2998
|
-
`LLM::Agent#repl` now accepts a `tools:` keyword argument that attaches
|
|
2999
|
-
additional tool classes or instances for the duration of the repl session.
|
|
3000
|
-
These tools are combined with any tools already configured on the agent,
|
|
3001
|
-
and are discarded when the session ends.
|
|
3002
|
-
|
|
3003
|
-
* **repl: add repl support to ActiveRecord and Sequel agent models** <br>
|
|
3004
|
-
`acts_as_agent` (ActiveRecord) and `plugin :agent` (Sequel) models now
|
|
3005
|
-
expose a `repl` method that delegates to the underlying agent's
|
|
3006
|
-
read-eval-print loop. This allows interactive debugging and inspection
|
|
3007
|
-
of persisted agent state at runtime. Note that changes made during a
|
|
3008
|
-
repl session do not persist back to the database.
|
|
3009
|
-
|
|
3010
|
-
* **repl: add extra padding between markdown nodes** <br>
|
|
3011
|
-
The curses-based REPL markdown renderer now adds extra vertical spacing
|
|
3012
|
-
between certain markdown elements: paragraphs, headers, and codeblocks
|
|
3013
|
-
for improved readability of model responses.
|
|
3014
|
-
|
|
3015
|
-
* **repl: add a visual divider between transcript and the rows below it** <br>
|
|
3016
|
-
The curses-based REPL now draws a horizontal divider line (using a unicode
|
|
3017
|
-
`─` character) to separate the transcript area from the status and input
|
|
3018
|
-
rows below it. A single empty buffer row is also added between the
|
|
3019
|
-
transcript and the divider, preventing transcript text from running too
|
|
3020
|
-
close to the status and input rows.
|
|
3021
|
-
|
|
3022
|
-
* **repl: expand input field to 3 rows** <br>
|
|
3023
|
-
The curses-based REPL input field now spans three rows instead of one,
|
|
3024
|
-
wrapping text that exceeds the terminal width onto subsequent lines. A
|
|
3025
|
-
scrollable viewport follows the cursor so the active line stays visible,
|
|
3026
|
-
and common navigation commands (Ctrl+A, Ctrl+E, cursor keys) work across
|
|
3027
|
-
all three rows of the expanded input area.
|
|
3028
|
-
|
|
3029
|
-
* **Refresh OpenAI model metadata** <br>
|
|
3030
|
-
Add new OpenAI models to the registry, including `gpt-5.6`,
|
|
3031
|
-
`gpt-5.6-luna`, `gpt-5.6-terra`, `gpt-5.6-sol`, and
|
|
3032
|
-
`gpt-realtime-2.1`, with associated pricing, capabilities, and
|
|
3033
|
-
limits.
|
|
3034
|
-
|
|
3035
|
-
### Fix
|
|
3036
|
-
|
|
3037
|
-
* **Fix Ollama non-streaming response handling** <br>
|
|
3038
|
-
Fix the Ollama provider to properly handle the non-streaming path. When
|
|
3039
|
-
the provider returns a raw NDJSON response body (instead of streaming),
|
|
3040
|
-
the response is now parsed and merged into a single `LLM::Object` before
|
|
3041
|
-
being returned to the caller. Previously the non-streaming path was
|
|
3042
|
-
effectively broken and would fail to produce a valid completion response.
|
|
3043
|
-
|
|
3044
|
-
* **repl: handle a negative context window allowance in the usage bar** <br>
|
|
3045
|
-
Fix a crash in the curses-based REPL context-usage bar when the context
|
|
3046
|
-
window allowance is exceeded (used > total). The negative width value that
|
|
3047
|
-
resulted from this edge case could cause curses errors; it now gracefully
|
|
3048
|
-
defaults to `0%` and zero bar width.
|
|
3049
|
-
|
|
3050
|
-
* **Fix YARD documentation across provider and tool files** <br>
|
|
3051
|
-
Fix unnamed, misnamed, and missing `@param` tags in `LLM::Repl::Status`,
|
|
3052
|
-
`LLM::Tool::Git`, `LLM::Tool::Pwd`, `LLM::Tool::Rg`, and
|
|
3053
|
-
`LLM::Tool::SwapText`.
|
|
3054
|
-
|
|
3055
|
-
## v12.2.0
|
|
3056
|
-
|
|
3057
|
-
Changes since `v12.1.0`.
|
|
3058
|
-
|
|
3059
|
-
This release adds Mistral as a new provider with chat completions, streaming, tool calls,
|
|
3060
|
-
structured outputs, file/image attachments, and embeddings support. It introduces
|
|
3061
|
-
the `trace:` option to `LLM::Agent#repl` for keeping the tracer active during
|
|
3062
|
-
interactive sessions.
|
|
3063
|
-
|
|
3064
|
-
Several fixes land for the Google provider (generationConfig parameter leakage),
|
|
3065
|
-
`LLM::Context#tracer=` (always assigning nil), `LLM::Provider#with_tracer(nil)`
|
|
3066
|
-
(nil fallback), and `LLM::Context#repair!` (dropping Struct returns).
|
|
3067
|
-
|
|
3068
|
-
The default HTTP timeout has been increased from 60s to 180s to better accommodate
|
|
3069
|
-
reasoning models and large structured outputs, and the Anthropic default model has
|
|
3070
|
-
been updated to `claude-opus-4-8`. Model metadata has been refreshed across
|
|
3071
|
-
Anthropic, AWS Bedrock, DeepInfra, Google, and xAI, with Mistral model data added
|
|
3072
|
-
to the registry.
|
|
3073
|
-
|
|
3074
|
-
### Add
|
|
3075
|
-
|
|
3076
|
-
* **Add `trace:` option to `LLM::Agent#repl`** <br>
|
|
3077
|
-
`LLM::Agent#repl` now accepts a `trace:` keyword argument. By default
|
|
3078
|
-
the tracer is disabled for the duration of the repl session to prevent
|
|
3079
|
-
curses UI interference from output written to `$stdout` or `$stderr`.
|
|
3080
|
-
Set `trace: true` to keep the tracer active during the session, which
|
|
3081
|
-
is useful when the tracer writes to a file rather than the terminal.
|
|
3082
|
-
|
|
3083
|
-
* **Add a new provider: LLM::Mistral** <br>
|
|
3084
|
-
[Mistral](https://mistral.ai) is now supported through its
|
|
3085
|
-
OpenAI-compatible API. The provider supports chat completions,
|
|
3086
|
-
streaming, tool calls, structured output (schema), file/image
|
|
3087
|
-
attachments, and embeddings. Use `LLM.mistral(...)` to create a
|
|
3088
|
-
provider instance.
|
|
3089
|
-
|
|
3090
|
-
* **Add `LLM.mistral(...)` convenience method** <br>
|
|
3091
|
-
A new top-level accessor (`LLM.mistral`) returns an `LLM::Mistral`
|
|
3092
|
-
provider instance, matching the pattern used by other providers.
|
|
3093
|
-
|
|
3094
|
-
### Fix
|
|
3095
|
-
|
|
3096
|
-
* **Fix Google `generationConfig` parameter leakage** <br>
|
|
3097
|
-
Fix a bug in the Google provider where non-generation parameters
|
|
3098
|
-
(`role`, `model`, `messages`, `stream`) were leaking into the
|
|
3099
|
-
`generationConfig` object alongside legitimate generation config
|
|
3100
|
-
parameters such as `temperature`. Non-config parameters are now
|
|
3101
|
-
filtered out before constructing `generationConfig`.
|
|
3102
|
-
|
|
3103
|
-
* **Fix `LLM::Context#tracer=` always assigning `nil`** <br>
|
|
3104
|
-
The `LLM::Context#tracer=` setter had a bug where it always assigned
|
|
3105
|
-
`nil` regardless of the tracer value passed. It now correctly assigns
|
|
3106
|
-
the given tracer or falls back to `LLM::Tracer::Null`.
|
|
3107
|
-
|
|
3108
|
-
* **Fix `LLM::Provider#with_tracer(nil)` fallback** <br>
|
|
3109
|
-
`LLM::Provider#with_tracer(nil)` now falls back to
|
|
3110
|
-
`LLM::Tracer::Null` instead of setting a `nil` tracer directly.
|
|
3111
|
-
|
|
3112
|
-
* **Fix `LLM::Context#repair!` dropping `Struct` returns** <br>
|
|
3113
|
-
`LLM::Context#repair!` used `[*prompt]` to wrap the prompt before
|
|
3114
|
-
grepping for return objects. Since `LLM::Function::Return` is a
|
|
3115
|
-
`Struct`, the splat operator expanded it into its member values
|
|
3116
|
-
instead of wrapping it, causing the grep to silently drop returns.
|
|
3117
|
-
The fix wraps both sources in an array before flattening.
|
|
3118
|
-
|
|
3119
|
-
### Change
|
|
3120
|
-
|
|
3121
|
-
* **Increase default provider timeout from 60s to 180s** <br>
|
|
3122
|
-
The default HTTP timeout for all providers has been increased from
|
|
3123
|
-
60 to 180 seconds to better accommodate long-running requests such
|
|
3124
|
-
as reasoning models and large structured outputs.
|
|
3125
|
-
|
|
3126
|
-
* **Change Anthropic default model to `claude-opus-4-8`** <br>
|
|
3127
|
-
The default Anthropic chat model has been updated from
|
|
3128
|
-
`claude-sonnet-4-20250514` to `claude-opus-4-8`, reflecting the
|
|
3129
|
-
latest model release from Anthropic.
|
|
3130
|
-
|
|
3131
|
-
* **Refresh model metadata** <br>
|
|
3132
|
-
Update model listings, pricing, and capabilities for Anthropic,
|
|
3133
|
-
AWS Bedrock, DeepInfra, Google, and xAI. Add Mistral model data
|
|
3134
|
-
to the registry.
|
|
3135
|
-
|
|
3136
|
-
## v12.1.0
|
|
3137
|
-
|
|
3138
|
-
Changes since `v12.0.0`.
|
|
3139
|
-
|
|
3140
|
-
This release adds `LLM::Agent#repl` with a curses-based
|
|
3141
|
-
interactive read-eval-print loop. It requires the optional
|
|
3142
|
-
dependency `curses` and it is probably the most notable
|
|
3143
|
-
feature in this release.
|
|
3144
|
-
|
|
3145
|
-
Multiple _opt-in_ tools have been added to the `llm/tools/*.rb`
|
|
3146
|
-
directory. They serve as examples and as general-purpose tools
|
|
3147
|
-
that happen to power the repository's agents.
|
|
3148
|
-
|
|
3149
|
-
Other changes include small-ish bug fixes. <br>
|
|
3150
|
-
As always, see the changelog details for a thorough overview.
|
|
3151
|
-
|
|
3152
|
-
### Add
|
|
3153
|
-
|
|
3154
|
-
* **Add `LLM::Agent#repl`** <br>
|
|
3155
|
-
Add a curses-based read-eval-print loop for `LLM::Agent` that lets
|
|
3156
|
-
developers interact with an agent after it has been set up or has
|
|
3157
|
-
performed a task. It is similar to `binding.irb`: once you exit,
|
|
3158
|
-
you can continue with the rest of your program. It requires the
|
|
3159
|
-
`curses` gem.
|
|
3160
|
-
|
|
3161
|
-
* **Add `#tracer=` setter on Provider, Context and Agent** <br>
|
|
3162
|
-
`LLM::Provider`, `LLM::Context` and `LLM::Agent` can now configure
|
|
3163
|
-
the tracer after initialization via the `#tracer=` setter. It accepts
|
|
3164
|
-
a subclass of `LLM::Tracer` or `nil` to disable the tracer.
|
|
3165
|
-
|
|
3166
|
-
* **Add `LLM::Tool::Shell`** <br>
|
|
3167
|
-
Add a built-in shell tool that can run a command with arguments. <br>
|
|
3168
|
-
It must be required explicitly with `require "llm/tools/shell"` and
|
|
3169
|
-
requires the `test-cmd.rb` gem.
|
|
3170
|
-
|
|
3171
|
-
* **Add `LLM::Tool::ReadFile`** <br>
|
|
3172
|
-
Add a built-in tool for reading the contents of a file, with optional
|
|
3173
|
-
`start` and `stop` line offsets. <br>
|
|
3174
|
-
It must be required explicitly with `require "llm/tools/read_file"`.
|
|
3175
|
-
|
|
3176
|
-
* **Add `LLM::Tool::Chdir`** <br>
|
|
3177
|
-
Add a built-in tool for changing the current working directory. <br>
|
|
3178
|
-
It must be required explicitly with `require "llm/tools/chdir"`.
|
|
3179
|
-
|
|
3180
|
-
* **Add `LLM::Tool::Git`** <br>
|
|
3181
|
-
Add a built-in tool that can perform git actions (`log`, `diff`,
|
|
3182
|
-
`show`, `commit`, `checkout`, `branch`). <br>
|
|
3183
|
-
It must be required explicitly with `require "llm/tools/git"` and requires the `test-cmd.rb` gem.
|
|
3184
|
-
|
|
3185
|
-
* **Add `LLM::Tool::Rg`** <br>
|
|
3186
|
-
Add a built-in tool that wraps the `rg` (ripgrep) command for
|
|
3187
|
-
recursively searching the current directory for patterns. <br>
|
|
3188
|
-
It must be required explicitly with `require "llm/tools/rg"` and requires the `test-cmd.rb` gem.
|
|
3189
|
-
|
|
3190
|
-
* **Add `LLM::Tool::SwapText`** <br>
|
|
3191
|
-
Add a built-in tool that can replace an exact snippet of text in a
|
|
3192
|
-
file with a new piece of text. <br>
|
|
3193
|
-
It must be required explicitly with `require "llm/tools/swap_text"`.
|
|
3194
|
-
|
|
3195
|
-
* **Add `LLM::Tool::Pwd`** <br>
|
|
3196
|
-
Add a built-in tool that returns the current working directory. <br>
|
|
3197
|
-
It must be required explicitly with `require "llm/tools/pwd"`.
|
|
3198
|
-
|
|
3199
|
-
* **Add `LLM::Tool::WriteFile`** <br>
|
|
3200
|
-
Add a built-in tool that can write a given string to a given file
|
|
3201
|
-
path. <br>
|
|
3202
|
-
It must be required explicitly with `require "llm/tools/write_file"`.
|
|
3203
|
-
|
|
3204
|
-
* **Add `LLM::Tool::Mkdir`** <br>
|
|
3205
|
-
Add a built-in tool that can create a tree of new directories. <br>
|
|
3206
|
-
It must be required explicitly with `require "llm/tools/mkdir"` and
|
|
3207
|
-
requires the `test-cmd.rb` gem.
|
|
3208
|
-
|
|
3209
|
-
### Change
|
|
3210
|
-
|
|
3211
|
-
* **Change LlamaCpp default port (8080 => 8013)** <br>
|
|
3212
|
-
The default port for the LlamaCpp provider has changed from `8080` to
|
|
3213
|
-
`8013` since llamacpp itself defaults to that port.
|
|
3214
|
-
|
|
3215
|
-
* **Change LlamaCpp default model to `nil`** <br>
|
|
3216
|
-
The default model for LlamaCpp is now `nil`, letting whatever model
|
|
3217
|
-
is served by the llamacpp server act as the default. Previously it
|
|
3218
|
-
defaulted to `qwen3`.
|
|
3219
|
-
|
|
3220
|
-
### Fix
|
|
3221
|
-
|
|
3222
|
-
* **Fix `LLM::Agent.tools` Symbol resolution** <br>
|
|
3223
|
-
When an agent defined tools via `tools :method_name`, the resolved
|
|
3224
|
-
symbol was incorrectly forwarded as `[:method_name]` (an array) to
|
|
3225
|
-
`LLM::Context`. This fix copies the same pattern used by other
|
|
3226
|
-
attribute resolvers (e.g., `skills`) so a single Symbol is resolved
|
|
3227
|
-
through the agent instance correctly.
|
|
3228
|
-
|
|
3229
|
-
* **Encode strings as UTF-8 in the JSON adapter** <br>
|
|
3230
|
-
The `json` gem will reject BINARY-encoded strings from version 3 and
|
|
3231
|
-
beyond. The `LLM::JSONAdapter.dump` method now walks serialized data
|
|
3232
|
-
and encodes every string into UTF-8, using `String#scrub` to replace
|
|
3233
|
-
bytes that are not valid UTF-8.
|