more-compute 0.1.3__py3-none-any.whl → 0.2.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.
- frontend/app/globals.css +322 -77
- frontend/app/layout.tsx +98 -82
- frontend/components/Cell.tsx +234 -95
- frontend/components/Notebook.tsx +430 -199
- frontend/components/{AddCellButton.tsx → cell/AddCellButton.tsx} +0 -2
- frontend/components/cell/MonacoCell.tsx +726 -0
- frontend/components/layout/ConnectionBanner.tsx +41 -0
- frontend/components/{Sidebar.tsx → layout/Sidebar.tsx} +16 -11
- frontend/components/modals/ConfirmModal.tsx +154 -0
- frontend/components/modals/SuccessModal.tsx +140 -0
- frontend/components/output/MarkdownRenderer.tsx +116 -0
- frontend/components/popups/ComputePopup.tsx +674 -365
- frontend/components/popups/MetricsPopup.tsx +11 -7
- frontend/components/popups/SettingsPopup.tsx +11 -13
- frontend/contexts/PodWebSocketContext.tsx +247 -0
- frontend/eslint.config.mjs +11 -0
- frontend/lib/monaco-themes.ts +160 -0
- frontend/lib/settings.ts +128 -26
- frontend/lib/themes.json +9973 -0
- frontend/lib/websocket-native.ts +19 -8
- frontend/lib/websocket.ts +59 -11
- frontend/next.config.ts +8 -0
- frontend/package-lock.json +1705 -3
- frontend/package.json +8 -1
- frontend/styling_README.md +18 -0
- kernel_run.py +161 -43
- more_compute-0.2.0.dist-info/METADATA +126 -0
- more_compute-0.2.0.dist-info/RECORD +100 -0
- morecompute/__version__.py +1 -0
- morecompute/execution/executor.py +31 -20
- morecompute/execution/worker.py +68 -7
- morecompute/models/__init__.py +31 -0
- morecompute/models/api_models.py +197 -0
- morecompute/notebook.py +50 -7
- morecompute/server.py +574 -94
- morecompute/services/data_manager.py +379 -0
- morecompute/services/lsp_service.py +335 -0
- morecompute/services/pod_manager.py +122 -20
- morecompute/services/pod_monitor.py +138 -0
- morecompute/services/prime_intellect.py +87 -63
- morecompute/utils/config_util.py +59 -0
- morecompute/utils/special_commands.py +11 -5
- morecompute/utils/zmq_util.py +51 -0
- frontend/components/MarkdownRenderer.tsx +0 -84
- frontend/components/popups/PythonPopup.tsx +0 -292
- more_compute-0.1.3.dist-info/METADATA +0 -173
- more_compute-0.1.3.dist-info/RECORD +0 -85
- /frontend/components/{CellButton.tsx → cell/CellButton.tsx} +0 -0
- /frontend/components/{ErrorModal.tsx → modals/ErrorModal.tsx} +0 -0
- /frontend/components/{CellOutput.tsx → output/CellOutput.tsx} +0 -0
- /frontend/components/{ErrorDisplay.tsx → output/ErrorDisplay.tsx} +0 -0
- {more_compute-0.1.3.dist-info → more_compute-0.2.0.dist-info}/WHEEL +0 -0
- {more_compute-0.1.3.dist-info → more_compute-0.2.0.dist-info}/entry_points.txt +0 -0
- {more_compute-0.1.3.dist-info → more_compute-0.2.0.dist-info}/licenses/LICENSE +0 -0
- {more_compute-0.1.3.dist-info → more_compute-0.2.0.dist-info}/top_level.txt +0 -0
frontend/package.json
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"@codemirror/theme-one-dark": "^6.1.3",
|
|
15
15
|
"@hugeicons/core-free-icons": "^1.1.0",
|
|
16
16
|
"@hugeicons/react": "^1.1.1",
|
|
17
|
+
"@monaco-editor/react": "^4.7.0",
|
|
17
18
|
"@radix-ui/react-icons": "^1.3.2",
|
|
18
19
|
"@types/socket.io-client": "^1.4.36",
|
|
19
20
|
"@uiw/react-codemirror": "^4.25.2",
|
|
@@ -21,11 +22,17 @@
|
|
|
21
22
|
"codemirror": "^5.65.2",
|
|
22
23
|
"framer-motion": "^12.23.22",
|
|
23
24
|
"lucide-react": "^0.544.0",
|
|
25
|
+
"monaco-editor": "^0.54.0",
|
|
26
|
+
"monaco-languageclient": "^10.2.0",
|
|
24
27
|
"next": "15.5.4",
|
|
25
28
|
"react": "19.1.0",
|
|
26
29
|
"react-dom": "19.1.0",
|
|
30
|
+
"react-window": "^2.2.1",
|
|
31
|
+
"react-window-infinite-loader": "^2.0.0",
|
|
27
32
|
"socket.io-client": "^4.8.1",
|
|
28
|
-
"sortablejs": "^1.15.3"
|
|
33
|
+
"sortablejs": "^1.15.3",
|
|
34
|
+
"vscode-languageclient": "^9.0.1",
|
|
35
|
+
"vscode-languageserver-protocol": "^3.17.5"
|
|
29
36
|
},
|
|
30
37
|
"devDependencies": {
|
|
31
38
|
"@eslint/eslintrc": "^3",
|
kernel_run.py
CHANGED
|
@@ -8,9 +8,11 @@ import time
|
|
|
8
8
|
import signal
|
|
9
9
|
import threading
|
|
10
10
|
import webbrowser
|
|
11
|
+
import platform
|
|
11
12
|
from pathlib import Path
|
|
12
13
|
|
|
13
14
|
from morecompute.notebook import Notebook
|
|
15
|
+
from morecompute.__version__ import __version__
|
|
14
16
|
|
|
15
17
|
DEFAULT_NOTEBOOK_NAME = "notebook.ipynb"
|
|
16
18
|
|
|
@@ -21,6 +23,7 @@ class NotebookLauncher:
|
|
|
21
23
|
self.root_dir = Path(__file__).parent
|
|
22
24
|
self.debug = debug
|
|
23
25
|
self.notebook_path = notebook_path
|
|
26
|
+
self.is_windows = platform.system() == "Windows"
|
|
24
27
|
root_dir = notebook_path.parent if notebook_path.parent != Path('') else Path.cwd()
|
|
25
28
|
os.environ["MORECOMPUTE_ROOT"] = str(root_dir.resolve())
|
|
26
29
|
os.environ["MORECOMPUTE_NOTEBOOK_PATH"] = str(self.notebook_path)
|
|
@@ -77,48 +80,107 @@ class NotebookLauncher:
|
|
|
77
80
|
sys.exit(1)
|
|
78
81
|
|
|
79
82
|
def _ensure_port_available(self, port: int) -> None:
|
|
83
|
+
"""Cross-platform port availability check and cleanup"""
|
|
80
84
|
import socket
|
|
85
|
+
|
|
81
86
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
|
82
87
|
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
83
88
|
try:
|
|
84
89
|
s.bind(("127.0.0.1", port))
|
|
85
|
-
return # free
|
|
90
|
+
return # Port is free
|
|
86
91
|
except OSError:
|
|
87
|
-
pass # in use
|
|
88
|
-
|
|
89
|
-
print(f"\nPort {port}
|
|
92
|
+
pass # Port is in use
|
|
93
|
+
|
|
94
|
+
print(f"\nPort {port} is currently in use.")
|
|
90
95
|
pids = []
|
|
96
|
+
|
|
91
97
|
try:
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
if self.is_windows:
|
|
99
|
+
# Windows: Use netstat
|
|
100
|
+
out = subprocess.check_output(
|
|
101
|
+
["netstat", "-ano"],
|
|
102
|
+
text=True,
|
|
103
|
+
encoding='utf-8',
|
|
104
|
+
errors='replace'
|
|
105
|
+
)
|
|
106
|
+
for line in out.splitlines():
|
|
107
|
+
if f":{port}" in line and "LISTENING" in line:
|
|
108
|
+
parts = line.split()
|
|
109
|
+
if parts and parts[-1].isdigit():
|
|
110
|
+
pid = int(parts[-1])
|
|
111
|
+
if pid not in pids:
|
|
112
|
+
pids.append(pid)
|
|
113
|
+
# Get process name
|
|
114
|
+
try:
|
|
115
|
+
proc_out = subprocess.check_output(
|
|
116
|
+
["tasklist", "/FI", f"PID eq {pid}", "/FO", "CSV", "/NH"],
|
|
117
|
+
text=True,
|
|
118
|
+
encoding='utf-8',
|
|
119
|
+
errors='replace'
|
|
120
|
+
)
|
|
121
|
+
proc_name = proc_out.split(',')[0].strip('"')
|
|
122
|
+
print(f" PID {pid}: {proc_name}")
|
|
123
|
+
except Exception:
|
|
124
|
+
print(f" PID {pid}")
|
|
125
|
+
else:
|
|
126
|
+
# Unix: Use lsof
|
|
127
|
+
out = subprocess.check_output(
|
|
128
|
+
["lsof", "-nP", f"-iTCP:{port}", "-sTCP:LISTEN"],
|
|
129
|
+
text=True,
|
|
130
|
+
encoding='utf-8',
|
|
131
|
+
errors='replace'
|
|
132
|
+
)
|
|
133
|
+
print(out)
|
|
134
|
+
for line in out.splitlines()[1:]:
|
|
135
|
+
parts = line.split()
|
|
136
|
+
if len(parts) > 1 and parts[1].isdigit():
|
|
137
|
+
pids.append(int(parts[1]))
|
|
138
|
+
except Exception as e:
|
|
139
|
+
print(f"Could not list processes: {e}")
|
|
140
|
+
|
|
141
|
+
if not pids:
|
|
142
|
+
print(f"Could not find process using port {port}.")
|
|
143
|
+
print("Please free the port manually or set MORECOMPUTE_PORT to a different port.")
|
|
144
|
+
sys.exit(1)
|
|
145
|
+
|
|
146
|
+
resp = input(f"Kill process(es) on port {port} and continue? [y/N]: ").strip().lower()
|
|
101
147
|
if resp != "y":
|
|
102
148
|
print("Aborting. Set MORECOMPUTE_PORT to a different port to override.")
|
|
103
149
|
sys.exit(1)
|
|
104
|
-
|
|
150
|
+
|
|
151
|
+
# Kill processes
|
|
105
152
|
for pid in pids:
|
|
106
153
|
try:
|
|
107
|
-
|
|
154
|
+
if self.is_windows:
|
|
155
|
+
subprocess.run(
|
|
156
|
+
["taskkill", "/F", "/PID", str(pid)],
|
|
157
|
+
stdout=subprocess.DEVNULL,
|
|
158
|
+
stderr=subprocess.DEVNULL,
|
|
159
|
+
encoding='utf-8',
|
|
160
|
+
errors='replace'
|
|
161
|
+
)
|
|
162
|
+
else:
|
|
163
|
+
os.kill(pid, signal.SIGKILL)
|
|
164
|
+
except Exception as e:
|
|
165
|
+
print(f"Failed to kill PID {pid}: {e}")
|
|
166
|
+
|
|
167
|
+
# Fallback: kill known patterns (Unix only)
|
|
168
|
+
if not self.is_windows:
|
|
169
|
+
try:
|
|
170
|
+
subprocess.run(["pkill", "-f", "uvicorn .*morecompute.server:app"],
|
|
171
|
+
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
108
172
|
except Exception:
|
|
109
173
|
pass
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
#
|
|
120
|
-
time.sleep(0.5)
|
|
121
|
-
# Poll until it binds
|
|
174
|
+
try:
|
|
175
|
+
subprocess.run(["pkill", "-f", "morecompute.execution.worker"],
|
|
176
|
+
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
177
|
+
except Exception:
|
|
178
|
+
pass
|
|
179
|
+
|
|
180
|
+
# Windows needs more time to release ports
|
|
181
|
+
time.sleep(1.0 if self.is_windows else 0.5)
|
|
182
|
+
|
|
183
|
+
# Poll until port is available
|
|
122
184
|
start = time.time()
|
|
123
185
|
while time.time() - start < 5.0:
|
|
124
186
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s2:
|
|
@@ -128,6 +190,7 @@ class NotebookLauncher:
|
|
|
128
190
|
return
|
|
129
191
|
except OSError:
|
|
130
192
|
time.sleep(0.25)
|
|
193
|
+
|
|
131
194
|
print(f"Port {port} still busy. Please free it or set MORECOMPUTE_PORT to another port.")
|
|
132
195
|
sys.exit(1)
|
|
133
196
|
|
|
@@ -136,25 +199,51 @@ class NotebookLauncher:
|
|
|
136
199
|
try:
|
|
137
200
|
frontend_dir = self.root_dir / "frontend"
|
|
138
201
|
|
|
202
|
+
# Use Windows-specific npm command
|
|
203
|
+
npm_cmd = "npm.cmd" if self.is_windows else "npm"
|
|
204
|
+
|
|
205
|
+
# Verify npm exists
|
|
206
|
+
try:
|
|
207
|
+
subprocess.run(
|
|
208
|
+
[npm_cmd, "--version"],
|
|
209
|
+
check=True,
|
|
210
|
+
stdout=subprocess.DEVNULL,
|
|
211
|
+
stderr=subprocess.DEVNULL,
|
|
212
|
+
shell=self.is_windows,
|
|
213
|
+
encoding='utf-8',
|
|
214
|
+
errors='replace'
|
|
215
|
+
)
|
|
216
|
+
except (subprocess.CalledProcessError, FileNotFoundError):
|
|
217
|
+
print("\nError: npm not found. Please install Node.js from https://nodejs.org/")
|
|
218
|
+
print("After installation, restart your terminal and try again.")
|
|
219
|
+
self.cleanup()
|
|
220
|
+
sys.exit(1)
|
|
221
|
+
|
|
139
222
|
# Check if node_modules exists
|
|
140
223
|
if not (frontend_dir / "node_modules").exists():
|
|
141
224
|
print("Installing dependencies...")
|
|
142
225
|
subprocess.run(
|
|
143
|
-
[
|
|
226
|
+
[npm_cmd, "install"],
|
|
144
227
|
cwd=frontend_dir,
|
|
145
228
|
check=True,
|
|
146
229
|
stdout=subprocess.DEVNULL,
|
|
147
|
-
stderr=subprocess.DEVNULL
|
|
230
|
+
stderr=subprocess.DEVNULL,
|
|
231
|
+
shell=self.is_windows,
|
|
232
|
+
encoding='utf-8',
|
|
233
|
+
errors='replace'
|
|
148
234
|
)
|
|
149
235
|
|
|
150
236
|
fe_stdout = None if self.debug else subprocess.DEVNULL
|
|
151
237
|
fe_stderr = None if self.debug else subprocess.DEVNULL
|
|
152
238
|
|
|
153
239
|
self.frontend_process = subprocess.Popen(
|
|
154
|
-
[
|
|
240
|
+
[npm_cmd, "run", "dev"],
|
|
155
241
|
cwd=frontend_dir,
|
|
156
242
|
stdout=fe_stdout,
|
|
157
|
-
stderr=fe_stderr
|
|
243
|
+
stderr=fe_stderr,
|
|
244
|
+
shell=self.is_windows, # CRITICAL for Windows
|
|
245
|
+
encoding='utf-8',
|
|
246
|
+
errors='replace'
|
|
158
247
|
)
|
|
159
248
|
|
|
160
249
|
# Wait a bit then open browser
|
|
@@ -169,18 +258,40 @@ class NotebookLauncher:
|
|
|
169
258
|
def cleanup(self):
|
|
170
259
|
"""Clean up processes on exit"""
|
|
171
260
|
if self.frontend_process:
|
|
172
|
-
self.frontend_process.terminate()
|
|
173
261
|
try:
|
|
174
|
-
self.
|
|
175
|
-
|
|
176
|
-
|
|
262
|
+
if self.is_windows:
|
|
263
|
+
# Windows: Use taskkill for more reliable cleanup
|
|
264
|
+
subprocess.run(
|
|
265
|
+
["taskkill", "/F", "/T", "/PID", str(self.frontend_process.pid)],
|
|
266
|
+
stdout=subprocess.DEVNULL,
|
|
267
|
+
stderr=subprocess.DEVNULL
|
|
268
|
+
)
|
|
269
|
+
else:
|
|
270
|
+
self.frontend_process.terminate()
|
|
271
|
+
try:
|
|
272
|
+
self.frontend_process.wait(timeout=5)
|
|
273
|
+
except subprocess.TimeoutExpired:
|
|
274
|
+
self.frontend_process.kill()
|
|
275
|
+
except Exception:
|
|
276
|
+
pass
|
|
177
277
|
|
|
178
278
|
if self.backend_process:
|
|
179
|
-
self.backend_process.terminate()
|
|
180
279
|
try:
|
|
181
|
-
self.
|
|
182
|
-
|
|
183
|
-
|
|
280
|
+
if self.is_windows:
|
|
281
|
+
# Windows: Use taskkill for more reliable cleanup
|
|
282
|
+
subprocess.run(
|
|
283
|
+
["taskkill", "/F", "/T", "/PID", str(self.backend_process.pid)],
|
|
284
|
+
stdout=subprocess.DEVNULL,
|
|
285
|
+
stderr=subprocess.DEVNULL
|
|
286
|
+
)
|
|
287
|
+
else:
|
|
288
|
+
self.backend_process.terminate()
|
|
289
|
+
try:
|
|
290
|
+
self.backend_process.wait(timeout=5)
|
|
291
|
+
except subprocess.TimeoutExpired:
|
|
292
|
+
self.backend_process.kill()
|
|
293
|
+
except Exception:
|
|
294
|
+
pass
|
|
184
295
|
|
|
185
296
|
def run(self):
|
|
186
297
|
"""Main run method"""
|
|
@@ -189,12 +300,19 @@ class NotebookLauncher:
|
|
|
189
300
|
|
|
190
301
|
# Set up signal handlers
|
|
191
302
|
def signal_handler(signum, frame):
|
|
192
|
-
|
|
303
|
+
# Shutdown immediately on Ctrl+C
|
|
304
|
+
print("\nREMINDER: Any running GPU pods will continue to incur costs until you terminate them in the Compute popup.")
|
|
305
|
+
print("\n Thanks for using MoreCompute!\n")
|
|
193
306
|
self.cleanup()
|
|
194
307
|
sys.exit(0)
|
|
195
308
|
|
|
196
|
-
signal
|
|
197
|
-
|
|
309
|
+
# Windows signal handling is different
|
|
310
|
+
if not self.is_windows:
|
|
311
|
+
signal.signal(signal.SIGINT, signal_handler)
|
|
312
|
+
signal.signal(signal.SIGTERM, signal_handler)
|
|
313
|
+
else:
|
|
314
|
+
# Windows only supports SIGINT and SIGBREAK
|
|
315
|
+
signal.signal(signal.SIGINT, signal_handler)
|
|
198
316
|
|
|
199
317
|
# Start services
|
|
200
318
|
self.start_backend()
|
|
@@ -226,7 +344,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
226
344
|
"--version",
|
|
227
345
|
"-v",
|
|
228
346
|
action="version",
|
|
229
|
-
version="%(prog)s
|
|
347
|
+
version=f"%(prog)s {__version__}",
|
|
230
348
|
)
|
|
231
349
|
parser.add_argument(
|
|
232
350
|
"notebook_path",
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: more-compute
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: An interactive notebook environment for local and GPU computing
|
|
5
|
+
Home-page: https://github.com/DannyMang/MORECOMPUTE
|
|
6
|
+
Author: MoreCompute Team
|
|
7
|
+
Author-email: MoreCompute Team <hello@morecompute.dev>
|
|
8
|
+
License: MIT
|
|
9
|
+
Project-URL: Homepage, https://github.com/DannyMang/MORECOMPUTE
|
|
10
|
+
Project-URL: Repository, https://github.com/DannyMang/MORECOMPUTE
|
|
11
|
+
Project-URL: Issues, https://github.com/DannyMang/MORECOMPUTE/issues
|
|
12
|
+
Keywords: jupyter,notebook,gpu,computing,interactive
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Requires-Python: >=3.8
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Requires-Dist: fastapi>=0.104.0
|
|
27
|
+
Requires-Dist: uvicorn[standard]>=0.24.0
|
|
28
|
+
Requires-Dist: python-multipart>=0.0.5
|
|
29
|
+
Requires-Dist: nbformat>=5.0.0
|
|
30
|
+
Requires-Dist: click>=8.0.0
|
|
31
|
+
Requires-Dist: pyzmq>=25.0.0
|
|
32
|
+
Requires-Dist: psutil>=5.9.0
|
|
33
|
+
Requires-Dist: httpx>=0.24.0
|
|
34
|
+
Requires-Dist: cachetools>=5.3.0
|
|
35
|
+
Requires-Dist: matplotlib>=3.5.0
|
|
36
|
+
Dynamic: author
|
|
37
|
+
Dynamic: home-page
|
|
38
|
+
Dynamic: license-file
|
|
39
|
+
Dynamic: requires-python
|
|
40
|
+
|
|
41
|
+
# more-compute
|
|
42
|
+
|
|
43
|
+
[](https://pypi.org/project/more-compute/)
|
|
44
|
+
[](https://www.python.org/downloads/)
|
|
45
|
+
[](LICENSE)
|
|
46
|
+
|
|
47
|
+
Interactive notebook environment for local Python development. Works with standard `.ipynb` files,
|
|
48
|
+
similar to Jupyter Lab but more awesome.
|
|
49
|
+
|
|
50
|
+
## Installation
|
|
51
|
+
|
|
52
|
+
**Prerequisites:** [Node.js](https://nodejs.org/) 16+ required for web interface.
|
|
53
|
+
|
|
54
|
+
### Using uv (Recommended)
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
# macOS/Linux
|
|
58
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
59
|
+
uv tool install more-compute
|
|
60
|
+
|
|
61
|
+
# Windows
|
|
62
|
+
powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
|
|
63
|
+
uv tool install more-compute
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Using pip
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
pip install more-compute
|
|
70
|
+
|
|
71
|
+
# Add to PATH if needed:
|
|
72
|
+
# macOS/Linux: echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
|
|
73
|
+
# Windows: See troubleshooting below
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Usage
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
more-compute notebook.ipynb # Open existing notebook
|
|
80
|
+
more-compute # Create and open new notebook
|
|
81
|
+
more-compute --debug # Show logs
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Opens automatically at http://localhost:8000
|
|
85
|
+
|
|
86
|
+
## Troubleshooting
|
|
87
|
+
|
|
88
|
+
**Command not found:**
|
|
89
|
+
```bash
|
|
90
|
+
uv tool update-shell # Fixes PATH automatically
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**Manual PATH fix (macOS/Linux):**
|
|
94
|
+
```bash
|
|
95
|
+
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
|
|
96
|
+
source ~/.bashrc
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**Manual PATH fix (Windows):**
|
|
100
|
+
```powershell
|
|
101
|
+
$pythonScripts = python -c "import site; print(site.USER_BASE)"
|
|
102
|
+
$userPath = [Environment]::GetEnvironmentVariable("Path", "User")
|
|
103
|
+
[Environment]::SetEnvironmentVariable("Path", "$userPath;$pythonScripts\Scripts", "User")
|
|
104
|
+
# Restart PowerShell
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**Port in use:**
|
|
108
|
+
```bash
|
|
109
|
+
export MORECOMPUTE_PORT=8080 # macOS/Linux
|
|
110
|
+
$env:MORECOMPUTE_PORT = "8080" # Windows
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Development
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
git clone https://github.com/DannyMang/MORECOMPUTE.git
|
|
117
|
+
cd MORECOMPUTE
|
|
118
|
+
uv venv && source .venv/bin/activate
|
|
119
|
+
uv pip install -e .
|
|
120
|
+
cd frontend && npm install && cd ..
|
|
121
|
+
python kernel_run.py notebook.ipynb
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## License
|
|
125
|
+
|
|
126
|
+
MIT - see [LICENSE](LICENSE)
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
kernel_run.py,sha256=otblv5fH9VyucgBsh_LwcgAWLNun0E9ioGIZvzWuP84,14656
|
|
2
|
+
frontend/.DS_Store,sha256=uQeHnkKyuTF1AVax3NPqtN0uCH6XNXAxL9Nkb6Q9cGw,8196
|
|
3
|
+
frontend/.gitignore,sha256=IH4mX_SQH5rZ-W2M4IUw4E-fxgCBVHKmbQpEYJbWVM0,480
|
|
4
|
+
frontend/README.md,sha256=YLVf9995r3JZD5UkII5GZCvDK9wXXNrUE0loHA4vlY8,1450
|
|
5
|
+
frontend/__init__.py,sha256=L5SAOdfDfKqlgEVCvYQQRDZBTlCxutZKSpJp4018IG4,100
|
|
6
|
+
frontend/eslint.config.mjs,sha256=LBCCw4SomtiVMmlTSpYRXfkRs6Xs04R1YFfoyorYyT8,879
|
|
7
|
+
frontend/next-env.d.ts,sha256=ha5a7nXwEZZ88tJcvDQvYtaTFOnZJff0qjRW_Cz_zKY,262
|
|
8
|
+
frontend/next.config.mjs,sha256=n0o6cIIVIoOtI6JlvAK-HUFd2lg1pQPfUwlFS4O6TK0,346
|
|
9
|
+
frontend/next.config.ts,sha256=OL_rEfTIZxsB_B5R9JX2AxYXgC0Fc_XiDoRlOGEDEpk,368
|
|
10
|
+
frontend/package-lock.json,sha256=uv1cRVRHKZCzk5ceHJN8bOhJzv4a3XjeIbyTF2oYCho,306688
|
|
11
|
+
frontend/package.json,sha256=P7Hrwl1vUCUjhi44Eg3oqc4-4l-V0Ea6__S2Mx_Qp7A,1367
|
|
12
|
+
frontend/postcss.config.mjs,sha256=FB7yTKJ6mdCJYiEP3yAhLTQ1_c-iG0bNiLRNIvdR364,81
|
|
13
|
+
frontend/styling_README.md,sha256=RAQ4q-axGqugym8L22KpxB2ReBbEtg67YCEHLvTVvAw,196
|
|
14
|
+
frontend/tailwind.config.ts,sha256=eP9nVaAuyYo46vGQfCyWbo25_pr2hW830fs1Itcix9Q,620
|
|
15
|
+
frontend/tsconfig.json,sha256=7SvBlRBYmuXAlAteRQTGwEE7ooWuNaPUrZ219dOo61E,598
|
|
16
|
+
frontend/app/favicon.ico,sha256=K4rS0zRVqPc2_DqOv48L3qiEitTA20iigzvQ-c13WTI,25931
|
|
17
|
+
frontend/app/globals.css,sha256=imFZBoEmnHEOpo7Jegj8F71iPwSwNQivtH0ylpCKyWE,35682
|
|
18
|
+
frontend/app/layout.tsx,sha256=R1vUDTImQzUJIW51rdvBUIsvejSbjfLrwgbLSjrmcMM,6035
|
|
19
|
+
frontend/app/page.tsx,sha256=p-DgDv8xDnwcRfDJY4rtfSQ2VdYwbnP3G-otWqDR1l0,256
|
|
20
|
+
frontend/components/Cell.tsx,sha256=JeXn5Z_jzezYOtrgl291b6HBKJdfhQ88dTTDZjFoWmk,12237
|
|
21
|
+
frontend/components/Notebook.tsx,sha256=tJt45gyNch3ifWjnKUDoyvik7snrgRqt55gQgiYEnlc,21528
|
|
22
|
+
frontend/components/cell/AddCellButton.tsx,sha256=ZC2Vck0JIRDxGYhYv3LPYAdKDo13U6008WG_-XoPlIM,1012
|
|
23
|
+
frontend/components/cell/CellButton.tsx,sha256=BjfazBKzlybA5Syx6tXGTJa1U_jwL8C_IeQKbcHlkyk,1364
|
|
24
|
+
frontend/components/cell/MonacoCell.tsx,sha256=11jSlfGcUpxvbRDJr1GGocNtzZIarioDdgg7WLuZs9U,24334
|
|
25
|
+
frontend/components/layout/ConnectionBanner.tsx,sha256=-m77wKCFpeRJ_AQwnM38jLwCY5vfpqE846xeXmT3p8A,870
|
|
26
|
+
frontend/components/layout/Sidebar.tsx,sha256=kxgO2mXX11xI6rX478cUqqQ3xB40jlby21y27GTJSLU,1551
|
|
27
|
+
frontend/components/modals/ConfirmModal.tsx,sha256=3WgXy_wmR8FHZPwzMevfZHFXa0blR4v_SbKG3d5McT4,3652
|
|
28
|
+
frontend/components/modals/ErrorModal.tsx,sha256=kkGHQvgyMYlScKwni04-V_Dq6a0-lg0WodglLARR-uA,3536
|
|
29
|
+
frontend/components/modals/SuccessModal.tsx,sha256=7NVg0MFPVvsBGDOHPVyZTNx4kmZLHgxdhZKKtTD9FTU,3385
|
|
30
|
+
frontend/components/output/CellOutput.tsx,sha256=D1ZIvOmQvFn_4Y1ahGM5rp0Yr5_Zp_loNJaEUA-GCrA,2073
|
|
31
|
+
frontend/components/output/ErrorDisplay.tsx,sha256=d6I2WVyDLPsonyDuqsrrN8sc_KHg0VTAW86DfcqfqL0,5978
|
|
32
|
+
frontend/components/output/MarkdownRenderer.tsx,sha256=RtZ5yNRxDXIh_NkNsNiy30wMGIW7V1gfhsjecgMdc80,3341
|
|
33
|
+
frontend/components/popups/ComputePopup.tsx,sha256=B7AwwjpJ9_nNI5qSN53jdR2tAulB5yl7spjMXjH4yDM,44021
|
|
34
|
+
frontend/components/popups/FilterPopup.tsx,sha256=4kx9txg8cWeC6XHlh0pu0-BAfZkLTDYEU93fMdzn86M,13818
|
|
35
|
+
frontend/components/popups/FolderPopup.tsx,sha256=V2tDAbztvNIUyPWtFiwjeIoCmFGQyDosQgST_JsAzLo,5215
|
|
36
|
+
frontend/components/popups/MetricsPopup.tsx,sha256=V4Srat-RRr7B046Ezg5w99_E_t9j0P3Ch5DO9rF7Nx0,5028
|
|
37
|
+
frontend/components/popups/PackagesPopup.tsx,sha256=K_9kGlQ-y-njugOLrahbv0KHRh_mXIlzuMg0giRsTb8,3606
|
|
38
|
+
frontend/components/popups/SettingsPopup.tsx,sha256=etwDuB4HHfadzN7CZf9omZ0uNl_4vhwJ92C5p3rleuY,2395
|
|
39
|
+
frontend/contexts/PodWebSocketContext.tsx,sha256=QyEg1Msyum7h3T6F3j6pn4Ds5sXFT_9WYu5_0YZ2IUI,8256
|
|
40
|
+
frontend/lib/api.ts,sha256=0N4PCSC5pfbq7GvR_6aOdPZ3JGujzi1Rz4V51g8sHP8,10852
|
|
41
|
+
frontend/lib/monaco-themes.ts,sha256=jh_pZAmSMKjY_belbMbZX2WpFBN7baRxvJp9shUDYgk,5396
|
|
42
|
+
frontend/lib/settings.ts,sha256=ZAdwLErjPITkIc87D3DTcHYhhUP3fBI-LpDYsH9eVE4,5842
|
|
43
|
+
frontend/lib/themes.json,sha256=mk6IGy6o_DCOerBH3QmfXozTHEiy-alsTLTTIaba7No,292018
|
|
44
|
+
frontend/lib/websocket-native.ts,sha256=6QUSLSONnCImPp9hpFo_XdXME5X7DOptW1mrtG7E99A,5962
|
|
45
|
+
frontend/lib/websocket.ts,sha256=V2Y7sktt2dm1G-ZILggcqIWccsh-xoAG9dLHpNVqIqs,4500
|
|
46
|
+
frontend/public/file.svg,sha256=K2eBLDJcGZoCU2zb7qDFk6cvcH0yO3LuPgjbqwZ1O9Q,391
|
|
47
|
+
frontend/public/globe.svg,sha256=thS5vxg5JZV2YayFFJj-HYAp_UOmL7_thvniYkpX588,1035
|
|
48
|
+
frontend/public/next.svg,sha256=VZld-tbstJRaHoVt3KA8XhaqW_E_0htN9qdK55NXvPw,1375
|
|
49
|
+
frontend/public/vercel.svg,sha256=8IEzey_uY1tFW2MnVAaj5_OdagFOJa2Q2rWmfmKhKsQ,128
|
|
50
|
+
frontend/public/window.svg,sha256=ZEdoxKrrR2e84pM0TusMEl-4BKlNgBRAQkByIC2F46E,385
|
|
51
|
+
frontend/public/assets/icons/add.svg,sha256=_R2g6_rQSd9uQ52d_yxLY8kFGvAgJBAOvWUN00aoSkY,511
|
|
52
|
+
frontend/public/assets/icons/check.svg,sha256=MdLhklRIgz5dZqWuUQe0CdBzfA5W63X6RbvOT3C9YTE,261
|
|
53
|
+
frontend/public/assets/icons/copy.svg,sha256=Bd_NXZR-inj4X0WGhkpojErq6F6UXwpdp3DL6rm57r0,355
|
|
54
|
+
frontend/public/assets/icons/folder.svg,sha256=uzpAVxIsuGivdrte56WJOfqCWc1TNN9ldPhrHKEA674,400
|
|
55
|
+
frontend/public/assets/icons/metric.svg,sha256=biafr0qAHRV_8CnMEWrh3Wk3ZMAgbfaL0I1N7Uqfp_U,440
|
|
56
|
+
frontend/public/assets/icons/packages.svg,sha256=70XqerYscy3b-YkLpuNSBalECfmlfRVjgN5Pq4Uvd-I,460
|
|
57
|
+
frontend/public/assets/icons/play.svg,sha256=ca5409xQtDl8rOHp0Dv6t6WWra7QvTupbcGj-OubjL8,326
|
|
58
|
+
frontend/public/assets/icons/python.svg,sha256=uuyGYqFGHCyswya1TScnyy8c4HugLQD1uCbQNRBPZJ8,9605
|
|
59
|
+
frontend/public/assets/icons/setting.svg,sha256=oUwRxiQjyK33aAv4AK1r8FYwIM8RnuwKTTaMU9v_l-U,610
|
|
60
|
+
frontend/public/assets/icons/stop.svg,sha256=98xoeoVwCEFq142v5IYE1GxcD4o3_UGa0pCOu3wzbqw,285
|
|
61
|
+
frontend/public/assets/icons/trash.svg,sha256=ikf6zdvwlLWmmGISVPzrtDlQNMPJ3VskgoCfQCEbCck,398
|
|
62
|
+
frontend/public/assets/icons/up-down.svg,sha256=ocaOoU8RZDKuyrlcPJjESz24GGvas16rytFbC7DXzGg,339
|
|
63
|
+
frontend/public/assets/icons/x.svg,sha256=TPiVYZTK-vRlaG-nLrARcnPIWCJ1xA9sqhr-9Y4Kquk,270
|
|
64
|
+
frontend/public/fonts/Fira.ttf,sha256=dbSM4W7Drd9n_EkfXq8P31KuxbjZ1wtuFiZ8aFebvTw,242896
|
|
65
|
+
frontend/public/fonts/Tiempos.woff2,sha256=h83bJKvAK301wXCMIvK7ZG5j0H2K3tzAfgo0yk4z8OE,13604
|
|
66
|
+
frontend/public/fonts/VeraMono.ttf,sha256=2kKB3H2xej385kpiztkodcWJU0AFXsi6JKORTrl7NJ0,49224
|
|
67
|
+
frontend/types/notebook.ts,sha256=v23RaZe6H3lU5tq6sqnJDPxC2mu0NZFDCJfiN0mgvSs,1359
|
|
68
|
+
more_compute-0.2.0.dist-info/licenses/LICENSE,sha256=0Ot-XIetYt06iay6IhtpJkruD-cLZtjyv7_aIEE-oSc,1073
|
|
69
|
+
morecompute/__init__.py,sha256=pcMVq8Q7qb42AOn7tqgoZJOi3epDDBnEriiv2WVKnXY,87
|
|
70
|
+
morecompute/__version__.py,sha256=Zn1KFblwuFHiDRdRAiRnDBRkbPttWh44jKa5zG2ov0E,22
|
|
71
|
+
morecompute/cli.py,sha256=kVvzvPBqF8xO6UuhU_-TBn99nKwJ405R2mAS6zU0KBc,734
|
|
72
|
+
morecompute/notebook.py,sha256=KEcv0eOEh9N7bPVGoRuKJb47G9MmpQ5zz1B6Dm58w18,4651
|
|
73
|
+
morecompute/process_worker.py,sha256=KsE3r-XpkYGuyO4w3t54VKkD51LfNHAZc3TYattMtrg,7185
|
|
74
|
+
morecompute/server.py,sha256=W_EfX4NiSYZgK3ZZFP-LPO78_Sjzuyo3eiKprnAxsfI,40144
|
|
75
|
+
morecompute/execution/__init__.py,sha256=jPmBmq8BZWbUEY9XFSpqt5FkgX04uNS10WnUlr7Rnms,134
|
|
76
|
+
morecompute/execution/__main__.py,sha256=pAWB_1bn99u8Gb-tVMSMI-NYvbYbDiwbn40L0a0djeA,202
|
|
77
|
+
morecompute/execution/executor.py,sha256=v8OKWuHykuLvhnvsRydFECcgqRqfCFM__FAHb22ARcg,20128
|
|
78
|
+
morecompute/execution/worker.py,sha256=9z1-3ruT5DbsqRJWw54J8VFVOnm_X1Vwcp6GCV5JP4o,12465
|
|
79
|
+
morecompute/models/__init__.py,sha256=VLJ5GWi2uTNiZBdvl-ipSbmA6EL8FZHZ5oq-rJmm9z0,640
|
|
80
|
+
morecompute/models/api_models.py,sha256=-ydvi9SeTfdoY9oVPNQS4by-kQGSknx6BHhGD8E2tpo,4553
|
|
81
|
+
morecompute/services/data_manager.py,sha256=c4GKucetMM-VPNbHyzce6bZRvFfmz8kTd5RppLjoLVc,14329
|
|
82
|
+
morecompute/services/lsp_service.py,sha256=Le8ARImcg2P6oueF_14L8rStHOOseHruRTd_wfDVw7s,12237
|
|
83
|
+
morecompute/services/pod_manager.py,sha256=rg6mQJOieElDaO-KGBmg7wO6APkBThLpWmDi472Mvbw,21068
|
|
84
|
+
morecompute/services/pod_monitor.py,sha256=Y5aiNoVsvkGiHddNbfR1laAKn8G0eY0_nJyTM4VVkyg,4711
|
|
85
|
+
morecompute/services/prime_intellect.py,sha256=b705rHv3RPRsgWedRlHwoP_S-TxxZtMSyZhnaiZpMgk,10273
|
|
86
|
+
morecompute/static/styles.css,sha256=el_NtrUMbAUNSiMVBn1xlG70m3iPv7dyaIbWQMexhsY,19277
|
|
87
|
+
morecompute/utils/__init__.py,sha256=VIxCL3S1pnjEs4cjKGZqZB68_P8FegdeMIqBjJhI5jQ,419
|
|
88
|
+
morecompute/utils/cache_util.py,sha256=lVlXudHvtyvSo_kCSxORJrI85Jod8FrQLbI2f_JOIbA,661
|
|
89
|
+
morecompute/utils/config_util.py,sha256=fGTQll7Zh05ZHrW8LuQNTJGziGnIfvKIU3azbrY-I-s,1793
|
|
90
|
+
morecompute/utils/error_utils.py,sha256=e50WLFdD6ngIC30xAgrzdTYtD8tPOIFkKAAh_sPbK0I,11667
|
|
91
|
+
morecompute/utils/notebook_util.py,sha256=3hH94dtXvhizRVTU9a2b38m_51Y4igoXpkjAXUqpVBQ,1353
|
|
92
|
+
morecompute/utils/python_environment_util.py,sha256=l8WWWPwKbypknw8GwL22NXCji5i1FOy1vWG47J6og4g,7441
|
|
93
|
+
morecompute/utils/special_commands.py,sha256=JTc9II2EitmivwTTdnydEefShasiTa-7w8tNyYVIenw,19104
|
|
94
|
+
morecompute/utils/system_environment_util.py,sha256=32mQRubo0i4X61o-825T7m-eUSidcEp07qkInP1sWZA,4774
|
|
95
|
+
morecompute/utils/zmq_util.py,sha256=tx7-iS04UN69OFtBzkxcEnRhT7xtI9EzRnrZ_nsH_O0,1889
|
|
96
|
+
more_compute-0.2.0.dist-info/METADATA,sha256=ieZK-lZJs3LfJfflN59QmczXIRNEMp5GrgdkGKE0U70,3593
|
|
97
|
+
more_compute-0.2.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
98
|
+
more_compute-0.2.0.dist-info/entry_points.txt,sha256=xp7z9eRPNRM4oxkZZVlyXkhkSjN1AjoYI_B7qpDJ1bI,49
|
|
99
|
+
more_compute-0.2.0.dist-info/top_level.txt,sha256=Tamm6ADzjwaQa1z27O7Izcyhyt9f0gVjMv1_tC810aI,32
|
|
100
|
+
more_compute-0.2.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.2.0"
|
|
@@ -26,6 +26,7 @@ class NextZmqExecutor:
|
|
|
26
26
|
ctx: object # zmq.Context - untyped due to zmq type limitations
|
|
27
27
|
req: object # zmq.Socket - untyped due to zmq type limitations
|
|
28
28
|
sub: object # zmq.Socket - untyped due to zmq type limitations
|
|
29
|
+
is_remote: bool # Flag to track if connected to remote worker
|
|
29
30
|
|
|
30
31
|
def __init__(self, error_utils: "ErrorUtils", cmd_addr: str | None = None, pub_addr: str | None = None, interrupt_timeout: float = 0.5) -> None:
|
|
31
32
|
self.error_utils = error_utils
|
|
@@ -37,6 +38,7 @@ class NextZmqExecutor:
|
|
|
37
38
|
self.worker_proc = None
|
|
38
39
|
self.interrupted_cell = None
|
|
39
40
|
self.special_handler = None
|
|
41
|
+
self.is_remote = False # Start with local worker
|
|
40
42
|
self._ensure_special_handler()
|
|
41
43
|
self.ctx = zmq.Context.instance() # type: ignore[reportUnknownMemberType]
|
|
42
44
|
self.req = self.ctx.socket(zmq.REQ) # type: ignore[reportUnknownMemberType, reportAttributeAccessIssue]
|
|
@@ -51,6 +53,11 @@ class NextZmqExecutor:
|
|
|
51
53
|
self.special_handler = AsyncSpecialCommandHandler({"__name__": "__main__"})
|
|
52
54
|
|
|
53
55
|
def _ensure_worker(self) -> None:
|
|
56
|
+
"""Ensure a worker is available. If connected to remote pod, skip local worker spawn."""
|
|
57
|
+
# If we're connected to a remote worker, don't try to spawn local worker
|
|
58
|
+
if self.is_remote:
|
|
59
|
+
return
|
|
60
|
+
|
|
54
61
|
# Use a temporary REQ socket for probing to avoid locking self.req's state
|
|
55
62
|
tmp = self.ctx.socket(zmq.REQ) # type: ignore[reportUnknownMemberType, reportAttributeAccessIssue]
|
|
56
63
|
tmp.setsockopt(zmq.LINGER, 0) # type: ignore[reportAttributeAccessIssue]
|
|
@@ -107,32 +114,36 @@ class NextZmqExecutor:
|
|
|
107
114
|
raise RuntimeError('Failed to start/connect ZMQ worker')
|
|
108
115
|
|
|
109
116
|
async def execute_cell(self, cell_index: int, source_code: str, websocket: WebSocket | None = None) -> dict[str, object]:
|
|
110
|
-
import sys
|
|
111
117
|
self._ensure_special_handler()
|
|
112
118
|
handler = self.special_handler
|
|
113
119
|
normalized_source = source_code
|
|
114
120
|
if handler is not None:
|
|
115
121
|
normalized_source = handler._coerce_source_to_text(source_code) # type: ignore[reportPrivateUsage]
|
|
116
122
|
if handler.is_special_command(normalized_source):
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
'
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
123
|
+
# If connected to remote pod, send special commands to remote worker
|
|
124
|
+
# Otherwise they execute locally which gives wrong results
|
|
125
|
+
if not self.is_remote:
|
|
126
|
+
# Execute special command locally
|
|
127
|
+
execution_count = getattr(self, 'execution_count', 0) + 1
|
|
128
|
+
self.execution_count = execution_count
|
|
129
|
+
start_time = time.time()
|
|
130
|
+
result: dict[str, object] = {
|
|
131
|
+
'outputs': [],
|
|
132
|
+
'error': None,
|
|
133
|
+
'status': 'ok',
|
|
134
|
+
'execution_count': execution_count,
|
|
135
|
+
'execution_time': None,
|
|
136
|
+
}
|
|
137
|
+
if websocket:
|
|
138
|
+
await websocket.send_json({'type': 'execution_start', 'data': {'cell_index': cell_index, 'execution_count': execution_count}})
|
|
139
|
+
result = await handler.execute_special_command(
|
|
140
|
+
normalized_source, result, start_time, execution_count, websocket, cell_index
|
|
141
|
+
)
|
|
142
|
+
result['execution_time'] = f"{(time.time()-start_time)*1000:.1f}ms"
|
|
143
|
+
if websocket:
|
|
144
|
+
await websocket.send_json({'type': 'execution_complete', 'data': {'cell_index': cell_index, 'result': result}})
|
|
145
|
+
return result
|
|
146
|
+
# For remote execution, fall through to send via ZMQ
|
|
136
147
|
|
|
137
148
|
execution_count = getattr(self, 'execution_count', 0) + 1
|
|
138
149
|
self.execution_count = execution_count
|