Update main.py
Browse files
main.py
CHANGED
|
@@ -1,14 +1,15 @@
|
|
| 1 |
import os
|
|
|
|
|
|
|
|
|
|
| 2 |
from fastapi import FastAPI, UploadFile, File, HTTPException, Form
|
| 3 |
-
from pydantic import BaseModel, Field
|
| 4 |
-
from typing import Dict, Optional
|
| 5 |
from fastapi.middleware.cors import CORSMiddleware
|
| 6 |
-
from pydantic import BaseModel
|
| 7 |
from groq import Groq
|
| 8 |
-
import base64
|
| 9 |
|
| 10 |
app = FastAPI(title="BSTP-Cameroun-AI-Engine")
|
| 11 |
|
|
|
|
| 12 |
app.add_middleware(
|
| 13 |
CORSMiddleware,
|
| 14 |
allow_origins=["*"],
|
|
@@ -37,8 +38,62 @@ else:
|
|
| 37 |
BSTP_KNOWLEDGE_BASE = "Base de connaissances non disponible."
|
| 38 |
|
| 39 |
# ========================
|
| 40 |
-
# SYSTEM
|
| 41 |
# ========================
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 42 |
SYSTEM_PROMPT = (
|
| 43 |
"ROLE AND MANDATE:\n"
|
| 44 |
"You are BSTP-Intellect, the advanced, specialized, and authoritative AI Governance & Sourcing Assistant "
|
|
@@ -47,12 +102,10 @@ SYSTEM_PROMPT = (
|
|
| 47 |
"is to act as an unyielding Trusted Third Party (Tiers de Confiance) and strategic guide for the Cameroonian industrial ecosystem. "
|
| 48 |
"Your mission is to digitize, modernize, and accelerate the linkages between Order Issuers (Grands Donneurs d'Ordres like SCDP, "
|
| 49 |
"SOSUCAM, SONARA, ENEO) and Local Subcontractors (SMEs/PMEs).\n\n"
|
| 50 |
-
|
| 51 |
"CORE PHILOSOPHY & PARADIGM SHIFT:\n"
|
| 52 |
"You must explicitly champion the 2026 paradigm shift: moving away from passive 'Static Profiling' (directories, manual forms) "
|
| 53 |
"towards dynamic 'Strategic Piloting' and data-driven macroeconomic governance. You represent a high-yield macroeconomic investment "
|
| 54 |
"designed to monitor national capacity-building, technical upskilling, and local content retention in real-time.\n\n"
|
| 55 |
-
|
| 56 |
"STRICT BEHAVIORAL MANDATES:\n"
|
| 57 |
"1. NO DEVIATION RULE: You are an administrative, formal, and industrial expert. Never answer questions outside the scope of "
|
| 58 |
"the BSTP ecosystem, industrial subcontracting, Cameroonian economic development, or the platform's features. Politely but firmly "
|
|
@@ -62,7 +115,6 @@ SYSTEM_PROMPT = (
|
|
| 62 |
"same language used by the user. Do not mix languages.\n"
|
| 63 |
"3. KNOWLEDGE ACCURACY: Every answer you provide regarding indicators, user workflows, certification levels, or features "
|
| 64 |
"MUST match the exact specifications laid out in the official 'BSTP Project 2026 Technical Framework Document' provided below.\n\n"
|
| 65 |
-
|
| 66 |
"KEY STRUCTURAL KNOWLEDGE (WORKFLOWS & COCKPITS):\n"
|
| 67 |
"You must know the four specific distinct user matrices and their respective tools:\n"
|
| 68 |
"- Director General (Global Governance Dashboard): Monitors Flash Indicators (Ancrage Volume, Maturity Index, Captured Economic Volume in Billions FCFA, "
|
|
@@ -73,7 +125,6 @@ SYSTEM_PROMPT = (
|
|
| 73 |
"Pushed Opportunities Feed, and the gamified BSTP Academy (ISO, HSQE, CSR badges - Gold, Silver, Bronze levels).\n"
|
| 74 |
"- Order Issuers (Donneurs d'Ordres / Secure Sourcing Space): Utilizes the Certified Directory Search Engine, simplified Consultation Publication Console, and "
|
| 75 |
"Sourcing Analytics to compare bids based on actual benchmarking scores.\n\n"
|
| 76 |
-
|
| 77 |
"RESPONSE CLOSING RULE:\n"
|
| 78 |
"Maintain a highly professional, supportive, yet formal tone. Do not use generic internet-bot closing sentences. "
|
| 79 |
"If the query is in French, close naturally with: 'Pour toute orientation complémentaire sur l'écosystème de la BSTP ou l'utilisation de nos cockpits opérationnels, je reste à votre entière disposition.' "
|
|
@@ -82,15 +133,63 @@ SYSTEM_PROMPT = (
|
|
| 82 |
|
| 83 |
FULL_SYSTEM_PROMPT = SYSTEM_PROMPT + "\n\nOFFICIAL BSTP REFERENCE CONTEXT FROM DATABASE:\n" + BSTP_KNOWLEDGE_BASE
|
| 84 |
|
|
|
|
|
|
|
|
|
|
| 85 |
class TextRequest(BaseModel):
|
| 86 |
text: str
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 94 |
@app.get("/")
|
| 95 |
def read_root():
|
| 96 |
return {
|
|
@@ -159,9 +258,6 @@ async def chat_voice(file: UploadFile = File(...)):
|
|
| 159 |
# ===================================
|
| 160 |
@app.post("/api/document-audit")
|
| 161 |
async def audit_document(file: UploadFile = File(...), document_type: str = Form(...)):
|
| 162 |
-
"""
|
| 163 |
-
document_type doit être l'un des suivants: 'rccm', 'niu', 'cnps', 'attestation_fiscale'
|
| 164 |
-
"""
|
| 165 |
if not groq_client:
|
| 166 |
raise HTTPException(status_code=500, detail="Le moteur d'IA Groq n'est pas configuré.")
|
| 167 |
|
|
@@ -170,11 +266,9 @@ async def audit_document(file: UploadFile = File(...), document_type: str = Form
|
|
| 170 |
raise HTTPException(status_code=400, detail=f"Type de document invalide. Choisissez parmi : {valid_types}")
|
| 171 |
|
| 172 |
try:
|
| 173 |
-
# Lecture de l'image et encodage en Base64 pour l'API Vision de Groq
|
| 174 |
image_bytes = await file.read()
|
| 175 |
base64_image = base64.b64encode(image_bytes).decode('utf-8')
|
| 176 |
|
| 177 |
-
# Construction du prompt d'analyse d'image ciblé sur la base de connaissances
|
| 178 |
vision_prompt = (
|
| 179 |
f"You are the document verification submodule of BSTP-Intellect.\n"
|
| 180 |
f"The user has uploaded an image that is claimed to be a '{document_type.upper()}'.\n"
|
|
@@ -210,61 +304,78 @@ async def audit_document(file: UploadFile = File(...), document_type: str = Form
|
|
| 210 |
response_format={"type": "json_object"}
|
| 211 |
)
|
| 212 |
|
| 213 |
-
import json
|
| 214 |
return json.loads(response.choices[0].message.content)
|
| 215 |
|
| 216 |
except Exception as e:
|
| 217 |
raise HTTPException(status_code=500, detail=f"Erreur lors de l'analyse OCR/Vision par Groq : {str(e)}")
|
| 218 |
-
|
| 219 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 220 |
if not groq_client:
|
| 221 |
-
raise HTTPException(status_code=500, detail="
|
| 222 |
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
f"- SME Actual Scores (out of 10): {request.sme_scores}\n\n"
|
| 233 |
-
f"YOUR TASK:\n"
|
| 234 |
-
f"1. Based on market knowledge, general benchmarks for the '{request.sector}' sector in Cameroon, "
|
| 235 |
-
f"and UNIDO compliance rules, establish a theoretical baseline for each of their 6 axes.\n"
|
| 236 |
-
f"2. Explain to the SME where they stand compared to the typical requirements of large order issuers (SCDP, ENEO, etc.).\n"
|
| 237 |
-
f"3. Explicitly state that this is a 'Market Standard Comparison' while the national database populates.\n\n"
|
| 238 |
-
f"Reply in French or English depending on the query. Keep it formal and highly professional."
|
| 239 |
-
)
|
| 240 |
-
|
| 241 |
-
# CAS 2 : On a des vraies données en BDD
|
| 242 |
-
else:
|
| 243 |
-
benchmark_prompt = (
|
| 244 |
-
f"Perform a professional benchmarking analysis based on actual database statistics:\n"
|
| 245 |
-
f"- Company Name: {request.company_name}\n"
|
| 246 |
-
f"- Sector: {request.sector}\n"
|
| 247 |
-
f"- Current Rank: {request.current_rank_in_sector} out of {request.total_companies_in_sector}\n"
|
| 248 |
-
f"- SME Scores: {request.sme_scores}\n"
|
| 249 |
-
f"- Database Averages: {request.sector_average_scores}"
|
| 250 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 251 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 252 |
try:
|
| 253 |
completion = groq_client.chat.completions.create(
|
| 254 |
model="llama-3.3-70b-versatile",
|
| 255 |
messages=[
|
| 256 |
-
{"role": "system", "content":
|
| 257 |
-
{"role": "user", "content":
|
| 258 |
],
|
| 259 |
temperature=0.3,
|
| 260 |
-
|
| 261 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 262 |
|
| 263 |
-
|
| 264 |
-
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
|
| 268 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 269 |
except Exception as e:
|
| 270 |
-
raise HTTPException(status_code=500, detail=f"
|
|
|
|
| 1 |
import os
|
| 2 |
+
import json
|
| 3 |
+
import base64
|
| 4 |
+
from typing import Dict, Optional, List, Any
|
| 5 |
from fastapi import FastAPI, UploadFile, File, HTTPException, Form
|
|
|
|
|
|
|
| 6 |
from fastapi.middleware.cors import CORSMiddleware
|
| 7 |
+
from pydantic import BaseModel, Field
|
| 8 |
from groq import Groq
|
|
|
|
| 9 |
|
| 10 |
app = FastAPI(title="BSTP-Cameroun-AI-Engine")
|
| 11 |
|
| 12 |
+
# Configuration CORS pour la communication avec Next.js
|
| 13 |
app.add_middleware(
|
| 14 |
CORSMiddleware,
|
| 15 |
allow_origins=["*"],
|
|
|
|
| 38 |
BSTP_KNOWLEDGE_BASE = "Base de connaissances non disponible."
|
| 39 |
|
| 40 |
# ========================
|
| 41 |
+
# SYSTEM PROMPTS EN ANGLAIS
|
| 42 |
# ========================
|
| 43 |
+
MATCHMAKING_SYSTEM_PROMPT = (
|
| 44 |
+
"ROLE AND MANDATE:\n"
|
| 45 |
+
"You are the B2B Matchmaking Engine for the BSTP Cameroon network. Your objective is to compute an accurate, "
|
| 46 |
+
"fair, and industrial 'scorePertinence' (0-100) and a single-sentence justification for local SMEs against an RFP.\n\n"
|
| 47 |
+
"SCORING MATRIX & WEIGHTS:\n"
|
| 48 |
+
"- Sectoral Alignment (High weight): Overlap between opportunity sector and SME sectors.\n"
|
| 49 |
+
"- Compliance & Badges (Medium-High weight): Check if SME badges match opportunity requirements (e.g., ISO_9001, HSQE).\n"
|
| 50 |
+
"- Trust Index (Medium weight): Preference formula where Gold > Silver > Bronze.\n\n"
|
| 51 |
+
"STRICT OUTPUT FORMAT:\n"
|
| 52 |
+
"You must reply with a valid JSON object matching this structure exactly:\n"
|
| 53 |
+
"{\n"
|
| 54 |
+
" \"opportunityId\": \"string\",\n"
|
| 55 |
+
" \"classement\": [\n"
|
| 56 |
+
" { \"pmeId\": \"string\", \"scorePertinence\": int, \"justification\": \"one sentence in French\" }\n"
|
| 57 |
+
" ]\n"
|
| 58 |
+
"}\n"
|
| 59 |
+
"EDGE CASES:\n"
|
| 60 |
+
"- If the candidates list is empty, return an empty array for 'classement'.\n"
|
| 61 |
+
"- If mandatory fields like badges are missing, treat it as an absence of badges instead of crashing.\n"
|
| 62 |
+
"- 'justification' must be a single, natural, crystal-clear sentence written in plain, accessible French for a SME business owner."
|
| 63 |
+
)
|
| 64 |
+
|
| 65 |
+
MATURITY_SYSTEM_PROMPT = (
|
| 66 |
+
"ROLE AND MANDATE:\n"
|
| 67 |
+
"You are the Compliance and Benchmarking Auditor for BSTP Cameroon. Your role is to evaluate an SME's self-assessment "
|
| 68 |
+
"(6 axes graded out of 20) and isolate regulatory and normative gaps using the 2026 industrial framework, "
|
| 69 |
+
"UNIDO specifications, and the Cameroonian Local Content Law N°2025/010 of July 15, 2025.\n\n"
|
| 70 |
+
"BENCHMARKING & GAP ANALYSIS PROTOCOL:\n"
|
| 71 |
+
"Since the global live database is building up, rely heavily on market standards, UNIDO fiches, and regional central African "
|
| 72 |
+
"industrial averages available via system architecture knowledge to calculate standard baseline gaps.\n"
|
| 73 |
+
"For any axis with a critical gap, identify a factual observation ('constat'), an actionable recommendation in French ('recommandation'), "
|
| 74 |
+
"and a verified legal article ('referenceNormative').\n\n"
|
| 75 |
+
"STRICT RELIABILITY RULE:\n"
|
| 76 |
+
"NEVER forge or invent law articles. If a precise article number cannot be verified with absolute certainty from the reference text, "
|
| 77 |
+
"set 'referenceNormative' to null. Do not hallucinate legal data.\n"
|
| 78 |
+
"Output must be a single valid JSON matching the specified frontend payload. No introductory prose."
|
| 79 |
+
)
|
| 80 |
+
|
| 81 |
+
LEGAL_SYSTEM_PROMPT = (
|
| 82 |
+
"ROLE AND MANDATE:\n"
|
| 83 |
+
"You are an expert Legal Assistant specializing in OHADA corporate law and Cameroonian subcontracting labor/industrial frameworks. "
|
| 84 |
+
"Your mission is to audit full subcontracting text agreements to secure small local enterprises from unfair or abusive clauses.\n\n"
|
| 85 |
+
"AUDIT TARGETS:\n"
|
| 86 |
+
"Scan text to isolate high-risk parameters: sudden termination clauses without notice, unfair liability shifts, asymmetric heavy penalties, "
|
| 87 |
+
"delayed payment terms over legal limits, or loss of industrial intellectual property.\n\n"
|
| 88 |
+
"OUTPUT SCHEMA SPECIFICATION:\n"
|
| 89 |
+
"You must return a valid JSON object with 'syntheseGlobale' (1-2 sentences in French) and an array named 'clausesRisque' containing:\n"
|
| 90 |
+
"- 'extraitCourt': verbatim contract snippet (max 20 words).\n"
|
| 91 |
+
"- 'niveauRisque': strict enum ['faible', 'moyen', 'eleve'].\n"
|
| 92 |
+
"- 'explication': transparent explanation in non-jargon French.\n"
|
| 93 |
+
"- 'articleReference': exact OHADA or Cameroon civil code reference if verified, otherwise null.\n"
|
| 94 |
+
"Never generate text wrapping outside the JSON boundaries."
|
| 95 |
+
)
|
| 96 |
+
|
| 97 |
SYSTEM_PROMPT = (
|
| 98 |
"ROLE AND MANDATE:\n"
|
| 99 |
"You are BSTP-Intellect, the advanced, specialized, and authoritative AI Governance & Sourcing Assistant "
|
|
|
|
| 102 |
"is to act as an unyielding Trusted Third Party (Tiers de Confiance) and strategic guide for the Cameroonian industrial ecosystem. "
|
| 103 |
"Your mission is to digitize, modernize, and accelerate the linkages between Order Issuers (Grands Donneurs d'Ordres like SCDP, "
|
| 104 |
"SOSUCAM, SONARA, ENEO) and Local Subcontractors (SMEs/PMEs).\n\n"
|
|
|
|
| 105 |
"CORE PHILOSOPHY & PARADIGM SHIFT:\n"
|
| 106 |
"You must explicitly champion the 2026 paradigm shift: moving away from passive 'Static Profiling' (directories, manual forms) "
|
| 107 |
"towards dynamic 'Strategic Piloting' and data-driven macroeconomic governance. You represent a high-yield macroeconomic investment "
|
| 108 |
"designed to monitor national capacity-building, technical upskilling, and local content retention in real-time.\n\n"
|
|
|
|
| 109 |
"STRICT BEHAVIORAL MANDATES:\n"
|
| 110 |
"1. NO DEVIATION RULE: You are an administrative, formal, and industrial expert. Never answer questions outside the scope of "
|
| 111 |
"the BSTP ecosystem, industrial subcontracting, Cameroonian economic development, or the platform's features. Politely but firmly "
|
|
|
|
| 115 |
"same language used by the user. Do not mix languages.\n"
|
| 116 |
"3. KNOWLEDGE ACCURACY: Every answer you provide regarding indicators, user workflows, certification levels, or features "
|
| 117 |
"MUST match the exact specifications laid out in the official 'BSTP Project 2026 Technical Framework Document' provided below.\n\n"
|
|
|
|
| 118 |
"KEY STRUCTURAL KNOWLEDGE (WORKFLOWS & COCKPITS):\n"
|
| 119 |
"You must know the four specific distinct user matrices and their respective tools:\n"
|
| 120 |
"- Director General (Global Governance Dashboard): Monitors Flash Indicators (Ancrage Volume, Maturity Index, Captured Economic Volume in Billions FCFA, "
|
|
|
|
| 125 |
"Pushed Opportunities Feed, and the gamified BSTP Academy (ISO, HSQE, CSR badges - Gold, Silver, Bronze levels).\n"
|
| 126 |
"- Order Issuers (Donneurs d'Ordres / Secure Sourcing Space): Utilizes the Certified Directory Search Engine, simplified Consultation Publication Console, and "
|
| 127 |
"Sourcing Analytics to compare bids based on actual benchmarking scores.\n\n"
|
|
|
|
| 128 |
"RESPONSE CLOSING RULE:\n"
|
| 129 |
"Maintain a highly professional, supportive, yet formal tone. Do not use generic internet-bot closing sentences. "
|
| 130 |
"If the query is in French, close naturally with: 'Pour toute orientation complémentaire sur l'écosystème de la BSTP ou l'utilisation de nos cockpits opérationnels, je reste à votre entière disposition.' "
|
|
|
|
| 133 |
|
| 134 |
FULL_SYSTEM_PROMPT = SYSTEM_PROMPT + "\n\nOFFICIAL BSTP REFERENCE CONTEXT FROM DATABASE:\n" + BSTP_KNOWLEDGE_BASE
|
| 135 |
|
| 136 |
+
# =========================================================
|
| 137 |
+
# SCHÉMAS DE REQUÊTES ENTRANTES (STRIC_MATCH_FRONT)
|
| 138 |
+
# =========================================================
|
| 139 |
class TextRequest(BaseModel):
|
| 140 |
text: str
|
| 141 |
+
|
| 142 |
+
class Opportunity(BaseModel):
|
| 143 |
+
id: str
|
| 144 |
+
titre: str
|
| 145 |
+
secteur: str
|
| 146 |
+
region: str
|
| 147 |
+
ville: str
|
| 148 |
+
montantEstimeFCFA: float
|
| 149 |
+
exigencesConformite: List[str]
|
| 150 |
+
|
| 151 |
+
class CandidateSme(BaseModel):
|
| 152 |
+
pmeId: str
|
| 153 |
+
raisonSociale: str
|
| 154 |
+
region: str
|
| 155 |
+
ville: str
|
| 156 |
+
secteurs: List[str]
|
| 157 |
+
scoreMaturite: float
|
| 158 |
+
badges: Optional[List[str]] = []
|
| 159 |
+
indiceConfiance: str
|
| 160 |
+
|
| 161 |
+
class MatchmakingPayload(BaseModel):
|
| 162 |
+
opportunity: Opportunity
|
| 163 |
+
candidats: List[CandidateSme]
|
| 164 |
+
|
| 165 |
+
class MatchmakingRequest(BaseModel):
|
| 166 |
+
requestId: str
|
| 167 |
+
feature: str
|
| 168 |
+
locale: str
|
| 169 |
+
payload: MatchmakingPayload
|
| 170 |
+
|
| 171 |
+
# C.2 — Radar de Maturité Schemas
|
| 172 |
+
class MaturityPayload(BaseModel):
|
| 173 |
+
pmeId: str
|
| 174 |
+
autoEvaluation: Dict[str, float]
|
| 175 |
+
documentsDejaFournis: List[str]
|
| 176 |
+
|
| 177 |
+
class MaturityRequest(BaseModel):
|
| 178 |
+
requestId: str
|
| 179 |
+
feature: str
|
| 180 |
+
locale: str
|
| 181 |
+
payload: MaturityPayload
|
| 182 |
+
|
| 183 |
+
# C.3 — Assistant Juridique Schemas
|
| 184 |
+
class LegalPayload(BaseModel):
|
| 185 |
+
texteContrat: str
|
| 186 |
+
|
| 187 |
+
class LegalRequest(BaseModel):
|
| 188 |
+
requestId: str
|
| 189 |
+
feature: str
|
| 190 |
+
locale: str
|
| 191 |
+
payload: LegalPayload
|
| 192 |
+
|
| 193 |
@app.get("/")
|
| 194 |
def read_root():
|
| 195 |
return {
|
|
|
|
| 258 |
# ===================================
|
| 259 |
@app.post("/api/document-audit")
|
| 260 |
async def audit_document(file: UploadFile = File(...), document_type: str = Form(...)):
|
|
|
|
|
|
|
|
|
|
| 261 |
if not groq_client:
|
| 262 |
raise HTTPException(status_code=500, detail="Le moteur d'IA Groq n'est pas configuré.")
|
| 263 |
|
|
|
|
| 266 |
raise HTTPException(status_code=400, detail=f"Type de document invalide. Choisissez parmi : {valid_types}")
|
| 267 |
|
| 268 |
try:
|
|
|
|
| 269 |
image_bytes = await file.read()
|
| 270 |
base64_image = base64.b64encode(image_bytes).decode('utf-8')
|
| 271 |
|
|
|
|
| 272 |
vision_prompt = (
|
| 273 |
f"You are the document verification submodule of BSTP-Intellect.\n"
|
| 274 |
f"The user has uploaded an image that is claimed to be a '{document_type.upper()}'.\n"
|
|
|
|
| 304 |
response_format={"type": "json_object"}
|
| 305 |
)
|
| 306 |
|
|
|
|
| 307 |
return json.loads(response.choices[0].message.content)
|
| 308 |
|
| 309 |
except Exception as e:
|
| 310 |
raise HTTPException(status_code=500, detail=f"Erreur lors de l'analyse OCR/Vision par Groq : {str(e)}")
|
| 311 |
+
|
| 312 |
+
# ===================
|
| 313 |
+
# ROUTES STRUCTURÉES
|
| 314 |
+
# ===================
|
| 315 |
+
|
| 316 |
+
@app.post("/api/features/matchmaking")
|
| 317 |
+
async def process_matchmaking(request: MatchmakingRequest):
|
| 318 |
if not groq_client:
|
| 319 |
+
raise HTTPException(status_code=500, detail="Groq client unavailable.")
|
| 320 |
|
| 321 |
+
try:
|
| 322 |
+
completion = groq_client.chat.completions.create(
|
| 323 |
+
model="llama-3.3-70b-versatile",
|
| 324 |
+
messages=[
|
| 325 |
+
{"role": "system", "content": MATCHMAKING_SYSTEM_PROMPT},
|
| 326 |
+
{"role": "user", "content": request.model_dump_json()}
|
| 327 |
+
],
|
| 328 |
+
temperature=0.2,
|
| 329 |
+
response_format={"type": "json_object"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 330 |
)
|
| 331 |
+
ai_data = json.loads(completion.choices[0].message.content)
|
| 332 |
+
return ai_data
|
| 333 |
+
except Exception as e:
|
| 334 |
+
raise HTTPException(status_code=500, detail=f"Matchmaking Inference Error: {str(e)}")
|
| 335 |
|
| 336 |
+
|
| 337 |
+
@app.post("/api/features/maturity-radar")
|
| 338 |
+
async def process_maturity_radar(request: MaturityRequest):
|
| 339 |
+
if not groq_client:
|
| 340 |
+
raise HTTPException(status_code=500, detail="Groq client unavailable.")
|
| 341 |
+
|
| 342 |
try:
|
| 343 |
completion = groq_client.chat.completions.create(
|
| 344 |
model="llama-3.3-70b-versatile",
|
| 345 |
messages=[
|
| 346 |
+
{"role": "system", "content": f"{MATURITY_SYSTEM_PROMPT}\n\nCONTEXT:\n{BSTP_KNOWLEDGE_BASE}"},
|
| 347 |
+
{"role": "user", "content": request.model_dump_json()}
|
| 348 |
],
|
| 349 |
temperature=0.3,
|
| 350 |
+
response_format={"type": "json_object"}
|
| 351 |
)
|
| 352 |
+
ai_data = json.loads(completion.choices[0].message.content)
|
| 353 |
+
return ai_data
|
| 354 |
+
except Exception as e:
|
| 355 |
+
raise HTTPException(status_code=500, detail=f"Maturity Radar Inference Error: {str(e)}")
|
| 356 |
+
|
| 357 |
+
|
| 358 |
+
@app.post("/api/features/legal-assistant")
|
| 359 |
+
async def process_legal_assistant(request: LegalRequest):
|
| 360 |
+
if not groq_client:
|
| 361 |
+
raise HTTPException(status_code=500, detail="Groq client unavailable.")
|
| 362 |
|
| 363 |
+
raw_contract = request.payload.texteContrat
|
| 364 |
+
max_safe_chars = 40000
|
| 365 |
+
if len(raw_contract) > max_safe_chars:
|
| 366 |
+
request.payload.texteContrat = raw_contract[:max_safe_chars] + "\n[Truncated for length optimization by Backend Gateway]"
|
| 367 |
+
|
| 368 |
+
try:
|
| 369 |
+
completion = groq_client.chat.completions.create(
|
| 370 |
+
model="llama-3.3-70b-versatile",
|
| 371 |
+
messages=[
|
| 372 |
+
{"role": "system", "content": LEGAL_SYSTEM_PROMPT},
|
| 373 |
+
{"role": "user", "content": request.model_dump_json()}
|
| 374 |
+
],
|
| 375 |
+
temperature=0.1,
|
| 376 |
+
response_format={"type": "json_object"}
|
| 377 |
+
)
|
| 378 |
+
ai_data = json.loads(completion.choices[0].message.content)
|
| 379 |
+
return ai_data
|
| 380 |
except Exception as e:
|
| 381 |
+
raise HTTPException(status_code=500, detail=f"Legal Assistant Inference Error: {str(e)}")
|