prabalGaur commited on
Commit
8c26fa6
·
verified ·
1 Parent(s): 98e555f

Upload community_contributions/chrys/app.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. community_contributions/chrys/app.py +159 -0
community_contributions/chrys/app.py ADDED
@@ -0,0 +1,159 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Gradio UI for ARIA: agent-colored log stream and results table."""
2
+ import html
3
+ import logging
4
+ import queue
5
+ import sys
6
+ import os
7
+ import time
8
+
9
+ # Ensure chrys is on path
10
+ _this_dir = os.path.dirname(os.path.abspath(__file__))
11
+ if _this_dir not in sys.path:
12
+ sys.path.insert(0, _this_dir)
13
+
14
+ import gradio as gr
15
+ from dotenv import load_dotenv
16
+ load_dotenv()
17
+
18
+ from orchestrator import run_pipeline
19
+ from models import DecisionRecord
20
+
21
+ # Agent name + color for UI (logger name -> (display name, hex color))
22
+ AGENT_COLORS = {
23
+ "aria.data_fetcher": ("DATA_FETCHER", "#3498db"),
24
+ "aria.tech_analyst": ("TECH_ANALYST", "#27ae60"),
25
+ "aria.sentiment_agent": ("SENTIMENT_AGENT", "#f39c12"),
26
+ "aria.decision_agent": ("DECISION_AGENT", "#9b59b6"),
27
+ "aria.notifier": ("NOTIFIER", "#1abc9c"),
28
+ }
29
+ DEFAULT_COLOR = "#bdc3c7"
30
+ MAX_LOG_LINES = 150
31
+
32
+
33
+ class AgentQueueHandler(logging.Handler):
34
+ """Put (logger_name, formatted_message) into queue for colored UI."""
35
+ def __init__(self, log_queue: queue.Queue):
36
+ super().__init__()
37
+ self.log_queue = log_queue
38
+
39
+ def emit(self, record: logging.LogRecord):
40
+ try:
41
+ msg = self.format(record)
42
+ self.log_queue.put((record.name, msg))
43
+ except Exception:
44
+ self.handleError(record)
45
+
46
+
47
+ def agent_line_to_html(logger_name: str, message: str) -> str:
48
+ label, color = AGENT_COLORS.get(logger_name, ("ARIA", DEFAULT_COLOR))
49
+ safe_msg = html.escape(message)
50
+ return f'<span style="color:{color};font-weight:600">[{label}]</span> {safe_msg}'
51
+
52
+
53
+ def html_for_log(log_lines: list) -> str:
54
+ recent = log_lines[-MAX_LOG_LINES:]
55
+ content = "<br>".join(recent)
56
+ return f"""
57
+ <div style="height:420px;overflow-y:auto;border:1px solid #444;background:#1e1e2e;padding:12px;font-family:monospace;font-size:13px;color:#cdd6f4;">
58
+ {content}
59
+ </div>
60
+ """
61
+
62
+
63
+ def setup_aria_logging(log_queue: queue.Queue) -> None:
64
+ root = logging.getLogger()
65
+ handler = AgentQueueHandler(log_queue)
66
+ handler.setFormatter(logging.Formatter("[%(asctime)s] %(message)s", datefmt="%H:%M:%S"))
67
+ root.addHandler(handler)
68
+ root.setLevel(logging.INFO)
69
+ for name in AGENT_COLORS:
70
+ logging.getLogger(name).setLevel(logging.INFO)
71
+
72
+
73
+ def table_for(records: list[DecisionRecord]) -> list[list]:
74
+ if not records:
75
+ return []
76
+ return [
77
+ [r.asset, str(r.tech_score), r.sentiment, f"{r.final_score:.1f}", r.decision, r.skip_reason or ""]
78
+ for r in records
79
+ ]
80
+
81
+
82
+ def run_pipeline_with_logging(log_queue: queue.Queue, result_queue: queue.Queue) -> None:
83
+ try:
84
+ records, _ = run_pipeline()
85
+ result_queue.put(records)
86
+ except Exception as e:
87
+ logging.exception("Pipeline failed")
88
+ result_queue.put([])
89
+
90
+
91
+ def run_clicked(log_state):
92
+ log_queue = queue.Queue()
93
+ result_queue = queue.Queue()
94
+ root = logging.getLogger()
95
+ for h in root.handlers[:]:
96
+ if isinstance(h, AgentQueueHandler):
97
+ root.removeHandler(h)
98
+ setup_aria_logging(log_queue)
99
+ log_lines = []
100
+ thread = __import__("threading").Thread(
101
+ target=run_pipeline_with_logging,
102
+ args=(log_queue, result_queue),
103
+ daemon=True,
104
+ )
105
+ thread.start()
106
+ table = []
107
+ while True:
108
+ while True:
109
+ try:
110
+ logger_name, msg = log_queue.get_nowait()
111
+ log_lines.append(agent_line_to_html(logger_name, msg))
112
+ except queue.Empty:
113
+ break
114
+ try:
115
+ records = result_queue.get_nowait()
116
+ table = table_for(records)
117
+ break
118
+ except queue.Empty:
119
+ pass
120
+ yield log_lines, html_for_log(log_lines), table
121
+ time.sleep(0.08)
122
+ if not thread.is_alive():
123
+ try:
124
+ records = result_queue.get_nowait()
125
+ table = table_for(records)
126
+ except queue.Empty:
127
+ table = []
128
+ break
129
+ yield log_lines, html_for_log(log_lines), table
130
+
131
+
132
+ def build_ui():
133
+ with gr.Blocks(title="ARIA — Market Intelligence", theme=gr.themes.Soft(), css="""
134
+ .log-panel { font-family: ui-monospace, monospace; }
135
+ """) as ui:
136
+ gr.Markdown("# ARIA — Automated Real-time Investment Alert Agent")
137
+ gr.Markdown("Multi-agent pipeline: Data → Tech Analysis → Sentiment → Decision → Pushover alerts.")
138
+ log_state = gr.State([])
139
+ with gr.Row():
140
+ run_btn = gr.Button("Run pipeline", variant="primary")
141
+ with gr.Row():
142
+ log_html = gr.HTML(value=html_for_log([]), label="Agent log")
143
+ with gr.Row():
144
+ results_df = gr.Dataframe(
145
+ headers=["Asset", "Tech score", "Sentiment", "Final score", "Decision", "Skip reason"],
146
+ datatype=["str", "str", "str", "str", "str", "str"],
147
+ label="Last run results",
148
+ )
149
+ run_btn.click(
150
+ fn=run_clicked,
151
+ inputs=[log_state],
152
+ outputs=[log_state, log_html, results_df],
153
+ )
154
+ return ui
155
+
156
+
157
+ if __name__ == "__main__":
158
+ ui = build_ui()
159
+ ui.launch(share=False, inbrowser=True)