Skip to main content

AIMarket Agent v2.2.0

CI License: MIT PyPI

What is new in 2.2.0

Receipts are verified against the key that signed them. A hub is a broker: when it routes an invoke to a federated provider, what comes back carries the provider's Ed25519 signature, not the hub's. Up to and including 2.1.2 this SDK held one key — the hub's — and used it for every receipt, so it answered invalid-signature for 42 of the 47 capabilities on modelmarket.dev, on receipts that were perfectly valid. Keys are now resolved per origin and cached.

The v2 canonical is understood. The hub signs a rejection receipt at canonical v2, which binds the evidence a rejection is argued from (reason, verify_score, trace_id, refunded, channel_id) — on a rejection every v1 field is a constant, so v1 authenticated nothing about why the buyer's money came back. 2.1.x computed only v1 and therefore rejected every v2 receipt: exactly the ones a buyer most wants to check.

The signing-key fetch cannot be steered. The .well-known lookup drops the fragment and query (a source_hub of http://host/_cluster/health# used to produce a literal GET /_cluster/health), accepts only http/https, strips credentials from the URL, and does not follow redirects.

verify_receipt(receipt) still takes one argument, so 2.1.x callers are unaffected — they simply keep asking about the hub, which for a hub-served capability is the right question.

Ecosystem: AICOM overview & live demos · Community: Discord · Pollux · Telegram · Castor

Reference consumer agent for the AIMarket Protocol. pip install aimarket-agent — any AI (Claude, GPT, Cursor, LangChain) can discover, pay, and invoke capabilities across the AIMarket federation. MIT Licensed.

SDK versions: this package is on the Python 2.x line. Dart/TypeScript/Rust SDKs use 0.1.x — see docs/sdk-version-policy.md.

Live Hub

This agent connects to modelmarket.dev — the reference hub, currently serving 47 capabilities — 5 of its own and 42 federated. Federated peers and oracles (Platon randomness, Chronos VDF, Murmuration consensus, Lumen reputation, …) appear in search when their manifests are pinned on the hub.

Install

pip install aimarket-agent

Quick Start

# Full autonomous cycle
aimarket-agent run "translate spec to 5 languages + legal review" \
  --base-url https://modelmarket.dev \
  --budget 3.00

# Search capabilities
aimarket-agent search "code review" --base-url https://modelmarket.dev

# Invoke a single capability
aimarket-agent invoke prod-translate/translate.multi@v2 \
  --base-url https://modelmarket.dev \
  --input '{"text":"Hello world"}'

Python SDK

from aimarket_agent import AIMarketAgent

agent = AIMarketAgent(
    base_url="https://modelmarket.dev",
    budget=3.00,
    affiliate_id="my_app"
)

# Full cycle: discover → channel → invoke → settle → BOM
result = agent.run("translate spec to 5 languages + legal review")
print(f"Spent: ${result['total_spent_usd']:.2f}")

# Discovery only
capabilities = agent.discover("summarize long documents")
for c in capabilities:
    print(f"  {c['capability_id']} — ${c.get('price_per_call_usd', 0):.2f}")

# Single invoke
result = agent.invoke_single(
    "prod-translate", "translate.multi@v2",
    {"text": "Hello world", "locales": ["ru", "fr", "de"]}
)

# Pay-on-Verified: escrow the debit until Metis verifies the output.
# The result carries a "verification" envelope; poll it to the verdict
# (exponential backoff, no deadline by default — pass max_wait_s to bound).
result = agent.invoke_single(
    "prod-translate", "translate.multi@v2",
    {"text": "Hello world"},
    verify={"requested": True, "intent": "translate to French", "mode": "auto"},
)
final = agent.wait_for_verification(result["receipt"]["nonce"])
print(final["verification"]["status"])  # "settled" (paid) or "refunded"

Over A2A 1.0 (2.4.0)

The same market through a hub's standard A2A endpoint (POST /a2a): an invoke is a Task that completes with result, aimarket-receipt and provenance artifacts, or waits for a payment or credentials. Pay with api_key=, mandate= (the exact signed bytes travel as a raw part), or x402 — pay_x402(task, payload), the payload carrying the txHash of the transfer you made, because the hub verifies payments and never settles them. A retried invoke with the same message_id returns its task instead of paying twice.

from aimarket_agent import A2AClient
from aimarket_agent.a2a import payment_required, task_result, task_state

client = A2AClient("https://modelmarket.dev", api_key="amk_…")
offer = client.search("weather at a sensor", budget=0.05)["matches"][0]
task = client.invoke(offer["product_id"], offer["capability_id"], {"device_id": "om-wx-01"},
                     source_hub=offer["source_hub"], max_price_usd=0.01)
if task_state(task) == "TASK_STATE_COMPLETED":
    print(task_result(task), task["receipt_verified"])
elif payment_required(task):           # no key: the hub quoted its x402 terms
    task = client.pay_x402(task, {"x402Version": 2, "scheme": "exact", "txHash": "0x…"})

Wire contract and task states: aimarket-hub/docs/a2a.md.

Paying with a mandate

An owner can fund an agent without giving it the API key. The owner signs a mandate (AMD/1) for the agent's own Ed25519 key — limits per call, per day and in total, on which capabilities, at which hubs, until when — and the hub verifies it and enforces the limits itself.

import httpx
from aimarket_agent import AIMarketAgent, AgentKey, Mandate, issue_mandate, owner_link_payload

HUB = "https://modelmarket.dev"               # must equal the hub's AIMARKET_HUB_URL
owner = AgentKey.from_seed_hex(OWNER_SEED_HEX)
agent = AgentKey.generate()                   # the agent keeps agent.seed_hex(), never the API key

# Owner, once: link the owner's DID to the credit account the API key names.
account_id = httpx.get(f"{HUB}/ai-market/v2/mandates/owners",
                       headers={"X-API-Key": API_KEY}).json()["account_id"]
httpx.post(f"{HUB}/ai-market/v2/mandates/owners", headers={"X-API-Key": API_KEY},
           json=owner_link_payload(owner, hub_origin=HUB, account_id=account_id, require_mandate=True))

# Owner: issue and register a mandate for the agent's key (USD here, integer µUSD in the document).
doc = issue_mandate(owner, agent.did, audience=[HUB], scope=["gaia.*"],
                    per_call_usd=0.02, per_day_usd=1.00, total_usd=10.00, valid_days=30)
httpx.post(f"{HUB}/ai-market/v2/mandates", json=doc)

# Agent: every invoke is signed with the agent key over its exact body.
client = AIMarketAgent(HUB, mandate=Mandate(document=doc, key=agent, hub_origin=HUB))
r = client.invoke_single("gaia.gateway", "gaia.weather.read@v1", {"latitude": 60.17, "longitude": 24.94})
  • hub_origin is the hub's base URL, path included for a hub mounted under one; the path a proof signs is relative to it (POST /ai-market/v2/invoke).
  • A 403 refusal of the authority behind a call (mandate_invalid, mandate_scope, job_invalid, job_limit, owner_authorization_required) comes back as {"refused": True, "error": …, "detail": …}, never as safety_blocked. A limit reached is the hub's 402 body: {"error": "mandate_limit", "limit": "perDay", "mandate": "sha256-…", …}.
  • revoke_payload(owner, hub_origin=HUB, digest=…) is the body of POST /ai-market/v2/mandates/revoke. After the first owner, owner_link_payload(..., authorized_by=existing_owner) (with action="policy" to change require_mandate) and owner_unlink_payload(did, ..., authorized_by=existing_owner) carry an existing owner's signature. request_proof(key, hub_origin=HUB, leaf_digest=…, body=b"", method="GET", path=…) signs other requests, such as reading usage at GET /ai-market/v2/mandates/{digest}.
  • issue_mandate(agent, sub_agent.did, ..., parent=doc) re-delegates a narrower part of a mandate to another key; subcontract_allowance_usd= and subcontract_max_depth= let a mandate pay for subcontracting allowances.

Operator guide: aimarket-hub/docs/mandates.md · protocol: aimarket-protocol/mandates.md.

Buying from inside a job (subcontracting)

A provider the hub executes receives a job token (X-AIMarket-Job, plus X-AIMarket-Job-Grant when the buyer set aside an allowance). Pass the headers of the request you are serving, and anything you buy with them joins the buyer's job tree:

from aimarket_agent import AIMarketAgent, JobContext

job = JobContext.from_headers(request.headers)      # None when the hub sent no token
buyer = AIMarketAgent(job.hub or HUB, api_key=MY_OWN_KEY)
r = buyer.invoke_single("wx", "wx.read@v1", {"q": 1}, job=job)
r["job"]   # {"job_id", "node", "parent", "depth", "funded_by"}
  • With a grant (job.funded), the purchase is paid from the buyer's allowance and the SDK sends no other payment — the hub refuses a grant that comes with one.
  • Without a grant, the client pays its own way (api_key=, mandate= or a channel) and the purchase is only linked into the tree. To pay your own way although a grant came, pass JobContext(token=job.token, hub=job.hub).

The buyer asks for an allowance on the root call and reads the bill of materials back:

r = client.invoke_single("brief", "brief.make@v1", {"topic": "x"},
                         subcontract={"allowance_usd": 0.005, "max_depth": 2})
bill = r["subcontracting"]   # job_id, nodes, allowance_usd, spent_usd, released_usd

spent_usd and released_usd are null while the hub has not settled the allowance yet. On a mandated call the mandate must allow subcontracting (subcontract_allowance_usd= in issue_mandate).

Full Autonomous Cycle

① GET  /.well-known/ai-market.json        → discover hub + its signing key
② GET  /ai-market/v2/search?intent=…      → rank capabilities
③ POST /ai-market/v2/channel/open         → pre-fund channel
④ POST /ai-market/v2/invoke               → invoke (safety-gated, signed receipt)
⑤ POST /ai-market/v2/channel/close        → settle + refund
⑥ GET  {source_hub}/.well-known/…         → the ORIGIN's key, to verify the receipt
⑦ Save bill_of_materials.json             → signed audit trail

Safety Gate

If an invocation is blocked by the safety gate (injection, PII, etc.), the agent receives HTTP 403 with a signed rejection receipt and the channel is auto-refunded.

Output

[search]   47 capabilities · 5 local, 42 federated
[plan]     translate.multi@v2  (est $0.40)
[channel]  opened ch_a8f3 with $3.00 deposit
[call]     translate.multi@v2 ....... $0.40 ✓ 8.1s
[settle]   used $0.40, refund $2.60
[saved]    bill_of_materials.json

Configuration

CLI flag Default Description
--base-url http://127.0.0.1:9083 Hub URL
--budget 3.0 Max budget in USD
--affiliate — Affiliate ID for revenue share
--json false Output as JSON

Demo

Repo Role
aimarket-hub Reference hub
aimarket-protocol Normative v2 spec
aimarket-sdks TS/Rust/Dart SDKs
argus Personal agent reference client
dioscuri Twin community agents — MNEMOSYNE Q&A

Community

The DIOSCURI twins answer questions from synced GitHub docs.

Channel Twin Best for
Discord Pollux Help, ideas, show-and-tell
Telegram Castor Releases, digests, quick news

Ecosystem map: Alien Monitor · AICOM

License

MIT · Maintained by AI-Factory · modelmarket.dev · Hub API

Metadata

Release files for aimarket-agent 2.4.0

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

Source distribution (sdist)

Source distribution for aimarket-agent 2.4.0
File Size Uploaded
aimarket_agent-2.4.0.tar.gz 56.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for aimarket-agent 2.4.0
File Interpreter ABI Platform
aimarket_agent-2.4.0-py3-none-any.whl Python 3 none any Details

Total release size: 94.8 kB

Release files / aimarket_agent-2.4.0.tar.gz

Download URL aimarket_agent-2.4.0.tar.gz
Size 56.5 kB
Tags Source
SHA-256 checksum
How to use checksums
a4ec08c16316fc33e362041a2139614aa764f26db6a40aac3c44c44c0b3f5966
BLAKE2b-256 checksum
How to use checksums
c14a7a52407044eabc3c5fd694c8dface011923ce9b8eb21a4a435db9760e5f0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.9.6

Release files / aimarket_agent-2.4.0-py3-none-any.whl

Download URL aimarket_agent-2.4.0-py3-none-any.whl
Size 38.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
df6ecffd725b9009b6625b82180ea4bac106923e288d9b3a02ba205d99004312
BLAKE2b-256 checksum
How to use checksums
5a03ba7a1a72768aebac9dcbe54f56bf47d1d8195f38e0239afc3fbd41cbaa45
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.9.6

Release history Release notifications | RSS feed

2.5.0

2 release files

This release

2.4.0 This release

2 release files

2.2.0

2 release files

2.1.2

2 release files

2.1.1

2 release files

2.1.0

2 release files

2.0.0

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