Skip to content

Commit 4347068

Browse files
committed
refactor: finish canonical provider identifier audit
1 parent c52f118 commit 4347068

4 files changed

Lines changed: 112 additions & 10 deletions

File tree

packages/types/src/__tests__/provider-settings.test.ts

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { getApiProtocol } from "../provider-settings.js"
1+
import { getApiProtocol, providerIdentifiers, type ProviderName } from "../index.js"
22

33
describe("getApiProtocol", () => {
44
describe("Anthropic-style providers", () => {
@@ -15,6 +15,19 @@ describe("getApiProtocol", () => {
1515
})
1616

1717
describe("Vertex provider with Claude models", () => {
18+
it("uses the canonical Vertex identifier for Claude protocol selection", () => {
19+
const identifiers = providerIdentifiers as Record<string, string>
20+
const originalIdentifier = identifiers.vertex!
21+
22+
try {
23+
identifiers.vertex = "canonical-vertex"
24+
25+
expect(getApiProtocol(identifiers.vertex as ProviderName, "claude-3-opus")).toBe("anthropic")
26+
} finally {
27+
identifiers.vertex = originalIdentifier
28+
}
29+
})
30+
1831
it("should return 'anthropic' for vertex provider with claude models", () => {
1932
expect(getApiProtocol("vertex", "claude-3-opus")).toBe("anthropic")
2033
expect(getApiProtocol("vertex", "Claude-3-Sonnet")).toBe("anthropic")
@@ -34,6 +47,27 @@ describe("getApiProtocol", () => {
3447
})
3548

3649
describe("Vercel AI Gateway provider", () => {
50+
it("uses canonical gateway identifiers for Anthropic model protocol selection", () => {
51+
const identifiers = providerIdentifiers as Record<string, string>
52+
const originalVercelIdentifier = identifiers.vercelAiGateway!
53+
const originalZooIdentifier = identifiers.zooGateway!
54+
55+
try {
56+
identifiers.vercelAiGateway = "canonical-vercel-ai-gateway"
57+
identifiers.zooGateway = "canonical-zoo-gateway"
58+
59+
expect(getApiProtocol(identifiers.vercelAiGateway as ProviderName, "anthropic/claude-3-opus")).toBe(
60+
"anthropic",
61+
)
62+
expect(getApiProtocol(identifiers.zooGateway as ProviderName, "anthropic/claude-3-opus")).toBe(
63+
"anthropic",
64+
)
65+
} finally {
66+
identifiers.vercelAiGateway = originalVercelIdentifier
67+
identifiers.zooGateway = originalZooIdentifier
68+
}
69+
})
70+
3771
it("should return 'anthropic' for vercel-ai-gateway provider with anthropic models", () => {
3872
expect(getApiProtocol("vercel-ai-gateway", "anthropic/claude-3-opus")).toBe("anthropic")
3973
expect(getApiProtocol("vercel-ai-gateway", "anthropic/claude-3.5-sonnet")).toBe("anthropic")
@@ -54,6 +88,19 @@ describe("getApiProtocol", () => {
5488
})
5589

5690
describe("Opencode Go provider", () => {
91+
it("uses the canonical OpenCode Go identifier for model-specific protocol selection", () => {
92+
const identifiers = providerIdentifiers as Record<string, string>
93+
const originalIdentifier = identifiers.opencodeGo!
94+
95+
try {
96+
identifiers.opencodeGo = "canonical-opencode-go"
97+
98+
expect(getApiProtocol(identifiers.opencodeGo as ProviderName, "qwen3.7-max")).toBe("anthropic")
99+
} finally {
100+
identifiers.opencodeGo = originalIdentifier
101+
}
102+
})
103+
57104
it("should return 'anthropic' for opencode-go Anthropic-format models (Qwen/MiniMax)", () => {
58105
expect(getApiProtocol("opencode-go", "qwen3.7-max")).toBe("anthropic")
59106
expect(getApiProtocol("opencode-go", "qwen3.7-plus")).toBe("anthropic")

packages/types/src/provider-settings.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -581,21 +581,25 @@ export const modelIdKeysByProvider: Record<TypicalProvider, ModelIdKey> = {
581581
*/
582582

583583
// Providers that use Anthropic-style API protocol.
584-
export const ANTHROPIC_STYLE_PROVIDERS: ProviderName[] = ["anthropic", "bedrock", "minimax"]
584+
export const ANTHROPIC_STYLE_PROVIDERS: ProviderName[] = [
585+
providerIdentifiers.anthropic,
586+
providerIdentifiers.bedrock,
587+
providerIdentifiers.minimax,
588+
]
585589

586590
export const getApiProtocol = (provider: ProviderName | undefined, modelId?: string): "anthropic" | "openai" => {
587591
if (provider && ANTHROPIC_STYLE_PROVIDERS.includes(provider)) {
588592
return "anthropic"
589593
}
590594

591-
if (provider && provider === "vertex" && modelId && modelId.toLowerCase().includes("claude")) {
595+
if (provider && provider === providerIdentifiers.vertex && modelId && modelId.toLowerCase().includes("claude")) {
592596
return "anthropic"
593597
}
594598

595599
// Vercel AI Gateway and Zoo Gateway use the anthropic protocol for anthropic models.
596600
if (
597601
provider &&
598-
["vercel-ai-gateway", "zoo-gateway"].includes(provider) &&
602+
(provider === providerIdentifiers.vercelAiGateway || provider === providerIdentifiers.zooGateway) &&
599603
modelId &&
600604
modelId.toLowerCase().startsWith("anthropic/")
601605
) {
@@ -609,7 +613,12 @@ export const getApiProtocol = (provider: ProviderName | undefined, modelId?: str
609613
// models must use the anthropic protocol so token/cost aggregation adds the
610614
// cache tokens back into the input total — otherwise the cached prefix is
611615
// dropped from `contextTokens`, undercounting context-window usage.
612-
if (provider && provider === "opencode-go" && modelId && isOpencodeGoAnthropicFormatModel(modelId)) {
616+
if (
617+
provider &&
618+
provider === providerIdentifiers.opencodeGo &&
619+
modelId &&
620+
isOpencodeGoAnthropicFormatModel(modelId)
621+
) {
613622
return "anthropic"
614623
}
615624

src/shared/__tests__/checkExistApiConfig.spec.ts

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// npx vitest run src/shared/__tests__/checkExistApiConfig.spec.ts
22

3-
import type { ProviderSettings } from "@roo-code/types"
3+
import { providerIdentifiers, type ProviderSettings } from "@roo-code/types"
44

55
import { checkExistKey } from "../checkExistApiConfig"
66

@@ -66,6 +66,19 @@ describe("checkExistKey", () => {
6666
expect(checkExistKey(config)).toBe(true)
6767
})
6868

69+
it("recognizes keyless providers through their canonical identifiers", () => {
70+
const identifiers = providerIdentifiers as Record<string, string>
71+
const originalIdentifier = identifiers.fakeAi!
72+
73+
try {
74+
identifiers.fakeAi = "canonical-fake-ai"
75+
76+
expect(checkExistKey({ apiProvider: identifiers.fakeAi } as ProviderSettings)).toBe(true)
77+
} finally {
78+
identifiers.fakeAi = originalIdentifier
79+
}
80+
})
81+
6982
it("should return true for openai-codex provider without API key", () => {
7083
const config: ProviderSettings = {
7184
apiProvider: "openai-codex",
@@ -95,6 +108,21 @@ describe("checkExistKey", () => {
95108
expect(checkExistKey(config)).toBe(true)
96109
})
97110

111+
it("recognizes OAuth authentication through the canonical Kimi Code identifier", () => {
112+
const identifiers = providerIdentifiers as Record<string, string>
113+
const originalIdentifier = identifiers.kimiCode!
114+
115+
try {
116+
identifiers.kimiCode = "canonical-kimi-code"
117+
118+
expect(
119+
checkExistKey({ apiProvider: identifiers.kimiCode, kimiCodeAuthMethod: "oauth" } as ProviderSettings),
120+
).toBe(true)
121+
} finally {
122+
identifiers.kimiCode = originalIdentifier
123+
}
124+
})
125+
98126
it("should return true for kimi-code provider without auth method (defaults to OAuth)", () => {
99127
const config: ProviderSettings = {
100128
apiProvider: "kimi-code",
@@ -128,6 +156,19 @@ describe("checkExistKey", () => {
128156
expect(checkExistKey(config, false)).toBe(false)
129157
})
130158

159+
it("recognizes session authentication through the canonical Zoo Gateway identifier", () => {
160+
const identifiers = providerIdentifiers as Record<string, string>
161+
const originalIdentifier = identifiers.zooGateway!
162+
163+
try {
164+
identifiers.zooGateway = "canonical-zoo-gateway"
165+
166+
expect(checkExistKey({ apiProvider: identifiers.zooGateway } as ProviderSettings, true)).toBe(true)
167+
} finally {
168+
identifiers.zooGateway = originalIdentifier
169+
}
170+
})
171+
131172
it("should return true for zoo-gateway when profile has zooSessionToken", () => {
132173
const config: ProviderSettings = {
133174
apiProvider: "zoo-gateway",

src/shared/checkExistApiConfig.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { SECRET_STATE_KEYS, GLOBAL_SECRET_KEYS, ProviderSettings } from "@roo-code/types"
1+
import { SECRET_STATE_KEYS, GLOBAL_SECRET_KEYS, providerIdentifiers, ProviderSettings } from "@roo-code/types"
22

33
/**
44
* Returns whether a provider profile is sufficiently configured to leave the
@@ -14,17 +14,22 @@ export function checkExistKey(config: ProviderSettings | undefined, zooCodeIsAut
1414
}
1515

1616
// Special case for fake-ai, openai-codex, and qwen-code providers which don't need any configuration.
17-
if (config.apiProvider && ["fake-ai", "openai-codex", "qwen-code"].includes(config.apiProvider)) {
17+
const configurationFreeProviders: ProviderSettings["apiProvider"][] = [
18+
providerIdentifiers.fakeAi,
19+
providerIdentifiers.openaiCodex,
20+
providerIdentifiers.qwenCode,
21+
]
22+
if (config.apiProvider && configurationFreeProviders.includes(config.apiProvider)) {
1823
return true
1924
}
2025

21-
if (config.apiProvider === "kimi-code" && (config.kimiCodeAuthMethod ?? "oauth") === "oauth") {
26+
if (config.apiProvider === providerIdentifiers.kimiCode && (config.kimiCodeAuthMethod ?? "oauth") === "oauth") {
2227
return true
2328
}
2429

2530
// Zoo Gateway uses session auth (profile token and/or global Zoo Code login),
2631
// not a traditional API key listed in SECRET_STATE_KEYS.
27-
if (config.apiProvider === "zoo-gateway") {
32+
if (config.apiProvider === providerIdentifiers.zooGateway) {
2833
return Boolean(config.zooSessionToken) || Boolean(zooCodeIsAuthenticated)
2934
}
3035

0 commit comments

Comments
 (0)