jeysshon commited on
Commit
63dc01f
verified
1 Parent(s): a10dd23

Update embedding.py

Browse files
Files changed (1) hide show
  1. embedding.py +38 -146
embedding.py CHANGED
@@ -1,168 +1,74 @@
1
- import time
2
- import requests
3
- from requests.exceptions import ReadTimeout, HTTPError
4
  import logging
5
- import json
6
  import pandas as pd
7
- import chromadb
8
- from chromadb.utils import embedding_functions
9
- import os
10
- from dotenv import load_dotenv
11
- import datetime
12
- import uuid
13
- from chroma_setup import initialize_client
14
  import numpy as np
 
 
 
15
 
16
- # Carga las variables de entorno
17
- load_dotenv()
18
-
19
- def get_embedding_model():
20
- """
21
- Retorna una funci贸n de incrustaci贸n (embedding) basada en un modelo de HuggingFace.
22
- Lee la clave de la API desde las variables de entorno.
23
- """
24
- return embedding_functions.HuggingFaceEmbeddingFunction(
25
- api_key=os.getenv("HUGGINGFACE_API_KEY"),
26
- model_name="sentence-transformers/all-MiniLM-L6-v2",
27
- )
28
-
29
- def embed_with_retry(embedding_model, text_chunk, max_retries=3, backoff_factor=2):
30
- """
31
- Reintenta la generaci贸n de embeddings en caso de errores de timeout o l铆mites de la API.
32
-
33
- Par谩metros:
34
- -----------
35
- embedding_model : objeto de funci贸n
36
- Funci贸n de incrustaci贸n proporcionada por HuggingFaceEmbeddingFunction.
37
- text_chunk : str
38
- Texto a convertir en embedding.
39
- max_retries : int
40
- M谩ximo n煤mero de reintentos.
41
- backoff_factor : int
42
- Factor de espera exponencial antes de cada reintento.
43
-
44
- Retorna:
45
- --------
46
- list[float]
47
- Lista de valores flotantes que representan el embedding del texto.
48
- """
49
- retries = 0
50
- while retries < max_retries:
51
- try:
52
- embedding = embedding_model(input=text_chunk)
53
- return embedding
54
- except ReadTimeout as e:
55
- logging.warning(f"Timeout (ReadTimeout): {e}. Reintentando... ({retries+1}/{max_retries})")
56
- retries += 1
57
- time.sleep(backoff_factor ** retries)
58
- except HTTPError as e:
59
- if e.response.status_code == 429: # L铆mite de peticiones
60
- retry_after = int(e.response.headers.get("Retry-After", 60))
61
- logging.warning(f"L铆mite de la API alcanzado. Reintentando en {retry_after} segundos...")
62
- time.sleep(retry_after)
63
- retries += 1
64
- else:
65
- raise e
66
-
67
- raise Exception(f"No se pudo generar el embedding despu茅s de {max_retries} intentos.")
68
 
69
  def embed_text_chunks(pages_and_chunks: list[dict]) -> pd.DataFrame:
70
  """
71
- Genera embeddings para cada chunk de texto usando un modelo de HuggingFace,
72
- con l贸gica de reintento en caso de errores.
73
-
74
- Par谩metros:
75
- -----------
76
- pages_and_chunks : list[dict]
77
- Lista de diccionarios que contienen chunks de texto y metadatos.
78
-
79
- Retorna:
80
- --------
81
- pd.DataFrame
82
- DataFrame que incluye cada chunk, sus metadatos y su embedding.
83
  """
84
- embedding_model = get_embedding_model()
85
-
86
  for item in pages_and_chunks:
 
87
  try:
88
- embedding = embed_with_retry(embedding_model, item["sentence_chunk"])
89
- # Verifica que sea una lista anidada y la aplana
90
- if isinstance(embedding, list):
91
- embedding = [float(val) for sublist in embedding for val in sublist]
92
- else:
93
- raise ValueError(f"Formato de embedding inesperado: {type(embedding)}")
94
-
95
  item["embedding"] = embedding
 
96
  except Exception as e:
97
- logging.error(f"No se pudo generar embedding para: {item['sentence_chunk']}. Error: {e}")
98
  item["embedding"] = None
99
 
100
  return pd.DataFrame(pages_and_chunks)
101
 
102
  def save_to_chroma_db(embeddings_df: pd.DataFrame, user_id: str, document_id: str):
103
  """
104
- Guarda en la base de datos Chroma los embeddings generados,
105
- asign谩ndoles metadatos con un identificador combinado de usuario y documento.
106
-
107
- Par谩metros:
108
- -----------
109
- embeddings_df : pd.DataFrame
110
- DataFrame con los chunks y sus embeddings.
111
- user_id : str
112
- Identificador 煤nico de usuario.
113
- document_id : str
114
- Identificador 煤nico de documento.
115
  """
116
  client = initialize_client()
 
 
117
  collection = client.get_or_create_collection(name=f"text_embeddings_{user_id}")
118
 
119
  combined_key = f"{user_id}_{document_id}"
120
 
121
  ids = [f"{combined_key}_{i}" for i in range(len(embeddings_df))]
122
  documents = embeddings_df["sentence_chunk"].tolist()
 
123
 
124
- embeddings = []
125
- for embedding in embeddings_df["embedding"]:
126
- if isinstance(embedding, np.ndarray):
127
- embeddings.append(embedding.flatten().tolist())
128
- else:
129
- embeddings.append(embedding)
130
-
131
- metadatas = [{"combined_key": combined_key} for _ in range(len(embeddings_df))]
132
- print(f"Guardando documentos con combined_key: {combined_key}")
133
 
 
 
134
  collection.add(
135
  documents=documents,
136
  embeddings=embeddings,
137
  ids=ids,
138
- metadatas=metadatas
139
  )
140
 
141
- def query_chroma_db(user_id: str, document_id: str, query: str):
142
- """
143
- Consulta la base de datos Chroma para recuperar los fragmentos de texto m谩s
144
- relevantes basados en la consulta dada.
145
-
146
- Par谩metros:
147
- -----------
148
- user_id : str
149
- Identificador 煤nico de usuario.
150
- document_id : str
151
- Identificador 煤nico de documento.
152
- query : str
153
- Consulta que se desea realizar.
154
 
155
- Retorna:
156
- --------
157
- str
158
- Texto combinado de los documentos m谩s relevantes, o mensaje indicando
159
- que no se encontraron documentos.
160
- """
161
  client = initialize_client()
162
  collection = client.get_collection(name=f"text_embeddings_{user_id}")
163
-
164
  combined_key = f"{user_id}_{document_id}"
165
- print(f"Consultando con combined_key: {combined_key}")
166
 
167
  results = collection.query(
168
  query_texts=[query],
@@ -170,24 +76,10 @@ def query_chroma_db(user_id: str, document_id: str, query: str):
170
  where={"combined_key": combined_key},
171
  )
172
 
173
- print(f"Resultados de la consulta: {results}")
174
-
175
  documents = results.get("documents", [])
176
- if documents:
177
- relevant_docs = [doc for sublist in documents for doc in sublist] # Aplanar la lista
178
- context = "\n\n".join(relevant_docs)
179
- else:
180
- context = "No se encontraron documentos"
181
 
182
- return context
183
-
184
- def generate_document_id() -> str:
185
- """
186
- Genera un ID 煤nico de documento usando UUID.
187
-
188
- Retorna:
189
- --------
190
- str
191
- Cadena 煤nica que identifica el documento.
192
- """
193
- return str(uuid.uuid4())
 
1
+ # embedding.py
 
 
2
  import logging
 
3
  import pandas as pd
 
 
 
 
 
 
 
4
  import numpy as np
5
+ from sentence_transformers import SentenceTransformer
6
+ from chroma_setup import initialize_client
7
+ import uuid
8
 
9
+ # Creamos una instancia del modelo local de sentence-transformers
10
+ # (se descargar谩 y cachear谩 la primera vez que se ejecute)
11
+ model = SentenceTransformer('sentence-transformers/all-MiniLM-L6-v2')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
 
13
  def embed_text_chunks(pages_and_chunks: list[dict]) -> pd.DataFrame:
14
  """
15
+ Genera embeddings para cada chunk de texto usando un modelo local
16
+ de sentence-transformers.
 
 
 
 
 
 
 
 
 
 
17
  """
 
 
18
  for item in pages_and_chunks:
19
+ text_chunk = item["sentence_chunk"]
20
  try:
21
+ # encode() acepta una lista de strings y retorna una lista de embeddings (ndarray).
22
+ embedding_array = model.encode([text_chunk])
23
+ # Devuelve una matriz shape (1, 384) si es all-MiniLM-L6-v2, as铆 que tomamos el [0]
24
+ embedding = embedding_array[0].tolist()
25
+ # embedding ahora es una lista de floats
 
 
26
  item["embedding"] = embedding
27
+
28
  except Exception as e:
29
+ logging.error(f"Fallo al generar embedding para: {text_chunk}. Error: {e}")
30
  item["embedding"] = None
31
 
32
  return pd.DataFrame(pages_and_chunks)
33
 
34
  def save_to_chroma_db(embeddings_df: pd.DataFrame, user_id: str, document_id: str):
35
  """
36
+ Guarda en ChromaDB los embeddings generados.
 
 
 
 
 
 
 
 
 
 
37
  """
38
  client = initialize_client()
39
+ # Creas o recuperas la colecci贸n. Aseg煤rate de usar el mismo nombre
40
+ # que luego usar谩s en tus queries.
41
  collection = client.get_or_create_collection(name=f"text_embeddings_{user_id}")
42
 
43
  combined_key = f"{user_id}_{document_id}"
44
 
45
  ids = [f"{combined_key}_{i}" for i in range(len(embeddings_df))]
46
  documents = embeddings_df["sentence_chunk"].tolist()
47
+ embeddings = embeddings_df["embedding"].tolist()
48
 
49
+ # Verificamos que ninguno sea None
50
+ for idx, emb in enumerate(embeddings):
51
+ if emb is None:
52
+ raise ValueError(
53
+ f"El chunk con ID {ids[idx]} no tiene embedding v谩lido (None)."
54
+ )
 
 
 
55
 
56
+ # 隆Ahora todos deben ser listas de floats!
57
+ # Podemos a帽adirlos a la colecci贸n:
58
  collection.add(
59
  documents=documents,
60
  embeddings=embeddings,
61
  ids=ids,
62
+ metadatas=[{"combined_key": combined_key} for _ in range(len(embeddings_df))]
63
  )
64
 
65
+ def generate_document_id() -> str:
66
+ return str(uuid.uuid4())
 
 
 
 
 
 
 
 
 
 
 
67
 
68
+ def query_chroma_db(user_id: str, document_id: str, query: str):
 
 
 
 
 
69
  client = initialize_client()
70
  collection = client.get_collection(name=f"text_embeddings_{user_id}")
 
71
  combined_key = f"{user_id}_{document_id}"
 
72
 
73
  results = collection.query(
74
  query_texts=[query],
 
76
  where={"combined_key": combined_key},
77
  )
78
 
 
 
79
  documents = results.get("documents", [])
80
+ if not documents:
81
+ return "No se encontraron documentos"
 
 
 
82
 
83
+ # Aplanar la lista de documentos
84
+ relevant_docs = [doc for sublist in documents for doc in sublist]
85
+ return "\n\n".join(relevant_docs)