poh-sdk
Python SDK for the Decentralized Artificial Intelligence network.
Install
pip install poh-sdk
# For transaction signing:
pip install poh-sdk cryptography
Quick start
import asyncio
from poh_sdk import DAIClient
async def main():
async with DAIClient("https://miner.iamai.kg") as dai:
result = await dai.scan("0xabc...")
print(result.result) # True = human, False = bot, None = inconclusive
asyncio.run(main())
Sync usage
Use DAIClient.sync(...) to get a synchronous wrapper exposing the same
methods (no _sync suffix) without await:
from poh_sdk import DAIClient
dai = DAIClient.sync("https://iamai.kg")
result = dai.scan("0xabc...")
balance = dai.get_balance("dai...")
Natural language jobs
Skill jobs always require a fee — pass budget (DAI), wallet_address, and
private_key_pem in AskOptions so the SDK can sign the payment. The node
verifies the signature and debits the fee before it will run the job at all;
it rejects the request outright (no job ever runs) without a valid signed
payment. Pass currency in AskOptions to pay the fee in a stablecoin
(see Stablecoins).
from poh_sdk import AskOptions
async with DAIClient("https://iamai.kg") as dai:
options = AskOptions(budget=0.5, wallet_address="dai...", private_key_pem=my_private_key)
# Submit a question
ref = await dai.submit_job(
"What does vitalik.eth write about on Paragraph?",
options,
)
# Wait for the answer
result = await dai.poll_job_result(ref.job_id)
print(result.output) # skill-specific structured data
print(result.nl_response) # LLM natural-language answer
# One-liner convenience
result = await dai.ask_and_wait("What NFTs does gmoney.eth hold?", options)
Compute jobs (your own model + dataset)
Run inference with a model of your choice, optionally grounded in a Hugging
Face dataset already installed on the node. Like skill jobs, compute jobs
are never free — run_compute always signs a fee payment.
from poh_sdk import ComputeOptions
async with DAIClient("https://iamai.kg") as dai:
ref = await dai.run_compute("Summarize the top 5 rows", ComputeOptions(
model="llama3.1:8b",
dataset="some-org/some-dataset", # optional
budget=0.5, # DAI (or `currency` display units)
wallet_address="dai...",
private_key_pem=my_private_key,
))
result = await dai.poll_job_result(ref.job_id)
print(result.output)
Before either of these will work, the wallet's signing key must be registered
with the node once via register_signing_key() / register_key_pair() — the
node has no way to verify a signature for a key it has never seen.
Estimating a job's fee
Before paying for a job, ask the node what it will cost — the eth_estimateGas of
DAI. Send the same fields you would submit (prompt, attachments, a skill, MCP tools, a
dataset). The node sizes the whole pipeline — attachment text, skill and MCP output, dataset
rows, planner and synthesis calls — and returns the AI tokens it will use, the minimum fee
it accepts, and a recommended budget. It is read-only: nothing runs and nothing is paid.
from poh_sdk import ChatAttachment, EstimateOptions
async with DAIClient("https://iamai.kg") as dai:
est = await dai.estimate(
"Summarize this report and compare it with the latest news",
EstimateOptions(
attachments=[ChatAttachment(name="report.md", content=report_text)], # text is inlined and measured
# skill_id="web_search", mcp=["shop__search"], dataset="some-org/some-dataset",
# currency="aiKGS", max_output_tokens=512, route=False,
),
)
est.fees.minimum.raw # μDAI the node will accept, at minimum
est.fees.recommended.raw # μDAI to escrow — covers the worst case
est.total_tokens # TokenRange(min=…, max=…)
est.breakdown # what each part contributed, and how sure that is
# run_compute takes DAI; estimate returns μDAI
budget = est.fees.recommended.raw / 1e9
What comes back:
| Field | Meaning |
|---|---|
prompt_tokens, output_tokens, skill_compute_tokens, total_tokens |
Each a TokenRange(min, max) |
breakdown |
Every contributor, tagged measured (counted exactly — prompt, attachment text, dataset rows), bounded (capped by the executing code — what a skill fetched, an MCP tool returned) or assumed |
calls |
Each model call the pipeline makes (planner, skill answer, synthesis…) |
fees.minimum (FeeQuote) |
The lowest fee the node accepts — bids below it are rejected (/job floors at a fixed amount, chat at the prompt alone) |
fees.recommended |
Covers the pipeline's worst case, never below the minimum. Escrow this |
route |
Which pipeline would run; predicted: true means it comes from the deterministic router — the live model-planner may choose differently |
outputCap, warnings |
Whether the budget caps output, and anything unusual (images are not billed; job output is capped at 512 tokens) |
Amounts are in raw units of the fee currency (μDAI for DAI; 1 DAI = 1e9 μDAI), so divide by
1e9 for runCompute's budget. For a non-DAI currency the price is quoted off the live P2P
book; if nothing quotes that pair the quote says unavailable instead of inventing a number,
and a DAI figure is returned alongside.
Needs a node newer than 0.4.36 (it adds POST /api/estimate); older nodes answer 404.
estimate is read-only, so — unlike the other POST methods — it works against remote nodes
without a local_base_url.
Wallet / blockchain
async with DAIClient("https://iamai.kg") as dai:
# Balance (μDAI — divide by 1e9 for DAI)
bal = await dai.get_balance("dai...")
print(bal.balance / 1e9, "DAI")
# Nonce
nonce = await dai.get_nonce("dai...")
# Transaction history (balance journal)
history = await dai.get_transaction_history("dai...", limit=50)
for entry in history.entries:
print(entry.tx_hash, entry.delta)
# Raw transaction records involving an address
txs = await dai.get_transactions("dai...")
# Miner info
info = await dai.get_miner_info()
print(info.model, info.reputation)
Signing & transactions
from poh_sdk import (
generate_key_pair,
build_transfer,
sign_transaction,
create_signing_proof,
)
# 1. Generate a keypair — address is derived from the signing public key
private_key_pem, public_key_pem, my_address = generate_key_pair()
# 2. Register the public key with your local node (one-time)
async with DAIClient(
"https://miner.iamai.kg",
local_base_url="http://127.0.0.1:3456",
) as dai:
await dai.register_signing_key(
my_address, public_key_pem, create_signing_proof(my_address, private_key_pem)
)
# 3. Build, sign, and submit a transfer
nonce_resp = await dai.get_nonce(my_address)
tx = build_transfer(my_address, recipient, amount_dai=5.0, nonce=nonce_resp.nonce + 1)
signed = sign_transaction(tx, private_key_pem)
result = await dai.submit_transaction(signed)
print(result.tx_hash)
# One-liner convenience (fetches nonce automatically)
result = await dai.transfer(my_address, recipient, 5.0, private_key_pem)
register_key_pair(KeyPair(...)) does the same registration from a KeyPair,
deriving the proof automatically and also publishing the wallet's X25519
encryption key (see Chat record encryption).
To replace an already-registered key, sign a
create_rotation_proof(address, new_signing_public_key, existing_private_key_pem)
with the current key and pass it as rotation_proof.
Stablecoins (multi-currency)
Five regional stablecoins ride alongside DAI: aiGEL, aiKGS, aiAMD,
aiETB, aiBTN (2 decimals — 1 unit = 100 raw; DAI keeps 9).
# Transfer 12.50 aiGEL — build + sign + submit manually
# (transfer() itself is DAI-only; it has no currency parameter)
tx = build_transfer(from_addr, to, 12.5, nonce + 1, currency="aiGEL")
signed = sign_transaction(tx, private_key_pem)
await dai.submit_transaction(signed)
# Pay a compute job in aiKGS — the miner receives exactly aiKGS
ref = await dai.run_compute("Summarize…", ComputeOptions(
model="qwen3-1.7b", budget=5.0, currency="aiKGS",
wallet_address=addr, private_key_pem=key,
))
DAI transactions/job payments hash exactly as before (currency enters the
signed preimage only when non-DAI) — existing integrations are unaffected.
Chat record encryption
Public-job chat records (promptCipher / replyCipher) are sealed to the
requester wallet's X25519 key, derived deterministically from its Ed25519
signing key. register_key_pair() publishes the encryption key automatically.
from poh_sdk import derive_encryption_keypair, is_envelope, unseal
keys = derive_encryption_keypair(private_key_pem)
# keys["publicKeyB64"], keys["privateScalarB64"]
if is_envelope(record["promptCipher"]):
prompt = unseal(record["promptCipher"], keys["privateScalarB64"])
Bulk scans
async with DAIClient("https://iamai.kg") as dai:
job = await dai.scan_bulk(["0xaaa", "0xbbb", "0xccc"])
# Stream progress
async for snap in dai.watch_job(job.job_id):
print(f"{snap.percent:.0f}% done")
# Or wait in one call
final = await dai.scan_and_wait(["0xaaa", "0xbbb"])
Multi-node
dai = DAIClient(nodes=[
"https://miner.iamai.kg",
"https://iamai.kg",
"https://miner.iamai.kg",
])
# Automatically picks the fastest responding node
API reference
Scanning
| Method | Description |
|---|---|
scan(input, opts?) |
Single-address scan |
scan_bulk(inputs, opts?) |
Submit bulk scan job |
get_job(job_id) |
Current snapshot of a bulk scan job |
poll_job(job_id, opts?) |
Poll until job completes |
watch_job(job_id, opts?) |
Async generator of job snapshots |
scan_and_wait(inputs, opts?) |
Bulk + poll in one call |
get_brain_verdict(brain_key) |
AI verdict |
poll_brain_verdict(brain_key, opts?) |
Poll until verdict resolves |
scan_and_verdict(input, scan_opts?, brain_opts?) |
Scan + verdict in one call |
Signal methods
| Method | Description |
|---|---|
get_methods(wallet_address?) |
List signal verification methods, ordered by vote score |
get_method(method_id) |
Fetch a single signal method by ID |
Natural language jobs
| Method | Description |
|---|---|
submit_job(question, options?) |
Submit NL question (AskOptions). Skill jobs always require a fee — pass budget, wallet_address, private_key_pem; optional currency (stablecoin fee). |
run_compute(prompt, options) |
Submit a job that runs a specific model (and optional dataset); ComputeOptions (optional currency, job_id). Always requires a fee. |
estimate(prompt, options?) |
Estimate a job's or chat's fee before paying (EstimateOptions) — tokens, minimum fee, recommended budget. Read-only; works on remote nodes. |
get_job_status(job_id) |
Poll status |
get_job_result(job_id) |
Fetch result |
poll_job_result(job_id, opts?) |
Poll until result ready |
ask_and_wait(question, ask_options?, poll_options?) |
Submit + wait |
Wallet / blockchain
| Method | Description |
|---|---|
get_balance(address) |
Balance in μDAI |
get_nonce(address) |
Account nonce (+ pending_nonce when mempool txs reserve higher) |
get_transaction_history(address, limit) |
Balance journal history |
get_transactions(address) |
Raw transaction records involving an address |
get_pending_transactions() |
Mempool pending txs |
submit_transaction(tx) |
Submit signed tx |
register_signing_key(addr, pub_key_pem, proof, rotation_proof?, encryption_public_key?) |
Register signing key (+ optional X25519 encryption key) |
register_key_pair(key_pair, rotation_proof?) |
Register a KeyPair; auto-derives proof + encryption key |
transfer(from, to, amount_dai, private_key_pem, fee?, memo?) |
Full transfer (DAI only — use build_transfer(currency=...) for stablecoins) |
Signing utilities
| Function | Description |
|---|---|
generate_key_pair() |
Fresh Ed25519 keypair → (private_pem, public_pem, address) |
derive_address_from_signing_key(public_key_pem) |
Canonical dai… address for a signing public key |
sign_data(message, private_key_pem) |
Sign arbitrary data → base64 |
create_signing_proof(address, private_key_pem) |
Proof for key registration |
create_rotation_proof(address, new_signing_public_key, existing_private_key_pem) |
Proof for replacing a registered key |
build_transfer(from, to, amount_dai, nonce, fee?, memo?, currency?) |
Build unsigned tx (amount in display units of currency) |
sign_transaction(tx, private_key_pem) |
Sign a DAITxData |
compute_tx_hash(...) |
SHA-256 tx hash hex |
compute_job_payment_hash(...) |
Canonical hash for a job fee payment (used internally by submit_job/run_compute) |
sign_job_payment(...) |
Sign a job fee payment proof (used internally by submit_job/run_compute) |
Chat encryption utilities
| Function | Description |
|---|---|
derive_encryption_keypair(stable_secret) |
X25519 keypair dict (publicKeyB64, privateScalarB64) derived from the signing key |
seal(recipient_pub_b64, plaintext) |
Encrypt to a sealed envelope dict |
unseal(envelope, private_scalar_b64) |
Decrypt a sealed envelope |
seal_json(recipient_pub_b64, obj) / unseal_json(envelope, private_scalar_b64) |
JSON convenience wrappers |
is_envelope(x) |
Check whether a value is a sealed envelope |
Node info
| Method | Description |
|---|---|
get_node_info() |
Node metadata |
get_miner_info() |
Miner details |
list_skills() |
Available skills |
Differences from the JS SDK
The Python SDK does not (yet) implement some features present in
@poh_network/sdk:
chat()— no free-form chat endpoint wrappersubmit_feedback()— no job star-ratingget_assets()— no asset-registry / gas-price endpoint wrapperget_balance()returns only the μDAI balance (no stablecoinassetsmap)transfer()has nocurrencyparameter — stablecoin transfers go throughbuild_transfer(currency=...)+submit_transaction()TxSubmitResulthas noidempotentflag- No
pick_strategyoption (multi-node always picks the fastest node)
License
MIT
Release files for poh-sdk 0.7.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 | |
|---|---|---|---|
| poh_sdk-0.7.0.tar.gz | 32.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| poh_sdk-0.7.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 61.0 kB
Release files / poh_sdk-0.7.0.tar.gz
| Download URL | poh_sdk-0.7.0.tar.gz |
|---|---|
| Size | 32.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
10344bb87843e75f7e88201e0bbe5b65fbb11351727918b4d7f12426479ad77d
|
|
BLAKE2b-256 checksum How to use checksums |
37d7c45a1dd100af0ac6a2b1aa14272d749e7a7197824f7dd4143a4caddbdefd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|
Release files / poh_sdk-0.7.0-py3-none-any.whl
| Download URL | poh_sdk-0.7.0-py3-none-any.whl |
|---|---|
| Size | 28.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9b729f1517fe66279a8434f3b1bdc58885d0a3f995db9eb1876a449e567ec14e
|
|
BLAKE2b-256 checksum How to use checksums |
37630a602604042bc8a1af414d8da413c49fa4ea0d4b421850a42c2ef6d8850e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|