llm.rb 7.0.0 → 8.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +151 -1
  3. data/README.md +45 -25
  4. data/data/bedrock.json +2948 -0
  5. data/data/deepseek.json +8 -8
  6. data/data/openai.json +39 -2
  7. data/data/xai.json +35 -0
  8. data/data/zai.json +1 -1
  9. data/lib/llm/active_record/acts_as_agent.rb +2 -6
  10. data/lib/llm/active_record/acts_as_llm.rb +4 -82
  11. data/lib/llm/active_record.rb +80 -2
  12. data/lib/llm/agent.rb +9 -4
  13. data/lib/llm/error.rb +4 -0
  14. data/lib/llm/function/array.rb +7 -3
  15. data/lib/llm/function/fiber_group.rb +9 -3
  16. data/lib/llm/function/fork/job.rb +67 -0
  17. data/lib/llm/function/fork/task.rb +76 -0
  18. data/lib/llm/function/fork.rb +8 -0
  19. data/lib/llm/function/fork_group.rb +36 -0
  20. data/lib/llm/function/ractor/task.rb +13 -3
  21. data/lib/llm/function/task.rb +10 -2
  22. data/lib/llm/function.rb +24 -11
  23. data/lib/llm/mcp/command.rb +1 -1
  24. data/lib/llm/mcp/transport/http.rb +2 -2
  25. data/lib/llm/mcp.rb +7 -4
  26. data/lib/llm/object/kernel.rb +8 -2
  27. data/lib/llm/object.rb +75 -21
  28. data/lib/llm/{mcp/pipe.rb → pipe.rb} +9 -8
  29. data/lib/llm/provider/transport/http/execution.rb +1 -1
  30. data/lib/llm/provider/transport/http.rb +1 -1
  31. data/lib/llm/provider.rb +7 -0
  32. data/lib/llm/providers/bedrock/error_handler.rb +80 -0
  33. data/lib/llm/providers/bedrock/models.rb +109 -0
  34. data/lib/llm/providers/bedrock/request_adapter/completion.rb +153 -0
  35. data/lib/llm/providers/bedrock/request_adapter.rb +95 -0
  36. data/lib/llm/providers/bedrock/response_adapter/completion.rb +143 -0
  37. data/lib/llm/providers/bedrock/response_adapter/models.rb +34 -0
  38. data/lib/llm/providers/bedrock/response_adapter.rb +40 -0
  39. data/lib/llm/providers/bedrock/signature.rb +166 -0
  40. data/lib/llm/providers/bedrock/stream_decoder.rb +140 -0
  41. data/lib/llm/providers/bedrock/stream_parser.rb +201 -0
  42. data/lib/llm/providers/bedrock.rb +272 -0
  43. data/lib/llm/stream/queue.rb +1 -1
  44. data/lib/llm/version.rb +1 -1
  45. data/lib/llm.rb +27 -1
  46. data/llm.gemspec +2 -1
  47. metadata +33 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6c923952039095a2234eb1bd5c058a951b0d797d27577cdf7f679df59b49060b
4
- data.tar.gz: 3667e0d79e44634f769dfced198dd07c1039f173cb43b72aab7d3204aa3638f8
3
+ metadata.gz: 8aa3ee461642fb157bece63a4ebe00ceda8ec66ce24df5c842efdcc176861a53
4
+ data.tar.gz: 2d26e36b812704a80e5c8ba4814cfbec770afd5694be71b69d7937422f9a642c
5
5
  SHA512:
