mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-05-23 08:25:57 +00:00
fix(packaging): add postgres extra for store/checkpointer supportFix postgres extra install guidance (#2584)
* Fix postgres extra install guidance * Fix postgres install message lint * Format postgres install messages * Fix postgres install guidance and config docs
This commit is contained in:
@@ -14,12 +14,13 @@ class CheckpointerConfig(BaseModel):
|
|||||||
description="Checkpointer backend type. "
|
description="Checkpointer backend type. "
|
||||||
"'memory' is in-process only (lost on restart). "
|
"'memory' is in-process only (lost on restart). "
|
||||||
"'sqlite' persists to a local file (requires langgraph-checkpoint-sqlite). "
|
"'sqlite' persists to a local file (requires langgraph-checkpoint-sqlite). "
|
||||||
"'postgres' persists to PostgreSQL (requires langgraph-checkpoint-postgres)."
|
"'postgres' persists to PostgreSQL (install with deerflow-harness[postgres])."
|
||||||
)
|
)
|
||||||
connection_string: str | None = Field(
|
connection_string: str | None = Field(
|
||||||
default=None,
|
default=None,
|
||||||
description="Connection string for sqlite (file path) or postgres (DSN). "
|
description="Connection string for sqlite (file path) or postgres (DSN). "
|
||||||
"Required for sqlite and postgres types. "
|
"Optional for sqlite and defaults to 'store.db' when omitted. "
|
||||||
|
"Required for postgres. "
|
||||||
"For sqlite, use a file path like '.deer-flow/checkpoints.db' or ':memory:' for in-memory. "
|
"For sqlite, use a file path like '.deer-flow/checkpoints.db' or ':memory:' for in-memory. "
|
||||||
"For postgres, use a DSN like 'postgresql://user:pass@localhost:5432/db'.",
|
"For postgres, use a DSN like 'postgresql://user:pass@localhost:5432/db'.",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -81,7 +81,9 @@ async def init_engine(
|
|||||||
try:
|
try:
|
||||||
import asyncpg # noqa: F401
|
import asyncpg # noqa: F401
|
||||||
except ImportError:
|
except ImportError:
|
||||||
raise ImportError("database.backend is set to 'postgres' but asyncpg is not installed.\nInstall it with:\n uv sync --extra postgres\nOr switch to backend: sqlite in config.yaml for single-node deployment.") from None
|
raise ImportError(
|
||||||
|
"database.backend is set to 'postgres' but asyncpg is not installed.\nInstall it with:\n uv sync --all-packages --extra postgres\nOr switch to backend: sqlite in config.yaml for single-node deployment."
|
||||||
|
) from None
|
||||||
|
|
||||||
if backend == "sqlite":
|
if backend == "sqlite":
|
||||||
import os
|
import os
|
||||||
|
|||||||
@@ -36,7 +36,9 @@ logger = logging.getLogger(__name__)
|
|||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
SQLITE_INSTALL = "langgraph-checkpoint-sqlite is required for the SQLite checkpointer. Install it with: uv add langgraph-checkpoint-sqlite"
|
SQLITE_INSTALL = "langgraph-checkpoint-sqlite is required for the SQLite checkpointer. Install it with: uv add langgraph-checkpoint-sqlite"
|
||||||
POSTGRES_INSTALL = "langgraph-checkpoint-postgres is required for the PostgreSQL checkpointer. Install it with: uv add langgraph-checkpoint-postgres psycopg[binary] psycopg-pool"
|
POSTGRES_INSTALL = (
|
||||||
|
"langgraph-checkpoint-postgres is required for the PostgreSQL checkpointer. Install the package extra with: pip install 'deerflow-harness[postgres]' (or use: uv sync --all-packages --extra postgres when developing locally)"
|
||||||
|
)
|
||||||
POSTGRES_CONN_REQUIRED = "checkpointer.connection_string is required for the postgres backend"
|
POSTGRES_CONN_REQUIRED = "checkpointer.connection_string is required for the postgres backend"
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -36,7 +36,9 @@ logger = logging.getLogger(__name__)
|
|||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
SQLITE_STORE_INSTALL = "langgraph-checkpoint-sqlite is required for the SQLite store. Install it with: uv add langgraph-checkpoint-sqlite"
|
SQLITE_STORE_INSTALL = "langgraph-checkpoint-sqlite is required for the SQLite store. Install it with: uv add langgraph-checkpoint-sqlite"
|
||||||
POSTGRES_STORE_INSTALL = "langgraph-checkpoint-postgres is required for the PostgreSQL store. Install it with: uv add langgraph-checkpoint-postgres psycopg[binary] psycopg-pool"
|
POSTGRES_STORE_INSTALL = (
|
||||||
|
"langgraph-checkpoint-postgres is required for the PostgreSQL store. Install the package extra with: pip install 'deerflow-harness[postgres]' (or use: uv sync --all-packages --extra postgres when developing locally)"
|
||||||
|
)
|
||||||
POSTGRES_CONN_REQUIRED = "checkpointer.connection_string is required for the postgres backend"
|
POSTGRES_CONN_REQUIRED = "checkpointer.connection_string is required for the postgres backend"
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
"""Unit tests for checkpointer config and singleton factory."""
|
"""Unit tests for checkpointer config, packaging metadata, and factories."""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
import tomllib
|
||||||
|
from pathlib import Path
|
||||||
from unittest.mock import AsyncMock, MagicMock, patch
|
from unittest.mock import AsyncMock, MagicMock, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
@@ -13,6 +15,8 @@ from deerflow.config.checkpointer_config import (
|
|||||||
set_checkpointer_config,
|
set_checkpointer_config,
|
||||||
)
|
)
|
||||||
from deerflow.runtime.checkpointer import get_checkpointer, reset_checkpointer
|
from deerflow.runtime.checkpointer import get_checkpointer, reset_checkpointer
|
||||||
|
from deerflow.runtime.checkpointer.provider import POSTGRES_INSTALL
|
||||||
|
from deerflow.runtime.store.provider import POSTGRES_STORE_INSTALL
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
@@ -67,6 +71,42 @@ class TestCheckpointerConfig:
|
|||||||
with pytest.raises(Exception):
|
with pytest.raises(Exception):
|
||||||
load_checkpointer_config_from_dict({"type": "unknown"})
|
load_checkpointer_config_from_dict({"type": "unknown"})
|
||||||
|
|
||||||
|
def test_connection_string_description_matches_runtime_defaults(self):
|
||||||
|
description = CheckpointerConfig.model_fields["connection_string"].description
|
||||||
|
|
||||||
|
assert description is not None
|
||||||
|
assert "Optional for sqlite" in description
|
||||||
|
assert "defaults to 'store.db'" in description
|
||||||
|
assert "Required for postgres" in description
|
||||||
|
|
||||||
|
|
||||||
|
class TestHarnessPackaging:
|
||||||
|
def test_pyproject_declares_postgres_extra(self):
|
||||||
|
pyproject_path = Path(__file__).resolve().parents[1] / "packages" / "harness" / "pyproject.toml"
|
||||||
|
data = tomllib.loads(pyproject_path.read_text())
|
||||||
|
|
||||||
|
optional_dependencies = data["project"]["optional-dependencies"]
|
||||||
|
assert "postgres" in optional_dependencies
|
||||||
|
assert optional_dependencies["postgres"] == [
|
||||||
|
"asyncpg>=0.29",
|
||||||
|
"langgraph-checkpoint-postgres>=3.0.5",
|
||||||
|
"psycopg[binary]>=3.3.3",
|
||||||
|
"psycopg-pool>=3.3.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
def test_workspace_pyproject_forwards_postgres_extra_to_harness(self):
|
||||||
|
pyproject_path = Path(__file__).resolve().parents[1] / "pyproject.toml"
|
||||||
|
data = tomllib.loads(pyproject_path.read_text())
|
||||||
|
|
||||||
|
optional_dependencies = data["project"]["optional-dependencies"]
|
||||||
|
assert optional_dependencies["postgres"] == ["deerflow-harness[postgres]"]
|
||||||
|
|
||||||
|
def test_postgres_missing_dependency_messages_recommend_package_extra(self):
|
||||||
|
assert "deerflow-harness[postgres]" in POSTGRES_INSTALL
|
||||||
|
assert "deerflow-harness[postgres]" in POSTGRES_STORE_INSTALL
|
||||||
|
assert "uv sync --all-packages --extra postgres" in POSTGRES_INSTALL
|
||||||
|
assert "uv sync --all-packages --extra postgres" in POSTGRES_STORE_INSTALL
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Factory tests
|
# Factory tests
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ Tests:
|
|||||||
5. Postgres missing-dep error message
|
5. Postgres missing-dep error message
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
from datetime import UTC, datetime
|
from datetime import UTC, datetime
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@@ -221,13 +223,8 @@ class TestEngineLifecycle:
|
|||||||
"""If asyncpg is not installed, error message tells user what to do."""
|
"""If asyncpg is not installed, error message tells user what to do."""
|
||||||
from deerflow.persistence.engine import init_engine
|
from deerflow.persistence.engine import init_engine
|
||||||
|
|
||||||
try:
|
with (
|
||||||
import asyncpg # noqa: F401
|
patch.dict(sys.modules, {"asyncpg": None}),
|
||||||
|
pytest.raises(ImportError, match="uv sync --all-packages --extra postgres"),
|
||||||
pytest.skip("asyncpg is installed -- cannot test missing-dep path")
|
):
|
||||||
except ImportError:
|
|
||||||
# asyncpg is not installed — this is the expected state for this test.
|
|
||||||
# We proceed to verify that init_engine raises an actionable ImportError.
|
|
||||||
pass # noqa: S110 — intentionally ignored
|
|
||||||
with pytest.raises(ImportError, match="uv sync --extra postgres"):
|
|
||||||
await init_engine("postgres", url="postgresql+asyncpg://x:x@localhost/x")
|
await init_engine("postgres", url="postgresql+asyncpg://x:x@localhost/x")
|
||||||
|
|||||||
Reference in New Issue
Block a user