more-compute 0.4.4__py3-none-any.whl → 0.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. frontend/app/globals.css +734 -27
  2. frontend/app/layout.tsx +13 -3
  3. frontend/components/Notebook.tsx +2 -14
  4. frontend/components/cell/MonacoCell.tsx +99 -5
  5. frontend/components/layout/Sidebar.tsx +39 -4
  6. frontend/components/panels/ClaudePanel.tsx +461 -0
  7. frontend/components/popups/ComputePopup.tsx +738 -447
  8. frontend/components/popups/FilterPopup.tsx +305 -189
  9. frontend/components/popups/MetricsPopup.tsx +20 -1
  10. frontend/components/popups/ProviderConfigModal.tsx +322 -0
  11. frontend/components/popups/ProviderDropdown.tsx +398 -0
  12. frontend/components/popups/SettingsPopup.tsx +1 -1
  13. frontend/contexts/ClaudeContext.tsx +392 -0
  14. frontend/contexts/PodWebSocketContext.tsx +16 -21
  15. frontend/hooks/useInlineDiff.ts +269 -0
  16. frontend/lib/api.ts +323 -12
  17. frontend/lib/settings.ts +5 -0
  18. frontend/lib/websocket-native.ts +4 -8
  19. frontend/lib/websocket.ts +1 -2
  20. frontend/package-lock.json +733 -36
  21. frontend/package.json +2 -0
  22. frontend/public/assets/icons/providers/lambda_labs.svg +22 -0
  23. frontend/public/assets/icons/providers/prime_intellect.svg +18 -0
  24. frontend/public/assets/icons/providers/runpod.svg +9 -0
  25. frontend/public/assets/icons/providers/vastai.svg +1 -0
  26. frontend/settings.md +54 -0
  27. frontend/tsconfig.tsbuildinfo +1 -0
  28. frontend/types/claude.ts +194 -0
  29. kernel_run.py +13 -0
  30. {more_compute-0.4.4.dist-info → more_compute-0.5.0.dist-info}/METADATA +53 -11
  31. {more_compute-0.4.4.dist-info → more_compute-0.5.0.dist-info}/RECORD +56 -37
  32. {more_compute-0.4.4.dist-info → more_compute-0.5.0.dist-info}/WHEEL +1 -1
  33. morecompute/__init__.py +1 -1
  34. morecompute/__version__.py +1 -1
  35. morecompute/execution/executor.py +24 -67
  36. morecompute/execution/worker.py +6 -72
  37. morecompute/models/api_models.py +62 -0
  38. morecompute/notebook.py +11 -0
  39. morecompute/server.py +641 -133
  40. morecompute/services/claude_service.py +392 -0
  41. morecompute/services/pod_manager.py +168 -67
  42. morecompute/services/pod_monitor.py +67 -39
  43. morecompute/services/prime_intellect.py +0 -4
  44. morecompute/services/providers/__init__.py +92 -0
  45. morecompute/services/providers/base_provider.py +336 -0
  46. morecompute/services/providers/lambda_labs_provider.py +394 -0
  47. morecompute/services/providers/provider_factory.py +194 -0
  48. morecompute/services/providers/runpod_provider.py +504 -0
  49. morecompute/services/providers/vastai_provider.py +407 -0
  50. morecompute/utils/cell_magics.py +0 -3
  51. morecompute/utils/config_util.py +93 -3
  52. morecompute/utils/special_commands.py +5 -32
  53. morecompute/utils/version_check.py +117 -0
  54. frontend/styling_README.md +0 -23
  55. {more_compute-0.4.4.dist-info/licenses → more_compute-0.5.0.dist-info}/LICENSE +0 -0
  56. {more_compute-0.4.4.dist-info → more_compute-0.5.0.dist-info}/entry_points.txt +0 -0
  57. {more_compute-0.4.4.dist-info → more_compute-0.5.0.dist-info}/top_level.txt +0 -0
@@ -7,54 +7,84 @@ interface FilterPopupProps {
7
7
  filters: GpuAvailabilityParams;
8
8
  onFiltersChange: (filters: GpuAvailabilityParams) => void;
9
9
  onApply: () => void;
10
+ providerName?: string;
10
11
  }
