Spaces:
Running
Running
| """Hybrid Grant Intelligence System — admin dashboard, rekomendacje, research.""" | |
| from __future__ import annotations | |
| import logging | |
| import os | |
| from typing import Any, Dict, List, Optional | |
| from fastapi import APIRouter, Depends, Query | |
| from pydantic import BaseModel, Field | |
| from core.subscription.db import SessionLocal | |
| logger = logging.getLogger(__name__) | |
| def get_db(): | |
| db = SessionLocal() | |
| try: | |
| yield db | |
| finally: | |
| db.close() | |
| router = APIRouter(prefix="/intelligence", tags=["grant-intelligence"]) | |
| class CompanyProfileRequest(BaseModel): | |
| name: Optional[str] = "" | |
| size: str = "mikro" | |
| region: Optional[str] = None | |
| voivodeship: Optional[str] = None | |
| pkd: List[str] = Field(default_factory=list) | |
| pkd_codes: List[str] = Field(default_factory=list) | |
| entity_type: str = "przedsiębiorca" | |
| industry: Optional[str] = None | |
| class HybridSearchRequest(BaseModel): | |
| query: str = "" | |
| filters: Dict[str, Any] = Field(default_factory=dict) | |
| limit: int = 50 | |
| trusted_only: bool = False | |
| class GrantResearchRequest(BaseModel): | |
| grant_id: str | |
| company_profile: Optional[CompanyProfileRequest] = None | |
| changes: List[Dict[str, Any]] = Field(default_factory=list) | |
| class ResolveVerificationRequest(BaseModel): | |
| resolution_note: str = "" | |
| status: str = "approved" | |
| async def intelligence_dashboard(db=Depends(get_db)): | |
| """Dashboard administracyjny: research, kompletność, kolejka weryfikacji.""" | |
| from core.grants.catalog_service import get_catalog_stats | |
| from core.grants.human_verification import hvq_stats, list_pending_verifications | |
| from core.grants.live_research import research_source_state | |
| from core.grants.versioning import get_recent_versions | |
| return { | |
| "status": "ok", | |
| "layers": { | |
| "curated_kb": get_catalog_stats(db), | |
| "live_research": research_source_state(db), | |
| "human_verification": hvq_stats(db), | |
| "recent_changes": get_recent_versions(db, limit=15), | |
| "pending_verifications": list_pending_verifications(db, limit=20), | |
| }, | |
| "feature_flags": { | |
| "ENABLE_LIVE_RESEARCH": os.environ.get("ENABLE_LIVE_RESEARCH", "true"), | |
| "ENABLE_GRANT_RESEARCH_AGENT": os.environ.get("ENABLE_GRANT_RESEARCH_AGENT", "true"), | |
| "ENABLE_HYBRID_SEARCH": os.environ.get("ENABLE_HYBRID_SEARCH", "true"), | |
| "ENABLE_PINECONE_SEMANTIC": os.environ.get("ENABLE_PINECONE_SEMANTIC", "true"), | |
| "HUMAN_VERIF_QUEUE": os.environ.get("HUMAN_VERIF_QUEUE", "true"), | |
| }, | |
| "hybrid_search_status": __import__( | |
| "core.grants.hybrid_search_status", fromlist=["get_hybrid_search_status"] | |
| ).get_hybrid_search_status(), | |
| } | |
| async def company_recommendations(request: CompanyProfileRequest, db=Depends(get_db)): | |
| """Rekomendacje dla firmy z relevance score i wyjaśnieniem.""" | |
| from core.grants.recommendations import recommend_grants_for_company | |
| company = request.model_dump() | |
| if company.get("voivodeship") and not company.get("region"): | |
| company["region"] = company["voivodeship"] | |
| if company.get("pkd_codes") and not company.get("pkd"): | |
| company["pkd"] = company["pkd_codes"] | |
| return recommend_grants_for_company(db, company, limit=15) | |
| async def hybrid_search_endpoint(request: HybridSearchRequest, db=Depends(get_db)): | |
| """Hybrydowe wyszukiwanie PostgreSQL FTS + Pinecone semantic.""" | |
| from core.grants.hybrid_search import hybrid_search_catalog | |
| results = hybrid_search_catalog( | |
| db, | |
| query=request.query, | |
| filters=request.filters, | |
| limit=request.limit, | |
| trusted_only=request.trusted_only, | |
| ) | |
| return {"status": "ok", "count": len(results), "grants": results} | |
| async def run_research_cycle(db=Depends(get_db)): | |
| """Uruchamia cykl Live Research (detection + job queue).""" | |
| from core.grants.live_research import run_live_research_cycle | |
| return {"status": "ok", **run_live_research_cycle(db)} | |
| async def list_jobs( | |
| status: Optional[str] = Query(None), | |
| limit: int = Query(50, ge=1, le=200), | |
| db=Depends(get_db), | |
| ): | |
| from core.grants.live_research import list_research_jobs | |
| return {"status": "ok", "jobs": list_research_jobs(db, status=status, limit=limit)} | |
| async def run_research_agent(request: GrantResearchRequest, db=Depends(get_db)): | |
| """Ręczne uruchomienie grant_research_agent LangGraph.""" | |
| from agents.grant_research_agent import run_grant_research_agent | |
| from core.grants.completeness import grant_dict_from_row | |
| from core.grants.models import Grant | |
| row = db.query(Grant).filter(Grant.source_id == request.grant_id).first() | |
| if not row: | |
| return {"status": "error", "detail": "grant_not_found"} | |
| grant_data = grant_dict_from_row(row) | |
| company = request.company_profile.model_dump() if request.company_profile else {} | |
| result = run_grant_research_agent( | |
| grant_data, | |
| changes=request.changes, | |
| company_profile=company, | |
| ) | |
| return {"status": "ok", "result": result} | |
| async def verification_queue( | |
| status: str = Query("pending"), | |
| limit: int = Query(50, ge=1, le=200), | |
| db=Depends(get_db), | |
| ): | |
| from core.grants.human_verification import list_pending_verifications | |
| return { | |
| "status": "ok", | |
| "items": list_pending_verifications(db, status=status, limit=limit), | |
| } | |
| async def resolve_verification_item( | |
| item_id: str, | |
| request: ResolveVerificationRequest, | |
| db=Depends(get_db), | |
| ): | |
| from core.grants.human_verification import resolve_verification | |
| return resolve_verification( | |
| db, | |
| item_id, | |
| resolution_note=request.resolution_note, | |
| new_status=request.status, | |
| ) | |
| async def grant_versions( | |
| grant_id: Optional[str] = Query(None), | |
| limit: int = Query(50, ge=1, le=200), | |
| db=Depends(get_db), | |
| ): | |
| from core.grants.versioning import get_recent_versions | |
| return { | |
| "status": "ok", | |
| "versions": get_recent_versions(db, grant_source_id=grant_id, limit=limit), | |
| } | |
| def register_intelligence_routes(app) -> None: | |
| """Montuje router na aplikacji FastAPI.""" | |
| app.include_router(router) | |
| logger.info("[GrantIntelligence] endpoints registered at /api/grants/intelligence") | |