diff --git a/docs/chat-chain-changes/2026-11-05-context-fallback.md b/docs/chat-chain-changes/2026-11-05-context-fallback.md new file mode 100644 index 0000000..332d232 --- /dev/null +++ b/docs/chat-chain-changes/2026-11-05-context-fallback.md @@ -0,0 +1,9 @@ +--- +date: 2026-11-05 +pr: pending +feature: Unknown model context fallback +impact: When no model context length can be resolved, the Web UI estimates it as 1000000 tokens instead of 256000. Explicit settings and model metadata retain priority. +--- + +This estimate also affects Web UI compression thresholds for models with no +known context length. It does not change the model provider's actual limit. diff --git a/packages/client/src/i18n/locales/en.ts b/packages/client/src/i18n/locales/en.ts index 5354b9f..8f2ded9 100644 --- a/packages/client/src/i18n/locales/en.ts +++ b/packages/client/src/i18n/locales/en.ts @@ -1415,7 +1415,7 @@ export default { customType: 'Custom', provider: 'Provider', contextLength: 'Context Length', - contextLengthPlaceholder: 'e.g. 256000 (optional)', + contextLengthPlaceholder: 'Optional; estimated as 1000000 when unknown', apiMode: 'API Mode', local: 'Local ({host})', selectProviderRequired: 'Please select a provider', diff --git a/packages/client/src/i18n/locales/zh.ts b/packages/client/src/i18n/locales/zh.ts index 7ecd90a..79b2eed 100644 --- a/packages/client/src/i18n/locales/zh.ts +++ b/packages/client/src/i18n/locales/zh.ts @@ -1415,7 +1415,7 @@ export default { customType: '自定义', provider: 'Provider', contextLength: '上下文长度', - contextLengthPlaceholder: '例如 256000(可选)', + contextLengthPlaceholder: '可选;未知时按 1000000 估算', apiMode: 'API 模式', local: '本地 ({host})', selectProviderRequired: '请选择 Provider', diff --git a/packages/server/src/services/hermes/model-context.ts b/packages/server/src/services/hermes/model-context.ts index 232d252..eadff40 100644 --- a/packages/server/src/services/hermes/model-context.ts +++ b/packages/server/src/services/hermes/model-context.ts @@ -9,7 +9,7 @@ import { getCompatibleCustomProviders } from './custom-providers-compat' const HERMES_BASE = detectHermesHome() const MODELS_DEV_CACHE = resolve(HERMES_BASE, 'models_dev_cache.json') -const DEFAULT_CONTEXT_LENGTH = 256_000 +const DEFAULT_CONTEXT_LENGTH = 1_000_000 export interface ModelContextLengthOptions { profile?: string diff --git a/tests/server/model-context.test.ts b/tests/server/model-context.test.ts index 155d06e..83ed07c 100644 --- a/tests/server/model-context.test.ts +++ b/tests/server/model-context.test.ts @@ -59,6 +59,11 @@ describe('getModelContextLength', () => { homeDir = '' }) + it('uses one million tokens when no model context is available', async () => { + const { getModelContextLength } = await loadModelContext() + expect(getModelContextLength()).toBe(1_000_000) + }) + it('does not borrow a same-named model context from another provider when the configured provider is uncached', async () => { writeConfig(`model:\n default: gpt-5.5\n provider: openai-codex\n`) writeModelsCache({ @@ -71,7 +76,7 @@ describe('getModelContextLength', () => { const { getModelContextLength } = await loadModelContext() - expect(getModelContextLength()).toBe(256_000) + expect(getModelContextLength()).toBe(1_000_000) }) it('does not scan other providers when the configured provider exists without that model', async () => { @@ -91,7 +96,7 @@ describe('getModelContextLength', () => { const { getModelContextLength } = await loadModelContext() - expect(getModelContextLength()).toBe(256_000) + expect(getModelContextLength()).toBe(1_000_000) }) it('uses the configured provider cache entry when the provider matches', async () => { @@ -250,14 +255,14 @@ describe('getModelContextLength', () => { writeModelsCache({ deepseek: { models: { - 'deepseek-v4-pro': { limit: { context: 1_000_000 } }, + 'deepseek-v4-pro': { limit: { context: 1_050_000 } }, }, }, }) const { getModelContextLength } = await loadModelContext() - expect(getModelContextLength()).toBe(256_000) + expect(getModelContextLength()).toBe(1_000_000) }) it('does not trust custom:name alone when the matched custom provider entry points at an unknown proxy url', async () => { @@ -265,14 +270,14 @@ describe('getModelContextLength', () => { writeModelsCache({ deepseek: { models: { - 'deepseek-v4-pro': { limit: { context: 1_000_000 } }, + 'deepseek-v4-pro': { limit: { context: 1_050_000 } }, }, }, }) const { getModelContextLength } = await loadModelContext() - expect(getModelContextLength()).toBe(256_000) + expect(getModelContextLength()).toBe(1_000_000) }) it('does not fall through to a unique global match after a resolved custom:name provider misses in its scoped cache provider', async () => { @@ -285,14 +290,14 @@ describe('getModelContextLength', () => { }, deepseek: { models: { - 'deepseek-v4-pro': { limit: { context: 1_000_000 } }, + 'deepseek-v4-pro': { limit: { context: 1_050_000 } }, }, }, }) const { getModelContextLength } = await loadModelContext() - expect(getModelContextLength()).toBe(256_000) + expect(getModelContextLength()).toBe(1_000_000) }) it('allows a unique global model-name fallback for unresolved custom providers', async () => { @@ -330,14 +335,14 @@ describe('getModelContextLength', () => { writeModelsCache({ vercel: { models: { - 'openai/gpt-5': { limit: { context: 1_000_000 } }, + 'openai/gpt-5': { limit: { context: 1_050_000 } }, }, }, }) const { getModelContextLength } = await loadModelContext() - expect(getModelContextLength()).toBe(256_000) + expect(getModelContextLength()).toBe(1_000_000) }) it('does not guess across multiple cache providers when a custom provider remains unresolved', async () => { @@ -345,7 +350,7 @@ describe('getModelContextLength', () => { writeModelsCache({ deepseek: { models: { - 'shared-model': { limit: { context: 1_000_000 } }, + 'shared-model': { limit: { context: 1_050_000 } }, }, }, openai: { @@ -357,6 +362,6 @@ describe('getModelContextLength', () => { const { getModelContextLength } = await loadModelContext() - expect(getModelContextLength()).toBe(256_000) + expect(getModelContextLength()).toBe(1_000_000) }) })