Liquid

Conecta tu agente a cualquier API HTTP sobre la marcha: descubre y mapea cualquier API REST una vez, luego obtiene datos tipados de manera determinista.

Documentación

Liquid

Conecta tu agente de IA a cualquier cosa — sin necesidad de escribir ni mantener conectores.

Apunta Liquid a una URL o una base de datos y él descubre la interfaz por ti: detecta su forma, la mapea a los campos que pediste, y maneja autenticación, paginación y normalización — registros tipados, sin código de cliente. Cuando el sistema upstream cambia, se re-mapea y sigue funcionando. La misma API pequeña — fetch · query · write · sense — alcanza APIs web, bases de datos, otros agentes (MCP/A2A), correo electrónico, e incluso sistemas IoT e industriales (MQTT, Modbus, OPC UA, BACnet). Un LLM hace el aprendizaje en la configuración (y ante cambios); la ruta de datos en sí no hace ninguna llamada a un modelo.

PyPI License Python


Lo que un agente puede alcanzar a través de Liquid

Una API orientada al agente (fetch · query · write · sense) sobre todo lo que un agente podría necesitar tocar — Liquid descubre cómo hablar con ello para que el agente no tenga que hacerlo. Son los sentidos y las manos del agente: fetch/query sondean, sense percibe un flujo de eventos en vivo, write actúa sobre el mundo.

  • APIs web y mensajería — REST/JSON, GraphQL, SOAP/WSDL, gRPC, WebSocket, flujos SSE/NDJSON, MQTT (pub/sub IoT — suscríbete para sentir, publica para actuar)
  • Correo electrónico — IMAP/SMTP (cualquier proveedor, contraseña de aplicación u OAuth2 XOAUTH2) y la API de Gmail (OAuth2): lee un buzón, sense correo nuevo a medida que llega, y envía
  • Industrial / OT — Modbus (PLCs, sensores) y OPC UA (nodos Industry-4.0, suscripciones nativas) para la planta de fábrica; BACnet para edificios (HVAC/BMS) — lee, escribe y siente
  • Dispositivos Android — teléfonos, TV boxes, kioscos vía ADB: siente logcat, lee shell, actúa con input/am
  • Otros agentes y herramientas — cualquier servidor MCP, agentes A2A, manifiestos de plugins de ChatGPT
  • Bases de datos — Postgres (+ pgvector), MySQL/MariaDB, SQLite, DuckDB, SQL Server, Neo4j (grafos), MongoDB (documentos), Redis (clave-valor)
  • Personas, lugares y cosas — un humano, un hogar o un coche como nodo vía connectors: Telegram (percibe mensajes, send respuestas), Home Assistant (percibe los cambios de estado de un hogar inteligente completo, actúa vía call_service — luces, cerraduras, medios), y Smartcar (percibe un vehículo conectado en ~30 marcas — ubicación/batería/combustible — y actúa: lock/unlock, carga)

Apunta a un endpoint https://…, un DSN postgres://… / mongodb://… / redis://…, un objetivo grpc://…, u otro servidor MCP — el descubrimiento identifica la interfaz, aprende su forma, y entrega registros tipados a tu agente. El mismo fetch/query/write funciona sin importar lo que haya debajo. Sin conector por servicio que escribir a mano; la integración se mantiene sola cuando el upstream cambia.

# A web API it has never seen — no spec, no connector, no auth
adapter = await liquid.get_or_create(
    "https://api.openbrewerydb.org/v1/breweries",
    target_model={"name": "str", "city": "str", "country": "str"},
    auto_approve=True,
)
breweries = await liquid.fetch(adapter)            # typed records

# A database is just another interface — same API, and it writes too
db = await liquid.get_or_create("postgresql://reader@host/shop",
                                target_model={"id": "int", "email": "str"},
                                auto_approve=True)
orders = await liquid.fetch(db, "/public/orders")
await liquid.write(db, "/public/orders", op="insert",
                   values={"email": "a@b.com", "total_cents": 9900},
                   allow_write=True)               # opt-in; mutates the store

No escribes ningún conector ni esquema: un LLM aprende la interfaz una vez en la configuración (las bases de datos se auto-inspeccionan y se saltan incluso eso), y la integración se repara sola cuando el upstream cambia. El runtime es transporte determinista simple — costo predecible, comportamiento reproducible, nada que supervisar.

