diff --git a/.vscodeignore b/.vscodeignore index 2a46fdd142..2ef0f606c5 100644 --- a/.vscodeignore +++ b/.vscodeignore @@ -26,9 +26,11 @@ demo.gif .prettierignore .clinerules* .roomodes +.roo/** cline_docs/** coverage/** locales/** +benchmark/** # Ignore all webview-ui files except the build directory (https://github.com/microsoft/vscode-webview-ui-toolkit-samples/blob/main/frameworks/hello-world-react-cra/.vscodeignore) webview-ui/src/** diff --git a/src/api/providers/openai.ts b/src/api/providers/openai.ts index 53851ad4d2..4f5477d97d 100644 --- a/src/api/providers/openai.ts +++ b/src/api/providers/openai.ts @@ -55,10 +55,20 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl baseURL, apiKey, apiVersion: this.options.azureApiVersion || azureOpenAiDefaultApiVersion, - defaultHeaders, + defaultHeaders: { + ...defaultHeaders, + ...(this.options.openAiHostHeader ? { Host: this.options.openAiHostHeader } : {}), + }, }) } else { - this.client = new OpenAI({ baseURL, apiKey, defaultHeaders }) + this.client = new OpenAI({ + baseURL, + apiKey, + defaultHeaders: { + ...defaultHeaders, + ...(this.options.openAiHostHeader ? { Host: this.options.openAiHostHeader } : {}), + }, + }) } } @@ -67,6 +77,7 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl const modelUrl = this.options.openAiBaseUrl ?? "" const modelId = this.options.openAiModelId ?? "" const enabledR1Format = this.options.openAiR1FormatEnabled ?? false + const enabledLegacyFormat = this.options.openAiLegacyFormat ?? false const isAzureAiInference = this._isAzureAiInference(modelUrl) const urlHost = this._getUrlHost(modelUrl) const deepseekReasoner = modelId.includes("deepseek-reasoner") || enabledR1Format @@ -85,7 +96,7 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl let convertedMessages if (deepseekReasoner) { convertedMessages = convertToR1Format([{ role: "user", content: systemPrompt }, ...messages]) - } else if (ark) { + } else if (ark || enabledLegacyFormat) { convertedMessages = [systemMessage, ...convertToSimpleMessages(messages)] } else { if (modelInfo.supportsPromptCache) { @@ -190,7 +201,9 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl model: modelId, messages: deepseekReasoner ? convertToR1Format([{ role: "user", content: systemPrompt }, ...messages]) - : [systemMessage, ...convertToOpenAiMessages(messages)], + : enabledLegacyFormat + ? [systemMessage, ...convertToSimpleMessages(messages)] + : [systemMessage, ...convertToOpenAiMessages(messages)], } const response = await this.client.chat.completions.create( @@ -330,7 +343,7 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl } } -export async function getOpenAiModels(baseUrl?: string, apiKey?: string) { +export async function getOpenAiModels(baseUrl?: string, apiKey?: string, hostHeader?: string) { try { if (!baseUrl) { return [] @@ -341,9 +354,18 @@ export async function getOpenAiModels(baseUrl?: string, apiKey?: string) { } const config: Record = {} + const headers: Record = {} if (apiKey) { - config["headers"] = { Authorization: `Bearer ${apiKey}` } + headers["Authorization"] = `Bearer ${apiKey}` + } + + if (hostHeader) { + headers["Host"] = hostHeader + } + + if (Object.keys(headers).length > 0) { + config["headers"] = headers } const response = await axios.get(`${baseUrl}/models`, config) diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index c8c61a4e55..281db62b3f 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -423,7 +423,11 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We break case "refreshOpenAiModels": if (message?.values?.baseUrl && message?.values?.apiKey) { - const openAiModels = await getOpenAiModels(message?.values?.baseUrl, message?.values?.apiKey) + const openAiModels = await getOpenAiModels( + message?.values?.baseUrl, + message?.values?.apiKey, + message?.values?.hostHeader, + ) provider.postMessageToWebview({ type: "openAiModels", openAiModels }) } diff --git a/src/exports/roo-code.d.ts b/src/exports/roo-code.d.ts index 6af38733dd..490324752b 100644 --- a/src/exports/roo-code.d.ts +++ b/src/exports/roo-code.d.ts @@ -86,6 +86,8 @@ type ProviderSettings = { vertexRegion?: string | undefined openAiBaseUrl?: string | undefined openAiApiKey?: string | undefined + openAiHostHeader?: string | undefined + openAiLegacyFormat?: boolean | undefined openAiR1FormatEnabled?: boolean | undefined openAiModelId?: string | undefined openAiCustomModelInfo?: diff --git a/src/exports/types.ts b/src/exports/types.ts index d9824ef1db..b4391d986d 100644 --- a/src/exports/types.ts +++ b/src/exports/types.ts @@ -87,6 +87,8 @@ type ProviderSettings = { vertexRegion?: string | undefined openAiBaseUrl?: string | undefined openAiApiKey?: string | undefined + openAiHostHeader?: string | undefined + openAiLegacyFormat?: boolean | undefined openAiR1FormatEnabled?: boolean | undefined openAiModelId?: string | undefined openAiCustomModelInfo?: diff --git a/src/schemas/index.ts b/src/schemas/index.ts index f5cd620e2a..ba01402684 100644 --- a/src/schemas/index.ts +++ b/src/schemas/index.ts @@ -338,6 +338,8 @@ export const providerSettingsSchema = z.object({ // OpenAI openAiBaseUrl: z.string().optional(), openAiApiKey: z.string().optional(), + openAiHostHeader: z.string().optional(), + openAiLegacyFormat: z.boolean().optional(), openAiR1FormatEnabled: z.boolean().optional(), openAiModelId: z.string().optional(), openAiCustomModelInfo: modelInfoSchema.nullish(), @@ -431,6 +433,8 @@ const providerSettingsRecord: ProviderSettingsRecord = { // OpenAI openAiBaseUrl: undefined, openAiApiKey: undefined, + openAiHostHeader: undefined, + openAiLegacyFormat: undefined, openAiR1FormatEnabled: undefined, openAiModelId: undefined, openAiCustomModelInfo: undefined, diff --git a/webview-ui/src/components/settings/ApiOptions.tsx b/webview-ui/src/components/settings/ApiOptions.tsx index 0e7050390f..fb633df155 100644 --- a/webview-ui/src/components/settings/ApiOptions.tsx +++ b/webview-ui/src/components/settings/ApiOptions.tsx @@ -103,6 +103,8 @@ const ApiOptions = ({ const [anthropicBaseUrlSelected, setAnthropicBaseUrlSelected] = useState(!!apiConfiguration?.anthropicBaseUrl) const [azureApiVersionSelected, setAzureApiVersionSelected] = useState(!!apiConfiguration?.azureApiVersion) const [openRouterBaseUrlSelected, setOpenRouterBaseUrlSelected] = useState(!!apiConfiguration?.openRouterBaseUrl) + const [openAiHostHeaderSelected, setOpenAiHostHeaderSelected] = useState(!!apiConfiguration?.openAiHostHeader) + const [openAiLegacyFormatSelected, setOpenAiLegacyFormatSelected] = useState(!!apiConfiguration?.openAiLegacyFormat) const [googleGeminiBaseUrlSelected, setGoogleGeminiBaseUrlSelected] = useState( !!apiConfiguration?.googleGeminiBaseUrl, ) @@ -145,7 +147,11 @@ const ApiOptions = ({ } else if (selectedProvider === "openai") { vscode.postMessage({ type: "refreshOpenAiModels", - values: { baseUrl: apiConfiguration?.openAiBaseUrl, apiKey: apiConfiguration?.openAiApiKey }, + values: { + baseUrl: apiConfiguration?.openAiBaseUrl, + apiKey: apiConfiguration?.openAiApiKey, + hostHeader: apiConfiguration?.openAiHostHeader, + }, }) } else if (selectedProvider === "ollama") { vscode.postMessage({ type: "requestOllamaModels", text: apiConfiguration?.ollamaBaseUrl }) @@ -779,6 +785,16 @@ const ApiOptions = ({ onChange={handleInputChange("openAiR1FormatEnabled", noTransform)} openAiR1FormatEnabled={apiConfiguration?.openAiR1FormatEnabled ?? false} /> +
+ { + setOpenAiLegacyFormatSelected(checked) + setApiConfigurationField("openAiLegacyFormat", checked) + }}> + {t("settings:providers.useLegacyFormat")} + +
@@ -811,6 +827,28 @@ const ApiOptions = ({ )} +
+ { + setOpenAiHostHeaderSelected(checked) + + if (!checked) { + setApiConfigurationField("openAiHostHeader", "") + } + }}> + {t("settings:providers.useHostHeader")} + + {openAiHostHeaderSelected && ( + + )} +
+
{t("settings:providers.customModel.capabilities")} diff --git a/webview-ui/src/i18n/locales/ca/settings.json b/webview-ui/src/i18n/locales/ca/settings.json index 715450014d..063feb4dd0 100644 --- a/webview-ui/src/i18n/locales/ca/settings.json +++ b/webview-ui/src/i18n/locales/ca/settings.json @@ -105,6 +105,8 @@ "awsCustomArnDesc": "Assegureu-vos que la regió a l'ARN coincideix amb la regió d'AWS seleccionada anteriorment.", "apiKeyStorageNotice": "Les claus API s'emmagatzemen de forma segura a l'Emmagatzematge Secret de VSCode", "useCustomBaseUrl": "Utilitzar URL base personalitzada", + "useHostHeader": "Utilitzar capçalera Host personalitzada", + "useLegacyFormat": "Utilitzar el format d'API OpenAI antic", "openRouterTransformsText": "Comprimir prompts i cadenes de missatges a la mida del context (Transformacions d'OpenRouter)", "model": "Model", "getOpenRouterApiKey": "Obtenir clau API d'OpenRouter", diff --git a/webview-ui/src/i18n/locales/de/settings.json b/webview-ui/src/i18n/locales/de/settings.json index c5f7eb083b..2da6ddfcb0 100644 --- a/webview-ui/src/i18n/locales/de/settings.json +++ b/webview-ui/src/i18n/locales/de/settings.json @@ -109,6 +109,8 @@ "glamaApiKey": "Glama API-Schlüssel", "getGlamaApiKey": "Glama API-Schlüssel erhalten", "useCustomBaseUrl": "Benutzerdefinierte Basis-URL verwenden", + "useHostHeader": "Benutzerdefinierten Host-Header verwenden", + "useLegacyFormat": "Altes OpenAI API-Format verwenden", "requestyApiKey": "Requesty API-Schlüssel", "getRequestyApiKey": "Requesty API-Schlüssel erhalten", "openRouterTransformsText": "Prompts und Nachrichtenketten auf Kontextgröße komprimieren (OpenRouter Transformationen)", diff --git a/webview-ui/src/i18n/locales/en/settings.json b/webview-ui/src/i18n/locales/en/settings.json index 757fc29928..044ce1ff81 100644 --- a/webview-ui/src/i18n/locales/en/settings.json +++ b/webview-ui/src/i18n/locales/en/settings.json @@ -109,6 +109,8 @@ "glamaApiKey": "Glama API Key", "getGlamaApiKey": "Get Glama API Key", "useCustomBaseUrl": "Use custom base URL", + "useHostHeader": "Use custom Host header", + "useLegacyFormat": "Use legacy OpenAI API format", "requestyApiKey": "Requesty API Key", "getRequestyApiKey": "Get Requesty API Key", "openRouterTransformsText": "Compress prompts and message chains to the context size (OpenRouter Transforms)", diff --git a/webview-ui/src/i18n/locales/es/settings.json b/webview-ui/src/i18n/locales/es/settings.json index 700ce2f82e..29b57eb44f 100644 --- a/webview-ui/src/i18n/locales/es/settings.json +++ b/webview-ui/src/i18n/locales/es/settings.json @@ -109,6 +109,8 @@ "glamaApiKey": "Clave API de Glama", "getGlamaApiKey": "Obtener clave API de Glama", "useCustomBaseUrl": "Usar URL base personalizada", + "useHostHeader": "Usar encabezado Host personalizado", + "useLegacyFormat": "Usar formato API de OpenAI heredado", "requestyApiKey": "Clave API de Requesty", "getRequestyApiKey": "Obtener clave API de Requesty", "openRouterTransformsText": "Comprimir prompts y cadenas de mensajes al tamaño del contexto (Transformaciones de OpenRouter)", diff --git a/webview-ui/src/i18n/locales/fr/settings.json b/webview-ui/src/i18n/locales/fr/settings.json index 039a0168f9..e3fe009057 100644 --- a/webview-ui/src/i18n/locales/fr/settings.json +++ b/webview-ui/src/i18n/locales/fr/settings.json @@ -109,6 +109,8 @@ "glamaApiKey": "Clé API Glama", "getGlamaApiKey": "Obtenir la clé API Glama", "useCustomBaseUrl": "Utiliser une URL de base personnalisée", + "useHostHeader": "Utiliser un en-tête Host personnalisé", + "useLegacyFormat": "Utiliser le format API OpenAI hérité", "requestyApiKey": "Clé API Requesty", "getRequestyApiKey": "Obtenir la clé API Requesty", "openRouterTransformsText": "Compresser les prompts et chaînes de messages à la taille du contexte (Transformations OpenRouter)", diff --git a/webview-ui/src/i18n/locales/hi/settings.json b/webview-ui/src/i18n/locales/hi/settings.json index 0bbc862a6b..c427eb5284 100644 --- a/webview-ui/src/i18n/locales/hi/settings.json +++ b/webview-ui/src/i18n/locales/hi/settings.json @@ -109,6 +109,8 @@ "glamaApiKey": "Glama API कुंजी", "getGlamaApiKey": "Glama API कुंजी प्राप्त करें", "useCustomBaseUrl": "कस्टम बेस URL का उपयोग करें", + "useHostHeader": "कस्टम होस्ट हेडर का उपयोग करें", + "useLegacyFormat": "पुराने OpenAI API प्रारूप का उपयोग करें", "requestyApiKey": "Requesty API कुंजी", "getRequestyApiKey": "Requesty API कुंजी प्राप्त करें", "openRouterTransformsText": "संदर्भ आकार के लिए प्रॉम्प्ट और संदेश श्रृंखलाओं को संपीड़ित करें (OpenRouter ट्रांसफॉर्म)", diff --git a/webview-ui/src/i18n/locales/it/settings.json b/webview-ui/src/i18n/locales/it/settings.json index d8ce9e4d88..c38a61d6b4 100644 --- a/webview-ui/src/i18n/locales/it/settings.json +++ b/webview-ui/src/i18n/locales/it/settings.json @@ -109,6 +109,8 @@ "glamaApiKey": "Chiave API Glama", "getGlamaApiKey": "Ottieni chiave API Glama", "useCustomBaseUrl": "Usa URL base personalizzato", + "useHostHeader": "Usa intestazione Host personalizzata", + "useLegacyFormat": "Usa formato API OpenAI legacy", "requestyApiKey": "Chiave API Requesty", "getRequestyApiKey": "Ottieni chiave API Requesty", "openRouterTransformsText": "Comprimi prompt e catene di messaggi alla dimensione del contesto (Trasformazioni OpenRouter)", diff --git a/webview-ui/src/i18n/locales/ja/settings.json b/webview-ui/src/i18n/locales/ja/settings.json index bbe187bae2..4157e9095a 100644 --- a/webview-ui/src/i18n/locales/ja/settings.json +++ b/webview-ui/src/i18n/locales/ja/settings.json @@ -109,6 +109,8 @@ "glamaApiKey": "Glama APIキー", "getGlamaApiKey": "Glama APIキーを取得", "useCustomBaseUrl": "カスタムベースURLを使用", + "useHostHeader": "カスタムHostヘッダーを使用", + "useLegacyFormat": "レガシーOpenAI API形式を使用", "requestyApiKey": "Requesty APIキー", "getRequestyApiKey": "Requesty APIキーを取得", "openRouterTransformsText": "プロンプトとメッセージチェーンをコンテキストサイズに圧縮 (OpenRouter Transforms)", diff --git a/webview-ui/src/i18n/locales/ko/settings.json b/webview-ui/src/i18n/locales/ko/settings.json index 3b1fc76e13..c6b4345967 100644 --- a/webview-ui/src/i18n/locales/ko/settings.json +++ b/webview-ui/src/i18n/locales/ko/settings.json @@ -109,6 +109,8 @@ "glamaApiKey": "Glama API 키", "getGlamaApiKey": "Glama API 키 받기", "useCustomBaseUrl": "사용자 정의 기본 URL 사용", + "useHostHeader": "사용자 정의 Host 헤더 사용", + "useLegacyFormat": "레거시 OpenAI API 형식 사용", "requestyApiKey": "Requesty API 키", "getRequestyApiKey": "Requesty API 키 받기", "openRouterTransformsText": "프롬프트와 메시지 체인을 컨텍스트 크기로 압축 (OpenRouter Transforms)", diff --git a/webview-ui/src/i18n/locales/pl/settings.json b/webview-ui/src/i18n/locales/pl/settings.json index d456ea4252..0389a650a8 100644 --- a/webview-ui/src/i18n/locales/pl/settings.json +++ b/webview-ui/src/i18n/locales/pl/settings.json @@ -109,6 +109,8 @@ "glamaApiKey": "Klucz API Glama", "getGlamaApiKey": "Uzyskaj klucz API Glama", "useCustomBaseUrl": "Użyj niestandardowego URL bazowego", + "useHostHeader": "Użyj niestandardowego nagłówka Host", + "useLegacyFormat": "Użyj starszego formatu API OpenAI", "requestyApiKey": "Klucz API Requesty", "getRequestyApiKey": "Uzyskaj klucz API Requesty", "openRouterTransformsText": "Kompresuj podpowiedzi i łańcuchy wiadomości do rozmiaru kontekstu (Transformacje OpenRouter)", diff --git a/webview-ui/src/i18n/locales/pt-BR/settings.json b/webview-ui/src/i18n/locales/pt-BR/settings.json index dfd072b528..67b2650cb0 100644 --- a/webview-ui/src/i18n/locales/pt-BR/settings.json +++ b/webview-ui/src/i18n/locales/pt-BR/settings.json @@ -109,6 +109,8 @@ "glamaApiKey": "Chave de API Glama", "getGlamaApiKey": "Obter chave de API Glama", "useCustomBaseUrl": "Usar URL base personalizado", + "useHostHeader": "Usar cabeçalho Host personalizado", + "useLegacyFormat": "Usar formato de API OpenAI legado", "requestyApiKey": "Chave de API Requesty", "getRequestyApiKey": "Obter chave de API Requesty", "openRouterTransformsText": "Comprimir prompts e cadeias de mensagens para o tamanho do contexto (Transformações OpenRouter)", diff --git a/webview-ui/src/i18n/locales/tr/settings.json b/webview-ui/src/i18n/locales/tr/settings.json index 38cf4192c3..837023d639 100644 --- a/webview-ui/src/i18n/locales/tr/settings.json +++ b/webview-ui/src/i18n/locales/tr/settings.json @@ -109,6 +109,8 @@ "glamaApiKey": "Glama API Anahtarı", "getGlamaApiKey": "Glama API Anahtarı Al", "useCustomBaseUrl": "Özel temel URL kullan", + "useHostHeader": "Özel Host başlığı kullan", + "useLegacyFormat": "Eski OpenAI API formatını kullan", "requestyApiKey": "Requesty API Anahtarı", "getRequestyApiKey": "Requesty API Anahtarı Al", "openRouterTransformsText": "İstem ve mesaj zincirlerini bağlam boyutuna sıkıştır (OpenRouter Dönüşümleri)", diff --git a/webview-ui/src/i18n/locales/vi/settings.json b/webview-ui/src/i18n/locales/vi/settings.json index d30d897af0..d636cba5f7 100644 --- a/webview-ui/src/i18n/locales/vi/settings.json +++ b/webview-ui/src/i18n/locales/vi/settings.json @@ -109,6 +109,8 @@ "glamaApiKey": "Khóa API Glama", "getGlamaApiKey": "Lấy khóa API Glama", "useCustomBaseUrl": "Sử dụng URL cơ sở tùy chỉnh", + "useHostHeader": "Sử dụng tiêu đề Host tùy chỉnh", + "useLegacyFormat": "Sử dụng định dạng API OpenAI cũ", "requestyApiKey": "Khóa API Requesty", "getRequestyApiKey": "Lấy khóa API Requesty", "anthropicApiKey": "Khóa API Anthropic", diff --git a/webview-ui/src/i18n/locales/zh-CN/settings.json b/webview-ui/src/i18n/locales/zh-CN/settings.json index 0c03dd8fdb..d3d2cf36b3 100644 --- a/webview-ui/src/i18n/locales/zh-CN/settings.json +++ b/webview-ui/src/i18n/locales/zh-CN/settings.json @@ -107,6 +107,8 @@ "getOpenRouterApiKey": "获取 OpenRouter API 密钥", "apiKeyStorageNotice": "API 密钥安全存储在 VSCode 的密钥存储中", "useCustomBaseUrl": "使用自定义基础 URL", + "useHostHeader": "使用自定义 Host 标头", + "useLegacyFormat": "使用传统 OpenAI API 格式", "glamaApiKey": "Glama API 密钥", "getGlamaApiKey": "获取 Glama API 密钥", "requestyApiKey": "Requesty API 密钥", diff --git a/webview-ui/src/i18n/locales/zh-TW/settings.json b/webview-ui/src/i18n/locales/zh-TW/settings.json index 6718201030..d7965b4b2f 100644 --- a/webview-ui/src/i18n/locales/zh-TW/settings.json +++ b/webview-ui/src/i18n/locales/zh-TW/settings.json @@ -109,6 +109,8 @@ "glamaApiKey": "Glama API 金鑰", "getGlamaApiKey": "取得 Glama API 金鑰", "useCustomBaseUrl": "使用自訂基礎 URL", + "useHostHeader": "使用自訂 Host 標頭", + "useLegacyFormat": "使用舊版 OpenAI API 格式", "requestyApiKey": "Requesty API 金鑰", "getRequestyApiKey": "取得 Requesty API 金鑰", "openRouterTransformsText": "將提示和訊息鏈壓縮到上下文大小 (OpenRouter 轉換)",