Faro SDK: run free Faro tools on-device via the bundled Rust core, fall back to the API for the rest. Local and remote results share the identical envelope.
Project description
askfaro
The Faro Python SDK. One call — run(capability, intent) — runs any
capability. Where it runs is a transparent optimization you never choose: if the
bundled Rust core can run it on-device it does (free, instant, no key, no network,
even offline); otherwise it goes to Faro's hosted skill agent, which picks the
tools, enforces your budget, and bills your account. Both paths return the
identical canonical envelope, so your result-handling code is the same whether a
capability ran on your machine or in the cloud.
pip install askfaro
from askfaro import Faro
faro = Faro() # no key needed for on-device runs
r = faro.run("astronomy", {"latitude": 48.85, "longitude": 2.35})
assert r.ok and r.local # the core ran it on-device — $0
# A capability the core can't run goes to the skill agent automatically — same
# call, same result shape. The agent picks the tools, enforces your budget, and
# bills your account, so this path needs an API key.
faro = Faro(api_key="faro_...")
r = faro.run("image", {"prompt": "a red bicycle"})
if r.ok:
print(r.data, r.credits_charged)
else:
print("failed:", r.error) # a failed run is a result, not an exception
The Rust core is compiled into this package (askfaro._core), so a single
pip install askfaro is all you need — there is no separate core package to
install.
Transparent routing
run() is the single entry point. You never pick on-device vs. server — the
SDK routes for you:
- if the bundled core can run the capability (
calc,units,phone,astronomy, …) it runs in-core: no key, no network, no credits, even offline; - otherwise
run()POSTs to Faro's hosted skill agent (skill.askfaro.com), which selects the operations, calls the underlying tools, enforces your budget, and bills your account — so that path needs an API key.
What runs on-device is the bundled core's capability list
(Faro.local_namespaces()), not a pricing flag; it grows as more tools are ported
into the core, and capabilities silently get cheaper/faster with no code change on
your side.
faro.run("astronomy", {"latitude": 48.85, "longitude": 2.35}) # on-device, $0
faro.run("image", {"prompt": "a red bicycle"}) # skill agent, billed
Advanced — invoke("namespace/tool") is an escape hatch that forces
on-device execution of a specific core tool and raises if it can't run there. Most
callers should just use run(); reach for invoke() only when a call must stay
local (e.g. a hard no-network guarantee).
Results & errors
run() (and invoke()) returns an InvokeResult (the canonical envelope). Read
.status, which is one of three outcomes — a failed call is a result, not an
exception:
r = faro.run("audio-intelligence", {"url": "https://.../clip.mp3"}, confirm_above=50)
if r.ok: # status == "success"
use(r.data) # .data, .summary, .meta, .credits_charged
elif r.status == "needs_input":
# A clarification, or a budget QUOTE that crossed confirm_above. Inspect
# r.needs_input, then resume the *same* plan with the signed token:
r = faro.run("audio-intelligence", {...}, continuation=r.continuation)
else: # status == "failed"
print(r.error["code"], r.error["message"]) # auth | insufficient_credits | ...
Only transport/HTTP problems raise: FaroError (bad input, missing key) and
RemoteError (network failure, or an HTTP error from the skill agent, with
.status and .retryable). Everything the skill itself reports — failure,
clarification, budget quote — comes back on the result so you handle all outcomes
in one place. Cost ceilings: max_credits is a hard cap (the run aborts rather
than exceed it); confirm_above is the soft ceiling that returns the quote above.
Safe retries. Pass idempotency_key on any run() you might retry. A repeat of
the same key replays the prior successful result instead of running — and
charging — again; a failed run releases the key so a retry actually re-runs. Scope a
fresh key per distinct logical call.
faro.run("image", "a red bicycle", idempotency_key="order-42") # retry-safe
Async
For server-side consumers on an event loop (e.g. an async FastAPI backend),
AsyncFaro mirrors Faro with awaitable network methods, so you don't wrap calls
in asyncio.to_thread:
from askfaro import AsyncFaro
async with AsyncFaro(api_key="faro_...") as faro:
hits = await faro.search("transcribe an audio file")
r = await faro.run("image", {"prompt": "a red bicycle"})
assert r.ok
Same constructor and result types as Faro. The network methods (search,
describe, browse, run) are coroutines; invoke() is awaitable too for a
uniform surface, but the on-device core runs in-process (sub-millisecond), so
there is no blocking I/O to offload.
Discovery
Describe what you want and get a ranked skill to run(). Discovery needs no API key.
faro = Faro()
# Describe what you want; get ranked, ready-to-run skills:
for hit in faro.search("transcribe an audio file"):
print(hit.id, hit.short_description, hit.pricing)
# A hit's .id is exactly what run() takes:
best = faro.search("transcribe an audio file")[0]
faro.run(best.id, {"url": "https://.../clip.mp3"}) # paid -> needs a key
# Full input schema + pricing for one candidate:
faro.describe("audio-intelligence/transcribe")
# Browse instead of search: a progressive-context (pcx) map you expand one branch
# at a time, sized for small / on-device context windows:
manifest = faro.browse(budget="4k") # navigate via its self-describing `usage` field
search() is hybrid lexical + semantic over the public catalog; browse()
returns the progressive-context
manifest. Both work with no account. run() on a paid skill needs a key and credits.
browse() and navigator() cache the manifest on disk and revalidate by ETag
(the catalog changes only on a rebuild), so repeat calls cost a cheap 304, not a
full re-download — and a rebuilt catalog is always picked up. The cache lives under
~/.cache/askfaro/pcx (shared with the askfaro CLI) and is keyed by API host +
budget. Control it per client:
Faro() # default: on-disk cache
Faro(manifest_cache=False) # in-memory only, no disk writes
Faro(manifest_cache="/tmp/my-cache") # a directory of your choosing
This uses progressive-context's ManifestLoader / AsyncManifestLoader, so the
async AsyncFaro revalidates the same way.
What's bundled
askfaro._core is the MIT open-source free-tool slice of the Faro core (the
faro-core-free Rust crate): calc, units, phone, astronomy, encoding, datetime,
timezone, random, and timer, plus the canonical envelope builders. The proprietary
parts of Faro (selection gate, signed continuations, cloud client, billing) are NOT
in this package; vendor-backed tools run server-side via the API.
Development
This is a maturin mixed Rust/Python project, fully self-contained:
core-free/— thefaro-core-freeRust crate (the free-tool implementations + the canonical envelope)src/lib.rs— the PyO3 binding that builds theaskfaro._coreextension from itpython/askfaro/— the pure-Python SDK (routing, client, result types)examples/quickstart.py— a runnable tour
cargo test -p faro-core-free # Rust tests
uv venv && uv pip install ".[dev]" # builds askfaro._core via maturin
.venv/bin/pytest # offline Python tests (mocked, no network)
FARO_API_KEY=faro_... .venv/bin/pytest -m live # contract tests vs the real API
python examples/quickstart.py
Tests come in two layers — offline (mocked) and live contract tests that hit the real endpoints and gate the release. Mocks can't catch contract drift, so see TESTING.md for the rule: every mocked boundary must be paired with a live check.
Published wheels are prebuilt (manylinux x86_64/aarch64 + macOS universal2), so end users install with no Rust toolchain.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file askfaro-0.8.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: askfaro-0.8.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 4.1 MB
- Tags: CPython 3.9+, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c46b683e2c7ae3d85013668e1753b102087ff8300b7770f6c8be41a27b8df5a2
|
|
| MD5 |
18085f52be5c269678c06b046b9197c0
|
|
| BLAKE2b-256 |
9a9c5be56e70fdeea4f3186c0c4e10adfb9427fe6b55e55ab9138c6361e62562
|
Provenance
The following attestation bundles were made for askfaro-0.8.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:
Publisher:
release.yml on poolside-ventures/askfaro
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
askfaro-0.8.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl -
Subject digest:
c46b683e2c7ae3d85013668e1753b102087ff8300b7770f6c8be41a27b8df5a2 - Sigstore transparency entry: 1867002634
- Sigstore integration time:
-
Permalink:
poolside-ventures/askfaro@225db369be05e70579231d145855d71ebd7898c9 -
Branch / Tag:
refs/tags/v0.8.0 - Owner: https://github.com/poolside-ventures
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@225db369be05e70579231d145855d71ebd7898c9 -
Trigger Event:
push
-
Statement type:
File details
Details for the file askfaro-0.8.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.
File metadata
- Download URL: askfaro-0.8.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
- Upload date:
- Size: 3.7 MB
- Tags: CPython 3.9+, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
335085492060bf2dfae0f965b8b694d577ec451c53b91efc48f6e08643f2f68c
|
|
| MD5 |
303119ed945b2496258e424ba51f8f1f
|
|
| BLAKE2b-256 |
bd72f3caf0033960b2b8831f04c168701898bc11712ff426a6d4e9c52180b017
|
Provenance
The following attestation bundles were made for askfaro-0.8.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:
Publisher:
release.yml on poolside-ventures/askfaro
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
askfaro-0.8.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl -
Subject digest:
335085492060bf2dfae0f965b8b694d577ec451c53b91efc48f6e08643f2f68c - Sigstore transparency entry: 1867002576
- Sigstore integration time:
-
Permalink:
poolside-ventures/askfaro@225db369be05e70579231d145855d71ebd7898c9 -
Branch / Tag:
refs/tags/v0.8.0 - Owner: https://github.com/poolside-ventures
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@225db369be05e70579231d145855d71ebd7898c9 -
Trigger Event:
push
-
Statement type:
File details
Details for the file askfaro-0.8.0-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl.
File metadata
- Download URL: askfaro-0.8.0-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl
- Upload date:
- Size: 7.4 MB
- Tags: CPython 3.9+, macOS 10.12+ universal2 (ARM64, x86-64), macOS 10.12+ x86-64, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0959f4ab1dad161999785f26bcdf5e6013f686be131d322524989248aca4e9f7
|
|
| MD5 |
d6d4a8dec922d684caed35fbf152bc52
|
|
| BLAKE2b-256 |
41d918e51bd5f36284768132ab32f97cb99b273f7e18360c54649aa08e14bef5
|
Provenance
The following attestation bundles were made for askfaro-0.8.0-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl:
Publisher:
release.yml on poolside-ventures/askfaro
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
askfaro-0.8.0-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl -
Subject digest:
0959f4ab1dad161999785f26bcdf5e6013f686be131d322524989248aca4e9f7 - Sigstore transparency entry: 1867002600
- Sigstore integration time:
-
Permalink:
poolside-ventures/askfaro@225db369be05e70579231d145855d71ebd7898c9 -
Branch / Tag:
refs/tags/v0.8.0 - Owner: https://github.com/poolside-ventures
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@225db369be05e70579231d145855d71ebd7898c9 -
Trigger Event:
push
-
Statement type: