import os from dotenv import load_dotenv load_dotenv() # LLM Provider Configuration LLM_PROVIDERS = { "openrouter": { "name": "OpenRouter", "base_url": "https://openrouter.ai/api/v1", "api_key_env": "OPENROUTER_API_KEY", "description": "Access to multiple models through OpenRouter", "supports_all_models": True }, "anthropic": { "name": "Anthropic Direct", "base_url": "https://api.anthropic.com", "api_key_env": "ANTHROPIC_API_KEY", "description": "Direct access to Claude models", "supports_all_models": False, "supported_models": ["anthropic/claude-sonnet-4", "anthropic/claude-3.5-sonnet-20241022", "anthropic/claude-3-sonnet-20240229", "anthropic/claude-3-haiku-20240307", "anthropic/claude-3-opus-20240229"] }, "google": { "name": "Google AI Studio", "base_url": "https://generativelanguage.googleapis.com", "api_key_env": "GOOGLE_API_KEY", "description": "Direct access to Gemini models", "supports_all_models": False, "supported_models": ["google/gemini-2.5-pro-preview-05-06", "google/gemini-pro-vision"] } } # Default provider and model (can be None if no API key is set) DEFAULT_PROVIDER = None DEFAULT_MODEL = None def get_api_key(provider): """Get API key for specified provider""" return os.getenv(LLM_PROVIDERS[provider]["api_key_env"]) def get_available_providers(): """Get list of providers with valid API keys""" available = [] for provider_id, provider_info in LLM_PROVIDERS.items(): if get_api_key(provider_id): available.append(provider_id) return available def get_models_for_provider(provider_id): """Get available models for a specific provider""" available_models = {} for model_id, model_info in AVAILABLE_MODELS.items(): if provider_id in model_info.get("providers", []): available_models[model_id] = model_info return available_models def get_default_provider_and_model(): """Get default provider and model based on available API keys""" try: available_providers = get_available_providers() if not available_providers: return None, None # Prefer providers in order: anthropic, openrouter, google preferred_order = ["anthropic", "openrouter", "google"] selected_provider = None for provider in preferred_order: if provider in available_providers: selected_provider = provider break if not selected_provider: selected_provider = available_providers[0] # Get a recommended model for this provider available_models = get_models_for_provider(selected_provider) recommended_models = {k: v for k, v in available_models.items() if v.get("recommended", False)} if recommended_models: selected_model = list(recommended_models.keys())[0] elif available_models: selected_model = list(available_models.keys())[0] else: selected_model = None return selected_provider, selected_model except Exception: # If anything fails, return None values return None, None # Available models for soil analysis (recommended for structured outputs) AVAILABLE_MODELS = { # Claude Models (Excellent for technical analysis) "anthropic/claude-sonnet-4": { "name": "Claude-4.0 Sonnet", "description": "Latest Claude model with superior reasoning and technical analysis", "cost": "Medium", "recommended": True, "supports_images": True, "providers": ["openrouter", "anthropic"] }, "anthropic/claude-3.5-sonnet-20241022": { "name": "Claude-3.5 Sonnet", "description": "Previous Claude model, excellent reasoning and technical analysis", "cost": "Medium", "recommended": True, "supports_images": True, "providers": ["openrouter", "anthropic"] }, "anthropic/claude-3-sonnet-20240229": { "name": "Claude-3 Sonnet (Legacy)", "description": "Previous version, balanced performance", "cost": "Medium", "recommended": False, "supports_images": True, "providers": ["openrouter", "anthropic"] }, "anthropic/claude-3-haiku-20240307": { "name": "Claude-3 Haiku", "description": "Faster and cheaper, good for basic analysis", "cost": "Low", "recommended": False, "supports_images": True, "providers": ["openrouter", "anthropic"] }, "anthropic/claude-3-opus-20240229": { "name": "Claude-3 Opus", "description": "Most capable legacy model, best for complex analysis", "cost": "High", "recommended": True, "supports_images": True, "providers": ["openrouter", "anthropic"] }, # GPT Models (Good structured output) "openai/gpt-4-turbo": { "name": "GPT-4 Turbo", "description": "Fast and capable, good JSON output", "cost": "Medium", "recommended": True, "supports_images": True, "providers": ["openrouter"] }, "openai/gpt-3.5-turbo": { "name": "GPT-3.5 Turbo", "description": "Fast and cheap, basic analysis", "cost": "Low", "recommended": False, "supports_images": False, "providers": ["openrouter"] }, # Specialized Models "meta-llama/llama-3.1-70b-instruct": { "name": "Llama-3.1 70B", "description": "Open source, good performance", "cost": "Low", "recommended": False, "supports_images": False, "providers": ["openrouter"] }, "mistralai/mixtral-8x7b-instruct": { "name": "Mixtral 8x7B", "description": "Good multilingual support", "cost": "Low", "recommended": False, "supports_images": False, "providers": ["openrouter"] }, # xAI Models "x-ai/grok-3-beta": { "name": "xAI Grok 3", "description": "Latest xAI model with advanced reasoning capabilities (text-only)", "cost": "Medium", "recommended": True, "supports_images": False, "providers": ["openrouter"] }, # Google Models "google/gemini-2.5-pro-preview-05-06": { "name": "Gemini 2.5 Pro Preview", "description": "Latest Google Gemini model with advanced multimodal capabilities", "cost": "Medium", "recommended": True, "supports_images": True, "providers": ["openrouter", "google"] }, "google/gemini-pro-vision": { "name": "Gemini Pro Vision", "description": "Google's multimodal model optimized for vision tasks", "cost": "Medium", "recommended": False, "supports_images": True, "providers": ["openrouter", "google"] } } SOIL_TYPES = { "clay": ["soft clay", "medium clay", "stiff clay", "very stiff clay", "hard clay"], "sand": ["loose sand", "medium dense sand", "dense sand", "very dense sand"], "silt": ["soft silt", "medium silt", "stiff silt"], "gravel": ["loose gravel", "dense gravel"], "rock": ["weathered rock", "soft rock", "hard rock"] } STRENGTH_PARAMETERS = { "clay": "Su (kPa)", "sand": "SPT-N", "silt": "SPT-N", "gravel": "SPT-N", "rock": "UCS (MPa)" }