cleanup: убрать мёртвые env-переменные, консолидировать чтение в agent/_config.py

Удалены из кода:
- JWT_SECRET — мёртвая (decode_token использует AUTH_SECRET_KEY)
- SESSION_SECRET_KEY — заменён на прямой AUTH_SECRET_KEY
- POSTGRES_URL — deprecated fallback, удалён из database.py и reencrypt.py

Консолидировано чтение env-переменных agent-модуля:
- Создан agent/_config.py — единый модуль для FASTAPI_URL,
  SERVICE_JWT, GRADIO_*, STORAGE_ROOT, AGENT_* (9 констант)
- Все agent/*.py импортируют из _config вместо разрозненных os.getenv

Удалены or-дефолты (безопасность):
- agent/langgraph_setup.py — удалён hardcoded DB URL postgres:postgres
- agent/langgraph_setup.py — удалены fallback API URL и model name
- scripts/reencrypt.py — удалён hardcoded DB URL postgres:postgres
- plugins/llm_analysis/service.py — удалены or-дефолты URL/app name

.env.example — минимализация:
- backend/.env.example: только 4 обязательные переменные
- root/.env.example: обязательные + docker + SSO/админ

Обновлены тесты (139 passed)
This commit is contained in:
2026-07-04 14:58:43 +03:00
parent a99c1d6d01
commit ec9bc76a37
16 changed files with 163 additions and 179 deletions

View File

@@ -1,7 +1,7 @@
# backend/src/agent/langgraph_setup.py
# #region AgentChat.LangGraph.Setup [C:4] [TYPE Module] [SEMANTICS agent-chat,langgraph,agent]
# @defgroup AgentChat LangGraph agent setup: create_react_agent with PostgresSaver.
# @PRE LLM provider configured. Priority: 1) llm_config param 2) env vars LLM_API_KEY/LLM_BASE_URL/LLM_MODEL.
# @PRE LLM provider configured via backend API /api/agent/llm-config.
# @POST Compiled StateGraph ready for astream_events().
# @SIDE_EFFECT Initializes checkpointer and message history tables on first call.
# @RELATION DEPENDS_ON -> [AgentChat.Tools]
@@ -20,6 +20,7 @@ from langgraph.checkpoint.postgres.aio import AsyncPostgresSaver
from langgraph.prebuilt import create_react_agent
from psycopg.rows import dict_row
from src.agent._config import FASTAPI_URL, AGENT_CONFIRM_TOOLS, AGENT_INTERRUPT_BEFORE as _INTERRUPT_BEFORE
from src.core.logger import logger
# ── Monkey-patch: OpenAI SDK for Pydantic BaseModel classes ──
@@ -79,7 +80,7 @@ async def init_checkpointer() -> None:
global _CHECKPOINTER, _CHECKPOINTER_INIT, _CHECKPOINTER_CONN
if _CHECKPOINTER_INIT:
return
db_url = os.getenv("DATABASE_URL", "postgresql+psycopg2://postgres:postgres@localhost:5432/ss_tools")
db_url = os.getenv("DATABASE_URL")
# Convert SQLAlchemy-style URL to psycopg format
pg_url = db_url.replace("postgresql+psycopg2://", "postgres://").replace("postgresql://", "postgres://")
_CHECKPOINTER_CONN = await psycopg.AsyncConnection.connect(pg_url, autocommit=True, row_factory=dict_row)
@@ -101,11 +102,11 @@ async def _fetch_llm_config() -> dict | None:
"""Fetch LLM config from FastAPI.
Called on every create_agent() to pick up Admin UI changes immediately.
Falls back to cached config or env vars on failure.
Falls back to cached config if fetch fails.
"""
global _llm_config
try:
fastapi_url = os.getenv("FASTAPI_URL", "http://localhost:8000")
fastapi_url = FASTAPI_URL
async with httpx.AsyncClient(timeout=5) as client:
resp = await client.get(f"{fastapi_url}/api/agent/llm-config")
if resp.status_code == 200:
@@ -121,9 +122,9 @@ async def _fetch_llm_config() -> dict | None:
def _interrupt_before_from_env() -> list[str]:
"""Return LangGraph node names that must pause for HITL confirmation."""
if (os.getenv("AGENT_CONFIRM_TOOLS", "") or "").strip().lower() in ("true", "1", "yes"):
if AGENT_CONFIRM_TOOLS:
return ["tools"]
raw = os.getenv("AGENT_INTERRUPT_BEFORE", "") or ""
raw = _INTERRUPT_BEFORE
if not raw:
return []
return [name.strip() for name in raw.split(",") if name.strip()]
@@ -136,10 +137,9 @@ async def create_agent(
):
"""Create the LangGraph agent with PostgreSQL checkpointer and message history.
LLM configuration priority:
1. llm_config from FastAPI /api/agent/llm-config (fetched on every call)
2. Environment vars: LLM_API_KEY, LLM_BASE_URL, LLM_MODEL
3. Defaults: gpt-4o, https://api.openai.com/v1
LLM configuration source:
llm_config from FastAPI /api/agent/llm-config (fetched on every call).
If backend has no configured provider, agent raises an error.
Returns a compiled StateGraph ready for astream_events().
interrupt_before is set from AGENT_CONFIRM_TOOLS (or AGENT_INTERRUPT_BEFORE env var)
@@ -152,19 +152,13 @@ async def create_agent(
if config and config.get("configured"):
api_key = config["api_key"]
base_url = config.get("base_url") or "https://api.openai.com/v1"
model = config.get("default_model") or "gpt-4o-mini"
base_url = config.get("base_url")
model = config.get("default_model")
config_source = "FastAPI"
else:
api_key = os.getenv("LLM_API_KEY")
base_url = os.getenv("LLM_BASE_URL", "https://api.openai.com/v1")
model = os.getenv("LLM_MODEL", "gpt-4o")
config_source = "env vars"
logger.explore(
"LLM config not found in FastAPI, falling back to env vars",
payload={"model": model, "provider_type": config.get("provider_type") if config else None},
error="No configured LLM provider in FastAPI",
extra={"src": "AgentChat.LangGraph.Setup"},
raise RuntimeError(
"No LLM provider configured in backend. "
"Configure one via Settings → AI Providers in the web UI."
)
logger.reason(