fix: respect custom OpenRouter URL for all API operations (#8951)

Co-authored-by: Roo Code <roomote@roocode.com>
This commit is contained in:
roomote[bot] 2025-11-07 10:35:12 -05:00 committed by GitHub
parent 47415fa19a
commit 2abdad6d55
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 39 additions and 17 deletions

View file

@ -304,7 +304,8 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH
}
try {
const response = await fetch("https://openrouter.ai/api/v1/chat/completions", {
const baseURL = this.options.openRouterBaseUrl || "https://openrouter.ai/api/v1"
const response = await fetch(`${baseURL}/chat/completions`, {
method: "POST",
headers: {
Authorization: `Bearer ${apiKey}`,

View file

@ -786,7 +786,7 @@ export class ClineProvider
webviewView.webview.html =
this.contextProxy.extensionMode === vscode.ExtensionMode.Development
? await this.getHMRHtmlContent(webviewView.webview)
: this.getHtmlContent(webviewView.webview)
: await this.getHtmlContent(webviewView.webview)
// Sets up an event listener to listen for messages passed from the webview view context
// and executes code based on the message that is received.
@ -1061,6 +1061,12 @@ export class ClineProvider
const nonce = getNonce()
// Get the OpenRouter base URL from configuration
const { apiConfiguration } = await this.getState()
const openRouterBaseUrl = apiConfiguration.openRouterBaseUrl || "https://openrouter.ai"
// Extract the domain for CSP
const openRouterDomain = openRouterBaseUrl.match(/^(https?:\/\/[^\/]+)/)?.[1] || "https://openrouter.ai"
const stylesUri = getUri(webview, this.contextProxy.extensionUri, [
"webview-ui",
"build",
@ -1097,7 +1103,7 @@ export class ClineProvider
`img-src ${webview.cspSource} https://storage.googleapis.com https://img.clerk.com data:`,
`media-src ${webview.cspSource}`,
`script-src 'unsafe-eval' ${webview.cspSource} https://* https://*.posthog.com http://${localServerUrl} http://0.0.0.0:${localPort} 'nonce-${nonce}'`,
`connect-src ${webview.cspSource} https://* https://*.posthog.com ws://${localServerUrl} ws://0.0.0.0:${localPort} http://${localServerUrl} http://0.0.0.0:${localPort}`,
`connect-src ${webview.cspSource} ${openRouterDomain} https://* https://*.posthog.com ws://${localServerUrl} ws://0.0.0.0:${localPort} http://${localServerUrl} http://0.0.0.0:${localPort}`,
]
return /*html*/ `
@ -1136,7 +1142,7 @@ export class ClineProvider
* @returns A template string literal containing the HTML that should be
* rendered within the webview panel
*/
private getHtmlContent(webview: vscode.Webview): string {
private async getHtmlContent(webview: vscode.Webview): Promise<string> {
// Get the local path to main script run in the webview,
// then convert it to a uri we can use in the webview.
@ -1171,6 +1177,12 @@ export class ClineProvider
*/
const nonce = getNonce()
// Get the OpenRouter base URL from configuration
const { apiConfiguration } = await this.getState()
const openRouterBaseUrl = apiConfiguration.openRouterBaseUrl || "https://openrouter.ai"
// Extract the domain for CSP
const openRouterDomain = openRouterBaseUrl.match(/^(https?:\/\/[^\/]+)/)?.[1] || "https://openrouter.ai"
// Tip: Install the es6-string-html VS Code extension to enable code highlighting below
return /*html*/ `
<!DOCTYPE html>
@ -1179,7 +1191,7 @@ export class ClineProvider
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; font-src ${webview.cspSource} data:; 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 ${webview.cspSource} https://openrouter.ai https://api.requesty.ai https://us.i.posthog.com https://us-assets.i.posthog.com;">
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; font-src ${webview.cspSource} data:; 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 ${webview.cspSource} ${openRouterDomain} https://api.requesty.ai https://us.i.posthog.com https://us-assets.i.posthog.com;">
<link rel="stylesheet" type="text/css" href="${stylesUri}">
<link href="${codiconsUri}" rel="stylesheet" />
<script nonce="${nonce}">

View file

@ -192,13 +192,17 @@ const ApiOptions = ({
const { data: routerModels, refetch: refetchRouterModels } = useRouterModels()
const { data: openRouterModelProviders } = useOpenRouterModelProviders(apiConfiguration?.openRouterModelId, {
enabled:
!!apiConfiguration?.openRouterModelId &&
routerModels?.openrouter &&
Object.keys(routerModels.openrouter).length > 1 &&
apiConfiguration.openRouterModelId in routerModels.openrouter,
})
const { data: openRouterModelProviders } = useOpenRouterModelProviders(
apiConfiguration?.openRouterModelId,
apiConfiguration?.openRouterBaseUrl,
{
enabled:
!!apiConfiguration?.openRouterModelId &&
routerModels?.openrouter &&
Object.keys(routerModels.openrouter).length > 1 &&
apiConfiguration.openRouterModelId in routerModels.openrouter,
},
)
// Update `apiModelId` whenever `selectedModelId` changes.
useEffect(() => {

View file

@ -43,11 +43,12 @@ type OpenRouterModelProvider = ModelInfo & {
label: string
}
async function getOpenRouterProvidersForModel(modelId: string) {
async function getOpenRouterProvidersForModel(modelId: string, baseUrl?: string) {
const models: Record<string, OpenRouterModelProvider> = {}
try {
const response = await axios.get(`https://openrouter.ai/api/v1/models/${modelId}/endpoints`)
const apiBaseUrl = baseUrl || "https://openrouter.ai/api/v1"
const response = await axios.get(`${apiBaseUrl}/models/${modelId}/endpoints`)
const result = openRouterEndpointsSchema.safeParse(response.data)
if (!result.success) {
@ -100,9 +101,13 @@ type UseOpenRouterModelProvidersOptions = Omit<
"queryKey" | "queryFn"
>
export const useOpenRouterModelProviders = (modelId?: string, options?: UseOpenRouterModelProvidersOptions) =>
export const useOpenRouterModelProviders = (
modelId?: string,
baseUrl?: string,
options?: UseOpenRouterModelProvidersOptions,
) =>
useQuery<Record<string, OpenRouterModelProvider>>({
queryKey: ["openrouter-model-providers", modelId],
queryFn: () => (modelId ? getOpenRouterProvidersForModel(modelId) : {}),
queryKey: ["openrouter-model-providers", modelId, baseUrl],
queryFn: () => (modelId ? getOpenRouterProvidersForModel(modelId, baseUrl) : {}),
...options,
})