ag-ui-protocol 0.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 +7 -0
- data/lib/ag_ui_protocol/core/events.rb +1024 -0
- data/lib/ag_ui_protocol/core/types.rb +717 -0
- data/lib/ag_ui_protocol/encoder/event_encoder.rb +83 -0
- data/lib/ag_ui_protocol/util.rb +76 -0
- data/lib/ag_ui_protocol/version.rb +6 -0
- data/lib/ag_ui_protocol.rb +15 -0
- metadata +181 -0
|
@@ -0,0 +1,717 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "sorbet-runtime"
|
|
5
|
+
require_relative "../util"
|
|
6
|
+
|
|
7
|
+
module AgUiProtocol
|
|
8
|
+
module Core
|
|
9
|
+
# The Agent User Interaction Protocol Ruby SDK is built on a set of core types
|
|
10
|
+
# that represent the fundamental structures used throughout the system. This page
|
|
11
|
+
# documents these types and their properties.
|
|
12
|
+
#
|
|
13
|
+
# ## Message Types
|
|
14
|
+
#
|
|
15
|
+
# The SDK includes several message types that represent different kinds of
|
|
16
|
+
# messages in the system.
|
|
17
|
+
#
|
|
18
|
+
module Types
|
|
19
|
+
# Represents the possible roles a message sender can have.
|
|
20
|
+
#
|
|
21
|
+
# ```ruby
|
|
22
|
+
#
|
|
23
|
+
# AgUiProtocol::Core::Types::Role
|
|
24
|
+
# # => ["developer", "system", "assistant", "user", "tool", "activity"]
|
|
25
|
+
#
|
|
26
|
+
# ```
|
|
27
|
+
# @category Message Types
|
|
28
|
+
Role = ["developer", "system", "assistant", "user", "tool", "activity"].freeze
|
|
29
|
+
|
|
30
|
+
# Base model for protocol entities.
|
|
31
|
+
#
|
|
32
|
+
# Subclasses should implement {#to_h}. JSON serialization is derived from
|
|
33
|
+
# that hash via {#as_json} and {#to_json}.
|
|
34
|
+
class Model
|
|
35
|
+
extend T::Sig
|
|
36
|
+
|
|
37
|
+
# Returns a Ruby Hash representation using snake_case keys or raise NotImplementedError in case of not implemented.
|
|
38
|
+
#
|
|
39
|
+
# Subclasses override this method to provide their shape.
|
|
40
|
+
#
|
|
41
|
+
# @return [Hash<Symbol, Object>, raise NotImplementedError]
|
|
42
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
|
43
|
+
def to_h
|
|
44
|
+
raise NotImplementedError, 'Implement this method by concrect class'
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Returns a JSON-ready representation.
|
|
48
|
+
#
|
|
49
|
+
# This converts keys to camelCase and removes nil values recursively.
|
|
50
|
+
#
|
|
51
|
+
# @return [Object]
|
|
52
|
+
sig { returns(T.untyped) }
|
|
53
|
+
def as_json
|
|
54
|
+
AgUiProtocol::Util.deep_transform_keys_to_camel(AgUiProtocol::Util.deep_compact(to_h))
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Serializes the model to a JSON string.
|
|
58
|
+
#
|
|
59
|
+
# @param _args [Array<Object>] Unused; kept for compatibility with ActiveSupport.
|
|
60
|
+
# @return [String]
|
|
61
|
+
sig { params(_args: T.untyped).returns(String) }
|
|
62
|
+
def to_json(*_args)
|
|
63
|
+
AgUiProtocol::Util.dump_json(as_json)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Function invocation descriptor used inside tool calls.
|
|
68
|
+
#
|
|
69
|
+
# ```ruby
|
|
70
|
+
#
|
|
71
|
+
# fn = AgUiProtocol::Core::Types::FunctionCall.new(
|
|
72
|
+
# name: "search",
|
|
73
|
+
# arguments: "{\"q\":\"AG-UI\"}"
|
|
74
|
+
# )
|
|
75
|
+
#
|
|
76
|
+
# ```
|
|
77
|
+
# @category ToolCall
|
|
78
|
+
class FunctionCall < Model
|
|
79
|
+
sig { returns(String) }
|
|
80
|
+
attr_reader :name
|
|
81
|
+
|
|
82
|
+
sig { returns(String) }
|
|
83
|
+
attr_reader :arguments
|
|
84
|
+
|
|
85
|
+
# @param name [String] Function name.
|
|
86
|
+
# @param arguments [String] JSON-encoded arguments.
|
|
87
|
+
sig { params(name: String, arguments: String).void }
|
|
88
|
+
def initialize(name:, arguments:)
|
|
89
|
+
@name = name
|
|
90
|
+
@arguments = arguments
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
|
94
|
+
def to_h
|
|
95
|
+
{
|
|
96
|
+
name: @name,
|
|
97
|
+
arguments: @arguments
|
|
98
|
+
}
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Tool calls are embedded within assistant messages.
|
|
103
|
+
#
|
|
104
|
+
# ```ruby
|
|
105
|
+
#
|
|
106
|
+
# tool_call = AgUiProtocol::Core::Types::ToolCall.new(
|
|
107
|
+
# id: "tc_1",
|
|
108
|
+
# function: { name: "search", arguments: "{\"q\":\"AG-UI\"}" }
|
|
109
|
+
# )
|
|
110
|
+
#
|
|
111
|
+
# ```
|
|
112
|
+
class ToolCall < Model
|
|
113
|
+
sig { returns(String) }
|
|
114
|
+
attr_reader :id
|
|
115
|
+
|
|
116
|
+
sig { returns(String) }
|
|
117
|
+
attr_reader :type
|
|
118
|
+
|
|
119
|
+
sig { returns(FunctionCall) }
|
|
120
|
+
attr_reader :function
|
|
121
|
+
|
|
122
|
+
# @param id [String] Unique identifier for the tool call
|
|
123
|
+
# @param function [FunctionCall, Hash] Function name and arguments
|
|
124
|
+
# @param type [String] Type of the tool call
|
|
125
|
+
sig { params(id: String, function: T.untyped, type: String).void }
|
|
126
|
+
def initialize(id:, function:, type: 'function')
|
|
127
|
+
@id = id
|
|
128
|
+
@type = type
|
|
129
|
+
@function = function.is_a?(FunctionCall) ? function : FunctionCall.new(**function)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
|
133
|
+
def to_h
|
|
134
|
+
{
|
|
135
|
+
id: @id,
|
|
136
|
+
type: @type,
|
|
137
|
+
function: @function
|
|
138
|
+
}
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Base class for message shapes.
|
|
143
|
+
class BaseMessage < Model
|
|
144
|
+
sig { returns(String) }
|
|
145
|
+
attr_reader :id
|
|
146
|
+
|
|
147
|
+
sig { returns(String) }
|
|
148
|
+
attr_reader :role
|
|
149
|
+
|
|
150
|
+
sig { returns(T.nilable(T.any(String, T::Array[T.any(TextInputContent, BinaryInputContent)]))) }
|
|
151
|
+
attr_reader :content
|
|
152
|
+
|
|
153
|
+
sig { returns(T.nilable(String)) }
|
|
154
|
+
attr_reader :name
|
|
155
|
+
|
|
156
|
+
# @param id [String] Unique identifier for the message
|
|
157
|
+
# @param role [String] Role of the message sender
|
|
158
|
+
# @param content [Object] Text content of the message
|
|
159
|
+
# @param name [String] Optional name of the sender
|
|
160
|
+
sig { params(id: String, role: String, content: T.nilable(T.any(String, T::Array[T.any(TextInputContent, BinaryInputContent)])), name: T.nilable(String)).void }
|
|
161
|
+
def initialize(id:, role:, content: nil, name: nil)
|
|
162
|
+
@id = id
|
|
163
|
+
@role = role
|
|
164
|
+
@content = content
|
|
165
|
+
@name = name
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
|
169
|
+
def to_h
|
|
170
|
+
{
|
|
171
|
+
id: @id,
|
|
172
|
+
role: @role,
|
|
173
|
+
content: @content,
|
|
174
|
+
name: @name
|
|
175
|
+
}
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Represents a message from a developer.
|
|
180
|
+
#
|
|
181
|
+
# ```ruby
|
|
182
|
+
#
|
|
183
|
+
# msg = AgUiProtocol::Core::Types::DeveloperMessage.new(
|
|
184
|
+
# id: "dev_1",
|
|
185
|
+
# content: "You are a helpful assistant."
|
|
186
|
+
# )
|
|
187
|
+
#
|
|
188
|
+
# ```
|
|
189
|
+
# @category Message Types
|
|
190
|
+
class DeveloperMessage < BaseMessage
|
|
191
|
+
|
|
192
|
+
sig { returns(String) }
|
|
193
|
+
attr_reader :content
|
|
194
|
+
|
|
195
|
+
# @param id [String] Unique identifier for the message
|
|
196
|
+
# @param content [Object] Text content of the message (required)
|
|
197
|
+
# @param name [String] Optional name of the sender
|
|
198
|
+
sig { params(id: String, content: String, name: T.nilable(String)).void }
|
|
199
|
+
def initialize(id:, content:, name: nil)
|
|
200
|
+
super(id: id, role: "developer", content: content, name: name)
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# Represents a system message.
|
|
205
|
+
#
|
|
206
|
+
# ```ruby
|
|
207
|
+
#
|
|
208
|
+
# msg = AgUiProtocol::Core::Types::SystemMessage.new(
|
|
209
|
+
# id: "sys_1",
|
|
210
|
+
# content: "Follow the protocol."
|
|
211
|
+
# )
|
|
212
|
+
#
|
|
213
|
+
# ```
|
|
214
|
+
# @category Message Types
|
|
215
|
+
class SystemMessage < BaseMessage
|
|
216
|
+
|
|
217
|
+
# @param id [String] Unique identifier for the message
|
|
218
|
+
# @param content [Object] Text content of the message (required)
|
|
219
|
+
# @param name [String] Optional name of the sender
|
|
220
|
+
sig { params(id: String, content: String, name: T.nilable(String)).void }
|
|
221
|
+
def initialize(id:, content:, name: nil)
|
|
222
|
+
super(id: id, role: "system", content: content, name: name)
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
# Represents a message from an assistant.
|
|
227
|
+
#
|
|
228
|
+
# ```ruby
|
|
229
|
+
#
|
|
230
|
+
# msg = AgUiProtocol::Core::Types::AssistantMessage.new(
|
|
231
|
+
# id: "asst_1",
|
|
232
|
+
# content: "Hello!",
|
|
233
|
+
# tool_calls: [
|
|
234
|
+
# {
|
|
235
|
+
# id: "tc_1",
|
|
236
|
+
# function: { name: "search", arguments: "{\"q\":\"AG-UI\"}" }
|
|
237
|
+
# }
|
|
238
|
+
# ]
|
|
239
|
+
# )
|
|
240
|
+
#
|
|
241
|
+
# ```
|
|
242
|
+
# @category Message Types
|
|
243
|
+
class AssistantMessage < BaseMessage
|
|
244
|
+
|
|
245
|
+
sig { returns(T.nilable(T::Array[ToolCall])) }
|
|
246
|
+
attr_reader :tool_calls
|
|
247
|
+
|
|
248
|
+
# @param id [String] Unique identifier for the message
|
|
249
|
+
# @param content [Object] Text content of the message
|
|
250
|
+
# @param tool_calls [Array<ToolCall, Hash>] Tool calls made in this message
|
|
251
|
+
# @param name [String] Name of the sender
|
|
252
|
+
sig { params(id: String, content: T.untyped, tool_calls: T.nilable(T::Array[ToolCall]), name: T.nilable(String)).void }
|
|
253
|
+
def initialize(id:, content: nil, tool_calls: nil, name: nil)
|
|
254
|
+
super(id: id, role: "assistant", content: content, name: name)
|
|
255
|
+
@tool_calls = tool_calls&.map do |tc|
|
|
256
|
+
tc.is_a?(ToolCall) ? tc : ToolCall.new(**tc)
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
|
261
|
+
def to_h
|
|
262
|
+
super.merge(tool_calls: @tool_calls)
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
# Represents a text fragment inside a multimodal user message.
|
|
267
|
+
#
|
|
268
|
+
# ```ruby
|
|
269
|
+
#
|
|
270
|
+
# content = AgUiProtocol::Core::Types::TextInputContent.new(text: "hello")
|
|
271
|
+
#
|
|
272
|
+
# ```
|
|
273
|
+
# @category Message Types
|
|
274
|
+
class TextInputContent < Model
|
|
275
|
+
sig { returns(String) }
|
|
276
|
+
attr_reader :type
|
|
277
|
+
|
|
278
|
+
sig { returns(String) }
|
|
279
|
+
attr_reader :text
|
|
280
|
+
|
|
281
|
+
# @param text [String] Text content
|
|
282
|
+
# @param type [String] Identifies the fragment type
|
|
283
|
+
sig { params(text: String, type: String).void }
|
|
284
|
+
def initialize(text:, type: "text")
|
|
285
|
+
@type = type
|
|
286
|
+
@text = text
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
|
290
|
+
def to_h
|
|
291
|
+
{
|
|
292
|
+
type: @type,
|
|
293
|
+
text: @text
|
|
294
|
+
}
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
# Represents binary data such as images, audio, or files.
|
|
299
|
+
#
|
|
300
|
+
# ```ruby
|
|
301
|
+
#
|
|
302
|
+
# content = AgUiProtocol::Core::Types::BinaryInputContent.new(
|
|
303
|
+
# mime_type: "image/png",
|
|
304
|
+
# url: "https://example.com/cat.png"
|
|
305
|
+
# )
|
|
306
|
+
#
|
|
307
|
+
# ```
|
|
308
|
+
#
|
|
309
|
+
# > **Validation:** At least one of `id`, `url`, or `data` must be provided.
|
|
310
|
+
# @category Message Types
|
|
311
|
+
class BinaryInputContent < Model
|
|
312
|
+
sig { returns(String) }
|
|
313
|
+
attr_reader :type
|
|
314
|
+
|
|
315
|
+
sig { returns(String) }
|
|
316
|
+
attr_reader :mime_type
|
|
317
|
+
|
|
318
|
+
sig { returns(T.nilable(String)) }
|
|
319
|
+
attr_reader :id
|
|
320
|
+
|
|
321
|
+
sig { returns(T.nilable(String)) }
|
|
322
|
+
attr_reader :url
|
|
323
|
+
|
|
324
|
+
sig { returns(T.nilable(String)) }
|
|
325
|
+
attr_reader :data
|
|
326
|
+
|
|
327
|
+
sig { returns(T.nilable(String)) }
|
|
328
|
+
attr_reader :filename
|
|
329
|
+
|
|
330
|
+
# @param type [String] Identifies the fragment type
|
|
331
|
+
# @param mime_type [String] MIME type, for example `"image/png"`
|
|
332
|
+
# @param id [String] Reference to previously uploaded content
|
|
333
|
+
# @param url [String] Remote URL where the content can be retrieved
|
|
334
|
+
# @param data [String] Base64 encoded content
|
|
335
|
+
# @param filename [String] Optional filename hint
|
|
336
|
+
sig do
|
|
337
|
+
params(
|
|
338
|
+
mime_type: String,
|
|
339
|
+
type: String,
|
|
340
|
+
id: T.nilable(String),
|
|
341
|
+
url: T.nilable(String),
|
|
342
|
+
data: T.nilable(String),
|
|
343
|
+
filename: T.nilable(String)
|
|
344
|
+
).void
|
|
345
|
+
end
|
|
346
|
+
def initialize(mime_type:, type: "binary", id: nil, url: nil, data: nil, filename: nil)
|
|
347
|
+
if [id, url, data].all?(&:nil?)
|
|
348
|
+
raise ArgumentError, "BinaryInputContent requires id, url, or data to be provided."
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
@type = type
|
|
352
|
+
@mime_type = mime_type
|
|
353
|
+
@id = id
|
|
354
|
+
@url = url
|
|
355
|
+
@data = data
|
|
356
|
+
@filename = filename
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
|
360
|
+
def to_h
|
|
361
|
+
{
|
|
362
|
+
type: @type,
|
|
363
|
+
mime_type: @mime_type,
|
|
364
|
+
id: @id,
|
|
365
|
+
url: @url,
|
|
366
|
+
data: @data,
|
|
367
|
+
filename: @filename
|
|
368
|
+
}
|
|
369
|
+
end
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
# Represents a message from a user.
|
|
373
|
+
#
|
|
374
|
+
# ```ruby
|
|
375
|
+
#
|
|
376
|
+
# msg = AgUiProtocol::Core::Types::UserMessage.new(
|
|
377
|
+
# id: "user_2",
|
|
378
|
+
# content: [
|
|
379
|
+
# { type: "text", text: "Please describe this image" },
|
|
380
|
+
# { type: "binary", mimeType: "image/png", url: "https://example.com/cat.png" }
|
|
381
|
+
# ]
|
|
382
|
+
# )
|
|
383
|
+
#
|
|
384
|
+
# ```
|
|
385
|
+
# @category Message Types
|
|
386
|
+
class UserMessage < BaseMessage
|
|
387
|
+
sig { returns(String) }
|
|
388
|
+
attr_reader :id
|
|
389
|
+
|
|
390
|
+
sig { returns(String) }
|
|
391
|
+
attr_reader :role
|
|
392
|
+
|
|
393
|
+
sig { returns(T.any(String, T::Array[T.any(TextInputContent, BinaryInputContent)])) }
|
|
394
|
+
attr_reader :content
|
|
395
|
+
|
|
396
|
+
sig { returns(T.nilable(String)) }
|
|
397
|
+
attr_reader :name
|
|
398
|
+
|
|
399
|
+
# @param id [String] Unique identifier for the message
|
|
400
|
+
# @param content [String, Array<TextInputContent | BinaryInputContent>] Either a plain text string or an ordered list of multimodal fragments
|
|
401
|
+
# @param name [String] Optional name of the sender
|
|
402
|
+
sig { params(id: String, content: T.any(String, T::Array[T.any(TextInputContent, BinaryInputContent)]), name: T.nilable(String)).void }
|
|
403
|
+
def initialize(id:, content:, name: nil)
|
|
404
|
+
super(id: id, role: "user", content: normalize_user_content(content), name: name)
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
sig { params(content: T.any(String, T::Array[T.any(TextInputContent, BinaryInputContent)])).returns(T.any(String, T::Array[T.any(TextInputContent, BinaryInputContent)])) }
|
|
408
|
+
def normalize_user_content(content)
|
|
409
|
+
if content.is_a?(Array)
|
|
410
|
+
content.map do |c|
|
|
411
|
+
if c.is_a?(Model)
|
|
412
|
+
c
|
|
413
|
+
elsif c.is_a?(Hash)
|
|
414
|
+
case c[:type] || c["type"]
|
|
415
|
+
when "text"
|
|
416
|
+
TextInputContent.new(text: c[:text] || c["text"])
|
|
417
|
+
when "binary"
|
|
418
|
+
BinaryInputContent.new(
|
|
419
|
+
mime_type: c[:mime_type] || c["mime_type"] || c[:mimeType] || c["mimeType"],
|
|
420
|
+
id: c[:id] || c["id"],
|
|
421
|
+
url: c[:url] || c["url"],
|
|
422
|
+
data: c[:data] || c["data"],
|
|
423
|
+
filename: c[:filename] || c["filename"]
|
|
424
|
+
)
|
|
425
|
+
else
|
|
426
|
+
c
|
|
427
|
+
end
|
|
428
|
+
else
|
|
429
|
+
c
|
|
430
|
+
end
|
|
431
|
+
end
|
|
432
|
+
else
|
|
433
|
+
content
|
|
434
|
+
end
|
|
435
|
+
end
|
|
436
|
+
|
|
437
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
|
438
|
+
def to_h
|
|
439
|
+
{
|
|
440
|
+
id: @id,
|
|
441
|
+
role: @role,
|
|
442
|
+
content: @content,
|
|
443
|
+
name: @name
|
|
444
|
+
}
|
|
445
|
+
end
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
# Tool result message.
|
|
449
|
+
#
|
|
450
|
+
# ```ruby
|
|
451
|
+
#
|
|
452
|
+
# msg = AgUiProtocol::Core::Types::ToolMessage.new(
|
|
453
|
+
# id: "tool_msg_1",
|
|
454
|
+
# tool_call_id: "tc_1",
|
|
455
|
+
# content: "ok"
|
|
456
|
+
# )
|
|
457
|
+
#
|
|
458
|
+
# ```
|
|
459
|
+
# @category Message Types
|
|
460
|
+
class ToolMessage < BaseMessage
|
|
461
|
+
sig { returns(String) }
|
|
462
|
+
attr_reader :id
|
|
463
|
+
|
|
464
|
+
sig { returns(String) }
|
|
465
|
+
attr_reader :role
|
|
466
|
+
|
|
467
|
+
sig { returns(String) }
|
|
468
|
+
attr_reader :content
|
|
469
|
+
|
|
470
|
+
sig { returns(String) }
|
|
471
|
+
attr_reader :tool_call_id
|
|
472
|
+
|
|
473
|
+
sig { returns(T.nilable(String)) }
|
|
474
|
+
attr_reader :error
|
|
475
|
+
|
|
476
|
+
# @param id [String] Unique identifier for the message.
|
|
477
|
+
# @param content [String] Tool result content.
|
|
478
|
+
# @param tool_call_id [String] ID of the tool call this message responds to.
|
|
479
|
+
# @param error [String] Error payload if the tool call failed.
|
|
480
|
+
sig { params(id: String, content: String, tool_call_id: String, error: T.nilable(String)).void }
|
|
481
|
+
def initialize(id:, content:, tool_call_id:, error: nil)
|
|
482
|
+
super(id: id, role: "tool", content: content)
|
|
483
|
+
@tool_call_id = tool_call_id
|
|
484
|
+
@error = error
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
|
488
|
+
def to_h
|
|
489
|
+
{
|
|
490
|
+
id: @id,
|
|
491
|
+
role: @role,
|
|
492
|
+
content: @content,
|
|
493
|
+
tool_call_id: @tool_call_id,
|
|
494
|
+
error: @error
|
|
495
|
+
}
|
|
496
|
+
end
|
|
497
|
+
end
|
|
498
|
+
|
|
499
|
+
# Represents structured activity progress emitted between chat messages.
|
|
500
|
+
#
|
|
501
|
+
# ```ruby
|
|
502
|
+
#
|
|
503
|
+
# msg = AgUiProtocol::Core::Types::ActivityMessage.new(
|
|
504
|
+
# id: "activity_1",
|
|
505
|
+
# activity_type: "progress",
|
|
506
|
+
# content: { "pct" => 10 }
|
|
507
|
+
# )
|
|
508
|
+
#
|
|
509
|
+
# ```
|
|
510
|
+
# @category Message Types
|
|
511
|
+
class ActivityMessage < BaseMessage
|
|
512
|
+
sig { returns(String) }
|
|
513
|
+
attr_reader :id
|
|
514
|
+
|
|
515
|
+
sig { returns(String) }
|
|
516
|
+
attr_reader :role
|
|
517
|
+
|
|
518
|
+
sig { returns(String) }
|
|
519
|
+
attr_reader :activity_type
|
|
520
|
+
|
|
521
|
+
sig { returns(T::Hash[T.any(Symbol, String), T.untyped]) }
|
|
522
|
+
attr_reader :content
|
|
523
|
+
|
|
524
|
+
# @param id [String] Unique identifier for the activity message.
|
|
525
|
+
# @param activity_type [String] Activity discriminator used for renderer selection.
|
|
526
|
+
# @param content [Hash] Structured payload representing the activity state.
|
|
527
|
+
sig { params(id: String, activity_type: String, content: T::Hash[T.any(Symbol, String), T.untyped]).void }
|
|
528
|
+
def initialize(id:, activity_type:, content:)
|
|
529
|
+
@id = id
|
|
530
|
+
@role = 'activity'
|
|
531
|
+
@activity_type = activity_type
|
|
532
|
+
@content = content
|
|
533
|
+
end
|
|
534
|
+
|
|
535
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
|
536
|
+
def to_h
|
|
537
|
+
{
|
|
538
|
+
id: @id,
|
|
539
|
+
role: @role,
|
|
540
|
+
activity_type: @activity_type,
|
|
541
|
+
content: @content
|
|
542
|
+
}
|
|
543
|
+
end
|
|
544
|
+
end
|
|
545
|
+
|
|
546
|
+
# Represents a piece of contextual information provided to an agent.
|
|
547
|
+
#
|
|
548
|
+
# ```ruby
|
|
549
|
+
#
|
|
550
|
+
# ctx = AgUiProtocol::Core::Types::Context.new(description: "User locale", value: "es-CL")
|
|
551
|
+
#
|
|
552
|
+
# ```
|
|
553
|
+
class Context < Model
|
|
554
|
+
sig { returns(String) }
|
|
555
|
+
attr_reader :description
|
|
556
|
+
|
|
557
|
+
sig { returns(String) }
|
|
558
|
+
attr_reader :value
|
|
559
|
+
|
|
560
|
+
# @param description [String] Description of what this context represents.
|
|
561
|
+
# @param value [String] The actual context value.
|
|
562
|
+
sig { params(description: String, value: String).void }
|
|
563
|
+
def initialize(description:, value:)
|
|
564
|
+
@description = description
|
|
565
|
+
@value = value
|
|
566
|
+
end
|
|
567
|
+
|
|
568
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
|
569
|
+
def to_h
|
|
570
|
+
{
|
|
571
|
+
description: @description,
|
|
572
|
+
value: @value
|
|
573
|
+
}
|
|
574
|
+
end
|
|
575
|
+
end
|
|
576
|
+
|
|
577
|
+
# Defines a tool that can be called by an agent.
|
|
578
|
+
#
|
|
579
|
+
# ```ruby
|
|
580
|
+
#
|
|
581
|
+
# tool = AgUiProtocol::Core::Types::Tool.new(
|
|
582
|
+
# name: "search",
|
|
583
|
+
# description: "Search the web",
|
|
584
|
+
# parameters: { "type" => "object", "properties" => { "q" => { "type" => "string" } } }
|
|
585
|
+
# )
|
|
586
|
+
#
|
|
587
|
+
# ```
|
|
588
|
+
class Tool < Model
|
|
589
|
+
sig { returns(String) }
|
|
590
|
+
attr_reader :name
|
|
591
|
+
|
|
592
|
+
sig { returns(String) }
|
|
593
|
+
attr_reader :description
|
|
594
|
+
|
|
595
|
+
sig { returns(T.untyped) }
|
|
596
|
+
attr_reader :parameters
|
|
597
|
+
|
|
598
|
+
# @param name [String] Name of the tool.
|
|
599
|
+
# @param description [String] Description of what the tool does.
|
|
600
|
+
# @param parameters [Object] JSON Schema for tool parameters.
|
|
601
|
+
sig { params(name: String, description: String, parameters: T.untyped).void }
|
|
602
|
+
def initialize(name:, description:, parameters:)
|
|
603
|
+
@name = name
|
|
604
|
+
@description = description
|
|
605
|
+
@parameters = parameters
|
|
606
|
+
end
|
|
607
|
+
|
|
608
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
|
609
|
+
def to_h
|
|
610
|
+
{
|
|
611
|
+
name: @name,
|
|
612
|
+
description: @description,
|
|
613
|
+
parameters: @parameters
|
|
614
|
+
}
|
|
615
|
+
end
|
|
616
|
+
end
|
|
617
|
+
|
|
618
|
+
# Input parameters for running an agent. In the HTTP API, this is the body of the `POST` request.
|
|
619
|
+
#
|
|
620
|
+
# ```ruby
|
|
621
|
+
#
|
|
622
|
+
# input = AgUiProtocol::Core::Types::RunAgentInput.new(
|
|
623
|
+
# thread_id: "thread_123",
|
|
624
|
+
# run_id: "run_123",
|
|
625
|
+
# parent_run_id: nil,
|
|
626
|
+
# state: {},
|
|
627
|
+
# messages: [],
|
|
628
|
+
# tools: [],
|
|
629
|
+
# context: [],
|
|
630
|
+
# forwarded_props: {}
|
|
631
|
+
# )
|
|
632
|
+
#
|
|
633
|
+
# ```
|
|
634
|
+
class RunAgentInput < Model
|
|
635
|
+
sig { returns(String) }
|
|
636
|
+
attr_reader :thread_id
|
|
637
|
+
|
|
638
|
+
sig { returns(String) }
|
|
639
|
+
attr_reader :run_id
|
|
640
|
+
|
|
641
|
+
sig { returns(T.nilable(String)) }
|
|
642
|
+
attr_reader :parent_run_id
|
|
643
|
+
|
|
644
|
+
sig { returns(T.untyped) }
|
|
645
|
+
attr_reader :state
|
|
646
|
+
|
|
647
|
+
sig { returns(T::Array[BaseMessage]) }
|
|
648
|
+
attr_reader :messages
|
|
649
|
+
|
|
650
|
+
sig { returns(T::Array[Tool]) }
|
|
651
|
+
attr_reader :tools
|
|
652
|
+
|
|
653
|
+
sig { returns(T::Array[Context]) }
|
|
654
|
+
attr_reader :context
|
|
655
|
+
|
|
656
|
+
sig { returns(T.untyped) }
|
|
657
|
+
attr_reader :forwarded_props
|
|
658
|
+
|
|
659
|
+
# @param thread_id [String] ID of the conversation thread
|
|
660
|
+
# @param run_id [String] ID of the current run
|
|
661
|
+
# @param state [Object] Current state of the agent
|
|
662
|
+
# @param messages [Array<BaseMessage>] List of messages in the conversation
|
|
663
|
+
# @param tools [Array<Tool>] List of tools available to the agent
|
|
664
|
+
# @param context [Array<Context>] List of context objects provided to the agent
|
|
665
|
+
# @param forwarded_props [Object] Additional properties forwarded to the agent
|
|
666
|
+
# @param parent_run_id [String] Lineage pointer for branching/time travel
|
|
667
|
+
# @raise [ArgumentError] if messages is not an Array of BaseMessage
|
|
668
|
+
sig do
|
|
669
|
+
params(
|
|
670
|
+
thread_id: String,
|
|
671
|
+
run_id: String,
|
|
672
|
+
state: T.untyped,
|
|
673
|
+
messages: T::Array[BaseMessage],
|
|
674
|
+
tools: T::Array[Tool],
|
|
675
|
+
context: T::Array[Context],
|
|
676
|
+
forwarded_props: T.untyped,
|
|
677
|
+
parent_run_id: T.nilable(String)
|
|
678
|
+
).void.checked(:always)
|
|
679
|
+
end
|
|
680
|
+
def initialize(thread_id:, run_id:, state:, messages:, tools:, context:, forwarded_props:, parent_run_id: nil)
|
|
681
|
+
unless messages.is_a?(Array) && messages.all? { |m| m.is_a?(BaseMessage) }
|
|
682
|
+
raise ArgumentError, "messages must be an Array of BaseMessage"
|
|
683
|
+
end
|
|
684
|
+
unless tools.is_a?(Array) && tools.all? { |m| m.is_a?(Tool) }
|
|
685
|
+
raise ArgumentError, "tools must be an Array of Tool"
|
|
686
|
+
end
|
|
687
|
+
unless context.is_a?(Array) && context.all? { |m| m.is_a?(Context) }
|
|
688
|
+
raise ArgumentError, "context must be an Array of Context"
|
|
689
|
+
end
|
|
690
|
+
|
|
691
|
+
@thread_id = thread_id
|
|
692
|
+
@run_id = run_id
|
|
693
|
+
@parent_run_id = parent_run_id
|
|
694
|
+
@state = state
|
|
695
|
+
@messages = messages
|
|
696
|
+
@tools = tools
|
|
697
|
+
@context = context
|
|
698
|
+
@forwarded_props = forwarded_props
|
|
699
|
+
end
|
|
700
|
+
|
|
701
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
|
702
|
+
def to_h
|
|
703
|
+
{
|
|
704
|
+
thread_id: @thread_id,
|
|
705
|
+
run_id: @run_id,
|
|
706
|
+
parent_run_id: @parent_run_id,
|
|
707
|
+
state: @state,
|
|
708
|
+
messages: @messages,
|
|
709
|
+
tools: @tools,
|
|
710
|
+
context: @context,
|
|
711
|
+
forwarded_props: @forwarded_props
|
|
712
|
+
}
|
|
713
|
+
end
|
|
714
|
+
end
|
|
715
|
+
end
|
|
716
|
+
end
|
|
717
|
+
end
|