11
12
 
13
+ // Common GPU types across providers
12
14
  const GPU_TYPES = [
13
- { value: "H100_80GB", label: "H100 80GB" },
14
- { value: "H200_96GB", label: "H200 96GB" },
15
- { value: "GH200_96GB", label: "GH200 96GB" },
16
- { value: "H200_141GB", label: "H200 141GB" },
17
- { value: "B200_180GB", label: "B200 180GB" },
18
- { value: "A100_80GB", label: "A100 80GB" },
19
- { value: "A100_40GB", label: "A100 40GB" },
20
- { value: "A10_24GB", label: "A10 24GB" },
21
- { value: "A30_24GB", label: "A30 24GB" },
22
- { value: "A40_48GB", label: "A40 48GB" },
23
- { value: "RTX4090_24GB", label: "RTX 4090 24GB" },
24
- { value: "RTX5090_32GB", label: "RTX 5090 32GB" },
25
- { value: "RTX4080_16GB", label: "RTX 4080 16GB" },
26
- { value: "RTX4080Ti_16GB", label: "RTX 4080 Ti 16GB" },
27
- { value: "RTX4070Ti_12GB", label: "RTX 4070 Ti 12GB" },
28
- { value: "RTX3090_24GB", label: "RTX 3090 24GB" },
29
- { value: "RTX3090Ti_24GB", label: "RTX 3090 Ti 24GB" },
30
- { value: "RTX3080_10GB", label: "RTX 3080 10GB" },
31
- { value: "RTX3080Ti_12GB", label: "RTX 3080 Ti 12GB" },
32
- { value: "RTX3070_8GB", label: "RTX 3070 8GB" },
33
- { value: "L40S_48GB", label: "L40S 48GB" },
34
- { value: "L40_48GB", label: "L40 48GB" },
35
- { value: "L4_24GB", label: "L4 24GB" },
36
- { value: "V100_32GB", label: "V100 32GB" },
37
- { value: "V100_16GB", label: "V100 16GB" },
38
- { value: "T4_16GB", label: "T4 16GB" },
39
- { value: "P100_16GB", label: "P100 16GB" },
40
- { value: "A6000_48GB", label: "A6000 48GB" },
41
- { value: "A5000_24GB", label: "A5000 24GB" },
42
- { value: "A4000_16GB", label: "A4000 16GB" },
43
- { value: "RTX6000Ada_48GB", label: "RTX 6000 Ada 48GB" },
44
- { value: "RTX5000Ada_32GB", label: "RTX 5000 Ada 32GB" },
45
- { value: "RTX4000Ada_20GB", label: "RTX 4000 Ada 20GB" },
15
+ { value: "H100", label: "H100" },
16
+ { value: "H200", label: "H200" },
17
+ { value: "A100", label: "A100" },
18
+ { value: "A10", label: "A10" },
19
+ { value: "A30", label: "A30" },
20
+ { value: "A40", label: "A40" },
21
+ { value: "A6000", label: "A6000" },
22
+ { value: "A5000", label: "A5000" },
23
+ { value: "A4000", label: "A4000" },
24
+ { value: "L40S", label: "L40S" },
25
+ { value: "L40", label: "L40" },
26
+ { value: "L4", label: "L4" },
27
+ { value: "RTX 4090", label: "RTX 4090" },
28
+ { value: "RTX 4080", label: "RTX 4080" },
29
+ { value: "RTX 4070", label: "RTX 4070" },
30
+ { value: "RTX 3090", label: "RTX 3090" },
31
+ { value: "RTX 3080", label: "RTX 3080" },
32
+ { value: "RTX 3070", label: "RTX 3070" },
33
+ { value: "RTX 6000", label: "RTX 6000 Ada" },
34
+ { value: "RTX 5000", label: "RTX 5000 Ada" },
35
+ { value: "RTX 4000", label: "RTX 4000 Ada" },
36
+ { value: "V100", label: "V100" },
37
+ { value: "T4", label: "T4" },
38
+ { value: "P100", label: "P100" },
46
39
  ];
47
40
 
