anthropic-a2ui 0.1.0__tar.gz

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,26 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .eggs/
6
+ build/
7
+ dist/
8
+ .venv/
9
+ venv/
10
+
11
+ # Tests
12
+ .pytest_cache/
13
+ .coverage
14
+ htmlcov/
15
+
16
+ # uv
17
+ .uv/
18
+
19
+ # Fire test results (generated, not committed)
20
+ fire_test/results/
21
+ fire_test/results_exhaustive/
22
+ fire_test/results_natural/
23
+ fire_test/viewer.html
24
+
25
+ # OS
26
+ .DS_Store
@@ -0,0 +1,59 @@
1
+ # Changelog
2
+
3
+ All notable changes to `anthropic-a2ui` are documented here.
4
+ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
5
+ and the versioning follows [SemVer](https://semver.org/).
6
+
7
+ ## [0.1.0] - 2026-06-21
8
+
9
+ ### Added
10
+
11
+ - **`generate_a2ui`**: high-level function that calls Claude, validates,
12
+ repairs, and retries on failure. Returns a `RetryResult` with the
13
+ validated A2UI payload.
14
+ - **`generate_a2ui_async`**: async version of `generate_a2ui` for
15
+ integration with async servers (FastAPI, Starlette, etc.).
16
+ - **`A2uiConversation`**: multi-turn conversation that maintains message
17
+ history across turns so the user can iterate ("make me a form" ->
18
+ "add a field" -> "change the color"). Claude generates a new A2UI
19
+ payload with all changes applied in each turn.
20
+ - **`A2uiConversationAsync`**: async version of `A2uiConversation`.
21
+ - **Prompt caching**: `use_cache=True` by default in `generate_a2ui` and
22
+ `A2uiConversation`. Adds `cache_control` to the system prompt to
23
+ reduce cost by ~80% on repeated calls.
24
+ - **Third mode `response_format`**: `create_a2ui_response_format` and
25
+ `parse_json_response` for forced JSON output without tools or tags.
26
+ - **Repair logging**: `log_repairs=True` records what was repaired
27
+ ("Icon 'cloud' substituted with 'info'", etc.).
28
+ - **`py.typed`**: type marker for IDE support (autocompletion, type
29
+ checking).
30
+ - **`ClaudeA2uiPromptBuilder`**: constructs the system prompt with schema
31
+ + few-shot examples + list of 59 valid icon names + list of 14 valid
32
+ catalog functions.
33
+ - **`create_a2ui_tool`**: defines the `send_a2ui_json_to_client` tool
34
+ with schema wrapped in `{"a2ui_json": [...]}` to avoid `oneOf` at the
35
+ root level (which the Anthropic API rejects).
36
+ - **`ClaudeStreamParser`**: parses the Anthropic stream and emits
37
+ `ResponsePart` objects (text + `<a2ui-json>` blocks and tool use).
38
+ Repairs and validates automatically.
39
+ - **`to_a2ui_part` / `A2uiPart`**: transport helpers with MIME
40
+ `application/a2ui+json`.
41
+ - **`validate_tool_input`**: validates A2UI payloads against the schema
42
+ with automatic repair.
43
+ - **Five automatic repairs**:
44
+ 1. `patch_catalog_schema`: fixes `DateTimeInput.min/max` ambiguous
45
+ `oneOf` by changing it to `anyOf`.
46
+ 2. `repair_orphans`: reconnects orphaned components to the `root`
47
+ tree.
48
+ 3. `repair_icons`: maps 100+ common icon aliases to the 59 valid
49
+ catalog icons; unknown names become `info`.
50
+ 4. `repair_functions`: replaces non-existent function calls
51
+ (`ternary`, `if`, `switch`) with literal values.
52
+ 5. `repair_childlists`: converts dynamic child lists
53
+ (`{componentId, path}`) in `Row`/`Column` to static string arrays.
54
+ - **620 tests** covering: 19 cross-catalog components, 15 functions,
55
+ 4 message types, optional properties, multi-catalog, multi-version,
56
+ 5 repairs, multi-turn, async, caching, response_format.
57
+ - **Fire test**: 30 natural prompts x 3 models = 30/30 OK (Haiku 4.5,
58
+ Opus 4.7, Opus 4.8).
59
+ - **CI** with GitHub Actions (pytest + pyink on Python 3.10-3.13).
@@ -0,0 +1,17 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ Copyright 2026
6
+
7
+ Licensed under the Apache License, Version 2.0 (the "License");
8
+ you may not use this file except in compliance with the License.
9
+ You may obtain a copy of the License at
10
+
11
+ http://www.apache.org/licenses/LICENSE-2.0
12
+
13
+ Unless required by applicable law or agreed to in writing, software
14
+ distributed under the License is distributed on an "AS IS" BASIS,
15
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ See the License for the specific language governing permissions and
17
+ limitations under the License.
@@ -0,0 +1,451 @@
1
+ Metadata-Version: 2.4
2
+ Name: anthropic-a2ui
3
+ Version: 0.1.0
4
+ Summary: Que Claude genere interfaces de usuario con A2UI en sus respuestas.
5
+ License-Expression: Apache-2.0
6
+ License-File: LICENSE
7
+ Keywords: a2ui,agent,anthropic,claude,generative-ui,ui
8
+ Requires-Python: >=3.10
9
+ Requires-Dist: a2ui-agent-sdk<0.3,>=0.2.4
10
+ Requires-Dist: anthropic<1,>=0.40
11
+ Requires-Dist: jsonschema>=4.0
12
+ Description-Content-Type: text/markdown
13
+
14
+ ```
15
+ ___ _ _ _____
16
+ /\ |__ \ | | | | |_ _|
17
+ / \ ) | | | | | | |
18
+ / /\ \ / / | | | | | |
19
+ / ____ \ / /_ | |__| | _| |_
20
+ /_/ \_\ |____| \____/ |_____|
21
+ ```
22
+
23
+ # anthropic-a2ui
24
+
25
+ > Let Claude generate user interfaces with A2UI in its responses.
26
+
27
+ `anthropic-a2ui` bridges the A2UI protocol with the Anthropic SDK, enabling
28
+ Claude to emit declarative, interactive user interfaces as part of its
29
+ natural responses. Instead of returning plain text, Claude can now build
30
+ forms, dashboards, product cards, surveys, calendars, configuration panels,
31
+ modals, and any other UI composed from the A2UI component catalog — all
32
+ validated, repaired, and ready to render in any A2UI-compatible renderer
33
+ (Lit, React, Angular, Flutter, SwiftUI, and more).
34
+
35
+ The package does not wrap or replace the Anthropic SDK. It composes with
36
+ it: you use `anthropic` directly and plug in the A2UI pieces where they add
37
+ value. The system prompt, the tool definition, the stream parser, the
38
+ validator, the automatic repair layer, and the retry-with-feedback loop are
39
+ all designed to be transparent and composable.
40
+
41
+ ---
42
+
43
+ ## How it works
44
+
45
+ When a user asks Claude for an interface — "make me a contact form", "show
46
+ me a shopping cart", "build me a weather panel" — the following happens
47
+ behind the scenes:
48
+
49
+ 1. **Prompt injection**: `ClaudeA2uiPromptBuilder` constructs a system
50
+ prompt containing the A2UI JSON Schema, few-shot examples, the list of
51
+ 59 valid icon names, and the 14 catalog functions. Claude receives this
52
+ as its system context and knows how to speak A2UI.
53
+
54
+ 2. **Tool definition**: `create_a2ui_tool` produces a tool definition
55
+ (`send_a2ui_json_to_client`) that Claude can invoke to deliver UI. The
56
+ schema is wrapped to comply with Anthropic's constraint against
57
+ `oneOf`/`allOf`/`anyOf` at the top level of `input_schema`.
58
+
59
+ 3. **Streaming parse**: As Claude streams its response, `ClaudeStreamParser`
60
+ intercepts tool-use events (or `<a2ui-json>` text blocks) and emits
61
+ `ResponsePart` objects containing either conversational text or a
62
+ complete A2UI payload.
63
+
64
+ 4. **Automatic repair**: Before validation, the payload passes through five
65
+ repair functions that fix known issues silently — invalid icon names,
66
+ non-existent catalog functions, orphaned components, ambiguous
67
+ DateTimeInput formats, and dynamic child lists in Row/Column.
68
+
69
+ 5. **Validation**: The repaired payload is validated against the A2UI
70
+ schema. If it fails, the error is fed back to Claude as a tool result,
71
+ and Claude corrects itself on the next attempt (up to `max_retries`).
72
+
73
+ 6. **Delivery**: The validated payload is wrapped in an `A2uiPart` with
74
+ MIME `application/a2ui+json`, ready to be sent to any A2UI renderer.
75
+
76
+ The user never mentions A2UI, components, functions, or schemas. They just
77
+ talk naturally, and Claude responds with a working interface.
78
+
79
+ ---
80
+
81
+ ## Installation
82
+
83
+ ```bash
84
+ uv add anthropic-a2ui
85
+ # or
86
+ pip install anthropic-a2ui
87
+ ```
88
+
89
+ Requires `ANTHROPIC_API_KEY` in the environment.
90
+
91
+ ---
92
+
93
+ ## Quick start
94
+
95
+ The simplest way to use the package is `generate_a2ui`. One function call
96
+ handles everything: prompt construction, Claude invocation, streaming,
97
+ parsing, repair, validation, and retry-on-failure.
98
+
99
+ ```python
100
+ import anthropic
101
+ from anthropic_a2ui import generate_a2ui
102
+
103
+ client = anthropic.Anthropic()
104
+
105
+ result = generate_a2ui(
106
+ client,
107
+ "make me a registration form with name, email, and a submit button",
108
+ model="claude-haiku-4-5-20251001",
109
+ log_repairs=True,
110
+ )
111
+
112
+ if result.success:
113
+ # result.a2ui_json is a validated list of A2UI messages
114
+ # ready to be rendered by any A2UI-compatible renderer
115
+ render(result.a2ui_json)
116
+ for repair in result.repairs:
117
+ print(f"Repaired: {repair}")
118
+ else:
119
+ print(f"Failed after {result.attempts} attempts: {result.error}")
120
+ ```
121
+
122
+ An async version is also available for integration with async web
123
+ frameworks like FastAPI or Starlette:
124
+
125
+ ```python
126
+ import anthropic
127
+ from anthropic_a2ui import generate_a2ui_async
128
+
129
+ client = anthropic.AsyncAnthropic()
130
+ result = await generate_a2ui_async(
131
+ client,
132
+ "make me a registration form",
133
+ model="claude-haiku-4-5-20251001",
134
+ )
135
+ ```
136
+
137
+ ---
138
+
139
+ ## Usage modes
140
+
141
+ The package offers four complementary ways to generate A2UI, depending on
142
+ the level of control and the use case.
143
+
144
+ ### Mode 1: Single call with `generate_a2ui`
145
+
146
+ Best for one-shot UI generation. The user asks for something, Claude
147
+ responds with a complete A2UI payload. If the payload is invalid, the
148
+ package automatically feeds the validation error back to Claude and
149
+ retries (up to `max_retries` times, default 2). Claude corrects itself
150
+ and resubmits.
151
+
152
+ ```python
153
+ result = generate_a2ui(
154
+ client,
155
+ "make me a contact form with name, email, subject, and message",
156
+ model="claude-opus-4-8",
157
+ max_retries=3,
158
+ )
159
+
160
+ if result.success:
161
+ render(result.a2ui_json)
162
+ ```
163
+
164
+ Key parameters:
165
+
166
+ - `builder`: custom `ClaudeA2uiPromptBuilder` (defaults to v0.9 Basic
167
+ Catalog).
168
+ - `model`: any Claude model that supports tool use.
169
+ - `max_tokens`: per-attempt token budget (default 8192).
170
+ - `max_retries`: how many times to retry on validation failure (default 2).
171
+ - `use_cache`: enable prompt caching to reduce cost ~80% on repeated calls
172
+ (default `True`).
173
+ - `log_repairs`: record what was repaired for debugging (default `False`).
174
+
175
+ ### Mode 2: Multi-turn conversation with `A2uiConversation`
176
+
177
+ Best for iterative UI design. The user starts with a request, then refines
178
+ it across multiple turns: "make me a form" -> "add a phone field" ->
179
+ "change the button color to blue". Claude maintains the full conversation
180
+ context and generates a new A2UI payload with all changes applied in each
181
+ turn.
182
+
183
+ ```python
184
+ import anthropic
185
+ from anthropic_a2ui import A2uiConversation
186
+
187
+ client = anthropic.Anthropic()
188
+ conv = A2uiConversation(client, model="claude-haiku-4-5-20251001")
189
+
190
+ # Turn 1: create the initial form
191
+ r1 = conv.send("make me a contact form")
192
+ # r1.a2ui_json contains the form
193
+ # r1.success is True
194
+
195
+ # Turn 2: add a field
196
+ r2 = conv.send("add a phone number field")
197
+ # r2.a2ui_json contains the form + phone field
198
+
199
+ # Turn 3: change the styling
200
+ r3 = conv.send("make the submit button red")
201
+ # r3.a2ui_json contains the form + phone field + red button
202
+
203
+ # Access the full conversation history
204
+ print(f"Turns: {len(conv.turns)}")
205
+ print(f"Last valid A2UI: {conv.last_a2ui_json is not None}")
206
+
207
+ # Reset to start a new conversation
208
+ conv.reset()
209
+ ```
210
+
211
+ The async version works identically:
212
+
213
+ ```python
214
+ import anthropic
215
+ from anthropic_a2ui import A2uiConversationAsync
216
+
217
+ client = anthropic.AsyncAnthropic()
218
+ conv = A2uiConversationAsync(client)
219
+
220
+ r1 = await conv.send("make me a form")
221
+ r2 = await conv.send("add an email field")
222
+ ```
223
+
224
+ ### Mode 3: Forced JSON with `response_format`
225
+
226
+ Best when you want pure UI output with no conversational text. This mode
227
+ uses Anthropic's `response_format` parameter with `json_schema` to
228
+ guarantee that the response is parseable JSON. No tools, no tags — just a
229
+ structured payload.
230
+
231
+ ```python
232
+ import anthropic
233
+ from anthropic_a2ui import (
234
+ ClaudeA2uiPromptBuilder,
235
+ create_a2ui_response_format,
236
+ parse_json_response,
237
+ )
238
+
239
+ builder = ClaudeA2uiPromptBuilder()
240
+ response_format = create_a2ui_response_format(builder.get_catalog())
241
+
242
+ client = anthropic.Anthropic()
243
+ response = client.messages.create(
244
+ model="claude-sonnet-4-5",
245
+ system=builder.build(role_description="You create user interfaces."),
246
+ response_format=response_format,
247
+ max_tokens=8192,
248
+ messages=[{"role": "user", "content": "make me a form"}],
249
+ )
250
+
251
+ # Extract, unwrap, validate, and repair the A2UI payload
252
+ a2ui_json = parse_json_response(response, builder.get_catalog())
253
+ render(a2ui_json)
254
+ ```
255
+
256
+ ### Mode 4: Manual streaming
257
+
258
+ Best when you need full control over the stream, token by token. You
259
+ construct the system prompt, define the tool, and process the stream
260
+ yourself. The parser handles unwrapping, repair, and validation.
261
+
262
+ ```python
263
+ import anthropic
264
+ from anthropic_a2ui import (
265
+ ClaudeA2uiPromptBuilder,
266
+ create_a2ui_tool,
267
+ ClaudeStreamParser,
268
+ )
269
+
270
+ builder = ClaudeA2uiPromptBuilder()
271
+ tool = create_a2ui_tool(builder.get_catalog())
272
+ parser = ClaudeStreamParser(catalog=builder.get_catalog())
273
+
274
+ client = anthropic.Anthropic()
275
+ with client.messages.stream(
276
+ model="claude-sonnet-4-5",
277
+ system=builder.build(role_description="You create user interfaces."),
278
+ tools=[tool],
279
+ max_tokens=8192,
280
+ messages=[{"role": "user", "content": "make me a form"}],
281
+ ) as stream:
282
+ for event in stream:
283
+ for part in parser.process_event(event):
284
+ if part.a2ui_json:
285
+ # A complete, validated, repaired A2UI payload
286
+ render(part.a2ui_json)
287
+ if part.text:
288
+ # Conversational text from Claude
289
+ print(part.text, end="")
290
+ ```
291
+
292
+ The parser also supports the `<a2ui-json>` tag mode (no tool use), where
293
+ Claude embeds the JSON directly in its text response between
294
+ `<a2ui-json>` and `</a2ui-json>` tags.
295
+
296
+ ---
297
+
298
+ ## Automatic repairs
299
+
300
+ Claude is remarkably good at generating valid A2UI, but it occasionally
301
+ makes mistakes — especially the smaller models. The package includes five
302
+ repair functions that fix these issues transparently before validation, so
303
+ the caller never sees a false rejection:
304
+
305
+ | Repair | What it fixes | Example |
306
+ |---|---|---|
307
+ | `patch_catalog_schema` | `DateTimeInput.min/max` uses `oneOf` of three date formats, which `jsonschema` rejects as ambiguous | Changes `oneOf` to `anyOf` in the schema |
308
+ | `repair_orphans` | Components created by Claude but not connected to the `root` tree | Reconnects orphans as children of the root container |
309
+ | `repair_icons` | Icon names that don't exist in the catalog enum (`cloud`, `sunny`, `trash`, ...) | Maps 100+ common aliases to the 59 valid icons; unknown names become `info` |
310
+ | `repair_functions` | `FunctionCall` with functions that don't exist in the catalog (`ternary`, `if`, `switch`) | Replaces with a literal value (`False` for boolean, `""` for string) or removes the check |
311
+ | `repair_childlists` | Dynamic child lists (`{componentId, path}`) used in `Row` or `Column`, which only accept static string arrays | Converts to a static array of component IDs |
312
+
313
+ All repairs are enabled by default in `generate_a2ui`, `A2uiConversation`,
314
+ and `ClaudeStreamParser`. They can be disabled with `repair=False` for
315
+ strict validation.
316
+
317
+ When `log_repairs=True`, the `RetryResult.repairs` list contains
318
+ human-readable descriptions of what was repaired:
319
+
320
+ ```python
321
+ result = generate_a2ui(client, "make me a weather panel", log_repairs=True)
322
+ for r in result.repairs:
323
+ print(f"Repaired: {r}")
324
+ # Repaired: Icon 'sun_icon' substituted with 'info'
325
+ # Repaired: Text 'star1' emptied (possible function removed)
326
+ ```
327
+
328
+ ---
329
+
330
+ ## Prompt caching
331
+
332
+ The A2UI system prompt is large: it includes the full JSON Schema, few-shot
333
+ examples, the list of 59 valid icon names, and the 14 catalog functions.
334
+ Sending this on every call would be expensive and slow.
335
+
336
+ `generate_a2ui` and `A2uiConversation` enable prompt caching by default
337
+ (`use_cache=True`). This adds `cache_control: {type: "ephemeral"}` to the
338
+ system prompt block, allowing Anthropic to cache it and reduce the cost by
339
+ approximately 80% on subsequent calls within the cache window.
340
+
341
+ To disable caching (for debugging or testing):
342
+
343
+ ```python
344
+ result = generate_a2ui(client, "make me a form", use_cache=False)
345
+ ```
346
+
347
+ ---
348
+
349
+ ## Type safety
350
+
351
+ The package ships with a `py.typed` marker, so IDEs like VS Code (with
352
+ Pylance) and PyCharm provide full autocompletion, type checking, and
353
+ inline documentation for all public APIs.
354
+
355
+ ---
356
+
357
+ ## Supported versions
358
+
359
+ A2UI is an evolving protocol. The package supports three versions:
360
+
361
+ | Version | Catalog | Components | Functions |
362
+ |---|---|---|---|
363
+ | v0.8 | standard (legacy) | 18 (includes `MultipleChoice`) | 0 |
364
+ | v0.9 | basic | 18 (includes `ChoicePicker`) | 14 |
365
+ | v0.9 | minimal | 5 (Text, Row, Column, Button, TextField) | 1 (`capitalize`) |
366
+ | v0.9.1 | basic | 18 | 14 |
367
+
368
+ The default is v0.9 with the Basic Catalog. To use a different version or
369
+ catalog:
370
+
371
+ ```python
372
+ from anthropic_a2ui import ClaudeA2uiPromptBuilder
373
+
374
+ # v0.8 legacy
375
+ builder = ClaudeA2uiPromptBuilder(version="0.8")
376
+
377
+ # Pruned to specific components (saves tokens)
378
+ builder = ClaudeA2uiPromptBuilder()
379
+ prompt = builder.build(
380
+ role_description="You create simple forms.",
381
+ allowed_components=["Text", "TextField", "Button"],
382
+ )
383
+ ```
384
+
385
+ ---
386
+
387
+ ## Tested models
388
+
389
+ The package has been tested with natural conversational prompts (no
390
+ mention of A2UI, components, or functions) across four Claude models. Each
391
+ model received 10 different prompts and was asked to generate a complete
392
+ A2UI interface:
393
+
394
+ | Model | Success rate | Avg. time |
395
+ |---|---|---|
396
+ | claude-haiku-4-5 | 10/10 (100%) | ~8s |
397
+ | claude-opus-4-7 | 10/10 (100%) | ~17s |
398
+ | claude-opus-4-8 | 10/10 (100%) | ~15s |
399
+ | claude-sonnet-4-5 | 8/8 (100%) | ~12s |
400
+
401
+ All 30 test cases (10 prompts x 3 models) passed validation after
402
+ automatic repair. The test prompts cover forms, dashboards, surveys,
403
+ calendars, profiles, configuration panels, task lists, product galleries,
404
+ and modal dialogs.
405
+
406
+ ---
407
+
408
+ ## API reference
409
+
410
+ ### High-level functions
411
+
412
+ - **`generate_a2ui(client, prompt, **kwargs) -> RetryResult`**: one-shot
413
+ generation with retries and caching.
414
+ - **`generate_a2ui_async(client, prompt, **kwargs) -> RetryResult`**:
415
+ async version.
416
+ - **`A2uiConversation(client, **kwargs)`**: multi-turn conversation.
417
+ `.send(prompt) -> ConversationTurn`, `.reset()`.
418
+ - **`A2uiConversationAsync(client, **kwargs)`**: async version.
419
+ - **`create_a2ui_response_format(catalog) -> dict`**: response_format for
420
+ forced JSON mode. Pair with `parse_json_response(message, catalog)`.
421
+
422
+ ### Building blocks
423
+
424
+ - **`ClaudeA2uiPromptBuilder(version, catalogs)`**: constructs the system
425
+ prompt. `.build(role_description, ...)` returns the prompt string.
426
+ `.get_catalog()` returns the `A2uiCatalog`.
427
+ - **`create_a2ui_tool(catalog, **kwargs) -> dict`**: tool definition for
428
+ Anthropic.
429
+ - **`ClaudeStreamParser(catalog, **kwargs)`**: stream parser.
430
+ `.process_event(event) -> list[ResponsePart]`. `.parse_stream(stream)`
431
+ is an iterator shortcut.
432
+ - **`validate_tool_input(catalog, input_json, *, repair, strict_integrity)`**:
433
+ validates a payload against the schema.
434
+ - **`to_a2ui_part(payload) -> A2uiPart`**: wraps a payload for transport
435
+ with MIME `application/a2ui+json`.
436
+
437
+ ### Repair functions
438
+
439
+ - **`repair_orphans(payload)`**: reconnects orphaned components.
440
+ - **`repair_icons(payload)`**: fixes invalid icon names.
441
+ - **`repair_functions(payload)`**: fixes non-existent function calls.
442
+ - **`repair_childlists(payload)`**: fixes dynamic child lists in
443
+ Row/Column.
444
+ - **`patch_catalog_schema(schema)`**: patches the DateTimeInput schema.
445
+ - **`find_orphans(payload) -> list[str]`**: diagnostic, returns orphan IDs.
446
+
447
+ ---
448
+
449
+ ## License
450
+
451
+ Apache-2.0.