From 79fc7690521dfbdc25644ee99decad8f53f910ec Mon Sep 17 00:00:00 2001 From: DFFM-maker Date: Fri, 26 Dec 2025 10:58:49 +0100 Subject: [PATCH] Fix: RAg search --- .../96a093b6-6a00-4f64-9edb-5f0d04a0a417.txt} | 0 .../7dd47205-21b0-41f7-a494-7cf1373a9eb5.txt | 310 ------------------ __pycache__/app.cpython-310.pyc | Bin 5139 -> 5097 bytes app.py | 118 ++++--- 4 files changed, 56 insertions(+), 372 deletions(-) rename .files/{ea321724-2461-4b93-b351-0d429539e778/30609877-aa0b-458d-9b0c-36601bb29531.txt => 5e7c4a81-1697-47da-a003-d9c7c0a70512/96a093b6-6a00-4f64-9edb-5f0d04a0a417.txt} (100%) delete mode 100644 .files/ea321724-2461-4b93-b351-0d429539e778/7dd47205-21b0-41f7-a494-7cf1373a9eb5.txt diff --git a/.files/ea321724-2461-4b93-b351-0d429539e778/30609877-aa0b-458d-9b0c-36601bb29531.txt b/.files/5e7c4a81-1697-47da-a003-d9c7c0a70512/96a093b6-6a00-4f64-9edb-5f0d04a0a417.txt similarity index 100% rename from .files/ea321724-2461-4b93-b351-0d429539e778/30609877-aa0b-458d-9b0c-36601bb29531.txt rename to .files/5e7c4a81-1697-47da-a003-d9c7c0a70512/96a093b6-6a00-4f64-9edb-5f0d04a0a417.txt diff --git a/.files/ea321724-2461-4b93-b351-0d429539e778/7dd47205-21b0-41f7-a494-7cf1373a9eb5.txt b/.files/ea321724-2461-4b93-b351-0d429539e778/7dd47205-21b0-41f7-a494-7cf1373a9eb5.txt deleted file mode 100644 index 0c1ca348..00000000 --- a/.files/ea321724-2461-4b93-b351-0d429539e778/7dd47205-21b0-41f7-a494-7cf1373a9eb5.txt +++ /dev/null @@ -1,310 +0,0 @@ -VAR Internal -Idecon FB_IDECON_Client False False -Enable BOOL True False False -MsgFilter UINT 17 False False -fbTcpConnect SktTCPConnect False False -fbSocketSend SktTCPSend False False -fbSocketRcv SktTCPRcv False False -fbClose SktClose False False -Config _sSOCKET_ADDRESS (PortNo := 50000, IpAdr := '172.16.224.200') False False -SocketID_Int _sSOCKET False False -tmpIpAddress STRING[256] False False -tmpPort UINT False False -Connected BOOL False False -bConnectDone BOOL False False -bConnectBusy BOOL False False -bConnectError BOOL False False -wConnectErrorID WORD False False -bSendDone BOOL False False -bSendBusy BOOL False False -bSendError BOOL False False -wSendErrorID WORD False False -bRcvDone BOOL False False -bRcvBusy BOOL False False -bRcvError BOOL False False -wRcvErrorID WORD False False -RxChunk ARRAY[0..511] OF BYTE False False -RxChunkLen UINT False False -RxChunkValid BOOL False False -RcvTimeout UINT 50 False False -TxConsumed BOOL False False -prevTxOutValid BOOL False False -sendTrigger BOOL False False -Command STRING[255] False False -CommandTrigger BOOL False False -prevCommandTrigger BOOL False False -tonPollingSTATSV TON False False -tonPollingSTATP TON False False -prevPollSVQ BOOL False False -prevPollPQ BOOL False False -pollResetSV BOOL False False -pollResetP BOOL False False -prevLastMessage STRING[512] False False -newMsg BOOL False False -newWeightMsg BOOL False False -prevWeightMsg BOOL False False -totalRejected DINT False False -TxChunk ARRAY[0..511] OF BYTE False False -TxChunkLen UINT False False -iTx UINT False False -TxBuffer ARRAY[0..511] OF BYTE False False -tmpWeightData Idecon_WeightData False False -tmpStatPData Idecon_StatPData False False -rTrigRcv R_TRIG False False -bExecuteRcv BOOL False False -iRcvState INT False False - -END VAR - -VAR External -G_Idecon_Config False -G_System_Error False -G_ProgressiveID False -G_OPCUA_Idecon False -G_Idecon_StatP False -G_Idecon_LastWeight False -G_Idecon_LastSTATSV False -G_Idecon_WorkMode False -G_Idecon_BatchCode False -G_Idecon_ProductionOrder False -END VAR - -// ---------- Program ProcessIdecon---------------// - - -IF NOT Enable THEN - Connected := FALSE; - RxChunkLen := 0; - RxChunkValid := FALSE; - TxConsumed := FALSE; - Command := ''; - CommandTrigger := FALSE; - prevCommandTrigger := FALSE; - prevTxOutValid := FALSE; - pollResetSV := FALSE; - pollResetP := FALSE; -END_IF; - -tmpIpAddress := Config.IpAdr; -tmpPort := Config.PortNo; - -fbTcpConnect( - Execute := (Enable AND NOT Connected), - DstAdr := tmpIpAddress, // Passiamo la variabile semplice (FIX per "Member of structure") - DstTcpPort := tmpPort, // Passiamo la variabile semplice - SrcTcpPort := 0, // (Opzionale) 0 = Assegnazione automatica porta locale - Socket => SocketID_Int, - Done => bConnectDone, - Busy => bConnectBusy, - Error => bConnectError, - ErrorID => wConnectErrorID -); -IF bConnectDone THEN - Connected := TRUE; -END_IF; - -IF bConnectError THEN - Connected := FALSE; -END_IF; - -// --------------------------------------------------------- -// MACCHINA A STATI "INFINITY LOOP" (Per lettura continua) -// --------------------------------------------------------- - -CASE iRcvState OF - 0: // STATO IDLE: Pronti a ricevere? - // Se siamo connessi e il blocco non è occupato o in errore... - IF Connected AND NOT bRcvBusy AND NOT bRcvError THEN - bExecuteRcv := TRUE; // ALZA IL FRONTE DI SALITA - iRcvState := 10; // Passa in attesa - END_IF; - - 10: // STATO WAIT: Aspetta i dati - IF bRcvDone THEN - // Dati ricevuti! Andiamo a resettare - iRcvState := 20; - ELSIF bRcvError OR NOT Connected THEN - // Se cade la connessione o c'è errore socket - iRcvState := 99; - END_IF; - - 20: // STATO RESET (FONDAMENTALE PER LEGGERE IL SUCCESSIVO) - bExecuteRcv := FALSE; // Abbassa Execute - - // Aspettiamo che il blocco confermi di essersi spento (Done deve tornare False) - // Senza questo check, il riarmo è troppo veloce e fallisce! - IF NOT bRcvDone AND NOT bRcvBusy THEN - iRcvState := 0; // Torna all'inizio per leggere il prossimo - END_IF; - - 99: // STATO ERRORE - bExecuteRcv := FALSE; - // Se l'errore sparisce o la connessione cade, resetta la macchina - IF NOT bRcvError THEN - iRcvState := 0; - END_IF; -END_CASE; - -// --- Chiamata al Blocco (Fuori dal CASE) --- -fbSocketRcv( - Execute := bExecuteRcv, - Socket := SocketID_Int, - TimeOut := 1, //RcvTimeout, // Assicurati sia basso (es. 10 o 50 ms) - Size := UINT#512, - RcvDat := RxChunk[0], - Done => bRcvDone, - Busy => bRcvBusy, - Error => bRcvError, - ErrorID => wRcvErrorID, - RcvSize => RxChunkLen -); - -rTrigRcv(CLK := bRcvDone); -RxChunkValid := rTrigRcv.Q AND (RxChunkLen > 0); - -// Gestione disconnessione su errore grave -IF bRcvError THEN - Connected := FALSE; - iRcvState := 99; -END_IF; - -tonPollingSTATSV(IN := (Enable AND Connected AND NOT pollResetSV), PT := T#2S); -tonPollingSTATP(IN := (Enable AND Connected AND NOT pollResetP), PT := T#5S); - -pollResetSV := FALSE; -pollResetP := FALSE; - -CommandTrigger := FALSE; -IF tonPollingSTATSV.Q AND NOT prevPollSVQ THEN - Command := 'STATSV'; - CommandTrigger := TRUE; - pollResetSV := TRUE; -ELSIF tonPollingSTATP.Q AND NOT prevPollPQ THEN - Command := 'STATREQ'; - CommandTrigger := TRUE; - pollResetP := TRUE; -END_IF; -prevPollSVQ := tonPollingSTATSV.Q; -prevPollPQ := tonPollingSTATP.Q; - -Idecon( - Enable := Enable, - MsgFilter := MsgFilter, - RxIn := RxChunk, - RxInLen := RxChunkLen, - RxInValid := RxChunkValid, - TxConsumed := TxConsumed, - Command := Command, - CommandTrigger := CommandTrigger -); -tmpWeightData := Idecon.Weight; -tmpStatPData := Idecon.StatP; -// Gestione Messaggi -newMsg := (Idecon.LastMessage <> prevLastMessage); -prevLastMessage := Idecon.LastMessage; - -newWeightMsg := newMsg AND IDECON_StartsWith(Idecon.LastMessage, 'WEIGHT='); - -IF newWeightMsg AND NOT prevWeightMsg THEN - G_ProgressiveID := G_ProgressiveID + 1; -END_IF; -prevWeightMsg := newWeightMsg; -G_Idecon_LastSTATSV := Idecon.LastSTATSV; - - -IF tmpWeightData.Valid THEN - G_Idecon_LastWeight.DateTimeText := tmpWeightData.DateTimeText; - G_Idecon_LastWeight.ProductionOrder := tmpWeightData.ProductionOrder; - G_Idecon_LastWeight.BatchCode := tmpWeightData.BatchCode; - G_Idecon_LastWeight.RecipeName := tmpWeightData.RecipeName; - G_Idecon_LastWeight.LineCode := tmpWeightData.LineCode; - G_Idecon_LastWeight.SerialNumber := tmpWeightData.SerialNumber; - G_Idecon_LastWeight.WeightMg := tmpWeightData.WeightMg; - G_Idecon_LastWeight.DeltaToNominalMg:= tmpWeightData.DeltaToNominalMg; - G_Idecon_LastWeight.Classification := tmpWeightData.Classification; - G_Idecon_LastWeight.Valid := TRUE; -END_IF; - -// --- E anche qui usiamo tmpStatPData --- -IF tmpStatPData.Valid THEN - G_Idecon_StatP.TotalProducts := tmpStatPData.TotalProducts; - G_Idecon_StatP.TotalAccepted := tmpStatPData.TotalAccepted; - G_Idecon_StatP.RejectedMinus := tmpStatPData.RejectedMinus; - G_Idecon_StatP.RejectedMinusMinus := tmpStatPData.RejectedMinusMinus; - G_Idecon_StatP.RejectedPlus := tmpStatPData.RejectedPlus; - G_Idecon_StatP.RejectedPlusPlus := tmpStatPData.RejectedPlusPlus; - G_Idecon_StatP.CannotBeWeighed := tmpStatPData.CannotBeWeighed; - G_Idecon_StatP.MetalCategory := tmpStatPData.MetalCategory; - G_Idecon_StatP.Valid := TRUE; -END_IF; - - -G_System_Error := bConnectError OR bSendError OR bRcvError OR Idecon.Error; - -IF G_Idecon_StatP.Valid AND (G_Idecon_StatP.TotalProducts >= G_Idecon_StatP.TotalAccepted) THEN - totalRejected := G_Idecon_StatP.TotalProducts - G_Idecon_StatP.TotalAccepted; -ELSE - totalRejected := 0; -END_IF; - -// OPC UA Mapping -G_OPCUA_Idecon.Connected := Connected; -G_OPCUA_Idecon.WorkMode := G_Idecon_WorkMode; -G_OPCUA_Idecon.LastSTATSV := G_Idecon_LastSTATSV; -G_OPCUA_Idecon.BatchCode := G_Idecon_BatchCode; -G_OPCUA_Idecon.ProductionOrder := G_Idecon_ProductionOrder; -G_OPCUA_Idecon.Error := G_System_Error; -G_OPCUA_Idecon.ErrorId := UINT_TO_UDINT(WORD_TO_UINT(wConnectErrorID)); - -G_OPCUA_Idecon.LastWeight.ProgressiveId := G_ProgressiveID; -G_OPCUA_Idecon.LastWeight.TimestampText := G_Idecon_LastWeight.DateTimeText; -G_OPCUA_Idecon.LastWeight.ProductionOrder := G_Idecon_LastWeight.ProductionOrder; -G_OPCUA_Idecon.LastWeight.BatchCode := G_Idecon_LastWeight.BatchCode; -G_OPCUA_Idecon.LastWeight.RecipeName := G_Idecon_LastWeight.RecipeName; -G_OPCUA_Idecon.LastWeight.LineCode := G_Idecon_LastWeight.LineCode; -G_OPCUA_Idecon.LastWeight.SerialNumber := G_Idecon_LastWeight.SerialNumber; -G_OPCUA_Idecon.LastWeight.Weight_g := DINT_TO_LREAL(G_Idecon_LastWeight.WeightMg) / LREAL#1000.0; -G_OPCUA_Idecon.LastWeight.Delta_g := DINT_TO_LREAL(G_Idecon_LastWeight.DeltaToNominalMg) / LREAL#1000.0; -G_OPCUA_Idecon.LastWeight.Classification := G_Idecon_LastWeight.Classification; -G_OPCUA_Idecon.LastWeight.Expelled := FALSE; -G_OPCUA_Idecon.LastWeight.Valid := G_Idecon_LastWeight.Valid; - -G_OPCUA_Idecon.Stats.TotalProducts := G_Idecon_StatP.TotalProducts; -G_OPCUA_Idecon.Stats.TotalAccepted := G_Idecon_StatP.TotalAccepted; -G_OPCUA_Idecon.Stats.TotalRejected := totalRejected; -G_OPCUA_Idecon.Stats.RejectedPlusPlus := G_Idecon_StatP.RejectedPlusPlus; -G_OPCUA_Idecon.Stats.CannotBeWeighed := G_Idecon_StatP.CannotBeWeighed; -G_OPCUA_Idecon.Stats.MetalCategory := G_Idecon_StatP.MetalCategory; -G_OPCUA_Idecon.Stats.Valid := G_Idecon_StatP.Valid; - -// Invio Socket -sendTrigger := (Idecon.TxOutValid AND NOT prevTxOutValid); -prevTxOutValid := Idecon.TxOutValid; - -IF sendTrigger THEN - // Copiamo i dati dall'output dell'FB al buffer LOCALE - // Questo evita l'errore "Member of function block instance variable" - TxBuffer := Idecon.TxOut; -END_IF; - -fbSocketSend( - Execute := (Enable AND Connected AND sendTrigger), - Socket := SocketID_Int, - SendDat := TxBuffer[0], // --- CORREZIONE 2: Usa buffer locale --- - Size := Idecon.TxOutLen, - Done => bSendDone, - Busy => bSendBusy, - Error => bSendError, - ErrorID => wSendErrorID -); - -TxConsumed := bSendDone; - -IF bSendError THEN - Connected := FALSE; -END_IF; - -fbClose( - Execute := (Enable AND NOT Connected), - Socket := SocketID_Int -); diff --git a/__pycache__/app.cpython-310.pyc b/__pycache__/app.cpython-310.pyc index b0399de53e80313ab61dd7881b8e7d3fde3cdcf5..b98d51a5206c388d6d1b1d673b7ff31349fb2db0 100644 GIT binary patch delta 2805 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 diff --git a/app.py b/app.py index b0eb7134..cc52580a 100644 --- a/app.py +++ b/app.py @@ -5,14 +5,13 @@ from datetime import datetime import shutil import uuid import ollama -from qdrant_client import QdrantClient -from qdrant_client.http.models import PointStruct +from qdrant_client import AsyncQdrantClient +from qdrant_client.models import PointStruct, Distance, VectorParams -# --- CONFIGURAZIONE HARD-CODED PER ROMPERE IL BLOCCO 127.0.0.1 --- +# --- CONFIGURAZIONE HARD-CODED --- OLLAMA_URL = "http://192.168.1.243:11434" -# ----------------------------------------------------------------------------- +# --------------------------------- -# Define user roles mapping USER_ROLES = { 'moglie@esempio.com': 'business', 'ingegnere@esempio.com': 'engineering', @@ -20,7 +19,6 @@ USER_ROLES = { 'admin@esempio.com': 'admin' } -# Define the path for workspaces WORKSPACES_DIR = "./workspaces" def create_workspace(user_role): @@ -44,31 +42,28 @@ def limit_history(history): return history async def connect_to_qdrant(): - client = QdrantClient("http://qdrant:6333") + client = AsyncQdrantClient(url="http://qdrant:6333") collection_name = "documents" - try: - client.get_collection(collection_name) - except Exception: - client.create_collection( + # Check if collection exists + if not await client.collection_exists(collection_name): + await client.create_collection( collection_name=collection_name, - vectors_config={"size": 768, "distance": "Cosine"} + vectors_config=VectorParams(size=768, distance=Distance.COSINE) ) return client async def get_embeddings(text): - # --- FIX: Splitto Host e Port per evitare confusione --- - client = ollama.Client(host=OLLAMA_URL) # Uso l'URL intero + client = ollama.Client(host=OLLAMA_URL) - # Controllo lunghezza testo - if len(text) > 12000: - text = text[:12000] + # Limite di sicurezza per evitare errori 500 su Ollama + limit = 2000 + if len(text) > limit: + text = text[:limit] try: response = client.embed(model='nomic-embed-text', input=text) - - # Gestione risposta if 'embeddings' in response: return response['embeddings'][0] return response.get('embedding') @@ -82,26 +77,29 @@ async def search_qdrant(query_text, user_role): qdrant_client = await connect_to_qdrant() query_embedding = await get_embeddings(query_text) - # Se non trova embedding (errore connessione), non cercare if not query_embedding: return "" - # Cerca i 3 documenti più simili - search_result = qdrant_client.search( + # Usa query_points (nuova API per AsyncClient) + search_result = await qdrant_client.query_points( collection_name="documents", - query_vector=query_embedding, + query=query_embedding, limit=3 ) + hits = search_result.points contexts = [] - # FIX: controllo sicurezza per evitare 'list index out of range' - if search_result: - for hit in search_result: + if hits: + for hit in hits: try: - if 'payload' in hit and 'file_name' in hit['payload']: - contexts.append(f"Documento: {hit['payload']['file_name']}") - except Exception: - pass + if hit.payload: + # --- FIX IMPORTANTE: Recupera il contenuto reale --- + nome_file = hit.payload.get('file_name', 'Sconosciuto') + contenuto = hit.payload.get('content', '') + + contexts.append(f"--- Documento: {nome_file} ---\n{contenuto}\n----------------") + except Exception as e: + print(f"Error parsing hit: {e}") return "\n".join(contexts) except Exception as e: @@ -110,7 +108,6 @@ async def search_qdrant(query_text, user_role): @cl.on_chat_start async def chat_start(): - # Hardcode per test user_email = "admin@esempio.com" user_role = USER_ROLES.get(user_email, 'guest') @@ -119,16 +116,8 @@ async def chat_start(): cl.user_session.set("history", []) cl.user_session.set("role", user_role) - if user_role == 'admin': - await cl.Message(content="Welcome, Admin!").send() - elif user_role == 'engineering': - await cl.Message(content="Welcome, Engineer!").send() - elif user_role == 'business': - await cl.Message(content="Welcome, Business User!").send() - elif user_role == 'architecture': - await cl.Message(content="Welcome, Architect!").send() - else: - await cl.Message(content="Welcome, Guest!").send() + welcome_msg = f"Welcome, {user_role.capitalize()}!" + await cl.Message(content=welcome_msg).send() @cl.on_message async def message(message): @@ -139,24 +128,11 @@ async def message(message): return try: - # Client Ollama URL Hardcoded client = ollama.Client(host=OLLAMA_URL) - - # History & Sliding Window history = cl.user_session.get("history", []) history = limit_history(history) - # --- RAG STEP 1: Cerca nei documenti --- - context_text = await search_qdrant(message.content, user_role) - - # Se trova contesto, iniettalo - if context_text: - system_prompt = f"Contexto dai documenti:\n{context_text}\n\nRispondi usando questo contesto." - history.insert(0, {"role": "system", "content": system_prompt}) - - history.append({"role": "user", "content": message.content}) - - # Gestione Upload e Indexing + # --- PASSO 1: Gestione Upload e Indexing (PRIMA della ricerca) --- if message.elements: uploaded_files = [] for element in message.elements: @@ -166,16 +142,25 @@ async def message(message): shutil.copyfileobj(src, dst) if element.name.endswith('.txt'): - with open(dest_path, 'r') as f: + # Encoding utf-8 per sicurezza + with open(dest_path, 'r', encoding='utf-8') as f: content = f.read() - # Indexing embeddings = await get_embeddings(content) if embeddings: qdrant_client = await connect_to_qdrant() - point_id = uuid.uuid4() - point = PointStruct(id=point_id, vector=embeddings, payload={"file_name": element.name}) - qdrant_client.upsert(collection_name="documents", points=[point]) + point_id = str(uuid.uuid4()) + + # --- FIX IMPORTANTE: Salviamo anche il contenuto nel payload --- + point = PointStruct( + id=point_id, + vector=embeddings, + payload={ + "file_name": element.name, + "content": content + } + ) + await qdrant_client.upsert(collection_name="documents", points=[point]) await cl.Message(content=f"Documento '{element.name}' indicizzato.").send() uploaded_files.append(element.name) @@ -184,11 +169,20 @@ async def message(message): if uploaded_files: await cl.Message(content=f"Files saved: {', '.join(uploaded_files)}").send() + + # --- PASSO 2: Cerca nei documenti --- + context_text = await search_qdrant(message.content, user_role) - # Chat + if context_text: + system_prompt = f"Usa esclusivamente il seguente contesto per rispondere alla domanda. Se la risposta non è nel contesto, dillo.\n\nContesto:\n{context_text}" + history.insert(0, {"role": "system", "content": system_prompt}) + + history.append({"role": "user", "content": message.content}) + + # --- PASSO 3: Chat Generation --- response = client.chat(model='qwen2.5-coder:7b', messages=history) - # Code Extracting + # Code Extraction code_blocks = re.findall(r"```python(.*?)```", response['message']['content'], re.DOTALL) elements = []