Metadata-Version: 2.5
Name: neura-agent
Version: 0.2.0
Summary: Agent client for NEURA Compute: OpenAI-compatible inference where the agent pays for itself. HTTP 402 -> wallet sign-in -> USDC on Base -> credited -> retry, as a drop-in http_client for the OpenAI Python SDK.
Project-URL: Homepage, https://app.neuracomputes.com
Project-URL: Docs, https://app.neuracomputes.com/docs
License: MIT
Keywords: 402,agent,base,crewai,inference,langchain,neura,openai,siwe,usdc,x402
Requires-Python: >=3.9
Requires-Dist: eth-account>=0.10
Requires-Dist: httpx>=0.25
Requires-Dist: web3>=6.0
Provides-Extra: examples
Requires-Dist: crewai>=0.30; extra == 'examples'
Requires-Dist: langchain-openai>=0.1; extra == 'examples'
Requires-Dist: openai>=1.0; extra == 'examples'
Provides-Extra: test
Requires-Dist: pytest>=7; extra == 'test'
Description-Content-Type: text/markdown

# neura-agent — the agent client for NEURA Compute (Python)

OpenAI-compatible inference where the agent pays for itself.

1. **Automatic HTTP 402 handling** — when NEURA says *payment required* it
   answers with an x402 envelope. The client settles it from the agent's own
   wallet on Base (USDC, 1 credit = $1), submits the tx hash, waits for the
   credit, and retries. No human, no card.
2. **Wallet-only onboarding (SIWE / EIP-4361)** — an agent holding only a
   wallet key creates its account and self-provisions an API key. If the very
   first request is anonymous, the client does this itself before paying.

```bash
pip install neura-agent
```

## With the official OpenAI SDK

```python
import os
from openai import OpenAI
from neura_agent import NeuraAgent

neura = NeuraAgent(
    wallet_key=os.environ["AGENT_WALLET_KEY"],  # a hot wallet holding a little USDC on Base
    auto_fund=True,                             # 402 -> (login) -> pay USDC -> credited -> retry
    max_usdc_per_fund=5,                        # hard cap per payment
)

client = OpenAI(
    base_url=neura.base_url + "/v1",
    api_key="wallet",                 # placeholder — the client injects the key it provisions
    http_client=neura.http_client(),  # 402s handled transparently
)

r = client.chat.completions.create(
    model="openai/gpt-oss-120b",
    messages=[{"role": "user", "content": "Hello!"}],
)
```

Already have an API key? Pass `api_key=` and the wallet is only used to pay.

## With LangChain

```python
from langchain_openai import ChatOpenAI
from neura_agent import NeuraAgent

neura = NeuraAgent(wallet_key=os.environ["AGENT_WALLET_KEY"], auto_fund=True)
llm = ChatOpenAI(
    model="openai/gpt-oss-120b",
    base_url=neura.base_url + "/v1",
    api_key="wallet",
    http_client=neura.http_client(),
)
print(llm.invoke("Who pays for this call?").content)
```

## With CrewAI

CrewAI routes through LiteLLM, which does not take a custom http client, so
pre-fund before the crew runs and give it the provisioned key:

```python
from crewai import LLM
from neura_agent import NeuraAgent

neura = NeuraAgent(wallet_key=os.environ["AGENT_WALLET_KEY"], auto_fund=True)
neura.login_with_wallet()
if neura.credit_balance() < 2:
    neura.fund(neura.discovery(), credits=2)   # 2 USDC -> 2 credits

llm = LLM(model="openai/openai/gpt-oss-120b", base_url=neura.base_url + "/v1", api_key=neura.api_key)
```

See `examples/` for runnable versions of all three.

## What the platform sends, and what the client does with it

A 402 carries an x402 v1 envelope. Production today:

```json
{
  "code": "PAYMENT_REQUIRED",
  "x402Version": 1,
  "accepts": [{
    "scheme": "exact", "network": "base",
    "maxAmountRequired": "1000000",
    "payTo": "0x404f4E91Fc8Bf0c5fcfC71b4C06E2c0faACdb2c5",
    "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
    "extra": { "settlement": "transfer", "assetSymbol": "USDC", "decimals": 6,
               "creditEndpoint": "/api/depin/credits/topup-usdc" }
  }],
  "authenticate": { "scheme": "EIP-4361", "nonce": "/api/auth/wallet/nonce", "verify": "/api/auth/wallet" }
}
```

