ci(deps): bump actions/cache from 3 to 5 in gpu-benchmark.yml

Resolves remaining Dependabot PR #42
This commit is contained in:
2026-03-26 08:47:19 +01:00
parent 8efaf9fa08
commit d82ea9594f
24 changed files with 5108 additions and 79 deletions

View File

@@ -0,0 +1,140 @@
"""
Unified configuration for AITBC Coordinator API
Provides environment-based adapter selection and consolidated settings.
"""
import os
from pydantic_settings import BaseSettings, SettingsConfigDict
from typing import List, Optional
from pathlib import Path
class DatabaseConfig(BaseSettings):
"""Database configuration with adapter selection."""
adapter: str = "sqlite" # sqlite, postgresql
url: Optional[str] = None
pool_size: int = 10
max_overflow: int = 20
pool_pre_ping: bool = True
@property
def effective_url(self) -> str:
"""Get the effective database URL."""
if self.url:
return self.url
# Default SQLite path
if self.adapter == "sqlite":
return "sqlite:////opt/data/coordinator.db"
# Default PostgreSQL connection string
return f"{self.adapter}://localhost:5432/coordinator"
model_config = SettingsConfigDict(
env_file=".env", env_file_encoding="utf-8", case_sensitive=False, extra="allow"
)
class Settings(BaseSettings):
"""Unified application settings with environment-based configuration."""
model_config = SettingsConfigDict(
env_file=".env", env_file_encoding="utf-8", case_sensitive=False, extra="allow"
)
# Environment
app_env: str = "dev"
app_host: str = "127.0.0.1"
app_port: int = 8011
audit_log_dir: str = "/var/log/aitbc/audit"
# Database
database: DatabaseConfig = DatabaseConfig()
# API Keys
client_api_keys: List[str] = []
miner_api_keys: List[str] = []
admin_api_keys: List[str] = []
# Security
hmac_secret: Optional[str] = None
jwt_secret: Optional[str] = None
jwt_algorithm: str = "HS256"
jwt_expiration_hours: int = 24
# CORS
allow_origins: List[str] = [
"http://localhost:3000",
"http://localhost:8080",
"http://localhost:8000",
"http://localhost:8011",
]
# Job Configuration
job_ttl_seconds: int = 900
heartbeat_interval_seconds: int = 10
heartbeat_timeout_seconds: int = 30
# Rate Limiting
rate_limit_requests: int = 60
rate_limit_window_seconds: int = 60
# Receipt Signing
receipt_signing_key_hex: Optional[str] = None
receipt_attestation_key_hex: Optional[str] = None
# Logging
log_level: str = "INFO"
log_format: str = "json" # json or text
# Mempool
mempool_backend: str = "database" # database, memory
# Blockchain RPC
blockchain_rpc_url: str = "http://localhost:8082"
# Test Configuration
test_mode: bool = False
test_database_url: Optional[str] = None
def validate_secrets(self) -> None:
"""Validate that all required secrets are provided."""
if self.app_env == "production":
if not self.jwt_secret:
raise ValueError(
"JWT_SECRET environment variable is required in production"
)
if self.jwt_secret == "change-me-in-production":
raise ValueError("JWT_SECRET must be changed from default value")
@property
def database_url(self) -> str:
"""Get the database URL (backward compatibility)."""
# Use test database if in test mode and test_database_url is set
if self.test_mode and self.test_database_url:
return self.test_database_url
if self.database.url:
return self.database.url
# Default SQLite path for backward compatibility
return "sqlite:////opt/data/coordinator.db"
@database_url.setter
def database_url(self, value: str):
"""Allow setting database URL for tests"""
if not self.test_mode:
raise RuntimeError("Cannot set database_url outside of test mode")
self.test_database_url = value
settings = Settings()
# Enable test mode if environment variable is set
if os.getenv("TEST_MODE") == "true":
settings.test_mode = True
if os.getenv("TEST_DATABASE_URL"):
settings.test_database_url = os.getenv("TEST_DATABASE_URL")
# Validate secrets on import
settings.validate_secrets()

