diff --git a/clawdforge/runner.py b/clawdforge/runner.py index 261cd71..80e4216 100644 --- a/clawdforge/runner.py +++ b/clawdforge/runner.py @@ -34,6 +34,7 @@ class Runner: model: str | None = None, system: str | None = None, system_mode: str | None = "append", + effort: str | None = None, files: list[str] | None = None, timeout_secs: int | None = None, ) -> RunResult: @@ -60,6 +61,8 @@ class Runner: cmd += ["--system-prompt", system] else: cmd += ["--append-system-prompt", system] + if effort: + cmd += ["--effort", effort] if files: for f in files: cmd += ["--files", f] diff --git a/clawdforge/server.py b/clawdforge/server.py index 5e940ff..1836ed2 100644 --- a/clawdforge/server.py +++ b/clawdforge/server.py @@ -122,6 +122,12 @@ class RunRequest(BaseModel): # Default is "append" for back-compat — every caller before this # field landed used append semantics implicitly. system_mode: Literal["append", "replace"] | None = "append" + # claude -p --effort . Controls extended-thinking budget on the + # reasoning models. None = let claude default decide (today: medium). + # Right for prose-craft tasks where the model benefits from extra + # thinking; e.g. skald passes "max" on gen + cleanup so author personas + # get the budget they deserve. + effort: Literal["low", "medium", "high", "xhigh", "max"] | None = None files: list[str] | None = None timeout_secs: int | None = Field(default=None, ge=5, le=600) @@ -203,6 +209,7 @@ def run( model=body.model, system=body.system, system_mode=body.system_mode, + effort=body.effort, files=file_paths or None, timeout_secs=body.timeout_secs, ) diff --git a/clients/rust/src/lib.rs b/clients/rust/src/lib.rs index bf3c207..7f3010e 100644 --- a/clients/rust/src/lib.rs +++ b/clients/rust/src/lib.rs @@ -53,6 +53,6 @@ pub use client::{Client, ClientBuilder}; pub use error::Error; pub use session::{Session, SessionList, SessionOptions, SessionState, TurnEvent, TurnResult}; pub use types::{ - AppToken, AppTokenInfo, FileToken, Healthz, RunFailure, RunRequest, RunResult, + AppToken, AppTokenInfo, Effort, FileToken, Healthz, RunFailure, RunRequest, RunResult, SystemMode, TokenCreateRequest, TokenList, }; diff --git a/clients/rust/src/types.rs b/clients/rust/src/types.rs index 6f62b9d..47a77bb 100644 --- a/clients/rust/src/types.rs +++ b/clients/rust/src/types.rs @@ -26,6 +26,30 @@ impl Default for SystemMode { } } +/// Maps to `claude -p --effort `. Controls the extended-thinking +/// budget on the reasoning models. `None` on [`RunRequest::effort`] means +/// "let the CLI default decide" (today: medium). +/// +/// Use [`Effort::Max`] for prose-craft tasks (fiction generation, +/// long-form rewrite) where the author persona benefits from extra +/// thinking. Leave default for tool-use / structured-JSON tasks. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum Effort { + /// `--effort low` — minimum thinking budget. + Low, + /// `--effort medium` — claude's default if no flag is passed. + Medium, + /// `--effort high`. + High, + /// `--effort xhigh`. + Xhigh, + /// `--effort max` — maximum thinking budget. Right for long-form + /// prose generation where the author persona benefits from extra + /// thinking before each paragraph. + Max, +} + /// `GET /healthz` response body. #[derive(Debug, Clone, Deserialize, Serialize)] pub struct Healthz { @@ -80,6 +104,11 @@ pub struct RunRequest { #[serde(skip_serializing_if = "Option::is_none")] pub system_mode: Option, + /// Maps to `claude -p --effort `. `None` lets the CLI default + /// decide. Use [`Effort::Max`] on prose-craft / long-form generation. + #[serde(skip_serializing_if = "Option::is_none")] + pub effort: Option, + /// File tokens previously returned from [`Client::upload_file`]. /// /// [`Client::upload_file`]: crate::Client::upload_file