llm.rb 16.0.0.pre.1 → 16.0.0.pre.3
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 +71 -11
- data/README.md +11 -6
- data/data/alibaba.json +16 -8
- data/data/anthropic.json +60 -6
- data/data/bedrock.json +406 -179
- data/data/deepinfra.json +105 -53
- data/data/deepseek.json +8 -4
- data/data/google.json +48 -24
- data/data/mistral.json +22 -42
- data/data/moonshot.json +6 -3
- data/data/openai.json +28 -14
- data/data/openrouter.json +650 -387
- data/data/xai.json +10 -5
- data/data/zai.json +14 -7
- data/docs/deepdive/features/builtin_tools.md +3 -1
- data/docs/deepdive/features/console.md +7 -0
- data/docs/deepdive/features/database.md +19 -0
- data/docs/deepdive/fundamentals/schema.md +46 -0
- data/docs/deepdive/fundamentals/tools.md +68 -0
- data/docs/deepdive/protocols/a2a.md +91 -0
- data/docs/deepdive/protocols/mcp.md +50 -0
- data/docs/deepdive/reference/model_registry.md +1 -1
- data/lib/llm/active_record/acts_as_llm.rb +20 -2
- data/lib/llm/function/async/reactor.rb +10 -0
- data/lib/llm/function/async/task.rb +107 -12
- data/lib/llm/function/fiber/task.rb +17 -3
- data/lib/llm/function/fork/job.rb +32 -6
- data/lib/llm/function/ractor/job.rb +33 -9
- data/lib/llm/function/sequential/group.rb +9 -4
- data/lib/llm/function/task.rb +3 -2
- data/lib/llm/function/thread/task.rb +30 -3
- data/lib/llm/function.rb +157 -8
- data/lib/llm/schema/leaf.rb +4 -2
- data/lib/llm/sequel/plugin.rb +6 -1
- data/lib/llm/tool/param.rb +8 -8
- data/lib/llm/version.rb +1 -1
- data/llm.gemspec +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a8bd3b4f303036ba390981de3ec63ea0bb523cc653c4b77b221e4591e9b9a446
|
|
4
|
+
data.tar.gz: 3036ce3c4248e3ddad524657f31ea2c300f98282ad0aa68791848f56cd1b91f5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 604bafd36e8a55405e3fc487957ae97b9e4ffd67777711c071f7bfb9cfe005e0400294c089e72fbec3019538f4706c92cfe5bb15f3d0c2f994a324b9ba1a3322
|
|
7
|
+
data.tar.gz: d3bf4d7e1f9337cd7bdedb40ac4cfb4f95b6195e6df3360d428bfa6c220994edfaf8c5f053256e968c1feb9ae025b29854586dd9834dc84948729644f7a7b8ff
|
data/CHANGELOG.md
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
| Old | New |
|
|
23
23
|
|-----|-----|
|
|
24
24
|
| Ruby 3.3 or later | Ruby 3.4 or later |
|
|
25
|
+
| `record.messages` returns the messages the runtime holds | a `:jsonb` record returns a relation of `LLM::ActiveRecord::Message` rows |
|
|
25
26
|
|
|
26
27
|
* **drop Ruby 3.3 support** <br>
|
|
27
28
|
The gem now requires Ruby 3.4 or later: `required_ruby_version` is
|
|
@@ -30,6 +31,21 @@
|
|
|
30
31
|
cancel that arrives before the tool starts is not delivered there, while
|
|
31
32
|
Ruby 3.4 and 4.0 both deliver it.
|
|
32
33
|
|
|
34
|
+
* **activerecord: read a `:jsonb` record's messages from the column** <br>
|
|
35
|
+
[`LLM::ActiveRecord`](https://r.uby.dev/api-docs/llm.rb/LLM/ActiveRecord.html)
|
|
36
|
+
now answers `#messages` for a record whose `format` is `:jsonb` with an
|
|
37
|
+
[`ActiveRecord::Relation`](https://api.rubyonrails.org/classes/ActiveRecord/Relation.html)
|
|
38
|
+
over the column, through
|
|
39
|
+
[`LLM::ActiveRecord::Message`](https://r.uby.dev/api-docs/llm.rb/LLM/ActiveRecord/Message.html),
|
|
40
|
+
so a conversation can be filtered, counted, and ordered in SQL, and reading it
|
|
41
|
+
needs no provider and no credentials. Before this, every record loaded the
|
|
42
|
+
runtime and returned the messages it held, whatever the format. The relation
|
|
43
|
+
is not a drop-in replacement for that buffer: it reads what is persisted
|
|
44
|
+
rather than what a runtime holds in memory, it is unordered, so order by
|
|
45
|
+
`position`, and its rows answer `unwrap!` rather than being
|
|
46
|
+
[`LLM::Message`](https://r.uby.dev/api-docs/llm.rb/LLM/Message.html) objects.
|
|
47
|
+
Every other format, and Sequel, still loads the runtime.
|
|
48
|
+
|
|
33
49
|
### Console
|
|
34
50
|
|
|
35
51
|
* **console: erase with the backspace key on OpenBSD** <br>
|
|
@@ -39,18 +55,25 @@
|
|
|
39
55
|
terminal's terminfo describes the key. All three erase. Before this only 127
|
|
40
56
|
did, so the backspace key did nothing on the OpenBSD console.
|
|
41
57
|
|
|
58
|
+
### Fix
|
|
59
|
+
|
|
60
|
+
* **fork: require xchan.rb `~> 0.24`** <br>
|
|
61
|
+
The `:fork` concurrency strategy now requires the `xchan.rb` gem at `~> 0.24`
|
|
62
|
+
instead of `~> 0.23`, and the gemspec's development dependency follows. An
|
|
63
|
+
application that uses `:fork` needs `xchan.rb` 0.24 or later.
|
|
64
|
+
|
|
42
65
|
### Function
|
|
43
66
|
|
|
44
67
|
* **function: deliver an interrupt to the tool, not to whatever is running** <br>
|
|
45
68
|
[`LLM::Function::Window`](https://r.uby.dev/api-docs/llm.rb/LLM/Function/Window.html)
|
|
46
|
-
is the stretch of a call that an interrupt belongs to. The `:fork` and
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
the
|
|
53
|
-
got to.
|
|
69
|
+
is the stretch of a call that an interrupt belongs to. The `:fork` and `:ractor`
|
|
70
|
+
strategies now raise
|
|
71
|
+
[`LLM::Interrupt`](https://r.uby.dev/api-docs/llm.rb/LLM/Interrupt.html) on the
|
|
72
|
+
thread that runs the tool, and only while the tool runs: a cancel that arrives
|
|
73
|
+
before the tool starts is held until it does, so the tool's own `rescue` can
|
|
74
|
+
handle it, and one that arrives after the tool has returned is a no-op, which
|
|
75
|
+
no longer throws the result away. Before this, the interrupt was raised on the
|
|
76
|
+
child process's or the ractor's main thread wherever that thread had got to.
|
|
54
77
|
|
|
55
78
|
* **function: answer a ractor-backed task once its ractor has gone** <br>
|
|
56
79
|
A `:ractor` tool's result is handed to a ractor the task holds it in,
|
|
@@ -68,6 +91,30 @@
|
|
|
68
91
|
error, and a group's cancel stopped at the first task that had already
|
|
69
92
|
returned, leaving the calls after it uncancelled.
|
|
70
93
|
|
|
94
|
+
* **function: tell an `:async` tool it was cancelled** <br>
|
|
95
|
+
A cancel now raises `LLM::Interrupt` on the fiber the tool runs in, through the
|
|
96
|
+
reactor's scheduler, so the tool is told and can answer with its own value. A
|
|
97
|
+
cancel that arrives before the tool started stops it from running, and the
|
|
98
|
+
reactor is stopped by whoever waits. Before this, the cancel pushed an
|
|
99
|
+
interrupt sentinel to the task's result queue and left the tool running, its
|
|
100
|
+
side effects happening and its result written to a queue nobody read, and the
|
|
101
|
+
reactor thread stayed alive until a group's `wait` stopped it.
|
|
102
|
+
|
|
103
|
+
* **function: run the interrupt hook on every strategy** <br>
|
|
104
|
+
A tool that implements
|
|
105
|
+
[`LLM::Tool#on_interrupt`](https://r.uby.dev/api-docs/llm.rb/LLM/Tool.html#on_interrupt-instance_method)
|
|
106
|
+
or
|
|
107
|
+
[`LLM::Tool#on_cancel`](https://r.uby.dev/api-docs/llm.rb/LLM/Tool.html#on_cancel-instance_method)
|
|
108
|
+
is now told on every concurrency strategy. `:thread`, `:fiber`, and `:async`
|
|
109
|
+
run the hook on the thread or fiber that ran the call, `:fork` and `:ractor`
|
|
110
|
+
run it inside the child or the ractor before the interrupt is delivered, and
|
|
111
|
+
a sequential group's cancel tells every task it holds. Before this, `:async`,
|
|
112
|
+
`:fork`, and `:ractor` never ran the hook, the others ran it on the thread
|
|
113
|
+
that cancelled, and a class-backed tool ran it on no strategy at all, because
|
|
114
|
+
`interrupt!` read the hook off the tool's definition and a class answers
|
|
115
|
+
`respond_to?` with false for an instance method. A hook that raises becomes
|
|
116
|
+
what the caller sees in place of the call's result.
|
|
117
|
+
|
|
71
118
|
### Provider
|
|
72
119
|
|
|
73
120
|
* **deepseek: support image attachments in chat completions** <br>
|
|
@@ -88,13 +135,26 @@
|
|
|
88
135
|
`qwen3.5-flash`, `qwen3.7-flash`, and `qwen3.8-omni-flash`, and gives
|
|
89
136
|
`qwen3.7-plus` attachments, structured output, and a lower price; OpenAI adds
|
|
90
137
|
`gpt-daybreak-blue-latest` and `gpt-daybreak-red-latest`; Bedrock adds
|
|
91
|
-
`openai.gpt-6-sol
|
|
92
|
-
`tencent/Hy4-preview`
|
|
138
|
+
`openai.gpt-6-sol`, `openai.gpt-6-luna`, and
|
|
139
|
+
`global.anthropic.claude-sonnet-5-5`; and DeepInfra adds `tencent/Hy4-preview`
|
|
140
|
+
and the Xiaomi MiMo V2.6 Pro and V2.6 Flash models.
|
|
93
141
|
Bedrock also corrects the context and output limits of fifteen models, Google
|
|
94
142
|
lowers the limits of `gemini-2.5-computer-use-preview-10-2025` and
|
|
95
|
-
`gemini-3-pro-image`, and OpenRouter adds
|
|
143
|
+
`gemini-3-pro-image`, and OpenRouter adds five models, drops six, and reprices
|
|
96
144
|
many of the DeepSeek, Z.ai, and Qwen entries.
|
|
97
145
|
|
|
146
|
+
### Schema
|
|
147
|
+
|
|
148
|
+
* **schema: apply every `parameter` option to the leaf** <br>
|
|
149
|
+
[`LLM::Tool`](https://r.uby.dev/api-docs/llm.rb/LLM/Tool.html) now applies each
|
|
150
|
+
option given to `parameter` to the leaf its type resolves to, so settings such
|
|
151
|
+
as `min:`, `max:`, `multiple_of:`, and `const:` reach the schema instead of
|
|
152
|
+
being dropped. Before this only `required`, `default`, and `enum` were read, so
|
|
153
|
+
`parameter :age, Integer, "Age", min: 1, max: 9` sent a plain integer and lost
|
|
154
|
+
the range. A `default:` of `false` survives now as well, where the old code
|
|
155
|
+
discarded a false default, and `required: false` marks the parameter optional
|
|
156
|
+
rather than being ignored.
|
|
157
|
+
|
|
98
158
|
## v15.5.0
|
|
99
159
|
|
|
100
160
|
Changes since `v15.4.1`.
|
data/README.md
CHANGED
|
@@ -18,16 +18,21 @@ llm.rb is a runtime for building agentic AI applications
|
|
|
18
18
|
on CRuby. It has zero runtime dependencies by default, supports
|
|
19
19
|
concurrent and parallel tool execution and has a single coherent API
|
|
20
20
|
that spans 14+ providers.
|
|
21
|
+
The README covers the common cases. For everything else there is the
|
|
22
|
+
[deepdive](docs/deepdive.md), a reference with a chapter for each
|
|
23
|
+
topic, and for what changes between releases there is the
|
|
24
|
+
[changelog](CHANGELOG.md).
|
|
21
25
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
**Still in early development.**
|
|
26
|
+
[The r.uby.dev website](https://r.uby.dev) hosts
|
|
27
|
+
an agentic platform that provides users with
|
|
28
|
+
personalized agents who can access GitHub, and
|
|
29
|
+
other services. It is built with llm.rb. Check it
|
|
30
|
+
out if curious. **Still in early development.**
|
|
28
31
|
|
|
29
32
|
## Install
|
|
30
33
|
|
|
34
|
+
llm.rb requires Ruby 3.4 or later.
|
|
35
|
+
|
|
31
36
|
```bash
|
|
32
37
|
gem install llm.rb
|
|
33
38
|
```
|
data/data/alibaba.json
CHANGED
|
@@ -90,7 +90,8 @@
|
|
|
90
90
|
"output": 0.4,
|
|
91
91
|
"cache_read": 0.01,
|
|
92
92
|
"cache_write": 0.125
|
|
93
|
-
}
|
|
93
|
+
},
|
|
94
|
+
"canonical_model_id": "alibaba/qwen3.5-flash"
|
|
94
95
|
},
|
|
95
96
|
"qwen3.7-max": {
|
|
96
97
|
"id": "qwen3.7-max",
|
|
@@ -349,7 +350,8 @@
|
|
|
349
350
|
"input": 0.2,
|
|
350
351
|
"output": 0.4,
|
|
351
352
|
"cache_read": 0.04
|
|
352
|
-
}
|
|
353
|
+
},
|
|
354
|
+
"canonical_model_id": "deepseek/deepseek-v4-flash-0731"
|
|
353
355
|
},
|
|
354
356
|
"qwen-max": {
|
|
355
357
|
"id": "qwen-max",
|
|
@@ -470,7 +472,8 @@
|
|
|
470
472
|
"output": 6,
|
|
471
473
|
"cache_read": 0.25,
|
|
472
474
|
"cache_write": 2.5
|
|
473
|
-
}
|
|
475
|
+
},
|
|
476
|
+
"canonical_model_id": "alibaba/qwen3.8-max"
|
|
474
477
|
},
|
|
475
478
|
"kimi-k3": {
|
|
476
479
|
"id": "kimi-k3",
|
|
@@ -515,7 +518,8 @@
|
|
|
515
518
|
"input": 3,
|
|
516
519
|
"output": 15,
|
|
517
520
|
"cache_read": 0.3
|
|
518
|
-
}
|
|
521
|
+
},
|
|
522
|
+
"canonical_model_id": "moonshotai/kimi-k3"
|
|
519
523
|
},
|
|
520
524
|
"qwen-plus-character-ja": {
|
|
521
525
|
"id": "qwen-plus-character-ja",
|
|
@@ -799,7 +803,8 @@
|
|
|
799
803
|
}
|
|
800
804
|
}
|
|
801
805
|
]
|
|
802
|
-
}
|
|
806
|
+
},
|
|
807
|
+
"canonical_model_id": "alibaba/qwen3.7-flash"
|
|
803
808
|
},
|
|
804
809
|
"qwen3-max": {
|
|
805
810
|
"id": "qwen3-max",
|
|
@@ -1046,7 +1051,8 @@
|
|
|
1046
1051
|
"input": 0.15,
|
|
1047
1052
|
"output": 0.47,
|
|
1048
1053
|
"cache_read": 0.016
|
|
1049
|
-
}
|
|
1054
|
+
},
|
|
1055
|
+
"canonical_model_id": "alibaba/qwen3.8-omni-flash"
|
|
1050
1056
|
},
|
|
1051
1057
|
"qvq-max": {
|
|
1052
1058
|
"id": "qvq-max",
|
|
@@ -1685,7 +1691,8 @@
|
|
|
1685
1691
|
"output": 0.47,
|
|
1686
1692
|
"cache_read": 0.016,
|
|
1687
1693
|
"cache_write": 0.2
|
|
1688
|
-
}
|
|
1694
|
+
},
|
|
1695
|
+
"canonical_model_id": "alibaba/qwen3.8-flash"
|
|
1689
1696
|
},
|
|
1690
1697
|
"qwen2-5-vl-7b-instruct": {
|
|
1691
1698
|
"id": "qwen2-5-vl-7b-instruct",
|
|
@@ -1956,7 +1963,8 @@
|
|
|
1956
1963
|
"output": 4.4,
|
|
1957
1964
|
"cache_read": 0.28,
|
|
1958
1965
|
"cache_write": 0
|
|
1959
|
-
}
|
|
1966
|
+
},
|
|
1967
|
+
"canonical_model_id": "zhipuai/glm-5.2"
|
|
1960
1968
|
},
|
|
1961
1969
|
"qwen-omni-turbo": {
|
|
1962
1970
|
"id": "qwen-omni-turbo",
|
data/data/anthropic.json
CHANGED
|
@@ -46,7 +46,8 @@
|
|
|
46
46
|
"output": 5,
|
|
47
47
|
"cache_read": 0.1,
|
|
48
48
|
"cache_write": 1.25
|
|
49
|
-
}
|
|
49
|
+
},
|
|
50
|
+
"canonical_model_id": "anthropic/claude-haiku-4-5"
|
|
50
51
|
},
|
|
51
52
|
"claude-opus-4-5": {
|
|
52
53
|
"id": "claude-opus-4-5",
|
|
@@ -95,7 +96,8 @@
|
|
|
95
96
|
"output": 25,
|
|
96
97
|
"cache_read": 0.5,
|
|
97
98
|
"cache_write": 6.25
|
|
98
|
-
}
|
|
99
|
+
},
|
|
100
|
+
"canonical_model_id": "anthropic/claude-opus-4-5"
|
|
99
101
|
},
|
|
100
102
|
"claude-sonnet-4-5": {
|
|
101
103
|
"id": "claude-sonnet-4-5",
|
|
@@ -136,7 +138,8 @@
|
|
|
136
138
|
"output": 15,
|
|
137
139
|
"cache_read": 0.3,
|
|
138
140
|
"cache_write": 3.75
|
|
139
|
-
}
|
|
141
|
+
},
|
|
142
|
+
"canonical_model_id": "anthropic/claude-sonnet-4-5"
|
|
140
143
|
},
|
|
141
144
|
"claude-opus-5-5": {
|
|
142
145
|
"id": "claude-opus-5-5",
|
|
@@ -203,7 +206,8 @@
|
|
|
203
206
|
"output": 20,
|
|
204
207
|
"cache_read": 0.2,
|
|
205
208
|
"cache_write": 5
|
|
206
|
-
}
|
|
209
|
+
},
|
|
210
|
+
"canonical_model_id": "anthropic/claude-opus-5-5"
|
|
207
211
|
},
|
|
208
212
|
"claude-fable-5-1": {
|
|
209
213
|
"id": "claude-fable-5-1",
|
|
@@ -250,7 +254,8 @@
|
|
|
250
254
|
"output": 50,
|
|
251
255
|
"cache_read": 0.25,
|
|
252
256
|
"cache_write": 12.5
|
|
253
|
-
}
|
|
257
|
+
},
|
|
258
|
+
"canonical_model_id": "anthropic/claude-fable-5-1"
|
|
254
259
|
},
|
|
255
260
|
"claude-opus-4-5-20251101": {
|
|
256
261
|
"id": "claude-opus-4-5-20251101",
|
|
@@ -366,7 +371,8 @@
|
|
|
366
371
|
"output": 25,
|
|
367
372
|
"cache_read": 0.5,
|
|
368
373
|
"cache_write": 6.25
|
|
369
|
-
}
|
|
374
|
+
},
|
|
375
|
+
"canonical_model_id": "anthropic/claude-opus-5"
|
|
370
376
|
},
|
|
371
377
|
"claude-fable-5": {
|
|
372
378
|
"id": "claude-fable-5",
|
|
@@ -713,6 +719,54 @@
|
|
|
713
719
|
"cache_write": 3.75
|
|
714
720
|
}
|
|
715
721
|
},
|
|
722
|
+
"claude-sonnet-5-5": {
|
|
723
|
+
"id": "claude-sonnet-5-5",
|
|
724
|
+
"name": "Claude Sonnet 5.5",
|
|
725
|
+
"description": "Fast Claude model for everyday coding, agents, and knowledge work",
|
|
726
|
+
"family": "claude-sonnet",
|
|
727
|
+
"attachment": true,
|
|
728
|
+
"reasoning": true,
|
|
729
|
+
"reasoning_options": [
|
|
730
|
+
{
|
|
731
|
+
"type": "effort",
|
|
732
|
+
"values": [
|
|
733
|
+
"low",
|
|
734
|
+
"medium",
|
|
735
|
+
"high",
|
|
736
|
+
"xhigh",
|
|
737
|
+
"max"
|
|
738
|
+
]
|
|
739
|
+
}
|
|
740
|
+
],
|
|
741
|
+
"tool_call": true,
|
|
742
|
+
"structured_output": true,
|
|
743
|
+
"temperature": false,
|
|
744
|
+
"knowledge": "2026-06",
|
|
745
|
+
"release_date": "2026-09-28",
|
|
746
|
+
"last_updated": "2026-09-28",
|
|
747
|
+
"modalities": {
|
|
748
|
+
"input": [
|
|
749
|
+
"text",
|
|
750
|
+
"image",
|
|
751
|
+
"pdf"
|
|
752
|
+
],
|
|
753
|
+
"output": [
|
|
754
|
+
"text"
|
|
755
|
+
]
|
|
756
|
+
},
|
|
757
|
+
"open_weights": false,
|
|
758
|
+
"limit": {
|
|
759
|
+
"context": 1000000,
|
|
760
|
+
"output": 128000
|
|
761
|
+
},
|
|
762
|
+
"cost": {
|
|
763
|
+
"input": 2,
|
|
764
|
+
"output": 10,
|
|
765
|
+
"cache_read": 0.2,
|
|
766
|
+
"cache_write": 2.5
|
|
767
|
+
},
|
|
768
|
+
"canonical_model_id": "anthropic/claude-sonnet-5-5"
|
|
769
|
+
},
|
|
716
770
|
"claude-opus-4-7": {
|
|
717
771
|
"id": "claude-opus-4-7",
|
|
718
772
|
"name": "Claude Opus 4.7",
|