anthropic-a2ui 0.1.0__py3-none-any.whl
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.
- anthropic_a2ui/__init__.py +83 -0
- anthropic_a2ui/_internal/__init__.py +6 -0
- anthropic_a2ui/parts.py +108 -0
- anthropic_a2ui/prompt_builder.py +198 -0
- anthropic_a2ui/py.typed +0 -0
- anthropic_a2ui/repair.py +643 -0
- anthropic_a2ui/retry.py +877 -0
- anthropic_a2ui/stream_parser.py +233 -0
- anthropic_a2ui/tool.py +212 -0
- anthropic_a2ui/version.py +3 -0
- anthropic_a2ui-0.1.0.dist-info/METADATA +451 -0
- anthropic_a2ui-0.1.0.dist-info/RECORD +15 -0
- anthropic_a2ui-0.1.0.dist-info/WHEEL +4 -0
- anthropic_a2ui-0.1.0.dist-info/entry_points.txt +2 -0
- anthropic_a2ui-0.1.0.dist-info/licenses/LICENSE +17 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""anthropic-a2ui: integración de A2UI con el SDK de Anthropic.
|
|
2
|
+
|
|
3
|
+
Paquete que compone ``a2ui-agent-sdk`` con ``anthropic`` para que Claude
|
|
4
|
+
genere respuestas conformes al protocolo A2UI. No envuelve el SDK de
|
|
5
|
+
Anthropic: se usa ``anthropic`` directamente y se plugan las piezas de A2UI.
|
|
6
|
+
|
|
7
|
+
API pública:
|
|
8
|
+
|
|
9
|
+
- ``ClaudeA2uiPromptBuilder``: genera el system prompt con esquema + ejemplos.
|
|
10
|
+
- ``create_a2ui_tool``: define la tool ``send_a2ui_json_to_client``.
|
|
11
|
+
- ``ClaudeStreamParser``: parsea el stream de Anthropic y emite
|
|
12
|
+
``ResponsePart`` de A2UI.
|
|
13
|
+
- ``to_a2ui_part``, ``A2uiPart``, ``MIME_A2UI``: helpers de transporte.
|
|
14
|
+
- ``SUPPORTED_VERSIONS``, ``DEFAULT_VERSION``: constantes del protocolo.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
from .parts import A2uiPart, MIME_A2UI, parse_a2ui_part_json, to_a2a_datapart, to_a2ui_part
|
|
20
|
+
from .prompt_builder import (
|
|
21
|
+
DEFAULT_VERSION,
|
|
22
|
+
SUPPORTED_VERSIONS,
|
|
23
|
+
ClaudeA2uiPromptBuilder,
|
|
24
|
+
)
|
|
25
|
+
from .repair import (
|
|
26
|
+
find_orphans,
|
|
27
|
+
patch_catalog_schema,
|
|
28
|
+
repair_childlists,
|
|
29
|
+
repair_functions,
|
|
30
|
+
repair_icons,
|
|
31
|
+
repair_orphans,
|
|
32
|
+
)
|
|
33
|
+
from .retry import (
|
|
34
|
+
A2uiConversation,
|
|
35
|
+
A2uiConversationAsync,
|
|
36
|
+
ConversationTurn,
|
|
37
|
+
RetryResult,
|
|
38
|
+
create_a2ui_response_format,
|
|
39
|
+
generate_a2ui,
|
|
40
|
+
generate_a2ui_async,
|
|
41
|
+
parse_json_response,
|
|
42
|
+
)
|
|
43
|
+
from .stream_parser import ClaudeStreamParser
|
|
44
|
+
from .tool import (
|
|
45
|
+
TOOL_DESCRIPTION,
|
|
46
|
+
TOOL_NAME,
|
|
47
|
+
create_a2ui_tool,
|
|
48
|
+
make_parser_for_tool,
|
|
49
|
+
validate_tool_input,
|
|
50
|
+
)
|
|
51
|
+
from .version import __version__
|
|
52
|
+
|
|
53
|
+
__all__ = [
|
|
54
|
+
"A2uiConversation",
|
|
55
|
+
"A2uiConversationAsync",
|
|
56
|
+
"A2uiPart",
|
|
57
|
+
"ConversationTurn",
|
|
58
|
+
"MIME_A2UI",
|
|
59
|
+
"RetryResult",
|
|
60
|
+
"SUPPORTED_VERSIONS",
|
|
61
|
+
"DEFAULT_VERSION",
|
|
62
|
+
"TOOL_NAME",
|
|
63
|
+
"TOOL_DESCRIPTION",
|
|
64
|
+
"ClaudeA2uiPromptBuilder",
|
|
65
|
+
"ClaudeStreamParser",
|
|
66
|
+
"create_a2ui_response_format",
|
|
67
|
+
"create_a2ui_tool",
|
|
68
|
+
"find_orphans",
|
|
69
|
+
"generate_a2ui",
|
|
70
|
+
"generate_a2ui_async",
|
|
71
|
+
"make_parser_for_tool",
|
|
72
|
+
"parse_a2ui_part_json",
|
|
73
|
+
"parse_json_response",
|
|
74
|
+
"patch_catalog_schema",
|
|
75
|
+
"repair_childlists",
|
|
76
|
+
"repair_functions",
|
|
77
|
+
"repair_icons",
|
|
78
|
+
"repair_orphans",
|
|
79
|
+
"to_a2a_datapart",
|
|
80
|
+
"to_a2ui_part",
|
|
81
|
+
"validate_tool_input",
|
|
82
|
+
"__version__",
|
|
83
|
+
]
|
anthropic_a2ui/parts.py
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"""Helpers de transporte para envolver JSON de A2UI en DataParts.
|
|
2
|
+
|
|
3
|
+
Esta pieza es la menos acoplada a Anthropic: sirve igual si el JSON viene de
|
|
4
|
+
tool use, de tags ``<a2ui-json>`` o de ``response_format``. Produce la
|
|
5
|
+
envoltura estándar que los renderers de A2UI esperan (MIME
|
|
6
|
+
``application/a2ui+json``), compatible con A2A.
|
|
7
|
+
|
|
8
|
+
El módulo no depende de ``a2a-sdk`` para evitar arrastrar el transporte al
|
|
9
|
+
importar ``anthropic-a2ui``; en su lugar define ``A2uiPart`` como un dataclass
|
|
10
|
+
ligero. Si el caller ya usa ``a2a-sdk`` puede convertirlo fácilmente, y se
|
|
11
|
+
ofrece ``to_a2a_datapart`` que intenta el bridge si ``a2a`` está instalado.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import json
|
|
17
|
+
from dataclasses import dataclass, field
|
|
18
|
+
from typing import Any, Optional
|
|
19
|
+
|
|
20
|
+
MIME_A2UI = "application/a2ui+json"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass(frozen=True, slots=True)
|
|
24
|
+
class A2uiPart:
|
|
25
|
+
"""Envoltura estándar de un mensaje A2UI para transporte.
|
|
26
|
+
|
|
27
|
+
Attributes:
|
|
28
|
+
mime: Tipo MIME bajo el que se etiqueta el payload. El protocolo A2UI
|
|
29
|
+
define ``application/a2ui+json`` como estándar para los DataParts.
|
|
30
|
+
data: El JSON A2UI ya parseado (lista de mensajes o mensaje único). Se
|
|
31
|
+
conserva como objeto para que el receptor lo use directamente; si hace
|
|
32
|
+
falta serializar, usar ``part.to_json_string()``.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
mime: str = MIME_A2UI
|
|
36
|
+
data: Any = field(default=None)
|
|
37
|
+
|
|
38
|
+
def to_json_string(self, **kwargs: Any) -> str:
|
|
39
|
+
"""Serializa ``data`` a cadena JSON compacta.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
**kwargs: Argumentos extra para ``json.dumps`` (por ejemplo
|
|
43
|
+
``indent=2`` para pretty-print).
|
|
44
|
+
"""
|
|
45
|
+
kwargs.setdefault("ensure_ascii", False)
|
|
46
|
+
return json.dumps(self.data, **kwargs)
|
|
47
|
+
|
|
48
|
+
def to_dict(self) -> dict[str, Any]:
|
|
49
|
+
"""Convierte el part a un dict plano apto para serialización genérica.
|
|
50
|
+
|
|
51
|
+
Útil para enviar por JSON-RPC, SSE o cualquier canal que no sea A2A.
|
|
52
|
+
"""
|
|
53
|
+
return {"mimeType": self.mime, "data": self.data}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def to_a2ui_part(payload: Any, *, mime: str = MIME_A2UI) -> A2uiPart:
|
|
57
|
+
"""Envuelve un payload A2UI (dict o lista) en un ``A2uiPart``.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
payload: El JSON A2UI ya parseado. Puede ser un único mensaje (dict) o
|
|
61
|
+
una lista de mensajes; el protocolo admite ambos.
|
|
62
|
+
mime: Tipo MIME a usar. Por defecto el estándar del protocolo.
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
Un ``A2uiPart`` listo para entregar al renderer o al transporte.
|
|
66
|
+
"""
|
|
67
|
+
return A2uiPart(mime=mime, data=payload)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def to_a2a_datapart(part: A2uiPart) -> Any:
|
|
71
|
+
"""Intenta convertir un ``A2uiPart`` a ``a2a.types.DataPart``.
|
|
72
|
+
|
|
73
|
+
Esta función es opcional y solo funciona si ``a2a-sdk`` está instalado. Si
|
|
74
|
+
no lo está, devuelve el dict plano (``part.to_dict()``) para que el caller
|
|
75
|
+
lo envuelva como prefiera.
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
Un ``DataPart`` de ``a2a`` si está disponible, o un dict con
|
|
79
|
+
``{"mimeType": ..., "data": ...}`` si no.
|
|
80
|
+
"""
|
|
81
|
+
try:
|
|
82
|
+
from a2a.types import DataPart # type: ignore
|
|
83
|
+
except Exception:
|
|
84
|
+
return part.to_dict()
|
|
85
|
+
dp = DataPart(**part.to_dict()) # type: ignore[call-arg]
|
|
86
|
+
return dp
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def parse_a2ui_part_json(raw: str | bytes) -> Any:
|
|
90
|
+
"""Parsea una cadena JSON de A2UI y devuelve el objeto.
|
|
91
|
+
|
|
92
|
+
Es un atajo sobre ``json.loads`` que acepta ``str`` o ``bytes`` y que lanza
|
|
93
|
+
``ValueError`` (en vez de ``json.JSONDecodeError``) para que el caller pueda
|
|
94
|
+
capturar de forma agnóstica al framework de serialización.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
raw: Cadena o bytes con JSON A2UI.
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
El objeto parseado (dict o lista).
|
|
101
|
+
|
|
102
|
+
Raises:
|
|
103
|
+
ValueError: Si el contenido no es JSON válido.
|
|
104
|
+
"""
|
|
105
|
+
try:
|
|
106
|
+
return json.loads(raw)
|
|
107
|
+
except json.JSONDecodeError as exc:
|
|
108
|
+
raise ValueError(f"JSON A2UI inválido: {exc}") from exc
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
"""Generación del system prompt para que Claude emita respuestas A2UI.
|
|
2
|
+
|
|
3
|
+
Esta pieza delega en ``A2uiSchemaManager`` de ``a2ui-agent-sdk`` para montar
|
|
4
|
+
el esquema del catálogo y los ejemplos few-shot, y expone una API pensada
|
|
5
|
+
para el flujo de Anthropic: construir el prompt, pasar a ``system`` de
|
|
6
|
+
``client.messages.stream(...)``, y opcionalmente podar componentes para
|
|
7
|
+
ahorrar tokens.
|
|
8
|
+
|
|
9
|
+
El paquete no redefine el formato del prompt; el ``A2uiSchemaManager`` es la
|
|
10
|
+
fuente canónica del protocolo. Aquí solo se adapta el punto de entrada y se
|
|
11
|
+
ofrece conveniencia (defaults razonables, validación de versiones soportadas).
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from typing import Any, Callable, Optional
|
|
17
|
+
|
|
18
|
+
from a2ui.basic_catalog.provider import BasicCatalog
|
|
19
|
+
from a2ui.schema.catalog import CatalogConfig
|
|
20
|
+
from a2ui.schema.manager import A2uiSchemaManager
|
|
21
|
+
|
|
22
|
+
# Versiones de protocolo que a2ui-agent-sdk 0.2.x soporta. Se mantiene aquí
|
|
23
|
+
# para dar errores claros antes de llegar al A2uiSchemaManager (que lanza un
|
|
24
|
+
# ValueError genérico).
|
|
25
|
+
SUPPORTED_VERSIONS = ("0.8", "0.9", "0.9.1")
|
|
26
|
+
DEFAULT_VERSION = "0.9"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class ClaudeA2uiPromptBuilder:
|
|
30
|
+
"""Construye el system prompt para que Claude genere respuestas A2UI.
|
|
31
|
+
|
|
32
|
+
Envuelve a ``A2uiSchemaManager`` con defaults orientados a Anthropic: el
|
|
33
|
+
rol del asistente, descripción del flujo y activación de esquema +
|
|
34
|
+
ejemplos. Permite reutilizar una instancia para varias llamamas y podar el
|
|
35
|
+
catálogo a un subconjunto de componentes para reducir tokens.
|
|
36
|
+
|
|
37
|
+
Attributes:
|
|
38
|
+
version: Versión del protocolo A2UI (``"0.9"`` por defecto).
|
|
39
|
+
catalogs: Lista de ``CatalogConfig`` activos.
|
|
40
|
+
manager: El ``A2uiSchemaManager`` subyacente, expuesto para uso avanzado.
|
|
41
|
+
|
|
42
|
+
Example:
|
|
43
|
+
```python
|
|
44
|
+
builder = ClaudeA2uiPromptBuilder()
|
|
45
|
+
system = builder.build(role_description="Construye formularios de contacto.")
|
|
46
|
+
# system -> string listo para pasar a client.messages.stream(system=system)
|
|
47
|
+
```
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
def __init__(
|
|
51
|
+
self,
|
|
52
|
+
catalogs: Optional[list[CatalogConfig]] = None,
|
|
53
|
+
version: str = DEFAULT_VERSION,
|
|
54
|
+
accepts_inline_catalogs: bool = False,
|
|
55
|
+
schema_modifiers: Optional[
|
|
56
|
+
list[Callable[[dict[str, Any]], dict[str, Any]]]
|
|
57
|
+
] = None,
|
|
58
|
+
) -> None:
|
|
59
|
+
"""Inicializa el builder.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
catalogs: Catálogos de componentes. Si es ``None`` se usa el
|
|
63
|
+
``BasicCatalog`` (Button, Text, TextField, etc.) bundled en
|
|
64
|
+
``a2ui-agent-sdk``.
|
|
65
|
+
version: Versión del protocolo. Debe ser una de
|
|
66
|
+
``SUPPORTED_VERSIONS``.
|
|
67
|
+
accepts_inline_catalogs: Si el cliente acepta catálogos inline.
|
|
68
|
+
schema_modifiers: Funciones para modificar el esquema antes de
|
|
69
|
+
inyectarlo en el prompt (caso avanzado).
|
|
70
|
+
|
|
71
|
+
Raises:
|
|
72
|
+
ValueError: Si ``version`` no está en ``SUPPORTED_VERSIONS``.
|
|
73
|
+
"""
|
|
74
|
+
if version not in SUPPORTED_VERSIONS:
|
|
75
|
+
raise ValueError(
|
|
76
|
+
f"Versión no soportada: {version!r}. Versiones válidas: {SUPPORTED_VERSIONS}"
|
|
77
|
+
)
|
|
78
|
+
self.version = version
|
|
79
|
+
if catalogs is None:
|
|
80
|
+
catalogs = [BasicCatalog.get_config(version=version)]
|
|
81
|
+
self.catalogs = list(catalogs)
|
|
82
|
+
self.manager = A2uiSchemaManager(
|
|
83
|
+
version=version,
|
|
84
|
+
catalogs=self.catalogs,
|
|
85
|
+
accepts_inline_catalogs=accepts_inline_catalogs,
|
|
86
|
+
schema_modifiers=schema_modifiers,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
def build(
|
|
90
|
+
self,
|
|
91
|
+
role_description: str,
|
|
92
|
+
workflow_description: str = "",
|
|
93
|
+
ui_description: str = "",
|
|
94
|
+
allowed_components: Optional[list[str]] = None,
|
|
95
|
+
allowed_messages: Optional[list[str]] = None,
|
|
96
|
+
include_schema: bool = True,
|
|
97
|
+
include_examples: bool = True,
|
|
98
|
+
validate_examples: bool = False,
|
|
99
|
+
include_icon_list: bool = True,
|
|
100
|
+
include_function_list: bool = True,
|
|
101
|
+
) -> str:
|
|
102
|
+
"""Genera el system prompt listo para pasar a Anthropic.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
role_description: Descripcion del rol del agente (inyecta al inicio).
|
|
106
|
+
workflow_description: Descripcion del flujo de trabajo (opcional).
|
|
107
|
+
ui_description: Descripcion extra de la UI (opcional).
|
|
108
|
+
allowed_components: Subconjunto de nombres de componentes que el
|
|
109
|
+
agente puede usar. Si es ``None``, todos los del catalogo.
|
|
110
|
+
allowed_messages: Subconjunto de tipos de mensaje.
|
|
111
|
+
include_schema: Si incluir el JSON Schema en el prompt.
|
|
112
|
+
include_examples: Si incluir ejemplos few-shot.
|
|
113
|
+
validate_examples: Si validar los ejemplos contra el esquema.
|
|
114
|
+
include_icon_list: Si anadir la lista de iconos validos al final del
|
|
115
|
+
prompt. Evita que Claude invente nombres de iconos no permitidos.
|
|
116
|
+
Por defecto ``True``.
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
Cadena con el system prompt completo.
|
|
120
|
+
"""
|
|
121
|
+
prompt = self.manager.generate_system_prompt(
|
|
122
|
+
role_description=role_description,
|
|
123
|
+
workflow_description=workflow_description,
|
|
124
|
+
ui_description=ui_description,
|
|
125
|
+
client_ui_capabilities=None,
|
|
126
|
+
allowed_components=allowed_components,
|
|
127
|
+
allowed_messages=allowed_messages,
|
|
128
|
+
include_schema=include_schema,
|
|
129
|
+
include_examples=include_examples,
|
|
130
|
+
validate_examples=validate_examples,
|
|
131
|
+
)
|
|
132
|
+
if include_icon_list:
|
|
133
|
+
icons = _extract_valid_icons(self.get_catalog())
|
|
134
|
+
if icons:
|
|
135
|
+
prompt += (
|
|
136
|
+
"\n\n--- ICONOS VALIDOS ---\n"
|
|
137
|
+
"El componente Icon solo acepta estos nombres (no inventes "
|
|
138
|
+
"otros):\n"
|
|
139
|
+
+ ", ".join(icons)
|
|
140
|
+
+ "\n"
|
|
141
|
+
)
|
|
142
|
+
if include_function_list:
|
|
143
|
+
functions = _extract_valid_functions(self.get_catalog())
|
|
144
|
+
if functions:
|
|
145
|
+
prompt += (
|
|
146
|
+
"\n\n--- FUNCIONES VALIDAS ---\n"
|
|
147
|
+
"Las unicas funciones que puedes usar en FunctionCall son estas "
|
|
148
|
+
"(no inventes otras como ternary, if, switch, etc.):\n"
|
|
149
|
+
+ ", ".join(functions)
|
|
150
|
+
+ "\n"
|
|
151
|
+
)
|
|
152
|
+
return prompt
|
|
153
|
+
|
|
154
|
+
def get_catalog(self) -> Any:
|
|
155
|
+
"""Devuelve el ``A2uiCatalog`` seleccionado (con poda aplicada si la hubo).
|
|
156
|
+
|
|
157
|
+
Expuesto para que el caller pueda obtener un validador o podar el
|
|
158
|
+
catálogo de forma independiente al prompt.
|
|
159
|
+
"""
|
|
160
|
+
return self.manager.get_selected_catalog()
|
|
161
|
+
|
|
162
|
+
def get_validator(self) -> Any:
|
|
163
|
+
"""Atajo para obtener el ``A2uiValidator`` del catalogo seleccionado."""
|
|
164
|
+
return self.get_catalog().validator
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _extract_valid_icons(catalog: Any) -> list[str]:
|
|
168
|
+
"""Extrae la lista de iconos validos del catalogo.
|
|
169
|
+
|
|
170
|
+
Recorre el schema del componente ``Icon`` y devuelve los valores del
|
|
171
|
+
``enum`` en su propiedad ``name``.
|
|
172
|
+
"""
|
|
173
|
+
components = catalog.catalog_schema.get("components", {})
|
|
174
|
+
icon_def = components.get("Icon")
|
|
175
|
+
if not icon_def:
|
|
176
|
+
return []
|
|
177
|
+
for sub in icon_def.get("allOf", []):
|
|
178
|
+
if "properties" not in sub:
|
|
179
|
+
continue
|
|
180
|
+
name_prop = sub["properties"].get("name")
|
|
181
|
+
if not name_prop:
|
|
182
|
+
continue
|
|
183
|
+
for branch in name_prop.get("oneOf", []):
|
|
184
|
+
if "enum" in branch:
|
|
185
|
+
return branch["enum"]
|
|
186
|
+
if "enum" in name_prop:
|
|
187
|
+
return name_prop["enum"]
|
|
188
|
+
return []
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def _extract_valid_functions(catalog: Any) -> list[str]:
|
|
192
|
+
"""Extrae la lista de funciones validas del catalogo.
|
|
193
|
+
|
|
194
|
+
Devuelve los nombres de las funciones definidas en el catalogo, que son
|
|
195
|
+
las unicas que se pueden usar en ``FunctionCall``.
|
|
196
|
+
"""
|
|
197
|
+
functions = catalog.catalog_schema.get("functions", {})
|
|
198
|
+
return list(functions.keys())
|
anthropic_a2ui/py.typed
ADDED
|
File without changes
|