From e467bd47fb38868753d17ad15c2fa6ddfaa53f04 Mon Sep 17 00:00:00 2001 From: Miguel Palhas Date: Tue, 25 Aug 2026 08:29:59 +0100 Subject: [PATCH] feat(arr): add the OpenAI endpoint fields to /settings Shown only when openai is among available_engines. Placeholders say the base URL is optional and what a local one looks like. --- web/src/settings.ts | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/web/src/settings.ts b/web/src/settings.ts index 949aacd..7abd344 100644 --- a/web/src/settings.ts +++ b/web/src/settings.ts @@ -46,6 +46,15 @@ export interface SubtitleSettings { provider_daily_budgets: Record; translator_daily_budgets: Record; remote_command_timeout_seconds: number; + /** + * Where the OpenAI-compatible backend points and which model it names + * (§15, #220). `null` on either means the backend's own default — that + * backend is anything speaking the shape, `llama.cpp` included, so both + * are settings rather than deployment facts. Neither is a secret; the API + * key stays in the environment. + */ + openai_base_url: string | null; + openai_model: string | null; /** Engines this binary compiled in — `translation_engine` is always one of these, or null. */ available_engines: string[]; } @@ -815,6 +824,20 @@ export function settingsMain(views: { hide: () => void }[], goHome: () => void): const timeout = numberControl(settings.remote_command_timeout_seconds); grid.append(field("remote command timeout (s)", timeout)); + // #220: only meaningful when this build compiled the backend in, and the + // engine list is exactly that fact. Placeholders carry the two things + // the labels cannot: that the base URL is optional, and what a local one + // looks like. + const hasOpenAi = settings.available_engines.includes("openai"); + const openAiBaseUrl = textControl( + settings.openai_base_url ?? "", + "optional — e.g. http://127.0.0.1:8080/v1", + ); + const openAiModel = textControl(settings.openai_model ?? "", "optional — e.g. gpt-4o-mini"); + if (hasOpenAi) { + grid.append(field("openai base url", openAiBaseUrl), field("openai model", openAiModel)); + } + panel.append(grid); if (!hasEngines) { panel.append( @@ -908,6 +931,10 @@ export function settingsMain(views: { hide: () => void }[], goHome: () => void): provider_daily_budgets: providerBudgets, translator_daily_budgets: translatorBudgets, remote_command_timeout_seconds: Number(timeout.value), + // An emptied field is "use the backend's default", which the API + // stores as NULL. + openai_base_url: hasOpenAi ? openAiBaseUrl.value.trim() || null : settings.openai_base_url, + openai_model: hasOpenAi ? openAiModel.value.trim() || null : settings.openai_model, }; void saveSubtitleSettings(input).then((outcome) => { if (outcome.ok) {