Spaces:
Running
Running
File size: 4,036 Bytes
a63c61f 81b613f a63c61f 81b613f a63c61f 3be9e60 8425041 a63c61f 8425041 a63c61f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | import os
import uuid
from typing import Optional
from fastapi import APIRouter, Depends, HTTPException
from fastapi.responses import StreamingResponse
from fastapi.security import OAuth2PasswordBearer
from src.core.domain.schemas import ChatRequest, ChatResponse, SearchResponse
from src.core.use_cases.search_use_case import SearchUseCase
from src.core.use_cases.rag_chat_use_case import RagChatUseCase
from src.core.use_cases.agent_router_use_case import AgentRouterUseCase
from src.api.dependencies import get_search_use_case, get_rag_chat_use_case, get_agent_router_use_case
from src.core.security import get_current_user
from src.core.domain.db_models import User
from jose import jwt, JWTError
from src.core.config import settings
from src.infrastructure.database import get_db
from sqlalchemy.orm import Session
router = APIRouter()
# Optional bearer — doesn't raise if token is missing
_optional_bearer = OAuth2PasswordBearer(tokenUrl=f"{settings.API_V1_STR}/auth/login", auto_error=False)
def get_optional_user(
token: Optional[str] = Depends(_optional_bearer),
db: Session = Depends(get_db)
) -> Optional[User]:
"""Returns the authenticated user or None for guests."""
if not token:
return None
try:
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=["HS256"])
if payload.get("type") != "access":
return None
email = payload.get("sub")
if not email:
return None
user = db.query(User).filter(User.email == email).first()
return user if user and user.is_active else None
except JWTError:
return None
@router.post("/search")
def direct_search(
request: ChatRequest,
search_use_case: SearchUseCase = Depends(get_search_use_case),
current_user: User = Depends(get_current_user)
):
"""Lightning-fast hybrid search bypassing the LLM."""
try:
results = search_use_case.execute(
query=request.query,
limit=request.top_k,
source_filter=request.source_filter,
language_filter=request.language_filter,
days_back=getattr(request, 'days_back', None)
)
hits = [{"content": r.content, "metadata": r.metadata, "score": r.score, "doc_id": r.doc_id} for r in results]
return {"results": hits, "query": request.query}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@router.post("/chat/test", response_model=ChatResponse)
async def chat_test(
request: ChatRequest,
agent_router_use_case: AgentRouterUseCase = Depends(get_agent_router_use_case)
):
"""Test RAG chat endpoint without authentication for debugging"""
# Get endpoint from environment variable or use default
os.getenv("RAG_ENDPOINT", "/rag/chat/test")
result = await agent_router_use_case.execute_chat(request)
return result
@router.post("/chat/stream")
async def chat_with_rag_stream(
request: ChatRequest,
router_use_case: AgentRouterUseCase = Depends(get_agent_router_use_case),
current_user: Optional[User] = Depends(get_optional_user)
):
"""Streaming RAG chat. Works for both authenticated users and guests."""
try:
if current_user is None and not request.session_id:
request.session_id = f"guest_{uuid.uuid4().hex[:12]}"
user_id = current_user.id if current_user else None
# X-Accel-Buffering: no tells HF nginx NOT to buffer — fixes ERR_HTTP2_PROTOCOL_ERROR
# NOTE: Do NOT set Transfer-Encoding: chunked — it is prohibited in HTTP/2 (RFC 7540 §8.1.2.2)
headers = {
"X-Accel-Buffering": "no",
"Cache-Control": "no-cache, no-transform",
}
return StreamingResponse(
router_use_case.execute_stream(request, is_guest=(current_user is None), user_id=user_id),
media_type="text/event-stream",
headers=headers,
)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
|