Skip to content

SDK Reference ​

Complete guide for the JarvisClaw Python and Go SDKs. Both SDKs handle authentication, the x402 payment flow, streaming, retries, and error handling out of the box.


Installation ​

bash
pip install jarvisclaw

# x402 wallet payments on Base (EVM)
pip install jarvisclaw[agent]

# x402 wallet payments on Solana
pip install jarvisclaw[solana]

# asyncio clients
pip install jarvisclaw[async]

# everything
pip install jarvisclaw[all]
bash
go get github.com/api-jarvisclaw/go-sdk/v2@latest

Choosing a client ​

The Python SDK is organised as one client per surface, plus two aggregate entry points. Pick the smallest one that covers your use case.

ClientUse for
AgentAutonomous loops — ask(), run() with tools, budget tracking
JarvisClawThe AIP surface — resolve(), execute(), stream(), audit()
IntentClientAIP with preferences and analytics helpers
ChatClientChat completions only
ImageClient / VideoClient / AudioClientMedia generation
SearchClientWeb search and page extraction
MarketplaceClientRaw marketplace service calls
WalletClientBalance, history, limits, pools
PromptCoachClientPrompt optimization
NetworkClientNetwork peers and crawling
OpenAIDrop-in openai-shaped client (client.chat.completions.create)

The Go SDK exposes one *Client carrying every method, plus thin typed wrappers (ChatClient, ImageClient, VideoClient, AudioClient, SearchClient, MarketplaceClient) created via NewChatClient(...) and friends.


Initialization ​

Two authentication modes — API Key (managed billing) or Private Key (autonomous x402 settlement).

python
from jarvisclaw import JarvisClaw

client = JarvisClaw(api_key="sk-your-api-key")
python
from jarvisclaw import JarvisClaw

# Agent signs payment directly; no managed account needed.
# Hex key → Base (EVM). Base58 keypair → Solana. Detected from the key format.
client = JarvisClaw(private_key="0x<your-hex-private-key>")
go
package main

import jc "github.com/api-jarvisclaw/go-sdk/v2"

func main() {
    client, err := jc.NewClient(jc.WithAPIKey("sk-your-api-key"))
    if err != nil {
        panic(err)
    }
    _ = client
}
go
package main

import jc "github.com/api-jarvisclaw/go-sdk/v2"

func main() {
    // EVM hex key for Base. Solana is Python-only today.
    client, err := jc.NewClient(jc.WithPrivateKey("0x<your-hex-private-key>"))
    if err != nil {
        panic(err)
    }
    _ = client
}

Both constructors return an error in Go

jc.NewClient and every jc.NewXxxClient return (*Client, error). Ignoring the second value will not compile.

Environment variables:

bash
export JARVISCLAW_API_KEY=sk-...
export JARVISCLAW_WALLET_KEY=0x...                     # wallet private key (hex or base58)
export JARVISCLAW_BASE_URL=https://api.jarvisclaw.ai   # default

If env vars are set, you can initialize with no arguments. JARVISCLAW_API_KEY wins when both are present.

python
client = JarvisClaw()  # reads from env
go
client, err := jc.NewClient()  // reads from env

Timeouts ​

Timeout is set per client, not per request.

python
from jarvisclaw import JarvisClaw

client = JarvisClaw(api_key="sk-...", timeout=60)  # seconds; default 120
go
import (
    "context"
    "time"
    jc "github.com/api-jarvisclaw/go-sdk/v2"
)

client, _ := jc.NewClient(jc.WithAPIKey("sk-..."), jc.WithTimeout(60*time.Second))

// Per-call deadlines use the context you pass to each method:
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

resp, err := client.Execute(ctx, jc.ExecuteRequest{
    Intent:  "chat_completion",
    Payload: map[string]any{"messages": []map[string]any{{"role": "user", "content": "Hello"}}},
})

In Python the SDK retries 429 and 5xx up to 3 times with exponential backoff. There is no per-call timeout argument — construct a second client if you need a different budget.


Quickest path: Agent ​

Agent wraps intent resolution and chat into a single call and tracks spend for you.

python
from jarvisclaw import Agent

agent = Agent(api_key="sk-your-api-key", default_budget=1.00)

# One line: resolve the best model within budget, then call it
print(agent.ask("Explain quantum computing in 3 sentences", budget=0.01, optimize="cost"))

# Streaming
for chunk in agent.stream("Write a poem about AI agents"):
    print(chunk, end="", flush=True)

