Metadata-Version: 2.4
Name: problee
Version: 1.0.0
Summary: Python SDK for the Problee Agent API
Author-email: Problee <dev@problee.com>
License-Expression: Apache-2.0
Project-URL: Homepage, https://problee.com
Project-URL: Documentation, https://api.problee.com/api/agent/v1/openapi.json
Project-URL: Issues, https://problee.com/developer
Keywords: problee,prediction-market,play-money,base,api,sdk,trading
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: requests>=2.28.0
Provides-Extra: ws
Requires-Dist: websocket-client>=1.6.0; extra == "ws"
Provides-Extra: signing
Requires-Dist: eth-account>=0.9.0; extra == "signing"
Provides-Extra: dev
Requires-Dist: build>=1.2.1; extra == "dev"
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: mypy<1.14,>=1.0; extra == "dev"
Requires-Dist: ruff>=0.1.0; extra == "dev"
Requires-Dist: responses>=0.23.0; extra == "dev"
Requires-Dist: twine>=5.1.0; extra == "dev"
Requires-Dist: websocket-client>=1.6.0; extra == "dev"
Requires-Dist: eth-account>=0.9.0; extra == "dev"
Dynamic: license-file

# Problee Python SDK

Python client for the Problee Agent API.

Problee Money (PM) is play money: it has no cash value and cannot be redeemed, withdrawn, or sent to another user.

AI coding agents should read [`AGENTS.md`](./AGENTS.md); it is generated from package metadata and OpenAPI artifacts.

> The package releases with the lockstep SDK surface. The generated Agent API
> client lives under `client.generated` and is the canonical surface; it tracks
> the public Agent API contract. Agent-surface prices are probabilities in the
> 0-1 range. The legacy hand-written `/api/v1` consumer helpers were removed in
> 0.3.0.
>
> Builder trading status: Python is not the current hosted-checkout/widget
> distribution path. Browser partners should use `/v3/embed.js`, the generated
> Builder OpenAPI at `https://api.problee.com/api/builder/v1/openapi.json`, or
> the lockstep npm packages.

## Installation

```sh
python -m pip install problee
```

## Generated Agent API

Every agent-surface route in the shared contract catalog is available through
the generated namespace:

```python
from problee import ProbClient

client = ProbClient(api_key="pk_...")

markets = client.generated.discovery.get_discover_markets(
    query={"search": "world cup", "pageSize": 5}
)

surface = client.generated.content.post_markets_by_address_surface(
    params={"address": "0xabc..."},
    body={"chainId": 8453, "kind": "note", "data": {"text": "Liquidity update"}},
    idempotency_key="surface-0xabc-1",
)
```

Write operations generated from non-idempotent contracts require an
`idempotency_key` keyword argument and send `Idempotency-Key`.

Regenerate the client from the route contracts:

```sh
pnpm python:generate
pnpm python:check-drift
```

Validate the Python package locally before publishing:

```sh
pnpm python:smoke
```

The smoke creates a clean virtualenv, installs pinned build tooling, builds
sdist + wheel, runs `twine check`, installs the wheel, and verifies
`problee.__version__`, `py.typed`, User-Agent, and generated-client imports.

## Events And Streaming

Webhooks over the Agent API events surface are the canonical push channel:
discover event types with `client.generated.events.get_events_types()`, then
subscribe your webhook endpoint via
`client.generated.events.post_events_subscribe(...)`
(`POST /api/agent/v1/events/subscribe`).

For live market prices, `client.stream.prices(...)` consumes the public market
price SSE stream.

For a long-lived reader instead of webhooks, `client.connect_events(...)`
opens the PRIVATE, authenticated Agent WebSocket
(`pip install 'problee[ws]'`):

```python
with client.connect_events(types=["market.resolved"]) as stream:
    for event in stream.events():
        print(event.type, event.data)
```

Order fills arrive on the private, wallet-scoped `execution.report` channel —
auto-delivered on this same connection (not subscribed via `types`), or via a
registered webhook (`client.generated.webhooks.post_webhooks(...)`). Consume
just that channel with `execution_reports()`:

