gaurv007 commited on
Commit
ed73de7
Β·
verified Β·
1 Parent(s): 8bc677b

feat: add Gradio UI for viewing generated alphas with timestamps, copy button, and live generation

Browse files
Files changed (1) hide show
  1. alpha_factory/ui.py +180 -0
alpha_factory/ui.py ADDED
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Alpha Factory β€” Gradio UI
3
+ View generated alphas, copy expressions, run new batches.
4
+
5
+ Run: uv run python -m alpha_factory.ui
6
+ """
7
+ import duckdb
8
+ import gradio as gr
9
+ from pathlib import Path
10
+ from datetime import datetime
11
+
12
+
13
+ DB_PATH = Path("factor_store/alphas.duckdb")
14
+
15
+
16
+ def get_alphas_from_db(limit=50):
17
+ """Fetch alphas from DuckDB with all relevant info."""
18
+ if not DB_PATH.exists():
19
+ return []
20
+ conn = duckdb.connect(str(DB_PATH), read_only=True)
21
+ try:
22
+ rows = conn.execute(f"""
23
+ SELECT
24
+ alpha_id,
25
+ submitted_at,
26
+ expression,
27
+ theme,
28
+ archetype,
29
+ anomaly_tag,
30
+ neutralization,
31
+ decay,
32
+ fields_used,
33
+ verdict
34
+ FROM alphas
35
+ ORDER BY submitted_at DESC
36
+ LIMIT {limit}
37
+ """).fetchall()
38
+ return rows
39
+ except Exception as e:
40
+ return []
41
+ finally:
42
+ conn.close()
43
+
44
+
45
+ def format_alphas_table():
46
+ """Format alphas into a display-friendly list."""
47
+ rows = get_alphas_from_db()
48
+ if not rows:
49
+ return "No alphas generated yet. Run the pipeline first:\n\nuv run python -m alpha_factory.run --dry-run --batch-size 5"
50
+
51
+ output = []
52
+ for row in rows:
53
+ alpha_id, submitted_at, expression, theme, archetype, tag, neutral, decay, fields, verdict = row
54
+ timestamp = submitted_at.strftime("%Y-%m-%d %H:%M:%S") if submitted_at else "unknown"
55
+ fields_str = ", ".join(fields) if fields else "none"
56
+ verdict_emoji = {"promote": "βœ…", "iterate": "πŸ”„", "kill": "❌"}.get(verdict or "", "⏳")
57
+
58
+ output.append(f"""{'='*80}
59
+ {verdict_emoji} Alpha: {alpha_id}
60
+ πŸ“… Generated: {timestamp}
61
+ 🏷️ Theme: {theme} | Archetype: {archetype} | Tag: {tag}
62
+ βš™οΈ Neutralization: {neutral} | Decay: {decay}
63
+ πŸ“Š Fields: {fields_str}
64
+
65
+ πŸ“ EXPRESSION (copy below):
66
+ {expression}
67
+ {'='*80}
68
+ """)
69
+
70
+ return "\n".join(output)
71
+
72
+
73
+ def get_single_alpha(index: int):
74
+ """Get a single alpha expression for easy copying."""
75
+ rows = get_alphas_from_db()
76
+ if not rows or index >= len(rows):
77
+ return "No alpha at this index"
78
+ return rows[index][2] # expression column
79
+
80
+
81
+ def get_alpha_cards():
82
+ """Return alpha data as list of dicts for dataframe display."""
83
+ rows = get_alphas_from_db()
84
+ if not rows:
85
+ return []
86
+
87
+ data = []
88
+ for row in rows:
89
+ alpha_id, submitted_at, expression, theme, archetype, tag, neutral, decay, fields, verdict = row
90
+ timestamp = submitted_at.strftime("%Y-%m-%d %H:%M") if submitted_at else "?"
91
+ verdict_emoji = {"promote": "PASS", "iterate": "PENDING", "kill": "FAIL"}.get(verdict or "", "NEW")
92
+ data.append({
93
+ "Time": timestamp,
94
+ "ID": alpha_id[:8] + "...",
95
+ "Theme": theme or "",
96
+ "Archetype": archetype or "",
97
+ "Tag": tag or "",
98
+ "Decay": decay or 0,
99
+ "Status": verdict_emoji,
100
+ "Expression": expression[:100] + "..." if len(expression or "") > 100 else (expression or ""),
101
+ })
102
+ return data
103
+
104
+
105
+ def get_full_expression(evt: gr.SelectData, data):
106
+ """When user clicks a row, show the full expression."""
107
+ if evt.index is None:
108
+ return ""
109
+ rows = get_alphas_from_db()
110
+ if not rows:
111
+ return ""
112
+ row_idx = evt.index[0] if isinstance(evt.index, (list, tuple)) else evt.index
113
+ if row_idx < len(rows):
114
+ return rows[row_idx][2] # full expression
115
+ return ""
116
+
117
+
118
+ def run_batch_and_refresh(batch_size):
119
+ """Run a dry-run batch and refresh the table."""
120
+ import subprocess
121
+ import sys
122
+ result = subprocess.run(
123
+ [sys.executable, "-m", "alpha_factory.run", "--dry-run", "--batch-size", str(int(batch_size))],
124
+ capture_output=True, text=True, encoding="utf-8", errors="replace"
125
+ )
126
+ return get_alpha_cards(), result.stdout[-2000:] if result.stdout else result.stderr[-2000:]
127
+
128
+
129
+ def build_ui():
130
+ """Build the Gradio interface."""
131
+ with gr.Blocks(title="Alpha Factory", theme=gr.themes.Soft()) as app:
132
+ gr.Markdown("""
133
+ # 🏭 Alpha Factory β€” Generated Alphas
134
+ View, copy, and manage alphas generated by the pipeline.
135
+ """)
136
+
137
+ with gr.Row():
138
+ with gr.Column(scale=1):
139
+ batch_size_input = gr.Number(value=5, label="Batch Size", minimum=1, maximum=20)
140
+ generate_btn = gr.Button("πŸš€ Generate New Batch (Dry Run)", variant="primary")
141
+ refresh_btn = gr.Button("πŸ”„ Refresh Table")
142
+ with gr.Column(scale=3):
143
+ gr.Markdown("### Stats")
144
+ stats_display = gr.Markdown("")
145
+
146
+ gr.Markdown("### πŸ“‹ Generated Alphas (click row to see full expression)")
147
+
148
+ alpha_table = gr.Dataframe(
149
+ value=get_alpha_cards(),
150
+ headers=["Time", "ID", "Theme", "Archetype", "Tag", "Decay", "Status", "Expression"],
151
+ interactive=False,
152
+ wrap=True,
153
+ )
154
+
155
+ gr.Markdown("### πŸ“ Full Expression (click a row above)")
156
+ full_expr = gr.Textbox(
157
+ label="Full Expression (select & copy)",
158
+ lines=5,
159
+ show_copy_button=True,
160
+ interactive=False,
161
+ )
162
+
163
+ gr.Markdown("### πŸ“œ Pipeline Output")
164
+ pipeline_log = gr.Textbox(label="Last Run Log", lines=10, interactive=False)
165
+
166
+ # Event handlers
167
+ alpha_table.select(get_full_expression, inputs=[alpha_table], outputs=[full_expr])
168
+ refresh_btn.click(lambda: get_alpha_cards(), outputs=[alpha_table])
169
+ generate_btn.click(
170
+ run_batch_and_refresh,
171
+ inputs=[batch_size_input],
172
+ outputs=[alpha_table, pipeline_log],
173
+ )
174
+
175
+ return app
176
+
177
+
178
+ if __name__ == "__main__":
179
+ app = build_ui()
180
+ app.launch(share=False)