A first-class Agent2Agent (A2A) protocol surface for Lemonade local AI systems.
Lemonade A2A is an open-source reference implementation and upstream-oriented proposal for exposing models served by Lemonade as standards-compliant Agent2Agent (A2A) agents.
If Lemonade can serve intelligence locally, it should be able to expose that intelligence as an interoperable A2A agent.
The project intentionally uses local AI systems rather than AMD AI PCs as its platform concept. Lemonade is built for local AI across PCs and operating systems, with multiple CPU/GPU/NPU backends including AMD, NVIDIA, Apple Silicon, Vulkan and CPU execution. AMD acceleration remains an important optimization target, not a protocol requirement.
OpenAI-compatible APIs standardize model inference. A2A standardizes agent interoperability. Lemonade A2A adds the latter without inventing another agent framework or inference runtime.
Application / Agent Framework
│
├──── OpenAI-compatible API ───► Model inference
│
└──── A2A ─────────────────────► Agent interoperability
│
▼
Lemonade
│
Local AI System Runtime
│
CPU / GPU / NPU backends
- A2A v1-compatible protocol surface for Lemonade-hosted intelligence.
- Automatic Agent Card generation.
- A2A Message/Task/Artifact ↔ Lemonade inference translation.
- Streaming and cancellation propagation.
- Local-first operation with no mandatory cloud dependency.
- Framework and hardware independence.
- Thin protocol layer: this process does not load AI models.
- Preserve Lemonade's backend abstraction rather than binding A2A to a particular accelerator.
- Upstream-friendly architecture that can evolve toward native Lemonade integration.
A2A Clients / Agent Frameworks
│
A2A v1
│
▼
┌─────────────────────────┐
│ Lemonade A2A │
│ │
│ Agent Card / Discovery│
│ Message Adapter │
│ Task / Artifact Map │
│ Streaming / Cancel │
│ Security Policy │
└────────────┬────────────┘
│
OpenAI-compatible
Lemonade endpoint
│
▼
┌─────────────────────────┐
│ Lemonade │
│ Model / backend routing │
│ Local inference runtime │
└────────────┬────────────┘
│
┌─────────────────┼─────────────────┐
▼ ▼ ▼
CPU GPU NPU
│ │ │
└────────── Local AI ────────┘
A2A should remain above Lemonade's model/backend router. It should not know whether inference is ultimately executed by ROCm, CUDA, Vulkan, Metal, CPU, Ryzen AI or another backend. This keeps the protocol surface aligned with Lemonade's vision: standard local AI APIs with hardware-specific optimization behind the server boundary.
See docs/architecture.md.
Validated so far (details: roadmap, benchmarks):
- official A2A SDK integration with JSON-RPC and HTTP+JSON bindings served at the base URL (legacy
/a2a/jsonrpcand/a2a/restpaths kept); - automatic Agent Card, SSE streaming into A2A artifacts, cancellation that stops the real backend generation, backend failures mapped to
FAILEDtasks; - resource bounds (input size/parts, per-task deadline, concurrency cap with
REJECTED, bounded task store, slow-consumer backpressure), exposure profiles, API-key auth with several named callers and per-user task isolation, rate limiting, TLS and mutual TLS, graceful shutdown, optional cancel-on-disconnect; - property-based fuzzing of messages, parts, metadata and backend stream output (it found and fixed two real defects);
- a compatibility specification you can run:
lemonade-a2a doctorcross-validates the installeda2a-sdk,lemonade-a2aand Lemonade after an upgrade against a generated manifest and a feature registry;strict/warnstartup modes, feature flags, layered config, a support bundle, and a weekly canary against the lowest, latest and pre-release SDK (specification, upgrading); - OpenTelemetry observability: opt-in traces, metrics and logs with standard
OTEL_*configuration, private by default (prompts are never exported unless you opt in), a measured overhead budget, and a ready dashboard and alerts (observability); - an extension API (backends, task stores, authenticators, telemetry) with a persistent SQLite task store as the reference plugin (extending);
- reasoning models and backend errors handled honestly: thinking is dropped (or opt-in streamed as a separate artifact), and an error Lemonade reports inside a 200 stream fails the task instead of completing it empty;
- the official A2A TCK against the protocol surface: 157 passed, 0 failed, 4 expected-fail (SHOULD), the rest skipped for capabilities not declared (see conformance for scope);
- independent clients (A2A CLI, and the JavaScript, Go, .NET and Java SDKs, plus the Inspector's validators) pass over JSON-RPC and HTTP+JSON, on the mock and on real Lemonade (interoperability);
- CI on Python 3.11-3.13 (ruff, ruff format, pytest) plus a deterministic mock-Lemonade end-to-end job;
- a real Lemonade 2026.40.0 setup on llama.cpp CUDA, Vulkan and CPU with a 1.7B and a 4B model: the validator and an opt-in pytest suite pass, A2A adds roughly 5-15 ms TTFT within a defined overhead budget on those models (a 12B model on CUDA was over budget, likely noise, and is only partly measured), and concurrent load (up to 8) shows no adapter-added cost (throughput is bounded by the backend).
Not yet done: ITK (it tests SDKs against each other, so it does not apply to a standalone adapter), NPU/ROCm/Metal and non-llama.cpp backends (no hardware available so far), OAuth/OIDC inside the adapter (by decision, done at a gateway), URL/file fetching (disabled; its safety policy is written and tested first). The next milestone is cross-backend evidence on AMD hardware and the upstream-native design.
The north star is for A2A to become another standard Lemonade server surface rather than a permanently separate inference stack:
lemonade serve --a2aConceptually:
OpenAI API http://localhost:13305/v1/...
A2A http://localhost:13305/a2a/...
Agent Card http://localhost:13305/.well-known/agent-card.json
The exact CLI and paths above are design targets, not claims about current upstream Lemonade behavior.
- Discovery — generate and serve an Agent Card.
- Messaging — translate A2A messages into Lemonade inference requests.
- Execution — expose Lemonade inference through an A2A
AgentExecutor. - Artifacts — return generated content using A2A-native messages/artifacts.
- Streaming — preserve incremental local generation.
- Lifecycle — propagate cancellation and map failures/timeouts correctly.
- Compatibility — validate against official A2A tooling/TCK.
- Runtime validation — prove the same surface against a real Lemonade installation and multiple Lemonade backends.
Pre-alpha reference implementation. Pinning and API details may change while A2A v1 integration is validated.
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e .
export LEMONADE_BASE_URL=http://localhost:13305/v1
export LEMONADE_MODEL=your-model
lemonade-a2a # serves on http://127.0.0.1:9100After installing or upgrading anything (pip install -U a2a-sdk lemonade-a2a), check the combination:
lemonade-a2a doctor # versions, config, Lemonade, a tiny generation with --deep
lemonade-a2a capabilities # what is on, what is available and how to switch it
lemonade-a2a config show # effective settings and where each value came from
lemonade-a2a support-bundle # a redacted archive for a bug reportThe adapter expects a running Lemonade server exposing an OpenAI-compatible endpoint. It listens on 9100 by default because a stock Lemonade install already uses port 9000 for its WebSocket.
| Variable | Default | Purpose |
|---|---|---|
LEMONADE_BASE_URL |
http://localhost:13305/v1 |
Lemonade OpenAI-compatible endpoint |
LEMONADE_MODEL |
empty (server default) | Model id sent to Lemonade |
LEMONADE_A2A_HOST / LEMONADE_A2A_PORT |
127.0.0.1 / 9100 |
Listen address |
LEMONADE_A2A_PUBLIC_URL |
http://localhost:9100 |
URL advertised in the Agent Card; keep in sync with host/port |
LEMONADE_A2A_AGENT_NAME / LEMONADE_A2A_AGENT_DESCRIPTION |
see config.py |
Agent Card text |
LEMONADE_TIMEOUT_SECONDS |
120 |
Backend request timeout |
LEMONADE_A2A_MAX_INPUT_CHARS / LEMONADE_A2A_MAX_INPUT_PARTS |
100000 / 32 |
Maximum input text / message parts |
LEMONADE_A2A_MAX_REQUEST_BYTES |
1048576 |
Maximum HTTP request body (413 above it) |
LEMONADE_A2A_MAX_TASK_SECONDS |
600 |
Per-task deadline (task fails when exceeded) |
LEMONADE_A2A_MAX_CONCURRENT_TASKS |
8 |
Running-task cap; extra tasks are REJECTED |
LEMONADE_A2A_MAX_STORED_TASKS |
1000 |
Task store size; oldest finished tasks are evicted |
LEMONADE_A2A_PROFILE |
local |
How the adapter is exposed: local (loopback only), lan (needs auth), external (needs auth + TLS + rate limit). A mismatch stops startup, see docs/security.md |
LEMONADE_A2A_API_KEY |
empty (no auth) | Require Authorization: Bearer / X-API-Key; Agent Card and /healthz stay public |
LEMONADE_A2A_API_KEYS |
empty | Several callers: alice:key1,bob:key2. Each name owns its tasks; others cannot see them |
LEMONADE_A2A_RATE_LIMIT_PER_MINUTE |
0 (off) |
Per-identity request budget; HTTP 429 with Retry-After beyond it |
LEMONADE_A2A_CANCEL_ON_DISCONNECT |
0 |
Cancel a task when the streaming client that started it disconnects |
LEMONADE_API_KEY |
empty | Key sent to a protected Lemonade server |
LEMONADE_A2A_SSL_CERTFILE / LEMONADE_A2A_SSL_KEYFILE |
empty | Serve HTTPS (set both; use an https:// public URL) |
LEMONADE_A2A_SSL_CA_CERTS / LEMONADE_A2A_SSL_REQUIRE_CLIENT_CERT |
empty / 0 |
Mutual TLS: refuse clients without a certificate signed by this CA |
LEMONADE_A2A_REASONING |
drop |
A reasoning model's thinking: drop, or artifact to stream it as a separate lemonade-reasoning artifact |
LEMONADE_A2A_TASK_STORE / LEMONADE_A2A_TASK_DB |
memory / lemonade-a2a-tasks.sqlite |
sqlite keeps tasks across restarts (needs lemonade-a2a[sqlite]; the file holds prompts and answers) |
LEMONADE_A2A_COMPAT |
warn |
Startup compatibility gate: off, warn or strict (refuse to start on any failed or warned check) |
LEMONADE_A2A_FEATURES |
empty | Toggle registry features: +adapter.cancel_on_disconnect,-a2a.streaming (see lemonade-a2a capabilities) |
LEMONADE_A2A_EXPOSE_CAPABILITIES |
0 |
Serve the authenticated /.well-known/lemonade-a2a/capabilities endpoint and an optional card extension |
LEMONADE_A2A_OTEL |
0 |
Turn on OpenTelemetry (needs lemonade-a2a[otel]); exporters, sampling and resource come from the standard OTEL_* variables |
LEMONADE_A2A_OTEL_CAPTURE |
none |
none, metadata (adds ids) or content (adds truncated prompt/response text; local use only) |
LEMONADE_A2A_OTEL_PROMETHEUS / LEMONADE_A2A_LOG_FORMAT |
0 / text |
Serve an authenticated /metrics; json logs carry trace_id and span_id |
LEMONADE_A2A_BACKEND / _AUTHENTICATOR / _TELEMETRY_PLUGIN |
lemonade / empty / empty |
Select an extension by name |
LEMONADE_A2A_CONFIG |
empty | A TOML file ([lemonade_a2a] table of setting names); environment and command-line flags override it |
Every setting can also be given in a TOML file or as a command-line flag (lemonade-a2a --config c.toml serve --port 9200); precedence is defaults, file, environment, command line, and lemonade-a2a config schema prints a JSON Schema of all of them. Invalid values fail at startup. See examples/ for an Agent Card and a client, and docs/real-lemonade-validation.md for validating against a real Lemonade install.
Standards first. Use A2A rather than creating a proprietary agent protocol.
Thin data plane. A2A translation belongs here; model inference and backend selection belong in Lemonade.
Local first. A complete interaction can remain on the local AI system or trusted LAN.
Hardware agnostic at the protocol boundary. A2A clients should not need to know whether Lemonade selected CPU, GPU or NPU execution.
Untrusted by default. Agent Cards, messages, files, URLs, metadata and artifacts are external input.
Upstream friendly. Keep protocol boundaries explicit so the reference implementation can inform a future native Lemonade A2A surface.
Lemonade A2A is not a new agent reasoning framework. Model intelligence remains a Lemonade concern. The A2A layer is responsible for interoperability, lifecycle, streaming, discovery and policy—not inventing a second model-routing or reasoning stack.
If Lemonade later gains richer routing, model selection or scheduling capabilities, A2A should consume those capabilities through Lemonade rather than duplicate them here.
The next step is to move from reference implementation validation toward an upstream-native A2A surface:
Today
A2A Client
│
▼
Python Lemonade A2A reference adapter
│
▼
OpenAI-compatible Lemonade API
│
▼
Lemonade Router → backend → local hardware
Next
A2A Client OpenAI / Anthropic / Ollama clients
│ │
└──────────────┬───────────────────────┘
▼
Lemonade Server
│
┌──────────┴──────────┐
│ Protocol/API layer │
│ OpenAI | ... | A2A │
└──────────┬──────────┘
│
Lemonade Router
│
Model/backend manager
│
CPU / GPU / NPU backends
Before implementing the native C++ surface, the reference adapter should pass real-runtime, lifecycle, conformance and overhead gates. That gives the upstream proposal measurable evidence rather than only an architectural concept.
- Hardware evidence: NPU, ROCm, Metal and non-llama.cpp engines need a machine with that hardware; the adapter has no hardware-specific code, so the check is to run
lemonade-a2a doctor --deep, the validation script and the benchmarks there. - Real inference on a second machine, and a first PyPI release (needs the owner's approval; the package build and manifest generation are ready).
- Draft the native Lemonade A2A API boundary and map it onto Lemonade's HTTP/router architecture, claiming features by the registry's ids and passing the same contract tests; then prototype the native endpoint and propose it upstream.
See docs/roadmap.md for the tracked TODO list.
- Architecture
- Protocol mapping
- Security
- Compatibility specification, feature registry, upgrading and versioning, extending
- Observability with OpenTelemetry and the ready-made stack
- Conformance
- Interoperability
- Real Lemonade validation
- Benchmarks
- Roadmap / TODO
- Upstream integration
- Native Lemonade A2A: design and implementation plan (draft)
Experimental / pre-alpha. The protocol surface passes the official TCK, the integration is validated against a deterministic mock and a real Lemonade server, and the adapter has basic resource bounds and optional auth/TLS. The project is moving into AMD-hardware evidence, interoperability testing and upstream-native design.
Apache License 2.0. See LICENSE.
Built on the open-source work of the Lemonade community and the Linux Foundation Agent2Agent project. This repository is an independent community project and is not an official AMD or Linux Foundation product.