from __future__ import annotations import asyncio from datetime import datetime import httpx from fastapi import Depends, FastAPI, HTTPException from fastapi.middleware.cors import CORSMiddleware from info import debate_types, explainer, limit_warning, party_colors from .schemas import ( ChatRequest, ChatResponse, FeedbackRequest, FeedbackResponse, SearchRequest, SearchResponse, TalkHit, ) from .services import ChatService, SearchService from backend.routes.chat import router as chat_router from .services.names_autocomplete import router as names_autocomplete_router app = FastAPI(title="Riksdagen API", version="0.1.0") app.add_middleware( CORSMiddleware, allow_origins=["*"], # tighten for production allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) search_service = SearchService() chat_service = ChatService() app.include_router(chat_router) app.include_router(names_autocomplete_router) @app.get("/api/meta") def meta(): return { "parties": party_colors, "debate_types": debate_types, "explainer": explainer, "limit_warning": limit_warning, } @app.post("/api/search", response_model=SearchResponse) def search(payload: SearchRequest): print('PAYLOAD IN APP', payload) results, stats, limit_reached = search_service.search( payload, include_snippets=payload.include_snippets ) print(f'Search service returned {len(results)} results') # Log first result to see structure if results: print('First result structure:', results[0].keys() if isinstance(results[0], dict) else 'not a dict') print('First result _id:', results[0].get('_id') if isinstance(results[0], dict) else 'N/A') # Try to convert results to TalkHit objects hits = [] for idx, hit in enumerate(results): try: talk_hit = TalkHit(**hit) hits.append(talk_hit) except Exception as e: print(f'Error converting result {idx} to TalkHit: {e}') print(f'Problematic result: {hit}') # Continue with other results instead of failing completely continue print(f'Successfully converted {len(hits)} results to TalkHit objects') return SearchResponse( results=hits, stats=stats, active_filters={ "parties": payload.parties, "people": payload.people, "debates": payload.debates, "from_year": payload.from_year, "to_year": payload.to_year, "speaker_ids": payload.speaker_ids, "speaker": payload.speaker, }, limit_reached=limit_reached, ) @app.post("/api/chat", response_model=ChatResponse) def chat(payload: ChatRequest) -> ChatResponse: """ Generate a chat answer plus citations via the retrieval-aware ChatService. Args: payload (ChatRequest): Chat history, retrieval strategy, and result limit. Returns: ChatResponse: Assistant reply and supporting sources. """ if not payload.messages: raise HTTPException(status_code=400, detail="messages cannot be empty") messages = [message.dict() for message in payload.messages] limit = getattr(payload, "top_k", None) if limit is None: limit = getattr(payload, "limit", None) top_k = limit or 5 chat_result = chat_service.get_chat_response( messages=messages, top_k=top_k, ) return ChatResponse(answer=chat_result["answer"], sources=chat_result["sources"])