llm.rb 15.1.0 → 15.2.1
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 +336 -11
- data/README.md +90 -52
- data/bin/llm.rb +12 -4
- data/data/alibaba.json +912 -823
- data/data/anthropic.json +234 -187
- data/data/bedrock.json +3611 -2217
- data/data/deepinfra.json +1164 -989
- data/data/deepseek.json +65 -81
- data/data/google.json +668 -666
- data/data/mistral.json +501 -460
- data/data/moonshot.json +43 -248
- data/data/openai.json +1008 -914
- data/data/openrouter.json +7798 -7661
- data/data/xai.json +194 -194
- data/data/zai.json +242 -149
- data/docs/deepdive/advanced/compaction.md +5 -5
- data/docs/deepdive/advanced/guard.md +2 -2
- data/docs/deepdive/features/builtin_tools.md +93 -22
- data/docs/deepdive/features/{repl.md → console.md} +28 -28
- data/docs/deepdive/features/database.md +3 -3
- data/docs/deepdive/fundamentals/agents.md +2 -2
- data/docs/deepdive/fundamentals/providers.md +91 -6
- data/docs/deepdive/fundamentals/skills.md +14 -6
- data/docs/deepdive/fundamentals/tools.md +63 -31
- data/docs/deepdive/reference/cost.md +2 -2
- data/docs/deepdive/reference/model_registry.md +2 -2
- data/docs/deepdive/reference/tracer.md +15 -13
- data/docs/deepdive.md +2 -2
- data/lib/llm/active_record/acts_as_agent.rb +10 -6
- data/lib/llm/agent.rb +64 -30
- data/lib/llm/{repl → console}/bar.rb +3 -3
- data/lib/llm/{repl → console}/buffer.rb +4 -4
- data/lib/llm/{repl → console}/color.rb +2 -2
- data/lib/llm/{repl → console}/command.rb +12 -12
- data/lib/llm/{repl → console}/commands/exit.rb +4 -4
- data/lib/llm/{repl → console}/commands/help.rb +1 -1
- data/lib/llm/{repl/commands/compact.rb → console/commands/keep.rb} +11 -9
- data/lib/llm/{repl → console}/commands/model.rb +2 -2
- data/lib/llm/{repl → console}/input/cache.rb +2 -2
- data/lib/llm/{repl → console}/input/char.rb +2 -2
- data/lib/llm/{repl → console}/input/row.rb +1 -1
- data/lib/llm/{repl → console}/input.rb +13 -6
- data/lib/llm/console/markdown/parser.rb +78 -0
- data/lib/llm/{repl → console}/markdown/table.rb +3 -3
- data/lib/llm/{repl → console}/markdown.rb +13 -30
- data/lib/llm/{repl → console}/node.rb +3 -3
- data/lib/llm/{repl → console}/status.rb +11 -11
- data/lib/llm/{repl → console}/stream.rb +9 -9
- data/lib/llm/{repl → console}/walker.rb +1 -1
- data/lib/llm/{repl → console}/window.rb +10 -10
- data/lib/llm/{repl.rb → console.rb} +38 -19
- data/lib/llm/context/deserializer.rb +2 -1
- data/lib/llm/context.rb +1 -0
- data/lib/llm/function/async/reactor.rb +20 -1
- data/lib/llm/function.rb +8 -9
- data/lib/llm/json_adapter.rb +40 -28
- data/lib/llm/message.rb +7 -0
- data/lib/llm/provider.rb +2 -2
- data/lib/llm/providers/alibaba.rb +1 -1
- data/lib/llm/providers/deepseek.rb +1 -1
- data/lib/llm/providers/openai.rb +1 -0
- data/lib/llm/schema/leaf.rb +34 -2
- data/lib/llm/schema.rb +4 -2
- data/lib/llm/sequel/agent.rb +10 -6
- data/lib/llm/tool/param.rb +5 -1
- data/lib/llm/tool.rb +5 -0
- data/lib/llm/tools/bundle.rb +53 -0
- data/lib/llm/tools/edit-file.rb +7 -2
- data/lib/llm/tools/exec.rb +78 -0
- data/lib/llm/tools/git.rb +27 -26
- data/lib/llm/tools/mkdir.rb +12 -19
- data/lib/llm/tools/read_file.rb +69 -9
- data/lib/llm/tools/rg.rb +20 -24
- data/lib/llm/tools/ruby.rb +17 -25
- data/lib/llm/tools/utils.rb +74 -1
- data/lib/llm/tools/write_file.rb +4 -1
- data/lib/llm/tracer/logger.rb +2 -2
- data/lib/llm/tracer/pretty_logger.rb +4 -4
- data/lib/llm/tracer/telemetry.rb +2 -2
- data/lib/llm/tracer.rb +33 -0
- data/lib/llm/transport/utils.rb +1 -1
- data/lib/llm/version.rb +1 -1
- data/lib/llm.rb +4 -13
- data/llm.gemspec +7 -8
- metadata +64 -37
- data/lib/llm/tools/shell.rb +0 -55
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8437f48c9a995054696648dfbe3386a5f0d8c8a62379765d2eca8694cf3adb3b
|
|
4
|
+
data.tar.gz: d2c36a69342e2fef94472abc25e813a0ee8bbef9aba15e68b275d89d307a6405
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d1d120fe448ee693ba627cfdfe213cd42030fe8448f72c8cf6ecaddb62d2ac5f7b07ded6191142ec28571b41acbdf8c96b458420889e7fb75990f53f51b3ab43
|
|
7
|
+
data.tar.gz: 816b6feffe0339f52798062de3f36188b3d6574203289f450fd07de2e1e600b29aa956a8ba50947d3ae27540a4132222f3b31bb865784f88e24074388a852058
|
data/CHANGELOG.md
CHANGED
|
@@ -11,10 +11,335 @@
|
|
|
11
11
|
</p>
|
|
12
12
|
|
|
13
13
|
> Changelog <br>
|
|
14
|
-
>
|
|
14
|
+
> [r.uby.dev](https://r.uby.dev) project
|
|
15
15
|
|
|
16
16
|
## What's next
|
|
17
17
|
|
|
18
|
+
*No unreleased changes yet. Check back after the next release.*
|
|
19
|
+
|
|
20
|
+
## v15.2.1
|
|
21
|
+
|
|
22
|
+
Changes since `v15.2.0`.
|
|
23
|
+
|
|
24
|
+
This release makes the agent count its tool budget across the whole turn
|
|
25
|
+
and emit tool returns to the stream once that budget is spent, and lets
|
|
26
|
+
`LLM::Function#cancel` carry extra return fields.
|
|
27
|
+
|
|
28
|
+
### Agent
|
|
29
|
+
|
|
30
|
+
* **agent: count the tool budget across the whole turn** <br>
|
|
31
|
+
[`LLM::Agent.tool_budget`](https://r.uby.dev/api-docs/llm.rb/LLM/Agent.html#tool_budget-class_method)
|
|
32
|
+
now caps the tool calls a turn runs in total. A batch of calls is
|
|
33
|
+
spent as a batch, and a batch that would take the turn past its
|
|
34
|
+
budget is not run at all; once the budget is spent no tool call runs,
|
|
35
|
+
and the agent keeps sending its in-band advisory until the model
|
|
36
|
+
answers without requesting more tools. Previously the agent ran up to
|
|
37
|
+
the budget, then ran further batches after each advisory, so a turn
|
|
38
|
+
could run more tool calls than its budget allowed.
|
|
39
|
+
|
|
40
|
+
* **agent: emit tool returns when the budget is spent** <br>
|
|
41
|
+
Fix a bug where, once a turn's tool budget was spent, the agent passed
|
|
42
|
+
its in-band returns to the model without emitting them to the stream.
|
|
43
|
+
The stream went silent, so a stream that tracked tool-call state left
|
|
44
|
+
the calls in the `call` state. The returns are now emitted through
|
|
45
|
+
[`LLM::Stream#on_tool_return`](https://r.uby.dev/api-docs/llm.rb/LLM/Stream.html#on_tool_return-instance_method)
|
|
46
|
+
like any other tool return.
|
|
47
|
+
|
|
48
|
+
### Function
|
|
49
|
+
|
|
50
|
+
* **function: `LLM::Function#cancel` takes extra return fields** <br>
|
|
51
|
+
[`LLM::Function#cancel`](https://r.uby.dev/api-docs/llm.rb/LLM/Function.html#cancel-instance_method)
|
|
52
|
+
now accepts keywords beyond `reason:` and merges them into the
|
|
53
|
+
[`LLM::Function::Return`](https://r.uby.dev/api-docs/llm.rb/LLM/Function/Return.html)
|
|
54
|
+
it builds. `LLM::Function#budget_spent` now builds its return through
|
|
55
|
+
`cancel` instead of hand-rolling an error, so a budget-spent return is
|
|
56
|
+
marked `cancelled: true` and carries `action:` and `advice:` hints. A
|
|
57
|
+
callback that receives returns can now tell a cancellation apart from
|
|
58
|
+
an ordinary error.
|
|
59
|
+
|
|
60
|
+
## v15.2.0
|
|
61
|
+
|
|
62
|
+
Changes since `v15.1.0`.
|
|
63
|
+
|
|
64
|
+
This release renames the REPL to `LLM::Console` (with `/keep` replacing
|
|
65
|
+
`/compact`) and routes every shell-out tool through a shared, bounded
|
|
66
|
+
`exec` runner. It also adds `LLM::Message#created_at`, the `LLM::Tracer`
|
|
67
|
+
factory methods, a `bundle` tool, per-tool `max_bytes` output limits,
|
|
68
|
+
and a `-v` switch to the CLI, and refreshes the model registry.
|
|
69
|
+
|
|
70
|
+
### Core
|
|
71
|
+
|
|
72
|
+
* **message: add `LLM::Message#created_at`** <br>
|
|
73
|
+
[`LLM::Message#created_at`](https://r.uby.dev/api-docs/llm.rb/LLM/Message.html#created_at-instance_method)
|
|
74
|
+
returns the time the message was created, defaulting to the moment the
|
|
75
|
+
message is initialized. The timestamp is serialized into
|
|
76
|
+
[`LLM::Context#to_json`](https://r.uby.dev/api-docs/llm.rb/LLM/Context.html#to_json-instance_method)
|
|
77
|
+
as an ISO-8601 string and restored on deserialization, so it can be
|
|
78
|
+
stored alongside the rest of the conversation.
|
|
79
|
+
|
|
80
|
+
### Agent
|
|
81
|
+
|
|
82
|
+
* **agent: inherit the ORM model's name** <br>
|
|
83
|
+
An `acts_as_agent` (ActiveRecord) or `plugin :agent` (Sequel) model now
|
|
84
|
+
names its generated
|
|
85
|
+
[`LLM::Agent`](https://r.uby.dev/api-docs/llm.rb/LLM/Agent.html) after
|
|
86
|
+
the model class. Previously the agent was an anonymous subclass, so
|
|
87
|
+
without an explicit name it defaulted to a gibberish `#<Class:0x...>`
|
|
88
|
+
string. The wrapper now initializes the agent's name before `.agent`
|
|
89
|
+
returns, and
|
|
90
|
+
[`LLM::Agent.name`](https://r.uby.dev/api-docs/llm.rb/LLM/Agent.html#name-class_method)
|
|
91
|
+
kebab-cases a `Class` argument, so an `AdminUser` model yields an agent
|
|
92
|
+
named `admin-user`.
|
|
93
|
+
|
|
94
|
+
### Cli
|
|
95
|
+
|
|
96
|
+
* **cli: add a `-v` switch** <br>
|
|
97
|
+
`bin/llm.rb` gains a `-v` switch that prints the version (`llm.rb v#{LLM::VERSION}`) and exits.
|
|
98
|
+
|
|
99
|
+
### Console
|
|
100
|
+
|
|
101
|
+
* **console: raise `LLM::Interrupt` on the agent's thread** <br>
|
|
102
|
+
Pressing Esc to cancel now also raises `LLM::Interrupt` on the agent's
|
|
103
|
+
thread. `LLM::Agent#cancel!` alone can be a no-op at some stages of the
|
|
104
|
+
request lifecycle, so the console backs it up by interrupting the thread
|
|
105
|
+
that runs the agent.
|
|
106
|
+
|
|
107
|
+
* **console: add a `/keep` command and retire `/compact`** <br>
|
|
108
|
+
The console now offers `/keep` for freeing space in the context window;
|
|
109
|
+
the `/compact` command is removed. `/keep` takes the same argument, so
|
|
110
|
+
`/keep 20%` keeps 20% of the context window. Closes
|
|
111
|
+
[issue #161](https://github.com/r-uby-dev/llm.rb/issues/161).
|
|
112
|
+
|
|
113
|
+
* **console: keep the UI responsive during long streams** <br>
|
|
114
|
+
A model can emit many chunks in a single turn. The console now draws
|
|
115
|
+
at most four streamed chunks at a time, then checks for input, so the
|
|
116
|
+
UI stays responsive even when a turn produces a large amount of
|
|
117
|
+
output.
|
|
118
|
+
|
|
119
|
+
* **console: persist the conversation when a turn is done** <br>
|
|
120
|
+
The console now saves the agent's state after the turn finishes,
|
|
121
|
+
rather than while the response is still streaming. State is still
|
|
122
|
+
saved every turn, but not until the turn has completed.
|
|
123
|
+
|
|
124
|
+
* **console: render markdown text as typed** <br>
|
|
125
|
+
Fix a bug where [`LLM::Console::Markdown`](https://r.uby.dev/api-docs/llm.rb/LLM/Console/Markdown.html)
|
|
126
|
+
mangled the model's output: HTML could render invisible, and
|
|
127
|
+
sequences like `...` were converted to unicode glyphs. The renderer
|
|
128
|
+
now uses a custom kramdown parser that disables the HTML, smart-quote,
|
|
129
|
+
and typographic-symbol parsers, so tags and punctuation come through
|
|
130
|
+
exactly as written.
|
|
131
|
+
|
|
132
|
+
* **console: fix a crash in the markdown parser** <br>
|
|
133
|
+
Fix a bug where the markdown renderer raised an error on an unclosed
|
|
134
|
+
HTML tag or a partial tag taken out of context, such as `4 < 5`. The
|
|
135
|
+
parser now emits the `<...` run literally when there is no closing
|
|
136
|
+
`>`, so the text renders instead of crashing.
|
|
137
|
+
|
|
138
|
+
* **console: stop rendering bare pipes as tables** <br>
|
|
139
|
+
Fix a bug where the markdown renderer treated a lone `|foo|` in prose
|
|
140
|
+
as a table and mangled its output. A pipe line now parses as a table
|
|
141
|
+
only when a header row is followed by a delimiter row, so bare pipes
|
|
142
|
+
come through literally while real tables still render.
|
|
143
|
+
|
|
144
|
+
* **console: find the worker thread when cancelling** <br>
|
|
145
|
+
Fix a bug where pressing Esc to cancel raised `LLM::Interrupt` on an
|
|
146
|
+
instance variable that does not exist, so the interrupt was a no-op and
|
|
147
|
+
a cancel could leave the turn running. The console now resolves the
|
|
148
|
+
worker thread through its `#thread` reader and interrupts it.
|
|
149
|
+
|
|
150
|
+
* **console: protect the state write from cancellation** <br>
|
|
151
|
+
The console now defers `LLM::Interrupt` while it saves the agent's
|
|
152
|
+
state after a turn, so a cancel that arrives during the write cannot
|
|
153
|
+
interrupt `agent.save` mid-flight and risk a lost or corrupted session
|
|
154
|
+
file.
|
|
155
|
+
|
|
156
|
+
### Tools
|
|
157
|
+
|
|
158
|
+
* **tools: the command runner is now `exec`** <br>
|
|
159
|
+
The command tool that spawns a process without a shell is now
|
|
160
|
+
[`LLM::Tool::Exec`](https://r.uby.dev/api-docs/llm.rb/LLM/Tool/Exec.html),
|
|
161
|
+
with the tool name `exec` instead of the previous `shell`. This is an
|
|
162
|
+
internal refactor of the shell-out tools: `git`, `rg`, `mkdir`,
|
|
163
|
+
`ruby`, and `bundle` all route through it and inherit
|
|
164
|
+
its bounded output.
|
|
165
|
+
|
|
166
|
+
* **tools: report when a command cannot be found** <br>
|
|
167
|
+
`LLM::Tool::Exec` now returns `{ok: false, error: "command 'NAME' was
|
|
168
|
+
not found on this system"}` when the requested command is missing,
|
|
169
|
+
instead of a bare `{ok: false}` result that did not tell the model why
|
|
170
|
+
the tool failed.
|
|
171
|
+
|
|
172
|
+
* **tools: drop the `name:` parameter from `LLM::Tool::Exec#call`** <br>
|
|
173
|
+
`LLM::Tool::Exec#call` now takes a single `arguments:` array instead of
|
|
174
|
+
separate `name:` and `arguments:` parameters, with the command name as
|
|
175
|
+
the first element (for example `arguments: ["rg", "-m", "10", "lib"]`).
|
|
176
|
+
The `Git`, `Mkdir`, `Rg`, `Ruby`, and `Bundle` tools build their calls
|
|
177
|
+
the same way. The change was made after models were observed confusing
|
|
178
|
+
the two parameters, so a single list is simpler and more reliable.
|
|
179
|
+
|
|
180
|
+
* **tools: rename `repl` as `console`** <br>
|
|
181
|
+
The interactive loop is renamed to
|
|
182
|
+
[`LLM::Console`](https://r.uby.dev/api-docs/llm.rb/LLM/Console.html),
|
|
183
|
+
which better reflects what it does. `agent.console` is the primary
|
|
184
|
+
entry point, and the require path moves from `llm/repl` to
|
|
185
|
+
`llm/console`. Backwards-compatible aliases remain: `LLM::Repl`,
|
|
186
|
+
`LLM::Agent#repl`, the ORM wrappers' `#repl`, and `LLM::Command =`
|
|
187
|
+
`LLM::Console::Command`.
|
|
188
|
+
|
|
189
|
+
* **tools: `LLM::Tool::Git#call` takes an `arguments:` array** <br>
|
|
190
|
+
[`LLM::Tool::Git#call`](https://r.uby.dev/api-docs/llm.rb/LLM/Tool/Git.html)
|
|
191
|
+
now takes a single `arguments:` array in place of the previous
|
|
192
|
+
`subcommand:` parameter. The first element must be one of `log`,
|
|
193
|
+
`diff`, `commit`, `checkout`, `branch`, or `show`, validated before the
|
|
194
|
+
command is spawned; the remaining elements are forwarded to git.
|
|
195
|
+
|
|
196
|
+
* **tools: `LLM::Tool::Utils` now owns command spawning** <br>
|
|
197
|
+
The shared [`LLM::Tool::Utils`](https://r.uby.dev/api-docs/llm.rb/LLM/Tool/Utils.html)
|
|
198
|
+
module now requires the `test-cmd.rb` gem (at `~> 2.7.1`) itself and
|
|
199
|
+
exposes the `spawn` and `wait` helpers, so any tool that includes
|
|
200
|
+
`Utils` gets command spawning without requiring `exec` directly. The
|
|
201
|
+
`Git`, `Mkdir`, `Rg`, `Ruby`, `Exec`, and `Bundle` tools all
|
|
202
|
+
inherit their bounded-output protections from this shared runner.
|
|
203
|
+
|
|
204
|
+
* **tools: route `git`, `rg`, `mkdir`, and `ruby` through `exec`** <br>
|
|
205
|
+
`LLM::Tool::Git`, `LLM::Tool::Rg`, `LLM::Tool::Mkdir`, and
|
|
206
|
+
`LLM::Tool::Ruby` now implement their calls through the `exec` tool,
|
|
207
|
+
completing the refactor so every tool that shells out flows through
|
|
208
|
+
the shared command runner with its bounded output.
|
|
209
|
+
|
|
210
|
+
* **tools: read-file returns structured lines** <br>
|
|
211
|
+
`LLM::Tool::ReadFile#call` now returns its content as structured
|
|
212
|
+
`{lineno:, content:}` lines under a `lines:` key instead of a single
|
|
213
|
+
`content:` string, and adds a `truncated:` flag. A reversed range
|
|
214
|
+
(`start: 20, stop: 2`) is swapped to read lines 2 through 20. The
|
|
215
|
+
truncation marker is kept out of the returned lines, so the model
|
|
216
|
+
does not mistake it for a real file line.
|
|
217
|
+
|
|
218
|
+
* **tools: write-file appends a trailing newline by default** <br>
|
|
219
|
+
`LLM::Tool::WriteFile` now ensures written content ends with a newline,
|
|
220
|
+
adding one when the content does not already end with `\n`. It previously
|
|
221
|
+
wrote the content exactly as given. A new `newline:` parameter (default
|
|
222
|
+
`true`) controls this, so `newline: false` writes the content exactly as
|
|
223
|
+
given.
|
|
224
|
+
|
|
225
|
+
* **tools: fix `edit-file` treating `before` as a regex** <br>
|
|
226
|
+
`LLM::Tool::EditFile` now escapes the `before` snippet with
|
|
227
|
+
`Regexp.escape`, so regex metacharacters are matched literally, and
|
|
228
|
+
switches to the block form of `sub` so the `after` replacement keeps
|
|
229
|
+
backslash sequences like `\1` and `\&` literal.
|
|
230
|
+
|
|
231
|
+
* **tools: bound tool output with a per-tool `max_bytes`** <br>
|
|
232
|
+
Each of the `Exec`, `ReadFile`, `Rg`, `Mkdir`, `Ruby`, and
|
|
233
|
+
`Bundle` tools gains a `max_bytes` limit (default 75,000) for the
|
|
234
|
+
maximum number of bytes a tool returns to the model. `Exec` and
|
|
235
|
+
`ReadFile` add the class-level `max_bytes` accessor, which the other
|
|
236
|
+
tools inherit through `Exec`, so each tool's cap can be configured
|
|
237
|
+
independently, for example `LLM::Tool::ReadFile.max_bytes(175_000)`.
|
|
238
|
+
It does not enforce the limit by itself;
|
|
239
|
+
[`LLM::Tool::Utils#truncate`](https://r.uby.dev/api-docs/llm.rb/LLM/Tool/Utils.html#truncate-instance_method)
|
|
240
|
+
trims a string within the limit and marks the trailing content as
|
|
241
|
+
truncated, and `truncate!` returns a `[content, truncated]` tuple for
|
|
242
|
+
callers that structure truncated output themselves. `rg` also gains a
|
|
243
|
+
`max_count:` parameter that caps the number of results per file.
|
|
244
|
+
|
|
245
|
+
* **tools: add a `bundle` tool** <br>
|
|
246
|
+
A new [`LLM::Tool::Bundle`](https://r.uby.dev/api-docs/llm.rb/LLM/Tool/Bundle.html)
|
|
247
|
+
tool runs a command through `bundle`. It uses the `BUNDLE_GEMFILE`
|
|
248
|
+
environment variable when set, or a `Gemfile` in the current working
|
|
249
|
+
directory otherwise. The tool takes an `arguments:` array, so the
|
|
250
|
+
model passes the bundle command and its arguments as a single list,
|
|
251
|
+
for example `arguments: ["exec", "rspec"]`.
|
|
252
|
+
|
|
253
|
+
* **tools: resolve defaults through `LLM::Utils.resolve_option`** <br>
|
|
254
|
+
A tool parameter default can now be an immediate value, a Symbol resolved
|
|
255
|
+
as a method on the tool, or a Proc evaluated lazily at runtime, matching
|
|
256
|
+
how `LLM::Agent` resolves its attributes. This lets a default track a
|
|
257
|
+
value that can change between boot and runtime, such as a tool's
|
|
258
|
+
`max_bytes`.
|
|
259
|
+
|
|
260
|
+
### Registry
|
|
261
|
+
|
|
262
|
+
* **refresh model metadata** <br>
|
|
263
|
+
Update `data/` with current pricing, limits, and capabilities for the
|
|
264
|
+
OpenRouter, OpenAI, Bedrock, DeepInfra, DeepSeek, Google, Mistral,
|
|
265
|
+
Moonshot, Z.ai, and Alibaba registries.
|
|
266
|
+
|
|
267
|
+
### Provider
|
|
268
|
+
|
|
269
|
+
* **provider: retry `Net::WriteTimeout`, too** <br>
|
|
270
|
+
Requests that raise `Net::WriteTimeout` are now retried alongside the
|
|
271
|
+
other timed-out and rate-limited requests, up to the `retry_budget`,
|
|
272
|
+
matching how `Net::OpenTimeout` and `Net::ReadTimeout` are handled. The
|
|
273
|
+
console status bar also reports a write timeout as `Timed out`.
|
|
274
|
+
|
|
275
|
+
* **alibaba: default to a retry budget of 8** <br>
|
|
276
|
+
An agent that runs on the Alibaba provider now defaults to a retry
|
|
277
|
+
budget of 8 instead of 5, because Alibaba (token plan) frequently rate
|
|
278
|
+
limits and times out requests that it later recovers from. An explicit
|
|
279
|
+
`retry_budget:` still overrides the default.
|
|
280
|
+
|
|
281
|
+
* **deepseek: default to the `deepseek-flash` model** <br>
|
|
282
|
+
The default DeepSeek chat model is now `deepseek-flash` instead of
|
|
283
|
+
`deepseek-v4-flash`. DeepSeek resolves `deepseek-flash` to
|
|
284
|
+
`deepseek-v4.1-flash` and recommends the name in its documentation and
|
|
285
|
+
API error messages, so the default follows the current model alias
|
|
286
|
+
instead of a pinned version.
|
|
287
|
+
|
|
288
|
+
### Tracer
|
|
289
|
+
|
|
290
|
+
* **tracer: add `LLM::Tracer` factory methods** <br>
|
|
291
|
+
Add
|
|
292
|
+
[`LLM::Tracer.logger`](https://r.uby.dev/api-docs/llm.rb/LLM/Tracer.html#logger-class_method),
|
|
293
|
+
[`LLM::Tracer.pretty_logger`](https://r.uby.dev/api-docs/llm.rb/LLM/Tracer.html#pretty_logger-class_method),
|
|
294
|
+
and
|
|
295
|
+
[`LLM::Tracer.telemetry`](https://r.uby.dev/api-docs/llm.rb/LLM/Tracer.html#telemetry-class_method)
|
|
296
|
+
as the preferred way to build a tracer for a provider, so switching
|
|
297
|
+
between tracers means changing a factory method instead of a class name.
|
|
298
|
+
The old `LLM.logger(llm, ...)` convenience method is removed in favor
|
|
299
|
+
of `LLM::Tracer.logger(llm, ...)`.
|
|
300
|
+
|
|
301
|
+
* **tracer: add `path:` support to `LLM::Tracer::PrettyLogger`** <br>
|
|
302
|
+
[`LLM::Tracer::PrettyLogger`](https://r.uby.dev/api-docs/llm.rb/LLM/Tracer/PrettyLogger.html)
|
|
303
|
+
now accepts a `path:` option to write its human-readable entries to a
|
|
304
|
+
file, matching `LLM::Tracer::Logger`. It previously only accepted `io:`.
|
|
305
|
+
|
|
306
|
+
### Fix
|
|
307
|
+
|
|
308
|
+
* **json: scrub invalid UTF-8 on dump** <br>
|
|
309
|
+
Fix a bug where [`LLM::JSONAdapter`](https://r.uby.dev/api-docs/llm.rb/LLM/JSONAdapter.html)
|
|
310
|
+
raised a JSON generator error when dumping a string tagged as UTF-8 that
|
|
311
|
+
carried invalid bytes. The normalize step now transcodes every string to
|
|
312
|
+
valid UTF-8, replacing invalid sequences with the replacement character,
|
|
313
|
+
so dumping works on `json ~> 3.0`. The `oj` and `yajl` adapters now run
|
|
314
|
+
the same normalization, so every backend scrubs invalid bytes before
|
|
315
|
+
serializing.
|
|
316
|
+
|
|
317
|
+
* **fork: require xchan.rb `~> 0.23`** <br>
|
|
318
|
+
The `:fork` concurrency strategy now requires the `xchan.rb` gem at
|
|
319
|
+
`~> 0.23` instead of `~> 0.22`. xchan.rb 0.23.0 replaces the external
|
|
320
|
+
`lockf.rb` gem with a built-in, Fiddle-based `Chan::Lockf`, so fork
|
|
321
|
+
channels no longer carry that extra dependency. (The socket
|
|
322
|
+
length-header deadlock fix shipped earlier, in xchan.rb 0.22.0.)
|
|
323
|
+
|
|
324
|
+
* **async: fix a shutdown exception on the reactor thread** <br>
|
|
325
|
+
Fix a bug where the `:async` strategy's
|
|
326
|
+
[`LLM::Function::Async::Reactor`](https://r.uby.dev/api-docs/llm.rb/LLM/Function/Async/Reactor.html)
|
|
327
|
+
raised a `TypeError` on shutdown with recent `async` and `io-event`
|
|
328
|
+
versions, because their internals tried to raise an integer as an
|
|
329
|
+
exception. The scheduler is now detached from the reactor thread
|
|
330
|
+
before it exits, which avoids that code path entirely, and teardown
|
|
331
|
+
is managed by `reactor.stop`, so the thread exits promptly instead of
|
|
332
|
+
abruptly or hanging.
|
|
333
|
+
|
|
334
|
+
* **openai: prevent the loss of user messages in the completions path** <br>
|
|
335
|
+
Fix a bug where the request body was built from `params[:messages]`
|
|
336
|
+
alone when that key was present, discarding the messages built from the
|
|
337
|
+
prompt. The DeepSeek and Alibaba schema support injects the schema
|
|
338
|
+
system message into `params[:messages]`, so a request with a `schema:`
|
|
339
|
+
could be sent with the schema message only, dropping the user's
|
|
340
|
+
messages. The built messages now lead with `params[:messages]`, and the
|
|
341
|
+
key is removed before the body is assembled.
|
|
342
|
+
|
|
18
343
|
## v15.1.0
|
|
19
344
|
|
|
20
345
|
Changes since `v15.0.3`.
|
|
@@ -23,7 +348,7 @@ This release adds the OpenRouter provider, splits provider timeouts
|
|
|
23
348
|
into `connect_timeout` and `read_timeout`, retries timed-out requests,
|
|
24
349
|
and renames `on_rate_limit` to `on_retry`. Skills now gain a
|
|
25
350
|
frontmatter `model:` parameter and inherit the parent agent's model,
|
|
26
|
-
while the CLI gains `-m` and `-x` switches, and the
|
|
351
|
+
while the CLI gains `-m` and `-x` switches, and the console shows retry
|
|
27
352
|
progress and measures text by display width.
|
|
28
353
|
|
|
29
354
|
### Provider
|
|
@@ -132,21 +457,21 @@ progress and measures text by display width.
|
|
|
132
457
|
up to three stack lines from the backtrace, so the failure is easier to
|
|
133
458
|
locate and report than a bare diagnostic.
|
|
134
459
|
|
|
135
|
-
###
|
|
460
|
+
### Console
|
|
136
461
|
|
|
137
|
-
* **
|
|
138
|
-
When a request is rate limited or times out, the curses-based
|
|
462
|
+
* **console: show retry progress in the status bar** <br>
|
|
463
|
+
When a request is rate limited or times out, the curses-based console status
|
|
139
464
|
bar shows a retry indicator with the error and the remaining attempts, for
|
|
140
465
|
example `🔁 Rate limited • attempt 2 of 5`.
|
|
141
466
|
|
|
142
|
-
* **
|
|
143
|
-
The curses-based
|
|
467
|
+
* **console: measure text width with `unicode-display_width`** <br>
|
|
468
|
+
The curses-based console now counts and slices text by display column width
|
|
144
469
|
instead of character count, so wrapping, table columns, and clipping stay
|
|
145
470
|
aligned for wide characters such as emoji. It requires the optional
|
|
146
471
|
`unicode-display_width` gem.
|
|
147
472
|
|
|
148
|
-
* **
|
|
149
|
-
The curses-based
|
|
473
|
+
* **console: treat `LLM::InsufficientQuotaError` as a rate limit in the status bar** <br>
|
|
474
|
+
The curses-based console status bar now shows `Rate limited` when a request
|
|
150
475
|
raises
|
|
151
476
|
[`LLM::InsufficientQuotaError`](https://r.uby.dev/api-docs/llm.rb/LLM/InsufficientQuotaError.html),
|
|
152
477
|
matching how ordinary `LLM::RateLimitError`s are shown, instead of falling
|
|
@@ -1222,7 +1547,7 @@ reliable across all six concurrency backends. The `functions` and
|
|
|
1222
1547
|
`LLM.require` now accepts a second `version` parameter that is passed
|
|
1223
1548
|
to `Kernel#gem` before loading, enabling version constraints for
|
|
1224
1549
|
optional runtime dependencies. For example,
|
|
1225
|
-
`LLM.require "test-cmd.rb", "~> 2.
|
|
1550
|
+
`LLM.require "test-cmd.rb", "~> 2.2"` ensures a minimum gem version
|
|
1226
1551
|
is available. This is used internally by the `Git`, `Rg`, `Mkdir`,
|
|
1227
1552
|
and `Shell` tools to enforce compatibility with the `test-cmd.rb` gem.
|
|
1228
1553
|
|
|
@@ -2286,7 +2611,7 @@ As always, see the changelog details for a thorough overview.
|
|
|
2286
2611
|
* **Add `LLM::Agent#repl`** <br>
|
|
2287
2612
|
Add a curses-based read-eval-print loop for `LLM::Agent` that lets
|
|
2288
2613
|
developers interact with an agent after it has been set up or has
|
|
2289
|
-
performed a task. It is similar to `binding.
|
|
2614
|
+
performed a task. It is similar to `binding.irb`: once you exit,
|
|
2290
2615
|
you can continue with the rest of your program. It requires the
|
|
2291
2616
|
`curses` gem.
|
|
2292
2617
|
|