llm.rb 10.0.0 → 11.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +158 -10
- data/README.md +145 -44
- data/lib/llm/a2a/card/capabilities.rb +41 -0
- data/lib/llm/a2a/card/interface.rb +34 -0
- data/lib/llm/a2a/card/provider.rb +27 -0
- data/lib/llm/a2a/card/skill.rb +68 -0
- data/lib/llm/a2a/card.rb +144 -0
- data/lib/llm/a2a/error.rb +49 -0
- data/lib/llm/a2a/notifications.rb +53 -0
- data/lib/llm/a2a/tasks.rb +55 -0
- data/lib/llm/a2a/transport/http.rb +131 -0
- data/lib/llm/a2a.rb +452 -0
- data/lib/llm/active_record/acts_as_agent.rb +15 -9
- data/lib/llm/active_record/acts_as_llm.rb +4 -4
- data/lib/llm/agent.rb +15 -9
- data/lib/llm/buffer.rb +1 -2
- data/lib/llm/context.rb +52 -5
- data/lib/llm/file.rb +7 -0
- data/lib/llm/function.rb +13 -5
- data/lib/llm/mcp/transport/http.rb +5 -18
- data/lib/llm/mcp/transport/stdio.rb +7 -0
- data/lib/llm/mcp.rb +20 -17
- data/lib/llm/message.rb +1 -1
- data/lib/llm/object/builder.rb +1 -0
- data/lib/llm/object/kernel.rb +1 -1
- data/lib/llm/object.rb +9 -0
- data/lib/llm/provider.rb +2 -9
- data/lib/llm/response.rb +1 -1
- data/lib/llm/schema.rb +23 -5
- data/lib/llm/sequel/agent.rb +14 -9
- data/lib/llm/sequel/plugin.rb +8 -7
- data/lib/llm/skill.rb +44 -14
- data/lib/llm/tool.rb +57 -27
- data/lib/llm/tracer/telemetry.rb +3 -1
- data/lib/llm/tracer.rb +1 -1
- data/lib/llm/transport/http.rb +1 -1
- data/lib/llm/transport/stream_decoder.rb +6 -3
- data/lib/llm/transport/utils.rb +35 -0
- data/lib/llm/transport.rb +1 -0
- data/lib/llm/utils.rb +44 -0
- data/lib/llm/version.rb +1 -1
- data/lib/llm.rb +23 -4
- data/llm.gemspec +16 -1
- metadata +26 -3
- data/lib/llm/mcp/transport/http/event_handler.rb +0 -68
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class LLM::Transport
|
|
4
|
+
##
|
|
5
|
+
# Shared utility methods for HTTP-backed transports.
|
|
6
|
+
#
|
|
7
|
+
# @api private
|
|
8
|
+
module Utils
|
|
9
|
+
extend self
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
def resolve_transport(uri, transport, timeout)
|
|
13
|
+
return default_transport(uri, timeout) if transport.nil?
|
|
14
|
+
if Class === transport && transport <= LLM::Transport
|
|
15
|
+
transport.new(
|
|
16
|
+
host: uri.host,
|
|
17
|
+
port: uri.port,
|
|
18
|
+
timeout:,
|
|
19
|
+
ssl: uri.scheme == "https"
|
|
20
|
+
)
|
|
21
|
+
else
|
|
22
|
+
transport
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def default_transport(uri, timeout)
|
|
27
|
+
LLM::Transport::HTTP.new(
|
|
28
|
+
host: uri.host,
|
|
29
|
+
port: uri.port,
|
|
30
|
+
timeout:,
|
|
31
|
+
ssl: uri.scheme == "https"
|
|
32
|
+
)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
data/lib/llm/transport.rb
CHANGED
|
@@ -27,6 +27,7 @@ module LLM
|
|
|
27
27
|
# transport-specific classes.
|
|
28
28
|
class Transport
|
|
29
29
|
require_relative "transport/response"
|
|
30
|
+
require_relative "transport/utils"
|
|
30
31
|
require_relative "transport/stream_decoder"
|
|
31
32
|
require_relative "transport/http"
|
|
32
33
|
require_relative "transport/persistent_http"
|
data/lib/llm/utils.rb
CHANGED
|
@@ -25,5 +25,49 @@ module LLM
|
|
|
25
25
|
else option
|
|
26
26
|
end
|
|
27
27
|
end
|
|
28
|
+
|
|
29
|
+
##
|
|
30
|
+
# Normalizes an HTTP API base path.
|
|
31
|
+
#
|
|
32
|
+
# Blank paths normalize to an empty string. Non-empty paths are
|
|
33
|
+
# prefixed with a leading slash and stripped of trailing slashes.
|
|
34
|
+
#
|
|
35
|
+
# @param [String, nil] path
|
|
36
|
+
# @return [String]
|
|
37
|
+
def normalize_base_path(path)
|
|
38
|
+
path = path.to_s.strip
|
|
39
|
+
return "" if path.empty? || path == "/"
|
|
40
|
+
path = "/#{path}" unless path.start_with?("/")
|
|
41
|
+
path.sub(%r{/+\z}, "")
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
##
|
|
45
|
+
# Returns the Ruby module or class name for an object.
|
|
46
|
+
#
|
|
47
|
+
# This bypasses overridden `#name` implementations by binding
|
|
48
|
+
# {Module#name} directly.
|
|
49
|
+
#
|
|
50
|
+
# @param [Module] obj
|
|
51
|
+
# @return [String, nil]
|
|
52
|
+
def name_of(obj)
|
|
53
|
+
::Module.instance_method(:name).bind(obj).call
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
##
|
|
57
|
+
# Renders the class-and-object-id portion of an inspect string.
|
|
58
|
+
#
|
|
59
|
+
# This returns strings like `LLM::Tool:0x1234abcd`, which can be
|
|
60
|
+
# embedded into custom inspect output.
|
|
61
|
+
#
|
|
62
|
+
# @param [Object] obj
|
|
63
|
+
# @return [String]
|
|
64
|
+
def object_id(obj)
|
|
65
|
+
klass = if Class === obj
|
|
66
|
+
name_of(obj) || name_of(obj.superclass) || obj.class.name
|
|
67
|
+
else
|
|
68
|
+
obj.class.name || obj.class.to_s
|
|
69
|
+
end
|
|
70
|
+
"#{klass}:0x#{obj.object_id.to_s(16)}"
|
|
71
|
+
end
|
|
28
72
|
end
|
|
29
73
|
end
|
data/lib/llm/version.rb
CHANGED
data/lib/llm.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module LLM
|
|
4
4
|
require "stringio"
|
|
5
|
+
require "securerandom"
|
|
5
6
|
require_relative "llm/json_adapter"
|
|
6
7
|
require_relative "llm/tracer"
|
|
7
8
|
require_relative "llm/error"
|
|
@@ -35,6 +36,7 @@ module LLM
|
|
|
35
36
|
require_relative "llm/skill"
|
|
36
37
|
require_relative "llm/server_tool"
|
|
37
38
|
require_relative "llm/mcp"
|
|
39
|
+
require_relative "llm/a2a"
|
|
38
40
|
|
|
39
41
|
##
|
|
40
42
|
# Thread-safe monitors for different contexts
|
|
@@ -179,8 +181,6 @@ module LLM
|
|
|
179
181
|
end
|
|
180
182
|
|
|
181
183
|
##
|
|
182
|
-
# @param [LLM::Provider, nil] llm
|
|
183
|
-
# The provider to use for MCP transports that need one
|
|
184
184
|
# @param [Hash, nil] stdio
|
|
185
185
|
# @option stdio [Array<String>] :argv
|
|
186
186
|
# The command to run for the MCP process
|
|
@@ -189,8 +189,27 @@ module LLM
|
|
|
189
189
|
# @option stdio [String, nil] :cwd
|
|
190
190
|
# The working directory for the MCP process
|
|
191
191
|
# @return [LLM::MCP]
|
|
192
|
-
def mcp(
|
|
193
|
-
LLM::MCP.new(
|
|
192
|
+
def mcp(**)
|
|
193
|
+
LLM::MCP.new(**)
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
##
|
|
197
|
+
# Creates a new A2A client connected to a remote agent.
|
|
198
|
+
#
|
|
199
|
+
# @param [Hash, nil] http
|
|
200
|
+
# @option http [String] :url
|
|
201
|
+
# The base URL of the A2A agent (e.g., "https://agent.example.com")
|
|
202
|
+
# @option http [Hash<String, String>] :headers
|
|
203
|
+
# Extra HTTP headers (e.g., Authorization)
|
|
204
|
+
# @option http [Integer, nil] :timeout
|
|
205
|
+
# Request timeout in seconds
|
|
206
|
+
# @option http [LLM::Transport, Class, nil] :transport
|
|
207
|
+
# Optional transport override
|
|
208
|
+
# @param [Symbol] binding
|
|
209
|
+
# The protocol binding to use. One of `:rest` or `:jsonrpc`
|
|
210
|
+
# @return [LLM::A2A]
|
|
211
|
+
def a2a(http:, binding: :rest)
|
|
212
|
+
LLM::A2A.http(**http, binding:)
|
|
194
213
|
end
|
|
195
214
|
|
|
196
215
|
##
|
data/llm.gemspec
CHANGED
|
@@ -9,7 +9,22 @@ Gem::Specification.new do |spec|
|
|
|
9
9
|
spec.email = ["azantar@proton.me", "0x1eef@hardenedbsd.org"]
|
|
10
10
|
|
|
11
11
|
spec.summary = "Ruby's most capable AI runtime"
|
|
12
|
-
spec.description =
|
|
12
|
+
spec.description = <<~DESC
|
|
13
|
+
llm.rb is Ruby's most capable AI runtime.
|
|
14
|
+
|
|
15
|
+
It runs on Ruby's standard library by default. loads optional pieces
|
|
16
|
+
only when needed, and offers a single runtime for providers, agents,
|
|
17
|
+
tools, skills, MCP, A2A (Agent2Agent), RAG (vector stores & embeddings),
|
|
18
|
+
streaming, files, and persisted state. As a bonus, llm.rb is also available
|
|
19
|
+
for mruby.
|
|
20
|
+
|
|
21
|
+
It supports OpenAI, OpenAI-compatible endpoints, Anthropic, Google
|
|
22
|
+
Gemini, DeepSeek, xAI, Z.ai, AWS Bedrock, Ollama, and llama.cpp. It
|
|
23
|
+
also includes built-in ActiveRecord and Sequel support, plus concurrent
|
|
24
|
+
tool execution through threads, tasks (via async gem), fibers, ractors,
|
|
25
|
+
and fork (via xchan.rb gem).
|
|
26
|
+
DESC
|
|
27
|
+
|
|
13
28
|
spec.license = "0BSD"
|
|
14
29
|
spec.required_ruby_version = ">= 3.3.0"
|
|
15
30
|
|
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:
|
|
4
|
+
version: 11.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Antar Azri
|
|
@@ -264,7 +264,20 @@ dependencies:
|
|
|
264
264
|
- - "~>"
|
|
265
265
|
- !ruby/object:Gem::Version
|
|
266
266
|
version: '1.5'
|
|
267
|
-
description:
|
|
267
|
+
description: |
|
|
268
|
+
llm.rb is Ruby's most capable AI runtime.
|
|
269
|
+
|
|
270
|
+
It runs on Ruby's standard library by default. loads optional pieces
|
|
271
|
+
only when needed, and offers a single runtime for providers, agents,
|
|
272
|
+
tools, skills, MCP, A2A (Agent2Agent), RAG (vector stores & embeddings),
|
|
273
|
+
streaming, files, and persisted state. As a bonus, llm.rb is also available
|
|
274
|
+
for mruby.
|
|
275
|
+
|
|
276
|
+
It supports OpenAI, OpenAI-compatible endpoints, Anthropic, Google
|
|
277
|
+
Gemini, DeepSeek, xAI, Z.ai, AWS Bedrock, Ollama, and llama.cpp. It
|
|
278
|
+
also includes built-in ActiveRecord and Sequel support, plus concurrent
|
|
279
|
+
tool execution through threads, tasks (via async gem), fibers, ractors,
|
|
280
|
+
and fork (via xchan.rb gem).
|
|
268
281
|
email:
|
|
269
282
|
- azantar@proton.me
|
|
270
283
|
- 0x1eef@hardenedbsd.org
|
|
@@ -283,6 +296,16 @@ files:
|
|
|
283
296
|
- data/xai.json
|
|
284
297
|
- data/zai.json
|
|
285
298
|
- lib/llm.rb
|
|
299
|
+
- lib/llm/a2a.rb
|
|
300
|
+
- lib/llm/a2a/card.rb
|
|
301
|
+
- lib/llm/a2a/card/capabilities.rb
|
|
302
|
+
- lib/llm/a2a/card/interface.rb
|
|
303
|
+
- lib/llm/a2a/card/provider.rb
|
|
304
|
+
- lib/llm/a2a/card/skill.rb
|
|
305
|
+
- lib/llm/a2a/error.rb
|
|
306
|
+
- lib/llm/a2a/notifications.rb
|
|
307
|
+
- lib/llm/a2a/tasks.rb
|
|
308
|
+
- lib/llm/a2a/transport/http.rb
|
|
286
309
|
- lib/llm/active_record.rb
|
|
287
310
|
- lib/llm/active_record/acts_as_agent.rb
|
|
288
311
|
- lib/llm/active_record/acts_as_llm.rb
|
|
@@ -329,7 +352,6 @@ files:
|
|
|
329
352
|
- lib/llm/mcp/router.rb
|
|
330
353
|
- lib/llm/mcp/rpc.rb
|
|
331
354
|
- lib/llm/mcp/transport/http.rb
|
|
332
|
-
- lib/llm/mcp/transport/http/event_handler.rb
|
|
333
355
|
- lib/llm/mcp/transport/stdio.rb
|
|
334
356
|
- lib/llm/message.rb
|
|
335
357
|
- lib/llm/mime.rb
|
|
@@ -466,6 +488,7 @@ files:
|
|
|
466
488
|
- lib/llm/transport/response.rb
|
|
467
489
|
- lib/llm/transport/response/http.rb
|
|
468
490
|
- lib/llm/transport/stream_decoder.rb
|
|
491
|
+
- lib/llm/transport/utils.rb
|
|
469
492
|
- lib/llm/usage.rb
|
|
470
493
|
- lib/llm/utils.rb
|
|
471
494
|
- lib/llm/version.rb
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module LLM::MCP::Transport
|
|
4
|
-
##
|
|
5
|
-
# The {LLM::MCP::Transport::HTTP::EventHandler LLM::MCP::Transport::HTTP::EventHandler}
|
|
6
|
-
# class adapts generic server-sent event callbacks into decoded JSON-RPC
|
|
7
|
-
# messages for {LLM::MCP::Transport::HTTP LLM::MCP::Transport::HTTP}.
|
|
8
|
-
# It accumulates event data until a blank line terminates the current
|
|
9
|
-
# event, then parses the payload as JSON and yields it to the callback
|
|
10
|
-
# given at initialization.
|
|
11
|
-
# @private
|
|
12
|
-
class HTTP::EventHandler
|
|
13
|
-
##
|
|
14
|
-
# @yieldparam [Hash] message
|
|
15
|
-
# A decoded JSON-RPC message
|
|
16
|
-
# @return [LLM::MCP::Transport::HTTP::EventHandler]
|
|
17
|
-
def initialize(&on_message)
|
|
18
|
-
@on_message = on_message
|
|
19
|
-
reset
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
##
|
|
23
|
-
# Receives the SSE event name.
|
|
24
|
-
# @param [LLM::EventStream::Event, String, nil] event
|
|
25
|
-
# @param [String, nil] chunk
|
|
26
|
-
# The event stream event
|
|
27
|
-
# @return [void]
|
|
28
|
-
def on_event(event, chunk = nil)
|
|
29
|
-
@event = chunk ? event : event.value
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
##
|
|
33
|
-
# Receives one line of SSE data.
|
|
34
|
-
# @param [LLM::EventStream::Event, String, nil] event
|
|
35
|
-
# @param [String, nil] chunk
|
|
36
|
-
# The event stream event
|
|
37
|
-
# @return [void]
|
|
38
|
-
def on_data(event, chunk = nil)
|
|
39
|
-
@data << (chunk ? event : event.value).to_s
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
# The generic event stream parser dispatches one line at a time.
|
|
43
|
-
# A blank line terminates the current SSE event.
|
|
44
|
-
# @param [LLM::EventStream::Event, String] event
|
|
45
|
-
# The event stream event
|
|
46
|
-
# @return [void]
|
|
47
|
-
def on_chunk(event, chunk = nil)
|
|
48
|
-
flush if (chunk || event&.chunk || event) == "\n"
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
private
|
|
52
|
-
|
|
53
|
-
def flush
|
|
54
|
-
return reset if @data.empty? && @event.nil?
|
|
55
|
-
payload = @data.join("\n")
|
|
56
|
-
reset
|
|
57
|
-
return if payload.empty? || payload == "[DONE]"
|
|
58
|
-
@on_message.call(LLM.json.load(payload))
|
|
59
|
-
rescue *LLM.json.parser_error
|
|
60
|
-
reset
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
def reset
|
|
64
|
-
@event = nil
|
|
65
|
-
@data = []
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
end
|