Contracts
Every package talks through protocols. Agents code to interfaces, not implementations. Swap Redis for in-memory, Postgres for SQLite — same contract, one config line.
50+ packages, 100+ protocols, 533 error codes — built for scale, typed for agents.
Every backend starts the same way: you wire up SQL, cache, auth, queues, events. Lexigram gives you a ready-to-use foundation — and agents can reason about every layer.
Contracts
Every package talks through protocols. Agents code to interfaces, not implementations. Swap Redis for in-memory, Postgres for SQLite — same contract, one config line.
Providers
Lifecycle and wiring live in one place. Boot order is explicit. Agents register and resolve through the container, not ad-hoc wiring.
Import linter
Enforces architectural boundaries. Catches violations before they ship. Agents can verify their own imports.
Web
ASGI controllers, routing, middleware, OpenAPI docs, CORS, rate limiting. One module, one call.
SQL
Async Postgres, MySQL, SQLite. Repositories, migrations, query building. Swap backends with a config line.
Auth
JWT, OAuth2, session management. Guards, permissions, multi-tenancy. Protocol-driven, backend-agnostic.
Four steps from contract to running application. No boilerplate, no globals, no magic.
Say what you need, not who provides it. Contracts live in lexigram-contracts so nothing across the rest of the codebase has to know who’s on the other end.
from typing import Protocol
class NotificationServiceProtocol(Protocol): async def send(self, user_id: str, message: str) -> None: """Send a notification to a user."""Constructor injection, no globals. Your IDE gets full type inference, your tests get whatever fake you want to pass in.
from lexigram import injectable
@injectableclass CheckoutService: def __init__(self, notifier: NotificationServiceProtocol) -> None: self.notifier = notifier
async def complete_order(self, user_id: str) -> None: # Process order... await self.notifier.send(user_id, "Order Complete!")A provider is the seam where “what I need” meets “what to use.” Group your bindings, set a priority, and the container handles the rest.
from lexigram import Provider, ProviderPriority
class NotificationProvider(Provider): name = "notification" priority = ProviderPriority.COMMS
async def register(self, container) -> None: # Bind the concrete implementation to the protocol container.singleton(NotificationServiceProtocol, SlackNotifier)Boot a single Application with the providers you want wired together. That’s the whole backend, ready when you are.
from lexigram import Application
async with Application.boot( name="my-app", providers=[NotificationProvider(), WebProvider()]) as app: # App is fully booted. Infrastructure is ready. passRecent activity from the project — blog posts, releases, and commits.
Join the contributors shaping the AI-friendly Python framework.