ag-ui 0.3.0 → 1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6092a88471f821fdd20d43bd3dad28205f114bb5b2068faea39234b4f1bc87da
4
- data.tar.gz: 93566155eed8ad3464386a74b8d5afb6db6ea124eccac4bf6382b264e02733ec
3
+ metadata.gz: 226774b66734c0e15d3668f1333c061ef41caee6fd160a3d74227ccb0766b43e
4
+ data.tar.gz: 48926b867c6e753d0a6f4865b8636704b826add52510522afee69726d05de4d7
5
5
  SHA512:
6
- metadata.gz: cf108e6f97a63801cf5b3a936241d187a3492bf8c92209ff0353d5e98ae68bb3752c05aedeffcb8f7a1deee0d91378128dc9608b3a4f997e8a98b9712412fc89
7
- data.tar.gz: 541b3da611c70fdd1ec02b97dd9c8fad76d30f20e124d38800896d57b431a099c148d3635193ebef28aaae6d70b303989e41380abeb6c4e489820cd2fc1d6d1c
6
+ metadata.gz: 144142fdc3950ec5925887614db47e416e3bc92a6810fb3cd7bea146de1a3b54de3655d110fe6bba4246761a70a88f9d26c9c2d7101657a3848c52c86dff5fcb
7
+ data.tar.gz: 8b62bdfd10acb644e867aa8f0ac71e1c4fbb1897644f43c71642c05076792fe32a030244c0f7d4aa2451774f9a357a226e60fa784bffa0c53db6c147f8fa0034
data/data/ag_ui.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "$comment": "Generated by data/generate-ag-ui-schema.py from ag-ui sdks/python (77 models). Do not edit.",
2
+ "$comment": "Generated by bin/generate-ag-ui-schema from ag-ui sdks/python (77 models). Do not edit.",
3
3
  "definitions": {
4
4
  "ActivityDeltaEvent": {
5
5
  "additionalProperties": true,
@@ -23,8 +23,8 @@ module AgUi
23
23
  A2UI_RECOVERY_ACTIVITY_TYPE = "a2ui_recovery"
24
24
 
25
25
  NO_TOOL_CALL_ERROR = {
26
- "code" => "empty_components",
27
- "path" => "components",
26
+ "code" => "empty_components",
27
+ "path" => "components",
28
28
  "message" => "Sub-agent did not call render_a2ui",
29
29
  }.freeze
30
30
 
@@ -57,15 +57,22 @@ module AgUi
57
57
  prompt = augment_prompt_with_validation_errors(base_prompt, last_errors)
58
58
  args = invoke_subagent.call(prompt, attempt)
59
59
 
60
- record =
61
- if args.nil? || args.empty?
62
- { "attempt" => attempt, "ok" => false, "errors" => [NO_TOOL_CALL_ERROR] }
60
+ if args.nil? || args.empty?
61
+ record = { "attempt" => attempt, "ok" => false, "errors" => [NO_TOOL_CALL_ERROR] }
62
+ else
63
+ if args["components"].is_a?(Array)
64
+ components = args["components"]
65
+ else
66
+ components = []
67
+ end
68
+ if args["data"].is_a?(Hash)
69
+ data = args["data"]
63
70
  else
64
- components = args["components"].is_a?(Array) ? args["components"] : []
65
- data = args["data"].is_a?(Hash) ? args["data"] : {}
66
- result = Validate.validate_components(components: components, data: data, catalog: catalog)
67
- { "attempt" => attempt, "ok" => result["valid"], "errors" => result["errors"] }
71
+ data = {}
68
72
  end
73
+ result = Validate.validate_components(components: components, data: data, catalog: catalog)
74
+ record = { "attempt" => attempt, "ok" => result["valid"], "errors" => result["errors"] }
75
+ end
69
76
 
70
77
  attempts << record
71
78
  on_attempt&.call(record)
@@ -82,7 +89,7 @@ module AgUi
82
89
  {
83
90
  "envelope" => exhausted_envelope(max_attempts, attempts),
84
91
  "attempts" => attempts,
85
- "ok" => false,
92
+ "ok" => false,
86
93
  }
87
94
  end
88
95
  end
@@ -91,8 +98,8 @@ module AgUi
91
98
  def exhausted_envelope(max_attempts, attempts)
92
99
  JSON.generate(
93
100
  {
94
- "error" => "Failed to generate valid A2UI after #{max_attempts} attempt(s)",
95
- "code" => "a2ui_recovery_exhausted",
101
+ "error" => "Failed to generate valid A2UI after #{max_attempts} attempt(s)",
102
+ "code" => "a2ui_recovery_exhausted",
96
103
  "attempts" => attempts,
97
104
  },
98
105
  )
@@ -26,42 +26,66 @@ module AgUi
26
26
  def validate_components(components:, data: nil, catalog: nil, validate_bindings: true)
27
27
  if !components.is_a?(Array) || components.empty?
28
28
  {
29
- "valid" => false,
29
+ "valid" => false,
30
30
  "errors" => [{
31
- "code" => "empty_components",
32
- "path" => "components",
31
+ "code" => "empty_components",
32
+ "path" => "components",
33
33
  "message" => "A2UI components must be a non-empty array",
34
- }],
34
+ }
35
+ ],
35
36
  }
