File size: 6,519 Bytes
9262dd1
121e4bb
9262dd1
 
121e4bb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
dd81284
b0ddedd
121e4bb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5d81699
1
2
3
4
5
6
7
8
9
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
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# Monkey-patch typing. NotRequired for Python 3.10

from patch_langmem import patch_langmem_for_python310
patch_langmem_for_python310()

###################################################################################
import gradio as gr
from gradio import ChatMessage
from agents import build_graph
import os
import random
from dotenv import dotenv_values

js_func = """
function refresh() {
    const url = new URL(window.location);

    if (url.searchParams.get('__theme') !== 'dark') {
        url.searchParams.set('__theme', 'dark');
        window.location.href = url.href;
    }
}
"""


def run_agent_stream(history, question, groq_key, reddit_id, reddit_secret, reddit_agent, news_key):
    os.environ["GROQ_API_KEY"] = groq_key or ""
    os.environ["REDDIT_CLIENT_ID"] = reddit_id or ""
    os.environ["REDDIT_CLIENT_SECRET"] = reddit_secret or ""
    os.environ["REDDIT_USER_AGENT"] = reddit_agent or ""
    os.environ["NEWS_API"] = news_key or ""

    try:
        graph = build_graph()
        state = {"messages": [{"role": "user", "content": question}]}
        history = history or []
        history.append(ChatMessage(role="user", content=question))
        history.append(ChatMessage(role="assistant", content="**Supervisor:**\n I am working with my agents to get a result. Sit tight, I’ll get back with a detailed report."))
        yield history, "<div class='loader'></div>", gr.update(interactive=False)

        for chunk in graph.stream(state):
            agent_key = next(iter(chunk))
            messages = chunk[agent_key]["messages"]
            for msg in messages:
                if msg.__class__.__name__ == "HumanMessage":
                    continue
                name = getattr(msg, "name", agent_key)
                if str(name).split('_')[0] == 'transfer':
                    continue
                content = msg.content.strip()
                
                if not content or "Transferring back" in content or "Successfully transferred" in content:
                    continue

                is_final = msg.response_metadata.get("finish_reason") == "stop"
                is_supervisor = msg.name == "supervisor"
                if is_final and is_supervisor:
                    history.append(ChatMessage(role="user",content=" "))
                    history.append(ChatMessage(role="assistant", content=content, metadata= {"title": "βœ… Final Report"}))
                    yield history, "", gr.update(interactive=True)
                else:
                    emojis = {
                        "Stock Analyst": "🧠",
                        "Web Surfer": "🌐",
                        "Social Media Analyst": "🎦",
                        "Business Journalist": "πŸ“Š"
                    }
                    emoji = emojis.get(name, random.choice(["🏹", "🧭", "🚩"]))
                    title = f"{emoji} {name}" if name else ""
                    his = ChatMessage(role="assistant", content=content, metadata={"title": title})
                    if his not in history:
                        history.append(his)
                    yield history, "<div class='loader'></div>", gr.update(interactive=False)
    except Exception as e:
        print("Error: ", e)


def load_env_file(file):
    try:
        values = dotenv_values(file.name)
        return (
            values.get("GROQ_API_KEY", ""),
            values.get("REDDIT_CLIENT_ID", ""),
            values.get("REDDIT_CLIENT_SECRET", ""),
            values.get("REDDIT_USER_AGENT", ""),
            values.get("NEWS_API", "")
        )
    except Exception as e:
        print("Failed to load .env file:", e)
        return "", "", "", "", ""


custom_css = """
<style>
.loader {
  border: 4px solid #f3f3f3;
  border-top: 4px solid #3498db;
  border-radius: 50%;
  width: 20px;
  height: 20px;
  animation: spin 1s linear infinite;
  margin-top: 10px;
}
@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}
#toggle-btn {
  position: fixed;
  top: 10px;
  left: 10px;
  z-index: 1000;
  width: 28px;
  height: 28px;
  padding: 0;
  font-size: 18px;
  font-weight: bold;
  line-height: 1;
  text-align: center;
  background: #eee;
  border: 1px solid #ccc;
  border-radius: 4px;
}
</style>
"""

def toggle_sidebar(is_visible):
    return (
        gr.update(visible=not is_visible),
        not is_visible,
        "+" if is_visible else "Γ—"
    )

with gr.Blocks(title="Stock Market AI", js=js_func) as demo:
    gr.HTML(custom_css)
    gr.Markdown("## 🧠 Stock Market AI Agent\nAnalyze a stock using multiple AI agents (fundamentals, news, sentiment, etc).")

    sidebar_state = gr.State(True)
    toggle_btn = gr.Button("Γ—", elem_id="toggle-btn")

    with gr.Row():
        with gr.Column(scale=1) as sidebar:
            gr.Markdown("### πŸ” Enter Your API Keys or Upload a .env File")
            file_upload = gr.File(label="Upload .env or txt file", file_types=[".txt", ".env"])
            groq_key = gr.Textbox(label="GROQ_API_KEY", placeholder="Paste your GROQ API Key here", type="password")
            reddit_id = gr.Textbox(label="REDDIT_CLIENT_ID (Visit: https://www.reddit.com/prefs/apps)", placeholder="Your Reddit App Client ID", type="password")
            reddit_secret = gr.Textbox(label="REDDIT_CLIENT_SECRET", placeholder="Your Reddit App Secret", type="password")
            reddit_agent = gr.Textbox(label="REDDIT_USER_AGENT", placeholder="Your Reddit User Agent")
            news_key = gr.Textbox(label="NEWS_API", placeholder="Your Newsdata.io API Key", type="password")

        with gr.Column(scale=4):
            chatbot = gr.Chatbot(label="Agent Chat", type="messages", resizable=True, show_copy_button=True)
            msg = gr.Textbox(label="Your Stock Question", placeholder="Should I invest in Tesla?")
            spinner = gr.HTML("")
            clear = gr.Button("Clear")

    file_upload.change(
        fn=load_env_file,
        inputs=file_upload,
        outputs=[groq_key, reddit_id, reddit_secret, reddit_agent, news_key]
    )

    toggle_btn.click(
        toggle_sidebar,
        inputs=[sidebar_state],
        outputs=[sidebar, sidebar_state, toggle_btn]
    )

    msg.submit(
        run_agent_stream,
        inputs=[chatbot, msg, groq_key, reddit_id, reddit_secret, reddit_agent, news_key],
        outputs=[chatbot, spinner, msg],
        queue=True
    )

    clear.click(lambda: ([], "", gr.update(interactive=True)), outputs=[chatbot, spinner, msg])

demo.launch()