Files
tatlock/src/main.py
T
jpmschweitzerandClaude Sonnet 4.5 6eed5f4d13 feat: implement Phase 2 two-tier architecture with Steward
Add comprehensive two-tier architecture where Steward analyzes requests
and Tatlock executes with scoped tools. Includes full infrastructure for
request preprocessing, tool tracking, benchmarking, and streaming.

**Added:**
- Steward agent for request analysis and capability recommendation
- Household Registry for centralized capability management
- Request preprocessing pipeline (Steward → Tatlock flow)
- Tool usage tracking and benchmarking system
- Streaming transparency (Steward reasoning visible in streams)
- Structured logging with operation timing
- Redis benchmark storage with 30-day expiry
- Benchmark analysis CLI tools

**Infrastructure:**
- src/agents/steward/ - Steward agent implementation
- src/agents/tatlock_core/ - Tatlock capability domain
- src/core/preprocessing.py - Request preprocessing pipeline
- src/core/tool_tracking.py - Tool call tracking
- src/core/benchmarks.py - Benchmark recording system
- src/core/household_registry.py - Capability registry
- src/core/startup.py - Application startup coordination
- src/core/logging_config.py - Structured logging setup

**Integration:**
- Responses API uses Steward for Tatlock requests
- Chat Completions wraps Responses API for OpenAI compatibility
- Streaming coordinator supports Steward + Tatlock flow
- Tool scoping per request based on Steward recommendations

**Testing:**
- Integration tests for Steward-Tatlock flow
- Benchmark and registry unit tests
- Steward streaming tests

See PHASE2_PLAN.md and PHASE2_COMPLETE.md for detailed documentation.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-07 15:39:20 +01:00

179 lines
5.1 KiB
Python

"""
FastAPI application entry point.
Following best practices from github.com/zhanymkanov/fastapi-best-practices
Main responsibilities:
- Application configuration
- Middleware setup
- Exception handlers
- Router registration
- Lifecycle management
"""
from contextlib import asynccontextmanager
from typing import AsyncGenerator
from fastapi import FastAPI, Request, status
from fastapi.exceptions import RequestValidationError
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from src.chat.router import router as chat_router
from src.core.config import config
from src.core.exceptions import AppException
from src.core.logging_config import get_logger
from src.core.router import router as core_router
from src.core.startup import initialize_application
from src.models.router import router as models_router
from src.responses.router import router as responses_router
# Get structured logger
logger = get_logger(__name__)
@asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
"""
Application lifespan manager.
Handles startup and shutdown logic.
"""
# Startup
logger.info(
"application_starting",
app_name=config.APP_NAME,
version=config.APP_VERSION,
environment=config.ENVIRONMENT.value,
ollama_host=str(config.OLLAMA_HOST),
ollama_model=config.OLLAMA_DEFAULT_MODEL,
redis_url=config.redis_url,
log_format=config.log_format,
)
# Initialize application (register household members, etc.)
initialize_application()
yield
# Shutdown
logger.info("application_shutdown")
def create_application() -> FastAPI:
"""
Application factory.
Creates and configures the FastAPI application.
Following best practice of using factory pattern.
"""
# Create app
application = FastAPI(
title=config.APP_NAME,
version=config.APP_VERSION,
lifespan=lifespan,
debug=config.DEBUG,
)
# Add middleware
application.add_middleware(
CORSMiddleware,
allow_origins=config.CORS_ORIGINS,
allow_credentials=config.CORS_ALLOW_CREDENTIALS,
allow_methods=config.CORS_ALLOW_METHODS,
allow_headers=config.CORS_ALLOW_HEADERS,
)
# Register exception handlers
register_exception_handlers(application)
# Include routers
application.include_router(core_router) # Health and root endpoints
application.include_router(chat_router, prefix=config.API_PREFIX)
application.include_router(models_router, prefix=config.API_PREFIX)
application.include_router(responses_router, prefix=config.API_PREFIX) # Responses API
return application
def register_exception_handlers(application: FastAPI) -> None:
"""
Register global exception handlers.
Provides consistent error responses compatible with OpenAI API.
"""
@application.exception_handler(AppException)
async def app_exception_handler(
request: Request,
exc: AppException,
) -> JSONResponse:
"""Handle custom application exceptions."""
logger.error(
"application_exception",
error_message=exc.message,
error_type=exc.__class__.__name__,
status_code=exc.status_code,
details=exc.details,
path=request.url.path,
)
return JSONResponse(
status_code=exc.status_code,
content={
"error": {
"message": exc.message,
"type": exc.__class__.__name__,
"details": exc.details,
}
},
)
@application.exception_handler(RequestValidationError)
async def validation_exception_handler(
request: Request,
exc: RequestValidationError,
) -> JSONResponse:
"""Handle Pydantic validation errors."""
logger.error(
"validation_error",
errors=exc.errors(),
path=request.url.path,
)
return JSONResponse(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
content={
"error": {
"message": "Invalid request",
"type": "invalid_request_error",
"details": exc.errors(),
}
},
)
@application.exception_handler(Exception)
async def general_exception_handler(
request: Request,
exc: Exception,
) -> JSONResponse:
"""Handle unexpected exceptions."""
logger.exception(
"unexpected_error",
error_type=type(exc).__name__,
error_message=str(exc),
path=request.url.path,
)
return JSONResponse(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
content={
"error": {
"message": "An internal error occurred",
"type": "internal_server_error",
}
},
)
# Create application instance
app = create_application()