mirror of
https://gitcode.com/JianFeeeee/ModelRouter.git
synced 2026-11-09 10:47:24 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7df413453b | |||
| 791d198f47 |
4
cmd/gui/package-lock.json
generated
4
cmd/gui/package-lock.json
generated
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "modelrouter-gui",
|
||||
"version": "1.5.5",
|
||||
"version": "1.5.6",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "modelrouter-gui",
|
||||
"version": "1.5.5",
|
||||
"version": "1.5.6",
|
||||
"license": "Proprietary",
|
||||
"devDependencies": {
|
||||
"electron": "^33.0.0",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "modelrouter-gui",
|
||||
"version": "1.5.5",
|
||||
"version": "1.5.6",
|
||||
"description": "ModelRouter Desktop — embedded ModelRouter core with tray",
|
||||
"author": "ModelRouter",
|
||||
"main": "main.js",
|
||||
|
||||
@ -31,12 +31,20 @@ end
|
||||
|
||||
function adapter.build_headers(meta)
|
||||
local ts = tostring(meta.timestamp or "")
|
||||
local src = (meta.source and meta.source.name) or ""
|
||||
return {
|
||||
["User-Agent"] = adapter.headers["User-Agent"],
|
||||
["x-opencode-client"] = "cli",
|
||||
-- project 固定:同网关实例共享一个工作区身份
|
||||
["x-opencode-project"] = string.sub(sha256_hex("llmsproxy|" .. (meta.source and meta.source.name or "")), 1, 32),
|
||||
["x-opencode-session"] = rand_id("ses_", "session|" .. ts),
|
||||
["x-opencode-project"] = string.sub(sha256_hex("llmsproxy|" .. src), 1, 32),
|
||||
-- session 必须**按源稳定**,不能每请求换:上游的前缀缓存在同一 session
|
||||
-- 内才复用。实测(同一段 6032 token 提示词):
|
||||
-- 固定 session -> 第 2 次命中 5888/6032,cached_tokens=5888
|
||||
-- 每请求换 session -> 永远 0 命中
|
||||
-- 原先用 meta.timestamp 派生,等于每请求都是新会话,缓存永远无效,
|
||||
-- 上游也无法做会话亲和路由。
|
||||
["x-opencode-session"] = rand_id("ses_", "session|llmsproxy|" .. src),
|
||||
-- request id 仍每请求唯一(它只是请求标识,不参与缓存键)
|
||||
["x-opencode-request"] = rand_id("msg_", "request|" .. ts .. "|" .. tostring(meta.body or "")),
|
||||
}
|
||||
end
|
||||
|
||||
@ -32,12 +32,20 @@ end
|
||||
|
||||
function adapter.build_headers(meta)
|
||||
local ts = tostring(meta.timestamp or "")
|
||||
local src = (meta.source and meta.source.name) or ""
|
||||
return {
|
||||
["User-Agent"] = adapter.headers["User-Agent"],
|
||||
["x-opencode-client"] = "cli",
|
||||
-- project 固定:同网关实例共享一个工作区身份
|
||||
["x-opencode-project"] = string.sub(sha256_hex("llmsproxy|" .. (meta.source and meta.source.name or "")), 1, 32),
|
||||
["x-opencode-session"] = rand_id("ses_", "session|" .. ts),
|
||||
["x-opencode-project"] = string.sub(sha256_hex("llmsproxy|" .. src), 1, 32),
|
||||
-- session 必须**按源稳定**,不能每请求换:上游的前缀缓存在同一 session
|
||||
-- 内才复用。实测(同一段 6032 token 提示词):
|
||||
-- 固定 session -> 第 2 次命中 5888/6032,cached_tokens=5888
|
||||
-- 每请求换 session -> 永远 0 命中
|
||||
-- 原先用 meta.timestamp 派生,等于每请求都是新会话,缓存永远无效,
|
||||
-- 上游也无法做会话亲和路由。
|
||||
["x-opencode-session"] = rand_id("ses_", "session|llmsproxy|" .. src),
|
||||
-- request id 仍每请求唯一(它只是请求标识,不参与缓存键)
|
||||
["x-opencode-request"] = rand_id("msg_", "request|" .. ts .. "|" .. tostring(meta.body or "")),
|
||||
}
|
||||
end
|
||||
|
||||
@ -231,3 +231,53 @@ func TestOpenCodeGoVsZenReasoning(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestOpenCodeSessionIsStableForCache pins that the opencode client header set
|
||||
// keeps x-opencode-session STABLE per source while x-opencode-request stays
|
||||
// unique per call.
|
||||
//
|
||||
// Measured against the live OpenCode Go endpoint: the upstream prefix cache is
|
||||
// session-scoped. Replaying the same 6032-token prompt hits 5888 cached tokens
|
||||
// when the session id is fixed, and 0 when it changes per request. Deriving the
|
||||
// session from meta.timestamp (the previous behaviour) made every request a new
|
||||
// session, so the cache could never hit.
|
||||
func TestOpenCodeSessionIsStableForCache(t *testing.T) {
|
||||
for _, name := range []string{"opencodego", "opencodezen"} {
|
||||
vm := NewVM(freshAdapterDir(t))
|
||||
if err := vm.Start(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
metaA := map[string]interface{}{
|
||||
"timestamp": 1000,
|
||||
"body": `{"messages":[{"role":"user","content":"one"}]}`,
|
||||
"source": map[string]interface{}{"name": "somesource", "meta": nil},
|
||||
}
|
||||
metaB := map[string]interface{}{
|
||||
"timestamp": 2000, // different second, different body
|
||||
"body": `{"messages":[{"role":"user","content":"two"}]}`,
|
||||
"source": map[string]interface{}{"name": "somesource", "meta": nil},
|
||||
}
|
||||
h1, err := vm.BuildHeaders(name, metaA)
|
||||
if err != nil {
|
||||
t.Fatalf("%s headers A: %v", name, err)
|
||||
}
|
||||
h2, err := vm.BuildHeaders(name, metaB)
|
||||
if err != nil {
|
||||
t.Fatalf("%s headers B: %v", name, err)
|
||||
}
|
||||
|
||||
if h1["x-opencode-session"] == "" {
|
||||
t.Fatalf("%s: session header must be set", name)
|
||||
}
|
||||
if h1["x-opencode-session"] != h2["x-opencode-session"] {
|
||||
t.Errorf("%s: x-opencode-session must be stable across requests (%q vs %q) — "+
|
||||
"the upstream prefix cache is session-scoped, a rotating session kills every cache hit",
|
||||
name, h1["x-opencode-session"], h2["x-opencode-session"])
|
||||
}
|
||||
if h1["x-opencode-request"] == h2["x-opencode-request"] {
|
||||
t.Errorf("%s: x-opencode-request must differ per request", name)
|
||||
}
|
||||
vm.Stop()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user