6
- metadata.gz: 655d450b2ffeb71ed9564b7c5c23a2a86e9e385de9dc1abdac18588e460cffdecd1b2da1d5ef9fc162dc3f3286b7d2c979baec3953cd1ddbdab74d1ef5b87112
7
- data.tar.gz: a044fedb675c4d92eff55c210d588b68b80c7e3967188674c2de4d8f6bc69d76e8f15c18f49fb54e09a8c93dff89074304d231609337bfa3bc79c96e1f3f576b
6
+ metadata.gz: 3a30bf9d5309bf49c660137ed5e81b74f9b028f8846077f3db0b7c92745a5d96b16115db765a4bd1970ba0cbaaa7bd805e0a4a37c04c7e63aacdf3d019d268ec
7
+ data.tar.gz: 4e297d159dc459ee9ec228862f271b7a21be48ce06f092773c4b56d9cc007252b1cfeb66a119c7e14f3e683213e5923d34b0c256397b92cfa981cd47fe023008
data/CHANGELOG.md CHANGED
@@ -2,6 +2,150 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## v8.1.0
6
+
7
+ Changes since `v8.0.0`.
8
+
9
+ This release adds Amazon Bedrock provider support through the Converse
10
+ API, including AWS SigV4 request signing, event stream decoding,
11
+ structured output through `schema:`, and a models.dev-backed registry.
12
+ It exposes `llm.models.all` for Bedrock via the ListFoundationModels
13
+ API and adds `LLM::Object#transform_values!` for in-place value
14
+ transformation. Several Bedrock-specific fixes land as well, including
15
+ response id exposure, blank text block suppression in tool turns, and
16
+ DSML tool-marker filtering in streamed text.
17
+
18
+ ### Add
19
+
20
+ * **Add AWS Bedrock provider support** <br>
21
+ Add `LLM.bedrock(...)` with Bedrock Converse chat support, AWS SigV4
22
+ request signing, Bedrock event stream decoding, structured output
23
+ support through `schema:`, and models.dev-backed `bedrock.json`
24
+ registry generation.
25
+
26
+ * **Add AWS Bedrock Models endpoint support** <br>
27
+ Add `llm.models.all` for Bedrock via the ListFoundationModels API,
28
+ including SigV4 signing for the control-plane endpoint and normalized
29
+ `LLM::Model` collection responses.
30
+
31
+ * **Add `LLM::Object#transform_values!`** <br>
32
+ Let `LLM::Object` transform stored values in place through
33
+ `#transform_values!`.
34
+
35
+ ### Fix
36
+
37
+ * **Expose response ids on Bedrock completion responses** <br>
38
+ Read the Bedrock request id into `LLM::Response#id` for completion
39
+ responses adapted from the Converse API.
40
+
41
+ * **Avoid blank assistant text blocks in Bedrock tool turns** <br>
42
+ Stop replaying assistant tool-call messages with empty text content
43
+ blocks that Bedrock rejects.
44
+
45
+ * **Suppress Bedrock DSML tool markers in streamed text** <br>
46
+ Filter `"<|DSML|function_calls"` markers out of streamed Bedrock
47
+ assistant text so tool-call sentinels do not leak into user-visible
48
+ output.
49
+
50
+ ## v8.0.0
51
+
52
+ Changes since `v7.0.0`.
53
+
54
+ This release adds Unix-fork concurrency for process-isolated tool
55
+ execution, extends `LLM::Object` with `#merge` and `#delete`, and drops
56
+ Ruby 3.2 support due to segfaults observed with the `:fork` path. It
57
+ promotes `LLM::Pipe` to the top-level namespace and adds
58
+ `persistent: true` on `LLM::MCP.http` for direct persistent transport
59
+ configuration. `LLM::Function#runner` is exposed as public API, agent
60
+ tracer overrides are supported, fiber execution now uses `Fiber.schedule`,
61
+ missing optional dependencies raise clearer `LLM::LoadError` guidance,
62
+ and ActiveRecord wrapper plumbing is deduplicated between `acts_as_llm`
63
+ and `acts_as_agent`.
64
+
65
+ ### Breaking
66
+
67
+ * **Drop Ruby 3.2 support** <br>
68
+ Stop supporting Ruby 3.2 due to a segfault observed with the `:fork`
69
+ tool concurrency strategy.
70
+
71
+ ### Add
72
+
73
+ * **Add `LLM::Object#merge`** <br>
74
+ Let `LLM::Object` return a new wrapped object when merging hash-like
75
+ data through `#merge`.
76
+
77
+ * **Add `LLM::Object#delete`** <br>
78
+ Let `LLM::Object` delete keys directly through `#delete`.
79
+
80
+ ### Change
81
+
82
+ * **Add fork-based tool concurrency** <br>
83
+ Add `:fork` as a new concurrency strategy for `LLM::Function#spawn`,
84
+ `LLM::Function::Array#wait`, and `LLM::Agent.concurrency` that runs
85
+ class-based tools in isolated child processes. Fork-backed tools support
86
+ tracer callbacks, `on_interrupt`/`on_cancel` hooks, and `alive?` checks.
87
+ Requires the `xchan` gem for inter-process communication with `:fork`.
88
+ This is especially useful for tools that need process isolation, such as
89
+ running shell commands or handling unsafe data.
90
+
91
+ * **Promote `LLM::Pipe` from MCP namespace to top-level** <br>
92
+ Move `LLM::MCP::Pipe` to `LLM::Pipe` so the pipe abstraction is available
93
+ outside MCP internals. The new class adds a `binmode:` option for binary
94
+ pipes. `LLM::MCP::Command` and related MCP transport code have been updated
95
+ to use `LLM::Pipe`.
96
+
97
+ * **Allow `persistent: true` on `LLM::MCP.http`** <br>
98
+ Let `LLM::MCP.http(...)` enable persistent HTTP transport directly
99
+ through `persistent: true`, instead of requiring a separate
100
+ `.persistent` call after construction.
101
+
102
+ * **Expose `LLM::Function#runner` as public API** <br>
103
+ Promote the internal runner instantiation to a public `runner` method on
104
+ `LLM::Function`, so callers can inspect or reuse the resolved tool instance
105
+ that a function wraps.
106
+
107
+ * **Allow agent instance tracer overrides** <br>
108
+ Let `LLM::Agent.new(..., tracer: ...)` override the class-level tracer
109
+ for that agent instance.
110
+
111
+ * **Make `:fiber` use scheduler-backed fibers** <br>
112
+ Change `:fiber` tool execution to use `Fiber.schedule` and require
113
+ `Fiber.scheduler`, instead of wrapping direct calls in raw fibers. This
114
+ gives `:fiber` a real cooperative concurrency model instead of acting as
115
+ a thin wrapper around sequential execution.
116
+
117
+ * **Read stored values from zero-argument `LLM::Object` method calls** <br>
118
+ Let calls like `obj.delete`, `obj.fetch`, `obj.merge`, `obj.key?`,
119
+ `obj.dig`, `obj.slice`, or `obj.keys` return a stored value when that
120
+ method name exists as a key and no arguments are given.
121
+
122
+ * **Harden `LLM::Object` against arbitrary key names** <br>
123
+ Move internal lookup logic off `LLM::Object` instances and onto the
124
+ singleton class instead, making stored keys like `method_missing`
125
+ more resilient while preserving normal dynamic field access.
126
+
127
+ * **Deduplicate ActiveRecord wrapper plumbing** <br>
128
+ Move shared ActiveRecord wrapper defaults and utility methods into
129
+ `LLM::ActiveRecord`, reducing duplication between `acts_as_llm` and
130
+ `acts_as_agent`.
131
+
132
+ * **Raise clearer errors for missing optional runtime dependencies** <br>
133
+ Route optional `async`, `xchan`, and `net/http/persistent` loads
134
+ through `LLM.require` so missing runtime gems raise `LLM::LoadError`
135
+ with installation guidance instead of leaking raw `LoadError`
136
+ exceptions.
137
+
138
+ ### Fix
139
+
140
+ * **Avoid `RuntimeError` from `Async::Task.current` lookups** <br>
141
+ Check `Async::Task.current?` before reading the current Async task so
142
+ provider transports fall back to `Fiber.current` without raising when
143
+ no Async task is active.
144
+
145
+ * **Serialize `LLM::Object` values correctly through `LLM.json`** <br>
146
+ Make `LLM::Object#to_json` call `LLM.json.dump(to_h, ...)` so
147
+ `LLM::Object` values serialize through the llm.rb JSON adapter.
148
+
5
149
  ## v7.0.0
