anthropics / anthropics/claude-agent-sdk-python

[FEATURE] : Add set_effort and set_thinking to ClaudeSDKClient

Open
#981 0 comments 0 reactions 0 assignees View on GitHub
enhancement
Dominant language
Python
Stars
8.1k
Forks
1.3k
Avg merge
2d 31m
Merged PRs (30d)
1

Description

## Problem

`ClaudeSDKClient` exposes `set_model` and `set_permission_mode` as public, mid-conversation setters backed by control-protocol subtypes. `effort` and `thinking` are first-class fields on `ClaudeAgentOptions` , but the Python SDK exposes no equivalent setter - both are launch-only, mapped to `--effort` / `--thinking` on the CLI command line.

The TypeScript SDK exposes `applyFlagSettings` on its `Query` interface as a partial workaround. The Python SDK does not surface it at all.

There is no public mid-conversation knob in the Python SDK for either effort or thinking.

## Reproduction

- SDK: `claude-agent-sdk` `0.1.60`
- Bundled CLI: `cli.js` `2.1.111`
- Python 3.11.13, Linux

```python
import asyncio
from claude_agent_sdk import ClaudeAgentOptions, ClaudeSDKClient

async def main():
options = ClaudeAgentOptions(model="claude-opus-4-7", effort="low")
async with ClaudeSDKClient(options=options) as client:
await client.query("Quick exploratory question.")
async for _ in client.receive_response():
pass

# Want to bump effort before the next turn — no public method exists.
# client.set_effort("max") # AttributeError
# client.set_thinking({"type": "adaptive"}) # AttributeError

await client.query("Now give a deep planning answer.")
# ...still running at effort=low.

asyncio.run(main())
```

The only mid-conversation setters on `ClaudeSDKClient` are:

```python
await client.set_model(...)
await client.set_permission_mode(...)
```

## Workaround

Send the raw control request through the private `Query._send_control_request`:

```python
await client._query._send_control_request({
"subtype": "apply_flag_settings",
"settings": {"effortLevel": "max"},
})
```

This is not a clean substitute for dedicated setters:

- `_send_control_request` is private. `apply_flag_settings` has no `TypedDict` in `SDKControlRequest`.
- `{ effortLevel: "max" }` updates the in-memory `state.effortValue` but is **stripped before persistence** — `flagSettings` source serialises as `{}`.

CLI versions inspected for a dedicated subtype: cli.js 2.1.19 → 2.1.111 and native binaries 2.1.114 → 2.1.143. No `set_effort` or `set_thinking` subtype exists in any of them.

## What we'd like

1. Confirm whether there's a public API we missed for changing effort or thinking mid-conversation. We checked the methods on `ClaudeSDKClient` (`set_model`, `set_permission_mode`, `set_effort?`, `set_thinking?`) and the public exports, and didn't find one.

2. If not, please add public `client.set_effort()` and `client.set_thinking()` methods, symmetric with `set_model`:

```python
class ClaudeSDKClient:
async def set_effort(self, effort: EffortLevel | None) -> None: ...
async def set_thinking(self, thinking: ThinkingConfig | None) -> None: ...
```

`EffortLevel = Literal["low", "medium", "high", "xhigh", "max"]` and `ThinkingConfig` are already in `types.py`. Semantics matching `set_model`: streaming-mode only; effective on next turn; `None` resets to the value supplied at launch.

3. Backed by new control-protocol subtypes on `@anthropic-ai/claude-code`:

```jsonc
{ "type": "control_request", "request_id": "...",
"request": {
"subtype": "set_effort",
"effort": "low" | "medium" | "high" | "xhigh" | "max" | null
}
}

{ "type": "control_request", "request_id": "...",
"request": {
"subtype": "set_thinking",
"thinking":
{ "type": "adaptive", "display": "summarized" | "omitted" } |
{ "type": "enabled", "budget_tokens": number, "display": "summarized" | "omitted" } |
{ "type": "disabled" } |
null
}
}
```

Add corresponding `TypedDict` variants (`SDKControlSetEffortRequest`, `SDKControlSetThinkingRequest`) to the `SDKControlRequest` union in `types.py`. Also add the missing `SDKControlSetModelRequest` for parity — `set_model` is currently dispatched as an untyped dict.

4. Once the CLI subtypes land, the Python SDK PR is a forwarder identical in shape to `set_model`:

```python
async def set_effort(self, effort: EffortLevel | None) -> None:
await self._send_control_request({"subtype": "set_effort", "effort": effort})

async def set_thinking(self, thinking: ThinkingConfig | None) -> None:
await self._send_control_request({"subtype": "set_thinking", "thinking": thinking})
```

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with ClaudeSDKClient.set_model and types.py, including the SDKControlRequest union and existing EffortLevel and ThinkingConfig definitions. Confirm the required CLI control subtypes first; done means public set_effort and set_thinking forward those requests, with typed request variants and SDKControlSetModelRequest added for parity.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
api
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.