mirror of
https://gitcode.com/JianFeeeee/ModelRouter.git
synced 2026-11-07 09:47:23 +00:00
P1-1 anthropic 基座补 build_headers(x-api-key + anthropic-version) 根因:基座只有静态 anthropic-version、没有 build_headers ⇒ 内核兜底发 Authorization: Bearer,而 Anthropic 协议要 x-api-key。 ★ 证据冲突(计划 P1-1 原文一半有误):zcode 的 /zcode-plan/anthropic/v1/messages 虽然**说 Anthropic 协议**,鉴权却是 Bearer <zcode_jwt>(参考 zcode-transport.ts:121-125;全仓 grep x-api-key = 0 次命中)。故 zcode 必须**显式覆盖** build_headers 回到 Bearer, 否则基座的正向修复会把它从"对"改成"错"。 内核改动:internal/provider/provider.go —— 兜底 Bearer 让位给 x-api-key, 避免同一请求带两套鉴权(部分 Anthropic 兼容上游对歧义鉴权直接 401)。 P1-2 channels-cline 的 build_headers 补 workos: 前缀 根因:参考 cline.ts 发 `Bearer workos:<jwt>`,而池里存的是第二步 register 换回的裸 token ⇒ 之前无 build_headers,内核兜底发裸 Bearer。 幂等:已带前缀(含大小写变体)不重复拼。 P1-3 channels-gemini 只确认不改 参考 gemini.ts:353-366 同样是 OAuth Bearer + 身份五头、刻意不发 x-goog-api-key ⇒ 我们现状本来就对。加判据把这个结论钉住,防下轮照 P1-1 的模子把它改坏。 P2 源名契约:仅 [A-Za-z0-9_-],禁 : 与 / 根因:: 与 / 是限定名分隔符,出现在源名里会让 `源名:模型名` 有两种都 自圆其说的切法,同一请求串在不同部署下可能路由到不同源,且**不报错**。 校验加在写入口 core.normalizeSource(硬拒,API 400);启动时只告警, 避免存量带 : 的源名让升级 brick 掉整个网关。 解析入口 splitPinned 不做校验(解析不该拒绝已有数据)。 P3 夹具形状判据(本轮最重要的一条) 根因:trae 的 Result 信封缺陷在扁平夹具下所有判据全绿,真实登录却报 「缺少 Token」—— 夹具形状错 ⇒ 判据必然虚假通过。 通用形状层:shapeFamilies 登记表,每族给 real/naive/fact/eval/assert, 断言 fact(real) 且 !fact(naive) 且 assert(eval(real))。 刻意**不比较 eval 输出形状**:trae 余额实现合法保留顶层兼容路径, 拿输出比对会把合法兼容判成错误;能区分真假的是 fact(额度在哪一层)。 另有仓库扫描:对特征明确的退化形状静态反查,需退化夹具时写 SHAPE-OK 豁免。 判据:11 个文件 / 30 用例。全部变异已验证会报红(9 处)。
92 lines
4.1 KiB
Go
92 lines
4.1 KiB
Go
package lua
|
||
|
||
import (
|
||
"testing"
|
||
)
|
||
|
||
// ★ P1-3(**只确认不改**):channels-gemini 的「静态身份五头 + 内核兜底 Bearer」
|
||
// 本来就是**对的**,不需要像 zcode 那样补 x-api-key。
|
||
//
|
||
// 这是本轮唯一一个"看起来同型、实际不是缺陷"的渠道,所以把它钉成判据,
|
||
// 免得下一轮有人照 P1-1 的模子给它也改一遍(那会把它弄坏)。
|
||
//
|
||
// 证据链(两条独立来源一致):
|
||
//
|
||
// ① 参考实现 vendor/channel-pack/src/gemini.ts:353-366 geminiHeaders()
|
||
// headers.set('Authorization', `Bearer ${credential.access_token}`)
|
||
// 再逐字注入 GEMINI_IDENTITY_HEADERS 五头
|
||
// 并**刻意不注入** x-goog-api-key(源注释:原版不带)
|
||
// ② 参考 src/gemini.ts:110-117 的 GEMINI_IDENTITY_HEADERS 与我们
|
||
// adapter.headers 的五头逐字相同(UA / x-client-name / x-client-version /
|
||
// x-machine-id / x-vscode-sessionid)
|
||
//
|
||
// 也就是说:gemini 走的是 **OAuth Bearer**(Cloud Code 凭据),不是
|
||
// Anthropic 那种 x-api-key,也不是 Cline 那种 workos: 前缀。它的鉴权
|
||
// 由内核兜底 `Authorization: Bearer <api_key>` 提供 —— 而 gemini 未声明
|
||
// credential_shape ⇒ api_key = 凭据里的 access_token ⇒ 兜底值正确。
|
||
//
|
||
// ⇒ **结论:不改。** 本判据把这个结论钉住:一旦有人给 channels-gemini 加上
|
||
//
|
||
// build_headers 发别的鉴权头(例如照 zcode 改成 x-api-key),这里会红。
|
||
func TestChannelsGeminiAuthIsBearerByDesign(t *testing.T) {
|
||
vm, _ := zcodeHarness(t)
|
||
|
||
// ① 适配器不该自己发鉴权头:静态头里**不得**出现 Authorization /
|
||
// x-api-key / x-goog-api-key。鉴权交给内核兜底 Bearer。
|
||
hdrs, err := vm.BuildHeaders("channels-gemini", map[string]interface{}{
|
||
"api_key": "GA-OAUTH-TOKEN",
|
||
"url": "https://cloudcode-pa.googleapis.com/v1internal:generateContent",
|
||
"method": "POST",
|
||
"body": `{}`,
|
||
})
|
||
if err != nil {
|
||
t.Fatalf("BuildHeaders: %v", err)
|
||
}
|
||
for _, forbidden := range []string{"Authorization", "x-api-key", "x-goog-api-key"} {
|
||
if v, has := hdrs[forbidden]; has {
|
||
t.Fatalf("★ channels-gemini 不该自己发 %s(实得 %q)—— "+
|
||
"Cloud Code 走 OAuth Bearer,由内核兜底发;"+
|
||
"自己发会让它与兜底冲突或改变鉴权方案(P1-3 结论是「不改」)",
|
||
forbidden, v)
|
||
}
|
||
}
|
||
// ② 身份五头必须还在(它们是准入条件,与鉴权无关)
|
||
for k, want := range map[string]string{
|
||
"User-Agent": "antigravity/4.3.0 (cmdc-pak)",
|
||
"x-client-name": "antigravity",
|
||
"x-client-version": "4.3.0",
|
||
"x-machine-id": "cmdc-pak",
|
||
"x-vscode-sessionid": "proxy",
|
||
} {
|
||
if got := hdrs[k]; got != want {
|
||
t.Fatalf("身份头 %s 应为 %q,实得 %q(参考 GEMINI_IDENTITY_HEADERS 逐字)", k, want, got)
|
||
}
|
||
}
|
||
t.Logf("✓ channels-gemini 只发身份五头、不自带鉴权(兜底 Bearer 即正确): %v", keysOf(hdrs))
|
||
}
|
||
|
||
// 配对判据:经**内核**装出来的头必须真的是 `Authorization: Bearer <token>`。
|
||
//
|
||
// 这条在 internal/provider 侧另有一条同型判据(build_headers_fallback_test.go),
|
||
// 这里从「适配器没抢鉴权」推到「兜底确实补上了」,把 P1-3 的结论补成闭环。
|
||
func TestChannelsGeminiKernelFallbackSendsBearer(t *testing.T) {
|
||
vm, _ := zcodeHarness(t)
|
||
|
||
// 适配器产出:不含任何鉴权头(见上一条)
|
||
hdrs, err := vm.BuildHeaders("channels-gemini", map[string]interface{}{
|
||
"api_key": "GA-OAUTH-TOKEN",
|
||
})
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if _, has := hdrs["Authorization"]; has {
|
||
t.Fatalf("适配器不该发 Authorization,实得 %v", hdrs)
|
||
}
|
||
// 于是内核兜底条件成立(Authorization 与 x-api-key 都为空)⇒ 会补 Bearer。
|
||
// 这里断言**条件成立**,而不是重复实现兜底逻辑(兜底本身由 provider 判据咬)。
|
||
if hdrs["x-api-key"] != "" {
|
||
t.Fatalf("x-api-key 必须为空(否则内核兜底不会补 Bearer),实得 %v", hdrs)
|
||
}
|
||
t.Log("✓ 适配器不装鉴权 ⇒ 内核兜底条件成立 ⇒ 发 Authorization: Bearer <access_token>")
|
||
}
|