# Autonomous loop with tools
@agent.tool
def calculator(expression: str) -> str:
    """Evaluate a math expression."""
    return str(eval(expression))

result = agent.run("What is 2^100 + 3^50?")
print(result.text)
print(result.cost)        # CostTracker — spend for this run
print(result.iterations)
go
ctx := context.Background()
client, _ := jc.NewClient(jc.WithAPIKey("sk-your-api-key"))

// Resolve within budget, then chat — one call
text, err := client.Ask(ctx, "Explain quantum computing in 3 sentences",
    jc.AskOptions{Budget: 0.01, Optimize: "cost"})
if err != nil {
    log.Fatal(err)
}
fmt.Println(text)

optimize / Optimize accepts cost, quality, or latency. Defaults to cost.


Intent Examples ​

JarvisClaw.execute() takes intent and payload positionally, then optional budget and constraints keywords. The response is the resolved provider's raw body — for chat_completion that is an OpenAI-shaped completion.

1. Chat Completion (chat_completion) ​

python
result = client.execute(
    "chat_completion",
    {
        "messages": [
            {"role": "system", "content": "You are a helpful assistant."},
            {"role": "user", "content": "Explain quantum computing in 3 sentences."},
        ],
        "temperature": 0.7,
        "max_tokens": 200,
    },
    budget={"max_total_usd": 0.05},
)

print(result["choices"][0]["message"]["content"])
go
raw, err := client.Execute(ctx, jc.ExecuteRequest{
    Intent: "chat_completion",
    Payload: map[string]any{
        "messages": []map[string]any{
            {"role": "system", "content": "You are a helpful assistant."},
            {"role": "user", "content": "Explain quantum computing in 3 sentences."},
        },
        "temperature": 0.7,
        "max_tokens":  200,
    },
})
if err != nil {
    log.Fatal(err)
}
// Execute returns json.RawMessage — the provider's body, unmodified.
var out struct {
    Choices []struct {
        Message struct{ Content string } `json:"message"`
    } `json:"choices"`
}
_ = json.Unmarshal(raw, &out)
fmt.Println(out.Choices[0].Message.Content)

Need preferences?

JarvisClaw.execute() does not accept preferences. Use IntentClient when you want to steer selection:

python
from jarvisclaw import IntentClient

intent = IntentClient(api_key="sk-...")
result = intent.execute(
    "chat_completion",
    {"messages": [{"role": "user", "content": "Hello"}]},
    constraints={"max_price_usd": 0.05},
    preferences={"optimize_for": "quality"},
)

2. Streaming ​

Streaming goes through /v1/intent/subscribe and yields SSE events, not plain strings. Each event is {"event": ..., "data": ...}.

python
for event in client.stream(
    "chat_completion",
    {"messages": [{"role": "user", "content": "Write a poem about AI agents."}]},
):
    if event["event"] == "chunk":
        print(event["data"].get("content", ""), end="", flush=True)
    elif event["event"] == "done":
        print(f"\n\nCost: ${event['data'].get('actual_cost_usd')}")
go
stream, err := client.Subscribe(ctx, jc.SubscribeRequest{
    Intent:  "chat_completion",
    Payload: map[string]any{"messages": []map[string]any{{"role": "user", "content": "Write a poem about AI agents."}}},
})
if err != nil {
    log.Fatal(err)
}
defer stream.Close()

for {
    ev, err := stream.Next()
    if err != nil {
        break
    }
    fmt.Printf("[%s] %v\n", ev.Event, ev.Data)
}

For plain token streaming without AIP, ChatClient.stream() (Python) and ChatClient.Stream() (Go) yield text chunks directly:

python
from jarvisclaw import ChatClient

chat = ChatClient(api_key="sk-...")
for chunk in chat.stream("Tell me a joke"):
    print(chunk, end="")
go
cc, _ := jc.NewChatClient(jc.WithAPIKey("sk-..."))
stream, _ := cc.Stream(ctx, "Tell me a joke")
for chunk := range stream.Channel() {
    fmt.Print(chunk)
}

3. Image Generation (image_generation) ​

python
result = client.execute(
    "image_generation",
    {"prompt": "A cyberpunk cityscape at sunset", "size": "1024x1024"},
    budget={"max_total_usd": 0.08},
)
print(result["data"][0]["url"])
print(f"Cost: ${result['price']['amount']} USD")
go
// Or use the dedicated client, which parses the response for you:
ic, _ := jc.NewImageClient(jc.WithAPIKey("sk-..."))
img, err := ic.Generate(ctx, "A cyberpunk cityscape at sunset", jc.WithSize("1024x1024"))
if err != nil {
    log.Fatal(err)
}
fmt.Println(img.URL)

