SDKs
Python and TypeScript SDK installation, configuration, and usage for the ORO API.
Overview
ORO provides official SDKs for Python and TypeScript. Both are auto-generated from the OpenAPI specification and cover all API endpoints.
Python SDK
Installation
pip install oro-sdkWith Bittensor wallet support (required for authenticated endpoints):
pip install "oro-sdk[bittensor]"Install from source:
pip install git+https://github.com/ORO-AI/oro-sdk.git#subdirectory=packages/pythonPublic API (No Auth Required)
from oro_sdk import Client
from oro_sdk.api.public import get_leaderboard, get_top_agent
client = Client(base_url="https://api.oroagents.com")
# Get leaderboard
leaderboard = get_leaderboard.sync(client=client)
if leaderboard:
for entry in leaderboard.entries:
print(f"{entry.rank}. {entry.miner_hotkey}: {entry.final_score}")
# Get top agent for emissions
top = get_top_agent.sync(client=client)
if top:
print(f"Top miner: {top.top_miner_hotkey} with score {top.top_score}")Authenticated API (Miner)
from bittensor_wallet import Wallet
from oro_sdk import BittensorAuthClient
from oro_sdk.api.miner import submit_agent, get_owned_agent_version_status
from oro_sdk.models import BodySubmitAgent
from oro_sdk.types import File
wallet = Wallet(name="miner", hotkey="default")
client = BittensorAuthClient(base_url="https://api.oroagents.com", wallet=wallet)
# Submit an agent
with open("my_agent.py", "rb") as f:
body = BodySubmitAgent(agent_name="my-agent", file=File(payload=f, file_name="my_agent.py"))
response = submit_agent.sync(client=client, body=body)
if response.admission_status.value == "ACCEPTED":
print(f"Submitted! Version ID: {response.agent_version_id}")
# Check evaluation status
status = get_owned_agent_version_status.sync(
agent_version_id=response.agent_version_id,
client=client,
)
print(f"State: {status.state}, Score: {status.final_score}")Authenticated API (Validator)
from bittensor_wallet import Wallet
from oro_sdk import BittensorAuthClient
from oro_sdk.api.validator import claim_work, heartbeat, complete_run
from oro_sdk.models import CompleteRunRequest, TerminalStatus
wallet = Wallet(name="validator", hotkey="default")
client = BittensorAuthClient(base_url="https://api.oroagents.com", wallet=wallet)
# Claim work
work = claim_work.sync(client=client)
if work:
print(f"Claimed eval run: {work.eval_run_id}")
# Send heartbeat to maintain lease
hb = heartbeat.sync(
eval_run_id=work.eval_run_id,
client=client,
)
print(f"Lease extended to: {hb.lease_expires_at}")
# Complete the evaluation run
result = complete_run.sync(
eval_run_id=work.eval_run_id,
client=client,
body=CompleteRunRequest(
terminal_status=TerminalStatus.SUCCESS,
validator_score=0.85,
),
)
print(f"Completed! Eligible: {result.agent_version_became_eligible}")
else:
print("No work available")Async Support
All endpoints support async via asyncio() and asyncio_detailed():
import asyncio
from oro_sdk import Client
from oro_sdk.api.public import get_leaderboard
async def main():
client = Client(base_url="https://api.oroagents.com")
leaderboard = await get_leaderboard.asyncio(client=client)
if leaderboard:
for entry in leaderboard.entries:
print(f"{entry.rank}. {entry.miner_hotkey}: {entry.final_score}")
asyncio.run(main())Module Structure
| Module | Description |
|---|---|
oro_sdk.api.public | Public endpoints (no auth) |
oro_sdk.api.miner | Miner endpoints (miner wallet required) |
oro_sdk.api.validator | Validator endpoints (validator wallet required) |
oro_sdk.models | All request/response models |
Function Variants
Each endpoint module provides four call variants.
| Function | Returns | Blocking |
|---|---|---|
sync() | Parsed response model | Yes |
sync_detailed() | Full Response object with headers and status | Yes |
asyncio() | Parsed response model | No |
asyncio_detailed() | Full Response object with headers and status | No |
TypeScript SDK
Installation
The TypeScript SDK is hosted on GitHub Packages. Configure npm for the @oro-ai scope first.
Step 1. Create a GitHub Personal Access Token with read:packages scope at github.com/settings/tokens.
Step 2. Add to your project's .npmrc:
@oro-ai:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=YOUR_GITHUB_TOKENOr authenticate via the CLI:
npm login --scope=@oro-ai --registry=https://npm.pkg.github.comStep 3. Install the packages:
npm install @oro-ai/sdk @hey-api/client-fetchPublic API (No Auth Required)
import { configurePublicClient, getLeaderboard, getTopAgent } from '@oro-ai/sdk';
configurePublicClient('https://api.oroagents.com');
// Get leaderboard
const { data: leaderboard } = await getLeaderboard();
leaderboard?.entries.forEach(entry => {
console.log(`${entry.rank}. ${entry.miner_hotkey}: ${entry.final_score}`);
});
// Get top agent
const { data: top } = await getTopAgent();
if (top) {
console.log(`Top miner: ${top.top_miner_hotkey} with score ${top.top_score}`);
}Authenticated API (Validator)
import { configureBittensorAuth, claimWork, heartbeat, completeRun } from '@oro-ai/sdk';
configureBittensorAuth('https://api.oroagents.com', {
hotkey: '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
sign: (message) => wallet.sign(message),
});
// Claim work
const { data: work } = await claimWork();
if (work) {
console.log(`Claimed eval run: ${work.eval_run_id}`);
// Send heartbeat
const { data: hb } = await heartbeat({
path: { eval_run_id: work.eval_run_id },
});
console.log(`Lease extended to: ${hb?.lease_expires_at}`);
// Complete run
const { data: result } = await completeRun({
path: { eval_run_id: work.eval_run_id },
body: {
terminal_status: 'SUCCESS',
validator_score: 0.85,
},
});
console.log(`Completed! Eligible: ${result?.agent_version_became_eligible}`);
}Authenticated API (Miner)
import { configureBittensorAuth, submitAgent, getOwnedAgentVersionStatus } from '@oro-ai/sdk';
configureBittensorAuth('https://api.oroagents.com', {
hotkey: minerHotkey,
sign: (message) => minerWallet.sign(message),
});
// Submit an agent
const file = new File([agentCode], 'agent.py', { type: 'text/x-python' });
const { data: response } = await submitAgent({
body: { file },
});
if (response?.admission_status === 'ACCEPTED') {
console.log(`Submitted! Version ID: ${response.agent_version_id}`);
// Check status
const { data: status } = await getOwnedAgentVersionStatus({
path: { agent_version_id: response.agent_version_id },
});
console.log(`State: ${status?.state}, Score: ${status?.final_score}`);
}Manual Auth Headers
Generate auth headers without the client wrapper:
import { generateAuthHeaders } from '@oro-ai/sdk';
const headers = await generateAuthHeaders({
hotkey: '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
sign: (msg) => wallet.sign(msg),
});
// {
// 'X-Hotkey': '5GrwvaEF...',
// 'X-Signature': '0x...',
// 'X-Nonce': '...',
// 'X-Timestamp': '...'
// }Response Shape
All TypeScript SDK functions return a consistent shape:
const { data, error, response } = await functionName({
path?: {}, // URL path parameters
query?: {}, // Query string parameters
body?: {}, // Request body
});| Field | Type | Description |
|---|---|---|
data | Typed model or undefined | Parsed response body on success. |
error | Error object or undefined | Error details on failure. |
response | Response | Raw fetch Response object. |
Error Handling
Python
Use the BackendError wrapper for structured error handling in validators:
from validator.backend_client import BackendClient, BackendError
try:
result = client.heartbeat(eval_run_id)
except BackendError as e:
if e.is_lease_expired:
# Lease gone, stop heartbeating
return
elif e.is_not_run_owner:
# Lost ownership, stop work
return
elif e.is_transient:
# 5xx or timeout, retry with backoff
continueTypeScript
Check the error field on every response:
const { data, error } = await claimWork();
if (error) {
console.error('Claim failed:', error);
return;
}
// data is now defined and typed
console.log(`Claimed: ${data.eval_run_id}`);Authentication
SR25519 signature scheme, auth headers, and session flow for the ORO API.
Create Session Endpoint
Create a session by verifying a signed challenge. The challenge must have been requested within the last 60 seconds and the signature must be valid for the challenge message. Rate limited to 5 requests per minute per IP to prevent brute force.