llm.rb 11.0.0 → 11.2.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 (52) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +126 -1
  3. data/README.md +58 -18
  4. data/lib/llm/a2a/transport/http.rb +9 -8
  5. data/lib/llm/a2a.rb +14 -7
  6. data/lib/llm/agent.rb +6 -3
  7. data/lib/llm/context.rb +41 -6
  8. data/lib/llm/function/array.rb +6 -0
  9. data/lib/llm/function.rb +38 -4
  10. data/lib/llm/json_adapter.rb +8 -2
  11. data/lib/llm/mcp/transport/http.rb +7 -5
  12. data/lib/llm/mcp.rb +6 -7
  13. data/lib/llm/object/builder.rb +1 -0
  14. data/lib/llm/object.rb +9 -0
  15. data/lib/llm/provider.rb +1 -18
  16. data/lib/llm/providers/anthropic/files.rb +6 -6
  17. data/lib/llm/providers/anthropic/models.rb +1 -1
  18. data/lib/llm/providers/anthropic.rb +1 -1
  19. data/lib/llm/providers/bedrock/models.rb +4 -4
  20. data/lib/llm/providers/bedrock/signature.rb +3 -3
  21. data/lib/llm/providers/bedrock.rb +1 -1
  22. data/lib/llm/providers/google/files.rb +5 -5
  23. data/lib/llm/providers/google/images.rb +1 -1
  24. data/lib/llm/providers/google/models.rb +1 -1
  25. data/lib/llm/providers/google.rb +2 -2
  26. data/lib/llm/providers/ollama/models.rb +1 -1
  27. data/lib/llm/providers/ollama.rb +2 -2
  28. data/lib/llm/providers/openai/audio.rb +3 -3
  29. data/lib/llm/providers/openai/files.rb +5 -5
  30. data/lib/llm/providers/openai/images.rb +3 -3
  31. data/lib/llm/providers/openai/models.rb +1 -1
  32. data/lib/llm/providers/openai/moderations.rb +1 -1
  33. data/lib/llm/providers/openai/responses.rb +3 -3
  34. data/lib/llm/providers/openai/vector_stores.rb +11 -11
  35. data/lib/llm/providers/openai.rb +2 -2
  36. data/lib/llm/schema.rb +23 -5
  37. data/lib/llm/skill.rb +44 -14
  38. data/lib/llm/tool.rb +21 -0
  39. data/lib/llm/tracer/telemetry.rb +3 -1
  40. data/lib/llm/transport/curb.rb +246 -0
  41. data/lib/llm/transport/execution.rb +1 -1
  42. data/lib/llm/transport/http.rb +9 -4
  43. data/lib/llm/transport/net_http_adapter.rb +61 -0
  44. data/lib/llm/transport/persistent_http.rb +10 -5
  45. data/lib/llm/transport/request.rb +121 -0
  46. data/lib/llm/transport/response/curb.rb +112 -0
  47. data/lib/llm/transport/response.rb +1 -0
  48. data/lib/llm/transport/utils.rb +42 -17
  49. data/lib/llm/transport.rb +17 -45
  50. data/lib/llm/version.rb +1 -1
  51. data/llm.gemspec +3 -3
  52. metadata +8 -4
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ class LLM::Transport::Response
4
+ ##
5
+ # {LLM::Transport::Response::Curb LLM::Transport::Response::Curb}
6
+ # adapts a raw status code, header hash, and body string to the
7
+ # {LLM::Transport::Response LLM::Transport::Response} interface.
8
+ #
9
+ # This is the response wrapper used by the
10
+ # {LLM::Transport::Curb LLM::Transport::Curb} transport.
11
+ class Curb < self
12
+ ##
13
+ # @return [Integer]
14
+ attr_reader :code
15
+
16
+ ##
17
+ # @return [Hash]
18
+ attr_reader :headers
19
+
20
+ ##
21
+ # @return [String]
22
+ attr_accessor :body
23
+
24
+ ##
25
+ # @param [#to_i] code
26
+ # @param [Hash] headers
27
+ # @param [String] body
28
+ # @return [LLM::Transport::Response::Curb]
29
+ def initialize(code, headers = {}, body = +"")
30
+ @code = code.to_i
31
+ @headers = {}
32
+ (headers || {}).each { @headers[_1.to_s.downcase] = _2.to_s }
33
+ @body = body
34
+ end
35
+
36
+ ##
37
+ # @param [String] key
38
+ # @return [String, nil]
39
+ def [](key)
40
+ @headers[key.to_s.downcase]
41
+ end
42
+
43
+ ##
44
+ # @param [Object, nil] dest
45
+ # @yieldparam [String] chunk
46
+ # @return [void]
47
+ def read_body(dest = nil, &block)
48
+ return @body unless block_given? || dest
49
+ if dest
50
+ dest << @body.to_s
51
+ else
52
+ yield @body.to_s
53
+ end
54
+ @body
55
+ end
56
+
57
+ ##
58
+ # @return [Boolean]
59
+ def success?
60
+ code.between?(200, 299)
61
+ end
62
+
63
+ ##
64
+ # @return [Boolean]
65
+ def ok?
66
+ code == 200
67
+ end
68
+
69
+ ##
70
+ # @return [Boolean]
71
+ def bad_request?
72
+ code == 400
73
+ end
74
+
75
+ ##
76
+ # @return [Boolean]
77
+ def unauthorized?
78
+ code == 401
79
+ end
80
+
81
+ ##
82
+ # @return [Boolean]
83
+ def forbidden?
84
+ code == 403
85
+ end
86
+
87
+ ##
88
+ # @return [Boolean]
89
+ def not_found?
90
+ code == 404
91
+ end
92
+
93
+ ##
94
+ # @return [Boolean]
95
+ def rate_limited?
96
+ code == 429
97
+ end
98
+
99
+ ##
100
+ # @return [Boolean]
101
+ def server_error?
102
+ code.between?(500, 599)
103
+ end
104
+
105
+ ##
106
+ # @return [String]
107
+ def inspect
108
+ "#<#{self.class.name}:0x#{object_id.to_s(16)}" \
109
+ " @code=#{@code} @headers=#{@headers.inspect}>"
110
+ end
111
+ end
112
+ end
@@ -17,6 +17,7 @@ class LLM::Transport
17
17
  # how the request was actually performed.
