llm.rb 10.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.
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ class LLM::A2A
4
+ ##
5
+ # Generic A2A protocol error.
6
+ Error = Class.new(LLM::Error) do
7
+ ##
8
+ # @return [Integer, nil]
9
+ attr_reader :code
10
+
11
+ ##
12
+ # @return [Object, nil]
13
+ attr_reader :data
14
+
15
+ ##
16
+ # @param [String] message
17
+ # @param [Integer, nil] code
18
+ # @param [Object, nil] data
19
+ def initialize(message, code = nil, data = nil)
20
+ super(message)
21
+ @code = code
22
+ @data = data
23
+ end
24
+ end
25
+
26
+ ##
27
+ # Raised when the agent card cannot be fetched or parsed.
28
+ AgentCardError = Class.new(Error)
29
+
30
+ ##
31
+ # Raised when a task is not found.
32
+ TaskNotFoundError = Class.new(Error)
33
+
34
+ ##
35
+ # Raised when a task cannot be cancelled.
36
+ TaskNotCancelableError = Class.new(Error)
37
+
38
+ ##
39
+ # Raised when the agent does not support the requested operation.
40
+ UnsupportedOperationError = Class.new(Error)
41
+
42
+ ##
43
+ # Raised when a content type is not supported.
44
+ ContentTypeNotSupportedError = Class.new(Error)
45
+
46
+ ##
47
+ # Raised when the A2A protocol version is not supported.
48
+ VersionNotSupportedError = Class.new(Error)
49
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ class LLM::A2A
4
+ ##
5
+ # Groups push notification configuration operations.
6
+ class Notifications
7
+ ##
8
+ # @param [LLM::A2A] a2a
9
+ def initialize(a2a)
10
+ @a2a = a2a
11
+ end
12
+
13
+ ##
14
+ # Creates a push notification configuration for a task.
15
+ # @param [String] task_id
16
+ # @param [String] url
17
+ # @param [String, nil] token
18
+ # @param [Hash, nil] authentication
19
+ # @param [String, nil] id
20
+ # @return [LLM::Object]
21
+ def create(task_id, url:, token: nil, authentication: nil, id: nil)
22
+ @a2a.create_task_push_notification_config(task_id, url:, token:, authentication:, id:)
23
+ end
24
+
25
+ ##
26
+ # Retrieves a push notification configuration for a task.
27
+ # @param [String] task_id
28
+ # @param [String] id
29
+ # @return [LLM::Object]
30
+ def get(task_id, id)
31
+ @a2a.get_task_push_notification_config(task_id, id)
32
+ end
33
+
34
+ ##
35
+ # Lists push notification configurations for a task.
36
+ # @param [String] task_id
37
+ # @param [Integer, nil] page_size
38
+ # @param [String, nil] page_token
39
+ # @return [LLM::Object]
40
+ def list(task_id, page_size: nil, page_token: nil)
41
+ @a2a.list_task_push_notification_configs(task_id, page_size:, page_token:)
42
+ end
43
+
44
+ ##
45
+ # Deletes a push notification configuration for a task.
46
+ # @param [String] task_id
47
+ # @param [String] id
48
+ # @return [LLM::Object]
49
+ def delete(task_id, id)
50
+ @a2a.delete_task_push_notification_config(task_id, id)
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ class LLM::A2A
4
+ ##
5
+ # Groups task-oriented A2A operations.
6
+ class Tasks
7
+ ##
8
+ # @param [LLM::A2A] a2a
9
+ def initialize(a2a)
10
+ @a2a = a2a
11
+ end
12
+
13
+ ##
14
+ # Returns the current state of a task.
15
+ # @param [String] task_id
16
+ # @param [Integer, nil] history_length
17
+ # @return [LLM::Object]
18
+ def get(task_id, history_length: nil)
19
+ @a2a.get_task(task_id, history_length:)
20
+ end
21
+
22
+ ##
23
+ # Lists tasks with optional filtering.
24
+ # @param [String, nil] context_id
25
+ # @param [String, nil] status
26
+ # @param [Integer, nil] history_length
27
+ # @param [String, nil] status_timestamp_after
28
+ # @param [Boolean, nil] include_artifacts
29
+ # @param [Integer] page_size
30
+ # @param [String, nil] page_token
31
+ # @return [LLM::Object]
32
+ def list(context_id: nil, status: nil, history_length: nil, status_timestamp_after: nil,
33
+ include_artifacts: nil, page_size: 20, page_token: nil)
34
+ @a2a.list_tasks(context_id:, status:, history_length:, status_timestamp_after:,
35
+ include_artifacts:, page_size:, page_token:)
36
+ end
37
+
38
+ ##
39
+ # Cancels a task in progress.
40
+ # @param [String] task_id
41
+ # @return [LLM::Object]
42
+ def cancel(task_id, metadata: nil)
43
+ @a2a.cancel_task(task_id, metadata:)
44
+ end
45
+
46
+ ##
47
+ # Subscribes to streaming updates for an existing task.
48
+ # @param [String] task_id
49
+ # @yieldparam [LLM::Object] event
50
+ # @return [void]
51
+ def subscribe(task_id, &on_event)
52
+ @a2a.subscribe_to_task(task_id, &on_event)
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,131 @@
1
+ # frozen_string_literal: true
2
+
3
+ class LLM::A2A
4
+ module Transport
5
+ ##
6
+ # The {LLM::A2A::Transport::HTTP} class provides the HTTP+JSON/REST
7
+ # protocol binding for the A2A protocol. It sends JSON payloads to
8
+ # A2A agent endpoints and handles both synchronous responses and
9
+ # Server-Sent Events for streaming operations.
10
+ #
11
+ # This transport implements the HTTP+JSON/REST binding as defined
12
+ # in the A2A specification (Section 11).
13
+ class HTTP
14
+ include LLM::Transport::Utils
15
+
16
+ ##
17
+ # @param [String] url The base URL of the A2A agent
18
+ # @param [Hash<String, String>] headers Extra HTTP headers
19
+ # @param [Integer, nil] timeout The timeout in seconds
20
+ # @param [LLM::Transport, Class, nil] transport Override transport
21
+ # @param [String] protocol_version The A2A protocol version header
22
+ def initialize(url:, headers: {}, timeout: nil, transport: nil, protocol_version: "1.0")
23
+ @uri = URI.parse(url)
24
+ @headers = headers
25
+ @protocol_version = protocol_version
26
+ @transport = resolve_transport(@uri, transport, timeout)
27
+ end
28
+
29
+ ##
30
+ # Sends a GET request.
31
+ # @param [String] path The URL path
32
+ # @return [Hash]
33
+ def get(path, accept: "application/json")
34
+ req = Net::HTTP::Get.new(request_path(path), headers(accept:))
35
+ res = transport.request(req, owner: self)
36
+ parse_response(res)
37
+ end
38
+
39
+ ##
40
+ # Sends a POST request.
41
+ # @param [String] path The URL path
42
+ # @param [Hash] body The JSON body
43
+ # @return [Hash]
44
+ def post(path, body, content_type: "application/json", accept: "application/json")
45
+ req = Net::HTTP::Post.new(request_path(path), headers(content_type:, accept:))
46
+ req.body = LLM.json.dump(body)
47
+ res = transport.request(req, owner: self)
48
+ parse_response(res)
49
+ end
50
+
51
+ ##
52
+ # Sends a DELETE request.
53
+ # @param [String] path The URL path
54
+ # @return [Hash]
55
+ def delete(path, accept: "application/json")
56
+ req = Net::HTTP::Delete.new(request_path(path), headers(accept:))
57
+ res = transport.request(req, owner: self)
58
+ parse_response(res)
59
+ end
60
+
61
+ ##
62
+ # Sends a GET request with a streaming (SSE) response.
63
+ #
64
+ # The block is called for each event parsed from the event stream.
65
+ # @param [String] path The URL path
66
+ # @yieldparam [LLM::Object] event A stream event
67
+ # @return [void]
68
+ def get_stream(path, &on_event)
69
+ req = Net::HTTP::Get.new(request_path(path), headers(accept: "text/event-stream"))
70
+ stream(req, &on_event)
71
+ end
72
+
73
+ ##
74
+ # Sends a POST request with a streaming (SSE) response.
75
+ #
76
+ # The block is called for each event parsed from the event stream.
77
+ # @param [String] path The URL path
78
+ # @param [Hash] body The JSON body
79
+ # @yieldparam [LLM::Object] event A stream event
80
+ # @return [void]
81
+ def post_stream(path, body, content_type: "application/json", &on_event)
82
+ req = Net::HTTP::Post.new(request_path(path), headers(content_type:, accept: "text/event-stream"))
83
+ req.body = LLM.json.dump(body)
84
+ stream(req, &on_event)
85
+ end
86
+
87
+ private
88
+
89
+ attr_reader :transport
90
+
91
+ def headers(content_type: "application/json", accept: "application/json")
92
+ {
93
+ "A2A-Version" => @protocol_version,
94
+ "content-type" => content_type,
95
+ "accept" => accept
96
+ }.merge(@headers)
97
+ end
98
+
99
+ def request_path(path)
100
+ path = LLM::Utils.normalize_base_path(path)
101
+ path.empty? ? "/" : path
102
+ end
103
+
104
+ def stream(req, &on_event)
105
+ transport.request(req, owner: self) do |raw|
106
+ raw = LLM::Transport::Response.from(raw)
107
+ next raw unless raw.success?
108
+ decoder = LLM::Transport::StreamDecoder.new(&on_event)
109
+ raw.read_body { decoder << _1 }
110
+ decoder.free
111
+ end
112
+ end
113
+
114
+ def parse_response(res)
115
+ res = LLM::Transport::Response.from(res)
116
+ body = res.body.to_s
117
+ if res.success?
118
+ body.empty? ? {} : LLM.json.load(body)
119
+ else
120
+ handle_error(res.code, body)
121
+ end
122
+ end
123
+
124
+ def handle_error(code, body)
125
+ data = LLM.json.load(body) rescue {}
126
+ msg = data.dig("error", "message") || data["message"] || "HTTP #{code}"
127
+ raise LLM::A2A::Error.new(msg, code.to_i, data)
128
+ end
129
+ end
130
+ end
131
+ end