llm.rb 15.0.1 → 15.0.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 +33 -0
- data/README.md +14 -50
- data/lib/llm/active_record.rb +1 -1
- data/lib/llm/mcp.rb +31 -6
- data/lib/llm/version.rb +1 -1
- data/llm.gemspec +2 -2
- metadata +4 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b2700592b8b9fbb85694c24a0a7379a84494b0e4f2157d6302fad1552df9d684
|
|
4
|
+
data.tar.gz: 43864e55f008478a40860511f9ca138f86010695225f91f0b2a07fbcff1ed448
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 464e203f6e4dc1fb2ea2aaa5bd4a2f95753d569190e63e4cd590b014aed4e369fa2f2835a4d6d95394b6988210c880e976abd5d8e8c69f015b057bb1f193609d
|
|
7
|
+
data.tar.gz: 9303d2475e2bd32dc53b65b7b9d54fa28611616a0714015bd1c0d74e8ca7cd66e6d88889b73231ed19dda38b2f2888ded08569694f23bd834a8e7c1f3fd6db35
|
data/CHANGELOG.md
CHANGED
|
@@ -15,6 +15,39 @@
|
|
|
15
15
|
|
|
16
16
|
## What's next
|
|
17
17
|
|
|
18
|
+
## v15.0.3
|
|
19
|
+
|
|
20
|
+
Changes since `v15.0.2`.
|
|
21
|
+
|
|
22
|
+
This release fixes ActiveRecord `:json`/`:jsonb` serialization so tool
|
|
23
|
+
call arguments round-trip as JSON objects instead of arrays of pairs.
|
|
24
|
+
|
|
25
|
+
### Fix
|
|
26
|
+
|
|
27
|
+
* **activerecord: serialize tool call arguments properly** <br>
|
|
28
|
+
Fix a bug where the ActiveRecord `:json`/`:jsonb` layer serialized tool
|
|
29
|
+
call arguments as an array of pairs instead of a Hash. The context is
|
|
30
|
+
now serialized through its JSON form, so tool call arguments round-trip
|
|
31
|
+
as JSON objects that providers accept.
|
|
32
|
+
|
|
33
|
+
## v15.0.2
|
|
34
|
+
|
|
35
|
+
Changes since `v15.0.1`.
|
|
36
|
+
|
|
37
|
+
This release fixes a concurrency race in MCP tool calls by
|
|
38
|
+
reference-counting the transport session, so overlapping tool calls
|
|
39
|
+
reuse the running transport instead of racing `start`/`stop`.
|
|
40
|
+
|
|
41
|
+
### Fix
|
|
42
|
+
|
|
43
|
+
* **mcp: fix concurrent MCP tool call race** <br>
|
|
44
|
+
`LLM::MCP` now reference-counts its transport session: the first
|
|
45
|
+
caller starts the transport and the last caller stops it. Concurrent
|
|
46
|
+
or overlapping tool calls reuse the running transport instead of
|
|
47
|
+
racing `start`/`stop`, avoiding "MCP transport is not running" errors
|
|
48
|
+
that could occur with the `async` strategy. An externally started
|
|
49
|
+
transport is never stopped by a borrower.
|
|
50
|
+
|
|
18
51
|
## v15.0.1
|
|
19
52
|
|
|
20
53
|
Changes since `v15.0.0`.
|
data/README.md
CHANGED
|
@@ -15,15 +15,16 @@
|
|
|
15
15
|
Welcome to the canonical llm.rb repository.
|
|
16
16
|
|
|
17
17
|
llm.rb is an advanced runtime for building agentic AI applications
|
|
18
|
-
on CRuby. It has zero runtime dependencies by default,
|
|
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 13+ providers. Streaming, tools, guards, compaction, the
|
|
21
21
|
REPL, builtin MCP/A2A support and the database integrations all build
|
|
22
22
|
on the same three concepts: providers, contexts, and agents.
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
The most effective way to learn about llm.rb is to ask [the r.uby.dev chatbot](https://r.uby.dev)
|
|
25
|
+
a question. It is connected to the llm.rb GitHub repository, backed by
|
|
26
|
+
ActiveRecord and uses the builtin MCP feature to connect to GitHub. All
|
|
27
|
+
answers are grounded in the llm.rb source code.
|
|
27
28
|
|
|
28
29
|
## Install
|
|
29
30
|
|
|
@@ -39,8 +40,7 @@ The
|
|
|
39
40
|
[`LLM::Agent`](https://r.uby.dev/api-docs/llm.rb/LLM/Agent.html)
|
|
40
41
|
class is the default high-level interface,
|
|
41
42
|
and it is recommended for most use-cases. It manages tool execution
|
|
42
|
-
automatically and
|
|
43
|
-
[guards against infinite loops](https://r.uby.dev/llm/deepdive/advanced/guard),
|
|
43
|
+
automatically and guards against infinite loops,
|
|
44
44
|
manages conversation state, and much more.
|
|
45
45
|
|
|
46
46
|
```ruby
|
|
@@ -61,9 +61,6 @@ reasoning, tool calls, tool returns, and compaction.
|
|
|
61
61
|
Streams can also observe message transformers, which rewrite
|
|
62
62
|
outgoing messages before they reach the provider.
|
|
63
63
|
|
|
64
|
-
See the [deepdive.md](https://r.uby.dev/llm/deepdive/fundamentals/stream/)
|
|
65
|
-
to learn more.
|
|
66
|
-
|
|
67
64
|
```ruby
|
|
68
65
|
class MyStream < LLM::Stream
|
|
69
66
|
# Visible assistant output.
|
|
@@ -133,7 +130,7 @@ call them on your behalf, and they're one of the most powerful features
|
|
|
133
130
|
for extending the feature set or abilities of a model.
|
|
134
131
|
|
|
135
132
|
The runtime also ships with a catalog of built-in tools for
|
|
136
|
-
filesystem, search, and shell operations.
|
|
133
|
+
filesystem, search, and shell operations.
|
|
137
134
|
|
|
138
135
|
```ruby
|
|
139
136
|
class ReadFile < LLM::Tool
|
|
@@ -168,8 +165,6 @@ callback hands back the subagent that ran the skill, so you can inspect
|
|
|
168
165
|
its conversation, measure its usage, track costs or add a verification
|
|
169
166
|
step (eg `subagent.talk("verify your work")`).
|
|
170
167
|
|
|
171
|
-
See the [deepdive.md](https://r.uby.dev/llm/deepdive/fundamentals/skills) to learn more.
|
|
172
|
-
|
|
173
168
|
##### summary.md
|
|
174
169
|
|
|
175
170
|
```markdown
|
|
@@ -209,8 +204,6 @@ with the `:fork` and `:ractor` strategies. The
|
|
|
209
204
|
The `:fork` strategy also provides a separate process that offers
|
|
210
205
|
isolation from its parent.
|
|
211
206
|
|
|
212
|
-
See the [deepdive.md](https://r.uby.dev/llm/deepdive/features/concurrency) to learn more.
|
|
213
|
-
|
|
214
207
|
```ruby
|
|
215
208
|
require "llm"
|
|
216
209
|
require "llm/tools"
|
|
@@ -235,8 +228,6 @@ the control channel, a ractor via message passing, and pending tools
|
|
|
235
228
|
are stopped before they run. The in-flight HTTP request is closed
|
|
236
229
|
too, so a turn you no longer want stops without burning tokens.
|
|
237
230
|
|
|
238
|
-
See the [deepdive.md](https://r.uby.dev/llm/deepdive/advanced/cancellation) to learn more.
|
|
239
|
-
|
|
240
231
|
```ruby
|
|
241
232
|
llm = LLM.deepseek(key: ENV["KEY"])
|
|
242
233
|
agent = LLM::Agent.new(llm)
|
|
@@ -261,8 +252,6 @@ The REPL stands out because it connects to the surrounding
|
|
|
261
252
|
runtime and it can be extended by your code. Think of it as
|
|
262
253
|
`binding.pry` but for agents.
|
|
263
254
|
|
|
264
|
-
See the [deepdive.md](https://r.uby.dev/llm/deepdive/features/repl) to learn more.
|
|
265
|
-
|
|
266
255
|
##### Demo
|
|
267
256
|
|
|
268
257
|
[Watch in high quality on asciinema](https://asciinema.org/a/OsS8wwaasKasoDDz)
|
|
@@ -320,8 +309,6 @@ code. For database-backed persistence, ActiveRecord and Sequel
|
|
|
320
309
|
integrations are also available. All persistence options use the same
|
|
321
310
|
underlying serialization.
|
|
322
311
|
|
|
323
|
-
See the [deepdive.md](https://r.uby.dev/llm/deepdive/features/database) to learn more.
|
|
324
|
-
|
|
325
312
|
```ruby
|
|
326
313
|
require "llm"
|
|
327
314
|
|
|
@@ -348,8 +335,6 @@ for both Rack-based / Rails-based applications. On databases
|
|
|
348
335
|
where it is supported, such as PostgreSQL, the column can be optimized by using
|
|
349
336
|
the `jsonb` type.
|
|
350
337
|
|
|
351
|
-
See the [deepdive.md](https://r.uby.dev/llm/deepdive/features/database) to learn more.
|
|
352
|
-
|
|
353
338
|
```ruby
|
|
354
339
|
require "active_record"
|
|
355
340
|
require "llm"
|
|
@@ -411,9 +396,6 @@ used with
|
|
|
411
396
|
[`LLM::Context`](https://r.uby.dev/api-docs/llm.rb/LLM/Context.html) or
|
|
412
397
|
[`LLM::Agent`](https://r.uby.dev/api-docs/llm.rb/LLM/Agent.html).
|
|
413
398
|
|
|
414
|
-
See the [deepdive.md](https://r.uby.dev/llm/deepdive/protocols/mcp/), and the
|
|
415
|
-
[deepdive.md on persistent connections](https://r.uby.dev/llm/deepdive/features/transports) to learn more.
|
|
416
|
-
|
|
417
399
|
```ruby
|
|
418
400
|
require "llm"
|
|
419
401
|
|
|
@@ -434,9 +416,6 @@ used with
|
|
|
434
416
|
[`LLM::Context`](https://r.uby.dev/api-docs/llm.rb/LLM/Context.html) or
|
|
435
417
|
[`LLM::Agent`](https://r.uby.dev/api-docs/llm.rb/LLM/Agent.html).
|
|
436
418
|
|
|
437
|
-
See the [deepdive.md](https://r.uby.dev/llm/deepdive/protocols/a2a/), and the
|
|
438
|
-
[deepdive.md on persistent connections](https://r.uby.dev/llm/deepdive/features/transports) to learn more.
|
|
439
|
-
|
|
440
419
|
```ruby
|
|
441
420
|
require "llm"
|
|
442
421
|
|
|
@@ -509,8 +488,6 @@ and implement
|
|
|
509
488
|
The pending call arrives as `function:`. Return a value to close
|
|
510
489
|
the call, or `nil` to let it run:
|
|
511
490
|
|
|
512
|
-
See the [deepdive.md](https://r.uby.dev/llm/deepdive/advanced/guard) to learn more.
|
|
513
|
-
|
|
514
491
|
```ruby
|
|
515
492
|
class PolicyGuard < LLM::Guard
|
|
516
493
|
def call(function:)
|
|
@@ -536,8 +513,6 @@ Create a subclass and implement `call(message:)` to scrub sensitive data,
|
|
|
536
513
|
inject context, or normalize content. The transform runs automatically
|
|
537
514
|
on every turn, so you never have to change your prompt code.
|
|
538
515
|
|
|
539
|
-
See the [deepdive.md](https://r.uby.dev/llm/deepdive/advanced/transformer) to learn more.
|
|
540
|
-
|
|
541
516
|
```ruby
|
|
542
517
|
class RedactEmails < LLM::Transformer
|
|
543
518
|
def call(message:)
|
|
@@ -574,8 +549,6 @@ and
|
|
|
574
549
|
[`LLM::Stream#on_compaction_finish`](https://r.uby.dev/api-docs/llm.rb/LLM/Stream.html#on_compaction_finish)
|
|
575
550
|
callbacks.
|
|
576
551
|
|
|
577
|
-
See the [deepdive.md](https://r.uby.dev/llm/deepdive/advanced/compaction) to learn more.
|
|
578
|
-
|
|
579
552
|
```ruby
|
|
580
553
|
llm = LLM.deepseek(key: ENV["KEY"])
|
|
581
554
|
agent = LLM::Agent.new(
|
|
@@ -624,8 +597,6 @@ exports spans via OTLP for OpenTelemetry in production.
|
|
|
624
597
|
* [`LLM::Tracer::Logger`](https://r.uby.dev/api-docs/llm.rb/LLM/Tracer/Logger.html):
|
|
625
598
|
structured JSON to stdout or a file.
|
|
626
599
|
|
|
627
|
-
See the [deepdive.md](https://r.uby.dev/llm/deepdive/reference/tracer) to learn more.
|
|
628
|
-
|
|
629
600
|
```ruby
|
|
630
601
|
llm = LLM.deepseek(key: ENV["KEY"])
|
|
631
602
|
agent = LLM::Agent.new(llm, tracer: LLM::Tracer::PrettyLogger.new(llm))
|
|
@@ -669,9 +640,8 @@ Each provider is constructed with a class-level factory method on
|
|
|
669
640
|
[`LLM::Context`](https://r.uby.dev/api-docs/llm.rb/LLM/Context.html)
|
|
670
641
|
or
|
|
671
642
|
[`LLM::Agent`](https://r.uby.dev/api-docs/llm.rb/LLM/Agent.html). The
|
|
672
|
-
same API drives every one of them, so switching
|
|
673
|
-
change.
|
|
674
|
-
for a full provider reference.
|
|
643
|
+
same API drives every one of them, so switching providers is a one-line
|
|
644
|
+
change.
|
|
675
645
|
|
|
676
646
|
#### What providers does llm.rb support?
|
|
677
647
|
|
|
@@ -734,8 +704,6 @@ modalities with the gem, sourced from [models.dev](https://models.dev).
|
|
|
734
704
|
Reach it from any provider, context, or agent, enumerate models, or
|
|
735
705
|
sort them by price.
|
|
736
706
|
|
|
737
|
-
See the [deepdive.md](https://r.uby.dev/llm/deepdive/reference/model_registry) to learn more.
|
|
738
|
-
|
|
739
707
|
```ruby
|
|
740
708
|
require "llm"
|
|
741
709
|
|
|
@@ -758,8 +726,6 @@ is always available and the default, `net/http/persistent` pools
|
|
|
758
726
|
connections for many requests to the same host, and `curb` wraps
|
|
759
727
|
libcurl. They share one interface, so switching is a one-word change.
|
|
760
728
|
|
|
761
|
-
See the [deepdive.md](https://r.uby.dev/llm/deepdive/advanced/transports) to learn more.
|
|
762
|
-
|
|
763
729
|
```ruby
|
|
764
730
|
llm = LLM.deepseek(
|
|
765
731
|
key: ENV["KEY"],
|
|
@@ -781,8 +747,6 @@ llm.rb also includes support for OpenAI's vector store API. It
|
|
|
781
747
|
provides a vector database as a HTTP service but we won't cover
|
|
782
748
|
that here.
|
|
783
749
|
|
|
784
|
-
See the [deepdive.md](https://r.uby.dev/llm/deepdive/features/embeddings) to learn more.
|
|
785
|
-
|
|
786
750
|
```ruby
|
|
787
751
|
require "llm"
|
|
788
752
|
|
|
@@ -921,11 +885,11 @@ it as my primary driver for development.
|
|
|
921
885
|
|
|
922
886
|
## Resources
|
|
923
887
|
|
|
924
|
-
|
|
925
|
-
to
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
888
|
+
The [r.uby.dev chatbot](https://r.uby.dev) is connected
|
|
889
|
+
to this very GitHub repository. It can read documentation,
|
|
890
|
+
source code, issues, and pull requests. The [docs/](docs/)
|
|
891
|
+
directory contains the full documentation and the chatbot
|
|
892
|
+
can find the answers to your questions there.
|
|
929
893
|
|
|
930
894
|
## License
|
|
931
895
|
|
data/lib/llm/active_record.rb
CHANGED
|
@@ -39,7 +39,7 @@ module LLM::ActiveRecord
|
|
|
39
39
|
def self.serialize_context(ctx, format)
|
|
40
40
|
case format
|
|
41
41
|
when :string then ctx.to_json
|
|
42
|
-
when :json, :jsonb then ctx.
|
|
42
|
+
when :json, :jsonb then LLM.json.load(ctx.to_json)
|
|
43
43
|
else raise ArgumentError, "Unknown format: #{format.inspect}"
|
|
44
44
|
end
|
|
45
45
|
end
|
data/lib/llm/mcp.rb
CHANGED
|
@@ -87,7 +87,10 @@ class LLM::MCP
|
|
|
87
87
|
# @return [LLM::MCP] A new MCP instance
|
|
88
88
|
def initialize(stdio: nil, http: nil, timeout: 30)
|
|
89
89
|
@timeout = timeout
|
|
90
|
-
|
|
90
|
+
@lock = Mutex.new
|
|
91
|
+
@borrowers = 0
|
|
92
|
+
@owned = false
|
|
93
|
+
if stdio and http
|
|
91
94
|
raise ArgumentError, "stdio and http are mutually exclusive"
|
|
92
95
|
elsif stdio
|
|
93
96
|
@command = Command.new(**stdio)
|
|
@@ -125,7 +128,10 @@ class LLM::MCP
|
|
|
125
128
|
# Propagates errors raised by {#start}, the block itself, or {#stop}
|
|
126
129
|
# @return [void]
|
|
127
130
|
def run
|
|
128
|
-
|
|
131
|
+
@lock.synchronize do
|
|
132
|
+
start
|
|
133
|
+
@owned = false
|
|
134
|
+
end
|
|
129
135
|
yield
|
|
130
136
|
ensure
|
|
131
137
|
stop
|
|
@@ -182,13 +188,32 @@ class LLM::MCP
|
|
|
182
188
|
|
|
183
189
|
attr_reader :command, :transport, :timeout
|
|
184
190
|
|
|
191
|
+
##
|
|
192
|
+
# Borrows the transport for the duration of the block.
|
|
193
|
+
#
|
|
194
|
+
# The first borrower starts the transport, concurrent borrowers reuse
|
|
195
|
+
# it, and the last borrower stops it, so overlapping tool calls never
|
|
196
|
+
# race `start`/`stop` and trip over "MCP transport is not running".
|
|
197
|
+
# An externally started transport is never stopped by a borrower.
|
|
198
|
+
# @yield Runs while the transport is running
|
|
199
|
+
# @return [void]
|
|
185
200
|
def with_session
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
201
|
+
@lock.synchronize do
|
|
202
|
+
@borrowers += 1
|
|
203
|
+
unless transport.running?
|
|
204
|
+
start
|
|
205
|
+
@owned = true
|
|
206
|
+
end
|
|
207
|
+
end
|
|
189
208
|
yield
|
|
190
209
|
ensure
|
|
191
|
-
|
|
210
|
+
@lock.synchronize do
|
|
211
|
+
@borrowers -= 1
|
|
212
|
+
if @borrowers.zero? and @owned
|
|
213
|
+
@owned = false
|
|
214
|
+
stop
|
|
215
|
+
end
|
|
216
|
+
end
|
|
192
217
|
end
|
|
193
218
|
|
|
194
219
|
def adapt_content(content)
|
data/lib/llm/version.rb
CHANGED
data/llm.gemspec
CHANGED
|
@@ -40,13 +40,13 @@ DESCRIPTION
|
|
|
40
40
|
spec.post_install_message = "\n" \
|
|
41
41
|
"Got a question about llm.rb? " \
|
|
42
42
|
"\n" \
|
|
43
|
-
"Ask
|
|
43
|
+
"Ask the https://r.uby.dev chatbot." \
|
|
44
44
|
"\n" \
|
|
45
45
|
"It is connected to the official GitHub repository." \
|
|
46
46
|
"\n" \
|
|
47
47
|
"100% free to use." \
|
|
48
48
|
"\n" \
|
|
49
|
-
"
|
|
49
|
+
"Built with llm.rb and DeepSeek." \
|
|
50
50
|
"\n\n"
|
|
51
51
|
|
|
52
52
|
spec.add_development_dependency "webmock", "~> 3.24.0"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: llm.rb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 15.0.
|
|
4
|
+
version: 15.0.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Robert Gleeson
|
|
@@ -652,8 +652,9 @@ metadata:
|
|
|
652
652
|
source_code_uri: https://github.com/r-uby-dev/llm
|
|
653
653
|
documentation_uri: https://r.uby.dev
|
|
654
654
|
changelog_uri: https://github.com/r-uby-dev/llm/blob/main/CHANGELOG.md
|
|
655
|
-
post_install_message: "\nGot a question about llm.rb? \nAsk
|
|
656
|
-
to the official GitHub repository.\n100% free to use.\
|
|
655
|
+
post_install_message: "\nGot a question about llm.rb? \nAsk the https://r.uby.dev
|
|
656
|
+
chatbot.\nIt is connected to the official GitHub repository.\n100% free to use.\nBuilt
|
|
657
|
+
with llm.rb and DeepSeek.\n\n"
|
|
657
658
|
rdoc_options: []
|
|
658
659
|
require_paths:
|
|
659
660
|
- lib
|