File size: 5,367 Bytes
673ebf8
54f8754
 
 
 
 
 
 
e9275ec
6cce8c7
 
e9275ec
6cce8c7
 
e9275ec
6cce8c7
 
 
 
 
b76ceb5
6cce8c7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9de2bf4
6cce8c7
 
1b2d77c
 
 
 
54f8754
1b2d77c
 
 
 
 
e9275ec
1b2d77c
 
 
 
 
 
 
 
6cce8c7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e9275ec
 
6cce8c7
e9275ec
 
6cce8c7
e9275ec
 
 
54f8754
6cce8c7
 
 
54f8754
6cce8c7
c2a3a55
6cce8c7
c2a3a55
e02af7d
 
b4c9087
6cce8c7
5703cae
 
 
 
 
 
 
 
6cce8c7
 
 
 
 
 
 
 
 
 
54f8754
 
 
b4c9087
6cce8c7
e02af7d
6cce8c7
b4c9087
 
6cce8c7
 
 
 
 
 
 
 
a4d5268
6cce8c7
 
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
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()