tech-envision commited on
Commit
9f82d8d
·
1 Parent(s): 100e82d

feat(vm): persist containers across sessions

Browse files
Files changed (4) hide show
  1. README.md +8 -2
  2. run.py +3 -0
  3. src/config.py +1 -0
  4. src/vm.py +15 -3
README.md CHANGED
@@ -13,8 +13,11 @@ conversations can be resumed with context. One example tool is included:
13
  Execution happens asynchronously so the assistant can continue responding
14
  while the command runs.
15
  The VM is created when a chat session starts and reused for all subsequent
16
- tool calls. The environment includes Python and ``pip`` so complex tasks can
17
- be scripted using Python directly inside the terminal.
 
 
 
18
 
19
  Sessions share state through an in-memory registry so that only one generation
20
  can run at a time. Messages sent while a response is being produced are
@@ -77,6 +80,9 @@ back to ``python:3.11-slim``. This base image includes Python and ``pip`` so
77
  packages can be installed immediately. The container has network access enabled
78
  which allows fetching additional dependencies as needed.
79
 
 
 
 
80
  To use a fully featured Ubuntu environment, build a custom Docker image and set
81
  ``VM_IMAGE`` to that image. An example ``docker/Dockerfile.vm`` is provided:
82
 
 
13
  Execution happens asynchronously so the assistant can continue responding
14
  while the command runs.
15
  The VM is created when a chat session starts and reused for all subsequent
16
+ tool calls. When ``PERSIST_VMS`` is enabled (default), each user keeps the
17
+ same container across multiple chat sessions so any installed packages and
18
+ filesystem changes remain available. The environment includes Python and
19
+ ``pip`` so complex tasks can be scripted using Python directly inside the
20
+ terminal.
21
 
22
  Sessions share state through an in-memory registry so that only one generation
23
  can run at a time. Messages sent while a response is being produced are
 
80
  packages can be installed immediately. The container has network access enabled
81
  which allows fetching additional dependencies as needed.
82
 
83
+ Set ``PERSIST_VMS=0`` to revert to the previous behaviour where containers are
84
+ stopped once no sessions are using them.
85
+
86
  To use a fully featured Ubuntu environment, build a custom Docker image and set
87
  ``VM_IMAGE`` to that image. An example ``docker/Dockerfile.vm`` is provided:
88
 
run.py CHANGED
@@ -3,6 +3,7 @@ from __future__ import annotations
3
  import asyncio
4
 
5
  from src.chat import ChatSession
 
6
 
7
 
8
  async def _main() -> None:
@@ -24,3 +25,5 @@ if __name__ == "__main__":
24
  asyncio.run(_main())
25
  except KeyboardInterrupt:
26
  pass
 
 
 
3
  import asyncio
4
 
5
  from src.chat import ChatSession
6
+ from src.vm import VMRegistry
7
 
8
 
9
  async def _main() -> None:
 
25
  asyncio.run(_main())
26
  except KeyboardInterrupt:
27
  pass
28
+ finally:
29
+ VMRegistry.shutdown_all()
src/config.py CHANGED
@@ -10,6 +10,7 @@ MAX_TOOL_CALL_DEPTH: Final[int] = 5
10
  NUM_CTX: Final[int] = int(os.getenv("OLLAMA_NUM_CTX", "32000"))
11
  UPLOAD_DIR: Final[str] = os.getenv("UPLOAD_DIR", str(Path.cwd() / "uploads"))
12
  VM_IMAGE: Final[str] = os.getenv("VM_IMAGE", "python:3.11")
 
13
 
14
  SYSTEM_PROMPT: Final[str] = (
15
  "You are Starlette, a professional AI assistant with advanced tool orchestration. "
 
10
  NUM_CTX: Final[int] = int(os.getenv("OLLAMA_NUM_CTX", "32000"))
11
  UPLOAD_DIR: Final[str] = os.getenv("UPLOAD_DIR", str(Path.cwd() / "uploads"))
12
  VM_IMAGE: Final[str] = os.getenv("VM_IMAGE", "python:3.11")
13
+ PERSIST_VMS: Final[bool] = os.getenv("PERSIST_VMS", "1") == "1"
14
 
15
  SYSTEM_PROMPT: Final[str] = (
16
  "You are Starlette, a professional AI assistant with advanced tool orchestration. "
src/vm.py CHANGED
@@ -8,7 +8,7 @@ from pathlib import Path
8
 
9
  from threading import Lock
10
 
11
- from .config import UPLOAD_DIR, VM_IMAGE
12
 
13
  from .log import get_logger
14
 
@@ -180,6 +180,18 @@ class VMRegistry:
180
 
181
  cls._counts[username] -= 1
182
  if cls._counts[username] <= 0:
 
 
 
 
 
 
 
 
 
 
 
 
183
  vm.stop()
184
- del cls._vms[username]
185
- del cls._counts[username]
 
8
 
9
  from threading import Lock
10
 
11
+ from .config import UPLOAD_DIR, VM_IMAGE, PERSIST_VMS
12
 
13
  from .log import get_logger
14
 
 
180
 
181
  cls._counts[username] -= 1
182
  if cls._counts[username] <= 0:
183
+ cls._counts[username] = 0
184
+ if not PERSIST_VMS:
185
+ vm.stop()
186
+ del cls._vms[username]
187
+ del cls._counts[username]
188
+
189
+ @classmethod
190
+ def shutdown_all(cls) -> None:
191
+ """Stop and remove all managed VMs."""
192
+
193
+ with cls._lock:
194
+ for vm in cls._vms.values():
195
  vm.stop()
196
+ cls._vms.clear()
197
+ cls._counts.clear()