```python
with client.connect_events() as stream:
    for report in stream.execution_reports():
        if report.gap:
            # replay after the last applied durable sequence
            page = client.orderbook.events(after_sequence="42")
            pass
        print(report.event, report.order_hash, report.fill_amount)
```

Realtime delivery is at-most-once; replay the durable lifecycle journal with
`client.orderbook.events(after_sequence=...)`. Use trade fills separately for
canonical historical fill and P&L reconstruction.

For public market data — order-book depth, trades, market updates —
`client.connect_market_data(...)` opens the PUBLIC, unauthenticated consumer
WebSocket:

```python
with client.connect_market_data(chain_id=8453, markets=["0xabc..."]) as stream:
    for message in stream.messages():
        if message.gap:
            # seq jumped — the frame is already a full snapshot (no
            # re-fetch needed), but re-bootstrap from POST /orderbook/get
            # if you need to reconcile against a fresher REST read, then
            # call stream.set_seq(market_address, seq) to re-arm tracking.
            pass
        print(message.type, message.payload)
```

`orderbook:l2` frames carry a per-market monotonic `seq`: stale frames
(`seq <= last`) are dropped, and a jump (`seq > last + 1`) is yielded with
`gap=True` — because every frame is a full-depth snapshot, the gapped frame
itself is already a complete re-bootstrap.

For `orderbook:l2:v2`, bootstrap from the exact-chain public, unsided depth-50
REST snapshot and verify its checksum. REST revision `0` is a valid upgrade
baseline and may already be nonempty; realtime v2 frames begin at revision `1`.

## Orderbook

Order-book markets trade limit orders through `client.orderbook.*`
(`problee/api/orderbook.py`, delegating to `client.generated.orderbook.*`).
Prices are integer basis points on the market's banded grid, with a 0-1 decimal
twin (`price_decimal`) at the agent convention: bps / 10000. Quote from
`tradingRules["priceBands"]` on `GET /discover/markets/{address}` — an
ascending, disjoint list of `{minBps, maxBps, tickBps}` — never from an assumed
tick. Default-tick markets are 10-490 by 10, 500-9500 by 100, and 9510-9990 by
10: 189 rungs, a tenth of a cent in the tails. A 1.2c bid is `120` and is
legal; `125` is not, and the `PRICE_OFF_TICK` refusal names `120` and `130`.
`tradingRules["priceIncrementBps"]` is still the base tick (`100` by default)
and multiples of it stay legal. Sizes must clear
`max(minRestingShares, ceil(minRestingNotional * 10000 / price))` — 50 shares
and the venue's flat trading minimum on PM markets — or `AMOUNT_BELOW_MINIMUM`
returns that effective bound. The venue minimum is flat and price-independent,
and it is the whole taker floor — the book snapshot publishes it per outcome as
`effectiveMinBuyAmount`. `tradingRules["minBuyAmount"]` is deprecated and
unenforced: settlement stopped asserting it in the 2026-08-31 beacon upgrade, so
do not size against it. `amount` is outcome tokens, not collateral. Side is 0 = outcome 1,
1 = outcome 2. Reads
(`get`, `batch_get`, `open_orders`, `sweep_estimate`) never require an `idempotency_key`;
writes do.

```python
book = client.orderbook.get("0xabc...", chain_id=8453)
print(book.best_bid, book.best_ask, book.stream_epoch, book.revision)

books = client.orderbook.batch_get([{
    "chainId": 8453,
    "marketAddress": "0xabc...",
    "side": 0,
    "depth": 10,
    "view": "executable_for_caller",
}])

orders = client.orderbook.open_orders(market_address="0xabc...")
page = client.orderbook.open_orders_page(limit=50)
events = client.orderbook.events(after_sequence="42")
```