Diseñado para las restricciones que enfrentan los agentes reales

Llegar a todo es la mitad. La otra mitad es que los agentes pagan por cada token, se confunden con formas inconsistentes, y no pueden parsear prosa de errores. Liquid responde a cada una con un primitivo concreto — todo incluido, todo en PyPI.

Control del presupuesto de contexto

# Search / aggregate server-side instead of fetch-then-filter — 10-100x fewer tokens
orders = await liquid.search(adapter, "/orders",
    where={"total_cents": {"$gt": 10000}, "status": "paid"}, limit=20)

stats = await liquid.aggregate(adapter, "/orders",
    group_by="status", agg={"total_cents": "sum", "id": "count"})

hits = await liquid.text_search(adapter, "/tickets", "shipping delay")  # BM25-lite

data = await liquid.fetch(adapter, "/orders", max_tokens=2000)      # budget cap
data = await liquid.fetch(adapter, "/customers", verbosity="terse") # id + 1-2 fields

Normalización entre fuentes

liquid = Liquid(..., normalize_output=True)
# Stripe {amount:1000,currency:"usd"} · PayPal {value:"10.00",currency_code:"USD"}
#   → Money(amount_cents=1000, currency="USD", amount_decimal=Decimal("10.00"))

Las marcas de tiempo (Unix / ISO 8601 / RFC 2822) se colapsan a UTC datetime; los envoltorios de paginación ({data:[…]} / {results:[…]} / cabeceras Link) se aplanan; los campos de ID se normalizan entre id / _id / uuid / *_id.

Intenciones canónicas — un modelo mental único entre servicios

await liquid.execute_intent(adapter, "charge_customer",
    {"customer_id": "cus_xyz", "amount_cents": 9999, "currency": "USD"})
# Same intent on Stripe / Braintree / Square / Adyen — 71 canonical intents

Recuperación estructurada — los agentes se auto-curan sin parsear texto

try:
    await liquid.fetch(adapter, "/orders")
except LiquidError as e:
    if e.recovery and e.recovery.next_action:
        await agent.call_tool(e.recovery.next_action.tool, e.recovery.next_action.args)

Cada error lleva un Recovery con next_action: ToolCall, retry_safe, y retry_after_seconds. 401 → store_credentials. 404/410 → repair_adapter. 429 → reintenta después del retraso dado. Y cuando el esquema del upstream cambia, los adaptadores se auto-curan (repair_adapter) — el agente sigue funcionando.

Costo predecible — conoce antes de llamar

est = await liquid.estimate_fetch(adapter, "/orders")
# FetchEstimate(expected_items=250, expected_tokens=52_000, confidence="high", …)
if est.expected_tokens < my_budget:
    data = await liquid.fetch(adapter, "/orders")

Las herramientas emitidas por to_tools() llevan un bloque metadata (cost_credits, typical_latency_ms, cached, idempotent, side_effects, related_tools) para que el agente pueda razonar sobre qué herramienta elegir — y las herramientas ambientales (liquid_check_quota, liquid_list_adapters, …) le permiten preguntar sobre el estado en lugar de memorizarlo.


Impacto medido

Benchmarks deterministas en tareas realistas de agentes (fixtures de 500 pedidos, 200 tickets, HTTP simulado) — reproducibles vía python -m benchmarks.run:

TareaMétricaLínea baseCon LiquidDelta
Encontrar 10 pedidos de más de $100tokens75,4821,519−98%
Ingresos por estado (agregado)tokens75,482115−100%
Obtener cliente (solo id+email)tokens42412−97%
Recuperarse de 401next_action estructuradono
Encontrar el ticket de envíotokens14,588154−99%
Consistencia Stripe↔PayPalsuperposición de campos0.111.00+9×
Evitar llamada desperdiciada vía estimacióntokens14,9430−100%
Límite de presupuesto max_tokens=2000tokens14,9431,999−87%

Metodología completa + desglose por tarea: benchmarks/RESULTS.md.

Instalación

pip install liquid-api                 # core + bundled MCP server (the `liquid-mcp` command)
pip install 'liquid-api[discovery]'    # + an LLM for discovering spec-less REST APIs & field mapping