The image response is OpenAI-shaped with two JarvisClaw additions: a top-level id and a price object. Use data[0].url for the CDN link.


4. Video Generation (video_generation) ​

Video is asynchronous — submit, then poll. The dedicated client handles polling.

python
from jarvisclaw import VideoClient

video = VideoClient(api_key="sk-your-api-key")

# Blocking — SDK polls until the MP4 is ready
job = video.generate("A drone flying over a tropical island",
                     model="bytedance/seedance-2.0", duration=5)
print(job.url)

# Non-blocking
job = video.generate("Ocean waves at sunset", wait=False)
print(job.id, job.status)     # e.g. "bytedance:video_24d2...", "queued"
# ... later ...
print(video.status(job.id).url)
go
vc, _ := jc.NewVideoClient(jc.WithAPIKey("sk-your-api-key"))

job, err := vc.Generate(ctx, "A drone flying over a tropical island",
    jc.WithVideoModel("bytedance/seedance-2.0"), jc.WithDuration(5), jc.WithWait(true))
if err != nil {
    log.Fatal(err)
}
fmt.Println(job.URL)

Job IDs are provider-prefixed (bytedance:video_xxx), not vg_xxx. Generation takes 60–180s typically; allow more headroom for high resolutions. See Video Generation.


5. Text to Speech (text_to_speech) ​

python
from jarvisclaw import AudioClient

audio = AudioClient(api_key="sk-your-api-key")

# Returns AudioResponse with raw bytes — the SDK follows the CDN URL for you
result = audio.speech("Welcome to JarvisClaw.", model="elevenlabs/flash-v2.5", voice="sarah")
with open("output.mp3", "wb") as f:
    f.write(result.content)
go
ac, _ := jc.NewAudioClient(jc.WithAPIKey("sk-your-api-key"))

result, err := ac.Speech(ctx, "Welcome to JarvisClaw.",
    jc.WithAudioModel("elevenlabs/flash-v2.5"), jc.WithVoice("sarah"))
if err != nil {
    log.Fatal(err)
}
os.WriteFile("output.mp3", result.Data, 0644)

auto/tts and x402 wallets

Smart-route TTS has failed to settle for direct-wallet callers in testing. Pass an explicit ElevenLabs model when using private_key. Voice defaults to sarah.


6. Speech to Text ​

Transcription is multipart upload, not base64 in JSON.

python
from jarvisclaw import AudioClient

audio = AudioClient(api_key="sk-your-api-key")

with open("recording.mp3", "rb") as f:
    text = audio.transcribe(f, model="whisper-1", language="en")
print(text)
bash
curl https://api.jarvisclaw.ai/v1/audio/transcriptions \
  -H "Authorization: Bearer sk-your-api-key" \
  -F file="@recording.mp3" \
  -F model="whisper-1"

transcribe() returns the transcript as a plain string. There is no Go helper for transcription yet — post multipart to /v1/audio/transcriptions directly.


python
from jarvisclaw import SearchClient

search = SearchClient(api_key="sk-your-api-key")

# Goes to /v1/search (smart-routed summary), returns list[SearchResult]
for r in search.query("latest AI agent frameworks 2026", num_results=5):
    print(r.title, r.url, r.snippet[:80])

# Structured Exa results with real per-result URLs
contents = search.contents(["https://example.com/article"])
go
sc, _ := jc.NewSearchClient(jc.WithAPIKey("sk-your-api-key"))

resp, err := sc.Query(ctx, "latest AI agent frameworks 2026", jc.WithNumResults(5))
if err != nil {
    log.Fatal(err)
}
fmt.Println(resp.Summary, resp.SourcesUsed)
for _, r := range resp.Citations {
    fmt.Printf("  %s: %s\n", r.Title, r.URL)
}

query() returns a summary, not ranked links

SearchClient.query() posts to /v1/search with model=auto/search, which returns an AI-written summary with inline citations. When the response has no structured citations the Python SDK yields a single SearchResult(title="Search Result", url="", snippet=<summary>) — so .url is often empty. For per-result titles and URLs, call POST /v1/marketplace/exa/search directly. See Web Search.


Advanced Features ​

Budget Control ​

