SpecStream commited on
Commit
ff819b8
·
verified ·
1 Parent(s): 4821a5e

Upload SpecStream tool

Browse files
Files changed (4) hide show
  1. README.md +7 -5
  2. index.html +1 -19
  3. requirements.txt +1 -0
  4. tool.py +117 -0
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Specstream Find Web Data
3
- emoji: 😻
4
- colorFrom: green
5
- colorTo: green
6
  sdk: static
7
  pinned: false
 
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
1
  ---
2
+ title: SpecStream find web data
 
 
 
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-find-web-data", 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 find web data</title></head><body style="font-family:system-ui;max-width:40rem;margin:3rem auto;padding:0 1rem"><h1>SpecStream find web data</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-find-web-data", trust_remote_code=True)</code></p></body></html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ smolagents
tool.py ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Any, Optional
2
+ from smolagents.tools import Tool
3
+ import json
4
+ import urllib
5
+ import os
6
+
7
+ class SpecStreamFindWebData(Tool):
8
+ name = "specstream_find_web_data"
9
+ description = "Reads a request for web data and returns how SpecStream understood it, plus the maximum price. Nothing runs and nothing is charged until the person approves that maximum. Step 1: call with request only, show the reading and the price to the person. Step 2: only after the person says yes, call again with quote_id and confirm_max_cents set to the quoted maximum in cents. That starts the work and returns a find_id; read the rows with the specstream_get_rows tool. Needs SPECSTREAM_API_KEY."
10
+ inputs = {'request': {'type': 'string', 'description': 'The data wanted, in plain words, for example: dentists in Austin with their phone numbers.'}, 'count': {'type': 'integer', 'description': 'How many results to price, 1 to 500. Leave empty for the default.', 'nullable': True}, 'quote_id': {'type': 'string', 'description': 'The quote_id from step 1. Only with confirm_max_cents, after the person approved.', 'nullable': True}, 'confirm_max_cents': {'type': 'integer', 'description': 'The maximum in cents from step 1, exactly as quoted. Set only after the person approved it.', 'nullable': True}}
11
+ output_type = "string"
12
+
13
+ def _call(self, method: str, path: str, body: dict | None = None) -> dict:
14
+ import json
15
+ import os
16
+ from urllib.error import HTTPError, URLError
17
+ from urllib.request import Request, urlopen
18
+
19
+ key = os.environ.get("SPECSTREAM_API_KEY", "").strip()
20
+ base = os.environ.get("SPECSTREAM_API_BASE", "https://api.specstream.ai").rstrip("/")
21
+ data = None if body is None else json.dumps(body).encode("utf-8")
22
+ req = Request(
23
+ base + path,
24
+ data=data,
25
+ method=method,
26
+ headers={
27
+ "Authorization": "Bearer " + key,
28
+ "Accept": "application/json",
29
+ "Content-Type": "application/json",
30
+ "User-Agent": "specstream-smolagents/0.1",
31
+ },
32
+ )
33
+ try:
34
+ with urlopen(req, timeout=120) as resp:
35
+ payload = json.loads(resp.read().decode("utf-8"))
36
+ except HTTPError as exc:
37
+ try:
38
+ payload = json.loads(exc.read().decode("utf-8"))
39
+ except ValueError:
40
+ payload = {}
41
+ error = payload.get("error") if isinstance(payload, dict) else None
42
+ message = error.get("message") if isinstance(error, dict) else None
43
+ raise RuntimeError(message or "SpecStream answered HTTP %d" % exc.code)
44
+ except (URLError, OSError):
45
+ raise RuntimeError("SpecStream could not be reached")
46
+ found = payload.get("data") if isinstance(payload, dict) else None
47
+ return found if isinstance(found, dict) else {}
48
+
49
+ def _dollars(self, cents: int, currency: str) -> str:
50
+ symbol = "$" if currency == "USD" else currency + " "
51
+ return "%s%.2f" % (symbol, cents / 100)
52
+
53
+ def forward(
54
+ self,
55
+ request: str,
56
+ count: int | None = None,
57
+ quote_id: str | None = None,
58
+ confirm_max_cents: int | None = None,
59
+ ) -> str:
60
+ import os
61
+ from urllib.parse import quote as urlquote
62
+
63
+ if not os.environ.get("SPECSTREAM_API_KEY", "").strip():
64
+ return "Set SPECSTREAM_API_KEY to a SpecStream API key (https://specstream.ai/workspace?utm_source=smolagents)."
65
+ call = self._call
66
+ dollars = self._dollars
67
+
68
+ try:
69
+ if quote_id is None and confirm_max_cents is None:
70
+ reading = call("POST", "/v1/parse", {"q": request, "locale": "en-US"})
71
+ ast_hash = reading.get("ast_hash")
72
+ if not ast_hash:
73
+ return "SpecStream could not read that request. Name what you want and where."
74
+ sentence = reading.get("read_back") or reading.get("canonical_sentence") or ""
75
+ body = {"ast_hash": ast_hash, "operation": "find"}
76
+ if count is not None:
77
+ body["requested_units"] = count
78
+ quote = call("POST", "/v1/capacity/quotes", body)
79
+ if not quote.get("quote_id"):
80
+ return "SpecStream could not price this request yet."
81
+ cents = int(quote.get("maximum_cents") or 0)
82
+ money = dollars(cents, str(quote.get("currency") or "USD"))
83
+ return (
84
+ "SpecStream read it as: %s\n"
85
+ "Maximum price: %s. You pay for each result delivered; a search that finds nothing is free.\n"
86
+ "quote_id: %s\n"
87
+ "confirm_max_cents: %d\n"
88
+ "Nothing has run. Ask the person to approve %s. Only after a yes, call this tool again "
89
+ "with quote_id and confirm_max_cents." % (sentence, money, quote["quote_id"], cents, money)
90
+ )
91
+ if not quote_id or confirm_max_cents is None:
92
+ return "To run it, give both quote_id and confirm_max_cents. Nothing ran."
93
+ quoted = call("GET", "/v1/capacity/quotes/" + urlquote(quote_id, safe=""))
94
+ if quoted.get("quote_id") != quote_id:
95
+ return "That quote was not found. Nothing ran."
96
+ cents = int(quoted.get("maximum_cents") or 0)
97
+ if int(confirm_max_cents) != cents:
98
+ return "confirm_max_cents must equal the quoted maximum, %d. Nothing ran." % cents
99
+ started = call(
100
+ "POST",
101
+ "/v1/capacity/executions",
102
+ {
103
+ "quote_id": quote_id,
104
+ "quote_hash": quoted.get("quote_hash"),
105
+ "idempotency_key": "smolagents-" + quote_id,
106
+ "confirm_max_cents": cents,
107
+ },
108
+ )
109
+ find_id = started.get("find_id")
110
+ if not find_id:
111
+ return "SpecStream accepted the approval but has not started the work yet. Call again in a moment."
112
+ return "Started. find_id: %s. Read the rows with specstream_get_rows; the run takes a few minutes." % find_id
113
+ except RuntimeError as exc:
114
+ return "Nothing ran. %s" % exc
115
+
116
+ def __init__(self, *args, **kwargs):
117
+ self.is_initialized = False