Skip to content

Commit 83f3824

Browse files
fix: stop deleting dependency bytecode on startup
Fixes #50\n\nStartup no longer deletes dependency caches. Real bounded CLI and FastAPI startup smoke tests verify a dependency cache remains unchanged.
1 parent 83ec64e commit 83f3824

2 files changed

Lines changed: 93 additions & 7 deletions

File tree

‎main.py‎

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
(Run `python main.py` to launch the agent)
44
"""
55

6-
import pathlib
7-
import shutil
86
import sys
97
import warnings
108

@@ -57,10 +55,6 @@ def _terminal_supports_unicode() -> bool:
5755
if _UNICODE_OK: _SPINNER_FRAMES = ["◜", "◠", "◝", "◞", "◡", "◟"]; _BAR_FILL = "█"; _BAR_EMPTY = "░"; _CHECK = "✓"; _CIRCLE = "○"; _HALF = "◷"; _BOX_TL = "┌"; _BOX_H = "─"; _BOX_BL = "└"; _BOX_V = "│"; _DOT = "·"; _NBHYPHEN = "‑"
5856
else: _SPINNER_FRAMES = ["/", "-", "\\", "|"]; _BAR_FILL = "#"; _BAR_EMPTY = "."; _CHECK = "+"; _CIRCLE = "o"; _HALF = ">"; _BOX_TL = "+"; _BOX_H = "-"; _BOX_BL = "+"; _BOX_V = "|"; _DOT = "."; _NBHYPHEN = "-"
5957

60-
# Delete stale __pycache__ before any imports to avoid loading deprecated bytecode
61-
for p in pathlib.Path(__file__).parent.rglob("__pycache__"):
62-
shutil.rmtree(p, ignore_errors=True)
63-
6458
import ast
6559
import json
6660
import os
@@ -4756,7 +4750,6 @@ def _run_recovered_tasks(*, max_tasks: int = 20) -> list[dict[str, Any]]:
47564750

47574751

47584752
def main() -> None:
4759-
# Bytecode cache cleared already at module level (see top of file)
47604753
global _provider_config, llm_provider, DEEPSEEK_MODEL, DEEPSEEK_MODEL_SIMPLE, DEEPSEEK_MODEL_COMPLEX, MODEL_NAME
47614754
global _agent_profile_mode
47624755

‎tests/test_startup_cache.py‎

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import os
2+
import subprocess
3+
import sys
4+
import tempfile
5+
import time
6+
import unittest
7+
from pathlib import Path
8+
9+
10+
STARTUP_TIMEOUT_SECONDS = 30
11+
_CREDENTIAL_ENV_VARS = (
12+
"KYROZEN_API_KEY",
13+
"DEEPSEEK_API_KEY",
14+
"OPENAI_API_KEY",
15+
"ANTHROPIC_API_KEY",
16+
"GEMINI_API_KEY",
17+
)
18+
19+
20+
class StartupCacheTests(unittest.TestCase):
21+
def _run_startup(self, command: list[str], *, input_text: str = "") -> None:
22+
repository = Path(__file__).parents[1].resolve()
23+
with tempfile.TemporaryDirectory(
24+
prefix=".openkyrozen-startup-cache-", dir=repository
25+
) as fixture_dir, tempfile.TemporaryDirectory() as home_dir, tempfile.TemporaryDirectory() as state_dir:
26+
# This models a dependency cache below the checkout. The old
27+
# startup cleanup deleted it even though OpenKyrozen did not own it.
28+
sentinel = (
29+
Path(fixture_dir)
30+
/ "venv"
31+
/ "lib"
32+
/ "python3.12"
33+
/ "site-packages"
34+
/ "dependency"
35+
/ "__pycache__"
36+
/ "dependency.cpython-312.pyc"
37+
)
38+
sentinel.parent.mkdir(parents=True)
39+
sentinel.write_bytes(b"dependency-bytecode-sentinel")
40+
41+
env = os.environ.copy()
42+
env.update(
43+
{
44+
"HOME": home_dir,
45+
"KYROZEN_PROVIDER": "ollama",
46+
"KYROZEN_DB_PATH": str(Path(state_dir) / "startup.sqlite3"),
47+
"KYROZEN_DISABLE_VECTOR_INDEX": "1",
48+
"KYROZEN_EXECUTION_SURFACE": "cli",
49+
"KYROZEN_TURN_LOG": str(Path(state_dir) / "turns.log"),
50+
"PYTHONPATH": os.pathsep.join(
51+
part for part in (str(repository), env.get("PYTHONPATH", "")) if part
52+
),
53+
}
54+
)
55+
for variable in _CREDENTIAL_ENV_VARS:
56+
env.pop(variable, None)
57+
58+
started = time.monotonic()
59+
result = subprocess.run(
60+
command,
61+
cwd=repository,
62+
env=env,
63+
input=input_text,
64+
capture_output=True,
65+
text=True,
66+
timeout=STARTUP_TIMEOUT_SECONDS,
67+
)
68+
elapsed = time.monotonic() - started
69+
70+
output = result.stdout + result.stderr
71+
self.assertEqual(result.returncode, 0, output)
72+
self.assertLess(
73+
elapsed,
74+
STARTUP_TIMEOUT_SECONDS,
75+
f"startup exceeded {STARTUP_TIMEOUT_SECONDS}s:\n{output}",
76+
)
77+
self.assertTrue(sentinel.is_file(), "startup deleted a dependency cache")
78+
self.assertEqual(sentinel.read_bytes(), b"dependency-bytecode-sentinel")
79+
80+
def test_cli_startup_is_bounded_and_preserves_dependency_bytecode(self):
81+
self._run_startup([sys.executable, "main.py"], input_text="/quit\n")
82+
83+
def test_web_startup_is_bounded_and_preserves_dependency_bytecode(self):
84+
script = (
85+
"import asyncio; import server; "
86+
"asyncio.run(server.startup()); "
87+
"asyncio.run(server.shutdown()); print('web-startup-ready')"
88+
)
89+
self._run_startup([sys.executable, "-c", script])
90+
91+
92+
if __name__ == "__main__":
93+
unittest.main()

0 commit comments

Comments
 (0)