citrate-sdk-python
Part of the Citrate Network — own the means of computation. · Docs · Run a node · Contribute → free membership
The Python SDK and
citrateCLI for the Citrate Network (chain 40204) — connect to the chain, deploy/run models, encrypt inference inputs, and call the inference gateway.
What it is
citrate-labs-sdk is the Python client for the Citrate distributed-AI network: a JSON-RPC
CitrateClient, managers for compute/learning/staking/treasury/farming, an AES-GCM/HKDF
crypto envelope for encrypted inference, an OpenAI-compatible GatewayClient, and a
citrate command-line tool. Chain id 40204 is bound from a vendored federation-contract
artifact and enforced at signing time (a hostile RPC cannot make you sign for another
chain). It mirrors the canonical TypeScript SDK (@citratelabs/sdk) and may lag it.
Tokenomics are not yet determined; see docs.citrate.ai.
This release carries the r1006 address book (citrate-chain
contracts/addresses/r1006/40204.json, genesis block 0xa3651a27…6cdd4). Two networks can share
chain id 40204 (r1005 and r1006 do), so before the client or a manager sends a transaction, and
before verify_wallet_address_on_chain trusts the factory, the SDK reads block 0 and raises
WrongNetworkError (error_code="WRONG_NETWORK") unless its hash is that genesis.
client.verify_network() runs the check up front. LiquidStakingPool is not in r1006 block 0,
so StakingManager has no default address.
See the concepts in the docs: https://docs.citrate.ai. Depends on a running chain node (citrate-chain) and, for inference, the gateway (citrate-inference-gateway).
Prerequisites
python3 --version # >= 3.10 (3.10/3.11/3.12 supported)
python3 -m pip --version
# Optional: uv (a uv.lock is committed for reproducible installs)
# pipx install uv # or: curl -LsSf https://astral.sh/uv/install.sh | sh
# Optional, only for "Connect it locally": a local Citrate devnet node on :8545
Build from source
git clone https://github.com/CitrateNetwork/citrate-sdk-python.git
cd citrate-sdk-python
# pip (editable install with dev extras)
python3 -m venv .venv && source .venv/bin/activate
pip install -e '.[dev]'
# --- or with uv (uses the committed uv.lock) ---
# uv sync
pytest # run the test suite
citrate contract # smoke-test the CLI: prints the federation contract table
pyproject.toml is the single source of truth for dependencies (requests, cryptography,
eth-account, web3, numpy). The install exposes the citrate console script.
Run locally
This is a library plus a CLI. Install it into your environment, then either import it or use the CLI:
pip install citrate-labs-sdk # from PyPI (published as citrate-labs-sdk)
citrate --help # subcommands: contract, wallet, entitlement, gateway
30-second Quickstart (against the public testnet, chain 40204):
import os
from citrate_sdk import CitrateClient
client = CitrateClient(
rpc_url="https://rpc.citrate.ai", # testnet default (chain 40204)
private_key=os.getenv("CITRATE_PRIVATE_KEY"), # optional; required to sign
)
print("chain id:", client.get_chain_id()) # 40204
Call the inference gateway (OpenAI-compatible; needs a cgk_ key):
from citrate_sdk.gateway import GatewayClient
gw = GatewayClient(api_key=os.environ["CITRATE_GATEWAY_API_KEY"])
print(gw.chat_completions(
model="gemma-4-E4B-it-Q4_K_M",
messages=[{"role": "user", "content": "Say hi from Citrate"}],
))
Runnable examples live in examples/ (basic_usage.py, encrypted_inference.py,
marketplace_demo.py). Verify it's up: get_chain_id() returning 40204 confirms the RPC.
deploy_model never places key shares in deployment metadata; its key-share check on
caller-supplied metadata is a safety net against accidental inclusion, not a guarantee
against deliberately re-encoded data.
Security: the client warns/fails on a remote plaintext
http://RPC (keys and signed transactions would go out in cleartext). Loopbackhttp://is always allowed; passallow_insecure_http=Truefor a trusted TLS-less internal host.
Connect it locally ← the differentiator
Point the SDK at a local Citrate stack on one machine instead of the public testnet.
- Local chain — run a Citrate devnet node (chain 40204) from
citrate-chain and deploy its contract
book. It exposes JSON-RPC on
http://localhost:8545. - Point the SDK at it (loopback needs no opt-in):
from citrate_sdk import CitrateClient client = CitrateClient( rpc_url="http://localhost:8545", private_key="0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", # anvil acct #0 expected_genesis_hash=None, # a local devnet is not the r1006 genesis ) assert client.get_chain_id() == 40204
The client refuses to sign if the RPC'seth_chainIddoes not match the expected 40204; passchain_id=to override for a custom local chain. A local devnet has its own genesis, so also passexpected_genesis_hash=None(or your devnet's block-0 hash) when it uses chain id 40204. - Local inference gateway (optional) — run
citrate-inference-gateway
and point the gateway client at it:
gw = GatewayClient(api_key="cgk_...", base_url="http://localhost:8080")
- End-to-end check — run the basic example against your local node:
CITRATE_RPC_URL=http://localhost:8545 python examples/basic_usage.py
For the full multi-repo bring-up (chain → identity → bundler → gateway → SDKs), see the LOCAL_STACK guide at https://docs.citrate.ai.
Verify contracts on CitrateScan
citrate_sdk.verify submits Solidity source to CitrateScan, the explorer for chain 40204, through
its Etherscan-compatible API (https://explorer.citrate.ai/api). It POSTs verifysourcecode,
polls checkverifystatus, and returns when the explorer has recompiled the source and matched it
to the code on chain. Input is a forge or hardhat build-info, a hardhat contract artifact, or a
solc standard JSON input. Only the sources the contract's metadata lists are sent, as forge and
hardhat-verify do.
import os
from citrate_sdk import CitrateClient, deploy_and_verify, load_build_info, verify_contract
# forge: `forge build --build-info` writes out/build-info/<id>.json with the compiler input.
build_info, _ = load_build_info("out/build-info/<id>.json")
result = verify_contract(
chain_id=40204, # optional: sent as Etherscan v2 `chainid`; omit it for a local devnet explorer
address="0xYourContractAddress",
build_info=build_info,
contract="src/MyContract.sol:MyContract", # or "MyContract" when the name is unique
constructor_args="0x...", # optional: the explorer reads them from the creation transaction
)
print(result.status) # "verified" or "already_verified"
# hardhat: pass the contract artifact; load_build_info follows its .dbg.json.
bi, contract = load_build_info("artifacts/contracts/MyContract.sol/MyContract.json")
verify_contract(chain_id=40204, address="0xYourContractAddress", build_info=bi, contract=contract)
# Deploy from the build-info with the client's key (same genesis/chain-id checks as every
# transaction), then verify with the creation transaction hash.
client = CitrateClient(rpc_url="https://rpc.citrate.ai", private_key=os.environ["CITRATE_PRIVATE_KEY"])
deployed = deploy_and_verify(
client=client, chain_id=40204, build_info=build_info,
contract="src/MyContract.sol:MyContract", args=[7, "hello"],
)
print(deployed.address, deployed.tx_hash, deployed.verification.status)
Keywords:
chain_id: optional. When given, it is sent as Etherscan v2chainidon the submission and on every status call. When omitted, nochainidis sent, so an explorer for a local devnet with another chain id works. This matchescitrate contract verify --chain-id.explorer_url(defaulthttps://explorer.citrate.ai/api),api_key(sent asapikey; not required, and never included in error messages),compiler_version(otherwise read from the contract metadata),creation_tx_hash.wait=False: return the guid without polling.poll_interval(3 s) andtimeout(300 s) for the status polling. A transient error while polling (connection reset, request timeout, HTTP 5xx or 429) does not end the wait: the poll is retried with a backoff that doubles frompoll_intervalup totransient_backoff_max(30 s), withintimeout. A 4xx other than 429, or aFailverdict, ends it.poll_intervalis at leastMIN_POLL_INTERVAL(5 ms), so 0 does not retry in a tight loop.rate_limit_retries(3) andrate_limit_backoff(5 s) forMax rate limit reached. The explorer takes one submission per 5 s per IP, so while it has not indexed a just-deployed contract,deploy_and_verifyresubmits at most everymax(poll_interval, rate_limit_backoff)seconds, for up toindex_timeout(120 s).
check_verify_status(guid) and wait_for_verification(guid) poll a job you submitted earlier;
pass the same chain_id (or none) as the submission. CitrateClient.deploy_contract waits up to
receipt_timeout (120 s) for the receipt and raises CitrateError with code DEPLOY_FAILED
when the receipt has status 0.
Errors subclass VerificationError (a CitrateError):
BuildInfoError: bad input, nothing sent. A plainforge buildwithout--build-infogets this error, with the fix in the message.VerificationFailedError:Fail - Unable to verify.VerificationRejectedError: the explorer's refusal string.VerificationRateLimitedError: still rate limited after retries.VerificationTimeoutError: no verdict yet. The job may still finish.ExplorerResponseError: the explorer could not be reached or gave an unexpected answer.transientis true for a transport failure, a timeout, HTTP 5xx and 429.
The explorer accepts at most 64 files and 2 MiB, inline content only.
forge and hardhat can verify against the same API directly. Two details matter on chain 40204:
- forge: use
--verifier custom --verifier-url https://explorer.citrate.ai/api --verifier-api-key citrate. With forge 1.5.1,--verifier etherscanstops before sending anything ("No known Etherscan API URL for chain40204").--verifier-api-keymust be present, and any value works. - hardhat-verify: give
etherscan.apiKeyas a per-network object ({ citrate: "citrate" }) with acustomChainsentry for chainId 40204 (apiURL: "https://explorer.citrate.ai/api"). With hardhat-verify 2.1.3 a single stringapiKeyswitches it to Etherscan's v2 mode, which ignorescustomChainsand sends the request to api.etherscan.io.
Configuration
| Env var | Default | Purpose |
|---|---|---|
CITRATE_RPC_URL |
http://localhost:8545 |
chain JSON-RPC endpoint |
CITRATE_PRIVATE_KEY |
— | signer key (examples generate an ephemeral one if unset) |
CITRATE_GATEWAY_API_KEY |
— | cgk_ bearer key for the inference gateway |
CitrateClient(rpc_url, private_key=None, timeout=..., allow_insecure_http=False, chain_id=None, expected_genesis_hash=<vendored>). The expected chain id (40204), genesis hash (r1006) and
gateway base URL come from the vendored federation artifact; citrate contract prints the full
table. The managers take the same chain_id= / expected_genesis_hash= keywords.
Links
- Docs: https://docs.citrate.ai
- Depends on: citrate-chain · citrate-inference-gateway · citrate-identity
- Parity with: citrate-sdk-js (canonical SDK)
- Contributing (DCO):
CONTRIBUTING.md· Security:SECURITY.md· License:LICENSE
License
Licensed under the Apache License, Version 2.0 (see LICENSE). This is the open-source infrastructure tier of Citrate's open-core model. The commercial application layer is source-available under BUSL-1.1. Licensor: Citrate Inc.
Metadata
Release files for citrate-labs-sdk 0.8.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| citrate_labs_sdk-0.8.0.tar.gz | 132.7 kB | With provenance | Details for citrate_labs_sdk-0.8.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| citrate_labs_sdk-0.8.0-py3-none-any.whl | Python 3 | none | any | Details for citrate_labs_sdk-0.8.0-py3-none-any.whl |
Total release size: 266.9 kB
Release files / citrate_labs_sdk-0.8.0.tar.gz
| Download URL | citrate_labs_sdk-0.8.0.tar.gz |
|---|---|
| Size | 132.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e6191acb43c207d12c25f8e3d46e107ff4729c1e3c142e5293c1472622bba9fb
|
|
BLAKE2b-256 checksum How to use checksums |
f56241ff6f6723e552cea58814b2a4aaac4b612c6239ff6929b166457ee495dd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 9, 2026.
Transparency logRelease files / citrate_labs_sdk-0.8.0-py3-none-any.whl
| Download URL | citrate_labs_sdk-0.8.0-py3-none-any.whl |
|---|---|
| Size | 134.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
eeb09b0b86630f7c63dc6ec83b615e3177fd3f35cc10227e1791abe3760fd61e
|
|
BLAKE2b-256 checksum How to use checksums |
a35b15acf11a5b3427148402f90a810653d387cc5216a054d20fa1e57da9956a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 9, 2026.
Transparency logSecurity for citrate-labs-sdk 0.8.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for citrate_labs_sdk-0.8.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 9, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.