Saltar al contenido

Referencia

Generada del código. Escribir esto a mano sería escribir algo que caduca, y una referencia caducada manda a buscar un símbolo que ya no está.

Dice qué hay. El por qué y el cuándo están en las páginas anteriores, y eso no se deriva de una firma.

La superficie pública es lo que synaptum/__init__.py exporta, y nada más. Lo que no aparece aquí puede cambiar sin aviso, aunque se pueda importar.

Escribir un agente

Lo que se toca en el primer fichero.

Agent

Composición, no herencia. Un agente es su configuración más el bucle.

Agent(name: str, *, model: str, instructions: Any = None, tools: Sequence[Any] = (), delegates: Sequence[Any] = (), output: Any = None, limits: Limits | None = None) -> None
Miembro Firma
run run(self, task: str, *, session: Session) -> AsyncIterator[StepEvent] Ejecuta el agente cediendo cada paso.
stream stream(self, task: str, *, session: Session) -> AsyncIterator[StepEvent | StreamEvent] Lo mismo, entregando además los fragmentos del modelo según llegan.

Session

Un run: su identidad, por dónde sale y dónde se recuerda.

Campo Tipo Por defecto
run_id str obligatorio
gateway Gateway obligatorio
checkpointer Checkpointer (fábrica)

Reanudar es construir una Session con el mismo run_id y el mismo checkpointer. No hay nada más.

Limits

Topes del bucle.

Campo Tipo Por defecto
max_steps int 50
max_delegation_depth int 3
max_tool_chars int | None 16000
max_retries int 2
retry_base float 0.5
max_retry_wait float 30.0
reserved_output float 0.25

Son corrección, no política: evitan que un bucle mal formado no termine nunca. Los límites de gasto pertenecen al harness y llegan por la costura como Denied con terminate_run.

Delegate

Un subagente, tal como lo ve quien delega.

Campo Tipo Por defecto
agent 'Agent' obligatorio
description str ''
_depth int 0
Miembro Firma
definition Lo que el modelo ve: un nombre, cuándo usarlo, y un hueco para el brief.
execute async execute(self, brief: str, session: Any, run_id: str) -> tuple[Any, Any] Corre el subagente y devuelve (resultado, consumo).
name
risk

Se presenta al modelo como una herramienta de un solo parámetro: el brief. No se le ofrecen las herramientas del subagente, y eso es lo que hace barato delegar — el catálogo del padre no crece con el del hijo.

tool

def tool(fn: Callable[..., Any] | None = None, *, name: str | None = None, description: str | None = None, risk: Risk = <Risk.READ: read>, idempotent: bool = False, parameters: Mapping[str, Any] | None = None) -> Any

Convierte una función tipada en una herramienta.

Se puede usar con o sin paréntesis::

@tool def buscar(q: str) -> str: ...

@tool(risk=Risk.HARD_WRITE, idempotent=False) async def transferir(cuenta: str, importe: float) -> str: ...

Args: name: por defecto, el nombre de la función. description: por defecto, el primer párrafo del docstring. risk: clase de riesgo del efecto. Synaptum declara; el harness decide. idempotent: si el efecto puede repetirse sin consecuencias. Determina si el replay puede reintentar el paso tras una caída, y si su registro en el journal puede diferirse. parameters: esquema a mano, para el caso raro en el que la firma no baste. Salta la derivación por completo.

Tool

Una función con su contrato.

Tool(fn: Callable[..., Any], *, name: str | None = None, description: str | None = None, risk: Risk = <Risk.READ: read>, idempotent: bool = False, parameters: Mapping[str, Any] | None = None, localns: Mapping[str, Any] | None = None) -> None
Miembro Firma
invoke async invoke(self, call_id: str, arguments: Mapping[str, Any]) -> ToolResult Ejecuta y envuelve el resultado.
name

Sigue siendo invocable con normalidad — el decorador no la esconde — y además expone definition, que es lo que viaja en el Hello y lo que el modelo ve.

Risk

Nivel de riesgo del efecto de una herramienta — RM-21.

Valor
Risk.READ 'read'
Risk.SOFT_WRITE 'soft_write'
Risk.HARD_WRITE 'hard_write'
Risk.DESTRUCTIVE 'destructive'

Synaptum declara; el harness decide. El bucle garantiza que un paso destructivo no se ejecuta antes de tener una decisión; cuál sea esa decisión no es asunto suyo.

Vive aquí, junto a ToolDefinition, porque es parte del contrato de la herramienta: viaja con ella en el handshake y el gateway lo necesita para decidir antes de ejecutar.

ToolDefinition

Definición de una tool.

Campo Tipo Por defecto
name str obligatorio
description str ''
parameters Mapping[str, Any] (fábrica)
ref str | None None
risk Risk <Risk.READ: 'read'>
idempotent bool False

ref es la referencia versionada de H5. Cuando está presente, el esquema no viaja en cada llamada: el handshake lo resolvió una vez y el prefijo cacheado se mantiene estable. parameters sigue disponible para el modo autónomo, donde no hay registro contra el que resolver.

ToolChoice

ToolChoice(mode: "Literal['auto', 'none', 'required', 'named']" = 'auto', name: 'str | None' = None)

Campo Tipo Por defecto
mode Literal['auto', 'none', 'required', 'named'] 'auto'
name str | None None

Economía de contexto

Qué ve el modelo y qué costó. Usage dice cuánto; esto dice por qué.

cap_tool_output

def cap_tool_output(resultado: ToolResult, *, max_chars: int | None = 16000) -> ToolResult

Devuelve el resultado recortado, o el mismo si cabe.

Args: resultado: lo que la herramienta devolvió. max_chars: tope en caracteres. None desactiva el recorte.

Returns: Un ToolResult nuevo si hubo que recortar; el mismo objeto si no —así quien compare por identidad puede saber si se tocó algo.

economy

def economy(state: RunState) -> ContextEconomy

Calcula el informe de un run a partir de su journal.

Empareja cada intención de modelo con su resultado: la petición vive en ATTEMPTED —es donde está el prefijo— y el consumo en COMPLETED. Un turno sin resultado no se cuenta: se intentó y no se sabe qué costó.

ContextEconomy

El informe de un run.

Campo Tipo Por defecto
run_id str obligatorio
turns tuple[TurnEconomy, ...] ()
prefix_changes tuple[str, ...] (fábrica)
Miembro Firma
cache_hit_ratio Acierto de caché del run entero.
input_growth Cuánto crece la entrada por turno, en tokens.
prefix_rewrites Cuántas veces cambió el prefijo estable dentro del run.
report report(self) -> str El informe en texto, para leerlo en un terminal o pegarlo en un ticket.
total Suma de todos los turnos. None en un contador se propaga.

TurnEconomy

Un turno: una llamada al modelo y lo que costó.

Campo Tipo Por defecto
step_id str obligatorio
usage Usage obligatorio
prefix_rewritten bool obligatorio
Miembro Firma
cache_hit_ratio Fracción de la entrada servida desde caché, o None si no se midió.

Lo que cede el bucle

Los pasos que llegan por async for, y sus fases.

StepEvent

Base común. No se instancia directamente.

Campo Tipo Por defecto
run_id str obligatorio
step_id str obligatorio
step_seq int obligatorio
phase Phase obligatorio
at float | None None
meta Mapping[str, Any] (fábrica)
decision Decision | None None
kind str 'step'
Miembro Firma
durability
key

ModelStep

Una llamada al modelo.

Campo Tipo Por defecto
run_id str obligatorio
step_id str obligatorio
step_seq int obligatorio
phase Phase obligatorio
at float | None None
meta Mapping[str, Any] (fábrica)
decision Decision | None None
kind str 'model'
request Request | None None
response Response | None None
usage Usage (fábrica)
Miembro Firma
durability La intención es diferible; el resultado, no.
key

Siempre DURABLE: una inferencia cuesta dinero y no es reproducible, así que perder el registro de que ocurrió es exactamente el fallo que la durabilidad existe para evitar.

ToolStep

La ejecución de una herramienta.

Campo Tipo Por defecto
run_id str obligatorio
step_id str obligatorio
step_seq int obligatorio
phase Phase obligatorio
at float | None None
meta Mapping[str, Any] (fábrica)
decision Decision | None None
kind str 'tool'
call ToolCall | None None
result ToolResult | None None
risk Risk <Risk.READ: 'read'>
idempotent bool False
Miembro Firma
durability Depende del efecto, y ambas fases por igual.
key

Es el único evento cuya durabilidad depende del efecto: releer un fichero se puede repetir sin consecuencias, ordenar una transferencia no. Esa diferencia la declara el contrato de la tool, dentro del framework — es justamente lo que un harness no puede deducir desde fuera.

ApprovalStep

Una pausa a la espera de decisión.

Campo Tipo Por defecto
run_id str obligatorio
step_id str obligatorio
step_seq int obligatorio
phase Phase obligatorio
at float | None None
meta Mapping[str, Any] (fábrica)
decision Decision | None None
kind str 'approval'
subject str ''
Miembro Firma
durability
key

Siempre DURABLE, y por una razón distinta a las demás: no se le pregunta dos veces a una persona porque el proceso se cayó.

DelegateStep

Delegación a un subagente con contexto aislado.

Campo Tipo Por defecto
run_id str obligatorio
step_id str obligatorio
step_seq int obligatorio
phase Phase obligatorio
at float | None None
meta Mapping[str, Any] (fábrica)
decision Decision | None None
kind str 'delegate'
agent str ''
brief str ''
result Any None
usage Usage (fábrica)
Miembro Firma
durability
key

El worker recibe un brief estrecho y devuelve resultado y referencias, nunca su historial: es lo que hace que delegar aísle contexto en vez de duplicarlo.

FinalStep

Cierre del run.

Campo Tipo Por defecto
run_id str obligatorio
step_id str obligatorio
step_seq int obligatorio
phase Phase obligatorio
at float | None None
meta Mapping[str, Any] (fábrica)
decision Decision | None None
kind str 'final'
output Any None
usage Usage (fábrica)
Miembro Firma
durability
key

Phase

Las dos fases de un paso, con los nombres del contrato compartido.

Valor
Phase.ATTEMPTED 'attempted'
Phase.COMPLETED 'completed'

attempted y completed describen el estado del paso, no el contenido del evento — y coinciden con los nombres de las consultas derivadas de RunState, que es donde se leen.

Durability

Con qué urgencia debe persistirse un evento — RM-14.

Valor
Durability.DURABLE 'durable'
Durability.DEFERRABLE 'deferrable'

Separar las dos costuras no sirve para que el journal sea asíncrono: sirve para que cada evento tenga la política que le toca en vez de heredar la peor de las dos.

Event

Event = synaptum.core.events.ModelStep | synaptum.core.events.ToolStep | synaptum.core.events.DelegateStep | synaptum.core.events.ApprovalStep | synaptum.core.events.FinalStep

Represent a PEP 604 union type

make_step_id

def make_step_id(seq: int, kind: str) -> str

Acuña un identificador de paso determinista.

Deriva de la posición del paso en el run, nunca de un UUID ni de un reloj: un run reproducido debe acuñar exactamente los mismos identificadores, o el replay no puede reconocer lo ya hecho.

El ancho fijo mantiene el orden lexicográfico alineado con el numérico, de modo que un almacén que ordene por clave devuelve el journal en orden de ejecución sin índice adicional.

idempotency_key

def idempotency_key(event: "StepEvent") -> tuple[str, str, str]

La clave con la que el Checkpointer deduplica — A3.

Un append repetido con esta misma clave es un no-op, nunca un error.

Vocabulario del modelo

La forma que viaja por la costura. Contrato versionado, no detalle interno.

Message

Message(role: 'Role', content: 'tuple[ContentPart, ...]' = (), name: 'str | None' = None)

Campo Tipo Por defecto
role Role obligatorio
content tuple[ContentPart, ...] ()
name str | None None
Miembro Firma
assistant assistant(text: str) -> "Message"
developer developer(text: str) -> "Message"
text Concatena las partes de texto. Ignora razonamiento y binarios.
tool_calls
tool_results tool_results(*results: ToolResult) -> "Message"
user user(text: str, *, name: str | None = None) -> "Message"

Role

Los cinco roles que cualquier proveedor sabe expresar.

Valor
Role.SYSTEM 'system'
Role.USER 'user'
Role.ASSISTANT 'assistant'
Role.TOOL 'tool'
Role.DEVELOPER 'developer'

AUTO

AUTO = ToolChoice(mode='auto', name=None)

ToolChoice(mode: "Literal['auto', 'none', 'required', 'named']" = 'auto', name: 'str | None' = None)

ContentPart

ContentPart = synaptum.core.types.Text | synaptum.core.types.Image | synaptum.core.types.Audio | synaptum.core.types.Document | synaptum.core.types.ToolCall | synaptum.core.types.ToolResult | synaptum.core.types.Thinking | synaptum.core.types.RedactedThinking

