llm.rb 11.3.0 → 12.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 +4 -4
- data/CHANGELOG.md +270 -1
- data/LICENSE +92 -17
- data/README.md +209 -647
- data/data/anthropic.json +433 -249
- data/data/bedrock.json +2097 -1055
- data/data/deepinfra.json +993 -0
- data/data/deepseek.json +53 -28
- data/data/google.json +389 -771
- data/data/openai.json +1053 -771
- data/data/xai.json +133 -292
- data/data/zai.json +249 -141
- data/lib/llm/a2a.rb +2 -2
- data/lib/llm/active_record/acts_as_agent.rb +10 -41
- data/lib/llm/active_record/acts_as_llm.rb +25 -0
- data/lib/llm/active_record.rb +3 -3
- data/lib/llm/agent.rb +1 -1
- data/lib/llm/context.rb +9 -5
- data/lib/llm/contract/completion.rb +2 -2
- 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/provider.rb +2 -2
- 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/deepinfra/audio.rb +66 -0
- data/lib/llm/providers/deepinfra/images.rb +90 -0
- data/lib/llm/providers/deepinfra/response_adapter.rb +36 -0
- data/lib/llm/providers/deepinfra.rb +100 -0
- data/lib/llm/providers/deepseek/images.rb +109 -0
- data/lib/llm/providers/deepseek/request_adapter.rb +32 -0
- data/lib/llm/providers/deepseek/response_adapter/image.rb +9 -0
- data/lib/llm/providers/deepseek/response_adapter.rb +29 -0
- data/lib/llm/providers/deepseek.rb +4 -2
- data/lib/llm/providers/google/request_adapter.rb +23 -6
- data/lib/llm/providers/google.rb +4 -4
- data/lib/llm/providers/ollama/request_adapter.rb +1 -1
- data/lib/llm/providers/openai/audio.rb +6 -2
- data/lib/llm/providers/openai/images.rb +9 -50
- data/lib/llm/providers/openai/request_adapter/respond.rb +38 -4
- data/lib/llm/providers/openai/request_adapter.rb +1 -1
- data/lib/llm/providers/openai/response_adapter/audio.rb +5 -1
- data/lib/llm/providers/openai/response_adapter/completion.rb +1 -1
- data/lib/llm/providers/openai/response_adapter/image.rb +0 -4
- data/lib/llm/providers/openai/responses.rb +1 -0
- data/lib/llm/providers/openai/stream_parser.rb +5 -6
- data/lib/llm/providers/openai.rb +2 -2
- data/lib/llm/providers/xai/images.rb +49 -26
- data/lib/llm/providers/xai.rb +2 -2
- data/lib/llm/registry.rb +2 -2
- data/lib/llm/response.rb +11 -1
- data/lib/llm/schema/leaf.rb +7 -1
- data/lib/llm/schema/object.rb +1 -1
- data/lib/llm/schema/renderer.rb +121 -0
- data/lib/llm/schema.rb +30 -0
- data/lib/llm/sequel/agent.rb +2 -43
- data/lib/llm/sequel/plugin.rb +25 -7
- data/lib/llm/stream.rb +1 -1
- data/lib/llm/tool.rb +2 -2
- data/lib/llm/tracer/telemetry.rb +4 -6
- data/lib/llm/tracer.rb +9 -21
- data/lib/llm/transport/execution.rb +16 -1
- data/lib/llm/transport/http.rb +2 -2
- data/lib/llm/transport/net_http_adapter.rb +1 -1
- data/lib/llm/transport/persistent_http.rb +1 -1
- data/lib/llm/transport/response/http.rb +1 -1
- data/lib/llm/uridata.rb +16 -0
- data/lib/llm/utils.rb +1 -1
- data/lib/llm/version.rb +1 -1
- data/lib/llm.rb +20 -8
- data/llm.gemspec +14 -26
- data/resources/deepdive.md +966 -0
- metadata +26 -29
- data/lib/llm/tracer/langsmith.rb +0 -144
data/README.md
CHANGED
|
@@ -1,135 +1,86 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<a href="https://
|
|
3
|
-
<img
|
|
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
|
+
>
|
|
4
10
|
</a>
|
|
5
11
|
</p>
|
|
6
|
-
<p align="center">
|
|
7
|
-
<a href="https://llmrb.github.io/llm.rb">
|
|
8
|
-
<img src="https://img.shields.io/badge/docs-llmrb.github.io-blue.svg" alt="Official llm.rb website">
|
|
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-11.3.0-green.svg?" alt="Version">
|
|
15
|
-
</a>
|
|
16
|
-
</p>
|
|
17
|
-
|
|
18
|
-
## About
|
|
19
|
-
|
|
20
|
-
llm.rb is Ruby's most capable AI runtime.
|
|
21
|
-
|
|
22
|
-
It runs on Ruby's standard library by default. loads optional pieces
|
|
23
|
-
only when needed, and offers a single runtime for providers, agents,
|
|
24
|
-
tools, skills, MCP, A2A (Agent2Agent), RAG (vector stores & embeddings),
|
|
25
|
-
streaming, files, and persisted state.
|
|
26
12
|
|
|
27
|
-
|
|
28
|
-
Gemini, DeepSeek, xAI, Z.ai, AWS Bedrock, Ollama, and llama.cpp. It
|
|
29
|
-
also includes built-in ActiveRecord and Sequel support, plus concurrent
|
|
30
|
-
tool execution through threads, tasks (via async gem), fibers, ractors,
|
|
31
|
-
and fork (via xchan.rb gem).
|
|
13
|
+
> A [r.uby.dev](https://r.uby.dev) project.
|
|
32
14
|
|
|
33
|
-
|
|
15
|
+
Welcome to the canonical llm.rb repository.
|
|
34
16
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
accessible over SSH and are free to try. No account
|
|
41
|
-
required. Nothing to install.
|
|
17
|
+
llm.rb is not a library, framework or toolkit but an advanced runtime
|
|
18
|
+
for building highly capable AI applications on CRuby. By default
|
|
19
|
+
it has zero runtime dependencies although certain functionality –
|
|
20
|
+
such as ActiveRecord support – require optional dependencies
|
|
21
|
+
that are opt-in.
|
|
42
22
|
|
|
43
|
-
|
|
23
|
+
## Features
|
|
44
24
|
|
|
45
|
-
|
|
25
|
+
The runtime supports OpenAI, OpenAI-compatible endpoints, Anthropic, Google
|
|
26
|
+
Gemini, DeepSeek, DeepInfra, xAI, Z.ai, AWS Bedrock, Ollama, and llama.cpp.
|
|
27
|
+
It has first-class support for streaming, tool calls, MCP
|
|
28
|
+
and A2A, embeddings, vector stores and the RAG pattern.
|
|
46
29
|
|
|
47
|
-
|
|
30
|
+
There are multiple HTTP backends to choose from, tools can be run concurrently
|
|
31
|
+
or in parallel via threads, async tasks, fibers, ractors, and fork, and it is
|
|
32
|
+
also possible to make a tool call while the model is still streaming.
|
|
48
33
|
|
|
49
|
-
|
|
34
|
+
The runtime builds on top of three core concepts: providers, contexts, and agents,
|
|
35
|
+
so once you learn the fundamentals, everything else falls into place naturally. And once
|
|
36
|
+
you learn llm.rb, you will also be able to use <a href="https://r.uby.dev/mruby-llm">mruby-llm</a> and
|
|
37
|
+
<a href="https://r.uby.dev/wasm-llm">wasm-llm</a> because the API is pretty much identical.
|
|
50
38
|
|
|
51
|
-
|
|
39
|
+
## Install
|
|
52
40
|
|
|
53
|
-
|
|
41
|
+
```bash
|
|
42
|
+
gem install llm.rb
|
|
43
|
+
```
|
|
54
44
|
|
|
55
45
|
## Quick start
|
|
56
46
|
|
|
57
|
-
#### LLM::Context
|
|
58
|
-
|
|
59
|
-
The
|
|
60
|
-
[LLM::Context](https://0x1eef.github.io/x/llm.rb/LLM/Context.html)
|
|
61
|
-
object is at the heart of the runtime. Almost all other features build
|
|
62
|
-
on top of it. It is a low-level interface to a model, and requires tool
|
|
63
|
-
execution to be managed manually. The
|
|
64
|
-
[LLM::Agent](https://0x1eef.github.io/x/llm.rb/LLM/Agent.html)
|
|
65
|
-
class is almost the same as
|
|
66
|
-
[LLM::Context](https://0x1eef.github.io/x/llm.rb/LLM/Context.html)
|
|
67
|
-
but it manages tool execution for you - we'll cover agents next:
|
|
68
|
-
|
|
69
|
-
```ruby
|
|
70
|
-
require "llm"
|
|
71
|
-
|
|
72
|
-
llm = LLM.openai(key: ENV["KEY"])
|
|
73
|
-
ctx = LLM::Context.new(llm, stream: $stdout)
|
|
74
|
-
ctx.talk "Hello world"
|
|
75
|
-
```
|
|
76
|
-
|
|
77
47
|
#### LLM::Agent
|
|
78
48
|
|
|
79
|
-
The
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
It provides the same interface, but manages tool execution for you. It
|
|
84
|
-
also has builtin features such as a loop guard that detects repeated
|
|
85
|
-
tool call patterns, and another guard that detects infinite tool call
|
|
86
|
-
loops. Both guards advise the model to change course rather than raise
|
|
87
|
-
an error:
|
|
49
|
+
The [`LLM::Agent`](https://r.uby.dev/api-docs/llm.rb/LLM/Agent.html) class is the default high-level interface,
|
|
50
|
+
and it is recommended for most use-cases. It manages tool execution
|
|
51
|
+
automatically, guards against infinite loops, manages conversation
|
|
52
|
+
state, and much more.
|
|
88
53
|
|
|
89
54
|
```ruby
|
|
90
55
|
require "llm"
|
|
91
56
|
|
|
92
|
-
llm = LLM.
|
|
57
|
+
llm = LLM.deepseek(key: ENV["KEY"])
|
|
93
58
|
agent = LLM::Agent.new(llm, stream: $stdout)
|
|
94
59
|
agent.talk "Hello world"
|
|
95
60
|
```
|
|
96
61
|
|
|
97
|
-
####
|
|
62
|
+
#### LLM::Context
|
|
98
63
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
[`LLM::Function::Return`](https://0x1eef.github.io/x/llm.rb/LLM/Function/Return.html):
|
|
64
|
+
The [`LLM::Context`](https://r.uby.dev/api-docs/llm.rb/LLM/Context.html) class is at the heart of the runtime
|
|
65
|
+
and it is what [`LLM::Agent`](https://r.uby.dev/api-docs/llm.rb/LLM/Agent.html) uses under the hood.
|
|
66
|
+
It requires that the tool call loop be managed manually -
|
|
67
|
+
sometimes that can be useful, but usually for advanced use-cases.
|
|
68
|
+
If you're new to llm.rb, try [`LLM::Agent`](https://r.uby.dev/api-docs/llm.rb/LLM/Agent.html) first.
|
|
105
69
|
|
|
106
70
|
```ruby
|
|
107
71
|
require "llm"
|
|
108
72
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
def on_tool_confirmation(fn, strategy)
|
|
114
|
-
path = fn.arguments.path
|
|
115
|
-
if path.start_with?("/tmp/")
|
|
116
|
-
fn.spawn(strategy).wait
|
|
117
|
-
else
|
|
118
|
-
fn.cancel(reason: "Deletion requires approval")
|
|
119
|
-
end
|
|
120
|
-
end
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
llm = LLM.openai(key: ENV["KEY"])
|
|
124
|
-
Agent.new(llm, stream: $stdout).talk("Delete /tmp/example.txt.")
|
|
73
|
+
llm = LLM.deepseek(key: ENV["KEY"])
|
|
74
|
+
ctx = LLM::Context.new(llm, stream: $stdout)
|
|
75
|
+
ctx.talk "Hello world"
|
|
125
76
|
```
|
|
126
77
|
|
|
127
|
-
####
|
|
78
|
+
#### LLM::Tool
|
|
128
79
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
abilities of a model
|
|
80
|
+
Subclasses of [`LLM::Tool`](https://r.uby.dev/api-docs/llm.rb/LLM/Tool.html) are plain Ruby classes with
|
|
81
|
+
an optional set of typed parameters. <br> The model can choose to
|
|
82
|
+
call them on your behalf, and they're one of the most powerful features
|
|
83
|
+
for extending the feature set or abilities of a model.
|
|
133
84
|
|
|
134
85
|
```ruby
|
|
135
86
|
class ReadFile < LLM::Tool
|
|
@@ -144,632 +95,243 @@ class ReadFile < LLM::Tool
|
|
|
144
95
|
end
|
|
145
96
|
```
|
|
146
97
|
|
|
147
|
-
#### MCP
|
|
148
|
-
|
|
149
|
-
The
|
|
150
|
-
[LLM::MCP](https://0x1eef.github.io/x/llm.rb/LLM/MCP.html)
|
|
151
|
-
object lets llm.rb use tools provided by an MCP server. Those tools are
|
|
152
|
-
exposed through the same runtime as local tools, so you can pass them
|
|
153
|
-
to either
|
|
154
|
-
[LLM::Context](https://0x1eef.github.io/x/llm.rb/LLM/Context.html)
|
|
155
|
-
or
|
|
156
|
-
[LLM::Agent](https://0x1eef.github.io/x/llm.rb/LLM/Agent.html).
|
|
157
|
-
In this example, the MCP server runs over stdio and
|
|
158
|
-
[LLM::Agent](https://0x1eef.github.io/x/llm.rb/LLM/Agent.html)
|
|
159
|
-
manages the tool loop. For **stdio**, `mcp.session` is the preferred
|
|
160
|
-
pattern because it keeps one MCP session alive across discovery and
|
|
161
|
-
tool calls:
|
|
162
|
-
|
|
163
|
-
```ruby
|
|
164
|
-
require "llm"
|
|
165
|
-
|
|
166
|
-
llm = LLM.openai(key: ENV["KEY"])
|
|
167
|
-
mcp = LLM::MCP.stdio(argv: ["ruby", "server.rb"])
|
|
168
|
-
|
|
169
|
-
mcp.session do
|
|
170
|
-
agent = LLM::Agent.new(llm, stream: $stdout, tools: mcp.tools)
|
|
171
|
-
agent.talk "Use the available tools to inspect the environment."
|
|
172
|
-
end
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
MCP can also be used without `session`. Although it works it is generally
|
|
176
|
-
not recommended for the **stdio** transport because it is inefficient
|
|
177
|
-
to start and stop a fresh MCP process for tool discovery and each tool
|
|
178
|
-
call:
|
|
179
|
-
|
|
180
|
-
```ruby
|
|
181
|
-
require "llm"
|
|
182
|
-
|
|
183
|
-
llm = LLM.openai(key: ENV["KEY"])
|
|
184
|
-
mcp = LLM::MCP.stdio(argv: ["ruby", "server.rb"])
|
|
185
|
-
|
|
186
|
-
agent = LLM::Agent.new(llm, tools: mcp.tools)
|
|
187
|
-
agent.talk("Use the available tools to inspect the environment.")
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
The HTTP transport can be used with or without the `session` method,
|
|
191
|
-
and unlike the stdio transport it can remain efficient without the
|
|
192
|
-
`session` method through a persistent connection pool that is available
|
|
193
|
-
through the
|
|
194
|
-
[LLM::Transport.net_http_persistent](https://0x1eef.github.io/x/llm.rb/LLM/Transport.html#method-c-net_http_persistent)
|
|
195
|
-
transport:
|
|
196
|
-
|
|
197
|
-
```ruby
|
|
198
|
-
require "llm"
|
|
199
|
-
|
|
200
|
-
llm = LLM.openai(key: ENV["KEY"])
|
|
201
|
-
mcp = LLM::MCP.http(
|
|
202
|
-
url: "https://remote-mcp.example.com",
|
|
203
|
-
transport: :net_http_persistent
|
|
204
|
-
)
|
|
205
|
-
|
|
206
|
-
agent = LLM::Agent.new(llm, tools: mcp.tools)
|
|
207
|
-
agent.talk("Use the available tools to inspect the environment.")
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
#### A2A (Agent 2 Agent)
|
|
211
|
-
|
|
212
|
-
The
|
|
213
|
-
[LLM::A2A](https://0x1eef.github.io/x/llm.rb/LLM/A2A.html)
|
|
214
|
-
object lets llm.rb use skills provided by a remote A2A agent. Those
|
|
215
|
-
skills are exposed through the same runtime as local tools, so you can
|
|
216
|
-
pass them to either
|
|
217
|
-
[LLM::Context](https://0x1eef.github.io/x/llm.rb/LLM/Context.html)
|
|
218
|
-
or
|
|
219
|
-
[LLM::Agent](https://0x1eef.github.io/x/llm.rb/LLM/Agent.html).
|
|
220
|
-
|
|
221
|
-
Use remote skills as local tools:
|
|
222
|
-
|
|
223
|
-
```ruby
|
|
224
|
-
require "llm"
|
|
225
|
-
|
|
226
|
-
a2a = LLM::A2A.rest(
|
|
227
|
-
url: "https://remote-agent.example.com",
|
|
228
|
-
headers: {"Authorization" => "Bearer token"}
|
|
229
|
-
)
|
|
230
|
-
llm = LLM.openai(key: ENV["KEY"])
|
|
231
|
-
agent = LLM::Agent.new(llm, tools: a2a.skills)
|
|
232
|
-
agent.talk "Analyze this CSV and summarize the trends."
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
Use persistent HTTP connections:
|
|
236
|
-
|
|
237
|
-
```ruby
|
|
238
|
-
require "llm"
|
|
239
|
-
|
|
240
|
-
a2a = LLM::A2A.rest(
|
|
241
|
-
url: "https://remote-agent.example.com",
|
|
242
|
-
transport: :net_http_persistent
|
|
243
|
-
)
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
For more on direct messaging, task operations, push notification
|
|
247
|
-
configs, and JSON-RPC, see the
|
|
248
|
-
[LLM::A2A API docs](https://0x1eef.github.io/x/llm.rb/LLM/A2A.html).
|
|
249
|
-
|
|
250
|
-
#### Transports
|
|
251
|
-
|
|
252
|
-
Providers use Ruby's standard library Net::HTTP transport by default.
|
|
253
|
-
You can opt into persistent Net::HTTP connections with `persistent: true`,
|
|
254
|
-
or provide a transport shortcut when you want a different backend.
|
|
255
|
-
`transport: :curb` uses libcurl through the optional `curb` gem.
|
|
256
|
-
|
|
257
|
-
Custom transports can implement the
|
|
258
|
-
[LLM::Transport](https://0x1eef.github.io/x/llm.rb/LLM/Transport.html)
|
|
259
|
-
interface and receive transport-agnostic
|
|
260
|
-
[LLM::Transport::Request](https://0x1eef.github.io/x/llm.rb/LLM/Transport/Request.html)
|
|
261
|
-
objects from providers.
|
|
262
|
-
|
|
263
|
-
```ruby
|
|
264
|
-
require "llm"
|
|
265
|
-
|
|
266
|
-
llm = LLM.openai(key: ENV["KEY"], persistent: true)
|
|
267
|
-
llm = LLM.openai(key: ENV["KEY"], transport: :net_http_persistent)
|
|
268
|
-
llm = LLM.openai(key: ENV["KEY"], transport: :curb)
|
|
269
|
-
```
|
|
270
|
-
|
|
271
|
-
#### Skills
|
|
272
|
-
|
|
273
|
-
Skills are reusable instructions loaded from a `SKILL.md` directory. They let
|
|
274
|
-
you package behavior and tool access together, and they plug into the
|
|
275
|
-
same runtime as tools, agents, MCP, and A2A. When a skill runs, llm.rb
|
|
276
|
-
spawns a subagent with the skill instructions, access to only the tools
|
|
277
|
-
listed in the skill, and recent conversation context:
|
|
278
|
-
|
|
279
|
-
```yaml
|
|
280
|
-
---
|
|
281
|
-
name: release
|
|
282
|
-
description: Prepare a release
|
|
283
|
-
tools: ["search-docs", "git"]
|
|
284
|
-
---
|
|
285
|
-
|
|
286
|
-
## Task
|
|
287
|
-
|
|
288
|
-
Review the release state, summarize what changed, and prepare the release.
|
|
289
|
-
```
|
|
290
|
-
|
|
291
|
-
```ruby
|
|
292
|
-
require "llm"
|
|
293
|
-
|
|
294
|
-
class ReleaseAgent < LLM::Agent
|
|
295
|
-
model "gpt-5.4-mini"
|
|
296
|
-
skills "./skills/release"
|
|
297
|
-
end
|
|
298
|
-
|
|
299
|
-
llm = LLM.openai(key: ENV["KEY"])
|
|
300
|
-
ReleaseAgent.new(llm, stream: $stdout).talk("Prepare the next release.")
|
|
301
|
-
```
|
|
302
|
-
|
|
303
|
-
A skill can also have its sub-agent inherit the parents tools through the
|
|
304
|
-
`inherit` directive. The `inherit` directive has coverage for the "classic"
|
|
305
|
-
tools (a subclass of [LLM::Tool](https://0x1eef.github.io/x/llm.rb/LLM/Tool.html)),
|
|
306
|
-
MCP tools, and A2A tools that a parent context or agent has access to:
|
|
307
|
-
|
|
308
|
-
```yaml
|
|
309
|
-
---
|
|
310
|
-
name: release
|
|
311
|
-
description: Prepare a release
|
|
312
|
-
tools: inherit
|
|
313
|
-
---
|
|
314
|
-
```
|
|
315
|
-
|
|
316
98
|
#### LLM::Stream
|
|
317
99
|
|
|
318
|
-
|
|
319
|
-
[LLM::Stream](https://
|
|
320
|
-
|
|
321
|
-
can subclass it to handle streamed content in your own application:
|
|
100
|
+
Streams can be simple IO objects or subclasses of
|
|
101
|
+
[`LLM::Stream`](https://r.uby.dev/api-docs/llm.rb/LLM/Stream.html) with structured callbacks for content,
|
|
102
|
+
reasoning, tool calls, tool returns, and compaction.
|
|
322
103
|
|
|
323
104
|
```ruby
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
class Stream < LLM::Stream
|
|
105
|
+
class MyStream < LLM::Stream
|
|
327
106
|
def on_content(content)
|
|
328
|
-
|
|
107
|
+
print content
|
|
329
108
|
end
|
|
330
|
-
end
|
|
331
109
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
agent.talk "Write a haiku about Ruby."
|
|
335
|
-
```
|
|
336
|
-
|
|
337
|
-
#### LLM::Stream (advanced)
|
|
338
|
-
|
|
339
|
-
The
|
|
340
|
-
[LLM::Stream](https://0x1eef.github.io/x/llm.rb/LLM/Stream.html)
|
|
341
|
-
object can also resolve tool calls while output is still streaming. In
|
|
342
|
-
`on_tool_call`, you can spawn the tool, push the work onto the stream
|
|
343
|
-
queue, and later drain it with `wait`:
|
|
344
|
-
|
|
345
|
-
```ruby
|
|
346
|
-
require "llm"
|
|
347
|
-
|
|
348
|
-
class Stream < LLM::Stream
|
|
349
|
-
def on_content(content)
|
|
350
|
-
$stdout << content
|
|
351
|
-
end
|
|
352
|
-
|
|
353
|
-
def on_tool_call(tool, error)
|
|
354
|
-
return queue << error if error
|
|
355
|
-
queue << ctx.spawn(tool, :thread)
|
|
110
|
+
def on_reasoning_content(content)
|
|
111
|
+
warn content
|
|
356
112
|
end
|
|
357
113
|
end
|
|
358
114
|
|
|
359
|
-
llm = LLM.
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
ctx.talk(ctx.wait) while ctx.functions?
|
|
363
|
-
```
|
|
364
|
-
|
|
365
|
-
#### Concurrency
|
|
366
|
-
|
|
367
|
-
llm.rb can run tool work concurrently. This is useful when a model calls
|
|
368
|
-
multiple tools and you want to resolve them in parallel instead of one
|
|
369
|
-
at a time. On
|
|
370
|
-
[LLM::Agent](https://0x1eef.github.io/x/llm.rb/LLM/Agent.html),
|
|
371
|
-
you can enable this with `concurrency`. Common options are `:call` for
|
|
372
|
-
sequential execution, `:thread`, or `:task` for concurrent IO-bound work, and
|
|
373
|
-
`:ractor` or `:fork` for more isolated CPU-bound work:
|
|
374
|
-
|
|
375
|
-
```ruby
|
|
376
|
-
require "llm"
|
|
377
|
-
|
|
378
|
-
class Agent < LLM::Agent
|
|
379
|
-
model "gpt-5.4-mini"
|
|
380
|
-
tools ReadFile
|
|
381
|
-
concurrency :thread
|
|
382
|
-
end
|
|
383
|
-
|
|
384
|
-
llm = LLM.openai(key: ENV["KEY"])
|
|
385
|
-
agent = Agent.new(llm, stream: $stdout)
|
|
386
|
-
agent.talk "Read README.md and CHANGELOG.md and compare them."
|
|
387
|
-
```
|
|
388
|
-
|
|
389
|
-
#### Serialization
|
|
390
|
-
|
|
391
|
-
The [`LLM::Agent`](https://0x1eef.github.io/x/llm.rb/LLM/Agent.html)
|
|
392
|
-
object can be serialized to JSON, which makes it suitable for storing
|
|
393
|
-
in a file, a database column, or a Redis queue. The built-in
|
|
394
|
-
ActiveRecord and Sequel plugins are built on top of the same underlying
|
|
395
|
-
serialization feature:
|
|
396
|
-
|
|
397
|
-
```ruby
|
|
398
|
-
require "llm"
|
|
399
|
-
|
|
400
|
-
llm = LLM.openai(key: ENV["KEY"])
|
|
401
|
-
|
|
402
|
-
# Serialize an agent
|
|
403
|
-
agent1 = LLM::Agent.new(llm)
|
|
404
|
-
agent1.talk "Remember that my favorite language is Ruby"
|
|
405
|
-
string = agent1.to_json
|
|
406
|
-
|
|
407
|
-
# Restore an agent (from JSON)
|
|
408
|
-
agent2 = LLM::Agent.new(llm, stream: $stdout)
|
|
409
|
-
agent2.restore(string:)
|
|
410
|
-
agent2.talk "What is my favorite language?"
|
|
115
|
+
llm = LLM.deepseek(key: ENV["KEY"])
|
|
116
|
+
agent = LLM::Agent.new(llm, stream: MyStream.new)
|
|
117
|
+
agent.talk "Explain Ruby fibers."
|
|
411
118
|
```
|
|
412
119
|
|
|
413
|
-
####
|
|
120
|
+
#### LLM::MCP
|
|
414
121
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
[`LLM::Response`](https://0x1eef.github.io/x/llm.rb/LLM/Response.html),
|
|
421
|
-
so use `.content` when you want the text directly:
|
|
122
|
+
The Model Context Protocol (MCP) has first-class support
|
|
123
|
+
in llm.rb. The stdio and http transports work out of the
|
|
124
|
+
box. MCP tools are translated into subclasses of
|
|
125
|
+
[`LLM::Tool`](https://r.uby.dev/api-docs/llm.rb/LLM/Tool.html) that can be used with [`LLM::Context`](https://r.uby.dev/api-docs/llm.rb/LLM/Context.html)
|
|
126
|
+
or [`LLM::Agent`](https://r.uby.dev/api-docs/llm.rb/LLM/Agent.html).
|
|
422
127
|
|
|
423
128
|
```ruby
|
|
424
129
|
require "llm"
|
|
425
130
|
|
|
426
|
-
llm
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
puts agent.ask("Summarize this document.", with: "README.md").content
|
|
431
|
-
agent.ask("Stream this reply.") { $stdout << _1 }
|
|
432
|
-
```
|
|
433
|
-
|
|
434
|
-
## Installation
|
|
435
|
-
|
|
436
|
-
```bash
|
|
437
|
-
gem install llm.rb
|
|
131
|
+
llm = LLM.deepseek(key: ENV["KEY"])
|
|
132
|
+
mcp = LLM::MCP.stdio(argv: ["ruby", "server.rb"])
|
|
133
|
+
agent = LLM::Agent.new(llm, stream: $stdout, tools: mcp.tools)
|
|
134
|
+
agent.talk "Run the tool"
|
|
438
135
|
```
|
|
439
136
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
#### REPL
|
|
137
|
+
#### LLM::A2A
|
|
443
138
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
[
|
|
139
|
+
The Agent 2 Agent (A2A) protocol has first-class support
|
|
140
|
+
in llm.rb. The http and jsonrpc transports work out of the
|
|
141
|
+
box. A2A skills are translated into subclasses of
|
|
142
|
+
[`LLM::Tool`](https://r.uby.dev/api-docs/llm.rb/LLM/Tool.html) that can be used with [`LLM::Context`](https://r.uby.dev/api-docs/llm.rb/LLM/Context.html)
|
|
143
|
+
or [`LLM::Agent`](https://r.uby.dev/api-docs/llm.rb/LLM/Agent.html).
|
|
448
144
|
|
|
449
145
|
```ruby
|
|
450
146
|
require "llm"
|
|
451
147
|
|
|
452
|
-
llm
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
print "> "
|
|
457
|
-
agent.talk(STDIN.gets || break)
|
|
458
|
-
puts
|
|
459
|
-
end
|
|
148
|
+
llm = LLM.deepseek(key: ENV["KEY"])
|
|
149
|
+
a2a = LLM::A2A.rest(url: "https://remote-agent.example.com")
|
|
150
|
+
agent = LLM::Agent.new(llm, stream: $stdout, tools: a2a.skills)
|
|
151
|
+
agent.talk "Run the skill"
|
|
460
152
|
```
|
|
461
153
|
|
|
462
|
-
####
|
|
154
|
+
#### RAG
|
|
463
155
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
Ruby prompt into the provider-specific multimodal request schema.
|
|
156
|
+
Most providers offer an embedding model that can be
|
|
157
|
+
used for semantic search, or similarity search. An
|
|
158
|
+
embedding model can generate embeddings that can then
|
|
159
|
+
be stored in a database that is optimized for storing
|
|
160
|
+
and querying vectors, such as SQLite's [sqlite-vec](https://github.com/asg017/sqlite-vec)
|
|
161
|
+
or PostgreSQL's [pg-vector](https://github.com/pgvector/pgvector).
|
|
471
162
|
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
[`agent.local_file(...)`](https://0x1eef.github.io/x/llm.rb/LLM/Agent.html#local_file-instance_method)
|
|
476
|
-
object:
|
|
163
|
+
llm.rb also includes support for OpenAI's vector store API. It
|
|
164
|
+
provides a vector database as a HTTP service but we won't cover
|
|
165
|
+
that here.
|
|
477
166
|
|
|
478
167
|
```ruby
|
|
479
168
|
require "llm"
|
|
480
169
|
|
|
481
|
-
llm
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
```
|
|
485
|
-
|
|
486
|
-
#### Context Compaction
|
|
487
|
-
|
|
488
|
-
This example uses [`LLM::Agent`](https://0x1eef.github.io/x/llm.rb/LLM/Agent.html),
|
|
489
|
-
[`LLM::Compactor`](https://0x1eef.github.io/x/llm.rb/LLM/Compactor.html), and
|
|
490
|
-
[`LLM::Stream`](https://0x1eef.github.io/x/llm.rb/LLM/Stream.html) together so
|
|
491
|
-
long-lived conversations can summarize older history and expose the lifecycle
|
|
492
|
-
through stream hooks. This approach is inspired by General Intelligence
|
|
493
|
-
Systems. The
|
|
494
|
-
compactor can also use its own `model:` if you want summarization to run on a
|
|
495
|
-
different model from the main conversation. `token_threshold:` accepts either a
|
|
496
|
-
fixed token count or a percentage string like `"90%"`, which resolves
|
|
497
|
-
against the active model context window and triggers compaction once total
|
|
498
|
-
token usage goes over that percentage. See the
|
|
499
|
-
[deepdive (web)](https://llmrb.github.io/llm.rb/) or
|
|
500
|
-
[deepdive (markdown)](resources/deepdive.md) for more examples.
|
|
501
|
-
|
|
502
|
-
```ruby
|
|
503
|
-
require "llm"
|
|
170
|
+
llm = LLM.openai(key: ENV["KEY"])
|
|
171
|
+
body = "llm.rb is Ruby's capable AI runtime."
|
|
172
|
+
embedding = llm.embed([body]).embeddings.first
|
|
504
173
|
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
def on_compaction_finish(ctx, compactor)
|
|
511
|
-
puts "Compacted to #{ctx.messages.size} messages."
|
|
512
|
-
end
|
|
513
|
-
end
|
|
514
|
-
|
|
515
|
-
llm = LLM.openai(key: ENV["KEY"])
|
|
516
|
-
agent = LLM::Agent.new(
|
|
517
|
-
llm,
|
|
518
|
-
stream: Stream.new,
|
|
519
|
-
compactor: {
|
|
520
|
-
token_threshold: "90%",
|
|
521
|
-
retention_window: 8,
|
|
522
|
-
model: "gpt-5.4-mini"
|
|
523
|
-
}
|
|
174
|
+
Document.create!(
|
|
175
|
+
title: "llm.rb",
|
|
176
|
+
body:,
|
|
177
|
+
embedding:,
|
|
524
178
|
)
|
|
525
179
|
```
|
|
526
180
|
|
|
527
|
-
####
|
|
528
|
-
|
|
529
|
-
This example uses [`LLM::Stream`](https://0x1eef.github.io/x/llm.rb/LLM/Stream.html)
|
|
530
|
-
with the OpenAI Responses API so reasoning output is streamed separately from
|
|
531
|
-
visible assistant output. See the
|
|
532
|
-
[deepdive (web)](https://llmrb.github.io/llm.rb/) or
|
|
533
|
-
[deepdive (markdown)](resources/deepdive.md) for more examples.
|
|
534
|
-
|
|
535
|
-
To use the Responses API (OpenAI-specific), initialize an agent with
|
|
536
|
-
`mode: :responses` and keep using `talk` for turns.
|
|
537
|
-
|
|
538
|
-
```ruby
|
|
539
|
-
require "llm"
|
|
540
|
-
|
|
541
|
-
class Stream < LLM::Stream
|
|
542
|
-
def on_content(content)
|
|
543
|
-
$stdout << content
|
|
544
|
-
end
|
|
545
|
-
|
|
546
|
-
def on_reasoning_content(content)
|
|
547
|
-
$stderr << content
|
|
548
|
-
end
|
|
549
|
-
end
|
|
550
|
-
|
|
551
|
-
llm = LLM.openai(key: ENV["KEY"])
|
|
552
|
-
agent = LLM::Agent.new(
|
|
553
|
-
llm,
|
|
554
|
-
model: "gpt-5.4-mini",
|
|
555
|
-
mode: :responses,
|
|
556
|
-
reasoning: {effort: "medium"},
|
|
557
|
-
stream: Stream.new
|
|
558
|
-
)
|
|
559
|
-
agent.talk("Solve 17 * 19 and show your work.")
|
|
560
|
-
```
|
|
561
|
-
|
|
562
|
-
#### Request Cancellation
|
|
563
|
-
|
|
564
|
-
Need to cancel a stream? llm.rb has you covered through
|
|
565
|
-
[`LLM::Agent#interrupt!`](https://0x1eef.github.io/x/llm.rb/LLM/Agent.html#interrupt-21-instance_method).
|
|
566
|
-
<br> See the [deepdive (web)](https://llmrb.github.io/llm.rb/)
|
|
567
|
-
or [deepdive (markdown)](resources/deepdive.md) for more examples.
|
|
568
|
-
|
|
569
|
-
```ruby
|
|
570
|
-
require "llm"
|
|
571
|
-
require "io/console"
|
|
572
|
-
|
|
573
|
-
llm = LLM.openai(key: ENV["KEY"])
|
|
574
|
-
agent = LLM::Agent.new(llm, stream: $stdout)
|
|
575
|
-
worker = Thread.new do
|
|
576
|
-
agent.talk("Write a very long essay about network protocols.")
|
|
577
|
-
rescue LLM::Interrupt
|
|
578
|
-
puts "Request was interrupted!"
|
|
579
|
-
end
|
|
580
|
-
|
|
581
|
-
STDIN.getch
|
|
582
|
-
agent.interrupt!
|
|
583
|
-
worker.join
|
|
584
|
-
```
|
|
181
|
+
#### Concurrency
|
|
585
182
|
|
|
586
|
-
|
|
183
|
+
The runtime supports five different concurrency strategies that have
|
|
184
|
+
different attributes. The choice between all of them often depends
|
|
185
|
+
on the requirements of your application.
|
|
587
186
|
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
`
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
injects defaults such as `model:`. <br> See the
|
|
594
|
-
[deepdive (web)](https://llmrb.github.io/llm.rb/) or
|
|
595
|
-
[deepdive (markdown)](resources/deepdive.md) for more examples.
|
|
187
|
+
IO-bound tools are a good fit for the `:task`, `:thread`,
|
|
188
|
+
and `:fiber` strategies while true parallelism can be achieved
|
|
189
|
+
with the `:fork` and `:ractor` strategies. The
|
|
190
|
+
`:fork` strategy also provides a separate process that offers
|
|
191
|
+
isolation from its parent.
|
|
596
192
|
|
|
597
193
|
```ruby
|
|
598
194
|
require "llm"
|
|
599
|
-
require "net/http/persistent"
|
|
600
|
-
require "sequel"
|
|
601
|
-
require "sequel/plugins/llm"
|
|
602
|
-
|
|
603
|
-
class Context < Sequel::Model
|
|
604
|
-
plugin :llm, provider: :set_provider, context: :set_context
|
|
605
|
-
|
|
606
|
-
private
|
|
607
195
|
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
def set_context
|
|
613
|
-
{model: "gpt-5.4-mini", mode: :responses, store: false}
|
|
614
|
-
end
|
|
615
|
-
end
|
|
616
|
-
|
|
617
|
-
ctx = Context.create
|
|
618
|
-
ctx.talk("Remember that my favorite language is Ruby")
|
|
619
|
-
puts ctx.talk("What is my favorite language?").content
|
|
196
|
+
llm = LLM.deepseek(key: ENV["KEY"])
|
|
197
|
+
tools = [FetchNews, FetchStocks, FetchFeeds]
|
|
198
|
+
agent = LLM::Agent.new(llm, tools:, concurrency: :fork)
|
|
199
|
+
agent.talk "Run the tools in parallel"
|
|
620
200
|
```
|
|
621
201
|
|
|
622
|
-
####
|
|
202
|
+
#### ORM
|
|
623
203
|
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
columns, provide them to llm.rb through `provider:` and `context:` instead of
|
|
628
|
-
relying on reserved wrapper columns.
|
|
204
|
+
Because both [`LLM::Context`](https://r.uby.dev/api-docs/llm.rb/LLM/Context.html), and [`LLM::Agent`](https://r.uby.dev/api-docs/llm.rb/LLM/Agent.html)
|
|
205
|
+
can be serialized to JSON and stored in a simple string, both ActiveRecord
|
|
206
|
+
and Sequel support can be implemented within a single column on a single row.
|
|
629
207
|
|
|
630
|
-
|
|
631
|
-
|
|
208
|
+
The runtime includes first-class support for both ActiveRecord *and* Sequel, and
|
|
209
|
+
for both Rack-based applications *and* Rails-based applications. On databases
|
|
210
|
+
where it is supported, such as PostgreSQL, the column can be optimized by using
|
|
211
|
+
the `jsonb` type.
|
|
632
212
|
|
|
633
213
|
```ruby
|
|
634
|
-
require "llm"
|
|
635
214
|
require "active_record"
|
|
636
|
-
require "llm/active_record"
|
|
637
|
-
|
|
638
|
-
class Context < ApplicationRecord
|
|
639
|
-
acts_as_llm provider: :set_provider, context: :set_context
|
|
640
|
-
|
|
641
|
-
private
|
|
642
|
-
|
|
643
|
-
def set_provider
|
|
644
|
-
LLM.openai(key: ENV["OPENAI_SECRET"])
|
|
645
|
-
end
|
|
646
|
-
|
|
647
|
-
def set_context
|
|
648
|
-
{model: "gpt-5.4-mini", mode: :responses, store: false}
|
|
649
|
-
end
|
|
650
|
-
end
|
|
651
|
-
|
|
652
|
-
ctx = Context.create!
|
|
653
|
-
ctx.talk("Remember that my favorite language is Ruby")
|
|
654
|
-
puts ctx.talk("What is my favorite language?").content
|
|
655
|
-
```
|
|
656
|
-
|
|
657
|
-
```ruby
|
|
658
215
|
require "llm"
|
|
659
|
-
require "active_record"
|
|
660
216
|
require "llm/active_record"
|
|
661
217
|
|
|
662
|
-
class
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
private
|
|
669
|
-
|
|
670
|
-
def set_provider
|
|
671
|
-
LLM.public_send(provider_name, key: provider_key)
|
|
672
|
-
end
|
|
673
|
-
|
|
674
|
-
def set_context
|
|
675
|
-
{model: model_name, mode: :responses, store: false}
|
|
218
|
+
class Agent < ApplicationRecord
|
|
219
|
+
acts_as_agent do |agent|
|
|
220
|
+
agent.model "deepseek-v4-pro"
|
|
221
|
+
agent.instructions "solve the user's query"
|
|
222
|
+
agent.tools [Research, FinalizeResearch, ActOnResearch]
|
|
676
223
|
end
|
|
677
|
-
end
|
|
678
|
-
```
|
|
679
|
-
|
|
680
|
-
#### ActiveRecord (ORM): acts_as_agent
|
|
681
|
-
|
|
682
|
-
The `acts_as_agent` method wraps [`LLM::Agent`](https://0x1eef.github.io/x/llm.rb/LLM/Agent.html) and
|
|
683
|
-
manages tool execution for you. Like `acts_as_llm`, its built-in persistence
|
|
684
|
-
contract is one serialized `data` column. If your app has provider or model
|
|
685
|
-
columns, provide them to llm.rb through your hooks and agent DSL.
|
|
686
|
-
|
|
687
|
-
See the [deepdive (web)](https://llmrb.github.io/llm.rb/)
|
|
688
|
-
or [deepdive (markdown)](resources/deepdive.md) for more examples.
|
|
689
|
-
|
|
690
|
-
```ruby
|
|
691
|
-
require "llm"
|
|
692
|
-
require "active_record"
|
|
693
|
-
require "llm/active_record"
|
|
694
|
-
|
|
695
|
-
class Ticket < ApplicationRecord
|
|
696
|
-
acts_as_agent provider: :set_provider, context: :set_context
|
|
697
|
-
model "gpt-5.4-mini"
|
|
698
|
-
instructions "You are a concise support assistant."
|
|
699
|
-
tools SearchDocs, Escalate
|
|
700
|
-
concurrency :thread
|
|
701
224
|
|
|
702
225
|
private
|
|
703
226
|
|
|
227
|
+
# By convention, this method defines the provider for a model.
|
|
228
|
+
# If necessary, it can be renamed with: provider: :your_method.
|
|
704
229
|
def set_provider
|
|
705
|
-
LLM.
|
|
230
|
+
LLM.deepseek(key: ENV["KEY"])
|
|
706
231
|
end
|
|
707
232
|
|
|
233
|
+
# By convention, this method returns the context options given
|
|
234
|
+
# to LLM::Context or LLM::Agent.
|
|
708
235
|
def set_context
|
|
709
|
-
{
|
|
236
|
+
{}
|
|
710
237
|
end
|
|
711
238
|
end
|
|
712
239
|
|
|
713
|
-
|
|
714
|
-
|
|
240
|
+
agent = Agent.create!
|
|
241
|
+
agent.talk "perform research"
|
|
715
242
|
```
|
|
716
243
|
|
|
717
|
-
|
|
718
|
-
require "llm"
|
|
719
|
-
require "active_record"
|
|
720
|
-
require "llm/active_record"
|
|
244
|
+
## FAQ
|
|
721
245
|
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
246
|
+
<details>
|
|
247
|
+
<summary>What providers does llm.rb support?</summary>
|
|
248
|
+
<br>
|
|
249
|
+
<p>
|
|
250
|
+
China-based
|
|
726
251
|
|
|
727
|
-
|
|
252
|
+
* DeepSeek
|
|
253
|
+
* zAI
|
|
728
254
|
|
|
729
|
-
|
|
730
|
-
LLM.public_send(provider_name, key: provider_key)
|
|
731
|
-
end
|
|
255
|
+
US-based
|
|
732
256
|
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
257
|
+
* OpenAI
|
|
258
|
+
* Google (Gemini)
|
|
259
|
+
* xAI
|
|
260
|
+
* AWS bedrock
|
|
261
|
+
* DeepInfra
|
|
262
|
+
* Anthropic
|
|
738
263
|
|
|
739
|
-
|
|
264
|
+
Openweights
|
|
740
265
|
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
[deepdive (web)](https://llmrb.github.io/llm.rb/) or
|
|
746
|
-
[deepdive (markdown)](resources/deepdive.md) for more examples.
|
|
266
|
+
* DeepSeek
|
|
267
|
+
* zAI
|
|
268
|
+
* DeepInfra
|
|
269
|
+
* AWS bedrock
|
|
747
270
|
|
|
748
|
-
|
|
749
|
-
require "llm"
|
|
750
|
-
require "net/http/persistent"
|
|
271
|
+
Host your own
|
|
751
272
|
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
persistent: true
|
|
757
|
-
)
|
|
273
|
+
* Ollama
|
|
274
|
+
* Llamacpp
|
|
275
|
+
</p>
|
|
276
|
+
</details>
|
|
758
277
|
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
278
|
+
<details>
|
|
279
|
+
<summary>I have a limited budget. What should I do?</summary>
|
|
280
|
+
<br>
|
|
281
|
+
<p>
|
|
282
|
+
There a few options. The first option is to host
|
|
283
|
+
your own model, and use the ollama or llamacpp
|
|
284
|
+
providers. This can be diffilcult though because
|
|
285
|
+
a capable model requires hardware that can
|
|
286
|
+
match it. If you have the ability to self-host,
|
|
287
|
+
this would be my first option.
|
|
288
|
+
</p>
|
|
289
|
+
<p>
|
|
290
|
+
The second option is DeepSeek. <br>
|
|
291
|
+
The deepseek-v4-flash model costs pennies to use. <br>
|
|
292
|
+
And llm.rb has been optimized for deepseek. For example,
|
|
293
|
+
DeepSeek does not have image generation capabilities
|
|
294
|
+
but on the llm.rb runtime it does (vector graphics only,
|
|
295
|
+
though).
|
|
296
|
+
</p>
|
|
297
|
+
<p>
|
|
298
|
+
The same is true for structured outputs. DeepSeek does
|
|
299
|
+
not support structured outputs in the same way as OpenAI or
|
|
300
|
+
Google, but the llm.rb runtime makes it appear as
|
|
301
|
+
though it does, through the `json_object` response
|
|
302
|
+
type.
|
|
303
|
+
</p>
|
|
304
|
+
If you're on a budget, DeepSeek is hard to beat.
|
|
305
|
+
</details>
|
|
306
|
+
<details>
|
|
307
|
+
<summary>Can I download llm.rb via a decentralized network?</summary>
|
|
308
|
+
<br>
|
|
309
|
+
You can!
|
|
310
|
+
<br>
|
|
311
|
+
We are on the <a href="https://radicle.network">radicle.network</a>
|
|
312
|
+
<br>
|
|
313
|
+
Every commit that lands on GitHub also lands on Radicle.
|
|
314
|
+
<br>
|
|
315
|
+
Our repository ID is z2PtfQ6dYwyYaW2aGrztG1sMyDmCE.
|
|
316
|
+
<br>
|
|
317
|
+
Browse on <a href="https://radicle.network/nodes/iris.radicle.network/z2PtfQ6dYwyYaW2aGrztG1sMyDmCE">the web</a>.
|
|
318
|
+
</details>
|
|
762
319
|
|
|
763
320
|
## Resources
|
|
764
321
|
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
322
|
+
If you like what you read so far, check out the [deepdive.md](https://r.uby.dev/llm/deepdive/)
|
|
323
|
+
to learn more. Unfortunately it
|
|
324
|
+
wasn't possible to cover every feature without the README becoming a small book.
|
|
325
|
+
The [r.uby.dev](https://r.uby.dev) homepage also includes more learning material
|
|
326
|
+
and resources.
|
|
770
327
|
|
|
771
328
|
## License
|
|
772
329
|
|
|
773
|
-
[
|
|
330
|
+
[Business Source License 1.1](./LICENSE)
|
|
331
|
+
<br>
|
|
332
|
+
Commercial production use requires a commercial license.
|
|
333
|
+
<br>
|
|
334
|
+
Each version converts to the [BSD Zero Clause](https://choosealicense.com/licenses/0bsd/)
|
|
335
|
+
four years after its first public release.
|
|
774
336
|
<br>
|
|
775
|
-
|
|
337
|
+
Contact [robert@r.uby.dev](mailto:robert@r.uby.dev) for a commercial license.
|