mirror of
https://github.com/Xe138/AI-Trader.git
synced 2026-04-01 17:17:24 -04:00
docs: add integration tests and documentation for config override system
This commit is contained in:
@@ -729,6 +729,29 @@ Server loads model definitions from configuration file (default: `configs/defaul
|
||||
- `openai_base_url` - Optional custom API endpoint
|
||||
- `openai_api_key` - Optional model-specific API key
|
||||
|
||||
### Configuration Override System
|
||||
|
||||
**Default config:** `/app/configs/default_config.json` (baked into image)
|
||||
|
||||
**Custom config:** `/app/user-configs/config.json` (optional, via volume mount)
|
||||
|
||||
**Merge behavior:**
|
||||
- Custom config sections completely replace default sections (root-level merge)
|
||||
- If no custom config exists, defaults are used
|
||||
- Validation occurs at container startup (before API starts)
|
||||
- Invalid config causes immediate exit with detailed error message
|
||||
|
||||
**Example custom config** (overrides models only):
|
||||
```json
|
||||
{
|
||||
"models": [
|
||||
{"name": "gpt-5", "basemodel": "openai/gpt-5", "signature": "gpt-5", "enabled": true}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
All other sections (`agent_config`, `log_config`, etc.) inherited from default.
|
||||
|
||||
---
|
||||
|
||||
## OpenAPI / Swagger Documentation
|
||||
|
||||
@@ -54,7 +54,36 @@ JINA_API_KEY=your-jina-key-here
|
||||
|
||||
---
|
||||
|
||||
## Step 3: Start the API Server
|
||||
## Step 3: (Optional) Custom Model Configuration
|
||||
|
||||
To use different AI models than the defaults, create a custom config:
|
||||
|
||||
1. Create config directory:
|
||||
```bash
|
||||
mkdir -p configs
|
||||
```
|
||||
|
||||
2. Create `configs/config.json`:
|
||||
```json
|
||||
{
|
||||
"models": [
|
||||
{
|
||||
"name": "my-gpt-4",
|
||||
"basemodel": "openai/gpt-4",
|
||||
"signature": "my-gpt-4",
|
||||
"enabled": true
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
3. The Docker container will automatically merge this with default settings.
|
||||
|
||||
Your custom config only needs to include sections you want to override.
|
||||
|
||||
---
|
||||
|
||||
## Step 4: Start the API Server
|
||||
|
||||
```bash
|
||||
docker-compose up -d
|
||||
@@ -79,7 +108,7 @@ docker logs -f ai-trader-server
|
||||
|
||||
---
|
||||
|
||||
## Step 4: Verify Service is Running
|
||||
## Step 5: Verify Service is Running
|
||||
|
||||
```bash
|
||||
curl http://localhost:8080/health
|
||||
@@ -99,7 +128,7 @@ If you see `"status": "healthy"`, you're ready!
|
||||
|
||||
---
|
||||
|
||||
## Step 5: Run Your First Simulation
|
||||
## Step 6: Run Your First Simulation
|
||||
|
||||
Trigger a simulation for a single day with GPT-4:
|
||||
|
||||
@@ -130,7 +159,7 @@ curl -X POST http://localhost:8080/simulate/trigger \
|
||||
|
||||
---
|
||||
|
||||
## Step 6: Monitor Progress
|
||||
## Step 7: Monitor Progress
|
||||
|
||||
```bash
|
||||
# Replace with your job_id from Step 5
|
||||
@@ -175,7 +204,7 @@ curl http://localhost:8080/simulate/status/$JOB_ID
|
||||
|
||||
---
|
||||
|
||||
## Step 7: View Results
|
||||
## Step 8: View Results
|
||||
|
||||
```bash
|
||||
curl "http://localhost:8080/results?job_id=$JOB_ID" | jq '.'
|
||||
|
||||
121
tests/integration/test_config_override.py
Normal file
121
tests/integration/test_config_override.py
Normal file
@@ -0,0 +1,121 @@
|
||||
"""Integration tests for config override system."""
|
||||
|
||||
import pytest
|
||||
import json
|
||||
import subprocess
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def test_configs(tmp_path):
|
||||
"""Create test config files."""
|
||||
# Default config
|
||||
default_config = {
|
||||
"agent_type": "BaseAgent",
|
||||
"date_range": {"init_date": "2025-10-01", "end_date": "2025-10-21"},
|
||||
"models": [
|
||||
{"name": "default-model", "basemodel": "openai/gpt-4", "signature": "default", "enabled": True}
|
||||
],
|
||||
"agent_config": {"max_steps": 30, "max_retries": 3, "base_delay": 1.0, "initial_cash": 10000.0},
|
||||
"log_config": {"log_path": "./data/agent_data"}
|
||||
}
|
||||
|
||||
configs_dir = tmp_path / "configs"
|
||||
configs_dir.mkdir()
|
||||
|
||||
default_path = configs_dir / "default_config.json"
|
||||
with open(default_path, 'w') as f:
|
||||
json.dump(default_config, f, indent=2)
|
||||
|
||||
return configs_dir, default_config
|
||||
|
||||
|
||||
def test_config_override_models_only(test_configs):
|
||||
"""Test overriding only the models section."""
|
||||
configs_dir, default_config = test_configs
|
||||
|
||||
# Custom config - only override models
|
||||
custom_config = {
|
||||
"models": [
|
||||
{"name": "gpt-5", "basemodel": "openai/gpt-5", "signature": "gpt-5", "enabled": True}
|
||||
]
|
||||
}
|
||||
|
||||
user_configs_dir = configs_dir.parent / "user-configs"
|
||||
user_configs_dir.mkdir()
|
||||
|
||||
custom_path = user_configs_dir / "config.json"
|
||||
with open(custom_path, 'w') as f:
|
||||
json.dump(custom_config, f, indent=2)
|
||||
|
||||
# Run merge
|
||||
result = subprocess.run(
|
||||
[
|
||||
"python", "-c",
|
||||
f"import sys; sys.path.insert(0, '.'); "
|
||||
f"from tools.config_merger import DEFAULT_CONFIG_PATH, CUSTOM_CONFIG_PATH, OUTPUT_CONFIG_PATH, merge_and_validate; "
|
||||
f"import tools.config_merger; "
|
||||
f"tools.config_merger.DEFAULT_CONFIG_PATH = '{configs_dir}/default_config.json'; "
|
||||
f"tools.config_merger.CUSTOM_CONFIG_PATH = '{custom_path}'; "
|
||||
f"tools.config_merger.OUTPUT_CONFIG_PATH = '{configs_dir.parent}/runtime.json'; "
|
||||
f"merge_and_validate()"
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
cwd="/home/bballou/AI-Trader/.worktrees/config-override-system"
|
||||
)
|
||||
|
||||
assert result.returncode == 0, f"Merge failed: {result.stderr}"
|
||||
|
||||
# Verify merged config
|
||||
runtime_path = configs_dir.parent / "runtime.json"
|
||||
with open(runtime_path, 'r') as f:
|
||||
merged = json.load(f)
|
||||
|
||||
# Models should be overridden
|
||||
assert merged["models"] == custom_config["models"]
|
||||
|
||||
# Other sections should be from default
|
||||
assert merged["agent_config"] == default_config["agent_config"]
|
||||
assert merged["date_range"] == default_config["date_range"]
|
||||
|
||||
|
||||
def test_config_validation_fails_gracefully(test_configs):
|
||||
"""Test that invalid config causes exit with clear error."""
|
||||
configs_dir, _ = test_configs
|
||||
|
||||
# Invalid custom config (no enabled models)
|
||||
custom_config = {
|
||||
"models": [
|
||||
{"name": "test", "basemodel": "openai/gpt-4", "signature": "test", "enabled": False}
|
||||
]
|
||||
}
|
||||
|
||||
user_configs_dir = configs_dir.parent / "user-configs"
|
||||
user_configs_dir.mkdir()
|
||||
|
||||
custom_path = user_configs_dir / "config.json"
|
||||
with open(custom_path, 'w') as f:
|
||||
json.dump(custom_config, f, indent=2)
|
||||
|
||||
# Run merge (should fail)
|
||||
result = subprocess.run(
|
||||
[
|
||||
"python", "-c",
|
||||
f"import sys; sys.path.insert(0, '.'); "
|
||||
f"from tools.config_merger import merge_and_validate; "
|
||||
f"import tools.config_merger; "
|
||||
f"tools.config_merger.DEFAULT_CONFIG_PATH = '{configs_dir}/default_config.json'; "
|
||||
f"tools.config_merger.CUSTOM_CONFIG_PATH = '{custom_path}'; "
|
||||
f"tools.config_merger.OUTPUT_CONFIG_PATH = '{configs_dir.parent}/runtime.json'; "
|
||||
f"merge_and_validate()"
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
cwd="/home/bballou/AI-Trader/.worktrees/config-override-system"
|
||||
)
|
||||
|
||||
assert result.returncode == 1
|
||||
assert "CONFIG VALIDATION FAILED" in result.stderr
|
||||
assert "At least one model must be enabled" in result.stderr
|
||||
Reference in New Issue
Block a user