| Name | circuit-agent-sdk JSON |
| Version |
1.2.9
JSON |
| download |
| home_page | None |
| Summary | Official Python SDK for building and deploying agents on the Circuit platform |
| upload_time | 2025-10-30 22:26:47 |
| maintainer | None |
| docs_url | None |
| author | None |
| requires_python | >=3.12 |
| license | Proprietary |
| keywords |
agent
circuit
sdk
|
| VCS |
 |
| bugtrack_url |
|
| requirements |
No requirements were recorded.
|
| Travis-CI |
No Travis.
|
| coveralls test coverage |
No coveralls.
|
# Circuit Agent SDK - Python
> **Clean, unified, type-safe Python SDK for building cross-chain agents on Circuit**
A simplified Python SDK for building automated agents to deploy on Circuit. Agents receive a single `AgentContext` object containing everything they need - request data, SDK methods, and unified logging. No boilerplate, no return values to manage, just write your logic.
> **๐ก Best used with [Circuit Agents CLI](https://github.com/circuitorg/agents-cli)** - Deploy, manage, and test your agents with ease
## ๐ Table of Contents
- [Circuit Agent SDK - Python](#circuit-agent-sdk---python)
- [๐ Table of Contents](#-table-of-contents)
- [๐ Quick Start](#-quick-start)
- [Install the SDK](#install-the-sdk)
- [Create Your First Agent](#create-your-first-agent)
- [Required Asset setup](#required-asset-setup)
- [๐ฏ Core Concepts](#-core-concepts)
- [The AgentContext Object](#the-agentcontext-object)
- [Run/Stop Function Requirements](#runstop-function-requirements)
- [๐ Unified Logging with agent.log()](#-unified-logging-with-agentlog)
- [๐พ Session Memory Storage](#-session-memory-storage)
- [๐ Cross-Chain Swaps with Swidge](#-cross-chain-swaps-with-swidge)
- [Get and execute a quote](#get-and-execute-a-quote)
- [๐ Polymarket Prediction Markets](#-polymarket-prediction-markets)
- [Place Market Orders](#place-market-orders)
- [Redeem Positions](#redeem-positions)
- [Hyperliquid Trading](#hyperliquid-trading)
- [Account Information](#account-information)
- [Place Orders](#place-orders)
- [Order Management](#order-management)
- [Transfer Between Accounts](#transfer-between-accounts)
- [๐ Transaction History](#-transaction-history)
- [๐ Sign \& Send Transactions](#-sign--send-transactions)
- [Ethereum (any EVM chain)](#ethereum-any-evm-chain)
- [Solana](#solana)
- [โก Error Handling](#-error-handling)
- [๐ ๏ธ Deployment](#๏ธ-deployment)
- [What You Write](#what-you-write)
- [Deploy to Circuit](#deploy-to-circuit)
- [Test Locally](#test-locally)
- [๐งช Manual Instantiation (Jupyter/Testing)](#-manual-instantiation-jupytertesting)
- [๐ Working Examples](#-working-examples)
- [`demo-agent.py`](#demo-agentpy)
- [`examples/kitchen-sink.ipynb`](#exampleskitchen-sinkipynb)
- [๐ฏ Key Takeaways](#-key-takeaways)
- [๐ Additional Resources](#-additional-resources)
## ๐ Quick Start
### Install the SDK
```bash
pip install circuit-agent-sdk
# or with uv
uv pip install circuit-agent-sdk
```
### Create Your First Agent
Every agent receives a single `AgentContext` object that provides:
**Session Data:**
- `agent.sessionId` - Unique session identifier
- `agent.sessionWalletAddress` - The wallet address for this session
- `agent.currentPositions` - Assets allocated to the agent at the start of this execution
**Available Methods:**
- `agent.log()` - Send messages to users and log locally
- `agent.memory` - Persist data across executions (`.set()`, `.get()`, `.list()`, `.delete()`)
- `agent.platforms.polymarket` - Trade prediction markets (`.market_order()`, `.redeem_positions()`)
- `agent.platforms.hyperliquid` - trading (`.place_order()`, `.positions()`, `.balances()`, `.transfer()`)
- `agent.swidge` - Cross-chain swaps and bridges (`.quote()`, `.execute()`)
- `agent.sign_and_send()` - Execute custom built transactions on any supported chain
- `agent.sign_message()` - Sign messages (EVM only)
- `agent.transactions()` - Get transaction history with asset changes
**Important:** `currentPositions` reflects your allocated assets at the **start** of each execution. You will need to use the agent.get_current_positions() method to pull fresh values after executing transactions. It's important to consider the hasPendingTxs if you had recently submitted transactions. If hasPendingTxs is true, the balances you received may not be 100% up to date.
#### Required Asset setup
> Note: For native tokens, use the following null addresses for solana/ethereum
```toml
// Requiring 1 SOL
[[requiredAssets]]
network = "solana"
address = "11111111111111111111111111111111"
minimumAmount = "1000000000"
// Requiring 1 ETH
[[requiredAssets]]
network = "ethereum:<chainId>"
address = "0x0000000000000000000000000000000000000000"
minimumAmount = "1000000000000000000"
```
```python
from agent_sdk import Agent, AgentContext
import time
def run(agent: AgentContext) -> None:
"""
Main agent logic - receives AgentContext with everything needed.
No return value - errors are caught automatically.
"""
# Access session data
agent.log(f"Starting execution for session {agent.sessionId}")
agent.log(f"Wallet: {agent.sessionWalletAddress}")
agent.log(f"Managing {len(agent.currentPositions)} allocated positions")
# Use SDK methods
agent.memory.set("last_run", str(time.time()))
# Your agent logic here - track position changes within this execution
# Circuit will provide updated positions on the next run
def stop(agent: AgentContext) -> None:
"""Cleanup when agent is stopped."""
agent.log("Cleaning up resources")
agent.memory.delete("temp_data")
# Boilerplate - This should never change unless you want to change the names of your run and stop functions
agent = Agent(
run=run,
stop=stop
)
handler = agent.get_handler()
if __name__ == "__main__":
agent.run()
```
## ๐ฏ Core Concepts
### The AgentContext Object
Every agent function receives a single `AgentContext` object that contains:
**Request Data:**
- `agent.sessionId` - Unique session identifier
- `agent.sessionWalletAddress` - Wallet address for this session
- `agent.currentPositions` - Current positions allocated to this agent
**SDK Methods:**
- `agent.log()` - Unified logging (console + backend)
- `agent.memory` - Session-scoped key-value storage
- `agent.platforms.polymarket` - Prediction market operations
- `agent.swidge` - Cross-chain swap operations
- `agent.sign_and_send()` - Sign and broadcast transactions
- `agent.sign_message()` - Sign messages on EVM
- `agent.transactions()` - Get transaction history with asset changes
### Run/Stop Function Requirements
1. **Signature**: `def my_function(agent: AgentContext) -> None:`
2. **Return**: Always return `None` (or no return statement)
3. **Errors**: You should surface any relevant errors via agent.log('error message',error=True). All errors from built-in sdk functions will be caught gracefully and provided in the return data for you to handle as necessary.
## ๐ Unified Logging with agent.log()
Use `agent.log()` to communicate with your users and debug your agent. Every message appears in your terminal, and by default also shows up in the Circuit UI for your users to see. Simply pass debug=True to skip sending the message to the user.
```python
def run(agent: AgentContext) -> None:
# Standard log: Shows to user in Circuit UI
agent.log("Processing transaction")
# Error log: Shows to user in Circuit UI (as an error)
result = agent.memory.get("key")
if not result.success:
agent.log(result.error_message, error=True)
# Debug log: Only you see this in your terminal
agent.log("Internal state: processing...", debug=True)
# Logging dicts and pydantic models
# Both are pretty-printed to console and serialized/truncated for backend
agent.log({"wallet": agent.sessionWalletAddress, "status": "active"})
# Check if message reached the user
log_result = agent.log("Important message")
if not log_result.success:
# Rare - usually means Circuit UI is unreachable
pass
```
**What Your Users See:**
| Code | You See (Terminal) | User Sees (Circuit UI) |
|------|-------------------|----------------------|
| `agent.log("msg")` | โ
In your terminal | โ
In Circuit UI |
| `agent.log("msg", error=True)` | โ
As error in terminal | โ
As error in Circuit UI |
| `agent.log("msg", debug=True)` | โ
In terminal | โ Hidden from user |
| `agent.log("msg", error=True, debug=True)` | โ
As error in terminal | โ Hidden from user |
## ๐พ Session Memory Storage
Store and retrieve data for your agent's session with simple operations. Memory is **automatically scoped to your session ID**, and for now is simple string storage. You will need to handle serialization of whatever data you want to store here.
```python
def run(agent: AgentContext) -> None:
# Set a value
result = agent.memory.set("lastSwapNetwork", "ethereum:42161")
if not result.success:
agent.log(result.error_message, error=True)
# Get a value
result = agent.memory.get("lastSwapNetwork")
if result.success and result.data:
network = result.data.value
agent.log(f"Using network: {network}")
else:
agent.log("No saved network found")
# List all keys
result = agent.memory.list()
if result.success and result.data:
agent.log(f"Found {result.data.count} keys: {result.data.keys}")
# Delete a key
result = agent.memory.delete("tempData")
```
**All memory operations return responses with `.success` and `.error_message`:**
```python
result = agent.memory.set("key", "value")
if not result.success:
agent.log(f"Failed to save: {result.error_message}", error=True)
```
## ๐ Cross-Chain Swaps with Swidge
Built-in Swidge integration for seamless cross-chain token swaps and bridges.
### Get and execute a quote
> Note: It is important to always validate quotes before executing. Circuit will always do its best to return a quote, and as of now, will only filter out quotes with price impacts exceeding 100% to ensure maximum flexibility. It is on the agent to makes sure a quote is valid, given its own parameters
> **Bulk Execution:** `execute()` accepts both single quotes and lists. Pass a list to execute multiple swaps in parallel: `agent.swidge.execute([quote1.data, quote2.data])` returns a list of results. Note: Use explicit `is not None` checks for proper type narrowing.
```python
def run(agent: AgentContext) -> None:
# 1. Get quote
quote = agent.swidge.quote({
"from": {"network": "ethereum:8453", "address": agent.sessionWalletAddress},
"to": {"network": "ethereum:137", "address": agent.sessionWalletAddress},
"amount": "1000000000000000", # 0.001 ETH
"toToken": "0x2791bca1f2de4661ed88a30c99a7a9449aa84174",
"slippage": "2.0",
"priceImpact": "100.0"
})
if not quote.success:
_ = agent.log(f"Quote failed: {quote.error_message}", error=True)
return
if abs(float(quote.data.priceImpact.percentage)) > 10:
_ = agent.log(f"Warning: Price impact is too high: {quote.data.priceImpact.percentage}%", error=True, debug=True)
else:
_ = agent.log(f"You'll receive: {quote.data.assetReceive.amountFormatted}")
_ = agent.log(f"Fees: {', '.join([f.name for f in quote.data.fees])}")
# 2. Execute the swap
result = agent.swidge.execute(quote.data)
if result.success and result.data:
agent.log(f"Swap status: {result.data.status}")
if result.data.status == "success":
agent.log("โ
Swap completed!")
agent.log(f"In tx: {result.data.in_.txs}")
agent.log(f"Out tx: {result.data.out.txs}")
elif result.data.status == "failure":
agent.log("โ Swap failed", error=True)
else:
agent.log(result.error_message, error=True)
```
## ๐ Polymarket Prediction Markets
Trade prediction markets on Polygon.
### Place Market Orders
> Note: Right now, polymarket's API accepts different decimal precision for buys and sells, this will result in dust positions if you are selling out of a position before expiry. Once expired, dust can be cleaned up during the redeem step.
```python
def run(agent: AgentContext) -> None:
# Buy order - size is USD amount to spend
buy_order = agent.platforms.polymarket.market_order({
"tokenId": "86192057611122246511563653509192966169513312957180910360241289053249649036697",
"size": 3, # Spend $3
"side": "BUY"
})
if buy_order.success and buy_order.data:
agent.log(f"Order ID: {buy_order.data.orderInfo.orderId}")
agent.log(f"Price: ${buy_order.data.orderInfo.priceUsd}")
agent.log(f"Total: ${buy_order.data.orderInfo.totalPriceUsd}")
else:
agent.log(buy_order.error_message, error=True)
# Sell order - size is number of shares to sell
sell_order = agent.platforms.polymarket.market_order({
"tokenId": "86192057611122246511563653509192966169513312957180910360241289053249649036697",
"size": 5.5, # Sell 5.5 shares
"side": "SELL"
})
```
### Redeem Positions
```python
def stop(agent: AgentContext) -> None:
"""Redeem all settled positions."""
redemption = agent.platforms.polymarket.redeem_positions()
if redemption.success and redemption.data:
successful = [r for r in redemption.data if r.success]
agent.log(f"Redeemed {len(successful)} positions")
else:
agent.log(redemption.error_message, error=True)
```
## Hyperliquid Trading
Trade perpetual futures on Hyperliquid DEX with leverage.
### Account Information
```python
def run(agent: AgentContext) -> None:
# Check balances
balances = agent.platforms.hyperliquid.balances()
if balances.success and balances.data:
agent.log(f"Account value: ${balances.data.perp.accountValue}")
agent.log(f"Withdrawable: ${balances.data.perp.withdrawable}")
# View open positions
positions = agent.platforms.hyperliquid.positions()
if positions.success and positions.data:
for pos in positions.data:
agent.log(f"{pos.symbol}: {pos.side} {pos.size} @ {pos.entryPrice}")
agent.log(f"Unrealized PnL: ${pos.unrealizedPnl}")
```
### Place Orders
```python
def run(agent: AgentContext) -> None:
# Market order
order = agent.platforms.hyperliquid.place_order({
"symbol": "BTC-USD",
"side": "buy",
"size": 0.0001,
"price": 110000, # Acts as slippage limit for market orders
"market": "perp",
"type": "market"
})
if order.success and order.data:
agent.log(f"Order {order.data.orderId}: {order.data.status}")
else:
agent.log(order.error, error=True)
```
### Order Management
```python
def run(agent: AgentContext) -> None:
# Get open orders
open_orders = agent.platforms.hyperliquid.open_orders()
# Get specific order details
order = agent.platforms.hyperliquid.order("12345")
# Cancel an order
result = agent.platforms.hyperliquid.delete_order("12345", "BTC-USD")
# View historical orders
history = agent.platforms.hyperliquid.orders()
# Check order fills
fills = agent.platforms.hyperliquid.order_fills()
```
### Transfer Between Accounts
```python
def run(agent: AgentContext) -> None:
# Transfer USDC from spot to perp account
transfer = agent.platforms.hyperliquid.transfer({
"amount": 1000.0,
"toPerp": True
})
if transfer.success:
agent.log("Transfer completed")
```
## ๐ Transaction History
Get a list of asset changes for all confirmed transactions during your session. This is useful for tracking what assets have moved in and out of the agent's wallet.
> **Note:** The system needs to index new transactions, so there may be a slight delay between when you execute a transaction and when the resulting asset changes are returned in this method. Make sure you are taking that into consideration if dealing with assets the agent just transacted with.
```python
def run(agent: AgentContext) -> None:
# Get all transaction history for this session
result = agent.transactions()
if result.success and result.data:
agent.log(f"Found {len(result.data)} asset changes")
# Filter for outgoing transfers
outgoing = [c for c in result.data if c.from_ == agent.sessionWalletAddress]
agent.log(f"Outgoing transfers: {len(outgoing)}")
# Calculate total USD value (where price data is available)
total_usd = sum(
float(c.amount) * float(c.tokenUsdPrice)
for c in result.data
if c.tokenUsdPrice
)
agent.log(f"Total USD value: ${total_usd:.2f}")
# View specific transaction details
for change in result.data:
agent.log(f"{change.network}: {change.from_} โ {change.to}")
agent.log(f" Amount: {change.amount} {change.tokenType}")
if change.token:
agent.log(f" Token: {change.token}")
agent.log(f" Tx: {change.transactionHash}")
agent.log(f" Time: {change.timestamp}")
else:
agent.log(result.error or "Failed to fetch transactions", error=True)
```
**AssetChange Structure:**
Each asset change in the response contains:
- `network` - Network identifier (e.g., `"ethereum:1"`, `"solana"`)
- `transactionHash` - Transaction hash
- `from_` - Sender address (note the underscore to avoid Python keyword)
- `to` - Recipient address
- `amount` - Amount transferred (as string to preserve precision)
- `token` - Token contract address (`None` for native tokens)
- `tokenId` - Token ID for NFTs (`None` for fungible tokens)
- `tokenType` - Token type (e.g., `"native"`, `"ERC20"`, `"ERC721"`)
- `tokenUsdPrice` - Token price in USD at time of transaction (`None` if unavailable)
- `timestamp` - Transaction timestamp
## ๐ Sign & Send Transactions
### Ethereum (any EVM chain)
```python
def run(agent: AgentContext) -> None:
# Self-send demo - send a small amount to yourself
response = agent.sign_and_send({
"network": "ethereum:1",
"request": {
"to_address": agent.sessionWalletAddress, # Send to self
"data": "0x",
"value": "100000000000000" # 0.0001 ETH
},
"message": "Self-send demo"
})
if response.success:
agent.log(f"Transaction sent: {response.tx_hash}")
if response.transaction_url:
agent.log(f"View: {response.transaction_url}")
else:
agent.log(response.error_message, error=True)
```
### Solana
```python
def run(agent: AgentContext) -> None:
response = agent.sign_and_send({
"network": "solana",
"request": {
"hex_transaction": "010001030a0b..." # serialized VersionedTransaction
}
})
if response.success:
agent.log(f"Transaction: {response.tx_hash}")
else:
agent.log(response.error_message, error=True)
```
## โก Error Handling
**All SDK methods return response objects with `.success` and `.error_message`:**
**Uncaught Exceptions:**
If your function throws an uncaught exception, the Agent SDK automatically:
1. Logs the error to console (visible in local dev and cloud logs)
2. Updates the job in the circuit backend with status='failed' and logs the error.
```python
def run(agent: AgentContext) -> None:
# This typo will be caught and logged automatically
agent.memmory.set("key", "value") # AttributeError
# No need for try/except around everything!
```
## ๐ ๏ธ Deployment
### What You Write
Your agent code should look like this - just define your functions and add the boilerplate at the bottom:
```python
from agent_sdk import Agent, AgentContext
def run(agent: AgentContext) -> None:
agent.log("Hello from my agent!")
# Your agent logic here
def stop(agent: AgentContext) -> None:
agent.log("Cleaning up...")
# Optional cleanup logic
# ============================================================================
# BOILERPLATE - Don't modify, this handles local testing AND Circuit deployment
# ============================================================================
agent = Agent(
run=run,
stop=stop
)
handler = agent.get_handler()
if __name__ == "__main__":
agent.run()
```
That's it! The boilerplate code automatically handles the rest
### Deploy to Circuit
```bash
circuit publish
```
> See the circuit cli docs for more details
### Test Locally
## ๐งช Manual Instantiation (Jupyter/Testing)
For testing in Jupyter notebooks or scripts, you can manually create an `AgentContext`:
```python
from agent_sdk import AgentContext
from agent_sdk.agent_context import CurrentPosition
# Create agent context with test data
# Tip: Get a real session ID and wallet from running 'circuit run -m local -x execute'
SESSION_ID = <your-session-id>
WALLET_ADDRESS = "your-wallet-address"
# Create sample position data - helpful for testing your agents treatment of different scenarios
# for example if you want to test some re-balancing logic, you can build a sample set of positions here
# In production, or when using the cli, these will be live values for the session
SAMPLE_POSITION = CurrentPosition(
network="ethereum:137",
assetAddress="0x4d97dcd97ec945f40cf65f87097ace5ea0476045",
tokenId="86192057611122246511563653509192966169513312957180910360241289053249649036697",
avgUnitCost="0.779812797920499100",
currentQty="41282044"
)
agent = AgentContext(
sessionId=SESSION_ID,
sessionWalletAddress=WALLET_ADDRESS,
currentPositions=[SAMPLE_POSITION]
)
# Use it just like in production!
agent.log("Testing in Jupyter!")
agent.memory.set("test_key", "test_value")
result = agent.memory.get("test_key")
if result.success and result.data:
print(f"Value: {result.data.value}")
```
**Key Points:**
- Use real session IDs from the CLI for proper testing
- `currentPositions` is optional but helps test position-related logic
- All SDK methods work the same way as in production
## ๐ Working Examples
The SDK includes complete working examples to help you get started:
### [`demo-agent.py`](./examples/demo-agent.py)
An agent demonstrating all main features of the sdk:
- Memory operations (set, get, list, delete)
- Polymarket integration (market orders, position redemption)
- Swidge
- Self sending using sign and send
- Unified logging patterns
This is a great starting point for building your own agent.
### [`examples/kitchen-sink.ipynb`](./examples/kitchen-sink.ipynb)
An interactive Jupyter notebook showing all SDK features:
- Manual AgentContext instantiation for testing
- All memory operations with examples
- Cross-chain swaps with Swidge (quotes, execution, price impact checks)
- Polymarket operations (buy/sell orders, redemptions)
- Custom transactions with sign_and_send
- Logging patterns (standard, error, debug)
Run this notebook to experiment with the SDK interactively before deploying your agent.
## ๐ฏ Key Takeaways
1. **Single Interface**: Everything you need is in the `AgentContext` object
2. **No Return Values**: Functions return `None` - uncaught errors are handled automatically by circuit
3. **Unified Logging**: `agent.log()` with simple `debug` and `error` flags
4. **Check `.success`**: All SDK methods return response objects with `.success` and `.error_message`
5. **Let Errors Bubble**: Uncaught exceptions are automatically logged and reported
6. **Copy the Boilerplate**: Use the same boilerplate code for local testing and Circuit deployment
## ๐ Additional Resources
- [Circuit CLI](https://github.com/circuitorg/agents-cli) - Deploy and manage agents
- [`example_agent.py`](./example_agent.py) - Production-ready agent template
- [`examples/kitchen-sink.ipynb`](./examples/kitchen-sink.ipynb) - Interactive SDK demo
Raw data
{
"_id": null,
"home_page": null,
"name": "circuit-agent-sdk",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.12",
"maintainer_email": null,
"keywords": "agent, circuit, sdk",
"author": null,
"author_email": "Circuit <kyle@selvlabs.com>",
"download_url": "https://files.pythonhosted.org/packages/41/c5/9afe15006d092b90ee49e8bf9eed9bd0eced242e15ffeb08f6ce72a09ab6/circuit_agent_sdk-1.2.9.tar.gz",
"platform": null,
"description": "# Circuit Agent SDK - Python\n\n> **Clean, unified, type-safe Python SDK for building cross-chain agents on Circuit**\n\nA simplified Python SDK for building automated agents to deploy on Circuit. Agents receive a single `AgentContext` object containing everything they need - request data, SDK methods, and unified logging. No boilerplate, no return values to manage, just write your logic.\n\n> **\ud83d\udca1 Best used with [Circuit Agents CLI](https://github.com/circuitorg/agents-cli)** - Deploy, manage, and test your agents with ease\n\n## \ud83d\udcd1 Table of Contents\n\n- [Circuit Agent SDK - Python](#circuit-agent-sdk---python)\n - [\ud83d\udcd1 Table of Contents](#-table-of-contents)\n - [\ud83d\ude80 Quick Start](#-quick-start)\n - [Install the SDK](#install-the-sdk)\n - [Create Your First Agent](#create-your-first-agent)\n - [Required Asset setup](#required-asset-setup)\n - [\ud83c\udfaf Core Concepts](#-core-concepts)\n - [The AgentContext Object](#the-agentcontext-object)\n - [Run/Stop Function Requirements](#runstop-function-requirements)\n - [\ud83d\udcdd Unified Logging with agent.log()](#-unified-logging-with-agentlog)\n - [\ud83d\udcbe Session Memory Storage](#-session-memory-storage)\n - [\ud83c\udf09 Cross-Chain Swaps with Swidge](#-cross-chain-swaps-with-swidge)\n - [Get and execute a quote](#get-and-execute-a-quote)\n - [\ud83d\udcc8 Polymarket Prediction Markets](#-polymarket-prediction-markets)\n - [Place Market Orders](#place-market-orders)\n - [Redeem Positions](#redeem-positions)\n - [Hyperliquid Trading](#hyperliquid-trading)\n - [Account Information](#account-information)\n - [Place Orders](#place-orders)\n - [Order Management](#order-management)\n - [Transfer Between Accounts](#transfer-between-accounts)\n - [\ud83d\udcca Transaction History](#-transaction-history)\n - [\ud83d\ude80 Sign \\& Send Transactions](#-sign--send-transactions)\n - [Ethereum (any EVM chain)](#ethereum-any-evm-chain)\n - [Solana](#solana)\n - [\u26a1 Error Handling](#-error-handling)\n - [\ud83d\udee0\ufe0f Deployment](#\ufe0f-deployment)\n - [What You Write](#what-you-write)\n - [Deploy to Circuit](#deploy-to-circuit)\n - [Test Locally](#test-locally)\n - [\ud83e\uddea Manual Instantiation (Jupyter/Testing)](#-manual-instantiation-jupytertesting)\n - [\ud83d\udcda Working Examples](#-working-examples)\n - [`demo-agent.py`](#demo-agentpy)\n - [`examples/kitchen-sink.ipynb`](#exampleskitchen-sinkipynb)\n - [\ud83c\udfaf Key Takeaways](#-key-takeaways)\n - [\ud83d\udcd6 Additional Resources](#-additional-resources)\n\n## \ud83d\ude80 Quick Start\n\n### Install the SDK\n```bash\npip install circuit-agent-sdk\n# or with uv\nuv pip install circuit-agent-sdk\n```\n\n### Create Your First Agent\n\nEvery agent receives a single `AgentContext` object that provides:\n\n**Session Data:**\n- `agent.sessionId` - Unique session identifier\n- `agent.sessionWalletAddress` - The wallet address for this session\n- `agent.currentPositions` - Assets allocated to the agent at the start of this execution\n\n**Available Methods:**\n- `agent.log()` - Send messages to users and log locally\n- `agent.memory` - Persist data across executions (`.set()`, `.get()`, `.list()`, `.delete()`)\n- `agent.platforms.polymarket` - Trade prediction markets (`.market_order()`, `.redeem_positions()`)\n- `agent.platforms.hyperliquid` - trading (`.place_order()`, `.positions()`, `.balances()`, `.transfer()`)\n- `agent.swidge` - Cross-chain swaps and bridges (`.quote()`, `.execute()`)\n- `agent.sign_and_send()` - Execute custom built transactions on any supported chain\n- `agent.sign_message()` - Sign messages (EVM only)\n- `agent.transactions()` - Get transaction history with asset changes\n\n**Important:** `currentPositions` reflects your allocated assets at the **start** of each execution. You will need to use the agent.get_current_positions() method to pull fresh values after executing transactions. It's important to consider the hasPendingTxs if you had recently submitted transactions. If hasPendingTxs is true, the balances you received may not be 100% up to date.\n\n#### Required Asset setup\n> Note: For native tokens, use the following null addresses for solana/ethereum\n```toml\n// Requiring 1 SOL\n[[requiredAssets]]\nnetwork = \"solana\"\naddress = \"11111111111111111111111111111111\"\nminimumAmount = \"1000000000\"\n\n// Requiring 1 ETH\n[[requiredAssets]]\nnetwork = \"ethereum:<chainId>\"\naddress = \"0x0000000000000000000000000000000000000000\"\nminimumAmount = \"1000000000000000000\"\n```\n\n\n```python\nfrom agent_sdk import Agent, AgentContext\nimport time\n\ndef run(agent: AgentContext) -> None:\n \"\"\"\n Main agent logic - receives AgentContext with everything needed.\n No return value - errors are caught automatically.\n \"\"\"\n # Access session data\n agent.log(f\"Starting execution for session {agent.sessionId}\")\n agent.log(f\"Wallet: {agent.sessionWalletAddress}\")\n agent.log(f\"Managing {len(agent.currentPositions)} allocated positions\")\n\n # Use SDK methods\n agent.memory.set(\"last_run\", str(time.time()))\n\n # Your agent logic here - track position changes within this execution\n # Circuit will provide updated positions on the next run\n\ndef stop(agent: AgentContext) -> None:\n \"\"\"Cleanup when agent is stopped.\"\"\"\n agent.log(\"Cleaning up resources\")\n agent.memory.delete(\"temp_data\")\n\n# Boilerplate - This should never change unless you want to change the names of your run and stop functions\nagent = Agent(\n run=run,\n stop=stop\n)\n\nhandler = agent.get_handler()\n\nif __name__ == \"__main__\":\n agent.run()\n```\n\n## \ud83c\udfaf Core Concepts\n\n### The AgentContext Object\n\nEvery agent function receives a single `AgentContext` object that contains:\n\n**Request Data:**\n- `agent.sessionId` - Unique session identifier\n- `agent.sessionWalletAddress` - Wallet address for this session\n- `agent.currentPositions` - Current positions allocated to this agent\n\n**SDK Methods:**\n- `agent.log()` - Unified logging (console + backend)\n- `agent.memory` - Session-scoped key-value storage\n- `agent.platforms.polymarket` - Prediction market operations\n- `agent.swidge` - Cross-chain swap operations\n- `agent.sign_and_send()` - Sign and broadcast transactions\n- `agent.sign_message()` - Sign messages on EVM\n- `agent.transactions()` - Get transaction history with asset changes\n\n### Run/Stop Function Requirements\n\n1. **Signature**: `def my_function(agent: AgentContext) -> None:`\n2. **Return**: Always return `None` (or no return statement)\n3. **Errors**: You should surface any relevant errors via agent.log('error message',error=True). All errors from built-in sdk functions will be caught gracefully and provided in the return data for you to handle as necessary.\n\n## \ud83d\udcdd Unified Logging with agent.log()\n\nUse `agent.log()` to communicate with your users and debug your agent. Every message appears in your terminal, and by default also shows up in the Circuit UI for your users to see. Simply pass debug=True to skip sending the message to the user.\n\n```python\ndef run(agent: AgentContext) -> None:\n # Standard log: Shows to user in Circuit UI\n agent.log(\"Processing transaction\")\n\n # Error log: Shows to user in Circuit UI (as an error)\n result = agent.memory.get(\"key\")\n if not result.success:\n agent.log(result.error_message, error=True)\n\n # Debug log: Only you see this in your terminal\n agent.log(\"Internal state: processing...\", debug=True)\n\n # Logging dicts and pydantic models\n # Both are pretty-printed to console and serialized/truncated for backend\n agent.log({\"wallet\": agent.sessionWalletAddress, \"status\": \"active\"})\n\n # Check if message reached the user\n log_result = agent.log(\"Important message\")\n if not log_result.success:\n # Rare - usually means Circuit UI is unreachable\n pass\n```\n\n**What Your Users See:**\n\n| Code | You See (Terminal) | User Sees (Circuit UI) |\n|------|-------------------|----------------------|\n| `agent.log(\"msg\")` | \u2705 In your terminal | \u2705 In Circuit UI |\n| `agent.log(\"msg\", error=True)` | \u2705 As error in terminal | \u2705 As error in Circuit UI |\n| `agent.log(\"msg\", debug=True)` | \u2705 In terminal | \u274c Hidden from user |\n| `agent.log(\"msg\", error=True, debug=True)` | \u2705 As error in terminal | \u274c Hidden from user |\n\n## \ud83d\udcbe Session Memory Storage\n\nStore and retrieve data for your agent's session with simple operations. Memory is **automatically scoped to your session ID**, and for now is simple string storage. You will need to handle serialization of whatever data you want to store here.\n\n```python\ndef run(agent: AgentContext) -> None:\n # Set a value\n result = agent.memory.set(\"lastSwapNetwork\", \"ethereum:42161\")\n if not result.success:\n agent.log(result.error_message, error=True)\n\n # Get a value\n result = agent.memory.get(\"lastSwapNetwork\")\n if result.success and result.data:\n network = result.data.value\n agent.log(f\"Using network: {network}\")\n else:\n agent.log(\"No saved network found\")\n\n # List all keys\n result = agent.memory.list()\n if result.success and result.data:\n agent.log(f\"Found {result.data.count} keys: {result.data.keys}\")\n\n # Delete a key\n result = agent.memory.delete(\"tempData\")\n```\n\n**All memory operations return responses with `.success` and `.error_message`:**\n\n```python\nresult = agent.memory.set(\"key\", \"value\")\nif not result.success:\n agent.log(f\"Failed to save: {result.error_message}\", error=True)\n```\n\n## \ud83c\udf09 Cross-Chain Swaps with Swidge\n\nBuilt-in Swidge integration for seamless cross-chain token swaps and bridges.\n\n### Get and execute a quote\n> Note: It is important to always validate quotes before executing. Circuit will always do its best to return a quote, and as of now, will only filter out quotes with price impacts exceeding 100% to ensure maximum flexibility. It is on the agent to makes sure a quote is valid, given its own parameters\n\n> **Bulk Execution:** `execute()` accepts both single quotes and lists. Pass a list to execute multiple swaps in parallel: `agent.swidge.execute([quote1.data, quote2.data])` returns a list of results. Note: Use explicit `is not None` checks for proper type narrowing.\n\n```python\ndef run(agent: AgentContext) -> None:\n # 1. Get quote\n quote = agent.swidge.quote({\n \"from\": {\"network\": \"ethereum:8453\", \"address\": agent.sessionWalletAddress},\n \"to\": {\"network\": \"ethereum:137\", \"address\": agent.sessionWalletAddress},\n \"amount\": \"1000000000000000\", # 0.001 ETH\n \"toToken\": \"0x2791bca1f2de4661ed88a30c99a7a9449aa84174\",\n \"slippage\": \"2.0\",\n \"priceImpact\": \"100.0\"\n })\n\n if not quote.success:\n _ = agent.log(f\"Quote failed: {quote.error_message}\", error=True)\n return\n\n if abs(float(quote.data.priceImpact.percentage)) > 10:\n _ = agent.log(f\"Warning: Price impact is too high: {quote.data.priceImpact.percentage}%\", error=True, debug=True)\n else:\n _ = agent.log(f\"You'll receive: {quote.data.assetReceive.amountFormatted}\")\n _ = agent.log(f\"Fees: {', '.join([f.name for f in quote.data.fees])}\")\n\n # 2. Execute the swap\n result = agent.swidge.execute(quote.data)\n\n if result.success and result.data:\n agent.log(f\"Swap status: {result.data.status}\")\n if result.data.status == \"success\":\n agent.log(\"\u2705 Swap completed!\")\n agent.log(f\"In tx: {result.data.in_.txs}\")\n agent.log(f\"Out tx: {result.data.out.txs}\")\n elif result.data.status == \"failure\":\n agent.log(\"\u274c Swap failed\", error=True)\n else:\n agent.log(result.error_message, error=True)\n```\n\n## \ud83d\udcc8 Polymarket Prediction Markets\n\nTrade prediction markets on Polygon.\n\n### Place Market Orders\n> Note: Right now, polymarket's API accepts different decimal precision for buys and sells, this will result in dust positions if you are selling out of a position before expiry. Once expired, dust can be cleaned up during the redeem step.\n\n```python\ndef run(agent: AgentContext) -> None:\n # Buy order - size is USD amount to spend\n buy_order = agent.platforms.polymarket.market_order({\n \"tokenId\": \"86192057611122246511563653509192966169513312957180910360241289053249649036697\",\n \"size\": 3, # Spend $3\n \"side\": \"BUY\"\n })\n\n if buy_order.success and buy_order.data:\n agent.log(f\"Order ID: {buy_order.data.orderInfo.orderId}\")\n agent.log(f\"Price: ${buy_order.data.orderInfo.priceUsd}\")\n agent.log(f\"Total: ${buy_order.data.orderInfo.totalPriceUsd}\")\n else:\n agent.log(buy_order.error_message, error=True)\n\n # Sell order - size is number of shares to sell\n sell_order = agent.platforms.polymarket.market_order({\n \"tokenId\": \"86192057611122246511563653509192966169513312957180910360241289053249649036697\",\n \"size\": 5.5, # Sell 5.5 shares\n \"side\": \"SELL\"\n })\n```\n\n### Redeem Positions\n\n```python\ndef stop(agent: AgentContext) -> None:\n \"\"\"Redeem all settled positions.\"\"\"\n redemption = agent.platforms.polymarket.redeem_positions()\n\n if redemption.success and redemption.data:\n successful = [r for r in redemption.data if r.success]\n agent.log(f\"Redeemed {len(successful)} positions\")\n else:\n agent.log(redemption.error_message, error=True)\n```\n\n## Hyperliquid Trading\n\nTrade perpetual futures on Hyperliquid DEX with leverage.\n\n### Account Information\n\n```python\ndef run(agent: AgentContext) -> None:\n # Check balances\n balances = agent.platforms.hyperliquid.balances()\n if balances.success and balances.data:\n agent.log(f\"Account value: ${balances.data.perp.accountValue}\")\n agent.log(f\"Withdrawable: ${balances.data.perp.withdrawable}\")\n\n # View open positions\n positions = agent.platforms.hyperliquid.positions()\n if positions.success and positions.data:\n for pos in positions.data:\n agent.log(f\"{pos.symbol}: {pos.side} {pos.size} @ {pos.entryPrice}\")\n agent.log(f\"Unrealized PnL: ${pos.unrealizedPnl}\")\n```\n\n### Place Orders\n\n```python\ndef run(agent: AgentContext) -> None:\n # Market order\n order = agent.platforms.hyperliquid.place_order({\n \"symbol\": \"BTC-USD\",\n \"side\": \"buy\",\n \"size\": 0.0001,\n \"price\": 110000, # Acts as slippage limit for market orders\n \"market\": \"perp\",\n \"type\": \"market\"\n })\n\n if order.success and order.data:\n agent.log(f\"Order {order.data.orderId}: {order.data.status}\")\n else:\n agent.log(order.error, error=True)\n```\n\n### Order Management\n\n```python\ndef run(agent: AgentContext) -> None:\n # Get open orders\n open_orders = agent.platforms.hyperliquid.open_orders()\n\n # Get specific order details\n order = agent.platforms.hyperliquid.order(\"12345\")\n\n # Cancel an order\n result = agent.platforms.hyperliquid.delete_order(\"12345\", \"BTC-USD\")\n\n # View historical orders\n history = agent.platforms.hyperliquid.orders()\n\n # Check order fills\n fills = agent.platforms.hyperliquid.order_fills()\n```\n\n### Transfer Between Accounts\n\n```python\ndef run(agent: AgentContext) -> None:\n # Transfer USDC from spot to perp account\n transfer = agent.platforms.hyperliquid.transfer({\n \"amount\": 1000.0,\n \"toPerp\": True\n })\n\n if transfer.success:\n agent.log(\"Transfer completed\")\n```\n\n## \ud83d\udcca Transaction History\n\nGet a list of asset changes for all confirmed transactions during your session. This is useful for tracking what assets have moved in and out of the agent's wallet.\n\n> **Note:** The system needs to index new transactions, so there may be a slight delay between when you execute a transaction and when the resulting asset changes are returned in this method. Make sure you are taking that into consideration if dealing with assets the agent just transacted with.\n\n```python\ndef run(agent: AgentContext) -> None:\n # Get all transaction history for this session\n result = agent.transactions()\n\n if result.success and result.data:\n agent.log(f\"Found {len(result.data)} asset changes\")\n\n # Filter for outgoing transfers\n outgoing = [c for c in result.data if c.from_ == agent.sessionWalletAddress]\n agent.log(f\"Outgoing transfers: {len(outgoing)}\")\n\n # Calculate total USD value (where price data is available)\n total_usd = sum(\n float(c.amount) * float(c.tokenUsdPrice)\n for c in result.data\n if c.tokenUsdPrice\n )\n agent.log(f\"Total USD value: ${total_usd:.2f}\")\n\n # View specific transaction details\n for change in result.data:\n agent.log(f\"{change.network}: {change.from_} \u2192 {change.to}\")\n agent.log(f\" Amount: {change.amount} {change.tokenType}\")\n if change.token:\n agent.log(f\" Token: {change.token}\")\n agent.log(f\" Tx: {change.transactionHash}\")\n agent.log(f\" Time: {change.timestamp}\")\n else:\n agent.log(result.error or \"Failed to fetch transactions\", error=True)\n```\n\n**AssetChange Structure:**\n\nEach asset change in the response contains:\n- `network` - Network identifier (e.g., `\"ethereum:1\"`, `\"solana\"`)\n- `transactionHash` - Transaction hash\n- `from_` - Sender address (note the underscore to avoid Python keyword)\n- `to` - Recipient address\n- `amount` - Amount transferred (as string to preserve precision)\n- `token` - Token contract address (`None` for native tokens)\n- `tokenId` - Token ID for NFTs (`None` for fungible tokens)\n- `tokenType` - Token type (e.g., `\"native\"`, `\"ERC20\"`, `\"ERC721\"`)\n- `tokenUsdPrice` - Token price in USD at time of transaction (`None` if unavailable)\n- `timestamp` - Transaction timestamp\n\n## \ud83d\ude80 Sign & Send Transactions\n\n### Ethereum (any EVM chain)\n\n```python\ndef run(agent: AgentContext) -> None:\n # Self-send demo - send a small amount to yourself\n response = agent.sign_and_send({\n \"network\": \"ethereum:1\",\n \"request\": {\n \"to_address\": agent.sessionWalletAddress, # Send to self\n \"data\": \"0x\",\n \"value\": \"100000000000000\" # 0.0001 ETH\n },\n \"message\": \"Self-send demo\"\n })\n\n if response.success:\n agent.log(f\"Transaction sent: {response.tx_hash}\")\n if response.transaction_url:\n agent.log(f\"View: {response.transaction_url}\")\n else:\n agent.log(response.error_message, error=True)\n```\n\n### Solana\n\n```python\ndef run(agent: AgentContext) -> None:\n response = agent.sign_and_send({\n \"network\": \"solana\",\n \"request\": {\n \"hex_transaction\": \"010001030a0b...\" # serialized VersionedTransaction\n }\n })\n\n if response.success:\n agent.log(f\"Transaction: {response.tx_hash}\")\n else:\n agent.log(response.error_message, error=True)\n```\n\n## \u26a1 Error Handling\n\n**All SDK methods return response objects with `.success` and `.error_message`:**\n\n\n**Uncaught Exceptions:**\n\nIf your function throws an uncaught exception, the Agent SDK automatically:\n1. Logs the error to console (visible in local dev and cloud logs)\n2. Updates the job in the circuit backend with status='failed' and logs the error.\n\n```python\ndef run(agent: AgentContext) -> None:\n # This typo will be caught and logged automatically\n agent.memmory.set(\"key\", \"value\") # AttributeError\n\n # No need for try/except around everything!\n```\n\n## \ud83d\udee0\ufe0f Deployment\n\n### What You Write\n\nYour agent code should look like this - just define your functions and add the boilerplate at the bottom:\n\n```python\nfrom agent_sdk import Agent, AgentContext\n\ndef run(agent: AgentContext) -> None:\n agent.log(\"Hello from my agent!\")\n # Your agent logic here\n\ndef stop(agent: AgentContext) -> None:\n agent.log(\"Cleaning up...\")\n # Optional cleanup logic\n\n# ============================================================================\n# BOILERPLATE - Don't modify, this handles local testing AND Circuit deployment\n# ============================================================================\nagent = Agent(\n run=run,\n stop=stop\n)\n\nhandler = agent.get_handler()\n\nif __name__ == \"__main__\":\n agent.run()\n```\n\nThat's it! The boilerplate code automatically handles the rest\n\n### Deploy to Circuit\n\n```bash\ncircuit publish\n```\n> See the circuit cli docs for more details\n\n\n### Test Locally\n\n## \ud83e\uddea Manual Instantiation (Jupyter/Testing)\n\nFor testing in Jupyter notebooks or scripts, you can manually create an `AgentContext`:\n\n```python\nfrom agent_sdk import AgentContext\nfrom agent_sdk.agent_context import CurrentPosition\n\n# Create agent context with test data\n# Tip: Get a real session ID and wallet from running 'circuit run -m local -x execute'\nSESSION_ID = <your-session-id>\nWALLET_ADDRESS = \"your-wallet-address\"\n\n# Create sample position data - helpful for testing your agents treatment of different scenarios\n# for example if you want to test some re-balancing logic, you can build a sample set of positions here\n# In production, or when using the cli, these will be live values for the session\nSAMPLE_POSITION = CurrentPosition(\n network=\"ethereum:137\",\n assetAddress=\"0x4d97dcd97ec945f40cf65f87097ace5ea0476045\",\n tokenId=\"86192057611122246511563653509192966169513312957180910360241289053249649036697\",\n avgUnitCost=\"0.779812797920499100\",\n currentQty=\"41282044\"\n)\n\nagent = AgentContext(\n sessionId=SESSION_ID,\n sessionWalletAddress=WALLET_ADDRESS,\n currentPositions=[SAMPLE_POSITION]\n)\n\n# Use it just like in production!\nagent.log(\"Testing in Jupyter!\")\nagent.memory.set(\"test_key\", \"test_value\")\n\nresult = agent.memory.get(\"test_key\")\nif result.success and result.data:\n print(f\"Value: {result.data.value}\")\n```\n\n**Key Points:**\n- Use real session IDs from the CLI for proper testing\n- `currentPositions` is optional but helps test position-related logic\n- All SDK methods work the same way as in production\n\n## \ud83d\udcda Working Examples\n\nThe SDK includes complete working examples to help you get started:\n\n### [`demo-agent.py`](./examples/demo-agent.py)\nAn agent demonstrating all main features of the sdk:\n- Memory operations (set, get, list, delete)\n- Polymarket integration (market orders, position redemption)\n- Swidge\n- Self sending using sign and send\n- Unified logging patterns\n\nThis is a great starting point for building your own agent.\n\n### [`examples/kitchen-sink.ipynb`](./examples/kitchen-sink.ipynb)\nAn interactive Jupyter notebook showing all SDK features:\n- Manual AgentContext instantiation for testing\n- All memory operations with examples\n- Cross-chain swaps with Swidge (quotes, execution, price impact checks)\n- Polymarket operations (buy/sell orders, redemptions)\n- Custom transactions with sign_and_send\n- Logging patterns (standard, error, debug)\n\nRun this notebook to experiment with the SDK interactively before deploying your agent.\n\n## \ud83c\udfaf Key Takeaways\n\n1. **Single Interface**: Everything you need is in the `AgentContext` object\n2. **No Return Values**: Functions return `None` - uncaught errors are handled automatically by circuit\n3. **Unified Logging**: `agent.log()` with simple `debug` and `error` flags\n4. **Check `.success`**: All SDK methods return response objects with `.success` and `.error_message`\n5. **Let Errors Bubble**: Uncaught exceptions are automatically logged and reported\n6. **Copy the Boilerplate**: Use the same boilerplate code for local testing and Circuit deployment\n\n## \ud83d\udcd6 Additional Resources\n\n- [Circuit CLI](https://github.com/circuitorg/agents-cli) - Deploy and manage agents\n- [`example_agent.py`](./example_agent.py) - Production-ready agent template\n- [`examples/kitchen-sink.ipynb`](./examples/kitchen-sink.ipynb) - Interactive SDK demo\n",
"bugtrack_url": null,
"license": "Proprietary",
"summary": "Official Python SDK for building and deploying agents on the Circuit platform",
"version": "1.2.9",
"project_urls": {
"Issues": "https://github.com/circuitorg/agent-sdk-python/issues",
"Repository": "https://github.com/circuitorg/agent-sdk-python"
},
"split_keywords": [
"agent",
" circuit",
" sdk"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "1c6fcdbd9385f29e74eb0bbeb4775307bbcb8a9a5dc9aa0011f11c3b64aef448",
"md5": "08609acd105d41b4ade660ca34ef693f",
"sha256": "04a6316c718ea71f44d3d5bd4f2d9514d613477c4b2db0890b6648804d887f84"
},
"downloads": -1,
"filename": "circuit_agent_sdk-1.2.9-py3-none-any.whl",
"has_sig": false,
"md5_digest": "08609acd105d41b4ade660ca34ef693f",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.12",
"size": 72286,
"upload_time": "2025-10-30T22:26:46",
"upload_time_iso_8601": "2025-10-30T22:26:46.278482Z",
"url": "https://files.pythonhosted.org/packages/1c/6f/cdbd9385f29e74eb0bbeb4775307bbcb8a9a5dc9aa0011f11c3b64aef448/circuit_agent_sdk-1.2.9-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "41c59afe15006d092b90ee49e8bf9eed9bd0eced242e15ffeb08f6ce72a09ab6",
"md5": "f778ba0c2f8ea0aa4372854d77e3105f",
"sha256": "07944af4dd160fa833ae712e949c64961ce397f3a91f76634bcb1eb523ad03c5"
},
"downloads": -1,
"filename": "circuit_agent_sdk-1.2.9.tar.gz",
"has_sig": false,
"md5_digest": "f778ba0c2f8ea0aa4372854d77e3105f",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.12",
"size": 319641,
"upload_time": "2025-10-30T22:26:47",
"upload_time_iso_8601": "2025-10-30T22:26:47.529806Z",
"url": "https://files.pythonhosted.org/packages/41/c5/9afe15006d092b90ee49e8bf9eed9bd0eced242e15ffeb08f6ce72a09ab6/circuit_agent_sdk-1.2.9.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-10-30 22:26:47",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "circuitorg",
"github_project": "agent-sdk-python",
"github_not_found": true,
"lcname": "circuit-agent-sdk"
}