Represent a PEP 604 union type

Text

Text(text: 'str', kind: "Literal['text']" = 'text')

Campo Tipo Por defecto
text str obligatorio
kind Literal['text'] 'text'

Image

Image(media_type: 'str', data: 'str | None' = None, url: 'str | None' = None, kind: "Literal['image']" = 'image')

Campo Tipo Por defecto
media_type str obligatorio
data str | None None
url str | None None
kind Literal['image'] 'image'

Audio

Audio(media_type: 'str', data: 'str | None' = None, url: 'str | None' = None, kind: "Literal['audio']" = 'audio')

Campo Tipo Por defecto
media_type str obligatorio
data str | None None
url str | None None
kind Literal['audio'] 'audio'

Document

Document(media_type: 'str', data: 'str | None' = None, url: 'str | None' = None, name: 'str | None' = None, kind: "Literal['document']" = 'document')

Campo Tipo Por defecto
media_type str obligatorio
data str | None None
url str | None None
name str | None None
kind Literal['document'] 'document'

ToolCall

Invocación pedida por el modelo.

Campo Tipo Por defecto
id str obligatorio
name str obligatorio
arguments Mapping[str, Any] (fábrica)
kind Literal['tool_call'] 'tool_call'

arguments ya viene decodificado. Los proveedores que lo entregan como cadena JSON lo parsean en su adaptador: el bucle no debería tener que adivinar si recibió un objeto o su serialización.

ToolResult

Resultado devuelto al modelo.

Campo Tipo Por defecto
call_id str obligatorio
content tuple['ContentPart', ...] ()
is_error bool False
kind Literal['tool_result'] 'tool_result'
Miembro Firma
of of(call_id: str, text: str, *, is_error: bool = False) -> "ToolResult"

is_error conserva la evidencia del fallo en vez de ocultarla: un modelo que no ve el error no puede corregirlo.

Thinking

Razonamiento visible.

Campo Tipo Por defecto
text str obligatorio
signature str | None None
kind Literal['thinking'] 'thinking'

signature transporta el token de verificación que algunos proveedores exigen devolver intacto en el siguiente turno.

RedactedThinking

Razonamiento cifrado por el proveedor.

Campo Tipo Por defecto
data str obligatorio
kind Literal['redacted_thinking'] 'redacted_thinking'

Opaco para nosotros, pero debe reenviarse tal cual o el proveedor pierde el hilo de su propio razonamiento.

Request

Lo que cruza la costura hacia quien ejecuta la llamada.

Campo Tipo Por defecto
model str obligatorio
messages tuple[Message, ...] ()
system str | None None
tools tuple[ToolDefinition, ...] ()
tool_choice ToolChoice ToolChoice(mode='auto', name=None)
max_output_tokens int | None None
temperature float | None None
top_p float | None None
stop tuple[str, ...] ()
response_format ResponseFormat | None None
provider_options Mapping[str, Any] (fábrica)

provider_options es la válvula de escape para lo que un proveedor concreto expone y el vocabulario común no cubre. Existe para que nadie tenga que bifurcar el tipo; usarla para algo que sí es común es una señal de que falta un campo en la especificación.

Response

Response(message: 'Message', finish_reason: 'FinishReason', usage: 'Usage' = <factory>, model: 'str' = '', provider_metadata: 'Mapping[str, Any]' = <factory>)

Campo Tipo Por defecto
message Message obligatorio
finish_reason FinishReason obligatorio
usage Usage (fábrica)
model str ''
provider_metadata Mapping[str, Any] (fábrica)
Miembro Firma
text
tool_calls

Usage

Contadores de tokens — H3.

Campo Tipo Por defecto
input int | None None
output int | None None
reasoning int | None None
cache_read int | None None
cache_write int | None None
estimated bool False
Miembro Firma
cache_hit_ratio Fracción de input servida desde caché, o None si no se midió.
total Tokens facturables, o None si falta alguno por medir.
zero zero() -> "Usage" Punto de partida para acumular. Distinto de Usage(), que es «nada medido».

Cinco campos, y ninguno por defecto a cero. None significa «nadie lo midió»; 0 significa «se midió y fue cero». La distinción no es purismo: si cache_write llega como cero cuando en realidad la fuente no lo reporta, el bucle concluye que escribir en caché es gratis y decide mal en cada compactación. Ese fallo no salta — solo cuadra mal.

No es hipotético. Un backend llama.cpp, por ejemplo, solo puede alimentar input, output y cache_read; reasoning y cache_write no existen en la fuente.

estimated marca los contadores derivados en vez de reportados — llama.cpp obliga a deducirlos de los timings del chunk final. Sin ese bit, FinOps factura sobre una estimación creyéndola exacta.

input es inclusivo: contiene los tokens servidos desde caché, y cache_read dice cuántos de ellos lo fueron. Sin fijarlo, dos implementaciones eligen convenciones distintas y la factura sale mal sin que nada falle.

FinishReason

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Valor
FinishReason.STOP 'stop'
FinishReason.LENGTH 'length'
FinishReason.TOOL_CALLS 'tool_calls'
FinishReason.CONTENT_FILTER 'content_filter'
FinishReason.ERROR 'error'

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to 'utf-8'. errors defaults to 'strict'.

ResponseFormat

ResponseFormat(kind: "Literal['text', 'json_object', 'json_schema']" = 'text', schema: 'Mapping[str, Any] | None' = None, name: 'str | None' = None, strict: 'bool' = True)

Campo Tipo Por defecto
kind Literal['text', 'json_object', 'json_schema'] 'text'
schema Mapping[str, Any] | None None
name str | None None
strict bool True

Streaming

El ciclo start/delta/end de cada clase de contenido.

StreamEvent

StreamEvent = synaptum.core.types.StreamStart | synaptum.core.types.TextStart | synaptum.core.types.TextDelta | synaptum.core.types.TextEnd | synaptum.core.types.ReasoningStart | synaptum.core.types.ReasoningDelta | synaptum.core.types.ReasoningEnd | synaptum.core.types.ToolCallStart | synaptum.core.types.ToolCallDelta | synaptum.core.types.ToolCallEnd | synaptum.core.types.Finish

Represent a PEP 604 union type

StreamStart

StreamStart(model: 'str' = '', kind: "Literal['stream_start']" = 'stream_start')

Campo Tipo Por defecto
model str ''
kind Literal['stream_start'] 'stream_start'

TextStart

TextStart(index: 'int' = 0, kind: "Literal['text_start']" = 'text_start')

