Skip to content

Commit bc18fb6

Browse files
chrarnolduscobra91
andcommitted
Improvements for OpenRouter base urls
Co-authored-by: cobra91 <[email protected]>
1 parent fba89d9 commit bc18fb6

File tree

12 files changed

+67
-50
lines changed

12 files changed

+67
-50
lines changed

src/api/providers/fetchers/modelCache.ts

Lines changed: 6 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,6 @@ import { getLiteLLMModels } from "./litellm"
1616
import { GetModelsOptions } from "../../../shared/api"
1717
const memoryCache = new NodeCache({ stdTTL: 5 * 60, checkperiod: 5 * 60 })
1818

19-
async function writeModels(router: RouterName, data: ModelRecord) {
20-
const filename = `${router}_models.json`
21-
const cacheDir = await getCacheDirectoryPath(ContextProxy.instance.globalStorageUri.fsPath)
22-
await fs.writeFile(path.join(cacheDir, filename), JSON.stringify(data))
23-
}
24-
25-
async function readModels(router: RouterName): Promise<ModelRecord | undefined> {
26-
const filename = `${router}_models.json`
27-
const cacheDir = await getCacheDirectoryPath(ContextProxy.instance.globalStorageUri.fsPath)
28-
const filePath = path.join(cacheDir, filename)
29-
const exists = await fileExistsAtPath(filePath)
30-
return exists ? JSON.parse(await fs.readFile(filePath, "utf8")) : undefined
31-
}
32-
3319
/**
3420
* Get models from the cache or fetch them from the provider and cache them.
3521
* There are two caches:
@@ -43,15 +29,18 @@ async function readModels(router: RouterName): Promise<ModelRecord | undefined>
4329
*/
4430
export const getModels = async (options: GetModelsOptions): Promise<ModelRecord> => {
4531
const { provider } = options
46-
let models = memoryCache.get<ModelRecord>(provider)
32+
33+
const cacheKey = JSON.stringify(options)
34+
let models = memoryCache.get<ModelRecord>(cacheKey)
35+
4736
if (models) {
4837
return models
4938
}
5039

5140
try {
5241
switch (provider) {
5342
case "openrouter":
54-
models = await getOpenRouterModels()
43+
models = await getOpenRouterModels(options.baseUrl, options.apiKey)
5544
break
5645
case "requesty":
5746
// Requesty models endpoint requires an API key for per-user custom policies
@@ -76,17 +65,8 @@ export const getModels = async (options: GetModelsOptions): Promise<ModelRecord>
7665
}
7766

7867
// Cache the fetched models (even if empty, to signify a successful fetch with no models)
79-
memoryCache.set(provider, models)
80-
await writeModels(provider, models).catch((err) =>
81-
console.error(`[getModels] Error writing ${provider} models to file cache:`, err),
82-
)
68+
memoryCache.set(cacheKey, models)
8369

84-
try {
85-
models = await readModels(provider)
86-
// console.log(`[getModels] read ${router} models from file cache`)
87-
} catch (error) {
88-
console.error(`[getModels] error reading ${provider} models from file cache`, error)
89-
}
9070
return models || {}
9171
} catch (error) {
9272
// Log the error and re-throw it so the caller can handle it (e.g., show a UI message).

src/api/providers/fetchers/openrouter.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,14 @@ type OpenRouterModelEndpointsResponse = z.infer<typeof openRouterModelEndpointsR
9393
* getOpenRouterModels
9494
*/
9595

96-
export async function getOpenRouterModels(options?: ApiHandlerOptions): Promise<Record<string, ModelInfo>> {
96+
export async function getOpenRouterModels(baseUrl?: string, apiKey?: string): Promise<Record<string, ModelInfo>> {
9797
const models: Record<string, ModelInfo> = {}
98-
const baseURL = options?.openRouterBaseUrl || "https://openrouter.ai/api/v1"
98+
const baseURL = baseUrl || "https://openrouter.ai/api/v1"
9999

100100
try {
101-
const response = await axios.get<OpenRouterModelsResponse>(`${baseURL}/models`)
101+
const response = await axios.get<OpenRouterModelsResponse>(`${baseURL}/models`, {
102+
headers: apiKey ? { Authorization: `Bearer ${apiKey}` } : undefined,
103+
})
102104
const result = openRouterModelsResponseSchema.safeParse(response.data)
103105
const data = result.success ? result.data.data : response.data.data
104106

src/core/webview/ClineProvider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -752,7 +752,7 @@ export class ClineProvider
752752
<meta charset="utf-8">
753753
<meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no">
754754
<meta name="theme-color" content="#000000">
755-
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; font-src ${webview.cspSource}; style-src ${webview.cspSource} 'unsafe-inline'; img-src ${webview.cspSource} https://storage.googleapis.com https://img.clerk.com data:; media-src ${webview.cspSource}; script-src ${webview.cspSource} 'wasm-unsafe-eval' 'nonce-${nonce}' https://us-assets.i.posthog.com 'strict-dynamic'; connect-src https://openrouter.ai https://api.requesty.ai https://us.i.posthog.com https://us-assets.i.posthog.com;">
755+
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; font-src ${webview.cspSource}; style-src ${webview.cspSource} 'unsafe-inline'; img-src ${webview.cspSource} https://storage.googleapis.com https://img.clerk.com data:; media-src ${webview.cspSource}; script-src ${webview.cspSource} 'wasm-unsafe-eval' 'nonce-${nonce}' https://us-assets.i.posthog.com 'strict-dynamic'; connect-src https://* https://openrouter.ai https://api.requesty.ai https://us.i.posthog.com https://us-assets.i.posthog.com;">
756756
<link rel="stylesheet" type="text/css" href="${stylesUri}">
757757
<link href="${codiconsUri}" rel="stylesheet" />
758758
<script nonce="${nonce}">

src/core/webview/__tests__/ClineProvider.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2136,7 +2136,7 @@ describe("ClineProvider - Router Models", () => {
21362136
await messageHandler({ type: "requestRouterModels" })
21372137

21382138
// Verify getModels was called for each provider with correct options
2139-
expect(getModels).toHaveBeenCalledWith({ provider: "openrouter" })
2139+
expect(getModels).toHaveBeenCalledWith({ provider: "openrouter", apiKey: "openrouter-key" })
21402140
expect(getModels).toHaveBeenCalledWith({ provider: "requesty", apiKey: "requesty-key" })
21412141
expect(getModels).toHaveBeenCalledWith({ provider: "glama" })
21422142
expect(getModels).toHaveBeenCalledWith({ provider: "unbound", apiKey: "unbound-key" })

src/core/webview/__tests__/webviewMessageHandler.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ describe("webviewMessageHandler - requestRouterModels", () => {
5454
})
5555

5656
// Verify getModels was called for each provider
57-
expect(mockGetModels).toHaveBeenCalledWith({ provider: "openrouter" })
57+
expect(mockGetModels).toHaveBeenCalledWith({ provider: "openrouter", apiKey: "openrouter-key" })
5858
expect(mockGetModels).toHaveBeenCalledWith({ provider: "requesty", apiKey: "requesty-key" })
5959
expect(mockGetModels).toHaveBeenCalledWith({ provider: "glama" })
6060
expect(mockGetModels).toHaveBeenCalledWith({ provider: "unbound", apiKey: "unbound-key" })

src/core/webview/webviewMessageHandler.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,8 +349,14 @@ export const webviewMessageHandler = async (
349349
}
350350
}
351351

352+
const openRouterApiKey = apiConfiguration.openRouterApiKey || message?.values?.openRouterApiKey
353+
const openRouterBaseUrl = apiConfiguration.openRouterBaseUrl || message?.values?.openRouterBaseUrl
354+
352355
const modelFetchPromises: Array<{ key: RouterName; options: GetModelsOptions }> = [
353-
{ key: "openrouter", options: { provider: "openrouter" } },
356+
{
357+
key: "openrouter",
358+
options: { provider: "openrouter", apiKey: openRouterApiKey, baseUrl: openRouterBaseUrl },
359+
},
354360
{ key: "requesty", options: { provider: "requesty", apiKey: apiConfiguration.requestyApiKey } },
355361
{ key: "glama", options: { provider: "glama" } },
356362
{ key: "unbound", options: { provider: "unbound", apiKey: apiConfiguration.unboundApiKey } },

src/shared/api.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ export const getModelMaxOutputTokens = ({
7777
// GetModelsOptions
7878

7979
export type GetModelsOptions =
80-
| { provider: "openrouter" }
80+
| { provider: "openrouter"; apiKey?: string; baseUrl?: string }
8181
| { provider: "glama" }
8282
| { provider: "requesty"; apiKey?: string }
8383
| { provider: "unbound"; apiKey?: string }

webview-ui/src/components/settings/ApiOptions.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,10 @@ const ApiOptions = ({
135135
info: selectedModelInfo,
136136
} = useSelectedModel(apiConfiguration)
137137

138-
const { data: routerModels, refetch: refetchRouterModels } = useRouterModels()
138+
const { data: routerModels, refetch: refetchRouterModels } = useRouterModels({
139+
openRouterBaseUrl: apiConfiguration?.openRouterBaseUrl,
140+
openRouterApiKey: apiConfiguration?.openRouterApiKey,
141+
})
139142

140143
// Update `apiModelId` whenever `selectedModelId` changes.
141144
useEffect(() => {

webview-ui/src/components/settings/providers/OpenRouter.tsx

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,18 @@ export const OpenRouter = ({
5858
[setApiConfigurationField],
5959
)
6060

61-
const { data: openRouterModelProviders } = useOpenRouterModelProviders(apiConfiguration?.openRouterModelId, {
62-
enabled:
63-
!!apiConfiguration?.openRouterModelId &&
64-
routerModels?.openrouter &&
65-
Object.keys(routerModels.openrouter).length > 1 &&
66-
apiConfiguration.openRouterModelId in routerModels.openrouter,
67-
})
61+
const { data: openRouterModelProviders } = useOpenRouterModelProviders(
62+
apiConfiguration?.openRouterModelId,
63+
apiConfiguration?.openRouterBaseUrl,
64+
apiConfiguration?.apiKey,
65+
{
66+
enabled:
67+
!!apiConfiguration?.openRouterModelId &&
68+
routerModels?.openrouter &&
69+
Object.keys(routerModels.openrouter).length > 1 &&
70+
apiConfiguration.openRouterModelId in routerModels.openrouter,
71+
},
72+
)
6873

6974
return (
7075
<>

webview-ui/src/components/ui/hooks/useOpenRouterModelProviders.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,14 @@ type OpenRouterModelProvider = ModelInfo & {
3939
label: string
4040
}
4141

42-
async function getOpenRouterProvidersForModel(modelId: string) {
42+
async function getOpenRouterProvidersForModel(modelId: string, baseUrl?: string, apiKey?: string) {
4343
const models: Record<string, OpenRouterModelProvider> = {}
4444

4545
try {
46-
const response = await axios.get(`https://openrouter.ai/api/v1/models/${modelId}/endpoints`)
46+
const response = await axios.get(
47+
`${baseUrl?.trim() || "https://openrouter.ai/api/v1"}/models/${modelId}/endpoints`,
48+
apiKey ? { headers: { Authorization: `Bearer ${apiKey}` } } : undefined,
49+
)
4750
const result = openRouterEndpointsSchema.safeParse(response.data)
4851

4952
if (!result.success) {
@@ -112,9 +115,14 @@ type UseOpenRouterModelProvidersOptions = Omit<
112115
"queryKey" | "queryFn"
113116
>
114117

115-
export const useOpenRouterModelProviders = (modelId?: string, options?: UseOpenRouterModelProvidersOptions) =>
118+
export const useOpenRouterModelProviders = (
119+
modelId?: string,
120+
baseUrl?: string,
121+
apiKey?: string,
122+
options?: UseOpenRouterModelProvidersOptions,
123+
) =>
116124
useQuery<Record<string, OpenRouterModelProvider>>({
117-
queryKey: ["openrouter-model-providers", modelId],
118-
queryFn: () => (modelId ? getOpenRouterProvidersForModel(modelId) : {}),
125+
queryKey: ["openrouter-model-providers", modelId, baseUrl, apiKey],
126+
queryFn: () => (modelId ? getOpenRouterProvidersForModel(modelId, baseUrl, apiKey) : {}),
119127
...options,
120128
})

0 commit comments

Comments
 (0)