startrz commited on
Commit
bffc389
·
verified ·
1 Parent(s): 82d11aa

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +267 -54
app.py CHANGED
@@ -1,64 +1,277 @@
 
1
  import streamlit as st
2
- import openai
3
  import pandas as pd
4
- import datetime
 
 
 
 
 
 
 
 
 
5
 
6
- # Initialize Streamlit app
7
- st.set_page_config(page_title="OpenAI Usage Dashboard", layout="centered")
 
 
 
 
 
 
 
 
 
 
8
 
9
- # Set up OpenAI API key
10
- st.sidebar.title("OpenAI API Usage Dashboard")
11
- api_key = st.sidebar.text_input("Enter your OpenAI API Key:", type="password")
12
- openai.api_key = api_key
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
- # Title and description
15
- st.title("OpenAI Usage Tracker")
16
- st.write("This app retrieves and displays your OpenAI API usage data for a specified date range.")
 
 
 
 
 
 
 
 
 
 
17
 
18
- # Date selection
19
- start_date = st.sidebar.date_input("Select Start Date:", datetime.date.today() - datetime.timedelta(days=30))
20
- end_date = st.sidebar.date_input("Select End Date:", datetime.date.today())
 
 
 
 
 
 
 
 
 
 
21
 
22
- # Ensure end date is not before start date
23
- if start_date > end_date:
24
- st.error("End Date must be after Start Date")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
 
26
- # Function to retrieve usage data
27
- def get_usage_data(api_key, start_date, end_date):
28
  try:
29
- openai.api_key = api_key
30
- usage = openai.api_usage.get(start_date=start_date, end_date=end_date)
31
- usage_data = usage['data']
32
- # Create a DataFrame from the usage data
33
- data = pd.DataFrame(usage_data)
34
- data['date'] = pd.to_datetime(data['date'])
35
- data.set_index('date', inplace=True)
36
- return data[['n_tokens_total']]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  except Exception as e:
38
- st.error(f"An error occurred: {e}")
39
- return None
40
-
41
- # Retrieve and display usage data if API key is provided
42
- if api_key:
43
- usage_data = get_usage_data(api_key, start_date.strftime("%Y-%m-%d"), end_date.strftime("%Y-%m-%d"))
44
-
45
- # Display usage data as a table and line chart
46
- if usage_data is not None:
47
- st.subheader("Usage Data")
48
- st.write("Displaying usage from", start_date, "to", end_date)
49
- st.dataframe(usage_data)
50
-
51
- # Display line chart for usage over time
52
- st.subheader("Token Usage Over Time")
53
- st.line_chart(usage_data['n_tokens_total'])
54
-
55
- # Display statistics
56
- total_usage = usage_data['n_tokens_total'].sum()
57
- avg_usage = usage_data['n_tokens_total'].mean()
58
- st.subheader("Statistics")
59
- st.write(f"Total Token Usage: {total_usage}")
60
- st.write(f"Average Daily Token Usage: {avg_usage:.2f}")
61
- else:
62
- st.info("Enter your API key to view usage data.")
63
- else:
64
- st.warning("Please enter your OpenAI API key to proceed.")
 
1
+ # app.py
2
  import streamlit as st
 
3
  import pandas as pd
4
+ import plotly.express as px
5
+ import plotly.graph_objects as go
6
+ from datetime import datetime, timedelta
7
+ import os
8
+ from typing import Optional, Dict, List
9
+ import requests
10
+ import json
11
+ import altair as alt
12
+ from pathlib import Path
13
+ import base64
14
 
15
+ class OpenAIUsageTracker:
16
+ def __init__(self, api_key: Optional[str] = None):
17
+ """Initialize the OpenAI Usage Tracker."""
18
+ self.api_key = api_key
19
+ if not self.api_key:
20
+ raise ValueError("API key must be provided")
21
+
22
+ self.base_url = "https://api.openai.com/v1/dashboard/billing/usage"
23
+ self.headers = {
24
+ "Authorization": f"Bearer {self.api_key}",
25
+ "Content-Type": "application/json"
26
+ }
27
 
28
+ def get_usage(self, start_date: datetime, end_date: datetime) -> Dict:
29
+ """Get API usage for a specific date range."""
30
+ params = {
31
+ 'start_date': start_date.strftime('%Y-%m-%d'),
32
+ 'end_date': end_date.strftime('%Y-%m-%d')
33
+ }
34
+
35
+ try:
36
+ response = requests.get(
37
+ self.base_url,
38
+ headers=self.headers,
39
+ params=params
40
+ )
41
+ response.raise_for_status()
42
+ return response.json()
43
+ except requests.exceptions.RequestException as e:
44
+ st.error(f"Error fetching usage data: {str(e)}")
45
+ return None
46
 
