Skip to content

a2agora/sdk-reference

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

29 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

title Overview
nav_order 1
permalink /

ACMP Reference SDK

A dependency-light Python reference implementation of the Agent Compute Market Protocol (ACMP), the economic layer defined by A2Agora (GitHub · Codeberg).

Rendered docs → a2agora.org/sdk-reference · Project home → a2agora.org

This SDK exists to prove the protocol is implementable, not to be a production-grade agent framework. It follows the spec's Layer 1 (Transport & Invocation), Layer 2 (Task Decomposition Format), Layer 4 (Escrow & Settlement), and Layer 6 (Negotiation Protocol) documents field-for-field.

Status

  • Stage 1 — Layer 1 minimal transaction. A buyer invokes a single task on a provider and gets a result back, over an in-memory JSON-RPC transport.
  • Stage 2 — Layer 6 negotiation. A buyer requests an offer, accepts it (locking escrow), then invokes using the negotiated price and escrow id.
  • Stage 3 — Layer 2 DAG. A buyer-side orchestrator walks a task DAG, resolving each task's input from upstream outputs and invoking independent tasks concurrently.
  • Stage 4 — Layer 1 streaming, heartbeats & cancellation. Output streaming (acmp/streamChunk), input streaming (acmp/inputChunk), automatic + manual heartbeats (acmp/heartbeat), cooperative cancellation (acmp/cancel), buyer-side timeout_ms enforcement, and -33007 feature gating.
  • Stage 5 — Layer 4 escrow agent & a real network transport. A genuine EscrowAgent — lock/bind/release/reclaim/claim/dispute/status, the four-state lifecycle, op_ref idempotency, the bound-escrow reclaim guard, and the challenge-window auto-release and expiry auto-reclaim — served as its own party, never shared in-process. Plus an optional WebSocket transport, so buyer, provider, and Escrow Agent run as three real network endpoints end-to-end.

Further contributions (additional transports, streaming DAG edges, dispute-resolution mechanisms) are welcome — see the notes below on what's intentionally out of scope so far.

Install

cd sdk-reference
pip install -e ".[dev]"

The core SDK (src/acmp/) has no runtime dependencies. The WebSocket transport (acmp.ws_transport, used by demo 05) is an opt-in extra — pip install -e ".[dev]" already includes it for running the tests, but on its own it's:

pip install -e ".[ws]"

Run the demos

python examples/01_minimal_invoke.py       # Layer 1 only
python examples/02_negotiated_invoke.py    # Layer 6 -> Layer 1 -> Layer 4
python examples/03_dag_pipeline.py         # Layer 2 DAG -> Layer 1 (concurrent)
python examples/04_streaming_cancel.py     # streaming + heartbeats + cancel
python examples/05_escrow_e2e.py           # Layer 4 escrow agent over real WebSockets (needs [ws])

The first two spin up a provider offering a sentiment-analysis capability — the same scenario as the worked example in spec/layers/02-task-format.md §6.1 (GitHub · Codeberg). The second demo additionally runs the offer/accept exchange from spec/layers/06-negotiation-protocol.md (GitHub · Codeberg) before invoking, then locks, binds, releases, and reclaims through a real EscrowAgent — the RFC-0001 happy-path numbers (lock 0.005, release 0.003, reclaim 0.002). The third demo runs the split → parallel sentiment → aggregate pipeline from spec/layers/02-task-format.md §6.2 (GitHub · Codeberg). The fifth demo is the full three-party topology of spec/layers/04-escrow-settlement.md (GitHub · Codeberg) — buyer, provider, and Escrow Agent as three separate WebSocket connections — running both the happy path and the silent-buyer safety path (claim + challenge-window auto-release, via an injected clock rather than a real wait).

Run the tests

pytest

Package layout

src/acmp/
  errors.py       ACMP error codes (-33xxx, -35xxx) and the AcmpError exception
  messages.py     Task/Payload/Result dataclasses + JSON-RPC framing helpers
  transport.py    Transport ABC + InMemoryTransport (paired in-process channel)
  ws_transport.py Transport over a real websockets connection (optional, acmp[ws])
  provider.py     Capability registry, invoke/streaming/cancel dispatch, TaskContext
  buyer.py        invoke() with chunk/heartbeat callbacks, timeout, cancel, input chunks
  negotiation.py  Layer 6 offer request/offer/accept dataclasses + Negotiator
  escrow.py       Layer 4 EscrowAgent, EscrowClient, CreditLedger (the real escrow)
  dag.py          Layer 2 DAG/Edge/InputRef model + DagOrchestrator