¿Necesitas un LLM extra? Las interfaces auto-descriptivas — OpenAPI, GraphQL, gRPC, MCP, A2A, WSDL — y todas las bases de datos (introspección) se descubren sin LLM, y todo el runtime (fetch/query/write/sense) nunca llama a un modelo. Solo necesitas un backend LLM para descubrir una API REST que no tiene especificación legible por máquina (heurística + LLM) y para mapear sus campos. [discovery] trae LiteLLM, que alcanza OpenAI / Gemini / Anthropic / local / 100+ proveedores; o elige uno directamente:

pip install 'liquid-api[gemini]'     # Google Gemini   (or [anthropic]; OpenAI/local work with no extra via base_url)
pip install 'liquid-api[grpc]'       # gRPC transport (reflection)
pip install 'liquid-api[ws]'         # WebSocket transport
pip install 'liquid-api[pg]'         # Postgres / pgvector (asyncpg)
pip install 'liquid-api[mysql]'      # MySQL / MariaDB (aiomysql); SQLite needs no extra
pip install 'liquid-api[neo4j]'      # Neo4j graph (Bolt / Cypher)
pip install 'liquid-api[duckdb]'     # DuckDB (embedded analytics)
pip install 'liquid-api[mssql]'      # SQL Server (ODBC; needs a system ODBC driver)
pip install 'liquid-api[mongodb]'    # MongoDB (collections as endpoints)
pip install 'liquid-api[redis]'      # Redis (keyspace namespaces as endpoints)
pip install 'liquid-api[mqtt]'       # MQTT (IoT pub/sub)
pip install 'liquid-api[modbus]'     # Modbus (industrial registers)
pip install 'liquid-api[opcua]'      # OPC UA (Industry-4.0 nodes + subscriptions)
pip install 'liquid-api[bacnet]'     # BACnet (building automation; ADB needs the system `adb` binary)
# Framework integration (LangChain / OpenAI / Anthropic / MCP) is built in — no extra package.

El núcleo no tiene dependencias — la biblioteca de cada backend es un extra opcional, importada solo cuando se usa.

Vélo funcionar — en vivo, sin pre-configuración

Apunta Liquid a una API que nunca ha visto (sin adaptador, sin especificación OpenAPI, sin autenticación) y obtén registros tipados — no escribes ningún conector; el descubrimiento + mapeo es el único lugar donde corre un modelo. Ejecutable de principio a fin vía examples/live_quickstart.py:

Connecting to an API Liquid has never seen:
  https://api.openbrewerydb.org/v1/breweries

  discovery method : rest_heuristic
  mapped fields    : ['name', 'city', 'state', 'country']
  LLM calls so far : 2  (discovery + mapping)

fetch() -> 50 typed records; first 3:
   {'name': '(405) Brewing Co', 'city': 'Norman', 'state': 'Oklahoma', 'country': 'United States'}
   {'name': '(512) Brewing Co', 'city': 'Austin', 'state': 'Texas', 'country': 'United States'}
   {'name': '1 of Us Brewing Company', 'city': 'Mount Pleasant', 'state': 'Wisconsin', 'country': 'United States'}

  LLM calls during fetch : 0
  LLM calls on 2nd fetch : 0

No escribiste ningún conector, ni esquema, ni pegamento de autenticación — Liquid aprendió la interfaz por ti, y la reaprenderá si cambia. Ese es el punto: integraciones que no construyes ni supervisas.

Ejecútalo como servidor MCP (open source, auto-alojado)

Expón el motor a cualquier cliente MCP (Claude Desktop, Cursor, Claude Code) — se ejecuta en tu propio proceso, sin nube, sin cuenta, sin bloqueo:

Add to Cursor

Un clic en Cursor (el botón escribe el servidor en tu mcp.json; agrega tu OPENAI_API_KEY en la configuración MCP de Cursor después). O configúralo manualmente:

pip install liquid-api
export OPENAI_API_KEY=sk-...        # or GEMINI_API_KEY / ANTHROPIC_API_KEY,
                                    # or OPENAI_BASE_URL=http://localhost:11434/v1 for local (Ollama/vLLM)
liquid-mcp                          # or: python -m liquid.mcp_server