`choose_offer(body)` picks the rail: the USDC `exact` offer whenever present;
a `neura-burn` offer only when advertised with a live rate. If the platform
offers nothing the client can settle, it raises instead of guessing.

## Safety model

- Nothing moves on-chain unless you pass `wallet_key` **and** `auto_fund=True`
  (or call `fund()` yourself).
- `max_usdc_per_fund` (default 5) caps any single payment; `max_neura_per_burn`
  (default 100) caps any burn.
- `pin_pay_to` refuses to send USDC to any receiver other than the one you pin.
- On 402 without auto-funding, `PaymentRequiredError` carries `remediation`,
  `x402` and the full `body` so your own logic can decide.
- Use a dedicated hot wallet with a small balance — never a treasury key.

## Allowances: the owner's leash, honoured by the client

The platform's owner-set allowances answer with `403`/`429` and a code. The
client reads them before it ever thinks about paying:

- `AGENT_PAUSED` (the owner's kill switch) raises `AgentPausedError`. The
  client never funds, never retries: only the owner can resume the key.
- `ALLOWANCE_DAILY`, `ALLOWANCE_MONTHLY`, `OUTSIDE_ALLOWED_HOURS`,
  `SELF_LIMIT_EXCEEDED` are retryable and come with `Retry-After`. By default
  the client raises `AllowanceError` with `retry_after_seconds` so your loop
  can decide. Set `max_retry_after_seconds` and, when the wait fits, the
  client sleeps exactly that long and retries **once**.
- `MODEL_NOT_ALLOWED`, `ALLOWANCE_PER_CALL` are not retryable and raise
  `AllowanceError` with `retryable=False` even when waiting is allowed.
- Any other 403/429 passes through untouched.

The agent's own key can only tighten its allowance (`PUT /api/agent/allowance`).
A **wallet tenant** has no console, so its owner acts by signature:
`agent.owner_allowance(api_key_id, allowance={...})`, `action="kill"` or
`action="resume"` signs a fresh SIWE message with `wallet_key` and posts it to
`/api/agent/allowance/owner`. Run that where the wallet lives, not inside the
agent: whoever can sign can loosen the leash.

## API

| Member | Purpose |
|---|---|
| `NeuraAgent(...)` | `api_key`, `base_url`, `wallet_key`, `auto_fund`, `credits_per_fund`, `max_usdc_per_fund`, `max_neura_per_burn`, `confirmations`, `rpc_url`, `pin_pay_to`, `max_retry_after_seconds` |
| `agent.owner_allowance(api_key_id, allowance=None, action=None, reason=None)` | Owner-by-signature: set any allowance field, or `action="kill"` / `"resume"` |
| `AgentPausedError` / `AllowanceError` | Raised on `AGENT_PAUSED` / on an allowance refusal (`code`, `retryable`, `retry_after_seconds`, `body`) |
| `agent.http_client()` | An `httpx.Client` whose `send` settles 402s — pass to OpenAI / LangChain |
| `agent.chat(model, messages, **params)` | Plain chat completion with the same 402 handling |
| `agent.login_with_wallet()` | SIWE login → `{tenantId, address, apiKey}` |
| `agent.fund(body, credits=None)` | Settle a 402 body (or a single offer) on the rail it advertises |
| `agent.fund_usdc(offer, credits)` / `agent.fund_burn(remediation)` | The rails individually |
| `agent.credit_balance()` | Current credits (1 = $1) |
| `agent.discovery()` | `/.well-known/neura.json` |
| `choose_offer(body)` | Which rail a 402 lets the client settle |
| `PaymentRequiredError` | Raised on unhandled 402 — carries `remediation`, `x402`, `body` |

## Develop / publish

```bash
cd packages/neura-agent-py
pip install -e ".[test]"
pytest
python -m build && twine upload dist/*
```
