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>
150 lines
No EOL
6.5 KiB
Python
Executable file
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() |