Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

Synaptum

Framework de agentes y runtime durable, agnóstico al proveedor.

Synaptum es dueño de la semántica de ejecución de un agente: qué es un paso, dónde puede cortarse, qué puede repetirse, y cómo se vuelve a derivar el contexto. El sustrato —dónde se persiste, con qué retención, bajo qué política— pertenece al arnés que lo opera.

Versión: 1.0.0.dev0 · Python: ≥ 3.13 · Licencia: MIT

En construcción. La línea 0.x, con un diseño distinto, está congelada en v0.4.0. El estado real de esta por elemento está en roadmap.md.


La idea

El bucle de un agente no es un while escondido: es un generador asíncrono que cede el control en cada frontera significativa.

async for step in agent.run(tarea, session=session):
    match step:
        case ToolStep(phase=Phase.ATTEMPTED, risk=Risk.DESTRUCTIVE):
            ...
        case ModelStep(phase=Phase.COMPLETED, usage=consumo):
            ...
        case FinalStep(output=salida):
            ...

De esa forma salen cuatro propiedades, y ninguna otra estructura las da a la vez:

  1. El arnés obtiene sus puntos de enganche sin que Synaptum sepa que existe. Aprobaciones, guardarraíles y métricas son consumidores del stream.
  2. Cada yield es una frontera de checkpoint natural.
  3. Interrumpir es dejar de iterar; reanudar es volver a llamar con el mismo run_id.
  4. Probar es iterar una lista.

Lo que justifica todo lo demás

Al reanudar un run, una inferencia ya pagada no se paga otra vez.

store = SqliteCheckpointer("runs.db")

# Primera vuelta: dos llamadas al modelo, una a una herramienta.
async for step in agent.run("lee /x", session=Session("run-1", gateway, store)):
    ...

# El proceso muere. Otro proceso, otra conexión, mismo run_id.
async for step in agent.run("lee /x", session=Session("run-1", otro_gateway, store)):
    ...   # cero llamadas al modelo, cero a la herramienta

La ventana de contexto no se almacena: se vuelve a derivar de los mismos resultados en el mismo orden. Guardarla sería guardar dos veces lo mismo y arriesgarse a que discrepen.

Esto se apoya en una sola pieza: la identidad determinista de paso, publicada como especificación abierta. Quien la implemente obtiene la misma propiedad sin importar nada de Synaptum ni hablar Python.

Herramientas

El esquema sale de la firma. Un esquema escrito aparte se desincroniza, y cuando lo hace el modelo manda argumentos que la función no acepta — lejos del cambio que lo causó y con una inferencia ya pagada.

@tool(risk=Risk.DESTRUCTIVE)
async def borrar(path: Annotated[str, "Ruta absoluta"], forzar: bool = False) -> str:
    """Borra un fichero del disco."""
    ...

risk e idempotent se declaran y no se deducen: no son propiedades del tipo sino del efecto, y ninguna anotación puede saber que una función que devuelve str mueve dinero.

Los defaults son asimétricos a propósito — permisivo en riesgo, conservador en durabilidad: quien calla obtiene la clase más inocua y la garantía más cara.

Un tipo que no sabemos traducir falla al decorar, no al invocar.

Probarlo

uv run python examples/01_agente.py

Corre sin inferencia: las respuestas van guionizadas, y el resto es real —las herramientas se ejecutan, el journal se escribe, el consumo se mide—. Para apuntar a un modelo de verdad, dos variables y el mismo fichero sin tocar:

export SYNAPTUM_BASE_URL=http://localhost:8080/v1
export SYNAPTUM_MODEL=qwen3-0.6b
uv run python examples/01_agente.py

Que el mismo código sirva para las dos cosas no es comodidad: es la propiedad. Ver examples/ — el bucle entero, la reanudación medida (no afirmada), una aprobación humana a mitad de run, y el streaming con cancelación.

Sin dependencias

pip install synaptum      # 0 dependencias

El núcleo es stdlib puro. Pydantic, los proveedores, MCP y OpenTelemetry son extras. Los adaptadores se descubren por entry points, así que el núcleo no conoce a ninguno.

Desarrollo sin inferencia

No hay modelos locales disponibles en modo autónomo, así que el doble de desarrollo es infraestructura y no una utilidad de test. Ejercita todo lo que el bucle sabe hacer: llamadas a herramientas con ejecución real, streaming con cancelación, las tres disposiciones de denegación, la taxonomía de errores y el consumo de tres estados.

from synaptum.testing import FakeGateway, ReplayGateway, calls, says

# Guion escrito a mano: directo, y suficiente para la mayoría.
gateway = FakeGateway(calls("leer", path="/x"), says("dice hola"), tools=[leer])

# Respuestas reales grabadas, normalizadas por el adaptador de verdad.
gateway = ReplayGateway("fixtures/chat_completion.json", tools=[leer])

Un guion a mano dice lo que uno espera; una grabación dice lo que el proveedor hizo — y la diferencia aparece en los caminos que nadie escribe porque no se le ocurren.