execute_budget() lives on IntentClient and takes budget as a required third positional argument. The only field the server reads is max_total_usd (plus optional preferred_payment_method and allow_overdraft).

python
from jarvisclaw import IntentClient

intent = IntentClient(private_key="0x<agent-wallet-key>")

result = intent.execute_budget(
    "chat_completion",
    {"messages": [{"role": "user", "content": "Summarize this document..."}]},
    {"max_total_usd": 0.50},
)

print(result["status"])            # "success" | "rejected" | "error"
print(result.get("risk_level"))
print(result.get("actual_cost_usd"))
go
resp, err := client.ExecuteBudget(ctx, jc.ExecuteBudgetRequest{
    Intent:  "chat_completion",
    Payload: map[string]any{"messages": []map[string]any{{"role": "user", "content": "Summarize this document..."}}},
    Budget:  jc.Budget{MaxTotalUSD: 0.50},
})
if err != nil {
    log.Fatal(err)
}
fmt.Printf("status=%s risk=%s\n", resp.Status, resp.RiskLevel)
if resp.ActualCostUSD != nil {
    fmt.Printf("cost=%.6f\n", *resp.ActualCostUSD)
}

Per-call and daily caps

per_call_limit_usd and daily_limit_usd are not fields on this request. Set persistent limits with PUT /v1/wallet/limits (per_request_max_usd, daily_max_usd, monthly_max_usd). See Wallet & Treasury.


Natural Language Resolution ​

Describe what you need in plain language and AIP resolves the intent by embedding similarity. This endpoint resolves only — it does not execute or charge. Take the returned provider and call it yourself.

python
# No SDK helper yet — call the endpoint directly
result = client._post("/v1/intent/resolve/natural", json={
    "query": "I want to generate an image of a cat wearing sunglasses",
    "constraints": {"max_price_usd": 0.10},
})

if result["status"] == "resolved":
    print(result["intent"], result["confidence"])
    for m in result.get("matches", []):
        print(f"  {m['provider_name']}: ${m.get('price_usd')} → {m.get('endpoint')}")
elif result["status"] == "clarify":
    print(result["clarify"]["question"])
    for opt in result["clarify"].get("options", []):
        print(f"  - {opt}")
bash
curl -X POST https://api.jarvisclaw.ai/v1/intent/resolve/natural \
  -H "Authorization: Bearer sk-your-api-key" \
  -H "Content-Type: application/json" \
  -d '{"query": "generate an image of a cat wearing sunglasses"}'

status is one of resolved, clarify, budget_insufficient, no_match. On clarify the follow-up lives in a clarify object (question, options, round) — not a top-level message/options pair.


Resolve (Without Executing) ​

python
matches = client.resolve(
    "chat_completion",
    constraints={
        "max_price_usd": 0.05,
        "max_latency_ms": 3000,
        "features": ["function_calling", "json_mode"],
    },
)

for m in matches["matches"]:
    print(f"{m['provider_id']}: ${m['estimated_price_usd']}, score={m['score']}, {m['reason']}")
print(f"{matches['total_available']} providers available for {matches['intent_type']}")
go
maxPrice, maxLatency := 0.05, 3000

resp, err := client.Resolve(ctx, jc.ResolveRequest{
    Intent: "chat_completion",
    Constraints: jc.Constraints{
        MaxPriceUSD:  &maxPrice,
        MaxLatencyMS: &maxLatency,
        Features:     []string{"function_calling", "json_mode"},
    },
    Preferences: jc.Preferences{OptimizeFor: "quality"},
})
if err != nil {
    log.Fatal(err)
}
for _, m := range resp.Matches {
    fmt.Printf("%s: $%.6f score=%.2f %s\n", m.ProviderID, m.EstimatedPriceUSD, m.Score, m.Reason)
}

Go constraint fields are pointers

MaxPriceUSD is *float64 and MaxLatencyMS is *int. Take the address of a variable — a literal will not compile. Constraints and Preferences are values on ResolveRequest but pointers on ExecuteRequest.

Match fields are provider_id, score, estimated_price_usd, pricing, endpoint, model, reason. There is no resolution_id or expires_at — resolutions are not reserved, so nothing expires.


Audit Log ​

python
entries = client.audit()   # no parameters; scope comes from your credentials

for e in entries["entries"]:
    print(f"[{e['timestamp']}] {e['event_type']} req={e['request_id']}")
    print(f"  {e.get('details')}")
