This commit is contained in:
2026-04-10 00:13:29 +03:00
parent 214c9495f3
commit d30b5614f4
4 changed files with 137 additions and 39 deletions

View File

@@ -16,7 +16,7 @@ def build_database_url() -> str:
port = _env("POSTGRES_PORT", "5432")
db = _env("POSTGRES_DB", "devops_portal")
user = _env("POSTGRES_USER", "devops_portal")
password = _env("POSTGRES_PASSWORD", "change-me")
password = _env("POSTGRES_PASSWORD", "ADzXo0Gm!")
return f"postgresql+psycopg://{user}:{password}@{host}:{port}/{db}"

View File

@@ -6,7 +6,7 @@ import requests
import yaml
from fastapi import Depends, FastAPI, HTTPException
from pydantic import BaseModel
from sqlalchemy import select
from sqlalchemy import delete, select
from sqlalchemy.orm import Session
from sqlalchemy.exc import SQLAlchemyError
@@ -39,7 +39,7 @@ from backend.auth import ( # noqa: E402
verify_password,
)
from backend.db import engine, get_db # noqa: E402
from backend.models import Base, User # noqa: E402
from backend.models import Base, User, UserChatMessage # noqa: E402
class GenerationRequest(BaseModel):
@@ -169,9 +169,46 @@ def me(user: User = Depends(get_current_user)):
return {"id": str(user.id), "email": user.email, "full_name": user.full_name, "is_admin": user.is_admin}
@app.get("/chat/history")
def chat_history(
limit: int = 500,
db: Session = Depends(get_db),
user: User | None = Depends(get_current_user) if REQUIRE_AUTH_FOR_GENERATE else None,
):
if user is None:
return {"messages": []}
lim = max(1, min(limit, 2000))
rows = (
db.execute(
select(UserChatMessage)
.where(UserChatMessage.user_id == user.id)
.order_by(UserChatMessage.created_at.asc())
.limit(lim)
)
.scalars()
.all()
)
return {
"messages": [{"role": r.role, "content": r.content} for r in rows],
}
@app.delete("/chat/history")
def chat_history_clear(
db: Session = Depends(get_db),
user: User | None = Depends(get_current_user) if REQUIRE_AUTH_FOR_GENERATE else None,
):
if user is None:
return {"ok": True}
db.execute(delete(UserChatMessage).where(UserChatMessage.user_id == user.id))
db.commit()
return {"ok": True}
@app.post("/chat")
def chat_with_ollama(
req: ChatRequest,
db: Session = Depends(get_db),
user: User | None = Depends(get_current_user) if REQUIRE_AUTH_FOR_GENERATE else None,
):
if not req.messages:
@@ -193,6 +230,29 @@ def chat_with_ollama(
content = msg.get("content", "")
if not content and isinstance(data.get("response"), str):
content = data["response"]
if user is not None:
last = req.messages[-1]
if last.role == "user":
try:
db.add(
UserChatMessage(
user_id=user.id,
role="user",
content=last.content,
)
)
db.add(
UserChatMessage(
user_id=user.id,
role="assistant",
content=content,
)
)
db.commit()
except SQLAlchemyError:
db.rollback()
return {"role": "assistant", "content": content}
except requests.HTTPError as e:
detail = e.response.text if e.response is not None else str(e)

View File

@@ -3,7 +3,7 @@ from __future__ import annotations
import uuid
from datetime import datetime, timezone
from sqlalchemy import Boolean, DateTime, String
from sqlalchemy import Boolean, DateTime, ForeignKey, String, Text
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
@@ -27,3 +27,19 @@ class User(Base):
is_admin: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_now, nullable=False)
class UserChatMessage(Base):
"""История чата с ИИ (Ollama) по пользователю."""
__tablename__ = "user_chat_messages"
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
user_id: Mapped[uuid.UUID] = mapped_column(
ForeignKey("users.id", ondelete="CASCADE"),
index=True,
nullable=False,
)
role: Mapped[str] = mapped_column(String(32), nullable=False)
content: Mapped[str] = mapped_column(Text, nullable=False)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_now, nullable=False)