Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ License text copyright (c) 2020 MariaDB Corporation Ab, All Rights Reserved.
Parameters

Licensor: FastCrest
Licensed Work: Reflex Version 0.5.0 or later. The Licensed Work is (c) 2026
FastCrest
Licensed Work: Tether (formerly Reflex) Version 0.5.0 or later. The
Licensed Work is (c) 2026 FastCrest
Additional Use Grant: You may make production use of the Licensed Work, provided
Your use does not include offering the Licensed Work to third
parties on a hosted or embedded basis in order to compete with
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,9 +185,9 @@ when you're ready for per-robot normalization + ActionGuard clamping.
Then from your code:

```python
from tether.client import ReflexClient
from tether.client import TetherClient

with ReflexClient("http://localhost:8000") as client:
with TetherClient("http://localhost:8000") as client:
with client.episode() as ep: # auto episode_id, RTC reset
result = ep.act(image=numpy_frame, state=[0.1, 0.2, ...])
print(result["actions"]) # list of action chunks
Expand Down Expand Up @@ -240,9 +240,9 @@ The Python client sets `X-Tether-Key` when `api_key` is provided:
```python
import os

from tether.client import ReflexClient
from tether.client import TetherClient

with ReflexClient("http://localhost:8000", api_key=os.environ["TETHER_API_KEY"]) as client:
with TetherClient("http://localhost:8000", api_key=os.environ["TETHER_API_KEY"]) as client:
result = client.act(
image=numpy_frame,
state=[0.1, 0.2, 0.3, 0.4, 0.5, 0.6],
Expand Down
30 changes: 15 additions & 15 deletions dashboards/grafana_template.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"annotations": {
"list": []
},
"description": "Reflex VLA serve — out-of-the-box dashboard. 5 panels: latency p99, cache hit rate, in-flight, safety/SLO violations, server up. Tested against Grafana 10.4.0 (schemaVersion 39). Datasource ${DS_PROMETHEUS} resolved at import time. See features/01_serve/subfeatures/_ecosystem/prometheus-grafana.md.",
"description": "Tether VLA serve — out-of-the-box dashboard. 5 panels: latency p99, cache hit rate, in-flight, safety/SLO violations, server up. Tested against Grafana 10.4.0 (schemaVersion 39). Datasource ${DS_PROMETHEUS} resolved at import time. See features/01_serve/subfeatures/_ecosystem/prometheus-grafana.md.",
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 1,
Expand Down Expand Up @@ -48,7 +48,7 @@
"uid": "${DS_PROMETHEUS}"
},
"editorMode": "code",
"expr": "histogram_quantile(0.99, sum(rate(reflex_act_latency_seconds_bucket[5m])) by (embodiment, le))",
"expr": "histogram_quantile(0.99, sum(rate(tether_act_latency_seconds_bucket[5m])) by (embodiment, le))",
"legendFormat": "p99 — {{embodiment}}",
"range": true,
"refId": "A"
Expand Down Expand Up @@ -99,7 +99,7 @@
"uid": "${DS_PROMETHEUS}"
},
"editorMode": "code",
"expr": "sum(rate(reflex_cache_hit_total[5m])) / (sum(rate(reflex_cache_hit_total[5m])) + sum(rate(reflex_cache_miss_total[5m])))",
"expr": "sum(rate(tether_cache_hit_total[5m])) / (sum(rate(tether_cache_hit_total[5m])) + sum(rate(tether_cache_miss_total[5m])))",
"legendFormat": "Hit rate",
"refId": "A"
}
Expand Down Expand Up @@ -146,8 +146,8 @@
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"expr": "reflex_server_up",
"legendFormat": "reflex serve",
"expr": "tether_server_up",
"legendFormat": "tether serve",
"refId": "A"
}
],
Expand Down Expand Up @@ -181,7 +181,7 @@
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"expr": "sum(rate(reflex_safety_violations_total[5m])) by (violation_kind)",
"expr": "sum(rate(tether_safety_violations_total[5m])) by (violation_kind)",
"legendFormat": "safety: {{violation_kind}}",
"refId": "A"
},
Expand All @@ -190,7 +190,7 @@
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"expr": "sum(rate(reflex_slo_violations_total[5m])) by (slo_kind)",
"expr": "sum(rate(tether_slo_violations_total[5m])) by (slo_kind)",
"legendFormat": "SLO: {{slo_kind}}",
"refId": "B"
}
Expand Down Expand Up @@ -235,7 +235,7 @@
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"expr": "sum(reflex_in_flight_requests) by (embodiment)",
"expr": "sum(tether_in_flight_requests) by (embodiment)",
"legendFormat": "{{embodiment}}",
"refId": "A"
}
Expand All @@ -247,7 +247,7 @@
"refresh": "10s",
"schemaVersion": 39,
"style": "dark",
"tags": ["reflex-vla", "serve"],
"tags": ["tether-vla", "serve"],
"templating": {
"list": [
{
Expand All @@ -260,15 +260,15 @@
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"definition": "label_values(reflex_act_latency_seconds_count, embodiment)",
"definition": "label_values(tether_act_latency_seconds_count, embodiment)",
"hide": 0,
"includeAll": true,
"multi": true,
"name": "embodiment",
"options": [],
"query": {
"qryType": 1,
"query": "label_values(reflex_act_latency_seconds_count, embodiment)",
"query": "label_values(tether_act_latency_seconds_count, embodiment)",
"refId": "PrometheusVariableQueryEditor-VariableQuery"
},
"refresh": 1,
Expand All @@ -285,15 +285,15 @@
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"definition": "label_values(reflex_act_latency_seconds_count, model_id)",
"definition": "label_values(tether_act_latency_seconds_count, model_id)",
"hide": 0,
"includeAll": true,
"multi": true,
"name": "model_id",
"options": [],
"query": {
"qryType": 1,
"query": "label_values(reflex_act_latency_seconds_count, model_id)",
"query": "label_values(tether_act_latency_seconds_count, model_id)",
"refId": "PrometheusVariableQueryEditor-VariableQuery"
},
"refresh": 1,
Expand All @@ -305,8 +305,8 @@
"time": { "from": "now-1h", "to": "now" },
"timepicker": {},
"timezone": "",
"title": "Reflex VLA Serve",
"uid": "reflex-vla-serve",
"title": "Tether VLA Serve",
"uid": "tether-vla-serve",
"version": 1,
"weekStart": ""
}
26 changes: 13 additions & 13 deletions dashboards/tether_fleet.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"annotations": { "list": [] },
"description": "Tether fleet dashboard — per-robot breakdown. Requires `tether serve --robot-id <id>` on each process so the `reflex_robot_info` gauge is populated. Panels join hot metrics against robot_info via `instance`. Tested against Grafana 10.4.0 (schemaVersion 39). See features/01_serve/subfeatures/_ecosystem/fleet-telemetry/fleet-telemetry.md.",
"description": "Tether fleet dashboard — per-robot breakdown. Requires `tether serve --robot-id <id>` on each process so the `tether_robot_info` gauge is populated. Panels join hot metrics against robot_info via `instance`. Tested against Grafana 10.4.0 (schemaVersion 39). See features/01_serve/subfeatures/_ecosystem/fleet-telemetry/fleet-telemetry.md.",
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 1,
Expand All @@ -11,7 +11,7 @@
{
"datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
"title": "Active robots",
"description": "Count of robot processes reporting `reflex_robot_info`. One series per robot_id; drops to 0 when a robot goes offline.",
"description": "Count of robot processes reporting `tether_robot_info`. One series per robot_id; drops to 0 when a robot goes offline.",
"gridPos": { "h": 4, "w": 6, "x": 0, "y": 0 },
"id": 1,
"type": "stat",
Expand All @@ -30,7 +30,7 @@
"targets": [
{
"datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
"expr": "count(reflex_robot_info{robot_id=~\"$robot\"})",
"expr": "count(tether_robot_info{robot_id=~\"$robot\"})",
"legendFormat": "robots",
"refId": "A"
}
Expand Down Expand Up @@ -58,7 +58,7 @@
{
"datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
"editorMode": "code",
"expr": "histogram_quantile(0.99, sum by (le, instance) (rate(reflex_act_latency_seconds_bucket[5m]))) * on (instance) group_left(robot_id) reflex_robot_info{robot_id=~\"$robot\"}",
"expr": "histogram_quantile(0.99, sum by (le, instance) (rate(tether_act_latency_seconds_bucket[5m]))) * on (instance) group_left(robot_id) tether_robot_info{robot_id=~\"$robot\"}",
"legendFormat": "{{robot_id}}",
"refId": "A"
}
Expand All @@ -81,7 +81,7 @@
"targets": [
{
"datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
"expr": "sum by (instance) (rate(reflex_act_latency_seconds_count[1m])) * on (instance) group_left(robot_id) reflex_robot_info{robot_id=~\"$robot\"}",
"expr": "sum by (instance) (rate(tether_act_latency_seconds_count[1m])) * on (instance) group_left(robot_id) tether_robot_info{robot_id=~\"$robot\"}",
"legendFormat": "{{robot_id}}",
"refId": "A"
}
Expand All @@ -104,7 +104,7 @@
"targets": [
{
"datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
"expr": "sum by (instance, violation_kind) (increase(reflex_safety_violations_total[5m])) * on (instance) group_left(robot_id) reflex_robot_info{robot_id=~\"$robot\"}",
"expr": "sum by (instance, violation_kind) (increase(tether_safety_violations_total[5m])) * on (instance) group_left(robot_id) tether_robot_info{robot_id=~\"$robot\"}",
"legendFormat": "{{robot_id}} / {{violation_kind}}",
"refId": "A"
}
Expand All @@ -126,7 +126,7 @@
"targets": [
{
"datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
"expr": "sum by (instance) (reflex_in_flight_requests) * on (instance) group_left(robot_id) reflex_robot_info{robot_id=~\"$robot\"}",
"expr": "sum by (instance) (tether_in_flight_requests) * on (instance) group_left(robot_id) tether_robot_info{robot_id=~\"$robot\"}",
"legendFormat": "{{robot_id}}",
"refId": "A"
}
Expand All @@ -148,7 +148,7 @@
"targets": [
{
"datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
"expr": "sum by (instance, slo_kind) (increase(reflex_slo_violations_total[5m])) * on (instance) group_left(robot_id) reflex_robot_info{robot_id=~\"$robot\"}",
"expr": "sum by (instance, slo_kind) (increase(tether_slo_violations_total[5m])) * on (instance) group_left(robot_id) tether_robot_info{robot_id=~\"$robot\"}",
"legendFormat": "{{robot_id}} / {{slo_kind}}",
"refId": "A"
}
Expand All @@ -157,20 +157,20 @@
],
"refresh": "10s",
"schemaVersion": 39,
"tags": ["reflex", "fleet", "vla"],
"tags": ["tether", "fleet", "vla"],
"templating": {
"list": [
{
"current": { "selected": true, "text": "All", "value": "$__all" },
"datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
"definition": "label_values(reflex_robot_info, robot_id)",
"definition": "label_values(tether_robot_info, robot_id)",
"hide": 0,
"includeAll": true,
"label": "Robot",
"multi": true,
"name": "robot",
"options": [],
"query": { "query": "label_values(reflex_robot_info, robot_id)", "refId": "StandardVariableQuery" },
"query": { "query": "label_values(tether_robot_info, robot_id)", "refId": "StandardVariableQuery" },
"refresh": 2,
"regex": "",
"skipUrlSync": false,
Expand All @@ -182,8 +182,8 @@
"time": { "from": "now-1h", "to": "now" },
"timepicker": {},
"timezone": "",
"title": "Reflex Fleet",
"uid": "reflex-fleet",
"title": "Tether Fleet",
"uid": "tether-fleet",
"version": 1,
"weekStart": "",
"__inputs": [
Expand Down
4 changes: 2 additions & 2 deletions docs/getting_started.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ tether export nvidia/GR00T-N1.6-3B --target orin --output ./gr00t
Each command:
1. Downloads the checkpoint from HuggingFace (cached after first run)
2. Runs the model-specific exporter (auto-dispatches based on key prefix)
3. Writes ONNX + reflex_config.json into `--output`
3. Writes ONNX + tether_config.json into `--output`
4. Validates the ONNX numerically against the PyTorch reference (max_diff < 1e-5)
5. If trtexec is available, builds a TensorRT engine

Expand All @@ -60,7 +60,7 @@ After export your output directory looks like:
./pi0/
├── expert_stack.onnx # the graph (1.25MB)
├── expert_stack.onnx.data # the weights (~1.3GB for pi0)
├── reflex_config.json # model meta — used by serve
├── tether_config.json # model meta — used by serve
└── expert_stack.trt # TRT engine (only if trtexec was available)
```

Expand Down
2 changes: 1 addition & 1 deletion docs/verification.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ Total: **3 files, 247.5MB**
| File | Size | SHA256 |
|---|---|---|
| `model.onnx` | 245.3MB | `a1b2c3d4...` |
| `reflex_config.json` | 1.2KB | `e5f6a7b8...` |
| `tether_config.json` | 1.2KB | `e5f6a7b8...` |
| `tokenizer.json` | 957KB | `f9a0b1c2...` |
```

Expand Down
5 changes: 4 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"Topic :: Scientific/Engineering :: Artificial Intelligence",
"License :: OSI Approved :: Apache Software License",
# License is declared via the SPDX `license = "BUSL-1.1"` expression above
# (PEP 639). BUSL-1.1 is not OSI-approved, so there is no License ::
# classifier — and emitting an Apache one here would both misrepresent the
# license and conflict with the SPDX field on modern build backends.
"Programming Language :: Python :: 3",
]

Expand Down
2 changes: 1 addition & 1 deletion reference/NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ Our buffer is great, but we lack:
Steps:
1. Read `/reference/lerobot/src/lerobot/policies/xvla/action_hub.py` fully.
2. Extract action space definitions (min/max per joint, continuous vs. discrete).
3. Add action denormalization to `predict()` post-inference. Needs a config field in `reflex_config.json` (action_space definition).
3. Add action denormalization to `predict()` post-inference. Needs a config field in `tether_config.json` (action_space definition).
4. For RTC: add soft constraint checking in a new `ActionRTC` class (similar to `ActionGuard`). Return violations as telemetry, let the robot decide to replan.

### Priority for our roadmap
Expand Down
2 changes: 1 addition & 1 deletion src/tether/ci_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@

- name: Validate round-trip parity
run: |
tether validate ./sv_export --threshold 1e-4 --num-cases 3 --output-json > validate_result.json
tether validate export ./sv_export --threshold 1e-4 --num-cases 3 --output-json > validate_result.json

- name: Upload validation report
if: always()
Expand Down
6 changes: 3 additions & 3 deletions src/tether/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@

[bold cyan]Most-used:[/bold cyan]
[green]tether chat[/green] start the natural-language assistant
[green]tether chat --tui[/green] ↳ full-screen TUI (needs [dim]pip install 'tether\\[tui]'[/dim])
[green]tether chat --tui[/green] ↳ full-screen TUI (needs [dim]pip install 'fastcrest-tether\\[tui]'[/dim])
[green]tether go --model X[/green] one-command deploy: probe → pull → export → serve
[green]tether doctor[/green] diagnose install + GPU issues
[green]tether models list[/green] browse the curated model registry
Expand Down Expand Up @@ -3024,7 +3024,7 @@ def _find_lib(libname: str) -> str | None:
# in the doctor table.
libs = [
("libnvinfer.so.10", "TensorRT runtime",
r"pip install 'tether\[serve,gpu]' (brings tensorrt>=10)"),
r"pip install 'fastcrest-tether\[serve,gpu]' (brings tensorrt>=10)"),
("libcublas.so.12", "CUDA cuBLAS",
r"pip install nvidia-cublas-cu12 (auto-included in \[serve,gpu])"),
("libcudnn.so.9", "CUDA cuDNN",
Expand Down Expand Up @@ -3614,7 +3614,7 @@ def _arch_from_name(name: str) -> str:
add(
"fastapi + uvicorn",
False,
f"not installed — run `pip install 'tether\\[{extra}]'` for the server",
f"not installed — run `pip install 'fastcrest-tether\\[{extra}]'` for the server",
)

# safetensors
Expand Down
4 changes: 2 additions & 2 deletions src/tether/verification_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def _format_parity(parity: dict[str, Any] | None) -> str:
if not parity:
return (
"## Parity\n\n"
"_Not yet verified._ Run `tether validate <export_dir>` to populate.\n"
"_Not yet verified._ Run `tether validate export <export_dir>` to populate.\n"
)
summary = parity.get("summary", {}) or {}
results = parity.get("results", []) or []
Expand Down Expand Up @@ -174,7 +174,7 @@ def write_verification_report(
if model_id != "unknown":
lines.append("```bash")
lines.append(f"tether export {model_id} --target {target} --output <dir>")
lines.append("tether validate <dir>")
lines.append("tether validate export <dir>")
lines.append("```")
lines.append("")
lines.append(
Expand Down
Loading