ag-ui-protocol 0.1.4 → 0.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.
- checksums.yaml +4 -4
- data/lib/ag_ui_protocol/core/capabilities.rb +733 -0
- data/lib/ag_ui_protocol/core/events.rb +385 -28
- data/lib/ag_ui_protocol/core/types.rb +578 -80
- data/lib/ag_ui_protocol/encoder/event_encoder.rb +3 -4
- data/lib/ag_ui_protocol/util.rb +44 -1
- data/lib/ag_ui_protocol/version.rb +1 -1
- data/lib/ag_ui_protocol.rb +1 -0
- metadata +6 -8
|
@@ -21,11 +21,10 @@ module AgUiProtocol
|
|
|
21
21
|
# ```ruby
|
|
22
22
|
#
|
|
23
23
|
# AgUiProtocol::Core::Types::Role
|
|
24
|
-
# # => ["developer", "system", "assistant", "user", "tool", "activity"]
|
|
24
|
+
# # => ["developer", "system", "assistant", "user", "tool", "activity", "reasoning"]
|
|
25
25
|
#
|
|
26
26
|
# ```
|
|
27
|
-
|
|
28
|
-
Role = ["developer", "system", "assistant", "user", "tool", "activity"].freeze
|
|
27
|
+
Role = ["developer", "system", "assistant", "user", "tool", "activity", "reasoning"].freeze
|
|
29
28
|
|
|
30
29
|
# Base model for protocol entities.
|
|
31
30
|
#
|
|
@@ -41,7 +40,7 @@ module AgUiProtocol
|
|
|
41
40
|
# @return [Hash<Symbol, Object>, raise NotImplementedError]
|
|
42
41
|
sig { returns(T::Hash[Symbol, T.untyped]) }
|
|
43
42
|
def to_h
|
|
44
|
-
raise NotImplementedError,
|
|
43
|
+
raise NotImplementedError, "Implement this method in a concrete subclass"
|
|
45
44
|
end
|
|
46
45
|
|
|
47
46
|
# Returns a JSON-ready representation.
|
|
@@ -119,14 +118,23 @@ module AgUiProtocol
|
|
|
119
118
|
sig { returns(FunctionCall) }
|
|
120
119
|
attr_reader :function
|
|
121
120
|
|
|
121
|
+
sig { returns(T.nilable(String)) }
|
|
122
|
+
attr_reader :encrypted_value
|
|
123
|
+
|
|
122
124
|
# @param id [String] Unique identifier for the tool call
|
|
123
125
|
# @param function [FunctionCall, Hash] Function name and arguments
|
|
124
126
|
# @param type [String] Type of the tool call
|
|
125
|
-
|
|
126
|
-
|
|
127
|
+
# @param encrypted_value [String] Encrypted tool call value for zero-data-retention mode
|
|
128
|
+
sig { params(id: String, function: T.untyped, type: String, encrypted_value: T.nilable(String)).void }
|
|
129
|
+
def initialize(id:, function:, type: 'function', encrypted_value: nil)
|
|
127
130
|
@id = id
|
|
128
131
|
@type = type
|
|
129
|
-
@function = function.is_a?(FunctionCall)
|
|
132
|
+
@function = if function.is_a?(FunctionCall)
|
|
133
|
+
function
|
|
134
|
+
else
|
|
135
|
+
FunctionCall.new(**function.transform_keys(&:to_sym))
|
|
136
|
+
end
|
|
137
|
+
@encrypted_value = encrypted_value
|
|
130
138
|
end
|
|
131
139
|
|
|
132
140
|
sig { returns(T::Hash[Symbol, T.untyped]) }
|
|
@@ -134,7 +142,8 @@ module AgUiProtocol
|
|
|
134
142
|
{
|
|
135
143
|
id: @id,
|
|
136
144
|
type: @type,
|
|
137
|
-
function: @function
|
|
145
|
+
function: @function,
|
|
146
|
+
encrypted_value: @encrypted_value
|
|
138
147
|
}
|
|
139
148
|
end
|
|
140
149
|
end
|
|
@@ -147,22 +156,35 @@ module AgUiProtocol
|
|
|
147
156
|
sig { returns(String) }
|
|
148
157
|
attr_reader :role
|
|
149
158
|
|
|
150
|
-
sig { returns(T.nilable(T.any(String, T::Array[T.any(TextInputContent, BinaryInputContent)]))) }
|
|
159
|
+
sig { returns(T.nilable(T.any(String, T::Array[T.any(TextInputContent, BinaryInputContent, ImageInputContent, AudioInputContent, VideoInputContent, DocumentInputContent)]))) }
|
|
151
160
|
attr_reader :content
|
|
152
161
|
|
|
153
162
|
sig { returns(T.nilable(String)) }
|
|
154
163
|
attr_reader :name
|
|
155
164
|
|
|
165
|
+
sig { returns(T.nilable(String)) }
|
|
166
|
+
attr_reader :encrypted_value
|
|
167
|
+
|
|
156
168
|
# @param id [String] Unique identifier for the message
|
|
157
169
|
# @param role [String] Role of the message sender
|
|
158
170
|
# @param content [Object] Text content of the message
|
|
159
171
|
# @param name [String] Optional name of the sender
|
|
160
|
-
|
|
161
|
-
|
|
172
|
+
# @param encrypted_value [String] Encrypted content for zero-data-retention mode
|
|
173
|
+
sig do
|
|
174
|
+
params(
|
|
175
|
+
id: String,
|
|
176
|
+
role: String,
|
|
177
|
+
content: T.nilable(T.any(String, T::Array[T.any(TextInputContent, BinaryInputContent, ImageInputContent, AudioInputContent, VideoInputContent, DocumentInputContent)])),
|
|
178
|
+
name: T.nilable(String),
|
|
179
|
+
encrypted_value: T.nilable(String)
|
|
180
|
+
).void
|
|
181
|
+
end
|
|
182
|
+
def initialize(id:, role:, content: nil, name: nil, encrypted_value: nil)
|
|
162
183
|
@id = id
|
|
163
184
|
@role = role
|
|
164
185
|
@content = content
|
|
165
186
|
@name = name
|
|
187
|
+
@encrypted_value = encrypted_value
|
|
166
188
|
end
|
|
167
189
|
|
|
168
190
|
sig { returns(T::Hash[Symbol, T.untyped]) }
|
|
@@ -171,7 +193,8 @@ module AgUiProtocol
|
|
|
171
193
|
id: @id,
|
|
172
194
|
role: @role,
|
|
173
195
|
content: @content,
|
|
174
|
-
name: @name
|
|
196
|
+
name: @name,
|
|
197
|
+
encrypted_value: @encrypted_value
|
|
175
198
|
}
|
|
176
199
|
end
|
|
177
200
|
end
|
|
@@ -247,13 +270,23 @@ module AgUiProtocol
|
|
|
247
270
|
|
|
248
271
|
# @param id [String] Unique identifier for the message
|
|
249
272
|
# @param content [Object] Text content of the message
|
|
250
|
-
# @param tool_calls [Array<ToolCall, Hash>] Tool calls made in this message
|
|
273
|
+
# @param tool_calls [Array<ToolCall, Hash>] Tool calls made in this message; Hashes are normalized to ToolCall instances.
|
|
251
274
|
# @param name [String] Name of the sender
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
275
|
+
# @param encrypted_value [String] Encrypted content for zero-data-retention mode
|
|
276
|
+
sig do
|
|
277
|
+
params(
|
|
278
|
+
id: String,
|
|
279
|
+
content: T.untyped,
|
|
280
|
+
tool_calls: T.nilable(T::Array[T.any(ToolCall, T::Hash[T.any(Symbol, String), T.untyped])]),
|
|
281
|
+
name: T.nilable(String),
|
|
282
|
+
encrypted_value: T.nilable(String)
|
|
283
|
+
).void
|
|
284
|
+
end
|
|
285
|
+
def initialize(id:, content: nil, tool_calls: nil, name: nil, encrypted_value: nil)
|
|
286
|
+
super(id: id, role: "assistant", content: content, name: name, encrypted_value: encrypted_value)
|
|
255
287
|
@tool_calls = tool_calls&.map do |tc|
|
|
256
|
-
tc.is_a?(ToolCall)
|
|
288
|
+
next tc if tc.is_a?(ToolCall)
|
|
289
|
+
ToolCall.new(**tc.transform_keys(&:to_sym))
|
|
257
290
|
end
|
|
258
291
|
end
|
|
259
292
|
|
|
@@ -344,8 +377,8 @@ module AgUiProtocol
|
|
|
344
377
|
).void
|
|
345
378
|
end
|
|
346
379
|
def initialize(mime_type:, type: "binary", id: nil, url: nil, data: nil, filename: nil)
|
|
347
|
-
if [id, url, data].
|
|
348
|
-
raise ArgumentError, "BinaryInputContent requires id, url, or data to be
|
|
380
|
+
if [id, url, data].none? { |v| v.is_a?(String) && !v.empty? }
|
|
381
|
+
raise ArgumentError, "BinaryInputContent requires at least one of id, url, or data to be a non-empty string"
|
|
349
382
|
end
|
|
350
383
|
|
|
351
384
|
@type = type
|
|
@@ -384,34 +417,47 @@ module AgUiProtocol
|
|
|
384
417
|
# ```
|
|
385
418
|
# @category Message Types
|
|
386
419
|
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
420
|
# @param id [String] Unique identifier for the message
|
|
400
|
-
# @param content [String, Array<TextInputContent | BinaryInputContent>]
|
|
421
|
+
# @param content [String, Array<TextInputContent | BinaryInputContent | ImageInputContent | AudioInputContent | VideoInputContent | DocumentInputContent | Hash>] Accepted shapes: a String for plain text, or an Array whose elements are either *InputContent Models (TextInputContent, BinaryInputContent, ImageInputContent, AudioInputContent, VideoInputContent, DocumentInputContent) OR Hashes with a `type:` key (`text`, `binary`, `image`, `audio`, `video`, `document`); Hash entries are normalized internally to the corresponding Model.
|
|
401
422
|
# @param name [String] Optional name of the sender
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
423
|
+
# @param encrypted_value [String] Encrypted content for zero-data-retention mode
|
|
424
|
+
sig do
|
|
425
|
+
params(
|
|
426
|
+
id: String,
|
|
427
|
+
content: T.any(
|
|
428
|
+
String,
|
|
429
|
+
T::Array[T.any(
|
|
430
|
+
TextInputContent, BinaryInputContent, ImageInputContent, AudioInputContent, VideoInputContent, DocumentInputContent,
|
|
431
|
+
T::Hash[T.any(Symbol, String), T.untyped]
|
|
432
|
+
)]
|
|
433
|
+
),
|
|
434
|
+
name: T.nilable(String),
|
|
435
|
+
encrypted_value: T.nilable(String)
|
|
436
|
+
).void
|
|
437
|
+
end
|
|
438
|
+
def initialize(id:, content:, name: nil, encrypted_value: nil)
|
|
439
|
+
super(id: id, role: "user", content: normalize_user_content(content), name: name, encrypted_value: encrypted_value)
|
|
405
440
|
end
|
|
406
441
|
|
|
407
|
-
sig
|
|
442
|
+
sig do
|
|
443
|
+
params(
|
|
444
|
+
content: T.any(
|
|
445
|
+
String,
|
|
446
|
+
T::Array[T.any(
|
|
447
|
+
TextInputContent, BinaryInputContent, ImageInputContent, AudioInputContent, VideoInputContent, DocumentInputContent,
|
|
448
|
+
T::Hash[T.any(Symbol, String), T.untyped]
|
|
449
|
+
)]
|
|
450
|
+
)
|
|
451
|
+
).returns(T.any(String, T::Array[T.any(TextInputContent, BinaryInputContent, ImageInputContent, AudioInputContent, VideoInputContent, DocumentInputContent)]))
|
|
452
|
+
end
|
|
408
453
|
def normalize_user_content(content)
|
|
409
454
|
if content.is_a?(Array)
|
|
410
455
|
content.map do |c|
|
|
411
456
|
if c.is_a?(Model)
|
|
412
457
|
c
|
|
413
458
|
elsif c.is_a?(Hash)
|
|
414
|
-
|
|
459
|
+
src_type = c[:type] || c["type"]
|
|
460
|
+
case src_type
|
|
415
461
|
when "text"
|
|
416
462
|
TextInputContent.new(text: c[:text] || c["text"])
|
|
417
463
|
when "binary"
|
|
@@ -420,13 +466,21 @@ module AgUiProtocol
|
|
|
420
466
|
id: c[:id] || c["id"],
|
|
421
467
|
url: c[:url] || c["url"],
|
|
422
468
|
data: c[:data] || c["data"],
|
|
423
|
-
filename: c[:filename] || c["filename"]
|
|
469
|
+
filename: c[:filename] || c["filename"] || c[:fileName] || c["fileName"]
|
|
424
470
|
)
|
|
471
|
+
when "image"
|
|
472
|
+
ImageInputContent.new(source: c[:source] || c["source"], metadata: c[:metadata] || c["metadata"])
|
|
473
|
+
when "audio"
|
|
474
|
+
AudioInputContent.new(source: c[:source] || c["source"], metadata: c[:metadata] || c["metadata"])
|
|
475
|
+
when "video"
|
|
476
|
+
VideoInputContent.new(source: c[:source] || c["source"], metadata: c[:metadata] || c["metadata"])
|
|
477
|
+
when "document"
|
|
478
|
+
DocumentInputContent.new(source: c[:source] || c["source"], metadata: c[:metadata] || c["metadata"])
|
|
425
479
|
else
|
|
426
|
-
|
|
480
|
+
raise ArgumentError, "Unknown content type: #{src_type.inspect}"
|
|
427
481
|
end
|
|
428
482
|
else
|
|
429
|
-
c
|
|
483
|
+
raise ArgumentError, "Unknown content type: #{c.class}"
|
|
430
484
|
end
|
|
431
485
|
end
|
|
432
486
|
else
|
|
@@ -434,15 +488,6 @@ module AgUiProtocol
|
|
|
434
488
|
end
|
|
435
489
|
end
|
|
436
490
|
|
|
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
491
|
end
|
|
447
492
|
|
|
448
493
|
# Tool result message.
|
|
@@ -458,15 +503,6 @@ module AgUiProtocol
|
|
|
458
503
|
# ```
|
|
459
504
|
# @category Message Types
|
|
460
505
|
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
506
|
sig { returns(String) }
|
|
471
507
|
attr_reader :tool_call_id
|
|
472
508
|
|
|
@@ -477,27 +513,30 @@ module AgUiProtocol
|
|
|
477
513
|
# @param content [String] Tool result content.
|
|
478
514
|
# @param tool_call_id [String] ID of the tool call this message responds to.
|
|
479
515
|
# @param error [String] Error payload if the tool call failed.
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
516
|
+
# @param encrypted_value [String] Encrypted tool message value for zero-data-retention mode
|
|
517
|
+
sig { params(id: String, content: String, tool_call_id: String, error: T.nilable(String), encrypted_value: T.nilable(String)).void }
|
|
518
|
+
def initialize(id:, content:, tool_call_id:, error: nil, encrypted_value: nil)
|
|
519
|
+
super(id: id, role: "tool", content: content, encrypted_value: encrypted_value)
|
|
483
520
|
@tool_call_id = tool_call_id
|
|
484
521
|
@error = error
|
|
485
522
|
end
|
|
486
523
|
|
|
487
524
|
sig { returns(T::Hash[Symbol, T.untyped]) }
|
|
488
525
|
def to_h
|
|
489
|
-
|
|
490
|
-
id: @id,
|
|
491
|
-
role: @role,
|
|
492
|
-
content: @content,
|
|
526
|
+
super.merge(
|
|
493
527
|
tool_call_id: @tool_call_id,
|
|
494
528
|
error: @error
|
|
495
|
-
|
|
529
|
+
)
|
|
496
530
|
end
|
|
497
531
|
end
|
|
498
532
|
|
|
499
533
|
# Represents structured activity progress emitted between chat messages.
|
|
500
534
|
#
|
|
535
|
+
# ActivityMessage intentionally does NOT inherit from BaseMessage because its
|
|
536
|
+
# `content` is a structured Hash rather than text/multimodal, which is
|
|
537
|
+
# incompatible with BaseMessage#content's type. It still appears alongside
|
|
538
|
+
# other messages in the stream but is modeled as its own shape.
|
|
539
|
+
#
|
|
501
540
|
# ```ruby
|
|
502
541
|
#
|
|
503
542
|
# msg = AgUiProtocol::Core::Types::ActivityMessage.new(
|
|
@@ -508,7 +547,7 @@ module AgUiProtocol
|
|
|
508
547
|
#
|
|
509
548
|
# ```
|
|
510
549
|
# @category Message Types
|
|
511
|
-
class ActivityMessage <
|
|
550
|
+
class ActivityMessage < Model
|
|
512
551
|
sig { returns(String) }
|
|
513
552
|
attr_reader :id
|
|
514
553
|
|
|
@@ -527,7 +566,7 @@ module AgUiProtocol
|
|
|
527
566
|
sig { params(id: String, activity_type: String, content: T::Hash[T.any(Symbol, String), T.untyped]).void }
|
|
528
567
|
def initialize(id:, activity_type:, content:)
|
|
529
568
|
@id = id
|
|
530
|
-
@role =
|
|
569
|
+
@role = "activity"
|
|
531
570
|
@activity_type = activity_type
|
|
532
571
|
@content = content
|
|
533
572
|
end
|
|
@@ -538,7 +577,8 @@ module AgUiProtocol
|
|
|
538
577
|
id: @id,
|
|
539
578
|
role: @role,
|
|
540
579
|
activity_type: @activity_type,
|
|
541
|
-
content
|
|
580
|
+
# `content` is an arbitrary user-supplied payload — preserve keys verbatim on the wire.
|
|
581
|
+
content: @content.nil? ? nil : AgUiProtocol::Util::Opaque.new(@content)
|
|
542
582
|
}
|
|
543
583
|
end
|
|
544
584
|
end
|
|
@@ -610,7 +650,453 @@ module AgUiProtocol
|
|
|
610
650
|
{
|
|
611
651
|
name: @name,
|
|
612
652
|
description: @description,
|
|
613
|
-
parameters
|
|
653
|
+
# `parameters` is a JSON Schema document supplied by the user — preserve keys verbatim.
|
|
654
|
+
parameters: @parameters.nil? ? nil : AgUiProtocol::Util::Opaque.new(@parameters)
|
|
655
|
+
}
|
|
656
|
+
end
|
|
657
|
+
end
|
|
658
|
+
|
|
659
|
+
# Represents a data source for multimodal input content using base64-encoded data.
|
|
660
|
+
#
|
|
661
|
+
# ```ruby
|
|
662
|
+
#
|
|
663
|
+
# source = AgUiProtocol::Core::Types::InputContentDataSource.new(
|
|
664
|
+
# value: "base64encoded...",
|
|
665
|
+
# mime_type: "image/png"
|
|
666
|
+
# )
|
|
667
|
+
#
|
|
668
|
+
# ```
|
|
669
|
+
class InputContentDataSource < Model
|
|
670
|
+
sig { returns(String) }
|
|
671
|
+
attr_reader :type
|
|
672
|
+
|
|
673
|
+
sig { returns(String) }
|
|
674
|
+
attr_reader :value
|
|
675
|
+
|
|
676
|
+
sig { returns(String) }
|
|
677
|
+
attr_reader :mime_type
|
|
678
|
+
|
|
679
|
+
# @param value [String] Base64 encoded content
|
|
680
|
+
# @param mime_type [String] MIME type of the content
|
|
681
|
+
# @param type [String] Identifies the source type
|
|
682
|
+
sig { params(value: String, mime_type: String, type: String).void }
|
|
683
|
+
def initialize(value:, mime_type:, type: "data")
|
|
684
|
+
@type = type
|
|
685
|
+
@value = value
|
|
686
|
+
@mime_type = mime_type
|
|
687
|
+
end
|
|
688
|
+
|
|
689
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
|
690
|
+
def to_h
|
|
691
|
+
{
|
|
692
|
+
type: @type,
|
|
693
|
+
value: @value,
|
|
694
|
+
mime_type: @mime_type
|
|
695
|
+
}
|
|
696
|
+
end
|
|
697
|
+
end
|
|
698
|
+
|
|
699
|
+
# Represents a URL source for multimodal input content.
|
|
700
|
+
#
|
|
701
|
+
# ```ruby
|
|
702
|
+
#
|
|
703
|
+
# source = AgUiProtocol::Core::Types::InputContentUrlSource.new(
|
|
704
|
+
# value: "https://example.com/image.png",
|
|
705
|
+
# mime_type: "image/png"
|
|
706
|
+
# )
|
|
707
|
+
#
|
|
708
|
+
# ```
|
|
709
|
+
class InputContentUrlSource < Model
|
|
710
|
+
sig { returns(String) }
|
|
711
|
+
attr_reader :type
|
|
712
|
+
|
|
713
|
+
sig { returns(String) }
|
|
714
|
+
attr_reader :value
|
|
715
|
+
|
|
716
|
+
sig { returns(T.nilable(String)) }
|
|
717
|
+
attr_reader :mime_type
|
|
718
|
+
|
|
719
|
+
# @param value [String] URL string
|
|
720
|
+
# @param mime_type [String] Optional MIME type
|
|
721
|
+
# @param type [String] Identifies the source type
|
|
722
|
+
sig { params(value: String, mime_type: T.nilable(String), type: String).void }
|
|
723
|
+
def initialize(value:, mime_type: nil, type: "url")
|
|
724
|
+
@type = type
|
|
725
|
+
@value = value
|
|
726
|
+
@mime_type = mime_type
|
|
727
|
+
end
|
|
728
|
+
|
|
729
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
|
730
|
+
def to_h
|
|
731
|
+
{
|
|
732
|
+
type: @type,
|
|
733
|
+
value: @value,
|
|
734
|
+
mime_type: @mime_type
|
|
735
|
+
}
|
|
736
|
+
end
|
|
737
|
+
end
|
|
738
|
+
|
|
739
|
+
# Represents an image content fragment inside a multimodal user message.
|
|
740
|
+
#
|
|
741
|
+
# ```ruby
|
|
742
|
+
#
|
|
743
|
+
# content = AgUiProtocol::Core::Types::ImageInputContent.new(
|
|
744
|
+
# source: { type: "url", value: "https://example.com/cat.png", mime_type: "image/png" }
|
|
745
|
+
# )
|
|
746
|
+
#
|
|
747
|
+
# ```
|
|
748
|
+
class ImageInputContent < Model
|
|
749
|
+
sig { returns(String) }
|
|
750
|
+
attr_reader :type
|
|
751
|
+
|
|
752
|
+
sig { returns(T.any(InputContentDataSource, InputContentUrlSource)) }
|
|
753
|
+
attr_reader :source
|
|
754
|
+
|
|
755
|
+
sig { returns(T.untyped) }
|
|
756
|
+
attr_reader :metadata
|
|
757
|
+
|
|
758
|
+
# @param source [InputContentDataSource, InputContentUrlSource, Hash] Content source
|
|
759
|
+
# @param metadata [Object] Optional metadata
|
|
760
|
+
# @param type [String] Identifies the fragment type
|
|
761
|
+
sig { params(source: T.untyped, metadata: T.untyped, type: String).void }
|
|
762
|
+
def initialize(source:, metadata: nil, type: "image")
|
|
763
|
+
@type = type
|
|
764
|
+
@source = source.is_a?(Hash) ? build_source(source) : source
|
|
765
|
+
@metadata = metadata
|
|
766
|
+
end
|
|
767
|
+
|
|
768
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
|
769
|
+
def to_h
|
|
770
|
+
{
|
|
771
|
+
type: @type,
|
|
772
|
+
source: @source,
|
|
773
|
+
metadata: @metadata
|
|
774
|
+
}
|
|
775
|
+
end
|
|
776
|
+
|
|
777
|
+
private
|
|
778
|
+
|
|
779
|
+
def build_source(hash)
|
|
780
|
+
src_type = hash[:type] || hash["type"]
|
|
781
|
+
value = hash[:value] || hash["value"]
|
|
782
|
+
mime_type = hash[:mime_type] || hash["mime_type"] || hash[:mimeType] || hash["mimeType"]
|
|
783
|
+
case src_type
|
|
784
|
+
when "data"
|
|
785
|
+
InputContentDataSource.new(value: value, mime_type: mime_type)
|
|
786
|
+
when "url"
|
|
787
|
+
InputContentUrlSource.new(value: value, mime_type: mime_type)
|
|
788
|
+
else
|
|
789
|
+
raise ArgumentError, "Unknown source type: #{src_type.inspect}"
|
|
790
|
+
end
|
|
791
|
+
end
|
|
792
|
+
end
|
|
793
|
+
|
|
794
|
+
# Represents an audio content fragment inside a multimodal user message.
|
|
795
|
+
#
|
|
796
|
+
# ```ruby
|
|
797
|
+
#
|
|
798
|
+
# content = AgUiProtocol::Core::Types::AudioInputContent.new(
|
|
799
|
+
# source: { type: "url", value: "https://example.com/audio.mp3", mime_type: "audio/mp3" }
|
|
800
|
+
# )
|
|
801
|
+
#
|
|
802
|
+
# ```
|
|
803
|
+
class AudioInputContent < Model
|
|
804
|
+
sig { returns(String) }
|
|
805
|
+
attr_reader :type
|
|
806
|
+
|
|
807
|
+
sig { returns(T.any(InputContentDataSource, InputContentUrlSource)) }
|
|
808
|
+
attr_reader :source
|
|
809
|
+
|
|
810
|
+
sig { returns(T.untyped) }
|
|
811
|
+
attr_reader :metadata
|
|
812
|
+
|
|
813
|
+
# @param source [InputContentDataSource, InputContentUrlSource, Hash] Content source
|
|
814
|
+
# @param metadata [Object] Optional metadata
|
|
815
|
+
# @param type [String] Identifies the fragment type
|
|
816
|
+
sig { params(source: T.untyped, metadata: T.untyped, type: String).void }
|
|
817
|
+
def initialize(source:, metadata: nil, type: "audio")
|
|
818
|
+
@type = type
|
|
819
|
+
@source = source.is_a?(Hash) ? build_source(source) : source
|
|
820
|
+
@metadata = metadata
|
|
821
|
+
end
|
|
822
|
+
|
|
823
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
|
824
|
+
def to_h
|
|
825
|
+
{
|
|
826
|
+
type: @type,
|
|
827
|
+
source: @source,
|
|
828
|
+
metadata: @metadata
|
|
829
|
+
}
|
|
830
|
+
end
|
|
831
|
+
|
|
832
|
+
private
|
|
833
|
+
|
|
834
|
+
def build_source(hash)
|
|
835
|
+
src_type = hash[:type] || hash["type"]
|
|
836
|
+
value = hash[:value] || hash["value"]
|
|
837
|
+
mime_type = hash[:mime_type] || hash["mime_type"] || hash[:mimeType] || hash["mimeType"]
|
|
838
|
+
case src_type
|
|
839
|
+
when "data"
|
|
840
|
+
InputContentDataSource.new(value: value, mime_type: mime_type)
|
|
841
|
+
when "url"
|
|
842
|
+
InputContentUrlSource.new(value: value, mime_type: mime_type)
|
|
843
|
+
else
|
|
844
|
+
raise ArgumentError, "Unknown source type: #{src_type.inspect}"
|
|
845
|
+
end
|
|
846
|
+
end
|
|
847
|
+
end
|
|
848
|
+
|
|
849
|
+
# Represents a video content fragment inside a multimodal user message.
|
|
850
|
+
#
|
|
851
|
+
# ```ruby
|
|
852
|
+
#
|
|
853
|
+
# content = AgUiProtocol::Core::Types::VideoInputContent.new(
|
|
854
|
+
# source: { type: "url", value: "https://example.com/video.mp4", mime_type: "video/mp4" }
|
|
855
|
+
# )
|
|
856
|
+
#
|
|
857
|
+
# ```
|
|
858
|
+
class VideoInputContent < Model
|
|
859
|
+
sig { returns(String) }
|
|
860
|
+
attr_reader :type
|
|
861
|
+
|
|
862
|
+
sig { returns(T.any(InputContentDataSource, InputContentUrlSource)) }
|
|
863
|
+
attr_reader :source
|
|
864
|
+
|
|
865
|
+
sig { returns(T.untyped) }
|
|
866
|
+
attr_reader :metadata
|
|
867
|
+
|
|
868
|
+
# @param source [InputContentDataSource, InputContentUrlSource, Hash] Content source
|
|
869
|
+
# @param metadata [Object] Optional metadata
|
|
870
|
+
# @param type [String] Identifies the fragment type
|
|
871
|
+
sig { params(source: T.untyped, metadata: T.untyped, type: String).void }
|
|
872
|
+
def initialize(source:, metadata: nil, type: "video")
|
|
873
|
+
@type = type
|
|
874
|
+
@source = source.is_a?(Hash) ? build_source(source) : source
|
|
875
|
+
@metadata = metadata
|
|
876
|
+
end
|
|
877
|
+
|
|
878
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
|
879
|
+
def to_h
|
|
880
|
+
{
|
|
881
|
+
type: @type,
|
|
882
|
+
source: @source,
|
|
883
|
+
metadata: @metadata
|
|
884
|
+
}
|
|
885
|
+
end
|
|
886
|
+
|
|
887
|
+
private
|
|
888
|
+
|
|
889
|
+
def build_source(hash)
|
|
890
|
+
src_type = hash[:type] || hash["type"]
|
|
891
|
+
value = hash[:value] || hash["value"]
|
|
892
|
+
mime_type = hash[:mime_type] || hash["mime_type"] || hash[:mimeType] || hash["mimeType"]
|
|
893
|
+
case src_type
|
|
894
|
+
when "data"
|
|
895
|
+
InputContentDataSource.new(value: value, mime_type: mime_type)
|
|
896
|
+
when "url"
|
|
897
|
+
InputContentUrlSource.new(value: value, mime_type: mime_type)
|
|
898
|
+
else
|
|
899
|
+
raise ArgumentError, "Unknown source type: #{src_type.inspect}"
|
|
900
|
+
end
|
|
901
|
+
end
|
|
902
|
+
end
|
|
903
|
+
|
|
904
|
+
# Represents a document content fragment inside a multimodal user message.
|
|
905
|
+
#
|
|
906
|
+
# ```ruby
|
|
907
|
+
#
|
|
908
|
+
# content = AgUiProtocol::Core::Types::DocumentInputContent.new(
|
|
909
|
+
# source: { type: "url", value: "https://example.com/doc.pdf", mime_type: "application/pdf" }
|
|
910
|
+
# )
|
|
911
|
+
#
|
|
912
|
+
# ```
|
|
913
|
+
class DocumentInputContent < Model
|
|
914
|
+
sig { returns(String) }
|
|
915
|
+
attr_reader :type
|
|
916
|
+
|
|
917
|
+
sig { returns(T.any(InputContentDataSource, InputContentUrlSource)) }
|
|
918
|
+
attr_reader :source
|
|
919
|
+
|
|
920
|
+
sig { returns(T.untyped) }
|
|
921
|
+
attr_reader :metadata
|
|
922
|
+
|
|
923
|
+
# @param source [InputContentDataSource, InputContentUrlSource, Hash] Content source
|
|
924
|
+
# @param metadata [Object] Optional metadata
|
|
925
|
+
# @param type [String] Identifies the fragment type
|
|
926
|
+
sig { params(source: T.untyped, metadata: T.untyped, type: String).void }
|
|
927
|
+
def initialize(source:, metadata: nil, type: "document")
|
|
928
|
+
@type = type
|
|
929
|
+
@source = source.is_a?(Hash) ? build_source(source) : source
|
|
930
|
+
@metadata = metadata
|
|
931
|
+
end
|
|
932
|
+
|
|
933
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
|
934
|
+
def to_h
|
|
935
|
+
{
|
|
936
|
+
type: @type,
|
|
937
|
+
source: @source,
|
|
938
|
+
metadata: @metadata
|
|
939
|
+
}
|
|
940
|
+
end
|
|
941
|
+
|
|
942
|
+
private
|
|
943
|
+
|
|
944
|
+
def build_source(hash)
|
|
945
|
+
src_type = hash[:type] || hash["type"]
|
|
946
|
+
value = hash[:value] || hash["value"]
|
|
947
|
+
mime_type = hash[:mime_type] || hash["mime_type"] || hash[:mimeType] || hash["mimeType"]
|
|
948
|
+
case src_type
|
|
949
|
+
when "data"
|
|
950
|
+
InputContentDataSource.new(value: value, mime_type: mime_type)
|
|
951
|
+
when "url"
|
|
952
|
+
InputContentUrlSource.new(value: value, mime_type: mime_type)
|
|
953
|
+
else
|
|
954
|
+
raise ArgumentError, "Unknown source type: #{src_type.inspect}"
|
|
955
|
+
end
|
|
956
|
+
end
|
|
957
|
+
end
|
|
958
|
+
|
|
959
|
+
# Represents a reasoning message from an agent with chain-of-thought content.
|
|
960
|
+
#
|
|
961
|
+
# ```ruby
|
|
962
|
+
#
|
|
963
|
+
# msg = AgUiProtocol::Core::Types::ReasoningMessage.new(
|
|
964
|
+
# id: "reason_1",
|
|
965
|
+
# content: "Let me think through this step by step...",
|
|
966
|
+
# encrypted_value: nil
|
|
967
|
+
# )
|
|
968
|
+
#
|
|
969
|
+
# ```
|
|
970
|
+
class ReasoningMessage < BaseMessage
|
|
971
|
+
|
|
972
|
+
sig { returns(String) }
|
|
973
|
+
attr_reader :content
|
|
974
|
+
|
|
975
|
+
# @param id [String] Unique identifier for the message
|
|
976
|
+
# @param content [String] Reasoning content (plaintext when not encrypted)
|
|
977
|
+
# @param encrypted_value [String] Encrypted reasoning content when in zero-data-retention mode
|
|
978
|
+
sig { params(id: String, content: String, encrypted_value: T.nilable(String)).void }
|
|
979
|
+
def initialize(id:, content:, encrypted_value: nil)
|
|
980
|
+
super(id: id, role: "reasoning", content: content, encrypted_value: encrypted_value)
|
|
981
|
+
end
|
|
982
|
+
end
|
|
983
|
+
|
|
984
|
+
# Represents an interrupt that occurred during agent execution for human-in-the-loop workflows.
|
|
985
|
+
#
|
|
986
|
+
# ```ruby
|
|
987
|
+
#
|
|
988
|
+
# interrupt = AgUiProtocol::Core::Types::Interrupt.new(
|
|
989
|
+
# id: "int_1",
|
|
990
|
+
# reason: "input_required",
|
|
991
|
+
# message: "Please provide additional information"
|
|
992
|
+
# )
|
|
993
|
+
#
|
|
994
|
+
# ```
|
|
995
|
+
class Interrupt < Model
|
|
996
|
+
sig { returns(String) }
|
|
997
|
+
attr_reader :id
|
|
998
|
+
|
|
999
|
+
sig { returns(String) }
|
|
1000
|
+
attr_reader :reason
|
|
1001
|
+
|
|
1002
|
+
sig { returns(T.nilable(String)) }
|
|
1003
|
+
attr_reader :message
|
|
1004
|
+
|
|
1005
|
+
sig { returns(T.nilable(String)) }
|
|
1006
|
+
attr_reader :tool_call_id
|
|
1007
|
+
|
|
1008
|
+
sig { returns(T.untyped) }
|
|
1009
|
+
attr_reader :response_schema
|
|
1010
|
+
|
|
1011
|
+
sig { returns(T.nilable(String)) }
|
|
1012
|
+
attr_reader :expires_at
|
|
1013
|
+
|
|
1014
|
+
sig { returns(T.untyped) }
|
|
1015
|
+
attr_reader :metadata
|
|
1016
|
+
|
|
1017
|
+
# @param id [String] Unique identifier
|
|
1018
|
+
# @param reason [String] Reason for the interrupt
|
|
1019
|
+
# @param message [String] Human-readable message
|
|
1020
|
+
# @param tool_call_id [String] Associated tool call if applicable
|
|
1021
|
+
# @param response_schema [Object] JSON schema for response
|
|
1022
|
+
# @param expires_at [String] ISO timestamp when interrupt expires
|
|
1023
|
+
# @param metadata [Object] Arbitrary metadata
|
|
1024
|
+
sig do
|
|
1025
|
+
params(
|
|
1026
|
+
id: String,
|
|
1027
|
+
reason: String,
|
|
1028
|
+
message: T.nilable(String),
|
|
1029
|
+
tool_call_id: T.nilable(String),
|
|
1030
|
+
response_schema: T.untyped,
|
|
1031
|
+
expires_at: T.nilable(String),
|
|
1032
|
+
metadata: T.untyped
|
|
1033
|
+
).void
|
|
1034
|
+
end
|
|
1035
|
+
def initialize(id:, reason:, message: nil, tool_call_id: nil, response_schema: nil, expires_at: nil, metadata: nil)
|
|
1036
|
+
@id = id
|
|
1037
|
+
@reason = reason
|
|
1038
|
+
@message = message
|
|
1039
|
+
@tool_call_id = tool_call_id
|
|
1040
|
+
@response_schema = response_schema
|
|
1041
|
+
@expires_at = expires_at
|
|
1042
|
+
@metadata = metadata
|
|
1043
|
+
end
|
|
1044
|
+
|
|
1045
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
|
1046
|
+
def to_h
|
|
1047
|
+
{
|
|
1048
|
+
id: @id,
|
|
1049
|
+
reason: @reason,
|
|
1050
|
+
message: @message,
|
|
1051
|
+
tool_call_id: @tool_call_id,
|
|
1052
|
+
# `response_schema` is a JSON Schema document supplied by the user — preserve keys verbatim.
|
|
1053
|
+
response_schema: @response_schema.nil? ? nil : AgUiProtocol::Util::Opaque.new(@response_schema),
|
|
1054
|
+
expires_at: @expires_at,
|
|
1055
|
+
# `metadata` is arbitrary user-defined key/value data — preserve keys verbatim.
|
|
1056
|
+
metadata: @metadata.nil? ? nil : AgUiProtocol::Util::Opaque.new(@metadata)
|
|
1057
|
+
}
|
|
1058
|
+
end
|
|
1059
|
+
end
|
|
1060
|
+
|
|
1061
|
+
# Represents an entry for resuming an interrupted run.
|
|
1062
|
+
#
|
|
1063
|
+
# ```ruby
|
|
1064
|
+
#
|
|
1065
|
+
# entry = AgUiProtocol::Core::Types::ResumeEntry.new(
|
|
1066
|
+
# interrupt_id: "int_1",
|
|
1067
|
+
# status: "resolved",
|
|
1068
|
+
# payload: { "answer" => "42" }
|
|
1069
|
+
# )
|
|
1070
|
+
#
|
|
1071
|
+
# ```
|
|
1072
|
+
class ResumeEntry < Model
|
|
1073
|
+
sig { returns(String) }
|
|
1074
|
+
attr_reader :interrupt_id
|
|
1075
|
+
|
|
1076
|
+
# Free-form string; protocol values are "resolved" or "cancelled" but not enforced by this SDK.
|
|
1077
|
+
sig { returns(T.nilable(String)) }
|
|
1078
|
+
attr_reader :status
|
|
1079
|
+
|
|
1080
|
+
sig { returns(T.untyped) }
|
|
1081
|
+
attr_reader :payload
|
|
1082
|
+
|
|
1083
|
+
# @param interrupt_id [String] ID of the interrupt being resolved
|
|
1084
|
+
# @param status [String] Resolution status (free-form; protocol values are "resolved" or "cancelled")
|
|
1085
|
+
# @param payload [Object] Response payload for the interrupt
|
|
1086
|
+
sig { params(interrupt_id: String, status: T.nilable(String), payload: T.untyped).void }
|
|
1087
|
+
def initialize(interrupt_id:, status: nil, payload: nil)
|
|
1088
|
+
@interrupt_id = interrupt_id
|
|
1089
|
+
@status = status
|
|
1090
|
+
@payload = payload
|
|
1091
|
+
end
|
|
1092
|
+
|
|
1093
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
|
1094
|
+
def to_h
|
|
1095
|
+
{
|
|
1096
|
+
interrupt_id: @interrupt_id,
|
|
1097
|
+
status: @status,
|
|
1098
|
+
# `payload` is the user's resume response — preserve keys verbatim.
|
|
1099
|
+
payload: @payload.nil? ? nil : AgUiProtocol::Util::Opaque.new(@payload)
|
|
614
1100
|
}
|
|
615
1101
|
end
|
|
616
1102
|
end
|
|
@@ -644,7 +1130,7 @@ module AgUiProtocol
|
|
|
644
1130
|
sig { returns(T.untyped) }
|
|
645
1131
|
attr_reader :state
|
|
646
1132
|
|
|
647
|
-
sig { returns(T::Array[BaseMessage]) }
|
|
1133
|
+
sig { returns(T::Array[T.any(BaseMessage, ActivityMessage)]) }
|
|
648
1134
|
attr_reader :messages
|
|
649
1135
|
|
|
650
1136
|
sig { returns(T::Array[Tool]) }
|
|
@@ -656,30 +1142,35 @@ module AgUiProtocol
|
|
|
656
1142
|
sig { returns(T.untyped) }
|
|
657
1143
|
attr_reader :forwarded_props
|
|
658
1144
|
|
|
1145
|
+
sig { returns(T.nilable(T::Array[ResumeEntry])) }
|
|
1146
|
+
attr_reader :resume
|
|
1147
|
+
|
|
659
1148
|
# @param thread_id [String] ID of the conversation thread
|
|
660
1149
|
# @param run_id [String] ID of the current run
|
|
661
1150
|
# @param state [Object] Current state of the agent
|
|
662
|
-
# @param messages [Array<BaseMessage>] List of messages in the conversation
|
|
1151
|
+
# @param messages [Array<BaseMessage, ActivityMessage>] List of messages in the conversation
|
|
663
1152
|
# @param tools [Array<Tool>] List of tools available to the agent
|
|
664
1153
|
# @param context [Array<Context>] List of context objects provided to the agent
|
|
665
1154
|
# @param forwarded_props [Object] Additional properties forwarded to the agent
|
|
666
1155
|
# @param parent_run_id [String] Lineage pointer for branching/time travel
|
|
667
|
-
# @
|
|
1156
|
+
# @param resume [Array<ResumeEntry>] Entries for resuming interrupted runs
|
|
1157
|
+
# @raise [ArgumentError] if messages is not an Array of BaseMessage or ActivityMessage
|
|
668
1158
|
sig do
|
|
669
1159
|
params(
|
|
670
1160
|
thread_id: String,
|
|
671
1161
|
run_id: String,
|
|
672
1162
|
state: T.untyped,
|
|
673
|
-
messages: T::Array[BaseMessage],
|
|
1163
|
+
messages: T::Array[T.any(BaseMessage, ActivityMessage)],
|
|
674
1164
|
tools: T::Array[Tool],
|
|
675
1165
|
context: T::Array[Context],
|
|
676
1166
|
forwarded_props: T.untyped,
|
|
677
|
-
parent_run_id: T.nilable(String)
|
|
1167
|
+
parent_run_id: T.nilable(String),
|
|
1168
|
+
resume: T.nilable(T::Array[ResumeEntry])
|
|
678
1169
|
).void.checked(:always)
|
|
679
1170
|
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"
|
|
1171
|
+
def initialize(thread_id:, run_id:, state:, messages:, tools:, context:, forwarded_props:, parent_run_id: nil, resume: nil)
|
|
1172
|
+
unless messages.is_a?(Array) && messages.all? { |m| m.is_a?(BaseMessage) || m.is_a?(ActivityMessage) }
|
|
1173
|
+
raise ArgumentError, "messages must be an Array of BaseMessage or ActivityMessage"
|
|
683
1174
|
end
|
|
684
1175
|
unless tools.is_a?(Array) && tools.all? { |m| m.is_a?(Tool) }
|
|
685
1176
|
raise ArgumentError, "tools must be an Array of Tool"
|
|
@@ -687,6 +1178,9 @@ module AgUiProtocol
|
|
|
687
1178
|
unless context.is_a?(Array) && context.all? { |m| m.is_a?(Context) }
|
|
688
1179
|
raise ArgumentError, "context must be an Array of Context"
|
|
689
1180
|
end
|
|
1181
|
+
unless resume.nil? || (resume.is_a?(Array) && resume.all? { |r| r.is_a?(ResumeEntry) })
|
|
1182
|
+
raise ArgumentError, "resume must be an Array of ResumeEntry"
|
|
1183
|
+
end
|
|
690
1184
|
|
|
691
1185
|
@thread_id = thread_id
|
|
692
1186
|
@run_id = run_id
|
|
@@ -696,6 +1190,7 @@ module AgUiProtocol
|
|
|
696
1190
|
@tools = tools
|
|
697
1191
|
@context = context
|
|
698
1192
|
@forwarded_props = forwarded_props
|
|
1193
|
+
@resume = resume
|
|
699
1194
|
end
|
|
700
1195
|
|
|
701
1196
|
sig { returns(T::Hash[Symbol, T.untyped]) }
|
|
@@ -704,11 +1199,14 @@ module AgUiProtocol
|
|
|
704
1199
|
thread_id: @thread_id,
|
|
705
1200
|
run_id: @run_id,
|
|
706
1201
|
parent_run_id: @parent_run_id,
|
|
707
|
-
state
|
|
1202
|
+
# `state` is the agent's user-defined state object — preserve keys verbatim.
|
|
1203
|
+
state: @state.nil? ? nil : AgUiProtocol::Util::Opaque.new(@state),
|
|
708
1204
|
messages: @messages,
|
|
709
1205
|
tools: @tools,
|
|
710
1206
|
context: @context,
|
|
711
|
-
forwarded_props
|
|
1207
|
+
# `forwarded_props` is arbitrary user-defined key/value data — preserve keys verbatim.
|
|
1208
|
+
forwarded_props: @forwarded_props.nil? ? nil : AgUiProtocol::Util::Opaque.new(@forwarded_props),
|
|
1209
|
+
resume: @resume
|
|
712
1210
|
}
|
|
713
1211
|
end
|
|
714
1212
|
end
|