跳转至

Middleware Component Promotion Policy (No Backward Compatibility)

Date: 2026-01-11
Status: ✅ Active (Enforced)
Scope: SAGE monorepo (packages/*), especially sage-libs and sage-middleware

Why this policy exists

SAGE uses a layered architecture (L1–L6). Historically, the most common architectural failure mode is “L3 libs → L4 middleware upward dependency” when an apparently “library” feature actually needs runtime backends such as Vector DB, Memory, Refiner, or external services.

This policy declares a hard rule:

If it needs upward/runtime/backends, it is not a library. Promote it to sage-middleware.

This is considered the cleanest and most scalable way to keep package boundaries correct.

Definitions

sage-libs (L3) is for

  • Pure algorithms and policies
  • Data types and interfaces (ABC/Protocol)
  • Code that depends only on:
  • sage-common / sage-platform
  • Python stdlib
  • lightweight third-party deps
  • Code that can be unit-tested without external services

sage-middleware (L4) is for

  • Runtime-bound implementations and adapters
  • Anything that touches or depends on:
  • Vector stores / indices: SageVDB (isage-vdb), FAISS, Milvus, etc.
  • Memory backends: Neuromem (isage-neuromem), Redis, RocksDB, etc.
  • Refiners / compressors (LLMLingua, LongRefiner adapters)
  • External services (HTTP APIs), persistent storage, connection pools
  • Long-running/background workers
  • Pipeline-as-a-service style orchestration (operators)

Hard rules

  1. No upward dependency in source
  2. packages/sage-libs/src/** must not import sage.middleware.*.
  3. Any code requiring sage.middleware.* must be moved to sage-middleware.

  4. No backward compatibility during refactors

  5. When moving code from sage-libssage-middleware, do NOT keep re-export shims.
  6. Update all in-repo imports in the same change.
  7. Let broken imports fail fast.

  8. Optional dependencies live with middleware

  9. Heavy deps (isage-vdb, isage-neuromem, faiss-*, etc.) must be declared in packages/sage-middleware/pyproject.toml (usually under optional-dependencies).

To avoid sage-middleware becoming a “catch-all”, new code should be grouped by domain:

  • sage/middleware/components/retrieval/
  • vector_stores/ (SageVDB/FAISS/Milvus adapters)
  • retrievers/ (dense/hybrid/graph retrievers)
  • sage/middleware/components/memory/
  • neuromem_backend/ (Neuromem adapter)
  • stores/ (session/long-term stores)
  • sage/middleware/operators/rag/
  • operator wrappers (config schema + input/output contract)

Refactor workflow (Prompt Checklist)

Use the following prompt as an internal task plan for a large refactor.

Prompt: "Promote libs code to middleware components (no backward compatibility)"

Goal: Move runtime/backend-dependent code from sage-libs into sage-middleware cleanly.

  1. Discovery
  2. Locate candidates (examples): retrievers touching vector stores, memory adapters, refiner adapters.
  3. Identify call sites across packages.

  4. Target placement

  5. Decide final module path under sage.middleware.components.* or sage.middleware.operators.*.

  6. Move code + fix imports

  7. Move modules.
  8. Update all imports across repo.
  9. Do NOT add re-export modules.

  10. Dependency updates

  11. Move heavy deps to sage-middleware (pyproject).
  12. Ensure version ranges follow dependencies-spec.yaml.

  13. Tests & docs

  14. Move/adjust tests to match new modules.
  15. Update docs references (package architecture, user guides).

  16. Quality gates

  17. Run: formatting/lint/typecheck as applicable.
  18. Run: unit tests.
  19. Ensure no sage-libs imports sage.middleware.
  • docs-public/docs_src/dev-notes/package-architecture.md
  • docs-public/docs_src/dev-notes/l4-middleware/README.md