mrmd-python 0.3.0__tar.gz

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.
@@ -0,0 +1,44 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ build/
8
+ develop-eggs/
9
+ dist/
10
+ downloads/
11
+ eggs/
12
+ .eggs/
13
+ lib/
14
+ lib64/
15
+ parts/
16
+ sdist/
17
+ var/
18
+ wheels/
19
+ *.egg-info/
20
+ .installed.cfg
21
+ *.egg
22
+
23
+ # Virtual environments
24
+ .venv/
25
+ venv/
26
+ ENV/
27
+
28
+ # IDE
29
+ .idea/
30
+ .vscode/
31
+ *.swp
32
+ *.swo
33
+
34
+ # Testing
35
+ .pytest_cache/
36
+ .coverage
37
+ htmlcov/
38
+
39
+ # Runtime assets (generated)
40
+ .mrmd-assets/
41
+
42
+ # OS
43
+ .DS_Store
44
+ Thumbs.db
@@ -0,0 +1,162 @@
1
+ Metadata-Version: 2.4
2
+ Name: mrmd-python
3
+ Version: 0.3.0
4
+ Summary: Python runtime server implementing the MRMD Runtime Protocol (MRP)
5
+ Author: mrmd contributors
6
+ License: MIT
7
+ Keywords: ipython,mrmd,mrp,notebook,runtime
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Requires-Python: >=3.10
17
+ Requires-Dist: black>=25.12.0
18
+ Requires-Dist: httpx>=0.27.0
19
+ Requires-Dist: ipython>=8.0.0
20
+ Requires-Dist: jedi>=0.19.0
21
+ Requires-Dist: sse-starlette>=2.0.0
22
+ Requires-Dist: starlette>=0.40.0
23
+ Requires-Dist: uvicorn>=0.32.0
24
+ Provides-Extra: dev
25
+ Requires-Dist: httpx>=0.27.0; extra == 'dev'
26
+ Requires-Dist: pytest-asyncio>=0.24.0; extra == 'dev'
27
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
28
+ Provides-Extra: format
29
+ Requires-Dist: black>=24.0.0; extra == 'format'
30
+ Description-Content-Type: text/markdown
31
+
32
+ # mrmd-python
33
+
34
+ Independent Python runtime for MRMD notebooks. Runs as a daemon process with full GPU memory isolation.
35
+
36
+ ## Features
37
+
38
+ - **Independent daemon process** - survives if parent dies, variables persist
39
+ - **GPU memory isolation** - kill daemon to release VRAM (critical for vLLM)
40
+ - **Auto venv detection** - uses current venv or `VIRTUAL_ENV`
41
+ - **Registry-based discovery** - find runtimes via `~/.mrmd/runtimes/`
42
+ - **Full MRP protocol** - execute, completions, inspect, variables, streaming
43
+
44
+ ## Installation
45
+
46
+ ```bash
47
+ # With uv
48
+ uv pip install mrmd-python
49
+
50
+ # Or run directly without installing
51
+ uvx mrmd-python
52
+ ```
53
+
54
+ ## Quick Start
55
+
56
+ ```bash
57
+ # Start a daemon runtime (auto-detects venv)
58
+ mrmd-python
59
+
60
+ # The daemon runs in background. Use the API:
61
+ curl http://localhost:PORT/mrp/v1/capabilities
62
+
63
+ # List running runtimes
64
+ mrmd-python --list
65
+
66
+ # Kill when done (releases GPU memory)
67
+ mrmd-python --kill default
68
+ ```
69
+
70
+ ## CLI Reference
71
+
72
+ ```bash
73
+ # Start daemon
74
+ mrmd-python # Start with ID "default"
75
+ mrmd-python --id vllm # Start with custom ID
76
+ mrmd-python --venv /path/venv # Use specific venv
77
+ mrmd-python --port 8000 # Use specific port
78
+
79
+ # Management
80
+ mrmd-python --list # List all running runtimes
81
+ mrmd-python --info ID # Get runtime details
82
+ mrmd-python --kill ID # Kill a runtime
83
+ mrmd-python --kill-all # Kill all runtimes
84
+
85
+ # Development
86
+ mrmd-python --foreground # Run in foreground (no daemon)
87
+ ```
88
+
89
+ ## Virtual Environment Detection
90
+
91
+ When `--venv` is not specified, mrmd-python auto-detects:
92
+
93
+ 1. Current venv (if running inside one via `sys.prefix`)
94
+ 2. `VIRTUAL_ENV` environment variable
95
+ 3. Falls back to system Python
96
+
97
+ ## API Endpoints
98
+
99
+ All endpoints at `/mrp/v1/`:
100
+
101
+ | Endpoint | Method | Purpose |
102
+ |----------|--------|---------|
103
+ | `/capabilities` | GET | Runtime info and features |
104
+ | `/sessions` | GET/POST | List/create sessions |
105
+ | `/sessions/{id}` | GET/DELETE | Get/destroy session |
106
+ | `/execute` | POST | Run code |
107
+ | `/execute/stream` | POST | Run code with SSE streaming |
108
+ | `/complete` | POST | Get completions |
109
+ | `/inspect` | POST | Get symbol documentation |
110
+ | `/hover` | POST | Get hover tooltip |
111
+ | `/variables` | POST | List user variables |
112
+ | `/variables/{name}` | POST | Get variable details |
113
+ | `/is_complete` | POST | Check if code is complete |
114
+ | `/format` | POST | Format code with black |
115
+
116
+ ## Architecture
117
+
118
+ ```
119
+ ~/.mrmd/
120
+ ├── runtimes/
121
+ │ └── {id}.json # Registry: pid, port, url, venv, cwd
122
+ └── logs/
123
+ └── {id}.log # Daemon logs
124
+ ```
125
+
126
+ Each runtime is a fully independent process:
127
+ - Double-forked daemon (survives parent death)
128
+ - Own IPython shell with persistent variables
129
+ - HTTP server on auto-assigned port
130
+ - Registered in `~/.mrmd/runtimes/` for discovery
131
+
132
+ ## GPU Memory Management
133
+
134
+ For vLLM and other GPU workloads, memory is only released when the process dies:
135
+
136
+ ```bash
137
+ # Load model in runtime
138
+ mrmd-python --id vllm
139
+ # ... use the model ...
140
+
141
+ # Release GPU memory
142
+ mrmd-python --kill vllm
143
+ ```
144
+
145
+ ## Programmatic Usage
146
+
147
+ ```python
148
+ from mrmd_python import create_app
149
+ import uvicorn
150
+
151
+ # Create app (daemon_mode=True for use inside daemon)
152
+ app = create_app(
153
+ cwd="/path/to/project",
154
+ venv="/path/to/venv",
155
+ daemon_mode=True,
156
+ )
157
+ uvicorn.run(app, host="localhost", port=8000)
158
+ ```
159
+
160
+ ## Protocol
161
+
162
+ See [PROTOCOL.md](../mrmd-editor/PROTOCOL.md) for the full MRP specification.
@@ -0,0 +1,131 @@
1
+ # mrmd-python
2
+
3
+ Independent Python runtime for MRMD notebooks. Runs as a daemon process with full GPU memory isolation.
4
+
5
+ ## Features
6
+
7
+ - **Independent daemon process** - survives if parent dies, variables persist
8
+ - **GPU memory isolation** - kill daemon to release VRAM (critical for vLLM)
9
+ - **Auto venv detection** - uses current venv or `VIRTUAL_ENV`
10
+ - **Registry-based discovery** - find runtimes via `~/.mrmd/runtimes/`
11
+ - **Full MRP protocol** - execute, completions, inspect, variables, streaming
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ # With uv
17
+ uv pip install mrmd-python
18
+
19
+ # Or run directly without installing
20
+ uvx mrmd-python
21
+ ```
22
+
23
+ ## Quick Start
24
+
25
+ ```bash
26
+ # Start a daemon runtime (auto-detects venv)
27
+ mrmd-python
28
+
29
+ # The daemon runs in background. Use the API:
30
+ curl http://localhost:PORT/mrp/v1/capabilities
31
+
32
+ # List running runtimes
33
+ mrmd-python --list
34
+
35
+ # Kill when done (releases GPU memory)
36
+ mrmd-python --kill default
37
+ ```
38
+
39
+ ## CLI Reference
40
+
41
+ ```bash
42
+ # Start daemon
43
+ mrmd-python # Start with ID "default"
44
+ mrmd-python --id vllm # Start with custom ID
45
+ mrmd-python --venv /path/venv # Use specific venv
46
+ mrmd-python --port 8000 # Use specific port
47
+
48
+ # Management
49
+ mrmd-python --list # List all running runtimes
50
+ mrmd-python --info ID # Get runtime details
51
+ mrmd-python --kill ID # Kill a runtime
52
+ mrmd-python --kill-all # Kill all runtimes
53
+
54
+ # Development
55
+ mrmd-python --foreground # Run in foreground (no daemon)
56
+ ```
57
+
58
+ ## Virtual Environment Detection
59
+
60
+ When `--venv` is not specified, mrmd-python auto-detects:
61
+
62
+ 1. Current venv (if running inside one via `sys.prefix`)
63
+ 2. `VIRTUAL_ENV` environment variable
64
+ 3. Falls back to system Python
65
+
66
+ ## API Endpoints
67
+
68
+ All endpoints at `/mrp/v1/`:
69
+
70
+ | Endpoint | Method | Purpose |
71
+ |----------|--------|---------|
72
+ | `/capabilities` | GET | Runtime info and features |
73
+ | `/sessions` | GET/POST | List/create sessions |
74
+ | `/sessions/{id}` | GET/DELETE | Get/destroy session |
75
+ | `/execute` | POST | Run code |
76
+ | `/execute/stream` | POST | Run code with SSE streaming |
77
+ | `/complete` | POST | Get completions |
78
+ | `/inspect` | POST | Get symbol documentation |
79
+ | `/hover` | POST | Get hover tooltip |
80
+ | `/variables` | POST | List user variables |
81
+ | `/variables/{name}` | POST | Get variable details |
82
+ | `/is_complete` | POST | Check if code is complete |
83
+ | `/format` | POST | Format code with black |
84
+
85
+ ## Architecture
86
+
87
+ ```
88
+ ~/.mrmd/
89
+ ├── runtimes/
90
+ │ └── {id}.json # Registry: pid, port, url, venv, cwd
91
+ └── logs/
92
+ └── {id}.log # Daemon logs
93
+ ```
94
+
95
+ Each runtime is a fully independent process:
96
+ - Double-forked daemon (survives parent death)
97
+ - Own IPython shell with persistent variables
98
+ - HTTP server on auto-assigned port
99
+ - Registered in `~/.mrmd/runtimes/` for discovery
100
+
101
+ ## GPU Memory Management
102
+
103
+ For vLLM and other GPU workloads, memory is only released when the process dies:
104
+
105
+ ```bash
106
+ # Load model in runtime
107
+ mrmd-python --id vllm
108
+ # ... use the model ...
109
+
110
+ # Release GPU memory
111
+ mrmd-python --kill vllm
112
+ ```
113
+
114
+ ## Programmatic Usage
115
+
116
+ ```python
117
+ from mrmd_python import create_app
118
+ import uvicorn
119
+
120
+ # Create app (daemon_mode=True for use inside daemon)
121
+ app = create_app(
122
+ cwd="/path/to/project",
123
+ venv="/path/to/venv",
124
+ daemon_mode=True,
125
+ )
126
+ uvicorn.run(app, host="localhost", port=8000)
127
+ ```
128
+
129
+ ## Protocol
130
+
131
+ See [PROTOCOL.md](../mrmd-editor/PROTOCOL.md) for the full MRP specification.
@@ -0,0 +1,51 @@
1
+ [project]
2
+ name = "mrmd-python"
3
+ version = "0.3.0"
4
+ description = "Python runtime server implementing the MRMD Runtime Protocol (MRP)"
5
+ readme = "README.md"
6
+ requires-python = ">=3.10"
7
+ license = { text = "MIT" }
8
+ authors = [{ name = "mrmd contributors" }]
9
+ keywords = ["notebook", "ipython", "runtime", "mrmd", "mrp"]
10
+ classifiers = [
11
+ "Development Status :: 3 - Alpha",
12
+ "Intended Audience :: Developers",
13
+ "License :: OSI Approved :: MIT License",
14
+ "Programming Language :: Python :: 3",
15
+ "Programming Language :: Python :: 3.10",
16
+ "Programming Language :: Python :: 3.11",
17
+ "Programming Language :: Python :: 3.12",
18
+ "Programming Language :: Python :: 3.13",
19
+ ]
20
+
21
+ dependencies = [
22
+ "starlette>=0.40.0",
23
+ "uvicorn>=0.32.0",
24
+ "sse-starlette>=2.0.0",
25
+ "ipython>=8.0.0",
26
+ "jedi>=0.19.0",
27
+ "black>=25.12.0",
28
+ "httpx>=0.27.0", # For daemon runtime HTTP client
29
+ ]
30
+
31
+ [project.optional-dependencies]
32
+ format = ["black>=24.0.0"]
33
+ dev = [
34
+ "pytest>=8.0.0",
35
+ "pytest-asyncio>=0.24.0",
36
+ "httpx>=0.27.0",
37
+ ]
38
+
39
+ [project.scripts]
40
+ mrmd-python = "mrmd_python.cli:main"
41
+
42
+ [build-system]
43
+ requires = ["hatchling"]
44
+ build-backend = "hatchling.build"
45
+
46
+ [tool.hatch.build.targets.wheel]
47
+ packages = ["src/mrmd_python"]
48
+
49
+ [tool.pytest.ini_options]
50
+ asyncio_mode = "auto"
51
+ testpaths = ["tests"]
@@ -0,0 +1,16 @@
1
+ """
2
+ mrmd-python: Python runtime server implementing the MRMD Runtime Protocol (MRP)
3
+
4
+ Usage:
5
+ mrmd-python [--host HOST] [--port PORT] [--cwd DIR]
6
+
7
+ Or programmatically:
8
+ from mrmd_python import create_app
9
+ app = create_app()
10
+ """
11
+
12
+ from .server import create_app, MRPServer
13
+ from .worker import IPythonWorker
14
+
15
+ __version__ = "0.1.0"
16
+ __all__ = ["create_app", "MRPServer", "IPythonWorker", "__version__"]
@@ -0,0 +1,225 @@
1
+ """
2
+ mrmd-python CLI
3
+
4
+ Independent Python runtime for MRMD notebooks.
5
+
6
+ Usage:
7
+ # Start a daemon runtime (default)
8
+ mrmd-python
9
+ mrmd-python --id my-session --venv /path/to/venv
10
+
11
+ # Management commands
12
+ mrmd-python --list # List running runtimes
13
+ mrmd-python --kill my-session # Kill a runtime
14
+ mrmd-python --kill-all # Kill all runtimes
15
+ mrmd-python --info my-session # Get runtime info
16
+
17
+ # Development/debugging
18
+ mrmd-python --foreground # Run in foreground (no daemon)
19
+
20
+ # Using with uvx (no install needed)
21
+ uvx mrmd-python
22
+ uvx mrmd-python --list
23
+ """
24
+
25
+ import argparse
26
+ import json
27
+ import os
28
+ import sys
29
+
30
+
31
+ def main():
32
+ parser = argparse.ArgumentParser(
33
+ prog="mrmd-python",
34
+ description="Independent Python runtime for MRMD notebooks",
35
+ formatter_class=argparse.RawDescriptionHelpFormatter,
36
+ epilog="""
37
+ Examples:
38
+ mrmd-python Start default runtime (daemon mode)
39
+ mrmd-python --id vllm Start runtime named 'vllm'
40
+ mrmd-python --venv ~/.venv/ml Use specific virtual environment
41
+ mrmd-python --list List all running runtimes
42
+ mrmd-python --kill vllm Kill the 'vllm' runtime
43
+ mrmd-python --foreground Run in foreground (for debugging)
44
+
45
+ Virtual Environment Detection:
46
+ If --venv is not specified, mrmd-python auto-detects:
47
+ 1. Current venv (if running inside one)
48
+ 2. VIRTUAL_ENV environment variable
49
+ 3. Falls back to system Python
50
+
51
+ Using with uvx (no install needed):
52
+ uvx mrmd-python Start default runtime
53
+ uvx mrmd-python --list List runtimes
54
+ """,
55
+ )
56
+
57
+ # Runtime configuration
58
+ parser.add_argument(
59
+ "--id",
60
+ default="default",
61
+ help="Runtime ID (default: 'default')",
62
+ )
63
+ parser.add_argument(
64
+ "--port",
65
+ type=int,
66
+ default=0,
67
+ help="Port to bind to (0 = auto-assign)",
68
+ )
69
+ parser.add_argument(
70
+ "--venv",
71
+ default=None,
72
+ help="Virtual environment path (auto-detected if not specified)",
73
+ )
74
+ parser.add_argument(
75
+ "--cwd",
76
+ default=None,
77
+ help="Working directory (default: current directory)",
78
+ )
79
+ parser.add_argument(
80
+ "--assets-dir",
81
+ default=None,
82
+ help="Directory for saving assets like plots",
83
+ )
84
+
85
+ # Execution mode
86
+ parser.add_argument(
87
+ "--foreground",
88
+ action="store_true",
89
+ help="Run in foreground instead of daemon mode",
90
+ )
91
+
92
+ # Management commands
93
+ parser.add_argument(
94
+ "--list",
95
+ action="store_true",
96
+ help="List all running runtimes",
97
+ )
98
+ parser.add_argument(
99
+ "--kill",
100
+ metavar="ID",
101
+ help="Kill a runtime by ID",
102
+ )
103
+ parser.add_argument(
104
+ "--kill-all",
105
+ action="store_true",
106
+ help="Kill all running runtimes",
107
+ )
108
+ parser.add_argument(
109
+ "--info",
110
+ metavar="ID",
111
+ help="Get detailed info about a runtime",
112
+ )
113
+
114
+ args = parser.parse_args()
115
+
116
+ # Import daemon functions (lazy import for fast --help)
117
+ from .runtime_daemon import (
118
+ list_runtimes,
119
+ kill_runtime,
120
+ read_runtime_info,
121
+ is_runtime_alive,
122
+ spawn_daemon,
123
+ run_daemon,
124
+ )
125
+
126
+ # Handle management commands
127
+ if args.list:
128
+ runtimes = list_runtimes()
129
+ if not runtimes:
130
+ print("No runtimes running")
131
+ print("\nStart one with: mrmd-python")
132
+ else:
133
+ print(f"{'ID':<20} {'STATUS':<8} {'PID':<10} {'PORT':<8} {'URL'}")
134
+ print("-" * 80)
135
+ for rt in runtimes:
136
+ status = "ALIVE" if rt.get("alive") else "DEAD"
137
+ print(f"{rt['id']:<20} {status:<8} {rt.get('pid', '-'):<10} {rt.get('port', '-'):<8} {rt.get('url', '-')}")
138
+ return
139
+
140
+ if args.kill:
141
+ if kill_runtime(args.kill):
142
+ print(f"Killed runtime '{args.kill}'")
143
+ else:
144
+ print(f"Runtime '{args.kill}' not found or already dead")
145
+ return
146
+
147
+ if args.kill_all:
148
+ runtimes = list_runtimes()
149
+ if not runtimes:
150
+ print("No runtimes to kill")
151
+ return
152
+ killed = 0
153
+ for rt in runtimes:
154
+ if kill_runtime(rt["id"]):
155
+ print(f"Killed '{rt['id']}'")
156
+ killed += 1
157
+ print(f"\nKilled {killed} runtime(s)")
158
+ return
159
+
160
+ if args.info:
161
+ info = read_runtime_info(args.info)
162
+ if info:
163
+ info["alive"] = is_runtime_alive(args.info)
164
+ print(json.dumps(info, indent=2))
165
+ else:
166
+ print(f"Runtime '{args.info}' not found")
167
+ return
168
+
169
+ # Determine venv
170
+ venv = args.venv
171
+ if not venv:
172
+ # Auto-detect venv
173
+ if sys.prefix != sys.base_prefix:
174
+ venv = sys.prefix
175
+ else:
176
+ venv = os.environ.get("VIRTUAL_ENV", sys.prefix)
177
+
178
+ # Determine cwd
179
+ cwd = args.cwd or os.getcwd()
180
+
181
+ # Check if runtime already exists
182
+ existing = read_runtime_info(args.id)
183
+ if existing and is_runtime_alive(args.id):
184
+ print(f"Runtime '{args.id}' is already running:")
185
+ print(f" URL: {existing.get('url')}")
186
+ print(f" PID: {existing.get('pid')}")
187
+ print(f" venv: {existing.get('venv')}")
188
+ print(f"\nTo kill it: mrmd-python --kill {args.id}")
189
+ return
190
+
191
+ if args.foreground:
192
+ # Run in foreground (blocking, for debugging)
193
+ print(f"Starting mrmd-python in foreground mode...")
194
+ print(f" ID: {args.id}")
195
+ print(f" venv: {venv}")
196
+ print(f" cwd: {cwd}")
197
+ print(f" port: {args.port or 'auto'}")
198
+ print(f"\nPress Ctrl+C to stop\n")
199
+ run_daemon(args.id, args.port, venv, cwd, args.assets_dir)
200
+ else:
201
+ # Spawn daemon (non-blocking)
202
+ print(f"Starting mrmd-python daemon...")
203
+ info = spawn_daemon(
204
+ runtime_id=args.id,
205
+ port=args.port,
206
+ venv=venv,
207
+ cwd=cwd,
208
+ assets_dir=args.assets_dir,
209
+ )
210
+ print(f"""
211
+ Runtime '{info['id']}' started:
212
+ URL: {info['url']}
213
+ PID: {info['pid']}
214
+ venv: {info['venv']}
215
+ cwd: {info['cwd']}
216
+
217
+ Management:
218
+ mrmd-python --list List all runtimes
219
+ mrmd-python --info {info['id']:<8} Get runtime info
220
+ mrmd-python --kill {info['id']:<8} Kill this runtime
221
+ """)
222
+
223
+
224
+ if __name__ == "__main__":
225
+ main()