pagezyhf HF Staff commited on
Commit
deb3471
·
verified ·
1 Parent(s): 7339aa1

Upload 7 files

Browse files
Dockerfile ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.9-slim
2
+
3
+ WORKDIR /app
4
+
5
+ # Install system dependencies
6
+ RUN apt-get update && apt-get install -y \
7
+ build-essential \
8
+ && rm -rf /var/lib/apt/lists/*
9
+
10
+ # Copy requirements first to leverage Docker cache
11
+ COPY requirements.txt .
12
+ RUN pip install --no-cache-dir -r requirements.txt
13
+
14
+ # Copy the rest of the application
15
+ COPY app/ ./app/
16
+
17
+ # Set environment variables
18
+ ENV PORT=7860
19
+ ENV HOST=0.0.0.0
20
+
21
+ # Expose the port
22
+ EXPOSE 7860
23
+
24
+ # Command to run the application
25
+ CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "7860"]
app/main.py ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, Request
2
+ from fastapi.templating import Jinja2Templates
3
+ from fastapi.staticfiles import StaticFiles
4
+ from fastapi.responses import JSONResponse
5
+ from optimum.neuron import utils
6
+
7
+ app = FastAPI()
8
+
9
+ # Mount static files and templates
10
+ app.mount("/static", StaticFiles(directory="app/static"), name="static")
11
+ templates = Jinja2Templates(directory="app/templates")
12
+
13
+ @app.get("/health")
14
+ async def health_check():
15
+ return {"status": "healthy"}
16
+
17
+ @app.get("/")
18
+ async def home(request: Request):
19
+ return templates.TemplateResponse("index.html", {"request": request})
20
+
21
+ @app.get("/api/models")
22
+ async def get_model_list():
23
+ try:
24
+ # Get actual model configurations
25
+ model_list = utils.get_hub_cached_models(mode="inference")
26
+
27
+ # Transform the data into the expected format
28
+ models = []
29
+ seen_models = set()
30
+
31
+ for model_tuple in model_list:
32
+ architecture, org, model_id = model_tuple
33
+ full_model_id = f"{org}/{model_id}"
34
+
35
+ if full_model_id not in seen_models:
36
+ models.append({
37
+ "id": full_model_id,
38
+ "name": full_model_id, # This will be used as the title
39
+ "type": architecture # This will be used as the subtitle
40
+ })
41
+ seen_models.add(full_model_id)
42
+
43
+ return JSONResponse(content=models)
44
+ except Exception as e:
45
+ return JSONResponse(
46
+ status_code=500,
47
+ content={"error": str(e)}
48
+ )
49
+
50
+ @app.get("/api/models/{model_id}")
51
+ async def get_model_info_endpoint(model_id: str):
52
+ try:
53
+ configs = utils.get_hub_cached_entries(model_id=model_id, mode="inference")
54
+ # Return empty list if no configurations found
55
+ if not configs:
56
+ return JSONResponse(content={"configurations": []})
57
+ return JSONResponse(content={"configurations": configs})
58
+ except Exception as e:
59
+ return JSONResponse(
60
+ status_code=500,
61
+ content={"error": str(e)}
62
+ )
app/static/css/styles.css ADDED
@@ -0,0 +1,328 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ :root {
2
+ --primary: #4a6bdf;
3
+ --primary-dark: #3a56b2;
4
+ --secondary: #f8f9fa;
5
+ --text: #333;
6
+ --text-light: #555;
7
+ --border: #e0e0e0;
8
+ --card-shadow: 0 4px 8px rgba(0,0,0,0.1);
9
+ --transition: all 0.3s ease;
10
+ }
11
+
12
+ * {
13
+ margin: 0;
14
+ padding: 0;
15
+ box-sizing: border-box;
16
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
17
+ }
18
+
19
+ body {
20
+ background-color: #f5f7fa;
21
+ color: var(--text);
22
+ line-height: 1.6;
23
+ }
24
+
25
+ .container {
26
+ max-width: 1200px;
27
+ margin: 0 auto;
28
+ padding: 20px;
29
+ }
30
+
31
+ /* Component 1: Search Section */
32
+ .search-section {
33
+ background: white;
34
+ border-radius: 10px;
35
+ padding: 30px;
36
+ margin-bottom: 30px;
37
+ box-shadow: var(--card-shadow);
38
+ text-align: center;
39
+ transition: var(--transition);
40
+ }
41
+
42
+ .search-section:hover {
43
+ box-shadow: 0 10px 20px rgba(0,0,0,0.15);
44
+ }
45
+
46
+ .search-section h1 {
47
+ font-size: 2.5rem;
48
+ margin-bottom: 10px;
49
+ color: var(--primary);
50
+ }
51
+
52
+ .search-section p {
53
+ font-size: 1.1rem;
54
+ color: var(--text-light);
55
+ margin-bottom: 25px;
56
+ }
57
+
58
+ .search-box {
59
+ display: flex;
60
+ max-width: 600px;
61
+ margin: 0 auto;
62
+ }
63
+
64
+ .search-box input {
65
+ flex: 1;
66
+ padding: 15px 20px;
67
+ border: 1px solid var(--border);
68
+ border-radius: 50px 0 0 50px;
69
+ font-size: 1rem;
70
+ outline: none;
71
+ transition: var(--transition);
72
+ }
73
+
74
+ .search-box input:focus {
75
+ border-color: var(--primary);
76
+ }
77
+
78
+ .search-box button {
79
+ padding: 0 25px;
80
+ background-color: var(--primary);
81
+ color: white;
82
+ border: none;
83
+ border-radius: 0 50px 50px 0;
84
+ cursor: pointer;
85
+ font-size: 1rem;
86
+ transition: var(--transition);
87
+ }
88
+
89
+ .search-box button:hover {
90
+ background-color: var(--primary-dark);
91
+ }
92
+
93
+ /* Component 2: Model Grid */
94
+ .model-grid-section {
95
+ background: white;
96
+ border-radius: 10px;
97
+ padding: 30px;
98
+ margin-bottom: 30px;
99
+ box-shadow: var(--card-shadow);
100
+ }
101
+
102
+ .section-header {
103
+ display: flex;
104
+ justify-content: space-between;
105
+ align-items: center;
106
+ margin-bottom: 25px;
107
+ }
108
+
109
+ .section-header h2 {
110
+ font-size: 1.8rem;
111
+ color: var(--primary);
112
+ }
113
+
114
+ .model-grid {
115
+ display: grid;
116
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
117
+ gap: 20px;
118
+ margin-bottom: 30px;
119
+ }
120
+
121
+ .model-card {
122
+ background: white;
123
+ border: 1px solid var(--border);
124
+ border-radius: 8px;
125
+ padding: 20px;
126
+ transition: var(--transition);
127
+ cursor: pointer;
128
+ position: relative;
129
+ overflow: hidden;
130
+ }
131
+
132
+ .model-card:hover {
133
+ transform: translateY(-5px);
134
+ box-shadow: var(--card-shadow);
135
+ border-color: var(--primary);
136
+ }
137
+
138
+ .model-card h3 {
139
+ font-size: 1.3rem;
140
+ margin-bottom: 8px;
141
+ color: var(--primary);
142
+ font-weight: 600;
143
+ }
144
+
145
+ .model-card .subtitle {
146
+ color: var(--text-light);
147
+ font-size: 0.9rem;
148
+ margin-bottom: 12px;
149
+ font-weight: 500;
150
+ }
151
+
152
+ .model-card p {
153
+ color: var(--text-light);
154
+ font-size: 0.9rem;
155
+ margin-bottom: 5px;
156
+ }
157
+
158
+ .model-card .meta {
159
+ display: flex;
160
+ justify-content: space-between;
161
+ margin-top: 15px;
162
+ font-size: 0.85rem;
163
+ color: var(--text-light);
164
+ border-top: 1px solid var(--border);
165
+ padding-top: 12px;
166
+ }
167
+
168
+ .pagination {
169
+ display: flex;
170
+ justify-content: center;
171
+ gap: 10px;
172
+ margin-top: 20px;
173
+ }
174
+
175
+ .pagination button {
176
+ padding: 8px 16px;
177
+ background-color: var(--primary);
178
+ color: white;
179
+ border: none;
180
+ border-radius: 5px;
181
+ cursor: pointer;
182
+ transition: var(--transition);
183
+ }
184
+
185
+ .pagination button:disabled {
186
+ background-color: #ccc;
187
+ cursor: not-allowed;
188
+ }
189
+
190
+ .pagination button:hover:not(:disabled) {
191
+ background-color: var(--primary-dark);
192
+ }
193
+
194
+ /* Component 3: Configuration Details */
195
+ .config-section {
196
+ background: white;
197
+ border-radius: 10px;
198
+ padding: 30px;
199
+ margin-bottom: 30px;
200
+ box-shadow: var(--card-shadow);
201
+ display: none;
202
+ animation: fadeIn 0.5s ease;
203
+ }
204
+
205
+ @keyframes fadeIn {
206
+ from { opacity: 0; transform: translateY(20px); }
207
+ to { opacity: 1; transform: translateY(0); }
208
+ }
209
+
210
+ .config-section.active {
211
+ display: block;
212
+ }
213
+
214
+ .config-header {
215
+ display: flex;
216
+ justify-content: space-between;
217
+ align-items: center;
218
+ margin-bottom: 20px;
219
+ }
220
+
221
+ .config-header h3 {
222
+ font-size: 1.5rem;
223
+ color: var(--primary);
224
+ }
225
+
226
+ .config-list {
227
+ display: grid;
228
+ gap: 15px;
229
+ }
230
+
231
+ .config-card {
232
+ border: 1px solid var(--border);
233
+ border-radius: 8px;
234
+ padding: 20px;
235
+ }
236
+
237
+ .config-card h4 {
238
+ font-size: 1.1rem;
239
+ margin-bottom: 10px;
240
+ color: var(--primary-dark);
241
+ }
242
+
243
+ .config-details {
244
+ display: flex;
245
+ flex-direction: column;
246
+ gap: 20px;
247
+ }
248
+
249
+ .config-group {
250
+ background: var(--secondary);
251
+ border-radius: 6px;
252
+ padding: 15px;
253
+ }
254
+
255
+ .config-group h5 {
256
+ color: var(--primary);
257
+ font-size: 1rem;
258
+ margin-bottom: 12px;
259
+ font-weight: 600;
260
+ }
261
+
262
+ .detail-item {
263
+ display: flex;
264
+ justify-content: space-between;
265
+ margin-bottom: 8px;
266
+ padding: 4px 0;
267
+ border-bottom: 1px solid var(--border);
268
+ }
269
+
270
+ .detail-item:last-child {
271
+ border-bottom: none;
272
+ }
273
+
274
+ .detail-item strong {
275
+ color: var(--text);
276
+ font-weight: 500;
277
+ }
278
+
279
+ .detail-item span {
280
+ color: var(--text-light);
281
+ }
282
+
283
+ /* Empty State */
284
+ .empty-state {
285
+ text-align: center;
286
+ padding: 40px 20px;
287
+ color: var(--text-light);
288
+ }
289
+
290
+ .empty-state i {
291
+ font-size: 3rem;
292
+ margin-bottom: 20px;
293
+ color: var(--border);
294
+ }
295
+
296
+ /* Responsive */
297
+ @media (max-width: 768px) {
298
+ .model-grid {
299
+ grid-template-columns: 1fr;
300
+ }
301
+
302
+ .search-section h1 {
303
+ font-size: 2rem;
304
+ }
305
+
306
+ .section-header {
307
+ flex-direction: column;
308
+ align-items: flex-start;
309
+ gap: 15px;
310
+ }
311
+ }
312
+
313
+ /* Loading spinner */
314
+ .spinner {
315
+ display: none;
316
+ width: 40px;
317
+ height: 40px;
318
+ margin: 20px auto;
319
+ border: 4px solid rgba(0, 0, 0, 0.1);
320
+ border-radius: 50%;
321
+ border-top: 4px solid var(--primary);
322
+ animation: spin 1s linear infinite;
323
+ }
324
+
325
+ @keyframes spin {
326
+ 0% { transform: rotate(0deg); }
327
+ 100% { transform: rotate(360deg); }
328
+ }
app/static/js/main.js ADDED
@@ -0,0 +1,217 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ let models = [];
2
+ let currentPage = 1;
3
+ const modelsPerPage = 10;
4
+
5
+ // DOM Elements
6
+ const modelGrid = document.getElementById('modelGrid');
7
+ const configSection = document.getElementById('configSection');
8
+ const configList = document.getElementById('configList');
9
+ const selectedModelId = document.getElementById('selectedModelId');
10
+ const configModelTitle = document.getElementById('configModelTitle');
11
+ const noConfig = document.getElementById('noConfig');
12
+ const prevPageBtn = document.getElementById('prevPage');
13
+ const nextPageBtn = document.getElementById('nextPage');
14
+ const pageInfo = document.getElementById('pageInfo');
15
+ const searchButton = document.getElementById('searchButton');
16
+ const modelIdSearch = document.getElementById('modelIdSearch');
17
+ const closeConfig = document.getElementById('closeConfig');
18
+ const loadingModels = document.getElementById('loadingModels');
19
+ const loadingConfig = document.getElementById('loadingConfig');
20
+
21
+ // Fetch models from the API
22
+ async function fetchModels() {
23
+ loadingModels.style.display = 'block';
24
+
25
+ try {
26
+ const response = await fetch('/api/models');
27
+ const data = await response.json();
28
+ models = data;
29
+ renderModels();
30
+ updatePagination();
31
+ } catch (error) {
32
+ console.error('Error fetching models:', error);
33
+ alert('Failed to fetch models. Please try again later.');
34
+ } finally {
35
+ loadingModels.style.display = 'none';
36
+ }
37
+ }
38
+
39
+ // Fetch configurations for a specific model
40
+ async function showModelConfigurations(modelId) {
41
+ configSection.style.display = 'block';
42
+ selectedModelId.textContent = modelId;
43
+ configList.innerHTML = '';
44
+ loadingConfig.style.display = 'block';
45
+ noConfig.style.display = 'none';
46
+
47
+ try {
48
+ const response = await fetch(`/api/models/${modelId}`);
49
+ if (!response.ok) {
50
+ throw new Error(`HTTP error! status: ${response.status}`);
51
+ }
52
+ const data = await response.json();
53
+ const configs = data.configurations;
54
+
55
+ if (!configs || configs.length === 0) {
56
+ noConfig.style.display = 'block';
57
+ configList.innerHTML = '';
58
+ } else {
59
+ configs.forEach((config, idx) => {
60
+ const configCard = document.createElement('div');
61
+ configCard.className = 'config-card';
62
+
63
+ // Group related configurations
64
+ const groups = {
65
+ 'Model Information': {
66
+ 'checkpoint_id': config.checkpoint_id,
67
+ 'checkpoint_revision': config.checkpoint_revision,
68
+ 'task': config.task
69
+ },
70
+ 'Hardware Configuration': {
71
+ 'num_cores': config.num_cores,
72
+ 'auto_cast_type': config.auto_cast_type
73
+ },
74
+ 'Compiler Settings': {
75
+ 'compiler_type': config.compiler_type,
76
+ 'compiler_version': config.compiler_version
77
+ },
78
+ 'Model Parameters': {
79
+ 'batch_size': config.batch_size,
80
+ 'sequence_length': config.sequence_length
81
+ }
82
+ };
83
+
84
+ let detailsHtml = '';
85
+ for (const [groupName, groupFields] of Object.entries(groups)) {
86
+ detailsHtml += `
87
+ <div class="config-group">
88
+ <h5>${groupName}</h5>
89
+ ${Object.entries(groupFields)
90
+ .map(([key, value]) => `
91
+ <div class="detail-item">
92
+ <strong>${key.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase())}:</strong>
93
+ <span>${value}</span>
94
+ </div>
95
+ `).join('')}
96
+ </div>
97
+ `;
98
+ }
99
+
100
+ configCard.innerHTML = `
101
+ <h4>Configuration ${idx + 1}</h4>
102
+ <div class="config-details">
103
+ ${detailsHtml}
104
+ </div>
105
+ `;
106
+ configList.appendChild(configCard);
107
+ });
108
+ }
109
+ } catch (error) {
110
+ console.error('Error fetching configurations:', error);
111
+ noConfig.style.display = 'block';
112
+ noConfig.innerHTML = `
113
+ <i class="fas fa-exclamation-circle"></i>
114
+ <h4>Error loading configurations</h4>
115
+ <p>Failed to fetch configurations. Please try again later.</p>
116
+ `;
117
+ configList.innerHTML = '';
118
+ } finally {
119
+ loadingConfig.style.display = 'none';
120
+ }
121
+ }
122
+
123
+ // Initialize the page
124
+ document.addEventListener('DOMContentLoaded', () => {
125
+ fetchModels();
126
+
127
+ // Add event listeners
128
+ document.getElementById('searchButton').addEventListener('click', handleSearch);
129
+ document.getElementById('prevPage').addEventListener('click', goToPrevPage);
130
+ document.getElementById('nextPage').addEventListener('click', goToNextPage);
131
+ document.getElementById('closeConfig').addEventListener('click', () => {
132
+ document.getElementById('configSection').style.display = 'none';
133
+ });
134
+ });
135
+
136
+ function renderModels() {
137
+ loadingModels.style.display = 'block';
138
+ modelGrid.innerHTML = '';
139
+
140
+ // Simulate API delay
141
+ setTimeout(() => {
142
+ const startIdx = (currentPage - 1) * modelsPerPage;
143
+ const endIdx = startIdx + modelsPerPage;
144
+ const paginatedModels = models.slice(startIdx, endIdx);
145
+
146
+ if (paginatedModels.length === 0) {
147
+ modelGrid.innerHTML = `
148
+ <div class="empty-state">
149
+ <i class="fas fa-exclamation-circle"></i>
150
+ <h4>No models available</h4>
151
+ <p>There are currently no models to display.</p>
152
+ </div>
153
+ `;
154
+ } else {
155
+ paginatedModels.forEach(model => {
156
+ const modelCard = document.createElement('div');
157
+ modelCard.className = 'model-card';
158
+ modelCard.innerHTML = `
159
+ <h3>${model.name}</h3>
160
+ <p class="subtitle">Architecture: ${model.type}</p>
161
+ `;
162
+ modelCard.addEventListener('click', () => showModelConfigurations(model.id));
163
+ modelGrid.appendChild(modelCard);
164
+ });
165
+ }
166
+
167
+ loadingModels.style.display = 'none';
168
+ }, 500);
169
+ }
170
+
171
+ function getModelIcon(type) {
172
+ switch(type.toLowerCase()) {
173
+ case 'regression': return 'fa-chart-line';
174
+ case 'classification': return 'fa-tags';
175
+ case 'ensemble': return 'fa-layer-group';
176
+ case 'forecasting': return 'fa-calendar-alt';
177
+ case 'clustering': return 'fa-object-group';
178
+ default: return 'fa-cube';
179
+ }
180
+ }
181
+
182
+ function handleSearch() {
183
+ const searchTerm = modelIdSearch.value.trim();
184
+ if (!searchTerm) {
185
+ alert('Please enter a Model ID');
186
+ return;
187
+ }
188
+
189
+ // Show configurations for the searched model
190
+ showModelConfigurations(searchTerm);
191
+ }
192
+
193
+ function updatePagination() {
194
+ const totalPages = Math.ceil(models.length / modelsPerPage);
195
+
196
+ prevPageBtn.disabled = currentPage <= 1;
197
+ nextPageBtn.disabled = currentPage >= totalPages;
198
+ pageInfo.textContent = `Page ${currentPage} of ${totalPages}`;
199
+ }
200
+
201
+ function goToPrevPage() {
202
+ if (currentPage > 1) {
203
+ currentPage--;
204
+ renderModels();
205
+ updatePagination();
206
+ }
207
+ }
208
+
209
+ function goToNextPage() {
210
+ const totalPages = Math.ceil(models.length / modelsPerPage);
211
+
212
+ if (currentPage < totalPages) {
213
+ currentPage++;
214
+ renderModels();
215
+ updatePagination();
216
+ }
217
+ }
app/templates/index.html ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Model Configuration Explorer</title>
7
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
8
+ <link rel="stylesheet" href="{{ url_for('static', path='/css/styles.css') }}">
9
+ </head>
10
+ <body>
11
+ <div class="container">
12
+ <!-- Component 1: Search Section -->
13
+ <section class="search-section">
14
+ <h1>Model Configuration Explorer</h1>
15
+ <p>Search for model configurations by entering a Model ID below</p>
16
+ <div class="search-box">
17
+ <input type="text" id="modelIdSearch" placeholder="Enter Model ID (e.g., MODEL123)">
18
+ <button id="searchButton"><i class="fas fa-search"></i> Search</button>
19
+ </div>
20
+ </section>
21
+
22
+ <!-- Component 2: Model Grid -->
23
+ <section class="model-grid-section">
24
+ <div class="section-header">
25
+ <h2><i class="fas fa-list"></i> Available Models</h2>
26
+ </div>
27
+
28
+ <div id="modelGrid" class="model-grid">
29
+ <!-- Models will be populated here -->
30
+ </div>
31
+
32
+ <div id="loadingModels" class="spinner"></div>
33
+
34
+ <div class="pagination">
35
+ <button id="prevPage" disabled><i class="fas fa-chevron-left"></i> Previous</button>
36
+ <span id="pageInfo">Page 1 of 1</span>
37
+ <button id="nextPage" disabled>Next <i class="fas fa-chevron-right"></i></button>
38
+ </div>
39
+ </section>
40
+
41
+ <!-- Component 3: Configuration Details -->
42
+ <section id="configSection" class="config-section">
43
+ <div class="config-header">
44
+ <h3 id="configModelTitle">Configurations for <span id="selectedModelId"></span></h3>
45
+ <button id="closeConfig" class="close-btn"><i class="fas fa-times"></i></button>
46
+ </div>
47
+
48
+ <div id="configList" class="config-list">
49
+ <!-- Configurations will be populated here -->
50
+ </div>
51
+
52
+ <div id="loadingConfig" class="spinner"></div>
53
+
54
+ <div id="noConfig" class="empty-state">
55
+ <i class="far fa-folder-open"></i>
56
+ <h4>No configurations found</h4>
57
+ <p>This model has no saved configurations yet.</p>
58
+ </div>
59
+ </section>
60
+ </div>
61
+
62
+ <script src="{{ url_for('static', path='/js/main.js') }}"></script>
63
+ </body>
64
+ </html>
docker-compose.yml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+ services:
3
+ web:
4
+ build: .
5
+ ports:
6
+ - "7860:7860"
7
+ environment:
8
+ - HF_TOKEN=${HF_TOKEN}
9
+ volumes:
10
+ - ./app:/app/app
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ fastapi==0.104.1
2
+ uvicorn==0.24.0
3
+ jinja2==3.1.2
4
+ optimum-neuron==0.1.0
5
+ python-multipart==0.0.6