TauOpt quickstart#
Closed beta: nothing to install. The package is not distributed during the closed beta, and you do not need it: with a tau_ token minted at app.tauopt.com/settings/tokens (Hosted beta: getting started walks the sign-in and the mint), the hosted loop at the end of this page runs over plain HTTP from Python’s standard library, and the MCP server runs the same loop from an agent. The in-process sections between here and there are for maintainers and collaborators with repository access.
With repository access you install TauOpt from a clone, run one tuning loop with the full engine in-process (no server, no account), and read the result. The last section runs the same loop against the hosted service, first with nothing installed and then by changing one constructor call. The local loop and the package-free hosted loop were run while validating this page (the hosted one against the service on the validation date, when it ran engine 0.4.0, the version these pages describe); the client variant of the hosted section was verified by reading python/tauopt/client.py and python/examples/branin_hosted.py and was not run against a live service.
Before you start#
Closed beta (nothing to install): Python 3.10 or newer and a
tau_token minted on the web app after signing in (the API ishttps://api.tauopt.com; the MCP server, a separate host, ishttps://mcp.tauopt.comif you drive it from an agent); skip to Run the same loop against the hosted service.Repository access: Python 3.10 or newer, and
git.Repository access: a Python environment you can
pip installinto; a fresh virtualenv is the safe choice. For uv, conda, or an air-gapped host, use Install TauOpt and rejoin at “Verify the install”.No GPU. The engine runs on CPU only and never allocates one.
Install from a clone#
Clone the repository and enter it:
git clone https://github.com/talariasci/tauopt && cd tauoptThe repository is private during the beta; request access at tauopt.com.
On Linux, install CPU torch first. Plain
pipresolves the multi-gigabyte CUDA build otherwise; the CPU pin inpython/pyproject.tomlis honored by uv only:pip install torch --index-url https://download.pytorch.org/whl/cpuThe checkpoint is the
+cpusuffix in pip’sSuccessfully installed ... torch-<version>+cpuline. An environment that already holds a CUDA torch printsRequirement already satisfiedinstead and keeps it; start from a fresh virtualenv.Install the package with every extra (
[all]is the local driver plus the Gaussian-process (GP) strategies):pip install "./python[all]"
The PyPI name holds a placeholder during the closed beta (pip install tauopt installs that placeholder rather than the engine), so the clone is the install path.
Verify the install#
Print the version:
python -c "import tauopt; print(tauopt.__version__)"
0.4.0
If the import fails, python is not the interpreter you installed into: run the install again as python -m pip install "./python[all]" from the clone.
Run the loop#
Save this as
quickstart.py(it is the README example, unchanged):
import math
from tauopt import Client, OptimizationComplete
def evaluate(a):
return -((math.log10(a["lr"]) + 3) ** 2) - (a["layers"] - 4) ** 2
client = Client(driver="local")
exp = client.create_experiment(
name="my-tuning-run",
parameters=[
{"name": "lr", "type": "double", "bounds": (1e-5, 1e-1), "log_scale": True},
{"name": "layers", "type": "integer", "bounds": (1, 8)},
],
metric={"name": "val_score", "objective": "maximize"},
observation_budget=30, # a planning hint the engine reads when it plans candidates, never an enforced stop
)
for _ in range(30):
try:
sug = client.suggest(exp.id)
except OptimizationComplete:
break # a terminal completion (today: proven exhaustion, finite spaces only)
client.observe(exp.id, suggestion_id=sug.id, value=evaluate(sug.assignments))
best = client.best(exp.id).data[0]
print(best.assignments, round(best.value, 4))
Run it:
python quickstart.py{'lr': 0.0009985601381847934, 'layers': 4} -0.0The run took under five seconds of CPU time on the machine that validated this page, with torch 2.12.1+cpu, botorch 0.18.1, and gpytorch 1.15.2 on Python 3.12 (that machine’s versions). Depending on the torch version, a one-line
FutureWarningabouttorch.jit.scriptmay print on stderr before the result; it comes from the GP dependencies at import rather than from TauOpt, and it is harmless.
Read the result#
The printed line is the best assignments found and the best value, rounded. The toy objective peaks at 0 when lr = 1e-3 and layers = 4. The engine found lr = 0.000998 and layers = 4, so the best value is a tiny negative number that rounds to -0.0. The output is deterministic for a given machine and dependency set; a different CPU class or torch/BoTorch version can shift the lr digits, while layers: 4 and -0.0 are the checkpoint. The digits also assume an unloaded machine: the default timebox mode (a 10-second wall-clock budget per GP rung, timebox_s; the modes are in Pin a strategy and seed) can step down to a cheaper strategy under load, recorded in the suggestion’s provenance (Reproducibility in Troubleshooting).
If your best value lands far from -0.0, the GP strategies did not run: without the [botorch] extra the engine falls back to space-filling strategies without raising, and Confirm the GP strategies run shows how to see that in each suggestion’s provenance.
Run the same loop against the hosted service#
Beta participants sign in at app.tauopt.com with the email address their invitation names and mint a tau_ token on the tokens page (Hosted beta: getting started walks both). Two variants of the loop run against the service, and the first needs nothing installed. Both read TAUOPT_BASE_URL and TAUOPT_TOKEN from the environment.
Export the two values in your shell (never put the token in a script or on a command line):
export TAUOPT_BASE_URL="https://api.tauopt.com" export TAUOPT_TOKEN="tau_<the-value-the-tokens-page-showed>"
Check the pair before any loop; the number is your user id:
curl -sS -H "Authorization: Bearer $TAUOPT_TOKEN" "$TAUOPT_BASE_URL/v1/auth/check"{"user_id":2}With nothing installed (the closed beta): save this as
quickstart_inline.pyand run it with any Python 3.10 or newer. It is the same loop as five calls to the REST API (create the experiment; per round, ask for a suggestion and report the observation; read the best; archive), through the standard library only:
import json
import math
import os
import urllib.error
import urllib.request
BASE = os.environ["TAUOPT_BASE_URL"].rstrip("/") # every path below starts with its own slash
TOKEN = os.environ["TAUOPT_TOKEN"]
def call(method, path, body=None):
req = urllib.request.Request(
BASE + path,
data=None if body is None else json.dumps(body).encode(),
method=method,
headers={"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"},
)
try:
with urllib.request.urlopen(req, timeout=120) as r:
return json.loads(r.read()) if r.status != 204 else None
except urllib.error.HTTPError as e:
print(e.code, e.read().decode()) # the service's JSON error body
raise
def evaluate(a):
return -((math.log10(a["lr"]) + 3) ** 2) - (a["layers"] - 4) ** 2
exp = call(
"POST",
"/v1/experiments",
{
"name": "my-tuning-run",
"parameters": [
{"name": "lr", "type": "double", "bounds": [1e-5, 1e-1], "log_scale": True},
{"name": "layers", "type": "integer", "bounds": [1, 8]},
],
"metric": {"name": "val_score", "objective": "maximize"},
"observation_budget": 30,
},
)
for i in range(30):
sug = call("POST", f"/v1/experiments/{exp['id']}/suggestions", {})
value = evaluate(sug["assignments"])
call("POST", f"/v1/experiments/{exp['id']}/observations", {"suggestion_id": sug["id"], "value": value})
print(f"[{i + 1:02d}] {value:.4f}")
best = call("GET", f"/v1/experiments/{exp['id']}/best_assignments")["data"][0]
print(best["assignments"], round(best["value"], 4))
call("DELETE", f"/v1/experiments/{exp['id']}") # archive: a soft delete that stays readable and frees an active-experiment slot
python quickstart_inline.py
[01] -9.3870
[02] -0.1255
...
[29] -0.0054
[30] -0.0057
{'lr': 0.0009749255000242994, 'layers': 4} -0.0001
One line per round (26 elided here), then the best point: the same checkpoint as the local run, layers: 4 and a value within a thousandth of 0; the lr digits and the exact value depend on the lane’s engine build and the run: this loop pins no seed (nine of the ten use-case twins pin one with timebox_mode: "caps"). This minimal loop stops at the first error: a rejected token prints the service’s JSON body (invalid token, or missing Bearer token) and raises urllib.error.HTTPError: HTTP Error 401: Unauthorized; an unresolvable or unreachable base URL raises urllib.error.URLError, and so does a failed certificate check (CERTIFICATE_VERIFY_FAILED in its text, with the reason after it: unable to get local issuer certificate means the host’s chain is not in your Python’s trust store, which a python.org macOS build fixes by running its Install Certificates.command once; an expired certificate or a hostname mismatch is the service’s to fix, so report it); an http:// base URL is answered with a redirect that the default opener follows as a GET, so the create’s exp['id'] fails with KeyError: 'id' (use https://api.tauopt.com exactly as step 1 exports it; a trailing slash is stripped by the loop itself). quickstart_rest.py, one of the hosted examples (a file to save beside your shell), is the same loop with the service’s answers handled (a 429 waited out by its Retry-After, a 503 experiment_busy the same way, a 409 space_exhausted read as completion, a redirect refused, a connection that fails before the request is sent retried), and Run against the hosted service lists the calls one by one with curl.
With the Python client (repository access): change the constructor; the loop stays the same, and one closing line archives the experiment so it stops counting against the hosted active-experiment cap. The hosted client needs only the featherweight install (httpx + pydantic); the math runs on the service.
import math
import os
from tauopt import Client, OptimizationComplete
def evaluate(a):
return -((math.log10(a["lr"]) + 3) ** 2) - (a["layers"] - 4) ** 2
client = Client(base_url=os.environ["TAUOPT_BASE_URL"], token=os.environ["TAUOPT_TOKEN"])
exp = client.create_experiment(
name="my-tuning-run",
parameters=[
{"name": "lr", "type": "double", "bounds": (1e-5, 1e-1), "log_scale": True},
{"name": "layers", "type": "integer", "bounds": (1, 8)},
],
metric={"name": "val_score", "objective": "maximize"},
observation_budget=30,
)
for _ in range(30):
try:
sug = client.suggest(exp.id)
except OptimizationComplete:
break
client.observe(exp.id, suggestion_id=sug.id, value=evaluate(sug.assignments))
best = client.best(exp.id).data[0]
print(best.assignments, round(best.value, 4))
client.archive_experiment(exp.id)
Run it the same way. The printed line has the same shape; the digits depend on the service’s engine build. An unset variable raises
KeyError: 'TAUOPT_BASE_URL'(or'TAUOPT_TOKEN') from theos.environ[...]lookup before the client is built;ValueError: hosted driver requires base_url and tokenmeans a variable is set but empty (a bareClient()raises it too); a[401] unauthorizedApiErrormeans the token was rejected: it is unknown, revoked, or issued for another lane (a lane is one deployment of the service, and a token works only on the lane that issued it, as Run against the hosted service explains; tokens are shown once, when minted on the web app’s tokens page).
Tokens, the two timeouts, 429 responses, and recovery after a lost response are covered in Run against the hosted service.
Next steps#
Install TauOpt – uv, conda, the extras tiers, air-gapped hosts, and the GP-strategies check.
Run against the hosted service – tokens, timeouts, 429s, archiving, recovery after a lost response.
Experiments and the ask loop – what an experiment, a suggestion, and an observation are.
Python API – every
Clientmethod and model field.The hosted examples – the package-free scripts the closed beta runs: this loop, Branin with its provenance triple, parallel workers.
Examples – the runnable scripts: mixed spaces, multiple metrics, exhaustion, parallel workers.
Troubleshooting – the errors a run meets, by symptom.