View File

@@ -0,0 +1,70 @@
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from prometheus_client import make_asgi_app
from .config import settings
from .storage import init_db
from .routers import (
client,
miner,
admin,
marketplace,
marketplace_gpu,
exchange,
users,
services,
marketplace_offers,
zk_applications,
explorer,
payments,
)
from .routers.governance import router as governance
from .routers.partners import router as partners
from .storage.models_governance import GovernanceProposal, ProposalVote, TreasuryTransaction, GovernanceParameter
def create_app() -> FastAPI:
app = FastAPI(
title="AITBC Coordinator API",
version="0.1.0",
description="Stage 1 coordinator service handling job orchestration between clients and miners.",
)
# Create database tables
init_db()
app.add_middleware(
CORSMiddleware,
allow_origins=settings.allow_origins,
allow_credentials=True,
allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"],
allow_headers=["*"] # Allow all headers for API keys and content types
)
app.include_router(client, prefix="/v1")
app.include_router(miner, prefix="/v1")
app.include_router(admin, prefix="/v1")
app.include_router(marketplace, prefix="/v1")
app.include_router(marketplace_gpu, prefix="/v1")
app.include_router(exchange, prefix="/v1")
app.include_router(users, prefix="/v1/users")
app.include_router(services, prefix="/v1")
app.include_router(payments, prefix="/v1")
app.include_router(marketplace_offers, prefix="/v1")
app.include_router(zk_applications.router, prefix="/v1")
app.include_router(governance, prefix="/v1")
app.include_router(partners, prefix="/v1")
app.include_router(explorer, prefix="/v1")
# Add Prometheus metrics endpoint
metrics_app = make_asgi_app()
app.mount("/metrics", metrics_app)
@app.get("/v1/health", tags=["health"], summary="Service healthcheck")
async def health() -> dict[str, str]:
return {"status": "ok", "env": settings.app_env}
return app
app = create_app()

View File

@@ -0,0 +1,92 @@
"""
Database storage module for AITBC Coordinator API
Provides unified database session management with connection pooling.
"""
from __future__ import annotations
from contextlib import contextmanager
from typing import Annotated, Generator
from fastapi import Depends
from sqlalchemy.engine import Engine
from sqlalchemy.pool import QueuePool
from sqlmodel import Session, SQLModel, create_engine
from ..config import settings
from ..domain import (
Job,
Miner,
MarketplaceOffer,
MarketplaceBid,
JobPayment,
PaymentEscrow,
GPURegistry,
GPUBooking,
GPUReview,
)
from ..domain.gpu_marketplace import ConsumerGPUProfile, EdgeGPUMetrics
from .models_governance import GovernanceProposal, ProposalVote, TreasuryTransaction, GovernanceParameter
_engine: Engine | None = None
def get_engine() -> Engine:
"""Get or create the database engine with connection pooling."""
global _engine
if _engine is None:
# Allow tests to override via settings.database_url (fixtures set this directly)
db_override = getattr(settings, "database_url", None)
db_config = settings.database
effective_url = db_override or db_config.effective_url
if "sqlite" in effective_url:
_engine = create_engine(
effective_url,
echo=False,
connect_args={"check_same_thread": False},
)
else:
_engine = create_engine(
effective_url,
echo=False,
poolclass=QueuePool,
pool_size=db_config.pool_size,
max_overflow=db_config.max_overflow,
pool_pre_ping=db_config.pool_pre_ping,
)
return _engine
def init_db() -> Engine:
"""Initialize database tables."""
engine = get_engine()
SQLModel.metadata.create_all(engine)
return engine
@contextmanager
def session_scope() -> Generator[Session, None, None]:
"""Context manager for database sessions."""
engine = get_engine()
session = Session(engine)
try:
yield session
session.commit()
except Exception:
session.rollback()
raise
finally:
session.close()
def get_session() -> Generator[Session, None, None]:
"""Get a database session (for FastAPI dependency)."""
with session_scope() as session:
yield session
SessionDep = Annotated[Session, Depends(get_session)]