File size: 9,783 Bytes
10f2497
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c6aeefd
 
 
 
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# chatbot_tema.py
# Chatbot por tema específico + vocabulario limitado a 1000 palabras.

import os
import re
import random
from collections import Counter

# =========================
# CONFIG
# =========================
TOPICO = "entrenamiento en el gimnasio"  # <— Cambiá el tema acá
PALABRAS_CLAVE = {
    # Palabras que identifican el tema. Agregá o quitá según tu caso.
    "gimnasio","entrenar","rutina","pesas","mancuernas","series","repeticiones",
    "calentamiento","piernas","pecho","espalda","hombros","biceps","triceps",
    "cardio","fuerza","hipertrofia","descanso","nutricion","proteina","creatina",
    "tecnica","maquina","sentadilla","press","remar","dominadas","plan","progreso"
}
ARCHIVO_VOCAB = "vocab.txt"
TAMANIO_VOCAB = 1000

# Mensaje de rechazo cuando está fuera de tema
MENSAJE_FUERA_DE_TEMA = (
    "lo siento. solo puedo hablar sobre " + TOPICO + ". "
    "por favor hacé una pregunta relacionada."
)

# =========================
# UTILIDADES
# =========================
def normalizar(texto: str) -> str:
    texto = texto.lower()
    texto = re.sub(r"[^\wáéíóúñü ]", " ", texto, flags=re.UNICODE)
    texto = re.sub(r"\s+", " ", texto).strip()
    return texto

def cargar_o_crear_vocab(ruta: str, objetivo: int) -> list:
    """
    - Si existe vocab.txt: lo carga y valida tamaño.
    - Si no existe: crea una lista de palabras base + tokens de relleno hasta llegar a 1000
      y la guarda en el archivo para que la puedas editar.
    """
    if os.path.exists(ruta):
        with open(ruta, "r", encoding="utf-8") as f:
            palabras = [normalizar(l) for l in f if l.strip()]
        # Quitar duplicados respetando orden
        seen = set()
        vocab = []
        for p in palabras:
            if p not in seen:
                seen.add(p)
                vocab.append(p)
        # Ajustar a tamaño deseado (recortar o avisar)
        if len(vocab) > objetivo:
            vocab = vocab[:objetivo]
        elif len(vocab) < objetivo:
            faltan = objetivo - len(vocab)
            # Relleno controlado
            inicio = len(vocab) + 1
            for i in range(inicio, inicio + faltan):
                vocab.append(f"token{i:03d}")
        return vocab
    else:
        base = set("""
yo tu vos usted ustedes nosotros hola chau gracias por favor si no tal vez
puedo podes podesme decir contar explicar como cuando donde cual porque para
hoy ayer manana semana mes anio ahora antes despues poco mucho bien mal mas
menos igual tema topico relacionado ejemplo paso guia idea ayuda duda
pregunta respuesta claro listo simple rapido seguro lento suave duro facil dificil
cuerpo espalda pecho hombro pierna brazo biceps triceps abdomen gluteo
calentar descanso energia fuerza peso repetir serie rutina plan objetivo
progreso tecnica forma postura respiracion dolor cuidado prevenir riesgo
maquina banco barra mancuerna polea cuerda banda remo press sentadilla
dominada elevacion curl extension cardio cinta bici remadora
tiempo minuto segundo intervalo dia
nutricion comida proteina carbohidrato grasa agua creatina suplemento
tomar comer cocinar batido leche avena huevo arroz pollo carne banana
antes durante despues entrenar
""".split())
        # Asegurar que las plantillas puedan hablar
        base.update({
            "hola","bienvenido","estoy","listo","para","ayudar","sobre","entrenamiento",
            "en","el","gimnasio","es","importante","usar","buena","tecnica","y","progresar",
            "de","a","poco","si","sos","principiante","empeza","con","poco","peso","y",
            "aumenta","cuando","la","forma","sea","solida","queres","rutina","para","hoy",
            "tenes","alguna","meta","fuerza","o","hipertrofia","recorda","descansar",
            "entre","series","y","dormir","bien"
        })
        base = [normalizar(w) for w in base if w.strip()]
        # Completar hasta objetivo
        vocab = []
        seen = set()
        for w in base:
            if w not in seen:
                seen.add(w)
                vocab.append(w)
        i = 1
        while len(vocab) < objetivo:
            token = f"token{i:03d}"
            if token not in seen:
                seen.add(token)
                vocab.append(token)
            i += 1
        # Guardar para que puedas editarlo
        with open(ruta, "w", encoding="utf-8") as f:
            f.write("\n".join(vocab))
        return vocab

def es_en_tema(texto_usuario: str, palabras_clave: set) -> bool:
    tu = set(normalizar(texto_usuario).split())
    # Coincidencia si hay intersección con palabras clave o con el nombre del tópico
    if tu & palabras_clave:
        return True
    for w in normalizar(TOPICO).split():
        if w in tu:
            return True
    return False

