File size: 2,389 Bytes
790b5af
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5857a45
790b5af
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8ac8a9d
790b5af
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
"""Configuration management using Pydantic Settings."""

from pydantic import Field
from pydantic_settings import BaseSettings, SettingsConfigDict


class Settings(BaseSettings):
    """Application settings loaded from environment variables."""

    model_config = SettingsConfigDict(
        env_file=".env", env_file_encoding="utf-8", case_sensitive=False, extra="ignore"
    )

    # === LLM Providers ===
    openrouter_api_key: str = Field(..., description="OpenRouter API key")

    # === Search APIs ===
    tavily_api_key: str = Field(..., description="Tavily API key for web search")
    brave_search_api_key: str | None = Field(
        None, description="Brave Search API key (optional backup)"
    )

    # === Observability ===
    langsmith_api_key: str | None = Field(
        None, description="LangSmith API key for tracing"
    )
    langchain_tracing: bool = Field(True, description="Enable LangChain tracing")
    langchain_project: str = Field(
        "market-intelligence-prod", description="LangSmith project name"
    )
    langchain_endpoint: str = Field(
        "https://api.smith.langchain.com", description="LangSmith endpoint"
    )

    # === Application Settings ===
    environment: str = Field(
        "development", description="Environment: development, production"
    )
    default_model: str = Field(
        "x-ai/grok-4.1-fast:free",  # Free for testing; production: anthropic/claude-sonnet-4.5 or google/gemini-3-pro-preview
        description="Default LLM model (via OpenRouter)",
    )
    max_cost_per_run: float = Field(
        2.0, description="Maximum cost per workflow run (USD)"
    )

    # === Optional: Local LLM ===
    ollama_base_url: str | None = Field(
        None, description="Ollama base URL for local models"
    )
    ollama_model: str = Field("llama3.2:3b", description="Ollama model name")

    @property
    def is_production(self) -> bool:
        """Check if running in production."""
        return self.environment.lower() == "production"

    @property
    def openrouter_base_url(self) -> str:
        """OpenRouter API base URL."""
        return "https://openrouter.ai/api/v1"


def get_settings() -> Settings:
    """Get settings instance (lazy-loaded)."""
    return Settings()  # type: ignore[call-arg]


# For convenience, but use get_settings() in tests
settings = None  # Will be initialized when needed