llm.rb 9.0.0 → 10.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 197ff330dc5e414f4f9291835fbcdeece4450ee3a8d3748e4f9cf28a46db07b1
4
- data.tar.gz: 3020a4511134f292ed38c6fc826b157f05cc31c722e9fe52692b8b2f705551c7
3
+ metadata.gz: 6ba756238fa72e58ba774567a0c8e2a6d7351cb6313f9c8c08cbdeec8ec9cfa4
4
+ data.tar.gz: cba8295670dab2843cec902ae97b7ae14e775359380ba401ca5a0066eb60ad0e
5
5
  SHA512:
6
- metadata.gz: 41f733d7d5b8a329420497f85c289f070f9016cf4d1bfdf5c5e49e274714310f5285e5598d4d27f5daa84cf26e837f311541ac8526bd987d7c7d917eb60eca21
7
- data.tar.gz: f751b3887bd380e8f911106bedf6a0c606bcdc813ea4d7b01f2f332311ddd974c6dcfe838c7db5446d1683844ffbf379b2f5c8ef4b94459bedefaafc70be2098
6
+ metadata.gz: b8347b2adfe05a4700ec42e0ed5992a1332355bd20330590d8b3de214d980476a490855ff7e69b5b36c75f3684304c4ee61bdff9ecbcf8001f0b477b8010d064
7
+ data.tar.gz: a41512ffbc52b3665118161251441152389ca9daba1a6f4e010303490938dc33393da62f5e821521b2a9f4b45d85fd219b558fa7d2e185c24f43777d26e36a14
data/CHANGELOG.md CHANGED
@@ -2,6 +2,78 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## v10.0.0
6
+
7
+ Changes since `v9.0.0`.
8
+
9
+ This release unifies context turns under `#talk`, removes the
10
+ deprecated `LLM::Bot` alias, and adds shared option resolution
11
+ through `LLM::Utils`.
12
+
13
+ Class-level agent tunables can now be resolved lazily via Proc,
14
+ `Array[...]` schema/tool param types are supported, and a `key?`
15
+ method has been added on providers.
16
+
17
+ Agent tool confirmation hooks let selected tools be approved or
18
+ cancelled before execution. Keep reading to learn more.
19
+
20
+ ### Breaking
21
+
22
+ * **Unify context turns under `#talk`** <br>
23
+ Remove `LLM::Context#respond` and route responses-mode turns through
24
+ `LLM::Context#talk` with `mode: :responses` instead.
25
+
26
+ * **Remove the `LLM::Bot` alias** <br>
27
+ Remove the backward-compatible `LLM::Bot` alias for `LLM::Context`.
28
+ Use `LLM::Context` directly instead.
29
+
30
+ ### Add
31
+
32
+ * **Add shared option resolution through `LLM::Utils`** <br>
33
+ Add `LLM::Utils.resolve_option` for resolving configured values as
34
+ literals, procs, symbol-named methods, or duplicated hashes, and use
35
+ it in agent and ORM option resolution paths.
36
+
37
+ * **Resolve all class-level agent tunables via Proc** <br>
38
+ Let `model`, `tools`, `skills`, `schema`, `stream`, and `tracer`
39
+ declared with a block be lazily evaluated against the agent instance
40
+ at initialization time, matching how `stream` and `tracer` already
41
+ worked.
42
+
43
+ Add `LLM::Agent#params` for direct access to the underlying context
44
+ parameters.
45
+
46
+ Ported from mruby-llm.
47
+
48
+ * **Support `Array[...]` schema and tool param types** <br>
49
+ Let `LLM::Schema` properties and `LLM::Tool` params accept
50
+ `Array[...]` type declarations, including mixed item unions that are
51
+ serialized as `anyOf` array items.
52
+
53
+ * **Add `LLM::Provider#key?`** <br>
54
+ Add `key?` to providers so callers can check whether a non-blank API
55
+ key has been configured.
56
+
57
+ * **Add agent tool confirmation hooks** <br>
58
+ Add `LLM::Agent.confirm` and `LLM::Agent#on_tool_confirmation` so
59
+ selected tools can be approved or cancelled before execution. Pending
60
+ tool resolution now relies on `LLM::Context#functions` so confirmed
61
+ tools are not executed twice when mixed with unconfirmed tool calls.
62
+
63
+ * **Add `LLM::Function#spawn(:call).wait`** <br>
64
+ Add task-shaped sequential execution support for direct
65
+ `LLM::Function#spawn(:call).wait`.
66
+
67
+ ### Fix
68
+
69
+ * **Reduce private internal methods on `LLM::Stream`** <br>
70
+ Remove `tool_not_found` and `__tools__` from `LLM::Stream`. The
71
+ `__tools__` logic is inlined directly into `__find__` since that
72
+ was its only caller. The `tool_not_found` utility method was unused
73
+ externally and added unnecessary surface to LLM::Stream.
74
+
75
+ Ported from mruby-llm.
76
+
5
77
  ## v9.0.0
6
78
 
7
79
  Changes since `v8.1.0`.
@@ -162,7 +234,7 @@ DSML tool-marker filtering in streamed text.
162
234
  blocks that Bedrock rejects.
163
235
 
164
236
  * **Suppress Bedrock DSML tool markers in streamed text** <br>
165
- Filter `"<|DSML|function_calls"` markers out of streamed Bedrock
237
+ Filter `\"<|DSML|function_calls\"` markers out of streamed Bedrock
166
238
  assistant text so tool-call sentinels do not leak into user-visible
167
239
  output.
168
240
 
@@ -313,7 +385,7 @@ provider usage has been recorded yet.
313
385
  buffer API.
314
386
 
315
387
  * **Support percentage compaction token thresholds** <br>
316
- Let `LLM::Compactor` accept `token_threshold:` values like `"90%"` so
388
+ Let `LLM::Compactor` accept `token_threshold:` values like `\"90%\"` so
317
389
  compaction can trigger at a percentage of the active model context
318
390
  window.
319
391
 
@@ -1096,7 +1168,7 @@ Changes since `v4.9.0`.
1096
1168
 
1097
1169
  - Add HTTP transport for MCP with `LLM::MCP::Transport::HTTP` for remote servers
1098
1170
  - Add JSON Schema union types (`any_of`, `all_of`, `one_of`) with parser integration
1099
- - Add JSON Schema type array union support (e.g., `"type\": [\"object\", \"null\"]`)
1171
+ - Add JSON Schema type array union support (e.g., `\"type\": [\"object\", \"null\"]`)
1100
1172
  - Add JSON Schema type inference from `const`, `enum`, or `default` fields
1101
1173
 
1102
1174
  ### Change
@@ -1197,7 +1269,7 @@ Notable merged work in this range includes:
1197
1269
  - `Add rack + websocket example (#130)`
1198
1270
  - `feat(gemspec): add changelog URI (#136)`
1199
1271
  - `feat(function): alias ThreadGroup#wait as ThreadGroup#value (#62)`
1200
- - README and screencast refresh across `#66`, `#67`, `#68`, `#71`, and
1272
+ - README and screencast refresh across `#66`, `#68`, `#71`, and
1201
1273
  `#72`
1202
1274
  - `chore(bot): update deprecation warning from v5.0 to v6.0`
1203
1275
  - `fix(deepseek): tolerate malformed tool arguments`
data/README.md CHANGED
@@ -1,10 +1,18 @@
1
1
  <p align="center">
2
- <a href="llm.rb"><img src="https://github.com/llmrb/llm.rb/raw/main/llm.png" width="200" height="200" border="0" alt="llm.rb"></a>
2
+ <a href="llm.rb">
3
+ <img src="https://github.com/llmrb/llm.rb/raw/main/llm.png" width="200" height="200" border="0" alt="llm.rb">
4
+ </a>
3
5
  </p>
4
6
  <p align="center">
5
- <a href="https://0x1eef.github.io/x/llm.rb?rebuild=1"><img src="https://img.shields.io/badge/docs-0x1eef.github.io-blue.svg" alt="RubyDoc"></a>
6
- <a href="https://opensource.org/license/0bsd"><img src="https://img.shields.io/badge/License-0BSD-orange.svg?" alt="License"></a>
7
- <a href="https://github.com/llmrb/llm.rb/tags"><img src="https://img.shields.io/badge/version-9.0.0-green.svg?" alt="Version"></a>
7
+ <a href="https://0x1eef.github.io/x/llm.rb?rebuild=1">
8
+ <img src="https://img.shields.io/badge/docs-0x1eef.github.io-blue.svg" alt="RubyDoc">
9
+ </a>
10
+ <a href="https://opensource.org/license/0bsd">
11
+ <img src="https://img.shields.io/badge/License-0BSD-orange.svg?" alt="License">
12
+ </a>
13
+ <a href="https://github.com/llmrb/llm.rb/tags">
14
+ <img src="https://img.shields.io/badge/version-10.0.0-green.svg?" alt="Version">
15
+ </a>
8
16
  </p>
9
17
 
10
18
  ## About
@@ -64,6 +72,36 @@ agent = LLM::Agent.new(llm, stream: $stdout)
64
72
  agent.talk "Hello world"