47
+ def get_subscription_data(self) -> Dict:
48
+ """Get subscription data including total available credits."""
49
+ subscription_url = "https://api.openai.com/v1/dashboard/billing/subscription"
50
+ try:
51
+ response = requests.get(
52
+ subscription_url,
53
+ headers=self.headers
54
+ )
55
+ response.raise_for_status()
56
+ return response.json()
57
+ except requests.exceptions.RequestException as e:
58
+ st.error(f"Error fetching subscription data: {str(e)}")
59
+ return None
60
 
61
+ def create_daily_usage_chart(daily_costs: pd.DataFrame) -> alt.Chart:
62
+ """Create an interactive daily usage chart using Altair."""
63
+ chart = alt.Chart(daily_costs).mark_bar().encode(
64
+ x=alt.X('date:T', title='Date'),
65
+ y=alt.Y('cost:Q', title='Cost ($)'),
66
+ tooltip=['date', 'cost']
67
+ ).properties(
68
+ title='Daily API Usage Cost',
69
+ width=600,
70
+ height=400
71
+ ).interactive()
72
+
73
+ return chart
74
 
75
+ def create_model_usage_chart(model_usage: pd.DataFrame) -> go.Figure:
76
+ """Create a pie chart for model usage distribution."""
77
+ fig = px.pie(
78
+ model_usage,
79
+ values='cost',
80
+ names='model',
81
+ title='Cost Distribution by Model'
82
+ )
83
+ fig.update_traces(textposition='inside', textinfo='percent+label')
84
+ return fig
85
+
86
+ def format_large_number(num: float) -> str:
87
+ """Format large numbers with K/M suffix."""
88
+ if num >= 1_000_000:
89
+ return f"${num/1_000_000:.2f}M"
90
+ elif num >= 1_000:
91
+ return f"${num/1_000:.2f}K"
92
+ return f"${num:.2f}"
93
+
94
+ def export_to_csv(df: pd.DataFrame, filename: str):
95
+ """Generate a CSV download link."""
96
+ csv = df.to_csv(index=False)
97
+ b64 = base64.b64encode(csv.encode()).decode()
98
+ href = f'data:file/csv;base64,{b64}'
99
+ return href
100
+
101
+ def main():
102
+ st.set_page_config(
103
+ page_title="OpenAI API Usage Analytics",
104
+ page_icon="📊",
105
+ layout="wide"
106
+ )
107
+
108
+ # Custom CSS
109
+ st.markdown("""
110
+ <style>
111
+ .stApp {
112
+ max-width: 1200px;
113
+ margin: 0 auto;
114
+ }
115
+ .metric-card {
116
+ background-color: #f0f2f6;
117
+ border-radius: 10px;
118
+ padding: 20px;
119
+ text-align: center;
120
+ }
121
+ .metric-value {
122
+ font-size: 24px;
123
+ font-weight: bold;
124
+ color: #0068c9;
125
+ }
126
+ </style>
127
+ """, unsafe_allow_html=True)
128
+
129
+ st.title("📊 OpenAI API Usage Analytics Dashboard")
130
+
131
+ # Sidebar
132
+ st.sidebar.header("Configuration")
133
+
134
+ # API Key input
135
+ api_key = st.sidebar.text_input("Enter OpenAI API Key", type="password")
136
+ if not api_key:
137
+ st.warning("Please enter your OpenAI API key to continue.")
138
+ st.stop()
139
+
140
+ # Date range selection
141
+ st.sidebar.subheader("Date Range")
142
+ end_date = datetime.now()
143
+ date_ranges = {
144
+ "Last 7 days": 7,
145
+ "Last 30 days": 30,
146
+ "Last 90 days": 90,
147
+ "Custom range": 0
148
+ }
149
+ selected_range = st.sidebar.selectbox("Select time period", list(date_ranges.keys()))
150
+
151
+ if selected_range == "Custom range":
152
+ col1, col2 = st.sidebar.columns(2)
153
+ with col1:
154
+ start_date = st.date_input("Start date", end_date - timedelta(days=30))
155
+ with col2:
156
+ end_date = st.date_input("End date", end_date)
157
+ else:
158
+ days = date_ranges[selected_range]
159
+ start_date = end_date - timedelta(days=days)
160
 
 
 
161
  try:
