https://github.com/julian-corbet/sporewright
A self-learning, multi-objective router for LLM and work routing — Rust + TypeScript (byte-equivalent), with failover, online learning, and a device↔orchestrator protocol
https://github.com/julian-corbet/sporewright
ai-agents failover llm llm-router load-balancing orchestration resilience router rust typescript
Last synced: 20 days ago
JSON representation
A self-learning, multi-objective router for LLM and work routing — Rust + TypeScript (byte-equivalent), with failover, online learning, and a device↔orchestrator protocol
- Host: GitHub
- URL: https://github.com/julian-corbet/sporewright
- Owner: julian-corbet
- License: apache-2.0
- Created: 2026-05-31T20:57:47.000Z (28 days ago)
- Default Branch: main
- Last Pushed: 2026-06-08T16:17:12.000Z (20 days ago)
- Last Synced: 2026-06-08T18:16:08.221Z (20 days ago)
- Topics: ai-agents, failover, llm, llm-router, load-balancing, orchestration, resilience, router, rust, typescript
- Language: TypeScript
- Size: 229 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
- Security: SECURITY.md
- Notice: NOTICE
- Agents: AGENTS.md
Awesome Lists containing this project
README
# sporewright
[](https://github.com/julian-corbet/sporewright/actions/workflows/ci.yml)
[](LICENSE)
[](docs/MODEL.md)
[](#the-object)
A **self-learning, multi-objective router** — one object, a sparse tensor, and the
operations on it. It answers one question for any kind of work (LLM calls, scraping,
rendering, embedding…) across any set of providers or devices (browser tabs, shells,
containers, servers, reachable peers):
> Given everything that *could* do this, in what order should *this* device try the
> options, what happens when one fails, and how does the order improve as we learn?
The engine has **no opinions about your domain**, and the same core ships in **Rust
and TypeScript, decision-equivalent**, so a browser tab and a server agree.
## The object
Everything is a read or write on one sparse tensor:
```
T[ level ][ instance ][ option ][ dimension ] → value
```
- **level** — a fixed, ordered hierarchy, coarse→fine (`global ≺ workspace ≺ … ≺ device`).
- **instance** — the horizontal axis a level spawns: `global` is one (`""`); `workspace`/`device` branch one-per-id.
- **option** — the candidates; the resolved **queue is a vector along this axis**.
- **dimension** — the per-option decision parameters (`latency`, `financial`, `reliability`, a gate…). A dimension's **weight** lives one axis coarser.
You read with a **cursor** (one instance per axis — `{workspace: acme, device: A}`).
Three operators:
- **fold** — collapse the level axis; the deepest set cell wins (most-specific inheritance, the CSS cascade).
- **resolve** — collapse the dimension axis; `Σ w·v`, a `+∞` gate drops the option, order ascending → the queue.
- **reduce** — the one path *up*: the median over a level's instances, the orchestrator's trusted aggregation.
Writes obey **write-down** (a device sets its own slice, never the shared levels), and
robustness is the orchestrator reading where evidence is thin — not metadata in the
cells. The full model, maths, and design choices are in **[docs/MODEL.md](docs/MODEL.md)**;
for *why* one `resolve` becomes a self-learning, multi-objective, resource-aware router
— the three-role dimension vector, the Lagrangian-dual maths, and eight worked stories —
read **[docs/ROUTING-MODEL.md](docs/ROUTING-MODEL.md)**.
## Quickstart
**Rust** (`crates/sporewright`)
```rust
use sporewright::tensor::{Tensor, Cursor};
let mut t = Tensor::new(["global", "workspace", "device"]);
let mut w = t.writer("global").unwrap();
w.set_value("global", "", "groq", "latency", 0.4).unwrap();
w.set_value("global", "", "cerebras", "latency", 0.3).unwrap();
w.set_weight("global", "", "latency", 1.0).unwrap();
// a premium workspace loosens a weight on its own slice; a device records its own value
w.set_value("device", "A", "groq", "latency", 0.05).unwrap();
let mut cursor = Cursor::new();
cursor.insert("device".into(), "A".into());
assert_eq!(t.resolve(&cursor), vec!["groq".to_string(), "cerebras".to_string()]);
```
**TypeScript** (`packages/sporewright`) — same model, decision-equivalent.
```ts
import { Tensor } from "sporewright";
const t = new Tensor(["global", "workspace", "device"]);
const w = t.writer("global")!;
w.setValue("global", "", "groq", "latency", 0.4);
w.setValue("global", "", "cerebras", "latency", 0.3);
w.setWeight("global", "", "latency", 1.0);
w.setValue("device", "A", "groq", "latency", 0.05);
t.resolve({ device: "A" }); // -> ["groq", "cerebras"]
```
## Build
```
cargo test -p sporewright # the Rust core
cd packages/sporewright && bun test # the TypeScript core
```
The two cores are kept decision-equivalent (the wire round-trips); see
[CONTRIBUTING.md](CONTRIBUTING.md) and [SCOPE.md](SCOPE.md) (what lives in the
engine vs the product).