65
73
  ```
66
74
 
75
+ #### Agents (Advanced)
76
+
77
+ An agent can be configured to require confirmation before a tool is
78
+ executed. When a matching tool is called, llm.rb runs
79
+ `on_tool_confirmation`. That callback must decide whether to cancel the
80
+ tool call or approve it and execute it by calling
81
+ `fn.spawn(strategy).wait`, and it must always return an instance of
82
+ [`LLM::Function::Return`](https://0x1eef.github.io/x/llm.rb/LLM/Function/Return.html):
83
+
84
+ ```ruby
85
+ require "llm"
86
+
87
+ class Agent < LLM::Agent
88
+ tools DeleteFile
89
+ confirm "delete-file"
90
+
91
+ def on_tool_confirmation(fn, strategy)
92
+ path = fn.arguments["path"] || fn.arguments[:path]
93
+ if path.start_with?("/tmp/")
94
+ fn.spawn(strategy).wait
95
+ else
96
+ fn.cancel(reason: "Deletion requires approval")
97
+ end
98
+ end
99
+ end
100
+
101
+ llm = LLM.openai(key: ENV["KEY"])
102
+ Agent.new(llm, stream: $stdout).talk("Delete /tmp/example.txt.")
103
+ ```
104
+
67
105
  #### Tools
68
106
 
69
107
  The
@@ -249,7 +287,10 @@ gem install llm.rb
249
287
 
250
288
  #### REPL
251
289
 
252
- This example uses [`LLM::Context`](https://0x1eef.github.io/x/llm.rb/LLM/Context.html) directly for an interactive REPL. <br> See the [deepdive (web)](https://0x1eef.github.io/x/llm.rb/file.deepdive.html) or [deepdive (markdown)](resources/deepdive.md) for more examples.
290
+ This example uses [`LLM::Context`](https://0x1eef.github.io/x/llm.rb/LLM/Context.html)
291
+ directly for an interactive REPL. <br> See the
292
+ [deepdive (web)](https://0x1eef.github.io/x/llm.rb/file.deepdive.html) or
293
+ [deepdive (markdown)](resources/deepdive.md) for more examples.
253
294
 
254
295
  ```ruby
255
296
  require "llm"
@@ -299,7 +340,9 @@ compactor can also use its own `model:` if you want summarization to run on a
299
340
  different model from the main context. `token_threshold:` accepts either a
300
341
  fixed token count or a percentage string like `"90%"`, which resolves
301
342
  against the active model context window and triggers compaction once total
