feat: enhance database transactions, add Makefiles, and improve Docker setup

- Refactored database batch operations to ensure transaction atomicity and simplify nested structure.
- Added `Makefile` for `knowledge-base` and `llm-gateway` modules to streamline development workflows.
- Simplified `Dockerfile` for `llm-gateway` by removing multi-stage builds and optimizing dependencies.
- Improved code readability in `collection_manager` and `failover` modules with refined logic.
- Minor fixes in `test_server` and Redis health check handling for better diagnostics.
This commit is contained in:
2026-01-05 00:49:19 +01:00
parent db12937495
commit 4154dd5268
8 changed files with 259 additions and 119 deletions

View File

@@ -1,39 +1,25 @@
# Syndarix LLM Gateway MCP Server
# Multi-stage build for minimal image size
FROM python:3.12-slim
# Build stage
FROM python:3.12-slim AS builder
WORKDIR /app
# Install system dependencies (needed for tiktoken regex compilation)
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
&& rm -rf /var/lib/apt/lists/*
# Install uv for fast package management
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv
WORKDIR /app
# Copy dependency files
# Copy project files
COPY pyproject.toml ./
COPY *.py ./
# Create virtual environment and install dependencies
RUN uv venv /app/.venv
ENV PATH="/app/.venv/bin:$PATH"
RUN uv pip install -e .
# Runtime stage
FROM python:3.12-slim AS runtime
# Install dependencies to system Python
RUN uv pip install --system --no-cache .
# Create non-root user for security
RUN groupadd --gid 1000 appgroup && \
useradd --uid 1000 --gid appgroup --shell /bin/bash --create-home appuser
WORKDIR /app
# Copy virtual environment from builder
COPY --from=builder /app/.venv /app/.venv
ENV PATH="/app/.venv/bin:$PATH"
# Copy application code
COPY --chown=appuser:appgroup . .
# Switch to non-root user
RUN useradd --create-home --shell /bin/bash appuser
USER appuser
# Environment variables
@@ -47,7 +33,7 @@ EXPOSE 8001
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD python -c "import httpx; httpx.get('http://localhost:8001/health').raise_for_status()" || exit 1
CMD python -c "import httpx; httpx.get('http://localhost:8001/health').raise_for_status()"
# Run the server
CMD ["python", "server.py"]

View File

@@ -0,0 +1,79 @@
.PHONY: help install install-dev lint lint-fix format type-check test test-cov validate clean run
# Default target
help:
@echo "LLM Gateway MCP Server - Development Commands"
@echo ""
@echo "Setup:"
@echo " make install - Install production dependencies"
@echo " make install-dev - Install development dependencies"
@echo ""
@echo "Quality Checks:"
@echo " make lint - Run Ruff linter"
@echo " make lint-fix - Run Ruff linter with auto-fix"
@echo " make format - Format code with Ruff"
@echo " make type-check - Run mypy type checker"
@echo ""
@echo "Testing:"
@echo " make test - Run pytest"
@echo " make test-cov - Run pytest with coverage"
@echo ""
@echo "All-in-one:"
@echo " make validate - Run lint, type-check, and tests"
@echo ""
@echo "Running:"
@echo " make run - Run the server locally"
@echo ""
@echo "Cleanup:"
@echo " make clean - Remove cache and build artifacts"
# Setup
install:
@echo "Installing production dependencies..."
@uv pip install -e .
install-dev:
@echo "Installing development dependencies..."
@uv pip install -e ".[dev]"
# Quality checks
lint:
@echo "Running Ruff linter..."
@uv run ruff check .
lint-fix:
@echo "Running Ruff linter with auto-fix..."
@uv run ruff check --fix .
format:
@echo "Formatting code..."
@uv run ruff format .
type-check:
@echo "Running mypy..."
@uv run mypy . --ignore-missing-imports
# Testing
test:
@echo "Running tests..."
@uv run pytest tests/ -v
test-cov:
@echo "Running tests with coverage..."
@uv run pytest tests/ -v --cov=. --cov-report=term-missing --cov-report=html
# All-in-one validation
validate: lint type-check test
@echo "All validations passed!"
# Running
run:
@echo "Starting LLM Gateway server..."
@uv run python server.py
# Cleanup
clean:
@echo "Cleaning up..."
@rm -rf __pycache__ .pytest_cache .mypy_cache .ruff_cache .coverage htmlcov
@find . -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true
@find . -type f -name "*.pyc" -delete 2>/dev/null || true

View File

@@ -110,14 +110,13 @@ class CircuitBreaker:
"""
if self._state == CircuitState.OPEN:
time_in_open = time.time() - self._stats.state_changed_at
if time_in_open >= self.recovery_timeout:
# Only transition if still in OPEN state (double-check)
if self._state == CircuitState.OPEN:
self._transition_to(CircuitState.HALF_OPEN)
logger.info(
f"Circuit {self.name} transitioned to HALF_OPEN "
f"after {time_in_open:.1f}s"
)
# Double-check state after time calculation (for thread safety)
if time_in_open >= self.recovery_timeout and self._state == CircuitState.OPEN:
self._transition_to(CircuitState.HALF_OPEN)
logger.info(
f"Circuit {self.name} transitioned to HALF_OPEN "
f"after {time_in_open:.1f}s"
)
def _transition_to(self, new_state: CircuitState) -> None:
"""Transition to a new state."""