Files
OpenCode 68a9071cf1 feat: reconcile local master with origin (exa/hot-platform hunters, leaks ledger, latest results)
Local branch had diverged from origin/master (sibling commits on the same base). Rewrote local history linearly on top of origin/master, folding in all local content: exa/hot-platform discovery hunters, leaks ledger, nightly sweep orchestrator, updated .gitignore and skill docs, plus the latest hunt output and vault state. Remote-only files (vault keys, channel scripts) were restored rather than dropped, so the resulting tree is a full union of both sides.
2026-08-07 12:53:47 +08:00

1 line
50 KiB
JSON

{"repo": "bigfatsea/vmr", "files": {"internal/adapter/anthropic/anthropic_test.go": {"c2c6df744386811c0d2f7dd28dbe002831dcf723": {"t": 1785746085.9987402, "n": 4528, "h": "eae3327500c91cbf", "c": "// Ver 2026-07-07, by Fable 5\npackage anthropic\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"vmr/internal/core\"\n)\n\nfunc TestBuildRequest(t *testing.T) {\n\traw := []byte(`{\"model\":\"claude\",\"max_tokens\":16,\"stream\":true,\"messages\":[{\"role\":\"user\",\"content\":\"hi\"}],\"future_param\":1}`)\n\tep := &core.Endpoint{Provider: \"p\", BaseURL: \"https://api.deepseek.com/anthropic/v1/\", APIKey: \"sk-1\", Model: \"deepseek-v4-pro\"}\n\tep.FullURL = Anthropic{}.ResolveURL(ep.BaseURL)\n\n\treq, outBody, err := Anthropic{}.BuildRequest(context.Background(), ep, &core.CanonicalRequest{Model: \"claude\", Stream: true, Raw: raw})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif len(outBody) == 0 {\n\t\tt.Fatal(\"BuildRequest returned empty outbound body\")\n\t}\n\tif req.URL.String() != \"https://api.deepseek.com/anthropic/v1/messages\" {\n\t\tt.Errorf(\"url: %s\", req.URL)\n\t}\n\tif got := req.Header.Get(\"x-api-key\"); got != \"sk-1\" {\n\t\tt.Errorf(\"x-api-key: %q\", got)\n\t}\n\t// The client sent no anthropic-version, and BuildRequest must not\n\t// invent one — omitting it is what a direct connection would see too.\n\tif got := req.Header.Get(\"anthropic-version\"); got != \"\" {\n\t\tt.Errorf(\"anthropic-version should be left unset when the client omits it, got %q\", got)\n\t}\n\tvar m map[string]json.RawMessage\n\tbody, _ := req.GetBody()\n\tif err := json.NewDecoder(body).Decode(&m); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tvar model string\n\tjson.Unmarshal(m[\"model\"], &model)\n\tif model != \"deepseek-v4-pro\" {\n\t\tt.Errorf(\"model not rewritten: %q\", model)\n\t}\n\tif string(m[\"future_param\"]) != \"1\" || string(m[\"max_tokens\"]) != \"16\" {\n\t\tt.Error(\"unknown/other fields mangled\")\n\t}\n}\n\nfunc TestBuildRequestForwardsProtocolHeaders(t *testing.T) {\n\thdr := http.Header{}\n\thdr.Set(\"anthropic-version\", \"2024-10-22\")\n\thdr.Set(\"anthropic-beta\", \"context-1m-2025-08-07\")\n\tep := &core.Endpoint{Provider: \"p\", BaseURL: \"https://x.example/v1\", APIKey: \"k\", Model: \"m\"}\n\tep.FullURL = Anthropic{}.ResolveURL(ep.BaseURL)\n\treq, _, err := Anthropic{}.BuildRequest(context.Background(), ep,\n\t\t&core.CanonicalRequest{Model: \"vm\", Raw: []byte(`{\"model\":\"vm\"}`), Header: hdr})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif got := req.Header.Get(\"anthropic-version\"); got != \"2024-10-22\" {\n\t\tt.Errorf(\"version not forwarded: %q\", got)\n\t}\n\tif got := req.Header.Get(\"anthropic-beta\"); got != \"context-1m-2025-08-07\" {\n\t\tt.Errorf(\"beta not forwarded: %q\", got)\n\t}\n}\n\n// TestBuildRequestAppliesRoleMap closes the gap between classify_test.go's\n// coverage of RewriteRoles itself and its actual wiring into BuildRequest.\nfunc TestBuildRequestAppliesRoleMap(t *testing.T) {\n\traw := []byte(`{\"model\":\"vm\",\"messages\":[{\"role\":\"developer\",\"content\":\"be helpful\"},{\"role\":\"user\",\"content\":\"hi\"}]}`)\n\tep := &core.Endpoint{Provider: \"p\", BaseURL: \"https://x.example/v1\", APIKey: \"k\", Model: \"m\", RoleMap: map[string]string{\"developer\": \"system\"}}\n\tep.FullURL = Anthropic{}.ResolveURL(ep.BaseURL)\n\n\t_, outBody, err := Anthropic{}.BuildRequest(context.Background(), ep, &core.CanonicalRequest{Model: \"vm\", Raw: raw})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif !bytes.Contains(outBody, []byte(`\"role\":\"system\"`)) {\n\t\tt.Errorf(\"developer role not remapped: %s\", outBody)\n\t}\n\tif bytes.Contains(outBody, []byte(`\"developer\"`)) {\n\t\tt.Errorf(\"developer role still present: %s\", outBody)\n\t}\n}\n\nfunc TestClassifyError(t *testing.T) {\n\tcases := []struct {\n\t\tstatus int\n\t\tbody string\n\t\twant core.ErrorClass\n\t}{\n\t\t{400, `{\"type\":\"error\",\"error\":{\"type\":\"invalid_request_error\",\"message\":\"max_tokens required\"}}`, core.ErrClient},\n\t\t// DeepSeek anthropic endpoint, live-captured wording for a wrong model name:\n\t\t{400, `{\"error\":{\"message\":\"The supported API model names are deepseek-v4-pro or deepseek-v4-flash, but you passed no-such.\"}}`, core.ErrEndpoint},\n\t\t{401, `{\"type\":\"error\",\"error\":{\"type\":\"authentication_error\",\"message\":\"login fail\"}}`, core.ErrAuth},\n\t\t{404, `{\"type\":\"error\",\"error\":{\"type\":\"not_found_error\",\"message\":\"model not found\"}}`, core.ErrEndpoint},\n\t\t{429, `{\"type\":\"error\",\"error\":{\"type\":\"rate_limit_error\",\"message\":\"slow down\"}}`, core.ErrRateLimit},\n\t\t{500, `{}`, core.ErrTransient},\n\t\t{529, `{\"type\":\"error\",\"error\":{\"type\":\"overloaded_error\",\"message\":\"Overloaded\"}}`, core.ErrTransient},\n\t}\n\tfor _, c := range cases {\n\t\tif got := (Anthropic{}).ClassifyError(c.status, []byte(c.body)); got != c.want {\n\t\t\tt.Errorf(\"status=%d body=%s: got %v want %v\", c.status, c.body, got, c.want)\n\t\t}\n\t}\n}\n\nfunc TestProtocol(t *testing.T) {\n\tif (Anthropic{}).Protocol() != \"anthropic\" {\n\t\tt.Error(\"protocol\")\n\t}\n}\n"}, "0713eea0734bed76c18fc46d8a47c173e9e86e49": {"t": 1785759764.6395805, "n": 4528, "h": "eae3327500c91cbf", "c": "// Ver 2026-07-07, by Fable 5\npackage anthropic\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"vmr/internal/core\"\n)\n\nfunc TestBuildRequest(t *testing.T) {\n\traw := []byte(`{\"model\":\"claude\",\"max_tokens\":16,\"stream\":true,\"messages\":[{\"role\":\"user\",\"content\":\"hi\"}],\"future_param\":1}`)\n\tep := &core.Endpoint{Provider: \"p\", BaseURL: \"https://api.deepseek.com/anthropic/v1/\", APIKey: \"sk-1\", Model: \"deepseek-v4-pro\"}\n\tep.FullURL = Anthropic{}.ResolveURL(ep.BaseURL)\n\n\treq, outBody, err := Anthropic{}.BuildRequest(context.Background(), ep, &core.CanonicalRequest{Model: \"claude\", Stream: true, Raw: raw})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif len(outBody) == 0 {\n\t\tt.Fatal(\"BuildRequest returned empty outbound body\")\n\t}\n\tif req.URL.String() != \"https://api.deepseek.com/anthropic/v1/messages\" {\n\t\tt.Errorf(\"url: %s\", req.URL)\n\t}\n\tif got := req.Header.Get(\"x-api-key\"); got != \"sk-1\" {\n\t\tt.Errorf(\"x-api-key: %q\", got)\n\t}\n\t// The client sent no anthropic-version, and BuildRequest must not\n\t// invent one — omitting it is what a direct connection would see too.\n\tif got := req.Header.Get(\"anthropic-version\"); got != \"\" {\n\t\tt.Errorf(\"anthropic-version should be left unset when the client omits it, got %q\", got)\n\t}\n\tvar m map[string]json.RawMessage\n\tbody, _ := req.GetBody()\n\tif err := json.NewDecoder(body).Decode(&m); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tvar model string\n\tjson.Unmarshal(m[\"model\"], &model)\n\tif model != \"deepseek-v4-pro\" {\n\t\tt.Errorf(\"model not rewritten: %q\", model)\n\t}\n\tif string(m[\"future_param\"]) != \"1\" || string(m[\"max_tokens\"]) != \"16\" {\n\t\tt.Error(\"unknown/other fields mangled\")\n\t}\n}\n\nfunc TestBuildRequestForwardsProtocolHeaders(t *testing.T) {\n\thdr := http.Header{}\n\thdr.Set(\"anthropic-version\", \"2024-10-22\")\n\thdr.Set(\"anthropic-beta\", \"context-1m-2025-08-07\")\n\tep := &core.Endpoint{Provider: \"p\", BaseURL: \"https://x.example/v1\", APIKey: \"k\", Model: \"m\"}\n\tep.FullURL = Anthropic{}.ResolveURL(ep.BaseURL)\n\treq, _, err := Anthropic{}.BuildRequest(context.Background(), ep,\n\t\t&core.CanonicalRequest{Model: \"vm\", Raw: []byte(`{\"model\":\"vm\"}`), Header: hdr})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif got := req.Header.Get(\"anthropic-version\"); got != \"2024-10-22\" {\n\t\tt.Errorf(\"version not forwarded: %q\", got)\n\t}\n\tif got := req.Header.Get(\"anthropic-beta\"); got != \"context-1m-2025-08-07\" {\n\t\tt.Errorf(\"beta not forwarded: %q\", got)\n\t}\n}\n\n// TestBuildRequestAppliesRoleMap closes the gap between classify_test.go's\n// coverage of RewriteRoles itself and its actual wiring into BuildRequest.\nfunc TestBuildRequestAppliesRoleMap(t *testing.T) {\n\traw := []byte(`{\"model\":\"vm\",\"messages\":[{\"role\":\"developer\",\"content\":\"be helpful\"},{\"role\":\"user\",\"content\":\"hi\"}]}`)\n\tep := &core.Endpoint{Provider: \"p\", BaseURL: \"https://x.example/v1\", APIKey: \"k\", Model: \"m\", RoleMap: map[string]string{\"developer\": \"system\"}}\n\tep.FullURL = Anthropic{}.ResolveURL(ep.BaseURL)\n\n\t_, outBody, err := Anthropic{}.BuildRequest(context.Background(), ep, &core.CanonicalRequest{Model: \"vm\", Raw: raw})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif !bytes.Contains(outBody, []byte(`\"role\":\"system\"`)) {\n\t\tt.Errorf(\"developer role not remapped: %s\", outBody)\n\t}\n\tif bytes.Contains(outBody, []byte(`\"developer\"`)) {\n\t\tt.Errorf(\"developer role still present: %s\", outBody)\n\t}\n}\n\nfunc TestClassifyError(t *testing.T) {\n\tcases := []struct {\n\t\tstatus int\n\t\tbody string\n\t\twant core.ErrorClass\n\t}{\n\t\t{400, `{\"type\":\"error\",\"error\":{\"type\":\"invalid_request_error\",\"message\":\"max_tokens required\"}}`, core.ErrClient},\n\t\t// DeepSeek anthropic endpoint, live-captured wording for a wrong model name:\n\t\t{400, `{\"error\":{\"message\":\"The supported API model names are deepseek-v4-pro or deepseek-v4-flash, but you passed no-such.\"}}`, core.ErrEndpoint},\n\t\t{401, `{\"type\":\"error\",\"error\":{\"type\":\"authentication_error\",\"message\":\"login fail\"}}`, core.ErrAuth},\n\t\t{404, `{\"type\":\"error\",\"error\":{\"type\":\"not_found_error\",\"message\":\"model not found\"}}`, core.ErrEndpoint},\n\t\t{429, `{\"type\":\"error\",\"error\":{\"type\":\"rate_limit_error\",\"message\":\"slow down\"}}`, core.ErrRateLimit},\n\t\t{500, `{}`, core.ErrTransient},\n\t\t{529, `{\"type\":\"error\",\"error\":{\"type\":\"overloaded_error\",\"message\":\"Overloaded\"}}`, core.ErrTransient},\n\t}\n\tfor _, c := range cases {\n\t\tif got := (Anthropic{}).ClassifyError(c.status, []byte(c.body)); got != c.want {\n\t\t\tt.Errorf(\"status=%d body=%s: got %v want %v\", c.status, c.body, got, c.want)\n\t\t}\n\t}\n}\n\nfunc TestProtocol(t *testing.T) {\n\tif (Anthropic{}).Protocol() != \"anthropic\" {\n\t\tt.Error(\"protocol\")\n\t}\n}\n"}, "1c96f45772f0e7ab19ce1e249b31fa6d766bc5d0": {"t": 1785759766.089111, "n": 3614, "h": "0d91c2f7b06e1fc1", "c": "// Ver 2026-07-07, by Fable 5\npackage anthropic\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"vmr/internal/core\"\n)\n\nfunc TestBuildRequest(t *testing.T) {\n\traw := []byte(`{\"model\":\"claude\",\"max_tokens\":16,\"stream\":true,\"messages\":[{\"role\":\"user\",\"content\":\"hi\"}],\"future_param\":1}`)\n\tep := &core.Endpoint{Provider: \"p\", BaseURL: \"https://api.deepseek.com/anthropic/v1/\", APIKey: \"sk-1\", Model: \"deepseek-v4-pro\"}\n\tep.FullURL = Anthropic{}.ResolveURL(ep.BaseURL)\n\n\treq, outBody, err := Anthropic{}.BuildRequest(context.Background(), ep, &core.CanonicalRequest{Model: \"claude\", Stream: true, Raw: raw})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif len(outBody) == 0 {\n\t\tt.Fatal(\"BuildRequest returned empty outbound body\")\n\t}\n\tif req.URL.String() != \"https://api.deepseek.com/anthropic/v1/messages\" {\n\t\tt.Errorf(\"url: %s\", req.URL)\n\t}\n\tif got := req.Header.Get(\"x-api-key\"); got != \"sk-1\" {\n\t\tt.Errorf(\"x-api-key: %q\", got)\n\t}\n\t// The client sent no anthropic-version, and BuildRequest must not\n\t// invent one — omitting it is what a direct connection would see too.\n\tif got := req.Header.Get(\"anthropic-version\"); got != \"\" {\n\t\tt.Errorf(\"anthropic-version should be left unset when the client omits it, got %q\", got)\n\t}\n\tvar m map[string]json.RawMessage\n\tbody, _ := req.GetBody()\n\tif err := json.NewDecoder(body).Decode(&m); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tvar model string\n\tjson.Unmarshal(m[\"model\"], &model)\n\tif model != \"deepseek-v4-pro\" {\n\t\tt.Errorf(\"model not rewritten: %q\", model)\n\t}\n\tif string(m[\"future_param\"]) != \"1\" || string(m[\"max_tokens\"]) != \"16\" {\n\t\tt.Error(\"unknown/other fields mangled\")\n\t}\n}\n\nfunc TestBuildRequestForwardsProtocolHeaders(t *testing.T) {\n\thdr := http.Header{}\n\thdr.Set(\"anthropic-version\", \"2024-10-22\")\n\thdr.Set(\"anthropic-beta\", \"context-1m-2025-08-07\")\n\tep := &core.Endpoint{Provider: \"p\", BaseURL: \"https://x.example/v1\", APIKey: \"k\", Model: \"m\"}\n\tep.FullURL = Anthropic{}.ResolveURL(ep.BaseURL)\n\treq, _, err := Anthropic{}.BuildRequest(context.Background(), ep,\n\t\t&core.CanonicalRequest{Model: \"vm\", Raw: []byte(`{\"model\":\"vm\"}`), Header: hdr})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif got := req.Header.Get(\"anthropic-version\"); got != \"2024-10-22\" {\n\t\tt.Errorf(\"version not forwarded: %q\", got)\n\t}\n\tif got := req.Header.Get(\"anthropic-beta\"); got != \"context-1m-2025-08-07\" {\n\t\tt.Errorf(\"beta not forwarded: %q\", got)\n\t}\n}\n\nfunc TestClassifyError(t *testing.T) {\n\tcases := []struct {\n\t\tstatus int\n\t\tbody string\n\t\twant core.ErrorClass\n\t}{\n\t\t{400, `{\"type\":\"error\",\"error\":{\"type\":\"invalid_request_error\",\"message\":\"max_tokens required\"}}`, core.ErrClient},\n\t\t// DeepSeek anthropic endpoint, live-captured wording for a wrong model name:\n\t\t{400, `{\"error\":{\"message\":\"The supported API model names are deepseek-v4-pro or deepseek-v4-flash, but you passed no-such.\"}}`, core.ErrEndpoint},\n\t\t{401, `{\"type\":\"error\",\"error\":{\"type\":\"authentication_error\",\"message\":\"login fail\"}}`, core.ErrAuth},\n\t\t{404, `{\"type\":\"error\",\"error\":{\"type\":\"not_found_error\",\"message\":\"model not found\"}}`, core.ErrEndpoint},\n\t\t{429, `{\"type\":\"error\",\"error\":{\"type\":\"rate_limit_error\",\"message\":\"slow down\"}}`, core.ErrRateLimit},\n\t\t{500, `{}`, core.ErrTransient},\n\t\t{529, `{\"type\":\"error\",\"error\":{\"type\":\"overloaded_error\",\"message\":\"Overloaded\"}}`, core.ErrTransient},\n\t}\n\tfor _, c := range cases {\n\t\tif got := (Anthropic{}).ClassifyError(c.status, []byte(c.body)); got != c.want {\n\t\t\tt.Errorf(\"status=%d body=%s: got %v want %v\", c.status, c.body, got, c.want)\n\t\t}\n\t}\n}\n\nfunc TestProtocol(t *testing.T) {\n\tif (Anthropic{}).Protocol() != \"anthropic\" {\n\t\tt.Error(\"protocol\")\n\t}\n}\n"}, "7b1932a91970d3acf9df76592f3280a7f817d441": {"t": 1785759767.436736, "n": 3516, "h": "c797812c9846e557", "c": "// Ver 2026-07-07, by Fable 5\npackage anthropic\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"vmr/internal/core\"\n)\n\nfunc TestBuildRequest(t *testing.T) {\n\traw := []byte(`{\"model\":\"claude\",\"max_tokens\":16,\"stream\":true,\"messages\":[{\"role\":\"user\",\"content\":\"hi\"}],\"future_param\":1}`)\n\tep := &core.Endpoint{Provider: \"p\", BaseURL: \"https://api.deepseek.com/anthropic/v1/\", APIKey: \"sk-1\", Model: \"deepseek-v4-pro\"}\n\n\treq, outBody, err := Anthropic{}.BuildRequest(context.Background(), ep, &core.CanonicalRequest{Model: \"claude\", Stream: true, Raw: raw})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif len(outBody) == 0 {\n\t\tt.Fatal(\"BuildRequest returned empty outbound body\")\n\t}\n\tif req.URL.String() != \"https://api.deepseek.com/anthropic/v1/messages\" {\n\t\tt.Errorf(\"url: %s\", req.URL)\n\t}\n\tif got := req.Header.Get(\"x-api-key\"); got != \"sk-1\" {\n\t\tt.Errorf(\"x-api-key: %q\", got)\n\t}\n\t// The client sent no anthropic-version, and BuildRequest must not\n\t// invent one — omitting it is what a direct connection would see too.\n\tif got := req.Header.Get(\"anthropic-version\"); got != \"\" {\n\t\tt.Errorf(\"anthropic-version should be left unset when the client omits it, got %q\", got)\n\t}\n\tvar m map[string]json.RawMessage\n\tbody, _ := req.GetBody()\n\tif err := json.NewDecoder(body).Decode(&m); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tvar model string\n\tjson.Unmarshal(m[\"model\"], &model)\n\tif model != \"deepseek-v4-pro\" {\n\t\tt.Errorf(\"model not rewritten: %q\", model)\n\t}\n\tif string(m[\"future_param\"]) != \"1\" || string(m[\"max_tokens\"]) != \"16\" {\n\t\tt.Error(\"unknown/other fields mangled\")\n\t}\n}\n\nfunc TestBuildRequestForwardsProtocolHeaders(t *testing.T) {\n\thdr := http.Header{}\n\thdr.Set(\"anthropic-version\", \"2024-10-22\")\n\thdr.Set(\"anthropic-beta\", \"context-1m-2025-08-07\")\n\tep := &core.Endpoint{Provider: \"p\", BaseURL: \"https://x.example/v1\", APIKey: \"k\", Model: \"m\"}\n\treq, _, err := Anthropic{}.BuildRequest(context.Background(), ep,\n\t\t&core.CanonicalRequest{Model: \"vm\", Raw: []byte(`{\"model\":\"vm\"}`), Header: hdr})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif got := req.Header.Get(\"anthropic-version\"); got != \"2024-10-22\" {\n\t\tt.Errorf(\"version not forwarded: %q\", got)\n\t}\n\tif got := req.Header.Get(\"anthropic-beta\"); got != \"context-1m-2025-08-07\" {\n\t\tt.Errorf(\"beta not forwarded: %q\", got)\n\t}\n}\n\nfunc TestClassifyError(t *testing.T) {\n\tcases := []struct {\n\t\tstatus int\n\t\tbody string\n\t\twant core.ErrorClass\n\t}{\n\t\t{400, `{\"type\":\"error\",\"error\":{\"type\":\"invalid_request_error\",\"message\":\"max_tokens required\"}}`, core.ErrClient},\n\t\t// DeepSeek anthropic endpoint, live-captured wording for a wrong model name:\n\t\t{400, `{\"error\":{\"message\":\"The supported API model names are deepseek-v4-pro or deepseek-v4-flash, but you passed no-such.\"}}`, core.ErrEndpoint},\n\t\t{401, `{\"type\":\"error\",\"error\":{\"type\":\"authentication_error\",\"message\":\"login fail\"}}`, core.ErrAuth},\n\t\t{404, `{\"type\":\"error\",\"error\":{\"type\":\"not_found_error\",\"message\":\"model not found\"}}`, core.ErrEndpoint},\n\t\t{429, `{\"type\":\"error\",\"error\":{\"type\":\"rate_limit_error\",\"message\":\"slow down\"}}`, core.ErrRateLimit},\n\t\t{500, `{}`, core.ErrTransient},\n\t\t{529, `{\"type\":\"error\",\"error\":{\"type\":\"overloaded_error\",\"message\":\"Overloaded\"}}`, core.ErrTransient},\n\t}\n\tfor _, c := range cases {\n\t\tif got := (Anthropic{}).ClassifyError(c.status, []byte(c.body)); got != c.want {\n\t\t\tt.Errorf(\"status=%d body=%s: got %v want %v\", c.status, c.body, got, c.want)\n\t\t}\n\t}\n}\n\nfunc TestProtocol(t *testing.T) {\n\tif (Anthropic{}).Protocol() != \"anthropic\" {\n\t\tt.Error(\"protocol\")\n\t}\n}\n"}, "2d972e103903796177187bb414ef25087dd36638": {"t": 1785759768.754936, "n": 3334, "h": "de9083147cdb34a5", "c": "// Ver 2026-07-07, by Fable 5\npackage anthropic\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"vmr/internal/core\"\n)\n\nfunc TestBuildRequest(t *testing.T) {\n\traw := []byte(`{\"model\":\"claude\",\"max_tokens\":16,\"stream\":true,\"messages\":[{\"role\":\"user\",\"content\":\"hi\"}],\"future_param\":1}`)\n\tep := &core.Endpoint{Provider: \"p\", BaseURL: \"https://api.deepseek.com/anthropic/v1/\", APIKey: \"sk-1\", Model: \"deepseek-v4-pro\"}\n\n\treq, outBody, err := Anthropic{}.BuildRequest(context.Background(), ep, &core.CanonicalRequest{Model: \"claude\", Stream: true, Raw: raw})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif len(outBody) == 0 {\n\t\tt.Fatal(\"BuildRequest returned empty outbound body\")\n\t}\n\tif req.URL.String() != \"https://api.deepseek.com/anthropic/v1/messages\" {\n\t\tt.Errorf(\"url: %s\", req.URL)\n\t}\n\tif got := req.Header.Get(\"x-api-key\"); got != \"sk-1\" {\n\t\tt.Errorf(\"x-api-key: %q\", got)\n\t}\n\tif got := req.Header.Get(\"anthropic-version\"); got != \"2023-06-01\" {\n\t\tt.Errorf(\"default version: %q\", got)\n\t}\n\tvar m map[string]json.RawMessage\n\tbody, _ := req.GetBody()\n\tif err := json.NewDecoder(body).Decode(&m); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tvar model string\n\tjson.Unmarshal(m[\"model\"], &model)\n\tif model != \"deepseek-v4-pro\" {\n\t\tt.Errorf(\"model not rewritten: %q\", model)\n\t}\n\tif string(m[\"future_param\"]) != \"1\" || string(m[\"max_tokens\"]) != \"16\" {\n\t\tt.Error(\"unknown/other fields mangled\")\n\t}\n}\n\nfunc TestBuildRequestForwardsProtocolHeaders(t *testing.T) {\n\thdr := http.Header{}\n\thdr.Set(\"anthropic-version\", \"2024-10-22\")\n\thdr.Set(\"anthropic-beta\", \"context-1m-2025-08-07\")\n\tep := &core.Endpoint{Provider: \"p\", BaseURL: \"https://x.example/v1\", APIKey: \"k\", Model: \"m\"}\n\treq, _, err := Anthropic{}.BuildRequest(context.Background(), ep,\n\t\t&core.CanonicalRequest{Model: \"vm\", Raw: []byte(`{\"model\":\"vm\"}`), Header: hdr})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif got := req.Header.Get(\"anthropic-version\"); got != \"2024-10-22\" {\n\t\tt.Errorf(\"version not forwarded: %q\", got)\n\t}\n\tif got := req.Header.Get(\"anthropic-beta\"); got != \"context-1m-2025-08-07\" {\n\t\tt.Errorf(\"beta not forwarded: %q\", got)\n\t}\n}\n\nfunc TestClassifyError(t *testing.T) {\n\tcases := []struct {\n\t\tstatus int\n\t\tbody string\n\t\twant core.ErrorClass\n\t}{\n\t\t{400, `{\"type\":\"error\",\"error\":{\"type\":\"invalid_request_error\",\"message\":\"max_tokens required\"}}`, core.ErrClient},\n\t\t// DeepSeek anthropic endpoint, live-captured wording for a wrong model name:\n\t\t{400, `{\"error\":{\"message\":\"The supported API model names are deepseek-v4-pro or deepseek-v4-flash, but you passed no-such.\"}}`, core.ErrEndpoint},\n\t\t{401, `{\"type\":\"error\",\"error\":{\"type\":\"authentication_error\",\"message\":\"login fail\"}}`, core.ErrAuth},\n\t\t{404, `{\"type\":\"error\",\"error\":{\"type\":\"not_found_error\",\"message\":\"model not found\"}}`, core.ErrEndpoint},\n\t\t{429, `{\"type\":\"error\",\"error\":{\"type\":\"rate_limit_error\",\"message\":\"slow down\"}}`, core.ErrRateLimit},\n\t\t{500, `{}`, core.ErrTransient},\n\t\t{529, `{\"type\":\"error\",\"error\":{\"type\":\"overloaded_error\",\"message\":\"Overloaded\"}}`, core.ErrTransient},\n\t}\n\tfor _, c := range cases {\n\t\tif got := (Anthropic{}).ClassifyError(c.status, []byte(c.body)); got != c.want {\n\t\t\tt.Errorf(\"status=%d body=%s: got %v want %v\", c.status, c.body, got, c.want)\n\t\t}\n\t}\n}\n\nfunc TestProtocol(t *testing.T) {\n\tif (Anthropic{}).Protocol() != \"anthropic\" {\n\t\tt.Error(\"protocol\")\n\t}\n}\n"}, "df038c9eca16884d7a8550856902f716731b668f": {"t": 1785759770.176902, "n": 3240, "h": "8497619f4beae06c", "c": "// Ver 2026-07-07, by Fable 5\npackage anthropic\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"vmr/internal/core\"\n)\n\nfunc TestBuildRequest(t *testing.T) {\n\traw := []byte(`{\"model\":\"claude\",\"max_tokens\":16,\"stream\":true,\"messages\":[{\"role\":\"user\",\"content\":\"hi\"}],\"future_param\":1}`)\n\tep := &core.Endpoint{Provider: \"p\", BaseURL: \"https://api.deepseek.com/anthropic/v1/\", APIKey: \"sk-1\", Model: \"deepseek-v4-pro\"}\n\n\treq, err := Anthropic{}.BuildRequest(context.Background(), ep, &core.CanonicalRequest{Model: \"claude\", Stream: true, Raw: raw})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif req.URL.String() != \"https://api.deepseek.com/anthropic/v1/messages\" {\n\t\tt.Errorf(\"url: %s\", req.URL)\n\t}\n\tif got := req.Header.Get(\"x-api-key\"); got != \"sk-1\" {\n\t\tt.Errorf(\"x-api-key: %q\", got)\n\t}\n\tif got := req.Header.Get(\"anthropic-version\"); got != \"2023-06-01\" {\n\t\tt.Errorf(\"default version: %q\", got)\n\t}\n\tvar m map[string]json.RawMessage\n\tbody, _ := req.GetBody()\n\tif err := json.NewDecoder(body).Decode(&m); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tvar model string\n\tjson.Unmarshal(m[\"model\"], &model)\n\tif model != \"deepseek-v4-pro\" {\n\t\tt.Errorf(\"model not rewritten: %q\", model)\n\t}\n\tif string(m[\"future_param\"]) != \"1\" || string(m[\"max_tokens\"]) != \"16\" {\n\t\tt.Error(\"unknown/other fields mangled\")\n\t}\n}\n\nfunc TestBuildRequestForwardsProtocolHeaders(t *testing.T) {\n\thdr := http.Header{}\n\thdr.Set(\"anthropic-version\", \"2024-10-22\")\n\thdr.Set(\"anthropic-beta\", \"context-1m-2025-08-07\")\n\tep := &core.Endpoint{Provider: \"p\", BaseURL: \"https://x.example/v1\", APIKey: \"k\", Model: \"m\"}\n\treq, err := Anthropic{}.BuildRequest(context.Background(), ep,\n\t\t&core.CanonicalRequest{Model: \"vm\", Raw: []byte(`{\"model\":\"vm\"}`), Header: hdr})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif got := req.Header.Get(\"anthropic-version\"); got != \"2024-10-22\" {\n\t\tt.Errorf(\"version not forwarded: %q\", got)\n\t}\n\tif got := req.Header.Get(\"anthropic-beta\"); got != \"context-1m-2025-08-07\" {\n\t\tt.Errorf(\"beta not forwarded: %q\", got)\n\t}\n}\n\nfunc TestClassifyError(t *testing.T) {\n\tcases := []struct {\n\t\tstatus int\n\t\tbody string\n\t\twant core.ErrorClass\n\t}{\n\t\t{400, `{\"type\":\"error\",\"error\":{\"type\":\"invalid_request_error\",\"message\":\"max_tokens required\"}}`, core.ErrClient},\n\t\t// DeepSeek anthropic endpoint, live-captured wording for a wrong model name:\n\t\t{400, `{\"error\":{\"message\":\"The supported API model names are deepseek-v4-pro or deepseek-v4-flash, but you passed no-such.\"}}`, core.ErrEndpoint},\n\t\t{401, `{\"type\":\"error\",\"error\":{\"type\":\"authentication_error\",\"message\":\"login fail\"}}`, core.ErrAuth},\n\t\t{404, `{\"type\":\"error\",\"error\":{\"type\":\"not_found_error\",\"message\":\"model not found\"}}`, core.ErrEndpoint},\n\t\t{429, `{\"type\":\"error\",\"error\":{\"type\":\"rate_limit_error\",\"message\":\"slow down\"}}`, core.ErrRateLimit},\n\t\t{500, `{}`, core.ErrTransient},\n\t\t{529, `{\"type\":\"error\",\"error\":{\"type\":\"overloaded_error\",\"message\":\"Overloaded\"}}`, core.ErrTransient},\n\t}\n\tfor _, c := range cases {\n\t\tif got := (Anthropic{}).ClassifyError(c.status, []byte(c.body)); got != c.want {\n\t\t\tt.Errorf(\"status=%d body=%s: got %v want %v\", c.status, c.body, got, c.want)\n\t\t}\n\t}\n}\n\nfunc TestProtocol(t *testing.T) {\n\tif (Anthropic{}).Protocol() != \"anthropic\" {\n\t\tt.Error(\"protocol\")\n\t}\n}\n"}, "4fc7e9b7b4f92ce275034618ce92f7861ee147c2": {"t": 1786067578.3382, "n": 4528, "h": "eae3327500c91cbf", "c": "// Ver 2026-07-07, by Fable 5\npackage anthropic\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"vmr/internal/core\"\n)\n\nfunc TestBuildRequest(t *testing.T) {\n\traw := []byte(`{\"model\":\"claude\",\"max_tokens\":16,\"stream\":true,\"messages\":[{\"role\":\"user\",\"content\":\"hi\"}],\"future_param\":1}`)\n\tep := &core.Endpoint{Provider: \"p\", BaseURL: \"https://api.deepseek.com/anthropic/v1/\", APIKey: \"sk-1\", Model: \"deepseek-v4-pro\"}\n\tep.FullURL = Anthropic{}.ResolveURL(ep.BaseURL)\n\n\treq, outBody, err := Anthropic{}.BuildRequest(context.Background(), ep, &core.CanonicalRequest{Model: \"claude\", Stream: true, Raw: raw})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif len(outBody) == 0 {\n\t\tt.Fatal(\"BuildRequest returned empty outbound body\")\n\t}\n\tif req.URL.String() != \"https://api.deepseek.com/anthropic/v1/messages\" {\n\t\tt.Errorf(\"url: %s\", req.URL)\n\t}\n\tif got := req.Header.Get(\"x-api-key\"); got != \"sk-1\" {\n\t\tt.Errorf(\"x-api-key: %q\", got)\n\t}\n\t// The client sent no anthropic-version, and BuildRequest must not\n\t// invent one — omitting it is what a direct connection would see too.\n\tif got := req.Header.Get(\"anthropic-version\"); got != \"\" {\n\t\tt.Errorf(\"anthropic-version should be left unset when the client omits it, got %q\", got)\n\t}\n\tvar m map[string]json.RawMessage\n\tbody, _ := req.GetBody()\n\tif err := json.NewDecoder(body).Decode(&m); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tvar model string\n\tjson.Unmarshal(m[\"model\"], &model)\n\tif model != \"deepseek-v4-pro\" {\n\t\tt.Errorf(\"model not rewritten: %q\", model)\n\t}\n\tif string(m[\"future_param\"]) != \"1\" || string(m[\"max_tokens\"]) != \"16\" {\n\t\tt.Error(\"unknown/other fields mangled\")\n\t}\n}\n\nfunc TestBuildRequestForwardsProtocolHeaders(t *testing.T) {\n\thdr := http.Header{}\n\thdr.Set(\"anthropic-version\", \"2024-10-22\")\n\thdr.Set(\"anthropic-beta\", \"context-1m-2025-08-07\")\n\tep := &core.Endpoint{Provider: \"p\", BaseURL: \"https://x.example/v1\", APIKey: \"k\", Model: \"m\"}\n\tep.FullURL = Anthropic{}.ResolveURL(ep.BaseURL)\n\treq, _, err := Anthropic{}.BuildRequest(context.Background(), ep,\n\t\t&core.CanonicalRequest{Model: \"vm\", Raw: []byte(`{\"model\":\"vm\"}`), Header: hdr})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif got := req.Header.Get(\"anthropic-version\"); got != \"2024-10-22\" {\n\t\tt.Errorf(\"version not forwarded: %q\", got)\n\t}\n\tif got := req.Header.Get(\"anthropic-beta\"); got != \"context-1m-2025-08-07\" {\n\t\tt.Errorf(\"beta not forwarded: %q\", got)\n\t}\n}\n\n// TestBuildRequestAppliesRoleMap closes the gap between classify_test.go's\n// coverage of RewriteRoles itself and its actual wiring into BuildRequest.\nfunc TestBuildRequestAppliesRoleMap(t *testing.T) {\n\traw := []byte(`{\"model\":\"vm\",\"messages\":[{\"role\":\"developer\",\"content\":\"be helpful\"},{\"role\":\"user\",\"content\":\"hi\"}]}`)\n\tep := &core.Endpoint{Provider: \"p\", BaseURL: \"https://x.example/v1\", APIKey: \"k\", Model: \"m\", RoleMap: map[string]string{\"developer\": \"system\"}}\n\tep.FullURL = Anthropic{}.ResolveURL(ep.BaseURL)\n\n\t_, outBody, err := Anthropic{}.BuildRequest(context.Background(), ep, &core.CanonicalRequest{Model: \"vm\", Raw: raw})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif !bytes.Contains(outBody, []byte(`\"role\":\"system\"`)) {\n\t\tt.Errorf(\"developer role not remapped: %s\", outBody)\n\t}\n\tif bytes.Contains(outBody, []byte(`\"developer\"`)) {\n\t\tt.Errorf(\"developer role still present: %s\", outBody)\n\t}\n}\n\nfunc TestClassifyError(t *testing.T) {\n\tcases := []struct {\n\t\tstatus int\n\t\tbody string\n\t\twant core.ErrorClass\n\t}{\n\t\t{400, `{\"type\":\"error\",\"error\":{\"type\":\"invalid_request_error\",\"message\":\"max_tokens required\"}}`, core.ErrClient},\n\t\t// DeepSeek anthropic endpoint, live-captured wording for a wrong model name:\n\t\t{400, `{\"error\":{\"message\":\"The supported API model names are deepseek-v4-pro or deepseek-v4-flash, but you passed no-such.\"}}`, core.ErrEndpoint},\n\t\t{401, `{\"type\":\"error\",\"error\":{\"type\":\"authentication_error\",\"message\":\"login fail\"}}`, core.ErrAuth},\n\t\t{404, `{\"type\":\"error\",\"error\":{\"type\":\"not_found_error\",\"message\":\"model not found\"}}`, core.ErrEndpoint},\n\t\t{429, `{\"type\":\"error\",\"error\":{\"type\":\"rate_limit_error\",\"message\":\"slow down\"}}`, core.ErrRateLimit},\n\t\t{500, `{}`, core.ErrTransient},\n\t\t{529, `{\"type\":\"error\",\"error\":{\"type\":\"overloaded_error\",\"message\":\"Overloaded\"}}`, core.ErrTransient},\n\t}\n\tfor _, c := range cases {\n\t\tif got := (Anthropic{}).ClassifyError(c.status, []byte(c.body)); got != c.want {\n\t\t\tt.Errorf(\"status=%d body=%s: got %v want %v\", c.status, c.body, got, c.want)\n\t\t}\n\t}\n}\n\nfunc TestProtocol(t *testing.T) {\n\tif (Anthropic{}).Protocol() != \"anthropic\" {\n\t\tt.Error(\"protocol\")\n\t}\n}\n"}, "518f966cc78e02f9bbcf95cadc7e290a295ca2bd": {"t": 1786068374.0169713, "n": 4528, "h": "eae3327500c91cbf", "c": "// Ver 2026-07-07, by Fable 5\npackage anthropic\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"vmr/internal/core\"\n)\n\nfunc TestBuildRequest(t *testing.T) {\n\traw := []byte(`{\"model\":\"claude\",\"max_tokens\":16,\"stream\":true,\"messages\":[{\"role\":\"user\",\"content\":\"hi\"}],\"future_param\":1}`)\n\tep := &core.Endpoint{Provider: \"p\", BaseURL: \"https://api.deepseek.com/anthropic/v1/\", APIKey: \"sk-1\", Model: \"deepseek-v4-pro\"}\n\tep.FullURL = Anthropic{}.ResolveURL(ep.BaseURL)\n\n\treq, outBody, err := Anthropic{}.BuildRequest(context.Background(), ep, &core.CanonicalRequest{Model: \"claude\", Stream: true, Raw: raw})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif len(outBody) == 0 {\n\t\tt.Fatal(\"BuildRequest returned empty outbound body\")\n\t}\n\tif req.URL.String() != \"https://api.deepseek.com/anthropic/v1/messages\" {\n\t\tt.Errorf(\"url: %s\", req.URL)\n\t}\n\tif got := req.Header.Get(\"x-api-key\"); got != \"sk-1\" {\n\t\tt.Errorf(\"x-api-key: %q\", got)\n\t}\n\t// The client sent no anthropic-version, and BuildRequest must not\n\t// invent one — omitting it is what a direct connection would see too.\n\tif got := req.Header.Get(\"anthropic-version\"); got != \"\" {\n\t\tt.Errorf(\"anthropic-version should be left unset when the client omits it, got %q\", got)\n\t}\n\tvar m map[string]json.RawMessage\n\tbody, _ := req.GetBody()\n\tif err := json.NewDecoder(body).Decode(&m); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tvar model string\n\tjson.Unmarshal(m[\"model\"], &model)\n\tif model != \"deepseek-v4-pro\" {\n\t\tt.Errorf(\"model not rewritten: %q\", model)\n\t}\n\tif string(m[\"future_param\"]) != \"1\" || string(m[\"max_tokens\"]) != \"16\" {\n\t\tt.Error(\"unknown/other fields mangled\")\n\t}\n}\n\nfunc TestBuildRequestForwardsProtocolHeaders(t *testing.T) {\n\thdr := http.Header{}\n\thdr.Set(\"anthropic-version\", \"2024-10-22\")\n\thdr.Set(\"anthropic-beta\", \"context-1m-2025-08-07\")\n\tep := &core.Endpoint{Provider: \"p\", BaseURL: \"https://x.example/v1\", APIKey: \"k\", Model: \"m\"}\n\tep.FullURL = Anthropic{}.ResolveURL(ep.BaseURL)\n\treq, _, err := Anthropic{}.BuildRequest(context.Background(), ep,\n\t\t&core.CanonicalRequest{Model: \"vm\", Raw: []byte(`{\"model\":\"vm\"}`), Header: hdr})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif got := req.Header.Get(\"anthropic-version\"); got != \"2024-10-22\" {\n\t\tt.Errorf(\"version not forwarded: %q\", got)\n\t}\n\tif got := req.Header.Get(\"anthropic-beta\"); got != \"context-1m-2025-08-07\" {\n\t\tt.Errorf(\"beta not forwarded: %q\", got)\n\t}\n}\n\n// TestBuildRequestAppliesRoleMap closes the gap between classify_test.go's\n// coverage of RewriteRoles itself and its actual wiring into BuildRequest.\nfunc TestBuildRequestAppliesRoleMap(t *testing.T) {\n\traw := []byte(`{\"model\":\"vm\",\"messages\":[{\"role\":\"developer\",\"content\":\"be helpful\"},{\"role\":\"user\",\"content\":\"hi\"}]}`)\n\tep := &core.Endpoint{Provider: \"p\", BaseURL: \"https://x.example/v1\", APIKey: \"k\", Model: \"m\", RoleMap: map[string]string{\"developer\": \"system\"}}\n\tep.FullURL = Anthropic{}.ResolveURL(ep.BaseURL)\n\n\t_, outBody, err := Anthropic{}.BuildRequest(context.Background(), ep, &core.CanonicalRequest{Model: \"vm\", Raw: raw})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif !bytes.Contains(outBody, []byte(`\"role\":\"system\"`)) {\n\t\tt.Errorf(\"developer role not remapped: %s\", outBody)\n\t}\n\tif bytes.Contains(outBody, []byte(`\"developer\"`)) {\n\t\tt.Errorf(\"developer role still present: %s\", outBody)\n\t}\n}\n\nfunc TestClassifyError(t *testing.T) {\n\tcases := []struct {\n\t\tstatus int\n\t\tbody string\n\t\twant core.ErrorClass\n\t}{\n\t\t{400, `{\"type\":\"error\",\"error\":{\"type\":\"invalid_request_error\",\"message\":\"max_tokens required\"}}`, core.ErrClient},\n\t\t// DeepSeek anthropic endpoint, live-captured wording for a wrong model name:\n\t\t{400, `{\"error\":{\"message\":\"The supported API model names are deepseek-v4-pro or deepseek-v4-flash, but you passed no-such.\"}}`, core.ErrEndpoint},\n\t\t{401, `{\"type\":\"error\",\"error\":{\"type\":\"authentication_error\",\"message\":\"login fail\"}}`, core.ErrAuth},\n\t\t{404, `{\"type\":\"error\",\"error\":{\"type\":\"not_found_error\",\"message\":\"model not found\"}}`, core.ErrEndpoint},\n\t\t{429, `{\"type\":\"error\",\"error\":{\"type\":\"rate_limit_error\",\"message\":\"slow down\"}}`, core.ErrRateLimit},\n\t\t{500, `{}`, core.ErrTransient},\n\t\t{529, `{\"type\":\"error\",\"error\":{\"type\":\"overloaded_error\",\"message\":\"Overloaded\"}}`, core.ErrTransient},\n\t}\n\tfor _, c := range cases {\n\t\tif got := (Anthropic{}).ClassifyError(c.status, []byte(c.body)); got != c.want {\n\t\t\tt.Errorf(\"status=%d body=%s: got %v want %v\", c.status, c.body, got, c.want)\n\t\t}\n\t}\n}\n\nfunc TestProtocol(t *testing.T) {\n\tif (Anthropic{}).Protocol() != \"anthropic\" {\n\t\tt.Error(\"protocol\")\n\t}\n}\n"}}, "config.example.yaml": {"4fc7e9b7b4f92ce275034618ce92f7861ee147c2": {"t": 1786067310.1989918, "n": 14918, "h": "95fa8e18d614d8b3", "c": "# Ver 2026-07-30 12:00, by Sonnet 5\n# vmr config — copy to config.yaml and adjust.\n# providers = what you have (a flat list of accounts); models = what clients see\n# and in which order to use it.\n#\n# providers is a flat list: one entry per upstream account, however many of\n# the two ingress protocols (\"openai\" | \"anthropic\") that account actually\n# speaks — base_url is itself keyed by protocol, so the same account (same\n# api_key, same proxy policy) can back both surfaces from one entry instead\n# of being declared twice.\n#\n# models is keyed by virtual-model name only. A virtual model's own top-level\n# `capabilities`/`max_context_tokens` are the *base* shared by every endpoint\n# under it — set once instead of repeating the same declaration on every\n# try-order candidate (see the \"agent\" example below). Each entry under a\n# model's `endpoints:` list is one try-order candidate: it names its own\n# protocol (which ingress can reach it — POST /v1/chat/completions for\n# \"openai\", POST /v1/messages for \"anthropic\"), a provider (resolved by name\n# against the providers list above), and a `models:` list of upstream model\n# names that all share this entry's capabilities/max_context_tokens/\n# role_map/sticky_ttl/priority — each name expands into its own independent,\n# individually health-tracked endpoint, in list order. An entry's own\n# `capabilities` is *additive* to the model's base (union — declare only\n# what this specific entry adds beyond the shared floor); its own\n# `max_context_tokens` *overrides* the base instead when set (a single\n# number can't be unioned). A virtual model name can mix protocols: give it\n# one endpoint-group entry with protocol: openai and another with protocol:\n# anthropic, and it becomes reachable — with potentially different upstream\n# candidates — from both ingress protocols at once (see the \"agent\" example\n# below). Mixing protocols within one endpoint-group entry has no syntax to\n# express, rather than being a config error to catch: each entry is exactly\n# one protocol.\n\nlisten: 127.0.0.1:8800\n# api_keys: # optional: protect the router (Bearer or x-api-key). Each key's\n# # tail also tags its caller: `vmr report` then auto-writes a\n# # vmr-requests-<tag>.jsonl / vmr-requests-index-<tag>.md pair per\n# # tag, alongside the usual full files. No flag needed for this.\n# #\n# # Tag = last 8 chars of the key, cut to after the last \"-\" in that\n# # window — so end each key \"-<label>\" (e.g. ...-alice -> tag\n# # \"alice\"). Use >= 3-4 chars so two keys don't land on the same tag.\n# # The tag becomes a filename (sanitized: non A-Za-z0-9._- -> \"-\"),\n# # and vmr does NOT detect two DIFFERENT tags colliding after that —\n# # pick suffixes that are already filename-safe and stay distinct.\n# # Each key must be >= 16 chars, else the tag window is the whole key.\n# - ${VMR_KEY_ALICE}\n# - ${VMR_KEY_OPENCLAW}\n#\n# No api_keys configured: the door stays open, but each client's own Authorization/x-api-key\n# value still gets the same tag treatment (no 16-char minimum here — it's not a secret). A\n# client sending nothing stays untagged.\n#\n# (Singular `api_key` was removed — a config still using it fails to load with a migration\n# message. Unknown/misspelled keys are load errors too, never silently ignored.)\n# max_attempts: 0 # cap on upstream tries; default 0 = walk every available endpoint\n# probe_mode: active # \"active\" (default): a half-open endpoint gets a small dedicated background probe\n# # request before real traffic is allowed back onto it — real requests never wait on\n# # or get diverted by someone else's slow recovery check. \"passive\": the old\n# # behavior — the next real request IS the probe, so its own size/duration decides\n# # how long that single-flight probe slot (and, under concurrent load, every other\n# # request's access to this endpoint) stays locked.\n# probe_timeout: 15s # active mode only: upper bound on one background probe call\n# max_request_body_mb: 8 # inbound request body size cap (stability, unrelated to audit logging)\n# max_concurrency: 8 # global gate; excess requests wait in memory (0/absent = unlimited)\n# https_proxy: http://127.0.0.1:7890 # proxy server URL for https base_urls — the ONLY way vmr uses a proxy (env vars are\n# # ignored; write ${HTTPS_PROXY} to reference one explicitly). Declaring this URL\n# # does NOT turn proxying on by itself — see `proxy` below.\n# http_proxy: http://127.0.0.1:7890 # same, for http base_urls (e.g. a LAN llama.cpp server)\n# proxy: false # global default for providers with no proxy switch of their own (default false:\n# # http_proxy/https_proxy above only declare where the proxy lives, they opt nobody\n# # in). Recommended: leave this false and opt individual providers in with their own\n# # `proxy: true` (see openrouter below) — explicit per-provider intent, so a provider\n# # added later doesn't silently inherit a stale global default. Flip this to true only\n# # if you want \"proxied\" to be the default and carve out exceptions with a provider's\n# # own `proxy: false` instead.\n# image_downscale: 512 # shrink inline request images to this many px on the long side (0/absent = off); a model's own image_downscale below overrides this\n# image_cache_ttl_days: 7 # evict downscale-cache entries unused for this many days (0/absent = default 7)\n# sticky_ttl: 10m # global default for Sticky Model's affinity window (0/absent = default 10m);\n# # each endpoint below can override it — see the \"cheap\" model for an example;\n# # hard ceiling 24h either way (the sticky registry's own memory-eviction\n# # backstop) — vmr check/start reject anything above that\n# audit_retention_days: 30 # delete audit files older than this; 0/absent = keep forever (rotation always compresses old days to .zst regardless)\n# log_dir: ~/.vmr/logs # audit JSONL directory; used exactly as given (~/ expands); absent = ~/.vmr/logs. Changing it needs a restart\n# image_cache_dir: ~/.vmr/image_cache # downscale-result cache directory; same rule; absent = ~/.vmr/image_cache; follows hot reload\n# timeouts:\n# connect: 10s\n# response_header: 120s # time to first byte from upstream\n# stream_idle: 120s # abort any upstream body (stream, JSON, error) silent for this long\n\nproviders:\n # base_url must include the provider's own full API version (/v1, /v3,\n # whatever it calls it) — vmr just appends the protocol's bare path\n # (/chat/completions for OpenAI, /messages for Anthropic) with no\n # normalization. Providers that don't version their OpenAI/Anthropic\n # surface as \"v1\" (e.g. Volcengine's coding-plan endpoint uses /v3)\n # are exactly why: hardcoding \"/v1\" into the suffix would silently\n # produce the wrong URL for them.\n #\n # base_url declares at least one protocol, up to both — MiniMax speaks\n # both surfaces on the same account, so one entry covers it instead of\n # two (the old format's providers.openai.minimax / providers.anthropic.minimax\n # duplication). A models.<name> endpoint-group's own `protocol:` picks\n # which of these a given try-order entry actually uses.\n - name: minimax\n base_url: {openai: https://api.minimaxi.com/v1, anthropic: https://api.minimaxi.com/anthropic/v1}\n api_key: ${MINIMAX_PLAN_KEY}\n # proxy: false # no-op here — the recommended baseline (global\n # # `proxy` absent/false) is already direct-by-default.\n # # Only needed if you flip the global default to true\n # # and this domestic provider needs to opt back out.\n\n - name: openrouter\n base_url: {openai: https://openrouter.ai/api/v1, anthropic: https://openrouter.ai/api/v1} # OpenRouter speaks both surfaces on the same base\n api_key: ${OPENROUTER_API_KEY}\n proxy: true # per-provider switch: true = always go through\n # https_proxy/http_proxy above, whatever the global\n # `proxy` default says (recommended way to opt a\n # foreign provider in); false = always direct; absent\n # = follow the global `proxy` default. No env\n # fallback; true with no matching global proxy URL\n # fails validation. YAML 1.2: write true/false, not\n # on/off.\n\n - name: deepseek\n base_url: {openai: https://api.deepseek.com/v1, anthropic: https://api.deepseek.com/anthropic/v1}\n api_key: ${DEEPSEEK_API_KEY}\n\n # A provider whose API doesn't accept all OpenAI roles can have it rewritten\n # per endpoint-group (models.<name>.endpoints[].role_map below) — a plain\n # old->new mapping applied to \"role\" values inside the top-level \"messages\"\n # array before the body is sent upstream. The canonical use case: some\n # providers (e.g. DashScope/Qianwen) reject the \"developer\" role OpenAI\n # introduced for o1/o3-series models — mapping it back to \"system\" makes\n # those clients work without any client-side change. role_map lives on the\n # endpoint-group rather than the provider: the same account can back several\n # virtual models with different upstream model families, and not all of\n # them necessarily need the same rewrite. Only the specific roles listed\n # are affected; every other byte of the request is preserved.\n # - name: dashscope\n # base_url: {openai: https://dashscope.aliyuncs.com/compatible-mode/v1}\n # api_key: ${DASHSCOPE_PLAN_KEY}\n\nmodels:\n coding:\n # strategy defaults to [priority]; endpoints below have no explicit\n # priority — ties (everyone defaults to 0) keep this file order, so\n # listing them in the order you want them tried is enough.\n #\n # capabilities/max_context_tokens (condition routing) and sticky_ttl\n # (Sticky Model) are all optional and default to \"unconstrained\" /\n # \"inherit the global default\" — omitting them, as every endpoint\n # below does, is identical to how this file behaved before these\n # fields existed. sticky itself (session affinity) defaults to true\n # per virtual model; see the \"cheap\" model below for the opt-out and\n # per-endpoint override forms. Full reference: docs/UserGuide.md\n # \"Condition-based routing\" / \"Sticky Model\".\n endpoints:\n - protocol: openai\n provider: openrouter\n models: [z-ai/glm-5.2]\n - protocol: openai\n provider: deepseek\n models: [deepseek-v4-pro]\n\n cheap:\n # image_downscale: 0 # per-model override example: force this model\n # to skip downscaling even if the global\n # setting above is on (omit to inherit it)\n # sticky: false # opt out of session affinity for a model that's\n # genuinely one-shot (no multi-turn value to protect)\n endpoints:\n - protocol: openai\n provider: minimax\n models: [MiniMax-M3]\n # capabilities: [image] # this model has no base capabilities above, so this\n # # endpoint's effective set is exactly [image] alone\n # max_context_tokens: 1000000 # declare once you want vmr to skip this endpoint\n # # for requests it can't actually serve\n - protocol: openai\n provider: openrouter\n # A models: list with more than one name expands into that many\n # independent, individually health-tracked endpoints sharing this\n # entry's provider/protocol/capabilities/priority — tried in list\n # order (each still subject to its own failover/health state).\n models: [tencent/hy3-preview, qwen/qwen3-max]\n - protocol: openai\n provider: deepseek\n models: [deepseek-v4-flash]\n # sticky_ttl: 2h # DeepSeek's disk-based prompt cache lasts hours to days —\n # override the global sticky_ttl for just this endpoint\n\n # \"agent\" mixes both ingress protocols under one virtual model name: the\n # openai-protocol entry is reachable only from POST /v1/chat/completions,\n # the anthropic-protocol entry only from POST /v1/messages — a client on\n # either protocol can ask for \"agent\" and get its own independent try-order\n # list, sharing nothing but the name.\n #\n # capabilities/max_context_tokens here are the *base* every endpoint below\n # inherits: both backing models support at least text+tools up to 256k\n # tokens, so that's declared once instead of on each entry. The minimax\n # endpoint's own capabilities: [...] list ADDS to the base (union — its\n # effective set is text,tools,image,audio,video,thinking, not just the\n # four listed here) and its own max_context_tokens OVERRIDES the base to\n # 512000 for that endpoint alone; the openrouter entry declares neither,\n # so it simply inherits the base as-is.\n agent:\n capabilities: [text, tools]\n max_context_tokens: 256000\n endpoints:\n - protocol: openai\n provider: openrouter\n models: [gemini-3.6-flash-high, gemini-3.1-pro-high]\n - protocol: anthropic\n provider: minimax\n models: [MiniMax-M3]\n max_context_tokens: 512000\n capabilities: [image, audio, video, thinking]\n\n # Anthropic-only virtual model: reachable only via POST /v1/messages.\n # \"claude\" needs no openai-protocol entry — nothing on the openai side\n # references it, and a request for it over POST /v1/chat/completions\n # simply finds no route, since that ingress only ever looks at\n # protocol: openai entries.\n claude:\n endpoints:\n - protocol: anthropic\n provider: minimax\n models: [MiniMax-M3]\n - protocol: anthropic\n provider: deepseek\n models: [deepseek-v4-pro]\n - protocol: anthropic\n provider: openrouter\n models: [minimax/minimax-m3] # any OpenRouter model, spoken over Anthropic protocol\n"}}}}