mirror of
https://github.com/andrewyng/openworker.git
synced 2026-09-13 15:50:02 +00:00
Enable Claude extended thinking by default, drop the settings field
Fixed 8192 budget; the provider profile key stays a hidden override (0 = off). Per-turn composer control is future work.
This commit is contained in:
@@ -35,6 +35,12 @@ from .capabilities import capabilities_for
|
||||
# Required by the Messages API; a ceiling, not a spend target.
|
||||
DEFAULT_MAX_TOKENS = 16000
|
||||
|
||||
# Extended thinking is ON by default (owner call 2026-07-23: no user-facing setting —
|
||||
# most users wouldn't know what a budget is; a per-turn composer control is future work).
|
||||
# The provider profile's `thinking_budget` remains a hidden override: a number replaces
|
||||
# the default, 0 disables thinking entirely.
|
||||
DEFAULT_THINKING_BUDGET = 8192
|
||||
|
||||
# Anthropic stop_reason → the engine's OpenAI-shaped finish_reason vocabulary.
|
||||
_STOP_REASON_MAP = {
|
||||
"end_turn": "stop",
|
||||
|
||||
@@ -106,11 +106,15 @@ def _build_openai(profile: dict[str, Any], secrets: Any) -> ProviderClient:
|
||||
def _build_anthropic(profile: dict[str, Any], secrets: Any) -> ProviderClient:
|
||||
# Key resolution stays in AnthropicProvider/resolve_api_key (explicit → env → SecretStore),
|
||||
# deferred to first call so the provider can be built before a key exists.
|
||||
# thinking_budget: hidden profile override — absent/invalid → the default (ON),
|
||||
# explicit 0 → off (see DEFAULT_THINKING_BUDGET).
|
||||
from .anthropic_provider import DEFAULT_THINKING_BUDGET
|
||||
|
||||
api_key = ((profile or {}).get("api_key") or "").strip() or None
|
||||
try:
|
||||
thinking_budget = int(str((profile or {}).get("thinking_budget") or "").strip())
|
||||
except ValueError:
|
||||
thinking_budget = 0
|
||||
thinking_budget = DEFAULT_THINKING_BUDGET
|
||||
return AnthropicProvider(
|
||||
api_key=api_key, secrets=secrets, thinking_budget=thinking_budget
|
||||
)
|
||||
@@ -225,13 +229,8 @@ DESCRIPTORS: list[ProviderDescriptor] = [
|
||||
secret=True,
|
||||
placeholder="sk-ant-…",
|
||||
),
|
||||
ProviderField(
|
||||
"thinking_budget",
|
||||
"Extended thinking budget (tokens, optional)",
|
||||
required=False,
|
||||
placeholder="e.g. 8192 — blank = off",
|
||||
help="Turns on Claude's extended thinking for every request, with this token budget. The thought process shows in the transcript.",
|
||||
),
|
||||
# No thinking_budget field (owner call 2026-07-23): extended thinking is
|
||||
# on by default; the profile key stays a hidden override (0 = off).
|
||||
],
|
||||
build=_build_anthropic,
|
||||
recommended_model="claude-fable-5",
|
||||
|
||||
@@ -328,7 +328,7 @@ const PROVIDERS = [
|
||||
// openai: configured + used (drives the "Last used" sub-line and the status dot).
|
||||
{ name: "openai", title: "OpenAI", needs_key: true, fields: [{ key: "api_key", label: "OpenAI API key", secret: true, required: true, help: "", placeholder: "sk-…" }], configured: true, values: {}, suggested_models: ["gpt-5.5"], key_set_at: "2026-06-12", last_used_at: Math.floor(Date.now() / 1000) - 7200 },
|
||||
// anthropic: configured but never used ("Not used yet").
|
||||
{ name: "anthropic", title: "Claude (Anthropic)", needs_key: true, fields: [{ key: "api_key", label: "API key", secret: true, required: true, help: "", placeholder: "sk-…" }, { key: "thinking_budget", label: "Extended thinking budget (tokens, optional)", secret: false, required: false, help: "Turns on Claude's extended thinking for every request.", placeholder: "e.g. 8192 — blank = off" }], configured: true, values: {}, suggested_models: ["claude-opus-4-8"], key_set_at: null, last_used_at: null },
|
||||
{ name: "anthropic", title: "Claude (Anthropic)", needs_key: true, fields: [{ key: "api_key", label: "API key", secret: true, required: true, help: "", placeholder: "sk-…" }], configured: true, values: {}, suggested_models: ["claude-opus-4-8"], key_set_at: null, last_used_at: null },
|
||||
// zai: an OpenAI-compatible vendor — unconfigured, with a prefilled editable endpoint + blurb.
|
||||
{ name: "zai", title: "Z AI (GLM)", needs_key: true, blurb: "Uses Z AI's OpenAI-compatible API — the endpoint is prefilled, just add your key.", fields: [{ key: "api_key", label: "Z AI API key", secret: true, required: true, help: "", placeholder: "" }, { key: "base_url", label: "Endpoint", secret: false, required: false, help: "Prefilled with Z AI's international endpoint.", placeholder: "https://api.z.ai/api/paas/v4", default: "https://api.z.ai/api/paas/v4" }], configured: false, values: {}, suggested_models: ["glm-5.2"], key_set_at: null, last_used_at: null },
|
||||
// ollama: keyless local provider — "configured" without proving anything runs; the
|
||||
|
||||
@@ -53,20 +53,21 @@ test("a configured provider's form opens with the saved state, no plaintext key"
|
||||
await expect(page.getByTestId("set-field-api_key")).toHaveAttribute("placeholder", "••••••••");
|
||||
});
|
||||
|
||||
test("non-secret extras blur-save on a configured provider (thinking budget)", async ({
|
||||
test("non-secret fields blur-save on a configured provider (ollama endpoint)", async ({
|
||||
page,
|
||||
}) => {
|
||||
// Owner-hit 2026-07-23: typed a thinking budget, left Settings, value silently never
|
||||
// saved — the Test button was the form's only save path. Blur now saves extras.
|
||||
// Owner-hit 2026-07-23 (as the thinking-budget field, since folded into a default):
|
||||
// the Test button was the form's only save path — typing into a non-secret field and
|
||||
// leaving Settings silently discarded it. Blur now saves.
|
||||
await openModels(page);
|
||||
await page.getByTestId("set-provider-anthropic").click();
|
||||
const budget = page.getByTestId("set-field-thinking_budget");
|
||||
await budget.fill("8192");
|
||||
await budget.blur();
|
||||
await expect(page.getByTestId("set-field-saved-thinking_budget")).toBeVisible();
|
||||
await page.getByTestId("set-provider-ollama").click();
|
||||
const endpoint = page.getByTestId("set-field-base_url");
|
||||
await endpoint.fill("http://127.0.0.1:9999");
|
||||
await endpoint.blur();
|
||||
await expect(page.getByTestId("set-field-saved-base_url")).toBeVisible();
|
||||
|
||||
// Leave and come back: the value survived (served from the provider's stored values).
|
||||
await page.getByTestId("set-back").click();
|
||||
await page.getByTestId("set-provider-anthropic").click();
|
||||
await expect(page.getByTestId("set-field-thinking_budget")).toHaveValue("8192");
|
||||
await page.getByTestId("set-provider-ollama").click();
|
||||
await expect(page.getByTestId("set-field-base_url")).toHaveValue("http://127.0.0.1:9999");
|
||||
});
|
||||
|
||||
@@ -623,3 +623,14 @@ def test_convert_replays_thinking_blocks_ahead_of_tool_use():
|
||||
assistant = msgs[1]["content"]
|
||||
assert assistant[0] == {"type": "thinking", "thinking": "plan", "signature": "S"}
|
||||
assert assistant[1]["type"] == "tool_use"
|
||||
|
||||
|
||||
def test_thinking_defaults_on_with_hidden_profile_override():
|
||||
"""No user-facing setting (owner call 2026-07-23): thinking is ON by default; the
|
||||
profile's thinking_budget stays a hidden override, 0 = off."""
|
||||
from coworker.providers.anthropic_provider import DEFAULT_THINKING_BUDGET
|
||||
from coworker.providers.registry import build_provider_client
|
||||
|
||||
assert build_provider_client("anthropic", {}, None).thinking_budget == DEFAULT_THINKING_BUDGET
|
||||
assert build_provider_client("anthropic", {"thinking_budget": "2048"}, None).thinking_budget == 2048
|
||||
assert build_provider_client("anthropic", {"thinking_budget": "0"}, None).thinking_budget == 0
|
||||
|
||||
+5
-14
@@ -769,22 +769,13 @@ def test_google_one_click_paused_but_manual_alive(tmp_path):
|
||||
|
||||
|
||||
def test_set_provider_persists_extra_fields(tmp_path):
|
||||
"""Non-secret descriptor extras (anthropic thinking_budget) round-trip: saved into the
|
||||
"""Non-secret descriptor extras (ollama's endpoint) round-trip: saved into the
|
||||
profile, echoed by get_providers for form prefill, cleared by an empty save."""
|
||||
manager = SessionManager(workspace=tmp_path, provider=ScriptedProvider([]))
|
||||
assert manager.set_provider(
|
||||
"anthropic", {"api_key": "sk-ant-test", "thinking_budget": "8192"}
|
||||
)["ok"]
|
||||
assert manager.set_provider("ollama", {"base_url": "http://127.0.0.1:9999"})["ok"]
|
||||
providers = {p["name"]: p for p in manager.get_providers()}
|
||||
assert providers["anthropic"]["values"]["thinking_budget"] == "8192"
|
||||
assert providers["ollama"]["values"]["base_url"] == "http://127.0.0.1:9999"
|
||||
|
||||
from coworker.providers.registry import build_provider_client
|
||||
|
||||
built = build_provider_client(
|
||||
"anthropic", manager.secrets.get("provider:anthropic"), manager.secrets
|
||||
)
|
||||
assert built.thinking_budget == 8192
|
||||
|
||||
manager.set_provider("anthropic", {"thinking_budget": ""})
|
||||
manager.set_provider("ollama", {"base_url": ""})
|
||||
providers = {p["name"]: p for p in manager.get_providers()}
|
||||
assert "thinking_budget" not in providers["anthropic"]["values"]
|
||||
assert "base_url" not in providers["ollama"]["values"]
|
||||
|
||||
Reference in New Issue
Block a user