302
- token usage goes over that percentage. See the [deepdive (web)](https://0x1eef.github.io/x/llm.rb/file.deepdive.html) or [deepdive (markdown)](resources/deepdive.md) for more examples.
343
+ token usage goes over that percentage. See the
344
+ [deepdive (web)](https://0x1eef.github.io/x/llm.rb/file.deepdive.html) or
345
+ [deepdive (markdown)](resources/deepdive.md) for more examples.
303
346
 
304
347
  ```ruby
305
348
  require "llm"
@@ -328,7 +371,15 @@ ctx = LLM::Context.new(
328
371
 
329
372
  #### Reasoning
330
373
 
331
- This example uses [`LLM::Stream`](https://0x1eef.github.io/x/llm.rb/LLM/Stream.html) with the OpenAI Responses API so reasoning output is streamed separately from visible assistant output. See the [deepdive (web)](https://0x1eef.github.io/x/llm.rb/file.deepdive.html) or [deepdive (markdown)](resources/deepdive.md) for more examples.
374
+ This example uses [`LLM::Stream`](https://0x1eef.github.io/x/llm.rb/LLM/Stream.html)
375
+ with the OpenAI Responses API so reasoning output is streamed separately from
376
+ visible assistant output. See the
377
+ [deepdive (web)](https://0x1eef.github.io/x/llm.rb/file.deepdive.html) or
378
+ [deepdive (markdown)](resources/deepdive.md) for more examples.
379
+
380
+ To use the Responses API (OpenAI-specific), initialize a
381
+ context or agent with `mode: :responses` and keep using
382
+ `talk` for turns.
332
383
 
333
384
  ```ruby
334
385
  require "llm"
@@ -356,7 +407,10 @@ ctx.talk("Solve 17 * 19 and show your work.")
356
407
 
357
408
  #### Request Cancellation
358
409
 
359
- Need to cancel a stream? llm.rb has you covered through [`LLM::Context#interrupt!`](https://0x1eef.github.io/x/llm.rb/LLM/Context.html#interrupt-21-instance_method). <br> See the [deepdive (web)](https://0x1eef.github.io/x/llm.rb/file.deepdive.html) or [deepdive (markdown)](resources/deepdive.md) for more examples.
410
+ Need to cancel a stream? llm.rb has you covered through
411
+ [`LLM::Context#interrupt!`](https://0x1eef.github.io/x/llm.rb/LLM/Context.html#interrupt-21-instance_method).
412
+ <br> See the [deepdive (web)](https://0x1eef.github.io/x/llm.rb/file.deepdive.html)
413
+ or [deepdive (markdown)](resources/deepdive.md) for more examples.
360
414
 
361
415
  ```ruby
362
416
  require "llm"
@@ -377,7 +431,14 @@ worker.join
377
431
 
378
432
  #### Sequel (ORM)
379
433
 
380
- The `plugin :llm` integration wraps [`LLM::Context`](https://0x1eef.github.io/x/llm.rb/LLM/Context.html) on a `Sequel::Model` and keeps tool execution explicit. Like the ActiveRecord wrappers, its built-in persistence contract is the serialized `data` column, while `provider:` resolves a real `LLM::Provider` instance and `context:` injects defaults such as `model:`. <br> See the [deepdive (web)](https://0x1eef.github.io/x/llm.rb/file.deepdive.html) or [deepdive (markdown)](resources/deepdive.md) for more examples.
434
+ The `plugin :llm` integration wraps
435
+ [`LLM::Context`](https://0x1eef.github.io/x/llm.rb/LLM/Context.html) on a
436
+ `Sequel::Model` and keeps tool execution explicit. Like the ActiveRecord
437
+ wrappers, its built-in persistence contract is the serialized `data` column,
438
+ while `provider:` resolves a real `LLM::Provider` instance and `context:`
439
+ injects defaults such as `model:`. <br> See the
440
+ [deepdive (web)](https://0x1eef.github.io/x/llm.rb/file.deepdive.html) or
441
+ [deepdive (markdown)](resources/deepdive.md) for more examples.
381
442
 
382
443
  ```ruby
383
444
  require "llm"
@@ -412,7 +473,8 @@ one serialized `data` column. If your app has provider, model, or usage
412
473
  columns, provide them to llm.rb through `provider:` and `context:` instead of
413
474
  relying on reserved wrapper columns.
414
475
 
415
- See the [deepdive (web)](https://0x1eef.github.io/x/llm.rb/file.deepdive.html) or [deepdive (markdown)](resources/deepdive.md) for more examples.
476
+ See the [deepdive (web)](https://0x1eef.github.io/x/llm.rb/file.deepdive.html)
477
+ or [deepdive (markdown)](resources/deepdive.md) for more examples.
416
478
 
417
479
  ```ruby
418
480
  require "llm"
@@ -468,7 +530,8 @@ manages tool execution for you. Like `acts_as_llm`, its built-in persistence
468
530
  contract is one serialized `data` column. If your app has provider or model
469
531
  columns, provide them to llm.rb through your hooks and agent DSL.
470
532
 
471
- See the [deepdive (web)](https://0x1eef.github.io/x/llm.rb/file.deepdive.html) or [deepdive (markdown)](resources/deepdive.md) for more examples.
533
+ See the [deepdive (web)](https://0x1eef.github.io/x/llm.rb/file.deepdive.html)
534
+ or [deepdive (markdown)](resources/deepdive.md) for more examples.
472
535
 
473
536
  ```ruby
474
537
  require "llm"
@@ -521,7 +584,12 @@ end
521
584
 
522
585
  #### MCP
523
586
 
524
- This example uses [`LLM::MCP`](https://0x1eef.github.io/x/llm.rb/LLM/MCP.html) over HTTP so remote GitHub MCP tools run through the same `LLM::Context` tool path as local tools. It expects a GitHub token in `ENV["GITHUB_PAT"]`. See the [deepdive (web)](https://0x1eef.github.io/x/llm.rb/file.deepdive.html) or [deepdive (markdown)](resources/deepdive.md) for more examples.
587
+ This example uses [`LLM::MCP`](https://0x1eef.github.io/x/llm.rb/LLM/MCP.html)
588
+ over HTTP so remote GitHub MCP tools run through the same
589
+ `LLM::Context` tool path as local tools. It expects a GitHub token in
590
+ `ENV["GITHUB_PAT"]`. See the
591
+ [deepdive (web)](https://0x1eef.github.io/x/llm.rb/file.deepdive.html) or
592
+ [deepdive (markdown)](resources/deepdive.md) for more examples.
525
593
 
526
594
  ```ruby
527
595
  require "llm"