yc1838 commited on
Commit
6b91a00
·
1 Parent(s): c338c4c
.gitignore CHANGED
@@ -9,4 +9,5 @@ src/lilith_agent/__pycache__/
9
  submission.jsonl
10
  anatomy.pdf
11
  anatomy_full.pdf
12
- *.csv
 
 
9
  submission.jsonl
10
  anatomy.pdf
11
  anatomy_full.pdf
12
+ *.csv
13
+ .pycache/*
requirements.txt CHANGED
@@ -2,7 +2,8 @@ gradio
2
  requests
3
  httpx
4
  pandas
5
- langgraph>=0.2.0
 
6
  langmem>=0.0.1
7
  langchain-core>=0.3.0
8
  langchain-anthropic>=0.2.0
 
2
  requests
3
  httpx
4
  pandas
5
+ langgraph>=1.0,<2.0
6
+ langgraph-checkpoint-sqlite
7
  langmem>=0.0.1
8
  langchain-core>=0.3.0
9
  langchain-anthropic>=0.2.0
src/lilith_agent/app.py CHANGED
@@ -9,8 +9,6 @@ from typing import Callable
9
 
10
  from langchain_core.messages import AIMessage, HumanMessage, ToolMessage
11
  from langchain_core.tools import BaseTool
12
- from langgraph.checkpoint.sqlite import SqliteSaver
13
- import sqlite3
14
  import os
15
  from pathlib import Path
16
  from langgraph.graph import END, StateGraph
@@ -24,6 +22,7 @@ class AgentState(TypedDict):
24
 
25
 
26
  from lilith_agent.config import Config
 
27
  from lilith_agent.models import get_cheap_model, get_extra_strong_model
28
 
29
  log = logging.getLogger(__name__)
@@ -646,13 +645,8 @@ def build_react_agent(cfg: Config):
646
  graph.add_edge("fail_safe", "extract_memory")
647
  graph.add_edge("extract_memory", END)
648
 
649
- # Setup SQLite Saver
650
  lilith_home = Path(os.getenv("LILITH_HOME", ".lilith"))
651
- lilith_home.mkdir(parents=True, exist_ok=True)
652
- db_path = lilith_home / "threads.sqlite"
653
-
654
- conn = sqlite3.connect(str(db_path), check_same_thread=False)
655
- memory_saver = SqliteSaver(conn)
656
 
657
  compiled = graph.compile(checkpointer=memory_saver)
658
  return compiled.with_config({"recursion_limit": cfg.recursion_limit})
 
9
 
10
  from langchain_core.messages import AIMessage, HumanMessage, ToolMessage
11
  from langchain_core.tools import BaseTool
 
 
12
  import os
13
  from pathlib import Path
14
  from langgraph.graph import END, StateGraph
 
22
 
23
 
24
  from lilith_agent.config import Config
25
+ from lilith_agent.checkpointing import build_checkpointer
26
  from lilith_agent.models import get_cheap_model, get_extra_strong_model
27
 
28
  log = logging.getLogger(__name__)
 
645
  graph.add_edge("fail_safe", "extract_memory")
646
  graph.add_edge("extract_memory", END)
647
 
 
648
  lilith_home = Path(os.getenv("LILITH_HOME", ".lilith"))
649
+ memory_saver = build_checkpointer(lilith_home)
 
 
 
 
650
 
651
  compiled = graph.compile(checkpointer=memory_saver)
652
  return compiled.with_config({"recursion_limit": cfg.recursion_limit})
src/lilith_agent/checkpointing.py ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import importlib
4
+ import logging
5
+ import sqlite3
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+ log = logging.getLogger(__name__)
10
+
11
+
12
+ def build_checkpointer(lilith_home: Path) -> Any:
13
+ """Create the graph checkpointer, preferring SQLite persistence when available."""
14
+ lilith_home.mkdir(parents=True, exist_ok=True)
15
+ db_path = lilith_home / "threads.sqlite"
16
+
17
+ try:
18
+ sqlite_module = importlib.import_module("langgraph.checkpoint.sqlite")
19
+ sqlite_saver_cls = sqlite_module.SqliteSaver
20
+ except ModuleNotFoundError as exc:
21
+ memory_module = importlib.import_module("langgraph.checkpoint.memory")
22
+ in_memory_saver_cls = memory_module.InMemorySaver
23
+ log.warning(
24
+ "langgraph-checkpoint-sqlite missing; using InMemorySaver instead. "
25
+ "Install langgraph-checkpoint-sqlite to restore persistent thread checkpoints. "
26
+ "Original import error: %s",
27
+ exc,
28
+ )
29
+ return in_memory_saver_cls()
30
+
31
+ conn = sqlite3.connect(str(db_path), check_same_thread=False)
32
+ return sqlite_saver_cls(conn)
test_build_checkpointer_uses_sqlite_when_available/threads.sqlite ADDED
File without changes
tests/test_checkpointing.py ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import importlib
4
+ import types
5
+ import unittest
6
+ from pathlib import Path
7
+ from unittest.mock import patch
8
+
9
+ from lilith_agent.checkpointing import build_checkpointer
10
+
11
+
12
+ class CheckpointingTests(unittest.TestCase):
13
+ def test_build_checkpointer_uses_sqlite_when_available(self) -> None:
14
+ tmp_path = Path(self._testMethodName)
15
+
16
+ class FakeSqliteSaver:
17
+ def __init__(self, conn) -> None:
18
+ self.conn = conn
19
+
20
+ def fake_import_module(name: str):
21
+ if name == "langgraph.checkpoint.sqlite":
22
+ return types.SimpleNamespace(SqliteSaver=FakeSqliteSaver)
23
+ raise AssertionError(f"unexpected import: {name}")
24
+
25
+ with patch.object(importlib, "import_module", side_effect=fake_import_module):
26
+ saver = build_checkpointer(tmp_path)
27
+
28
+ self.assertIsInstance(saver, FakeSqliteSaver)
29
+ self.assertTrue((tmp_path / "threads.sqlite").exists())
30
+
31
+ def test_build_checkpointer_falls_back_to_in_memory_when_sqlite_package_missing(self) -> None:
32
+ tmp_path = Path(self._testMethodName)
33
+
34
+ class FakeInMemorySaver:
35
+ pass
36
+
37
+ def fake_import_module(name: str):
38
+ if name == "langgraph.checkpoint.sqlite":
39
+ raise ModuleNotFoundError("No module named 'langgraph.checkpoint.sqlite'")
40
+ if name == "langgraph.checkpoint.memory":
41
+ return types.SimpleNamespace(InMemorySaver=FakeInMemorySaver)
42
+ raise AssertionError(f"unexpected import: {name}")
43
+
44
+ with patch.object(importlib, "import_module", side_effect=fake_import_module):
45
+ with self.assertLogs("lilith_agent.checkpointing", level="WARNING") as captured:
46
+ saver = build_checkpointer(tmp_path)
47
+
48
+ self.assertIsInstance(saver, FakeInMemorySaver)
49
+ self.assertIn("langgraph-checkpoint-sqlite missing", "\n".join(captured.output))
50
+
51
+ def test_requirements_include_langgraph_checkpoint_sqlite(self) -> None:
52
+ requirements = Path("requirements.txt").read_text().splitlines()
53
+ self.assertIn("langgraph-checkpoint-sqlite", requirements)
54
+
55
+
56
+ if __name__ == "__main__":
57
+ unittest.main()