grantforge-api / backend /endpoints /grants_match_fix.py
GrantForge Bot
Deploy sha-fa63b2c4b490e66ba3ddbbe9af20584a8573fddd — source build (no GHCR)
6b9ceb7
Raw
History Blame Contribute Delete
4.8 kB
"""Lepsza odpowiedź Matcher AI gdy baza naborów jest pusta."""
import logging
from fastapi import Depends, HTTPException
from endpoints.grants import router, MatchRequest, get_nabory, run_advanced_match_core, MATCH_FALLBACK_QUESTIONS, _rule_based_advanced_match_fallback
from endpoints.projects import get_db
from core.subscription.middleware import verify_token
from core.projects.models import Project
logger = logging.getLogger(__name__)
_orig_route = None
def apply_match_fix():
global _orig_route
for route in list(router.routes):
if getattr(route, "path", "") == "/match" and "POST" in getattr(route, "methods", set()):
router.routes.remove(route)
_orig_route = route
break
@router.post("/match")
async def match_grants_fixed(
request: MatchRequest,
token_data: dict = Depends(verify_token),
db=Depends(get_db),
):
clerk_id = token_data.get("sub")
project = db.query(Project).filter(
Project.id == request.project_id,
Project.clerk_user_id == clerk_id,
).first()
if not project:
raise HTTPException(status_code=404, detail="Projekt nie istnieje lub brak dostępu")
nabory_list: list = []
try:
nabory_resp = await get_nabory(force_refresh=False, db=db)
nabory_list = nabory_resp.get("nabory", [])
stats = nabory_resp.get("count", 0)
if not nabory_list:
return {
"status": "ok",
"project_id": request.project_id,
"needs_more_info": True,
"clarifying_questions": [
"Baza naborów jest pusta. Uruchom import: POST /api/admin/grants/import-wyszukiwarka "
"lub skrypt scripts/import_wyszukiwarka.py z plikiem WYSZUKIWARKA."
],
"matches": [],
"catalog_empty": True,
"hint": "Ustaw WYSZUKIWARKA_JSON=/ścieżka/do/dotacje-2026-06-21.json i zaimportuj katalog.",
}
result = await run_advanced_match_core(
title=project.title or "",
description=project.description or "Brak opisu",
estimated_value=project.estimated_value or 0.0,
external_context=project.external_context or {},
industry=project.industry if hasattr(project, "industry") else "",
user_answers=request.user_answers or [],
nabory_list=nabory_list,
)
if not result.needs_more_info:
external_context = project.external_context or {}
external_context["ai_matches"] = [m.model_dump() for m in result.matches]
if request.user_answers:
external_context["ai_matches_qa_history"] = [
qa.model_dump() for qa in request.user_answers
]
project.external_context = external_context
db.commit()
return {
"status": "ok",
"project_id": request.project_id,
"needs_more_info": result.needs_more_info,
"clarifying_questions": result.clarifying_questions,
"matches": [m.model_dump() for m in result.matches],
"nabory_in_catalog": stats,
}
except Exception as e:
logger.error("Error in match_grants for project %s: %s", request.project_id, e, exc_info=True)
try:
fallback = _rule_based_advanced_match_fallback(
project.title or "",
project.description or "",
project.external_context or {},
request.user_answers or [],
nabory_list,
)
return {
"status": "ok",
"project_id": request.project_id,
"needs_more_info": fallback.needs_more_info,
"clarifying_questions": fallback.clarifying_questions,
"matches": [m.model_dump() for m in fallback.matches],
"fallback_mode": "rule_based",
"detail": str(e)[:200],
}
except Exception:
return {
"status": "error",
"detail": f"Błąd generowania dopasowań przez AI: {str(e)}",
"needs_more_info": True,
"clarifying_questions": MATCH_FALLBACK_QUESTIONS,
"matches": [],
}
logger.info("[grants_match_fix] POST /match patched with catalog-empty handling.")