Skip to content

Commit 13800d1

Browse files
committed
sdk ready
1 parent 91ab281 commit 13800d1

12 files changed

Lines changed: 685 additions & 199 deletions

File tree

agentic_exchange/README.md

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
# Agentic Exchange Python SDK
2+
3+
[![PyPI version](https://img.shields.io/pypi/v/agentic-exchange.svg)](https://pypi.org/project/agentic-exchange/)
4+
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5+
6+
**Agentic Exchange** is the decentralized infrastructure layer for the autonomous AI economy. This SDK allows developers to programmatically discover, purchase, and orchestrate intelligent agents directly on the Algorand blockchain.
7+
8+
---
9+
10+
## 🚀 Quick Start
11+
12+
### Installation
13+
14+
```bash
15+
pip install agentic-exchange
16+
```
17+
18+
### Basic Usage
19+
20+
Initialize the client with your API key to start interacting with the marketplace.
21+
22+
```python
23+
from agentic_exchange import AgenticClient
24+
25+
# Initialize the client
26+
client = AgenticClient(api_key="your_sk_test_...")
27+
28+
# 1. Discover agents
29+
agents = client.list_agents(limit=5)
30+
for agent in agents:
31+
print(f"Agent: {agent.name} | Price: {agent.price_microalgos / 1e6} ALGO")
32+
33+
# 2. Check reputation
34+
rep = client.get_agent_reputation(agents[0].agent_id)
35+
print(f"Trust Score: {rep.reputation_score}%")
36+
```
37+
38+
---
39+
40+
## 🤖 Multi-Agent Orchestration
41+
42+
Chain multiple agents together into a seamless automated pipeline. The output of the first agent is intelligently passed as context to the next.
43+
44+
```python
45+
# Execute a Research -> Copywriter pipeline
46+
run = client.run_workflow(
47+
steps=["demo_research", "demo_copywriter"],
48+
input_data={"prompt": "Analyze the latest trends in Algorand DeFi and write a marketing thread."}
49+
)
50+
51+
if run.status == "completed":
52+
print("Workflow Result:", run.final_output.get("result"))
53+
```
54+
55+
---
56+
57+
## 🧠 Marketplace Intelligence
58+
59+
### AI Recommendations
60+
Not sure which agents to use? Let our recommendation engine select the best tools for your specific intent.
61+
62+
```python
63+
recommended = client.recommend_agents(intent="I need to audit a smart contract for security vulnerabilities")
64+
65+
# Execute a recommended pipeline automatically
66+
pipeline_run = client.execute_pipeline(
67+
intent="Create a technical blog post from a whitepaper PDF link",
68+
input_payload={"url": "https://example.com/whitepaper.pdf"}
69+
)
70+
```
71+
72+
### Reputation & Trust Metrics
73+
Integrate enterprise-grade trust signals into your application to ensure you only deploy the highest-performing agents.
74+
75+
```python
76+
reputation = client.get_agent_reputation("agent_uuid_123")
77+
if reputation.enterprise_grade:
78+
print("This agent meets enterprise reliability standards (99.9% uptime).")
79+
```
80+
81+
---
82+
83+
## 🛡️ Error Handling
84+
85+
The SDK provides strongly typed exceptions to help you build resilient integrations.
86+
87+
```python
88+
from agentic_exchange.exceptions import (
89+
AuthenticationError,
90+
RateLimitError,
91+
WorkflowExecutionError
92+
)
93+
94+
try:
95+
client.run_workflow(steps=["agent_id"])
96+
except AuthenticationError:
97+
print("Invalid API Key.")
98+
except RateLimitError:
99+
print("Slow down! You've reached your execution limit.")
100+
except WorkflowExecutionError as e:
101+
print(f"Orchestration failed: {e}")
102+
```
103+
104+
---
105+
106+
## 💳 Billing & Costs
107+
108+
Agentic Exchange uses Algorand Atomic Transfers for trustless settlement. You can estimate costs before triggering expensive multi-agent runs.
109+
110+
```python
111+
estimate = client.estimate_execution_cost(steps=["research_agent", "seo_agent"])
112+
print(f"Estimated Cost: {estimate['total_algo']} ALGO")
113+
```
114+
115+
---
116+
117+
## 🛠️ Advanced Configuration
118+
119+
### Resilient Retries
120+
The SDK automatically handles transient network failures with **Exponential Backoff**.
121+
122+
```python
123+
# Configure a custom timeout for long-running workflows
124+
client = AgenticClient(
125+
api_key="sk_...",
126+
timeout=60, # 60 seconds
127+
debug=True # Enable verbose execution logs
128+
)
129+
```
130+
131+
---
132+
133+
## 📖 API Reference
134+
135+
| Method | Description |
136+
| --- | --- |
137+
| `list_agents()` | Fetch all published marketplace agents. |
138+
| `get_agent(id)` | Fetch detailed metadata for a single agent. |
139+
| `recommend_agents(intent)` | Get AI-driven suggestions for a task. |
140+
| `get_agent_reputation(id)` | Retrieve trust scores and performance metrics. |
141+
| `run_workflow(steps, input)` | Trigger a multi-agent orchestration pipeline. |
142+
| `execute_pipeline(intent)` | Recommended -> Execute workflow in one call. |
143+
| `get_workflow_status(id)` | Fetch the execution trace of a run. |
144+
| `estimate_execution_cost(steps)` | Calculate total ALGO cost for a pipeline. |
145+
146+
---
147+
148+
## 📄 License
149+
MIT License. See [LICENSE](LICENSE) for details.

agentic_exchange/__init__.py

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,34 @@
1-
"""Agentic Exchange Python SDK public package.
1+
"""
2+
Agentic Exchange Python SDK.
23
3-
This is the canonical import surface for the published SDK.
4+
The decentralized infrastructure layer for autonomous AI agents on Algorand.
5+
Discover, purchase, and orchestrate intelligent agents programmatically.
46
"""
57

68
from .client import AgenticClient
9+
from .models import Agent, WorkflowRun, AgentReputation
10+
from .exceptions import (
11+
AgenticExchangeError,
12+
AuthenticationError,
13+
RateLimitError,
14+
ValidationError,
15+
WorkflowExecutionError,
16+
NetworkError,
17+
ResourceNotFoundError
18+
)
19+
20+
__all__ = [
21+
"AgenticClient",
22+
"Agent",
23+
"WorkflowRun",
24+
"AgentReputation",
25+
"AgenticExchangeError",
26+
"AuthenticationError",
27+
"RateLimitError",
28+
"ValidationError",
29+
"WorkflowExecutionError",
30+
"NetworkError",
31+
"ResourceNotFoundError"
32+
]
733

8-
__all__ = ["AgenticClient"]
34+
__version__ = "0.1.0"

agentic_exchange/api.py

Lines changed: 118 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,126 @@
1-
"""HTTP API client layer for Agentic Exchange SDK."""
1+
"""Resilient HTTP API client for the Agentic Exchange SDK."""
22

3-
from typing import Any, Dict, Optional
3+
import logging
4+
import time
5+
from typing import Any, Dict, Optional, Tuple, Type
46

57
import requests
68
from requests import Session
79

8-
from .exceptions import AuthenticationError, NetworkError, RateLimitError, ValidationError
9-
from .utils import exponential_backoff_retry, logger
10+
from .exceptions import (
11+
AuthenticationError,
12+
NetworkError,
13+
RateLimitError,
14+
ResourceNotFoundError,
15+
ValidationError,
16+
)
17+
from .utils import exponential_backoff_retry
18+
19+
# Configure default logger
20+
logger = logging.getLogger("agentic_exchange")
1021

1122

1223
class ApiClient:
13-
def __init__(self, api_key: str, base_url: str = "https://api.agentic.exchange", timeout: int = 15):
14-
self.api_key = api_key
15-
self.base_url = base_url.rstrip("/")
16-
self.timeout = timeout
17-
self.session: Session = requests.Session()
18-
self.session.headers.update(
19-
{
20-
"Authorization": f"Bearer {self.api_key}",
21-
"Content-Type": "application/json",
22-
"Accept": "application/json",
23-
}
24-
)
25-
26-
def _url(self, path: str) -> str:
27-
return f"{self.base_url}{path}"
28-
29-
@exponential_backoff_retry(
30-
retries=3,
31-
initial_delay=0.5,
32-
factor=2.0,
33-
allowed_exceptions=(requests.RequestException,),
34-
)
35-
def post(self, path: str, json: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
36-
url = self._url(path)
37-
try:
38-
resp = self.session.post(url, json=json, timeout=self.timeout)
39-
except requests.RequestException as e:
40-
logger.debug("Network error posting to %s: %s", url, e)
41-
raise NetworkError("Network error", e)
42-
return self._handle_response(resp)
43-
44-
@exponential_backoff_retry(
45-
retries=3,
46-
initial_delay=0.5,
47-
factor=2.0,
48-
allowed_exceptions=(requests.RequestException,),
49-
)
50-
def get(self, path: str, params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
51-
url = self._url(path)
52-
try:
53-
resp = self.session.get(url, params=params, timeout=self.timeout)
54-
except requests.RequestException as e:
55-
logger.debug("Network error getting %s: %s", url, e)
56-
raise NetworkError("Network error", e)
57-
return self._handle_response(resp)
58-
59-
def _handle_response(self, resp: requests.Response) -> Dict[str, Any]:
60-
status = resp.status_code
61-
try:
62-
payload = resp.json()
63-
except ValueError:
64-
payload = {"message": resp.text}
65-
66-
if status == 401:
67-
raise AuthenticationError(payload.get("message", "Unauthorized"))
68-
if status == 422:
69-
raise ValidationError(payload.get("message", "Validation error"))
70-
if status == 429:
71-
raise RateLimitError(payload.get("message", "Rate limited"))
72-
if status >= 400:
73-
raise NetworkError(f"HTTP {status}: {payload}")
74-
75-
return payload
24+
"""Internal client handling HTTP communication, retries, and error mapping."""
25+
26+
def __init__(
27+
self,
28+
api_key: str,
29+
base_url: str = "http://127.0.0.1:8000",
30+
timeout: int = 30,
31+
debug: bool = False
32+
):
33+
self.api_key = api_key
34+
self.base_url = base_url.rstrip("/")
35+
self.timeout = timeout
36+
self.debug = debug
37+
self.session: Session = requests.Session()
38+
39+
self.session.headers.update({
40+
"Authorization": f"Bearer {self.api_key}",
41+
"Content-Type": "application/json",
42+
"Accept": "application/json",
43+
"User-Agent": "AgenticExchange-PythonSDK/0.1.0"
44+
})
45+
46+
if self.debug:
47+
logger.setLevel(logging.DEBUG)
48+
49+
def _url(self, path: str) -> str:
50+
# Ensure path starts with /
51+
if not path.startswith("/"):
52+
path = f"/{path}"
53+
return f"{self.base_url}{path}"
54+
55+
@exponential_backoff_retry(
56+
retries=3,
57+
initial_delay=1.0,
58+
factor=2.0,
59+
allowed_exceptions=(requests.RequestException, NetworkError),
60+
)
61+
def request(
62+
self,
63+
method: str,
64+
path: str,
65+
json: Optional[Dict[str, Any]] = None,
66+
params: Optional[Dict[str, Any]] = None
67+
) -> Dict[str, Any]:
68+
"""Perform an HTTP request with automatic retries and error handling."""
69+
url = self._url(path)
70+
if self.debug:
71+
logger.debug(f"Request: {method} {url} | Params: {params} | Body: {json}")
72+
73+
try:
74+
resp = self.session.request(
75+
method=method,
76+
url=url,
77+
json=json,
78+
params=params,
79+
timeout=self.timeout
80+
)
81+
return self._handle_response(resp)
82+
except requests.Timeout as e:
83+
logger.error(f"Timeout connecting to {url}")
84+
raise NetworkError(f"Request timed out after {self.timeout}s", e)
85+
except requests.RequestException as e:
86+
logger.error(f"Network error: {e}")
87+
raise NetworkError("Failed to connect to Agentic Exchange API", e)
88+
89+
def _handle_response(self, resp: requests.Response) -> Dict[str, Any]:
90+
"""Maps HTTP status codes to custom SDK exceptions."""
91+
status = resp.status_code
92+
93+
try:
94+
payload = resp.json()
95+
except ValueError:
96+
payload = {"detail": resp.text}
97+
98+
if self.debug:
99+
logger.debug(f"Response: {status} | Payload: {payload}")
100+
101+
if 200 <= status < 300:
102+
return payload
103+
104+
error_msg = payload.get("detail", payload.get("message", "An unexpected error occurred"))
105+
106+
if status == 401:
107+
raise AuthenticationError(f"Authentication failed: {error_msg}", payload)
108+
if status == 404:
109+
raise ResourceNotFoundError(f"Resource not found: {error_msg}", payload)
110+
if status == 422:
111+
raise ValidationError(f"Invalid request parameters: {error_msg}", payload)
112+
if status == 429:
113+
raise RateLimitError(f"Rate limit exceeded: {error_msg}", payload)
114+
if status >= 500:
115+
raise NetworkError(f"Server error (HTTP {status}): {error_msg}")
116+
117+
raise NetworkError(f"Unexpected HTTP {status}: {error_msg}", payload)
118+
119+
def get(self, path: str, params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
120+
return self.request("GET", path, params=params)
121+
122+
def post(self, path: str, json: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
123+
return self.request("POST", path, json=json)
124+
125+
def patch(self, path: str, json: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
126+
return self.request("PATCH", path, json=json)

0 commit comments

Comments
 (0)