Spaces:
Running
Running
| import os | |
| import torch | |
| import torch.nn as nn | |
| import torch.nn.functional as F | |
| from typing import Dict, Any | |
| class CommunitySaccoClassifier(nn.Module): | |
| def __init__(self, input_dim=10, hidden_dim=24): | |
| super().__init__() | |
| self.net = nn.Sequential( | |
| nn.Linear(input_dim, hidden_dim), | |
| nn.ReLU(), | |
| nn.Linear(hidden_dim, 3) # 3 classes: Stable, Caution, At Risk | |
| ) | |
| def forward(self, x): | |
| return self.net(x) | |
| class SentiCommunityEngine: | |
| def __init__(self, weights_path: str): | |
| self.model = CommunitySaccoClassifier() | |
| if os.path.exists(weights_path): | |
| self.model.load_state_dict(torch.load(weights_path, map_location=torch.device('cpu'))) | |
| self.model.eval() | |
| def predict(self, text: str) -> Dict[str, Any]: | |
| raw = text.lower() | |
| feat = [ | |
| len(raw) / 10000.0, | |
| 1.0 if "sacco" in raw else 0.0, | |
| 1.0 if "dividend" in raw else 0.0, | |
| 1.0 if "microfinance" in raw else 0.0, | |
| 1.0 if "literacy" in raw else 0.0, | |
| 1.0 if "expenditure" in raw else 0.0, | |
| 1.0 if "budget" in raw else 0.0, | |
| 1.0 if "swahili" in raw else 0.0, | |
| 1.0 if "sheng" in raw else 0.0, | |
| 1.0 if "behavioral" in raw else 0.0 | |
| ] | |
| with torch.no_grad(): | |
| x = torch.tensor([feat], dtype=torch.float32) | |
| logits = self.model(x) | |
| probs = F.softmax(logits, dim=1).numpy()[0] | |
| pred_class = int(logits.argmax(dim=1).item()) | |
| health_states = ["Stable / Liquid", "Caution / Review dividend payouts", "At Risk / High NPLs"] | |
| nudge = "Weka akiba leo kwa maisha bora ya baadaye." | |
| if pred_class == 2: | |
| nudge = "Chunga mfuko wako! Punguza madeni na ongeza hisa zako katika SACCO." | |
| elif pred_class == 1: | |
| nudge = "Tenga bajeti vizuri ili uweze kulipa mkopo wako bila shida." | |
| return { | |
| "sacco_health_status": health_states[pred_class], | |
| "nudge_message": nudge, | |
| "confidence": float(probs[pred_class]), | |
| "framework": "SASRA Sacco Regulations & Financial Literacy Guidelines" | |
| } | |
| # ββ RLM Integration ββββββββββββββββββββββββββββββββββββββββββββββ | |
| class SentiCommunityRLM: | |
| """ | |
| SentiCommunityRLM wraps the shared RLMEngine to provide deep reasoning capabilities | |
| using a dedicated Ollama specialist model (senti-community-rlm). | |
| """ | |
| def __init__(self, model_name: str = "senti-community-rlm"): | |
| import sys | |
| import os | |
| base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | |
| if base_dir not in sys.path: | |
| sys.path.insert(0, base_dir) | |
| from senti.core.engines.superpacks.rlm_engine import RLMEngine | |
| self.engine = RLMEngine(model=model_name) | |
| async def predict_deep(self, text: str, tier: str = "C") -> dict: | |
| import time | |
| context = { | |
| "tier": tier, | |
| "domain": "senticommunity", | |
| "timestamp": time.time(), | |
| } | |
| system_suffix = ( | |
| "Focus on SACCO dividend planning, public budget expenditure explainers, financial literacy teaching, and Sheng/Swahili cognitive behavioral spend nudges." | |
| ) | |
| rlm_response = await self.engine.reason( | |
| query=text, | |
| context=context, | |
| system_suffix=system_suffix | |
| ) | |
| res = rlm_response.to_dict() | |
| # Map RLM decision to legacy SML fields for backward compatibility | |
| decision = res.get("decision", "stable").lower() | |
| health_status = "Stable / Liquid" | |
| nudge = "Weka akiba leo kwa maisha bora ya baadaye." | |
| if any(w in decision for w in ["risk", "poor", "insolvent", "alert"]): | |
| health_status = "At Risk / High NPLs" | |
| nudge = "Chunga mfuko wako! Punguza madeni na ongeza hisa zako katika SACCO." | |
| elif any(w in decision for w in ["caution", "review", "moderate"]): | |
| health_status = "Caution / Review dividend payouts" | |
| nudge = "Tenga bajeti vizuri ili uweze kulipa mkopo wako bila shida." | |
| res["sacco_health_status"] = health_status | |
| res["nudge_message"] = nudge if "swahili" in res.get("justification", "").lower() or "sheng" in res.get("justification", "").lower() else res.get("justification", nudge) | |
| res["confidence"] = res.get("confidence", 0.5) | |
| res["framework"] = "SASRA Sacco Regulations & Financial Literacy Guidelines" | |
| return res | |