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.
@@ -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,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -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,4 @@
1
+
2
+ [web3]
3
+ web3>=6.0
4
+ eth-account>=0.10
@@ -0,0 +1 @@
1
+ tensorfeed