Campo Tipo Por defecto
index int 0
kind Literal['text_start'] 'text_start'

TextDelta

TextDelta(text: 'str' = '', index: 'int' = 0, kind: "Literal['text_delta']" = 'text_delta')

Campo Tipo Por defecto
text str ''
index int 0
kind Literal['text_delta'] 'text_delta'

TextEnd

TextEnd(index: 'int' = 0, kind: "Literal['text_end']" = 'text_end')

Campo Tipo Por defecto
index int 0
kind Literal['text_end'] 'text_end'

ReasoningStart

ReasoningStart(index: 'int' = 0, kind: "Literal['reasoning_start']" = 'reasoning_start')

Campo Tipo Por defecto
index int 0
kind Literal['reasoning_start'] 'reasoning_start'

ReasoningDelta

ReasoningDelta(text: 'str' = '', index: 'int' = 0, kind: "Literal['reasoning_delta']" = 'reasoning_delta')

Campo Tipo Por defecto
text str ''
index int 0
kind Literal['reasoning_delta'] 'reasoning_delta'

ReasoningEnd

ReasoningEnd(index: 'int' = 0, signature: 'str | None' = None, kind: "Literal['reasoning_end']" = 'reasoning_end')

Campo Tipo Por defecto
index int 0
signature str | None None
kind Literal['reasoning_end'] 'reasoning_end'

ToolCallStart

ToolCallStart(id: 'str' = '', name: 'str' = '', index: 'int' = 0, kind: "Literal['tool_call_start']" = 'tool_call_start')

Campo Tipo Por defecto
id str ''
name str ''
index int 0
kind Literal['tool_call_start'] 'tool_call_start'

ToolCallDelta

Fragmento de los argumentos, tal como llegan del proveedor.

Campo Tipo Por defecto
arguments_delta str ''
index int 0
kind Literal['tool_call_delta'] 'tool_call_delta'

Los argumentos se transmiten troceados y sin garantía de ser JSON válido hasta el final; quien consume acumula y solo parsea al recibir el ToolCallEnd correspondiente.

ToolCallEnd

ToolCallEnd(index: 'int' = 0, kind: "Literal['tool_call_end']" = 'tool_call_end')

Campo Tipo Por defecto
index int 0
kind Literal['tool_call_end'] 'tool_call_end'

Finish

Cierre del stream con la respuesta acumulada.

Campo Tipo Por defecto
response Response obligatorio
kind Literal['finish'] 'finish'

Lleva el Response completo para que quien consumió los deltas no tenga que reconstruirlo, y para que Usage vuelva por la costura (H3) aunque el span de chat lo emita quien ejecutó la llamada (A5).

Las dos costuras

Protocolos estructurales: quien los implemente no hereda ni importa nada.

Gateway

Decide y ejecuta. Vive fuera del proceso del bucle.

Gateway(*args, **kwargs)
Miembro Firma
handshake async handshake(self, hello: Hello) -> Welcome Negocia versión y registra el catálogo de tools. Una vez por sesión.
invoke_model async invoke_model(self, request: Request, ctx: CallContext) -> Response Ejecuta una llamada al modelo y devuelve la respuesta completa.
invoke_tool async invoke_tool(self, call: ToolCall, ctx: CallContext, *, risk: Risk, tool_ref: str | None = None) -> ToolResult Ejecuta una herramienta.
stream_model stream_model(self, request: Request, ctx: CallContext) -> AsyncIterator[StreamEvent] Ejecuta una llamada al modelo en streaming — H2.

Cada método puede lanzar Denied con la Decision que corresponda — deny_step admite que el bucle intente otra cosa, terminate_run no, y require_approval suspende el run sin que sea un fallo.

Toda respuesta devuelve Usage (H3). El span de la operación lo emite este lado, porque es quien la realiza; pero los contadores vuelven, porque la economía de contexto del bucle depende de ellos y no son derivables desde fuera.

Checkpointer

Persiste el journal. No decide nada.

Checkpointer(*args, **kwargs)
Miembro Firma
append async append(self, run_id: str, event: StepEvent) -> AppendResult Añade un evento al journal.
load async load(self, run_id: str) -> RunState Reconstruye el diario de un run, en orden de escritura.

Es lo único que el harness tiene que implementar de forma obligatoria: dos métodos contra el almacén que ya use. No necesita entender el bucle del agente, ni la lógica de replay, ni cómo se acuñan los identificadores.

CallContext

Lo que acompaña a cada cruce de la costura.

Campo Tipo Por defecto
run_id str obligatorio
step_id str obligatorio
traceparent str | None None
tracestate str | None None
deadline_s float | None None
extra Mapping[str, Any] (fábrica)

traceparent y tracestate son W3C y no son decorativos: el harness emite los spans de chat y execute_tool porque es quien ejecuta la E/S, mientras el bucle emite los suyos de estructura. Sin propagar el contexto, unos y otros acaban en árboles de traza distintos y nadie puede seguir un run de punta a punta.

Decision

Decision(disposition: 'Disposition', reason_code: 'str' = '', message: 'str' = '')

Campo Tipo Por defecto
disposition Disposition obligatorio
reason_code str ''
message str ''
Miembro Firma
allowed

Disposition

«No» no es una sola cosa.

Valor
Disposition.ALLOW 'allow'
Disposition.DENY_STEP 'deny_step'
Disposition.TERMINATE_RUN 'terminate_run'
Disposition.REQUIRE_APPROVAL 'require_approval'

Sin distinguir estos casos el bucle no sabe si reintentar con otra cosa, esperar o parar, y cada implementación acabaría inventando su convención.

ALLOW

ALLOW = Decision(disposition=<Disposition.ALLOW: 'allow'>, reason_code='', message='')

Decision(disposition: 'Disposition', reason_code: 'str' = '', message: 'str' = '')

Hello

Apertura de sesión, enviada por el bucle.

Campo Tipo Por defecto
versions tuple[str, ...] (fábrica)
tools tuple[ToolDefinition, ...] ()
client str 'synaptum'

Combina la negociación de versión con el registro de herramientas en un solo viaje, y no por ahorrar una llamada: las dos cosas tienen que ocurrir antes del primer turno y ninguna puede repetirse a mitad de sesión. Cambiar el catálogo de tools con la sesión abierta reescribe el prefijo del prompt y tira la caché del proveedor.

Welcome

Respuesta del harness al Hello.

Campo Tipo Por defecto
version str obligatorio
tool_refs Mapping[str, str] (fábrica)
session_id str ''