Cero instalación con uvx (el paquete liquid-mcp hace que el comando se ejecute por nombre) — Claude Code:

claude mcp add liquid --scope user -e OPENAI_API_KEY=sk-... -- uvx liquid-mcp

Claude Desktop / cualquier cliente MCP:

{ "mcpServers": { "liquid": {
  "command": "uvx",
  "args": ["liquid-mcp"],
  "env": { "OPENAI_API_KEY": "sk-..." }
} } }

(O después de pip install liquid-api, elimina uvx y usa "command": "liquid-mcp" directamente.)

Un clic en Claude Desktop: instala el bundle .mcpb — te pide tu clave de modelo durante la instalación (almacenada en el llavero del SO), sin JSON que editar. Requiere uv en la máquina.

Herramientas: liquid_connect (descubre + mapea cualquier interfaz), liquid_fetch, liquid_query (búsqueda/agregación del lado del servidor), liquid_estimate (costo/tamaño pre-vuelo, sin llamada), liquid_list_adapters, liquid_discover. La superficie es solo lectura por defecto; inicia el servidor con LIQUID_ALLOW_WRITES=1 para también exponer liquid_execute (insertar/actualizar/eliminar en base de datos). Los adaptadores y credenciales persisten bajo ~/.liquid. Respaldado por cualquier LLM — OpenAI, Gemini, Anthropic, cualquier endpoint compatible con OpenAI/local vía base_url, 100+ proveedores vía LiteLLM, o tu propia función a través de CallableBackend.

Inicio rápido — agente LangGraph

from liquid import Liquid, InMemoryCache, RateLimiter
from liquid._defaults import InMemoryVault, InMemoryAdapterRegistry, CollectorSink
from liquid_langchain import LiquidToolkit
from langgraph.prebuilt import create_react_agent
from langchain_openai import ChatOpenAI

liquid = Liquid(
    llm=my_llm, vault=InMemoryVault(), sink=CollectorSink(),
    registry=InMemoryAdapterRegistry(), cache=InMemoryCache(), rate_limiter=RateLimiter(),
    normalize_output=True,    # cross-source canonical shapes
    include_meta=True,        # _meta block on every response
)

adapter = await liquid.get_or_create(
    "https://api.shopify.com",
    target_model={"id": "str", "total_cents": "int", "customer_email": "str"},
    credentials={"access_token": "shpat_..."},
    auto_approve=True,
)

tools = LiquidToolkit(adapter, liquid).get_tools()
agent = create_react_agent(ChatOpenAI(model="gpt-4o-mini"), tools)
result = await agent.ainvoke(
    {"messages": [("user", "Find 5 recent orders over $100 from VIP customers")]}
)

Las herramientas del agente vienen con descripciones ricas (CUÁNDO usar, PARA QUÉ NO, forma de retorno, costo), recuperación estructurada en cada error, y búsqueda del lado del servidor para que nunca traiga 500 pedidos para encontrar 5.

Cada interfaz, una API

El descubrimiento identifica el objetivo y etiqueta cada endpoint con un protocolo; un controlador de transporte conectable lo ejecuta — pero la API orientada al agente (fetch, query, write, mapeo, recuperación, caché, límites de tasa) es idéntica en todos ellos.

