personal memory agent
1# SPDX-License-Identifier: AGPL-3.0-only
2# Copyright (c) 2026 sol pbc
3
4from __future__ import annotations
5
6import logging
7from pathlib import Path
8from types import SimpleNamespace
9from typing import Any
10
11import pytest
12
13from solstone.think import supervisor
14from solstone.think.providers import (
15 local_cuda,
16 local_vulkan,
17 parakeet_install,
18 parakeet_server,
19)
20from solstone.think.providers.parakeet_placement import (
21 PARAKEET_ATT_CONTEXT_ENV,
22 PARAKEET_ATT_CONTEXT_FRAMES,
23)
24
25_LaunchRecord = dict[str, Any]
26
27
28def _assert_att_context(launch: _LaunchRecord) -> None:
29 assert launch["env"][PARAKEET_ATT_CONTEXT_ENV] == str(PARAKEET_ATT_CONTEXT_FRAMES)
30
31
32def _launch_log(backend: str) -> str:
33 return (
34 "parakeet-server launch "
35 f"backend={backend} attention=local "
36 f"att_context_frames={PARAKEET_ATT_CONTEXT_FRAMES}"
37 )
38
39
40@pytest.fixture(autouse=True)
41def _reset_vulkan_detect_cache():
42 local_vulkan.reset_detect_cache()
43 yield
44 local_vulkan.reset_detect_cache()
45
46
47class _FakeProcess:
48 def __init__(self, poll_value: int | None, returncode: int | None = None):
49 self.pid = 12345
50 self.returncode = returncode
51 self._poll_value = poll_value
52
53 def poll(self) -> int | None:
54 return self._poll_value
55
56
57class _FakeManaged:
58 def __init__(self, poll_value: int | None, returncode: int | None = None):
59 self.name = supervisor.PARAKEET_SERVER_PROCESS_NAME
60 self.process = _FakeProcess(poll_value, returncode)
61 self.cleanup_called = False
62
63 def cleanup(self) -> None:
64 self.cleanup_called = True
65
66
67def _nvidia_probe(
68 *,
69 vram_mib: int,
70 memory_source: str = local_cuda.MEMORY_SOURCE_NVIDIA_VRAM,
71) -> local_cuda.NvidiaProbe:
72 return local_cuda.NvidiaProbe(
73 index=0,
74 compute_cap="sm_75",
75 driver_cuda_version=13,
76 vram_mib=vram_mib,
77 tiering_memory_mib=vram_mib,
78 memory_source=memory_source,
79 detected=True,
80 )
81
82
83def _patch_ready_parakeet_launch(
84 monkeypatch,
85 launches: list[_LaunchRecord],
86 *,
87 poll_sequence: list[tuple[int | None, int | None]] | None = None,
88) -> list[tuple[str, int]]:
89 def fake_ensure(backend: str):
90 return Path(f"/tmp/{backend}/parakeet-server"), Path("/tmp/model.gguf")
91
92 monkeypatch.setattr(parakeet_install, "ensure_artifacts_installed", fake_ensure)
93 monkeypatch.setattr(supervisor, "find_available_port", lambda: 45123)
94 ports: list[tuple[str, int]] = []
95 monkeypatch.setattr(
96 supervisor,
97 "write_service_port",
98 lambda service, port: ports.append((service, port)),
99 )
100 monkeypatch.setattr(supervisor, "parakeet_physical_thread_count", lambda: 6)
101 monkeypatch.setattr(
102 supervisor, "_parakeet_runtime_library_dirs", lambda: [Path("/parakeet/lib")]
103 )
104 monkeypatch.setattr(
105 parakeet_server, "probe_state", lambda: (parakeet_server.STATE_READY, None)
106 )
107
108 sequence = poll_sequence or [(None, None)]
109
110 def fake_launch_process(
111 name, cmd, *, restart=False, shutdown_timeout=15, ref=None, env=None
112 ):
113 index = min(len(launches), len(sequence) - 1)
114 poll_value, returncode = sequence[index]
115 managed = _FakeManaged(poll_value, returncode)
116 launches.append(
117 {
118 "name": name,
119 "cmd": cmd,
120 "restart": restart,
121 "env": env,
122 "managed": managed,
123 }
124 )
125 return managed
126
127 monkeypatch.setattr(supervisor, "_launch_process", fake_launch_process)
128 return ports
129
130
131def test_parakeet_server_is_sweepable_orphan_name() -> None:
132 assert (
133 supervisor.PARAKEET_SERVER_PROCESS_NAME in supervisor._LOCAL_SERVER_PROCTITLES
134 )
135 assert supervisor._is_sweepable_orphan_name("parakeet-server") is True
136
137
138def test_resolve_launch_plan_cpu_ignores_gpu() -> None:
139 gpu = SimpleNamespace(index=2)
140
141 plan = supervisor.resolve_parakeet_server_launch_plan("cpu", gpu)
142
143 assert plan == supervisor.ParakeetServerLaunchPlan("cpu", {}, None)
144
145
146def test_resolve_launch_plan_auto_uses_selected_gpu() -> None:
147 gpu = SimpleNamespace(index=2)
148
149 plan = supervisor.resolve_parakeet_server_launch_plan("auto", gpu)
150
151 assert plan == supervisor.ParakeetServerLaunchPlan(
152 "vulkan", {"GGML_VK_VISIBLE_DEVICES": "2"}, 2
153 )
154
155
156def test_resolve_launch_plan_auto_without_gpu_uses_cpu() -> None:
157 plan = supervisor.resolve_parakeet_server_launch_plan("auto", None)
158
159 assert plan == supervisor.ParakeetServerLaunchPlan("cpu", {}, None)
160
161
162def test_resolve_launch_plan_rejects_invalid_device() -> None:
163 with pytest.raises(ValueError, match="auto"):
164 supervisor.resolve_parakeet_server_launch_plan("bogus", None)
165
166
167def test_parakeet_physical_thread_count_uses_physical(monkeypatch) -> None:
168 monkeypatch.setattr(supervisor.psutil, "cpu_count", lambda logical=False: 6)
169 monkeypatch.setattr(
170 supervisor.os,
171 "cpu_count",
172 lambda: pytest.fail("logical count should not be primary"),
173 )
174
175 assert supervisor.parakeet_physical_thread_count() == 6
176
177
178@pytest.mark.parametrize(
179 ("logical_count", "expected"),
180 [
181 (8, 4),
182 (None, 1),
183 ],
184)
185def test_parakeet_physical_thread_count_fallback_halves_logical(
186 monkeypatch, logical_count, expected
187) -> None:
188 monkeypatch.setattr(supervisor.psutil, "cpu_count", lambda logical=False: None)
189 monkeypatch.setattr(supervisor.os, "cpu_count", lambda: logical_count)
190
191 assert supervisor.parakeet_physical_thread_count() == expected
192
193
194def test_build_parakeet_cmd_load_bearing_invariants() -> None:
195 cmd = supervisor._build_parakeet_cmd(
196 Path("/tmp/parakeet-server"),
197 Path("/tmp/model.gguf"),
198 45123,
199 6,
200 )
201
202 assert "127.0.0.1" in cmd
203 assert "0.0.0.0" not in cmd
204 assert cmd[cmd.index("--model") + 1] == "/tmp/model.gguf"
205 assert cmd[cmd.index("--threads") + 1] == "6"
206
207
208def test_parakeet_runtime_library_dirs_aliases_bundled_libgomp(
209 tmp_path, monkeypatch
210) -> None:
211 site_dir = tmp_path / "site-packages"
212 libs_dir = site_dir / "scikit_learn.libs"
213 libs_dir.mkdir(parents=True)
214 bundled = libs_dir / "libgomp-e985bcbb.so.1.0.0"
215 bundled.write_text("runtime")
216 journal = tmp_path / "journal"
217
218 monkeypatch.setattr(supervisor, "_site_package_search_dirs", lambda: [site_dir])
219 monkeypatch.setattr(supervisor, "get_journal", lambda: str(journal))
220
221 result = supervisor._parakeet_runtime_library_dirs()
222
223 assert result == [journal / "cache" / "providers" / "parakeet" / "lib"]
224 alias = result[0] / "libgomp.so.1"
225 assert alias.is_symlink()
226 assert alias.resolve() == bundled.resolve()
227
228
229def test_with_library_path_prepends_dirs() -> None:
230 env = {"LD_LIBRARY_PATH": "/existing"}
231
232 result = supervisor._with_library_path(env, [Path("/parakeet/lib")])
233
234 assert result["LD_LIBRARY_PATH"] == "/parakeet/lib:/existing"
235 assert env["LD_LIBRARY_PATH"] == "/existing"
236
237
238def test_start_parakeet_server_vulkan_crash_falls_back_to_cpu(
239 monkeypatch,
240 tmp_path,
241 caplog,
242) -> None:
243 monkeypatch.setenv("SOLSTONE_JOURNAL", str(tmp_path / "journal"))
244 monkeypatch.delenv("GGML_VK_VISIBLE_DEVICES", raising=False)
245 monkeypatch.delenv("LD_LIBRARY_PATH", raising=False)
246 monkeypatch.setattr(supervisor.sys, "platform", "linux")
247 monkeypatch.setattr(supervisor, "linux_stt_uses_parakeet_cpp", lambda: True)
248 monkeypatch.setattr(supervisor, "_configured_parakeet_device", lambda: "auto")
249 monkeypatch.setattr(supervisor, "is_local_provider_needed", lambda: True)
250 monkeypatch.setattr(
251 "solstone.think.providers.local_endpoint.resolve_local_endpoint",
252 lambda: SimpleNamespace(is_bundled=True),
253 )
254 gpu = local_vulkan.VulkanDevice(
255 2,
256 "NVIDIA Test GPU",
257 local_vulkan.VK_TYPE_DISCRETE,
258 12288,
259 )
260 monkeypatch.setattr(local_vulkan, "detect_gpus", lambda: [gpu])
261 monkeypatch.setattr(local_vulkan, "select_device", lambda devices: devices[0])
262 monkeypatch.setattr(local_vulkan, "classify", lambda _device: "discrete")
263 monkeypatch.setattr(
264 local_cuda, "probe_nvidia_gpu", lambda: _nvidia_probe(vram_mib=12288)
265 )
266
267 def fake_ensure(backend: str):
268 return Path(f"/tmp/{backend}/parakeet-server"), Path("/tmp/model.gguf")
269
270 monkeypatch.setattr(parakeet_install, "ensure_artifacts_installed", fake_ensure)
271 monkeypatch.setattr(supervisor, "find_available_port", lambda: 45123)
272 ports: list[tuple[str, int]] = []
273 monkeypatch.setattr(
274 supervisor,
275 "write_service_port",
276 lambda service, port: ports.append((service, port)),
277 )
278 monkeypatch.setattr(supervisor, "parakeet_physical_thread_count", lambda: 6)
279 monkeypatch.setattr(
280 supervisor, "_parakeet_runtime_library_dirs", lambda: [Path("/parakeet/lib")]
281 )
282 monkeypatch.setattr(
283 parakeet_server, "probe_state", lambda: (parakeet_server.STATE_READY, None)
284 )
285 terminated = []
286 monkeypatch.setattr(
287 supervisor,
288 "_terminate_managed",
289 lambda managed, timeout, *, reason: terminated.append(
290 (managed, timeout, reason)
291 ),
292 )
293
294 launches: list[_LaunchRecord] = []
295
296 def fake_launch_process(
297 name, cmd, *, restart=False, shutdown_timeout=15, ref=None, env=None
298 ):
299 managed = _FakeManaged(9, 9) if not launches else _FakeManaged(None, None)
300 launches.append(
301 {
302 "name": name,
303 "cmd": cmd,
304 "restart": restart,
305 "env": env,
306 "managed": managed,
307 }
308 )
309 return managed
310
311 monkeypatch.setattr(supervisor, "_launch_process", fake_launch_process)
312
313 caplog.set_level(logging.INFO)
314 result = supervisor.start_parakeet_server()
315
316 assert result is launches[1]["managed"]
317 assert len(launches) == 2
318 assert launches[0]["name"] == supervisor.PARAKEET_SERVER_PROCESS_NAME
319 assert launches[0]["restart"] is True
320 assert launches[0]["cmd"][0] == "/tmp/vulkan/parakeet-server"
321 assert launches[0]["env"]["GGML_VK_VISIBLE_DEVICES"] == "2"
322 assert launches[0]["env"]["LD_LIBRARY_PATH"] == "/parakeet/lib"
323 _assert_att_context(launches[0])
324 assert launches[1]["cmd"][0] == "/tmp/cpu/parakeet-server"
325 assert "GGML_VK_VISIBLE_DEVICES" not in launches[1]["env"]
326 assert launches[1]["env"]["LD_LIBRARY_PATH"] == "/parakeet/lib"
327 _assert_att_context(launches[1])
328 assert launches[0]["managed"].cleanup_called is True
329 assert terminated[0][0] is launches[0]["managed"]
330 assert ports == [("parakeet-cpp", 45123)]
331 assert parakeet_server.read_parakeet_placement() == "cpu"
332 assert _launch_log("vulkan") in caplog.text
333 assert _launch_log("cpu") in caplog.text
334
335
336def test_start_parakeet_server_forces_cpu_on_small_single_discrete_bundled_brain(
337 monkeypatch,
338 tmp_path,
339 caplog,
340) -> None:
341 monkeypatch.setenv("SOLSTONE_JOURNAL", str(tmp_path / "journal"))
342 monkeypatch.delenv("GGML_VK_VISIBLE_DEVICES", raising=False)
343 monkeypatch.setattr(supervisor.sys, "platform", "linux")
344 monkeypatch.setattr(supervisor, "linux_stt_uses_parakeet_cpp", lambda: True)
345 monkeypatch.setattr(supervisor, "_configured_parakeet_device", lambda: "auto")
346 monkeypatch.setattr(supervisor, "is_local_provider_needed", lambda: True)
347 monkeypatch.setattr(
348 "solstone.think.providers.local_endpoint.resolve_local_endpoint",
349 lambda: SimpleNamespace(is_bundled=True),
350 )
351 gpu = local_vulkan.VulkanDevice(
352 2,
353 "NVIDIA Test GPU",
354 local_vulkan.VK_TYPE_DISCRETE,
355 6144,
356 )
357 monkeypatch.setattr(local_vulkan, "detect_gpus", lambda: [gpu])
358 monkeypatch.setattr(local_vulkan, "select_device", lambda devices: devices[0])
359 monkeypatch.setattr(local_vulkan, "classify", lambda _device: "discrete")
360 monkeypatch.setattr(
361 local_cuda, "probe_nvidia_gpu", lambda: _nvidia_probe(vram_mib=6144)
362 )
363 launches: list[_LaunchRecord] = []
364 ports = _patch_ready_parakeet_launch(monkeypatch, launches)
365
366 caplog.set_level(logging.INFO)
367 result = supervisor.start_parakeet_server()
368
369 assert result is launches[0]["managed"]
370 assert len(launches) == 1
371 assert launches[0]["cmd"][0] == "/tmp/cpu/parakeet-server"
372 assert "GGML_VK_VISIBLE_DEVICES" not in launches[0]["env"]
373 _assert_att_context(launches[0])
374 assert ports == [("parakeet-cpp", 45123)]
375 assert parakeet_server.read_parakeet_placement() == "cpu"
376 assert (
377 "parakeet-server auto placement resolved to CPU: tier=floor "
378 "tier_resident_mib=4137 parakeet_worst_case_mib=2947 margin_mib=1024 "
379 "required_mib=8108 gpu_vram_mib=6144 placement=cpu"
380 ) in caplog.text
381 assert _launch_log("cpu") in caplog.text
382
383
384def test_start_parakeet_server_brain_lane_inactive_keeps_auto_vulkan(
385 monkeypatch,
386 tmp_path,
387) -> None:
388 monkeypatch.setenv("SOLSTONE_JOURNAL", str(tmp_path / "journal"))
389 monkeypatch.setattr(supervisor.sys, "platform", "linux")
390 monkeypatch.setattr(supervisor, "linux_stt_uses_parakeet_cpp", lambda: True)
391 monkeypatch.setattr(supervisor, "_configured_parakeet_device", lambda: "auto")
392 monkeypatch.setattr(supervisor, "is_local_provider_needed", lambda: False)
393 gpu = local_vulkan.VulkanDevice(
394 2,
395 "NVIDIA Test GPU",
396 local_vulkan.VK_TYPE_DISCRETE,
397 6144,
398 )
399 monkeypatch.setattr(local_vulkan, "detect_gpus", lambda: [gpu])
400 monkeypatch.setattr(local_vulkan, "select_device", lambda devices: devices[0])
401 monkeypatch.setattr(local_vulkan, "classify", lambda _device: "discrete")
402 monkeypatch.setattr(
403 local_cuda, "probe_nvidia_gpu", lambda: _nvidia_probe(vram_mib=6144)
404 )
405 launches: list[_LaunchRecord] = []
406 _patch_ready_parakeet_launch(monkeypatch, launches)
407
408 result = supervisor.start_parakeet_server()
409
410 assert result is launches[0]["managed"]
411 assert len(launches) == 1
412 assert launches[0]["cmd"][0] == "/tmp/vulkan/parakeet-server"
413 assert launches[0]["env"]["GGML_VK_VISIBLE_DEVICES"] == "2"
414 _assert_att_context(launches[0])
415 assert parakeet_server.read_parakeet_placement() == "gpu"
416
417
418def test_start_parakeet_server_explicit_cpu_skips_auto_placement(
419 monkeypatch,
420 tmp_path,
421) -> None:
422 monkeypatch.setenv("SOLSTONE_JOURNAL", str(tmp_path / "journal"))
423 monkeypatch.setattr(supervisor.sys, "platform", "linux")
424 monkeypatch.setattr(supervisor, "linux_stt_uses_parakeet_cpp", lambda: True)
425 monkeypatch.setattr(supervisor, "_configured_parakeet_device", lambda: "cpu")
426 monkeypatch.setattr(
427 local_vulkan,
428 "detect_gpus",
429 lambda: pytest.fail("explicit CPU should not enumerate Vulkan devices"),
430 )
431 monkeypatch.setattr(
432 local_cuda,
433 "probe_nvidia_gpu",
434 lambda: pytest.fail("explicit CPU should not probe NVIDIA"),
435 )
436 launches: list[_LaunchRecord] = []
437 _patch_ready_parakeet_launch(monkeypatch, launches)
438
439 result = supervisor.start_parakeet_server()
440
441 assert result is launches[0]["managed"]
442 assert launches[0]["cmd"][0] == "/tmp/cpu/parakeet-server"
443 _assert_att_context(launches[0])
444 assert parakeet_server.read_parakeet_placement() == "cpu"
445
446
447def test_parakeet_attention_context_overrides_ambient_env(
448 monkeypatch,
449 tmp_path,
450) -> None:
451 monkeypatch.setenv("SOLSTONE_JOURNAL", str(tmp_path / "journal"))
452 monkeypatch.setenv(PARAKEET_ATT_CONTEXT_ENV, "0")
453 monkeypatch.setattr(supervisor.sys, "platform", "linux")
454 monkeypatch.setattr(supervisor, "linux_stt_uses_parakeet_cpp", lambda: True)
455 monkeypatch.setattr(supervisor, "_configured_parakeet_device", lambda: "cpu")
456 monkeypatch.setattr(
457 local_vulkan,
458 "detect_gpus",
459 lambda: pytest.fail("explicit CPU should not enumerate Vulkan devices"),
460 )
461 monkeypatch.setattr(
462 local_cuda,
463 "probe_nvidia_gpu",
464 lambda: pytest.fail("explicit CPU should not probe NVIDIA"),
465 )
466 launches: list[_LaunchRecord] = []
467 _patch_ready_parakeet_launch(monkeypatch, launches)
468
469 result = supervisor.start_parakeet_server()
470
471 assert result is launches[0]["managed"]
472 assert launches[0]["cmd"][0] == "/tmp/cpu/parakeet-server"
473 _assert_att_context(launches[0])
474 assert parakeet_server.read_parakeet_placement() == "cpu"
475
476
477@pytest.mark.parametrize(
478 (
479 "sys_platform",
480 "machine",
481 "backend",
482 "available_bytes",
483 "confidential",
484 "confidential_audio",
485 "local_backend",
486 "expected",
487 ),
488 [
489 ("linux", "x86_64", None, 5 * 1024**3, False, True, "parakeet", True),
490 ("linux", "x86_64", None, 3 * 1024**3, False, True, "parakeet", False),
491 (
492 "linux",
493 "x86_64",
494 "parakeet",
495 3 * 1024**3,
496 False,
497 True,
498 "parakeet",
499 True,
500 ),
501 (
502 "linux",
503 "x86_64",
504 "parakeet-cpp",
505 3 * 1024**3,
506 False,
507 True,
508 "parakeet",
509 True,
510 ),
511 ("linux", "aarch64", None, 5 * 1024**3, False, True, "parakeet", True),
512 (
513 "linux",
514 "aarch64",
515 "parakeet",
516 3 * 1024**3,
517 False,
518 True,
519 "parakeet",
520 True,
521 ),
522 ("darwin", "arm64", None, 5 * 1024**3, False, True, "parakeet", False),
523 (
524 "linux",
525 "x86_64",
526 "parakeet",
527 3 * 1024**3,
528 True,
529 True,
530 "parakeet",
531 True,
532 ),
533 ("linux", "x86_64", None, 3 * 1024**3, True, True, "parakeet", False),
534 ("linux", "x86_64", None, 3 * 1024**3, True, False, "parakeet", True),
535 ("linux", "x86_64", None, 3 * 1024**3, True, True, None, False),
536 ],
537)
538def test_linux_stt_uses_parakeet_cpp_truth_table(
539 monkeypatch,
540 sys_platform: str,
541 machine: str,
542 backend: str | None,
543 available_bytes: int,
544 confidential: bool,
545 confidential_audio: bool,
546 local_backend: str | None,
547 expected: bool,
548):
549 monkeypatch.setattr(supervisor.sys, "platform", sys_platform)
550 monkeypatch.setattr(supervisor.platform, "machine", lambda: machine)
551 transcribe_config = {}
552 if backend is not None:
553 transcribe_config["backend"] = backend
554 if not confidential_audio:
555 transcribe_config["confidential_audio"] = False
556 config = {"transcribe": transcribe_config} if transcribe_config else {}
557 monkeypatch.setattr(supervisor, "read_journal_config", lambda: config)
558 monkeypatch.setattr(supervisor, "read_available_bytes", lambda: available_bytes)
559 monkeypatch.setattr(supervisor, "stt_local_floor_bytes", lambda: 4 * 1024**3)
560 monkeypatch.setattr(supervisor, "local_stt_backend", lambda: local_backend)
561 monkeypatch.setattr(
562 "solstone.think.services.spp.confidential_provenance",
563 lambda: {"enabled_at": "2026-05-24T00:00:00Z"} if confidential else None,
564 )
565 monkeypatch.delenv("GOOGLE_API_KEY", raising=False)
566
567 assert supervisor.linux_stt_uses_parakeet_cpp() is expected
568
569
570def test_start_parakeet_server_early_returns_for_non_linux(
571 monkeypatch, tmp_path
572) -> None:
573 monkeypatch.setenv("SOLSTONE_JOURNAL", str(tmp_path / "journal"))
574 parakeet_server.write_parakeet_placement("gpu")
575 monkeypatch.setattr(supervisor.sys, "platform", "darwin")
576 monkeypatch.setattr(supervisor.platform, "machine", lambda: "arm64")
577 monkeypatch.setattr(
578 supervisor,
579 "read_journal_config",
580 lambda: pytest.fail("config should not be read off-linux"),
581 )
582
583 assert supervisor.start_parakeet_server() is None
584 assert parakeet_server.read_parakeet_placement() is None
585
586
587def test_start_parakeet_server_early_returns_for_other_backend(
588 monkeypatch, tmp_path
589) -> None:
590 monkeypatch.setenv("SOLSTONE_JOURNAL", str(tmp_path / "journal"))
591 parakeet_server.write_parakeet_placement("gpu")
592 monkeypatch.setattr(supervisor.sys, "platform", "linux")
593 monkeypatch.setattr(supervisor.platform, "machine", lambda: "x86_64")
594 monkeypatch.setattr(
595 supervisor,
596 "read_journal_config",
597 lambda: {"transcribe": {"backend": "confidential"}},
598 )
599 monkeypatch.setattr(
600 "solstone.think.services.spp.confidential_provenance",
601 lambda: {"enabled_at": "2026-05-24T00:00:00Z"},
602 )
603
604 assert supervisor.start_parakeet_server() is None
605 assert parakeet_server.read_parakeet_placement() is None
606
607
608def test_start_parakeet_server_starts_background_install_when_missing(
609 monkeypatch,
610 tmp_path,
611) -> None:
612 monkeypatch.setenv("SOLSTONE_JOURNAL", str(tmp_path / "journal"))
613 parakeet_server.write_parakeet_placement("gpu")
614 monkeypatch.setattr(supervisor, "linux_stt_uses_parakeet_cpp", lambda: True)
615 monkeypatch.setattr(supervisor, "_configured_parakeet_device", lambda: "cpu")
616 started: list[str] = []
617
618 class _Thread:
619 def __init__(self, *, target, name, daemon):
620 self.target = target
621 self.name = name
622 self.daemon = daemon
623
624 def is_alive(self):
625 return False
626
627 def start(self):
628 started.append(self.name)
629
630 monkeypatch.setattr(supervisor.threading, "Thread", _Thread)
631 monkeypatch.setattr(
632 parakeet_install,
633 "ensure_artifacts_installed",
634 lambda _backend: (_ for _ in ()).throw(
635 parakeet_install.ParakeetProviderError(
636 "model_missing", "Parakeet model is not installed."
637 )
638 ),
639 )
640 monkeypatch.setattr(
641 parakeet_install,
642 "inspect_readiness",
643 lambda: {
644 "install_state": "idle",
645 "binary_installed": False,
646 "model_installed": False,
647 },
648 )
649 monkeypatch.setattr(supervisor, "_parakeet_bootstrap_thread", None)
650
651 assert supervisor.start_parakeet_server() is None
652 assert started == ["parakeet-cpp-provider-bootstrap"]
653 assert parakeet_server.read_parakeet_placement() is None
654
655
656def test_parakeet_bootstrap_worker_requests_start_after_install(monkeypatch) -> None:
657 calls: list[str] = []
658 requests: list[str] = []
659
660 monkeypatch.setattr(
661 parakeet_install, "install_parakeet", lambda: calls.append("install")
662 )
663 monkeypatch.setattr(
664 supervisor, "_request_parakeet_server_start", lambda: requests.append("start")
665 )
666 monkeypatch.setattr(
667 supervisor,
668 "_parakeet_bootstrap_thread",
669 supervisor.threading.current_thread(),
670 )
671
672 supervisor._run_parakeet_bootstrap_worker()
673
674 assert calls == ["install"]
675 assert requests == ["start"]
676 assert supervisor._parakeet_bootstrap_thread is None
677
678
679def test_parakeet_bootstrap_worker_uses_captured_journal_path(
680 monkeypatch, tmp_path
681) -> None:
682 calls: list[Path] = []
683 requests: list[str] = []
684 journal_path = tmp_path / "original"
685
686 def fake_install_parakeet(*, journal_path=None):
687 calls.append(journal_path)
688
689 monkeypatch.setattr(parakeet_install, "install_parakeet", fake_install_parakeet)
690 monkeypatch.setattr(
691 supervisor, "_request_parakeet_server_start", lambda: requests.append("start")
692 )
693 monkeypatch.setattr(
694 supervisor,
695 "_parakeet_bootstrap_thread",
696 supervisor.threading.current_thread(),
697 )
698
699 supervisor._run_parakeet_bootstrap_worker(journal_path)
700
701 assert calls == [journal_path]
702 assert requests == ["start"]