refactor: improve error handling and remove hardcoded credentials

- Changed bare except clauses to specific exception types in web3_utils.py, testing.py, messages.py, and message_storage.py
- Replaced print() calls with logger in testing.py, agent_discovery.py, compliance_agent.py, coordinator.py, trading_agent.py, keys.py, escrow.py, persistent_spending_tracker.py, sync_cli.py, and client.py
- Added logger initialization using get_logger(__name__) in compliance_agent.py, coordinator.py, trading_agent.py, keys.py, escrow.py, persistent_spending_tracker.py, and client.py
- Removed hardcoded secret
This commit is contained in:
aitbc
2026-05-12 17:01:57 +02:00
parent 9133609603
commit 745f791eda
279 changed files with 12284 additions and 5061 deletions

View File

@@ -0,0 +1,3 @@
# Shared Core Library
Common configuration, logging, and database utilities for AITBC microservices.

View File

@@ -0,0 +1,21 @@
[tool.poetry]
name = "aitbc-{SERVICE_NAME}"
version = "0.1.0"
description = "AITBC {SERVICE_DESC}"
authors = ["AITBC Team <team@aitbc.dev>"]
readme = "README.md"
packages = [{include = "app", from = "src"}]
[tool.poetry.dependencies]
python = "^3.13"
aitbc = {path = "../../../"} # Root aitbc package
[tool.poetry.group.dev.dependencies]
pytest = ">=9.0.3"
pytest-asyncio = ">=1.3.0"
pytest-cov = ">=6.0.0"
httpx = ">=0.28.1"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

View File

View File

@@ -0,0 +1,63 @@
"""Shared configuration base for all AITBC services."""
from pydantic import Field, field_validator
from pydantic_settings import BaseSettings, SettingsConfigDict
from typing import List, Optional
import os
from pathlib import Path
from aitbc.constants import DATA_DIR, LOG_DIR
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
if self.adapter == "sqlite":
return f"sqlite:///{DATA_DIR}/data/service.db"
return f"{self.adapter}://localhost:5432/service"
model_config = SettingsConfigDict(
env_file=".env", env_file_encoding="utf-8", case_sensitive=False, extra="allow"
)
class ServiceSettings(BaseSettings):
"""Base settings for all AITBC microservices."""
model_config = SettingsConfigDict(
env_file=".env", env_file_encoding="utf-8", case_sensitive=False, extra="allow"
)
# Environment
service_name: str = "aitbc-service"
app_env: str = "dev"
app_host: str = "127.0.0.1"
app_port: int = 8000
debug: bool = False
# Logging
log_level: str = "INFO"
log_dir: str = str(LOG_DIR / "services")
# Database
database: DatabaseConfig = DatabaseConfig()
# API
api_prefix: str = "/api/v1"
# Feature flags
enable_metrics: bool = True
enable_health_check: bool = True

View File

@@ -0,0 +1,75 @@
"""Shared database utilities for AITBC services."""
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, declarative_base
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from typing import AsyncGenerator
import os
from .config import DatabaseConfig
Base = declarative_base()
def get_engine(config: DatabaseConfig):
"""Create SQLAlchemy engine based on configuration."""
if config.adapter == "sqlite":
# SQLite needs special handling for async
engine = create_engine(
config.effective_url,
pool_size=config.pool_size,
max_overflow=config.max_overflow,
pool_pre_ping=config.pool_pre_ping,
echo=config.db_echo if hasattr(config, 'db_echo') else False
)
return engine
else:
return create_engine(
config.effective_url,
pool_size=config.pool_size,
max_overflow=config.max_overflow,
pool_pre_ping=config.pool_pre_ping,
echo=config.db_echo if hasattr(config, 'db_echo') else False
)
def get_sessionmaker(engine):
"""Create session factory."""
return sessionmaker(bind=engine, autoflush=False, autocommit=False)
# Async support (for async services)
def get_async_engine(config: DatabaseConfig):
"""Create async SQLAlchemy engine."""
if config.adapter == "sqlite":
# SQLite async uses aiosqlite
async_url = config.effective_url.replace("sqlite:///", "sqlite+aiosqlite:///")
return create_async_engine(
async_url,
echo=config.db_echo if hasattr(config, 'db_echo') else False
)
else:
# PostgreSQL async uses asyncpg
async_url = config.effective_url.replace("postgresql://", "postgresql+asyncpg://")
return create_async_engine(
async_url,
pool_size=config.pool_size,
max_overflow=config.max_overflow,
echo=config.db_echo if hasattr(config, 'db_echo') else False
)
async def get_async_session(engine) -> AsyncGenerator[AsyncSession, None]:
"""Dependency for FastAPI async endpoints."""
async_session = sessionmaker(
engine, class_=AsyncSession, expire_on_commit=False
)
async with async_session() as session:
try:
yield session
await session.commit()
except Exception:
await session.rollback()
raise
finally:
await session.close()

View File

@@ -0,0 +1,66 @@
"""Shared logging configuration for AITBC services."""
import logging
import sys
from pathlib import Path
from typing import Optional
from ..core.config import ServiceSettings
def setup_logging(settings: Optional[ServiceSettings] = None, level: str = None) -> logging.Logger:
"""Configure structured logging for the service.
Args:
settings: Service settings containing log configuration
level: Override log level
Returns:
Configured root logger
"""
if settings:
log_level = level or settings.log_level
log_dir = Path(settings.log_dir)
else:
log_level = level or "INFO"
log_dir = Path("/var/log/aitbc/services")
log_dir.mkdir(parents=True, exist_ok=True)
# Create formatter
formatter = logging.Formatter(
fmt="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
datefmt="%Y-%m-%d %H:%M:%S"
)
# Configure root logger
root_logger = logging.getLogger()
root_logger.setLevel(getattr(logging, log_level.upper()))
# Clear existing handlers
root_logger.handlers.clear()
# Console handler
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setFormatter(formatter)
root_logger.addHandler(console_handler)
# File handler
if settings and settings.service_name:
file_handler = logging.FileHandler(
log_dir / f"{settings.service_name}.log"
)
file_handler.setFormatter(formatter)
root_logger.addHandler(file_handler)
return root_logger
def get_logger(name: str) -> logging.Logger:
"""Get a logger with the given name.
Usage:
from aitbc import get_logger
logger = get_logger(__name__)
"""
return logging.getLogger(name)