"""Configuration management for LinkedIn Workflow.""" from typing import Optional from pydantic_settings import BaseSettings, SettingsConfigDict from pathlib import Path class Settings(BaseSettings): """Application settings loaded from environment variables.""" # API Keys openai_api_key: str perplexity_api_key: str apify_api_key: str # Supabase supabase_url: str supabase_key: str supabase_service_role_key: str = "" # Required for admin operations like deleting users # Apify apify_actor_id: str = "apimaestro~linkedin-profile-posts" # Web Interface web_password: str = "" session_secret: str = "" # Development debug: bool = False log_level: str = "INFO" # Email Settings smtp_host: str = "" smtp_port: int = 587 smtp_user: str = "" smtp_password: str = "" smtp_from_name: str = "LinkedIn Post System" email_default_recipient: str = "" # Writer Features (can be toggled to disable new features) writer_multi_draft_enabled: bool = True # Generate multiple drafts and select best writer_multi_draft_count: int = 3 # Number of drafts to generate (2-5) writer_semantic_matching_enabled: bool = True # Use semantically similar example posts writer_learn_from_feedback: bool = True # Learn from recurring critic feedback writer_feedback_history_count: int = 10 # Number of past posts to analyze for patterns # Quality Refiner Features (NEW - post-processing improvements) quality_refiner_enabled: bool = True # Enable automatic quality refinement quality_smart_revision_enabled: bool = True # Enable smart revision if auto-refiner not enough quality_min_acceptable_score: int = 80 # Minimum score to accept (even if not perfect) quality_target_score: int = 85 # Target score to achieve quality_max_smart_revisions: int = 1 # Max LLM revisions (0 = disabled, 1 = safe) # User Frontend (LinkedIn OAuth via Supabase) user_frontend_enabled: bool = True # Enable user frontend with LinkedIn OAuth supabase_redirect_url: str = "" # OAuth Callback URL (e.g., https://linkedin.onyva.dev/auth/callback) # LinkedIn API (Custom OAuth for auto-posting) linkedin_client_id: str = "" linkedin_client_secret: str = "" linkedin_redirect_uri: str = "" # e.g., https://yourdomain.com/settings/linkedin/callback # Token Encryption encryption_key: str = "" # Generate with: python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())" # MOCO Integration moco_api_key: str = "" # Token für Authorization-Header moco_domain: str = "" # Subdomain: {domain}.mocoapp.com # Redis redis_url: str = "redis://redis:6379/0" scheduler_enabled: bool = False # True only on dedicated scheduler container model_config = SettingsConfigDict( env_file=".env", env_file_encoding="utf-8", case_sensitive=False ) # Global settings instance settings = Settings() # API pricing per 1M tokens (input, output) API_PRICING = { "gpt-4o": {"input": 2.50, "output": 10.00}, "gpt-4o-mini": {"input": 0.15, "output": 0.60}, "sonar": {"input": 1.00, "output": 1.00}, } def estimate_cost(model: str, prompt_tokens: int, completion_tokens: int) -> float: """Estimate cost in USD for an API call.""" pricing = API_PRICING.get(model, {"input": 1.00, "output": 1.00}) input_cost = (prompt_tokens / 1_000_000) * pricing["input"] output_cost = (completion_tokens / 1_000_000) * pricing["output"] return input_cost + output_cost