Metadata-Version: 2.4
Name: forgeintel-sdk
Version: 0.1.0b1
Summary: Forge telemetry, agent context and feedback for Python x402 APIs
Project-URL: Documentation, https://docs.development.forgeintel.co/integrations/python
Project-URL: Repository, https://github.com/ClawCash/forge-feedback
License-Expression: MIT
License-File: LICENSE
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: FastAPI
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: httpx<1,>=0.27
Provides-Extra: test
Requires-Dist: build>=1.2; extra == 'test'
Requires-Dist: fastapi<1,>=0.115; extra == 'test'
Requires-Dist: pytest-asyncio<2,>=0.24; extra == 'test'
Requires-Dist: pytest<10,>=8; extra == 'test'
Requires-Dist: ruff>=0.11; extra == 'test'
Requires-Dist: x402[evm,fastapi]==2.5.0; extra == 'test'
Description-Content-Type: text/markdown

# Forge SDK for Python

`forgeintel-sdk` (import `forgeintel`) brings Forge telemetry, agent context and
feedback to x402 services running FastAPI, Starlette, or another ASGI app.
Python 3.11+. Beta source package; **not yet published to PyPI**.

```sh
# From this repository
python -m pip install ./packages/sdk-python
```

```python
import os
from fastapi import FastAPI
from forgeintel import Forge, ForgeMiddleware

app = FastAPI()
# Install your x402 payment middleware here, FIRST.

forge = Forge(
    api_key=os.environ["FORGE_API_KEY"],
    backend_url="https://api.forgeintel.co/api/sdk/v2",
    public_url="https://api.example.com",
)
# Starlette runs the LAST added middleware FIRST.
app.add_middleware(ForgeMiddleware, forge=forge)
```

See the complete runnable merchant in [`examples/fastapi`](../../examples/fastapi).
Plain ASGI: `app = forge.wrap(app)`. Use one Forge instance per application/event
loop. ASGI lifespan shutdown flushes and closes Forge automatically. When lifespan
is disabled, call `await forge.shutdown()` in your application's shutdown hook.

## What ships

- x402 v1 JSON challenges / `X-PAYMENT` and v2 `PAYMENT-REQUIRED` /
  `PAYMENT-SIGNATURE` / `PAYMENT-RESPONSE`. Existing payment terms and merchant
  extensions, including Bazaar, remain intact.
- Locally signed feedback IDs, byte-compatible with the TypeScript SDK and Forge
  v2 collector. IDs, response decoration and context parsing need **no Forge
  network round trip on the merchant request path**. This is not a payment
  processor: the existing x402 middleware still verifies and settles payments.
- Background discovery, challenge and interaction telemetry with the same header
  allowlist as TypeScript. AgentCash, awal and pay.sh hints reach the backend,
  where caller classification and generic-traffic filtering happen.
- Agent context required on paid requests when enabled and stripped before
  merchant validation, in JSON bodies or query parameters. Configured
  independently from feedback.
- Feedback ID header, optional JSON body fields and optional plain-text trailer;
  structured feedback in v2 challenge and successful settlement extensions.
- Public feedback form, GET rating, POST rating with note, cached summary.
  Only these explicit rating/summary routes await the Forge backend. The backend
  owns rating idempotency and the correction window.
- OpenAPI 3.0–3.2 / Swagger 2.0 JSON enrichment. Shared schema components remain
  untouched. Composed schemas are conservatively header-only; JSON/YAML that
  cannot be safely enriched is served unchanged.
- Bounded event queue (1,000), batches (100), 5-second delivery timeout, retry
  after failure, diagnostics and a bounded shutdown flush. Best-effort delivery,
  not durable storage; separate queues per worker. Requires an asyncio event loop.

## Options

All option names use Python `snake_case`; pass keyword arguments to `Forge` or a
`ForgeOptions` dataclass. The Python beta deliberately supports the core options,
not TypeScript's custom tone/wording overrides.

