Update app.py
Browse files
app.py
CHANGED
|
@@ -1,293 +1,303 @@
|
|
| 1 |
-
#
|
| 2 |
-
#
|
| 3 |
-
#
|
| 4 |
#
|
| 5 |
-
#
|
| 6 |
-
#
|
| 7 |
-
#
|
|
|
|
| 8 |
|
| 9 |
import gradio as gr
|
| 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 |
</div>
|
| 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 |
-
crafted_prompt = f"""
|
| 92 |
-
Actúa como un experto estratega en comunicación corporativa y redacción de emails. Tu misión es redactar un borrador de email claro, conciso y diplomático.
|
| 93 |
-
|
| 94 |
-
**CONTEXTO DEL EMAIL:**
|
| 95 |
-
- **Destinatario:** {recipient}
|
| 96 |
-
- **Mi Rol:** {role}
|
| 97 |
-
- **Objetivo Principal:** {goal}
|
| 98 |
-
- **Tono Requerido:** {tone}
|
| 99 |
-
|
| 100 |
-
**CONTENIDO OBLIGATORIO:**
|
| 101 |
-
El email debe abordar, de manera estructurada y lógica, los siguientes puntos clave:
|
| 102 |
-
{key_points}
|
| 103 |
-
|
| 104 |
-
**INSTRUCCIONES DE FORMATO:**
|
| 105 |
-
1. **Asunto:** Crea un asunto breve y descriptivo que refleje el propósito del email.
|
| 106 |
-
2. **Apertura:** Comienza con un saludo profesional y una breve introducción que establezca el contexto.
|
| 107 |
-
3. **Cuerpo:** Desarrolla los puntos clave de forma clara y separada. Utiliza párrafos cortos.
|
| 108 |
-
4. **Cierre:** Concluye con un resumen de la acción deseada o el siguiente paso, y un cierre profesional.
|
| 109 |
-
5. **Revisión:** Asegúrate de que el lenguaje sea constructivo y esté alineado con el objetivo y el tono solicitados. Evita la ambigüedad.
|
| 110 |
-
"""
|
| 111 |
-
insight = """
|
| 112 |
-
Este prompt es eficaz porque utiliza la técnica **"Rol, Contexto, Instrucción"**.
|
| 113 |
-
1. **Rol (`Actúa como...`):** Pone a la IA en un modo experto específico.
|
| 114 |
-
2. **Contexto (`CONTEXTO DEL EMAIL`):** Le proporciona toda la información de fondo necesaria para evitar respuestas genéricas.
|
| 115 |
-
3. **Instrucción (`CONTENIDO OBLIGATORIO`, `INSTRUCCIONES DE FORMATO`):** Le da una estructura rígida y requisitos claros, forzando una salida de alta calidad y bien organizada.
|
| 116 |
-
"""
|
| 117 |
-
pro_tip = "La IA proporciona el 90% del camino. Siempre lee el borrador en voz alta para ajustarlo a tu voz personal antes de enviarlo. El toque humano final es insustituible."
|
| 118 |
-
|
| 119 |
-
return self._generate_output_blocks(crafted_prompt, insight, pro_tip)
|
| 120 |
-
|
| 121 |
-
def craft_creative_content(self, topic: str, content_format: str, audience: str, style: str, goals: str) -> tuple:
|
| 122 |
-
"""Forja un prompt para la creación de contenido original."""
|
| 123 |
-
if not all([topic, content_format, audience, style, goals]):
|
| 124 |
-
return gr.Textbox(visible=False), gr.Markdown(visible=False), gr.Markdown(visible=False)
|
| 125 |
-
|
| 126 |
-
crafted_prompt = f"""
|
| 127 |
-
Actúa como un creador de contenido de clase mundial y estratega de marketing digital. Tu tarea es generar un borrador para una pieza de contenido excepcional.
|
| 128 |
-
|
| 129 |
-
**BRIEF CREATIVO:**
|
| 130 |
-
- **Tema Central:** {topic}
|
| 131 |
-
- **Formato del Contenido:** {content_format}
|
| 132 |
-
- **Audiencia Objetivo:** {audience}
|
| 133 |
-
- **Estilo de Escritura Deseado:** {style}
|
| 134 |
-
- **Objetivos de la Pieza (¿Qué quiero que la audiencia piense, sienta o haga?):** {goals}
|
| 135 |
-
|
| 136 |
-
**DIRECTRICES DE CREACIÓN:**
|
| 137 |
-
1. **Gancho Inicial:** Comienza con una frase o párrafo de apertura que sea extremadamente cautivador y relevante para la audiencia objetivo.
|
| 138 |
-
2. **Desarrollo Estructurado:** Organiza el contenido de forma lógica. Si es un artículo, usa subtítulos. Si es un guion, usa escenas.
|
| 139 |
-
3. **Voz y Tono:** Mantén el estilo de escritura `{style}` de manera consistente en toda la pieza.
|
| 140 |
-
4. **Llamada a la Acción (CTA):** Integra de forma natural una llamada a la acción que se alinee con los objetivos definidos.
|
| 141 |
-
5. **Originalidad:** Evita clichés y frases comunes. Busca ángulos únicos y perspectivas frescas sobre el tema.
|
| 142 |
-
"""
|
| 143 |
-
insight = """
|
| 144 |
-
La clave de este prompt es el **"Brief Creativo"**. Al igual que en una agencia de publicidad, le damos a la IA un brief completo.
|
| 145 |
-
- Definir la **Audiencia** y los **Objetivos** obliga a la IA a pensar no solo en el *qué* (el tema), sino en el *para quién* y el *para qué*.
|
| 146 |
-
- Esto transforma una simple solicitud de texto en una tarea estratégica, resultando en un contenido mucho más enfocado y efectivo.
|
| 147 |
-
"""
|
| 148 |
-
pro_tip = "Usa la primera respuesta de la IA como una lluvia de ideas. Pídele que te dé '10 ganchos iniciales alternativos' o '3 estructuras diferentes para este contenido'. La primera idea no siempre es la mejor."
|
| 149 |
-
|
| 150 |
-
def craft_code_solution(self, language: str, task: str, constraints: str, code_snippet: str) -> tuple:
|
| 151 |
-
"""Forja un prompt para solucionar problemas de programación."""
|
| 152 |
-
if not all([language, task]):
|
| 153 |
-
return gr.Textbox(visible=False), gr.Markdown(visible=False), gr.Markdown(visible=False)
|
| 154 |
-
|
| 155 |
-
context_code = f"\n**CÓDIGO EXISTENTE (para referencia o depuración):**\n```\n{code_snippet}\n```" if code_snippet else ""
|
| 156 |
-
context_constraints = f"\n**RESTRICCIONES O REQUISITOS:**\n{constraints}" if constraints else ""
|
| 157 |
-
|
| 158 |
-
crafted_prompt = f"""
|
| 159 |
-
Actúa como un ingeniero de software senior experto en el lenguaje de programación **{language}**. Tu especialidad es escribir código limpio, eficiente y bien documentado.
|
| 160 |
-
|
| 161 |
-
**TAREA A REALIZAR:**
|
| 162 |
-
{task}
|
| 163 |
-
{context_constraints}
|
| 164 |
-
{context_code}
|
| 165 |
-
|
| 166 |
-
**INSTRUCCIONES DE ENTREGA:**
|
| 167 |
-
1. **Solución en Código:** Proporciona el bloque de código completo y funcional en `{language}`.
|
| 168 |
-
2. **Explicación Detallada:** Debajo del código, añade una sección `### EXPLICACIÓN` donde describas paso a paso cómo funciona tu solución, las decisiones clave que tomaste y por qué.
|
| 169 |
-
3. **Buenas Prácticas:** Asegúrate de que el código siga las mejores prácticas del lenguaje, incluyendo nombres de variables claros y comentarios donde sea necesario.
|
| 170 |
-
4. **Eficiencia:** Considera la eficiencia (complejidad temporal y espacial) si es relevante para la tarea.
|
| 171 |
-
"""
|
| 172 |
-
insight = """
|
| 173 |
-
Este prompt funciona por su **rigurosa estructura de entrega**.
|
| 174 |
-
- No solo pide el código, exige una **Explicación Detallada**. Esto obliga a la IA a "pensar en voz alta", lo que a menudo conduce a soluciones más correctas y robustas.
|
| 175 |
-
- Separar la **Tarea** de las **Restricciones** y el **Código Existente** ayuda a la IA a organizar la información de entrada de manera más efectiva.
|
| 176 |
-
"""
|
| 177 |
-
pro_tip = "Nunca copies y pegues código de una IA directamente en producción. Úsalo como un compañero de pair programming. Entiende cada línea que te da, cuestiónala y adáptala a tu proyecto."
|
| 178 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 179 |
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
# La construcción de la interfaz con Gradio. Es el espacio de trabajo del artesano.
|
| 183 |
-
# ---------------------------------------------------------------------------------------
|
| 184 |
|
| 185 |
-
class UIBuilder:
|
| 186 |
-
"""Construye la interfaz de usuario completa para Artisan Nexus."""
|
| 187 |
def __init__(self):
|
| 188 |
-
self.
|
| 189 |
-
self.
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 194 |
)
|
| 195 |
|
| 196 |
-
def
|
| 197 |
-
"""
|
| 198 |
-
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
|
| 218 |
-
|
| 219 |
-
prompt, insight, tip = self.build_output_components()
|
| 220 |
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 226 |
|
| 227 |
-
def build_creator_workbench(self):
|
| 228 |
-
"""Construye la interfaz para el Banco de Trabajo del Creador."""
|
| 229 |
-
with gr.Tab("🎨 El Estudio del Creador (Contenido)"):
|
| 230 |
with gr.Row():
|
|
|
|
| 231 |
with gr.Column(scale=1):
|
| 232 |
-
gr.Markdown("### 1.
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 239 |
with gr.Column(scale=2):
|
| 240 |
-
gr.Markdown("### 2.
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
|
| 246 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 247 |
)
|
| 248 |
|
| 249 |
-
|
| 250 |
-
|
| 251 |
-
|
| 252 |
-
|
| 253 |
-
|
| 254 |
-
|
| 255 |
-
lang_input = gr.Dropdown(label="Lenguaje de Programación", choices=["Python", "JavaScript", "SQL", "Java", "C++", "HTML/CSS", "Bash"], value="Python")
|
| 256 |
-
task_input = gr.Textbox(label="Describe la tarea de programación", placeholder="Ej: Crear una función que lea un CSV y devuelva el promedio de una columna.", lines=3)
|
| 257 |
-
constraints_input = gr.Textbox(label="Restricciones o Requisitos (Opcional)", placeholder="Ej: No usar la librería pandas, La función debe manejar archivos grandes, etc.")
|
| 258 |
-
snippet_input = gr.Code(label="Pega aquí tu código existente (Opcional)", language="python", lines=5)
|
| 259 |
-
craft_button = gr.Button("Forjar Prompt de Código", variant="primary")
|
| 260 |
-
with gr.Column(scale=2):
|
| 261 |
-
gr.Markdown("### 2. La Solución Compilada")
|
| 262 |
-
prompt, insight, tip = self.build_output_components()
|
| 263 |
|
| 264 |
-
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
|
| 268 |
-
fn=self.craftsman.craft_code_solution,
|
| 269 |
-
inputs=[lang_input, task_input, constraints_input, snippet_input],
|
| 270 |
-
outputs=[prompt, insight, tip]
|
| 271 |
)
|
|
|
|
|
|
|
|
|
|
| 272 |
|
| 273 |
-
def launch(self):
|
| 274 |
-
"""Construye y lanza la aplicación Gradio completa."""
|
| 275 |
-
with gr.Blocks(theme=self.theme, title=APP_TITLE) as app:
|
| 276 |
-
gr.Markdown(MARKDOWN_HEADER)
|
| 277 |
-
with gr.Tabs():
|
| 278 |
-
self.build_diplomat_workbench()
|
| 279 |
-
self.build_creator_workbench()
|
| 280 |
-
self.build_coder_workbench()
|
| 281 |
-
gr.Markdown(MARKDOWN_FOOTER)
|
| 282 |
-
|
| 283 |
-
# Lanza la aplicación con la cola habilitada para manejar múltiples usuarios.
|
| 284 |
app.queue().launch(show_error=True)
|
| 285 |
|
| 286 |
-
# ---------------------------------------------------------------------------------------
|
| 287 |
-
# IV. PUNTO DE ENTRADA DE LA APLICACIÓN
|
| 288 |
-
# El "main" que enciende la forja.
|
| 289 |
-
# ---------------------------------------------------------------------------------------
|
| 290 |
-
|
| 291 |
if __name__ == "__main__":
|
| 292 |
-
|
| 293 |
-
|
|
|
|
| 1 |
+
# ==================================================================================================
|
| 2 |
+
# SYNAPSE v0.9 (Codename: "Prometheus")
|
| 3 |
+
# The Dynamic Prompting Environment
|
| 4 |
#
|
| 5 |
+
# PHILOSOPHY:
|
| 6 |
+
# We don't generate prompts. We architect instructions. We build workflows.
|
| 7 |
+
# This is not a tool, it is a laboratory for command and control of generative AI.
|
| 8 |
+
# ==================================================================================================
|
| 9 |
|
| 10 |
import gradio as gr
|
| 11 |
+
import uuid
|
| 12 |
+
import json
|
| 13 |
+
|
| 14 |
+
# --------------------------------------------------------------------------------------------------
|
| 15 |
+
# I. CORE BLUEPRINTS & CONFIGURATION
|
| 16 |
+
# The DNA of the Synapse Environment. Defines the capabilities of each Node.
|
| 17 |
+
# --------------------------------------------------------------------------------------------------
|
| 18 |
+
|
| 19 |
+
class NodeBlueprint:
|
| 20 |
+
"""Defines the structure and capabilities of a functional node in the Synapse chain."""
|
| 21 |
+
|
| 22 |
+
NODE_TEMPLATES = {
|
| 23 |
+
"strategic_comm": {
|
| 24 |
+
"name": "Strategic Communicator",
|
| 25 |
+
"description": "For crafting complex, high-stakes communication (emails, memos, announcements).",
|
| 26 |
+
"icon": "📨",
|
| 27 |
+
"inputs": [
|
| 28 |
+
{"label": "Audience Profile", "placeholder": "E.g., 'C-Suite Executives, skeptical but data-driven'"},
|
| 29 |
+
{"label": "Primary Objective", "placeholder": "E.g., 'Secure funding for Project Phoenix'"},
|
| 30 |
+
{"label": "Key Information Payloads (one per line)", "lines": 4, "placeholder": "- Q2 results exceeded projections by 15%.\n- The project has a 6-month ROI timeline.\n- We have a lead engineer ready to start."},
|
| 31 |
+
{"label": "Desired Tone & Voice", "choices": ["Assertive & Confident", "Empathetic & Collaborative", "Urgent & Action-Oriented", "Formal & Deferential"], "value": "Assertive & Confident"},
|
| 32 |
+
],
|
| 33 |
+
"refinement_actions": ["make_more_concise", "increase_persuasion", "add_data_focus", "soften_tone"]
|
| 34 |
+
},
|
| 35 |
+
"creative_ideation": {
|
| 36 |
+
"name": "Creative Ideation Engine",
|
| 37 |
+
"description": "For brainstorming and generating novel concepts from a single spark.",
|
| 38 |
+
"icon": "💡",
|
| 39 |
+
"inputs": [
|
| 40 |
+
{"label": "Core Concept or Theme", "placeholder": "E.g., 'The future of decentralized cities'"},
|
| 41 |
+
{"label": "Output Format", "choices": ["5 Blog Post Titles", "3 YouTube Video Concepts", "10 Tweet-sized Ideas", "A one-paragraph story starter"], "value": "5 Blog Post Titles"},
|
| 42 |
+
{"label": "Creative Constraint", "placeholder": "E.g., 'Must be optimistic in tone', 'Avoid using tech jargon'"},
|
| 43 |
+
],
|
| 44 |
+
"refinement_actions": ["make_more_provocative", "explore_contrarian_angle", "target_a_niche_audience", "simplify_for_beginners"]
|
| 45 |
+
},
|
| 46 |
+
"technical_architect": {
|
| 47 |
+
"name": "Technical Architect",
|
| 48 |
+
"description": "For designing code logic, solving bugs, and explaining complex systems.",
|
| 49 |
+
"icon": "⚙️",
|
| 50 |
+
"inputs": [
|
| 51 |
+
{"label": "Programming Language / Tech Stack", "value": "Python"},
|
| 52 |
+
{"label": "Core Task or Problem", "lines": 3, "placeholder": "E.g., 'Design a Python function to asynchronously fetch data from multiple APIs and aggregate the results.'"},
|
| 53 |
+
{"label": "Key Constraints or Requirements", "placeholder": "E.g., 'Must use the asyncio library, must include error handling for failed requests.'"},
|
| 54 |
+
],
|
| 55 |
+
"refinement_actions": ["add_step_by_step_explanation", "generate_code_comments", "write_testing_plan", "optimize_for_performance"]
|
| 56 |
+
}
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
# --------------------------------------------------------------------------------------------------
|
| 60 |
+
# II. THE SYNAPSE ENGINE
|
| 61 |
+
# The core logic that powers the creation and refinement of prompt chains.
|
| 62 |
+
# --------------------------------------------------------------------------------------------------
|
| 63 |
+
|
| 64 |
+
class SynapseEngine:
|
| 65 |
+
"""The intelligent engine that translates user intent into architected prompt instructions."""
|
| 66 |
+
|
| 67 |
+
def _render_prompt_anatomy(self, prompt_text: str) -> str:
|
| 68 |
+
"""Visualizes the prompt's structure with Markdown and HTML for clarity."""
|
| 69 |
+
# This is a simplified visualization. A more complex version could use regex to tag parts.
|
| 70 |
+
return f"""
|
| 71 |
+
<div style="border: 1px solid #444; padding: 15px; border-radius: 8px; background-color: #1a1a1a;">
|
| 72 |
+
<details>
|
| 73 |
+
<summary><strong>🔬 Ver Anatomía del Prompt</strong></summary>
|
| 74 |
+
<pre><code style="color: #e0e0e0; white-space: pre-wrap;">{prompt_text}</code></pre>
|
| 75 |
+
</details>
|
| 76 |
</div>
|
| 77 |
+
"""
|
| 78 |
|
| 79 |
+
def craft_node_output(self, node_id: str, *args) -> dict:
|
| 80 |
+
"""Crafts a single, structured node for the chain."""
|
| 81 |
+
template = NodeBlueprint.NODE_TEMPLATES.get(node_id)
|
| 82 |
+
if not template: return None
|
| 83 |
|
| 84 |
+
# Basic validation
|
| 85 |
+
if not all(str(arg).strip() for arg in args):
|
| 86 |
+
return None
|
| 87 |
|
| 88 |
+
prompt = f"## PROMPT DIRECTIVE: ACT AS {template['name'].upper()} ##\n\n"
|
| 89 |
+
prompt += f"**MISSION CONTEXT:**\n"
|
| 90 |
+
for i, input_field in enumerate(template['inputs']):
|
| 91 |
+
prompt += f"- {input_field['label']}: {args[i]}\n"
|
| 92 |
+
|
| 93 |
+
prompt += f"\n**PRIMARY EXECUTION DIRECTIVE:**\nBased on the context, generate the specified output, adhering strictly to the defined format and constraints. The output must be of expert-level quality, demonstrating deep domain knowledge and strategic insight."
|
| 94 |
+
|
| 95 |
+
explanation = f"This node establishes a high-level **Expert Persona** ('{template['name']}') and provides a structured **Mission Context**. This forces the AI to move beyond generic responses and adopt a specific, professional mindset, leading to vastly superior results."
|
| 96 |
+
|
| 97 |
+
return {
|
| 98 |
+
"id": str(uuid.uuid4()),
|
| 99 |
+
"node_type": template['name'],
|
| 100 |
+
"icon": template['icon'],
|
| 101 |
+
"prompt": prompt,
|
| 102 |
+
"explanation": explanation,
|
| 103 |
+
"refinements_applied": []
|
| 104 |
+
}
|
| 105 |
+
|
| 106 |
+
def refine_node_prompt(self, prompt: str, action: str) -> str:
|
| 107 |
+
"""Applies a refinement action to an existing prompt string."""
|
| 108 |
+
refinement_map = {
|
| 109 |
+
"make_more_concise": "\n\n**REFINEMENT DIRECTIVE:** Review the entire response and condense it by 25-40% without losing critical information. Prioritize clarity and impact.",
|
| 110 |
+
"increase_persuasion": "\n\n**REFINEMENT DIRECTIVE:** Re-evaluate the prompt's output to maximize its persuasive power. Employ rhetorical devices, strengthen the call-to-action, and appeal directly to the audience's motivations.",
|
| 111 |
+
"add_data_focus": "\n\n**REFINEMENT DIRECTIVE:** Enhance the output by integrating real or hypothetical quantitative data and metrics to support all claims. The tone should be analytical and evidence-based.",
|
| 112 |
+
"soften_tone": "\n\n**REFINEMENT DIRECTIVE:** Adjust the language to be more collaborative, empathetic, and less confrontational. Use inclusive phrasing and acknowledge potential alternative viewpoints.",
|
| 113 |
+
"make_more_provocative": "\n\n**REFINEMENT DIRECTIVE:** Inject a bold, contrarian, or highly provocative element into the output. Challenge a common assumption. The goal is to spark debate and capture attention.",
|
| 114 |
+
"add_step_by_step_explanation": "\n\n**REFINEMENT DIRECTIVE:** Append a detailed, step-by-step explanation to the primary output. Assume the reader is intelligent but a novice in this specific domain. Use analogies.",
|
| 115 |
+
}
|
| 116 |
+
# Add a placeholder for unimplemented actions
|
| 117 |
+
refinement_instruction = refinement_map.get(action, f"\n\n**REFINEMENT DIRECTIVE:** Apply the concept of '{action.replace('_', ' ')}' to the output.")
|
| 118 |
+
|
| 119 |
+
# Avoid duplicating refinement instructions
|
| 120 |
+
if refinement_instruction in prompt:
|
| 121 |
+
return prompt
|
| 122 |
+
|
| 123 |
+
return prompt + refinement_instruction
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 124 |
|
| 125 |
+
# --------------------------------------------------------------------------------------------------
|
| 126 |
+
# III. THE LAB ENVIRONMENT (UI)
|
| 127 |
+
# The Gradio interface. Designed to feel like a professional, dynamic workspace.
|
| 128 |
+
# --------------------------------------------------------------------------------------------------
|
| 129 |
|
| 130 |
+
class SynapseUI:
|
| 131 |
+
"""Builds and manages the Gradio user interface for the Synapse Environment."""
|
|
|
|
|
|
|
| 132 |
|
|
|
|
|
|
|
| 133 |
def __init__(self):
|
| 134 |
+
self.engine = SynapseEngine()
|
| 135 |
+
self.css = """
|
| 136 |
+
:root { --syn-c-primary: #00A9FF; --syn-c-secondary: #89CFF3; --syn-c-background: #0d1117; --syn-c-surface: #161b22; --syn-c-text: #c9d1d9; }
|
| 137 |
+
body, .gradio-container { background-color: var(--syn-c-background); color: var(--syn-c-text); font-family: 'Inter', sans-serif; }
|
| 138 |
+
#chain_display { border: 1px solid #30363d; border-radius: 8px; padding: 1em; min-height: 400px; background-color: var(--syn-c-surface); }
|
| 139 |
+
.node-card { border: 1px solid #30363d; border-radius: 12px; padding: 1em; margin-bottom: 1em; background-color: rgba(0, 169, 255, 0.05); }
|
| 140 |
+
.node-header { display: flex; align-items: center; font-size: 1.5em; font-weight: 600; color: var(--syn-c-secondary); }
|
| 141 |
+
.node-icon { font-size: 1.5em; margin-right: 0.5em; }
|
| 142 |
+
button { border-radius: 8px !important; }
|
| 143 |
+
"""
|
| 144 |
+
self.theme = gr.themes.Base(
|
| 145 |
+
primary_hue="blue",
|
| 146 |
+
secondary_hue="cyan",
|
| 147 |
+
font="'Inter', sans-serif"
|
| 148 |
+
).set(
|
| 149 |
+
body_background_fill="var(--syn-c-background)",
|
| 150 |
+
block_background_fill="var(--syn-c-surface)",
|
| 151 |
+
block_border_width="0px",
|
| 152 |
+
body_text_color="var(--syn-c-text)",
|
| 153 |
+
button_primary_background_fill="var(--syn-c-primary)",
|
| 154 |
+
button_primary_text_color="#FFFFFF",
|
| 155 |
+
button_secondary_background_fill="#30363d",
|
| 156 |
+
button_secondary_text_color="#FFFFFF",
|
| 157 |
)
|
| 158 |
|
| 159 |
+
def _render_chain_display(self, chain_data: list) -> str:
|
| 160 |
+
"""Renders the current prompt chain as a rich Markdown string."""
|
| 161 |
+
if not chain_data:
|
| 162 |
+
return "<div style='text-align: center; padding: 5em 0;'><p style='font-size: 1.2em; color: #888;'>Your Prompt Chain is empty.</p><p style='color: #666;'>Select a Node Type from the Workshop to begin.</p></div>"
|
| 163 |
+
|
| 164 |
+
html = ""
|
| 165 |
+
for i, node in enumerate(chain_data):
|
| 166 |
+
refinements_str = ", ".join(r.replace("_", " ").title() for r in node['refinements_applied'])
|
| 167 |
+
refinements_html = f"<p style='color: #89CFF3; font-size: 0.9em;'><strong>Refinements:</strong> {refinements_str}</p>" if refinements_str else ""
|
| 168 |
+
|
| 169 |
+
html += f"""
|
| 170 |
+
<div class='node-card'>
|
| 171 |
+
<div class='node-header'><span class='node-icon'>{node['icon']}</span><span>Node {i+1}: {node['node_type']}</span></div>
|
| 172 |
+
<hr style='border-color: #30363d;'>
|
| 173 |
+
<p><strong>// Rationale //</strong><br>{node['explanation']}</p>
|
| 174 |
+
{refinements_html}
|
| 175 |
+
{self.engine._render_prompt_anatomy(node['prompt'])}
|
| 176 |
+
</div>
|
| 177 |
+
"""
|
| 178 |
+
if i < len(chain_data) - 1:
|
| 179 |
+
html += "<div style='text-align: center; font-size: 2em; color: #444; margin: -0.5em 0;'>↓</div>"
|
| 180 |
+
|
| 181 |
+
return html
|
|
|
|
| 182 |
|
| 183 |
+
def launch(self):
|
| 184 |
+
"""Builds and launches the full Gradio application."""
|
| 185 |
+
with gr.Blocks(theme=self.theme, css=self.css, title="SYNAPSE") as app:
|
| 186 |
+
# STATE MANAGEMENT
|
| 187 |
+
chain_state = gr.State([]) # Holds the list of node dicts
|
| 188 |
+
active_node_id = gr.State(None)
|
| 189 |
+
|
| 190 |
+
# HEADER
|
| 191 |
+
gr.HTML("""<div style='text-align: center; padding: 2rem 0;'><h1 style='color: #00A9FF; font-size: 3rem; font-weight: 800; margin: 0;'>S Y N A P S E</h1><p style='color: #89CFF3; font-size: 1.2rem;'>The Dynamic Prompting Environment</p></div>""")
|
| 192 |
|
|
|
|
|
|
|
|
|
|
| 193 |
with gr.Row():
|
| 194 |
+
# LEFT COLUMN: THE WORKSHOP (INPUTS)
|
| 195 |
with gr.Column(scale=1):
|
| 196 |
+
gr.Markdown("### <strong>1. The Workshop</strong>")
|
| 197 |
+
node_selector = gr.Dropdown(
|
| 198 |
+
label="Select Node Type to Add",
|
| 199 |
+
choices=[(d['name'], k) for k, d in NodeBlueprint.NODE_TEMPLATES.items()],
|
| 200 |
+
value=None
|
| 201 |
+
)
|
| 202 |
+
|
| 203 |
+
# Create input groups for all node types, initially hidden
|
| 204 |
+
input_groups = {}
|
| 205 |
+
for node_id, template in NodeBlueprint.NODE_TEMPLATES.items():
|
| 206 |
+
with gr.Group(visible=False) as input_groups[node_id]:
|
| 207 |
+
gr.Markdown(f"#### {template['icon']} {template['name']}")
|
| 208 |
+
gr.Markdown(f"<p style='font-size:0.9em; color:#888;'>{template['description']}</p>")
|
| 209 |
+
inputs = []
|
| 210 |
+
for field in template['inputs']:
|
| 211 |
+
if 'choices' in field:
|
| 212 |
+
inputs.append(gr.Dropdown(label=field['label'], choices=field['choices'], value=field.get('value')))
|
| 213 |
+
else:
|
| 214 |
+
inputs.append(gr.Textbox(label=field['label'], placeholder=field.get('placeholder', ''), lines=field.get('lines', 1)))
|
| 215 |
+
input_groups[node_id].__setattr__("inputs", inputs)
|
| 216 |
+
|
| 217 |
+
add_node_button = gr.Button("Add Node to Chain", variant="primary", visible=False)
|
| 218 |
+
|
| 219 |
+
# REFINEMENT WORKBENCH (dynamically populated)
|
| 220 |
+
with gr.Group(visible=False) as refinement_group:
|
| 221 |
+
gr.Markdown("### <strong>3. Refinement Workbench</strong>")
|
| 222 |
+
gr.Markdown("<p style='font-size:0.9em; color:#888;'>Mutate the **last node** in the chain.</p>")
|
| 223 |
+
refinement_buttons = []
|
| 224 |
+
all_actions = set(a for t in NodeBlueprint.NODE_TEMPLATES.values() for a in t['refinement_actions'])
|
| 225 |
+
for action in all_actions:
|
| 226 |
+
btn = gr.Button(action.replace("_", " ").title(), visible=False)
|
| 227 |
+
refinement_buttons.append((action, btn))
|
| 228 |
+
|
| 229 |
+
clear_chain_button = gr.Button("Clear Entire Chain", variant="secondary")
|
| 230 |
+
|
| 231 |
+
# RIGHT COLUMN: THE CHAIN DISPLAY
|
| 232 |
with gr.Column(scale=2):
|
| 233 |
+
gr.Markdown("### <strong>2. The Prompt Chain</strong>")
|
| 234 |
+
chain_display = gr.HTML(self._render_chain_display([]), elem_id="chain_display")
|
| 235 |
+
|
| 236 |
+
# EVENT HANDLING LOGIC
|
| 237 |
+
def on_node_select(node_id):
|
| 238 |
+
active_node_id.value = node_id
|
| 239 |
+
updates = {group: gr.update(visible=k==node_id) for k, group in input_groups.items()}
|
| 240 |
+
updates[add_node_button] = gr.update(visible=bool(node_id))
|
| 241 |
+
return updates
|
| 242 |
+
|
| 243 |
+
def add_node_to_chain(chain_data, node_id, *inputs):
|
| 244 |
+
node_output = self.engine.craft_node_output(node_id, *inputs)
|
| 245 |
+
if node_output:
|
| 246 |
+
chain_data.append(node_output)
|
| 247 |
+
|
| 248 |
+
# Update refinement buttons visibility
|
| 249 |
+
refinement_updates = {}
|
| 250 |
+
last_node_actions = set(NodeBlueprint.NODE_TEMPLATES[node_id]['refinement_actions'])
|
| 251 |
+
for action, btn in refinement_buttons:
|
| 252 |
+
refinement_updates[btn] = gr.update(visible=action in last_node_actions)
|
| 253 |
+
|
| 254 |
+
return {
|
| 255 |
+
chain_state: chain_data,
|
| 256 |
+
chain_display: self._render_chain_display(chain_data),
|
| 257 |
+
refinement_group: gr.update(visible=True),
|
| 258 |
+
**refinement_updates
|
| 259 |
+
}
|
| 260 |
+
|
| 261 |
+
def apply_refinement(chain_data, action):
|
| 262 |
+
if not chain_data: return chain_data, self._render_chain_display(chain_data)
|
| 263 |
+
|
| 264 |
+
last_node = chain_data[-1]
|
| 265 |
+
# Avoid applying the same refinement twice
|
| 266 |
+
if action not in last_node["refinements_applied"]:
|
| 267 |
+
original_prompt = last_node["prompt"]
|
| 268 |
+
refined_prompt = self.engine.refine_node_prompt(original_prompt, action)
|
| 269 |
+
last_node["prompt"] = refined_prompt
|
| 270 |
+
last_node["refinements_applied"].append(action)
|
| 271 |
+
|
| 272 |
+
return chain_data, self._render_chain_display(chain_data)
|
| 273 |
+
|
| 274 |
+
# Connect events to functions
|
| 275 |
+
node_selector.change(on_node_select, inputs=node_selector, outputs=[active_node_id, add_node_button] + list(input_groups.values()))
|
| 276 |
+
|
| 277 |
+
add_node_button.click(
|
| 278 |
+
add_node_to_chain,
|
| 279 |
+
inputs=[chain_state, active_node_id] + list(inp for grp in input_groups.values() for inp in grp.inputs),
|
| 280 |
+
outputs=[chain_state, chain_display, refinement_group] + [b for _, b in refinement_buttons]
|
| 281 |
)
|
| 282 |
|
| 283 |
+
for action, btn in refinement_buttons:
|
| 284 |
+
btn.click(
|
| 285 |
+
apply_refinement,
|
| 286 |
+
inputs=[chain_state, gr.State(action)],
|
| 287 |
+
outputs=[chain_state, chain_display]
|
| 288 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 289 |
|
| 290 |
+
clear_chain_button.click(
|
| 291 |
+
lambda: ([], self._render_chain_display([]), gr.update(visible=False)),
|
| 292 |
+
None,
|
| 293 |
+
[chain_state, chain_display, refinement_group]
|
|
|
|
|
|
|
|
|
|
| 294 |
)
|
| 295 |
+
|
| 296 |
+
# FOOTER
|
| 297 |
+
gr.Markdown("<hr style='border-color:#30363d;'><div style='text-align:center;color:#888;'>SYNAPSE v0.9 // Forging the future of human-AI interaction. <a href='https://www.linkedin.com/' target='_blank' style='color:#00A9FF;'>Let's connect.</a></div>")
|
| 298 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 299 |
app.queue().launch(show_error=True)
|
| 300 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 301 |
if __name__ == "__main__":
|
| 302 |
+
ui = SynapseUI()
|
| 303 |
+
ui.launch()
|