Class: Insika::Server::A2A::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/insika/server/a2a/client.rb

Overview

Outbound A2A client: calls a remote A2A agent. PURE — the http (post_json(url, body) -> Hash) is injected; the smoke test uses loopback.

Constant Summary collapse

TERMINAL =
%w[completed failed canceled rejected input-required].freeze
POLL_DELAY =
0.02

Instance Method Summary collapse

Constructor Details

#initialize(http:, poll_max: 30, sleeper: nil) ⇒ Client

Returns a new instance of Client.



24
25
26
27
28
29
# File 'lib/insika/server/a2a/client.rb', line 24

def initialize(http:, poll_max: 30, sleeper: nil)
  @http = http
  @poll_max = poll_max
  @sleeper = sleeper || ->(seconds) { Async::Task.current&.sleep(seconds) }
  @id_seq = 0
end

Instance Method Details

#call(url, text, context_id: nil) ⇒ Object

High level: send + poll until terminal. ALWAYS does ≥1 get_task — the inbound message/send projects the Task WITHOUT status.message (the content only comes from tasks/get). -> { text:, state:, id: } | { error:, state:, id: }. NEVER raises (wraps RemoteError).



47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/insika/server/a2a/client.rb', line 47

def call(url, text, context_id: nil)
  sent = send_message(url, text, context_id: context_id)
  id = remote_id(sent)
  task = get_task(url, id)
  attempts = 0
  until TERMINAL.include?(remote_state(task)) || attempts >= @poll_max
    @sleeper.call(POLL_DELAY)
    attempts += 1
    task = get_task(url, id)
  end
  project(task, id)
rescue RemoteError => e
  { error: e.message, state: "failed", id: nil }
end

#get_task(url, task_id) ⇒ Object

-> remote A2A Task (Hash) | raise RemoteError.



39
40
41
# File 'lib/insika/server/a2a/client.rb', line 39

def get_task(url, task_id)
  parse_envelope(@http.post_json(url, request("tasks/get", { "id" => task_id })))
end

#send_message(url, text, context_id: nil) ⇒ Object

-> remote A2A Task (Hash) | raise RemoteError.



32
33
34
35
36
# File 'lib/insika/server/a2a/client.rb', line 32

def send_message(url, text, context_id: nil)
  message = { "role" => "user", "parts" => [{ "kind" => "text", "text" => text.to_s }] }
  message["contextId"] = context_id if context_id
  parse_envelope(@http.post_json(url, request("message/send", { "message" => message })))
end