File size: 4,146 Bytes
d11e1fe
 
bec7021
dbd9820
1737ef1
037a839
1737ef1
037a839
200fee8
 
ec7f6a1
 
 
 
 
 
d11e1fe
 
ec7f6a1
7f617c9
 
ec7f6a1
 
 
 
 
 
 
073d270
7f617c9
ec7f6a1
 
 
 
 
 
 
 
 
 
d11e1fe
 
2583cf2
 
 
 
 
d11e1fe
 
ec7f6a1
8e038e1
 
2c6bd00
 
63ed81d
ec7f6a1
ecd203a
ec7f6a1
 
 
 
 
 
63ed81d
ecd203a
 
 
 
 
6a9136a
037a839
63ed81d
ec7f6a1
 
 
037a839
1737ef1
 
 
d11e1fe
97d2e91
 
359c625
63ed81d
 
359c625
 
 
 
 
 
 
63ed81d
359c625
2c6bd00
 
359c625
63ed81d
2c6bd00
63ed81d
bec7021
 
 
 
 
 
ec7f6a1
5ac0022
bec7021
5ac0022
d11e1fe
 
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
from fastapi import FastAPI
from pydantic import BaseModel
from typing import Optional

# ✅ Modules LlamaIndex – version >= 0.10.0+
from llama_index.core.settings import Settings
from llama_index.core import Document
from llama_index.llms.llama_cpp import LlamaCPP
from llama_index.core.node_parser import SemanticSplitterNodeParser

# ✅ Pour l'embedding LOCAL via transformers
from transformers import AutoTokenizer, AutoModel
import torch
import torch.nn.functional as F
import os

app = FastAPI()

# ✅ Configuration locale du cache HF pour Hugging Face
# ✅ Définir un chemin autorisé pour le cache (à l'intérieur du container Hugging Face)
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

# ✅ Configuration du modèle d’embedding local (ex: BGE / Nomic / GTE etc.)
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):
    with torch.no_grad():
        inputs = tokenizer(text, return_tensors="pt", truncation=True, padding=True)
        outputs = model(**inputs)
        embeddings = outputs.last_hidden_state[:, 0]
        return F.normalize(embeddings, p=2, dim=1).squeeze().tolist()

# ✅ Données entrantes du POST
class ChunkRequest(BaseModel):
    text: str
    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:
        # ✅ Vérification du texte reçu
        print(f"✅ Texte reçu ({len(data.text)} caractères) : {data.text[:200]}...")
        print("✅ ✔️ Reçu – On passe à la configuration du modèle LLM...")

        # ✅ Chargement du modèle LLM depuis Hugging Face (GGUF distant)
        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("✅✅ Le modèle CodeLlama-7B-Instruct Q4_K_M a été chargé sans erreur...")


        print("✅ ✔️ Modèle LLM chargé sans erreur on continue...")

        # ✅ Définition d’un wrapper simple pour l’embedding local
        class SimpleEmbedding:
            def get_text_embedding(self, text: str):
                return get_embedding(text)

        # ✅ Nouvelle configuration (⚠️ ne plus utiliser ServiceContext)
        Settings.llm = llm
        Settings.embed_model = SimpleEmbedding()

        print("✅ LLM et embedding configurés - prêt pour le split")
        print("✅ Début du split sémantique...", flush=True)

        # ✅ Utilisation du Semantic Splitter avec le LLM actuel
        parser = SemanticSplitterNodeParser.from_defaults(llm=llm)

        doc = Document(text=data.text)

        try:
            nodes = parser.get_nodes_from_documents([doc])
            print(f"✅ Nombre de chunks générés : {len(nodes)}")
            print(f"🧩 Exemple chunk : {nodes[0].text[:100]}...")

        except Exception as e:
            import traceback
            traceback.print_exc()
            print(f"❌ Erreur lors du split sémantique : {e}")
            return {"error": str(e)}

        # ✅ Résultat complet pour l’API
        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  # ← essentiel pour que n8n voie "rien à signaler"
        }

    except Exception as e:
        return {"error": str(e)}

if __name__ == "__main__":
    import uvicorn
    uvicorn.run("app:app", host="0.0.0.0", port=7860)