zypchn commited on
Commit
6cce8c7
·
verified ·
1 Parent(s): 0f532f9

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +97 -92
src/streamlit_app.py CHANGED
@@ -6,35 +6,42 @@ from llama_index.llms.openai import OpenAI
6
  from llama_index.core.agent import ReActAgent
7
  from llama_index.core.workflow import Context
8
  from llama_index.core.tools import FunctionTool
9
- from tools import PokemonAdvisorTools
10
-
11
- # --- 1. Setup Tools & LLM ---
12
- # Instantiate the class to load data
13
- advisor = PokemonAdvisorTools()
14
-
15
- # Create the list of tool methods to wrap
16
- tool_methods = [
17
- advisor.get_card_info,
18
- advisor.find_grading_opportunities,
19
- advisor.assess_risk_volatility,
20
- advisor.get_roi_metrics,
21
- advisor.get_recent_price_spikes,
22
- advisor.analyze_set_performance,
23
- advisor.find_cards_by_artist,
24
- advisor.get_market_movers
25
- ]
26
 
27
- # Wrap tools into LlamaIndex FunctionTools
28
- tools = [FunctionTool.from_defaults(fn=func) for func in tool_methods]
29
 
30
- # Initialize the LLM (Ensure OPENAI_API_KEY is set in your env)
31
- llm = OpenAI(model="gpt-4o-mini", temperature=0.6)
32
 
33
- # --- 2. System Prompt ---
34
- system_prompt = """
35
- ### ROLE
36
- You are the **cAsh, Pokemon Investment Advisor**, an expert algorithmic trading assistant for the Pokemon TCG market.
37
- You rely **strictly** on data. You do not guess. You do not hallucinate prices. Always answer in English.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
 
39
  ### TOOL USAGE PROTOCOL
40
  1. **Verify First:** If a user asks about a card, ALWAYS use `get_card_info` first to ensure it exists.
@@ -43,74 +50,60 @@ You rely **strictly** on data. You do not guess. You do not hallucinate prices.
43
  4. **Profit Hunting:** If a user asks "What should I buy?", use `find_grading_opportunities` or `get_market_movers`.
44
 
45
  ### TONE
46
- Professional, objective, and user-friendly.
47
- """
48
- # --- 3. Initialize Agent & Global Memory ---
49
- # We use timeout=120 because deep reasoning sequences can take time
50
- agent = ReActAgent(
51
- tools=tools,
52
- llm=llm,
53
- verbose=True,
54
- system_prompt=system_prompt,
55
- timeout=120
56
- )
57
-
58
- # Global Context acts as the "Server Memory" for this session
59
- global_ctx = Context(agent)
60
-
61
- # --- 4. Define the Chat Function (Async) ---
62
- async def ask_advisor(user_message, history):
63
- st.session_state.messages.append({"role": "user", "content": user_message})
64
- """
65
- Async function to handle the chat.
66
- It uses 'global_ctx' to maintain memory of previous turns/corrections.
67
- """
 
 
 
 
 
68
  if not user_message:
69
  return "Please enter a message."
70
-
71
  try:
72
  # Execute the agent workflow
73
- # The 'ctx' argument passes the memory of previous interactions
74
- response = await agent.run(user_msg=user_message, ctx=global_ctx)
75
- final_text = str(response)
76
- response_placeholder.markdown(final_text)
77
- st.session_state.messages.append({"role": "assistant", "content": final_text})
78
- # Return the final text response
79
  return str(response)
80
-
81
  except Exception as e:
82
  return f"⚠️ **Agent Error:** {str(e)}\n\n*Check the console logs for detailed tool output.*"
83
 
84
- # --- 2. Page Configuration ---
85
- st.set_page_config(page_title="cAsh Robo-Advisor", page_icon="🤖", layout="wide")
 
86
 