tool_refs mapea nombre de tool a referencia versionada (H5). A partir de aquí el esquema no viaja en cada llamada: se resolvió una vez y el prefijo cacheado se mantiene estable turno tras turno.

RunState

Lo que load devuelve: el journal de un run, en orden.

Campo Tipo Por defecto
run_id str obligatorio
events tuple[StepEvent, ...] ()
Miembro Firma
attempted attempted(self, step_id: str) -> bool True si hay intención y no resultado.
completed completed(self, step_id: str) -> bool
final El evento de cierre, si el run ya terminó.
next_seq Posición que ocupará la siguiente entrada del diario.
result_of result_of(self, step_id: str) -> StepEvent | None Resultado ya registrado de un paso, si lo hay.

El bucle lo usa para adelantar — fast-forward — sobre los pasos que ya tienen resultado registrado, en vez de volver a ejecutarlos. Ahí está el valor entero de la ejecución durable: al reanudar, una inferencia ya pagada no se paga otra vez.

SEAM_VERSION

SEAM_VERSION = '0.1'

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

SUPPORT_WINDOW

SUPPORT_WINDOW = 2

int([x]) -> integer int(x, base=10) -> integer

negotiate

def negotiate(peer: Sequence[str], *, current: str = 0.1) -> str

Elige la versión más alta que ambos extremos hablan.

Recorre las nuestras de más nueva a más vieja, de modo que dos extremos al día no se quedan atascados en una antigua solo porque ambos la soportan.

Raises: SeamVersionError: si no hay ninguna en común. Fallar aquí es mucho mejor que descubrir la incompatibilidad campo a campo.

supported_versions

def supported_versions(current: str = 0.1, window: int = 2) -> tuple[str, ...]

Versiones que este extremo acepta, de la más nueva a la más vieja.

Runtime

Implementaciones de referencia. Ninguna es para producción a escala.

LocalGateway

Gateway de referencia para modo autónomo.

LocalGateway(*, model: ModelCall, stream: ModelStream | None = None, tools: Sequence[Tool] = (), policy: Policy | None = None, warn: bool = True) -> None
Miembro Firma
handshake async handshake(self, hello: Hello) -> Welcome
invoke_model async invoke_model(self, request: Request, ctx: CallContext) -> Response
invoke_tool async invoke_tool(self, call: ToolCall, ctx: CallContext, *, risk: Risk = <Risk.READ: read>, tool_ref: str | None = None) -> ToolResult
report report(self) -> str Qué habría decidido un gateway real, en texto.
stream_model stream_model(self, request: Request, ctx: CallContext) -> AsyncIterator[StreamEvent]

Args: model: callable (Request) -> Awaitable[Response]. Cualquier adaptador de proveedor encaja sin que este módulo lo conozca. stream: opcional. Si falta, el streaming se sirve igual entregando la respuesta completa como un único fragmento — y el Finish lo declara en provider_metadata["streamed"]. Ocultar la diferencia haría creer a quien corta por presupuesto que está ahorrando cuando ya ha pagado la respuesta entera. tools: herramientas decoradas con @tool, que se ejecutan de verdad. policy: política simulada, opcional. warn: pon False solo si ya sabes que esto no aplica nada.

Check

Una decisión que un gateway real habría tomado.

Campo Tipo Por defecto
kind str obligatorio
name str obligatorio
step_id str obligatorio
risk Risk <Risk.READ: 'read'>
arguments Mapping[str, Any] (fábrica)
decision Decision | None None
enforced bool False
detail Mapping[str, Any] (fábrica)

MemoryCheckpointer

Checkpointer en memoria. Cumple el contrato completo.

MemoryCheckpointer() -> None
Miembro Firma
append async append(self, run_id: str, event: StepEvent) -> AppendResult Idempotente por (run_id, step_id, phase). Un duplicado es no-op.
event_count event_count(self, run_id: str) -> int
load async load(self, run_id: str) -> RunState

Existe para dos cosas: que Synaptum sea utilizable sin harness, y que haya una implementación contra la que contrastar cualquier otra. No es un motor de producción y no pretende serlo.

SqliteCheckpointer

Checkpointer sobre SQLite. Cumple el contrato completo.

SqliteCheckpointer(path: str | Path = ':memory:') -> None
Miembro Firma
append async append(self, run_id: str, event: StepEvent) -> AppendResult Añade un evento. Un duplicado es no-op, garantizado por la clave.
close close(self) -> None
load async load(self, run_id: str) -> RunState Reconstruye el estado del run, o None si no existe.
runs runs(self) -> list[str] Identificadores de los runs almacenados. Fuera del protocolo.

Journal

Escribe eventos respetando la durabilidad que cada uno declara.

Journal(checkpointer: Checkpointer, run_id: str) -> None
Miembro Firma
flush async flush(self) -> None Vacía lo diferido. Se llama al cerrar el run, y ante cualquier salida.
record async record(self, event: StepEvent) -> None

El orden importa más de lo que parece. Los eventos diferidos se vacían antes de escribir uno durable, de modo que el journal conserva el orden de ejecución: si no, un evento estructural anterior aparecería después del efecto que lo siguió, y el registro dejaría de describir lo que pasó.

Replay

Consulta al journal si un paso ya ocurrió.

Replay(state: RunState) -> None
Miembro Firma
active True si hay algo que reproducir.
closed El cierre del run, si ya lo hubo.
resolve resolve(self, step_id: str, *, idempotent: bool = True) -> StepEvent | None Devuelve el resultado ya registrado, o None si toca ejecutar.

Tres respuestas posibles para cada paso, y la tercera es la interesante:

HttpModel

Llama a un endpoint HTTP y devuelve respuestas ya normalizadas.

HttpModel(base_url: str, *, api_key: str | None = None, provider: str | None = None, path: str = '/chat/completions', headers: Mapping[str, str] | None = None, timeout: float = 120.0) -> None
Miembro Firma
stream stream(self, request: Request) -> AsyncIterator[StreamEvent]

Args: base_url: raíz del servicio, por ejemplo http://localhost:8080/v1. api_key: si falta, no se manda cabecera de autorización — hay despliegues locales que no la piden, y mandar Bearer None es peor que no mandar nada. provider: nombre del adaptador. Por defecto se toma del prefijo de Request.model ("openai-compatible:qwen3-0.6b"), que es lo que el agente ya escribe. path: ruta del endpoint de chat, relativa a base_url. headers: cabeceras extra. Se mandan tal cual. timeout: segundos de espera por lectura, no por respuesta completa. Un modelo de razonamiento puede tardar mucho en emitir el primer token y eso no es un fallo.

