grantforge-api / backend /core /match /fallback_match.py
GrantForge Bot
Deploy sha-3d4ebe3261a5907e50617c6c64d42c59bd3d4877 — source build (no GHCR)
61d1e19
Raw
History Blame Contribute Delete
5.12 kB
"""Rule-based and static fallbacks when LLM matching is unavailable."""
from __future__ import annotations
from typing import Any, Dict, List
STATIC_FALLBACK_PROGRAMS: List[Dict[str, Any]] = [
{
"id": "feng-smart-path",
"name": "Ścieżka SMART (FENG)",
"type": "FENG",
"match": 72,
"chance": "Średnia",
"amount": "do 2 mln PLN",
"shortDesc": "Wsparcie B+R i wdrożenia innowacji dla MŚP w ramach FENG.",
"fullDesc": "Program dla mikro, małych i średnich przedsiębiorstw realizujących projekty badawczo-rozwojowe.",
"url": "https://www.funduszeeuropejskie.gov.pl/",
"explanation": {
"reason": "Ogólne dopasowanie do profilu innowacyjnego MŚP (tryb awaryjny bez AI).",
"criteria": ["MŚP", "B+R", "innowacje produktowe"],
"risks": "Wymaga weryfikacji manualnej kwalifikowalności i statusu MŚP.",
},
},
{
"id": "parp-step-innovation",
"name": "STEP — Innowacje dla MŚP (PARP)",
"type": "PARP",
"match": 65,
"chance": "Średnia",
"amount": "zależnie od naboru",
"shortDesc": "PARP — wsparcie cyfryzacji i rozwoju kompetencji MŚP.",
"fullDesc": "Programy PARP dla przedsiębiorstw planujących inwestycje w technologie i usługi.",
"url": "https://www.parp.gov.pl/",
"explanation": {
"reason": "Alternatywne dopasowanie dla firm usługowych i produkcyjnych (tryb awaryjny).",
"criteria": ["MŚP", "cyfryzacja", "rozwój"],
"risks": "Sprawdź aktualny status naboru i region.",
},
},
]
def get_static_fallback_programs() -> List[Dict[str, Any]]:
return [dict(p) for p in STATIC_FALLBACK_PROGRAMS]
def scored_to_programs(scored: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
programs: List[Dict[str, Any]] = []
for rec in scored:
score = int(rec.get("relevance_score") or 0)
programs.append(
{
"id": rec.get("grant_id") or rec.get("id") or "unknown",
"name": rec.get("grant_name") or "Program dotacyjny",
"type": _program_type_from_grant(rec),
"match": score,
"chance": "Wysoka" if score >= 80 else ("Średnia" if score >= 60 else "Niska"),
"amount": "zależnie od naboru",
"shortDesc": (rec.get("why_it_fits") or "")[:200],
"fullDesc": rec.get("why_it_fits") or "",
"url": rec.get("official_page_url") or rec.get("regulation_url") or "",
"explanation": {
"reason": rec.get("why_it_fits") or "Dopasowanie rule-based.",
"criteria": rec.get("reasons") or [],
"risks": "; ".join(rec.get("penalties") or []) or "Brak",
},
}
)
return programs
def matches_to_programs(matches) -> List[Dict[str, Any]]:
programs: List[Dict[str, Any]] = []
for m in matches:
score = getattr(m, "score", None) or (m.get("score") if isinstance(m, dict) else 0)
programs.append(
{
"id": getattr(m, "program_id", None) or (m.get("program_id") if isinstance(m, dict) else ""),
"name": getattr(m, "program_name", None) or (m.get("program_name") if isinstance(m, dict) else ""),
"type": getattr(m, "source", None) or (m.get("source") if isinstance(m, dict) else "PROGRAM"),
"match": score,
"chance": "Wysoka" if score >= 80 else ("Średnia" if score >= 60 else "Niska"),
"amount": "TBD",
"shortDesc": getattr(m, "ui_description", None) or (m.get("ui_description") if isinstance(m, dict) else ""),
"fullDesc": getattr(m, "rationale", None) or (m.get("rationale") if isinstance(m, dict) else ""),
"url": getattr(m, "official_url", None) or (m.get("official_url") if isinstance(m, dict) else ""),
"explanation": {
"reason": getattr(m, "rationale", None) or (m.get("rationale") if isinstance(m, dict) else ""),
"criteria": [],
"risks": "Wymaga weryfikacji" if getattr(m, "requires_verification", False) else "Brak",
},
"regulation_grounding": getattr(m, "regulation_grounding", None)
or (m.get("regulation_grounding") if isinstance(m, dict) else ""),
}
)
return programs
def _program_type_from_grant(grant: Dict[str, Any]) -> str:
for key in ("operator", "program", "zrodlo", "source"):
val = (grant.get(key) or "").upper()
if "PARP" in val:
return "PARP"
if "FENG" in val:
return "FENG"
if "NCBR" in val:
return "NCBR"
name = (grant.get("grant_name") or grant.get("name") or "").upper()
if "SMART" in name:
return "SMART"
return "PROGRAM"