mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-06 08:18:39 +00:00
Customizable headers for the OpenAI-compatible provider (#3056)
* Customizable headers for the OpenAI-compatible provider * PR feedback * Fix migration * Update webview-ui/src/components/settings/ApiOptions.tsx Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> --------- Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
This commit is contained in:
parent
2d8beb2ed0
commit
a356d70669
25 changed files with 312 additions and 58 deletions
|
|
@ -35,12 +35,17 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl
|
|||
const urlHost = this._getUrlHost(this.options.openAiBaseUrl)
|
||||
const isAzureOpenAi = urlHost === "azure.com" || urlHost.endsWith(".azure.com") || options.openAiUseAzure
|
||||
|
||||
const headers = {
|
||||
...DEFAULT_HEADERS,
|
||||
...(this.options.openAiHeaders || {}),
|
||||
}
|
||||
|
||||
if (isAzureAiInference) {
|
||||
// Azure AI Inference Service (e.g., for DeepSeek) uses a different path structure
|
||||
this.client = new OpenAI({
|
||||
baseURL,
|
||||
apiKey,
|
||||
defaultHeaders: DEFAULT_HEADERS,
|
||||
defaultHeaders: headers,
|
||||
defaultQuery: { "api-version": this.options.azureApiVersion || "2024-05-01-preview" },
|
||||
})
|
||||
} else if (isAzureOpenAi) {
|
||||
|
|
@ -50,19 +55,13 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl
|
|||
baseURL,
|
||||
apiKey,
|
||||
apiVersion: this.options.azureApiVersion || azureOpenAiDefaultApiVersion,
|
||||
defaultHeaders: {
|
||||
...DEFAULT_HEADERS,
|
||||
...(this.options.openAiHostHeader ? { Host: this.options.openAiHostHeader } : {}),
|
||||
},
|
||||
defaultHeaders: headers,
|
||||
})
|
||||
} else {
|
||||
this.client = new OpenAI({
|
||||
baseURL,
|
||||
apiKey,
|
||||
defaultHeaders: {
|
||||
...DEFAULT_HEADERS,
|
||||
...(this.options.openAiHostHeader ? { Host: this.options.openAiHostHeader } : {}),
|
||||
},
|
||||
defaultHeaders: headers,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -361,7 +360,7 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl
|
|||
}
|
||||
}
|
||||
|
||||
export async function getOpenAiModels(baseUrl?: string, apiKey?: string, hostHeader?: string) {
|
||||
export async function getOpenAiModels(baseUrl?: string, apiKey?: string, openAiHeaders?: Record<string, string>) {
|
||||
try {
|
||||
if (!baseUrl) {
|
||||
return []
|
||||
|
|
@ -372,16 +371,15 @@ export async function getOpenAiModels(baseUrl?: string, apiKey?: string, hostHea
|
|||
}
|
||||
|
||||
const config: Record<string, any> = {}
|
||||
const headers: Record<string, string> = {}
|
||||
const headers: Record<string, string> = {
|
||||
...DEFAULT_HEADERS,
|
||||
...(openAiHeaders || {}),
|
||||
}
|
||||
|
||||
if (apiKey) {
|
||||
headers["Authorization"] = `Bearer ${apiKey}`
|
||||
}
|
||||
|
||||
if (hostHeader) {
|
||||
headers["Host"] = hostHeader
|
||||
}
|
||||
|
||||
if (Object.keys(headers).length > 0) {
|
||||
config["headers"] = headers
|
||||
}
|
||||
|
|
|
|||
|
|
@ -192,6 +192,16 @@ export class ContextProxy {
|
|||
// If a value is not present in the new configuration, then it is assumed
|
||||
// that the setting's value should be `undefined` and therefore we
|
||||
// need to remove it from the state cache if it exists.
|
||||
|
||||
// Ensure openAiHeaders is always an object even when empty
|
||||
// This is critical for proper serialization/deserialization through IPC
|
||||
if (values.openAiHeaders !== undefined) {
|
||||
// Check if it's empty or null
|
||||
if (!values.openAiHeaders || Object.keys(values.openAiHeaders).length === 0) {
|
||||
values.openAiHeaders = {}
|
||||
}
|
||||
}
|
||||
|
||||
await this.setValues({
|
||||
...PROVIDER_SETTINGS_KEYS.filter((key) => !isSecretStateKey(key))
|
||||
.filter((key) => !!this.stateCache[key])
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ export const providerProfilesSchema = z.object({
|
|||
.object({
|
||||
rateLimitSecondsMigrated: z.boolean().optional(),
|
||||
diffSettingsMigrated: z.boolean().optional(),
|
||||
openAiHeadersMigrated: z.boolean().optional(),
|
||||
})
|
||||
.optional(),
|
||||
})
|
||||
|
|
@ -38,6 +39,7 @@ export class ProviderSettingsManager {
|
|||
migrations: {
|
||||
rateLimitSecondsMigrated: true, // Mark as migrated on fresh installs
|
||||
diffSettingsMigrated: true, // Mark as migrated on fresh installs
|
||||
openAiHeadersMigrated: true, // Mark as migrated on fresh installs
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -90,6 +92,7 @@ export class ProviderSettingsManager {
|
|||
providerProfiles.migrations = {
|
||||
rateLimitSecondsMigrated: false,
|
||||
diffSettingsMigrated: false,
|
||||
openAiHeadersMigrated: false,
|
||||
} // Initialize with default values
|
||||
isDirty = true
|
||||
}
|
||||
|
|
@ -106,6 +109,12 @@ export class ProviderSettingsManager {
|
|||
isDirty = true
|
||||
}
|
||||
|
||||
if (!providerProfiles.migrations.openAiHeadersMigrated) {
|
||||
await this.migrateOpenAiHeaders(providerProfiles)
|
||||
providerProfiles.migrations.openAiHeadersMigrated = true
|
||||
isDirty = true
|
||||
}
|
||||
|
||||
if (isDirty) {
|
||||
await this.store(providerProfiles)
|
||||
}
|
||||
|
|
@ -175,6 +184,30 @@ export class ProviderSettingsManager {
|
|||
}
|
||||
}
|
||||
|
||||
private async migrateOpenAiHeaders(providerProfiles: ProviderProfiles) {
|
||||
try {
|
||||
for (const [_name, apiConfig] of Object.entries(providerProfiles.apiConfigs)) {
|
||||
// Use type assertion to access the deprecated property safely
|
||||
const configAny = apiConfig as any
|
||||
|
||||
// Check if openAiHostHeader exists but openAiHeaders doesn't
|
||||
if (
|
||||
configAny.openAiHostHeader &&
|
||||
(!apiConfig.openAiHeaders || Object.keys(apiConfig.openAiHeaders || {}).length === 0)
|
||||
) {
|
||||
// Create the headers object with the Host value
|
||||
apiConfig.openAiHeaders = { Host: configAny.openAiHostHeader }
|
||||
|
||||
// Delete the old property to prevent re-migration
|
||||
// This prevents the header from reappearing after deletion
|
||||
configAny.openAiHostHeader = undefined
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`[MigrateOpenAiHeaders] Failed to migrate OpenAI headers:`, error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List all available configs with metadata.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ describe("ProviderSettingsManager", () => {
|
|||
migrations: {
|
||||
rateLimitSecondsMigrated: true,
|
||||
diffSettingsMigrated: true,
|
||||
openAiHeadersMigrated: true,
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -310,7 +310,7 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We
|
|||
const openAiModels = await getOpenAiModels(
|
||||
message?.values?.baseUrl,
|
||||
message?.values?.apiKey,
|
||||
message?.values?.hostHeader,
|
||||
message?.values?.openAiHeaders,
|
||||
)
|
||||
|
||||
provider.postMessageToWebview({ type: "openAiModels", openAiModels })
|
||||
|
|
|
|||
7
src/exports/roo-code.d.ts
vendored
7
src/exports/roo-code.d.ts
vendored
|
|
@ -50,7 +50,6 @@ type ProviderSettings = {
|
|||
vertexRegion?: string | undefined
|
||||
openAiBaseUrl?: string | undefined
|
||||
openAiApiKey?: string | undefined
|
||||
openAiHostHeader?: string | undefined
|
||||
openAiLegacyFormat?: boolean | undefined
|
||||
openAiR1FormatEnabled?: boolean | undefined
|
||||
openAiModelId?: string | undefined
|
||||
|
|
@ -88,6 +87,12 @@ type ProviderSettings = {
|
|||
azureApiVersion?: string | undefined
|
||||
openAiStreamingEnabled?: boolean | undefined
|
||||
enableReasoningEffort?: boolean | undefined
|
||||
openAiHostHeader?: string | undefined
|
||||
openAiHeaders?:
|
||||
| {
|
||||
[x: string]: string
|
||||
}
|
||||
| undefined
|
||||
ollamaModelId?: string | undefined
|
||||
ollamaBaseUrl?: string | undefined
|
||||
vsCodeLmModelSelector?:
|
||||
|
|
|
|||
|
|
@ -51,7 +51,6 @@ type ProviderSettings = {
|
|||
vertexRegion?: string | undefined
|
||||
openAiBaseUrl?: string | undefined
|
||||
openAiApiKey?: string | undefined
|
||||
openAiHostHeader?: string | undefined
|
||||
openAiLegacyFormat?: boolean | undefined
|
||||
openAiR1FormatEnabled?: boolean | undefined
|
||||
openAiModelId?: string | undefined
|
||||
|
|
@ -89,6 +88,12 @@ type ProviderSettings = {
|
|||
azureApiVersion?: string | undefined
|
||||
openAiStreamingEnabled?: boolean | undefined
|
||||
enableReasoningEffort?: boolean | undefined
|
||||
openAiHostHeader?: string | undefined
|
||||
openAiHeaders?:
|
||||
| {
|
||||
[x: string]: string
|
||||
}
|
||||
| undefined
|
||||
ollamaModelId?: string | undefined
|
||||
ollamaBaseUrl?: string | undefined
|
||||
vsCodeLmModelSelector?:
|
||||
|
|
|
|||
|
|
@ -370,7 +370,6 @@ 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(),
|
||||
|
|
@ -379,6 +378,8 @@ export const providerSettingsSchema = z.object({
|
|||
azureApiVersion: z.string().optional(),
|
||||
openAiStreamingEnabled: z.boolean().optional(),
|
||||
enableReasoningEffort: z.boolean().optional(),
|
||||
openAiHostHeader: z.string().optional(), // Keep temporarily for backward compatibility during migration
|
||||
openAiHeaders: z.record(z.string(), z.string()).optional(),
|
||||
// Ollama
|
||||
ollamaModelId: z.string().optional(),
|
||||
ollamaBaseUrl: z.string().optional(),
|
||||
|
|
@ -470,7 +471,6 @@ const providerSettingsRecord: ProviderSettingsRecord = {
|
|||
// OpenAI
|
||||
openAiBaseUrl: undefined,
|
||||
openAiApiKey: undefined,
|
||||
openAiHostHeader: undefined,
|
||||
openAiLegacyFormat: undefined,
|
||||
openAiR1FormatEnabled: undefined,
|
||||
openAiModelId: undefined,
|
||||
|
|
@ -479,6 +479,8 @@ const providerSettingsRecord: ProviderSettingsRecord = {
|
|||
azureApiVersion: undefined,
|
||||
openAiStreamingEnabled: undefined,
|
||||
enableReasoningEffort: undefined,
|
||||
openAiHostHeader: undefined, // Keep temporarily for backward compatibility during migration
|
||||
openAiHeaders: undefined,
|
||||
// Ollama
|
||||
ollamaModelId: undefined,
|
||||
ollamaBaseUrl: undefined,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,13 @@ import { useDebounce, useEvent } from "react-use"
|
|||
import { Trans } from "react-i18next"
|
||||
import { LanguageModelChatSelector } from "vscode"
|
||||
import { Checkbox } from "vscrui"
|
||||
import { VSCodeLink, VSCodeRadio, VSCodeRadioGroup, VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
|
||||
import {
|
||||
VSCodeButton,
|
||||
VSCodeLink,
|
||||
VSCodeRadio,
|
||||
VSCodeRadioGroup,
|
||||
VSCodeTextField,
|
||||
} from "@vscode/webview-ui-toolkit/react"
|
||||
import { ExternalLinkIcon } from "@radix-ui/react-icons"
|
||||
|
||||
import { ReasoningEffort as ReasoningEffortType } from "@roo/schemas"
|
||||
|
|
@ -74,17 +80,92 @@ const ApiOptions = ({
|
|||
|
||||
const [openAiModels, setOpenAiModels] = useState<Record<string, ModelInfo> | null>(null)
|
||||
|
||||
const [customHeaders, setCustomHeaders] = useState<[string, string][]>(() => {
|
||||
const headers = apiConfiguration?.openAiHeaders || {}
|
||||
return Object.entries(headers)
|
||||
})
|
||||
|
||||
// Effect to synchronize internal customHeaders state with prop changes
|
||||
useEffect(() => {
|
||||
const propHeaders = apiConfiguration?.openAiHeaders || {}
|
||||
if (JSON.stringify(customHeaders) !== JSON.stringify(Object.entries(propHeaders))) setCustomHeaders(Object.entries(propHeaders))
|
||||
}, [apiConfiguration?.openAiHeaders])
|
||||
|
||||
const [anthropicBaseUrlSelected, setAnthropicBaseUrlSelected] = useState(!!apiConfiguration?.anthropicBaseUrl)
|
||||
const [openAiNativeBaseUrlSelected, setOpenAiNativeBaseUrlSelected] = useState(
|
||||
!!apiConfiguration?.openAiNativeBaseUrl,
|
||||
)
|
||||
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,
|
||||
)
|
||||
|
||||
const handleAddCustomHeader = useCallback(() => {
|
||||
// Only update the local state to show the new row in the UI
|
||||
setCustomHeaders((prev) => [...prev, ["", ""]])
|
||||
// Do not update the main configuration yet, wait for user input
|
||||
}, [])
|
||||
|
||||
const handleUpdateHeaderKey = useCallback((index: number, newKey: string) => {
|
||||
setCustomHeaders((prev) => {
|
||||
const updated = [...prev]
|
||||
if (updated[index]) {
|
||||
updated[index] = [newKey, updated[index][1]]
|
||||
}
|
||||
return updated
|
||||
})
|
||||
}, [])
|
||||
|
||||
const handleUpdateHeaderValue = useCallback((index: number, newValue: string) => {
|
||||
setCustomHeaders((prev) => {
|
||||
const updated = [...prev]
|
||||
if (updated[index]) {
|
||||
updated[index] = [updated[index][0], newValue]
|
||||
}
|
||||
return updated
|
||||
})
|
||||
}, [])
|
||||
|
||||
const handleRemoveCustomHeader = useCallback((index: number) => {
|
||||
setCustomHeaders((prev) => prev.filter((_, i) => i !== index))
|
||||
}, [])
|
||||
|
||||
// Helper to convert array of tuples to object (filtering out empty keys)
|
||||
const convertHeadersToObject = (headers: [string, string][]): Record<string, string> => {
|
||||
const result: Record<string, string> = {}
|
||||
|
||||
// Process each header tuple
|
||||
for (const [key, value] of headers) {
|
||||
const trimmedKey = key.trim()
|
||||
|
||||
// Skip empty keys
|
||||
if (!trimmedKey) continue
|
||||
|
||||
// For duplicates, the last one in the array wins
|
||||
// This matches how HTTP headers work in general
|
||||
result[trimmedKey] = value.trim()
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Debounced effect to update the main configuration when local customHeaders state stabilizes
|
||||
useDebounce(
|
||||
() => {
|
||||
const currentConfigHeaders = apiConfiguration?.openAiHeaders || {}
|
||||
const newHeadersObject = convertHeadersToObject(customHeaders)
|
||||
|
||||
// Only update if the processed object is different from the current config
|
||||
if (JSON.stringify(currentConfigHeaders) !== JSON.stringify(newHeadersObject)) {
|
||||
setApiConfigurationField("openAiHeaders", newHeadersObject)
|
||||
}
|
||||
},
|
||||
300,
|
||||
[customHeaders, apiConfiguration?.openAiHeaders, setApiConfigurationField],
|
||||
)
|
||||
|
||||
const [isDescriptionExpanded, setIsDescriptionExpanded] = useState(false)
|
||||
const noTransform = <T,>(value: T) => value
|
||||
|
||||
|
|
@ -121,12 +202,15 @@ const ApiOptions = ({
|
|||
useDebounce(
|
||||
() => {
|
||||
if (selectedProvider === "openai") {
|
||||
// Use our custom headers state to build the headers object
|
||||
const headerObject = convertHeadersToObject(customHeaders)
|
||||
vscode.postMessage({
|
||||
type: "requestOpenAiModels",
|
||||
values: {
|
||||
baseUrl: apiConfiguration?.openAiBaseUrl,
|
||||
apiKey: apiConfiguration?.openAiApiKey,
|
||||
hostHeader: apiConfiguration?.openAiHostHeader,
|
||||
customHeaders: {}, // Reserved for any additional headers
|
||||
openAiHeaders: headerObject,
|
||||
},
|
||||
})
|
||||
} else if (selectedProvider === "ollama") {
|
||||
|
|
@ -145,6 +229,7 @@ const ApiOptions = ({
|
|||
apiConfiguration?.openAiApiKey,
|
||||
apiConfiguration?.ollamaBaseUrl,
|
||||
apiConfiguration?.lmStudioBaseUrl,
|
||||
customHeaders,
|
||||
],
|
||||
)
|
||||
|
||||
|
|
@ -854,25 +939,44 @@ const ApiOptions = ({
|
|||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Checkbox
|
||||
checked={openAiHostHeaderSelected}
|
||||
onChange={(checked: boolean) => {
|
||||
setOpenAiHostHeaderSelected(checked)
|
||||
|
||||
if (!checked) {
|
||||
setApiConfigurationField("openAiHostHeader", "")
|
||||
}
|
||||
}}>
|
||||
{t("settings:providers.useHostHeader")}
|
||||
</Checkbox>
|
||||
{openAiHostHeaderSelected && (
|
||||
<VSCodeTextField
|
||||
value={apiConfiguration?.openAiHostHeader || ""}
|
||||
onInput={handleInputChange("openAiHostHeader")}
|
||||
placeholder="custom-api-hostname.example.com"
|
||||
className="w-full mt-1"
|
||||
/>
|
||||
{/* Custom Headers UI */}
|
||||
<div className="mb-4">
|
||||
<div className="flex justify-between items-center mb-2">
|
||||
<label className="block font-medium">{t("settings:providers.customHeaders")}</label>
|
||||
<VSCodeButton
|
||||
appearance="icon"
|
||||
title={t("settings:common.add")}
|
||||
onClick={handleAddCustomHeader}>
|
||||
<span className="codicon codicon-add"></span>
|
||||
</VSCodeButton>
|
||||
</div>
|
||||
{!customHeaders.length ? (
|
||||
<div className="text-sm text-vscode-descriptionForeground">
|
||||
{t("settings:providers.noCustomHeaders")}
|
||||
</div>
|
||||
) : (
|
||||
customHeaders.map(([key, value], index) => (
|
||||
<div key={index} className="flex items-center mb-2">
|
||||
<VSCodeTextField
|
||||
value={key}
|
||||
className="flex-1 mr-2"
|
||||
placeholder={t("settings:providers.headerName")}
|
||||
onInput={(e: any) => handleUpdateHeaderKey(index, e.target.value)}
|
||||
/>
|
||||
<VSCodeTextField
|
||||
value={value}
|
||||
className="flex-1 mr-2"
|
||||
placeholder={t("settings:providers.headerValue")}
|
||||
onInput={(e: any) => handleUpdateHeaderValue(index, e.target.value)}
|
||||
/>
|
||||
<VSCodeButton
|
||||
appearance="icon"
|
||||
title={t("settings:common.remove")}
|
||||
onClick={() => handleRemoveCustomHeader(index)}>
|
||||
<span className="codicon codicon-trash"></span>
|
||||
</VSCodeButton>
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@
|
|||
"done": "Fet",
|
||||
"cancel": "Cancel·lar",
|
||||
"reset": "Restablir",
|
||||
"select": "Seleccionar"
|
||||
"select": "Seleccionar",
|
||||
"add": "Afegir capçalera",
|
||||
"remove": "Eliminar"
|
||||
},
|
||||
"header": {
|
||||
"title": "Configuració",
|
||||
|
|
@ -107,6 +109,10 @@
|
|||
"useCustomBaseUrl": "Utilitzar URL base personalitzada",
|
||||
"useHostHeader": "Utilitzar capçalera Host personalitzada",
|
||||
"useLegacyFormat": "Utilitzar el format d'API OpenAI antic",
|
||||
"customHeaders": "Capçaleres personalitzades",
|
||||
"headerName": "Nom de la capçalera",
|
||||
"headerValue": "Valor de la capçalera",
|
||||
"noCustomHeaders": "No hi ha capçaleres personalitzades definides. Feu clic al botó + per afegir-ne una.",
|
||||
"openRouterTransformsText": "Comprimir prompts i cadenes de missatges a la mida del context (<a>Transformacions d'OpenRouter</a>)",
|
||||
"model": "Model",
|
||||
"getOpenRouterApiKey": "Obtenir clau API d'OpenRouter",
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@
|
|||
"done": "Fertig",
|
||||
"cancel": "Abbrechen",
|
||||
"reset": "Zurücksetzen",
|
||||
"select": "Auswählen"
|
||||
"select": "Auswählen",
|
||||
"add": "Header hinzufügen",
|
||||
"remove": "Entfernen"
|
||||
},
|
||||
"header": {
|
||||
"title": "Einstellungen",
|
||||
|
|
@ -111,6 +113,10 @@
|
|||
"useCustomBaseUrl": "Benutzerdefinierte Basis-URL verwenden",
|
||||
"useHostHeader": "Benutzerdefinierten Host-Header verwenden",
|
||||
"useLegacyFormat": "Altes OpenAI API-Format verwenden",
|
||||
"customHeaders": "Benutzerdefinierte Headers",
|
||||
"headerName": "Header-Name",
|
||||
"headerValue": "Header-Wert",
|
||||
"noCustomHeaders": "Keine benutzerdefinierten Headers definiert. Klicke auf die + Schaltfläche, um einen hinzuzufügen.",
|
||||
"requestyApiKey": "Requesty API-Schlüssel",
|
||||
"getRequestyApiKey": "Requesty API-Schlüssel erhalten",
|
||||
"openRouterTransformsText": "Prompts und Nachrichtenketten auf Kontextgröße komprimieren (<a>OpenRouter Transformationen</a>)",
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@
|
|||
"done": "Done",
|
||||
"cancel": "Cancel",
|
||||
"reset": "Reset",
|
||||
"select": "Select"
|
||||
"select": "Select",
|
||||
"add": "Add Header",
|
||||
"remove": "Remove"
|
||||
},
|
||||
"header": {
|
||||
"title": "Settings",
|
||||
|
|
@ -111,6 +113,10 @@
|
|||
"useCustomBaseUrl": "Use custom base URL",
|
||||
"useHostHeader": "Use custom Host header",
|
||||
"useLegacyFormat": "Use legacy OpenAI API format",
|
||||
"customHeaders": "Custom Headers",
|
||||
"headerName": "Header name",
|
||||
"headerValue": "Header value",
|
||||
"noCustomHeaders": "No custom headers defined. Click the + button to add one.",
|
||||
"requestyApiKey": "Requesty API Key",
|
||||
"getRequestyApiKey": "Get Requesty API Key",
|
||||
"openRouterTransformsText": "Compress prompts and message chains to the context size (<a>OpenRouter Transforms</a>)",
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@
|
|||
"done": "Hecho",
|
||||
"cancel": "Cancelar",
|
||||
"reset": "Restablecer",
|
||||
"select": "Seleccionar"
|
||||
"select": "Seleccionar",
|
||||
"add": "Añadir encabezado",
|
||||
"remove": "Eliminar"
|
||||
},
|
||||
"header": {
|
||||
"title": "Configuración",
|
||||
|
|
@ -111,6 +113,10 @@
|
|||
"useCustomBaseUrl": "Usar URL base personalizada",
|
||||
"useHostHeader": "Usar encabezado Host personalizado",
|
||||
"useLegacyFormat": "Usar formato API de OpenAI heredado",
|
||||
"customHeaders": "Encabezados personalizados",
|
||||
"headerName": "Nombre del encabezado",
|
||||
"headerValue": "Valor del encabezado",
|
||||
"noCustomHeaders": "No hay encabezados personalizados definidos. Haga clic en el botón + para añadir uno.",
|
||||
"requestyApiKey": "Clave API de Requesty",
|
||||
"getRequestyApiKey": "Obtener clave API de Requesty",
|
||||
"openRouterTransformsText": "Comprimir prompts y cadenas de mensajes al tamaño del contexto (<a>Transformaciones de OpenRouter</a>)",
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@
|
|||
"done": "Terminé",
|
||||
"cancel": "Annuler",
|
||||
"reset": "Réinitialiser",
|
||||
"select": "Sélectionner"
|
||||
"select": "Sélectionner",
|
||||
"add": "Ajouter un en-tête",
|
||||
"remove": "Supprimer"
|
||||
},
|
||||
"header": {
|
||||
"title": "Paramètres",
|
||||
|
|
@ -111,6 +113,10 @@
|
|||
"useCustomBaseUrl": "Utiliser une URL de base personnalisée",
|
||||
"useHostHeader": "Utiliser un en-tête Host personnalisé",
|
||||
"useLegacyFormat": "Utiliser le format API OpenAI hérité",
|
||||
"customHeaders": "En-têtes personnalisés",
|
||||
"headerName": "Nom de l'en-tête",
|
||||
"headerValue": "Valeur de l'en-tête",
|
||||
"noCustomHeaders": "Aucun en-tête personnalisé défini. Cliquez sur le bouton + pour en ajouter un.",
|
||||
"requestyApiKey": "Clé API Requesty",
|
||||
"getRequestyApiKey": "Obtenir la clé API Requesty",
|
||||
"openRouterTransformsText": "Compresser les prompts et chaînes de messages à la taille du contexte (<a>Transformations OpenRouter</a>)",
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@
|
|||
"done": "पूर्ण",
|
||||
"cancel": "रद्द करें",
|
||||
"reset": "रीसेट करें",
|
||||
"select": "चुनें"
|
||||
"select": "चुनें",
|
||||
"add": "हेडर जोड़ें",
|
||||
"remove": "हटाएं"
|
||||
},
|
||||
"header": {
|
||||
"title": "सेटिंग्स",
|
||||
|
|
@ -111,6 +113,10 @@
|
|||
"useCustomBaseUrl": "कस्टम बेस URL का उपयोग करें",
|
||||
"useHostHeader": "कस्टम होस्ट हेडर का उपयोग करें",
|
||||
"useLegacyFormat": "पुराने OpenAI API प्रारूप का उपयोग करें",
|
||||
"customHeaders": "कस्टम हेडर्स",
|
||||
"headerName": "हेडर नाम",
|
||||
"headerValue": "हेडर मूल्य",
|
||||
"noCustomHeaders": "कोई कस्टम हेडर परिभाषित नहीं है। एक जोड़ने के लिए + बटन पर क्लिक करें।",
|
||||
"requestyApiKey": "Requesty API कुंजी",
|
||||
"getRequestyApiKey": "Requesty API कुंजी प्राप्त करें",
|
||||
"openRouterTransformsText": "संदर्भ आकार के लिए प्रॉम्प्ट और संदेश श्रृंखलाओं को संपीड़ित करें (<a>OpenRouter ट्रांसफॉर्म</a>)",
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@
|
|||
"done": "Fatto",
|
||||
"cancel": "Annulla",
|
||||
"reset": "Ripristina",
|
||||
"select": "Seleziona"
|
||||
"select": "Seleziona",
|
||||
"add": "Aggiungi intestazione",
|
||||
"remove": "Rimuovi"
|
||||
},
|
||||
"header": {
|
||||
"title": "Impostazioni",
|
||||
|
|
@ -111,6 +113,10 @@
|
|||
"useCustomBaseUrl": "Usa URL base personalizzato",
|
||||
"useHostHeader": "Usa intestazione Host personalizzata",
|
||||
"useLegacyFormat": "Usa formato API OpenAI legacy",
|
||||
"customHeaders": "Intestazioni personalizzate",
|
||||
"headerName": "Nome intestazione",
|
||||
"headerValue": "Valore intestazione",
|
||||
"noCustomHeaders": "Nessuna intestazione personalizzata definita. Fai clic sul pulsante + per aggiungerne una.",
|
||||
"requestyApiKey": "Chiave API Requesty",
|
||||
"getRequestyApiKey": "Ottieni chiave API Requesty",
|
||||
"openRouterTransformsText": "Comprimi prompt e catene di messaggi alla dimensione del contesto (<a>Trasformazioni OpenRouter</a>)",
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@
|
|||
"done": "完了",
|
||||
"cancel": "キャンセル",
|
||||
"reset": "リセット",
|
||||
"select": "選択"
|
||||
"select": "選択",
|
||||
"add": "ヘッダーを追加",
|
||||
"remove": "削除"
|
||||
},
|
||||
"header": {
|
||||
"title": "設定",
|
||||
|
|
@ -111,6 +113,10 @@
|
|||
"useCustomBaseUrl": "カスタムベースURLを使用",
|
||||
"useHostHeader": "カスタムHostヘッダーを使用",
|
||||
"useLegacyFormat": "レガシーOpenAI API形式を使用",
|
||||
"customHeaders": "カスタムヘッダー",
|
||||
"headerName": "ヘッダー名",
|
||||
"headerValue": "ヘッダー値",
|
||||
"noCustomHeaders": "カスタムヘッダーが定義されていません。+ ボタンをクリックして追加してください。",
|
||||
"requestyApiKey": "Requesty APIキー",
|
||||
"getRequestyApiKey": "Requesty APIキーを取得",
|
||||
"openRouterTransformsText": "プロンプトとメッセージチェーンをコンテキストサイズに圧縮 (<a>OpenRouter Transforms</a>)",
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@
|
|||
"done": "완료",
|
||||
"cancel": "취소",
|
||||
"reset": "초기화",
|
||||
"select": "선택"
|
||||
"select": "선택",
|
||||
"add": "헤더 추가",
|
||||
"remove": "삭제"
|
||||
},
|
||||
"header": {
|
||||
"title": "설정",
|
||||
|
|
@ -111,6 +113,10 @@
|
|||
"useCustomBaseUrl": "사용자 정의 기본 URL 사용",
|
||||
"useHostHeader": "사용자 정의 Host 헤더 사용",
|
||||
"useLegacyFormat": "레거시 OpenAI API 형식 사용",
|
||||
"customHeaders": "사용자 정의 헤더",
|
||||
"headerName": "헤더 이름",
|
||||
"headerValue": "헤더 값",
|
||||
"noCustomHeaders": "정의된 사용자 정의 헤더가 없습니다. + 버튼을 클릭하여 추가하세요.",
|
||||
"requestyApiKey": "Requesty API 키",
|
||||
"getRequestyApiKey": "Requesty API 키 받기",
|
||||
"openRouterTransformsText": "프롬프트와 메시지 체인을 컨텍스트 크기로 압축 (<a>OpenRouter Transforms</a>)",
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@
|
|||
"done": "Gotowe",
|
||||
"cancel": "Anuluj",
|
||||
"reset": "Resetuj",
|
||||
"select": "Wybierz"
|
||||
"select": "Wybierz",
|
||||
"add": "Dodaj nagłówek",
|
||||
"remove": "Usuń"
|
||||
},
|
||||
"header": {
|
||||
"title": "Ustawienia",
|
||||
|
|
@ -111,6 +113,10 @@
|
|||
"useCustomBaseUrl": "Użyj niestandardowego URL bazowego",
|
||||
"useHostHeader": "Użyj niestandardowego nagłówka Host",
|
||||
"useLegacyFormat": "Użyj starszego formatu API OpenAI",
|
||||
"customHeaders": "Niestandardowe nagłówki",
|
||||
"headerName": "Nazwa nagłówka",
|
||||
"headerValue": "Wartość nagłówka",
|
||||
"noCustomHeaders": "Brak zdefiniowanych niestandardowych nagłówków. Kliknij przycisk +, aby dodać.",
|
||||
"requestyApiKey": "Klucz API Requesty",
|
||||
"getRequestyApiKey": "Uzyskaj klucz API Requesty",
|
||||
"openRouterTransformsText": "Kompresuj podpowiedzi i łańcuchy wiadomości do rozmiaru kontekstu (<a>Transformacje OpenRouter</a>)",
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@
|
|||
"done": "Concluído",
|
||||
"cancel": "Cancelar",
|
||||
"reset": "Redefinir",
|
||||
"select": "Selecionar"
|
||||
"select": "Selecionar",
|
||||
"add": "Adicionar cabeçalho",
|
||||
"remove": "Remover"
|
||||
},
|
||||
"header": {
|
||||
"title": "Configurações",
|
||||
|
|
@ -111,6 +113,10 @@
|
|||
"useCustomBaseUrl": "Usar URL base personalizado",
|
||||
"useHostHeader": "Usar cabeçalho Host personalizado",
|
||||
"useLegacyFormat": "Usar formato de API OpenAI legado",
|
||||
"customHeaders": "Cabeçalhos personalizados",
|
||||
"headerName": "Nome do cabeçalho",
|
||||
"headerValue": "Valor do cabeçalho",
|
||||
"noCustomHeaders": "Nenhum cabeçalho personalizado definido. Clique no botão + para adicionar um.",
|
||||
"requestyApiKey": "Chave de API Requesty",
|
||||
"getRequestyApiKey": "Obter chave de API Requesty",
|
||||
"openRouterTransformsText": "Comprimir prompts e cadeias de mensagens para o tamanho do contexto (<a>Transformações OpenRouter</a>)",
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@
|
|||
"done": "Готово",
|
||||
"cancel": "Отмена",
|
||||
"reset": "Сбросить",
|
||||
"select": "Выбрать"
|
||||
"select": "Выбрать",
|
||||
"add": "Добавить заголовок",
|
||||
"remove": "Удалить"
|
||||
},
|
||||
"header": {
|
||||
"title": "Настройки",
|
||||
|
|
@ -111,6 +113,10 @@
|
|||
"useCustomBaseUrl": "Использовать пользовательский базовый URL",
|
||||
"useHostHeader": "Использовать пользовательский Host-заголовок",
|
||||
"useLegacyFormat": "Использовать устаревший формат OpenAI API",
|
||||
"customHeaders": "Пользовательские заголовки",
|
||||
"headerName": "Имя заголовка",
|
||||
"headerValue": "Значение заголовка",
|
||||
"noCustomHeaders": "Пользовательские заголовки не определены. Нажмите кнопку +, чтобы добавить.",
|
||||
"requestyApiKey": "Requesty API-ключ",
|
||||
"getRequestyApiKey": "Получить Requesty API-ключ",
|
||||
"openRouterTransformsText": "Сжимать подсказки и цепочки сообщений до размера контекста (<a>OpenRouter Transforms</a>)",
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@
|
|||
"done": "Tamamlandı",
|
||||
"cancel": "İptal",
|
||||
"reset": "Sıfırla",
|
||||
"select": "Seç"
|
||||
"select": "Seç",
|
||||
"add": "Başlık Ekle",
|
||||
"remove": "Kaldır"
|
||||
},
|
||||
"header": {
|
||||
"title": "Ayarlar",
|
||||
|
|
@ -111,6 +113,10 @@
|
|||
"useCustomBaseUrl": "Özel temel URL kullan",
|
||||
"useHostHeader": "Özel Host başlığı kullan",
|
||||
"useLegacyFormat": "Eski OpenAI API formatını kullan",
|
||||
"customHeaders": "Özel Başlıklar",
|
||||
"headerName": "Başlık adı",
|
||||
"headerValue": "Başlık değeri",
|
||||
"noCustomHeaders": "Tanımlanmış özel başlık yok. Eklemek için + düğmesine tıklayın.",
|
||||
"requestyApiKey": "Requesty API Anahtarı",
|
||||
"getRequestyApiKey": "Requesty API Anahtarı Al",
|
||||
"openRouterTransformsText": "İstem ve mesaj zincirlerini bağlam boyutuna sıkıştır (<a>OpenRouter Dönüşümleri</a>)",
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@
|
|||
"done": "Hoàn thành",
|
||||
"cancel": "Hủy",
|
||||
"reset": "Đặt lại",
|
||||
"select": "Chọn"
|
||||
"select": "Chọn",
|
||||
"add": "Thêm tiêu đề",
|
||||
"remove": "Xóa"
|
||||
},
|
||||
"header": {
|
||||
"title": "Cài đặt",
|
||||
|
|
@ -111,6 +113,10 @@
|
|||
"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ũ",
|
||||
"customHeaders": "Tiêu đề tùy chỉnh",
|
||||
"headerName": "Tên tiêu đề",
|
||||
"headerValue": "Giá trị tiêu đề",
|
||||
"noCustomHeaders": "Chưa có tiêu đề tùy chỉnh nào được định nghĩa. Nhấp vào nút + để thêm.",
|
||||
"requestyApiKey": "Khóa API Requesty",
|
||||
"getRequestyApiKey": "Lấy khóa API Requesty",
|
||||
"anthropicApiKey": "Khóa API Anthropic",
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@
|
|||
"done": "完成",
|
||||
"cancel": "取消",
|
||||
"reset": "恢复默认设置",
|
||||
"select": "选择"
|
||||
"select": "选择",
|
||||
"add": "添加标头",
|
||||
"remove": "移除"
|
||||
},
|
||||
"header": {
|
||||
"title": "设置",
|
||||
|
|
@ -109,6 +111,10 @@
|
|||
"useCustomBaseUrl": "使用自定义基础 URL",
|
||||
"useHostHeader": "使用自定义 Host 标头",
|
||||
"useLegacyFormat": "使用传统 OpenAI API 格式",
|
||||
"customHeaders": "自定义标头",
|
||||
"headerName": "标头名称",
|
||||
"headerValue": "标头值",
|
||||
"noCustomHeaders": "暂无自定义标头。点击 + 按钮添加。",
|
||||
"glamaApiKey": "Glama API 密钥",
|
||||
"getGlamaApiKey": "获取 Glama API 密钥",
|
||||
"requestyApiKey": "Requesty API 密钥",
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@
|
|||
"done": "完成",
|
||||
"cancel": "取消",
|
||||
"reset": "重設",
|
||||
"select": "選擇"
|
||||
"select": "選擇",
|
||||
"add": "新增標頭",
|
||||
"remove": "移除"
|
||||
},
|
||||
"header": {
|
||||
"title": "設定",
|
||||
|
|
@ -111,6 +113,10 @@
|
|||
"useCustomBaseUrl": "使用自訂基礎 URL",
|
||||
"useHostHeader": "使用自訂 Host 標頭",
|
||||
"useLegacyFormat": "使用舊版 OpenAI API 格式",
|
||||
"customHeaders": "自訂標頭",
|
||||
"headerName": "標頭名稱",
|
||||
"headerValue": "標頭值",
|
||||
"noCustomHeaders": "尚未定義自訂標頭。點擊 + 按鈕以新增。",
|
||||
"requestyApiKey": "Requesty API 金鑰",
|
||||
"getRequestyApiKey": "取得 Requesty API 金鑰",
|
||||
"openRouterTransformsText": "將提示和訊息鏈壓縮到上下文大小 (<a>OpenRouter 轉換</a>)",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue