feat: add claude-code-router support for multi-model testing
- Add ROUTER=true flag to route requests through claude-code-router - Add router service to docker-compose with profile-based activation - Support OpenAI (gpt-4o) and Google Gemini (gemini-2.5-pro) as alternatives - Add router-config.json with provider configuration template - Update .env.example with provider API key options - Document router mode limitations (cost tracking shows $0)
This commit is contained in:
+26
-1
@@ -1,8 +1,33 @@
|
|||||||
# Shannon Environment Configuration
|
# Shannon Environment Configuration
|
||||||
# Copy this file to .env and fill in your credentials
|
# Copy this file to .env and fill in your credentials
|
||||||
|
|
||||||
# Anthropic API Key (required - choose one)
|
# Recommended output token configuration for larger tool outputs
|
||||||
|
CLAUDE_CODE_MAX_OUTPUT_TOKENS=64000
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# OPTION 1: Direct Anthropic (default, no router)
|
||||||
|
# =============================================================================
|
||||||
ANTHROPIC_API_KEY=your-api-key-here
|
ANTHROPIC_API_KEY=your-api-key-here
|
||||||
|
|
||||||
# OR use OAuth token instead
|
# OR use OAuth token instead
|
||||||
# CLAUDE_CODE_OAUTH_TOKEN=your-oauth-token-here
|
# CLAUDE_CODE_OAUTH_TOKEN=your-oauth-token-here
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# OPTION 2: Router Mode (use alternative providers)
|
||||||
|
# =============================================================================
|
||||||
|
# Enable router mode by running: ./shannon start ... ROUTER=true
|
||||||
|
# Then configure ONE of the providers below:
|
||||||
|
|
||||||
|
# --- OpenAI ---
|
||||||
|
# OPENAI_API_KEY=sk-your-openai-key
|
||||||
|
# ROUTER_DEFAULT=openai,gpt-4o
|
||||||
|
|
||||||
|
# --- Google Gemini ---
|
||||||
|
GEMINI_API_KEY=your-gemini-key
|
||||||
|
ROUTER_DEFAULT=gemini,gemini-2.5-pro
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Available Models
|
||||||
|
# =============================================================================
|
||||||
|
# OpenAI: gpt-4o, gpt-4o-mini
|
||||||
|
# Gemini: gemini-2.5-pro, gemini-2.5-flash
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ CONFIG=<file> YAML configuration file for authentication and testing pa
|
|||||||
OUTPUT=<path> Custom output directory for session folder (default: ./audit-logs/)
|
OUTPUT=<path> Custom output directory for session folder (default: ./audit-logs/)
|
||||||
PIPELINE_TESTING=true Use minimal prompts and fast retry intervals (10s instead of 5min)
|
PIPELINE_TESTING=true Use minimal prompts and fast retry intervals (10s instead of 5min)
|
||||||
REBUILD=true Force Docker rebuild with --no-cache (use when code changes aren't picked up)
|
REBUILD=true Force Docker rebuild with --no-cache (use when code changes aren't picked up)
|
||||||
|
ROUTER=true Route requests through claude-code-router for multi-model support (see limitations below)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Generate TOTP for Authentication
|
### Generate TOTP for Authentication
|
||||||
@@ -284,6 +285,16 @@ Missing tools can be skipped using `PIPELINE_TESTING=true` mode during developme
|
|||||||
- `subfinder` - Subdomain discovery
|
- `subfinder` - Subdomain discovery
|
||||||
- `whatweb` - Web technology detection
|
- `whatweb` - Web technology detection
|
||||||
|
|
||||||
|
### Router Mode Limitations
|
||||||
|
When using `ROUTER=true` to route requests through claude-code-router (e.g., to use OpenAI models):
|
||||||
|
|
||||||
|
**Cost tracking shows $0.00**: The Claude Agent SDK expects `total_cost_usd` in the result message, which is Anthropic-specific. OpenAI's API returns token counts in `usage` but not a cost field, and the router doesn't translate this. This is a known limitation of the router, not a Shannon bug.
|
||||||
|
|
||||||
|
**Workarounds:**
|
||||||
|
- Accept $0 costs when using router mode (recommended for dev/testing)
|
||||||
|
- Use Anthropic directly for production runs where cost tracking matters
|
||||||
|
- Use external tools like `ccusage` for post-hoc token analysis
|
||||||
|
|
||||||
### Diagnostic & Utility Scripts
|
### Diagnostic & Utility Scripts
|
||||||
```bash
|
```bash
|
||||||
# View Temporal workflow history
|
# View Temporal workflow history
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"HOST": "0.0.0.0",
|
||||||
|
"APIKEY": "shannon-router-key",
|
||||||
|
"LOG": true,
|
||||||
|
"LOG_LEVEL": "info",
|
||||||
|
"NON_INTERACTIVE_MODE": true,
|
||||||
|
"API_TIMEOUT_MS": 600000,
|
||||||
|
"Providers": [
|
||||||
|
{
|
||||||
|
"name": "openai",
|
||||||
|
"api_base_url": "https://api.openai.com/v1/chat/completions",
|
||||||
|
"api_key": "$OPENAI_API_KEY",
|
||||||
|
"models": ["gpt-4o", "gpt-4o-mini"],
|
||||||
|
"transformer": {
|
||||||
|
"use": [["maxtoken", { "max_tokens": 16384 }]]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "gemini",
|
||||||
|
"api_base_url": "https://generativelanguage.googleapis.com/v1beta/models/",
|
||||||
|
"api_key": "$GEMINI_API_KEY",
|
||||||
|
"models": ["gemini-2.5-pro", "gemini-2.5-flash"],
|
||||||
|
"transformer": {
|
||||||
|
"use": ["gemini"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Router": {
|
||||||
|
"default": "$ROUTER_DEFAULT"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,6 +20,8 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
- TEMPORAL_ADDRESS=temporal:7233
|
- TEMPORAL_ADDRESS=temporal:7233
|
||||||
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}
|
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}
|
||||||
|
- ANTHROPIC_BASE_URL=${ANTHROPIC_BASE_URL:-} # Optional: route through claude-code-router
|
||||||
|
- ANTHROPIC_AUTH_TOKEN=${ANTHROPIC_AUTH_TOKEN:-} # Auth token for router
|
||||||
- CLAUDE_CODE_OAUTH_TOKEN=${CLAUDE_CODE_OAUTH_TOKEN:-}
|
- CLAUDE_CODE_OAUTH_TOKEN=${CLAUDE_CODE_OAUTH_TOKEN:-}
|
||||||
- CLAUDE_CODE_MAX_OUTPUT_TOKENS=${CLAUDE_CODE_MAX_OUTPUT_TOKENS:-64000}
|
- CLAUDE_CODE_MAX_OUTPUT_TOKENS=${CLAUDE_CODE_MAX_OUTPUT_TOKENS:-64000}
|
||||||
depends_on:
|
depends_on:
|
||||||
@@ -36,5 +38,33 @@ services:
|
|||||||
security_opt:
|
security_opt:
|
||||||
- seccomp:unconfined
|
- seccomp:unconfined
|
||||||
|
|
||||||
|
# Optional: claude-code-router for multi-model support
|
||||||
|
# Start with: ROUTER=true ./shannon start ...
|
||||||
|
router:
|
||||||
|
image: node:20-slim
|
||||||
|
profiles: ["router"] # Only starts when explicitly requested
|
||||||
|
command: >
|
||||||
|
sh -c "apt-get update && apt-get install -y gettext-base &&
|
||||||
|
npm install -g @musistudio/claude-code-router &&
|
||||||
|
mkdir -p /root/.claude-code-router &&
|
||||||
|
envsubst < /config/router-config.json > /root/.claude-code-router/config.json &&
|
||||||
|
ccr start"
|
||||||
|
ports:
|
||||||
|
- "3456:3456"
|
||||||
|
volumes:
|
||||||
|
- ./configs/router-config.json:/config/router-config.json:ro
|
||||||
|
environment:
|
||||||
|
- HOST=0.0.0.0
|
||||||
|
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}
|
||||||
|
- OPENAI_API_KEY=${OPENAI_API_KEY:-}
|
||||||
|
- GEMINI_API_KEY=${GEMINI_API_KEY:-}
|
||||||
|
- ROUTER_DEFAULT=${ROUTER_DEFAULT:-openai,gpt-4o}
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "node", "-e", "require('http').get('http://localhost:3456/health', r => process.exit(r.statusCode === 200 ? 0 : 1)).on('error', () => process.exit(1))"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
start_period: 30s
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
temporal-data:
|
temporal-data:
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ Options for 'start':
|
|||||||
CONFIG=<path> Configuration file (YAML)
|
CONFIG=<path> Configuration file (YAML)
|
||||||
OUTPUT=<path> Output directory for reports (default: ./audit-logs/)
|
OUTPUT=<path> Output directory for reports (default: ./audit-logs/)
|
||||||
PIPELINE_TESTING=true Use minimal prompts for fast testing
|
PIPELINE_TESTING=true Use minimal prompts for fast testing
|
||||||
|
ROUTER=true Route requests through claude-code-router (multi-model support)
|
||||||
|
|
||||||
Options for 'stop':
|
Options for 'stop':
|
||||||
CLEAN=true Remove all data including volumes
|
CLEAN=true Remove all data including volumes
|
||||||
@@ -63,6 +64,7 @@ parse_args() {
|
|||||||
CLEAN=*) CLEAN="${arg#CLEAN=}" ;;
|
CLEAN=*) CLEAN="${arg#CLEAN=}" ;;
|
||||||
PIPELINE_TESTING=*) PIPELINE_TESTING="${arg#PIPELINE_TESTING=}" ;;
|
PIPELINE_TESTING=*) PIPELINE_TESTING="${arg#PIPELINE_TESTING=}" ;;
|
||||||
REBUILD=*) REBUILD="${arg#REBUILD=}" ;;
|
REBUILD=*) REBUILD="${arg#REBUILD=}" ;;
|
||||||
|
ROUTER=*) ROUTER="${arg#ROUTER=}" ;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
@@ -121,10 +123,16 @@ cmd_start() {
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check for API key
|
# Check for API key (router mode can use OPENAI_API_KEY or GEMINI_API_KEY instead)
|
||||||
if [ -z "$ANTHROPIC_API_KEY" ] && [ -z "$CLAUDE_CODE_OAUTH_TOKEN" ]; then
|
if [ -z "$ANTHROPIC_API_KEY" ] && [ -z "$CLAUDE_CODE_OAUTH_TOKEN" ]; then
|
||||||
echo "ERROR: Set ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN in .env"
|
if [ "$ROUTER" = "true" ] && { [ -n "$OPENAI_API_KEY" ] || [ -n "$GEMINI_API_KEY" ]; }; then
|
||||||
exit 1
|
# Router mode with alternative provider - set a placeholder for SDK init
|
||||||
|
export ANTHROPIC_API_KEY="router-mode"
|
||||||
|
else
|
||||||
|
echo "ERROR: Set ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN in .env"
|
||||||
|
echo " (or use ROUTER=true with OPENAI_API_KEY or GEMINI_API_KEY)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Determine container path for REPO
|
# Determine container path for REPO
|
||||||
@@ -149,6 +157,33 @@ cmd_start() {
|
|||||||
export OUTPUT_DIR="$OUTPUT"
|
export OUTPUT_DIR="$OUTPUT"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Handle ROUTER flag - start claude-code-router for multi-model support
|
||||||
|
if [ "$ROUTER" = "true" ]; then
|
||||||
|
# Check if router is already running
|
||||||
|
if docker compose -f "$COMPOSE_FILE" --profile router ps router 2>/dev/null | grep -q "running"; then
|
||||||
|
echo "Router already running, skipping startup..."
|
||||||
|
else
|
||||||
|
echo "Starting claude-code-router..."
|
||||||
|
|
||||||
|
# Check for OpenAI API key
|
||||||
|
if [ -z "$OPENAI_API_KEY" ] && [ -z "$GEMINI_API_KEY" ]; then
|
||||||
|
echo "WARNING: Neither OPENAI_API_KEY nor GEMINI_API_KEY set. Router may not work."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Start router with profile
|
||||||
|
docker compose -f "$COMPOSE_FILE" --profile router up -d router
|
||||||
|
|
||||||
|
# Give router a few seconds to start (health check disabled for now - TODO: debug later)
|
||||||
|
echo "Waiting for router to start..."
|
||||||
|
sleep 5
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set ANTHROPIC_BASE_URL to route through router
|
||||||
|
export ANTHROPIC_BASE_URL="http://router:3456"
|
||||||
|
# Set auth token to match router's APIKEY
|
||||||
|
export ANTHROPIC_AUTH_TOKEN="shannon-router-key"
|
||||||
|
fi
|
||||||
|
|
||||||
# Ensure containers are running (starts them if needed)
|
# Ensure containers are running (starts them if needed)
|
||||||
ensure_containers
|
ensure_containers
|
||||||
|
|
||||||
@@ -226,9 +261,9 @@ cmd_stop() {
|
|||||||
parse_args "$@"
|
parse_args "$@"
|
||||||
|
|
||||||
if [ "$CLEAN" = "true" ]; then
|
if [ "$CLEAN" = "true" ]; then
|
||||||
docker compose -f "$COMPOSE_FILE" down -v
|
docker compose -f "$COMPOSE_FILE" --profile router down -v
|
||||||
else
|
else
|
||||||
docker compose -f "$COMPOSE_FILE" down
|
docker compose -f "$COMPOSE_FILE" --profile router down
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user