Metadata-Version: 2.4
Name: biznet
Version: 0.1.1
Summary: Python SDK for the BizNetAI merchant API
Author: ConsumerGenie Corporation
License: Proprietary
Requires-Python: >=3.10
Requires-Dist: click>=8.0
Requires-Dist: httpx>=0.27
Requires-Dist: pydantic>=2.0
Provides-Extra: dev
Requires-Dist: fastapi>=0.110; extra == 'dev'
Requires-Dist: mcp>=1.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Requires-Dist: respx>=0.21; extra == 'dev'
Provides-Extra: mcp
Requires-Dist: mcp>=1.0; extra == 'mcp'
Description-Content-Type: text/markdown

# biznet

Python SDK for the BizNetAI merchant API. Part of ConsumerGenie Corporation.

## Install

```bash
pip install biznet
```

## Usage

```python
from biznet import BizNet

client = BizNet(api_key="sk_live_...", base_url="https://<your-biznet-api-host>")
```

Both values fall back to environment variables: `BIZNET_API_KEY` and `BIZNET_BASE_URL`.

An async client is available as `AsyncBizNet` with the same constructor.

Resources available now:

```python
client.users.me()          # GET  /v1/users/me
client.users.status()      # GET  /v1/users/me/status
client.users.deactivate()  # DELETE /v1/users/me

client.keys.issue(env="test", scopes=[])  # POST /v1/keys, returns the secret once
client.keys.list()                        # GET  /v1/keys
client.keys.revoke(key_id)                # DELETE /v1/keys/{key_id}

client.products.create(title, description, price)  # POST /v1/products -> Product
client.products.get(product_id)                    # GET  /v1/products/{id} -> Product
client.products.delete(product_id)                 # DELETE, soft delete
job_id = client.products.bulk_create([{...}])      # POST /v1/products/bulk

client.jobs.status(job_id)                # GET /v1/jobs/{id} -> Job
client.jobs.wait(job_id, timeout=300)     # poll with backoff until done/failed

client.adapters.connect("woocommerce", store_url=..., consumer_key=..., consumer_secret=...)
job_id = client.adapters.sync("woocommerce")       # POST .../sync
client.adapters.sync_status("woocommerce", job_id) # GET .../sync/status -> Job

client.policies.upload("returns.md")      # multipart; path or (name, bytes)
client.policies.list()                    # GET /v1/rag/policies
client.policies.delete("returns.md")      # DELETE by filename
```

Chat, UCP, and deploy live on the Merchant-node service, a separate
host from the main API. Set `node_url` on the client or `BIZNET_NODE_URL`:

```python
client.chat.complete(session_id, message)        # POST {node}/chat-sync
async for event in aclient.chat.stream(session_id, message):
    ...                                          # typed SSE events

client.ucp.configure(slug, business_name, stripe_account_id)
client.ucp.status()

client.deploy.shared()      # provision on shared infra (default)
client.deploy.dedicated()   # dedicated per-merchant services
client.deploy.status()
client.deploy.deprovision()
```

`biznet.resources.deploy.create_ucp_router(api_key)` returns a FastAPI router
serving `GET /.well-known/ucp` on a merchant's own domain (requires fastapi).

## MerchantNode

Run an MCP server on your own infra. Override only what your system supports;
the 8 operational tools ship with mock defaults, and the 4 UCP checkout tools
call the BizNet UCP service (set `BIZNET_UCP_URL`). Requires the mcp extra:
`pip install "biznet[mcp]"`.

```python
from biznet import MerchantNode

class MyStore(MerchantNode):
    async def get_stock_level(self, product_id: str) -> int:
        return my_inventory.count(product_id)

MyStore(api_key="sk_live_...").run()  # stdio; or run(transport="sse")
```

## CLI

`biznetai deploy` finds the MerchantNode subclass in the current directory,
packages it with requirements.txt, and provisions it on BizNet infrastructure:

```bash
export BIZNET_API_KEY=sk_live_...
export BIZNET_NODE_URL=https://<merchant-node-host>
biznetai deploy                 # shared infra (default)
biznetai deploy --tier dedicated
```

It streams provisioning status and prints the service URLs when live.

## Behavior

- Every request carries `X-API-Key`. Set `client.api_key` to rotate the key in place.
- Every request carries an `X-Request-Id` (uuid4). The same id is reused across
  retries and echoed back by the server for tracing.
- 429 and 5xx responses retry up to 3 attempts with jittered exponential backoff.
- Non-2xx responses raise typed exceptions mapped from the API error envelope
  `{code, message, request_id}`: `AuthError`, `NotFoundError`, `ValidationError`,
  `RateLimitError`, `InternalError`.
- SSE chat streams parse into typed `StreamEvent`s: `text`, `products`, `checkout`,
  `tool_call`, `error`, `done`.

## Development

```bash
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pytest
```