| Option | Default | Behavior |
| --- | --- | --- |
| `api_key`, `backend_url`, `public_url` | required | Merchant credentials, collector and public service URL. Never infer URLs from Host. Use the `/api/sdk/v2` collector URL; an origin-only backend URL selects `/v1`. |
| `feedback` | `True` | Feedback IDs, prompts and routes. False retains telemetry with an internal interaction UUID. |
| `base_path` | `/feedback` | Free feedback routes; must not collide with merchant routes. |
| `agent_context` | `True` | Require context on paid requests. `AgentContextOptions(search_query=False)` requires only the agent name; `False` disables context. Legacy `required=False` is ignored. |
| `describe_challenges`, `challenge_extension`, `receipt_extension` | `True` | Feedback additions on x402 challenges/receipts. |
| `inject_body`, `rate_hint` | `True` | Paid JSON object fields and rating sentence. |
| `inject_text` | `False` | Opt-in trailer on paid `text/plain` responses. |
| `openapi` | `True` | Boolean or `OpenAPIOptions(paths=("/openapi.json",), document=None, is_paid_operation=None)`. Document can be a dict or sync/async provider. |
| `ttl_ms` | `86400000` | Must match backend feedback TTL. |
| `flush_interval_ms` | `2000` | Background event flush interval. |
| `strict` | `False` | Raise on invalid options in CI; otherwise disable Forge and leave the API unchanged. |
| `on_error` | logging warning | Optional error callback; callback failures are isolated. |

`Forge(..., http_client=client)` accepts a caller-owned `httpx.AsyncClient` for
custom transports/testing. The caller closes an injected client.

```python
from forgeintel import AgentContextOptions, OpenAPIOptions

forge = Forge(
    api_key="ffk_...",
    backend_url="https://api.forgeintel.co/api/sdk/v2",
    public_url="https://api.example.com",
    agent_context=AgentContextOptions(search_query=True),
    # FastAPI doesn't infer 402 responses from payment middleware. Declare them
    # on paid routes, or explicitly select the paid operations here.
    openapi=OpenAPIOptions(is_paid_operation=lambda method, path, op: path == "/weather"),
)
```

Required context rejects payment-bearing requests with HTTP 400 **before** x402
processing. It requires a listed `agent_type` and `search_query` (`direct` when no
search was used), unless `search_query=False`. Initial unpaid challenges,
inspection and feedback routes remain available. This mode is opt-in.

JSON requests are read up to 1 MiB. Optional mode replays oversized, compressed or
invalid bodies unchanged; use query parameters for those bodies. Required mode
rejects unreadable JSON. Binary uploads are never buffered by Forge. Agent context
is still stripped when collection is disabled, where the body can be inspected.

JSON responses are rewritten only when bounded to 1 MiB (5 MiB for OpenAPI).
Unknown-length multi-chunk streams, SSE, compressed bodies, partial responses and
binary files remain unchanged; successful paid responses still receive the ID
header. Forge removes stale validators when it changes bytes and preserves repeated
headers such as `Set-Cookie`. No feedback is injected into redirects or errors.

For strict response schemas, provide `OpenAPIOptions(document=app.openapi)` or a
static document **and call `forge.enrich_openapi(app.openapi())` before serving
traffic**. Otherwise schema-aware body injection starts after the first spec fetch.
For a mounted app or reverse-proxy `root_path` such as `/api`, configure public paths: `base_path="/api/feedback"` and `OpenAPIOptions(paths=("/api/openapi.json",), ...)`.
Place Forge inside response compression and outside payment middleware. Framework
exceptions propagate normally; Forge never retries a merchant handler.

## Development

```sh
python -m venv packages/sdk-python/.venv
packages/sdk-python/.venv/bin/pip install -e './packages/sdk-python[test]'
packages/sdk-python/.venv/bin/python -m pytest packages/sdk-python/tests
packages/sdk-python/.venv/bin/python -m build packages/sdk-python
```

Tests include the real `x402==2.5.0` FastAPI middleware and client, with a fake
facilitator (no funds spent), ASGI streaming/body limits and shared TypeScript ID
vectors. Live merchant rollout and PyPI publication are separate release steps.
MPP and Flask/WSGI are deferred.