Errores

La reintentabilidad viaja en el tipo, no en una tabla de códigos.

SynaptumError

Raíz de todo lo que este framework lanza a propósito.

SynaptumError(message: str = '', *, retryable: bool | None = None, retry_after: float | None = None) -> None

retryable es la única pregunta que el bucle le hace a un error.

ConfigurationError

Falta algo, sobra algo, o dos cosas se contradicen.

ConfigurationError(message: str = '', *, retryable: bool | None = None, retry_after: float | None = None) -> None

Nunca reintentable: el entorno no se arregla solo entre dos intentos.

ProviderError

Error devuelto por el proveedor, con su estado HTTP si lo hubo.

ProviderError(message: str = '', *, status: int | None = None, provider: str = '', retryable: bool | None = None, retry_after: float | None = None) -> None

Cuando no se pasa retryable explícito, se deduce del estado según la regla de clasificación. Un error sin estado se considera transitorio.

RequestTimeoutError

La petición no respondió dentro del plazo.

RequestTimeoutError(message: str = '', *, retryable: bool | None = None, retry_after: float | None = None) -> None

NetworkError

Falló el camino, no el destino.

NetworkError(message: str = '', *, retryable: bool | None = None, retry_after: float | None = None) -> None

AbortError

Alguien canceló deliberadamente — H2.

AbortError(message: str = '', *, retryable: bool | None = None, retry_after: float | None = None) -> None

No es un fallo del sistema y no se reintenta: reintentar una cancelación es exactamente lo contrario de lo que pidió quien canceló.

Denied

La costura de aplicación no dejó ocurrir el efecto — H4.

Denied(decision: Decision) -> None
Miembro Firma
disposition
terminal True si el run no puede continuar por ninguna vía.

Lleva la Decision entera para que el bucle sepa qué hacer: deny_step admite intentar otra cosa, terminate_run no, y require_approval suspende el run sin que sea un fallo.

InvalidToolCallError

El modelo pidió una tool que no existe, o con argumentos que no validan.

InvalidToolCallError(message: str = '', *, tool: str = '', call_id: str = '') -> None

No reintentable tal cual: lo que corrige esto es devolverle el error al modelo como ToolResult para que rectifique, no repetir la llamada.

ToolExecutionError

La tool existía, se invocó bien, y falló al ejecutarse.

ToolExecutionError(message: str = '', *, tool: str = '', retryable: bool = False) -> None

Reintentable solo si quien la definió declara que el efecto es idempotente.

NoObjectGeneratedError

Se pidió salida estructurada y no salió un objeto válido.

NoObjectGeneratedError(message: str = '', *, raw: str = '') -> None

Reintentable: el muestreo es estocástico y una segunda pasada suele acertar.

LimitExceeded

Se agotó un límite del bucle: pasos, reintentos, presupuesto de ventana.

LimitExceeded(limit: str, value: int) -> None

Es corrección, no política: los límites de gasto pertenecen al harness y llegan como Denied con terminate_run.

UncertainEffect

Al reanudar hay una intención registrada sin resultado, y el efecto no es idempotente.

UncertainEffect(step_id: str, *, detail: str = '') -> None

El proceso cayó entre el registro y el efecto, así que el efecto pudo haber ocurrido y no hay forma de saberlo desde aquí. Repetirlo a ciegas es lo peor posible; ignorarlo, tampoco es correcto.

El bucle no puede resolverlo por sí mismo — lo levanta para que lo decida quien tiene la información: el harness, o una persona.

SeamVersionError

No hay versión de costura común entre los dos extremos — RM-12.

SeamVersionError(message: str = '', *, retryable: bool | None = None, retry_after: float | None = None) -> None

retryable_for_status

def retryable_for_status(status: int | None) -> bool

Aplica la regla de clasificación a un estado HTTP.

Sin estado devuelve True: un fallo que ni siquiera llegó a tener respuesta se parece más a un problema de camino que a una petición inválida.

Un 4xx desconocido, en cambio, devuelve False. La familia entera significa «tu petición es el problema», y repetirla sin cambiarla da el mismo resultado — el 429 es la excepción, y está contemplada aparte.

Esquemas y prompts

Schema

Un tipo de salida: su esquema y cómo validar lo que vuelve.

Schema(*args, **kwargs)
Miembro Firma
dump dump(self, obj: Any) -> Any Devuelve el objeto como datos JSON, para el journal.
json_schema json_schema(self) -> Mapping[str, Any] El JSON Schema que viaja al proveedor.
validate validate(self, data: Any) -> Any Convierte datos ya parseados en el objeto tipado.

schema_for

def schema_for(spec: Any) -> Schema

Convierte lo que el usuario tenga a mano en un Schema.

Acepta, por este orden: algo que ya cumple el protocolo, un modelo de Pydantic, un dataclass, o un JSON Schema como diccionario.

json_schema_for

def json_schema_for(fn: Callable[..., Any], *, localns: Mapping[str, Any] | None = None) -> dict[str, Any]

Deriva el JSON Schema de los parámetros de una función tipada.

PromptTemplate

Un prompt con su versión.

Campo Tipo Por defecto
content str obligatorio
version str '1.0'
description str ''
variables Mapping[str, Any] (fábrica)
Miembro Firma
placeholders Nombres de las variables que el template usa.
render render(self, **values: Any) -> str Interpola las variables. Los argumentos ganan sobre los del template.

version no es decoración: cuando una respuesta sale mal en producción, la primera pregunta es con qué prompt se generó, y un literal en el código no sabe contestarla.

PromptProvider

Cualquier fuente de prompts.

PromptProvider(*args, **kwargs)
Miembro Firma
exists exists(self, name: str) -> bool
get get(self, name: str) -> PromptTemplate Raises: KeyError si no existe.

InMemoryPrompts

Prompts en un diccionario. Para tests y para sobrescribir en local.

InMemoryPrompts(prompts: Mapping[str, PromptTemplate | str] | None = None) -> None
Miembro Firma
exists exists(self, name: str) -> bool
get get(self, name: str) -> PromptTemplate
register register(self, name: str, template: PromptTemplate | str) -> None

FilePrompts

Prompts en un fichero JSON o YAML.

FilePrompts(path: str | Path) -> None
Miembro Firma
exists exists(self, name: str) -> bool
get get(self, name: str) -> PromptTemplate
reload reload(self) -> None

