ac-tool/backend/core/config.py
Vadym Samoilenko 44a4fb7e06 Revert Google model to gemini-3.1-pro-preview
The error was from gemini-2.0-flash-exp (old docker-compose default),
not from gemini-3.1-pro-preview which is valid and working.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 15:53:12 +00:00

150 lines
No EOL
6.5 KiB
Python
Executable file

"""
Configuration management for Enhanced Brief Processing System
Loads environment variables and provides configuration validation
"""
import os
from typing import List, Dict, Any, Optional
from dotenv import load_dotenv
# Load environment variables from .env file
load_dotenv()
class Config:
"""Centralized configuration management"""
# API Keys — support both docker-compose names and legacy names
OPENAI_API_KEY: str = os.getenv('OPENAI_API_KEY', '')
ANTHROPIC_API_KEY: str = os.getenv('ANTHROPIC_API_KEY', '')
# GEMINI_API_KEY is the docker-compose / .env name; GOOGLE_API_KEY is the legacy name
GOOGLE_API_KEY: str = os.getenv('GEMINI_API_KEY') or os.getenv('GOOGLE_API_KEY', '')
# LLAMA_CLOUD_API_KEY is the docker-compose / official SDK name
LLAMACLOUD_API_KEY: str = os.getenv('LLAMA_CLOUD_API_KEY') or os.getenv('LLAMACLOUD_API_KEY', '')
# OpenAI Configuration
OPENAI_MODEL: str = os.getenv('OPENAI_MODEL', 'gpt-5.1')
OPENAI_REASONING_EFFORT: str = os.getenv('OPENAI_REASONING_EFFORT', 'medium')
OPENAI_TIMEOUT: int = int(os.getenv('OPENAI_TIMEOUT', '3600'))
OPENAI_MAX_RETRIES: int = int(os.getenv('OPENAI_MAX_RETRIES', '2'))
# Google Configuration
GOOGLE_MODEL: str = os.getenv('GOOGLE_MODEL', 'gemini-3.1-pro-preview')
GOOGLE_TEMPERATURE: float = float(os.getenv('GOOGLE_TEMPERATURE', '0.1'))
GOOGLE_MAX_OUTPUT_TOKENS: int = int(os.getenv('GOOGLE_MAX_OUTPUT_TOKENS', '8192'))
GOOGLE_THINKING_BUDGET: int = int(os.getenv('GOOGLE_THINKING_BUDGET', '12000'))
GOOGLE_TIMEOUT: int = int(os.getenv('GOOGLE_TIMEOUT', '300'))
# Anthropic Configuration
ANTHROPIC_MODEL_OPUS: str = os.getenv('ANTHROPIC_MODEL_OPUS', 'claude-opus-4-5-20251101')
ANTHROPIC_MODEL_SONNET: str = os.getenv('ANTHROPIC_MODEL_SONNET', 'claude-sonnet-4-5-20250929')
ANTHROPIC_TEMPERATURE: float = float(os.getenv('ANTHROPIC_TEMPERATURE', '0.1'))
ANTHROPIC_MAX_TOKENS: int = int(os.getenv('ANTHROPIC_MAX_TOKENS', '32000'))
ANTHROPIC_THINKING_BUDGET: int = int(os.getenv('ANTHROPIC_THINKING_BUDGET', '12000'))
ANTHROPIC_TIMEOUT: int = int(os.getenv('ANTHROPIC_TIMEOUT', '300'))
# Processing Configuration
DEFAULT_PRIMARY_MODELS: str = os.getenv('DEFAULT_PRIMARY_MODELS', 'openai-gpt51,anthropic-sonnet45,google-gemini31')
DEFAULT_CONSOLIDATION_MODEL: str = os.getenv('DEFAULT_CONSOLIDATION_MODEL', 'openai-gpt51')
MINIMUM_SUCCESS_THRESHOLD: int = int(os.getenv('MINIMUM_SUCCESS_THRESHOLD', '1'))
ENABLE_COST_ESTIMATION: bool = os.getenv('ENABLE_COST_ESTIMATION', 'true').lower() == 'true'
MAX_PROCESSING_COST_USD: float = float(os.getenv('MAX_PROCESSING_COST_USD', '10.00'))
# Model Pricing (per 1M tokens)
PRICING = {
'openai-gpt51': {
'input': 1.25,
'cached_input': 0.625,
'output': 10.00
},
'anthropic-opus45': {
'input': 5.00,
'output': 25.00
},
'anthropic-sonnet45': {
'input': 3.00,
'output': 15.00
},
'google-gemini31': {
'input': 1.25,
'output': 5.00
}
}
# Model mappings for CLI compatibility
MODEL_MAPPINGS = {
'openai-gpt51': ('openai', OPENAI_MODEL),
'anthropic-opus45': ('anthropic', ANTHROPIC_MODEL_OPUS),
'anthropic-sonnet45': ('anthropic', ANTHROPIC_MODEL_SONNET),
'google-gemini31': ('google', GOOGLE_MODEL)
}
@classmethod
def validate_api_keys(cls) -> Dict[str, bool]:
"""Validate that required API keys are set"""
return {
'openai': bool(cls.OPENAI_API_KEY and cls.OPENAI_API_KEY != 'your-openai-api-key-here'),
'anthropic': bool(cls.ANTHROPIC_API_KEY and cls.ANTHROPIC_API_KEY != 'your-anthropic-api-key-here'),
'google': bool(cls.GOOGLE_API_KEY and cls.GOOGLE_API_KEY != 'your-google-api-key-here'),
'llamacloud': bool(cls.LLAMACLOUD_API_KEY and cls.LLAMACLOUD_API_KEY != 'your-llamacloud-api-key-here')
}
@classmethod
def get_provider_config(cls, provider: str) -> Dict[str, Any]:
"""Get configuration for a specific provider"""
if provider == 'openai':
return {
'api_key': cls.OPENAI_API_KEY,
'model': cls.OPENAI_MODEL,
'reasoning_effort': cls.OPENAI_REASONING_EFFORT,
'timeout': cls.OPENAI_TIMEOUT,
'max_retries': cls.OPENAI_MAX_RETRIES
}
elif provider == 'google':
return {
'api_key': cls.GOOGLE_API_KEY,
'model': cls.GOOGLE_MODEL,
'temperature': cls.GOOGLE_TEMPERATURE,
'max_output_tokens': cls.GOOGLE_MAX_OUTPUT_TOKENS,
'thinking_budget': cls.GOOGLE_THINKING_BUDGET,
'timeout': cls.GOOGLE_TIMEOUT
}
elif provider == 'anthropic':
return {
'api_key': cls.ANTHROPIC_API_KEY,
'model_opus': cls.ANTHROPIC_MODEL_OPUS,
'model_sonnet': cls.ANTHROPIC_MODEL_SONNET,
'temperature': cls.ANTHROPIC_TEMPERATURE,
'max_tokens': cls.ANTHROPIC_MAX_TOKENS,
'thinking_budget': cls.ANTHROPIC_THINKING_BUDGET,
'timeout': cls.ANTHROPIC_TIMEOUT
}
else:
raise ValueError(f"Unknown provider: {provider}")
@classmethod
def get_default_primary_models(cls) -> List[str]:
"""Get default list of primary analysis models"""
return cls.DEFAULT_PRIMARY_MODELS.split(',')
@classmethod
def get_model_info(cls, model_key: str) -> tuple:
"""Get provider and model name for a model key"""
if model_key not in cls.MODEL_MAPPINGS:
raise ValueError(f"Unknown model key: {model_key}. Available: {list(cls.MODEL_MAPPINGS.keys())}")
return cls.MODEL_MAPPINGS[model_key]
@classmethod
def estimate_cost(cls, model_key: str, input_tokens: int, output_tokens: int, cached_tokens: int = 0) -> float:
"""Estimate processing cost for a model"""
if model_key not in cls.PRICING:
return 0.0
pricing = cls.PRICING[model_key]
input_cost = (input_tokens / 1_000_000) * pricing['input']
output_cost = (output_tokens / 1_000_000) * pricing['output']
cached_cost = (cached_tokens / 1_000_000) * pricing.get('cached_input', pricing['input'])
return input_cost + output_cost + cached_cost
# Global config instance
config = Config()