36
37
  else
37
- validate_populated(components, data, catalog, validate_bindings)
38
+ validate_populated(
39
+ components,
40
+ data,
41
+ catalog,
42
+ validate_bindings,
43
+ )
38
44
  end
39
45
  end
40
46
 
41
47
  def validate_populated(components, data, catalog, validate_bindings)
42
48
  errors = []
43
49
  ids = collect_ids(components, errors)
44
- catalog_components = catalog.is_a?(Hash) ? (catalog["components"] || {}) : {}
50
+ if catalog.is_a?(Hash)
51
+ catalog_components = (catalog["components"] || {})
52
+ else
53
+ catalog_components = {}
54
+ end
45
55
 
46
56
  components.each_with_index do |comp, i|
47
57
  check_identity(comp, i, errors)
48
- check_catalog(comp, i, catalog, catalog_components, errors)
49
- check_refs_and_bindings(comp, i, ids, catalog_components, data, validate_bindings, errors)
58
+ check_catalog(
59
+ comp,
60
+ i,
61
+ catalog,
62
+ catalog_components,
63
+ errors,
64
+ )
65
+ check_refs_and_bindings(
66
+ comp,
67
+ i,
68
+ ids,
69
+ catalog_components,
70
+ data,
71
+ validate_bindings,
72
+ errors,
73
+ )
50
74
  end
51
75
 
52
76
  find_child_cycles(components, catalog_components).each do |cycle|
53
77
  chain = (cycle + [cycle.first]).join(" -> ")
54
78
  errors << {
55
- "code" => "child_cycle",
56
- "path" => "components[id=#{cycle.first}]",
79
+ "code" => "child_cycle",
80
+ "path" => "components[id=#{cycle.first}]",
57
81
  "message" => "Child reference cycle detected: #{chain}",
58
82
  }
59
83
  end
60
84
 
61
85
  unless components.any? { |c| c.is_a?(Hash) && c["id"] == "root" }
62
86
  errors << {
63
- "code" => "no_root",
64
- "path" => "components",
87
+ "code" => "no_root",
88
+ "path" => "components",
65
89
  "message" => "No component has id 'root'",
66
90
  }
67
91
  end
@@ -70,61 +94,77 @@ module AgUi
70
94
  end
71
95
 
72
96
  def collect_ids(components, errors)
73
- ids = Set.new
74
- seen = Set.new
75
- components.each do |comp|
76
- cid = comp.is_a?(Hash) ? comp["id"] : nil
77
- if cid.is_a?(String)
78
- if seen.include?(cid)
79
- errors << {
80
- "code" => "duplicate_id",
81
- "path" => "components[id=#{cid}]",
82
- "message" => "Duplicate component id '#{cid}'",
83
- }
97
+ Set.new.tap do |ids|
98
+ seen = Set.new
99
+ components.each do |comp|
100
+ if comp.is_a?(Hash)
101
+ cid = comp["id"]
102
+ else
103
+ cid = nil
104
+ end
105
+ if cid.is_a?(String)
106
+ if seen.include?(cid)
107
+ errors << {
108
+ "code" => "duplicate_id",
109
+ "path" => "components[id=#{cid}]",
110
+ "message" => "Duplicate component id '#{cid}'",
111
+ }
112
+ end
113
+ seen << cid
114
+ ids << cid
84
115
  end
85
- seen << cid
86
- ids << cid
87
116
  end
88
117
  end
89
- ids
90
118
  end
91
119
 
92
120
  def check_identity(comp, index, errors)
93
- cid = comp.is_a?(Hash) ? comp["id"] : nil
94
- ctype = comp.is_a?(Hash) ? comp["component"] : nil
121
+ if comp.is_a?(Hash)
122
+ cid = comp["id"]
123
+ else
124
+ cid = nil
125
+ end
126
+ if comp.is_a?(Hash)
127
+ ctype = comp["component"]
128
+ else
129
+ ctype = nil
130
+ end
95
131
 
