Spaces:
Runtime error
Runtime error
File size: 4,627 Bytes
0fd380e d11e1fe bec7021 dbd9820 5f710b6 1737ef1 0fd380e 5f710b6 037a839 11c5d58 5f710b6 ec7f6a1 5f710b6 d11e1fe 5f710b6 7f617c9 ec7f6a1 5f710b6 ec7f6a1 7f617c9 ec7f6a1 5f710b6 ec7f6a1 5f710b6 ec7f6a1 5f710b6 d11e1fe 0fd380e 2583cf2 d11e1fe ec7f6a1 0fd380e 2c6bd00 5f710b6 ec7f6a1 ecd203a ec7f6a1 63ed81d 5f710b6 ecd203a 5f710b6 ec7f6a1 037a839 5f710b6 1737ef1 d11e1fe 5f710b6 359c625 5f710b6 359c625 5f710b6 359c625 0fd380e 5f710b6 359c625 5f710b6 0fd380e 2c6bd00 5f710b6 bec7021 ec7f6a1 5f710b6 bec7021 5ac0022 d11e1fe 0fd380e d11e1fe d00f6f0 5f710b6 d00f6f0 d5e5243 |
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 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
# ✅ API FastAPI de chunking sémantique intelligent avec fallback automatique
from fastapi import FastAPI
from pydantic import BaseModel
from typing import Optional
# ✅ LlamaIndex (version >= 0.10.0)
from llama_index.core import Document
from llama_index.core.settings import Settings
from llama_index.core.node_parser import SemanticSplitterNodeParser, RecursiveCharacterTextSplitter
from llama_index.llms.llama_cpp import LlamaCPP
from llama_index.core.base.llms.base import BaseLLM
# ✅ Embedding local (basé sur transformers + torch)
from transformers import AutoTokenizer, AutoModel
import torch
import torch.nn.functional as F
import os
# ✅ Initialisation de l'application FastAPI
app = FastAPI()
# ✅ Configuration du cache local de Hugging Face pour économiser l'espace dans le container
CACHE_DIR = "/app/cache"
os.environ["HF_HOME"] = CACHE_DIR
os.environ["TRANSFORMERS_CACHE"] = CACHE_DIR
os.environ["HF_MODULES_CACHE"] = CACHE_DIR
os.environ["HF_HUB_CACHE"] = CACHE_DIR
# ✅ Modèle d'embedding local utilisé pour vectoriser les textes
MODEL_NAME = "BAAI/bge-small-en-v1.5"
tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME, cache_dir=CACHE_DIR)
model = AutoModel.from_pretrained(MODEL_NAME, cache_dir=CACHE_DIR)
def get_embedding(text: str):
"""Fonction pour générer un embedding dense normalisé à partir d’un texte."""
with torch.no_grad():
inputs = tokenizer(text, return_tensors="pt", truncation=True, padding=True)
outputs = model(**inputs)
embeddings = outputs.last_hidden_state[:, 0] # On prend le vecteur [CLS]
return F.normalize(embeddings, p=2, dim=1).squeeze().tolist()
# ✅ Schéma des données attendues dans le POST
class ChunkRequest(BaseModel):
text: str
max_tokens: Optional[int] = 1000
overlap: Optional[int] = 350
source_id: Optional[str] = None
titre: Optional[str] = None
source: Optional[str] = None
type: Optional[str] = None
@app.post("/chunk")
async def chunk_text(data: ChunkRequest):
try:
print(f"\n✅ Texte reçu ({len(data.text)} caractères) : {data.text[:200]}...", flush=True)
# ✅ Chargement du modèle LLM CodeLlama quantifié (GGUF) via URL Hugging Face
llm = LlamaCPP(
model_url="https://huggingface.co/TheBloke/CodeLlama-7B-Instruct-GGUF/resolve/main/codellama-7b-instruct.Q4_K_M.gguf",
temperature=0.1,
max_new_tokens=512,
context_window=2048,
generate_kwargs={"top_p": 0.95},
model_kwargs={"n_gpu_layers": 1},
)
print("✅ Modèle CodeLlama-7B chargé avec succès !")
# ✅ Embedding local pour LlamaIndex
class SimpleEmbedding:
def get_text_embedding(self, text: str):
return get_embedding(text)
# ✅ Configuration du moteur dans LlamaIndex
assert isinstance(llm, BaseLLM), "❌ Le LLM n'est pas compatible avec Settings.llm"
Settings.llm = llm
Settings.embed_model = SimpleEmbedding()
print("✅ Configuration du LLM et de l'embedding terminée.")
# ✅ Document à découper
doc = Document(text=data.text)
# ✅ Split intelligent (semantic)
parser = SemanticSplitterNodeParser.from_defaults(llm=llm)
try:
nodes = parser.get_nodes_from_documents([doc])
print(f"✅ Semantic Splitter : {len(nodes)} chunks générés")
if not nodes:
raise ValueError("Aucun chunk généré par SemanticSplitter")
except Exception as e:
print(f"⚠️ Fallback vers RecursiveCharacterTextSplitter suite à : {e}")
splitter = RecursiveCharacterTextSplitter(
chunk_size=data.max_tokens,
chunk_overlap=data.overlap
)
nodes = splitter.get_nodes_from_documents([doc])
print(f"♻️ Recursive Splitter : {len(nodes)} chunks générés")
# ✅ Construction de la réponse
return {
"chunks": [node.text for node in nodes],
"metadatas": [node.metadata for node in nodes],
"source_id": data.source_id,
"titre": data.titre,
"source": data.source,
"type": data.type,
"error": None # utile pour n8n ou tout autre client
}
except Exception as e:
print(f"❌ Erreur critique : {e}")
return {"error": str(e)}
# ✅ Lancement du serveur si exécution directe (mode debug)
if __name__ == "__main__":
import uvicorn
uvicorn.run("app:app", host="0.0.0.0", port=7860)
|