Spaces:
Build error
Build error
from pathlib import Path | |
import toml | |
from pydantic import BaseModel, Field | |
from openhands.cli.tui import ( | |
UsageMetrics, | |
) | |
from openhands.events.event import Event | |
from openhands.llm.metrics import Metrics | |
_LOCAL_CONFIG_FILE_PATH = Path.home() / '.openhands' / 'config.toml' | |
_DEFAULT_CONFIG: dict[str, dict[str, list[str]]] = {'sandbox': {'trusted_dirs': []}} | |
def get_local_config_trusted_dirs() -> list[str]: | |
if _LOCAL_CONFIG_FILE_PATH.exists(): | |
with open(_LOCAL_CONFIG_FILE_PATH, 'r') as f: | |
try: | |
config = toml.load(f) | |
except Exception: | |
config = _DEFAULT_CONFIG | |
if 'sandbox' in config and 'trusted_dirs' in config['sandbox']: | |
return config['sandbox']['trusted_dirs'] | |
return [] | |
def add_local_config_trusted_dir(folder_path: str) -> None: | |
config = _DEFAULT_CONFIG | |
if _LOCAL_CONFIG_FILE_PATH.exists(): | |
try: | |
with open(_LOCAL_CONFIG_FILE_PATH, 'r') as f: | |
config = toml.load(f) | |
except Exception: | |
config = _DEFAULT_CONFIG | |
else: | |
_LOCAL_CONFIG_FILE_PATH.parent.mkdir(parents=True, exist_ok=True) | |
if 'sandbox' not in config: | |
config['sandbox'] = {} | |
if 'trusted_dirs' not in config['sandbox']: | |
config['sandbox']['trusted_dirs'] = [] | |
if folder_path not in config['sandbox']['trusted_dirs']: | |
config['sandbox']['trusted_dirs'].append(folder_path) | |
with open(_LOCAL_CONFIG_FILE_PATH, 'w') as f: | |
toml.dump(config, f) | |
def update_usage_metrics(event: Event, usage_metrics: UsageMetrics) -> None: | |
if not hasattr(event, 'llm_metrics'): | |
return | |
llm_metrics: Metrics | None = event.llm_metrics | |
if not llm_metrics: | |
return | |
usage_metrics.metrics = llm_metrics | |
class ModelInfo(BaseModel): | |
"""Information about a model and its provider.""" | |
provider: str = Field(description='The provider of the model') | |
model: str = Field(description='The model identifier') | |
separator: str = Field(description='The separator used in the model identifier') | |
def __getitem__(self, key: str) -> str: | |
"""Allow dictionary-like access to fields.""" | |
if key == 'provider': | |
return self.provider | |
elif key == 'model': | |
return self.model | |
elif key == 'separator': | |
return self.separator | |
raise KeyError(f'ModelInfo has no key {key}') | |
def extract_model_and_provider(model: str) -> ModelInfo: | |
"""Extract provider and model information from a model identifier. | |
Args: | |
model: The model identifier string | |
Returns: | |
A ModelInfo object containing provider, model, and separator information | |
""" | |
separator = '/' | |
split = model.split(separator) | |
if len(split) == 1: | |
# no "/" separator found, try with "." | |
separator = '.' | |
split = model.split(separator) | |
if split_is_actually_version(split): | |
split = [separator.join(split)] # undo the split | |
if len(split) == 1: | |
# no "/" or "." separator found | |
if split[0] in VERIFIED_OPENAI_MODELS: | |
return ModelInfo(provider='openai', model=split[0], separator='/') | |
if split[0] in VERIFIED_ANTHROPIC_MODELS: | |
return ModelInfo(provider='anthropic', model=split[0], separator='/') | |
# return as model only | |
return ModelInfo(provider='', model=model, separator='') | |
provider = split[0] | |
model_id = separator.join(split[1:]) | |
return ModelInfo(provider=provider, model=model_id, separator=separator) | |
def organize_models_and_providers( | |
models: list[str], | |
) -> dict[str, 'ProviderInfo']: | |
"""Organize a list of model identifiers by provider. | |
Args: | |
models: List of model identifiers | |
Returns: | |
A mapping of providers to their information and models | |
""" | |
result_dict: dict[str, ProviderInfo] = {} | |
for model in models: | |
extracted = extract_model_and_provider(model) | |
separator = extracted.separator | |
provider = extracted.provider | |
model_id = extracted.model | |
# Ignore "anthropic" providers with a separator of "." | |
# These are outdated and incompatible providers. | |
if provider == 'anthropic' and separator == '.': | |
continue | |
key = provider or 'other' | |
if key not in result_dict: | |
result_dict[key] = ProviderInfo(separator=separator, models=[]) | |
result_dict[key].models.append(model_id) | |
return result_dict | |
VERIFIED_PROVIDERS = ['openai', 'azure', 'anthropic', 'deepseek'] | |
VERIFIED_OPENAI_MODELS = [ | |
'gpt-4o', | |
'gpt-4o-mini', | |
'gpt-4-turbo', | |
'gpt-4', | |
'gpt-4-32k', | |
'o1-mini', | |
'o1', | |
'o3-mini', | |
'o3-mini-2025-01-31', | |
] | |
VERIFIED_ANTHROPIC_MODELS = [ | |
'claude-2', | |
'claude-2.1', | |
'claude-3-5-sonnet-20240620', | |
'claude-3-5-sonnet-20241022', | |
'claude-3-5-haiku-20241022', | |
'claude-3-haiku-20240307', | |
'claude-3-opus-20240229', | |
'claude-3-sonnet-20240229', | |
'claude-3-7-sonnet-20250219', | |
'claude-sonnet-4-20250514', | |
'claude-opus-4-20250514', | |
] | |
class ProviderInfo(BaseModel): | |
"""Information about a provider and its models.""" | |
separator: str = Field(description='The separator used in model identifiers') | |
models: list[str] = Field( | |
default_factory=list, description='List of model identifiers' | |
) | |
def __getitem__(self, key: str) -> str | list[str]: | |
"""Allow dictionary-like access to fields.""" | |
if key == 'separator': | |
return self.separator | |
elif key == 'models': | |
return self.models | |
raise KeyError(f'ProviderInfo has no key {key}') | |
def get(self, key: str, default: None = None) -> str | list[str] | None: | |
"""Dictionary-like get method with default value.""" | |
try: | |
return self[key] | |
except KeyError: | |
return default | |
def is_number(char: str) -> bool: | |
return char.isdigit() | |
def split_is_actually_version(split: list[str]) -> bool: | |
return ( | |
len(split) > 1 | |
and bool(split[1]) | |
and bool(split[1][0]) | |
and is_number(split[1][0]) | |
) | |
def read_file(file_path: str | Path) -> str: | |
with open(file_path, 'r') as f: | |
return f.read() | |
def write_to_file(file_path: str | Path, content: str) -> None: | |
with open(file_path, 'w') as f: | |
f.write(content) | |