diff --git a/openhands/core/config/llm_config.py b/openhands/core/config/llm_config.py index ae6742cacd..00d440fe4d 100644 --- a/openhands/core/config/llm_config.py +++ b/openhands/core/config/llm_config.py @@ -164,8 +164,8 @@ class LLMConfig(BaseModel): if self.openrouter_app_name: os.environ['OR_APP_NAME'] = self.openrouter_app_name - # Assign an API version for Azure models - # While it doesn't seem required, the format supported by the API without version seems old and will likely break. - # Azure issue: https://github.com/All-Hands-AI/OpenHands/issues/6777 + # Set an API version by default for Azure models + # Required for newer models. + # Azure issue: https://github.com/All-Hands-AI/OpenHands/issues/7755 if self.model.startswith('azure') and self.api_version is None: - self.api_version = '2024-08-01-preview' + self.api_version = '2024-12-01-preview' diff --git a/openhands/resolver/resolve_all_issues.py b/openhands/resolver/resolve_all_issues.py index dde5a884c9..3961b5e5a0 100644 --- a/openhands/resolver/resolve_all_issues.py +++ b/openhands/resolver/resolve_all_issues.py @@ -349,6 +349,7 @@ def main() -> None: model=my_args.llm_model or os.environ['LLM_MODEL'], api_key=SecretStr(api_key) if api_key else None, base_url=my_args.llm_base_url or os.environ.get('LLM_BASE_URL', None), + api_version=os.environ.get('LLM_API_VERSION', None), ) repo_instruction = None diff --git a/openhands/resolver/resolve_issue.py b/openhands/resolver/resolve_issue.py index 0156fe6ba4..2aafd7f8c5 100644 --- a/openhands/resolver/resolve_issue.py +++ b/openhands/resolver/resolve_issue.py @@ -656,12 +656,21 @@ def main() -> None: raise ValueError('Token is invalid.') api_key = my_args.llm_api_key or os.environ['LLM_API_KEY'] + model = my_args.llm_model or os.environ['LLM_MODEL'] + base_url = my_args.llm_base_url or os.environ.get('LLM_BASE_URL', None) + api_version = os.environ.get('LLM_API_VERSION', None) + + # Create LLMConfig instance llm_config = LLMConfig( - model=my_args.llm_model or os.environ['LLM_MODEL'], + model=model, api_key=SecretStr(api_key) if api_key else None, - base_url=my_args.llm_base_url or os.environ.get('LLM_BASE_URL', None), + base_url=base_url, ) + # Only set api_version if it was explicitly provided, otherwise let LLMConfig handle it + if api_version is not None: + llm_config.api_version = api_version + repo_instruction = None if my_args.repo_instruction_file: with open(my_args.repo_instruction_file, 'r') as f: diff --git a/tests/unit/test_llm_config.py b/tests/unit/test_llm_config.py index fba91aa54c..17fb0e9f6e 100644 --- a/tests/unit/test_llm_config.py +++ b/tests/unit/test_llm_config.py @@ -212,3 +212,45 @@ unknown_attr = "should_not_exist" assert custom_invalid.model == 'base-model' assert custom_invalid.api_key.get_secret_value() == 'base-api-key' assert custom_invalid.num_retries == 3 # default value + + +def test_azure_model_api_version( + default_config: AppConfig, tmp_path: pathlib.Path +) -> None: + """Test that Azure models get the correct API version by default.""" + toml_content = """ +[core] +workspace_base = "./workspace" + +[llm] +model = "azure/o3-mini" +api_key = "test-api-key" + """ + toml_file = tmp_path / 'azure_llm.toml' + toml_file.write_text(toml_content) + + load_from_toml(default_config, str(toml_file)) + + # Verify Azure model gets default API version + azure_llm = default_config.get_llm_config('llm') + assert azure_llm.model == 'azure/o3-mini' + assert azure_llm.api_version == '2024-12-01-preview' + + # Test that non-Azure models don't get default API version + toml_content = """ +[core] +workspace_base = "./workspace" + +[llm] +model = "anthropic/claude-3-sonnet" +api_key = "test-api-key" + """ + toml_file = tmp_path / 'non_azure_llm.toml' + toml_file.write_text(toml_content) + + load_from_toml(default_config, str(toml_file)) + + # Verify non-Azure model doesn't get default API version + non_azure_llm = default_config.get_llm_config('llm') + assert non_azure_llm.model == 'anthropic/claude-3-sonnet' + assert non_azure_llm.api_version is None