Dónde encaja

Synaptum se sostiene solo. Habla con dos piezas por protocolo, y trae una implementación de referencia completa de cada una:

arnés          política, aprobaciones, secretos, retención, escalado
  │            Gateway — decide y ejecuta          · por defecto: LocalGateway
Synaptum       semántica de ejecución              ← esto
  │            Checkpointer — persiste, no decide  · por defecto: SqliteCheckpointer
almacén
                                    ─────
proveedor      cualquier endpoint OpenAI-compatible · por defecto: HttpModel

Las dos costuras son protocolos estructurales (typing.Protocol), no clases base. Quien las implemente no hereda ni importa nada nuestro, y puede estar escrito en otro lenguaje al otro lado de un socket. No hay ningún arnés, SDK ni plataforma de inferencia concretos en el árbol de dependencias: pip install synaptum trae cero paquetes.

En el despliegue donde nació, esas dos ranuras las ocupan un arnés llamado Aeon y un SDK llamado Axonium sobre una plataforma de inferencia local. Nada de eso es un requisito, y el paquete no los nombra: son un relleno posible de un protocolo abierto. El extra [axonium] existe para quien tenga esa combinación, y es opcional como el de Anthropic o el de OpenAI.

LocalGateway avisa de que no aplica política y marca cada comprobación con enforced=False — una comprobación dentro del proceso gobernado es advisoria, y que un run pase por ahí sin denegaciones no dice nada sobre si pasaría por un gateway real.

Contratos compartidos

Tres especificaciones con casos dorados, ejecutables por cualquier implementación con su propio runner. Viven fuera de este repositorio porque son artefactos conjuntos de varios proyectos, y copiarlos aquí los convertiría en una copia que se desincroniza. Synaptum no depende de ellos: son evidencia adicional, y sin ellos la suite pasa igual (209 de 277; el resto se salta).

export SYNAPTUM_CONTRACTS=/ruta/a/contratos    # opcional
Contrato Estado
Costura de durabilidad 8 casos · verdes contra nuestras dos implementaciones
Identidad de paso 10 casos · verdes
Normalización entre proveedores 14 casos · verdes contra el adaptador Python, todos menos uno grabaciones reales

Los casos describen resultados observables y nunca estructuras internas: dos implementaciones sin una línea de código en común tienen que poder reproducirlos.

Que los cuerpos sean grabaciones reales y no ejemplos escritos a mano ya encontró dos fallos nuestros. Un stream que era solo razonamiento no emitía ni un evento, porque el cuerpo inventado que teníamos antes solo llevaba deltas de texto. Y en un stream de razonamiento que termina llamando a una herramienta, el cierre del pensamiento caía en mitad de la llamada: cerrábamos el ciclo al ver texto, y ahí no había texto.

Un caso dorado no fija cifras. Una regrabación tiró cinco de los nuestros sin que ninguna implementación hubiera cambiado: fijaban tokens y un id de réplica, que pertenecen a una grabación y no a la especificación. Lo que se afirma es si un contador está medido o sin medir, y cómo se relacionan entre sí.

Estabilidad

Qué se garantiza, durante cuánto y qué no: está escrito y comprobado por un test, no recordado. Un arnés retiró su DSL de autoría apoyándose en ese compromiso, que es por qué existe por escrito. Ver API.md.

Estado

Fase
0 · Contratos completa
1 · Núcleo durable completa
2 · Contexto y observabilidad sin empezar
3 · Multi-agente sin empezar

roadmap.md lleva el detalle por elemento.


MIT © 2026 · Emeric Espiritu Santiago

Release files for synaptum 1.0.0rc1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for synaptum 1.0.0rc1
File Size Uploaded
synaptum-1.0.0rc1.tar.gz 168.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for synaptum 1.0.0rc1
File Interpreter ABI Platform
synaptum-1.0.0rc1-py3-none-any.whl Python 3 none any Details

Total release size: 251.2 kB

Release files / synaptum-1.0.0rc1.tar.gz

Download URL synaptum-1.0.0rc1.tar.gz
Size 168.8 kB
Tags Source
SHA-256 checksum
How to use checksums
e6b6df8c40ab4ed29949951d1733d29969f15e40e6fb952f89be83b51ed66bec
BLAKE2b-256 checksum
How to use checksums
9f564971aaad2844460dda0ff6d92864df93ece51d2930e077ebd4b1054d6140
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 18, 2026.

Transparency log

Release files / synaptum-1.0.0rc1-py3-none-any.whl

Download URL synaptum-1.0.0rc1-py3-none-any.whl
Size 82.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
cd2620eccdb222bc83a14d87ceec2d3c11463c98ca421506b085f0587aba8647
BLAKE2b-256 checksum
How to use checksums
179a3a16c4c178e3366ab91820b236b3e5e2b7f0e7cfde9a39a8b1c9b2fdd0ed
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 18, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.0.0rc1 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page