tensorfeed 1.2.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.
- tensorfeed-1.2.0/PKG-INFO +238 -0
- tensorfeed-1.2.0/README.md +211 -0
- tensorfeed-1.2.0/pyproject.toml +50 -0
- tensorfeed-1.2.0/setup.cfg +4 -0
- tensorfeed-1.2.0/tensorfeed/__init__.py +24 -0
- tensorfeed-1.2.0/tensorfeed/client.py +407 -0
- tensorfeed-1.2.0/tensorfeed/web3_signer.py +175 -0
- tensorfeed-1.2.0/tensorfeed.egg-info/PKG-INFO +238 -0
- tensorfeed-1.2.0/tensorfeed.egg-info/SOURCES.txt +10 -0
- tensorfeed-1.2.0/tensorfeed.egg-info/dependency_links.txt +1 -0
- tensorfeed-1.2.0/tensorfeed.egg-info/requires.txt +4 -0
- tensorfeed-1.2.0/tensorfeed.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tensorfeed
|
|
3
|
+
Version: 1.2.0
|
|
4
|
+
Summary: Python SDK for the TensorFeed.ai API: AI news, status, model pricing, and agent-payable premium routing (USDC on Base, optional auto-send via web3)
|
|
5
|
+
Author-email: "TensorFeed.ai" <feedback@tensorfeed.ai>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://tensorfeed.ai
|
|
8
|
+
Project-URL: Documentation, https://tensorfeed.ai/developers/agent-payments
|
|
9
|
+
Project-URL: Repository, https://github.com/RipperMercs/tensorfeed
|
|
10
|
+
Project-URL: Bug Tracker, https://github.com/RipperMercs/tensorfeed/issues
|
|
11
|
+
Project-URL: Changelog, https://tensorfeed.ai/changelog
|
|
12
|
+
Keywords: tensorfeed,ai,agents,x402,usdc,base,model-routing,llm,api,stablecoin
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Requires-Python: >=3.9
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
Provides-Extra: web3
|
|
25
|
+
Requires-Dist: web3>=6.0; extra == "web3"
|
|
26
|
+
Requires-Dist: eth-account>=0.10; extra == "web3"
|
|
27
|
+
|
|
28
|
+
# tensorfeed
|
|
29
|
+
|
|
30
|
+
Python SDK for the [TensorFeed.ai](https://tensorfeed.ai) API.
|
|
31
|
+
|
|
32
|
+
Free endpoints (news, status, models, benchmarks, history, routing preview) need no auth. The premium tier (top-N routing, plus more endpoints landing later) is paid via USDC on Base. No accounts, no API keys, no traditional payment processors.
|
|
33
|
+
|
|
34
|
+
## Install
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pip install tensorfeed
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Stdlib-only. No external dependencies.
|
|
41
|
+
|
|
42
|
+
## Free Tier
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
from tensorfeed import TensorFeed
|
|
46
|
+
|
|
47
|
+
tf = TensorFeed()
|
|
48
|
+
|
|
49
|
+
# News
|
|
50
|
+
for article in tf.news(category="research", limit=10)["articles"]:
|
|
51
|
+
print(article["title"])
|
|
52
|
+
|
|
53
|
+
# Live AI service status
|
|
54
|
+
for svc in tf.status()["services"]:
|
|
55
|
+
print(f'{svc["name"]}: {svc["status"]}')
|
|
56
|
+
|
|
57
|
+
# Is a service down?
|
|
58
|
+
print(tf.is_down("claude"))
|
|
59
|
+
|
|
60
|
+
# Model pricing and benchmarks
|
|
61
|
+
print(tf.models())
|
|
62
|
+
print(tf.benchmarks())
|
|
63
|
+
|
|
64
|
+
# Daily history snapshots (the moat)
|
|
65
|
+
print(tf.history()) # list of available dates
|
|
66
|
+
print(tf.history_snapshot("2026-04-27", "pricing"))
|
|
67
|
+
|
|
68
|
+
# Free routing preview (top-1 model, 5 calls/day per IP)
|
|
69
|
+
preview = tf.routing_preview(task="code")
|
|
70
|
+
print(preview["recommendation"])
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Premium Tier (paid, USDC on Base)
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
from tensorfeed import TensorFeed, PaymentRequired
|
|
77
|
+
|
|
78
|
+
tf = TensorFeed()
|
|
79
|
+
|
|
80
|
+
# Step 1: get a 30-minute quote
|
|
81
|
+
quote = tf.buy_credits(amount_usd=1.00)
|
|
82
|
+
print(f"Send {quote['amount_usd']} USDC on Base to {quote['wallet']}")
|
|
83
|
+
print(f"Memo: {quote['memo']} (expires in {quote['ttl_seconds']}s)")
|
|
84
|
+
print(f"Will get: {quote['credits']} credits")
|
|
85
|
+
|
|
86
|
+
# Step 2: send the USDC tx with your wallet
|
|
87
|
+
# (auto-send via web3 is on the roadmap; for v1.1 you sign and send manually)
|
|
88
|
+
|
|
89
|
+
# Step 3: confirm with the tx hash
|
|
90
|
+
result = tf.confirm(tx_hash="0xYOUR_TX_HASH", nonce=quote["memo"])
|
|
91
|
+
print(f"Got {result['credits']} credits, token: {result['token']}")
|
|
92
|
+
# The token is also stored on `tf` automatically; routing() will use it.
|
|
93
|
+
|
|
94
|
+
# Step 4: call premium endpoints
|
|
95
|
+
rec = tf.routing(task="code", budget=5.0, top_n=5)
|
|
96
|
+
for r in rec["recommendations"]:
|
|
97
|
+
print(f'#{r["rank"]}: {r["model"]["name"]} (score: {r["composite_score"]:.2f})')
|
|
98
|
+
|
|
99
|
+
# Custom routing weights
|
|
100
|
+
rec = tf.routing(
|
|
101
|
+
task="general",
|
|
102
|
+
weights={"quality": 0.6, "cost": 0.3, "availability": 0.1, "latency": 0.0},
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
# Check remaining credits
|
|
106
|
+
print(tf.balance())
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Auto-Send (optional, web3 extra)
|
|
110
|
+
|
|
111
|
+
The base SDK keeps you dependency-free, but if you want to skip the manual tx step, install with the `web3` extra:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
pip install 'tensorfeed[web3]'
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Then `tf.purchase_credits()` quotes, signs, broadcasts, and confirms in one call:
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
import os
|
|
121
|
+
from tensorfeed import TensorFeed
|
|
122
|
+
|
|
123
|
+
tf = TensorFeed()
|
|
124
|
+
|
|
125
|
+
result = tf.purchase_credits(
|
|
126
|
+
amount_usd=1.00,
|
|
127
|
+
private_key=os.environ["TENSORFEED_PRIVATE_KEY"], # NEVER hardcode
|
|
128
|
+
# rpc_url="https://base-mainnet.g.alchemy.com/v2/<key>", # optional
|
|
129
|
+
)
|
|
130
|
+
print(result["token"]) # auto-stored on tf
|
|
131
|
+
print(result["tx_hash"]) # for your records
|
|
132
|
+
print(result["credits"]) # how many credits were minted
|
|
133
|
+
|
|
134
|
+
# Token already on tf, so:
|
|
135
|
+
rec = tf.routing(task="code")
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Security: read the key from a secret manager or env var. Never commit it. The raw key is held in memory only for the duration of one signing call.
|
|
139
|
+
|
|
140
|
+
## Reusing a Token Across Sessions
|
|
141
|
+
|
|
142
|
+
Save the token after `confirm()`. Reuse it next time:
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
# Save once
|
|
146
|
+
token = result["token"]
|
|
147
|
+
|
|
148
|
+
# Reuse in another process / job
|
|
149
|
+
tf = TensorFeed(token=token)
|
|
150
|
+
print(tf.balance())
|
|
151
|
+
rec = tf.routing(task="code")
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Error Handling
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
from tensorfeed import TensorFeed, PaymentRequired, RateLimited, TensorFeedError
|
|
158
|
+
|
|
159
|
+
tf = TensorFeed(token="bad_token")
|
|
160
|
+
try:
|
|
161
|
+
tf.routing(task="code")
|
|
162
|
+
except PaymentRequired as e:
|
|
163
|
+
# 402: token invalid, expired, or out of credits
|
|
164
|
+
# e.payload contains wallet, credits required, top_up_at, etc.
|
|
165
|
+
print("Need to top up:", e.payload)
|
|
166
|
+
except RateLimited as e:
|
|
167
|
+
# 429: free preview tier hit its 5/day per-IP limit
|
|
168
|
+
print("Hit the rate limit:", e.payload)
|
|
169
|
+
except TensorFeedError as e:
|
|
170
|
+
# Other API errors
|
|
171
|
+
print("API error", e.status_code, e.payload)
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## API Reference
|
|
175
|
+
|
|
176
|
+
### Free
|
|
177
|
+
|
|
178
|
+
| Method | Description |
|
|
179
|
+
|--------|-------------|
|
|
180
|
+
| `tf.news(category=, limit=)` | Latest AI news articles |
|
|
181
|
+
| `tf.status()` | Real-time AI service status |
|
|
182
|
+
| `tf.status_summary()` | Lightweight status summary |
|
|
183
|
+
| `tf.models()` | Model pricing and specs |
|
|
184
|
+
| `tf.benchmarks()` | Benchmark scores |
|
|
185
|
+
| `tf.is_down(service_name)` | Check if a specific service is down |
|
|
186
|
+
| `tf.agent_activity()` | Agent traffic metrics |
|
|
187
|
+
| `tf.history()` | List of available daily snapshot dates |
|
|
188
|
+
| `tf.history_snapshot(date, type)` | Read a specific snapshot |
|
|
189
|
+
| `tf.routing_preview(task=)` | Top-1 routing recommendation (5/day/IP) |
|
|
190
|
+
| `tf.health()` | API health check |
|
|
191
|
+
| `tf.payment_info()` | Wallet, pricing, supported payment flows |
|
|
192
|
+
| `tf.buy_credits(amount_usd=)` | Generate a 30-min payment quote |
|
|
193
|
+
| `tf.confirm(tx_hash=, nonce=)` | Verify USDC tx, mint credit token |
|
|
194
|
+
|
|
195
|
+
### Token-required
|
|
196
|
+
|
|
197
|
+
| Method | Cost | Description |
|
|
198
|
+
|--------|------|-------------|
|
|
199
|
+
| `tf.balance()` | Free | Check remaining credits |
|
|
200
|
+
| `tf.routing(task=, budget=, top_n=, weights=)` | 1 credit | Top-N ranked routing with full detail |
|
|
201
|
+
|
|
202
|
+
### Auto-send (requires `tensorfeed[web3]`)
|
|
203
|
+
|
|
204
|
+
| Method | Description |
|
|
205
|
+
|--------|-------------|
|
|
206
|
+
| `tf.purchase_credits(amount_usd=, private_key=, rpc_url=)` | One-call quote + sign + broadcast + confirm. Returns token, credits, tx_hash, block_number. |
|
|
207
|
+
|
|
208
|
+
## Wallet & Trust
|
|
209
|
+
|
|
210
|
+
The TensorFeed payment wallet is `0x549c82e6bfc54bdae9a2073744cbc2af5d1fc6d1` on Base mainnet. USDC contract: `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913`.
|
|
211
|
+
|
|
212
|
+
Cross-check this address before sending funds at:
|
|
213
|
+
|
|
214
|
+
- https://tensorfeed.ai/llms.txt
|
|
215
|
+
- https://tensorfeed.ai/api/payment/info
|
|
216
|
+
- https://github.com/RipperMercs/tensorfeed (README)
|
|
217
|
+
- https://x.com/tensorfeed (bio)
|
|
218
|
+
|
|
219
|
+
If any source disagrees, do not send.
|
|
220
|
+
|
|
221
|
+
## Premium Data Terms
|
|
222
|
+
|
|
223
|
+
Premium API responses are licensed for inference use only. Use of TensorFeed premium data for training, fine-tuning, evaluation, or distillation of ML models is prohibited.
|
|
224
|
+
|
|
225
|
+
## Refunds
|
|
226
|
+
|
|
227
|
+
Email `evan@tensorfeed.ai` with the tx hash within 24 hours of the charge for a manual USDC refund.
|
|
228
|
+
|
|
229
|
+
## Links
|
|
230
|
+
|
|
231
|
+
- [API Docs](https://tensorfeed.ai/developers)
|
|
232
|
+
- [TensorFeed.ai](https://tensorfeed.ai)
|
|
233
|
+
- [GitHub](https://github.com/RipperMercs/tensorfeed)
|
|
234
|
+
- [Changelog](https://tensorfeed.ai/changelog)
|
|
235
|
+
|
|
236
|
+
## License
|
|
237
|
+
|
|
238
|
+
MIT
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
# tensorfeed
|
|
2
|
+
|
|
3
|
+
Python SDK for the [TensorFeed.ai](https://tensorfeed.ai) API.
|
|
4
|
+
|
|
5
|
+
Free endpoints (news, status, models, benchmarks, history, routing preview) need no auth. The premium tier (top-N routing, plus more endpoints landing later) is paid via USDC on Base. No accounts, no API keys, no traditional payment processors.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install tensorfeed
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Stdlib-only. No external dependencies.
|
|
14
|
+
|
|
15
|
+
## Free Tier
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
from tensorfeed import TensorFeed
|
|
19
|
+
|
|
20
|
+
tf = TensorFeed()
|
|
21
|
+
|
|
22
|
+
# News
|
|
23
|
+
for article in tf.news(category="research", limit=10)["articles"]:
|
|
24
|
+
print(article["title"])
|
|
25
|
+
|
|
26
|
+
# Live AI service status
|
|
27
|
+
for svc in tf.status()["services"]:
|
|
28
|
+
print(f'{svc["name"]}: {svc["status"]}')
|
|
29
|
+
|
|
30
|
+
# Is a service down?
|
|
31
|
+
print(tf.is_down("claude"))
|
|
32
|
+
|
|
33
|
+
# Model pricing and benchmarks
|
|
34
|
+
print(tf.models())
|
|
35
|
+
print(tf.benchmarks())
|
|
36
|
+
|
|
37
|
+
# Daily history snapshots (the moat)
|
|
38
|
+
print(tf.history()) # list of available dates
|
|
39
|
+
print(tf.history_snapshot("2026-04-27", "pricing"))
|
|
40
|
+
|
|
41
|
+
# Free routing preview (top-1 model, 5 calls/day per IP)
|
|
42
|
+
preview = tf.routing_preview(task="code")
|
|
43
|
+
print(preview["recommendation"])
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Premium Tier (paid, USDC on Base)
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
from tensorfeed import TensorFeed, PaymentRequired
|
|
50
|
+
|
|
51
|
+
tf = TensorFeed()
|
|
52
|
+
|
|
53
|
+
# Step 1: get a 30-minute quote
|
|
54
|
+
quote = tf.buy_credits(amount_usd=1.00)
|
|
55
|
+
print(f"Send {quote['amount_usd']} USDC on Base to {quote['wallet']}")
|
|
56
|
+
print(f"Memo: {quote['memo']} (expires in {quote['ttl_seconds']}s)")
|
|
57
|
+
print(f"Will get: {quote['credits']} credits")
|
|
58
|
+
|
|
59
|
+
# Step 2: send the USDC tx with your wallet
|
|
60
|
+
# (auto-send via web3 is on the roadmap; for v1.1 you sign and send manually)
|
|
61
|
+
|
|
62
|
+
# Step 3: confirm with the tx hash
|
|
63
|
+
result = tf.confirm(tx_hash="0xYOUR_TX_HASH", nonce=quote["memo"])
|
|
64
|
+
print(f"Got {result['credits']} credits, token: {result['token']}")
|
|
65
|
+
# The token is also stored on `tf` automatically; routing() will use it.
|
|
66
|
+
|
|
67
|
+
# Step 4: call premium endpoints
|
|
68
|
+
rec = tf.routing(task="code", budget=5.0, top_n=5)
|
|
69
|
+
for r in rec["recommendations"]:
|
|
70
|
+
print(f'#{r["rank"]}: {r["model"]["name"]} (score: {r["composite_score"]:.2f})')
|
|
71
|
+
|
|
72
|
+
# Custom routing weights
|
|
73
|
+
rec = tf.routing(
|
|
74
|
+
task="general",
|
|
75
|
+
weights={"quality": 0.6, "cost": 0.3, "availability": 0.1, "latency": 0.0},
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
# Check remaining credits
|
|
79
|
+
print(tf.balance())
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Auto-Send (optional, web3 extra)
|
|
83
|
+
|
|
84
|
+
The base SDK keeps you dependency-free, but if you want to skip the manual tx step, install with the `web3` extra:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
pip install 'tensorfeed[web3]'
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Then `tf.purchase_credits()` quotes, signs, broadcasts, and confirms in one call:
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
import os
|
|
94
|
+
from tensorfeed import TensorFeed
|
|
95
|
+
|
|
96
|
+
tf = TensorFeed()
|
|
97
|
+
|
|
98
|
+
result = tf.purchase_credits(
|
|
99
|
+
amount_usd=1.00,
|
|
100
|
+
private_key=os.environ["TENSORFEED_PRIVATE_KEY"], # NEVER hardcode
|
|
101
|
+
# rpc_url="https://base-mainnet.g.alchemy.com/v2/<key>", # optional
|
|
102
|
+
)
|
|
103
|
+
print(result["token"]) # auto-stored on tf
|
|
104
|
+
print(result["tx_hash"]) # for your records
|
|
105
|
+
print(result["credits"]) # how many credits were minted
|
|
106
|
+
|
|
107
|
+
# Token already on tf, so:
|
|
108
|
+
rec = tf.routing(task="code")
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Security: read the key from a secret manager or env var. Never commit it. The raw key is held in memory only for the duration of one signing call.
|
|
112
|
+
|
|
113
|
+
## Reusing a Token Across Sessions
|
|
114
|
+
|
|
115
|
+
Save the token after `confirm()`. Reuse it next time:
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
# Save once
|
|
119
|
+
token = result["token"]
|
|
120
|
+
|
|
121
|
+
# Reuse in another process / job
|
|
122
|
+
tf = TensorFeed(token=token)
|
|
123
|
+
print(tf.balance())
|
|
124
|
+
rec = tf.routing(task="code")
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Error Handling
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
from tensorfeed import TensorFeed, PaymentRequired, RateLimited, TensorFeedError
|
|
131
|
+
|
|
132
|
+
tf = TensorFeed(token="bad_token")
|
|
133
|
+
try:
|
|
134
|
+
tf.routing(task="code")
|
|
135
|
+
except PaymentRequired as e:
|
|
136
|
+
# 402: token invalid, expired, or out of credits
|
|
137
|
+
# e.payload contains wallet, credits required, top_up_at, etc.
|
|
138
|
+
print("Need to top up:", e.payload)
|
|
139
|
+
except RateLimited as e:
|
|
140
|
+
# 429: free preview tier hit its 5/day per-IP limit
|
|
141
|
+
print("Hit the rate limit:", e.payload)
|
|
142
|
+
except TensorFeedError as e:
|
|
143
|
+
# Other API errors
|
|
144
|
+
print("API error", e.status_code, e.payload)
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## API Reference
|
|
148
|
+
|
|
149
|
+
### Free
|
|
150
|
+
|
|
151
|
+
| Method | Description |
|
|
152
|
+
|--------|-------------|
|
|
153
|
+
| `tf.news(category=, limit=)` | Latest AI news articles |
|
|
154
|
+
| `tf.status()` | Real-time AI service status |
|
|
155
|
+
| `tf.status_summary()` | Lightweight status summary |
|
|
156
|
+
| `tf.models()` | Model pricing and specs |
|
|
157
|
+
| `tf.benchmarks()` | Benchmark scores |
|
|
158
|
+
| `tf.is_down(service_name)` | Check if a specific service is down |
|
|
159
|
+
| `tf.agent_activity()` | Agent traffic metrics |
|
|
160
|
+
| `tf.history()` | List of available daily snapshot dates |
|
|
161
|
+
| `tf.history_snapshot(date, type)` | Read a specific snapshot |
|
|
162
|
+
| `tf.routing_preview(task=)` | Top-1 routing recommendation (5/day/IP) |
|
|
163
|
+
| `tf.health()` | API health check |
|
|
164
|
+
| `tf.payment_info()` | Wallet, pricing, supported payment flows |
|
|
165
|
+
| `tf.buy_credits(amount_usd=)` | Generate a 30-min payment quote |
|
|
166
|
+
| `tf.confirm(tx_hash=, nonce=)` | Verify USDC tx, mint credit token |
|
|
167
|
+
|
|
168
|
+
### Token-required
|
|
169
|
+
|
|
170
|
+
| Method | Cost | Description |
|
|
171
|
+
|--------|------|-------------|
|
|
172
|
+
| `tf.balance()` | Free | Check remaining credits |
|
|
173
|
+
| `tf.routing(task=, budget=, top_n=, weights=)` | 1 credit | Top-N ranked routing with full detail |
|
|
174
|
+
|
|
175
|
+
### Auto-send (requires `tensorfeed[web3]`)
|
|
176
|
+
|
|
177
|
+
| Method | Description |
|
|
178
|
+
|--------|-------------|
|
|
179
|
+
| `tf.purchase_credits(amount_usd=, private_key=, rpc_url=)` | One-call quote + sign + broadcast + confirm. Returns token, credits, tx_hash, block_number. |
|
|
180
|
+
|
|
181
|
+
## Wallet & Trust
|
|
182
|
+
|
|
183
|
+
The TensorFeed payment wallet is `0x549c82e6bfc54bdae9a2073744cbc2af5d1fc6d1` on Base mainnet. USDC contract: `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913`.
|
|
184
|
+
|
|
185
|
+
Cross-check this address before sending funds at:
|
|
186
|
+
|
|
187
|
+
- https://tensorfeed.ai/llms.txt
|
|
188
|
+
- https://tensorfeed.ai/api/payment/info
|
|
189
|
+
- https://github.com/RipperMercs/tensorfeed (README)
|
|
190
|
+
- https://x.com/tensorfeed (bio)
|
|
191
|
+
|
|
192
|
+
If any source disagrees, do not send.
|
|
193
|
+
|
|
194
|
+
## Premium Data Terms
|
|
195
|
+
|
|
196
|
+
Premium API responses are licensed for inference use only. Use of TensorFeed premium data for training, fine-tuning, evaluation, or distillation of ML models is prohibited.
|
|
197
|
+
|
|
198
|
+
## Refunds
|
|
199
|
+
|
|
200
|
+
Email `evan@tensorfeed.ai` with the tx hash within 24 hours of the charge for a manual USDC refund.
|
|
201
|
+
|
|
202
|
+
## Links
|
|
203
|
+
|
|
204
|
+
- [API Docs](https://tensorfeed.ai/developers)
|
|
205
|
+
- [TensorFeed.ai](https://tensorfeed.ai)
|
|
206
|
+
- [GitHub](https://github.com/RipperMercs/tensorfeed)
|
|
207
|
+
- [Changelog](https://tensorfeed.ai/changelog)
|
|
208
|
+
|
|
209
|
+
## License
|
|
210
|
+
|
|
211
|
+
MIT
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "tensorfeed"
|
|
7
|
+
version = "1.2.0"
|
|
8
|
+
description = "Python SDK for the TensorFeed.ai API: AI news, status, model pricing, and agent-payable premium routing (USDC on Base, optional auto-send via web3)"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.9"
|
|
12
|
+
authors = [{ name = "TensorFeed.ai", email = "feedback@tensorfeed.ai" }]
|
|
13
|
+
keywords = [
|
|
14
|
+
"tensorfeed",
|
|
15
|
+
"ai",
|
|
16
|
+
"agents",
|
|
17
|
+
"x402",
|
|
18
|
+
"usdc",
|
|
19
|
+
"base",
|
|
20
|
+
"model-routing",
|
|
21
|
+
"llm",
|
|
22
|
+
"api",
|
|
23
|
+
"stablecoin",
|
|
24
|
+
]
|
|
25
|
+
classifiers = [
|
|
26
|
+
"Development Status :: 4 - Beta",
|
|
27
|
+
"Intended Audience :: Developers",
|
|
28
|
+
"Programming Language :: Python :: 3",
|
|
29
|
+
"Programming Language :: Python :: 3.9",
|
|
30
|
+
"Programming Language :: Python :: 3.10",
|
|
31
|
+
"Programming Language :: Python :: 3.11",
|
|
32
|
+
"Programming Language :: Python :: 3.12",
|
|
33
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
34
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
[project.optional-dependencies]
|
|
38
|
+
# pip install 'tensorfeed[web3]' to enable TensorFeed.purchase_credits()
|
|
39
|
+
# (one-shot signed USDC transfer on Base + automatic confirm).
|
|
40
|
+
web3 = [
|
|
41
|
+
"web3>=6.0",
|
|
42
|
+
"eth-account>=0.10",
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
[project.urls]
|
|
46
|
+
Homepage = "https://tensorfeed.ai"
|
|
47
|
+
Documentation = "https://tensorfeed.ai/developers/agent-payments"
|
|
48
|
+
Repository = "https://github.com/RipperMercs/tensorfeed"
|
|
49
|
+
"Bug Tracker" = "https://github.com/RipperMercs/tensorfeed/issues"
|
|
50
|
+
Changelog = "https://tensorfeed.ai/changelog"
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TensorFeed.ai Python SDK
|
|
3
|
+
|
|
4
|
+
Free, no-auth API client for AI news, service status, and model data.
|
|
5
|
+
Plus a paid premium tier (USDC on Base) for ranked model routing
|
|
6
|
+
recommendations and other intelligence endpoints.
|
|
7
|
+
|
|
8
|
+
https://tensorfeed.ai/developers
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from .client import (
|
|
12
|
+
TensorFeed,
|
|
13
|
+
TensorFeedError,
|
|
14
|
+
PaymentRequired,
|
|
15
|
+
RateLimited,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"TensorFeed",
|
|
20
|
+
"TensorFeedError",
|
|
21
|
+
"PaymentRequired",
|
|
22
|
+
"RateLimited",
|
|
23
|
+
]
|
|
24
|
+
__version__ = "1.2.0"
|
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
"""TensorFeed API client.
|
|
2
|
+
|
|
3
|
+
Stdlib-only HTTP client for the TensorFeed.ai API. Covers all free
|
|
4
|
+
endpoints (news, status, models, benchmarks, history, routing preview,
|
|
5
|
+
agent activity) plus the paid premium tier (routing, payment flow).
|
|
6
|
+
|
|
7
|
+
Premium endpoints are paid via USDC on Base. No accounts, no API keys,
|
|
8
|
+
no traditional payment processors. See ``buy_credits()`` and ``confirm()``.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import json
|
|
14
|
+
import urllib.error
|
|
15
|
+
import urllib.parse
|
|
16
|
+
import urllib.request
|
|
17
|
+
from typing import Any # noqa: F401 (re-exported by purchase_credits return type)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
DEFAULT_BASE_URL = "https://tensorfeed.ai/api"
|
|
21
|
+
DEFAULT_USER_AGENT = "TensorFeed-SDK-Python/1.1"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class TensorFeedError(Exception):
|
|
25
|
+
"""Base class for all TensorFeed SDK errors."""
|
|
26
|
+
|
|
27
|
+
def __init__(self, status_code: int, payload: dict[str, Any]) -> None:
|
|
28
|
+
self.status_code = status_code
|
|
29
|
+
self.payload = payload
|
|
30
|
+
msg = payload.get("error") or payload.get("message") or str(payload)
|
|
31
|
+
super().__init__(f"TensorFeed API error {status_code}: {msg}")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class PaymentRequired(TensorFeedError):
|
|
35
|
+
"""Raised on HTTP 402.
|
|
36
|
+
|
|
37
|
+
Inspect ``e.payload`` for wallet, credits required, and pricing
|
|
38
|
+
metadata. Premium endpoints return 402 when no token is provided
|
|
39
|
+
or the token has insufficient credits.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class RateLimited(TensorFeedError):
|
|
44
|
+
"""Raised on HTTP 429 (free preview tier rate limit, 5/day per IP)."""
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class TensorFeed:
|
|
48
|
+
"""Client for the TensorFeed.ai API.
|
|
49
|
+
|
|
50
|
+
Free endpoints work without auth. Paid endpoints require a bearer
|
|
51
|
+
token obtained via ``buy_credits()`` and ``confirm()``.
|
|
52
|
+
|
|
53
|
+
Usage::
|
|
54
|
+
|
|
55
|
+
from tensorfeed import TensorFeed
|
|
56
|
+
|
|
57
|
+
# Free
|
|
58
|
+
tf = TensorFeed()
|
|
59
|
+
news = tf.news(limit=10)
|
|
60
|
+
preview = tf.routing_preview(task="code")
|
|
61
|
+
|
|
62
|
+
# Paid: buy credits, then call routing
|
|
63
|
+
quote = tf.buy_credits(amount_usd=1.00)
|
|
64
|
+
# ... send USDC tx ...
|
|
65
|
+
result = tf.confirm(tx_hash="0x...", nonce=quote["memo"])
|
|
66
|
+
# token is auto-stored on the client
|
|
67
|
+
rec = tf.routing(task="code", top_n=5)
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
def __init__(
|
|
71
|
+
self,
|
|
72
|
+
token: str | None = None,
|
|
73
|
+
base_url: str = DEFAULT_BASE_URL,
|
|
74
|
+
timeout: float = 15.0,
|
|
75
|
+
user_agent: str = DEFAULT_USER_AGENT,
|
|
76
|
+
) -> None:
|
|
77
|
+
self.token = token
|
|
78
|
+
self.base_url = base_url.rstrip("/")
|
|
79
|
+
self.timeout = timeout
|
|
80
|
+
self.user_agent = user_agent
|
|
81
|
+
|
|
82
|
+
# ── HTTP plumbing ──────────────────────────────────────────────
|
|
83
|
+
|
|
84
|
+
def _request(
|
|
85
|
+
self,
|
|
86
|
+
method: str,
|
|
87
|
+
path: str,
|
|
88
|
+
*,
|
|
89
|
+
params: dict[str, Any] | None = None,
|
|
90
|
+
body: dict[str, Any] | None = None,
|
|
91
|
+
require_token: bool = False,
|
|
92
|
+
) -> dict[str, Any]:
|
|
93
|
+
url = f"{self.base_url}{path}"
|
|
94
|
+
if params:
|
|
95
|
+
cleaned = {k: v for k, v in params.items() if v is not None}
|
|
96
|
+
if cleaned:
|
|
97
|
+
url = f"{url}?{urllib.parse.urlencode(cleaned)}"
|
|
98
|
+
|
|
99
|
+
headers: dict[str, str] = {"User-Agent": self.user_agent}
|
|
100
|
+
data: bytes | None = None
|
|
101
|
+
if body is not None:
|
|
102
|
+
headers["Content-Type"] = "application/json"
|
|
103
|
+
data = json.dumps(body).encode("utf-8")
|
|
104
|
+
|
|
105
|
+
# Premium endpoints, balance, and explicit-require-token paths get
|
|
106
|
+
# the Authorization header. Free endpoints do not advertise a token
|
|
107
|
+
# (avoids accidentally leaking it to public-data endpoints).
|
|
108
|
+
needs_auth = (
|
|
109
|
+
require_token
|
|
110
|
+
or "/api/premium/" in path
|
|
111
|
+
or path == "/api/payment/balance"
|
|
112
|
+
)
|
|
113
|
+
if needs_auth and self.token:
|
|
114
|
+
headers["Authorization"] = f"Bearer {self.token}"
|
|
115
|
+
|
|
116
|
+
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
|
117
|
+
try:
|
|
118
|
+
with urllib.request.urlopen(req, timeout=self.timeout) as resp:
|
|
119
|
+
return json.loads(resp.read().decode())
|
|
120
|
+
except urllib.error.HTTPError as e:
|
|
121
|
+
try:
|
|
122
|
+
payload = json.loads(e.read().decode())
|
|
123
|
+
except Exception:
|
|
124
|
+
payload = {"error": "non_json_response", "status": e.code}
|
|
125
|
+
if e.code == 402:
|
|
126
|
+
raise PaymentRequired(402, payload) from e
|
|
127
|
+
if e.code == 429:
|
|
128
|
+
raise RateLimited(429, payload) from e
|
|
129
|
+
raise TensorFeedError(e.code, payload) from e
|
|
130
|
+
|
|
131
|
+
def _get(self, path: str, **params: Any) -> dict[str, Any]:
|
|
132
|
+
return self._request("GET", path, params=params)
|
|
133
|
+
|
|
134
|
+
def _post(self, path: str, body: dict[str, Any]) -> dict[str, Any]:
|
|
135
|
+
return self._request("POST", path, body=body)
|
|
136
|
+
|
|
137
|
+
# ── Free: news, status, models ─────────────────────────────────
|
|
138
|
+
|
|
139
|
+
def news(
|
|
140
|
+
self, *, category: str | None = None, limit: int | None = None
|
|
141
|
+
) -> dict[str, Any]:
|
|
142
|
+
"""Get latest AI news articles. Free.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
category: Filter by category (e.g. "research", "tools")
|
|
146
|
+
limit: Number of articles (default 50, max 200)
|
|
147
|
+
"""
|
|
148
|
+
return self._get("/news", category=category, limit=limit)
|
|
149
|
+
|
|
150
|
+
def status(self) -> dict[str, Any]:
|
|
151
|
+
"""Get real-time AI service status. Free."""
|
|
152
|
+
return self._get("/status")
|
|
153
|
+
|
|
154
|
+
def status_summary(self) -> dict[str, Any]:
|
|
155
|
+
"""Get lightweight status summary. Free."""
|
|
156
|
+
return self._get("/status/summary")
|
|
157
|
+
|
|
158
|
+
def models(self) -> dict[str, Any]:
|
|
159
|
+
"""Get AI model pricing and specs. Free."""
|
|
160
|
+
return self._get("/models")
|
|
161
|
+
|
|
162
|
+
def benchmarks(self) -> dict[str, Any]:
|
|
163
|
+
"""Get AI model benchmark scores. Free."""
|
|
164
|
+
return self._get("/benchmarks")
|
|
165
|
+
|
|
166
|
+
def agent_activity(self) -> dict[str, Any]:
|
|
167
|
+
"""Get agent traffic metrics. Free."""
|
|
168
|
+
return self._get("/agents/activity")
|
|
169
|
+
|
|
170
|
+
def health(self) -> dict[str, Any]:
|
|
171
|
+
"""API health check. Free."""
|
|
172
|
+
return self._get("/health")
|
|
173
|
+
|
|
174
|
+
def is_down(self, service_name: str) -> dict[str, Any]:
|
|
175
|
+
"""Check if a specific AI service is down.
|
|
176
|
+
|
|
177
|
+
Args:
|
|
178
|
+
service_name: Service to check (e.g. "claude", "openai", "gemini")
|
|
179
|
+
"""
|
|
180
|
+
data = self.status()
|
|
181
|
+
needle = service_name.lower()
|
|
182
|
+
for svc in data.get("services", []):
|
|
183
|
+
if needle in svc["name"].lower() or needle in svc["provider"].lower():
|
|
184
|
+
return {
|
|
185
|
+
"name": svc["name"],
|
|
186
|
+
"status": svc["status"],
|
|
187
|
+
"is_down": svc["status"] == "down",
|
|
188
|
+
}
|
|
189
|
+
names = ", ".join(s["name"] for s in data.get("services", []))
|
|
190
|
+
raise ValueError(
|
|
191
|
+
f'Service "{service_name}" not found. Available: {names}'
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
# ── Free: history snapshots ────────────────────────────────────
|
|
195
|
+
|
|
196
|
+
def history(self) -> dict[str, Any]:
|
|
197
|
+
"""List available daily history snapshot dates. Free."""
|
|
198
|
+
return self._get("/history")
|
|
199
|
+
|
|
200
|
+
def history_snapshot(self, date: str, snapshot_type: str) -> dict[str, Any]:
|
|
201
|
+
"""Read a specific historical snapshot. Free.
|
|
202
|
+
|
|
203
|
+
Args:
|
|
204
|
+
date: YYYY-MM-DD UTC
|
|
205
|
+
snapshot_type: pricing, models, benchmarks, status, or agent-activity
|
|
206
|
+
"""
|
|
207
|
+
return self._get(f"/history/{date}/{snapshot_type}")
|
|
208
|
+
|
|
209
|
+
# ── Free: routing preview (rate-limited) ───────────────────────
|
|
210
|
+
|
|
211
|
+
def routing_preview(
|
|
212
|
+
self,
|
|
213
|
+
*,
|
|
214
|
+
task: str = "general",
|
|
215
|
+
budget: float | None = None,
|
|
216
|
+
min_quality: float | None = None,
|
|
217
|
+
) -> dict[str, Any]:
|
|
218
|
+
"""Top-1 model recommendation. Free, 5 calls/day per IP.
|
|
219
|
+
|
|
220
|
+
For full top-5 with score breakdown and no rate limit, use
|
|
221
|
+
``routing()`` with credits.
|
|
222
|
+
|
|
223
|
+
Args:
|
|
224
|
+
task: code, reasoning, creative, or general (default general)
|
|
225
|
+
budget: optional max blended USD per 1M tokens
|
|
226
|
+
min_quality: optional minimum quality score in [0, 1]
|
|
227
|
+
|
|
228
|
+
Raises:
|
|
229
|
+
RateLimited: after 5 free preview calls per UTC day from an IP
|
|
230
|
+
"""
|
|
231
|
+
return self._get(
|
|
232
|
+
"/preview/routing",
|
|
233
|
+
task=task,
|
|
234
|
+
budget=budget,
|
|
235
|
+
min_quality=min_quality,
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
# ── Payment flow ───────────────────────────────────────────────
|
|
239
|
+
|
|
240
|
+
def payment_info(self) -> dict[str, Any]:
|
|
241
|
+
"""Get wallet address, pricing, and supported flows. Free.
|
|
242
|
+
|
|
243
|
+
Use this to verify the wallet address before sending USDC.
|
|
244
|
+
Cross-check against ``llms.txt``, the GitHub README, and the
|
|
245
|
+
@tensorfeed X bio.
|
|
246
|
+
"""
|
|
247
|
+
return self._get("/payment/info")
|
|
248
|
+
|
|
249
|
+
def buy_credits(self, *, amount_usd: float) -> dict[str, Any]:
|
|
250
|
+
"""Generate a 30-min payment quote.
|
|
251
|
+
|
|
252
|
+
Returns a dict with ``wallet``, ``memo``, ``amount_usd``, ``credits``,
|
|
253
|
+
``expires_at``, ``ttl_seconds``, ``next_step``.
|
|
254
|
+
|
|
255
|
+
Send the USDC on Base to ``wallet`` (memo is optional but recommended),
|
|
256
|
+
then call ``confirm()`` with the tx hash and the ``memo`` as nonce.
|
|
257
|
+
|
|
258
|
+
Args:
|
|
259
|
+
amount_usd: USD value of credits to buy. Must be 0.5 to 10000.
|
|
260
|
+
Volume discounts apply at $5 (10%), $30 (25%), $200 (40%).
|
|
261
|
+
"""
|
|
262
|
+
return self._post("/payment/buy-credits", {"amount_usd": amount_usd})
|
|
263
|
+
|
|
264
|
+
def confirm(
|
|
265
|
+
self,
|
|
266
|
+
*,
|
|
267
|
+
tx_hash: str,
|
|
268
|
+
nonce: str | None = None,
|
|
269
|
+
) -> dict[str, Any]:
|
|
270
|
+
"""Verify a USDC tx on-chain and mint a credit token.
|
|
271
|
+
|
|
272
|
+
On success, the returned token is also stored on this client
|
|
273
|
+
instance, so subsequent calls to ``routing()``, ``balance()``,
|
|
274
|
+
etc. work immediately.
|
|
275
|
+
|
|
276
|
+
Returns a dict with ``token``, ``credits``, ``balance``,
|
|
277
|
+
``tx_amount_usd``, ``rate``.
|
|
278
|
+
|
|
279
|
+
Args:
|
|
280
|
+
tx_hash: 0x-prefixed Base mainnet transaction hash
|
|
281
|
+
nonce: optional memo from ``buy_credits()``. If provided and
|
|
282
|
+
the tx amount matches the quote within $0.01, the quoted
|
|
283
|
+
credits (with volume discount) are applied. Otherwise
|
|
284
|
+
credits are calculated from the actual tx amount.
|
|
285
|
+
"""
|
|
286
|
+
body: dict[str, Any] = {"tx_hash": tx_hash}
|
|
287
|
+
if nonce is not None:
|
|
288
|
+
body["nonce"] = nonce
|
|
289
|
+
result = self._post("/payment/confirm", body)
|
|
290
|
+
if isinstance(result, dict) and result.get("ok") and result.get("token"):
|
|
291
|
+
self.token = str(result["token"])
|
|
292
|
+
return result
|
|
293
|
+
|
|
294
|
+
def balance(self) -> dict[str, Any]:
|
|
295
|
+
"""Check remaining credits for the current token.
|
|
296
|
+
|
|
297
|
+
Raises:
|
|
298
|
+
ValueError: if no token is set on the client
|
|
299
|
+
"""
|
|
300
|
+
if not self.token:
|
|
301
|
+
raise ValueError(
|
|
302
|
+
"balance() requires a token. Set it via TensorFeed(token=...) "
|
|
303
|
+
"or call confirm() first."
|
|
304
|
+
)
|
|
305
|
+
return self._request("GET", "/payment/balance", require_token=True)
|
|
306
|
+
|
|
307
|
+
# ── Auto-purchase via web3 (optional dependency) ───────────────
|
|
308
|
+
|
|
309
|
+
def purchase_credits(
|
|
310
|
+
self,
|
|
311
|
+
*,
|
|
312
|
+
amount_usd: float,
|
|
313
|
+
private_key: str,
|
|
314
|
+
rpc_url: str | None = None,
|
|
315
|
+
wait_seconds: int = 90,
|
|
316
|
+
) -> dict[str, Any]:
|
|
317
|
+
"""Buy credits end-to-end: quote, sign USDC tx, broadcast, confirm.
|
|
318
|
+
|
|
319
|
+
Convenience wrapper around buy_credits() + raw signing + confirm()
|
|
320
|
+
that handles the whole flow in one call. The token is auto-stored
|
|
321
|
+
on this client on success, so subsequent ``routing()`` calls work
|
|
322
|
+
immediately.
|
|
323
|
+
|
|
324
|
+
Requires the optional ``web3`` extra:
|
|
325
|
+
pip install 'tensorfeed[web3]'
|
|
326
|
+
|
|
327
|
+
Args:
|
|
328
|
+
amount_usd: USD value of credits to buy (0.5 to 10000)
|
|
329
|
+
private_key: 0x-prefixed Ethereum private key. DO NOT
|
|
330
|
+
hardcode; read from an env var or secret manager.
|
|
331
|
+
rpc_url: Base mainnet RPC. Defaults to public Base RPC,
|
|
332
|
+
which is fine for occasional use. For production use
|
|
333
|
+
Alchemy or QuickNode.
|
|
334
|
+
wait_seconds: Max seconds to wait for tx confirmation
|
|
335
|
+
(default 90)
|
|
336
|
+
|
|
337
|
+
Returns:
|
|
338
|
+
Dict with token, credits, balance, tx_hash, tx_amount_usd,
|
|
339
|
+
rate, block_number.
|
|
340
|
+
|
|
341
|
+
Raises:
|
|
342
|
+
Web3NotInstalled: if pip install 'tensorfeed[web3]' was not run
|
|
343
|
+
TimeoutError: if the tx doesn't confirm in wait_seconds
|
|
344
|
+
RuntimeError: on RPC failure, on-chain revert, or
|
|
345
|
+
TensorFeed-side rejection
|
|
346
|
+
"""
|
|
347
|
+
from .web3_signer import auto_purchase_credits # lazy import
|
|
348
|
+
return auto_purchase_credits(
|
|
349
|
+
self,
|
|
350
|
+
amount_usd=amount_usd,
|
|
351
|
+
private_key=private_key,
|
|
352
|
+
rpc_url=rpc_url,
|
|
353
|
+
wait_seconds=wait_seconds,
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
# ── Paid: routing (Tier 2, 1 credit/call) ──────────────────────
|
|
357
|
+
|
|
358
|
+
def routing(
|
|
359
|
+
self,
|
|
360
|
+
*,
|
|
361
|
+
task: str = "general",
|
|
362
|
+
budget: float | None = None,
|
|
363
|
+
min_quality: float | None = None,
|
|
364
|
+
top_n: int = 5,
|
|
365
|
+
weights: dict[str, float] | None = None,
|
|
366
|
+
) -> dict[str, Any]:
|
|
367
|
+
"""Tier 2 routing: top-N ranked models with full score breakdown.
|
|
368
|
+
|
|
369
|
+
Costs 1 credit per call. Requires a token from ``confirm()`` or
|
|
370
|
+
passed to the constructor.
|
|
371
|
+
|
|
372
|
+
Args:
|
|
373
|
+
task: code, reasoning, creative, or general
|
|
374
|
+
budget: optional max blended USD per 1M tokens
|
|
375
|
+
min_quality: optional minimum quality score in [0, 1]
|
|
376
|
+
top_n: how many models to return (1 to 10, default 5)
|
|
377
|
+
weights: optional dict of {quality, availability, cost, latency}
|
|
378
|
+
weights to override the defaults (0.4, 0.3, 0.2, 0.1).
|
|
379
|
+
Will be normalized server-side to sum to 1.
|
|
380
|
+
|
|
381
|
+
Raises:
|
|
382
|
+
ValueError: if no token is set on the client
|
|
383
|
+
PaymentRequired: if the token has insufficient credits
|
|
384
|
+
|
|
385
|
+
Example::
|
|
386
|
+
|
|
387
|
+
rec = tf.routing(task="code", budget=5.0, top_n=3)
|
|
388
|
+
for r in rec["recommendations"]:
|
|
389
|
+
print(r["model"]["name"], r["composite_score"])
|
|
390
|
+
"""
|
|
391
|
+
if not self.token:
|
|
392
|
+
raise ValueError(
|
|
393
|
+
"routing() requires a token. Buy credits via buy_credits() and "
|
|
394
|
+
"confirm(), or pass an existing token to TensorFeed(token=...)."
|
|
395
|
+
)
|
|
396
|
+
params: dict[str, Any] = {"task": task, "top_n": top_n}
|
|
397
|
+
if budget is not None:
|
|
398
|
+
params["budget"] = budget
|
|
399
|
+
if min_quality is not None:
|
|
400
|
+
params["min_quality"] = min_quality
|
|
401
|
+
if weights:
|
|
402
|
+
for key in ("quality", "availability", "cost", "latency"):
|
|
403
|
+
if key in weights:
|
|
404
|
+
params[f"w_{key}"] = weights[key]
|
|
405
|
+
return self._request(
|
|
406
|
+
"GET", "/premium/routing", params=params, require_token=True
|
|
407
|
+
)
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Optional web3 integration for one-shot credit purchases.
|
|
3
|
+
|
|
4
|
+
This module is loaded lazily and is only imported when the user calls
|
|
5
|
+
``TensorFeed.purchase_credits()``. The base SDK has zero external
|
|
6
|
+
dependencies; web3.py is only required when you want auto-send.
|
|
7
|
+
|
|
8
|
+
Install with: pip install tensorfeed[web3]
|
|
9
|
+
|
|
10
|
+
Security:
|
|
11
|
+
The auto-send flow takes a raw private key as a Python string. This
|
|
12
|
+
is convenient but easy to leak. Prefer reading the key from an
|
|
13
|
+
environment variable, never hardcode it in source, and never log it.
|
|
14
|
+
Cloudflare Workers and similar environments should keep the key in
|
|
15
|
+
a secret manager.
|
|
16
|
+
|
|
17
|
+
For higher security, sign transactions externally (e.g., via a
|
|
18
|
+
hardware wallet or remote signer) and call ``TensorFeed.confirm()``
|
|
19
|
+
with the resulting tx hash directly. The auto-send path here is for
|
|
20
|
+
server-side agents that hold their own keys.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
import time
|
|
26
|
+
from typing import TYPE_CHECKING, Any
|
|
27
|
+
|
|
28
|
+
if TYPE_CHECKING:
|
|
29
|
+
from .client import TensorFeed
|
|
30
|
+
|
|
31
|
+
USDC_BASE_CONTRACT = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
|
|
32
|
+
DEFAULT_BASE_RPC = "https://mainnet.base.org"
|
|
33
|
+
BASE_CHAIN_ID = 8453
|
|
34
|
+
USDC_DECIMALS = 6
|
|
35
|
+
|
|
36
|
+
# Minimal ERC-20 transfer ABI; we do not need the full standard
|
|
37
|
+
USDC_TRANSFER_ABI = [
|
|
38
|
+
{
|
|
39
|
+
"name": "transfer",
|
|
40
|
+
"type": "function",
|
|
41
|
+
"stateMutability": "nonpayable",
|
|
42
|
+
"inputs": [
|
|
43
|
+
{"name": "to", "type": "address"},
|
|
44
|
+
{"name": "amount", "type": "uint256"},
|
|
45
|
+
],
|
|
46
|
+
"outputs": [{"name": "", "type": "bool"}],
|
|
47
|
+
},
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class Web3NotInstalled(ImportError):
|
|
52
|
+
"""Raised when web3.py is not installed but auto-send was requested."""
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def auto_purchase_credits(
|
|
56
|
+
tf: "TensorFeed",
|
|
57
|
+
*,
|
|
58
|
+
amount_usd: float,
|
|
59
|
+
private_key: str,
|
|
60
|
+
rpc_url: str | None = None,
|
|
61
|
+
wait_seconds: int = 90,
|
|
62
|
+
poll_interval: float = 2.0,
|
|
63
|
+
) -> dict[str, Any]:
|
|
64
|
+
"""Buy credits end-to-end: quote, sign USDC tx, broadcast, confirm.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
tf: The TensorFeed client. Token is auto-stored on success so
|
|
68
|
+
subsequent calls to ``tf.routing()`` etc. work immediately.
|
|
69
|
+
amount_usd: USD value of credits to buy. Must be 0.5 to 10000.
|
|
70
|
+
private_key: 0x-prefixed Ethereum private key for signing the
|
|
71
|
+
USDC transfer. NEVER hardcode this; read from a secure env
|
|
72
|
+
var. The key is held in memory only for the duration of
|
|
73
|
+
this call.
|
|
74
|
+
rpc_url: Base mainnet RPC endpoint. Defaults to the free public
|
|
75
|
+
Base RPC, which is fine for occasional use. For production
|
|
76
|
+
use Alchemy or QuickNode.
|
|
77
|
+
wait_seconds: Max wall-clock seconds to wait for tx confirmation
|
|
78
|
+
before raising TimeoutError. Default 90.
|
|
79
|
+
poll_interval: Seconds between receipt polls.
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
Dict with ``token``, ``credits``, ``balance``, ``tx_hash``,
|
|
83
|
+
``tx_amount_usd``, ``rate``.
|
|
84
|
+
|
|
85
|
+
Raises:
|
|
86
|
+
Web3NotInstalled: if web3.py is not installed
|
|
87
|
+
TimeoutError: if the tx does not confirm within wait_seconds
|
|
88
|
+
RuntimeError: on RPC connection failure, on-chain tx revert, or
|
|
89
|
+
TensorFeed-side rejection of the confirmed tx
|
|
90
|
+
"""
|
|
91
|
+
try:
|
|
92
|
+
from web3 import Web3
|
|
93
|
+
from eth_account import Account
|
|
94
|
+
except ImportError as e:
|
|
95
|
+
raise Web3NotInstalled(
|
|
96
|
+
"web3 is not installed. Run: pip install 'tensorfeed[web3]'\n"
|
|
97
|
+
"Then retry, or use the manual flow: tf.buy_credits() + sign "
|
|
98
|
+
"the USDC tx yourself + tf.confirm(tx_hash=..., nonce=...)."
|
|
99
|
+
) from e
|
|
100
|
+
|
|
101
|
+
rpc = rpc_url or DEFAULT_BASE_RPC
|
|
102
|
+
w3 = Web3(Web3.HTTPProvider(rpc, request_kwargs={"timeout": 15}))
|
|
103
|
+
if not w3.is_connected():
|
|
104
|
+
raise RuntimeError(f"Cannot reach Base RPC at {rpc}")
|
|
105
|
+
|
|
106
|
+
account = Account.from_key(private_key)
|
|
107
|
+
sender = account.address
|
|
108
|
+
|
|
109
|
+
# Step 1: TensorFeed-side quote
|
|
110
|
+
quote = tf.buy_credits(amount_usd=amount_usd)
|
|
111
|
+
receiving_wallet = quote["wallet"]
|
|
112
|
+
memo = quote["memo"]
|
|
113
|
+
|
|
114
|
+
# Step 2: build USDC transfer tx on Base
|
|
115
|
+
usdc = w3.eth.contract(
|
|
116
|
+
address=Web3.to_checksum_address(USDC_BASE_CONTRACT),
|
|
117
|
+
abi=USDC_TRANSFER_ABI,
|
|
118
|
+
)
|
|
119
|
+
amount_units = int(round(amount_usd * (10 ** USDC_DECIMALS)))
|
|
120
|
+
|
|
121
|
+
tx_nonce = w3.eth.get_transaction_count(sender)
|
|
122
|
+
tx = usdc.functions.transfer(
|
|
123
|
+
Web3.to_checksum_address(receiving_wallet), amount_units
|
|
124
|
+
).build_transaction(
|
|
125
|
+
{
|
|
126
|
+
"from": sender,
|
|
127
|
+
"nonce": tx_nonce,
|
|
128
|
+
"chainId": BASE_CHAIN_ID,
|
|
129
|
+
}
|
|
130
|
+
)
|
|
131
|
+
if "gas" not in tx:
|
|
132
|
+
tx["gas"] = w3.eth.estimate_gas(tx)
|
|
133
|
+
|
|
134
|
+
# Step 3: sign + broadcast
|
|
135
|
+
signed = Account.sign_transaction(tx, private_key=private_key)
|
|
136
|
+
raw_tx = getattr(signed, "raw_transaction", None) or getattr(signed, "rawTransaction")
|
|
137
|
+
tx_hash_bytes = w3.eth.send_raw_transaction(raw_tx)
|
|
138
|
+
tx_hash = "0x" + tx_hash_bytes.hex().lstrip("0x")
|
|
139
|
+
|
|
140
|
+
# Step 4: wait for confirmation
|
|
141
|
+
deadline = time.time() + wait_seconds
|
|
142
|
+
receipt = None
|
|
143
|
+
while time.time() < deadline:
|
|
144
|
+
try:
|
|
145
|
+
receipt = w3.eth.get_transaction_receipt(tx_hash_bytes)
|
|
146
|
+
if receipt is not None:
|
|
147
|
+
break
|
|
148
|
+
except Exception:
|
|
149
|
+
pass # tx may not be mined yet; keep polling
|
|
150
|
+
time.sleep(poll_interval)
|
|
151
|
+
|
|
152
|
+
if receipt is None:
|
|
153
|
+
raise TimeoutError(
|
|
154
|
+
f"Tx {tx_hash} did not confirm within {wait_seconds}s. "
|
|
155
|
+
f"You can complete the purchase manually once it confirms by "
|
|
156
|
+
f"calling tf.confirm(tx_hash={tx_hash!r}, nonce={memo!r})."
|
|
157
|
+
)
|
|
158
|
+
if int(getattr(receipt, "status", 0)) != 1:
|
|
159
|
+
raise RuntimeError(
|
|
160
|
+
f"Tx {tx_hash} reverted on-chain (status=0). No credits were minted."
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
# Step 5: TensorFeed-side confirm
|
|
164
|
+
result = tf.confirm(tx_hash=tx_hash, nonce=memo)
|
|
165
|
+
if not result.get("ok"):
|
|
166
|
+
raise RuntimeError(
|
|
167
|
+
f"Tx {tx_hash} mined but TensorFeed rejected confirm: {result}. "
|
|
168
|
+
f"Email evan@tensorfeed.ai with the tx hash for a manual reconciliation."
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
**result,
|
|
173
|
+
"tx_hash": tx_hash,
|
|
174
|
+
"block_number": getattr(receipt, "blockNumber", None),
|
|
175
|
+
}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tensorfeed
|
|
3
|
+
Version: 1.2.0
|
|
4
|
+
Summary: Python SDK for the TensorFeed.ai API: AI news, status, model pricing, and agent-payable premium routing (USDC on Base, optional auto-send via web3)
|
|
5
|
+
Author-email: "TensorFeed.ai" <feedback@tensorfeed.ai>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://tensorfeed.ai
|
|
8
|
+
Project-URL: Documentation, https://tensorfeed.ai/developers/agent-payments
|
|
9
|
+
Project-URL: Repository, https://github.com/RipperMercs/tensorfeed
|
|
10
|
+
Project-URL: Bug Tracker, https://github.com/RipperMercs/tensorfeed/issues
|
|
11
|
+
Project-URL: Changelog, https://tensorfeed.ai/changelog
|
|
12
|
+
Keywords: tensorfeed,ai,agents,x402,usdc,base,model-routing,llm,api,stablecoin
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Requires-Python: >=3.9
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
Provides-Extra: web3
|
|
25
|
+
Requires-Dist: web3>=6.0; extra == "web3"
|
|
26
|
+
Requires-Dist: eth-account>=0.10; extra == "web3"
|
|
27
|
+
|
|
28
|
+
# tensorfeed
|
|
29
|
+
|
|
30
|
+
Python SDK for the [TensorFeed.ai](https://tensorfeed.ai) API.
|
|
31
|
+
|
|
32
|
+
Free endpoints (news, status, models, benchmarks, history, routing preview) need no auth. The premium tier (top-N routing, plus more endpoints landing later) is paid via USDC on Base. No accounts, no API keys, no traditional payment processors.
|
|
33
|
+
|
|
34
|
+
## Install
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pip install tensorfeed
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Stdlib-only. No external dependencies.
|
|
41
|
+
|
|
42
|
+
## Free Tier
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
from tensorfeed import TensorFeed
|
|
46
|
+
|
|
47
|
+
tf = TensorFeed()
|
|
48
|
+
|
|
49
|
+
# News
|
|
50
|
+
for article in tf.news(category="research", limit=10)["articles"]:
|
|
51
|
+
print(article["title"])
|
|
52
|
+
|
|
53
|
+
# Live AI service status
|
|
54
|
+
for svc in tf.status()["services"]:
|
|
55
|
+
print(f'{svc["name"]}: {svc["status"]}')
|
|
56
|
+
|
|
57
|
+
# Is a service down?
|
|
58
|
+
print(tf.is_down("claude"))
|
|
59
|
+
|
|
60
|
+
# Model pricing and benchmarks
|
|
61
|
+
print(tf.models())
|
|
62
|
+
print(tf.benchmarks())
|
|
63
|
+
|
|
64
|
+
# Daily history snapshots (the moat)
|
|
65
|
+
print(tf.history()) # list of available dates
|
|
66
|
+
print(tf.history_snapshot("2026-04-27", "pricing"))
|
|
67
|
+
|
|
68
|
+
# Free routing preview (top-1 model, 5 calls/day per IP)
|
|
69
|
+
preview = tf.routing_preview(task="code")
|
|
70
|
+
print(preview["recommendation"])
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Premium Tier (paid, USDC on Base)
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
from tensorfeed import TensorFeed, PaymentRequired
|
|
77
|
+
|
|
78
|
+
tf = TensorFeed()
|
|
79
|
+
|
|
80
|
+
# Step 1: get a 30-minute quote
|
|
81
|
+
quote = tf.buy_credits(amount_usd=1.00)
|
|
82
|
+
print(f"Send {quote['amount_usd']} USDC on Base to {quote['wallet']}")
|
|
83
|
+
print(f"Memo: {quote['memo']} (expires in {quote['ttl_seconds']}s)")
|
|
84
|
+
print(f"Will get: {quote['credits']} credits")
|
|
85
|
+
|
|
86
|
+
# Step 2: send the USDC tx with your wallet
|
|
87
|
+
# (auto-send via web3 is on the roadmap; for v1.1 you sign and send manually)
|
|
88
|
+
|
|
89
|
+
# Step 3: confirm with the tx hash
|
|
90
|
+
result = tf.confirm(tx_hash="0xYOUR_TX_HASH", nonce=quote["memo"])
|
|
91
|
+
print(f"Got {result['credits']} credits, token: {result['token']}")
|
|
92
|
+
# The token is also stored on `tf` automatically; routing() will use it.
|
|
93
|
+
|
|
94
|
+
# Step 4: call premium endpoints
|
|
95
|
+
rec = tf.routing(task="code", budget=5.0, top_n=5)
|
|
96
|
+
for r in rec["recommendations"]:
|
|
97
|
+
print(f'#{r["rank"]}: {r["model"]["name"]} (score: {r["composite_score"]:.2f})')
|
|
98
|
+
|
|
99
|
+
# Custom routing weights
|
|
100
|
+
rec = tf.routing(
|
|
101
|
+
task="general",
|
|
102
|
+
weights={"quality": 0.6, "cost": 0.3, "availability": 0.1, "latency": 0.0},
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
# Check remaining credits
|
|
106
|
+
print(tf.balance())
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Auto-Send (optional, web3 extra)
|
|
110
|
+
|
|
111
|
+
The base SDK keeps you dependency-free, but if you want to skip the manual tx step, install with the `web3` extra:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
pip install 'tensorfeed[web3]'
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Then `tf.purchase_credits()` quotes, signs, broadcasts, and confirms in one call:
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
import os
|
|
121
|
+
from tensorfeed import TensorFeed
|
|
122
|
+
|
|
123
|
+
tf = TensorFeed()
|
|
124
|
+
|
|
125
|
+
result = tf.purchase_credits(
|
|
126
|
+
amount_usd=1.00,
|
|
127
|
+
private_key=os.environ["TENSORFEED_PRIVATE_KEY"], # NEVER hardcode
|
|
128
|
+
# rpc_url="https://base-mainnet.g.alchemy.com/v2/<key>", # optional
|
|
129
|
+
)
|
|
130
|
+
print(result["token"]) # auto-stored on tf
|
|
131
|
+
print(result["tx_hash"]) # for your records
|
|
132
|
+
print(result["credits"]) # how many credits were minted
|
|
133
|
+
|
|
134
|
+
# Token already on tf, so:
|
|
135
|
+
rec = tf.routing(task="code")
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Security: read the key from a secret manager or env var. Never commit it. The raw key is held in memory only for the duration of one signing call.
|
|
139
|
+
|
|
140
|
+
## Reusing a Token Across Sessions
|
|
141
|
+
|
|
142
|
+
Save the token after `confirm()`. Reuse it next time:
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
# Save once
|
|
146
|
+
token = result["token"]
|
|
147
|
+
|
|
148
|
+
# Reuse in another process / job
|
|
149
|
+
tf = TensorFeed(token=token)
|
|
150
|
+
print(tf.balance())
|
|
151
|
+
rec = tf.routing(task="code")
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Error Handling
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
from tensorfeed import TensorFeed, PaymentRequired, RateLimited, TensorFeedError
|
|
158
|
+
|
|
159
|
+
tf = TensorFeed(token="bad_token")
|
|
160
|
+
try:
|
|
161
|
+
tf.routing(task="code")
|
|
162
|
+
except PaymentRequired as e:
|
|
163
|
+
# 402: token invalid, expired, or out of credits
|
|
164
|
+
# e.payload contains wallet, credits required, top_up_at, etc.
|
|
165
|
+
print("Need to top up:", e.payload)
|
|
166
|
+
except RateLimited as e:
|
|
167
|
+
# 429: free preview tier hit its 5/day per-IP limit
|
|
168
|
+
print("Hit the rate limit:", e.payload)
|
|
169
|
+
except TensorFeedError as e:
|
|
170
|
+
# Other API errors
|
|
171
|
+
print("API error", e.status_code, e.payload)
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## API Reference
|
|
175
|
+
|
|
176
|
+
### Free
|
|
177
|
+
|
|
178
|
+
| Method | Description |
|
|
179
|
+
|--------|-------------|
|
|
180
|
+
| `tf.news(category=, limit=)` | Latest AI news articles |
|
|
181
|
+
| `tf.status()` | Real-time AI service status |
|
|
182
|
+
| `tf.status_summary()` | Lightweight status summary |
|
|
183
|
+
| `tf.models()` | Model pricing and specs |
|
|
184
|
+
| `tf.benchmarks()` | Benchmark scores |
|
|
185
|
+
| `tf.is_down(service_name)` | Check if a specific service is down |
|
|
186
|
+
| `tf.agent_activity()` | Agent traffic metrics |
|
|
187
|
+
| `tf.history()` | List of available daily snapshot dates |
|
|
188
|
+
| `tf.history_snapshot(date, type)` | Read a specific snapshot |
|
|
189
|
+
| `tf.routing_preview(task=)` | Top-1 routing recommendation (5/day/IP) |
|
|
190
|
+
| `tf.health()` | API health check |
|
|
191
|
+
| `tf.payment_info()` | Wallet, pricing, supported payment flows |
|
|
192
|
+
| `tf.buy_credits(amount_usd=)` | Generate a 30-min payment quote |
|
|
193
|
+
| `tf.confirm(tx_hash=, nonce=)` | Verify USDC tx, mint credit token |
|
|
194
|
+
|
|
195
|
+
### Token-required
|
|
196
|
+
|
|
197
|
+
| Method | Cost | Description |
|
|
198
|
+
|--------|------|-------------|
|
|
199
|
+
| `tf.balance()` | Free | Check remaining credits |
|
|
200
|
+
| `tf.routing(task=, budget=, top_n=, weights=)` | 1 credit | Top-N ranked routing with full detail |
|
|
201
|
+
|
|
202
|
+
### Auto-send (requires `tensorfeed[web3]`)
|
|
203
|
+
|
|
204
|
+
| Method | Description |
|
|
205
|
+
|--------|-------------|
|
|
206
|
+
| `tf.purchase_credits(amount_usd=, private_key=, rpc_url=)` | One-call quote + sign + broadcast + confirm. Returns token, credits, tx_hash, block_number. |
|
|
207
|
+
|
|
208
|
+
## Wallet & Trust
|
|
209
|
+
|
|
210
|
+
The TensorFeed payment wallet is `0x549c82e6bfc54bdae9a2073744cbc2af5d1fc6d1` on Base mainnet. USDC contract: `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913`.
|
|
211
|
+
|
|
212
|
+
Cross-check this address before sending funds at:
|
|
213
|
+
|
|
214
|
+
- https://tensorfeed.ai/llms.txt
|
|
215
|
+
- https://tensorfeed.ai/api/payment/info
|
|
216
|
+
- https://github.com/RipperMercs/tensorfeed (README)
|
|
217
|
+
- https://x.com/tensorfeed (bio)
|
|
218
|
+
|
|
219
|
+
If any source disagrees, do not send.
|
|
220
|
+
|
|
221
|
+
## Premium Data Terms
|
|
222
|
+
|
|
223
|
+
Premium API responses are licensed for inference use only. Use of TensorFeed premium data for training, fine-tuning, evaluation, or distillation of ML models is prohibited.
|
|
224
|
+
|
|
225
|
+
## Refunds
|
|
226
|
+
|
|
227
|
+
Email `evan@tensorfeed.ai` with the tx hash within 24 hours of the charge for a manual USDC refund.
|
|
228
|
+
|
|
229
|
+
## Links
|
|
230
|
+
|
|
231
|
+
- [API Docs](https://tensorfeed.ai/developers)
|
|
232
|
+
- [TensorFeed.ai](https://tensorfeed.ai)
|
|
233
|
+
- [GitHub](https://github.com/RipperMercs/tensorfeed)
|
|
234
|
+
- [Changelog](https://tensorfeed.ai/changelog)
|
|
235
|
+
|
|
236
|
+
## License
|
|
237
|
+
|
|
238
|
+
MIT
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
tensorfeed/__init__.py
|
|
4
|
+
tensorfeed/client.py
|
|
5
|
+
tensorfeed/web3_signer.py
|
|
6
|
+
tensorfeed.egg-info/PKG-INFO
|
|
7
|
+
tensorfeed.egg-info/SOURCES.txt
|
|
8
|
+
tensorfeed.egg-info/dependency_links.txt
|
|
9
|
+
tensorfeed.egg-info/requires.txt
|
|
10
|
+
tensorfeed.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
tensorfeed
|