18
18
  class Response
19
19
  require_relative "response/http"
20
+ require_relative "response/curb"
20
21
 
21
22
  ##
22
23
  # @param [Object] res
@@ -4,32 +4,57 @@ class LLM::Transport
4
4
  ##
5
5
  # Shared utility methods for HTTP-backed transports.
6
6
  #
7
+ # These methods resolve the transport options accepted by providers,
8
+ # MCP HTTP clients, and A2A HTTP clients into concrete
9
+ # {LLM::Transport} instances.
10
+ #
7
11
  # @api private
8
12
  module Utils
9
13
  extend self
10
- private
11
14
 
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
- )
15
+ ##
16
+ # Resolves a transport configuration into a transport instance.
17
+ #
18
+ # Nil values use the default Net::HTTP transport, or the persistent
19
+ # Net::HTTP transport when `persistent` is true. Transport subclasses
20
+ # are instantiated with the endpoint settings, symbols are resolved
21
+ # through {LLM::Transport} shortcut methods, and transport instances
22
+ # are returned as-is.
23
+ #
24
+ # @param [String] host
25
+ # @param [Integer] port
26
+ # @param [Integer, nil] timeout
27
+ # @param [Boolean] ssl
28
+ # @param [Boolean] persistent
29
+ # @param [LLM::Transport, Class, Symbol, nil] transport
30
+ # @return [LLM::Transport]
31
+ def resolve_transport(host:, port:, timeout:, ssl:, persistent:, transport:)
32
+ if transport.nil?
33
+ default_transport(host:, port:, timeout:, ssl:, persistent:)
34
+ elsif Class === transport && transport <= LLM::Transport
35
+ transport.new(host:, port:, timeout:, ssl:)
36
+ elsif Symbol === transport
37
+ transport = LLM::Transport.public_send(transport)
38
+ transport.new(host:, port:, timeout:, ssl:)
21
39
  else
