Spaces:
Sleeping
Sleeping
yc1838 commited on
Commit ·
6b91a00
1
Parent(s): c338c4c
fix
Browse files
.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>=
|
|
|
|
| 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 |
-
|
| 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()
|