Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

translated rag examples #18

Merged
merged 2 commits into from
Mar 11, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion rag_documents_flow.py
Original file line number Diff line number Diff line change
@@ -50,7 +50,7 @@

# Now we can use the matches to generate a response
SYSTEM_MESSAGE = """
You are a helpful assistant that answers questions about Maya civilization.
You are a helpful assistant that answers questions about insects.
You must use the data set to answer the questions,
you should not provide any info that is not in the provided sources.
Cite the sources you used to answer the question inside square brackets.
2 changes: 1 addition & 1 deletion rag_documents_hybrid.py
Original file line number Diff line number Diff line change
@@ -123,7 +123,7 @@ def hybrid_search(query, limit):

# Now we can use the matches to generate a response
SYSTEM_MESSAGE = """
You are a helpful assistant that answers questions about Maya civilization.
You are a helpful assistant that answers questions about insects.
You must use the data set to answer the questions,
you should not provide any info that is not in the provided sources.
Cite the sources you used to answer the question inside square brackets.
28 changes: 27 additions & 1 deletion spanish/README.md
Original file line number Diff line number Diff line change
@@ -2,7 +2,9 @@

Este repositorio contiene una colección de scripts en Python que demuestran cómo usar la API de OpenAI para generar completados de chat.

En orden creciente de complejidad, los scripts son:
## Librería OpenAI

Estos scripts demuestran como usar la libreria de OpenAI. En orden creciente de complejidad, los scripts son:

1. [`chat.py`](./chat.py): Un script simple que demuestra cómo usar la API de OpenAI para generar completados de chat.
2. [`chat_stream.py`](./chat_stream.py): Añade `stream=True` a la llamada de API para devolver un generador que transmite el completado mientras se está generando.
@@ -13,9 +15,33 @@ Además de estos scripts para demostrar características adicionales:

* [`chat_safety.py`](./chat_safety.py): El script simple con manejo de excepciones para errores de filtro de Seguridad de Contenido de Azure AI.
* [`chat_async.py`](./chat_async.py): Utiliza los clientes asíncronos para hacer llamadas asincrónicas, incluyendo un ejemplo de envío de múltiples solicitudes a la vez usando `asyncio.gather`.

## Popular LLM libraries

Estos scripts usan librerías populares para LLMs y demuestran como usarlas con la API de OpenAI:

* [`chat_langchain.py`](./chat_langchain.py): Utiliza el SDK de langchain para generar completados de chat. [Aprende más en la documentación de Langchain](https://python.langchain.com/docs/get_started/quickstart)
* [`chat_llamaindex.py`](./chat_llamaindex.py): Utiliza el SDK de LlamaIndex para generar completados de chat. [Aprende más en la documentación de LlamaIndex](https://docs.llamaindex.ai/en/stable/)
* [`chat_pydanticai.py`](./chat_pydanticai.py): Utiliza el SDK de PydanticAI para general completados de chat. [Aprende más en la documentación de PydanticAI](https://ai.pydantic.dev/)

## Generación Aumentada con Recuperación (RAG)

Estos scripts demuestran cómo usar la API de OpenAI para tareas de Generación Aumentada con Recuperación (RAG), donde el modelo recupera información relevante de una fuente y la utiliza para generar una respuesta.

Primero instala las dependencias de RAG:

```bash
python -m pip install -r requirements-rag.txt
```

Luego ejecuta los scripts (en orden de complejidad creciente):

* [`rag_csv.py`](./rag.py): Recupera resultados coincidentes de un archivo CSV y los utiliza para responder a la pregunta del usuario.
* [`rag_multiturn.py`](./rag_multiturn.py): La misma idea, pero con una interfaz de chat bidireccional usando `input()` que mantiene un registro de mensajes anteriores y los envía con cada llamada de completado de chat.them with each chat completion call.
* [`rag_queryrewrite.py`](./rag_queryrewrite.py): Añade un paso de reescritura de consulta al proceso RAG, donde la pregunta del usuario se reescribe para mejorar los resultados de recuperación.
* [`rag_documents_ingestion.py`](./rag_ingestion.py): Ingesta PDFs usando pymupdf para convertirlos a markdown, luego usa Langchain para dividirlos en fragmentos, después usa OpenAI para incrustar los fragmentos, y finalmente los almacena en un archivo JSON local.
* [`rag_documents_flow.py`](./rag_pdfs.py): Un flujo RAG que recupera resultados coincidentes del archivo JSON local creado por `rag_documents_ingestion.py`.
* [`rag_documents_hybrid.py`](./rag_documents_hybrid.py): Un flujo RAG que implementa una recuperación híbrida con búsqueda vectorial y por palabras clave, fusionando con Reciprocal Rank Fusion (RRF), y reclasificación semántica con un modelo cross-encoder.

## Configuración del entorno

Binary file added spanish/data/Apis_mellifera.pdf
Binary file not shown.
Binary file added spanish/data/Centris_pallida.pdf
Binary file not shown.
Binary file added spanish/data/Syrphidae.pdf
Binary file not shown.
Binary file added spanish/data/Xylocopa_californica.pdf
Binary file not shown.
74 changes: 74 additions & 0 deletions spanish/rag_csv.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import csv
import os

import azure.identity
import openai
from dotenv import load_dotenv
from lunr import lunr

# Setup the OpenAI client to use either Azure, OpenAI.com, or Ollama API
load_dotenv(override=True)
API_HOST = os.getenv("API_HOST")

if API_HOST == "azure":
token_provider = azure.identity.get_bearer_token_provider(
azure.identity.DefaultAzureCredential(), "https://cognitiveservices.azure.com/.default"
)
client = openai.AzureOpenAI(
api_version=os.environ["AZURE_OPENAI_VERSION"],
azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
azure_ad_token_provider=token_provider,
)
MODEL_NAME = os.environ["AZURE_OPENAI_DEPLOYMENT"]

elif API_HOST == "ollama":
client = openai.OpenAI(base_url=os.environ["OLLAMA_ENDPOINT"], api_key="nokeyneeded")
MODEL_NAME = os.environ["OLLAMA_MODEL"]

elif API_HOST == "github":
client = openai.OpenAI(base_url="https://models.inference.ai.azure.com", api_key=os.environ["GITHUB_TOKEN"])
MODEL_NAME = os.environ["GITHUB_MODEL"]

else:
client = openai.OpenAI(api_key=os.environ["OPENAI_KEY"])
MODEL_NAME = os.environ["OPENAI_MODEL"]

# Indexamos los datos del CSV
with open("hybridos.csv") as file:
reader = csv.reader(file)
rows = list(reader)
documents = [{"id": (i + 1), "body": " ".join(row)} for i, row in enumerate(rows[1:])]
index = lunr(ref="id", fields=["body"], documents=documents)

# Obteneemos la pregunta del usuario
user_question = "¿qué tan rápido es el Prius v?"

# Buscaamos en el índice la pregunta del usuario
results = index.search(user_question)
matching_rows = [rows[int(result["ref"])] for result in results]

# Formateamos como tabla markdown, ya que los llms entienden markdown
matches_table = " | ".join(rows[0]) + "\n" + " | ".join(" --- " for _ in range(len(rows[0]))) + "\n"
matches_table += "\n".join(" | ".join(row) for row in matching_rows)

print("Found matches:")
print(matches_table)

# Ahora podemos usar los resultados para generar una respuesta
SYSTEM_MESSAGE = """
Eres un asistente útil que responde preguntas sobre automóviles basándote en un conjunto de datos de autos híbridos.
Debes utilizar el conjunto de datos para responder las preguntas, no debes
proporcionar ninguna información que no esté en las fuentes proporcionadas.
"""

response = client.chat.completions.create(
model=MODEL_NAME,
temperature=0.3,
messages=[
{"role": "system", "content": SYSTEM_MESSAGE},
{"role": "user", "content": f"{user_question}\nSources: {matches_table}"},
],
)

print(f"\nResponse from {API_HOST}: \n")
print(response.choices[0].message.content)
70 changes: 70 additions & 0 deletions spanish/rag_documents_flow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import json
import os

import azure.identity
import openai
from dotenv import load_dotenv
from lunr import lunr

# Setup the OpenAI client to use either Azure, OpenAI.com, or Ollama API
load_dotenv(override=True)
API_HOST = os.getenv("API_HOST")

if API_HOST == "azure":
token_provider = azure.identity.get_bearer_token_provider(
azure.identity.DefaultAzureCredential(), "https://cognitiveservices.azure.com/.default"
)
client = openai.AzureOpenAI(
api_version=os.environ["AZURE_OPENAI_VERSION"],
azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
azure_ad_token_provider=token_provider,
)
MODEL_NAME = os.environ["AZURE_OPENAI_DEPLOYMENT"]

elif API_HOST == "ollama":
client = openai.OpenAI(base_url=os.environ["OLLAMA_ENDPOINT"], api_key="nokeyneeded")
MODEL_NAME = os.environ["OLLAMA_MODEL"]

elif API_HOST == "github":
client = openai.OpenAI(base_url="https://models.inference.ai.azure.com", api_key=os.environ["GITHUB_TOKEN"])
MODEL_NAME = os.environ["GITHUB_MODEL"]

else:
client = openai.OpenAI(api_key=os.environ["OPENAI_KEY"])
MODEL_NAME = os.environ["OPENAI_MODEL"]

# Indexar los datos del JSON - cada objeto tiene id, texto y embedding
with open("rag_ingested_chunks.json") as file:
documents = json.load(file)
documents_by_id = {doc["id"]: doc for doc in documents}
index = lunr(ref="id", fields=["text"], documents=documents)

# Obtener la pregunta del usuario
user_question = "¿dónde vive la abeja solitaria?"

# Buscar la pregunta del usuario en el índice
results = index.search(user_question)
retrieved_documents = [documents_by_id[result["ref"]] for result in results]
print(f"Recuperados {len(retrieved_documents)} documentos coincidentes, enviando sólo los primeros 5.")
context = "\n".join([f"{doc['id']}: {doc['text']}" for doc in retrieved_documents[0:5]])

# Ahora podemos usar las coincidencias para generar una respuesta
SYSTEM_MESSAGE = """
Eres un asistente útil que responde preguntas sobre insectos regionales.
Debes utilizar el conjunto de datos para responder las preguntas,
no debes proporcionar ninguna información que no esté en las fuentes proporcionadas.
Cita las fuentes que utilizaste para responder la pregunta entre corchetes.
Las fuentes están en el formato: <id>: <texto>.
"""

response = client.chat.completions.create(
model=MODEL_NAME,
temperature=0.3,
messages=[
{"role": "system", "content": SYSTEM_MESSAGE},
{"role": "user", "content": f"{user_question}\nFuentes: {context}"},
],
)

print(f"\nRespuesta de {MODEL_NAME} en {API_HOST}: \n")
print(response.choices[0].message.content)
143 changes: 143 additions & 0 deletions spanish/rag_documents_hybrid.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
# pip install sentence-transformers
import json
import os

import azure.identity
import openai
from dotenv import load_dotenv
from lunr import lunr
from sentence_transformers import CrossEncoder

# Setup the OpenAI client to use either Azure, OpenAI.com, or Ollama API
load_dotenv(override=True)
API_HOST = os.getenv("API_HOST")

if API_HOST == "azure":
token_provider = azure.identity.get_bearer_token_provider(
azure.identity.DefaultAzureCredential(), "https://cognitiveservices.azure.com/.default"
)
client = openai.AzureOpenAI(
api_version=os.environ["AZURE_OPENAI_VERSION"],
azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
azure_ad_token_provider=token_provider,
)
MODEL_NAME = os.environ["AZURE_OPENAI_DEPLOYMENT"]

elif API_HOST == "ollama":
client = openai.OpenAI(base_url=os.environ["OLLAMA_ENDPOINT"], api_key="nokeyneeded")
MODEL_NAME = os.environ["OLLAMA_MODEL"]

elif API_HOST == "github":
client = openai.OpenAI(base_url="https://models.inference.ai.azure.com", api_key=os.environ["GITHUB_TOKEN"])
MODEL_NAME = os.environ["GITHUB_MODEL"]

else:
client = openai.OpenAI(api_key=os.environ["OPENAI_KEY"])
MODEL_NAME = os.environ["OPENAI_MODEL"]

# Indexar los datos del JSON - cada objeto tiene id, texto y embedding
with open("rag_ingested_chunks.json") as file:
documents = json.load(file)
documents_by_id = {doc["id"]: doc for doc in documents}
index = lunr(ref="id", fields=["text"], documents=documents)


def full_text_search(query, limit):
"""
Realizar una búsqueda de texto completo en los documentos indexados.
"""
results = index.search(query)
retrieved_documents = [documents_by_id[result["ref"]] for result in results[:limit]]
return retrieved_documents


def vector_search(query, limit):
"""
Realizar una búsqueda vectorial en los documentos indexados
utilizando una función simple de similitud de coseno.
"""

def cosine_similarity(a, b):
return sum(x * y for x, y in zip(a, b)) / ((sum(x * x for x in a) ** 0.5) * (sum(y * y for y in b) ** 0.5))

query_embedding = client.embeddings.create(model="text-embedding-3-small", input=query).data[0].embedding
similarities = []
for doc in documents:
doc_embedding = doc["embedding"]
similarity = cosine_similarity(query_embedding, doc_embedding)
similarities.append((doc, similarity))
similarities.sort(key=lambda x: x[1], reverse=True)

retrieved_documents = [doc for doc, _ in similarities[:limit]]
return retrieved_documents


def reciprocal_rank_fusion(text_results, vector_results, alpha=0.5):
"""
Realizar la Fusión de Rango Recíproco en los resultados de búsquedas de texto y vectoriales.
"""
text_ids = {doc["id"] for doc in text_results}
vector_ids = {doc["id"] for doc in vector_results}

combined_results = []
for doc in text_results:
if doc["id"] in vector_ids:
combined_results.append((doc, alpha))
else:
combined_results.append((doc, 1 - alpha))
for doc in vector_results:
if doc["id"] not in text_ids:
combined_results.append((doc, alpha))
combined_results.sort(key=lambda x: x[1], reverse=True)
return [doc for doc, _ in combined_results]


def rerank(query, retrieved_documents):
"""
Reclasificar los resultados utilizando un cross-enconder modelo .
"""
encoder = CrossEncoder("cross-encoder/ms-marco-MiniLM-L-6-v2")
scores = encoder.predict([(query, doc["text"]) for doc in retrieved_documents])
scored_documents = [v for _, v in sorted(zip(scores, retrieved_documents), reverse=True)]
return scored_documents


def hybrid_search(query, limit):
"""
Realizar una búsqueda híbrida utilizando tanto búsqueda de texto completo como vectorial.
"""
text_results = full_text_search(query, limit * 2)
vector_results = vector_search(query, limit * 2)
combined_results = reciprocal_rank_fusion(text_results, vector_results)
combined_results = rerank(query, combined_results)
return combined_results[:limit]


# Obtener la pregunta del usuario
user_question = "gris y solitario"

# Buscar la pregunta del usuario en el índice
retrieved_documents = hybrid_search(user_question, limit=5)
print(f"Recuperados {len(retrieved_documents)} documentos coincidentes.")
context = "\n".join([f"{doc['id']}: {doc['text']}" for doc in retrieved_documents[0:5]])

# Ahora podemos usar las coincidencias para generar una respuesta
SYSTEM_MESSAGE = """
Eres un asistente útil que responde preguntas sobre insectos.
Debes utilizar el conjunto de datos para responder las preguntas,
no debes proporcionar ninguna información que no esté en las fuentes proporcionadas.
Cita las fuentes que utilizaste para responder la pregunta entre corchetes.
Las fuentes están en el formato: <id>: <texto>.
"""

response = client.chat.completions.create(
model=MODEL_NAME,
temperature=0.3,
messages=[
{"role": "system", "content": SYSTEM_MESSAGE},
{"role": "user", "content": f"{user_question}\nFuentes: {context}"},
],
)

print(f"\nRespuesta de {MODEL_NAME} en {API_HOST}: \n")
print(response.choices[0].message.content)
61 changes: 61 additions & 0 deletions spanish/rag_documents_ingestion.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import json
import os
import pathlib

import azure.identity
import openai
import pymupdf4llm
from dotenv import load_dotenv
from langchain_text_splitters import RecursiveCharacterTextSplitter

# Setup the OpenAI client to use either Azure, OpenAI.com, or Ollama API
load_dotenv(override=True)
API_HOST = os.getenv("API_HOST")

if API_HOST == "azure":
token_provider = azure.identity.get_bearer_token_provider(
azure.identity.DefaultAzureCredential(), "https://cognitiveservices.azure.com/.default"
)
client = openai.AzureOpenAI(
api_version=os.environ["AZURE_OPENAI_VERSION"],
azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
azure_ad_token_provider=token_provider,
)
MODEL_NAME = os.environ["AZURE_OPENAI_DEPLOYMENT"]

elif API_HOST == "ollama":
client = openai.OpenAI(base_url=os.environ["OLLAMA_ENDPOINT"], api_key="nokeyneeded")
MODEL_NAME = os.environ["OLLAMA_MODEL"]

elif API_HOST == "github":
client = openai.OpenAI(base_url="https://models.inference.ai.azure.com", api_key=os.environ["GITHUB_TOKEN"])
MODEL_NAME = os.environ["GITHUB_MODEL"]

else:
client = openai.OpenAI(api_key=os.environ["OPENAI_KEY"])
MODEL_NAME = os.environ["OPENAI_MODEL"]

data_dir = pathlib.Path(os.path.dirname(__file__)) / "data"
filenames = ["Xylocopa_californica.pdf", "Centris_pallida.pdf", "Apis_mellifera.pdf", "Syrphidae.pdf"]
all_chunks = []
for filename in filenames:
# Extraemos texto del archivo PDF
md_text = pymupdf4llm.to_markdown(data_dir / filename)

# Dividimos el texto en fragmentos más pequeños
text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(
model_name="gpt-4o", chunk_size=500, chunk_overlap=0
)
texts = text_splitter.create_documents([md_text])
file_chunks = [{"id": f"{filename}-{(i + 1)}", "text": text.page_content} for i, text in enumerate(texts)]

# Generamos embeddings utilizando el SDK de openAI para cada texto
for file_chunk in file_chunks:
file_chunk["embedding"] = (
client.embeddings.create(model="text-embedding-3-small", input=file_chunk["text"]).data[0].embedding
)
all_chunks.extend(file_chunks)

# Guardamos los documentos con embeddings en un archivo JSON
with open("rag_ingested_chunks.json", "w") as f:
json.dump(all_chunks, f, indent=4)
97,148 changes: 97,148 additions & 0 deletions spanish/rag_ingested_chunks.json

Large diffs are not rendered by default.

79 changes: 79 additions & 0 deletions spanish/rag_multiturn.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import csv
import os

import azure.identity
import openai
from dotenv import load_dotenv
from lunr import lunr

# Setup the OpenAI client to use either Azure, OpenAI.com, or Ollama API
load_dotenv(override=True)
API_HOST = os.getenv("API_HOST")

if API_HOST == "azure":
token_provider = azure.identity.get_bearer_token_provider(
azure.identity.DefaultAzureCredential(), "https://cognitiveservices.azure.com/.default"
)
client = openai.AzureOpenAI(
api_version=os.environ["AZURE_OPENAI_VERSION"],
azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
azure_ad_token_provider=token_provider,
)
MODEL_NAME = os.environ["AZURE_OPENAI_DEPLOYMENT"]

elif API_HOST == "ollama":
client = openai.OpenAI(base_url=os.environ["OLLAMA_ENDPOINT"], api_key="nokeyneeded")
MODEL_NAME = os.environ["OLLAMA_MODEL"]

elif API_HOST == "github":
client = openai.OpenAI(base_url="https://models.inference.ai.azure.com", api_key=os.environ["GITHUB_TOKEN"])
MODEL_NAME = os.environ["GITHUB_MODEL"]

else:
client = openai.OpenAI(api_key=os.environ["OPENAI_KEY"])
MODEL_NAME = os.environ["OPENAI_MODEL"]

# Indexamos los datos del CSV
with open("hybridos.csv") as file:
reader = csv.reader(file)
rows = list(reader)
documents = [{"id": (i + 1), "body": " ".join(row)} for i, row in enumerate(rows[1:])]
index = lunr(ref="id", fields=["body"], documents=documents)


def search(query):
# Buscamos en el índice la pregunta del usuario
query = query.lower().replace("?", "")
results = index.search(query)
matching_rows = [rows[int(result["ref"])] for result in results]

# Formateamos como tabla markdown, ya que los llms entienden markdown
matches_table = " | ".join(rows[0]) + "\n" + " | ".join(" --- " for _ in range(len(rows[0]))) + "\n"
matches_table += "\n".join(" | ".join(row) for row in matching_rows)
return matches_table


SYSTEM_MESSAGE = """
Eres un asistente útil que responde preguntas sobre automóviles basándote en un conjunto de datos de autos híbridos.
Debes utilizar el conjunto de datos para responder las preguntas, no debes proporcionar ninguna información que no
esté en las fuentes proporcionadas.
"""
messages = [{"role": "system", "content": SYSTEM_MESSAGE}]

while True:
question = input("\nTu pregunta acerca de carros híbridos: ")

# Buscar en el CSV la pregunta
matches = search(question)
print("Found matches:\n")
print(matches)

# Usar los resultados para generar una respuesta
messages.append({"role": "user", "content": f"{question}\nSources: {matches}"})
response = client.chat.completions.create(model=MODEL_NAME, temperature=0.3, messages=messages)

bot_response = response.choices[0].message.content
messages.append({"role": "assistant", "content": bot_response})

print(f"\nResponse from {API_HOST} {MODEL_NAME}: \n")
print(bot_response)
100 changes: 100 additions & 0 deletions spanish/rag_queryrewrite.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import csv
import os

import azure.identity
import openai
from dotenv import load_dotenv
from lunr import lunr

# Setup the OpenAI client to use either Azure, OpenAI.com, or Ollama API
load_dotenv(override=True)
API_HOST = os.getenv("API_HOST")

if API_HOST == "azure":
token_provider = azure.identity.get_bearer_token_provider(
azure.identity.DefaultAzureCredential(), "https://cognitiveservices.azure.com/.default"
)
client = openai.AzureOpenAI(
api_version=os.environ["AZURE_OPENAI_VERSION"],
azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
azure_ad_token_provider=token_provider,
)
MODEL_NAME = os.environ["AZURE_OPENAI_DEPLOYMENT"]

elif API_HOST == "ollama":
client = openai.OpenAI(base_url=os.environ["OLLAMA_ENDPOINT"], api_key="nokeyneeded")
MODEL_NAME = os.environ["OLLAMA_MODEL"]

elif API_HOST == "github":
client = openai.OpenAI(base_url="https://models.inference.ai.azure.com", api_key=os.environ["GITHUB_TOKEN"])
MODEL_NAME = os.environ["GITHUB_MODEL"]

else:
client = openai.OpenAI(api_key=os.environ["OPENAI_KEY"])
MODEL_NAME = os.environ["OPENAI_MODEL"]

# Indexar los datos del CSV
with open("hybridos.csv") as file:
reader = csv.reader(file)
rows = list(reader)
documents = [{"id": (i + 1), "body": " ".join(row)} for i, row in enumerate(rows[1:])]
index = lunr(ref="id", fields=["body"], documents=documents)


def search(query):
# Buscar en el índice la pregunta del usuario
results = index.search(query)
matching_rows = [rows[int(result["ref"])] for result in results]

# Formatear como una tabla markdown, ya que los modelos de lenguaje entienden markdown
matches_table = " | ".join(rows[0]) + "\n" + " | ".join(" --- " for _ in range(len(rows[0]))) + "\n"
matches_table += "\n".join(" | ".join(row) for row in matching_rows)
return matches_table


QUERY_REWRITE_SYSTEM_MESSAGE = """
Eres un asistente útil que reescribe preguntas de usuarios a consultas de alta calidad de tipo keyword
para un índice de filas CSV con estas columnas: vehicle, year, msrp, acceleration, mpg, class.
Consultas de alta calidad de keyword no tienen puntuación y están en minúsculas.
Se te proporcionará la nueva pregunta del usuario y el historial de conversación.
Responde SÓLO con la consulta de keyword sugerida, sin texto adicional.
"""

SYSTEM_MESSAGE = """
Eres un asistente útil que responde preguntas sobre automóviles basándose en un conjunto de datos de coches híbridos.
Debes utilizar el conjunto de datos para responder las preguntas, no debes proporcionar información que no
esté en las fuentes proporcionadas.
"""
messages = [{"role": "system", "content": SYSTEM_MESSAGE}]

while True:
question = input("\nTu pregunta sobre coches eléctricos: ")

# Reescribir la consulta para corregir errores tipográficos e incorporar contexto pasado
response = client.chat.completions.create(
model=MODEL_NAME,
temperature=0.3,
messages=[
{"role": "system", "content": QUERY_REWRITE_SYSTEM_MESSAGE},
{
"role": "user",
"content": f"Nueva pregunta del usuario:{question}\n\nHistorial de conversación:{messages}",
},
],
)
search_query = response.choices[0].message.content
print(f"Consulta reescrita: {search_query}")

# Buscar en el CSV la pregunta
matches = search(search_query)
print("Coincidencias encontradas:\n", matches)

# Usar las coincidencias para generar una respuesta
messages.append({"role": "user", "content": f"{question}\nFuentes: {matches}"})
response = client.chat.completions.create(model=MODEL_NAME, temperature=0.3, messages=messages)

bot_response = response.choices[0].message.content
messages.append({"role": "assistant", "content": bot_response})

print(f"\nRespuesta de {API_HOST} {MODEL_NAME}: \n")
print(bot_response)