InterfazRuntimeEscrituraInstalación
REST / HTTP+JSON✅ acciones (POST/PUT/PATCH/DELETE)
GraphQL✅ query + paginación Relay✅ mutaciones
SOAP / WSDL✅ XML de stdlib
gRPC✅ unario + server-streaming (reflexión)liquid-api[grpc]
WebSocket✅ lecturas por lotes limitadas + suscripción + sense en vivoliquid-api[ws]
SSE / NDJSON (server-push HTTP)✅ lecturas por lotes limitadas + sense en vivo
MCP (agente)✅ llamar herramientas / leer recursos + notificación sense✅ llamadas a herramientas
A2A (agente)✅ JSON-RPC message/send a habilidades de AgentCard
Postgres (+pgvector)✅ tablas/vistas, filtros, paginación, búsqueda vectorialliquid-api[pg]
MySQL / MariaDB✅ tablas/vistas, filtros, paginaciónliquid-api[mysql]
SQLite✅ tablas/vistas, filtros, paginación— (stdlib)
DuckDB✅ tablas/vistas, filtros, paginaciónliquid-api[duckdb]
SQL Server✅ tablas/vistas, paginación OFFSET/FETCHliquid-api[mssql]
Neo4j (grafo)✅ tipos de etiquetas/relaciones, filtros de propiedades✅ CRUD de nodosliquid-api[neo4j]
MongoDB (documento)✅ colecciones, filtros de campos, paginaciónliquid-api[mongodb]
Redis (clave-valor)✅ namespaces de keyspace, valores tipados, paginación SCAN✅ SET/HSET/DELliquid-api[redis]
MQTT (pub/sub IoT)✅ suscripción → lote + sense en vivo✅ publicarliquid-api[mqtt]
Modbus (industrial)✅ lectura de registros/coils + sondeo delta sense✅ escritura de registros/coilsliquid-api[modbus]
OPC UA (industrial)✅ lectura de nodos + sense de suscripción nativa✅ escritura de nodosliquid-api[opcua]
BACnet (edificios)✅ lectura de propiedades de objetos + sondeo delta sense✅ escritura de propiedadesliquid-api[bacnet]
ADB (Android)✅ lectura de shell + logcat sense✅ acciones de shell (input/am)— (adb del sistema)
Email — IMAP/SMTP✅ leer buzón por UID + sense de correo nuevo✅ enviar (MIME)— (stdlib)
Email — API de Gmail✅ listar/obtener + history sensemessages.send— (OAuth2)

Lectura y escritura. liquid.write(adapter, endpoint, op="insert", values={...}, allow_write=True) mutates any database (SQL INSERT/UPDATE/DELETE, insertar/actualizar/eliminar en Mongo, Redis SET/HSET/DEL, CRUD de nodos Neo4j); las escrituras web/agente pasan por acciones verificadas. Los identificadores provienen de la introspección y los valores están parametrizados; update/delete requieren un where (sin mutaciones generales); las escrituras están desactivadas hasta que optes con allow_write=True. Sense — el órgano aferente. liquid.sense(adapter, endpoint) percibe un flujo de eventos en vivo dondequiera que exista: deltas de filas SQL (y LISTEN/NOTIFY de Postgres), Redis pub/sub, tramas WebSocket, server-push HTTP (SSE/NDJSON) y notificaciones MCP — cada uno producido como un evento agnóstico de modalidad. Apuntado hacia adentro, liquid.sense_webhook(port=…, verifier=…) aloja un endpoint entrante para que un servicio (o un humano, mediante un webhook) que haga POST al agente se convierta también en una señal perceptible. Todo limitado por max_events / max_seconds, de modo que un agente pueda drenar mediante pull.

El bucle sensorimotor. react(stream, handler) impulsa un manejador para cada evento percibido — con aislamiento de errores y concurrencia acotada — para que un host pueda percibir → despertar al agente → actuar. merge_senses(*streams) distribuye varios sentidos en un solo bucle, de modo que un agente pueda vigilar una base de datos, una cola y un webhook a la vez:

events = merge_senses(
    await liquid.sense(orders, "/orders"),
    await liquid.sense_webhook(port=8088, verifier=stripe_verifier),
)
await react(events, on_event, max_concurrency=4)