22
40
  transport
23
41
  end
24
42
  end
25
43
 
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
- )
44
+ private
45
+
46
+ ##
47
+ # Builds the default Net::HTTP transport for an endpoint.
48
+ #
49
+ # @param [String] host
50
+ # @param [Integer] port
51
+ # @param [Integer, nil] timeout
52
+ # @param [Boolean] ssl
53
+ # @param [Boolean] persistent
54
+ # @return [LLM::Transport]
55
+ def default_transport(host:, port:, timeout:, ssl:, persistent:)
56
+ target = persistent ? LLM::Transport::PersistentHTTP : LLM::Transport::HTTP
57
+ target.new(host:, port:, timeout:, ssl:)
33
58
  end
34
59
  end
35
60
  end
data/lib/llm/transport.rb CHANGED
@@ -9,10 +9,10 @@ module LLM
9
9
  # execute provider requests without changing request adapters or
10
10
  # response adapters.
11
11
  #
12
- # Providers currently construct {Net::HTTPRequest Net::HTTPRequest}
13
- # objects before delegating to a transport. Custom transports are
14
- # therefore expected to execute those requests directly, or transform
15
- # them into backend-specific request objects before execution.
12
+ # Providers should construct {LLM::Transport::Request} objects before
13
+ # delegating to a transport. Custom transports can execute those
14
+ # requests directly, or transform them into backend-specific request
15
+ # objects before execution.
16
16
  #
17
17
  # Only {#request} is required. The remaining methods are optional hooks
18
18
  # for features such as interruption, request ownership, or persistence,
@@ -26,11 +26,14 @@ module LLM
26
26
  # can rely on one normalized response contract instead of
27
27
  # transport-specific classes.
28
28
  class Transport
29
+ require_relative "transport/request"
29
30
  require_relative "transport/response"
30
31
  require_relative "transport/utils"
31
32
  require_relative "transport/stream_decoder"
33
+ require_relative "transport/net_http_adapter"
32
34
  require_relative "transport/http"
33
35
  require_relative "transport/persistent_http"
36
+ require_relative "transport/curb"
34
37
  require_relative "transport/execution"
35
38
 
36
39
  ##
@@ -47,9 +50,17 @@ module LLM
47
50
  PersistentHTTP
48
51
  end
49
52
 
53
+ ##
54
+ # Returns the optional libcurl (curb) transport class.
55
+ # Requires the `curb` gem.
56
+ # @return [Class]
57
+ def self.curb
58
+ Curb
59
+ end
60
+
50
61
  ##
51
62
  # Performs a request through the transport.
52
- # @param [Net::HTTPRequest] request
63
+ # @param [LLM::Transport::Request] request
53
64
  # @param [Object] owner
54
65
  # @param [LLM::Object, nil] stream
55
66
  # @yieldparam [LLM::Transport::Response] response
@@ -90,51 +101,12 @@ module LLM
90
101
  end
91
102
 
92
103
  ##
93
- # @note
94
- # Custom transports may be able to reuse this helper when they
95
- # operate on Net::HTTPRequest objects, or implement their own
96
- # request body preparation path instead.
97
- # @param [Net::HTTPRequest] request
104
+ # @param [LLM::Transport::Request] request
98
105
  # @param [IO] io
99
106
  # @return [void]
100
107
  def set_body_stream(request, io)
101
108
  request.body_stream = io
102
109
  request["transfer-encoding"] = "chunked" unless request["content-length"]
103
110
  end