162
+ tracker = OpenAIUsageTracker(api_key)
163
+
164
+ # Get usage data
165
+ usage_data = tracker.get_usage(start_date, end_date)
166
+ subscription_data = tracker.get_subscription_data()
167
+
168
+ if usage_data and subscription_data:
169
+ # Process data
170
+ daily_costs = pd.DataFrame(usage_data.get('daily_costs', []))
171
+ total_usage = usage_data.get('total_usage', 0) / 100 # Convert to dollars
172
+
173
+ # Create metrics row
174
+ col1, col2, col3, col4 = st.columns(4)
175
+
176
+ with col1:
177
+ st.markdown("""
178
+ <div class="metric-card">
179
+ <h3>Total Cost</h3>
180
+ <div class="metric-value">{}</div>
181
+ </div>
182
+ """.format(format_large_number(total_usage)), unsafe_allow_html=True)
183
+
184
+ with col2:
185
+ remaining_credits = subscription_data.get('hard_limit_usd', 0)
186
+ st.markdown("""
187
+ <div class="metric-card">
188
+ <h3>Available Credits</h3>
189
+ <div class="metric-value">{}</div>
190
+ </div>
191
+ """.format(format_large_number(remaining_credits)), unsafe_allow_html=True)
192
+
193
+ with col3:
194
+ daily_avg = total_usage / len(daily_costs) if len(daily_costs) > 0 else 0
195
+ st.markdown("""
196
+ <div class="metric-card">
197
+ <h3>Daily Average</h3>
198
+ <div class="metric-value">${:.2f}</div>
199
+ </div>
200
+ """.format(daily_avg), unsafe_allow_html=True)
201
+
202
+ with col4:
203
+ projected_monthly = daily_avg * 30
204
+ st.markdown("""
205
+ <div class="metric-card">
206
+ <h3>Projected Monthly</h3>
207
+ <div class="metric-value">{}</div>
208
+ </div>
209
+ """.format(format_large_number(projected_monthly)), unsafe_allow_html=True)
210
+
211
+ # Process daily costs data
212
+ if not daily_costs.empty:
213
+ daily_costs['timestamp'] = pd.to_datetime(daily_costs['timestamp'])
214
+ daily_costs['date'] = daily_costs['timestamp'].dt.date
215
+ daily_costs['cost'] = daily_costs.apply(
216
+ lambda x: sum(item['cost'] for item in x['line_items']) / 100,
217
+ axis=1
218
+ )
219
+
220
+ # Create model usage DataFrame
221
+ model_data = []
222
+ for _, row in daily_costs.iterrows():
223
+ for item in row['line_items']:
224
+ model_data.append({
225
+ 'model': item['name'],
226
+ 'cost': item['cost'] / 100
227
+ })
228
+ model_usage = pd.DataFrame(model_data)
229
+ model_usage = model_usage.groupby('model')['cost'].sum().reset_index()
230
+
231
+ # Display charts
232
+ col1, col2 = st.columns(2)
233
+
234
+ with col1:
235
+ st.altair_chart(create_daily_usage_chart(daily_costs), use_container_width=True)
236
+
237
+ with col2:
238
+ st.plotly_chart(create_model_usage_chart(model_usage), use_container_width=True)
239
+
240
+ # Display detailed data tables
241
+ st.subheader("Detailed Usage Data")
242
+ tab1, tab2 = st.tabs(["Daily Usage", "Model Distribution"])
243
+
244
+ with tab1:
245
+ daily_table = daily_costs[['date', 'cost']].copy()
246
+ st.dataframe(daily_table, use_container_width=True)
247
+
248
+ # Download button for daily usage
249
+ st.download_button(
250
+ label="Download Daily Usage Data",
251
+ data=daily_table.to_csv(index=False),
252
+ file_name="daily_usage.csv",
253
+ mime="text/csv"
254
+ )
255
+
256
+ with tab2:
257
+ st.dataframe(model_usage, use_container_width=True)
258
+
259
+ # Download button for model usage
260
+ st.download_button(
261
+ label="Download Model Usage Data",
262
+ data=model_usage.to_csv(index=False),
263
+ file_name="model_usage.csv",
264
+ mime="text/csv"
265
+ )
266
+
267
+ else:
268
+ st.info("No usage data available for the selected period.")
269
+
270
+ else:
271
+ st.error("Failed to fetch data. Please check your API key and try again.")
272
+
273
  except Exception as e:
274
+ st.error(f"An error occurred: {str(e)}")
275
+
276
+ if __name__ == "__main__":
277
+ main()