Skip to main content

JarvisClaw Agent SDK — Agent-Native AIP with autonomous tool loops, budget guards, streaming, and OpenAI drop-in compatibility.

Project description

JarvisClaw Python SDK

The official Python SDK for JarvisClaw AI — intent-based AI routing with x402 micropayments.

Install

pip install jarvisclaw

Quick Start

from jarvisclaw import JarvisClaw

# x402 wallet mode (pay per request, no API key needed)
client = JarvisClaw(private_key="0x...")

# Or API key mode
client = JarvisClaw(api_key="sk-...")

# Execute an intent. The response is the raw provider payload — for
# chat_completion that means an OpenAI-shaped body.
result = client.execute(
    intent="chat_completion",
    payload={"messages": [{"role": "user", "content": "Hello!"}]},
)
print(result["choices"][0]["message"]["content"])

Core API

Resolve — Find the best provider without executing

options = client.resolve(
    intent="chat_completion",
    constraints={"max_price_usd": 0.01, "max_latency_ms": 3000},
)

for match in options["matches"]:
    print(f"{match['provider_id']} {match['model']} — ${match['estimated_price_usd']:.6f}")

resolve takes no payload: it ranks providers for an intent, it does not run anything. Constraint keys are max_price_usd, max_latency_ms and features.

Execute — Resolve + run in one call

result = client.execute(
    intent="chat_completion",
    payload={
        "messages": [{"role": "user", "content": "Write a haiku about distributed systems"}],
        "temperature": 0.7,
    },
)
print(result["choices"][0]["message"]["content"])

Without a budget, execute forwards the payload and returns the provider's own response. Pass a budget (or use execute_budget) to get the wrapped form with cost and settlement metadata instead.

Execute with Budget Guard

result = client.execute_budget(
    intent="chat_completion",
    payload={"messages": [{"role": "user", "content": "Summarize this paper"}]},
    budget={"max_total_usd": 0.03},
)

print(result["status"])                       # "success" | "rejected" | "error"
print(f"Spent: ${result['actual_cost_usd']:.6f} (limit was $0.03)")
print(result["result"])                       # the provider response

A rejected request does not raise — check status, and read reason when it is not "success".

Stream — Server-Sent Events

for event in client.stream(
    intent="chat_completion",
    payload={"messages": [{"role": "user", "content": "Tell me a story"}]},
):
    if event["event"] == "metadata":
        print("provider:", event["data"]["provider"])
        continue
    chunk = event["data"]
    if isinstance(chunk, dict):
        delta = chunk.get("choices", [{}])[0].get("delta", {})
        print(delta.get("content", ""), end="", flush=True)

stream yields {"event": ..., "data": ...} dicts, not bare text. The first event is metadata and the last is done; in between the gateway relays the upstream events verbatim, so data holds OpenAI-style chunks — including the literal "[DONE]" sentinel, which arrives as a string rather than a dict.

This endpoint takes no budget. Use execute_budget when you need a spend cap. client.subscribe(...) is the same call under its documented name.

Intent Types

chat_completion, image_generation, video_generation, text_to_speech, web_search, knowledge_search, prompt_optimization, document_processing, translation, code_generation, code_execution, data_analysis, utility. Call client.discover() for the live list plus which providers serve each.

# Image generation
result = client.execute(
    intent="image_generation",
    payload={"prompt": "A cyberpunk city at sunset", "size": "1024x1024"},
    budget={"max_total_usd": 0.08},
)

# Text-to-speech
result = client.execute(
    intent="text_to_speech",
    payload={"input": "Hello world", "voice": "alloy"},
    budget={"max_total_usd": 0.01},
)

# Code generation
result = client.execute(
    intent="code_generation",
    payload={"messages": [{"role": "user", "content": "Write a Python quicksort"}]},
    budget={"max_total_usd": 0.05},
)

There is no embedding intent — use EmbeddingsClient for /v1/embeddings.

Analytics & Audit

# Aggregated spend. Scope comes from your credentials: a non-admin caller only
# ever sees their own rows, so there is no scope parameter.
rows = client.spend(period="30d", group_by=["day", "model"])
for r in rows:
    print(f"{r['day']} {r['model']}: ${r['total_cost_usd']:.4f} ({r['total_reqs']} reqs)")

# Per-model and per-day shortcuts
by_model = client.model_breakdown(period="7d")
trend = client.daily_trend(period="30d")

# Filter to AIP traffic only
aip_only = client.spend(period="7d", filters={"api_source": "aip"})

# Budget utilisation, computed from actual spend
status = client.budget_status(daily_budget=5.0, monthly_budget=100.0)
print(f"Today: ${status['daily_spent']:.2f} / $5.00 ({status['daily_pct']:.0f}%)")
for alert in status["alerts"]:
    print("!", alert)

