- Auth/roles (no self-reg), admin user provision, JWT - Analyze: sales kit + initial pain-fit from form/upload - Persona generator: 15 personas (5/tier) w/ pain variety, negotiation, init mode, channel, latent/revealable, wrong_text special - Chat simulator: per-mode initiation, one-shot, hidden signals, judge-LLM debrief+coaching - Trainee loop: win/lose board, weak-areas, user-generated personas - Admin analytics; EN+TH Vue SPA served by Flask - Deploy: Dockerfile, docker-compose, README, eng-log + HANDOFF - Tests (mock LLM): m0/m1/routes/e2e all pass
74 lines
2.4 KiB
Python
74 lines
2.4 KiB
Python
"""Configuration from environment / .env."""
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
from pathlib import Path
|
|
from dotenv import load_dotenv
|
|
|
|
# Load .env from backend/ (project root for this app)
|
|
_BACKEND_DIR = Path(__file__).resolve().parent.parent
|
|
load_dotenv(_BACKEND_DIR / ".env", override=True)
|
|
|
|
|
|
def _get_bool(name: str, default: bool = False) -> bool:
|
|
raw = os.environ.get(name)
|
|
if raw is None:
|
|
return default
|
|
return raw.strip().lower() in {"1", "true", "yes", "on"}
|
|
|
|
|
|
def resolve_llm() -> tuple[str, str, str, str | None]:
|
|
"""Return (base_url, model, api_key, provider_name)."""
|
|
provider = os.environ.get("LLM_PROVIDER", "").strip()
|
|
explicit_base = os.environ.get("LLM_BASE_URL", "").strip()
|
|
explicit_model = os.environ.get("LLM_MODEL_NAME", "").strip()
|
|
api_key = os.environ.get("LLM_API_KEY", "").strip() or None
|
|
|
|
presets = {
|
|
"deepseek": ("https://api.deepseek.com/v1", "deepseek-chat"),
|
|
"openai": ("https://api.openai.com/v1", "gpt-4o-mini"),
|
|
"custom": ("", ""),
|
|
}
|
|
if provider and provider in presets:
|
|
base_url, model = presets[provider]
|
|
if explicit_base:
|
|
base_url = explicit_base
|
|
if explicit_model:
|
|
model = explicit_model
|
|
return base_url, model, api_key or "", provider
|
|
# No/unknown provider: fall back to explicit config
|
|
return (
|
|
explicit_base or "https://api.openai.com/v1",
|
|
explicit_model or "gpt-4o-mini",
|
|
api_key or "",
|
|
provider or None,
|
|
)
|
|
|
|
|
|
class Config:
|
|
APP_NAME = "Sales Trainer"
|
|
SECRET_KEY = os.environ.get("JWT_SECRET", "dev-secret-change-me")
|
|
JWT_ALGO = "HS256"
|
|
JWT_EXPIRES_HOURS = int(os.environ.get("JWT_EXPIRES_HOURS", "24"))
|
|
|
|
DATA_DIR = Path(
|
|
os.environ.get("DATA_DIR", str(_BACKEND_DIR / "data"))
|
|
).resolve()
|
|
|
|
FLASK_HOST = os.environ.get("FLASK_HOST", "0.0.0.0")
|
|
FLASK_PORT = int(os.environ.get("FLASK_PORT", "5001"))
|
|
FLASK_DEBUG = _get_bool("FLASK_DEBUG", True)
|
|
|
|
UPLOAD_MAX_MB = int(os.environ.get("UPLOAD_MAX_MB", "15"))
|
|
ALLOWED_UPLOAD_EXTS = {"pdf", "md", "txt"}
|
|
|
|
# LLM
|
|
LLM_BASE_URL, LLM_MODEL, LLM_API_KEY, LLM_PROVIDER = resolve_llm()
|
|
|
|
ROLES = ("super_admin", "admin", "user")
|
|
|
|
@classmethod
|
|
def ensure_dirs(cls) -> None:
|
|
for name in ("users", "orgs", "groups", "sessions"):
|
|
(cls.DATA_DIR / name).mkdir(parents=True, exist_ok=True)
|