- shared/config.py: add LANGFUSE_SECRET_KEY, LANGFUSE_PUBLIC_KEY, LANGFUSE_HOST - services/chat/app/tracing.py: new module — Langfuse client singleton, create_trace(), get_langfuse_callback(), get_prompt(), link_prompt_to_trace(), score_trace(), flush/shutdown helpers. Gracefully no-ops when keys are missing. - services/chat/app/llm.py: add callbacks param to get_llm() for LangChain callback handler injection - services/chat/app/deep_agent.py: accept langfuse_handler in all run_* and _run_single_agent* functions, pipe callbacks to LLM calls, fetch managed prompts from Langfuse with fallback to hardcoded system prompts - services/chat/app/redis_consumer.py: create Langfuse trace per request (home_request/floating_request), pass callback handler to deep_agent, link prompt name to trace, attach output preview, flush after each request - services/chat/app/main.py: shutdown Langfuse client in lifespan teardown - services/chat/requirements.txt: add langfuse>=2.0.0 Langfuse prompt names: 'home_system', 'floating_system' — create these in the Langfuse dashboard to manage prompts. Without them, hardcoded defaults are used transparently.
99 lines
4.5 KiB
Python
99 lines
4.5 KiB
Python
"""Shared configuration — Pydantic Settings loaded from environment.
|
|
|
|
All services import ``settings`` from here. Each service only uses a subset
|
|
of the vars, but keeping one Settings class avoids fragmentation.
|
|
"""
|
|
|
|
from pathlib import Path
|
|
from typing import Literal
|
|
|
|
from pydantic import field_validator
|
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
|
|
# Locate the repo root (adiuva-api/) so we can load its .env as a fallback.
|
|
# Works whether cwd is adiuva-api/ (monolith) or adiuva-api/services/xyz/ (microservice).
|
|
_this_dir = Path(__file__).resolve().parent # shared/
|
|
_repo_root = _this_dir.parent # adiuva-api/
|
|
_root_env = _repo_root / ".env"
|
|
|
|
|
|
class Settings(BaseSettings):
|
|
# ── Database ─────────────────────────────────────────────────────
|
|
DATABASE_URL: str = "postgresql+asyncpg://postgres:postgres@localhost:5432/adiuva"
|
|
|
|
# ── JWT ────────────────────────────────────────────────────────
|
|
# RS256 public key (PEM). Used by any service that needs to verify
|
|
# JWTs locally (optional — Traefik ForwardAuth handles this in prod).
|
|
# The private key lives ONLY in the Auth Service config.
|
|
JWT_PUBLIC_KEY: str = ""
|
|
|
|
@field_validator("JWT_PUBLIC_KEY", mode="before")
|
|
@classmethod
|
|
def _expand_pem_newlines(cls, v: str) -> str:
|
|
if isinstance(v, str) and r"\n" in v:
|
|
return v.replace(r"\n", "\n")
|
|
return v
|
|
|
|
JWT_ACCESS_TOKEN_EXPIRE_MINUTES: int = 30
|
|
JWT_REFRESH_TOKEN_EXPIRE_DAYS: int = 30
|
|
|
|
# ── Redis ────────────────────────────────────────────────────────
|
|
REDIS_URL: str = "redis://localhost:6379/0"
|
|
|
|
# ── Stripe ───────────────────────────────────────────────────────
|
|
STRIPE_SECRET_KEY: str = ""
|
|
STRIPE_WEBHOOK_SECRET: str = ""
|
|
|
|
# ── S3 ───────────────────────────────────────────────────────────
|
|
S3_BUCKET: str = ""
|
|
S3_REGION: str = "us-east-1"
|
|
S3_ENDPOINT_URL: str = ""
|
|
AWS_ACCESS_KEY_ID: str = ""
|
|
AWS_SECRET_ACCESS_KEY: str = ""
|
|
|
|
# ── Vector stores ────────────────────────────────────────────────
|
|
PINECONE_API_KEY: str = ""
|
|
PINECONE_INDEX: str = "adiuva"
|
|
QDRANT_URL: str = ""
|
|
QDRANT_API_KEY: str = ""
|
|
|
|
# ── LLM providers ────────────────────────────────────────────────
|
|
OPENAI_API_KEY: str = ""
|
|
ANTHROPIC_API_KEY: str = ""
|
|
GOOGLE_API_KEY: str = ""
|
|
CEREBRAS_API_KEY: str = ""
|
|
|
|
LLM_MODEL: str = "gpt-4o"
|
|
LLM_EMBED_MODEL: str = "text-embedding-3-small"
|
|
|
|
GITHUB_COPILOT_TOKEN_DIR: str = ""
|
|
|
|
# ── OAuth (integrations) ─────────────────────────────────────────
|
|
GMAIL_CLIENT_ID: str = ""
|
|
GMAIL_CLIENT_SECRET: str = ""
|
|
MS_CLIENT_ID: str = ""
|
|
MS_CLIENT_SECRET: str = ""
|
|
MS_TENANT_ID: str = "common"
|
|
OAUTH_ENCRYPTION_KEY: str = ""
|
|
|
|
# ── Langfuse (observability) ─────────────────────────────────────
|
|
LANGFUSE_SECRET_KEY: str = ""
|
|
LANGFUSE_PUBLIC_KEY: str = ""
|
|
LANGFUSE_HOST: str = "https://cloud.langfuse.com"
|
|
|
|
# ── CORS ─────────────────────────────────────────────────────────
|
|
CORS_ORIGINS: list[str] = ["app://.", "http://localhost:3000", "http://localhost:5173"]
|
|
|
|
# ── Environment ──────────────────────────────────────────────────
|
|
ENV: Literal["dev", "prod"] = "dev"
|
|
|
|
model_config = SettingsConfigDict(
|
|
# Local .env (cwd) takes priority; root .env is fallback.
|
|
env_file=(".env", str(_root_env)),
|
|
env_file_encoding="utf-8",
|
|
extra="ignore",
|
|
)
|
|
|
|
|
|
settings = Settings()
|