def limitar_a_vocab(texto: str, vocab_set: set) -> str:
    """
    Asegura que la salida solo use palabras del vocabulario.
    Palabras fuera del vocabulario se sustituyen por 'token001'.
    """
    palabras = normalizar(texto).split()
    out = []
    sustituto = "token001" if "token001" in vocab_set else next(iter(vocab_set))
    for p in palabras:
        out.append(p if p in vocab_set else sustituto)
    return " ".join(out)

# =========================
# NUCLEO DE RESPUESTAS
# =========================
def detectar_intencion(texto_usuario: str) -> str:
    t = normalizar(texto_usuario)
    if any(x in t for x in ["hola","buenas","que tal","buen dia","buenas tardes","buenas noches"]):
        return "saludo"
    if any(x in t for x in ["rutina","plan","programa","entreno","entrenar","hoy que hago"]):
        return "rutina"
    if any(x in t for x in ["pecho","espalda","pierna","hombro","biceps","triceps","abdomen","core"]):
        return "musculo"
    if any(x in t for x in ["tecnica","forma","postura","como hago","como se hace"]):
        return "tecnica"
    if any(x in t for x in ["nutricion","comer","proteina","creatina","batido","dieta","comida"]):
        return "nutricion"
    if any(x in t for x in ["descanso","series","repeticiones","cuanto","cuantas","minutos","intervalo"]):
        return "parametros"
    if any(x in t for x in ["chau","adios","gracias","nos vemos","hasta luego"]):
        return "despedida"
    return "desconocida"

def generar_respuesta_en_tema(intencion: str) -> str:
    # Plantillas sencillas y cortas; todas en vocabulario básico
    if intencion == "saludo":
        return (
            "hola. estoy listo para ayudar sobre entrenamiento en el gimnasio. "
            "queres una rutina para hoy o una guia por objetivo."
        )
    if intencion == "rutina":
        return (
            "rutina base: cuerpo completo. hacé tres series por ejercicio y ocho a doce repeticiones. "
            "ejemplo: sentadilla, press pecho, remo, elevacion hombro, curl biceps, extension triceps, plancha. "
            "descanso uno a dos minutos entre series."
        )
    if intencion == "musculo":
        return (
            "para ese musculo, usa uno a tres ejercicios con tecnica solida. "
            "progresá poco a poco y evita dolor raro. "
            "si la forma falla, baja el peso y repetí."
        )
    if intencion == "tecnica":
        return (
            "usa postura neutral, abdomen activo y movimiento controlado. "
            "sin rebote y con rango que puedas sostener sin dolor. "
            "respira: baja tomando aire y sube soltando."
        )
    if intencion == "nutricion":
        return (
            "nutricion simple: prioriza proteina en cada comida, hidrata con agua y dormi bien. "
            "si usas creatina: cinco gramos al dia con comida. "
            "no es magia, es constancia."
        )
    if intencion == "parametros":
        return (
            "series: dos a cuatro por ejercicio. repeticiones: seis a doce para hipertrofia. "
            "descanso: uno a tres minutos segun esfuerzo. "
            "progreso: subí poco el peso cuando la tecnica sea solida."
        )
    if intencion == "despedida":
        return "gracias por charlar. buen entrenamiento y buen descanso."
    # desconocida en tema
    return (
        "puedo hablar sobre rutina, tecnica, nutricion, series y progresion en el gimnasio. "
        "decime que queres y armamos algo simple."
    )

# =========================
# LOOP PRINCIPAL
# =========================
def main():
    vocab = cargar_o_crear_vocab(ARCHIVO_VOCAB, TAMANIO_VOCAB)
    vocab_set = set(vocab)

    print("chatbot:", limitar_a_vocab(
        f"hola. soy un asistente sobre {TOPICO}. usa mensajes simples. escribi 'salir' para terminar.",
        vocab_set
    ))

    while True:
        try:
            user = input("vos: ")
        except (EOFError, KeyboardInterrupt):
            print("\nchatbot: chau.")
            break

        if not user:
            continue

        if normalizar(user) == "salir":
            print("chatbot:", limitar_a_vocab("chau. hasta luego.", vocab_set))
            break

        # Verificar tema
        if not es_en_tema(user, PALABRAS_CLAVE):
            print("chatbot:", limitar_a_vocab(MENSAJE_FUERA_DE_TEMA, vocab_set))
            continue

        # Generar respuesta en tema y limitar a vocab
        intent = detectar_intencion(user)
        respuesta = generar_respuesta_en_tema(intent)
        respuesta_limited = limitar_a_vocab(respuesta, vocab_set)

        # Asegurar que no quede vacía
        if not respuesta_limited.strip():
            respuesta_limited = limitar_a_vocab(
                "lo siento. no tengo una respuesta. podes repetir con otras palabras.",
                vocab_set
            )

        print("chatbot:", respuesta_limited)

if __name__ == "__main__":
    main()
import gradio as gr

iface = gr.Interface(fn=chat, inputs="text", outputs="text")
iface.launch()