chore: initialize EverOS 1.0.0
md-first memory extraction framework for AI agents. Markdown is the single source of truth; SQLite holds state and LanceDB provides the rebuildable vector + BM25 + scalar index. The codebase follows a single-direction DDD layering (entrypoints -> service -> memory -> infra, with component / core / config cross-cutting) enforced by import-linter. Engineering surface: - Coding conventions in .claude/rules/ (path-scoped) and workflows in .claude/skills/ (/commit, /new-branch, /pr). - GitHub Actions CI runs make lint + test + integration; pre-commit mirrors the gates locally (ruff, hygiene hooks, gitlint commit-msg). - Commit messages follow Conventional Commits, enforced by gitlint. - make lint also enforces datetime two-zone discipline and OpenAPI drift.
This commit is contained in:
35
src/everos/entrypoints/api/lifespans/__init__.py
Normal file
35
src/everos/entrypoints/api/lifespans/__init__.py
Normal file
@ -0,0 +1,35 @@
|
||||
"""HTTP API lifespan providers.
|
||||
|
||||
Concrete :class:`everos.core.lifespan.LifespanProvider` implementations
|
||||
for the storage + chassis backends this entrypoint composes. They live next to
|
||||
``app.py`` because they are *application-bootstrap* details, not
|
||||
generic chassis: a different deployment mode (CLI, embedded, batch
|
||||
worker) may compose a different set of providers.
|
||||
|
||||
Putting these here also keeps ``core.lifespan`` free of concrete-
|
||||
backend imports — the chassis stays portable.
|
||||
|
||||
External usage::
|
||||
|
||||
from everos.entrypoints.api.lifespans import (
|
||||
LLMLifespanProvider,
|
||||
SqliteLifespanProvider,
|
||||
LanceDBLifespanProvider,
|
||||
CascadeLifespanProvider,
|
||||
OmeLifespanProvider,
|
||||
)
|
||||
"""
|
||||
|
||||
from .cascade import CascadeLifespanProvider as CascadeLifespanProvider
|
||||
from .lancedb import LanceDBLifespanProvider as LanceDBLifespanProvider
|
||||
from .llm import LLMLifespanProvider as LLMLifespanProvider
|
||||
from .ome import OmeLifespanProvider as OmeLifespanProvider
|
||||
from .sqlite import SqliteLifespanProvider as SqliteLifespanProvider
|
||||
|
||||
__all__ = [
|
||||
"CascadeLifespanProvider",
|
||||
"LLMLifespanProvider",
|
||||
"LanceDBLifespanProvider",
|
||||
"OmeLifespanProvider",
|
||||
"SqliteLifespanProvider",
|
||||
]
|
||||
55
src/everos/entrypoints/api/lifespans/cascade.py
Normal file
55
src/everos/entrypoints/api/lifespans/cascade.py
Normal file
@ -0,0 +1,55 @@
|
||||
"""Cascade lifespan provider — starts/stops :class:`CascadeOrchestrator`.
|
||||
|
||||
Ordered after SqliteLifespan + LanceDBLifespan: the orchestrator
|
||||
depends on both stores being ready before its watcher / scanner /
|
||||
worker tasks can take the first row.
|
||||
|
||||
Construction reads the live :class:`Settings` to build the embedding +
|
||||
tokenizer providers. If either is misconfigured the lifespan fails
|
||||
fast — the daemon would be useless without them anyway.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
from everos.component.embedding import build_embedding_provider
|
||||
from everos.component.tokenizer import build_tokenizer
|
||||
from everos.config import load_settings
|
||||
from everos.core.lifespan import LifespanProvider
|
||||
from everos.core.observability.logging import get_logger
|
||||
from everos.core.persistence import MemoryRoot
|
||||
from everos.memory.cascade import CascadeOrchestrator
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
class CascadeLifespanProvider(LifespanProvider):
|
||||
"""Manage the cascade subsystem for the app lifecycle."""
|
||||
|
||||
def __init__(self, order: int = 12) -> None:
|
||||
super().__init__(name="cascade", order=order)
|
||||
self._orchestrator: CascadeOrchestrator | None = None
|
||||
|
||||
async def startup(self, app: FastAPI) -> Any:
|
||||
settings = load_settings()
|
||||
memory_root = MemoryRoot.default()
|
||||
memory_root.ensure()
|
||||
|
||||
embedder = build_embedding_provider(settings.embedding)
|
||||
tokenizer = build_tokenizer()
|
||||
self._orchestrator = CascadeOrchestrator(
|
||||
memory_root=memory_root,
|
||||
embedder=embedder,
|
||||
tokenizer=tokenizer,
|
||||
)
|
||||
await self._orchestrator.start()
|
||||
logger.info("cascade_lifespan_ready")
|
||||
return self._orchestrator
|
||||
|
||||
async def shutdown(self, app: FastAPI) -> None:
|
||||
if self._orchestrator is not None:
|
||||
await self._orchestrator.stop()
|
||||
self._orchestrator = None
|
||||
55
src/everos/entrypoints/api/lifespans/lancedb.py
Normal file
55
src/everos/entrypoints/api/lifespans/lancedb.py
Normal file
@ -0,0 +1,55 @@
|
||||
"""LanceDB lifespan provider (HTTP API entrypoint).
|
||||
|
||||
Startup:
|
||||
Open the connection via ``get_connection`` (lazy, idempotent).
|
||||
Importing :mod:`everos.infra.persistence.lancedb` also triggers the
|
||||
side-effect import of ``tables`` so business schemas are loaded
|
||||
(future: preflight registration).
|
||||
|
||||
Shutdown:
|
||||
Close the connection (also clears the table cache).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
from everos.core.lifespan import LifespanProvider
|
||||
from everos.core.observability.logging import get_logger
|
||||
from everos.infra.persistence.lancedb import (
|
||||
dispose_connection,
|
||||
ensure_business_indexes,
|
||||
get_connection,
|
||||
verify_business_schemas,
|
||||
)
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
class LanceDBLifespanProvider(LifespanProvider):
|
||||
"""Manage the LanceDB connection + table cache for the app lifecycle.
|
||||
|
||||
Startup runs three steps:
|
||||
|
||||
1. ``get_connection`` — lazy-open the async connection.
|
||||
2. ``verify_business_schemas`` — fail loud if an on-disk table's
|
||||
columns drift from the current Pydantic schema. LanceDB has no
|
||||
online migration; cascade is rebuildable from md so the recovery
|
||||
is documented as ``rm -rf ~/.everos/.index/lancedb``.
|
||||
3. ``ensure_business_indexes`` — idempotent FTS index creation.
|
||||
"""
|
||||
|
||||
def __init__(self, order: int = 11) -> None:
|
||||
super().__init__(name="lancedb", order=order)
|
||||
|
||||
async def startup(self, app: FastAPI) -> Any:
|
||||
conn = await get_connection()
|
||||
await verify_business_schemas()
|
||||
await ensure_business_indexes()
|
||||
logger.info("lancedb_ready", uri=conn.uri)
|
||||
return conn
|
||||
|
||||
async def shutdown(self, app: FastAPI) -> None:
|
||||
await dispose_connection()
|
||||
36
src/everos/entrypoints/api/lifespans/llm.py
Normal file
36
src/everos/entrypoints/api/lifespans/llm.py
Normal file
@ -0,0 +1,36 @@
|
||||
"""LLM lifespan provider — eagerly resolves the LLM singleton at startup.
|
||||
|
||||
The framework's core value (memory extraction) is meaningless without
|
||||
an LLM, so misconfiguration must surface as a startup failure instead
|
||||
of N silent skips per request downstream. Ordered before the storage
|
||||
stack so we fail before paying to bring sqlite / lancedb / cascade up.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
from everos.component.llm import get_llm_client
|
||||
from everos.core.lifespan import LifespanProvider
|
||||
from everos.core.observability.logging import get_logger
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
class LLMLifespanProvider(LifespanProvider):
|
||||
"""Resolve the LLM client at startup; raise if credentials are missing."""
|
||||
|
||||
def __init__(self, order: int = 8) -> None:
|
||||
super().__init__(name="llm", order=order)
|
||||
|
||||
async def startup(self, app: FastAPI) -> Any:
|
||||
client = get_llm_client()
|
||||
logger.info("llm_lifespan_ready")
|
||||
return client
|
||||
|
||||
async def shutdown(self, app: FastAPI) -> None:
|
||||
# The client is stateless (algo facade over openai.AsyncOpenAI);
|
||||
# nothing to tear down.
|
||||
return None
|
||||
39
src/everos/entrypoints/api/lifespans/ome.py
Normal file
39
src/everos/entrypoints/api/lifespans/ome.py
Normal file
@ -0,0 +1,39 @@
|
||||
"""OME engine lifespan provider (HTTP API entrypoint).
|
||||
|
||||
Startup: build the singleton engine via service.memorize._get_engine
|
||||
(which also registers strategies) and start it.
|
||||
|
||||
Shutdown: stop the engine.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib
|
||||
from typing import Any
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
from everos.core.lifespan import LifespanProvider
|
||||
from everos.core.observability.logging import get_logger
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
class OmeLifespanProvider(LifespanProvider):
|
||||
"""Manage the OfflineEngine lifecycle for the FastAPI app."""
|
||||
|
||||
def __init__(self, order: int = 50) -> None:
|
||||
super().__init__(name="ome", order=order)
|
||||
|
||||
async def startup(self, app: FastAPI) -> Any:
|
||||
svc = importlib.import_module("everos.service.memorize")
|
||||
engine = svc._get_engine() # noqa: SLF001 — service-internal accessor
|
||||
await engine.start()
|
||||
logger.info("ome_engine_started")
|
||||
return engine
|
||||
|
||||
async def shutdown(self, app: FastAPI) -> None:
|
||||
svc = importlib.import_module("everos.service.memorize")
|
||||
engine = svc._get_engine() # noqa: SLF001
|
||||
await engine.stop()
|
||||
logger.info("ome_engine_stopped")
|
||||
45
src/everos/entrypoints/api/lifespans/sqlite.py
Normal file
45
src/everos/entrypoints/api/lifespans/sqlite.py
Normal file
@ -0,0 +1,45 @@
|
||||
"""SQLite system-DB lifespan provider (HTTP API entrypoint).
|
||||
|
||||
Startup:
|
||||
1. Build the engine via ``get_engine`` (lazy, idempotent). Importing
|
||||
:mod:`everos.infra.persistence.sqlite` also triggers the side-
|
||||
effect import of ``tables`` so every business SQLModel registers
|
||||
itself in ``SQLModel.metadata``.
|
||||
2. ``SQLModel.metadata.create_all`` so every registered table exists.
|
||||
|
||||
Shutdown:
|
||||
Dispose the engine + connection pool.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from fastapi import FastAPI
|
||||
from sqlmodel import SQLModel
|
||||
|
||||
from everos.core.lifespan import LifespanProvider
|
||||
from everos.core.observability.logging import get_logger
|
||||
from everos.infra.persistence.sqlite import dispose_engine, get_engine
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
class SqliteLifespanProvider(LifespanProvider):
|
||||
"""Manage the SQLite system-DB engine + schema for the app lifecycle."""
|
||||
|
||||
def __init__(self, order: int = 10) -> None:
|
||||
super().__init__(name="sqlite", order=order)
|
||||
|
||||
async def startup(self, app: FastAPI) -> Any:
|
||||
engine = get_engine()
|
||||
async with engine.begin() as conn:
|
||||
await conn.run_sync(SQLModel.metadata.create_all)
|
||||
logger.info(
|
||||
"sqlite_schema_ready",
|
||||
tables=len(SQLModel.metadata.tables),
|
||||
)
|
||||
return engine
|
||||
|
||||
async def shutdown(self, app: FastAPI) -> None:
|
||||
await dispose_engine()
|
||||
Reference in New Issue
Block a user