print(f"{entries['count']} entries")
go
resp, err := client.Audit(ctx)
if err != nil {
    log.Fatal(err)
}
for _, e := range resp.Entries {
    fmt.Printf("[%s] %s req=%s %v\n", e.Timestamp, e.EventType, e.RequestID, e.Details)
}

Audit is not filterable

GET /v1/intent/audit accepts no query parameters and returns {entries, count} — there is no total/page/page_size, and no filtering by date or intent type. Each entry is {timestamp, request_id, user_id, event_type, details}; event_type is a lifecycle event (intent_resolved, execution_complete, settlement_confirmed, …), not the intent name. Python's audit() takes no arguments either — it always calls /v1/intent/audit. For spend history use the analytics methods below.


Cost Analytics ​

python
from jarvisclaw import IntentClient

intent = IntentClient(api_key="sk-...")

# Aggregated spend and settlement rows
print(intent.spend(period="7d", group_by=["day", "model"]))

# Convenience wrappers over spend()
print(intent.cost_by_model(period="7d"))
print(intent.daily_trend(period="30d"))

# Mined quality signals and the deep-scan summary
print(intent.quality(period="7d"))
print(intent.insights(period="7d"))
go
rows, _ := client.Spend(ctx, jc.AnalyticsParams{
    Period:  "7d",
    GroupBy: []string{"day", "model"},
})

// Convenience wrappers over Spend
byModel, _ := client.CostByModel(ctx, jc.AnalyticsParams{Period: "7d"})
trend, _ := client.DailyTrend(ctx, jc.AnalyticsParams{Period: "30d"})

quality, _ := client.QualityMetrics(ctx, jc.AnalyticsParams{Period: "7d"})
insights, _ := client.Insights(ctx, jc.AnalyticsParams{Period: "7d"})

These map to /api/analytics/{aggregate,quality,insights}.

period accepts "24h", "7d" (default), "30d" or "90d"; any other value falls back to "7d" server-side. group_by accepts day, model, api_source, principal_type, channel, group and client_id, defaulting to day,model,api_source.

