import gradio as gr import requests import pandas as pd import plotly.graph_objects as go from huggingface_hub import InferenceClient # ----------------------------------------------------------------------------- # 1. CONFIGURATION & STATE # ----------------------------------------------------------------------------- # Global variable to store the latest market text for the AI MARKET_CONTEXT = "Market data is loading..." # Initialize the public model (no token required) client = InferenceClient(model="Qwen/Qwen2.5-VL-3B-Instruct") # ----------------------------------------------------------------------------- # 2. DATA FETCHING (CoinGecko) # ----------------------------------------------------------------------------- def fetch_crypto_data(): """ Fetches data from CoinGecko. Returns: A list of dicts for the UI, and updates the global AI context. """ url = "https://api.coingecko.com/api/v3/coins/markets" params = { "vs_currency": "usd", "ids": "bitcoin,ethereum,solana,binancecoin", "order": "market_cap_desc", "per_page": 4, "page": 1, "sparkline": "true", "price_change_percentage": "24h,7d" } global MARKET_CONTEXT try: response = requests.get(url, params=params, timeout=10) if response.status_code != 200: return None data = response.json() # Build the context string for AI context_parts = [] processed_data = [] for coin in data: symbol = coin['symbol'].upper() price = coin['current_price'] chg_24 = coin.get('price_change_percentage_24h_in_currency', 0) or 0 chg_7d = coin.get('price_change_percentage_7d_in_currency', 0) or 0 mcap = coin['market_cap'] or 0 history = coin.get('sparkline_in_7d', {}).get('price', []) # Add to AI context context_parts.append(f"[{symbol}: ${price}, 24h:{chg_24:.1f}%, 7d:{chg_7d:.1f}%]") processed_data.append({ "name": coin['name'], "symbol": symbol, "price": price, "chg_24": chg_24, "chg_7d": chg_7d, "mcap": mcap, "history": history }) MARKET_CONTEXT = " | ".join(context_parts) return processed_data except Exception as e: print(f"API Error: {e}") return None # ----------------------------------------------------------------------------- # 3. UI HELPERS (Plots & HTML) # ----------------------------------------------------------------------------- def create_sparkline(history, chg_24): """Creates a minimalist Plotly sparkline""" # Green if positive, Red if negative color = "#10B981" if chg_24 >= 0 else "#EF4444" fig = go.Figure() fig.add_trace(go.Scatter( y=history, mode='lines', fill='tozeroy', line=dict(color=color, width=2), fillcolor=f"rgba{tuple(int(color.lstrip('#')[i:i+2], 16) for i in (0, 2, 4)) + (0.1,)}" )) fig.update_layout( template="plotly_dark", paper_bgcolor='rgba(0,0,0,0)', plot_bgcolor='rgba(0,0,0,0)', margin=dict(l=0, r=0, t=10, b=0), xaxis=dict(visible=False, fixedrange=True), yaxis=dict(visible=False, fixedrange=True), showlegend=False, height=80, ) return fig def create_card_html(coin): """Generates the HTML card for a single coin""" if not coin: return "
Error Loading
" color_24 = "#10B981" if coin['chg_24'] >= 0 else "#EF4444" arrow = "▲" if coin['chg_24'] >= 0 else "▼" html = f"""

{coin['name']}

{coin['symbol']}
${coin['price']:,.2f}
{arrow} {coin['chg_24']:.2f}% (24h)
MCap: ${coin['mcap']/1e9:.1f}B
""" return html def refresh_dashboard(): """Main function called to update the dashboard""" data = fetch_crypto_data() # If API fails (rate limit), return dummy updates if not data: return [gr.update()] * 8 # 4 HTML blocks + 4 Plots outputs = [] for coin in data: outputs.append(create_card_html(coin)) outputs.append(create_sparkline(coin['history'], coin['chg_24'])) return outputs # ----------------------------------------------------------------------------- # 4. AI CHAT LOGIC # ----------------------------------------------------------------------------- def chat_logic(message, history): system_prompt = f"""You are a professional Crypto Dashboard Assistant. LIVE MARKET DATA (Use this if asked about prices): {MARKET_CONTEXT} INSTRUCTIONS: 1. If the user asks for the price, trend, or stats of a coin, USE THE LIVE DATA above. 2. If the user asks general questions ("What is mining?", "Who is Satoshi?"), answer normally. 3. Keep answers concise, helpful, and professional. """ messages = [{"role": "system", "content": system_prompt}] for human, assistant in history: messages.append({"role": "user", "content": human}) messages.append({"role": "assistant", "content": assistant}) messages.append({"role": "user", "content": message}) try: stream = client.chat_completion(messages, max_tokens=512, stream=True, temperature=0.7) partial_message = "" for chunk in stream: if chunk.choices[0].delta.content: partial_message += chunk.choices[0].delta.content yield partial_message except Exception as e: yield f"⚠️ AI Error: {str(e)}" # ----------------------------------------------------------------------------- # 5. GRADIO APP LAYOUT # ----------------------------------------------------------------------------- custom_css = """ body { background-color: #111827; } .contain { max-width: 1400px; margin: auto; padding-top: 20px; } h1 { color: #F3F4F6; text-align: center; margin-bottom: 30px; } .plot-container { border: none !important; background: transparent !important; } footer { visibility: hidden; } """ # We define the theme on the Blocks object, but remove it from ChatInterface with gr.Blocks(css=custom_css, theme=gr.themes.Soft(primary_hue="indigo", neutral_hue="slate")) as demo: gr.HTML("

⚡ CryptoDash AI

") # --- DASHBOARD ROW --- # We initialize with empty plots/html, they get filled by the load() event with gr.Row(): # Column 1: BTC with gr.Column(): c1_html = gr.HTML() c1_plot = gr.Plot(show_label=False, container=False) # Column 2: ETH with gr.Column(): c2_html = gr.HTML() c2_plot = gr.Plot(show_label=False, container=False) # Column 3: SOL with gr.Column(): c3_html = gr.HTML() c3_plot = gr.Plot(show_label=False, container=False) # Column 4: BNB with gr.Column(): c4_html = gr.HTML() c4_plot = gr.Plot(show_label=False, container=False) btn_refresh = gr.Button("🔄 Refresh Market Data", variant="secondary") # --- CHAT SECTION --- gr.HTML("
") gr.Markdown("### 🤖 Qwen Crypto Assistant") # NOTE: Removed 'theme' argument here to fix the error chat = gr.ChatInterface( fn=chat_logic, examples=["What is the price of Bitcoin?", "Is the market up today?", "Explain market cap"], ) # --- EVENT WIRING --- # Load data immediately when app starts demo.load( fn=refresh_dashboard, inputs=None, outputs=[c1_html, c1_plot, c2_html, c2_plot, c3_html, c3_plot, c4_html, c4_plot] ) # Refresh button click btn_refresh.click( fn=refresh_dashboard, inputs=None, outputs=[c1_html, c1_plot, c2_html, c2_plot, c3_html, c3_plot, c4_html, c4_plot] ) if __name__ == "__main__": demo.launch()