brief-extractor/backend/core/config.py
2026-03-06 18:42:46 +00:00

148 lines
No EOL
6.2 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
OPENAI_API_KEY: str = os.getenv('OPENAI_API_KEY', '')
ANTHROPIC_API_KEY: str = os.getenv('ANTHROPIC_API_KEY', '')
GOOGLE_API_KEY: str = os.getenv('GOOGLE_API_KEY', '')
LLAMACLOUD_API_KEY: str = 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()