Cristobal299 commited on
Commit
cbb7375
·
verified ·
1 Parent(s): 12b7a9b

Upload 3 files

Browse files
Files changed (3) hide show
  1. Genesis.json +12 -0
  2. app-68.py +240 -0
  3. requirements-23.txt +3 -0
Genesis.json ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "id": "Genesis",
3
+ "es": "Génesis",
4
+ "heb": "בְּרֵאשִׁית",
5
+ "capitulos": {
6
+ "1": [
7
+ { "n": 1, "he": "בְּרֵאשִׁית בָּרָא אֱלֹהִים אֵת הַשָּׁמַיִם וְאֵת הָאָרֶץ", "es": "En el principio creó Dios los cielos y la tierra." },
8
+ { "n": 2, "he": "וְהָאָרֶץ הָיְתָה תֹהוּ וָבֹהוּ וְחֹשֶׁךְ עַל־פְּנֵי תְהוֹם וְרוּחַ אֱלֹהִים מְרַחֶפֶת עַל־פְּנֵי הַמָּיִם", "es": "Y la tierra estaba desordenada y vacía, y las tinieblas estaban sobre la faz del abismo, y el espíritu de Dios se movía sobre la faz de las aguas." },
9
+ { "n": 3, "he": "וַיֹּאמֶר אֱלֹהִים יְהִי אוֹר וַיְהִי־אוֹר", "es": "Y dijo Dios: Sea la luz; y fue la luz." }
10
+ ]
11
+ }
12
+ }
app-68.py ADDED
@@ -0,0 +1,240 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Centralita Torá — HuggingFace Space (Gradio) · 100% gratis
3
+ ----------------------------------------------------------
4
+ Leer la Torá en HEBREO + ESPAÑOL a la vez · escuchar en ambos idiomas ·
5
+ estudiar con IA (Groq). Sin costes: Groq (tier gratis) + gTTS (voces gratis).
6
+
7
+ TÚ SUBES LOS LIBROS.
8
+ Pon un archivo JSON por libro en la carpeta libros/ del Space.
9
+ Formato de cada archivo (ejemplo: libros/Genesis.json):
10
+
11
+ {
12
+ "id": "Genesis",
13
+ "es": "Génesis",
14
+ "heb": "בְּרֵאשִׁית",
15
+ "capitulos": {
16
+ "1": [
17
+ {"n": 1, "he": "בְּרֵאשִׁית בָּרָא...", "es": "En el principio creó Dios..."},
18
+ {"n": 2, "he": "...", "es": "..."}
19
+ ]
20
+ }
21
+ }
22
+
23
+ Secret del Space (Settings → Variables and secrets):
24
+ GROQ_API_KEY = tu_clave (gratis en https://console.groq.com/keys)
25
+
26
+ requirements.txt: gradio groq gTTS
27
+ """
28
+ import os, glob, json, tempfile
29
+ import gradio as gr
30
+ from gtts import gTTS
31
+
32
+ try:
33
+ from groq import Groq
34
+ _groq = Groq() if os.environ.get("GROQ_API_KEY") else None
35
+ except Exception:
36
+ _groq = None
37
+
38
+ MODELO_ESTUDIA = "openai/gpt-oss-120b" # Groq, gratis, para el estudio
39
+ CARPETA = "libros"
40
+
41
+ # ---------- cargar los libros que subiste ----------
42
+ def cargar_libros():
43
+ libros = {}
44
+ for ruta in sorted(glob.glob(os.path.join(CARPETA, "*.json"))):
45
+ try:
46
+ with open(ruta, encoding="utf-8") as f:
47
+ d = json.load(f)
48
+ lid = d.get("id") or os.path.splitext(os.path.basename(ruta))[0]
49
+ libros[lid] = d
50
+ except Exception as e:
51
+ print("No pude leer", ruta, e)
52
+ return libros
53
+
54
+ LIBROS = cargar_libros()
55
+
56
+ def nombres_es():
57
+ return [d.get("es", lid) for lid, d in LIBROS.items()] or ["(sube tus libros)"]
58
+
59
+ def id_por_es(nombre):
60
+ for lid, d in LIBROS.items():
61
+ if d.get("es", lid) == nombre:
62
+ return lid
63
+ return None
64
+
65
+ def caps_de(lid):
66
+ caps = LIBROS.get(lid, {}).get("capitulos", {})
67
+ return sorted(int(c) for c in caps.keys()) if caps else [1]
68
+
69
+ def versiculos_de(lid, cap):
70
+ return LIBROS.get(lid, {}).get("capitulos", {}).get(str(cap), [])
71
+
72
+ # ---------- render bilingüe ----------
73
+ def render(lid, cap):
74
+ if not lid or lid not in LIBROS:
75
+ return ("### Aún no has subido libros\n"
76
+ "Sube un JSON por libro a la carpeta `libros/` del Space. "
77
+ "Mira la pestaña **Cómo subir**."), "", ""
78
+ d = LIBROS[lid]
79
+ vs = versiculos_de(lid, cap)
80
+ if not vs:
81
+ return f"### {d.get('es')} {cap}\n\n_No hay texto para este capítulo._", "", ""
82
+ partes = [f"### {d.get('heb','')} · {d.get('es')} {cap}\n"]
83
+ plano_es, plano_he = [], []
84
+ for v in vs:
85
+ he, es = v.get("he", ""), v.get("es", "")
86
+ bloque = f"**{v.get('n','')}**"
87
+ if he:
88
+ bloque += (f"\n<div dir='rtl' style='font-size:1.55em;line-height:1.9;"
89
+ f"font-family:serif'>{he}</div>")
90
+ plano_he.append(he)
91
+ if es:
92
+ bloque += f"\n\n{es}"
93
+ plano_es.append(es)
94
+ partes.append(bloque)
95
+ return "\n\n".join(partes), " ".join(plano_es), " ".join(plano_he)
96
+
97
+ # ---------- voces gratis (gTTS) ----------
98
+ def a_voz(texto, idioma):
99
+ if not texto.strip():
100
+ return None
101
+ ruta = tempfile.NamedTemporaryFile(suffix=".mp3", delete=False).name
102
+ lang = "iw" if idioma == "he" else "es"
103
+ try:
104
+ gTTS(text=texto[:4500], lang=lang).save(ruta)
105
+ return ruta
106
+ except Exception:
107
+ try: # algunos entornos usan "he" en vez de "iw"
108
+ gTTS(text=texto[:4500], lang="he" if idioma == "he" else "es").save(ruta)
109
+ return ruta
110
+ except Exception:
111
+ return None
112
+
113
+ # ---------- estudio con IA (Groq, gratis) ----------
114
+ def estudiar(historial, pregunta, contexto):
115
+ if not _groq:
116
+ return "Añade tu GROQ_API_KEY como Secret del Space para activar el estudio."
117
+ sistema = (
118
+ "Eres un compañero de estudio de la Torá que responde en español, cálido y honesto. "
119
+ "Ofreces el sentido literal (peshat), contexto histórico y lingüístico, capas de "
120
+ "interpretación de la tradición (midrash y comentaristas clásicos como Rashi cuando "
121
+ "venga al caso) y perspectivas espirituales para la reflexión. Presentas las "
122
+ "interpretaciones como perspectivas de estudio, no como dogma. Vas al grano.\n\n" + contexto
123
+ )
124
+ mensajes = [{"role": "system", "content": sistema}]
125
+ for u, a in historial:
126
+ mensajes.append({"role": "user", "content": u})
127
+ if a:
128
+ mensajes.append({"role": "assistant", "content": a})
129
+ mensajes.append({"role": "user", "content": pregunta})
130
+ try:
131
+ resp = _groq.chat.completions.create(
132
+ model=MODELO_ESTUDIA, max_tokens=1200, temperature=0.4, messages=mensajes
133
+ )
134
+ return (resp.choices[0].message.content or "").strip()
135
+ except Exception as e:
136
+ return f"No pude consultar a la IA ahora mismo ({e})."
137
+
138
+ # ============================ INTERFAZ ============================
139
+ CSS = ".gradio-container {max-width: 860px !important; margin: auto;}"
140
+
141
+ with gr.Blocks(css=CSS, title="Centralita Torá", theme=gr.themes.Soft()) as demo:
142
+ st_lid = gr.State(None)
143
+ st_cap = gr.State(1)
144
+ st_es = gr.State("")
145
+ st_he = gr.State("")
146
+ st_hist = gr.State([])
147
+
148
+ gr.Markdown("# ✡ Centralita Torá\nHebreo + español · escuchar · estudiar · todo gratis")
149
+
150
+ with gr.Tab("Leer"):
151
+ with gr.Row():
152
+ dd_libro = gr.Dropdown(nombres_es(), value=(nombres_es()[0]), label="Libro")
153
+ num_cap = gr.Number(value=1, precision=0, label="Capítulo", minimum=1)
154
+ btn_ver = gr.Button("Ver capítulo", variant="primary")
155
+ salida = gr.Markdown()
156
+ with gr.Row():
157
+ btn_es = gr.Button("🔊 Español")
158
+ btn_he = gr.Button("🔊 עברית")
159
+ audio = gr.Audio(label="", autoplay=True)
160
+
161
+ def ver(nombre, cap):
162
+ lid = id_por_es(nombre)
163
+ cap = max(1, int(cap or 1))
164
+ md, es, he = render(lid, cap)
165
+ return md, lid, cap, es, he
166
+
167
+ btn_ver.click(ver, [dd_libro, num_cap], [salida, st_lid, st_cap, st_es, st_he])
168
+ btn_es.click(lambda t: a_voz(t, "es"), st_es, audio)
169
+ btn_he.click(lambda t: a_voz(t, "he"), st_he, audio)
170
+
171
+ with gr.Tab("Buscar"):
172
+ txt_q = gr.Textbox(label="Palabra o frase (hebreo o español)", placeholder="ej. luz / אור")
173
+ btn_q = gr.Button("Buscar", variant="primary")
174
+ res = gr.Markdown()
175
+
176
+ def buscar(q):
177
+ q = (q or "").strip().lower()
178
+ if not q:
179
+ return "Escribe algo para buscar."
180
+ hits = []
181
+ for lid, d in LIBROS.items():
182
+ for c, vs in d.get("capitulos", {}).items():
183
+ for v in vs:
184
+ campos = " ".join(filter(None, [v.get("es"), v.get("he")])).lower()
185
+ if q in campos:
186
+ texto = v.get("es") or v.get("he")
187
+ hits.append(f"**{d.get('es', lid)} {c}:{v.get('n')}** — {texto}")
188
+ if len(hits) >= 100:
189
+ return "\n\n".join(hits)
190
+ return "\n\n".join(hits) if hits else "Sin resultados en los libros que subiste."
191
+
192
+ btn_q.click(buscar, txt_q, res)
193
+
194
+ with gr.Tab("Estudiar"):
195
+ gr.Markdown("La IA (Groq, gratis) usa el capítulo que tengas abierto en **Leer** como contexto.")
196
+ chat = gr.Chatbot(height=380)
197
+ txt_p = gr.Textbox(placeholder="Pregunta sobre el pasaje…", label="")
198
+ with gr.Row():
199
+ btn_env = gr.Button("Preguntar", variant="primary")
200
+ btn_com = gr.Button("Comentar este capítulo")
201
+
202
+ def responder(pregunta, hist, es, he, lid, cap):
203
+ if not (pregunta or "").strip():
204
+ return hist, ""
205
+ nombre = LIBROS.get(lid, {}).get("es", "") if lid else ""
206
+ contexto = f"Pasaje en pantalla — {nombre} {cap}:\nHebreo: {he}\nEspañol: {es}"
207
+ r = estudiar(hist, pregunta, contexto)
208
+ return hist + [[pregunta, r]], ""
209
+
210
+ btn_env.click(responder, [txt_p, st_hist, st_es, st_he, st_lid, st_cap], [chat, txt_p]) \
211
+ .then(lambda h: h, chat, st_hist)
212
+ btn_com.click(lambda h, es, he, l, c: responder(
213
+ "Comenta y ayúdame a estudiar este capítulo.", h, es, he, l, c),
214
+ [st_hist, st_es, st_he, st_lid, st_cap], [chat, txt_p]) \
215
+ .then(lambda h: h, chat, st_hist)
216
+
217
+ with gr.Tab("Cómo subir"):
218
+ gr.Markdown(
219
+ "### Tú subes los libros\n"
220
+ "Un archivo JSON por libro dentro de la carpeta `libros/` del Space. Ejemplo `libros/Genesis.json`:\n\n"
221
+ "```json\n"
222
+ "{\n"
223
+ ' "id": "Genesis",\n'
224
+ ' "es": "Génesis",\n'
225
+ ' "heb": "בְּרֵאשִׁית",\n'
226
+ ' "capitulos": {\n'
227
+ ' "1": [\n'
228
+ ' {"n": 1, "he": "בְּרֵאשִׁית בָּרָא...", "es": "En el principio creó Dios..."}\n'
229
+ " ]\n"
230
+ " }\n"
231
+ "}\n"
232
+ "```\n"
233
+ "- `he` es el hebreo y `es` el español de cada versículo. Puedes poner solo uno si aún no tienes el otro.\n"
234
+ "- Añade cuantos capítulos quieras dentro de `capitulos`.\n"
235
+ "- Súbelos con git (carpeta `libros/`) y reinicia el Space.\n\n"
236
+ "**Secret:** `GROQ_API_KEY` en Settings → Variables and secrets. Las voces no necesitan clave."
237
+ )
238
+
239
+ if __name__ == "__main__":
240
+ demo.launch()
requirements-23.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ gradio
2
+ groq
3
+ gTTS