96
132
  unless cid.is_a?(String) && !cid.empty?
97
133
  errors << {
98
- "code" => "missing_id",
99
- "path" => "components[#{index}].id",
134
+ "code" => "missing_id",
135
+ "path" => "components[#{index}].id",
100
136
  "message" => "Component at index #{index} is missing a string 'id'",
101
137
  }
102
138
  end
103
139
  unless ctype.is_a?(String) && !ctype.empty?
104
140
  errors << {
105
- "code" => "missing_component_type",
106
- "path" => "components[#{index}].component",
141
+ "code" => "missing_component_type",
142
+ "path" => "components[#{index}].component",
107
143
  "message" => "Component at index #{index} is missing a string 'component' type",
108
144
  }
109
145
  end
110
146
  end
111
147
 
112
148
  def check_catalog(comp, index, catalog, catalog_components, errors)
113
- ctype = comp.is_a?(Hash) ? comp["component"] : nil
149
+ if comp.is_a?(Hash)
150
+ ctype = comp["component"]
151
+ else
152
+ ctype = nil
153
+ end
114
154
  if catalog && ctype.is_a?(String)
115
155
  schema = catalog_components[ctype]
116
156
  if schema.nil?
117
157
  errors << {
118
- "code" => "unknown_component",
119
- "path" => "components[#{index}].component",
158
+ "code" => "unknown_component",
159
+ "path" => "components[#{index}].component",
120
160
  "message" => "Component type '#{ctype}' is not in the catalog",
121
161
  }
122
162
  else
123
163
  (schema["required"] || []).each do |req|
124
164
  unless comp.is_a?(Hash) && comp.key?(req)
125
165
  errors << {
126
- "code" => "missing_required_prop",
127
- "path" => "components[#{index}].#{req}",
166
+ "code" => "missing_required_prop",
167
+ "path" => "components[#{index}].#{req}",
128
168
  "message" => "Component '#{ctype}' (index #{index}) is missing required prop '#{req}'",
129
169
  }
130
170
  end
@@ -136,13 +176,17 @@ module AgUi
136
176
  def check_refs_and_bindings(comp, index, ids, catalog_components, data, validate_bindings, errors)
137
177
  if comp.is_a?(Hash)
138
178
  ctype = comp["component"]
139
- schema = ctype.is_a?(String) ? catalog_components[ctype] : nil
179
+ if ctype.is_a?(String)
180
+ schema = catalog_components[ctype]
181
+ else
182
+ schema = nil
183
+ end
140
184
 
141
185
  collect_component_ref_edges(comp, schema).each do |(ref_path, ref)|
142
186
  unless ids.include?(ref)
143
187
  errors << {
144
- "code" => "unresolved_child",
145
- "path" => "components[#{index}].#{ref_path}",
188
+ "code" => "unresolved_child",
189
+ "path" => "components[#{index}].#{ref_path}",
146
190
  "message" => "Child reference '#{ref}' does not match any component id",
147
191
  }
148
192
  end
@@ -152,8 +196,8 @@ module AgUi
152
196
  collect_absolute_binding_paths(comp, []).each do |path|
153
197
  unless absolute_path_resolves?(path, data || {})
154
198
  errors << {
155
- "code" => "unresolved_binding",
156
- "path" => "components[#{index}]",
199
+ "code" => "unresolved_binding",
200
+ "path" => "components[#{index}]",
157
201
  "message" => "Binding path '#{path}' does not resolve in the data model",
158
202
  }
159
203
  end
@@ -193,21 +237,21 @@ module AgUi
193
237
  # A bare string id, or a {componentId: ...} template — the two child
194
238
  # reference shapes.
195
239
  def collect_child_refs(children)
196
- refs = []
197
- push = ->(v) do
198
- if v.is_a?(String)
199
- refs << v
200
- elsif v.is_a?(Hash) && v["componentId"].is_a?(String)
201
- refs << v["componentId"]
240
+ [].tap do |refs|
241
+ push = ->(v) do
242
+ if v.is_a?(String)
243
+ refs << v
244
+ elsif v.is_a?(Hash) && v["componentId"].is_a?(String)
245
+ refs << v["componentId"]
246
+ end
202
247
  end
203
- end
204
248
 
205
- if children.is_a?(Array)
206
- children.each { |v| push.(v) }
207
- else
208
- push.(children)
249
+ if children.is_a?(Array)
250
+ children.each { |v| push.(v) }
251
+ else
252
+ push.(children)
253
+ end
209
254
  end
