engram / kvcos /api /server.py
eigengram's picture
feat: upload core kvcos library
0769ff3 verified
"""
ENGRAM Protocol — ENGRAM Server
FastAPI application factory with lifespan management.
Initializes storage, index, extractor, and retriever on startup.
"""
from __future__ import annotations
import logging
from contextlib import asynccontextmanager
from fastapi import FastAPI
from kvcos.api import routes
from kvcos.core.config import get_config
from kvcos.core.serializer import EngramSerializer
from kvcos.core.types import ENGRAM_VERSION, StateExtractionMode
from kvcos.core.manifold_index import ManifoldIndex
from kvcos.core.retriever import EGRRetriever
from kvcos.core.state_extractor import MARStateExtractor
from kvcos.storage.local import LocalStorageBackend
logger = logging.getLogger(__name__)
@asynccontextmanager
async def lifespan(app: FastAPI):
"""Initialize ENGRAM components on startup, clean up on shutdown."""
config = get_config()
# Initialize storage backend
storage = LocalStorageBackend(data_dir=config.data_dir)
# Initialize EGR manifold index
index_path = config.index_dir / "egr.faiss"
index = ManifoldIndex(dim=config.state_vec_dim, index_path=index_path)
# Initialize state extractor
extractor = MARStateExtractor(
mode=StateExtractionMode.SVD_PROJECT,
rank=config.state_vec_dim,
)
# Initialize retriever
serializer = EngramSerializer()
retriever = EGRRetriever(
extractor=extractor,
index=index,
storage=storage,
serializer=serializer,
)
# Wire into route handlers
routes._storage = storage
routes._index = index
routes._retriever = retriever
logger.info("ENGRAM v%s started", ENGRAM_VERSION)
logger.info(" Storage: %s (%d entries)", config.data_dir, storage.stats()["total_entries"])
logger.info(" Index: %s (%d vectors, dim=%d)", config.index_dir, index.n_entries, config.state_vec_dim)
logger.info(" Backend: %s", config.backend.value)
yield
# Shutdown: persist index
try:
index.save(index_path)
logger.info("Index saved to %s", index_path)
except Exception as e:
logger.warning("Failed to save index: %s", e)
# Clear route references
routes._storage = None
routes._index = None
routes._retriever = None
logger.info("ENGRAM shutdown complete")
def create_app() -> FastAPI:
"""Create the ENGRAM FastAPI application."""
app = FastAPI(
title="ENGRAM Protocol API",
description="ENGRAM Protocol: Cognitive state, persisted.",
version=ENGRAM_VERSION,
lifespan=lifespan,
docs_url="/docs",
redoc_url="/redoc",
)
app.include_router(routes.router)
return app
def main() -> None:
"""Entry point for `engram-server` console script."""
import uvicorn
config = get_config()
application = create_app()
uvicorn.run(
application,
host=config.host,
port=config.port,
log_level="info",
)
def _get_app() -> FastAPI:
"""Lazy app factory for `uvicorn kvcos.api.server:app`.
Defers create_app() until the attribute is actually accessed,
avoiding side effects on module import.
"""
return create_app()
def __getattr__(name: str) -> FastAPI:
if name == "app":
return _get_app()
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
if __name__ == "__main__":
main()