6
150
 
7
151
  Changes since `v6.1.0`.
@@ -121,6 +265,12 @@ and `LLM::RactorError` is raised for unsupported ractor tool work.
121
265
  for unsupported tool types such as skill-backed tools, instead of letting
122
266
  deeper Ruby isolation errors leak out later in execution.
123
267
 
268
+ * **Delegate interrupt to concurrent task implementations** <br>
269
+ Make `LLM::Function::Task#interrupt!` delegate to the underlying fork or
270
+ ractor task when it supports interruption, so `ctx.interrupt!` and
271
+ `task.interrupt!` work correctly for fork- and ractor-backed tool
272
+ execution.
273
+
124
274
  ## v5.4.0
125
275
 
126
276
  Changes since `v5.3.0`.
@@ -828,7 +978,7 @@ Changes since `v4.9.0`.
828
978
 
829
979
  - Add HTTP transport for MCP with `LLM::MCP::Transport::HTTP` for remote servers
830
980
  - Add JSON Schema union types (`any_of`, `all_of`, `one_of`) with parser integration
831
- - Add JSON Schema type array union support (e.g., `"type": ["object", "null"]`)
981
+ - Add JSON Schema type array union support (e.g., `"type\": [\"object\", \"null\"]`)
832
982
  - Add JSON Schema type inference from `const`, `enum`, or `default` fields
