mirror of
https://github.com/OpenHands/OpenHands.git
synced 2025-12-26 05:48:36 +08:00
Add o4-mini model and Mistral provider support to OpenHands CLI (#9217)
Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
parent
8c5995a5d8
commit
516f9fa635
@ -13,6 +13,7 @@ from openhands.cli.tui import (
|
||||
)
|
||||
from openhands.cli.utils import (
|
||||
VERIFIED_ANTHROPIC_MODELS,
|
||||
VERIFIED_MISTRAL_MODELS,
|
||||
VERIFIED_OPENAI_MODELS,
|
||||
VERIFIED_PROVIDERS,
|
||||
organize_models_and_providers,
|
||||
@ -158,7 +159,7 @@ async def modify_llm_settings_basic(
|
||||
provider_completer = FuzzyWordCompleter(provider_list)
|
||||
session = PromptSession(key_bindings=kb_cancel())
|
||||
|
||||
# Set default provider - prefer 'anthropic' if available, otherwise use the first provider
|
||||
# Set default provider - prefer 'anthropic' if available, otherwise use first
|
||||
provider = 'anthropic' if 'anthropic' in provider_list else provider_list[0]
|
||||
model = None
|
||||
api_key = None
|
||||
@ -168,15 +169,26 @@ async def modify_llm_settings_basic(
|
||||
print_formatted_text(
|
||||
HTML(f'\n<grey>Default provider: </grey><green>{provider}</green>')
|
||||
)
|
||||
change_provider = (
|
||||
cli_confirm(
|
||||
'Do you want to use a different provider?',
|
||||
[f'Use {provider}', 'Select another provider'],
|
||||
)
|
||||
== 1
|
||||
|
||||
# Show verified providers plus "Select another provider" option
|
||||
provider_choices = verified_providers + ['Select another provider']
|
||||
provider_choice = cli_confirm(
|
||||
'(Step 1/3) Select LLM Provider:',
|
||||
provider_choices,
|
||||
)
|
||||
|
||||
if change_provider:
|
||||
# Ensure provider_choice is an integer (for test compatibility)
|
||||
try:
|
||||
choice_index = int(provider_choice)
|
||||
except (TypeError, ValueError):
|
||||
# If conversion fails (e.g., in tests with mocks), default to 0
|
||||
choice_index = 0
|
||||
|
||||
if choice_index < len(verified_providers):
|
||||
# User selected one of the verified providers
|
||||
provider = verified_providers[choice_index]
|
||||
else:
|
||||
# User selected "Select another provider" - use manual selection
|
||||
# Define a validator function that prints an error message
|
||||
def provider_validator(x):
|
||||
is_valid = x in organized_models
|
||||
@ -196,7 +208,8 @@ async def modify_llm_settings_basic(
|
||||
|
||||
# Make sure the provider exists in organized_models
|
||||
if provider not in organized_models:
|
||||
# If the provider doesn't exist, prefer 'anthropic' if available, otherwise use the first provider
|
||||
# If the provider doesn't exist, prefer 'anthropic' if available,
|
||||
# otherwise use the first provider
|
||||
provider = (
|
||||
'anthropic'
|
||||
if 'anthropic' in organized_models
|
||||
@ -214,6 +227,11 @@ async def modify_llm_settings_basic(
|
||||
m for m in provider_models if m not in VERIFIED_ANTHROPIC_MODELS
|
||||
]
|
||||
provider_models = VERIFIED_ANTHROPIC_MODELS + provider_models
|
||||
if provider == 'mistral':
|
||||
provider_models = [
|
||||
m for m in provider_models if m not in VERIFIED_MISTRAL_MODELS
|
||||
]
|
||||
provider_models = VERIFIED_MISTRAL_MODELS + provider_models
|
||||
|
||||
# Set default model to the best verified model for the provider
|
||||
if provider == 'anthropic' and VERIFIED_ANTHROPIC_MODELS:
|
||||
@ -222,6 +240,9 @@ async def modify_llm_settings_basic(
|
||||
elif provider == 'openai' and VERIFIED_OPENAI_MODELS:
|
||||
# Use the first model in the VERIFIED_OPENAI_MODELS list as it's the best/newest
|
||||
default_model = VERIFIED_OPENAI_MODELS[0]
|
||||
elif provider == 'mistral' and VERIFIED_MISTRAL_MODELS:
|
||||
# Use the first model in the VERIFIED_MISTRAL_MODELS list as it's the best/newest
|
||||
default_model = VERIFIED_MISTRAL_MODELS[0]
|
||||
else:
|
||||
# For other providers, use the first model in the list
|
||||
default_model = (
|
||||
|
||||
@ -102,6 +102,8 @@ def extract_model_and_provider(model: str) -> ModelInfo:
|
||||
return ModelInfo(provider='openai', model=split[0], separator='/')
|
||||
if split[0] in VERIFIED_ANTHROPIC_MODELS:
|
||||
return ModelInfo(provider='anthropic', model=split[0], separator='/')
|
||||
if split[0] in VERIFIED_MISTRAL_MODELS:
|
||||
return ModelInfo(provider='mistral', model=split[0], separator='/')
|
||||
# return as model only
|
||||
return ModelInfo(provider='', model=model, separator='')
|
||||
|
||||
@ -143,9 +145,10 @@ def organize_models_and_providers(
|
||||
return result_dict
|
||||
|
||||
|
||||
VERIFIED_PROVIDERS = ['openai', 'azure', 'anthropic', 'deepseek']
|
||||
VERIFIED_PROVIDERS = ['anthropic', 'openai', 'mistral']
|
||||
|
||||
VERIFIED_OPENAI_MODELS = [
|
||||
'o4-mini',
|
||||
'gpt-4o',
|
||||
'gpt-4o-mini',
|
||||
'gpt-4-turbo',
|
||||
@ -171,6 +174,10 @@ VERIFIED_ANTHROPIC_MODELS = [
|
||||
'claude-2',
|
||||
]
|
||||
|
||||
VERIFIED_MISTRAL_MODELS = [
|
||||
'devstral-small-2505',
|
||||
]
|
||||
|
||||
|
||||
class ProviderInfo(BaseModel):
|
||||
"""Information about a provider and its models."""
|
||||
|
||||
@ -361,6 +361,22 @@ class TestModelAndProviderFunctions:
|
||||
assert result['model'] == 'claude-sonnet-4-20250514'
|
||||
assert result['separator'] == '/'
|
||||
|
||||
def test_extract_model_and_provider_mistral_implicit(self):
|
||||
model = 'devstral-small-2505'
|
||||
result = extract_model_and_provider(model)
|
||||
|
||||
assert result['provider'] == 'mistral'
|
||||
assert result['model'] == 'devstral-small-2505'
|
||||
assert result['separator'] == '/'
|
||||
|
||||
def test_extract_model_and_provider_o4_mini(self):
|
||||
model = 'o4-mini'
|
||||
result = extract_model_and_provider(model)
|
||||
|
||||
assert result['provider'] == 'openai'
|
||||
assert result['model'] == 'o4-mini'
|
||||
assert result['separator'] == '/'
|
||||
|
||||
def test_extract_model_and_provider_versioned(self):
|
||||
model = 'deepseek.deepseek-coder-1.3b'
|
||||
result = extract_model_and_provider(model)
|
||||
@ -382,6 +398,9 @@ class TestModelAndProviderFunctions:
|
||||
'openai/gpt-4o',
|
||||
'anthropic/claude-sonnet-4-20250514',
|
||||
'o3-mini',
|
||||
'o4-mini',
|
||||
'devstral-small-2505',
|
||||
'mistral/devstral-small-2505',
|
||||
'anthropic.claude-3-5', # Should be ignored as it uses dot separator for anthropic
|
||||
'unknown-model',
|
||||
]
|
||||
@ -390,15 +409,20 @@ class TestModelAndProviderFunctions:
|
||||
|
||||
assert 'openai' in result
|
||||
assert 'anthropic' in result
|
||||
assert 'mistral' in result
|
||||
assert 'other' in result
|
||||
|
||||
assert len(result['openai']['models']) == 2
|
||||
assert len(result['openai']['models']) == 3
|
||||
assert 'gpt-4o' in result['openai']['models']
|
||||
assert 'o3-mini' in result['openai']['models']
|
||||
assert 'o4-mini' in result['openai']['models']
|
||||
|
||||
assert len(result['anthropic']['models']) == 1
|
||||
assert 'claude-sonnet-4-20250514' in result['anthropic']['models']
|
||||
|
||||
assert len(result['mistral']['models']) == 2
|
||||
assert 'devstral-small-2505' in result['mistral']['models']
|
||||
|
||||
assert len(result['other']['models']) == 1
|
||||
assert 'unknown-model' in result['other']['models']
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user