Every module docstring cites the spec section it implements.

Spec conformance notes

  • Idempotency (Layer 1 §3.1.1): the Provider caches responses by task_id — invoking the same task twice returns the cached result instead of re-executing or re-billing.
  • Proof of execution: proof_method="result-hash" produces a sha256 hash of the canonical output JSON — a runnable stand-in for Layer 3.
  • Pricing: each registered capability has a price_cu; the provider rejects with budget_exceeded (-33001) if price_cu > task.max_price_cu (checked both at negotiation time and at invoke time).
  • Negotiation (Layer 6, now draft): Negotiator.request_offer() / .accept() implement the offer-request → offer → accept → ack flow. The spec formalized this after the SDK proved it, adopting the SDK's method names and -34xxx error codes wire-compatibly. Per the draft, the buyer locks escrow and supplies escrow_id at accept (the ack echoes it; omitting it means direct settlement), and offers carry the negotiated challenge_window_ms term. The offer sig envelope (Layer 7) is transported but not produced — this SDK has no key infrastructure.
  • Escrow (Layer 4, now draft): EscrowAgent is a genuine neutral third party — served over its own Transport connection(s), never shared in-process with buyer or provider, unlike the retired EscrowStub. It implements the four-state lifecycle (open → claimed → disputed → closed), all seven acmp/escrow* messages, op_ref idempotency (including a dedicated agent-wide dedup for escrowLock, which has no escrow_id yet to key a per-escrow cache off), party authorization (-35005), the once-only bind (including a lock-time payee_id already counting as bound), the claim fast-forward release rule (§4.3), the bound-escrow reclaim guard (§4.4), and the challenge-window auto-release and expiry auto-reclaim — both driven by an injectable clock so tests and demo 05's safety-path scenario never wait on a real timer. CreditLedger implements the §7.2 non-blockchain rail (P4): a plain funding/payout balance, with one idempotent payout per (escrow_id, transition). Dispute resolution is out of scope (v0.1 leaves it to agent policy, per the spec); an EscrowAgent only freezes a disputed escrow. EscrowClient is the buyer- or provider-side wrapper (built the same way Negotiator wraps a Buyer) and also satisfies the EscrowVerifier protocol a Provider uses for its escrow_invalid (-33005) check.
  • Transport: WebSocketTransport (in the optional acmp.ws_transport module, pip install acmp[ws]) implements the same Transport interface as InMemoryTransport over a real socket — the SDK's core stays dependency-light, and no core module imports it. party_id for an EscrowAgent connection is read from a ?party_id= query parameter on the connection URL (self-reported, same as the in-memory demos — verifiable identity binding is Layer 7's job).
  • DAG execution (Layer 2 §3): DagOrchestrator treats the DAG purely as a buyer-side plan — it is never sent over the wire. Providers only ever see individual, literal acmp/invoke tasks; InputRefs (the source form) are always resolved to a concrete Payload before invoking. Independent tasks (no unresolved dependency) are invoked concurrently.
  • Field path resolution (Layer 2 §3 "Path grammar"): supports the spec's small subset — member access (data.foo) and array indexing (data.items[0]), rooted at the referenced task's whole {type, data} output. Wildcards/slices are out of scope, per the spec.
  • Failure policy (Layer 2 §4): DagOrchestrator is fail-fast — the first task failure cancels sibling tasks still in flight, sends acmp/cancel so their providers stop working too, and re-raises. Best-effort (continue independent branches) is a valid alternative per the spec but not implemented here.
  • Streaming & liveness (Layer 1 §3.4–§3.7, §7): output and input streaming, heartbeats (automatic keep-alives at the provider's heartbeat_interval_ms plus manual progress via ctx.heartbeat), and cooperative cancellation are implemented. Capability handlers opt in by declaring a second parameter (handler(task, ctx) receives a TaskContext); single-parameter handlers keep working unchanged. task.timeout_ms is enforced buyer-side as the hard deadline (Layer 1 §3.1), with acmp/cancel sent on expiry. Feature gating uses -33007; since the in-memory transport has no MCP initialize handshake, the advertisement check is enforced provider-side only. Proof over streamed output is not implemented (-33006). stream_eligible DAG edges (Layer 2 §5) remain out of scope.

License

Apache 2.0, matching the A2Agora spec (GitHub · Codeberg).

About

Reference SDK for the Agent Compute Market Protocol (ACMP), the economic layer of A2Agora.

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages