| import os |
| import streamlit as st |
| from langchain_community.embeddings import HuggingFaceEmbeddings |
| from langchain_groq import ChatGroq |
| from langchain.chains.retrieval_qa.base import RetrievalQA |
| from langchain_pinecone import PineconeVectorStore |
|
|
| st.set_page_config(page_title="Opositor", page_icon="📚", layout="wide") |
| st.title("📚 Pregunta al TREBEP") |
|
|
| MODELOS_LLM = { |
| "llama-3.3-70b-versatile": "llama-3.3-70b-versatile", |
| "openai/gpt-oss-120b": "openai/gpt-oss-120b", |
| "moonshotai/kimi-k2-instruct-0905": "moonshotai/kimi-k2-instruct-0905", |
| } |
|
|
| with st.sidebar: |
| st.header("Configuración") |
| modelo_llm = st.selectbox("Modelo de lenguaje", MODELOS_LLM) |
| k_docs = st.slider("Número de fragmentos recuperados", 2, 8, 4) |
|
|
| if "chat_history" not in st.session_state: |
| st.session_state.chat_history = [] |
|
|
| @st.cache_resource |
| def setup_qa(modelo_llm: str, k_docs: int): |
| groq_api_key = os.getenv("GROQ_API_KEY") |
| pinecone_api_key = os.getenv("PINECONE_API_KEY") |
| langsmith_api_key = os.getenv("LANGCHAIN_API_KEY") |
|
|
| if not groq_api_key: |
| raise ValueError("Falta GROQ_API_KEY en los Secrets.") |
| if not pinecone_api_key: |
| raise ValueError("Falta PINECONE_API_KEY en los Secrets.") |
|
|
| if langsmith_api_key: |
| os.environ["LANGCHAIN_TRACING_V2"] = "true" |
| os.environ["LANGCHAIN_API_KEY"] = langsmith_api_key |
| os.environ["LANGCHAIN_PROJECT"] = "trebep" |
|
|
| os.environ["GROQ_API_KEY"] = groq_api_key |
| os.environ["PINECONE_API_KEY"] = pinecone_api_key |
|
|
| embedding = HuggingFaceEmbeddings( |
| model_name="intfloat/multilingual-e5-base" |
| ) |
|
|
| llm = ChatGroq( |
| model_name=modelo_llm, |
| temperature=0 |
| ) |
|
|
| vectorstore = PineconeVectorStore( |
| index_name="boe-intfloat-multilingual-e5-base", |
| namespace="trebep", |
| embedding=embedding, |
| ) |
|
|
| retriever = vectorstore.as_retriever(search_kwargs={"k": k_docs}) |
|
|
| qa = RetrievalQA.from_chain_type( |
| llm=llm, |
| chain_type="stuff", |
| retriever=retriever, |
| return_source_documents=True, |
| ) |
| return qa |
|
|
| pregunta = st.text_input("Haz una pregunta sobre el TREBEP") |
|
|
| if pregunta: |
| try: |
| qa = setup_qa(modelo_llm, k_docs) |
| respuesta = qa.invoke({"query": pregunta}) |
|
|
| result = respuesta.get("result", "").strip() |
| docs = respuesta.get("source_documents", []) |
|
|
| st.subheader("Respuesta") |
| st.success(result) |
|
|
| st.session_state.chat_history.append({ |
| "pregunta": pregunta, |
| "respuesta": result |
| }) |
|
|
| with st.expander("Fuentes recuperadas"): |
| if docs: |
| for i, doc in enumerate(docs, start=1): |
| st.markdown(f"**Fragmento {i}**") |
| st.write(doc.page_content) |
| if doc.metadata: |
| st.caption(str(doc.metadata)) |
| st.divider() |
| else: |
| st.write("No se recuperaron fragmentos.") |
|
|
| except Exception as e: |
| st.error(f"Error al procesar la consulta: {e}") |
|
|
| with st.sidebar: |
| st.header("Historial") |
| if st.session_state.chat_history: |
| for item in reversed(st.session_state.chat_history): |
| st.markdown(f"**Pregunta:** {item['pregunta']}") |
| st.write(item["respuesta"]) |
| st.divider() |
| else: |
| st.write("Todavía no hay preguntas en esta sesión.") |