41
+ // Provider-specific filter categories
42
+ type FilterCategory = "gpu_type" | "gpu_count" | "cloud_type" | "verified" | "reliability";
43
+
44
+ const getFilterCategoriesForProvider = (provider: string): { value: FilterCategory; label: string }[] => {
45
+ const common = [
46
+ { value: "gpu_type" as FilterCategory, label: "GPU Type" },
47
+ { value: "gpu_count" as FilterCategory, label: "GPU Count" },
48
+ ];
49
+
50
+ switch (provider) {
51
+ case "runpod":
52
+ return [
53
+ ...common,
54
+ { value: "cloud_type" as FilterCategory, label: "Cloud Type" },
55
+ ];
56
+ case "vastai":
57
+ return [
58
+ ...common,
59
+ { value: "verified" as FilterCategory, label: "Verified" },
60
+ { value: "reliability" as FilterCategory, label: "Reliability" },
61
+ ];
62
+ case "lambda_labs":
63
+ default:
64
+ return common;
65
+ }
66
+ };
67
+
48
68
  const FilterPopup: React.FC<FilterPopupProps> = ({
49
69
  isOpen,
50
70
  onClose,
51
71
  filters,
52
72
  onFiltersChange,
53
73
  onApply,
74
+ providerName = "runpod",
54
75
  }) => {
55
- const [filterCategory, setFilterCategory] = React.useState<string>("gpu_type");
76
+ const categories = getFilterCategoriesForProvider(providerName);
77
+ const [filterCategory, setFilterCategory] = React.useState<FilterCategory>(categories[0]?.value || "gpu_type");
56
78
  const [filterSearch, setFilterSearch] = React.useState<string>("");
57
79
 
80
+ // Reset category when provider changes
81
+ React.useEffect(() => {
82
+ const validCategories = getFilterCategoriesForProvider(providerName);
83
+ if (!validCategories.find(c => c.value === filterCategory)) {
84
+ setFilterCategory(validCategories[0]?.value || "gpu_type");
85
+ }
86
+ }, [providerName, filterCategory]);
87
+
58
88
  if (!isOpen) return null;
59
89
 
60
90
  const handleClearAll = () => {
@@ -62,6 +92,17 @@ const FilterPopup: React.FC<FilterPopupProps> = ({
62
92
  setFilterSearch("");
63
93
  };
64
94
 
95
+ // Get the count of active filters
96
+ const getActiveFilterCount = (): number => {
97
+ let count = 0;
98
+ if (filters.gpu_type) count++;
99
+ if (filters.gpu_count) count++;
100
+ if (filters.secure_cloud !== undefined || filters.community_cloud !== undefined) count++;
101
+ if (filters.verified !== undefined) count++;
102
+ if (filters.min_reliability !== undefined) count++;
103
+ return count;
104
+ };
105
+
65
106
  return (
66
107
  <>
67
108
  {/* Backdrop */}
@@ -113,7 +154,7 @@ const FilterPopup: React.FC<FilterPopupProps> = ({
113
154
  color: "var(--mc-text-color)",
114
155
  }}
115
156
  >
116
- Filter
157
+ Filter {getActiveFilterCount() > 0 && `(${getActiveFilterCount()})`}
117
158
  </h4>
118
159
  <button
119
160
  onClick={handleClearAll}
@@ -135,7 +176,7 @@ const FilterPopup: React.FC<FilterPopupProps> = ({
135
176
  <select
136
177
  value={filterCategory}
137
178
  onChange={(e) => {
138
- setFilterCategory(e.target.value);
179
+ setFilterCategory(e.target.value as FilterCategory);
139
180
  setFilterSearch("");
140
181
  }}
141
182
  style={{
@@ -150,30 +191,33 @@ const FilterPopup: React.FC<FilterPopupProps> = ({
150
191
  cursor: "pointer",
151
192
  }}
152
193
  >
153
- <option value="gpu_type">GPU Type</option>
154
- <option value="gpu_count">GPU Count</option>
155
- <option value="security">Security</option>
156
- <option value="socket">Socket</option>
194
+ {categories.map((cat) => (
195
+ <option key={cat.value} value={cat.value}>
196
+ {cat.label}
197
+ </option>
198
+ ))}
157
199
  </select>
158
200
 
159
- {/* Search within category */}
160
- <input
161
- type="text"
162
- placeholder="Search"
163
- value={filterSearch}
164
- onChange={(e) => setFilterSearch(e.target.value)}
165
- style={{
166
- width: "100%",
167
- padding: "8px 10px",
168
- borderRadius: "6px",
169
- border: "1px solid var(--mc-border)",
170
- backgroundColor: "var(--mc-input-background)",
171
- color: "var(--mc-text-color)",
172
- fontSize: "12px",
173
- marginBottom: "12px",
174
- boxSizing: "border-box",
175
- }}
176
- />
201
+ {/* Search within category (only for GPU type) */}
202
+ {filterCategory === "gpu_type" && (
203
+ <input
204
+ type="text"
205
+ placeholder="Search GPU types..."
206
+ value={filterSearch}
207
+ onChange={(e) => setFilterSearch(e.target.value)}
208
+ style={{
209
+ width: "100%",
210
+ padding: "8px 10px",
211
+ borderRadius: "6px",
212
+ border: "1px solid var(--mc-border)",
213
+ backgroundColor: "var(--mc-input-background)",
214
+ color: "var(--mc-text-color)",
215
+ fontSize: "12px",
216
+ marginBottom: "12px",
217
+ boxSizing: "border-box",
218
+ }}
219
+ />
220
+ )}
177
221
 
178
222
  {/* Options List */}
179
223
  <div
@@ -187,8 +231,40 @@ const FilterPopup: React.FC<FilterPopupProps> = ({
187
231
  padding: "4px",
188
232
  }}
189
233
  >
234
+ {/* GPU Type Filter - Universal */}
190
235
  {filterCategory === "gpu_type" && (
191
236
  <>
237
+ <label
238
+ style={{
239
+ display: "flex",
240
+ alignItems: "center",
241
+ padding: "8px 6px",
242
+ cursor: "pointer",
243
+ fontSize: "12px",
244
+ color: "var(--mc-text-color)",
245
+ borderRadius: "4px",
246
+ transition: "background-color 0.15s",
247
+ }}
248
+ onMouseEnter={(e) =>
249
+ (e.currentTarget.style.backgroundColor = "var(--mc-secondary)")
250
+ }
251
+ onMouseLeave={(e) =>
252
+ (e.currentTarget.style.backgroundColor = "transparent")
253
+ }
254
+ >
255
+ <input
256
+ type="radio"
257
+ checked={!filters.gpu_type}
258
+ onChange={() =>
259
+ onFiltersChange({
260
+ ...filters,
261
+ gpu_type: undefined,
262
+ })
263
+ }
264
+ style={{ marginRight: "10px", cursor: "pointer" }}
265
+ />
266
+ All GPUs
267
+ </label>
192
268
  {GPU_TYPES.filter((gpu) =>
193
269
  gpu.label.toLowerCase().includes(filterSearch.toLowerCase())
194
270
  ).map((gpu) => (
@@ -228,155 +304,195 @@ const FilterPopup: React.FC<FilterPopupProps> = ({
228
304
  </>
229
305
  )}
230
306
 
307
+ {/* GPU Count Filter - Universal */}
231
308
  {filterCategory === "gpu_count" && (
232
309
  <>
233
310
  {[
234
- { value: "", label: "Any" },
235
- { value: "1", label: "1 GPU" },
236
- { value: "2", label: "2 GPUs" },
237
- { value: "4", label: "4 GPUs" },
238
- { value: "8", label: "8 GPUs" },
239
- ]
240
- .filter((option) =>
241
- option.label.toLowerCase().includes(filterSearch.toLowerCase())
242
- )
243
- .map((option) => (
244
- <label
245
- key={option.value}
246
- style={{
247
- display: "flex",
248
- alignItems: "center",
249
- padding: "8px 6px",
250
- cursor: "pointer",
251
- fontSize: "12px",
252
- color: "var(--mc-text-color)",
253
- borderRadius: "4px",
254
- transition: "background-color 0.15s",
255
- }}
256
- onMouseEnter={(e) =>
257
- (e.currentTarget.style.backgroundColor = "var(--mc-secondary)")
258
- }
259
- onMouseLeave={(e) =>
260
- (e.currentTarget.style.backgroundColor = "transparent")
311
+ { value: undefined, label: "Any" },
312
+ { value: 1, label: "1 GPU" },
313
+ { value: 2, label: "2 GPUs" },
314
+ { value: 4, label: "4 GPUs" },
315
+ { value: 8, label: "8 GPUs" },
316
+ ].map((option) => (
317
+ <label
318
+ key={option.label}
319
+ style={{
320
+ display: "flex",
321
+ alignItems: "center",
322
+ padding: "8px 6px",
323
+ cursor: "pointer",
324
+ fontSize: "12px",
325
+ color: "var(--mc-text-color)",
326
+ borderRadius: "4px",
327
+ transition: "background-color 0.15s",
328
+ }}
329
+ onMouseEnter={(e) =>
330
+ (e.currentTarget.style.backgroundColor = "var(--mc-secondary)")
331
+ }
332
+ onMouseLeave={(e) =>
333
+ (e.currentTarget.style.backgroundColor = "transparent")
334
+ }
335
+ >
336
+ <input
337
+ type="radio"
338
+ checked={filters.gpu_count === option.value}
339
+ onChange={() =>
340
+ onFiltersChange({
341
+ ...filters,
342
+ gpu_count: option.value,
343
+ })
261
344
  }
262
- >
263
- <input
264
- type="radio"
265
- checked={
266
- (filters.gpu_count?.toString() || "") === option.value
267
- }
268
- onChange={() =>
269
- onFiltersChange({
270
- ...filters,
271
- gpu_count: option.value
272
- ? parseInt(option.value)
273
- : undefined,
274
- })
275
- }
276
- style={{ marginRight: "10px", cursor: "pointer" }}
277
- />
278
- {option.label}
279
- </label>
280
- ))}
345
+ style={{ marginRight: "10px", cursor: "pointer" }}
346
+ />
347
+ {option.label}
348
+ </label>
349
+ ))}
281
350
  </>
282
351
  )}
283
352
 
284
- {filterCategory === "security" && (
353
+ {/* Cloud Type Filter - RunPod specific */}
354
+ {filterCategory === "cloud_type" && providerName === "runpod" && (
285
355
  <>
286
356
  {[
287
- { value: "", label: "All" },
288
- { value: "secure_cloud", label: "Secure Cloud" },
289
- {
290
- value: "community_cloud",
291
- label: "Community Cloud",
292
- },
293
- ]
294
- .filter((option) =>
295
- option.label.toLowerCase().includes(filterSearch.toLowerCase())
296
- )
297
- .map((option) => (
298
- <label
299
- key={option.value}
300
- style={{
301
- display: "flex",
302
- alignItems: "center",
303
- padding: "8px 6px",
304
- cursor: "pointer",
305
- fontSize: "12px",
306
- color: "var(--mc-text-color)",
307
- borderRadius: "4px",
308
- transition: "background-color 0.15s",
309
- }}
310
- onMouseEnter={(e) =>
311
- (e.currentTarget.style.backgroundColor = "var(--mc-secondary)")
357
+ { secure: undefined, community: undefined, label: "All Clouds" },
358
+ { secure: true, community: undefined, label: "Secure Cloud Only" },
359
+ { secure: undefined, community: true, label: "Community Cloud Only" },
360
+ ].map((option) => (
361
+ <label
362
+ key={option.label}
363
+ style={{
364
+ display: "flex",
365
+ alignItems: "center",
366
+ padding: "8px 6px",
367
+ cursor: "pointer",
368
+ fontSize: "12px",
369
+ color: "var(--mc-text-color)",
370
+ borderRadius: "4px",
371
+ transition: "background-color 0.15s",
372
+ }}
373
+ onMouseEnter={(e) =>
374
+ (e.currentTarget.style.backgroundColor = "var(--mc-secondary)")
375
+ }
376
+ onMouseLeave={(e) =>
377
+ (e.currentTarget.style.backgroundColor = "transparent")
378
+ }
379
+ >
380
+ <input
381
+ type="radio"
382
+ checked={
383
+ filters.secure_cloud === option.secure &&
384
+ filters.community_cloud === option.community
312
385
  }
313
- onMouseLeave={(e) =>
314
- (e.currentTarget.style.backgroundColor = "transparent")
386
+ onChange={() =>
387
+ onFiltersChange({
388
+ ...filters,
389
+ secure_cloud: option.secure,
390
+ community_cloud: option.community,
391
+ })
315
392
  }
316
- >
317
- <input
318
- type="radio"
319
- checked={(filters.security || "") === option.value}
320
- onChange={() =>
321
- onFiltersChange({
322
- ...filters,
323
- security: option.value || undefined,
324
- })
325
- }
326
- style={{ marginRight: "10px", cursor: "pointer" }}
327
- />
328
- {option.label}
329
- </label>
330
- ))}
393
+ style={{ marginRight: "10px", cursor: "pointer" }}
394
+ />
395
+ {option.label}
396
+ </label>
397
+ ))}
398
+ <div style={{ padding: "8px 6px", fontSize: "11px", color: "var(--mc-text-secondary)", borderTop: "1px solid var(--mc-border)", marginTop: "8px" }}>
399
+ Secure Cloud: T3/T4 certified data centers<br />
400
+ Community Cloud: User-hosted GPUs
401
+ </div>
331
402
  </>
332
403
  )}
333
404
 
334
- {filterCategory === "socket" && (
405
+ {/* Verified Filter - Vast.ai specific */}
406
+ {filterCategory === "verified" && providerName === "vastai" && (
335
407
  <>
336
408
  {[
337
- { value: "", label: "All" },
338
- { value: "PCIe", label: "PCIe" },
339
- { value: "SXM4", label: "SXM4" },
340
- { value: "SXM5", label: "SXM5" },
341
- { value: "SXM6", label: "SXM6" },
342
- ]
343
- .filter((option) =>
344
- option.label.toLowerCase().includes(filterSearch.toLowerCase())
345
- )
346
- .map((option) => (
347
- <label
348
- key={option.value}
349
- style={{
350
- display: "flex",
351
- alignItems: "center",
352
- padding: "8px 6px",
353
- cursor: "pointer",
354
- fontSize: "12px",
355
- color: "var(--mc-text-color)",
356
- borderRadius: "4px",
357
- transition: "background-color 0.15s",
358
- }}
359
- onMouseEnter={(e) =>
360
- (e.currentTarget.style.backgroundColor = "var(--mc-secondary)")
409
+ { value: undefined, label: "All Hosts" },
410
+ { value: true, label: "Verified Hosts Only" },
411
+ ].map((option) => (
412
+ <label
413
+ key={option.label}
414
+ style={{
415
+ display: "flex",
416
+ alignItems: "center",
417
+ padding: "8px 6px",
418
+ cursor: "pointer",
419
+ fontSize: "12px",
420
+ color: "var(--mc-text-color)",
421
+ borderRadius: "4px",
422
+ transition: "background-color 0.15s",
423
+ }}
424
+ onMouseEnter={(e) =>
425
+ (e.currentTarget.style.backgroundColor = "var(--mc-secondary)")
426
+ }
427
+ onMouseLeave={(e) =>
428
+ (e.currentTarget.style.backgroundColor = "transparent")
429
+ }
430
+ >
431
+ <input
432
+ type="radio"
433
+ checked={filters.verified === option.value}
434
+ onChange={() =>
435
+ onFiltersChange({
436
+ ...filters,
437
+ verified: option.value,
438
+ })
361
439
  }
362
- onMouseLeave={(e) =>
363
- (e.currentTarget.style.backgroundColor = "transparent")
440
+ style={{ marginRight: "10px", cursor: "pointer" }}
441
+ />
442
+ {option.label}
443
+ </label>
444
+ ))}
445
+ <div style={{ padding: "8px 6px", fontSize: "11px", color: "var(--mc-text-secondary)", borderTop: "1px solid var(--mc-border)", marginTop: "8px" }}>
446
+ Verified hosts have been validated by Vast.ai for reliability
447
+ </div>
448
+ </>
449
+ )}
450
+
451
+ {/* Reliability Filter - Vast.ai specific */}
452
+ {filterCategory === "reliability" && providerName === "vastai" && (
453
+ <>
454
+ {[
455
+ { value: undefined, label: "Any Reliability" },
456
+ { value: 0.9, label: "90%+ Reliability" },
457
+ { value: 0.95, label: "95%+ Reliability" },
458
+ { value: 0.99, label: "99%+ Reliability" },
459
+ ].map((option) => (
460
+ <label
461
+ key={option.label}
462
+ style={{
463
+ display: "flex",
464
+ alignItems: "center",
465
+ padding: "8px 6px",
466
+ cursor: "pointer",
467
+ fontSize: "12px",
468
+ color: "var(--mc-text-color)",
469
+ borderRadius: "4px",
470
+ transition: "background-color 0.15s",
471
+ }}
472
+ onMouseEnter={(e) =>
473
+ (e.currentTarget.style.backgroundColor = "var(--mc-secondary)")
474
+ }
475
+ onMouseLeave={(e) =>
476
+ (e.currentTarget.style.backgroundColor = "transparent")
477
+ }
478
+ >
479
+ <input
480
+ type="radio"
481
+ checked={filters.min_reliability === option.value}
482
+ onChange={() =>
483
+ onFiltersChange({
484
+ ...filters,
485
+ min_reliability: option.value,
486
+ })
364
487
  }
365
- >
366
- <input
367
- type="radio"
368
- checked={(filters.socket || "") === option.value}
369
- onChange={() =>
370
- onFiltersChange({
371
- ...filters,
372
- socket: option.value || undefined,
373
- })
374
- }
375
- style={{ marginRight: "10px", cursor: "pointer" }}
376
- />
377
- {option.label}
378
- </label>
379
- ))}
488
+ style={{ marginRight: "10px", cursor: "pointer" }}
489
+ />
490
+ {option.label}
491
+ </label>
492
+ ))}
493
+ <div style={{ padding: "8px 6px", fontSize: "11px", color: "var(--mc-text-secondary)", borderTop: "1px solid var(--mc-border)", marginTop: "8px" }}>
494
+ Higher reliability means fewer unexpected interruptions
495
+ </div>
380
496
  </>
381
497
  )}
382
498
  </div>
@@ -14,9 +14,10 @@ const POLL_MS = 3000;
14
14
  interface MetricsPopupProps {
15
15
  onClose?: () => void;
16
16
  sharedHistory?: MetricsSnapshot[]; // Passed from parent when in persistent mode
17
+ connectedProvider?: string; // Provider name for the connected pod
17
18
  }
18
19
 
19
- const MetricsPopup: React.FC<MetricsPopupProps> = ({ onClose, sharedHistory }) => {
20
+ const MetricsPopup: React.FC<MetricsPopupProps> = ({ onClose, sharedHistory, connectedProvider }) => {
20
21
  const [metrics, setMetrics] = useState<MetricsSnapshot | null>(null);
21
22
  const [localHistory, setLocalHistory] = useState<MetricsSnapshot[]>([]);
22
23
  const intervalRef = useRef<number | null>(null);
@@ -58,8 +59,26 @@ const MetricsPopup: React.FC<MetricsPopupProps> = ({ onClose, sharedHistory }) =
58
59
 
59
60
  const hasGPU = metrics?.gpu && metrics.gpu.length > 0;
60
61
 
62
+ // Provider display name mapping (SSH-based providers only)
63
+ const getProviderDisplayName = (provider?: string) => {
64
+ const names: Record<string, string> = {
65
+ prime_intellect: "Prime Intellect",
66
+ runpod: "RunPod",
67
+ lambda_labs: "Lambda Labs",
68
+ vastai: "Vast.ai",
69
+ };
70
+ return provider ? names[provider] || provider : "Local";
71
+ };
72
+
61
73
  return (
62
74
  <div className="metrics-container">
75
+ {/* Provider badge */}
76
+ {connectedProvider && (
77
+ <div className="metrics-provider-badge ssh-provider">
78
+ <span className="provider-dot" />
79
+ <span>Metrics from {getProviderDisplayName(connectedProvider)}</span>
80
+ </div>
81
+ )}
63
82
  <div className="metrics-grid">
64
83
  {metrics?.cpu && (
65
84
  <Panel title="CPU Utilization" icon={<Cpu size={14} />}>