kz209 commited on
Commit
ab031a3
·
1 Parent(s): 41a349c

Add application file

Browse files
Files changed (3) hide show
  1. app.py +244 -0
  2. gpu_price_history.csv +0 -0
  3. llm_price_trends.csv +0 -0
app.py ADDED
@@ -0,0 +1,244 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import pandas as pd
3
+ import plotly.express as px
4
+ import re
5
+ import io
6
+ import os
7
+
8
+ # ==========================================
9
+ # 1. 数据读取引擎 (防弹版 - 保持不变)
10
+ # ==========================================
11
+
12
+ def clean_and_read_file(file_path):
13
+ """
14
+ Robust file reader:
15
+ 1. Handles .xlsx masquerading as .csv
16
+ 2. Cleans garbage tags like
17
+ 3. Fixes broken lines
18
+ """
19
+ if not file_path or not os.path.exists(file_path):
20
+ return pd.DataFrame()
21
+
22
+ # --- Strategy A: Try reading as Excel ---
23
+ try:
24
+ df = pd.read_excel(file_path)
25
+ return df
26
+ except:
27
+ pass
28
+
29
+ # --- Strategy B: Read as Text ---
30
+ raw_data = b""
31
+ try:
32
+ with open(file_path, 'rb') as f:
33
+ raw_data = f.read()
34
+ except Exception as e:
35
+ print(f"File read error: {e}")
36
+ return pd.DataFrame()
37
+
38
+ # Decode
39
+ content = ""
40
+ for enc in ['utf-8', 'gb18030', 'gbk']:
41
+ try:
42
+ content = raw_data.decode(enc)
43
+ break
44
+ except UnicodeDecodeError:
45
+ continue
46
+ if not content:
47
+ content = raw_data.decode('utf-8', errors='replace')
48
+
49
+ # --- Cleaning ---
50
+ content = re.sub(r"\\", "", content)
51
+
52
+ lines = content.splitlines()
53
+ cleaned_lines = []
54
+ buffer = ""
55
+ date_pattern = re.compile(r'^\s*202\d-\d{2}-\d{2}')
56
+
57
+ for line in lines:
58
+ line = line.strip()
59
+ if not line: continue
60
+
61
+ is_header = "Date" in line and ("," in line)
62
+ is_date_row = date_pattern.match(line) is not None
63
+
64
+ if is_header or is_date_row:
65
+ if buffer: cleaned_lines.append(buffer)
66
+ buffer = line
67
+ else:
68
+ buffer += " " + line
69
+
70
+ if buffer: cleaned_lines.append(buffer)
71
+
72
+ csv_content = "\n".join(cleaned_lines)
73
+ try:
74
+ df = pd.read_csv(io.StringIO(csv_content))
75
+ except:
76
+ try:
77
+ df = pd.read_csv(io.StringIO(csv_content), sep=None, engine='python')
78
+ except:
79
+ return pd.DataFrame()
80
+
81
+ return df
82
+
83
+ # ==========================================
84
+ # 2. 数据处理
85
+ # ==========================================
86
+
87
+ def process_gpu_data(df):
88
+ if df.empty: return df
89
+ df.columns = [str(c).strip() for c in df.columns]
90
+
91
+ if 'Date' in df.columns:
92
+ df['Date'] = pd.to_datetime(df['Date'], errors='coerce')
93
+
94
+ def clean_currency(x):
95
+ if isinstance(x, (int, float)): return float(x)
96
+ if isinstance(x, str):
97
+ match = re.search(r'(\d+\.?\d*)', x)
98
+ return float(match.group(1)) if match else 0.0
99
+ return 0.0
100
+
101
+ target_col = None
102
+ if 'Cloud Rent (/hr)' in df.columns:
103
+ target_col = 'Cloud Rent (/hr)'
104
+ else:
105
+ for c in df.columns:
106
+ if 'Rent' in c or '/hr' in c:
107
+ target_col = c
108
+ break
109
+
110
+ if target_col:
111
+ df['Rent_Price_Num'] = df[target_col].apply(clean_currency)
112
+
113
+ return df
114
+
115
+ def process_llm_data(df):
116
+ if df.empty: return df
117
+ df.columns = [str(c).strip() for c in df.columns]
118
+
119
+ if 'Date' in df.columns:
120
+ df['Date'] = pd.to_datetime(df['Date'], errors='coerce')
121
+
122
+ return df
123
+
124
+ # ==========================================
125
+ # 3. 绘图逻辑
126
+ # ==========================================
127
+
128
+ def plot_gpu_trends(df):
129
+ if df is None or df.empty or 'Rent_Price_Num' not in df.columns:
130
+ return None
131
+
132
+ plot_df = df.dropna(subset=['Date', 'Rent_Price_Num'])
133
+ if plot_df.empty: return None
134
+
135
+ chip_col = 'Chip' if 'Chip' in df.columns else df.columns[1]
136
+
137
+ fig = px.line(
138
+ plot_df,
139
+ x='Date',
140
+ y='Rent_Price_Num',
141
+ color=chip_col if chip_col in df.columns else None,
142
+ title='GPU Cloud Rental Price Trends ($/hr)',
143
+ labels={'Rent_Price_Num': 'Price ($/hr)', 'Date': 'Date'},
144
+ markers=True
145
+ )
146
+ return fig
147
+
148
+ def plot_llm_trends(df):
149
+ """绘制所有列的趋势,不再需要 selection"""
150
+ if df is None or df.empty:
151
+ return None
152
+
153
+ # 自动选择除了 Date 以外的所有列
154
+ value_vars = [c for c in df.columns if c != 'Date']
155
+ if not value_vars:
156
+ return None
157
+
158
+ plot_df = df[['Date'] + value_vars].copy().dropna(subset=['Date'])
159
+
160
+ # Melt
161
+ df_long = plot_df.melt(id_vars=['Date'], var_name='Model', value_name='Price')
162
+
163
+ fig = px.line(
164
+ df_long,
165
+ x='Date',
166
+ y='Price',
167
+ color='Model',
168
+ title='LLM API Price Trends',
169
+ labels={'Price': 'Price', 'Date': 'Date', 'Model': 'Model Type'},
170
+ markers=True
171
+ )
172
+ return fig
173
+
174
+ # ==========================================
175
+ # 4. Gradio 界面
176
+ # ==========================================
177
+
178
+ DEFAULT_GPU_FILE = "./gpu_script/gpu_price_history.csv"
179
+ DEFAULT_LLM_FILE = "./llm_token_tracker/llm_price_trends.csv"
180
+
181
+ def load_gpu_pipeline():
182
+ df = clean_and_read_file(DEFAULT_GPU_FILE)
183
+ df = process_gpu_data(df)
184
+ return df, plot_gpu_trends(df)
185
+
186
+ def load_llm_pipeline():
187
+ df = clean_and_read_file(DEFAULT_LLM_FILE)
188
+ df = process_llm_data(df)
189
+ return df, plot_llm_trends(df)
190
+
191
+ # --- UI Definition ---
192
+ with gr.Blocks(title="AI Price Tracker") as demo:
193
+ gr.Markdown("## 📊 AI Compute & Model Price Trends")
194
+
195
+ with gr.Tabs():
196
+ # GPU Tab
197
+ with gr.TabItem("GPU Prices"):
198
+ with gr.Row():
199
+ with gr.Column(scale=1):
200
+ gpu_plot = gr.Plot(label="Price Trend")
201
+ with gr.Row():
202
+ with gr.Accordion("Data Preview", open=False):
203
+ gpu_table = gr.DataFrame()
204
+
205
+ # LLM Tab (Updated: No Filter)
206
+ with gr.TabItem("LLM Prices"):
207
+ with gr.Row():
208
+ # 直接展示图表,不分栏
209
+ with gr.Column(scale=1):
210
+ llm_plot = gr.Plot(label="Price Trend")
211
+
212
+ with gr.Row():
213
+ with gr.Accordion("Data Preview", open=False):
214
+ llm_table = gr.DataFrame()
215
+
216
+ # --- Initialization Logic ---
217
+ def init_on_load():
218
+ # Load GPU
219
+ g_df, g_fig = load_gpu_pipeline()
220
+
221
+ # Load LLM (No checkbox needed anymore)
222
+ l_df, l_fig = load_llm_pipeline()
223
+
224
+ return (
225
+ g_fig, # gpu_plot
226
+ g_df, # gpu_table
227
+ l_fig, # llm_plot
228
+ l_df # llm_table
229
+ )
230
+
231
+ # 绑定加载事件
232
+ demo.load(
233
+ init_on_load,
234
+ inputs=None,
235
+ outputs=[
236
+ gpu_plot,
237
+ gpu_table,
238
+ llm_plot,
239
+ llm_table
240
+ ]
241
+ )
242
+
243
+ if __name__ == "__main__":
244
+ demo.launch(share=True)
gpu_price_history.csv ADDED
Binary file (6.34 kB). View file
 
llm_price_trends.csv ADDED
Binary file (6.09 kB). View file