mirror of
https://github.com/andrewyng/openworker.git
synced 2026-09-11 14:50:14 +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.
|
# Required by the Messages API; a ceiling, not a spend target.
|
||||||
DEFAULT_MAX_TOKENS = 16000
|
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.
|
# Anthropic stop_reason → the engine's OpenAI-shaped finish_reason vocabulary.
|
||||||
_STOP_REASON_MAP = {
|
_STOP_REASON_MAP = {
|
||||||
"end_turn": "stop",
|
"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:
|
def _build_anthropic(profile: dict[str, Any], secrets: Any) -> ProviderClient:
|
||||||
# Key resolution stays in AnthropicProvider/resolve_api_key (explicit → env → SecretStore),
|
# 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.
|
# 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
|
api_key = ((profile or {}).get("api_key") or "").strip() or None
|
||||||
try:
|
try:
|
||||||
thinking_budget = int(str((profile or {}).get("thinking_budget") or "").strip())
|
thinking_budget = int(str((profile or {}).get("thinking_budget") or "").strip())
|
||||||
except ValueError:
|
except ValueError:
|
||||||
thinking_budget = 0
|
thinking_budget = DEFAULT_THINKING_BUDGET
|
||||||
return AnthropicProvider(
|
return AnthropicProvider(
|
||||||
api_key=api_key, secrets=secrets, thinking_budget=thinking_budget
|
api_key=api_key, secrets=secrets, thinking_budget=thinking_budget
|
||||||
)
|
)
|
||||||
@@ -225,13 +229,8 @@ DESCRIPTORS: list[ProviderDescriptor] = [
|
|||||||
secret=True,
|
secret=True,
|
||||||
placeholder="sk-ant-…",
|
placeholder="sk-ant-…",
|
||||||
),
|
),
|
||||||
ProviderField(
|
# No thinking_budget field (owner call 2026-07-23): extended thinking is
|
||||||
"thinking_budget",
|
# on by default; the profile key stays a hidden override (0 = off).
|
||||||
"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.",
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
build=_build_anthropic,
|
build=_build_anthropic,
|
||||||
recommended_model="claude-fable-5",
|
recommended_model="claude-fable-5",
|
||||||
|
|||||||
@@ -328,7 +328,7 @@ const PROVIDERS = [
|
|||||||
// openai: configured + used (drives the "Last used" sub-line and the status dot).
|
// 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 },
|
{ 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").
|
// 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.
|
// 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 },
|
{ 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
|
// 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", "••••••••");
|
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,
|
page,
|
||||||
}) => {
|
}) => {
|
||||||
// Owner-hit 2026-07-23: typed a thinking budget, left Settings, value silently never
|
// Owner-hit 2026-07-23 (as the thinking-budget field, since folded into a default):
|
||||||
// saved — the Test button was the form's only save path. Blur now saves extras.
|
// 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 openModels(page);
|
||||||
await page.getByTestId("set-provider-anthropic").click();
|
await page.getByTestId("set-provider-ollama").click();
|
||||||
const budget = page.getByTestId("set-field-thinking_budget");
|
const endpoint = page.getByTestId("set-field-base_url");
|
||||||
await budget.fill("8192");
|
await endpoint.fill("http://127.0.0.1:9999");
|
||||||
await budget.blur();
|
await endpoint.blur();
|
||||||
await expect(page.getByTestId("set-field-saved-thinking_budget")).toBeVisible();
|
await expect(page.getByTestId("set-field-saved-base_url")).toBeVisible();
|
||||||
|
|
||||||
// Leave and come back: the value survived (served from the provider's stored values).
|
// Leave and come back: the value survived (served from the provider's stored values).
|
||||||
await page.getByTestId("set-back").click();
|
await page.getByTestId("set-back").click();
|
||||||
await page.getByTestId("set-provider-anthropic").click();
|
await page.getByTestId("set-provider-ollama").click();
|
||||||
await expect(page.getByTestId("set-field-thinking_budget")).toHaveValue("8192");
|
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"]
|
assistant = msgs[1]["content"]
|
||||||
assert assistant[0] == {"type": "thinking", "thinking": "plan", "signature": "S"}
|
assert assistant[0] == {"type": "thinking", "thinking": "plan", "signature": "S"}
|
||||||
assert assistant[1]["type"] == "tool_use"
|
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):
|
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."""
|
profile, echoed by get_providers for form prefill, cleared by an empty save."""
|
||||||
manager = SessionManager(workspace=tmp_path, provider=ScriptedProvider([]))
|
manager = SessionManager(workspace=tmp_path, provider=ScriptedProvider([]))
|
||||||
assert manager.set_provider(
|
assert manager.set_provider("ollama", {"base_url": "http://127.0.0.1:9999"})["ok"]
|
||||||
"anthropic", {"api_key": "sk-ant-test", "thinking_budget": "8192"}
|
|
||||||
)["ok"]
|
|
||||||
providers = {p["name"]: p for p in manager.get_providers()}
|
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
|
manager.set_provider("ollama", {"base_url": ""})
|
||||||
|
|
||||||
built = build_provider_client(
|
|
||||||
"anthropic", manager.secrets.get("provider:anthropic"), manager.secrets
|
|
||||||
)
|
|
||||||
assert built.thinking_budget == 8192
|
|
||||||
|
|
||||||
manager.set_provider("anthropic", {"thinking_budget": ""})
|
|
||||||
providers = {p["name"]: p for p in manager.get_providers()}
|
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