Merlintxu commited on
Commit
35ac7e4
·
verified ·
1 Parent(s): 98dff6d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +146 -107
app.py CHANGED
@@ -1,107 +1,146 @@
1
- import os
2
- import tempfile
3
- from pathlib import Path
4
- import gradio as gr
5
-
6
- from conversation_storyline.pipeline import run_pipeline_from_text
7
-
8
-
9
- THEME = gr.themes.Soft(primary_hue="blue").set(
10
- body_background_fill="*neutral_50",
11
- block_background_fill="*neutral_100",
12
- )
13
-
14
- TITLE = "Conversation Storyline Visualizer – v4"
15
-
16
-
17
- def process_transcript(transcript: str, model_selector: str):
18
- """
19
- Entrada: texto pegado (transcripción)
20
- Salida: storyline.png + resumen + figs plotly
21
- """
22
- if not transcript or not transcript.strip():
23
- raise gr.Error("Pega una transcripción en el cuadro de texto.")
24
-
25
- outdir = Path(tempfile.mkdtemp(prefix="storyline_v4_"))
26
-
27
- outputs = run_pipeline_from_text(
28
- transcript_text=transcript,
29
- out_dir=outdir,
30
- openai_model=model_selector,
31
- )
32
-
33
- return (
34
- str(outputs["storyline_png"]),
35
- outputs["summary_text"],
36
- outputs["fig_sentiment"],
37
- outputs["fig_bump"],
38
- outputs["fig_heatmap"],
39
- outputs["fig_hist_reply_dist"],
40
- outputs["fig_sankey"],
41
- outputs["storyline_html"],
42
- outputs["metrics_csv"],
43
- outputs["interactions_jsonl"],
44
- outputs["graph_json"],
45
- )
46
-
47
-
48
- with gr.Blocks(title=TITLE, theme=THEME) as demo:
49
- gr.Markdown("# Visualización Narrativa Avanzada de Conversaciones (v4)")
50
- gr.Markdown(
51
- "- Pega una transcripción tipo `Speaker A: ...`\n"
52
- "- Soporta conversaciones largas (chunking)\n"
53
- "- Reply-to Top-K embeddings + topic shifts ruptures + layout OR-Tools\n"
54
- )
55
-
56
- with gr.Row():
57
- model_selector = gr.Dropdown(
58
- choices=[
59
- "gpt-4o-2024-08-06",
60
- "gpt-4o-mini-2024-07-18",
61
- "none (offline)",
62
- ],
63
- value="none (offline)",
64
- label="Modelo (opcional; si hay OPENAI_API_KEY)",
65
- )
66
-
67
- input_text = gr.Textbox(label="Transcripción", lines=20, placeholder="Pega aquí la transcripción...")
68
- btn = gr.Button("Generar Visualizaciones", variant="primary")
69
-
70
- with gr.Tabs():
71
- with gr.Tab("Storyline Principal"):
72
- main_img = gr.Image(label="Storyline (PNG)")
73
- summary_box = gr.Textbox(label="Resumen", lines=10)
74
- storyline_html = gr.HTML(label="Storyline (HTML embebido)")
75
- with gr.Tab("Análisis Detallado"):
76
- sentiment_plot = gr.Plot(label="Sentiment (si aplica)")
77
- bump_plot = gr.Plot(label="Bump actividad por segmento")
78
- heatmap_plot = gr.Plot(label="Heatmap interacciones")
79
- hist_plot = gr.Plot(label="Histograma distancia reply_to")
80
- sankey_plot = gr.Plot(label="Sankey Speaker → Topic")
81
-
82
- with gr.Tab("Descargas"):
83
- metrics_csv = gr.File(label="metrics.csv")
84
- interactions_jsonl = gr.File(label="interactions.jsonl")
85
- graph_json = gr.File(label="graph.json")
86
-
87
- btn.click(
88
- fn=process_transcript,
89
- inputs=[input_text, model_selector],
90
- outputs=[
91
- main_img,
92
- summary_box,
93
- sentiment_plot,
94
- bump_plot,
95
- heatmap_plot,
96
- hist_plot,
97
- sankey_plot,
98
- storyline_html,
99
- metrics_csv,
100
- interactions_jsonl,
101
- graph_json,
102
- ],
103
- )
104
-
105
- if __name__ == "__main__":
106
- # HF Spaces
107
- demo.launch(server_name="0.0.0.0", server_port=int(os.getenv("PORT", "7860")))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ import tempfile
5
+ from pathlib import Path
6
+ from typing import Optional
7
+
8
+ import gradio as gr
9
+
10
+ from conversation_storyline.io import load_messages, load_messages_from_text
11
+ from conversation_storyline.pipeline import run_pipeline
12
+ from conversation_storyline.plots import (
13
+ load_graph_json,
14
+ load_interactions_df,
15
+ plot_reply_sankey,
16
+ plot_sentiment_histogram,
17
+ plot_sentiment_over_time,
18
+ plot_speaker_activity_heatmap,
19
+ plot_speaker_topic_heatmap,
20
+ plot_topic_shift_timeline,
21
+ )
22
+
23
+
24
+ def get_backend(name: str):
25
+ if name == "openai":
26
+ from conversation_storyline.llm_backends.openai_backend import OpenAIBackend
27
+ return OpenAIBackend()
28
+ elif name == "outlines":
29
+ from conversation_storyline.llm_backends.outlines_backend import OutlinesBackend
30
+ return OutlinesBackend()
31
+ else:
32
+ raise ValueError("backend inválido")
33
+
34
+
35
+ async def _run(file_path: Optional[str], transcript_text: str, backend: str):
36
+ transcript_text = (transcript_text or "").strip()
37
+ if transcript_text:
38
+ msgs = load_messages_from_text(transcript_text)
39
+ else:
40
+ if not file_path:
41
+ raise ValueError("Debes pegar un transcript o subir un archivo.")
42
+ msgs = load_messages(file_path)
43
+
44
+ b = get_backend(backend)
45
+
46
+ outdir = Path(tempfile.mkdtemp(prefix="storyline_"))
47
+ await run_pipeline(msgs, b, str(outdir))
48
+
49
+ png = outdir / "storyline.png"
50
+ html = outdir / "storyline.html"
51
+ graph = outdir / "graph.json"
52
+ interactions = outdir / "interactions.jsonl"
53
+ metrics = outdir / "metrics.parquet"
54
+
55
+ html_inline = html.read_text(encoding="utf-8", errors="ignore") if html.exists() else None
56
+
57
+ figs = [None] * 5
58
+ try:
59
+ df = load_interactions_df(outdir)
60
+ g = load_graph_json(outdir)
61
+ figs = [
62
+ plot_sentiment_over_time(df),
63
+ plot_sentiment_histogram(df),
64
+ plot_speaker_topic_heatmap(df),
65
+ plot_speaker_activity_heatmap(df),
66
+ plot_reply_sankey(g),
67
+ ]
68
+ topic_shift_fig = plot_topic_shift_timeline(df)
69
+ except Exception:
70
+ topic_shift_fig = None
71
+
72
+ return (
73
+ str(png) if png.exists() else None,
74
+ html_inline,
75
+ str(html) if html.exists() else None,
76
+ str(graph) if graph.exists() else None,
77
+ str(interactions) if interactions.exists() else None,
78
+ str(metrics) if metrics.exists() else None,
79
+ figs[0],
80
+ figs[1],
81
+ figs[2],
82
+ figs[3],
83
+ figs[4],
84
+ topic_shift_fig,
85
+ )
86
+
87
+
88
+ def run_ui(file_obj, transcript_text: str, backend: str):
89
+ file_path = file_obj.name if file_obj is not None else None
90
+ return asyncio.run(_run(file_path, transcript_text, backend))
91
+
92
+
93
+ with gr.Blocks(title="Conversation Storyline – v4") as demo:
94
+ gr.Markdown("# Conversation Storyline – v4\nPega un transcript o sube TXT/CSV.")
95
+
96
+ with gr.Row():
97
+ f = gr.File(label="Upload (.txt o .csv)")
98
+ backend = gr.Dropdown(choices=["openai", "outlines"], value="openai", label="Backend LLM")
99
+
100
+ transcript_text = gr.Textbox(label="O pega aquí el transcript", lines=10)
101
+
102
+ btn = gr.Button("Run", variant="primary")
103
+
104
+ with gr.Tabs():
105
+ with gr.Tab("Storyline"):
106
+ with gr.Row():
107
+ out_png = gr.Image(label="Storyline (PNG)", type="filepath")
108
+ out_story_html = gr.HTML(label="Storyline (HTML embebido)")
109
+ out_html_file = gr.File(label="Storyline HTML (descarga)")
110
+
111
+ with gr.Tab("Analítica"):
112
+ out_sentiment = gr.Plot(label="Sentiment timeline")
113
+ out_hist = gr.Plot(label="Sentiment histogram")
114
+ out_topic_heat = gr.Plot(label="Speaker × topic heatmap")
115
+ out_activity_heat = gr.Plot(label="Speaker activity heatmap")
116
+ out_topic_shifts = gr.Plot(label="Topic shifts timeline")
117
+
118
+ with gr.Tab("Grafo"):
119
+ out_sankey = gr.Plot(label="Sankey replies")
120
+ out_graph = gr.File(label="Graph JSON")
121
+
122
+ with gr.Tab("Artifacts"):
123
+ out_interactions = gr.File(label="interactions.jsonl")
124
+ out_metrics = gr.File(label="metrics.parquet")
125
+
126
+ btn.click(
127
+ fn=run_ui,
128
+ inputs=[f, transcript_text, backend],
129
+ outputs=[
130
+ out_png,
131
+ out_story_html,
132
+ out_html_file,
133
+ out_graph,
134
+ out_interactions,
135
+ out_metrics,
136
+ out_sentiment,
137
+ out_hist,
138
+ out_topic_heat,
139
+ out_activity_heat,
140
+ out_sankey,
141
+ out_topic_shifts,
142
+ ],
143
+ )
144
+
145
+ if __name__ == "__main__":
146
+ demo.launch()