JSON funciona sin dependencias. YAML necesita PyYAML, y por eso no es el camino por defecto: en la v0.4 lo era, y arrastraba una dependencia para todo el mundo por una comodidad de sintaxis.

Carga perezosa y cacheada. reload() la invalida, que es lo que hace utilizable editar prompts sin reiniciar.

PromptRegistry

Encadena proveedores por prioridad: el primero que tenga el prompt, gana.

PromptRegistry(*providers: PromptProvider) -> None
Miembro Firma
add add(self, provider: PromptProvider) -> "PromptRegistry"
exists exists(self, name: str) -> bool
get get(self, name: str) -> PromptTemplate
prepend prepend(self, provider: PromptProvider) -> "PromptRegistry" Lo pone delante de todo: es cómo se sobrescribe sin borrar.

El orden importa y es el de registro, como un PATH. Así se sobrescribe un prompt en local anteponiendo un proveedor, sin editar el fichero que comparte todo el mundo.

fmt_dict

def fmt_dict(data: Mapping[str, Any], *, max_value: int = 500) -> str

Serializa un diccionario como líneas clave: valor.

fmt_list

def fmt_list(items: list[Any], *, prefix: str = '· ') -> str

Serializa una lista como líneas con prefijo.

fmt_records

def fmt_records(items: list[Mapping[str, Any]], template: str) -> str

Serializa una lista de diccionarios con una plantilla por línea.

Proveedores y utilidades

Provider

Traduce entre el vocabulario unificado y el dialecto de un proveedor.

Provider(*args, **kwargs)
Miembro Firma
from_wire from_wire(self, body: Mapping[str, Any]) -> Response Del cuerpo nativo al vocabulario unificado.
stream_from_wire stream_from_wire(self, chunks: Iterable[Mapping[str, Any]]) -> Iterator[StreamEvent] De los fragmentos nativos al ciclo de eventos unificado.
to_wire to_wire(self, request: Request) -> Mapping[str, Any] Del vocabulario unificado al cuerpo que espera el proveedor.

Las tres operaciones son puras: no abren conexiones y no leen credenciales. Por eso el corpus dorado de normalización se puede ejecutar sin un servidor delante.

dumps

def dumps(obj: Any) -> str

Serializa de forma determinista.

Claves ordenadas y separadores fijos: dos objetos iguales producen la misma cadena byte a byte, en cualquier proceso y en cualquier ejecución. Es lo que hace comparables los eventos entre ejecución y replay, y lo que permite que un prefijo de prompt sea estable frente a la caché del proveedor.

to_jsonable

def to_jsonable(obj: Any) -> Any

Convierte a estructuras JSON puras, recursivamente.

Los None se omiten: reducen el tamaño del journal y evitan que añadir un campo opcional cambie la serialización de valores que no lo usan, lo que rompería la comparación de replay entre versiones.

b64

def b64(data: bytes) -> str

Codifica binario para los campos data de las partes de contenido.

__version__

__version__ = '1.0.0rc2'

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Sin agrupar

Exportados y todavía sin sitio en esta página. Que aparezcan aquí es un aviso para quien mantiene la referencia, no para quien la lee.

deferred

def deferred(herramientas: Sequence[Tool], *, max_resultados: int = 5) -> list[Tool]

Convierte un catálogo grande en dos herramientas de prefijo fijo.

Args: herramientas: el catálogo entero, decorado con @tool. max_resultados: cuántas devuelve una búsqueda. Más no ayuda: el modelo elige peor cuantas más ve, que es medio problema que esto resuelve.

Returns: [buscar_herramientas, usar_herramienta], listas para Agent(tools=…).

Ejemplo::

agente = Agent("a", model=…, tools=deferred(las_cuarenta))

merece_la_pena

def merece_la_pena(herramientas: Sequence[Any]) -> bool

¿Vale la pena diferir este catálogo?

Está expuesto a propósito: es mejor que alguien pueda preguntar a que lo descubra midiendo su factura.

Dobles de desarrollo

synaptum.testing — Infraestructura, no una utilidad de test: es la vía principal para construir sin gastar.

FakeGateway

Gateway guionizado. Ejecuta de verdad las tools que se le registran.

FakeGateway(*script: ScriptItem, tools: Sequence[Tool] = (), chunk_size: int = 8, deny_tools: Mapping[str, Decision] | None = None) -> None
Miembro Firma
handshake async handshake(self, hello: Hello) -> Welcome
invoke_model async invoke_model(self, request: Request, ctx: CallContext) -> Response
invoke_tool async invoke_tool(self, call: ToolCall, ctx: CallContext, *, risk: Risk = <Risk.READ: read>, tool_ref: str | None = None) -> ToolResult
stream_model stream_model(self, request: Request, ctx: CallContext) -> AsyncIterator[StreamEvent] Emite la respuesta troceada, honrando la cancelación.

El guion se consume en orden. Cada elemento puede ser:

ReplayGateway

Gateway que sirve respuestas grabadas, normalizadas de verdad.

ReplayGateway(*bodies: str | Path, provider: str = 'openai-compatible', tools: Sequence[Tool] = (), deny_tools: Mapping[str, Decision] | None = None) -> None
Miembro Firma
handshake async handshake(self, hello: Hello) -> Welcome
invoke_model async invoke_model(self, request: Request, ctx: CallContext) -> Response
invoke_tool async invoke_tool(self, call: ToolCall, ctx: CallContext, *, risk: Risk = <Risk.READ: read>, tool_ref: str | None = None) -> ToolResult
rewind rewind(self) -> None Vuelve al primer cuerpo. Útil para reanudar el mismo run dos veces.
stream_model stream_model(self, request: Request, ctx: CallContext) -> AsyncIterator[StreamEvent]

Args: bodies: rutas a cuerpos grabados. .sse se sirve como stream; cualquier otra extensión, como respuesta completa. provider: adaptador con el que normalizar. Por defecto, el dialecto OpenAI-compatible, que es el que habla casi todo. tools: herramientas decoradas, que se ejecutan de verdad. deny_tools: denegaciones simuladas por nombre de herramienta.

says

def says(text: str, *, usage: Usage | None = None) -> Response

Respuesta de texto que cierra el turno.

calls

def calls(name: str, *, id: str = 'call-1', usage: Usage | None = None, **arguments: Any) -> Response

Respuesta que pide una herramienta.

split_sse

def split_sse(text: str) -> list[dict[str, Any]]

Separa los fragmentos de un cuerpo SSE.

Vive aquí y no en el adaptador porque cómo llegan los fragmentos por el cable no es normalización: es transporte, y esa separación es lo que permite ejercitar la normalización con un fichero.

