Upload SpecStream tool
Browse files- README.md +7 -5
- index.html +1 -19
- requirements.txt +1 -0
- tool.py +66 -0
README.md
CHANGED
|
@@ -1,10 +1,12 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
-
emoji: 🏢
|
| 4 |
-
colorFrom: blue
|
| 5 |
-
colorTo: yellow
|
| 6 |
sdk: static
|
| 7 |
pinned: false
|
|
|
|
|
|
|
|
|
|
| 8 |
---
|
| 9 |
|
| 10 |
-
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
title: SpecStream get rows
|
|
|
|
|
|
|
|
|
|
| 3 |
sdk: static
|
| 4 |
pinned: false
|
| 5 |
+
tags:
|
| 6 |
+
- smolagents
|
| 7 |
+
- tool
|
| 8 |
---
|
| 9 |
|
| 10 |
+
SpecStream reads the live web and hands back every match with its source, so an agent can ask for data no one sells in plain words. Tool one shows the reading and the maximum price, and runs only after the person approves that maximum. Tool two reads the rows with their source URLs. Set SPECSTREAM_API_KEY. Start at https://specstream.ai/?utm_source=smolagents
|
| 11 |
+
|
| 12 |
+
Load it with `Tool.from_hub("SpecStreamAI/specstream-get-rows", trust_remote_code=True)`.
|
index.html
CHANGED
|
@@ -1,19 +1 @@
|
|
| 1 |
-
<!doctype html>
|
| 2 |
-
<html>
|
| 3 |
-
<head>
|
| 4 |
-
<meta charset="utf-8" />
|
| 5 |
-
<meta name="viewport" content="width=device-width" />
|
| 6 |
-
<title>My static Space</title>
|
| 7 |
-
<link rel="stylesheet" href="style.css" />
|
| 8 |
-
</head>
|
| 9 |
-
<body>
|
| 10 |
-
<div class="card">
|
| 11 |
-
<h1>Welcome to your static Space!</h1>
|
| 12 |
-
<p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
|
| 13 |
-
<p>
|
| 14 |
-
Also don't forget to check the
|
| 15 |
-
<a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
|
| 16 |
-
</p>
|
| 17 |
-
</div>
|
| 18 |
-
</body>
|
| 19 |
-
</html>
|
|
|
|
| 1 |
+
<!doctype html><html><head><meta charset="utf-8"><title>SpecStream get rows</title></head><body style="font-family:system-ui;max-width:40rem;margin:3rem auto;padding:0 1rem"><h1>SpecStream get rows</h1><p>SpecStream reads the live web and hands back every match with its source, so an agent can ask for data no one sells in plain words. Tool one shows the reading and the maximum price, and runs only after the person approves that maximum. Tool two reads the rows with their source URLs. Set SPECSTREAM_API_KEY. Start at https://specstream.ai/?utm_source=smolagents</p><p><code>Tool.from_hub("SpecStreamAI/specstream-get-rows", trust_remote_code=True)</code></p></body></html>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
requirements.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
smolagents
|
tool.py
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Any, Optional
|
| 2 |
+
from smolagents.tools import Tool
|
| 3 |
+
import json
|
| 4 |
+
import urllib
|
| 5 |
+
import os
|
| 6 |
+
|
| 7 |
+
class SpecStreamGetRows(Tool):
|
| 8 |
+
name = "specstream_get_rows"
|
| 9 |
+
description = "Reads the results of a SpecStream run by find_id, each value with the URL of the page it came from. Also says whether the run is still going. Reading costs nothing. Needs SPECSTREAM_API_KEY."
|
| 10 |
+
inputs = {'find_id': {'type': 'string', 'description': 'The find_id returned by specstream_find_web_data.'}, 'limit': {'type': 'integer', 'description': 'How many rows to read, 1 to 100. Default 25.', 'nullable': True}, 'cursor': {'type': 'string', 'description': 'The next_cursor from a previous call, to read the next page.', 'nullable': True}}
|
| 11 |
+
output_type = "string"
|
| 12 |
+
|
| 13 |
+
def forward(self, find_id: str, limit: int | None = None, cursor: str | None = None) -> str:
|
| 14 |
+
import json
|
| 15 |
+
import os
|
| 16 |
+
from urllib.error import HTTPError, URLError
|
| 17 |
+
from urllib.parse import quote as urlquote, urlencode
|
| 18 |
+
from urllib.request import Request, urlopen
|
| 19 |
+
|
| 20 |
+
key = os.environ.get("SPECSTREAM_API_KEY", "").strip()
|
| 21 |
+
if not key:
|
| 22 |
+
return "Set SPECSTREAM_API_KEY to a SpecStream API key (https://specstream.ai/workspace?utm_source=smolagents)."
|
| 23 |
+
base = os.environ.get("SPECSTREAM_API_BASE", "https://api.specstream.ai").rstrip("/")
|
| 24 |
+
size = 25 if limit is None else max(1, min(int(limit), 100))
|
| 25 |
+
query = {"limit": str(size)}
|
| 26 |
+
if cursor:
|
| 27 |
+
query["cursor"] = cursor
|
| 28 |
+
path = "/v1/finds/%s/rows?%s" % (urlquote(find_id, safe=""), urlencode(query))
|
| 29 |
+
req = Request(
|
| 30 |
+
base + path,
|
| 31 |
+
headers={
|
| 32 |
+
"Authorization": "Bearer " + key,
|
| 33 |
+
"Accept": "application/json",
|
| 34 |
+
"User-Agent": "specstream-smolagents/0.1",
|
| 35 |
+
},
|
| 36 |
+
)
|
| 37 |
+
try:
|
| 38 |
+
with urlopen(req, timeout=120) as resp:
|
| 39 |
+
payload = json.loads(resp.read().decode("utf-8"))
|
| 40 |
+
except HTTPError as exc:
|
| 41 |
+
return "SpecStream answered HTTP %d for that find_id." % exc.code
|
| 42 |
+
except (URLError, OSError):
|
| 43 |
+
return "SpecStream could not be reached."
|
| 44 |
+
page = payload.get("data") if isinstance(payload, dict) else None
|
| 45 |
+
if not isinstance(page, dict):
|
| 46 |
+
return "SpecStream returned no rows page."
|
| 47 |
+
rows = page.get("rows") or []
|
| 48 |
+
lines = ["status: %s, accepted rows: %s" % (page.get("status"), page.get("accepted_count"))]
|
| 49 |
+
for number, row in enumerate(rows, 1):
|
| 50 |
+
parts = []
|
| 51 |
+
for cell in row.get("cells") or []:
|
| 52 |
+
shown = cell.get("display")
|
| 53 |
+
if shown in (None, ""):
|
| 54 |
+
continue
|
| 55 |
+
source = (cell.get("provenance") or {}).get("source_url")
|
| 56 |
+
parts.append("%s: %s%s" % (cell.get("label"), shown, " (source: %s)" % source if source else ""))
|
| 57 |
+
lines.append("%d. %s" % (number, "; ".join(parts)))
|
| 58 |
+
if not rows:
|
| 59 |
+
lines.append("No rows yet. If the status is still running, call again in a minute.")
|
| 60 |
+
cursor_next = page.get("next_cursor")
|
| 61 |
+
if cursor_next:
|
| 62 |
+
lines.append("next_cursor: %s" % cursor_next)
|
| 63 |
+
return "\n".join(lines)
|
| 64 |
+
|
| 65 |
+
def __init__(self, *args, **kwargs):
|
| 66 |
+
self.is_initialized = False
|