210
- refs
211
255
  end
212
256
 
213
257
  # (path_suffix, ref_id) pairs for every child reference a component
@@ -217,47 +261,60 @@ module AgUi
217
261
  # array-of-object item schemas honoured per element — finds Tabs
218
262
  # tabItems[].child). Unmarked props are data, never refs.
219
263
  def collect_component_ref_edges(comp, schema)
220
- edges = []
264
+ [].tap do |edges|
221
265
 
222
- push_single = ->(field, value) do
223
- collect_child_refs(value).each { |ref| edges << [field, ref] }
224
- end
225
- push_list = ->(field, value) do
226
- if value.is_a?(Array)
227
- value.each_with_index do |item, k|
228
- collect_child_refs(item).each { |ref| edges << ["#{field}[#{k}]", ref] }
229
- end
230
- else
266
+ push_single = ->(field, value) do
231
267
  collect_child_refs(value).each { |ref| edges << [field, ref] }
232
268
  end
233
- end
269
+ push_list = ->(field, value) do
270
+ if value.is_a?(Array)
271
+ value.each_with_index do |item, k|
272
+ collect_child_refs(item).each { |ref| edges << ["#{field}[#{k}]", ref] }
273
+ end
274
+ else
275
+ collect_child_refs(value).each { |ref| edges << [field, ref] }
276
+ end
277
+ end
234
278
 
235
- push_single.("child", comp["child"])
236
- push_list.("children", comp["children"])
279
+ push_single.("child", comp["child"])
280
+ push_list.("children", comp["children"])
237
281
 
238
- props = schema.is_a?(Hash) ? schema["properties"] : nil
239
- if props.is_a?(Hash)
240
- props.each do |field, prop_schema|
241
- if %w[child children].include?(field) || !prop_schema.is_a?(Hash)
242
- next
243
- end
282
+ if schema.is_a?(Hash)
283
+ props = schema["properties"]
284
+ else
285
+ props = nil
286
+ end
287
+ if props.is_a?(Hash)
288
+ props.each do |field, prop_schema|
289
+ if %w[child children].include?(field) || !prop_schema.is_a?(Hash)
290
+ next
291
+ end
244
292
 
245
- case prop_schema["format"]
246
- when "componentRef"
247
- push_single.(field, comp[field])
248
- when "componentRefList"
249
- push_list.(field, comp[field])
250
- else
251
- collect_array_item_edges(comp, field, prop_schema, edges)
293
+ case prop_schema["format"]
294
+ when "componentRef"
295
+ push_single.(field, comp[field])
296
+ when "componentRefList"
297
+ push_list.(field, comp[field])
298
+ else
299
+ collect_array_item_edges(
300
+ comp,
301
+ field,
302
+ prop_schema,
303
+ edges,
304
+ )
305
+ end
252
306
  end
253
307
  end
254
308
  end
255
- edges
256
309
  end
257
310
 
258
311
  def collect_array_item_edges(comp, field, prop_schema, edges)
259
312
  items = prop_schema["items"]
260
- item_props = items.is_a?(Hash) ? items["properties"] : nil
313
+ if items.is_a?(Hash)
314
+ item_props = items["properties"]
315
+ else
316
+ item_props = nil
317
+ end
261
318
  arr_val = comp[field]
262
319
  if prop_schema["type"] == "array" && item_props.is_a?(Hash) && arr_val.is_a?(Array)
263
320
  arr_val.each_with_index do |item, k|
@@ -289,15 +346,19 @@ module AgUi
289
346
  end
290
347
 
291
348
  def child_adjacency(components, catalog_components)
292
- adj = {}
293
- components.each do |comp|
294
- if comp.is_a?(Hash) && comp["id"].is_a?(String)
295
- ctype = comp["component"]
296
- schema = ctype.is_a?(String) ? catalog_components[ctype] : nil
297
- adj[comp["id"]] = collect_component_ref_edges(comp, schema).map { |(_p, ref)| ref }
349
+ {}.tap do |adj|
350
+ components.each do |comp|
351
+ if comp.is_a?(Hash) && comp["id"].is_a?(String)
352
+ ctype = comp["component"]
353
+ if ctype.is_a?(String)
354
+ schema = catalog_components[ctype]
355
+ else
356
+ schema = nil
357
+ end
358
+ adj[comp["id"]] = collect_component_ref_edges(comp, schema).map { |(_p, ref)| ref }
359
+ end
298
360
  end
299
361
  end
300
- adj
301
362
  end
302
363
 
303
364
  # Unique child-reference cycles via ITERATIVE DFS (explicit frame