mirror of
https://github.com/Xe138/AI-Trader.git
synced 2026-04-02 09:37:23 -04:00
Major architecture transformation from batch-only to API service with
database persistence for Windmill integration.
## REST API Implementation
- POST /simulate/trigger - Start simulation jobs
- GET /simulate/status/{job_id} - Monitor job progress
- GET /results - Query results with filters (job_id, date, model)
- GET /health - Service health checks
## Database Layer
- SQLite persistence with 6 tables (jobs, job_details, positions,
holdings, reasoning_logs, tool_usage)
- Foreign key constraints with cascade deletes
- Replaces JSONL file storage
## Backend Components
- JobManager: Job lifecycle management with concurrency control
- RuntimeConfigManager: Thread-safe isolated runtime configs
- ModelDayExecutor: Single model-day execution engine
- SimulationWorker: Date-sequential, model-parallel orchestration
## Testing
- 102 unit and integration tests (85% coverage)
- Database: 98% coverage
- Job manager: 98% coverage
- API endpoints: 81% coverage
- Pydantic models: 100% coverage
- TDD approach throughout
## Docker Deployment
- Dual-mode: API server (persistent) + batch (one-time)
- Health checks with 30s interval
- Volume persistence for database and logs
- Separate entrypoints for each mode
## Validation Tools
- scripts/validate_docker_build.sh - Build validation
- scripts/test_api_endpoints.sh - Complete API testing
- scripts/test_batch_mode.sh - Batch mode validation
- DOCKER_API.md - Deployment guide
- TESTING_GUIDE.md - Testing procedures
## Configuration
- API_PORT environment variable (default: 8080)
- Backwards compatible with existing configs
- FastAPI, uvicorn, pydantic>=2.0 dependencies
Co-Authored-By: AI Assistant <noreply@example.com>
132 lines
4.0 KiB
Python
132 lines
4.0 KiB
Python
"""
|
|
Runtime configuration manager for isolated model-day execution.
|
|
|
|
This module provides:
|
|
- Isolated runtime config file creation per model-day
|
|
- Prevention of state collisions between concurrent executions
|
|
- Automatic cleanup of temporary config files
|
|
"""
|
|
|
|
import os
|
|
import json
|
|
from pathlib import Path
|
|
import logging
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class RuntimeConfigManager:
|
|
"""
|
|
Manages isolated runtime configuration files for concurrent model execution.
|
|
|
|
Problem:
|
|
Multiple models running concurrently need separate runtime_env.json files
|
|
to avoid race conditions on TODAY_DATE, SIGNATURE, IF_TRADE values.
|
|
|
|
Solution:
|
|
Create temporary runtime config file per model-day execution:
|
|
- /app/data/runtime_env_{job_id}_{model}_{date}.json
|
|
|
|
Lifecycle:
|
|
1. create_runtime_config() → Creates temp file
|
|
2. Executor sets RUNTIME_ENV_PATH env var
|
|
3. Agent uses isolated config via get_config_value/write_config_value
|
|
4. cleanup_runtime_config() → Deletes temp file
|
|
"""
|
|
|
|
def __init__(self, data_dir: str = "data"):
|
|
"""
|
|
Initialize RuntimeConfigManager.
|
|
|
|
Args:
|
|
data_dir: Directory for runtime config files (default: "data")
|
|
"""
|
|
self.data_dir = Path(data_dir)
|
|
self.data_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
def create_runtime_config(
|
|
self,
|
|
job_id: str,
|
|
model_sig: str,
|
|
date: str
|
|
) -> str:
|
|
"""
|
|
Create isolated runtime config file for this execution.
|
|
|
|
Args:
|
|
job_id: Job UUID
|
|
model_sig: Model signature
|
|
date: Trading date (YYYY-MM-DD)
|
|
|
|
Returns:
|
|
Path to created runtime config file
|
|
|
|
Example:
|
|
config_path = manager.create_runtime_config(
|
|
"abc123...",
|
|
"gpt-5",
|
|
"2025-01-16"
|
|
)
|
|
# Returns: "data/runtime_env_abc123_gpt-5_2025-01-16.json"
|
|
"""
|
|
# Generate unique filename (use first 8 chars of job_id for brevity)
|
|
job_id_short = job_id[:8] if len(job_id) > 8 else job_id
|
|
filename = f"runtime_env_{job_id_short}_{model_sig}_{date}.json"
|
|
config_path = self.data_dir / filename
|
|
|
|
# Initialize with default values
|
|
initial_config = {
|
|
"TODAY_DATE": date,
|
|
"SIGNATURE": model_sig,
|
|
"IF_TRADE": False,
|
|
"JOB_ID": job_id
|
|
}
|
|
|
|
with open(config_path, "w", encoding="utf-8") as f:
|
|
json.dump(initial_config, f, indent=4)
|
|
|
|
logger.debug(f"Created runtime config: {config_path}")
|
|
return str(config_path)
|
|
|
|
def cleanup_runtime_config(self, config_path: str) -> None:
|
|
"""
|
|
Delete runtime config file after execution.
|
|
|
|
Args:
|
|
config_path: Path to runtime config file
|
|
|
|
Note:
|
|
Silently ignores if file doesn't exist (already cleaned up)
|
|
"""
|
|
try:
|
|
if os.path.exists(config_path):
|
|
os.unlink(config_path)
|
|
logger.debug(f"Cleaned up runtime config: {config_path}")
|
|
except Exception as e:
|
|
logger.warning(f"Failed to cleanup runtime config {config_path}: {e}")
|
|
|
|
def cleanup_all_runtime_configs(self) -> int:
|
|
"""
|
|
Cleanup all runtime config files (for maintenance/startup).
|
|
|
|
Returns:
|
|
Number of files deleted
|
|
|
|
Use case:
|
|
- On API startup to clean stale configs from previous runs
|
|
- Periodic maintenance
|
|
"""
|
|
count = 0
|
|
for config_file in self.data_dir.glob("runtime_env_*.json"):
|
|
try:
|
|
config_file.unlink()
|
|
count += 1
|
|
logger.debug(f"Deleted stale runtime config: {config_file}")
|
|
except Exception as e:
|
|
logger.warning(f"Failed to delete {config_file}: {e}")
|
|
|
|
if count > 0:
|
|
logger.info(f"Cleaned up {count} stale runtime config files")
|
|
|
|
return count
|