El descubrimiento es automático — e identifica sobre la marcha. Antes de que el pipeline se ejecute, un paso de huella dactilar nombra el objetivo: un host:port desnudo se normaliza mediante puerto conocido (db:5432postgresql://db:5432), y liquid.identify(url) responde "¿qué es esto, y está instalado su driver?" con una pista de instalación cuando un backend falta. (Identificar un protocolo es factible sobre la marcha; hablar un nuevo protocolo binario autenticado no lo es — por lo que los desconocidos se nombran, no se adivinan.)

DescubrimientoDónde buscaCosto
Bases de datosintrospección de catálogo (postgres://, mysql://, mongodb://, redis://, neo4j://, …)Bajo
gRPC / WebSocket / SSEreflexión de servidor / muestreo de tramas / detección de content-typeBajo
MCP / A2A / Plugin/mcp, /.well-known/agent-card.json, /.well-known/ai-plugin.jsonBajo
OpenAPI / GraphQL / SOAPespecificación, introspección o WSDLBajo
Heurística RESTrutas comunes + interpretación LLMMedio
NavegadorPlaywright capturando redAlto

Añade un backend sin escribir código. Para la familia SQL el contrato es lo suficientemente declarativo como para ser datos: un manifiesto de dialecto (comillas, estilo de marcador de posición, paginación, SQL de introspección, mapa de errores, módulo DBAPI2) registrado mediante register_sql_manifest({...}) instala un driver funcional + descubrimiento — de modo que un nuevo almacén SQL / compatible por cable (CockroachDB, ClickHouse, cualquier driver DBAPI2), incluso uno obtenido de la red como JSON, se conecta sin una versión. Los nuevos protocolos se conectan de otro modo mediante el protocolo liquid.transport.ProtocolDriver; los backends SQL comparten un núcleo consciente de dialecto, por lo que uno nuevo es un adaptador de ~80 líneas.

¿Quieres enseñar a Liquid un nuevo protocolo? Un driver de transporte completo (fetch/write/sense) suele tener ~150 líneas — consulta docs/ADDING_A_DRIVER.md para el tutorial y una lista de deseos (CAN bus, CoAP, KNX, AMQP, NATS, SNMP, …). Las contribuciones son bienvenidas.

Más de 2,500 APIs están pre-descubiertas y pre-mapeadas en el catálogo global — los servicios más populares se conectan con costo de descubrimiento cero.

Arquitectura

URL / DSN                       Agent
   ↓                              ↑
 FINGERPRINT → DISCOVERY        FETCH · QUERY · WRITE · SEARCH · AGGREGATE
   ↓                              ↑
 one ProtocolDriver per          Deterministic per-protocol transport
 interface:                        • Query DSL (server-side filter)
   REST GraphQL gRPC WS SSE MQTT   • Output normalization
   MCP A2A · SQL graph doc KV ·    • Verbosity / max_tokens / _meta
   Modbus OPC-UA BACnet ADB …      • (full protocol list in the table above)
   ↓                              • Structured recovery + self-heal
 APISchema                        • Rate-limit-aware token bucket
   ↓                              • Response cache (Cache-Control aware)
 AI MAPPING (setup only)          • Empirical probing data (Cloud)
   ↓
 AdapterConfig

La IA participa solo en la configuración. El runtime es transporte puro con transformaciones — sin LLM por llamada, costo predecible, comportamiento reproducible (excepto search_nl, que almacena en caché sus compilaciones).

Componentes intercambiables

Cada preocupación transversal es un Protocol que puedes reemplazar:

from liquid.protocols import (
    Vault, LLMBackend, DataSink, KnowledgeStore, AdapterRegistry, CacheStore,
)

Se incluyen implementaciones en memoria para todos ellos; liquid-cloud proporciona PostgresVault, RedisCache, etc. para despliegues alojados.

Soporte de frameworks

adapter.to_tools(format="anthropic")   # Claude tool use
adapter.to_tools(format="openai")      # OpenAI function calling (LangChain/CrewAI consume these)
adapter.to_tools(format="mcp")         # MCP (Claude Desktop, Cursor)

Integración con frameworks

No hay paquetes adicionales que instalar — está integrado en liquid-api. adapter.to_tools(format="anthropic" | "openai" | "mcp") emite definiciones de herramientas listas para usar para el uso de herramientas de Claude, la llamada de funciones de OpenAI (que LangChain / LangGraph y CrewAI consumen directamente) y cualquier cliente MCP (Claude Desktop, Cursor, …). El servidor liquid-mcp incluido también expone Liquid como herramientas MCP de fábrica.

Comparación

CaracterísticaLiquidZapierHerramienta LangChainDIY
Auto-descubre cualquier interfaz (sin conector curado)nonono
APIs + bases de datos + agentes en una sola capaparcialnono
Lectura y escritura mediante una sola APIparcialno
Búsqueda / agregación del lado del servidornonoparcial
Normalización de salida entre fuentesparcialnono
Recuperación estructurada con next_actionnonono
Auto-reparación ante deriva de esquemanonono
Estimación de costo previanonono
MCP + A2A + LangChain + CrewAI nativonoparcialno
Código abiertonon/a

Documentación