more-compute 0.4.3__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 +739 -418
  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.3.dist-info → more_compute-0.5.0.dist-info}/METADATA +53 -11
  31. {more_compute-0.4.3.dist-info → more_compute-0.5.0.dist-info}/RECORD +56 -37
  32. {more_compute-0.4.3.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.3.dist-info/licenses → more_compute-0.5.0.dist-info}/LICENSE +0 -0
  56. {more_compute-0.4.3.dist-info → more_compute-0.5.0.dist-info}/entry_points.txt +0 -0
  57. {more_compute-0.4.3.dist-info → more_compute-0.5.0.dist-info}/top_level.txt +0 -0
frontend/app/layout.tsx CHANGED
@@ -14,6 +14,8 @@ import {
14
14
  PodWebSocketProvider,
15
15
  usePodWebSocket,
16
16
  } from "@/contexts/PodWebSocketContext";
17
+ import { ClaudeProvider, useClaude } from "@/contexts/ClaudeContext";
18
+ import ClaudePanel from "@/components/panels/ClaudePanel";
17
19
  import { loadSettings, applyTheme, type NotebookSettings } from "@/lib/settings";
18
20
  import { fetchMetrics, type MetricsSnapshot } from "@/lib/api";
19
21
  import "./globals.css";
@@ -25,6 +27,7 @@ function AppContent({ children }: { children: React.ReactNode }) {
25
27
  const [activePopup, setActivePopup] = useState<string | null>(null);
26
28
  const [showRestartModal, setShowRestartModal] = useState(false);
27
29
  const { connectionState, gpuPods, connectingPodId } = usePodWebSocket();
30
+ const { isPanelOpen: isClaudePanelOpen } = useClaude();
28
31
 
29
32
  // Persistent metrics collection
30
33
  const [metricsHistory, setMetricsHistory] = useState<MetricsSnapshot[]>([]);
@@ -74,7 +77,6 @@ function AppContent({ children }: { children: React.ReactNode }) {
74
77
  }, [appSettings.metricsCollectionMode]);
75
78
 
76
79
  const handleSettingsChange = (settings: NotebookSettings) => {
77
- console.log("Settings updated:", settings);
78
80
  setAppSettings(settings);
79
81
  };
80
82
 
