Spaces:
Sleeping
Sleeping
| 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="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(height=100, show_error=True) | |