833
983
 
834
984
  ### Change
data/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  <p align="center">
5
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
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-7.0.0-green.svg?" alt="Version"></a>
7
+ <a href="https://github.com/llmrb/llm.rb/tags"><img src="https://img.shields.io/badge/version-8.1.0-green.svg?" alt="Version"></a>
8
8
  </p>
9
9
 
10
10
  ## About
@@ -24,8 +24,18 @@ It provides one runtime for providers, agents, tools, skills, MCP servers, strea
24
24
  schemas, files, and persisted state, so real systems can be built out of one coherent
25
25
  execution model instead of a pile of adapters.
26
26
 
27
+ It supports providers including OpenAI, Anthropic, Google Gemini, DeepSeek, xAI,
28
+ Z.ai, and AWS Bedrock.
29
+
30
+ It provides concurrent tool execution with multiple strategies exposed through a single
31
+ runtime: async-task, threads, fibers, ractors and processes (fork). The first three are
32
+ good for IO-bound work and the last two are good for CPU-bound work. Ractor support is
33
+ experimental and comes with limitations.
34
+
27
35
  Want to see some code? Jump to [the examples](#examples) section. <br>
28
- Want to see a self-hosted LLM environment built on llm.rb? Check out [Relay](https://github.com/llmrb/relay).
36
+ Want to see a self-hosted LLM environment built on llm.rb? Check out [relay.app](https://github.com/llmrb/relay.app). <br>
37
+ Want to use llm.rb with mruby ? Check out [mruby-llm](https://github.com/llmrb/mruby-llm)
38
+
29
39
 
30
40
  ## Architecture
31
41
 
@@ -287,8 +297,13 @@ end
287
297
  #### Concurrency
288
298
 
289
299
  Tool execution can run sequentially with `:call` or concurrently through
290
- `:thread`, `:task`, `:fiber`, and experimental `:ractor`, without rewriting
291
- your tool layer.
300
+ `:thread`, `:task`, `:fiber`, `:fork`, and experimental `:ractor`, without
301
+ rewriting your tool layer. Async tasks, threads, and fibers are the
302
+ I/O-bound options. Fork and ractor are the CPU-bound options. `:fork`
303
+ requires [`xchan.rb`](https://github.com/0x1eef/xchan.rb#readme) support,
304
+ and `:ractor` is still experimental.
305
+
306
+ `:fiber` uses `Fiber.schedule`, so it requires `Fiber.scheduler`.
292
307
 
293
308
  ```ruby
294
309
  class Agent < LLM::Agent
@@ -311,8 +326,9 @@ finer sequential control across several steps before shutting the client down.
311
326
  ```ruby
312
327
  mcp = LLM::MCP.http(
313
328
  url: "https://api.githubcopilot.com/mcp/",
314
- headers: {"Authorization" => "Bearer #{ENV["GITHUB_PAT"]}"}
315
- ).persistent
329
+ headers: {"Authorization" => "Bearer #{ENV["GITHUB_PAT"]}"},
330
+ persistent: true
331
+ )
316
332
  mcp.run do
317
333
  ctx = LLM::Context.new(llm, tools: mcp.tools)
318
334
  end
@@ -367,13 +383,13 @@ worker.join
367
383
  Use `LLM::Agent` when you want the same stateful runtime surface as
368
384
  `LLM::Context`, but with tool loops executed automatically according to a
369
385
  configured concurrency mode such as `:call`, `:thread`, `:task`, `:fiber`,
370
- or experimental `:ractor` support for class-based tools. MCP tools are not
371
- supported by the current `:ractor` mode, but mixed tool sets can still
372
- route MCP tools and local tools through different strategies at runtime.
373
- By default, the tool attempt budget is `25`. When an agent exhausts that
374
- budget, it sends advisory tool errors back through the model instead of
375
- raising out of the runtime. Set `tool_attempts: nil` to disable that
376
- advisory behavior.
386
+ `:fork`, or experimental `:ractor` support for class-based tools. MCP tools
387
+ are not supported by the current `:ractor` mode, but mixed tool sets can
388
+ still route MCP tools and local tools through different strategies at
389
+ runtime. By default, the tool attempt budget is `25`. When an agent
390
+ exhausts that budget, it sends advisory tool errors back through the model
391
+ instead of raising out of the runtime. Set `tool_attempts: nil` to disable
392
+ that advisory behavior.
377
393
  - **Tool calls have an explicit lifecycle** <br>
378
394
  A tool call can be executed, cancelled through
379
395
  [`LLM::Function#cancel`](https://0x1eef.github.io/x/llm.rb/LLM/Function.html#cancel-instance_method),
@@ -385,13 +401,15 @@ worker.join
385
401
  [`LLM::Context#cancel!`](https://0x1eef.github.io/x/llm.rb/LLM/Context.html#cancel-21-instance_method)
386
402
  is inspired by Go's context cancellation model.
387
403
  - **Concurrency is a first-class feature** <br>
388
- Use threads, fibers, async tasks, or experimental ractors without
389
- rewriting your tool layer. The current `:ractor` mode is for class-based
390
- tools and does not support MCP tools, but mixed workloads can branch on
391
- `tool.mcp?` and choose a supported strategy per tool. Class-based
392
- `:ractor` tools still emit normal tool tracer callbacks. `:ractor` is
393
- especially useful for CPU-bound tools, while `:task`, `:fiber`, or
394
- `:thread` may be a better fit for I/O-bound work.
404
+ Use async tasks, threads, fibers, forks, or experimental ractors without
405
+ rewriting your tool layer. Async tasks, threads, and fibers are the
406
+ I/O-bound options. Fork and ractor are the CPU-bound options. `:fork`
407
+ requires [`xchan.rb`](https://github.com/0x1eef/xchan.rb#readme) support.
408
+ The current `:ractor` mode is for class-based tools, and MCP tools are
409
+ not supported by ractor, but mixed workloads can branch on `tool.mcp?`
410
+ and choose a supported strategy per tool. Class-based `:ractor` tools
411
+ still emit normal tool tracer callbacks. `:fiber` uses `Fiber.schedule`,
412
+ so it requires `Fiber.scheduler`.
395
413
  - **Advanced workloads are built in, not bolted on** <br>
396
414
  Streaming, concurrent tool execution, persistence, tracing, and MCP support
397
415
  all fit the same runtime model.
@@ -429,7 +447,7 @@ worker.join
429
447
  preserve OpenAI request shapes but change the API root path.
430
448
  - **Provider support is broad** <br>
431
449
  Work with OpenAI, OpenAI-compatible endpoints, Anthropic, Google, DeepSeek,
432
- Z.ai, xAI, llama.cpp, and Ollama through the same runtime.
450
+ Z.ai, xAI, AWS Bedrock, llama.cpp, and Ollama through the same runtime.
433
451
  - **Tools are explicit** <br>
434
452
  Run local tools, provider-native tools, and MCP tools through the same path
435
453
  with fewer special cases.
@@ -865,8 +883,9 @@ require "net/http/persistent"
865
883
  llm = LLM.openai(key: ENV["KEY"])
866
884
  mcp = LLM::MCP.http(
867
885
  url: "https://api.githubcopilot.com/mcp/",
868
- headers: {"Authorization" => "Bearer #{ENV["GITHUB_PAT"]}"}
869
- ).persistent
886
+ headers: {"Authorization" => "Bearer #{ENV["GITHUB_PAT"]}"},
887
+ persistent: true
888
+ )
870
889
 
871
890
  mcp.start
872
891
  ctx = LLM::Context.new(llm, stream: $stdout, tools: mcp.tools)
@@ -880,8 +899,9 @@ For scoped work, `mcp.run do ... end` is shorter and handles cleanup for you:
880
899
  ```ruby
881
900
  mcp = LLM::MCP.http(
882
901
  url: "https://api.githubcopilot.com/mcp/",
883
- headers: {"Authorization" => "Bearer #{ENV["GITHUB_PAT"]}"}
884
- ).persistent
902
+ headers: {"Authorization" => "Bearer #{ENV["GITHUB_PAT"]}"},
903
+ persistent: true
904
+ )
885
905
  mcp.run do
886
906
  ctx = LLM::Context.new(llm, stream: $stdout, tools: mcp.tools)
887
907
  ctx.talk("Pull information about my GitHub account.")