Update main.py
Browse files
main.py
CHANGED
|
@@ -1,8 +1,9 @@
|
|
| 1 |
import os
|
| 2 |
-
from fastapi import FastAPI, UploadFile, File, HTTPException
|
| 3 |
from fastapi.middleware.cors import CORSMiddleware
|
| 4 |
from pydantic import BaseModel
|
| 5 |
from groq import Groq
|
|
|
|
| 6 |
|
| 7 |
app = FastAPI(title="BSTP-Cameroun-AI-Engine")
|
| 8 |
|
|
@@ -18,7 +19,7 @@ app.add_middleware(
|
|
| 18 |
GROQ_API_KEY = os.getenv("GROQ_API_KEY")
|
| 19 |
|
| 20 |
if not GROQ_API_KEY:
|
| 21 |
-
print("
|
| 22 |
|
| 23 |
groq_client = Groq(api_key=GROQ_API_KEY) if GROQ_API_KEY else None
|
| 24 |
|
|
@@ -31,12 +32,12 @@ if os.path.exists(KNOWLEDGE_FILE_PATH):
|
|
| 31 |
with open(KNOWLEDGE_FILE_PATH, "r", encoding="utf-8") as f:
|
| 32 |
BSTP_KNOWLEDGE_BASE = f.read()
|
| 33 |
else:
|
| 34 |
-
print(f"
|
| 35 |
BSTP_KNOWLEDGE_BASE = "Base de connaissances non disponible."
|
| 36 |
|
| 37 |
-
# ========================
|
| 38 |
# SYSTEM PROMPT EN ANGLAIS
|
| 39 |
-
# ========================
|
| 40 |
SYSTEM_PROMPT = (
|
| 41 |
"ROLE AND MANDATE:\n"
|
| 42 |
"You are BSTP-Intellect, the advanced, specialized, and authoritative AI Governance & Sourcing Assistant "
|
|
@@ -78,7 +79,6 @@ SYSTEM_PROMPT = (
|
|
| 78 |
"If the query is in English, close naturally with: 'For any further guidance regarding the BSTP ecosystem or the execution of our operational cockpits, I remain entirely at your disposal.'"
|
| 79 |
)
|
| 80 |
|
| 81 |
-
# Assemblage final du Prompt et du contexte de la base de connaissances
|
| 82 |
FULL_SYSTEM_PROMPT = SYSTEM_PROMPT + "\n\nOFFICIAL BSTP REFERENCE CONTEXT FROM DATABASE:\n" + BSTP_KNOWLEDGE_BASE
|
| 83 |
|
| 84 |
class TextRequest(BaseModel):
|
|
@@ -106,7 +106,7 @@ async def chat_text(request: TextRequest):
|
|
| 106 |
{"role": "system", "content": FULL_SYSTEM_PROMPT},
|
| 107 |
{"role": "user", "content": request.text}
|
| 108 |
],
|
| 109 |
-
temperature=0.3,
|
| 110 |
max_tokens=1024
|
| 111 |
)
|
| 112 |
return {"ai_response": completion.choices[0].message.content}
|
|
@@ -122,22 +122,18 @@ async def chat_voice(file: UploadFile = File(...)):
|
|
| 122 |
raise HTTPException(status_code=500, detail="Le moteur d'IA Groq n'est pas configuré.")
|
| 123 |
|
| 124 |
try:
|
| 125 |
-
# Étape A : Transcription de la note vocale industrielle
|
| 126 |
transcription = groq_client.audio.transcriptions.create(
|
| 127 |
file=(file.filename, await file.read()),
|
| 128 |
model="whisper-large-v3",
|
| 129 |
response_format="json"
|
| 130 |
)
|
| 131 |
-
|
| 132 |
user_text = transcription.text
|
| 133 |
-
|
| 134 |
if not user_text or not user_text.strip():
|
| 135 |
return {
|
| 136 |
"user_said": "",
|
| 137 |
"ai_response": "Je n'ai pas pu intercepter de flux audio distinct concernant la BSTP. Pouvez-vous reformuler ? / I could not process the audio instruction. Please try again."
|
| 138 |
}
|
| 139 |
|
| 140 |
-
# Étape B : Soumission du texte transcrit au modèle orienté BSTP
|
| 141 |
completion = groq_client.chat.completions.create(
|
| 142 |
model="llama-3.3-70b-versatile",
|
| 143 |
messages=[
|
|
@@ -147,11 +143,68 @@ async def chat_voice(file: UploadFile = File(...)):
|
|
| 147 |
temperature=0.3,
|
| 148 |
max_tokens=1024
|
| 149 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 150 |
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
"ai_response": completion.choices[0].message.content
|
| 154 |
-
}
|
| 155 |
|
| 156 |
except Exception as e:
|
| 157 |
-
raise HTTPException(status_code=500, detail=f"Erreur
|
|
|
|
| 1 |
import os
|
| 2 |
+
from fastapi import FastAPI, UploadFile, File, HTTPException, Form
|
| 3 |
from fastapi.middleware.cors import CORSMiddleware
|
| 4 |
from pydantic import BaseModel
|
| 5 |
from groq import Groq
|
| 6 |
+
import base64
|
| 7 |
|
| 8 |
app = FastAPI(title="BSTP-Cameroun-AI-Engine")
|
| 9 |
|
|
|
|
| 19 |
GROQ_API_KEY = os.getenv("GROQ_API_KEY")
|
| 20 |
|
| 21 |
if not GROQ_API_KEY:
|
| 22 |
+
print("Attention: GROQ_API_KEY n'est pas configurée dans les secrets de l'espace Hugging Face.")
|
| 23 |
|
| 24 |
groq_client = Groq(api_key=GROQ_API_KEY) if GROQ_API_KEY else None
|
| 25 |
|
|
|
|
| 32 |
with open(KNOWLEDGE_FILE_PATH, "r", encoding="utf-8") as f:
|
| 33 |
BSTP_KNOWLEDGE_BASE = f.read()
|
| 34 |
else:
|
| 35 |
+
print(f"Erreur: Le fichier {KNOWLEDGE_FILE_PATH} est introuvable. Initialisation d'une base vide.")
|
| 36 |
BSTP_KNOWLEDGE_BASE = "Base de connaissances non disponible."
|
| 37 |
|
| 38 |
+
# ========================
|
| 39 |
# SYSTEM PROMPT EN ANGLAIS
|
| 40 |
+
# ========================
|
| 41 |
SYSTEM_PROMPT = (
|
| 42 |
"ROLE AND MANDATE:\n"
|
| 43 |
"You are BSTP-Intellect, the advanced, specialized, and authoritative AI Governance & Sourcing Assistant "
|
|
|
|
| 79 |
"If the query is in English, close naturally with: 'For any further guidance regarding the BSTP ecosystem or the execution of our operational cockpits, I remain entirely at your disposal.'"
|
| 80 |
)
|
| 81 |
|
|
|
|
| 82 |
FULL_SYSTEM_PROMPT = SYSTEM_PROMPT + "\n\nOFFICIAL BSTP REFERENCE CONTEXT FROM DATABASE:\n" + BSTP_KNOWLEDGE_BASE
|
| 83 |
|
| 84 |
class TextRequest(BaseModel):
|
|
|
|
| 106 |
{"role": "system", "content": FULL_SYSTEM_PROMPT},
|
| 107 |
{"role": "user", "content": request.text}
|
| 108 |
],
|
| 109 |
+
temperature=0.3,
|
| 110 |
max_tokens=1024
|
| 111 |
)
|
| 112 |
return {"ai_response": completion.choices[0].message.content}
|
|
|
|
| 122 |
raise HTTPException(status_code=500, detail="Le moteur d'IA Groq n'est pas configuré.")
|
| 123 |
|
| 124 |
try:
|
|
|
|
| 125 |
transcription = groq_client.audio.transcriptions.create(
|
| 126 |
file=(file.filename, await file.read()),
|
| 127 |
model="whisper-large-v3",
|
| 128 |
response_format="json"
|
| 129 |
)
|
|
|
|
| 130 |
user_text = transcription.text
|
|
|
|
| 131 |
if not user_text or not user_text.strip():
|
| 132 |
return {
|
| 133 |
"user_said": "",
|
| 134 |
"ai_response": "Je n'ai pas pu intercepter de flux audio distinct concernant la BSTP. Pouvez-vous reformuler ? / I could not process the audio instruction. Please try again."
|
| 135 |
}
|
| 136 |
|
|
|
|
| 137 |
completion = groq_client.chat.completions.create(
|
| 138 |
model="llama-3.3-70b-versatile",
|
| 139 |
messages=[
|
|
|
|
| 143 |
temperature=0.3,
|
| 144 |
max_tokens=1024
|
| 145 |
)
|
| 146 |
+
return {"user_said": user_text, "ai_response": completion.choices[0].message.content}
|
| 147 |
+
except Exception as e:
|
| 148 |
+
raise HTTPException(status_code=500, detail=f"Erreur du pipeline vocal Groq BSTP : {str(e)}")
|
| 149 |
+
|
| 150 |
+
# ===================================
|
| 151 |
+
# ROUTE 3 : AUDIT DOCUMENTAIRE VISION
|
| 152 |
+
# ===================================
|
| 153 |
+
@app.post("/api/document-audit")
|
| 154 |
+
async def audit_document(file: UploadFile = File(...), document_type: str = Form(...)):
|
| 155 |
+
"""
|
| 156 |
+
document_type doit être l'un des suivants: 'rccm', 'niu', 'cnps', 'attestation_fiscale'
|
| 157 |
+
"""
|
| 158 |
+
if not groq_client:
|
| 159 |
+
raise HTTPException(status_code=500, detail="Le moteur d'IA Groq n'est pas configuré.")
|
| 160 |
+
|
| 161 |
+
valid_types = ['rccm', 'niu', 'cnps', 'attestation_fiscale']
|
| 162 |
+
if document_type.lower() not in valid_types:
|
| 163 |
+
raise HTTPException(status_code=400, detail=f"Type de document invalide. Choisissez parmi : {valid_types}")
|
| 164 |
+
|
| 165 |
+
try:
|
| 166 |
+
# Lecture de l'image et encodage en Base64 pour l'API Vision de Groq
|
| 167 |
+
image_bytes = await file.read()
|
| 168 |
+
base64_image = base64.b64encode(image_bytes).decode('utf-8')
|
| 169 |
+
|
| 170 |
+
# Construction du prompt d'analyse d'image ciblé sur la base de connaissances
|
| 171 |
+
vision_prompt = (
|
| 172 |
+
f"You are the document verification submodule of BSTP-Intellect.\n"
|
| 173 |
+
f"The user has uploaded an image that is claimed to be a '{document_type.upper()}'.\n"
|
| 174 |
+
f"Analyze this image carefully. Perform OCR to extract key administrative data, then cross-reference "
|
| 175 |
+
f"the visual text with the official 'SME PROFILING DOCUMENT COMPLIANCE CRITERIA' in our knowledge base.\n\n"
|
| 176 |
+
f"Provide a structured JSON response in the identical language of the system with the following keys:\n"
|
| 177 |
+
f"1. 'is_valid': boolean (true if it matches the expected document type and criteria, false otherwise).\n"
|
| 178 |
+
f"2. 'extracted_info': a short text summarizing the key numbers, dates, or corporate names identified.\n"
|
| 179 |
+
f"3. 'compliance_report': a detailed administrative explanation of why the document is accepted or rejected based on Cameroon regulations.\n"
|
| 180 |
+
f"Do not return any conversational text around the JSON, return ONLY a valid JSON object."
|
| 181 |
+
)
|
| 182 |
+
response = groq_client.chat.completions.create(
|
| 183 |
+
model="llama-3.2-11b-vision-preview",
|
| 184 |
+
messages=[
|
| 185 |
+
{
|
| 186 |
+
"role": "system",
|
| 187 |
+
"content": f"{BSTP_KNOWLEDGE_BASE}\n\nYou must strictly output valid JSON structures."
|
| 188 |
+
},
|
| 189 |
+
{
|
| 190 |
+
"role": "user",
|
| 191 |
+
"content": [
|
| 192 |
+
{"type": "text", "text": vision_prompt},
|
| 193 |
+
{
|
| 194 |
+
"type": "image_url",
|
| 195 |
+
"image_url": {
|
| 196 |
+
"url": f"data:image/jpeg;base64,{base64_image}"
|
| 197 |
+
}
|
| 198 |
+
}
|
| 199 |
+
]
|
| 200 |
+
}
|
| 201 |
+
],
|
| 202 |
+
temperature=0.1,
|
| 203 |
+
response_format={"type": "json_object"}
|
| 204 |
+
)
|
| 205 |
|
| 206 |
+
import json
|
| 207 |
+
return json.loads(response.choices[0].message.content)
|
|
|
|
|
|
|
| 208 |
|
| 209 |
except Exception as e:
|
| 210 |
+
raise HTTPException(status_code=500, detail=f"Erreur lors de l'analyse OCR/Vision par Groq : {str(e)}")
|