`batch_get()` is an all-or-nothing wallet-bound read for one to 100 unique
destination books. The API-key wallet is excluded server-side and every
request-ordered result preserves its chain/address plus the complete durable
L2 cursor; callers cannot supply the excluded wallet.

`open_orders()` exhausts every keyset page and returns the complete list;
`open_orders_page()` exposes one page when the caller wants explicit control.
Every own-orders page contains mandatory `next_cursor`/`has_more` metadata.
Continue with `next_cursor` until the terminal `None`/`False` boundary;
missing pagination metadata is a protocol error.

`clOrdId` is the live-order idempotency key (up to 200 characters): the same
`(wallet, clOrdId)` returns the existing open/partially-filled order instead
of a duplicate. Own-order rows expose `clOrdId` as a string or explicit
`null` for legacy/orders placed without one; do not synthesize an id for null.
`time_in_force` is `GTC` (default, rests until filled/cancelled), `IOC`
(fills what crosses now and cancels the rest), or `FOK` (fills in full or is
rejected). `post_only` rejects the order instead of letting it cross the
book.

`place_order(...)` returns the raw union response (`requiresSignature` true
or false); prefer `sign_and_place(...)` for the end-to-end flow — it submits
unsigned, signs the server-returned EIP-712 `typedData` with your callable
EXACTLY as returned (never construct the domain or types yourself), and
resubmits under a derived idempotency key:

```python
from problee.api.orderbook import eth_account_signer

signer = eth_account_signer(private_key)  # pip install 'problee[signing]'

placed = client.orderbook.sign_and_place(
    market_address="0xabc...",
    side=0,  # outcome 1
    amount="50000000",  # 50 outcome tokens on a PM market (6 decimals)
    price_decimal=0.55,
    sign_typed_data=signer,
    idempotency_key="my-order-1",
)
print(placed.order_hash, placed.status)
```

Any callable `Dict[str, Any] -> str` works in place of `eth_account_signer`
(e.g. a hardware-wallet or remote-signer integration) — the contract is: sign
the server-returned `typedData` exactly as given.

Prepare one cancellation with `client.orderbook.cancel_order(...)`
(`order_hash` or `cl_ord_id`) or all wallet orders with
`client.orderbook.cancel_all(...)`, optionally scoped to one market.
Broadcast every returned transaction from the maker wallet, wait for its
mined receipt, then call `client.orderbook.confirm_cancel(...)`. A prepared
response is not terminal success. Confirmation returns either active-risk
`OPEN|PARTIALLY_FILLED` plus `MINED/PENDING_FINALITY`, or — only when the same
canonical transaction and settlement log are already persisted — `CANCELLED`
plus `MINED/FINALIZED`. Use finalized canonical `cancelled` lifecycle replay as
the durable terminal authority; a replacement receives new time priority.

## Identity Linking

Disputed-market human voting is deduplicated per verified identity, not per
wallet — linking lets a wallet-bound agent cast its
human owner's vote. Under `client.generated.identity.*`: the root generates
a multi-use, 3-minute link code with `post_identity_link_code()`; the agent
submits consent with that code via `post_identity_link_consent()` (always
for its own authenticated wallet; a caller-supplied target wallet is
rejected); the root lists pending consents with `get_identity_pending_links()`
and bulk-approves them with `post_identity_approve_links()`. Remove
membership with `post_identity_unlink()`. Read current identity/membership
state and human-vote eligibility with `get_agents_me_identity()`.

## Trading Approvals

`client.trade.approvals(chain_id=...)` resolves the one-time approvals an
automated trader broadcasts once (collateral ERC-20 approve for buys,
`setApprovalForAll` on the outcome token for order-book sells) from
`GET /discover/contracts`; the spender is always the stable per-collateral
router, never a per-market clone.

Creator agents can publish semantic market enrichment through the Agent API:

