File size: 4,438 Bytes
1e2c986
9eb2998
1e2c986
 
 
 
 
a2a383d
1e2c986
 
 
a2a383d
1e2c986
 
 
 
 
 
a2a383d
 
 
 
 
 
 
 
 
1e2c986
 
 
a2a383d
 
1e2c986
 
bbefc18
a2a383d
 
1e2c986
a2a383d
1e2c986
a2a383d
 
 
 
b958ad2
a2a383d
 
 
1e2c986
 
 
 
 
 
 
a2a383d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9eb2998
a2a383d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9eb2998
 
 
 
 
 
a2a383d
 
 
 
 
 
 
 
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import streamlit as st
from streamlit_pdf_viewer import pdf_viewer
import base64
import os
from io import BytesIO
from pypdf import PdfReader
from langchain.schema import Document
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_pinecone import PineconeVectorStore
from pinecone import Pinecone as PineconeClient, ServerlessSpec

# Configuración de entorno
hf_token = os.getenv("HUGGINGFACE_TOKEN")
pinecone_api_key = os.getenv("PINECONE_API_KEY")

st.set_page_config(page_title="Clasificador de CVs", layout="wide")
st.title("🎯 Clasificador de CVs por Puesto de Trabajo")

# Cargar modelo de embeddings
@st.cache_resource(show_spinner="⏳ Cargando modelo de embeddings...")
def load_embeddings():
    model = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
    _ = model.embed_query("test")  # Calentamiento
    return model

embedding = load_embeddings()

# Inputs
titulo_puesto = st.text_input("🧑‍💼 Título del puesto", placeholder="Ej: Desarrollador Backend Senior")
descripcion_puesto = st.text_area("📝 Descripción del puesto", height=200)

# Subida de archivos
uploaded_files = st.file_uploader("📎 Subir CVs (PDF)", type="pdf", accept_multiple_files=True)

if uploaded_files:
    if "cv_data" not in st.session_state:
        st.session_state["cv_data"] = {}

    cv_data = st.session_state["cv_data"]

    for file in uploaded_files:
        if file.name in cv_data:
            st.info(f"ℹ️ El archivo `{file.name}` ya fue subido. Ignorando duplicado.")
            continue

        buffer = BytesIO(file.read())
        buffer.seek(0)
        try:
            reader = PdfReader(buffer)
            text = ""
            for page in reader.pages:
                page_text = page.extract_text()
                if page_text:
                    text += page_text + "\n"

            if text.strip():
                cv_data[file.name] = {
                    "text": text.strip(),
                    "pdf": buffer
                }
                st.success(f"✅ Procesado `{file.name}` correctamente.")
            else:
                st.warning(f"⚠️ No se pudo extraer texto de `{file.name}`.")
        except Exception as e:
            st.error(f"❌ Error procesando `{file.name}`: {e}")

# Procesamiento de CVs
if st.button("📊 Procesar CVs"):
    cv_data = st.session_state.get("cv_data", {})

    if not cv_data:
        st.warning("No hay CVs para procesar.")
        st.stop()

    if not descripcion_puesto.strip():
        st.warning("Debes ingresar una descripción del puesto.")
        st.stop()

    # Inicializar Pinecone
    pc = PineconeClient(api_key=pinecone_api_key)
    index_name = "cv-index"

    if index_name not in pc.list_indexes().names():
        pc.create_index(
            name=index_name,
            dimension=384,
            metric='cosine',
            spec=ServerlessSpec(cloud='aws', region='us-east-1')
        )
    index = pc.Index(index_name)
    index.delete(delete_all=True)

    vector_store = PineconeVectorStore(index=index, embedding=embedding)

    # Crear documentos
    documents = []
    for filename, data in cv_data.items():
        doc = Document(
            page_content=data["text"],
            metadata={"filename": filename, "titulo_puesto": titulo_puesto}
        )
        documents.append(doc)

    # Subir a Pinecone
    vector_store.add_documents(documents)

    # Búsqueda por similitud
    results = vector_store.similarity_search_with_score(descripcion_puesto, k=len(documents))
    st.success(f"{len(results)} CV(s) procesado(s).")

    # Mostrar resultados
    for doc, score in results:
        filename = doc.metadata["filename"]
        data = cv_data[filename]

        st.markdown("---")
        col1, col2 = st.columns([2, 1])

        with col1:
            data["pdf"].seek(0)
            pdf_bytes = data["pdf"].read()
            st.markdown(f"#### 👀 Visualizador PDF: `{filename}`")
            if pdf_bytes:
                pdf_viewer(pdf_bytes)
            else:
                st.error("⚠️ El archivo PDF está vacío o no se pudo leer.")


        with col2:
            st.markdown("#### 📄 Detalles")
            st.write(f"**Nombre del archivo:** `{filename}`")
            st.write(f"**Similitud con descripción:** `{score * 100:.2f}%`")

    # Opcional: eliminar vectores del índice (descomenta si deseas limpiar después)