@@ -213,7 +215,13 @@ function AppContent({ children }: { children: React.ReactNode }) {
213
215
  </span>
214
216
  </div>
215
217
  </div>
216
- <div className="main-content">{children}</div>
218
+ <div
219
+ className="main-content"
220
+ style={{ marginRight: isClaudePanelOpen ? '400px' : '0' }}
221
+ >
222
+ {children}
223
+ </div>
224
+ <ClaudePanel />
217
225
  <div style={{ display: "none" }}>
218
226
  <span id="connection-status">Connected</span>
219
227
  <span id="kernel-status">Ready</span>
@@ -267,7 +275,9 @@ export default function RootLayout({
267
275
  </head>
268
276
  <body data-notebook-path={notebookPath} data-notebook-root={notebookRoot}>
269
277
  <PodWebSocketProvider>
270
- <AppContent>{children}</AppContent>
278
+ <ClaudeProvider>
279
+ <AppContent>{children}</AppContent>
280
+ </ClaudeProvider>
271
281
  </PodWebSocketProvider>
272
282
  <Script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/codemirror.min.js" />
273
283
  <Script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/mode/python/python.min.js" />
@@ -277,13 +277,9 @@ function notebookReducer(
277
277
  const payload = action.payload || {};
278
278
  const cell_index = payload.cell_index;
279
279
 
280
- console.log(`[EXECUTION_COMPLETE] Processing completion for cell ${cell_index}`, payload);
281
-
282
280
  // Support both shapes: { result: {...} } and flat payload {...}
283
281
  const result = payload && payload.result ? payload.result : payload || {};
284
282
 
285
- console.log(`[EXECUTION_COMPLETE] result.status=${result.status}, result.error=`, result.error);
286
-
287
283
  // ALWAYS remove cell from executingCells when completion arrives
288
284
  const newExecuting = new Set(state.executingCells);
289
285
  newExecuting.delete(cell_index);
@@ -319,8 +315,6 @@ function notebookReducer(
319
315
  case "EXECUTION_ERROR": {
320
316
  const { cell_index, error } = action.payload;
321
317
 
322
- console.log(`[EXECUTION_ERROR] Processing error for cell ${cell_index}`);
323
-
324
318
  // ALWAYS remove cell from executingCells when error arrives
325
319
  const newExecuting = new Set(state.executingCells);
326
320
  newExecuting.delete(cell_index);
@@ -361,7 +355,7 @@ function notebookReducer(
361
355
  ...cell,
362
356
  outputs: [],
363
357
  execution_count: null,
364
- execution_time: null,
358
+ execution_time: undefined,
365
359
  error: null,
366
360
  })),
367
361
  };
@@ -400,10 +394,6 @@ export const Notebook: React.FC<NotebookProps> = ({
400
394
  const deletionQueueRef = useRef<DeletedCell[]>([]);
401
395
  const [showUndoHint, setShowUndoHint] = useState(false);
402
396
 
403
- // DEBUG: Verify new code is loaded
404
- useEffect(() => {
405
- console.log("✅ NEW NOTEBOOK CODE LOADED - Undo functionality available");
406
- }, []);
407
397
 
408
398
  useEffect(() => {
409
399
  const body = document.body;
@@ -454,10 +444,9 @@ export const Notebook: React.FC<NotebookProps> = ({
454
444
  dispatch({ type: "RESET_KERNEL" });
455
445
  }, []);
456
446
 
457
- const handleHeartbeat = useCallback((data: any) => {
447
+ const handleHeartbeat = useCallback((_data: any) => {
458
448
  // Heartbeat received - execution still in progress
459
449
  // Cell spinner already showing via executingCells set
460
- console.log("[Heartbeat]", data?.message || "Execution in progress");
461
450
  }, []);
462
451
 
463
452
  const handleKernelStatusUpdate = useCallback(
@@ -599,7 +588,6 @@ export const Notebook: React.FC<NotebookProps> = ({
599
588
  const undoDelete = useCallback(() => {
600
589
  const deletedCell = deletionQueueRef.current.shift(); // Get most recent deletion
601
590
  if (!deletedCell) {
602
- console.log("No deletions to undo");
603
591
  return;
604
592
  }
605
593
 
@@ -18,7 +18,9 @@ import {
18
18
  } from "@radix-ui/react-icons";
19
19
  import { Check, X } from "lucide-react";
20
20
  import { loadMonacoThemes } from "@/lib/monaco-themes";
21
- import { loadSettings } from "@/lib/settings";
21
+ import { loadSettings, type NotebookSettings } from "@/lib/settings";
22
+ import { useClaude } from "@/contexts/ClaudeContext";
23
+ import { useInlineDiff } from "@/hooks/useInlineDiff";
22
24
 
23
25
  interface CellProps {
24
26
  cell: CellType;
@@ -230,6 +232,10 @@ export const MonacoCell: React.FC<CellProps> = ({
230
232
  const disposablesRef = useRef<monaco.IDisposable[]>([]);
231
233
  const isUnmountingRef = useRef(false);
232
234
 
235
+ // Claude AI integration
236
+ const { pendingEdits, applyEdit, rejectEdit } = useClaude();
237
+ const pendingEdit = pendingEdits.get(index);
238
+
233
239
  const [isEditing, setIsEditing] = useState(
234
240
  () => cell.cell_type === "code" || !cell.source?.trim()
235
241
  );
@@ -237,6 +243,93 @@ export const MonacoCell: React.FC<CellProps> = ({
237
243
  cell.execution_time ?? null
238
244
  );
239
245
 
246
+ // Track if we've applied the pending edit preview
247
+ const appliedEditIdRef = useRef<string | null>(null);
248
+ const originalCodeRef = useRef<string | null>(null);
249
+ const isApplyingPreviewRef = useRef(false);
250
+
251
+ // Handle pending edit: show new code in editor (only if claudeAutoPreview is enabled)
252
+ useEffect(() => {
253
+ const editor = editorRef.current;
254
+ if (!editor) return;
255
+
256
+ const model = editor.getModel();
257
+ if (!model) return;
258
+
259
+ // Check if auto-preview is enabled in settings
260
+ const settings = loadSettings();
261
+ const autoPreviewEnabled = settings.claudeAutoPreview;
262
+
263
+ if (pendingEdit && pendingEdit.status === "pending" && autoPreviewEnabled) {
264
+ // Only apply if we haven't already applied this edit's preview
265
+ if (appliedEditIdRef.current !== pendingEdit.id) {
266
+ // Store original code for reverting
267
+ originalCodeRef.current = model.getValue();
268
+ // Update editor to show new code (skip the onUpdate callback)
269
+ const currentValue = model.getValue();
270
+ if (currentValue !== pendingEdit.newCode) {
271
+ isApplyingPreviewRef.current = true;
272
+ model.setValue(pendingEdit.newCode);
273
+ isApplyingPreviewRef.current = false;
274
+ }
275
+ appliedEditIdRef.current = pendingEdit.id;
276
+ }
277
+ } else if (appliedEditIdRef.current && !pendingEdit) {
278
+ // Edit was rejected or cleared - restore original if we have it
279
+ // Note: If applied, the notebook update will sync the content anyway
280
+ appliedEditIdRef.current = null;
281
+ originalCodeRef.current = null;
282
+ }
283
+ }, [pendingEdit]);
284
+
285
+ // Custom handlers for diff that restore original on reject
286
+ const handleApplyEdit = useCallback((editId: string) => {
287
+ appliedEditIdRef.current = null;
288
+ originalCodeRef.current = null;
289
+ applyEdit(editId);
290
+ }, [applyEdit]);
291
+
292
+ const handleRejectEdit = useCallback((editId: string) => {
293
+ const editor = editorRef.current;
294
+ const model = editor?.getModel();
295
+
296
+ // Restore original code before rejecting
297
+ if (model && originalCodeRef.current !== null) {
298
+ const original = originalCodeRef.current;
299
+ // Clear refs before setting value to prevent re-triggering preview
300
+ appliedEditIdRef.current = null;
301
+ originalCodeRef.current = null;
302
+ // Restore original - we DO want to trigger onUpdate here to sync state
303
+ model.setValue(original);
304
+ } else {
305
+ appliedEditIdRef.current = null;
306
+ originalCodeRef.current = null;
307
+ }
308
+
309
+ rejectEdit(editId);
310
+ }, [rejectEdit]);
311
+
312
+ // Check if auto-preview is enabled (for diff decorations)
313
+ const [autoPreviewEnabled, setAutoPreviewEnabled] = useState(() => loadSettings().claudeAutoPreview);
314
+
315
+ // Listen for settings changes
316
+ useEffect(() => {
317
+ const handleSettingsChange = () => {
318
+ setAutoPreviewEnabled(loadSettings().claudeAutoPreview);
319
+ };
320
+ window.addEventListener('storage', handleSettingsChange);
321
+ return () => window.removeEventListener('storage', handleSettingsChange);
322
+ }, []);
323
+
324
+ // Inline diff for Claude AI edits (only when auto-preview is enabled)
325
+ const { hasDiff } = useInlineDiff({
326
+ editor: editorRef.current,
327
+ cellIndex: index,
328
+ pendingEdit: autoPreviewEnabled ? pendingEdit : undefined, // Only pass edit if auto-preview enabled
329
+ onApply: handleApplyEdit,
330
+ onReject: handleRejectEdit,
331
+ });
332
+
240
333
  // ============================================================================
241
334
  // UTILITIES
242
335
  // ============================================================================
@@ -359,7 +452,8 @@ export const MonacoCell: React.FC<CellProps> = ({
359
452
 
360
453
  // Handle content changes
361
454
  const changeDisposable = editor.onDidChangeModelContent(() => {
362
- if (!isUnmountingRef.current) {
455
+ // Skip update during preview application or unmounting
456
+ if (!isUnmountingRef.current && !isApplyingPreviewRef.current) {
363
457
  onUpdate(indexRef.current, editor.getValue());
364
458
  }
365
459
  });
@@ -577,7 +671,7 @@ export const MonacoCell: React.FC<CellProps> = ({
577
671
 
578
672
  {/* Main Cell Container */}
579
673
  <div
580
- className={`cell ${isActive ? "active" : ""} ${isExecuting ? "executing" : ""} ${isMarkdownWithContent ? "markdown-display-mode" : ""}`}
674
+ className={`cell ${isActive ? "active" : ""} ${isExecuting ? "executing" : ""} ${isMarkdownWithContent ? "markdown-display-mode" : ""} ${hasDiff ? "has-pending-diff" : ""}`}
581
675
  data-cell-index={index}
582
676
  >
583
677
  {/* Hover Controls */}
@@ -687,7 +781,7 @@ export const MonacoCell: React.FC<CellProps> = ({
687
781
  },
688
782
  contextmenu: true,
689
783
  folding: cell.cell_type === "code",
690
- glyphMargin: false,
784
+ glyphMargin: hasDiff,
691
785
  lineDecorationsWidth: 0,
692
786
  lineNumbersMinChars: 3,
693
787
  renderLineHighlight: "none", // Remove grey rectangle
@@ -698,7 +792,7 @@ export const MonacoCell: React.FC<CellProps> = ({
698
792
  overviewRulerBorder: false,
699
793
  overviewRulerLanes: 0,
700
794
  // NOTE: fixedOverflowWidgets has known positioning bugs in 2024 - disabled
701
- padding: { top: 8, bottom: 8, left: 8 }, // All padding managed by Monaco
795
+ padding: { top: 8, bottom: 8 }, // All padding managed by Monaco
702
796
  scrollbar: {
703
797
  vertical: "auto",
704
798
  horizontal: "auto",
@@ -1,5 +1,13 @@
1
1
  import React from "react";
2
- import { Folder, Package, Cpu, Settings, ChartArea } from "lucide-react";
2
+ import {
3
+ Folder,
4
+ Package,
5
+ Cpu,
6
+ Settings,
7
+ ChartArea,
8
+ Sparkles,
9
+ } from "lucide-react";
10
+ import { useClaude } from "@/contexts/ClaudeContext";
3
11
 
4
12
  interface SidebarItemData {
5
13
  id: string;
@@ -12,6 +20,7 @@ const sidebarItems: SidebarItemData[] = [
12
20
  { id: "packages", icon: <Package size={16} />, tooltip: "Packages" },
13
21
  { id: "compute", icon: <Cpu size={16} />, tooltip: "Compute" },
14
22
  { id: "metrics", icon: <ChartArea size={16} />, tooltip: "Metrics" },
23
+ { id: "claude", icon: <Sparkles size={16} />, tooltip: "Claude" },
15
24
  { id: "settings", icon: <Settings size={16} />, tooltip: "Settings" },
16
25
  ];
17
26
 
@@ -21,7 +30,33 @@ interface SidebarProps {
21
30
  }
22
31
 
23
32
  const Sidebar: React.FC<SidebarProps> = ({ onTogglePopup, activePopup }) => {
24
- const activeIndex = sidebarItems.findIndex((item) => item.id === activePopup);
33
+ const { isPanelOpen: isClaudePanelOpen, togglePanel: toggleClaudePanel } =
34
+ useClaude();
35
+
36
+ // Calculate active index, considering Claude panel state
37
+ const getActiveIndex = () => {
38
+ if (isClaudePanelOpen) {
39
+ return sidebarItems.findIndex((item) => item.id === "claude");
40
+ }
41
+ return sidebarItems.findIndex((item) => item.id === activePopup);
42
+ };
43
+
44
+ const activeIndex = getActiveIndex();
45
+
46
+ const handleItemClick = (itemId: string) => {
47
+ if (itemId === "claude") {
48
+ toggleClaudePanel();
49
+ } else {
50
+ onTogglePopup(itemId);
51
+ }
52
+ };
53
+
54
+ const isItemActive = (itemId: string) => {
55
+ if (itemId === "claude") {
56
+ return isClaudePanelOpen;
57
+ }
58
+ return activePopup === itemId;
59
+ };
25
60
 
26
61
  return (
27
62
  <div id="sidebar" className="sidebar">
@@ -36,9 +71,9 @@ const Sidebar: React.FC<SidebarProps> = ({ onTogglePopup, activePopup }) => {
36
71
  {sidebarItems.map((item) => (
37
72
  <div
38
73
  key={item.id}
39
- className={`sidebar-item ${activePopup === item.id ? "active" : ""}`}
74
+ className={`sidebar-item ${isItemActive(item.id) ? "active" : ""} ${item.id === "claude" ? "claude-item" : ""}`}
40
75
  data-popup={item.id}
41
- onClick={() => onTogglePopup(item.id)}
76
+ onClick={() => handleItemClick(item.id)}
42
77
  >
43
78
  <span className="sidebar-icon-wrapper">{item.icon}</span>
44
79
  <div className="sidebar-tooltip">{item.tooltip}</div>