El centinela termina el cuerpo; no se salta. Un gateway real llegó a mandar dos —reenviaba el del backend y añadía el suyo—, y la diferencia entre saltarlos y parar en el primero no era cosmética: todo lo que registraba la petición vivía pasado ese punto, así que el cliente correcto era justo el que no se facturaba. Se arregló allí, y por eso mismo conviene que esto no dependa de cuántos vengan.

DEFAULT_USAGE

DEFAULT_USAGE = Usage(input=100, output=20, reasoning=0, cache_read=0, cache_write=0, estimated=False)

Contadores de tokens — H3.

Cliente MCP

synaptum.mcp — Extra [mcp]. El núcleo no lo carga.

MCPTools

Las herramientas de un servidor MCP, listas para pasárselas a un Agent.

MCPTools(session: Any, *, prefix: str = '') -> None
Miembro Firma
connected connected(session: Any, *, prefix: str = '') -> "AsyncIterator[MCPTools]" Sobre una sesión que ya existe — la abre y la cierra quien la creó.
destructivas Las que entran marcadas como destructivas.
discover async discover(self) -> list[MCPTool] Pregunta al servidor qué publica y lo adapta.
stdio stdio(command: str, *args: str, prefix: str = '', env: Mapping[str, str] | None = None) -> "AsyncIterator[MCPTools]" Arranca un servidor MCP por stdio y descubre sus herramientas.

Args: session: sesión MCP ya inicializada. prefix: prefijo para los nombres. Con dos servidores que publiquen search, el modelo no puede distinguirlos — y el que gana es el que se registró último, en silencio.

mcp_risk

def mcp_risk(annotations: Any) -> Risk

Traduce las pistas de una herramienta MCP a un nivel de riesgo.

Los valores por defecto son los de la especificación MCP, no los nuestros: sin anotaciones, destructiva. Nuestro @tool usa READ por defecto porque quien escribe la función está delante y puede declarar; aquí el autor no está, y suponer en su lugar es suponer a favor.

Agentes remotos (A2A)

synaptum.a2a — Solo biblioteca estándar. En el camino gobernado la llamada sale por el proxy del arnés.

RemoteDelegate

Un agente remoto, tal como lo ve quien delega.

Campo Tipo Por defecto
name str obligatorio
url str obligatorio
risk Risk obligatorio
description str ''
poll_every float 1.0
timeout float 600.0
headers Any None
_depth int 0
Miembro Firma
definition
execute async execute(self, brief: str, session: Any, run_id: str) -> tuple[Any, Usage] Manda el brief, espera, y devuelve el resultado.

Args: name: con qué nombre lo ve el modelo. url: el agente, o el proxy que lo gobierna. En el camino gobernado es lo segundo: el framework apunta al endpoint del arnés y no cambia nada más. description: cuándo usarlo. Si falta se toma de su tarjeta. risk: obligatorio, sin valor por defecto. Ver abajo por qué no lo tiene. poll_every: segundos entre consultas mientras la tarea trabaja. timeout: tope total de espera.

A2AClient

Los cuatro métodos que hacen falta para delegar.

A2AClient(base_url: str, *, headers: Mapping[str, str] | None = None, timeout: float = 120.0) -> None
Miembro Firma
agent_card async agent_card(self) -> AgentCard Lee /.well-known/agent-card.json.
cancel_task async cancel_task(self, task_id: str) -> Task Cancelar es lo que hacemos al cerrar el iterador, también por red.
get_task async get_task(self, task_id: str) -> Task
list_tasks async list_tasks(self, *, context_id: str) -> list[Task] Las tareas de un contexto.
send_message async send_message(self, brief: str, *, context_id: str, message_id: str, task_id: str | None = None) -> Task

Args: base_url: raíz del agente remoto, o del proxy que lo gobierna. headers: cabeceras extra — lo que pida el securitySchemes de su tarjeta. timeout: segundos por lectura. Un agente puede tardar mucho en el primer byte y eso no es un fallo.

AgentCard

Lo que un agente publica en /.well-known/agent-card.json.

Campo Tipo Por defecto
name str obligatorio
description str ''
url str ''
version str ''
skills tuple[Mapping[str, Any], ...] ()
capabilities Mapping[str, Any] (fábrica)
security_schemes Mapping[str, Any] (fábrica)
Miembro Firma
push_notifications
streaming

Declara qué sabe hacer, no qué puede romper: no hay campo de riesgo en la especificación. De ahí que un agente remoto entre como destructivo mientras alguien no diga lo contrario.

Task

Una unidad de trabajo al otro lado.

Campo Tipo Por defecto
task_id str obligatorio
context_id str ''
state TaskState <TaskState.SUBMITTED: 'submitted'>
message str ''
artifacts tuple[Artifact, ...] ()
raw Mapping[str, Any] (fábrica)
Miembro Firma
esperando
result Lo que vuelve al agente que delegó.
terminal

task_id lo asigna el servidor y no podemos aportarlo — es el hecho que decide todo nuestro diseño de reanudación. context_id sí lo ponemos nosotros, y es por donde se reencuentra.

TaskState

Los ocho estados de una tarea A2A.

Valor
TaskState.SUBMITTED 'submitted'
TaskState.WORKING 'working'
TaskState.COMPLETED 'completed'
TaskState.FAILED 'failed'
TaskState.CANCELED 'canceled'
TaskState.REJECTED 'rejected'
TaskState.INPUT_REQUIRED 'input_required'
TaskState.AUTH_REQUIRED 'auth_required'

Se distinguen tres clases, y la distinción es la que decide qué hace el bucle: los terminales no aceptan más mensajes, los que esperan a alguien sí, y el resto siguen su curso.

Artifact

Un entregable producido por una tarea.

Campo Tipo Por defecto
artifact_id str ''
name str ''
parts tuple[Mapping[str, Any], ...] ()
Miembro Firma
text

Distinto de un mensaje: un mensaje es conversación, un artefacto es resultado.

TERMINALES

TERMINALES = frozenset({<TaskState.CANCELED: 'canceled'>, <TaskState.COMPLETED: 'completed'>, <TaskState.FAILED: 'failed'>, <TaskState.REJECTED: 'rejected'>})

Build an immutable unordered collection of unique elements.

ESPERANDO

ESPERANDO = frozenset({<TaskState.AUTH_REQUIRED: 'auth_required'>, <TaskState.INPUT_REQUIRED: 'input_required'>})

Build an immutable unordered collection of unique elements.