PokeRoboAdvisor / src /streamlit_app.py
zypchn's picture
Update src/streamlit_app.py
c2a3a55 verified
import streamlit as st
import pandas as pd
import os
import asyncio
from llama_index.llms.openai import OpenAI
from llama_index.core.agent import ReActAgent
from llama_index.core.workflow import Context
from llama_index.core.tools import FunctionTool
# --- Import tools class ---
from tools import PokemonAdvisorTools
# --- 1. Page Configuration (MUST be first Streamlit command) ---
st.set_page_config(page_title="cAsh Robo-Advisor", page_icon="πŸ€–")
# --- 2. Initialize Agent & Tools (with caching to avoid recreating on every rerun) ---
@st.cache_resource
def initialize_agent():
"""Initialize the agent and tools once and cache them."""
# Instantiate the class to load data
advisor = PokemonAdvisorTools("/app/src")
# Create the list of tool methods to wrap
tool_methods = [
advisor.get_card_info,
advisor.find_grading_opportunities,
advisor.assess_risk_volatility,
advisor.get_roi_metrics,
advisor.get_recent_price_spikes,
advisor.analyze_set_performance,
advisor.find_cards_by_artist,
advisor.get_market_movers
]
# Wrap tools into LlamaIndex FunctionTools
tools = [FunctionTool.from_defaults(fn=func) for func in tool_methods]
# Initialize the LLM (Ensure OPENAI_API_KEY is set in your env)
llm = OpenAI(model="gpt-4o-mini", temperature=0.5)
# System Prompt
system_prompt = """
### ROLE
You are **cAsh**, a sophisticated Pokemon Investment Advisor.
While you rely **strictly** on data and tools, your goal is to provide a helpful, engaging experience similar to a professional financial consultant.
### CONVERSATIONAL STYLE
- **Opening:** Start with a brief, friendly acknowledgment of the user's request.
- **Interpretation:** Don't just list numbers. Explain *why* these numbers matter (e.g., "The market is showing a significant spike for high-end Tag Team cards...").
- **Formatting:** Use bolding for card names and bullet points for readability, but keep the sentences flowing naturally.
- **Closing:** End with a helpful follow-up suggestion or a brief summary of the risk/opportunity.
### TOOL USAGE PROTOCOL
1. **Verify First:** ALWAYS use `get_card_info` first for specific card queries.
2. **Safety Check:** BEFORE recommending ANY card, you MUST call `assess_risk_volatility`.
3. **Data Integrity:** Never hallucinate. If the tool returns nothing, say "My database doesn't have the specific stats for that card yet."
### TONE
Professional, objective, insightful, and conversational. Always answer in English.
"""
# Initialize Agent
agent = ReActAgent(
tools=tools,
llm=llm,
verbose=True,
system_prompt=system_prompt,
timeout=120
)
return agent
# Get cached agent
agent = initialize_agent()
# --- 3. Initialize Session State ---
if "messages" not in st.session_state:
st.session_state.messages = []
if "context" not in st.session_state:
# Create a new Context for this session
st.session_state.context = Context(agent)
# --- 4. Define the Chat Function (Sync wrapper for async) ---
async def ask_advisor_async(user_message, ctx):
"""Async function to handle the chat."""
if not user_message:
return "Please enter a message."
try:
# Execute the agent workflow
response = await agent.run(user_msg=user_message, ctx=ctx)
return str(response)
except Exception as e:
return f"⚠️ **Agent Error:** {str(e)}\n\n*Check the console logs for detailed tool output.*"
def ask_advisor(user_message):
"""Synchronous wrapper for the async function."""
return asyncio.run(ask_advisor_async(user_message, st.session_state.context))
# --- 5. Styling & Header ---
st.title("πŸƒ πŸ”₯ cAsh πŸ’§ ⚑")
st.markdown("""
**πŸ€– Your AI Quantitative Analyst for Pokemon Cards.**
"""
)
# --- 6. Sidebar Examples ---
st.sidebar.header("Example Queries")
examples = [
"What are the top 3 grading opportunities right now?",
"What cards are trending down?",
"Show me profitable cards by Tomokazu Komiya."
]
for ex in examples:
if st.sidebar.button(ex, key=f"example_{ex}"):
# Add to messages and set a flag to process
st.session_state.messages.append({"role": "user", "content": ex})
# Process the example query
with st.spinner("Thinking..."):
response = ask_advisor(ex)
st.session_state.messages.append({"role": "assistant", "content": response})
st.rerun()
# --- 7. Display Chat History ---
for message in st.session_state.messages:
with st.chat_message(message["role"]):
st.markdown(message["content"])
# --- 8. Chat Input Logic ---
if prompt := st.chat_input("Ask cAsh..."):
# Display user message
with st.chat_message("user"):
st.markdown(prompt)
st.session_state.messages.append({"role": "user", "content": prompt})
# Generate Response
with st.chat_message("assistant"):
with st.spinner("Analyzing..."):
response = ask_advisor(prompt)
st.markdown(response)
st.session_state.messages.append({"role": "assistant", "content": response})
# Force a rerun to update the chat display
st.rerun()