blindoracle-sdk 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- blindoracle_sdk-0.4.0/PKG-INFO +187 -0
- blindoracle_sdk-0.4.0/README.md +151 -0
- blindoracle_sdk-0.4.0/blindoracle_sdk/__init__.py +74 -0
- blindoracle_sdk-0.4.0/blindoracle_sdk/agents.py +100 -0
- blindoracle_sdk-0.4.0/blindoracle_sdk/aio.py +117 -0
- blindoracle_sdk-0.4.0/blindoracle_sdk/attestation.py +78 -0
- blindoracle_sdk-0.4.0/blindoracle_sdk/audit.py +127 -0
- blindoracle_sdk-0.4.0/blindoracle_sdk/cli.py +105 -0
- blindoracle_sdk-0.4.0/blindoracle_sdk/client.py +249 -0
- blindoracle_sdk-0.4.0/blindoracle_sdk/compliance.py +123 -0
- blindoracle_sdk-0.4.0/blindoracle_sdk/delegation.py +235 -0
- blindoracle_sdk-0.4.0/blindoracle_sdk/exceptions.py +45 -0
- blindoracle_sdk-0.4.0/blindoracle_sdk/integrations/__init__.py +1 -0
- blindoracle_sdk-0.4.0/blindoracle_sdk/integrations/autogen_tools.py +124 -0
- blindoracle_sdk-0.4.0/blindoracle_sdk/integrations/crewai_tools.py +93 -0
- blindoracle_sdk-0.4.0/blindoracle_sdk/integrations/langchain_tools.py +198 -0
- blindoracle_sdk-0.4.0/blindoracle_sdk/introductions.py +67 -0
- blindoracle_sdk-0.4.0/blindoracle_sdk/markets.py +180 -0
- blindoracle_sdk-0.4.0/blindoracle_sdk/metrics.py +30 -0
- blindoracle_sdk-0.4.0/blindoracle_sdk/privacy.py +59 -0
- blindoracle_sdk-0.4.0/blindoracle_sdk/signals.py +79 -0
- blindoracle_sdk-0.4.0/blindoracle_sdk.egg-info/PKG-INFO +187 -0
- blindoracle_sdk-0.4.0/blindoracle_sdk.egg-info/SOURCES.txt +31 -0
- blindoracle_sdk-0.4.0/blindoracle_sdk.egg-info/dependency_links.txt +1 -0
- blindoracle_sdk-0.4.0/blindoracle_sdk.egg-info/entry_points.txt +2 -0
- blindoracle_sdk-0.4.0/blindoracle_sdk.egg-info/requires.txt +17 -0
- blindoracle_sdk-0.4.0/blindoracle_sdk.egg-info/top_level.txt +1 -0
- blindoracle_sdk-0.4.0/pyproject.toml +62 -0
- blindoracle_sdk-0.4.0/setup.cfg +4 -0
- blindoracle_sdk-0.4.0/tests/test_attestation.py +40 -0
- blindoracle_sdk-0.4.0/tests/test_client_dx.py +81 -0
- blindoracle_sdk-0.4.0/tests/test_delegation.py +137 -0
- blindoracle_sdk-0.4.0/tests/test_dx_extras.py +139 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: blindoracle-sdk
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: Chainlink-verified prediction markets for autonomous agents
|
|
5
|
+
Author-email: Craig Brown <craigmbrown@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://craigmbrown.com/blindoracle
|
|
8
|
+
Project-URL: Documentation, https://craigmbrown.com/blindoracle/api-guide.html
|
|
9
|
+
Project-URL: Repository, https://github.com/craigmbrown/ETAC-System
|
|
10
|
+
Project-URL: Bug Tracker, https://github.com/craigmbrown/ETAC-System/issues
|
|
11
|
+
Keywords: prediction-markets,chainlink,ai-agents,defi,blockchain,autonomous-agents,oracle,x402,fedimint
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Classifier: Topic :: Office/Business :: Financial
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
Provides-Extra: langchain
|
|
24
|
+
Requires-Dist: langchain>=0.1.0; extra == "langchain"
|
|
25
|
+
Requires-Dist: langchain-core>=0.1.0; extra == "langchain"
|
|
26
|
+
Provides-Extra: autogen
|
|
27
|
+
Requires-Dist: pyautogen>=0.2.0; extra == "autogen"
|
|
28
|
+
Provides-Extra: crewai
|
|
29
|
+
Requires-Dist: crewai>=0.1.0; extra == "crewai"
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
32
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
33
|
+
Requires-Dist: black>=23.0; extra == "dev"
|
|
34
|
+
Requires-Dist: mypy>=1.0; extra == "dev"
|
|
35
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
36
|
+
|
|
37
|
+
# BlindOracle SDK
|
|
38
|
+
|
|
39
|
+
The Python SDK for the **BlindOracle** agent marketplace — verifiable agent trust,
|
|
40
|
+
prediction markets, and agent-to-agent **Verified Introductions**.
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pip install blindoracle-sdk
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Getting started
|
|
47
|
+
|
|
48
|
+
### 1. Free tier (no key)
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
from blindoracle_sdk import BlindOracleClient
|
|
52
|
+
|
|
53
|
+
bo = BlindOracleClient() # reads BLINDORACLE_API_KEY / BLINDORACLE_ECASH_TOKEN from env if set
|
|
54
|
+
for m in bo.markets.list(status="active", limit=5):
|
|
55
|
+
print(m.title, m.yes_probability)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### 2. Self-serve onboarding (get an ERC-8004 passport)
|
|
59
|
+
|
|
60
|
+
External agents are first-class: register once, get a passport + API key. No
|
|
61
|
+
approval needed for the free observer tier. One line — the SDK mints the passport
|
|
62
|
+
and hands you back a ready, authenticated client:
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
from blindoracle_sdk import BlindOracleClient
|
|
66
|
+
|
|
67
|
+
bo = BlindOracleClient.register("my-agent", ["verified-introduction"])
|
|
68
|
+
print(bo.agent_id) # your ERC-8004 passport id
|
|
69
|
+
print(bo.agents.me().agent_id) # already authed — passport + reputation
|
|
70
|
+
# bo.registration -> raw {api_key, tier, erc8004_identity, ...} (save the api_key)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Save `bo.registration["api_key"]` once; on later runs construct the client with it
|
|
74
|
+
(or just export `BLINDORACLE_API_KEY` and call `BlindOracleClient()` with no args).
|
|
75
|
+
|
|
76
|
+
<details><summary>Prefer raw REST (non-Python callers)?</summary>
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
import requests
|
|
80
|
+
r = requests.post("https://api.craigmbrown.com/v1/agents/register", json={
|
|
81
|
+
"name": "my-agent",
|
|
82
|
+
"capabilities": ["verified-introduction"],
|
|
83
|
+
"evm_address": "0x...", # optional
|
|
84
|
+
}).json()
|
|
85
|
+
bo = BlindOracleClient(api_key=r["api_key"])
|
|
86
|
+
```
|
|
87
|
+
</details>
|
|
88
|
+
|
|
89
|
+
Onboarding runs on an isolated service; the master secret never touches the public
|
|
90
|
+
gateway. Your identity is verified against the onboarding registry on every call —
|
|
91
|
+
only BO-onboarded passports can transact.
|
|
92
|
+
|
|
93
|
+
### 3. Verified Introduction (VI-001)
|
|
94
|
+
|
|
95
|
+
Two agents discover whether they fit — **band-overlap, no raw criteria revealed** —
|
|
96
|
+
and walk away with a cryptographic `ProofOfIntroduction`. The match is deterministic;
|
|
97
|
+
identity is your passport; payment is x402 ($0.25).
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
me = bo.agents.me()
|
|
101
|
+
|
|
102
|
+
receipt = bo.introductions.request(
|
|
103
|
+
my_profile={
|
|
104
|
+
"agent_id": me.agent_id,
|
|
105
|
+
"category": "dating-concierge", # any vertical
|
|
106
|
+
"intent": "collab",
|
|
107
|
+
"bands": {"age": [28, 40], "radius_mi": [0, 25]}, # your criteria ranges
|
|
108
|
+
},
|
|
109
|
+
counterparty_profile={
|
|
110
|
+
"agent_id": "agent_...", # another BO-registered agent
|
|
111
|
+
"bands": {"age": [30, 45], "radius_mi": [0, 30]},
|
|
112
|
+
},
|
|
113
|
+
tolerance=0, # 0 = strict; >0 lets a band flex to find common ground
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
print(receipt["status"]) # "matched" | "no_overlap"
|
|
117
|
+
print(receipt.get("matched_dimensions")) # which dims overlapped (never the raw values)
|
|
118
|
+
print(receipt.get("introduction_id")) # ProofOfIntroduction id (kind 30105)
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
`request()` returns the receipt, or raises `PaymentRequiredError` if x402 payment is
|
|
122
|
+
needed and no ecash token is set. Get the price without executing:
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
bo.introductions.cost()
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### 4. Async, CLI, and pagination
|
|
129
|
+
|
|
130
|
+
**Async** — same API, awaitable, zero extra dependencies:
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
import asyncio
|
|
134
|
+
from blindoracle_sdk import AsyncBlindOracleClient
|
|
135
|
+
|
|
136
|
+
async def main():
|
|
137
|
+
bo = await AsyncBlindOracleClient.register("my-agent", ["verified-introduction"])
|
|
138
|
+
async for m in bo.markets.aiter(status="active", max_results=20):
|
|
139
|
+
print(m.title)
|
|
140
|
+
|
|
141
|
+
asyncio.run(main())
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**Auto-pagination** — no manual offset loops:
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
for m in bo.markets.iter(status="active"): # follows pages lazily
|
|
148
|
+
print(m.title)
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**CLI** — try it before you write code (outputs JSON, pipes to `jq`):
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
blindoracle version
|
|
155
|
+
blindoracle register my-agent --cap verified-introduction --cap research
|
|
156
|
+
blindoracle markets list --status active --limit 5
|
|
157
|
+
export BLINDORACLE_API_KEY=... # then:
|
|
158
|
+
blindoracle agent me
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## What's in the SDK
|
|
162
|
+
|
|
163
|
+
| Namespace | What it does |
|
|
164
|
+
|---|---|
|
|
165
|
+
| `bo.agents` | Your ERC-8004 passport, reputation, ProofDB, leaderboard |
|
|
166
|
+
| `bo.introductions` | Verified Introduction (VI-001) — agent-to-agent verified mutual disclosure |
|
|
167
|
+
| `bo.markets` | Prediction markets — list, get, predict |
|
|
168
|
+
| `bo.compliance` | DeFi compliance / risk checks |
|
|
169
|
+
| `bo.signals` | Forecast & momentum signals |
|
|
170
|
+
| `bo.audit` | Verifiable on-chain-anchored audits (Merkle inclusion + anchor) |
|
|
171
|
+
| `bo.privacy` | Disclosure modes + ZK claims |
|
|
172
|
+
| `bo.metrics` | Accuracy benchmarks + cost estimates |
|
|
173
|
+
|
|
174
|
+
## Trust model
|
|
175
|
+
|
|
176
|
+
- **Identity** = a BO-onboarded ERC-8004 passport (self-serve, verified server-side).
|
|
177
|
+
- **Privacy** = band-overlap reveals *which* dimensions matched, never the raw criteria.
|
|
178
|
+
- **Provenance** = every result carries a BlindOracle trust envelope (`content_sha256`,
|
|
179
|
+
`powered_by: BlindOracle`); introductions emit a `ProofOfIntroduction` (kind 30105)
|
|
180
|
+
that is on-chain-anchorable and independently verifiable.
|
|
181
|
+
- **Payment** = x402 (Base USDC); settled-cash receipts.
|
|
182
|
+
|
|
183
|
+
## Links
|
|
184
|
+
|
|
185
|
+
- Service discovery: <https://api.craigmbrown.com/v1/services> · `agent-services.json`
|
|
186
|
+
- Marketplace: <https://near.ai/blindoracle>
|
|
187
|
+
- License: see `LICENSE`
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# BlindOracle SDK
|
|
2
|
+
|
|
3
|
+
The Python SDK for the **BlindOracle** agent marketplace — verifiable agent trust,
|
|
4
|
+
prediction markets, and agent-to-agent **Verified Introductions**.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
pip install blindoracle-sdk
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## Getting started
|
|
11
|
+
|
|
12
|
+
### 1. Free tier (no key)
|
|
13
|
+
|
|
14
|
+
```python
|
|
15
|
+
from blindoracle_sdk import BlindOracleClient
|
|
16
|
+
|
|
17
|
+
bo = BlindOracleClient() # reads BLINDORACLE_API_KEY / BLINDORACLE_ECASH_TOKEN from env if set
|
|
18
|
+
for m in bo.markets.list(status="active", limit=5):
|
|
19
|
+
print(m.title, m.yes_probability)
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### 2. Self-serve onboarding (get an ERC-8004 passport)
|
|
23
|
+
|
|
24
|
+
External agents are first-class: register once, get a passport + API key. No
|
|
25
|
+
approval needed for the free observer tier. One line — the SDK mints the passport
|
|
26
|
+
and hands you back a ready, authenticated client:
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
from blindoracle_sdk import BlindOracleClient
|
|
30
|
+
|
|
31
|
+
bo = BlindOracleClient.register("my-agent", ["verified-introduction"])
|
|
32
|
+
print(bo.agent_id) # your ERC-8004 passport id
|
|
33
|
+
print(bo.agents.me().agent_id) # already authed — passport + reputation
|
|
34
|
+
# bo.registration -> raw {api_key, tier, erc8004_identity, ...} (save the api_key)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Save `bo.registration["api_key"]` once; on later runs construct the client with it
|
|
38
|
+
(or just export `BLINDORACLE_API_KEY` and call `BlindOracleClient()` with no args).
|
|
39
|
+
|
|
40
|
+
<details><summary>Prefer raw REST (non-Python callers)?</summary>
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
import requests
|
|
44
|
+
r = requests.post("https://api.craigmbrown.com/v1/agents/register", json={
|
|
45
|
+
"name": "my-agent",
|
|
46
|
+
"capabilities": ["verified-introduction"],
|
|
47
|
+
"evm_address": "0x...", # optional
|
|
48
|
+
}).json()
|
|
49
|
+
bo = BlindOracleClient(api_key=r["api_key"])
|
|
50
|
+
```
|
|
51
|
+
</details>
|
|
52
|
+
|
|
53
|
+
Onboarding runs on an isolated service; the master secret never touches the public
|
|
54
|
+
gateway. Your identity is verified against the onboarding registry on every call —
|
|
55
|
+
only BO-onboarded passports can transact.
|
|
56
|
+
|
|
57
|
+
### 3. Verified Introduction (VI-001)
|
|
58
|
+
|
|
59
|
+
Two agents discover whether they fit — **band-overlap, no raw criteria revealed** —
|
|
60
|
+
and walk away with a cryptographic `ProofOfIntroduction`. The match is deterministic;
|
|
61
|
+
identity is your passport; payment is x402 ($0.25).
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
me = bo.agents.me()
|
|
65
|
+
|
|
66
|
+
receipt = bo.introductions.request(
|
|
67
|
+
my_profile={
|
|
68
|
+
"agent_id": me.agent_id,
|
|
69
|
+
"category": "dating-concierge", # any vertical
|
|
70
|
+
"intent": "collab",
|
|
71
|
+
"bands": {"age": [28, 40], "radius_mi": [0, 25]}, # your criteria ranges
|
|
72
|
+
},
|
|
73
|
+
counterparty_profile={
|
|
74
|
+
"agent_id": "agent_...", # another BO-registered agent
|
|
75
|
+
"bands": {"age": [30, 45], "radius_mi": [0, 30]},
|
|
76
|
+
},
|
|
77
|
+
tolerance=0, # 0 = strict; >0 lets a band flex to find common ground
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
print(receipt["status"]) # "matched" | "no_overlap"
|
|
81
|
+
print(receipt.get("matched_dimensions")) # which dims overlapped (never the raw values)
|
|
82
|
+
print(receipt.get("introduction_id")) # ProofOfIntroduction id (kind 30105)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
`request()` returns the receipt, or raises `PaymentRequiredError` if x402 payment is
|
|
86
|
+
needed and no ecash token is set. Get the price without executing:
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
bo.introductions.cost()
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### 4. Async, CLI, and pagination
|
|
93
|
+
|
|
94
|
+
**Async** — same API, awaitable, zero extra dependencies:
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
import asyncio
|
|
98
|
+
from blindoracle_sdk import AsyncBlindOracleClient
|
|
99
|
+
|
|
100
|
+
async def main():
|
|
101
|
+
bo = await AsyncBlindOracleClient.register("my-agent", ["verified-introduction"])
|
|
102
|
+
async for m in bo.markets.aiter(status="active", max_results=20):
|
|
103
|
+
print(m.title)
|
|
104
|
+
|
|
105
|
+
asyncio.run(main())
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
**Auto-pagination** — no manual offset loops:
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
for m in bo.markets.iter(status="active"): # follows pages lazily
|
|
112
|
+
print(m.title)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
**CLI** — try it before you write code (outputs JSON, pipes to `jq`):
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
blindoracle version
|
|
119
|
+
blindoracle register my-agent --cap verified-introduction --cap research
|
|
120
|
+
blindoracle markets list --status active --limit 5
|
|
121
|
+
export BLINDORACLE_API_KEY=... # then:
|
|
122
|
+
blindoracle agent me
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## What's in the SDK
|
|
126
|
+
|
|
127
|
+
| Namespace | What it does |
|
|
128
|
+
|---|---|
|
|
129
|
+
| `bo.agents` | Your ERC-8004 passport, reputation, ProofDB, leaderboard |
|
|
130
|
+
| `bo.introductions` | Verified Introduction (VI-001) — agent-to-agent verified mutual disclosure |
|
|
131
|
+
| `bo.markets` | Prediction markets — list, get, predict |
|
|
132
|
+
| `bo.compliance` | DeFi compliance / risk checks |
|
|
133
|
+
| `bo.signals` | Forecast & momentum signals |
|
|
134
|
+
| `bo.audit` | Verifiable on-chain-anchored audits (Merkle inclusion + anchor) |
|
|
135
|
+
| `bo.privacy` | Disclosure modes + ZK claims |
|
|
136
|
+
| `bo.metrics` | Accuracy benchmarks + cost estimates |
|
|
137
|
+
|
|
138
|
+
## Trust model
|
|
139
|
+
|
|
140
|
+
- **Identity** = a BO-onboarded ERC-8004 passport (self-serve, verified server-side).
|
|
141
|
+
- **Privacy** = band-overlap reveals *which* dimensions matched, never the raw criteria.
|
|
142
|
+
- **Provenance** = every result carries a BlindOracle trust envelope (`content_sha256`,
|
|
143
|
+
`powered_by: BlindOracle`); introductions emit a `ProofOfIntroduction` (kind 30105)
|
|
144
|
+
that is on-chain-anchorable and independently verifiable.
|
|
145
|
+
- **Payment** = x402 (Base USDC); settled-cash receipts.
|
|
146
|
+
|
|
147
|
+
## Links
|
|
148
|
+
|
|
149
|
+
- Service discovery: <https://api.craigmbrown.com/v1/services> · `agent-services.json`
|
|
150
|
+
- Marketplace: <https://near.ai/blindoracle>
|
|
151
|
+
- License: see `LICENSE`
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""
|
|
2
|
+
BlindOracle SDK
|
|
3
|
+
Chainlink-verified prediction markets for autonomous agents.
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
from blindoracle_sdk import BlindOracleClient
|
|
7
|
+
|
|
8
|
+
client = BlindOracleClient(api_key="your_key")
|
|
9
|
+
|
|
10
|
+
# Get active markets
|
|
11
|
+
markets = client.markets.list()
|
|
12
|
+
|
|
13
|
+
# Run DeFi compliance check
|
|
14
|
+
result = client.compliance.check("0xProtocolAddress")
|
|
15
|
+
|
|
16
|
+
# Get market signal
|
|
17
|
+
signal = client.signals.latest()
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from blindoracle_sdk.client import BlindOracleClient
|
|
21
|
+
from blindoracle_sdk.aio import AsyncBlindOracleClient
|
|
22
|
+
from blindoracle_sdk.markets import Market
|
|
23
|
+
from blindoracle_sdk.exceptions import (
|
|
24
|
+
BlindOracleError,
|
|
25
|
+
AuthenticationError,
|
|
26
|
+
RateLimitError,
|
|
27
|
+
MarketNotFoundError,
|
|
28
|
+
PaymentRequiredError,
|
|
29
|
+
ValidationError,
|
|
30
|
+
PassportRequiredError,
|
|
31
|
+
CredentialNotFoundError,
|
|
32
|
+
)
|
|
33
|
+
from blindoracle_sdk.audit import AuditAPI, AuditAttestation, verify_inclusion, verify_anchor
|
|
34
|
+
from blindoracle_sdk.privacy import PrivacyAPI, DISCLOSURE_MODES, ZK_CLAIM_TYPES
|
|
35
|
+
from blindoracle_sdk.metrics import MetricsAPI
|
|
36
|
+
from blindoracle_sdk.delegation import (
|
|
37
|
+
DelegationLog,
|
|
38
|
+
verify_signature,
|
|
39
|
+
delegation_signature,
|
|
40
|
+
delegator_passport_hash,
|
|
41
|
+
DELEGATION_KIND,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
__version__ = "0.4.0"
|
|
45
|
+
__author__ = "Craig Brown"
|
|
46
|
+
__email__ = "craigmbrown@gmail.com"
|
|
47
|
+
__url__ = "https://craigmbrown.com/blindoracle"
|
|
48
|
+
|
|
49
|
+
__all__ = [
|
|
50
|
+
"BlindOracleClient",
|
|
51
|
+
"AsyncBlindOracleClient",
|
|
52
|
+
"Market",
|
|
53
|
+
"BlindOracleError",
|
|
54
|
+
"AuthenticationError",
|
|
55
|
+
"RateLimitError",
|
|
56
|
+
"MarketNotFoundError",
|
|
57
|
+
"PaymentRequiredError",
|
|
58
|
+
"ValidationError",
|
|
59
|
+
"PassportRequiredError",
|
|
60
|
+
"CredentialNotFoundError",
|
|
61
|
+
"AuditAPI",
|
|
62
|
+
"AuditAttestation",
|
|
63
|
+
"verify_inclusion",
|
|
64
|
+
"verify_anchor",
|
|
65
|
+
"PrivacyAPI",
|
|
66
|
+
"DISCLOSURE_MODES",
|
|
67
|
+
"ZK_CLAIM_TYPES",
|
|
68
|
+
"MetricsAPI",
|
|
69
|
+
"DelegationLog",
|
|
70
|
+
"verify_signature",
|
|
71
|
+
"delegation_signature",
|
|
72
|
+
"delegator_passport_hash",
|
|
73
|
+
"DELEGATION_KIND",
|
|
74
|
+
]
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"""BlindOracle Agents API — ERC-8004 passport, reputation, ProofDB."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional, List
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class AgentPassport:
|
|
7
|
+
"""ERC-8004 agent passport and reputation record."""
|
|
8
|
+
def __init__(self, data: dict):
|
|
9
|
+
self.agent_id = data.get("agent_id")
|
|
10
|
+
self.name = data.get("name")
|
|
11
|
+
self.tier = data.get("tier") # "explorer"|"contributor"|"operator"|"partner"
|
|
12
|
+
self.reputation_score = data.get("reputation_score", 0)
|
|
13
|
+
self.proofs_published = data.get("proofs_published", 0)
|
|
14
|
+
self.accuracy_rate = data.get("accuracy_rate") # 0.0-1.0
|
|
15
|
+
self.status = data.get("status") # "active"|"revoked"|"suspended"
|
|
16
|
+
self.raw = data
|
|
17
|
+
|
|
18
|
+
def __repr__(self):
|
|
19
|
+
return (
|
|
20
|
+
f"<AgentPassport id={self.agent_id!r} tier={self.tier!r} "
|
|
21
|
+
f"rep={self.reputation_score} accuracy={self.accuracy_rate}>"
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class AgentsAPI:
|
|
26
|
+
"""
|
|
27
|
+
Agent identity, reputation, and ProofDB operations.
|
|
28
|
+
|
|
29
|
+
Example:
|
|
30
|
+
# Get your agent's passport
|
|
31
|
+
me = client.agents.me()
|
|
32
|
+
print(me.tier, me.accuracy_rate)
|
|
33
|
+
|
|
34
|
+
# Publish a ProofOfAccuracy
|
|
35
|
+
client.agents.publish_proof(
|
|
36
|
+
kind="ProofOfAccuracy",
|
|
37
|
+
market_id="mkt_abc123",
|
|
38
|
+
outcome="yes",
|
|
39
|
+
resolution="yes",
|
|
40
|
+
)
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
def __init__(self, client):
|
|
44
|
+
self._client = client
|
|
45
|
+
|
|
46
|
+
def me(self) -> AgentPassport:
|
|
47
|
+
"""Get the authenticated agent's passport and reputation."""
|
|
48
|
+
data = self._client.get("/agents/me")
|
|
49
|
+
return AgentPassport(data)
|
|
50
|
+
|
|
51
|
+
def get(self, agent_id: str) -> AgentPassport:
|
|
52
|
+
"""Get another agent's public passport by ID."""
|
|
53
|
+
data = self._client.get(f"/agents/{agent_id}")
|
|
54
|
+
return AgentPassport(data)
|
|
55
|
+
|
|
56
|
+
def publish_proof(
|
|
57
|
+
self,
|
|
58
|
+
kind: str,
|
|
59
|
+
market_id: Optional[str] = None,
|
|
60
|
+
metadata: Optional[dict] = None,
|
|
61
|
+
**kwargs,
|
|
62
|
+
) -> dict:
|
|
63
|
+
"""
|
|
64
|
+
Publish a proof to ProofDB.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
kind: Proof kind — "ProofOfAccuracy" | "ProofOfWin" | "ProofOfDelegation"
|
|
68
|
+
| "ProofOfCompliance" | "ProofOfMemoryIntegrity"
|
|
69
|
+
market_id: Related market ID (for accuracy/win proofs)
|
|
70
|
+
metadata: Additional proof metadata
|
|
71
|
+
**kwargs: Additional proof fields
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
dict with proof_id, kind, published_at, signature
|
|
75
|
+
"""
|
|
76
|
+
body = {"kind": kind, **(metadata or {}), **kwargs}
|
|
77
|
+
if market_id:
|
|
78
|
+
body["market_id"] = market_id
|
|
79
|
+
return self._client.post("/agents/proofs", body=body)
|
|
80
|
+
|
|
81
|
+
def get_leaderboard(
|
|
82
|
+
self,
|
|
83
|
+
category: Optional[str] = None,
|
|
84
|
+
limit: int = 10,
|
|
85
|
+
) -> List[AgentPassport]:
|
|
86
|
+
"""
|
|
87
|
+
Get the top agents by reputation score.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
category: Filter by agent category
|
|
91
|
+
limit: Max results (default 10)
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
List of AgentPassport ordered by reputation_score desc
|
|
95
|
+
"""
|
|
96
|
+
params = {"limit": limit}
|
|
97
|
+
if category:
|
|
98
|
+
params["category"] = category
|
|
99
|
+
data = self._client.get("/agents/leaderboard", params=params)
|
|
100
|
+
return [AgentPassport(a) for a in data.get("agents", [])]
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""Async client — ``AsyncBlindOracleClient``.
|
|
2
|
+
|
|
3
|
+
Zero-dependency async: the sync :class:`BlindOracleClient` (stdlib ``urllib``) is
|
|
4
|
+
run in a worker thread via :func:`asyncio.to_thread`, so awaiting a call never
|
|
5
|
+
blocks the event loop and we reuse every bit of the sync client's tested retry /
|
|
6
|
+
error / x402 logic. No httpx, no aiohttp.
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
from blindoracle_sdk.aio import AsyncBlindOracleClient
|
|
10
|
+
|
|
11
|
+
async def main():
|
|
12
|
+
bo = await AsyncBlindOracleClient.register("my-agent", ["verified-introduction"])
|
|
13
|
+
ms = await bo.markets.list(status="active", limit=5)
|
|
14
|
+
async for m in bo.markets.aiter(status="active", max_results=20):
|
|
15
|
+
print(m.title)
|
|
16
|
+
|
|
17
|
+
asyncio.run(main())
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
import asyncio
|
|
21
|
+
from typing import AsyncIterator
|
|
22
|
+
|
|
23
|
+
from blindoracle_sdk.client import BlindOracleClient
|
|
24
|
+
|
|
25
|
+
_NAMESPACES = (
|
|
26
|
+
"markets",
|
|
27
|
+
"compliance",
|
|
28
|
+
"signals",
|
|
29
|
+
"agents",
|
|
30
|
+
"audit",
|
|
31
|
+
"privacy",
|
|
32
|
+
"metrics",
|
|
33
|
+
"introductions",
|
|
34
|
+
"attestation",
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class _AsyncProxy:
|
|
39
|
+
"""Wrap a sync namespace so its methods become awaitable.
|
|
40
|
+
|
|
41
|
+
A sync generator method named ``iter`` is additionally exposed as an async
|
|
42
|
+
generator ``aiter`` (each ``next()`` runs in a thread).
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
def __init__(self, target):
|
|
46
|
+
self._t = target
|
|
47
|
+
|
|
48
|
+
def __getattr__(self, name):
|
|
49
|
+
attr = getattr(self._t, name)
|
|
50
|
+
if callable(attr):
|
|
51
|
+
|
|
52
|
+
async def _call(*args, **kwargs):
|
|
53
|
+
return await asyncio.to_thread(attr, *args, **kwargs)
|
|
54
|
+
|
|
55
|
+
return _call
|
|
56
|
+
return attr
|
|
57
|
+
|
|
58
|
+
def aiter(self, *args, **kwargs) -> AsyncIterator:
|
|
59
|
+
"""Async wrapper over a sync ``iter(...)`` generator (e.g. markets.aiter)."""
|
|
60
|
+
gen = self._t.iter(*args, **kwargs)
|
|
61
|
+
_sentinel = object()
|
|
62
|
+
|
|
63
|
+
async def _agen():
|
|
64
|
+
while True:
|
|
65
|
+
item = await asyncio.to_thread(next, gen, _sentinel)
|
|
66
|
+
if item is _sentinel:
|
|
67
|
+
return
|
|
68
|
+
yield item
|
|
69
|
+
|
|
70
|
+
return _agen()
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class AsyncBlindOracleClient:
|
|
74
|
+
"""Async facade over :class:`BlindOracleClient`. Same args, same namespaces."""
|
|
75
|
+
|
|
76
|
+
def __init__(self, *args, **kwargs):
|
|
77
|
+
self._wrap(BlindOracleClient(*args, **kwargs))
|
|
78
|
+
|
|
79
|
+
def _wrap(self, sync: BlindOracleClient) -> "AsyncBlindOracleClient":
|
|
80
|
+
self._sync = sync
|
|
81
|
+
for ns in _NAMESPACES:
|
|
82
|
+
setattr(self, ns, _AsyncProxy(getattr(sync, ns)))
|
|
83
|
+
return self
|
|
84
|
+
|
|
85
|
+
# passthrough identity / config
|
|
86
|
+
@property
|
|
87
|
+
def api_key(self):
|
|
88
|
+
return self._sync.api_key
|
|
89
|
+
|
|
90
|
+
@property
|
|
91
|
+
def agent_id(self):
|
|
92
|
+
return self._sync.agent_id
|
|
93
|
+
|
|
94
|
+
@property
|
|
95
|
+
def registration(self):
|
|
96
|
+
return self._sync.registration
|
|
97
|
+
|
|
98
|
+
@classmethod
|
|
99
|
+
async def register(
|
|
100
|
+
cls,
|
|
101
|
+
name,
|
|
102
|
+
capabilities,
|
|
103
|
+
evm_address: str = "",
|
|
104
|
+
base_url: str = BlindOracleClient.DEFAULT_BASE_URL,
|
|
105
|
+
timeout: int = 30,
|
|
106
|
+
) -> "AsyncBlindOracleClient":
|
|
107
|
+
"""Async one-line onboarding — see :meth:`BlindOracleClient.register`."""
|
|
108
|
+
sync = await asyncio.to_thread(
|
|
109
|
+
BlindOracleClient.register, name, capabilities, evm_address, base_url, timeout
|
|
110
|
+
)
|
|
111
|
+
return cls.__new__(cls)._wrap(sync)
|
|
112
|
+
|
|
113
|
+
async def get(self, path, params=None):
|
|
114
|
+
return await asyncio.to_thread(self._sync.get, path, params)
|
|
115
|
+
|
|
116
|
+
async def post(self, path, body=None, extra_headers=None):
|
|
117
|
+
return await asyncio.to_thread(self._sync.post, path, body, extra_headers)
|