# Request-level audit trail (events, not costs)
history = client.audit_log()
for entry in history["entries"]:
    print(entry["event_type"], entry["request_id"])

The standalone /v1/aip/analytics/* endpoints were removed from the gateway and folded into /api/analytics/aggregate; spend() reads that, and AIP usage shows up tagged api_source="aip". The gateway has no budget endpoint, so budget_status derives its figures locally from spend() — the limits you pass are the ones compared against, not values stored server-side.

Federation — Peer Discovery

# Public registry — no auth needed
peers = client.discover_peers()
for peer in peers["data"]:
    print(f"{peer['name']}{peer['base_url']} (healthy={peer['healthy']})")

# Search callable resources across the federation
found = client.search_federation("crypto price", limit=10)
for r in found["data"]:
    print(f"{r['server_name']} {r['method']} {r['path']} — ${r['sell_price']}")

# Invoke one; the gateway settles with the peer on your behalf
result = client.federation_execute({"resource_id": 42, "payload": {...}})

Peer management is admin-only:

from jarvisclaw import FederationClient

fed = FederationClient(api_key="sk-...")
fed.add_peer("peer.example.com")     # POST, domain in the body
fed.remove_peer("peer.example.com")  # DELETE, domain in the body — not an id
fed.crawl()                          # crawls every registered peer; takes no seeds

Those four sit behind AdminAuth, which needs a dashboard session or an access token plus a New-Api-User header — an API key or wallet gets 401. The public registry methods above work with any credentials.

Other Endpoints

from jarvisclaw import EmbeddingsClient, SearchClient, UserAPIClient

emb = EmbeddingsClient(api_key="sk-...")
vector = emb.embed("text-embedding-3-small", "hello world")
vectors = emb.embed_batch("text-embedding-3-small", ["a", "b"])
ranked = emb.rerank_texts("rerank-v1", "cats", ["dogs bark", "cats purr"])
flags = emb.moderate("some text")

search = SearchClient(api_key="sk-...")
results = search.query("latest AI news")          # /v1/search via a search model
neural = search.exa_search("latest AI news")      # Exa, structured results
answer = search.answer("what is a transformer")   # Exa, grounded answer

uapi = UserAPIClient(api_key="sk-...")
listing = uapi.list(category="data")
out = uapi.call("weather", "forecast", method="POST", json={"city": "Tokyo"})

Wallet

from jarvisclaw import WalletClient

wallet = WalletClient(api_key="sk-...")

# On-chain USDC across Base and Solana. Account quota is deliberately excluded:
# x402 settles against the wallet and never debits quota.
bal = wallet.balance()
print(bal["balance_usd"], bal["wallets"]["base"]["usdc"])
print(wallet.total_usd())

# Limits: the PUT replaces the whole record, so change one field via set_limit
# rather than update_limits, or the others are stored as zero.
wallet.set_limit(daily_max_usd=30)

print(wallet.pools())
print(wallet.history(page=1, page_size=20))

Agent Mode — Autonomous with Tools

For multi-step autonomous tasks with tool use and budget control:

from jarvisclaw import Agent

agent = Agent(private_key="0x...", default_budget=1.00)

@agent.tool
def search(query: str) -> str:
    """Search the web for current information."""
    import requests
    resp = requests.get(f"https://api.search.com/v1?q={query}")
    return resp.json()["results"][0]["snippet"]

@agent.tool
def calculator(expression: str) -> str:
    """Evaluate a math expression."""
    return str(eval(expression))

result = agent.run("What's Jupiter's mass divided by Earth's mass?")
print(result.text)
print(f"Cost: ${result.cost.spent_usd:.4f}")
print(f"Iterations: {result.iterations}")

Agent Streaming

agent = Agent(private_key="0x...")
for chunk in agent.stream("Write a haiku about distributed systems"):
    print(chunk, end="", flush=True)

Agent Budget Guards

from jarvisclaw import Agent, BudgetExceededError

agent = Agent(default_budget=2.00)

try:
    result = agent.run("analyze this massive dataset", budget=0.50)
except BudgetExceededError as e:
    print(f"Stopped at ${e.spent:.4f} — limit was ${e.budget:.2f}")

x402 Wallet Payments

Pay per-request with on-chain USDC. No API key, no account needed:

# EVM (Base network)
client = JarvisClaw(private_key="0x...")

# Solana
client = JarvisClaw(private_key="base58...", network="solana")

# Check balance — reads the wallet's on-chain USDC directly, on Base for an EVM
# key and on Solana mainnet for a base58 key.
balance = client.get_balance()
print(f"Wallet balance: ${balance:.2f} USDC")

Solana support needs the extra: pip install jarvisclaw[solana]. EVM x402 needs pip install jarvisclaw[agent]. pip install jarvisclaw[all] covers both plus the async client.

OpenAI / Anthropic SDK Compatibility

Use official SDKs directly against JarvisClaw — just change the base URL:

from openai import OpenAI

client = OpenAI(
    api_key="sk-your-jarvisclaw-key",
    base_url="https://api.jarvisclaw.ai/v1"
)

resp = client.chat.completions.create(
    model="anthropic/claude-sonnet-4-20250514",
    messages=[{"role": "user", "content": "Hello!"}]
)
print(resp.choices[0].message.content)
import anthropic

client = anthropic.Anthropic(
    api_key="sk-your-jarvisclaw-key",
    base_url="https://api.jarvisclaw.ai"
)

message = client.messages.create(
    model="claude-sonnet-4-20250514",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Hello!"}]
)
print(message.content[0].text)

When to use which?

  • JarvisClaw — intent routing, x402 payments, budget control, federation
  • Agent — autonomous multi-step tasks with tools
  • openai/anthropic SDK — drop-in for existing code, Claude/GPT native features

Configuration

Env Variable Purpose
JARVISCLAW_API_KEY API key authentication
JARVISCLAW_WALLET_KEY x402 private key (EVM or Solana)
JARVISCLAW_BASE_URL Custom endpoint (default: https://api.jarvisclaw.ai)

Migration to v3

v3 removes methods that called gateway routes which no longer exist, and corrects response shapes that had drifted. All client classes keep their names, so imports do not change — but the methods and shapes below do.

pip install --upgrade jarvisclaw

Analytics. /v1/aip/analytics/* was consolidated into /api/analytics/aggregate, so these were 404ing:

Removed Use instead
IntentClient.cost_summary() spend()
IntentClient.cost_trend() daily_trend()
IntentClient.roi() spend() and compute from total_quota / total_cost_usd
IntentClient.budget_status() JarvisClaw.budget_status() (derived locally)
IntentClient.model_breakdown() cost_by_model()

There is no scope parameter any more — scope comes from your credentials.

Wallet balance. /v1/wallet/balance dropped the quota fields. quota, quota_usd, hd_wallet and subscription are gone; read balance_usd and wallets.{base,solana}.{usdc,address}.

Wallet limits. update_limits() replaces the whole record, so it zeroes anything you omit. Use set_limit(daily_max_usd=30) for single-field changes. Agent.set_limits() now does that read-modify-write internally.

Prompt coach. /v1/prompt-coach/score never existed. score() now returns an int on a 1-100 scale, derived from optimize(). optimize() returns the unwrapped data object with score_before / score_after as ints.

Discover. The body keys are intent, features and max_price — the old intent_type, protocol and min_uptime were never read by the server.

Federation. crawl() takes no arguments; the server crawls every registered peer. remove_peer(domain) sends the domain in the body. list_peers() returns the list directly, with camelCase keys.

Streaming. stream() and subscribe() accept constraints, preferences and optimize_for, not budget — the endpoint never read a budget. Both yield {"event", "data"} dicts.

Solana balances. get_balance() with a Solana key used to raise; it now queries Solana mainnet.

Per-capability clients still work

The unified JarvisClaw client is the recommended entry point, but the older per-capability classes are unchanged and stay supported:

from jarvisclaw import IntentClient
client = IntentClient(private_key="0x...")
client.execute("chat_completion", payload={...})

Links

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

jarvisclaw-3.0.1.tar.gz (78.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

jarvisclaw-3.0.1-py3-none-any.whl (69.3 kB view details)

Uploaded Python 3

File details

Details for the file jarvisclaw-3.0.1.tar.gz.

File metadata

  • Download URL: jarvisclaw-3.0.1.tar.gz
  • Upload date:
  • Size: 78.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.13

File hashes

Hashes for jarvisclaw-3.0.1.tar.gz
Algorithm Hash digest
SHA256 41727ca562128be70dc2c45ff0931e0e61fa80bf7dd44f414ee6fc2c4f93a478
MD5 32a27d41e20097c3175f7fa3c19db917
BLAKE2b-256 73260dbd7fe918ce81ee2bbc7c800d3a205a8c893ef805365f1cf66aff377af8

See more details on using hashes here.

File details

Details for the file jarvisclaw-3.0.1-py3-none-any.whl.

File metadata

  • Download URL: jarvisclaw-3.0.1-py3-none-any.whl
  • Upload date:
  • Size: 69.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.13

File hashes

Hashes for jarvisclaw-3.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 caa3ba116def753174f824956286c79e173339ed675e488417038e43ebbf4a38
MD5 546eb2e1afce5cb2bea398aded7383b1
BLAKE2b-256 b6718ffc28373e9e327877a349416ee55dff75f06f42cedd45d92252f6ea59ba

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page