4b6817381b
CI (OpenClaw E2E) / openclaw test (push) Has been cancelled
CI / coverage-report (push) Has been cancelled
CI / test-kubernetes (push) Has been cancelled
CI / should-run-thorough (push) Has been cancelled
CI / test-thorough (cloudwatch-demo) (push) Has been cancelled
CI / test-thorough (flink-ecs) (push) Has been cancelled
CI / test-thorough (upstream-lambda) (push) Has been cancelled
CI / test-thorough (prefect-ecs-fargate) (push) Has been cancelled
Release / build-binaries (zip, opensre.exe, onefile, windows-latest, windows-x64) (push) Has been cancelled
Benchmark image — build + push to ECR (any adapter) / build + push (push) Has been cancelled
CI / quality (ubuntu-latest) (push) Has been cancelled
CI / test (tools-runtime) (push) Has been cancelled
CI / test (e2e-general) (push) Has been cancelled
CI / test (cli-runtime) (push) Has been cancelled
CI / test (e2e-provider-and-openclaw) (push) Has been cancelled
CI / test (integrations-and-misc) (push) Has been cancelled
Release / verify (push) Has been cancelled
Release / build-python-dist (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-15-intel, darwin-x64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-latest, darwin-arm64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04, linux-x64) (push) Has been cancelled
Release / publish-release (push) Has been cancelled
Release / publish-main-release (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-checks (no-LLM) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-live shard ${{ matrix.shard_index }} (push) Has been cancelled
Release / prepare (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04-arm, linux-arm64) (push) Has been cancelled
Synthetic Deterministic Tests / Synthetic offline (deterministic) (push) Has been cancelled
139 lines
4.8 KiB
Python
139 lines
4.8 KiB
Python
"""Helpers for session-scoped reasoning effort overrides."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
from collections.abc import Iterator
|
|
from contextlib import contextmanager
|
|
from contextvars import ContextVar
|
|
from typing import Literal, cast
|
|
|
|
ReasoningEffortChoice = Literal["low", "medium", "high", "xhigh", "max"]
|
|
|
|
REASONING_EFFORT_OPTIONS: tuple[ReasoningEffortChoice, ...] = (
|
|
"low",
|
|
"medium",
|
|
"high",
|
|
"xhigh",
|
|
"max",
|
|
)
|
|
|
|
_RUNTIME_ENV_KEY = "OPENSRE_REASONING_EFFORT"
|
|
_RUNTIME_VALUES = frozenset({"low", "medium", "high", "xhigh"})
|
|
|
|
_reasoning_effort_session: ContextVar[str | None] = ContextVar(
|
|
"opensre_reasoning_effort_session", default=None
|
|
)
|
|
|
|
|
|
def parse_reasoning_effort(value: str | None) -> ReasoningEffortChoice | None:
|
|
"""Return the normalized user-facing effort choice, if valid."""
|
|
if value is None:
|
|
return None
|
|
normalized = value.strip().lower()
|
|
if normalized in REASONING_EFFORT_OPTIONS:
|
|
return cast(ReasoningEffortChoice, normalized)
|
|
return None
|
|
|
|
|
|
def runtime_reasoning_effort(choice: ReasoningEffortChoice | None) -> str | None:
|
|
"""Map a user-facing choice to the runtime value sent to model providers."""
|
|
if choice is None:
|
|
return None
|
|
return "xhigh" if choice == "max" else choice
|
|
|
|
|
|
def display_reasoning_effort(choice: ReasoningEffortChoice | None) -> str:
|
|
"""Human-readable label for tables and slash-command output."""
|
|
if choice is None:
|
|
return "(default)"
|
|
runtime = runtime_reasoning_effort(choice)
|
|
if runtime and runtime != choice:
|
|
return f"{choice} (runtime: {runtime})"
|
|
return choice
|
|
|
|
|
|
def get_active_reasoning_effort() -> str | None:
|
|
"""Return the runtime reasoning-effort value for this logical context.
|
|
|
|
Order: in-REPL session override (``apply_reasoning_effort``), then
|
|
``OPENSRE_REASONING_EFFORT`` in the process environment.
|
|
"""
|
|
session = _reasoning_effort_session.get()
|
|
if session is not None:
|
|
return session if session in _RUNTIME_VALUES else None
|
|
value = os.getenv(_RUNTIME_ENV_KEY, "").strip().lower()
|
|
if value in _RUNTIME_VALUES:
|
|
return value
|
|
return None
|
|
|
|
|
|
def provider_supports_reasoning_effort(provider: str | None) -> bool:
|
|
"""Whether the current provider is wired to consume the REPL effort override."""
|
|
return (provider or "").strip().lower() in {"openai", "codex"}
|
|
|
|
|
|
def infer_reasoning_effort_default(provider: str | None, model: str | None) -> str | None:
|
|
"""Best-effort default reasoning level for providers we wire today.
|
|
|
|
Returns ``None`` when the provider/model should fall back to its native
|
|
model default and we do not want to overstate a more precise value.
|
|
"""
|
|
normalized_provider = (provider or "").strip().lower()
|
|
normalized_model = (model or "").strip().lower()
|
|
if normalized_provider == "openai":
|
|
if normalized_model.startswith(("gpt-5.1", "gpt-5.2")):
|
|
return "none"
|
|
if normalized_model.startswith("gpt-5-pro"):
|
|
return "high"
|
|
if normalized_model.startswith(("gpt-5", "o1", "o3", "o4")):
|
|
return "medium"
|
|
return None
|
|
|
|
|
|
def describe_reasoning_effort_default(provider: str | None, model: str | None) -> str:
|
|
"""Human-readable default behavior for `/effort` when no override is set."""
|
|
normalized_provider = (provider or "").strip().lower() or "unknown"
|
|
visible_model = (model or "").strip() or "provider default"
|
|
if not provider_supports_reasoning_effort(normalized_provider):
|
|
return f"{normalized_provider} does not use reasoning-effort overrides"
|
|
inferred = infer_reasoning_effort_default(normalized_provider, visible_model)
|
|
if inferred is not None:
|
|
return f"{normalized_provider} · {visible_model}: {inferred}"
|
|
return f"{normalized_provider} · {visible_model}: model default"
|
|
|
|
|
|
@contextmanager
|
|
def apply_reasoning_effort(choice: ReasoningEffortChoice | None) -> Iterator[None]:
|
|
"""Temporarily expose a session effort override to downstream model clients.
|
|
|
|
``choice is None`` means defer to shell/env defaults: do not clear
|
|
``OPENSRE_REASONING_EFFORT`` or mutate the process environment.
|
|
|
|
Non-None choices use a :class:`contextvars.ContextVar` so concurrent REPL or
|
|
CLI invocations on different threads/tasks do not race on ``os.environ``.
|
|
"""
|
|
if choice is None:
|
|
yield
|
|
return
|
|
runtime = runtime_reasoning_effort(choice)
|
|
token = _reasoning_effort_session.set(runtime)
|
|
try:
|
|
yield
|
|
finally:
|
|
_reasoning_effort_session.reset(token)
|
|
|
|
|
|
__all__ = [
|
|
"REASONING_EFFORT_OPTIONS",
|
|
"ReasoningEffortChoice",
|
|
"apply_reasoning_effort",
|
|
"describe_reasoning_effort_default",
|
|
"display_reasoning_effort",
|
|
"get_active_reasoning_effort",
|
|
"infer_reasoning_effort_default",
|
|
"parse_reasoning_effort",
|
|
"provider_supports_reasoning_effort",
|
|
"runtime_reasoning_effort",
|
|
]
|