From f0adf01594bc8363bb0022184919670fea7228b2 Mon Sep 17 00:00:00 2001 From: zhouxun Date: Fri, 7 Aug 2026 16:06:17 +0800 Subject: [PATCH 1/3] feat(models): add context window controls (#1073) --- .../docs/reference/configuration/providers.md | 4 +- gui/src/i18n/de.ts | 10 + gui/src/i18n/en.ts | 10 + gui/src/i18n/ja.ts | 10 + gui/src/i18n/ko.ts | 10 + gui/src/i18n/ru.ts | 10 + gui/src/i18n/zh.ts | 10 + gui/src/models-groups.ts | 6 + gui/src/pages/Models.tsx | 180 ++++++++++++++++++ gui/tests/models-empty-provider.test.tsx | 45 +++++ gui/tests/models-provider-head.test.ts | 11 ++ src/server/management/provider-routes.ts | 38 ++++ src/types.ts | 4 +- tests/management-provider-validation.test.ts | 86 +++++++++ 14 files changed, 430 insertions(+), 4 deletions(-) diff --git a/docs-site/src/content/docs/reference/configuration/providers.md b/docs-site/src/content/docs/reference/configuration/providers.md index 4849f1d3a..4289d5aec 100644 --- a/docs-site/src/content/docs/reference/configuration/providers.md +++ b/docs-site/src/content/docs/reference/configuration/providers.md @@ -68,8 +68,8 @@ differing backup and rewrites known legacy namespaced selected ids to bare ids. | `models?` | `string[]` | Seed/fallback model list. With `liveModels: false`, these are the only discovered models. | | `liveModels?` | `boolean` | Fetch the live catalog on start/sync (default `true`). Custom providers use `${baseUrl}/models`; built-ins may use a registry URL and filter. | | `selectedModels?` | `string[]` | Catalog allowlist after discovery. Non-empty exposes only those ids; empty or omitted exposes all discovered models. | -| `contextWindow?` | `number` | Provider-wide Codex-visible context cap. Smaller live metadata is retained. | -| `modelContextWindows?` | `Record` | Per-model context caps. These override `contextWindow` and never raise smaller live metadata. | +| `contextWindow?` | `number` | Provider-wide context fallback when upstream metadata is absent; otherwise a cap that retains smaller live metadata. The Models dashboard exposes this separately from `providerContextCaps`. | +| `modelContextWindows?` | `Record` | Per-model context fallbacks/caps. These override `contextWindow`: an unknown window uses the configured value, while smaller live metadata remains authoritative. | | `modelInputModalities?` | `Record` | Per-model input hints such as `["text"]` or `["text", "image"]`. | | `modelMaxInputTokens?` | `Record` | Positive per-model max input limits used for catalog auto-compaction hints. | | `defaultMaxOutputTokens?` | `number` | Provider-wide `openai-chat` fallback when the client omits `max_output_tokens`. | diff --git a/gui/src/i18n/de.ts b/gui/src/i18n/de.ts index bc23cf5a2..dba0a8747 100644 --- a/gui/src/i18n/de.ts +++ b/gui/src/i18n/de.ts @@ -465,6 +465,16 @@ export const de: Record = { "models.v2ThreadsInvalid": "Thread-Limit muss eine ganze Zahl >= 1 sein", "models.v2ThreadsApply": "Anwenden", "models.capValue": "Limit {value}", + "models.contextSettings": "Kontextfenster", + "models.contextSettingsTitle": "Kontextfenster — {provider}", + "models.contextDefault": "Anbieterstandard", + "models.contextModel": "Modell", + "models.contextModelOverride": "Modellüberschreibung", + "models.contextHint": "Wird verwendet, wenn Upstream-Metadaten fehlen; andernfalls begrenzt der Wert ein größeres gemeldetes Fenster. Leer lassen für automatische Erkennung.", + "models.contextAutomatic": "Automatische Erkennung", + "models.contextSaved": "Kontextfenster aktualisiert — gilt ab der nächsten Codex-Runde.", + "models.contextSaveFailed": "Kontextfenster konnten nicht gespeichert werden", + "models.contextInvalid": "Kontextfenster müssen positive ganze Zahlen sein", "models.contextCappedValue": "{value}-Limit", "models.setAll": "Alle setzen", "models.setAllHint": "Wendet das {value}-Kontext-Limit auf alle gerouteten Anbieter an. Native Anbieter bleiben unberührt.", diff --git a/gui/src/i18n/en.ts b/gui/src/i18n/en.ts index c63a33495..2880ef7e7 100644 --- a/gui/src/i18n/en.ts +++ b/gui/src/i18n/en.ts @@ -484,6 +484,16 @@ export const en = { "models.v2ThreadsInvalid": "Thread limit must be an integer >= 1", "models.v2ThreadsApply": "Apply", "models.capValue": "Cap {value}", + "models.contextSettings": "Context windows", + "models.contextSettingsTitle": "Context windows — {provider}", + "models.contextDefault": "Provider default", + "models.contextModel": "Model", + "models.contextModelOverride": "Model override", + "models.contextHint": "Used when upstream metadata is missing; otherwise limits a larger reported window. Leave blank for automatic discovery.", + "models.contextAutomatic": "Automatic discovery", + "models.contextSaved": "Context windows updated — takes effect on the next Codex turn.", + "models.contextSaveFailed": "Failed to save context windows", + "models.contextInvalid": "Context windows must be positive whole numbers", "models.contextCappedValue": "{value} cap", "models.setAll": "Set all", "models.setAllHint": "Apply the {value} context cap to every routed provider. Native providers are unaffected.", diff --git a/gui/src/i18n/ja.ts b/gui/src/i18n/ja.ts index 8bef10acc..538f2b700 100644 --- a/gui/src/i18n/ja.ts +++ b/gui/src/i18n/ja.ts @@ -473,6 +473,16 @@ export const ja: Record = { "models.v2ThreadsInvalid": "スレッド上限は 1 以上の整数にしてください", "models.v2ThreadsApply": "適用", "models.capValue": "上限 {value}", + "models.contextSettings": "コンテキストウィンドウ", + "models.contextSettingsTitle": "コンテキストウィンドウ — {provider}", + "models.contextDefault": "プロバイダーのデフォルト", + "models.contextModel": "モデル", + "models.contextModelOverride": "モデル別の上書き", + "models.contextHint": "上流メタデータがない場合に使われ、メタデータがある場合は報告値の上限になります。自動検出に戻すには空欄にします。", + "models.contextAutomatic": "自動検出", + "models.contextSaved": "コンテキストウィンドウを更新しました — 次回の Codex ターンから有効です。", + "models.contextSaveFailed": "コンテキストウィンドウを保存できませんでした", + "models.contextInvalid": "コンテキストウィンドウは正の整数で指定してください", "models.contextCappedValue": "{value} 上限", "models.setAll": "すべて設定", "models.setAllHint": "{value} のコンテキスト上限をすべてのルーティング済みプロバイダーに適用します。ネイティブプロバイダーには影響しません。", diff --git a/gui/src/i18n/ko.ts b/gui/src/i18n/ko.ts index 4112cbd79..2fa80b4b3 100644 --- a/gui/src/i18n/ko.ts +++ b/gui/src/i18n/ko.ts @@ -476,6 +476,16 @@ export const ko: Record = { "models.v2ThreadsInvalid": "스레드 한도는 1 이상 정수여야 합니다", "models.v2ThreadsApply": "적용", "models.capValue": "{value} 제한", + "models.contextSettings": "컨텍스트 윈도우", + "models.contextSettingsTitle": "컨텍스트 윈도우 — {provider}", + "models.contextDefault": "프로바이더 기본값", + "models.contextModel": "모델", + "models.contextModelOverride": "모델별 재정의", + "models.contextHint": "업스트림 메타데이터가 없을 때 사용하며, 메타데이터가 있으면 더 큰 보고값의 상한으로 적용합니다. 자동 검색을 사용하려면 비워 두세요.", + "models.contextAutomatic": "자동 검색", + "models.contextSaved": "컨텍스트 윈도우가 업데이트되었습니다 — 다음 Codex 턴부터 적용됩니다.", + "models.contextSaveFailed": "컨텍스트 윈도우를 저장하지 못했습니다", + "models.contextInvalid": "컨텍스트 윈도우는 양의 정수여야 합니다", "models.contextCappedValue": "{value} 제한", "models.setAll": "전체 적용", "models.setAllHint": "{value} 컨텍스트 상한을 라우팅된 모든 프로바이더에 적용합니다. 네이티브 프로바이더는 영향을 받지 않습니다.", diff --git a/gui/src/i18n/ru.ts b/gui/src/i18n/ru.ts index d38a236bc..618d00cb6 100644 --- a/gui/src/i18n/ru.ts +++ b/gui/src/i18n/ru.ts @@ -478,6 +478,16 @@ export const ru: Record = { "models.v2ThreadsInvalid": "Лимит потоков должен быть целым числом >= 1", "models.v2ThreadsApply": "Применить", "models.capValue": "Лимит {value}", + "models.contextSettings": "Контекстные окна", + "models.contextSettingsTitle": "Контекстные окна — {provider}", + "models.contextDefault": "Значение провайдера", + "models.contextModel": "Модель", + "models.contextModelOverride": "Переопределение модели", + "models.contextHint": "Используется, если вышестоящие метаданные отсутствуют; иначе ограничивает большее заявленное окно. Оставьте поле пустым для автоматического определения.", + "models.contextAutomatic": "Автоматическое определение", + "models.contextSaved": "Контекстные окна обновлены — изменения вступят в силу на следующем ходе Codex.", + "models.contextSaveFailed": "Не удалось сохранить контекстные окна", + "models.contextInvalid": "Контекстные окна должны быть положительными целыми числами", "models.contextCappedValue": "Лимит {value}", "models.setAll": "Применить ко всем", "models.setAllHint": "Применяет лимит контекста {value} ко всем маршрутизируемым провайдерам. Нативные провайдеры не затрагиваются.", diff --git a/gui/src/i18n/zh.ts b/gui/src/i18n/zh.ts index ba9ca825e..0c2a8588a 100644 --- a/gui/src/i18n/zh.ts +++ b/gui/src/i18n/zh.ts @@ -473,6 +473,16 @@ export const zh: Record = { "models.v2ThreadsInvalid": "线程上限必须为 >= 1 的整数", "models.v2ThreadsApply": "应用", "models.capValue": "限制 {value}", + "models.contextSettings": "上下文窗口", + "models.contextSettingsTitle": "上下文窗口 — {provider}", + "models.contextDefault": "提供方默认值", + "models.contextModel": "模型", + "models.contextModelOverride": "模型覆盖值", + "models.contextHint": "上游缺少元数据时使用该值;上游已有元数据时,它只限制更大的报告值。留空则恢复自动发现。", + "models.contextAutomatic": "自动发现", + "models.contextSaved": "上下文窗口已更新 — 将在下一个 Codex 回合生效。", + "models.contextSaveFailed": "保存上下文窗口失败", + "models.contextInvalid": "上下文窗口必须为正整数", "models.contextCappedValue": "{value} 限制", "models.setAll": "全部设置", "models.setAllHint": "将 {value} 上下文上限应用到所有已路由的提供方。原生提供方不受影响。", diff --git a/gui/src/models-groups.ts b/gui/src/models-groups.ts index 193ae1e6a..e6cce7f5c 100644 --- a/gui/src/models-groups.ts +++ b/gui/src/models-groups.ts @@ -14,6 +14,8 @@ export interface ConfiguredProviderSummary { disabled?: boolean; liveModels?: boolean; models?: string[]; + contextWindow?: number; + modelContextWindows?: Record; discovery?: ProviderDiscoverySummary; } @@ -23,6 +25,8 @@ export interface ProviderModelGroup { native: boolean; liveModels: boolean; configuredModels: string[]; + contextWindow?: number; + modelContextWindows?: Record; discovery?: ProviderDiscoverySummary; } @@ -56,6 +60,8 @@ export function buildProviderModelGroups 0 && providerRows.every(row => row.native === true), liveModels: configured?.liveModels !== false, configuredModels: configured?.models ?? [], + contextWindow: configured?.contextWindow, + modelContextWindows: configured?.modelContextWindows, discovery: configured?.discovery, }; }) diff --git a/gui/src/pages/Models.tsx b/gui/src/pages/Models.tsx index 3c94b66a8..9aabdda91 100644 --- a/gui/src/pages/Models.tsx +++ b/gui/src/pages/Models.tsx @@ -174,6 +174,13 @@ export default function Models({ apiBase }: { apiBase: string }) { const [customFormModalities, setCustomFormModalities] = useState(["text"]); const [customSaving, setCustomSaving] = useState(false); const [customError, setCustomError] = useState(""); + const [contextModalProvider, setContextModalProvider] = useState(null); + const [contextModalModels, setContextModalModels] = useState([]); + const [contextModelId, setContextModelId] = useState(""); + const [contextDefaultDraft, setContextDefaultDraft] = useState(""); + const [contextModelDraft, setContextModelDraft] = useState(""); + const [contextSaving, setContextSaving] = useState(false); + const [contextError, setContextError] = useState(""); const [hoveredModel, setHoveredModel] = useState<{ namespaced: string; rect: DOMRect } | null>(null); const hoverTimerRef = useRef | null>(null); const [shadowCall, setShadowCall] = useState(null); @@ -342,6 +349,82 @@ export default function Models({ apiBase }: { apiBase: string }) { */ const catalogCountReady = models.length > 0 || catalogState.data !== undefined; + const openContextSettings = (group: ProviderModelGroup) => { + const modelIds = [...new Set([ + ...group.rows.map(model => model.id), + ...group.configuredModels, + ])].sort(); + const modelId = modelIds[0] ?? ""; + setContextModalProvider(group.provider); + setContextModalModels(modelIds); + setContextModelId(modelId); + setContextDefaultDraft(group.contextWindow ? String(group.contextWindow) : ""); + setContextModelDraft(modelId && group.modelContextWindows?.[modelId] + ? String(group.modelContextWindows[modelId]) + : ""); + setContextError(""); + }; + + const selectContextModel = (modelId: string) => { + const group = groups.find(candidate => candidate.provider === contextModalProvider); + setContextModelId(modelId); + setContextModelDraft(group?.modelContextWindows?.[modelId] + ? String(group.modelContextWindows[modelId]) + : ""); + }; + + const parseContextWindowDraft = (raw: string): number | null | undefined => { + const normalized = raw.replace(/[_,\s]/g, ""); + if (!normalized) return null; + const value = Number(normalized); + return Number.isFinite(value) && Number.isInteger(value) && value > 0 + ? value + : undefined; + }; + + const saveContextSettings = async () => { + if (!contextModalProvider) return; + const providerWindow = parseContextWindowDraft(contextDefaultDraft); + const modelWindow = parseContextWindowDraft(contextModelDraft); + if (providerWindow === undefined || modelWindow === undefined) { + setContextError(t("models.contextInvalid")); + return; + } + const group = groups.find(candidate => candidate.provider === contextModalProvider); + if (!group) { + setContextError(t("models.contextSaveFailed")); + return; + } + + setContextSaving(true); + setContextError(""); + try { + const body: Record = { contextWindow: providerWindow }; + if (contextModelId) { + body.modelContextWindows = { [contextModelId]: modelWindow }; + } + const response = await fetch( + `${apiBase}/api/providers?name=${encodeURIComponent(contextModalProvider)}`, + { + method: "PATCH", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(body), + }, + ); + await readJsonOrThrow(response, t("models.contextSaveFailed")); + const refreshed = await load(true); + if (!refreshed) { + setContextError(t("models.loadFail")); + return; + } + setContextModalProvider(null); + publishFeedback(true, t("models.contextSaved")); + } catch (error) { + setContextError(error instanceof Error ? error.message : t("models.contextSaveFailed")); + } finally { + setContextSaving(false); + } + }; // One-shot default collapse. It stays an effect on `groups` so CACHED groups collapse // immediately on first paint, even when revalidation is slow or fails; moving it into @@ -783,6 +866,14 @@ export default function Models({ apiBase }: { apiBase: string }) { {t("models.active", { active: activeCount, total: rows.length })}
+ {!isNative && ( + + )} {!isNative && ( +
+ + {contextError && {contextError}} +

{t("models.contextHint")}

+ +
+ + + {contextModalModels.length > 0 && ( + <> +
+ {t("models.contextModel")} + setContextModelDraft(event.target.value)} + disabled={contextSaving} + placeholder={t("models.contextAutomatic")} + /> + + + )} +
+ +
+ + +
+
+ + )} + {customModalOpen && (
; enabled: boolean }> = []; + const contextBodies: Array<{ + contextWindow: number | null; + modelContextWindows: Record; + }> = []; + let providerContextWindow: number | undefined = 256_000; + let providerModelContextWindows: Record = { "claude-opus": 64_000 }; let failNext = false; let failCatalog = false; let modelFetches = 0; @@ -142,9 +148,22 @@ test("Models page combines final visibility, atomic actions, discovery status, a name: provider, liveModels: true, models: ids, + contextWindow: providerContextWindow, + modelContextWindows: providerModelContextWindows, discovery: { status: "failed", reason: "http", httpStatus: 401 }, }]); } + if (url.includes("/api/providers?name=") && init?.method === "PATCH") { + const body = JSON.parse(String(init.body)) as (typeof contextBodies)[number]; + contextBodies.push(body); + if (body.contextWindow === null) providerContextWindow = undefined; + else if (typeof body.contextWindow === "number") providerContextWindow = body.contextWindow; + for (const [model, value] of Object.entries(body.modelContextWindows ?? {})) { + if (value === null) delete providerModelContextWindows[model]; + else providerModelContextWindows = { ...providerModelContextWindows, [model]: value }; + } + return Response.json({ success: true }); + } if (url.endsWith("/api/selected-models")) return Response.json({ selected: { [provider]: selected }, available: { [provider]: ids } }); if (url.endsWith("/api/provider-context-caps")) return Response.json({ caps: {} }); if (url.endsWith("/api/combos")) return Response.json({ combos: [] }); @@ -196,6 +215,32 @@ test("Models page combines final visibility, atomic actions, discovery status, a expect(container.querySelector(".badge.badge-amber")?.textContent).toContain("Discovery failed"); expect(container.textContent).not.toContain("Not selected"); + await act(async () => buttonText("Context windows").click()); + const contextDialog = container.querySelector('[role="dialog"][aria-label="Context windows"]')!; + const contextInputs = contextDialog.querySelectorAll("input"); + expect([...contextInputs].map(input => input.value)).toEqual(["256000", "64000"]); + await act(async () => { + const setValue = Object.getOwnPropertyDescriptor( + testWindow.HTMLInputElement.prototype, + "value", + )!.set!; + setValue.call(contextInputs[0]!, "350000"); + contextInputs[0]!.dispatchEvent(new testWindow.Event("input", { bubbles: true })); + setValue.call(contextInputs[1]!, "100000"); + contextInputs[1]!.dispatchEvent(new testWindow.Event("input", { bubbles: true })); + }); + const applyContext = [...contextDialog.querySelectorAll("button")] + .find(button => button.textContent === "Apply")!; + await act(async () => { + applyContext.click(); + await new Promise(resolve => testWindow.setTimeout(resolve, 0)); + }); + expect(contextBodies.at(-1)).toEqual({ + contextWindow: 350_000, + modelContextWindows: { "claude-opus": 100_000 }, + }); + expect(container.querySelector('[role="dialog"][aria-label="Context windows"]')).toBeNull(); + await act(async () => container.querySelector('button.select-trigger[aria-label="Shadow Call Intercept"]')?.click()); // The workspace Select portals its listbox to document.body, so the options are not inside // `container`. Query the document instead of the mount node. diff --git a/gui/tests/models-provider-head.test.ts b/gui/tests/models-provider-head.test.ts index 101906f17..83d397f0c 100644 --- a/gui/tests/models-provider-head.test.ts +++ b/gui/tests/models-provider-head.test.ts @@ -30,3 +30,14 @@ test("Models workspace stacks via content-width container query before mobile dr // Mobile media rule retained for drawer layouts. expect(css).toContain("@media (max-width: 768px)"); }); + +test("Models exposes provider and per-model context-window controls (#1073)", async () => { + const page = await Bun.file(new URL("../src/pages/Models.tsx", import.meta.url)).text(); + const groups = await Bun.file(new URL("../src/models-groups.ts", import.meta.url)).text(); + + expect(groups).toContain("contextWindow?: number"); + expect(groups).toContain("modelContextWindows?: Record"); + expect(page).toContain('t("models.contextSettings")'); + expect(page).toContain("modelContextWindows"); + expect(page).toMatch(/\/api\/providers\?name=.*method:\s*"PATCH"/s); +}); diff --git a/src/server/management/provider-routes.ts b/src/server/management/provider-routes.ts index 85c5a9400..5582e447e 100644 --- a/src/server/management/provider-routes.ts +++ b/src/server/management/provider-routes.ts @@ -165,6 +165,42 @@ function applyProviderPatchFields( next.liveModels = rawBody.liveModels; touched = true; } + // The Models page edits the catalog hints in place; keep them on the existing + // provider mutation path so validation, cache invalidation, and convergence stay unified (#1073). + if (Object.hasOwn(rawBody, "contextWindow")) { + const value = rawBody.contextWindow; + if (value === null) { + delete next.contextWindow; + } else if (typeof value === "number" && Number.isFinite(value) && Number.isInteger(value) && value > 0) { + next.contextWindow = value; + } else { + return { error: "contextWindow must be a positive finite integer or null" }; + } + touched = true; + } + if (Object.hasOwn(rawBody, "modelContextWindows")) { + const value = rawBody.modelContextWindows; + if (value === null) { + delete next.modelContextWindows; + } else { + if (!isPlainRecord(value)) return { error: "modelContextWindows must be a plain object or null" }; + const windows: Record = { ...(next.modelContextWindows ?? {}) }; + for (const [model, window] of Object.entries(value)) { + if (!model.trim()) return { error: "modelContextWindows keys must be nonblank model ids" }; + if (window === null) { + delete windows[model]; + continue; + } + if (typeof window !== "number" || !Number.isFinite(window) || !Number.isInteger(window) || window <= 0) { + return { error: "modelContextWindows values must be positive finite integers or null" }; + } + windows[model] = window; + } + if (Object.keys(windows).length > 0) next.modelContextWindows = windows; + else delete next.modelContextWindows; + } + touched = true; + } // headers is the one object-valued field in the mask. PATCH semantics merge it // shallowly into the existing block so a single fingerprint header can be added @@ -249,6 +285,8 @@ export async function handleProviderRoutes(ctx: ManagementContext): Promise; /** Model-specific Codex catalog input modalities, e.g. ["text"] or ["text", "image"]. */ modelInputModalities?: Record; diff --git a/tests/management-provider-validation.test.ts b/tests/management-provider-validation.test.ts index e850d3f84..4f6396df6 100644 --- a/tests/management-provider-validation.test.ts +++ b/tests/management-provider-validation.test.ts @@ -1973,6 +1973,92 @@ describe("provider management validation", () => { // Unknown-only bodies are rejected. expect((await patch("extra", { bogus: 1 }))?.status).toBe(400); }); + + test("provider management exposes and persists context-window hints for Models GUI (#1073)", async () => { + if (existsSync(TEST_DIR)) rmSync(TEST_DIR, { recursive: true }); + mkdirSync(TEST_DIR, { recursive: true }); + process.env.OPENCODEX_HOME = TEST_DIR; + const liveConfig: OcxConfig = { + port: 0, + hostname: "127.0.0.1", + defaultProvider: "openai", + openaiProviderTierVersion: 2, + providers: { + openai: { ...canonicalDirect }, + relay: { + adapter: "openai-chat", + baseUrl: "https://relay.example.test/v1", + apiKey: "sk-existing", + models: ["wide", "narrow"], + contextWindow: 256_000, + modelContextWindows: { narrow: 64_000 }, + }, + }, + }; + saveConfig(liveConfig); + + const request = async (method: "GET" | "PATCH", body?: unknown) => { + const req = new Request("http://127.0.0.1/api/providers?name=relay", { + method, + headers: body === undefined ? undefined : { "content-type": "application/json" }, + body: body === undefined ? undefined : JSON.stringify(body), + }); + return handleManagementAPI(req, new URL(req.url), liveConfig, { + createManagementConvergeCodex: catalogConvergenceFactory(() => {}), + }); + }; + + const listed = await request("GET"); + expect(listed?.status).toBe(200); + const rows = await listed!.json() as Array<{ + name: string; + contextWindow?: number; + modelContextWindows?: Record; + }>; + expect(rows.find(row => row.name === "relay")).toMatchObject({ + contextWindow: 256_000, + modelContextWindows: { narrow: 64_000 }, + }); + + const updated = await request("PATCH", { + contextWindow: 350_000, + modelContextWindows: { wide: 350_000 }, + }); + expect(updated?.status).toBe(200); + expect(liveConfig.providers.relay).toMatchObject({ + contextWindow: 350_000, + modelContextWindows: { wide: 350_000, narrow: 64_000 }, + }); + expect(loadConfig().providers.relay).toMatchObject({ + contextWindow: 350_000, + modelContextWindows: { wide: 350_000, narrow: 64_000 }, + }); + + for (const invalid of [ + { contextWindow: 0 }, + { contextWindow: 1.5 }, + { modelContextWindows: { "": 100_000 } }, + { modelContextWindows: { wide: -1 } }, + ]) { + expect((await request("PATCH", invalid))?.status).toBe(400); + } + expect(liveConfig.providers.relay).toMatchObject({ + contextWindow: 350_000, + modelContextWindows: { wide: 350_000, narrow: 64_000 }, + }); + + expect((await request("PATCH", { modelContextWindows: { wide: null } }))?.status).toBe(200); + expect(liveConfig.providers.relay.modelContextWindows).toEqual({ narrow: 64_000 }); + + const cleared = await request("PATCH", { + contextWindow: null, + modelContextWindows: null, + }); + expect(cleared?.status).toBe(200); + expect(liveConfig.providers.relay.contextWindow).toBeUndefined(); + expect(liveConfig.providers.relay.modelContextWindows).toBeUndefined(); + }); + test("provider PATCH manages custom headers with merge and clear semantics", async () => { if (existsSync(TEST_DIR)) rmSync(TEST_DIR, { recursive: true }); mkdirSync(TEST_DIR, { recursive: true }); From 14b3e17b47a1e0713f70721d0b6651eab385dfe6 Mon Sep 17 00:00:00 2001 From: zhouxun Date: Fri, 7 Aug 2026 16:43:56 +0800 Subject: [PATCH 2/3] fix(models): space context window fields (#1073) --- .../1073-context-window-controls.jpg | Bin 0 -> 83175 bytes gui/src/pages/Models.tsx | 2 +- gui/src/styles-models-workspace.css | 6 ++++++ gui/tests/models-provider-head.test.ts | 4 ++++ 4 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 docs-site/public/pr-screenshots/1073-context-window-controls.jpg diff --git a/docs-site/public/pr-screenshots/1073-context-window-controls.jpg b/docs-site/public/pr-screenshots/1073-context-window-controls.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fe157cf923631d0b7334bc67cd4245f5db04d557 GIT binary patch literal 83175 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∨L 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

{t("models.contextHint")}

-
+