Skip to content
Merged
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ go test ./router/ -bench=. -benchmem

This is a Go port of [BlockRunAI/ClawRouter](https://github.com/BlockRunAI/ClawRouter). Applicable routing, proxy and spending-control changes are ported from upstream source snapshots. OpenClaw/desktop lifecycle and provider-specific account services are excluded; x402 signing remains unimplemented. See [UPSTREAM_SYNC.md](UPSTREAM_SYNC.md) for details.

**Current sync**: v0.12.279 release (`becb296`, checked 2026-09-22). No new Go-applicable core changes since the previous snapshot; see the tracker for adapted and deferred features.
**Current sync**: ClawRouter 0.12.297 source (`78e489a`, checked 2026-10-22; latest published release: v0.12.294). Includes updated chat metadata, gateway catalog refresh, bounded outcome memory, request-scoring fixes and local proxy protections. See the tracker for adaptations and exclusions.

## License

Expand Down
62 changes: 61 additions & 1 deletion UPSTREAM_SYNC.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

**Upstream**: [BlockRunAI/ClawRouter](https://github.com/BlockRunAI/ClawRouter) (TypeScript)
**This repo**: [DOS/DOSRouter](https://github.com/DOS/DOSRouter) (Go port)
**Last synced**: v0.12.279 release (`becb2968711bb203a1498958de27a2b1be3780c3`, checked 2026-09-22; no new Go-applicable core changes since `05de1e0`, exclusions below)
**Last synced**: 0.12.297 source (`78e489a`, checked 2026-10-22; latest published release/tag is v0.12.294, source-only changes through 0.12.297 included; adaptations and exclusions below)

## Sync Workflow

Expand Down Expand Up @@ -30,6 +30,66 @@ These upstream areas are excluded (TS/npm-specific):

## Sync Log

### 2026-10-22 - Sync through 0.12.297 source

Owner: Codex, branch `codex/sync-clawrouter-20261022`. Compared the previous
release commit `becb2968711bb203a1498958de27a2b1be3780c3` with upstream main
`78e489a` (package version 0.12.297). GitHub's latest published release and
tag at capture time is v0.12.294; 0.12.295-297 are source versions, not newly
published GitHub releases. The sync includes the subsequent retired-Surf cleanup.

Pinned dependency sources: router-core
`8773a9954b091bd0090acdfec67c75a006c0e156` and model-catalog
`b95efe2f025c9968742e6479bc9ec71b6a40c0f4` (catalog 2026.10.20.4).

| Upstream change | Go disposition |
| --- | --- |
| v0.12.280-281 / 294-297 chat catalog | Add 17 chat records and 34 aliases, correct Sonnet 5 and DeepSeek Vision prices, carry Mistral Large 4's list price, remove Nano 30B from the free fallback lineup, and mark Hy3/retired GPT/Grok IDs. Preserve DOS-specific aliases, exact Nano 30B pins and retired free-to-free redirects. Bare `deepseek` follows V4.1 Flash; free V4 Flash aliases remain free. |
| v0.12.287 gateway catalog | Each serving proxy refreshes only its configured gateway's `/v1/models?format=json` at startup and every five minutes. Validate complete updates; preserve the last good snapshot after failures; refuse redirects and free-product billing changes. New chat models are explicit picks, with no inferred tool eligibility or automatic tier promotion. Each request retains its admission-price snapshot through settlement. No remote ordering policy is fetched. |
| v0.12.281 outcome memory | Per-server, mutex-protected last 10 outcomes for each model/chat-or-tools pair, 30-minute expiry, 512 keys. Three failures at a 50% rate move a model behind reliable candidates before the five-attempt cap. Preserve explicit pins and all-unreliable ordering. Count server failures, incomplete bodies, empty turns, attempt timeouts and observed SSE outcomes; ignore caller cancellation and payment errors. Empty non-streaming turns use the full bounded chain. |
| v0.12.291-297 verified tools | Add the 13 probe-verified recovery candidates behind configured eligible models, ordered by estimated token price outside Premium. Preserve proven capability metadata; catalog categories cannot grant tool support. All 16 configured tier chains retain the previous primaries. |
| v0.12.292 scoring | Size CJK characters at roughly one token each, bound intent scans to both ends of 8,000 characters, match word boundaries/inflections instead of unrelated substrings, use explicit `response_format` for structured output, and stop claiming savings for unknown prices. |
| v0.12.282 local trust | Bind the CLI server to `127.0.0.1`; reject non-local Host, cross-site/opaque Origin and cross-site browser requests on every route. `Server.Handler()` applies the same HTTP guards for embedders. |
| v0.12.283 strict spend reservations | Existing Go reservations already account atomically for in-flight attempts and conservatively settle lost/5xx responses. Keep that implementation. Gateway claims of zero pricing outside local `free/*` products cannot bypass configured limits. Quote-before-sign accounting is inapplicable while x402 signing remains unimplemented. |
| v0.12.284 / 286, media, host integration | Skip account-ledger reconciliation, Solana batch settlement, local img2img filesystem/URL readers, paid OpenClaw sender commands, frozen OpenClaw configuration and Desktop lifecycle: these surfaces do not exist in this Go runtime. Existing image passthrough and Pyth-backed partners remain unchanged. |
| npm/Node, brand, picker, Surf | Skip TS dependencies/build/publishing, Desktop UI, brand counters and picker-only changes. DOSRouter has no Surf integration to remove. Retain the existing Go CI/security workflows and independent automated review. |

Intentional limitations: the existing Go rules strategy remains in place; the
optional TypeScript portfolio strategy, empirical speed-probe decay and its
task-affinity model are not introduced by this sync. Go does not yet identify
repetitive-output loops as a separate failure signal. No x402 signer, ledger
reconciliation or new paid media/provider contract is enabled.

Prices and availability in the bundled snapshot are upstream source metadata,
not live DOS provider verification. Gemini 4 Argon's bundled $2/$10 promotion
ends after 2026-11-30 (then $4/$20); offline date-based repricing is not
implemented. Successful catalog refreshes use the configured gateway's rates.
The existing Gemini Flash promotion caveat below still applies offline.

Validation: regression tests, `go test ./...`, `go vet ./...` and
`go build ./...` passed locally. Native Windows has no cgo compiler, so the
race run is covered by the existing Linux CI job. CI race tests, vulnerability
scan, CodeQL, final-head review and merge acceptance are recorded on the sync
PR. Scope is DOSRouter source and documentation only; no deployment, paid
provider request, credential change or cross-repository write is included.

CI unblock: the first scanner run on `35a4b8c` found 10 reachable standard
library advisories in Go 1.26.6, including GO-2026-6809 (fixed in 1.26.10)
and the Go 1.26.9 HTTP/TLS fixes. No open PR owns this module change. The
smallest unblock raises only the preferred toolchain to Go 1.26.10; the Go
language minimum and module dependencies stay unchanged. Re-run the existing
security, build/race and CodeQL jobs on the resulting head before merge.
The same govulncheck command then passed locally with zero reachable
vulnerabilities (18 module-level advisories remain in code not called here).
Review hardening: bound non-streaming chat bodies to 64 MiB and error bodies
to 1 MiB. Incomplete/oversized bodies stop fallback and preserve accounting;
boundary tests and the existing truncated-response settlement regression pass.
The completed baseline CodeRabbit review identified three Go adaptation edges:
retarget bare Tencent/Hy3/Hunyuan aliases directly to the declared Qwen
successor (keep exact `tencent/hy3` pins), reject remote zero flat-price claims
under spend limits, and use Go's 500-character scan default for omitted or
negative limits. Each issue has a failing-then-passing regression test.

### 2026-09-22 - Sync to the v0.12.279 release

Verified the upstream release list, fetched tags, and compared the previous
Expand Down
17 changes: 17 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,23 @@

Complete reference for DOSRouter configuration options.

The standalone server listens on `127.0.0.1`. All routes reject non-local
Host headers, cross-site or opaque Origin headers, and cross-site browser
requests. Native local clients can omit Origin. Embedded servers should bind
to loopback and use `Server.Handler()` to retain these HTTP protections.

While serving, model metadata refreshes from the configured upstream's
`/v1/models?format=json` at startup and every five minutes, using only the
configured upstream API key. A failed or malformed refresh keeps the last good
snapshot and logs a generic warning. No other catalog host is contacted.
Aliases, free-product identity, proven tool support and automatic routing
policy remain defined by DOSRouter source. Newly discovered chat models are
available by explicit ID; remote zero-price claims cannot bypass spend limits.

Non-streaming chat responses are bounded to 64 MiB, and upstream error bodies
to 1 MiB. An oversized or incomplete response fails without repeating the
send; accounting conservatively retains the known charge or reservation.

## Table of Contents

- [Environment Variables](#environment-variables)
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ module github.com/DOS/DOSRouter

go 1.25.0

toolchain go1.26.6
toolchain go1.26.10

require (
golang.org/x/crypto v0.50.0 // indirect
Expand Down
20 changes: 18 additions & 2 deletions models/catalog_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,16 @@ func TestSyncedCatalogEconomicsAndLimits(t *testing.T) {
{"openai/chat-latest", 5, 30, 128000, 128000},
{"anthropic/claude-fable-5", 10, 50, 1000000, 128000},
{"anthropic/claude-sonnet-4.6", 3, 15, 1000000, 128000},
{"anthropic/claude-sonnet-5", 3, 15, 1000000, 128000},
{"anthropic/claude-sonnet-5", 2, 10, 1000000, 128000},
{"moonshot/kimi-k3", 3, 15, 1048576, 65536},
{"google/gemini-3.8-flash", 0.75, 3.75, 1048576, 65536},
{"google/gemini-3.6-flash", 0.75, 3.75, 1048576, 65536},
{"deepseek/deepseek-v4-pro", 1.32, 3.96, 1048576, 65536},
{"deepseek/deepseek-v4-flash-vision-exp", 0.44, 1.32, 1048576, 65536},
{"deepseek/deepseek-v4-flash-vision-exp", 0.30, 1.20, 1048576, 65536},
{"openai/gpt-6.1-sol", 2, 10, 1050000, 128000},
{"deepseek/deepseek-v4.1-flash", 0.14, 0.28, 1048576, 65536},
{"mistral/mistral-large-4", 1.36, 4.18, 524288, 262144},
{"google/gemini-4-argon", 2, 10, 1048576, 65536},
{"qwen/qwen3.8-flash", 0.15, 0.47, 1000000, 131072},
{"xiaomi/mimo-v2.5", 0.14, 0.28, 1048576, 131072},
{"zai/glm-5.3", 1.4, 4.4, 1000000, 131072},
Expand All @@ -49,6 +53,18 @@ func TestSyncedCatalogEconomicsAndLimits(t *testing.T) {
}
}

func TestRetiredTencentShorthandsResolveActiveMetadata(t *testing.T) {
for _, alias := range []string{"hunyuan", "hy3", "tencent"} {
m := GetModel(ResolveModelAlias(alias))
if m == nil || m.Deprecated {
t.Errorf("%s still targets retired metadata", alias)
}
}
if ResolveModelAlias("tencent/hy3") != "tencent/hy3" {
t.Fatal("explicit compatibility pin changed")
}
}

func TestCatalogIdentityAndRetirementInvariants(t *testing.T) {
seen := make(map[string]bool)
for _, m := range Models {
Expand Down
Loading
Loading