diff --git a/README.md b/README.md index 05dac442..2bbf123f 100644 --- a/README.md +++ b/README.md @@ -329,7 +329,7 @@ The example below uses a generic OpenAI-compatible `custom` provider so the comp "provider": "custom", "model": "model-id-from-your-provider", "maxTokens": 8192, - "contextWindowTokens": 65536, + "contextWindowTokens": 200000, "temperature": 0.1 } }, diff --git a/nanobot/cli/onboard.py b/nanobot/cli/onboard.py index 19aefa12..91c6a81a 100644 --- a/nanobot/cli/onboard.py +++ b/nanobot/cli/onboard.py @@ -993,7 +993,7 @@ def _try_auto_fill_context_window(model: BaseModel, new_model_name: str) -> None current_context = getattr(model, "context_window_tokens", None) - # Check if current value is the default (65536) + # Check if current value is the default # We only auto-fill if the user hasn't changed it from default from nanobot.config.schema import AgentDefaults diff --git a/nanobot/config/schema.py b/nanobot/config/schema.py index 9980d309..3994c273 100644 --- a/nanobot/config/schema.py +++ b/nanobot/config/schema.py @@ -100,7 +100,7 @@ class ModelPresetConfig(Base): model: str provider: str = "auto" max_tokens: int = 8192 - context_window_tokens: int = 65_536 + context_window_tokens: int = 200_000 temperature: float = 0.1 reasoning_effort: str | None = None @@ -123,7 +123,7 @@ class AgentDefaults(Base): "auto" # Provider name (e.g. "anthropic", "openrouter") or "auto" for auto-detection ) max_tokens: int = 8192 - context_window_tokens: int = 65_536 + context_window_tokens: int = 200_000 context_block_limit: int | None = None temperature: float = 0.1 fallback_models: list[FallbackCandidate] = Field(default_factory=list) diff --git a/nanobot/skills/my/references/examples.md b/nanobot/skills/my/references/examples.md index aecd6881..924ca2d0 100644 --- a/nanobot/skills/my/references/examples.md +++ b/nanobot/skills/my/references/examples.md @@ -33,9 +33,9 @@ Concrete scenarios showing when and how to use the my tool effectively. ### Large codebase analysis ``` → my(action="check") - → context_window_tokens: 65536 + → context_window_tokens: 200000 → my(action="set", key="context_window_tokens", value=131072) - → "Set context_window_tokens = 131072 (was 65536)" + → "Set context_window_tokens = 131072 (was 200000)" → "I've expanded my context window to handle this large codebase." ``` diff --git a/nanobot/webui/settings_api.py b/nanobot/webui/settings_api.py index fd109d92..78294779 100644 --- a/nanobot/webui/settings_api.py +++ b/nanobot/webui/settings_api.py @@ -106,7 +106,7 @@ _IMAGE_GENERATION_ASPECT_RATIOS = { "2:3", "21:9", } -_CONTEXT_WINDOW_TOKEN_OPTIONS = {65_536, 262_144} +_CONTEXT_WINDOW_TOKEN_OPTIONS = {65_536, 200_000, 262_144} _MODEL_CONFIGURATION_SLUG_RE = re.compile(r"[^a-z0-9_-]+") _ENV_REF_RE = re.compile(r"\$\{([A-Za-z_][A-Za-z0-9_]*)\}") @@ -606,7 +606,7 @@ def _parse_context_window_tokens(value: str | None) -> int | None: except ValueError: raise WebUISettingsError("context_window_tokens must be an integer") from None if parsed not in _CONTEXT_WINDOW_TOKEN_OPTIONS: - raise WebUISettingsError("context_window_tokens must be 65536 or 262144") + raise WebUISettingsError("context_window_tokens must be 65536, 200000, or 262144") return parsed diff --git a/tests/cli/test_restart_command.py b/tests/cli/test_restart_command.py index d0ff8ca9..20579315 100644 --- a/tests/cli/test_restart_command.py +++ b/tests/cli/test_restart_command.py @@ -183,7 +183,7 @@ class TestRestartCommand: assert response is not None assert "Model: test-model" in response.content assert "Tokens: 0 in / 0 out" in response.content - assert "Context: 20k/65k (31% of input budget)" in response.content + assert "Context: 20k/200k (10% of input budget)" in response.content assert "Session: 3 messages" in response.content assert "Uptime: 2m 5s" in response.content assert "Tasks: 0 active" in response.content @@ -256,7 +256,7 @@ class TestRestartCommand: assert response is not None assert "Tokens: 1200 in / 34 out" in response.content - assert "Context: 1k/65k (1% of input budget)" in response.content + assert "Context: 1k/200k (0% of input budget)" in response.content assert "Tasks: 0 active" in response.content @pytest.mark.asyncio diff --git a/tests/config/test_config_migration.py b/tests/config/test_config_migration.py index 1fd68b68..1183887f 100644 --- a/tests/config/test_config_migration.py +++ b/tests/config/test_config_migration.py @@ -34,7 +34,7 @@ def test_load_config_keeps_max_tokens_and_ignores_legacy_memory_window(tmp_path) config = load_config(config_path) assert config.agents.defaults.max_tokens == 1234 - assert config.agents.defaults.context_window_tokens == 65_536 + assert config.agents.defaults.context_window_tokens == 200_000 assert not hasattr(config.agents.defaults, "memory_window") @@ -60,7 +60,7 @@ def test_save_config_writes_context_window_tokens_but_not_memory_window(tmp_path defaults = saved["agents"]["defaults"] assert defaults["maxTokens"] == 2222 - assert defaults["contextWindowTokens"] == 65_536 + assert defaults["contextWindowTokens"] == 200_000 assert "memoryWindow" not in defaults @@ -85,6 +85,7 @@ def test_onboard_does_not_crash_with_legacy_memory_window(tmp_path, monkeypatch) monkeypatch.setattr("nanobot.cli.commands.get_workspace_path", lambda _workspace=None: workspace) from typer.testing import CliRunner + from nanobot.cli.commands import app runner = CliRunner() result = runner.invoke(app, ["onboard"], input="n\n") @@ -131,6 +132,7 @@ def test_onboard_refresh_backfills_missing_channel_fields(tmp_path, monkeypatch) ) from typer.testing import CliRunner + from nanobot.cli.commands import app runner = CliRunner() result = runner.invoke(app, ["onboard"], input="n\n") diff --git a/tests/webui/test_settings_api.py b/tests/webui/test_settings_api.py index c34715ea..4a41328f 100644 --- a/tests/webui/test_settings_api.py +++ b/tests/webui/test_settings_api.py @@ -233,11 +233,11 @@ def test_update_agent_settings_accepts_context_window_options( save_config(config, config_path) monkeypatch.setattr("nanobot.config.loader._current_config_path", config_path) - payload = update_agent_settings({"context_window_tokens": ["262144"]}) + payload = update_agent_settings({"context_window_tokens": ["200000"]}) - assert payload["agent"]["context_window_tokens"] == 262144 + assert payload["agent"]["context_window_tokens"] == 200000 saved = load_config(config_path) - assert saved.agents.defaults.context_window_tokens == 262144 + assert saved.agents.defaults.context_window_tokens == 200000 def test_update_model_configuration_accepts_context_window_options( @@ -274,7 +274,10 @@ def test_update_context_window_rejects_unknown_values( save_config(Config(), config_path) monkeypatch.setattr("nanobot.config.loader._current_config_path", config_path) - with pytest.raises(WebUISettingsError, match="context_window_tokens must be 65536 or 262144"): + with pytest.raises( + WebUISettingsError, + match="context_window_tokens must be 65536, 200000, or 262144", + ): update_agent_settings({"context_window_tokens": ["128000"]}) diff --git a/webui/src/components/settings/SettingsView.tsx b/webui/src/components/settings/SettingsView.tsx index d25a32d9..87e75dcb 100644 --- a/webui/src/components/settings/SettingsView.tsx +++ b/webui/src/components/settings/SettingsView.tsx @@ -195,7 +195,7 @@ type ProviderApiType = "auto" | "chat_completions" | "responses"; type ProviderForm = { apiKey: string; apiBase: string; apiType: ProviderApiType }; type CustomMcpTransport = "stdio" | "streamableHttp" | "sse"; -const CONTEXT_WINDOW_TOKEN_OPTIONS = [65_536, 262_144] as const; +const CONTEXT_WINDOW_TOKEN_OPTIONS = [65_536, 200_000, 262_144] as const; const DEFERRED_MODEL_LIST_PROVIDERS = new Set([ "aihubmix", "atomic_chat", @@ -335,7 +335,7 @@ function defaultPreset(payload: SettingsPayload): SettingsPayload["model_presets } function normalizeContextWindowTokens(value: number | null | undefined): number { - return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 65_536; + return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 200_000; } function editableDefaultProvider(payload: SettingsPayload): string { @@ -372,7 +372,7 @@ const DEFAULT_AGENT_SETTINGS_DRAFT: AgentSettingsDraft = { provider: "", modelPreset: "default", presetLabel: "Default", - contextWindowTokens: 65_536, + contextWindowTokens: 200_000, timezone: "UTC", botName: "nanobot", botIcon: "", @@ -2552,7 +2552,8 @@ function ModelsSettings({ value={String(form.contextWindowTokens)} options={CONTEXT_WINDOW_TOKEN_OPTIONS.map((tokens) => ({ value: String(tokens), - label: tokens === 262_144 ? "256K" : "64K", + label: + tokens === 262_144 ? "256K" : tokens === 200_000 ? "200K" : "64K", }))} onChange={(value) => setForm((prev) => ({ diff --git a/webui/src/tests/settings-view.test.tsx b/webui/src/tests/settings-view.test.tsx index 8522b59c..8222eeda 100644 --- a/webui/src/tests/settings-view.test.tsx +++ b/webui/src/tests/settings-view.test.tsx @@ -22,7 +22,7 @@ function settingsPayload(): SettingsPayload { has_api_key: true, model_preset: "default", max_tokens: 8192, - context_window_tokens: 65536, + context_window_tokens: 200000, temperature: 0.1, reasoning_effort: null, timezone: "UTC", @@ -38,7 +38,7 @@ function settingsPayload(): SettingsPayload { model: "openai/gpt-4o", provider: "auto", max_tokens: 8192, - context_window_tokens: 65536, + context_window_tokens: 200000, temperature: 0.1, reasoning_effort: null, }], @@ -421,6 +421,7 @@ describe("SettingsView Apps catalog", () => { expect(await screen.findByText("Context window")).toBeInTheDocument(); expect(screen.getByRole("button", { name: "64K" })).toBeInTheDocument(); + expect(screen.getByRole("button", { name: "200K" })).toBeInTheDocument(); expect(screen.getByRole("button", { name: "256K" })).toBeInTheDocument(); });