Spaces:
Running
Running
File size: 3,816 Bytes
ce8f04a | 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 | """Podmienia POST /api/projects/match-program na rule-based MatcherAI (bez LLM domyślnie)."""
import logging
logger = logging.getLogger(__name__)
def apply_projects_match_patch() -> None:
try:
import endpoints.projects as projects_mod
from fastapi import Body, Depends
from endpoints.projects import MatchProgramRequest, get_db, verify_token
except ImportError as e:
logger.warning("[ProjectsMatchPatch] skip: %s", e)
return
if getattr(projects_mod, "_match_program_patched", False):
return
router = projects_mod.router
def _is_match_program_route(route) -> bool:
path = (getattr(route, "path", "") or "").rstrip("/")
if path not in ("/match-program", "/api/projects/match-program"):
return False
methods = getattr(route, "methods", set()) or set()
return "POST" in methods
removed = 0
for route in list(router.routes):
if _is_match_program_route(route):
router.routes.remove(route)
removed += 1
if removed:
logger.info("[ProjectsMatchPatch] usunięto %s legacy POST /match-program", removed)
@router.post("/match-program")
async def match_program_patched(
payload: MatchProgramRequest = Body(...),
token_data: dict = Depends(verify_token),
db=Depends(get_db),
):
try:
from core.match import match_program_service
user_answers = None
if payload.user_answers:
user_answers = [ua.model_dump() for ua in payload.user_answers]
# Tenant-scope spine: same as unpatched projects.match_program —
# without clerk_user_id, try_load_spine can pick another user's
# CompanyProfile for the same NIP (cross-tenant eligibility leak).
result = await match_program_service.run_match_program(
db,
nip=payload.nip,
description=payload.description or "",
user_answers=user_answers,
company_type=payload.company_type,
company_size=payload.company_size,
voivodeship=payload.voivodeship,
innovation_type=payload.innovation_type,
strategy_path=getattr(payload, "strategy_path", None),
eligibility=getattr(payload, "eligibility", None),
clerk_user_id=token_data.get("sub"),
)
return {
"programs": result.get("programs", []),
"clarifying_questions": result.get("clarifying_questions", []),
"data_gaps": result.get("data_gaps", []),
"status": result.get("status", "ok"),
"match_mode": result.get("match_mode"),
"needs_more_info": result.get("needs_more_info", False),
"company_profile": result.get("company_profile"),
"catalog_count": result.get("catalog_count", 0),
"message": result.get("message"),
"strategy_path": result.get("strategy_path"),
}
except Exception as e:
logger.error("Error in match_program: %s", e, exc_info=True)
return {
"status": "error",
"programs": [],
"clarifying_questions": [
"Wystąpił błąd analizy dopasowań. Spróbuj ponownie lub uzupełnij NIP i opis projektu.",
"Podaj kod PKD i województwo realizacji projektu — umożliwi dopasowanie bez AI.",
],
"data_gaps": [],
"error": str(e)[:200],
}
projects_mod._match_program_patched = True
logger.info("[ProjectsMatchPatch] POST /match-program → rule-based MatcherAI aktywne.")
|