From e19b3dd1f0368b5722af4648b5327cd3eebc9c40 Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Mon, 29 Sep 2025 23:27:19 -0400 Subject: [PATCH] Add claude-sonnet-4-5 model support (#11179) Co-authored-by: openhands --- README.md | 2 +- docs/usage/how-to/cli-mode.mdx | 2 +- docs/usage/how-to/headless-mode.mdx | 2 +- docs/usage/llms/llms.mdx | 1 + docs/usage/llms/openhands-llms.mdx | 3 ++- frontend/src/mocks/handlers.ts | 2 ++ frontend/src/utils/verified-models.ts | 3 +++ openhands/cli/utils.py | 2 ++ openhands/llm/llm.py | 8 +++++++- openhands/llm/model_features.py | 1 + openhands/utils/llm.py | 1 + tests/unit/cli/test_cli_settings.py | 13 ++++++++++--- 12 files changed, 32 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index df7b0c6665..f1b01632d1 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,7 @@ docker run -it --rm --pull=always \ ### Getting Started When you open the application, you'll be asked to choose an LLM provider and add an API key. -[Anthropic's Claude Sonnet 4](https://www.anthropic.com/api) (`anthropic/claude-sonnet-4-20250514`) +[Anthropic's Claude Sonnet 4.5](https://www.anthropic.com/api) (`anthropic/claude-sonnet-4-5-20250929`) works best, but you have [many options](https://docs.all-hands.dev/usage/llms). See the [Running OpenHands](https://docs.all-hands.dev/usage/installation) guide for diff --git a/docs/usage/how-to/cli-mode.mdx b/docs/usage/how-to/cli-mode.mdx index 94fdeddb90..5cb296767b 100644 --- a/docs/usage/how-to/cli-mode.mdx +++ b/docs/usage/how-to/cli-mode.mdx @@ -105,7 +105,7 @@ The conversation history will be saved in `~/.openhands/sessions`. 1. Set the following environment variables in your terminal: - `SANDBOX_VOLUMES` to specify the directory you want OpenHands to access ([See using SANDBOX_VOLUMES for more info](../runtimes/docker#using-sandbox_volumes)) - - `LLM_MODEL` - the LLM model to use (e.g. `export LLM_MODEL="anthropic/claude-sonnet-4-20250514"`) + - `LLM_MODEL` - the LLM model to use (e.g. `export LLM_MODEL="anthropic/claude-sonnet-4-20250514"` or `export LLM_MODEL="anthropic/claude-sonnet-4-5-20250929"`) - `LLM_API_KEY` - your API key (e.g. `export LLM_API_KEY="sk_test_12345"`) 2. Run the following command: diff --git a/docs/usage/how-to/headless-mode.mdx b/docs/usage/how-to/headless-mode.mdx index 8b985a2e03..5d2ef9f558 100644 --- a/docs/usage/how-to/headless-mode.mdx +++ b/docs/usage/how-to/headless-mode.mdx @@ -53,7 +53,7 @@ Set environment variables and run the Docker command: ```bash # Set required environment variables export SANDBOX_VOLUMES="/path/to/workspace:/workspace:rw" # Format: host_path:container_path:mode -export LLM_MODEL="anthropic/claude-sonnet-4-20250514" +export LLM_MODEL="anthropic/claude-sonnet-4-20250514" # or "anthropic/claude-sonnet-4-5-20250929" export LLM_API_KEY="your-api-key" export SANDBOX_SELECTED_REPO="owner/repo-name" # Optional: requires GITHUB_TOKEN export GITHUB_TOKEN="your-token" # Required for repository operations diff --git a/docs/usage/llms/llms.mdx b/docs/usage/llms/llms.mdx index 1b27607dd6..19357a65de 100644 --- a/docs/usage/llms/llms.mdx +++ b/docs/usage/llms/llms.mdx @@ -18,6 +18,7 @@ Based on these findings and community feedback, these are the latest models that ### Cloud / API-Based Models - [anthropic/claude-sonnet-4-20250514](https://www.anthropic.com/api) (recommended) +- [anthropic/claude-sonnet-4-5-20250929](https://www.anthropic.com/api) (recommended) - [openai/gpt-5-2025-08-07](https://openai.com/api/) (recommended) - [gemini/gemini-2.5-pro](https://blog.google/technology/google-deepmind/gemini-model-thinking-updates-march-2025/) - [deepseek/deepseek-chat](https://api-docs.deepseek.com/) diff --git a/docs/usage/llms/openhands-llms.mdx b/docs/usage/llms/openhands-llms.mdx index 64b163e9a7..ce151374b2 100644 --- a/docs/usage/llms/openhands-llms.mdx +++ b/docs/usage/llms/openhands-llms.mdx @@ -15,7 +15,7 @@ description: OpenHands LLM provider with access to state-of-the-art (SOTA) agent When running OpenHands, you'll need to set the following in the OpenHands UI through the Settings under the `LLM` tab: - `LLM Provider` to `OpenHands` -- `LLM Model` to the model you will be using (e.g. claude-sonnet-4-20250514) +- `LLM Model` to the model you will be using (e.g. claude-sonnet-4-20250514 or claude-sonnet-4-5-20250929) - `API Key` to your OpenHands LLM API key copied from above ## Using OpenHands LLM Provider in the CLI @@ -36,6 +36,7 @@ Pricing follows official API provider rates. Below are the current pricing detai |-------|----------------------------|-----------------------------------|------------------------------|------------------|-------------------| | claude-opus-4-20250514 | $15.00 | $1.50 | $75.00 | 200,000 | 32,000 | | claude-sonnet-4-20250514 | $3.00 | $0.30 | $15.00 | 200,000 | 64,000 | +| claude-sonnet-4-5-20250929 | $3.00 | $0.30 | $15.00 | 200,000 | 64,000 | | devstral-medium-2507 | $0.40 | N/A | $2.00 | 128,000 | 128,000 | | devstral-small-2505 | $0.10 | N/A | $0.30 | 128,000 | 128,000 | | devstral-small-2507 | $0.10 | N/A | $0.30 | 128,000 | 128,000 | diff --git a/frontend/src/mocks/handlers.ts b/frontend/src/mocks/handlers.ts index 88ff9e71c3..761918a311 100644 --- a/frontend/src/mocks/handlers.ts +++ b/frontend/src/mocks/handlers.ts @@ -115,7 +115,9 @@ const openHandsHandlers = [ "gpt-4o-mini", "anthropic/claude-3.5", "anthropic/claude-sonnet-4-20250514", + "anthropic/claude-sonnet-4-5-20250929", "openhands/claude-sonnet-4-20250514", + "openhands/claude-sonnet-4-5-20250929", "sambanova/Meta-Llama-3.1-8B-Instruct", ]), ), diff --git a/frontend/src/utils/verified-models.ts b/frontend/src/utils/verified-models.ts index 7349c55224..9bdb9c8eb0 100644 --- a/frontend/src/utils/verified-models.ts +++ b/frontend/src/utils/verified-models.ts @@ -13,6 +13,7 @@ export const VERIFIED_MODELS = [ "claude-3-5-sonnet-20241022", "claude-3-7-sonnet-20250219", "claude-sonnet-4-20250514", + "claude-sonnet-4-5-20250929", "claude-opus-4-20250514", "claude-opus-4-1-20250805", "gemini-2.5-pro", @@ -51,6 +52,7 @@ export const VERIFIED_ANTHROPIC_MODELS = [ "claude-3-5-haiku-20241022", "claude-3-7-sonnet-20250219", "claude-sonnet-4-20250514", + "claude-sonnet-4-5-20250929", "claude-opus-4-20250514", "claude-opus-4-1-20250805", ]; @@ -67,6 +69,7 @@ export const VERIFIED_MISTRAL_MODELS = [ // (e.g., they return `claude-sonnet-4-20250514` instead of `openhands/claude-sonnet-4-20250514`) export const VERIFIED_OPENHANDS_MODELS = [ "claude-sonnet-4-20250514", + "claude-sonnet-4-5-20250929", "gpt-5-2025-08-07", "gpt-5-mini-2025-08-07", "claude-opus-4-20250514", diff --git a/openhands/cli/utils.py b/openhands/cli/utils.py index ed7570b5fa..f61cfb1298 100644 --- a/openhands/cli/utils.py +++ b/openhands/cli/utils.py @@ -165,6 +165,7 @@ VERIFIED_OPENAI_MODELS = [ VERIFIED_ANTHROPIC_MODELS = [ 'claude-sonnet-4-20250514', + 'claude-sonnet-4-5-20250929', 'claude-opus-4-20250514', 'claude-opus-4-1-20250805', 'claude-3-7-sonnet-20250219', @@ -186,6 +187,7 @@ VERIFIED_MISTRAL_MODELS = [ VERIFIED_OPENHANDS_MODELS = [ 'claude-sonnet-4-20250514', + 'claude-sonnet-4-5-20250929', 'gpt-5-2025-08-07', 'gpt-5-mini-2025-08-07', 'claude-opus-4-20250514', diff --git a/openhands/llm/llm.py b/openhands/llm/llm.py index 0569acbf52..e85cf82d12 100644 --- a/openhands/llm/llm.py +++ b/openhands/llm/llm.py @@ -148,7 +148,10 @@ class LLM(RetryMixin, DebugMixin): logger.debug( f'Gemini model {self.config.model} with reasoning_effort {self.config.reasoning_effort} mapped to thinking {kwargs.get("thinking")}' ) - + elif 'claude-sonnet-4-5' in self.config.model: + kwargs.pop( + 'reasoning_effort', None + ) # don't send reasoning_effort to Claude Sonnet 4.5 else: kwargs['reasoning_effort'] = self.config.reasoning_effort kwargs.pop( @@ -507,6 +510,7 @@ class LLM(RetryMixin, DebugMixin): 'claude-3-7-sonnet', 'claude-3.7-sonnet', 'claude-sonnet-4', + 'claude-sonnet-4-5-20250929', ] if any(model in self.config.model for model in sonnet_models): self.config.max_output_tokens = 64000 # litellm set max to 128k, but that requires a header to be set @@ -817,6 +821,8 @@ class LLM(RetryMixin, DebugMixin): message.force_string_serializer = True if 'openrouter/anthropic/claude-sonnet-4' in self.config.model: message.force_string_serializer = True + if 'openrouter/anthropic/claude-sonnet-4-5-20250929' in self.config.model: + message.force_string_serializer = True # let pydantic handle the serialization return [message.model_dump() for message in messages] diff --git a/openhands/llm/model_features.py b/openhands/llm/model_features.py index f6fae8dc60..0265701237 100644 --- a/openhands/llm/model_features.py +++ b/openhands/llm/model_features.py @@ -103,6 +103,7 @@ REASONING_EFFORT_PATTERNS: list[str] = [ 'gpt-5*', # DeepSeek reasoning family 'deepseek-r1-0528*', + 'claude-sonnet-4-5*', ] PROMPT_CACHE_PATTERNS: list[str] = [ diff --git a/openhands/utils/llm.py b/openhands/utils/llm.py index 61da34bbba..d65acb1f7b 100644 --- a/openhands/utils/llm.py +++ b/openhands/utils/llm.py @@ -56,6 +56,7 @@ def get_supported_llm_models(config: OpenHandsConfig) -> list[str]: # Add OpenHands provider models openhands_models = [ 'openhands/claude-sonnet-4-20250514', + 'openhands/claude-sonnet-4-5-20250929', 'openhands/gpt-5-2025-08-07', 'openhands/gpt-5-mini-2025-08-07', 'openhands/claude-opus-4-20250514', diff --git a/tests/unit/cli/test_cli_settings.py b/tests/unit/cli/test_cli_settings.py index c9c7466efb..a2cba1fffb 100644 --- a/tests/unit/cli/test_cli_settings.py +++ b/tests/unit/cli/test_cli_settings.py @@ -615,7 +615,12 @@ class TestModifyLLMSettingsBasic: ) @patch( 'openhands.cli.settings.VERIFIED_OPENHANDS_MODELS', - ['claude-sonnet-4-20250514', 'claude-opus-4-20250514', 'o3'], + [ + 'claude-sonnet-4-20250514', + 'claude-sonnet-4-5-20250929', + 'claude-opus-4-20250514', + 'o3', + ], ) @patch('openhands.cli.settings.get_supported_llm_models') @patch('openhands.cli.settings.organize_models_and_providers') @@ -638,6 +643,7 @@ class TestModifyLLMSettingsBasic: # Setup mocks mock_get_models.return_value = [ 'openhands/claude-sonnet-4-20250514', + 'openhands/claude-sonnet-4-5-20250929', 'openhands/claude-opus-4-20250514', 'openhands/o3', ] @@ -645,6 +651,7 @@ class TestModifyLLMSettingsBasic: 'openhands': { 'models': [ 'claude-sonnet-4-20250514', + 'claude-sonnet-4-5-20250929', 'claude-opus-4-20250514', 'o3', ], @@ -668,7 +675,7 @@ class TestModifyLLMSettingsBasic: # Change provider and model mock_confirm.side_effect = [ 0, # Select openhands (index 0 in ['openhands', 'anthropic']) - 2, # Select o3 (index 2 in ['claude-sonnet-4-20250514', 'claude-opus-4-20250514', 'o3']) + 3, # Select o3 (index 3 in ['claude-sonnet-4-20250514', 'claude-sonnet-4-5-20250929', 'claude-opus-4-20250514', 'o3']) 0, # Save settings ] @@ -702,7 +709,7 @@ class TestModifyLLMSettingsBasic: ) @patch( 'openhands.cli.settings.VERIFIED_ANTHROPIC_MODELS', - ['claude-sonnet-4-20250514', 'claude-3-opus'], + ['claude-sonnet-4-20250514', 'claude-sonnet-4-5-20250929', 'claude-3-opus'], ) @patch('openhands.cli.settings.get_supported_llm_models') @patch('openhands.cli.settings.organize_models_and_providers')