llm.rb 9.0.0 → 11.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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +182 -4
  3. data/README.md +194 -42
  4. data/data/anthropic.json +278 -258
  5. data/data/bedrock.json +1288 -1561
  6. data/data/deepseek.json +38 -38
  7. data/data/google.json +656 -579
  8. data/data/openai.json +860 -818
  9. data/data/xai.json +243 -552
  10. data/data/zai.json +168 -168
  11. data/lib/llm/a2a/card/capabilities.rb +41 -0
  12. data/lib/llm/a2a/card/interface.rb +34 -0
  13. data/lib/llm/a2a/card/provider.rb +27 -0
  14. data/lib/llm/a2a/card/skill.rb +68 -0
  15. data/lib/llm/a2a/card.rb +144 -0
  16. data/lib/llm/a2a/error.rb +49 -0
  17. data/lib/llm/a2a/notifications.rb +53 -0
  18. data/lib/llm/a2a/tasks.rb +55 -0
  19. data/lib/llm/a2a/transport/http.rb +131 -0
  20. data/lib/llm/a2a.rb +452 -0
  21. data/lib/llm/active_record/acts_as_agent.rb +20 -9
  22. data/lib/llm/active_record/acts_as_llm.rb +4 -4
  23. data/lib/llm/active_record.rb +1 -6
  24. data/lib/llm/agent.rb +96 -71
  25. data/lib/llm/buffer.rb +1 -2
  26. data/lib/llm/context.rb +77 -50
  27. data/lib/llm/file.rb +7 -0
  28. data/lib/llm/function/call_task.rb +46 -0
  29. data/lib/llm/function.rb +28 -2
  30. data/lib/llm/mcp/transport/http.rb +5 -18
  31. data/lib/llm/mcp/transport/stdio.rb +7 -0
  32. data/lib/llm/mcp.rb +20 -17
  33. data/lib/llm/message.rb +1 -1
  34. data/lib/llm/object/kernel.rb +1 -1
  35. data/lib/llm/provider.rb +9 -9
  36. data/lib/llm/providers/anthropic/stream_parser.rb +2 -2
  37. data/lib/llm/providers/bedrock/stream_parser.rb +2 -2
  38. data/lib/llm/providers/google/stream_parser.rb +2 -2
  39. data/lib/llm/providers/openai/responses/stream_parser.rb +2 -2
  40. data/lib/llm/providers/openai/stream_parser.rb +2 -2
  41. data/lib/llm/response.rb +1 -1
  42. data/lib/llm/schema.rb +11 -0
  43. data/lib/llm/sequel/agent.rb +19 -9
  44. data/lib/llm/sequel/plugin.rb +9 -13
  45. data/lib/llm/stream.rb +11 -36
  46. data/lib/llm/tool/param.rb +1 -8
  47. data/lib/llm/tool.rb +57 -27
  48. data/lib/llm/tracer.rb +1 -1
  49. data/lib/llm/transport/http.rb +1 -1
  50. data/lib/llm/transport/stream_decoder.rb +6 -3
  51. data/lib/llm/transport/utils.rb +35 -0
  52. data/lib/llm/transport.rb +1 -0
  53. data/lib/llm/utils.rb +73 -0
  54. data/lib/llm/version.rb +1 -1
  55. data/lib/llm.rb +24 -4
  56. data/llm.gemspec +16 -1
  57. metadata +29 -5
  58. data/lib/llm/bot.rb +0 -3
  59. 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 ADDED
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LLM
4
+ ##
5
+ # Shared utility methods used across the runtime.
6
+ module Utils
7
+ extend self
8
+
9
+ ##
10
+ # Resolves a configured option against an object instance.
11
+ #
12
+ # Proc values are evaluated with `instance_exec`, symbol values are
13
+ # optionally sent to the object as method calls, hashes are duplicated,
14
+ # and all other values are returned as-is.
15
+ #
16
+ # @param [Object] obj
17
+ # @param [Object] option
18
+ # @param [Boolean] resolve_symbol
19
+ # @return [Object]
20
+ def resolve_option(obj, option, resolve_symbol: true)
21
+ case option
22
+ when Proc then obj.instance_exec(&option)
23
+ when Symbol then resolve_symbol ? obj.send(option) : option
24
+ when Hash then option.dup
25
+ else option
26
+ end
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
72
+ end
73
+ end
data/lib/llm/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module LLM
4
- VERSION = "9.0.0"
4
+ VERSION = "11.0.0"
5
5
  end
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"
@@ -12,6 +13,7 @@ module LLM
12
13
  require_relative "llm/prompt"
13
14
  require_relative "llm/schema"
14
15
  require_relative "llm/object"
16
+ require_relative "llm/utils"
15
17
  require_relative "llm/model"
16
18
  require_relative "llm/version"
17
19
  require_relative "llm/message"
@@ -34,6 +36,7 @@ module LLM
34
36
  require_relative "llm/skill"
35
37
  require_relative "llm/server_tool"
36
38
  require_relative "llm/mcp"
39
+ require_relative "llm/a2a"
37
40
 
38
41
  ##
39
42
  # Thread-safe monitors for different contexts
@@ -178,8 +181,6 @@ module LLM
178
181
  end
179
182
 
180
183
  ##
181
- # @param [LLM::Provider, nil] llm
182
- # The provider to use for MCP transports that need one
183
184
  # @param [Hash, nil] stdio
184
185
  # @option stdio [Array<String>] :argv
185
186
  # The command to run for the MCP process
@@ -188,8 +189,27 @@ module LLM
188
189
  # @option stdio [String, nil] :cwd
189
190
  # The working directory for the MCP process
190
191
  # @return [LLM::MCP]
191
- def mcp(llm = nil, **)
192
- LLM::MCP.new(llm, **)
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:)
193
213
  end
194
214
 
195
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 = spec.summary
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: 9.0.0
4
+ version: 11.0.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: Ruby's most capable AI runtime
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,11 +296,20 @@ 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
289
312
  - lib/llm/agent.rb
290
- - lib/llm/bot.rb
291
313
  - lib/llm/buffer.rb
292
314
  - lib/llm/compactor.rb
293
315
  - lib/llm/context.rb
@@ -305,6 +327,7 @@ files:
305
327
  - lib/llm/function.rb
306
328
  - lib/llm/function/array.rb
307
329
  - lib/llm/function/call_group.rb
330
+ - lib/llm/function/call_task.rb
308
331
  - lib/llm/function/fiber_group.rb
309
332
  - lib/llm/function/fork.rb
310
333
  - lib/llm/function/fork/job.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,7 +488,9 @@ 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
493
+ - lib/llm/utils.rb
470
494
  - lib/llm/version.rb
471
495
  - lib/sequel/plugins/agent.rb
472
496
  - lib/sequel/plugins/llm.rb
@@ -493,7 +517,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
493
517
  - !ruby/object:Gem::Version
494
518
  version: '0'
495
519
  requirements: []
496
- rubygems_version: 4.0.3
520
+ rubygems_version: 4.0.6
497
521
  specification_version: 4
498
522
  summary: Ruby's most capable AI runtime
499
523
  test_files: []
data/lib/llm/bot.rb DELETED
@@ -1,3 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "context"
@@ -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