negoptimAi / backend /app /config.py
samir12321's picture
Add speech-to-text endpoint for the landing page's voice chat panel
01bd895
Raw
History Blame Contribute Delete
5.77 kB
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
groq_api_key: str = ""
groq_model: str = "llama-3.1-8b-instant"
groq_base_url: str = "https://api.groq.com/openai/v1"
# Speech-to-text for the voice chat panel — same Groq account/credential,
# different endpoint (audio/transcriptions instead of chat/completions).
groq_whisper_model: str = "whisper-large-v3-turbo"
# Comma-separated fallback models tried in order when primary hits rate
# limit (decommissioned entries are skipped automatically at runtime)
groq_fallback_models: str = (
"llama-3.3-70b-versatile,"
"openai/gpt-oss-20b,"
"openai/gpt-oss-120b,"
"qwen/qwen3-32b"
)
# ── Optional extra LLM providers (OpenAI-compatible) ─────────────────────
# Tried in order after Groq when its models are rate-limited/unavailable.
# A provider is skipped entirely when its API key is empty.
cerebras_api_key: str = ""
cerebras_base_url: str = "https://api.cerebras.ai/v1"
cerebras_models: str = "llama-3.3-70b,llama3.1-8b"
gemini_api_key: str = ""
gemini_base_url: str = "https://generativelanguage.googleapis.com/v1beta/openai"
gemini_models: str = "gemini-2.0-flash"
openrouter_api_key: str = ""
openrouter_base_url: str = "https://openrouter.ai/api/v1"
# All current free models on OpenRouter (ordered best → smallest).
# Audio/image/safety/vision-only and sub-2B models are excluded.
# Decommissioned entries are skipped automatically at runtime.
openrouter_models: str = (
"openai/gpt-oss-120b:free,"
"nvidia/nemotron-3-ultra-550b-a55b:free,"
"nousresearch/hermes-3-llama-3.1-405b:free,"
"nvidia/nemotron-3-super-120b-a12b:free,"
"meta-llama/llama-3.3-70b-instruct:free,"
"qwen/qwen3-next-80b-a3b-instruct:free,"
"qwen/qwen3-coder:free,"
"moonshotai/kimi-k2.6:free,"
"google/gemma-4-31b-it:free,"
"google/gemma-4-26b-a4b-it:free,"
"nvidia/nemotron-3-nano-30b-a3b:free,"
"nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free,"
"openai/gpt-oss-20b:free,"
"cognitivecomputations/dolphin-mistral-24b-venice-edition:free,"
"nvidia/nemotron-nano-9b-v2:free,"
"meta-llama/llama-3.2-3b-instruct:free,"
"poolside/laguna-m.1:free,"
"poolside/laguna-xs.2:free,"
"nex-agi/nex-n2-pro:free"
)
# Comma-separated list of allowed browser origins (frontend URLs).
cors_origins: str = "http://localhost:3000,http://127.0.0.1:3000"
# ── Abuse protection / cost control ──────────────────────────────────────
# Required to call POST /ingest over HTTP; empty = endpoint disabled
# (local ingestion via scripts/ingest.py is unaffected).
admin_token: str = ""
rate_limit_chat_per_minute: int = 20 # per client IP
rate_limit_voice_per_minute: int = 15 # per client IP, voice transcription
voice_max_upload_mb: int = 8 # ~60s of webm/opus, generous ceiling
email_max_per_session: int = 5
email_max_per_day: int = 50 # global, resets at UTC midnight
response_cache_ttl_seconds: int = 3600
response_cache_max_entries: int = 200
session_idle_ttl_seconds: int = 6 * 3600
max_sessions: int = 1000
chroma_persist_directory: str = "./chroma_db"
chroma_collection_name: str = "negoptim_knowledge"
embedding_model: str = "intfloat/multilingual-e5-small"
retrieval_top_k: int = 5
chunk_size: int = 700
chunk_overlap: int = 100
max_history_turns: int = 10
knowledge_base_path: str = "../knowledge"
# ── Email ─────────────────────────────────────────────────────────────────
# Priority: Resend (HTTP, works on any host) → SMTP (blocked on HF free tier)
# Either absent → simulation mode (logs + reports success, no real delivery).
resend_api_key: str = ""
resend_from: str = "Negoptim AI <onboarding@resend.dev>"
smtp_host: str = "smtp.gmail.com"
smtp_port: int = 587
smtp_user: str = ""
smtp_password: str = ""
smtp_from: str = "" # defaults to smtp_user when empty
smtp_from_name: str = "Negoptim AI"
# All outgoing mail is force-routed here while testing (per spec).
# Set to "" to deliver to the real recipient.
email_override_to: str = "samirmajzoub9@gmail.com"
@property
def fallback_model_list(self) -> list[str]:
return [m.strip() for m in self.groq_fallback_models.split(",") if m.strip()]
@property
def cerebras_model_list(self) -> list[str]:
return [m.strip() for m in self.cerebras_models.split(",") if m.strip()]
@property
def gemini_model_list(self) -> list[str]:
return [m.strip() for m in self.gemini_models.split(",") if m.strip()]
@property
def openrouter_model_list(self) -> list[str]:
return [m.strip() for m in self.openrouter_models.split(",") if m.strip()]
@property
def cors_origin_list(self) -> list[str]:
return [o.strip() for o in self.cors_origins.split(",") if o.strip()]
@property
def resend_configured(self) -> bool:
return bool(self.resend_api_key)
@property
def smtp_configured(self) -> bool:
return bool(self.smtp_host and self.smtp_user and self.smtp_password)
@property
def smtp_sender(self) -> str:
return self.smtp_from or self.smtp_user
class Config:
env_file = ".env"
settings = Settings()