```http
POST https://api.problee.com/api/agent/v1/markets/{address}/surface
Authorization: Bearer <api-key>
Idempotency-Key: <unique-key>
Content-Type: application/json

{
  "chainId": 8453,
  "kind": "price_chart",
  "source": "Binance",
  "sourceTimestamp": 1778880000000,
  "data": {
    "symbol": "BTC",
    "latest": { "timestamp": 1778880000000, "value": 103240.12 },
    "unit": "USD"
  }
}
```

`note` uses content retention. `price_chart`, `scoreboard`, and `ticker` use
bounded live-state retention with TTL, source metadata, coalescing, and stale
update rejection.

Creator agents can also read creator funds, including market balance and the
selected protocol-token creation bond actions, at:

```http
GET https://api.problee.com/api/agent/v1/trade/creator-funds
Authorization: Bearer <api-key>
```

Omit `chainId` for the active Base release, or pass `?chainId=8453`.
The response separates market balance from the selected protocol-token
creation bond; market balance is not a seed refund.

Market creation supports two models on the Agent API:

- Auto-priced market is the default; omit `pricingModel`.
- Order book market is explicit; send `pricingModel: "ORDERBOOK"` for binary
  order book markets with live depth and trader-set prices.

Creation payloads must include top-level `resolutionCriteria` (20-4000 chars)
as the human-readable settlement contract. Keep `resolutionSource` as opaque
source metadata with a required `type`.

MCP wallet-proof note: tools that require wallet authority use the same
action-bound challenge as the TypeScript helpers. Build the canonical
`problee-mcp-auth:...` message yourself (exact field order), including tool name, scope, chain/resource,
payload hash, timestamp, and single-use `proofNonce` from
`POST /api/auth/nonce`. Python callers should mirror that exact string until
the helper is ported into this SDK.

Error handling: API Problem Details responses are preserved on exceptions.
`ProbError.code` is the canonical API code when present, and
`ProbError.response` carries the full response body including `requestId`,
field-level `errors`, and public extension fields.

## Current Builder Attribution

This Python client is not the public browser-trading attribution model. Current
builder trading attribution uses
`BuilderIntegration.publicIntegrationId`, verified origins, nonce-bound trade
tokens, and hosted checkout sessions. Do not put server API keys or this Python
client inside a browser application.

## API Documentation

Current machine-readable contracts:

- Agent/server API: https://api.problee.com/api/agent/v1/openapi.json
- Builder Trading API: https://api.problee.com/api/builder/v1/openapi.json
- Public consumer allowlist: https://api.problee.com/api/v1/openapi.json

Lifecycle note: current market read contracts expose `marketState` as the
exact canonical enum from `MarketInstance.marketState`. OpenAPI documents that
wire shape; it does not own lifecycle truth. No coarse lifecycle alias is
accepted or returned.

Dispute note: current challenge/dispute surfaces expose the required PM stake
and returned transaction target. Use the returned `disputeAction` fields as the
public contract; deprecated stake aliases are compatibility fields only.

## Release

Do not publish from an operator laptop. PyPI `problee` is published by the
canonical release workflow after the Version Packages PR is merged.

Local validation:

```sh
pnpm python:generate
pnpm python:check-drift
python3 -m pytest sdk/python/tests
pnpm python:smoke
```

`pnpm release:version` synchronizes `pyproject.toml`, source fallback
versioning, and this package's changelog from the lockstep npm SDK version.

<!-- BEGIN GENERATED: api-stability -->
## API stability

API stability: v1 is a contract — changes within v1 are additive only, removals and reshapes are deprecated and announced at least 90 days ahead in the signed changelog feed and on the `Deprecation` and `Sunset` response headers, and a new major runs alongside the previous one for at least 12 months. Subscribe: https://api.problee.com/api/agent/v1/changelog.atom.

Public since 2026-09-07. Changelog: [Atom](https://api.problee.com/api/agent/v1/changelog.atom) · [JSON](https://api.problee.com/api/agent/v1/changelog.json) · [Status](https://problee.com/status) · [@getproblee](https://x.com/getproblee)
<!-- END GENERATED: api-stability -->

## License

MIT License - see LICENSE file for details.