87
- # --- 3. Chat Logic Function ---
88
- async def process_chat(user_message):
89
- """Handles the async call to the agent and updates session state."""
90
- st.session_state.messages.append({"role": "user", "content": user_message})
91
-
92
- # Placeholder for the assistant's response while it thinks
93
- with st.chat_message("assistant"):
94
- response_placeholder = st.empty()
95
- try:
96
- # Execute the agent workflow
97
- response = await agent.run(user_msg=user_message, ctx=global_ctx)
98
- final_text = str(response)
99
- response_placeholder.markdown(final_text)
100
- st.session_state.messages.append({"role": "assistant", "content": final_text})
101
- except Exception as e:
102
- error_msg = f"⚠️ **Agent Error:** {str(e)}"
103
- response_placeholder.markdown(error_msg)
104
-
105
- # --- 4. Sidebar & UI Styling ---
106
  st.title("🤖 cAsh Robo-Advisor")
107
- st.markdown("---")
 
108
 
109
- # Initialize Chat History
110
- if "messages" not in st.session_state:
111
- st.session_state.messages = []
 
 
 
112
 
113
- # Sidebar Examples
114
  st.sidebar.header("Example Queries")
115
  examples = [
116
  "What are the top 3 grading opportunities right now?",
@@ -119,22 +112,34 @@ examples = [
119
  "Show me profitable cards by Tomokazu Komiya."
120
  ]
121
 
122
- # Sidebar helper: clicking an example sends it immediately
123
  for ex in examples:
124
- if st.sidebar.button(ex):
125
- asyncio.run(process_chat(ex))
126
-
127
- # --- 5. Main Chat Interface ---
128
- # Display existing messages
 
 
 
 
 
129
  for message in st.session_state.messages:
130
  with st.chat_message(message["role"]):
131
  st.markdown(message["content"])
132
 
133
- # User Input
134
  if prompt := st.chat_input("Ask your Pokemon Quants advisor..."):
135
- # Clear the "user" UI immediately then run logic
136
  with st.chat_message("user"):
137
  st.markdown(prompt)
 
 
 
 
 
 
 
 
138
 
139
- asyncio.run(ask_advisor(prompt, global_ctx))
140
- #ask_advisor(prompt)
 
6
  from llama_index.core.agent import ReActAgent
7
  from llama_index.core.workflow import Context
8
  from llama_index.core.tools import FunctionTool
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
 
10
+ # --- Import tools class ---
11
+ from tools import PokemonAdvisorTools
12
 
13
+ # --- 1. Page Configuration (MUST be first Streamlit command) ---
14
+ st.set_page_config(page_title="cAsh Robo-Advisor", page_icon="🤖")
15
 
16
+ # --- 2. Initialize Agent & Tools (with caching to avoid recreating on every rerun) ---
17
+ @st.cache_resource
18
+ def initialize_agent():
19
+ """Initialize the agent and tools once and cache them."""
20
+ # Instantiate the class to load data
21
+ advisor = PokemonAdvisorTools()
22
+
23
+ # Create the list of tool methods to wrap
24
+ tool_methods = [
25
+ advisor.get_card_info,
26
+ advisor.find_grading_opportunities,
27
+ advisor.assess_risk_volatility,
28
+ advisor.get_roi_metrics,
29
+ advisor.get_recent_price_spikes,
30
+ advisor.analyze_set_performance,
31
+ advisor.find_cards_by_artist,
32
+ advisor.get_market_movers
33
+ ]
34
+
35
+ # Wrap tools into LlamaIndex FunctionTools
36
+ tools = [FunctionTool.from_defaults(fn=func) for func in tool_methods]
37
+
38
+ # Initialize the LLM (Ensure OPENAI_API_KEY is set in your env)
39
+ llm = OpenAI(model="gpt-4o-mini")
40
+
41
+ # System Prompt
42
+ system_prompt = """### ROLE
43
+ You are the **Poke-Alpha Investment Advisor**, an expert algorithmic trading assistant for the Pokémon TCG market.
44
+ You rely **strictly** on data. You do not guess. You do not hallucinate prices.
45
 
46
  ### TOOL USAGE PROTOCOL
47
  1. **Verify First:** If a user asks about a card, ALWAYS use `get_card_info` first to ensure it exists.
 
50
  4. **Profit Hunting:** If a user asks "What should I buy?", use `find_grading_opportunities` or `get_market_movers`.
51
 
52
  ### TONE
53
+ Professional, objective, concise, and user-friendly."""
54
+
55
+ # Initialize Agent
56
+ agent = ReActAgent(
57
+ tools=tools,
58
+ llm=llm,
59
+ verbose=True,
60
+ system_prompt=system_prompt,
61
+ timeout=120
62
+ )
63
+
64
+ return agent
65
+
66
+ # Get cached agent
67
+ agent = initialize_agent()
68
+
69
+ # --- 3. Initialize Session State ---
70
+ if "messages" not in st.session_state:
71
+ st.session_state.messages = []
72
+
73
+ if "context" not in st.session_state:
74
+ # Create a new Context for this session
75
+ st.session_state.context = Context(agent)
76
+
77
+ # --- 4. Define the Chat Function (Sync wrapper for async) ---
78
+ async def ask_advisor_async(user_message, ctx):
79
+ """Async function to handle the chat."""
80
  if not user_message:
81
  return "Please enter a message."
82
+
83
  try:
84
  # Execute the agent workflow
85
+ response = await agent.run(user_msg=user_message, ctx=ctx)
 
 
 
 
 
86
  return str(response)
 
87
  except Exception as e:
88
  return f"⚠️ **Agent Error:** {str(e)}\n\n*Check the console logs for detailed tool output.*"
89
 
90
+ def ask_advisor(user_message):
91
+ """Synchronous wrapper for the async function."""
92
+ return asyncio.run(ask_advisor_async(user_message, st.session_state.context))
93
 
94
+ # --- 5. Styling & Header ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
  st.title("🤖 cAsh Robo-Advisor")
96
+ st.markdown("""
97
+ **Your AI Quantitative Analyst for Pokemon Cards.**
98
 
99
+ Ask about:
100
+ * **Arbitrage:** "What are the best grading opportunities?"
101
+ * **Risk:** "Is Pikachu EX a safe investment?"
102
+ * **Trends:** "What is crashing right now?"
103
+ * **Sets:** "How is Evolving Skies performing?"
104
+ """)
105
 
106
+ # --- 6. Sidebar Examples ---
107
  st.sidebar.header("Example Queries")
108
  examples = [
109
  "What are the top 3 grading opportunities right now?",
 
112
  "Show me profitable cards by Tomokazu Komiya."
113
  ]
114
 
 
115
  for ex in examples:
116
+ if st.sidebar.button(ex, key=f"example_{ex}"):
117
+ # Add to messages and set a flag to process
118
+ st.session_state.messages.append({"role": "user", "content": ex})
119
+ # Process the example query
120
+ with st.spinner("Thinking..."):
121
+ response = ask_advisor(ex)
122
+ st.session_state.messages.append({"role": "assistant", "content": response})
123
+ st.rerun()
124
+
125
+ # --- 7. Display Chat History ---
126
  for message in st.session_state.messages:
127
  with st.chat_message(message["role"]):
128
  st.markdown(message["content"])
129
 
130
+ # --- 8. Chat Input Logic ---
131
  if prompt := st.chat_input("Ask your Pokemon Quants advisor..."):
132
+ # Display user message
133
  with st.chat_message("user"):
134
  st.markdown(prompt)
135
+ st.session_state.messages.append({"role": "user", "content": prompt})
136
+
137
+ # Generate Response
138
+ with st.chat_message("assistant"):
139
+ with st.spinner("Analyzing..."):
140
+ response = ask_advisor(prompt)
141
+ st.markdown(response)
142
+ st.session_state.messages.append({"role": "assistant", "content": response})
143
 
144
+ # Force a rerun to update the chat display
145
+ st.rerun()