llm.rb 11.3.0 → 11.3.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 +28 -0
- data/README.md +77 -96
- data/lib/llm/a2a.rb +2 -2
- data/lib/llm/active_record/acts_as_agent.rb +7 -0
- data/lib/llm/active_record/acts_as_llm.rb +7 -0
- data/lib/llm/agent.rb +1 -1
- data/lib/llm/cost.rb +1 -1
- data/lib/llm/function/fiber_group.rb +2 -2
- data/lib/llm/function/task_group.rb +2 -2
- data/lib/llm/function/thread_group.rb +3 -3
- data/lib/llm/pipe.rb +1 -1
- data/lib/llm/providers/anthropic/request_adapter.rb +1 -1
- data/lib/llm/providers/bedrock/request_adapter/completion.rb +5 -5
- data/lib/llm/providers/bedrock/request_adapter.rb +3 -3
- data/lib/llm/providers/bedrock/response_adapter/completion.rb +2 -2
- data/lib/llm/providers/bedrock/response_adapter.rb +2 -2
- data/lib/llm/providers/deepseek/request_adapter.rb +1 -1
- data/lib/llm/providers/google/request_adapter.rb +1 -1
- data/lib/llm/providers/ollama/request_adapter.rb +1 -1
- data/lib/llm/providers/openai/request_adapter.rb +1 -1
- data/lib/llm/registry.rb +2 -2
- data/lib/llm/response.rb +1 -1
- data/lib/llm/schema/object.rb +1 -1
- data/lib/llm/stream.rb +1 -1
- data/lib/llm/tool.rb +2 -2
- data/lib/llm/transport/http.rb +2 -2
- data/lib/llm/transport/persistent_http.rb +1 -1
- data/lib/llm/transport/response/http.rb +1 -1
- data/lib/llm/utils.rb +1 -1
- data/lib/llm/version.rb +1 -1
- data/lib/llm.rb +11 -8
- data/llm.gemspec +10 -9
- data/resources/deepdive.md +432 -0
- metadata +14 -14
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e3aad41c57654e1abef4c184da8904ae5840708e8f5ce937e1030279a5e5a240
|
|
4
|
+
data.tar.gz: 9b03ac6a4f04190a57f2e4698eab923087febbab6a1ff8ad67f391fce4bd5c1e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f429a75d5b612eb27968b6e6451bd231f2191b5b955262503c68baa7ca27f289c01ad564558e82d7fcc732393f635c32caf883a36815b41d86f6c1289cfffb61
|
|
7
|
+
data.tar.gz: 90924b997b5d47d74c3df6e303c890fb5d1a771bae10e63c349436bcbd061241604184f62df87cb2dee7d85405fbd53f08d279ef6b4d3a85a12b0d59b7377229
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,34 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## v11.3.1
|
|
6
|
+
|
|
7
|
+
Changes since `v11.3.0`.
|
|
8
|
+
|
|
9
|
+
This release rebrands the project under the r.uby.dev umbrella, removes
|
|
10
|
+
the Jekyll-based docs site in favor of a pure-markdown deepdive, and
|
|
11
|
+
cleans up YARD documentation across the codebase.
|
|
12
|
+
|
|
13
|
+
### Change
|
|
14
|
+
|
|
15
|
+
* **Rebrand to r.uby.dev** <br>
|
|
16
|
+
Update README.md with the new logo, streamlined copy, and r.uby.dev
|
|
17
|
+
URLs. Rewrite `resources/deepdive.md` as a concise walkthrough and
|
|
18
|
+
bundle it with the gem. Remove the `docs/` directory (Jekyll site).
|
|
19
|
+
Update all references from `llmrb.github.io` to `r.uby.dev`.
|
|
20
|
+
|
|
21
|
+
* **Update gemspec** <br>
|
|
22
|
+
Update homepage, metadata URLs, email, and author list. Switch the
|
|
23
|
+
YARD markdown processor from kramdown to redcarpet.
|
|
24
|
+
|
|
25
|
+
### Fix
|
|
26
|
+
|
|
27
|
+
* **Fix YARD documentation** <br>
|
|
28
|
+
Fix unnamed, misnamed, and missing `@param` tags across provider
|
|
29
|
+
adapters, transport classes, stream, tool, schema, registry, agent,
|
|
30
|
+
and ActiveRecord integration files. Fix backtick-wrapped constant
|
|
31
|
+
references and other YARD formatting issues.
|
|
32
|
+
|
|
5
33
|
## v11.3.0
|
|
6
34
|
|
|
7
35
|
Changes since `v11.2.0`.
|
data/README.md
CHANGED
|
@@ -1,44 +1,33 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<a href="https://
|
|
3
|
-
<img
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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-11.3.0-green.svg?" alt="Version">
|
|
2
|
+
<a href="https://r.uby.dev">
|
|
3
|
+
<img
|
|
4
|
+
src="https://github.com/r-uby-dev/llm.rb/raw/main/rubydev.svg"
|
|
5
|
+
width="400"
|
|
6
|
+
height="200"
|
|
7
|
+
border="0"
|
|
8
|
+
alt="a r.uby.dev project"
|
|
9
|
+
>
|
|
15
10
|
</a>
|
|
16
11
|
</p>
|
|
17
12
|
|
|
18
|
-
|
|
13
|
+
> A [r.uby.dev](https://r.uby.dev) project.
|
|
19
14
|
|
|
20
|
-
|
|
15
|
+
Ruby's capable AI runtime.
|
|
21
16
|
|
|
22
|
-
It
|
|
23
|
-
|
|
24
|
-
tools, skills, MCP, A2A (Agent2Agent), RAG (vector stores & embeddings),
|
|
25
|
-
streaming, files, and persisted state.
|
|
17
|
+
It provides one Ruby interface for building with large language models:
|
|
18
|
+
providers, agents, tools, skills, MCP, A2A, RAG, streaming, files, and persisted conversation state all share the same runtime.
|
|
26
19
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
and fork (via xchan.rb gem).
|
|
20
|
+
The gem runs on Ruby's standard library by default and loads optional
|
|
21
|
+
integrations only when needed. It supports OpenAI, OpenAI-compatible
|
|
22
|
+
endpoints, Anthropic, Google Gemini, DeepSeek, xAI, Z.ai, AWS Bedrock,
|
|
23
|
+
Ollama, and llama.cpp, with built-in ActiveRecord and Sequel support.
|
|
32
24
|
|
|
33
25
|
## Services
|
|
34
26
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
services. The following applications are publicly
|
|
40
|
-
accessible over SSH and are free to try. No account
|
|
41
|
-
required. Nothing to install.
|
|
27
|
+
llm.rb is a [r.uby.dev](https://r.uby.dev) project
|
|
28
|
+
that is part of a growing family of AI-related
|
|
29
|
+
projects that also includes publically accessible
|
|
30
|
+
SSH services.
|
|
42
31
|
|
|
43
32
|
#### matz - the mruby expert
|
|
44
33
|
|
|
@@ -57,13 +46,13 @@ See [https://4.4bsd.dev/robert](https://4.4bsd.dev/robert) for more information.
|
|
|
57
46
|
#### LLM::Context
|
|
58
47
|
|
|
59
48
|
The
|
|
60
|
-
[LLM::Context](https://
|
|
49
|
+
[LLM::Context](https://r.uby.dev/api-docs/llm.rb/LLM/Context.html)
|
|
61
50
|
object is at the heart of the runtime. Almost all other features build
|
|
62
51
|
on top of it. It is a low-level interface to a model, and requires tool
|
|
63
52
|
execution to be managed manually. The
|
|
64
|
-
[LLM::Agent](https://
|
|
53
|
+
[LLM::Agent](https://r.uby.dev/api-docs/llm.rb/LLM/Agent.html)
|
|
65
54
|
class is almost the same as
|
|
66
|
-
[LLM::Context](https://
|
|
55
|
+
[LLM::Context](https://r.uby.dev/api-docs/llm.rb/LLM/Context.html)
|
|
67
56
|
but it manages tool execution for you - we'll cover agents next:
|
|
68
57
|
|
|
69
58
|
```ruby
|
|
@@ -77,9 +66,9 @@ ctx.talk "Hello world"
|
|
|
77
66
|
#### LLM::Agent
|
|
78
67
|
|
|
79
68
|
The
|
|
80
|
-
[LLM::Agent](https://
|
|
69
|
+
[LLM::Agent](https://r.uby.dev/api-docs/llm.rb/LLM/Agent.html)
|
|
81
70
|
object is implemented on top of
|
|
82
|
-
[LLM::Context](https://
|
|
71
|
+
[LLM::Context](https://r.uby.dev/api-docs/llm.rb/LLM/Context.html).
|
|
83
72
|
It provides the same interface, but manages tool execution for you. It
|
|
84
73
|
also has builtin features such as a loop guard that detects repeated
|
|
85
74
|
tool call patterns, and another guard that detects infinite tool call
|
|
@@ -101,7 +90,7 @@ executed. When a matching tool is called, llm.rb runs
|
|
|
101
90
|
`on_tool_confirmation`. That callback must decide whether to cancel the
|
|
102
91
|
tool call or approve it and execute it by calling
|
|
103
92
|
`fn.spawn(strategy).wait`, and it must always return an instance of
|
|
104
|
-
[`LLM::Function::Return`](https://
|
|
93
|
+
[`LLM::Function::Return`](https://r.uby.dev/api-docs/llm.rb/LLM/Function/Return.html):
|
|
105
94
|
|
|
106
95
|
```ruby
|
|
107
96
|
require "llm"
|
|
@@ -127,7 +116,7 @@ Agent.new(llm, stream: $stdout).talk("Delete /tmp/example.txt.")
|
|
|
127
116
|
#### Tools
|
|
128
117
|
|
|
129
118
|
The
|
|
130
|
-
[LLM::Tool](https://
|
|
119
|
+
[LLM::Tool](https://r.uby.dev/api-docs/llm.rb/LLM/Tool.html)
|
|
131
120
|
class can be subclassed to implement your own tools that can extend the
|
|
132
121
|
abilities of a model:
|
|
133
122
|
|
|
@@ -139,7 +128,7 @@ class ReadFile < LLM::Tool
|
|
|
139
128
|
required %i[path]
|
|
140
129
|
|
|
141
130
|
def call(path:)
|
|
142
|
-
{contents: File.read(path)}
|
|
131
|
+
{ contents: File.read(path) }
|
|
143
132
|
end
|
|
144
133
|
end
|
|
145
134
|
```
|
|
@@ -147,15 +136,15 @@ end
|
|
|
147
136
|
#### MCP
|
|
148
137
|
|
|
149
138
|
The
|
|
150
|
-
[LLM::MCP](https://
|
|
139
|
+
[LLM::MCP](https://r.uby.dev/api-docs/llm.rb/LLM/MCP.html)
|
|
151
140
|
object lets llm.rb use tools provided by an MCP server. Those tools are
|
|
152
141
|
exposed through the same runtime as local tools, so you can pass them
|
|
153
142
|
to either
|
|
154
|
-
[LLM::Context](https://
|
|
143
|
+
[LLM::Context](https://r.uby.dev/api-docs/llm.rb/LLM/Context.html)
|
|
155
144
|
or
|
|
156
|
-
[LLM::Agent](https://
|
|
145
|
+
[LLM::Agent](https://r.uby.dev/api-docs/llm.rb/LLM/Agent.html).
|
|
157
146
|
In this example, the MCP server runs over stdio and
|
|
158
|
-
[LLM::Agent](https://
|
|
147
|
+
[LLM::Agent](https://r.uby.dev/api-docs/llm.rb/LLM/Agent.html)
|
|
159
148
|
manages the tool loop. For **stdio**, `mcp.session` is the preferred
|
|
160
149
|
pattern because it keeps one MCP session alive across discovery and
|
|
161
150
|
tool calls:
|
|
@@ -191,7 +180,7 @@ The HTTP transport can be used with or without the `session` method,
|
|
|
191
180
|
and unlike the stdio transport it can remain efficient without the
|
|
192
181
|
`session` method through a persistent connection pool that is available
|
|
193
182
|
through the
|
|
194
|
-
[LLM::Transport.net_http_persistent](https://
|
|
183
|
+
[LLM::Transport.net_http_persistent](https://r.uby.dev/api-docs/llm.rb/LLM/Transport.html#method-c-net_http_persistent)
|
|
195
184
|
transport:
|
|
196
185
|
|
|
197
186
|
```ruby
|
|
@@ -210,13 +199,13 @@ agent.talk("Use the available tools to inspect the environment.")
|
|
|
210
199
|
#### A2A (Agent 2 Agent)
|
|
211
200
|
|
|
212
201
|
The
|
|
213
|
-
[LLM::A2A](https://
|
|
202
|
+
[LLM::A2A](https://r.uby.dev/api-docs/llm.rb/LLM/A2A.html)
|
|
214
203
|
object lets llm.rb use skills provided by a remote A2A agent. Those
|
|
215
204
|
skills are exposed through the same runtime as local tools, so you can
|
|
216
205
|
pass them to either
|
|
217
|
-
[LLM::Context](https://
|
|
206
|
+
[LLM::Context](https://r.uby.dev/api-docs/llm.rb/LLM/Context.html)
|
|
218
207
|
or
|
|
219
|
-
[LLM::Agent](https://
|
|
208
|
+
[LLM::Agent](https://r.uby.dev/api-docs/llm.rb/LLM/Agent.html).
|
|
220
209
|
|
|
221
210
|
Use remote skills as local tools:
|
|
222
211
|
|
|
@@ -225,7 +214,7 @@ require "llm"
|
|
|
225
214
|
|
|
226
215
|
a2a = LLM::A2A.rest(
|
|
227
216
|
url: "https://remote-agent.example.com",
|
|
228
|
-
headers: {"Authorization" => "Bearer token"}
|
|
217
|
+
headers: { "Authorization" => "Bearer token" }
|
|
229
218
|
)
|
|
230
219
|
llm = LLM.openai(key: ENV["KEY"])
|
|
231
220
|
agent = LLM::Agent.new(llm, tools: a2a.skills)
|
|
@@ -245,7 +234,7 @@ a2a = LLM::A2A.rest(
|
|
|
245
234
|
|
|
246
235
|
For more on direct messaging, task operations, push notification
|
|
247
236
|
configs, and JSON-RPC, see the
|
|
248
|
-
[LLM::A2A API docs](https://
|
|
237
|
+
[LLM::A2A API docs](https://r.uby.dev/api-docs/llm.rb/LLM/A2A.html).
|
|
249
238
|
|
|
250
239
|
#### Transports
|
|
251
240
|
|
|
@@ -255,9 +244,9 @@ or provide a transport shortcut when you want a different backend.
|
|
|
255
244
|
`transport: :curb` uses libcurl through the optional `curb` gem.
|
|
256
245
|
|
|
257
246
|
Custom transports can implement the
|
|
258
|
-
[LLM::Transport](https://
|
|
247
|
+
[LLM::Transport](https://r.uby.dev/api-docs/llm.rb/LLM/Transport.html)
|
|
259
248
|
interface and receive transport-agnostic
|
|
260
|
-
[LLM::Transport::Request](https://
|
|
249
|
+
[LLM::Transport::Request](https://r.uby.dev/api-docs/llm.rb/LLM/Transport/Request.html)
|
|
261
250
|
objects from providers.
|
|
262
251
|
|
|
263
252
|
```ruby
|
|
@@ -302,7 +291,7 @@ ReleaseAgent.new(llm, stream: $stdout).talk("Prepare the next release.")
|
|
|
302
291
|
|
|
303
292
|
A skill can also have its sub-agent inherit the parents tools through the
|
|
304
293
|
`inherit` directive. The `inherit` directive has coverage for the "classic"
|
|
305
|
-
tools (a subclass of [LLM::Tool](https://
|
|
294
|
+
tools (a subclass of [LLM::Tool](https://r.uby.dev/api-docs/llm.rb/LLM/Tool.html)),
|
|
306
295
|
MCP tools, and A2A tools that a parent context or agent has access to:
|
|
307
296
|
|
|
308
297
|
```yaml
|
|
@@ -316,7 +305,7 @@ MCP tools, and A2A tools that a parent context or agent has access to:
|
|
|
316
305
|
#### LLM::Stream
|
|
317
306
|
|
|
318
307
|
The
|
|
319
|
-
[LLM::Stream](https://
|
|
308
|
+
[LLM::Stream](https://r.uby.dev/api-docs/llm.rb/LLM/Stream.html)
|
|
320
309
|
object lets you observe output and runtime events as they happen. You
|
|
321
310
|
can subclass it to handle streamed content in your own application:
|
|
322
311
|
|
|
@@ -337,7 +326,7 @@ agent.talk "Write a haiku about Ruby."
|
|
|
337
326
|
#### LLM::Stream (advanced)
|
|
338
327
|
|
|
339
328
|
The
|
|
340
|
-
[LLM::Stream](https://
|
|
329
|
+
[LLM::Stream](https://r.uby.dev/api-docs/llm.rb/LLM/Stream.html)
|
|
341
330
|
object can also resolve tool calls while output is still streaming. In
|
|
342
331
|
`on_tool_call`, you can spawn the tool, push the work onto the stream
|
|
343
332
|
queue, and later drain it with `wait`:
|
|
@@ -367,7 +356,7 @@ ctx.talk(ctx.wait) while ctx.functions?
|
|
|
367
356
|
llm.rb can run tool work concurrently. This is useful when a model calls
|
|
368
357
|
multiple tools and you want to resolve them in parallel instead of one
|
|
369
358
|
at a time. On
|
|
370
|
-
[LLM::Agent](https://
|
|
359
|
+
[LLM::Agent](https://r.uby.dev/api-docs/llm.rb/LLM/Agent.html),
|
|
371
360
|
you can enable this with `concurrency`. Common options are `:call` for
|
|
372
361
|
sequential execution, `:thread`, or `:task` for concurrent IO-bound work, and
|
|
373
362
|
`:ractor` or `:fork` for more isolated CPU-bound work:
|
|
@@ -388,7 +377,7 @@ agent.talk "Read README.md and CHANGELOG.md and compare them."
|
|
|
388
377
|
|
|
389
378
|
#### Serialization
|
|
390
379
|
|
|
391
|
-
The [`LLM::Agent`](https://
|
|
380
|
+
The [`LLM::Agent`](https://r.uby.dev/api-docs/llm.rb/LLM/Agent.html)
|
|
392
381
|
object can be serialized to JSON, which makes it suitable for storing
|
|
393
382
|
in a file, a database column, or a Redis queue. The built-in
|
|
394
383
|
ActiveRecord and Sequel plugins are built on top of the same underlying
|
|
@@ -412,12 +401,12 @@ agent2.talk "What is my favorite language?"
|
|
|
412
401
|
|
|
413
402
|
#### ask
|
|
414
403
|
|
|
415
|
-
[`LLM::Agent`](https://
|
|
404
|
+
[`LLM::Agent`](https://r.uby.dev/api-docs/llm.rb/LLM/Agent.html)
|
|
416
405
|
also provides `ask`, a convenience interface that is compatible with
|
|
417
406
|
RubyLLM's `ask` method. It accepts a prompt, an optional `with:`
|
|
418
407
|
attachment path or paths, an optional `stream:` target, and an optional
|
|
419
408
|
block that chunks are yielded to. It returns an
|
|
420
|
-
[`LLM::Response`](https://
|
|
409
|
+
[`LLM::Response`](https://r.uby.dev/api-docs/llm.rb/LLM/Response.html),
|
|
421
410
|
so use `.content` when you want the text directly:
|
|
422
411
|
|
|
423
412
|
```ruby
|
|
@@ -441,9 +430,9 @@ gem install llm.rb
|
|
|
441
430
|
|
|
442
431
|
#### REPL
|
|
443
432
|
|
|
444
|
-
This example uses [`LLM::Agent`](https://
|
|
433
|
+
This example uses [`LLM::Agent`](https://r.uby.dev/api-docs/llm.rb/LLM/Agent.html)
|
|
445
434
|
for an interactive REPL. <br> See the
|
|
446
|
-
[deepdive (web)](https://
|
|
435
|
+
[deepdive (web)](https://r.uby.dev/llm/) or
|
|
447
436
|
[deepdive (markdown)](resources/deepdive.md) for more examples.
|
|
448
437
|
|
|
449
438
|
```ruby
|
|
@@ -461,18 +450,18 @@ end
|
|
|
461
450
|
|
|
462
451
|
#### Multimodal: Local Files
|
|
463
452
|
|
|
464
|
-
In llm.rb, a prompt can be a string, an [`LLM::Prompt`](https://
|
|
453
|
+
In llm.rb, a prompt can be a string, an [`LLM::Prompt`](https://r.uby.dev/api-docs/llm.rb/LLM/Prompt.html), or an array.
|
|
465
454
|
When you use an array, each element can be plain text or a tagged object such as
|
|
466
|
-
[`agent.image_url(...)`](https://
|
|
467
|
-
[`agent.local_file(...)`](https://
|
|
468
|
-
or [`agent.remote_file(...)`](https://
|
|
455
|
+
[`agent.image_url(...)`](https://r.uby.dev/api-docs/llm.rb/LLM/Agent.html#image_url-instance_method),
|
|
456
|
+
[`agent.local_file(...)`](https://r.uby.dev/api-docs/llm.rb/LLM/Agent.html#local_file-instance_method),
|
|
457
|
+
or [`agent.remote_file(...)`](https://r.uby.dev/api-docs/llm.rb/LLM/Agent.html#remote_file-instance_method).
|
|
469
458
|
Those tagged objects carry the metadata the provider adapter needs to turn one
|
|
470
459
|
Ruby prompt into the provider-specific multimodal request schema.
|
|
471
460
|
|
|
472
461
|
If the model understands that file type, you can attach a local file directly
|
|
473
462
|
with `agent.ask(..., with: path)` instead of uploading it first through a
|
|
474
463
|
provider Files API. Under the hood, llm.rb tags the path as a
|
|
475
|
-
[`agent.local_file(...)`](https://
|
|
464
|
+
[`agent.local_file(...)`](https://r.uby.dev/api-docs/llm.rb/LLM/Agent.html#local_file-instance_method)
|
|
476
465
|
object:
|
|
477
466
|
|
|
478
467
|
```ruby
|
|
@@ -485,9 +474,9 @@ puts agent.ask("Summarize this document.", with: "README.md").content
|
|
|
485
474
|
|
|
486
475
|
#### Context Compaction
|
|
487
476
|
|
|
488
|
-
This example uses [`LLM::Agent`](https://
|
|
489
|
-
[`LLM::Compactor`](https://
|
|
490
|
-
[`LLM::Stream`](https://
|
|
477
|
+
This example uses [`LLM::Agent`](https://r.uby.dev/api-docs/llm.rb/LLM/Agent.html),
|
|
478
|
+
[`LLM::Compactor`](https://r.uby.dev/api-docs/llm.rb/LLM/Compactor.html), and
|
|
479
|
+
[`LLM::Stream`](https://r.uby.dev/api-docs/llm.rb/LLM/Stream.html) together so
|
|
491
480
|
long-lived conversations can summarize older history and expose the lifecycle
|
|
492
481
|
through stream hooks. This approach is inspired by General Intelligence
|
|
493
482
|
Systems. The
|
|
@@ -496,7 +485,7 @@ different model from the main conversation. `token_threshold:` accepts either a
|
|
|
496
485
|
fixed token count or a percentage string like `"90%"`, which resolves
|
|
497
486
|
against the active model context window and triggers compaction once total
|
|
498
487
|
token usage goes over that percentage. See the
|
|
499
|
-
[deepdive (web)](https://
|
|
488
|
+
[deepdive (web)](https://r.uby.dev/llm/) or
|
|
500
489
|
[deepdive (markdown)](resources/deepdive.md) for more examples.
|
|
501
490
|
|
|
502
491
|
```ruby
|
|
@@ -526,10 +515,10 @@ agent = LLM::Agent.new(
|
|
|
526
515
|
|
|
527
516
|
#### Reasoning
|
|
528
517
|
|
|
529
|
-
This example uses [`LLM::Stream`](https://
|
|
518
|
+
This example uses [`LLM::Stream`](https://r.uby.dev/api-docs/llm.rb/LLM/Stream.html)
|
|
530
519
|
with the OpenAI Responses API so reasoning output is streamed separately from
|
|
531
520
|
visible assistant output. See the
|
|
532
|
-
[deepdive (web)](https://
|
|
521
|
+
[deepdive (web)](https://r.uby.dev/llm/) or
|
|
533
522
|
[deepdive (markdown)](resources/deepdive.md) for more examples.
|
|
534
523
|
|
|
535
524
|
To use the Responses API (OpenAI-specific), initialize an agent with
|
|
@@ -553,7 +542,7 @@ agent = LLM::Agent.new(
|
|
|
553
542
|
llm,
|
|
554
543
|
model: "gpt-5.4-mini",
|
|
555
544
|
mode: :responses,
|
|
556
|
-
reasoning: {effort: "medium"},
|
|
545
|
+
reasoning: { effort: "medium" },
|
|
557
546
|
stream: Stream.new
|
|
558
547
|
)
|
|
559
548
|
agent.talk("Solve 17 * 19 and show your work.")
|
|
@@ -562,8 +551,8 @@ agent.talk("Solve 17 * 19 and show your work.")
|
|
|
562
551
|
#### Request Cancellation
|
|
563
552
|
|
|
564
553
|
Need to cancel a stream? llm.rb has you covered through
|
|
565
|
-
[`LLM::Agent#interrupt!`](https://
|
|
566
|
-
<br> See the [deepdive (web)](https://
|
|
554
|
+
[`LLM::Agent#interrupt!`](https://r.uby.dev/api-docs/llm.rb/LLM/Agent.html#interrupt-21-instance_method).
|
|
555
|
+
<br> See the [deepdive (web)](https://r.uby.dev/llm/)
|
|
567
556
|
or [deepdive (markdown)](resources/deepdive.md) for more examples.
|
|
568
557
|
|
|
569
558
|
```ruby
|
|
@@ -586,12 +575,12 @@ worker.join
|
|
|
586
575
|
#### Sequel (ORM)
|
|
587
576
|
|
|
588
577
|
The `plugin :llm` integration wraps
|
|
589
|
-
[`LLM::Context`](https://
|
|
578
|
+
[`LLM::Context`](https://r.uby.dev/api-docs/llm.rb/LLM/Context.html) on a
|
|
590
579
|
`Sequel::Model` and keeps tool execution explicit. Like the ActiveRecord
|
|
591
580
|
wrappers, its built-in persistence contract is the serialized `data` column,
|
|
592
581
|
while `provider:` resolves a real `LLM::Provider` instance and `context:`
|
|
593
582
|
injects defaults such as `model:`. <br> See the
|
|
594
|
-
[deepdive (web)](https://
|
|
583
|
+
[deepdive (web)](https://r.uby.dev/llm/) or
|
|
595
584
|
[deepdive (markdown)](resources/deepdive.md) for more examples.
|
|
596
585
|
|
|
597
586
|
```ruby
|
|
@@ -610,7 +599,7 @@ class Context < Sequel::Model
|
|
|
610
599
|
end
|
|
611
600
|
|
|
612
601
|
def set_context
|
|
613
|
-
{model: "gpt-5.4-mini", mode: :responses, store: false}
|
|
602
|
+
{ model: "gpt-5.4-mini", mode: :responses, store: false }
|
|
614
603
|
end
|
|
615
604
|
end
|
|
616
605
|
|
|
@@ -621,13 +610,13 @@ puts ctx.talk("What is my favorite language?").content
|
|
|
621
610
|
|
|
622
611
|
#### ActiveRecord (ORM): acts_as_llm
|
|
623
612
|
|
|
624
|
-
The `acts_as_llm` method wraps [`LLM::Context`](https://
|
|
613
|
+
The `acts_as_llm` method wraps [`LLM::Context`](https://r.uby.dev/api-docs/llm.rb/LLM/Context.html) and
|
|
625
614
|
provides full control over tool execution. Its built-in persistence contract is
|
|
626
615
|
one serialized `data` column. If your app has provider, model, or usage
|
|
627
616
|
columns, provide them to llm.rb through `provider:` and `context:` instead of
|
|
628
617
|
relying on reserved wrapper columns.
|
|
629
618
|
|
|
630
|
-
See the [deepdive (web)](https://
|
|
619
|
+
See the [deepdive (web)](https://r.uby.dev/llm/)
|
|
631
620
|
or [deepdive (markdown)](resources/deepdive.md) for more examples.
|
|
632
621
|
|
|
633
622
|
```ruby
|
|
@@ -645,7 +634,7 @@ class Context < ApplicationRecord
|
|
|
645
634
|
end
|
|
646
635
|
|
|
647
636
|
def set_context
|
|
648
|
-
{model: "gpt-5.4-mini", mode: :responses, store: false}
|
|
637
|
+
{ model: "gpt-5.4-mini", mode: :responses, store: false }
|
|
649
638
|
end
|
|
650
639
|
end
|
|
651
640
|
|
|
@@ -672,19 +661,19 @@ class Context < ApplicationRecord
|
|
|
672
661
|
end
|
|
673
662
|
|
|
674
663
|
def set_context
|
|
675
|
-
{model: model_name, mode: :responses, store: false}
|
|
664
|
+
{ model: model_name, mode: :responses, store: false }
|
|
676
665
|
end
|
|
677
666
|
end
|
|
678
667
|
```
|
|
679
668
|
|
|
680
669
|
#### ActiveRecord (ORM): acts_as_agent
|
|
681
670
|
|
|
682
|
-
The `acts_as_agent` method wraps [`LLM::Agent`](https://
|
|
671
|
+
The `acts_as_agent` method wraps [`LLM::Agent`](https://r.uby.dev/api-docs/llm.rb/LLM/Agent.html) and
|
|
683
672
|
manages tool execution for you. Like `acts_as_llm`, its built-in persistence
|
|
684
673
|
contract is one serialized `data` column. If your app has provider or model
|
|
685
674
|
columns, provide them to llm.rb through your hooks and agent DSL.
|
|
686
675
|
|
|
687
|
-
See the [deepdive (web)](https://
|
|
676
|
+
See the [deepdive (web)](https://r.uby.dev/llm/)
|
|
688
677
|
or [deepdive (markdown)](resources/deepdive.md) for more examples.
|
|
689
678
|
|
|
690
679
|
```ruby
|
|
@@ -706,7 +695,7 @@ class Ticket < ApplicationRecord
|
|
|
706
695
|
end
|
|
707
696
|
|
|
708
697
|
def set_context
|
|
709
|
-
{mode: :responses, store: false}
|
|
698
|
+
{ mode: :responses, store: false }
|
|
710
699
|
end
|
|
711
700
|
end
|
|
712
701
|
|
|
@@ -731,18 +720,18 @@ class Ticket < ApplicationRecord
|
|
|
731
720
|
end
|
|
732
721
|
|
|
733
722
|
def set_context
|
|
734
|
-
{mode: :responses, store: false}
|
|
723
|
+
{ mode: :responses, store: false }
|
|
735
724
|
end
|
|
736
725
|
end
|
|
737
726
|
```
|
|
738
727
|
|
|
739
728
|
#### MCP
|
|
740
729
|
|
|
741
|
-
This example uses [`LLM::MCP`](https://
|
|
730
|
+
This example uses [`LLM::MCP`](https://r.uby.dev/api-docs/llm.rb/LLM/MCP.html)
|
|
742
731
|
over HTTP so remote GitHub MCP tools run through the same
|
|
743
732
|
`LLM::Agent` tool path as local tools. It expects a GitHub token in
|
|
744
733
|
`ENV["GITHUB_PAT"]`. See the
|
|
745
|
-
[deepdive (web)](https://
|
|
734
|
+
[deepdive (web)](https://r.uby.dev/llm/) or
|
|
746
735
|
[deepdive (markdown)](resources/deepdive.md) for more examples.
|
|
747
736
|
|
|
748
737
|
```ruby
|
|
@@ -752,7 +741,7 @@ require "net/http/persistent"
|
|
|
752
741
|
llm = LLM.openai(key: ENV["KEY"], persistent: true)
|
|
753
742
|
mcp = LLM::MCP.http(
|
|
754
743
|
url: "https://api.githubcopilot.com/mcp/",
|
|
755
|
-
headers: {"Authorization" => "Bearer
|
|
744
|
+
headers: { "Authorization" => "Bearer " + ENV["GITHUB_PAT"].to_s },
|
|
756
745
|
persistent: true
|
|
757
746
|
)
|
|
758
747
|
|
|
@@ -760,14 +749,6 @@ agent = LLM::Agent.new(llm, stream: $stdout, tools: mcp.tools)
|
|
|
760
749
|
agent.talk("Pull information about my GitHub account.")
|
|
761
750
|
```
|
|
762
751
|
|
|
763
|
-
## Resources
|
|
764
|
-
|
|
765
|
-
- [deepdive (web)](https://llmrb.github.io/llm.rb/) and
|
|
766
|
-
[deepdive (markdown)](resources/deepdive.md) are the examples guide.
|
|
767
|
-
- [relay](https://github.com/llmrb/relay) shows a real application built on
|
|
768
|
-
top of llm.rb.
|
|
769
|
-
- [doc site](https://0x1eef.github.io/x/llm.rb?rebuild=1) has the API docs.
|
|
770
|
-
|
|
771
752
|
## License
|
|
772
753
|
|
|
773
754
|
[BSD Zero Clause](https://choosealicense.com/licenses/0bsd/)
|
data/lib/llm/a2a.rb
CHANGED
|
@@ -177,7 +177,7 @@ class LLM::A2A
|
|
|
177
177
|
##
|
|
178
178
|
# Sends a message to the agent and returns the response.
|
|
179
179
|
# @param [String] text The message text to send
|
|
180
|
-
# @param [Hash]
|
|
180
|
+
# @param [Hash] configuration
|
|
181
181
|
# Optional configuration (accepted_output_modes, return_immediately)
|
|
182
182
|
# @param [Hash, nil] metadata
|
|
183
183
|
# Optional metadata to attach to the request
|
|
@@ -198,7 +198,7 @@ class LLM::A2A
|
|
|
198
198
|
# The block is called for each {LLM::Object} event in the stream
|
|
199
199
|
# (Task, Message, TaskStatusUpdateEvent, TaskArtifactUpdateEvent).
|
|
200
200
|
# @param [String] text The message text to send
|
|
201
|
-
# @param [Hash]
|
|
201
|
+
# @param [Hash] configuration Optional configuration
|
|
202
202
|
# @yieldparam [LLM::Object] event A stream event
|
|
203
203
|
# @return [void]
|
|
204
204
|
def send_streaming_message(text, configuration = {}, &on_event)
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
# @!parse
|
|
4
|
+
# module ::ActiveRecord
|
|
5
|
+
# class Base
|
|
6
|
+
# end
|
|
7
|
+
# end
|
|
8
|
+
|
|
3
9
|
module LLM::ActiveRecord
|
|
4
10
|
##
|
|
5
11
|
# ActiveRecord integration for persisting {LLM::Agent LLM::Agent} state.
|
|
@@ -134,4 +140,5 @@ module LLM::ActiveRecord
|
|
|
134
140
|
end
|
|
135
141
|
end
|
|
136
142
|
|
|
143
|
+
# @!parse ::ActiveRecord::Base.extend(LLM::ActiveRecord::ActsAsAgent)
|
|
137
144
|
::ActiveRecord::Base.extend(LLM::ActiveRecord::ActsAsAgent)
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
# @!parse
|
|
4
|
+
# module ::ActiveRecord
|
|
5
|
+
# class Base
|
|
6
|
+
# end
|
|
7
|
+
# end
|
|
8
|
+
|
|
3
9
|
module LLM::ActiveRecord
|
|
4
10
|
##
|
|
5
11
|
# ActiveRecord integration for persisting {LLM::Context LLM::Context} state.
|
|
@@ -222,4 +228,5 @@ module LLM::ActiveRecord
|
|
|
222
228
|
end
|
|
223
229
|
end
|
|
224
230
|
|
|
231
|
+
# @!parse ::ActiveRecord::Base.extend(LLM::ActiveRecord::ActsAsLLM)
|
|
225
232
|
::ActiveRecord::Base.extend(LLM::ActiveRecord::ActsAsLLM)
|
data/lib/llm/agent.rb
CHANGED
data/lib/llm/cost.rb
CHANGED
|
@@ -37,7 +37,7 @@ class LLM::Cost < Struct.new(
|
|
|
37
37
|
)
|
|
38
38
|
##
|
|
39
39
|
# Build a cost breakdown from token usage and model pricing.
|
|
40
|
-
# @param [LLM::Context]
|
|
40
|
+
# @param [LLM::Context] ctx
|
|
41
41
|
# Context used to resolve provider, model, and token usage
|
|
42
42
|
# @return [LLM::Cost]
|
|
43
43
|
def self.from(ctx)
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
class LLM::Function
|
|
4
4
|
##
|
|
5
5
|
# The {LLM::Function::FiberGroup} class wraps an array of
|
|
6
|
-
#
|
|
6
|
+
# `Fiber` objects that are running {LLM::Function} calls
|
|
7
7
|
# concurrently using scheduler-backed fibers.
|
|
8
8
|
#
|
|
9
9
|
# This class provides the same interface as {LLM::Function::ThreadGroup}
|
|
@@ -26,7 +26,7 @@ class LLM::Function
|
|
|
26
26
|
# of fiber objects.
|
|
27
27
|
#
|
|
28
28
|
# @param [Array<Fiber>] fibers
|
|
29
|
-
# An array of fibers, each running an
|
|
29
|
+
# An array of fibers, each running an `LLM::Function#spawn_fiber` call.
|
|
30
30
|
#
|
|
31
31
|
# @return [LLM::Function::FiberGroup]
|
|
32
32
|
# Returns a new fiber group.
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
class LLM::Function
|
|
4
4
|
##
|
|
5
5
|
# The {LLM::Function::TaskGroup} class wraps an array of
|
|
6
|
-
#
|
|
6
|
+
# `Async::Task` objects that are running {LLM::Function} calls
|
|
7
7
|
# concurrently using the async gem.
|
|
8
8
|
#
|
|
9
9
|
# This class provides the same interface as {LLM::Function::ThreadGroup}
|
|
@@ -27,7 +27,7 @@ class LLM::Function
|
|
|
27
27
|
# of async task objects.
|
|
28
28
|
#
|
|
29
29
|
# @param [Array<Async::Task>] tasks
|
|
30
|
-
# An array of async tasks, each running an
|
|
30
|
+
# An array of async tasks, each running an `LLM::Function#spawn_async` call.
|
|
31
31
|
#
|
|
32
32
|
# @return [LLM::Function::TaskGroup]
|
|
33
33
|
# Returns a new task group.
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
class LLM::Function
|
|
4
4
|
##
|
|
5
5
|
# The {LLM::Function::ThreadGroup} class wraps an array of
|
|
6
|
-
#
|
|
6
|
+
# `Thread` objects that are running {LLM::Function} calls
|
|
7
7
|
# concurrently. It provides a single {#wait} method that
|
|
8
8
|
# collects the {LLM::Function::Return} values from those
|
|
9
9
|
# threads.
|
|
@@ -27,11 +27,11 @@ class LLM::Function
|
|
|
27
27
|
class ThreadGroup
|
|
28
28
|
##
|
|
29
29
|
# Creates a new {LLM::Function::ThreadGroup} from an array
|
|
30
|
-
# of
|
|
30
|
+
# of `Thread` objects.
|
|
31
31
|
#
|
|
32
32
|
# @param [Array<Thread>] threads
|
|
33
33
|
# An array of threads, each running an {LLM::Function#spawn}
|
|
34
|
-
# call. The thread's
|
|
34
|
+
# call. The thread's `Thread#value` will be an
|
|
35
35
|
# {LLM::Function::Return}.
|
|
36
36
|
#
|
|
37
37
|
# @return [LLM::Function::ThreadGroup]
|