CryptoCreeper commited on
Commit
bdc6692
·
verified ·
1 Parent(s): 970037e

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +239 -0
app.py ADDED
@@ -0,0 +1,239 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import requests
3
+ import pandas as pd
4
+ import plotly.graph_objects as go
5
+ from huggingface_hub import InferenceClient
6
+ import os
7
+
8
+ # -----------------------------------------------------------------------------
9
+ # 1. CONFIGURATION & STATE
10
+ # -----------------------------------------------------------------------------
11
+ # We keep a global variable to store the latest market text for the AI to read
12
+ MARKET_CONTEXT = "Market data is currently loading..."
13
+
14
+ def get_client():
15
+ # Tries to get the HF_TOKEN from the Space secrets, otherwise falls back to free tier
16
+ token = os.getenv("HF_TOKEN")
17
+ return InferenceClient(model="Qwen/Qwen2.5-VL-3B-Instruct", token=token)
18
+
19
+ # -----------------------------------------------------------------------------
20
+ # 2. DATA FETCHING & PROCESSING
21
+ # -----------------------------------------------------------------------------
22
+ def fetch_crypto_data():
23
+ """Fetches data from CoinGecko and returns a dict of metrics + sparklines + context string"""
24
+ url = "https://api.coingecko.com/api/v3/coins/markets"
25
+ params = {
26
+ "vs_currency": "usd",
27
+ "ids": "bitcoin,ethereum,solana,binancecoin",
28
+ "order": "market_cap_desc",
29
+ "per_page": 4,
30
+ "page": 1,
31
+ "sparkline": "true",
32
+ "price_change_percentage": "24h,7d"
33
+ }
34
+
35
+ global MARKET_CONTEXT
36
+
37
+ try:
38
+ response = requests.get(url, params=params, timeout=10)
39
+ data = response.json()
40
+
41
+ # Build the context string for AI
42
+ context_parts = []
43
+ processed_data = []
44
+
45
+ for coin in data:
46
+ symbol = coin['symbol'].upper()
47
+ price = coin['current_price']
48
+ chg_24 = coin.get('price_change_percentage_24h_in_currency', 0)
49
+ chg_7d = coin.get('price_change_percentage_7d_in_currency', 0)
50
+ mcap = coin['market_cap']
51
+ history = coin.get('sparkline_in_7d', {}).get('price', [])
52
+
53
+ # Add to AI context
54
+ context_parts.append(f"{symbol}: ${price}, 24h: {chg_24:.1f}%, 7d: {chg_7d:.1f}%")
55
+
56
+ processed_data.append({
57
+ "name": coin['name'],
58
+ "symbol": symbol,
59
+ "price": price,
60
+ "chg_24": chg_24,
61
+ "chg_7d": chg_7d,
62
+ "mcap": mcap,
63
+ "history": history
64
+ })
65
+
66
+ MARKET_CONTEXT = " | ".join(context_parts)
67
+ return processed_data
68
+
69
+ except Exception as e:
70
+ MARKET_CONTEXT = "Error fetching market data."
71
+ print(f"API Error: {e}")
72
+ return []
73
+
74
+ # -----------------------------------------------------------------------------
75
+ # 3. UI GENERATION (HTML + PLOTS)
76
+ # -----------------------------------------------------------------------------
77
+ def create_sparkline(history, chg_24):
78
+ """Creates a minimalist Plotly sparkline"""
79
+ color = "#00ff00" if chg_24 >= 0 else "#ff4444"
80
+
81
+ fig = go.Figure()
82
+ fig.add_trace(go.Scatter(
83
+ y=history,
84
+ mode='lines',
85
+ fill='tozeroy',
86
+ line=dict(color=color, width=2),
87
+ fillcolor=f"rgba{tuple(int(color.lstrip('#')[i:i+2], 16) for i in (0, 2, 4)) + (0.1,)}"
88
+ ))
89
+
90
+ fig.update_layout(
91
+ template="plotly_dark",
92
+ paper_bgcolor='rgba(0,0,0,0)',
93
+ plot_bgcolor='rgba(0,0,0,0)',
94
+ margin=dict(l=0, r=0, t=0, b=0),
95
+ xaxis=dict(visible=False, fixedrange=True),
96
+ yaxis=dict(visible=False, fixedrange=True),
97
+ showlegend=False,
98
+ height=80,
99
+ )
100
+ return fig
101
+
102
+ def generate_dashboard():
103
+ """Main function called to refresh the dashboard UI"""
104
+ data = fetch_crypto_data()
105
+ outputs = []
106
+
107
+ if not data:
108
+ # If API fails, return empty placeholders
109
+ return [gr.update(), gr.update()] * 4
110
+
111
+ for coin in data:
112
+ # 1. Create HTML Block for Metrics
113
+ color_24 = "#4caf50" if coin['chg_24'] >= 0 else "#ff5252"
114
+ arrow = "▲" if coin['chg_24'] >= 0 else "▼"
115
+
116
+ html_content = f"""
117
+ <div style="background-color: #1f2937; padding: 15px; border-radius: 10px; border: 1px solid #374151;">
118
+ <div style="display: flex; justify-content: space-between; align-items: center;">
119
+ <h3 style="margin: 0; color: white;">{coin['name']} <span style="font-size: 0.8em; color: #9ca3af;">{coin['symbol']}</span></h3>
120
+ <span style="font-size: 1.2em; font-weight: bold; color: white;">${coin['price']:,.2f}</span>
121
+ </div>
122
+ <div style="display: flex; justify-content: space-between; margin-top: 10px;">
123
+ <span style="color: {color_24}; font-weight: bold;">{arrow} {coin['chg_24']:.2f}% (24h)</span>
124
+ <span style="color: #9ca3af;">7d: {coin['chg_7d']:.2f}%</span>
125
+ </div>
126
+ <div style="font-size: 0.8em; color: #6b7280; margin-top: 5px;">MCap: ${coin['mcap']/1e9:.1f}B</div>
127
+ </div>
128
+ """
129
+ outputs.append(html_content)
130
+
131
+ # 2. Create Plot
132
+ fig = create_sparkline(coin['history'], coin['chg_24'])
133
+ outputs.append(fig)
134
+
135
+ return outputs
136
+
137
+ # -----------------------------------------------------------------------------
138
+ # 4. AI CHAT FUNCTION
139
+ # -----------------------------------------------------------------------------
140
+ def chat_response(message, history):
141
+ client = get_client()
142
+
143
+ system_prompt = f"""You are a helpful Crypto Dashboard Assistant using the Qwen model.
144
+
145
+ REAL-TIME MARKET CONTEXT:
146
+ {MARKET_CONTEXT}
147
+
148
+ INSTRUCTIONS:
149
+ 1. If the user asks for the price, trend, or stats of a coin in the list, USE THE CONTEXT PROVIDED.
150
+ 2. If the user asks general questions ("What is DeFi?", "Tell a joke"), answer normally.
151
+ 3. Keep answers concise and professional.
152
+ """
153
+
154
+ messages = [{"role": "system", "content": system_prompt}]
155
+
156
+ # Add history
157
+ for human, assistant in history:
158
+ messages.append({"role": "user", "content": human})
159
+ messages.append({"role": "assistant", "content": assistant})
160
+
161
+ messages.append({"role": "user", "content": message})
162
+
163
+ # Stream response
164
+ stream = client.chat_completion(messages, max_tokens=500, stream=True)
165
+
166
+ partial_message = ""
167
+ for chunk in stream:
168
+ if chunk.choices[0].delta.content:
169
+ partial_message += chunk.choices[0].delta.content
170
+ yield partial_message
171
+
172
+ # -----------------------------------------------------------------------------
173
+ # 5. GRADIO APP LAYOUT
174
+ # -----------------------------------------------------------------------------
175
+ custom_css = """
176
+ body { background-color: #0b0f19; }
177
+ .contain { max-width: 1200px; margin: auto; }
178
+ #dashboard-header { text-align: center; color: white; margin-bottom: 20px; }
179
+ .plot-container { border: none !important; }
180
+ footer { visibility: hidden; }
181
+ """
182
+
183
+ with gr.Blocks(css=custom_css, theme=gr.themes.Soft(primary_hue="indigo", neutral_hue="slate")) as demo:
184
+
185
+ gr.HTML("<h1 id='dashboard-header'>⚡ CryptoDash AI</h1>")
186
+
187
+ # --- DASHBOARD ROW ---
188
+ with gr.Row():
189
+ # We need 4 Columns, each containing HTML (metrics) and Plot (sparkline)
190
+ # Coin 1
191
+ with gr.Column():
192
+ c1_html = gr.HTML()
193
+ c1_plot = gr.Plot(label="Trend", show_label=False, container=False)
194
+
195
+ # Coin 2
196
+ with gr.Column():
197
+ c2_html = gr.HTML()
198
+ c2_plot = gr.Plot(label="Trend", show_label=False, container=False)
199
+
200
+ # Coin 3
201
+ with gr.Column():
202
+ c3_html = gr.HTML()
203
+ c3_plot = gr.Plot(label="Trend", show_label=False, container=False)
204
+
205
+ # Coin 4
206
+ with gr.Column():
207
+ c4_html = gr.HTML()
208
+ c4_plot = gr.Plot(label="Trend", show_label=False, container=False)
209
+
210
+ # Refresh Button
211
+ btn_refresh = gr.Button("🔄 Refresh Market Data", variant="secondary")
212
+
213
+ # --- CHAT ROW ---
214
+ gr.HTML("<br><hr style='border-color: #374151;'><br>")
215
+ gr.Markdown("### 🤖 Ask Qwen about the market")
216
+
217
+ chat_interface = gr.ChatInterface(
218
+ fn=chat_response,
219
+ examples=["What is the price of Bitcoin?", "Is Solana up or down today?", "Explain market cap"],
220
+ theme="soft"
221
+ )
222
+
223
+ # --- WIRING ---
224
+ # When app loads, fetch data and populate the 8 outputs (4 html + 4 plots)
225
+ demo.load(
226
+ fn=generate_dashboard,
227
+ inputs=None,
228
+ outputs=[c1_html, c1_plot, c2_html, c2_plot, c3_html, c3_plot, c4_html, c4_plot]
229
+ )
230
+
231
+ # When refresh clicked, do the same
232
+ btn_refresh.click(
233
+ fn=generate_dashboard,
234
+ inputs=None,
235
+ outputs=[c1_html, c1_plot, c2_html, c2_plot, c3_html, c3_plot, c4_html, c4_plot]
236
+ )
237
+
238
+ if __name__ == "__main__":
239
+ demo.launch()