Scope is enforced server-side from the auth context. Calling with an API token (sk-...), as the SDK does, always returns only your own rows: user_id is ignored on this path, even for a token owned by an admin. Widening the scope with user_id works only for admins signed in to the dashboard (session auth). AIP usage shows up in the same rows with api_source="aip" — the older /v1/aip/analytics/* endpoints have been removed.


Network ​

Peer listing and crawling require admin credentials.

python
from jarvisclaw import NetworkClient

net = NetworkClient(api_key="sk-admin-key")
print(net.list_peers())
print(net.crawl())
go
peers, _ := client.NetworkPeers(ctx)
_, _ = client.NetworkCrawl(ctx)

Browsing and executing network resources is public and has no SDK wrapper yet — call the endpoints directly:

bash
# Natural-language resource recommendation (free)
curl -X POST https://api.jarvisclaw.ai/v1/network/recommend \
  -H "Content-Type: application/json" \
  -d '{"intent": "I need to analyze stock market data", "limit": 5}'

# Full-text search across network resources (free)
curl "https://api.jarvisclaw.ai/v1/network/search?q=stock&limit=5"

# Execute a network resource (paid)
curl -X POST https://api.jarvisclaw.ai/v1/network/execute \
  -H "Authorization: Bearer sk-your-api-key" \
  -H "Content-Type: application/json" \
  -d '{"resource_id": 532, "payload": {"ticker": "AAPL"}}'

resource_id is the numeric federation_resources.id from /v1/network/apis, not a slug. See AIP.


Error Handling ​

python
from jarvisclaw import (
    JarvisClaw,
    JarvisClawError,
    APIError,
    AuthenticationError,
    RateLimitError,
    InsufficientBalanceError,
    PaymentError,
)

client = JarvisClaw(private_key="0x...")

try:
    result = client.execute(
        "chat_completion",
        {"messages": [{"role": "user", "content": "Hello"}]},
    )
except AuthenticationError as e:
    print(f"Bad key [{e.status_code}]: {e.message}")
except InsufficientBalanceError as e:
    print(f"Top up USDC [{e.status_code}]: {e.message}")
    print(f"Server said: {e.body}")
except RateLimitError as e:
    print(f"Rate limited; retry after {e.retry_after}s")
except APIError as e:
    print(f"API error [{e.status_code}]: {e.message}")
except JarvisClawError as e:
    print(f"SDK error: {e}")
go
import (
    "errors"
    jc "github.com/api-jarvisclaw/go-sdk/v2"
)

raw, err := client.Execute(ctx, jc.ExecuteRequest{
    Intent:  "chat_completion",
    Payload: map[string]any{"messages": []map[string]any{{"role": "user", "content": "Hello"}}},
})
if err != nil {
    var authErr *jc.AuthenticationError
    var balErr *jc.InsufficientBalanceError
    var rateErr *jc.RateLimitError
    var apiErr *jc.APIError

    switch {
    case errors.As(err, &authErr):
        fmt.Printf("bad key: %s\n", authErr.Message)
    case errors.As(err, &balErr):
        fmt.Printf("top up USDC: %s\n", balErr.Message)
    case errors.As(err, &rateErr):
        fmt.Printf("rate limited: %s\n", rateErr.Message)
    case errors.As(err, &apiErr):
        fmt.Printf("api error [%d]: %s\n", apiErr.StatusCode, apiErr.Message)
    default:
        fmt.Printf("transport error: %v\n", err)
    }
}
_ = raw

Python exception hierarchy: JarvisClawError → APIError (carries status_code, message, body) → AuthenticationError / RateLimitError / InsufficientBalanceError. PaymentError extends JarvisClawError for x402 signing failures. RateLimitError.retry_after reads retry_after from the response body.

Agent raises its own BudgetExceededError (.budget, .spent) when a run exceeds its cap.

Go types mirror this: APIError with StatusCode/Message/Body, embedded into AuthenticationError, RateLimitError, InsufficientBalanceError; PaymentError embeds JarvisClawError. None carry a .Type discriminator — switch on the concrete type or StatusCode.


Async (Python) ​

python
import asyncio
from jarvisclaw.aio import ChatClient, ImageClient

async def main():
    async with ChatClient(private_key="0x...") as chat, \
               ImageClient(private_key="0x...") as image:

        text, img = await asyncio.gather(
            chat.complete("What is AIP?"),
            image.generate("A cat on Mars"),
        )
        print(text, img.url)

        async for chunk in chat.stream("Tell me a story"):
            print(chunk, end="")

asyncio.run(main())

Available async clients: ChatClient, ImageClient, VideoClient, AudioClient, SearchClient, MarketplaceClient, WalletClient, IntentClient. Requires pip install jarvisclaw[async]. See Async & Concurrent.


OpenAI Drop-in ​

python
from jarvisclaw import OpenAI

client = OpenAI(api_key="sk-your-api-key")

resp = client.chat.completions.create(
    model="auto",
    messages=[{"role": "user", "content": "Hello!"}],
)
print(resp.choices[0].message.content)

This shim adds x402 support to the familiar shape. If you would rather use the official openai package, just point base_url at https://api.jarvisclaw.ai/v1 — see Quick Start.


Full Example: Agent Loop ​

python
from jarvisclaw import Agent, BudgetExceededError

agent = Agent(private_key="0x<agent-wallet-key>", default_budget=5.00)

def agent_loop(instructions: list[str]):
    for instruction in instructions:
        try:
            reply = agent.ask(instruction, budget=0.05)
        except BudgetExceededError as e:
            print(f"Stopped: spent ${e.spent:.4f} of ${e.budget:.4f}")
            return
        print(f"Agent: {reply}")

    print(f"Wallet balance: ${agent.get_balance():.4f} USDC")

agent_loop([
    "What are the tradeoffs of vector databases?",
    "Summarize that in one sentence.",
])
go
package main

import (
    "context"
    "fmt"
    "log"

    jc "github.com/api-jarvisclaw/go-sdk/v2"
)

func main() {
    client, err := jc.NewClient(jc.WithPrivateKey("0x<agent-wallet-key>"))
    if err != nil {
        log.Fatal(err)
    }
    ctx := context.Background()

    for _, instruction := range []string{
        "What are the tradeoffs of vector databases?",
        "Summarize that in one sentence.",
    } {
        reply, err := client.Ask(ctx, instruction, jc.AskOptions{Budget: 0.05})
        if err != nil {
            log.Fatal(err)
        }
        fmt.Printf("Agent: %s\n", reply)
    }

    balance, _ := client.GetBalance(ctx)
    fmt.Printf("Wallet balance: $%.4f USDC\n", balance)
}

For multi-turn conversations that keep history, use agent.run(...) (Python) or build the message array yourself and call ChatClient.Completion(ctx, messages, ...) (Go).