104
-
105
- private
106
-
107
- ##
108
- # @api private
109
- # @note
110
- # Custom transports may be able to reuse this helper when they
111
- # execute requests through a Net::HTTP-compatible client, or
112
- # implement their own request execution path instead.
113
- def perform_request(client, request, stream, &b)
114
- if stream
115
- client.request(request) do |raw|
116
- res = LLM::Transport::Response.from(raw)
117
- if res.success?
118
- parser = stream.decoder.new(stream.parser.new(stream.streamer))
119
- res.read_body(parser)
120
- body = parser.body
121
- res.body = (Hash === body || Array === body) ? LLM::Object.from(body) : body
122
- else
123
- body = +""
124
- res.read_body { body << _1 }
125
- res.body = body
126
- end
127
- ensure
128
- parser&.free
129
- end
130
- elsif b
131
- client.request(request) do |raw|
132
- res = LLM::Transport::Response.from(raw)
133
- res.success? ? b.call(res) : res
134
- end
135
- else
136
- LLM::Transport::Response.from(client.request(request))
137
- end
138
- end
139
111
  end
140
112
  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 = "11.0.0"
4
+ VERSION = "11.2.0"
5
5
  end
data/llm.gemspec CHANGED
@@ -28,10 +28,10 @@ Gem::Specification.new do |spec|
28
28
  spec.license = "0BSD"
29
29
  spec.required_ruby_version = ">= 3.3.0"
30
30
 
31
- spec.homepage = "https://github.com/llmrb/llm.rb"
32
- spec.metadata["homepage_uri"] = "https://github.com/llmrb/llm.rb"
31
+ spec.homepage = "https://llmrb.github.io"
32
+ spec.metadata["homepage_uri"] = spec.homepage
33
33
  spec.metadata["source_code_uri"] = "https://github.com/llmrb/llm.rb"
34
- spec.metadata["documentation_uri"] = "https://0x1eef.github.io/x/llm.rb"
34
+ spec.metadata["documentation_uri"] = "https://llmrb.github.io/llm.rb"
35
35
  spec.metadata["changelog_uri"] = "https://0x1eef.github.io/x/llm.rb/file.CHANGELOG.html"
36
36
 
37
37
  spec.files = Dir[
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: 11.0.0
4
+ version: 11.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Antar Azri
@@ -482,10 +482,14 @@ files:
482
482
  - lib/llm/tracer/null.rb
483
483
  - lib/llm/tracer/telemetry.rb
484
484
  - lib/llm/transport.rb
485
+ - lib/llm/transport/curb.rb
485
486
  - lib/llm/transport/execution.rb
486
487
  - lib/llm/transport/http.rb
488
+ - lib/llm/transport/net_http_adapter.rb
487
489
  - lib/llm/transport/persistent_http.rb
490
+ - lib/llm/transport/request.rb
488
491
  - lib/llm/transport/response.rb
492
+ - lib/llm/transport/response/curb.rb
489
493
  - lib/llm/transport/response/http.rb
490
494
  - lib/llm/transport/stream_decoder.rb
491
495
  - lib/llm/transport/utils.rb
@@ -495,13 +499,13 @@ files:
495
499
  - lib/sequel/plugins/agent.rb
496
500
  - lib/sequel/plugins/llm.rb
497
501
  - llm.gemspec
498
- homepage: https://github.com/llmrb/llm.rb
502
+ homepage: https://llmrb.github.io
499
503
  licenses:
500
504
  - 0BSD
501
505
  metadata:
502
- homepage_uri: https://github.com/llmrb/llm.rb
506
+ homepage_uri: https://llmrb.github.io
503
507
  source_code_uri: https://github.com/llmrb/llm.rb
504
- documentation_uri: https://0x1eef.github.io/x/llm.rb
508
+ documentation_uri: https://llmrb.github.io/llm.rb
505
509
  changelog_uri: https://0x1eef.github.io/x/llm.rb/file.CHANGELOG.html
506
510
  rdoc_options: []
507
511
  require_paths: