[build-system] requires = ["hatchling"] build-backend = "hatchling.build" [project] name = "forge-api" version = "0.1.0" description = "Forge - self-hosted platform for visually building, testing, and shipping LangChain/LangGraph agents & workflows." readme = "README.md" requires-python = ">=3.11,<3.14" license = { text = "MIT" } # Pins validated against PyPI on 2026-06-07. # Semver promise: langchain/langgraph 1.x minor upgrades are non-breaking. Re-verify each phase. dependencies = [ # --- Web / API --- "fastapi>=0.115,<1", "uvicorn[standard]>=0.32,<1", "sse-starlette>=2.1,<4", "python-multipart>=0.0.18", # --- Data / settings --- "pydantic>=2.9,<3", "pydantic-settings>=2.6,<3", "email-validator>=2.2,<3", # --- Persistence (SQLite default; Postgres is a documented prod swap) --- "sqlalchemy[asyncio]>=2.0.36,<3", "aiosqlite>=0.20,<1", "alembic>=1.14,<2", "greenlet>=3.1", # --- LangChain / LangGraph (MIT framework only; never langgraph-api/LangSmith) --- "langchain>=1.3,<2", "langchain-core>=1.3,<2", "langgraph>=1.2,<2", "langgraph-checkpoint>=4,<5", "langgraph-checkpoint-sqlite>=3,<4", # Deep Agents harness (planning/subagents/filesystem/sandbox). Core, not optional: # the `deep_agent` node is always registered in the palette, so it must always import. "deepagents>=0.6,<1", # --- Tooling primitives --- "jmespath>=1.0,<2", "jsonschema>=4.23,<5", "httpx>=0.27,<1", "RestrictedPython>=7.4,<9", # Accurate token counting for the cost meter / budgets (graceful len/4 fallback if absent). "tiktoken>=0.7", # --- Secrets / auth (platform) --- "cryptography>=43", "python-jose[cryptography]>=3.3,<4", # bcrypt directly (passlib 1.7.x is unmaintained and crashes probing bcrypt>=4.1). "bcrypt>=4,<6", ] [project.optional-dependencies] # Vector store (user-mandated: Chroma). Embedded persistent client - zero infra. vectors = ["chromadb>=1.5,<2"] # Native big-three model providers + universal gateways. providers = [ "langchain-openai>=1,<2", "langchain-anthropic>=1,<2", # deepagents (core) requires google-genai >=4.2.2; keep this compatible so # `pip install -e ".[providers]"` resolves cleanly alongside it. "langchain-google-genai>=4.2.2,<5", ] # MCP: consume external servers + expose projects as MCP servers. mcp = ["langchain-mcp-adapters>=0.2,<1", "mcp>=1.9", "fastmcp>=3,<4"] # Background workers + cache (prod). Local default uses in-process fakes. # croniter powers cron `schedule` triggers (interval schedules need nothing extra). workers = ["redis>=5,<9", "arq>=0.26,<1", "croniter>=2,<7"] # Durable Postgres checkpointer + async driver (prod run/HITL state, shared across workers). # Set FORGE_CHECKPOINT_BACKEND=postgres and FORGE_DATABASE_URL to a postgresql+asyncpg URL. postgres = ["langgraph-checkpoint-postgres>=2,<5", "psycopg[binary,pool]>=3.2", "asyncpg>=0.30"] # OpenTelemetry export (point at an OTLP collector or Langfuse). Opt-in: no-op if absent. observability = [ "opentelemetry-sdk>=1.20", "opentelemetry-exporter-otlp-proto-http>=1.20", ] # Knowledge ingestion loaders/splitters + lexical (BM25) ranking for hybrid search + # fastembed (local open-source ONNX embedder, no API cost / no PyTorch). knowledge = ["langchain-text-splitters>=1,<2", "pypdf>=5", "beautifulsoup4>=4.12", "rank-bm25>=0.2,<1", "fastembed>=0.3,<1"] # One-shot convenience for a fully-functional local app: vector store + model providers # + knowledge loaders + MCP. Excludes prod-only infra (workers, observability), which # the local stack fakes in-process. Install with: pip install -e ".[dev,all]" all = ["forge-api[vectors,providers,knowledge,mcp]"] dev = [ "pytest>=8.3,<10", "pytest-asyncio>=0.24,<2", "anyio>=4.6", # Pin the minor so CI lint is reproducible (an unbounded ruff pulls new rule sets that # can turn CI red without a code change). Bump deliberately. "ruff>=0.15,<0.16", "mypy>=1.13,<3", ] [tool.hatch.build.targets.wheel] packages = ["forge"] [tool.pytest.ini_options] asyncio_mode = "auto" testpaths = ["tests"] filterwarnings = ["ignore::DeprecationWarning"] [tool.mypy] python_version = "3.11" # Most runtime deps (langchain, chromadb, fastmcp, …) ship without type stubs. ignore_missing_imports = true warn_redundant_casts = true warn_unused_ignores = true # Gradual typing: the engine predates static type-checking, so a full strict pass is a # tracked backlog (~90 findings, dominated by dynamic SQLAlchemy Row/column access). CI # runs mypy in advisory (non-blocking) mode today; newly written / refactored modules are # kept clean. The gate is tightened as the backlog is cleared - see CONTRIBUTING.md. [tool.ruff] line-length = 100 target-version = "py311" [tool.ruff.lint] select = ["E", "F", "I", "UP", "B"] ignore = ["E501", "B008"]