code exec interface + preload
frdel committed
Aug 12, 2025 at 20:04 UTC
66e01e68e32170f0c1ca0a18302821bd70226181
8 files changed
+62
-19
preload.py
+2
-2
@@ -40,8 +40,8 @@ async def preload():
40
# async tasks to preload
41
tasks = [
42
preload_embedding(),
43
- preload_whisper(),
44
- preload_kokoro()
43
+ # preload_whisper(),
44
+ # preload_kokoro()
45
]
46
47
await asyncio.gather(*tasks, return_exceptions=True)
python/api/synthesize.py
+4
-4
@@ -7,11 +7,11 @@ from python.helpers import runtime, settings, kokoro_tts
7
class Synthesize(ApiHandler):
8
async def process(self, input: dict, request: Request) -> dict | Response:
9
text = input.get("text", "")
10
- ctxid = input.get("ctxid", "")
10
+ # ctxid = input.get("ctxid", "")
11
12
- context = self.get_context(ctxid)
13
- if not await kokoro_tts.is_downloaded():
14
- context.log.log(type="info", content="Kokoro TTS model is currently being initialized, please wait...")
12
+ # context = self.get_context(ctxid)
13
+ # if not await kokoro_tts.is_downloaded():
14
+ # context.log.log(type="info", content="Kokoro TTS model is currently being initialized, please wait...")
15
16
try:
17
# # Clean and chunk text for long responses
python/api/transcribe.py
+4
-4
@@ -5,11 +5,11 @@ from python.helpers import runtime, settings, whisper
5
class Transcribe(ApiHandler):
6
async def process(self, input: dict, request: Request) -> dict | Response:
7
audio = input.get("audio")
8
- ctxid = input.get("ctxid", "")
8
+ # ctxid = input.get("ctxid", "")
9
10
- context = self.get_context(ctxid)
11
- if not await whisper.is_downloaded():
12
- context.log.log(type="info", content="Whisper STT model is currently being initialized, please wait...")
10
+ # context = self.get_context(ctxid)
11
+ # if not await whisper.is_downloaded():
12
+ # context.log.log(type="info", content="Whisper STT model is currently being initialized, please wait...")
13
14
set = settings.get_settings()
15
result = await whisper.transcribe(set["stt_model_size"], audio) # type: ignore
python/helpers/fasta2a_server.py
+1
-1
@@ -258,7 +258,7 @@ class DynamicA2AProxy:
258
# Atomic update of the app
259
self.app = new_app
260
261
- _PRINTER.print("[A2A] FastA2A server configured successfully")
261
+ # _PRINTER.print("[A2A] FastA2A server configured successfully")
262
263
except Exception as e:
264
_PRINTER.print(f"[A2A] Failed to configure FastA2A server: {e}")
python/helpers/kokoro_tts.py
+13
@@ -7,6 +7,7 @@ import asyncio
7
import soundfile as sf
8
from python.helpers import runtime
9
from python.helpers.print_style import PrintStyle
10
+from python.helpers.notification import NotificationManager, NotificationType, NotificationPriority
11
12
warnings.filterwarnings("ignore", category=FutureWarning)
13
warnings.filterwarnings("ignore", category=UserWarning)
@@ -38,9 +39,21 @@ async def _preload():
39
try:
40
is_updating_model = True
41
if not _pipeline:
42
+ NotificationManager.send_notification(
43
+ NotificationType.INFO,
44
+ NotificationPriority.NORMAL,
45
+ "Loading Kokoro TTS model...",
46
+ display_time=99,
47
+ group="kokoro-preload")
48
PrintStyle.standard("Loading Kokoro TTS model...")
49
from kokoro import KPipeline
50
_pipeline = KPipeline(lang_code="a", repo_id="hexgrad/Kokoro-82M")
51
+ NotificationManager.send_notification(
52
+ NotificationType.INFO,
53
+ NotificationPriority.NORMAL,
54
+ "Kokoro TTS model loaded.",
55
+ display_time=2,
56
+ group="kokoro-preload")
57
finally:
58
is_updating_model = False
59
python/helpers/settings.py
+16
-2
@@ -84,6 +84,8 @@ class Settings(TypedDict):
84
rfc_port_http: int
85
rfc_port_ssh: int
86
87
+ shell_interface: Literal['local','ssh']
88
+
89
stt_model_size: str
90
stt_language: str
91
stt_silence_threshold: float
@@ -793,6 +795,17 @@ def convert_out(settings: Settings) -> SettingsOutput:
795
796
dev_fields: list[SettingsField] = []
797
798
+ dev_fields.append(
799
+ {
800
+ "id": "shell_interface",
801
+ "title": "Shell Interface",
802
+ "description": "Terminal interface used for Code Execution Tool. Local Python TTY works locally in both dockerized and development environments. SSH always connects to dockerized environment (automatically at localhost or RFC host address).",
803
+ "type": "select",
804
+ "value": settings["shell_interface"],
805
+ "options": [{"value": "local", "label": "Local Python TTY"}, {"value": "ssh", "label": "SSH"}],
806
+ }
807
+ )
808
+
809
if runtime.is_development():
810
# dev_fields.append(
811
# {
@@ -1378,6 +1391,7 @@ def get_default_settings() -> Settings:
1391
rfc_password="",
1392
rfc_port_http=55080,
1393
rfc_port_ssh=55022,
1394
+ shell_interface="local" if runtime.is_dockerized() else "ssh",
1395
stt_model_size="base",
1396
stt_language="en",
1397
stt_silence_threshold=0.3,
@@ -1539,7 +1553,7 @@ def set_root_password(password: str):
1553
def get_runtime_config(set: Settings):
1554
if runtime.is_dockerized():
1555
return {
1542
- "code_exec_ssh_enabled": False,
1556
+ "code_exec_ssh_enabled": set["shell_interface"] == "ssh",
1557
"code_exec_ssh_addr": "localhost",
1558
"code_exec_ssh_port": 22,
1559
"code_exec_ssh_user": "root",
@@ -1553,7 +1567,7 @@ def get_runtime_config(set: Settings):
1567
if host.endswith("/"):
1568
host = host[:-1]
1569
return {
1556
- "code_exec_ssh_enabled": True,
1570
+ "code_exec_ssh_enabled": set["shell_interface"] == "ssh",
1571
"code_exec_ssh_addr": host,
1572
"code_exec_ssh_port": set["rfc_port_ssh"],
1573
"code_exec_ssh_user": "root",
python/helpers/whisper.py
+16
-3
@@ -5,6 +5,7 @@ import tempfile
5
import asyncio
6
from python.helpers import runtime, rfc, settings, files
7
from python.helpers.print_style import PrintStyle
8
+from python.helpers.notification import NotificationManager, NotificationType, NotificationPriority
9
10
# Suppress FutureWarning from torch.load
11
warnings.filterwarnings("ignore", category=FutureWarning)
@@ -30,9 +31,21 @@ async def _preload(model_name:str):
31
try:
32
is_updating_model = True
33
if not _model or _model_name != model_name:
33
- PrintStyle.standard(f"Loading Whisper model: {model_name}")
34
- _model = whisper.load_model(name=model_name, download_root=files.get_abs_path("/tmp/models/whisper")) # type: ignore
35
- _model_name = model_name
34
+ NotificationManager.send_notification(
35
+ NotificationType.INFO,
36
+ NotificationPriority.NORMAL,
37
+ "Loading Whisper model...",
38
+ display_time=99,
39
+ group="whisper-preload")
40
+ PrintStyle.standard(f"Loading Whisper model: {model_name}")
41
+ _model = whisper.load_model(name=model_name, download_root=files.get_abs_path("/tmp/models/whisper")) # type: ignore
42
+ _model_name = model_name
43
+ NotificationManager.send_notification(
44
+ NotificationType.INFO,
45
+ NotificationPriority.NORMAL,
46
+ "Whisper model loaded.",
47
+ display_time=2,
48
+ group="whisper-preload")
49
finally:
50
is_updating_model = False
51
python/tools/code_execution_tool.py
+6
-3
@@ -15,6 +15,7 @@ import re
15
16
@dataclass
17
class State:
18
+ ssh_enabled: bool
19
shells: dict[int, LocalInteractiveSession | SSHInteractiveSession]
20
21
@@ -77,7 +78,8 @@ class CodeExecution(Tool):
78
79
async def prepare_state(self, reset=False, session: int | None = None):
80
self.state: State | None = self.agent.get_data("_cet_state")
80
- if not self.state:
81
+ # always reset state when ssh_enabled changes
82
+ if not self.state or self.state.ssh_enabled != self.agent.config.code_exec_ssh_enabled:
83
# initialize shells dictionary if not exists
84
shells: dict[int, LocalInteractiveSession | SSHInteractiveSession] = {}
85
else:
@@ -114,7 +116,7 @@ class CodeExecution(Tool):
116
shells[session] = shell
117
await shell.connect()
118
117
- self.state = State(shells=shells)
119
+ self.state = State(shells=shells, ssh_enabled=self.agent.config.code_exec_ssh_enabled)
120
self.agent.set_data("_cet_state", self.state)
121
return self.state
122
@@ -201,9 +203,10 @@ class CodeExecution(Tool):
203
204
# Common shell prompt regex patterns (add more as needed)
205
prompt_patterns = [
204
- re.compile(r"\\(venv\\).+[$#] ?$"), # (venv) ...$ or (venv) ...#
206
+ re.compile(r"\(venv\).+[$#] ?$"), # (venv) ...$ or (venv) ...#
207
re.compile(r"root@[^:]+:[^#]+# ?$"), # root@container:~#
208
re.compile(r"[a-zA-Z0-9_.-]+@[^:]+:[^$#]+[$#] ?$"), # user@host:~$
209
+ re.compile(r"bash-\d+\.\d+\$ ?$"), # bash-3.2$ (version can vary)
210
]
211
212
# potential dialog detection