Skip to content

Commit 22a2e95

Browse files
committed
Add Docker networking support, Redis container management, and enhance Swagger API documentation.
1 parent a0ba3c6 commit 22a2e95

File tree

8 files changed

+223
-42
lines changed

8 files changed

+223
-42
lines changed

bin/docker-run

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ HOST_PORT=""
1515
REDIS_HOST="${REDIS_HOST:-host.docker.internal}"
1616
REDIS_PORT="${REDIS_PORT:-6379}"
1717
DAEMON_MODE=""
18+
DOCKER_NETWORK="autobox-network" # Default to autobox-network for Redis connectivity
1819

1920
while [[ $# -gt 0 ]]; do
2021
case $1 in
@@ -42,6 +43,10 @@ while [[ $# -gt 0 ]]; do
4243
REDIS_PORT="$2"
4344
shift 2
4445
;;
46+
--network|-n)
47+
DOCKER_NETWORK="$2"
48+
shift 2
49+
;;
4550
--help|-h)
4651
echo "Usage: $0 [OPTIONS]"
4752
echo ""
@@ -50,8 +55,9 @@ while [[ $# -gt 0 ]]; do
5055
echo " -d, --daemon Keep server alive after simulation (default: false)"
5156
echo " -t, --tag TAG Docker image tag (default: latest)"
5257
echo " -p, --host-port PORT Host port to bind (default: auto-detect from server.json)"
53-
echo " --redis-host HOST Redis host (default: host.docker.internal)"
58+
echo " --redis-host HOST Redis host (default: host.docker.internal, or 'redis' if using --network)"
5459
echo " --redis-port PORT Redis port (default: 6379)"
60+
echo " -n, --network NAME Docker network to join (default: autobox-network)"
5561
echo " -h, --help Show this help message"
5662
exit 0
5763
;;
@@ -120,10 +126,70 @@ else
120126
PORT_ARGS=(-P)
121127
fi
122128

129+
# If network is specified, ensure it exists and setup Redis
130+
if [ -n "$DOCKER_NETWORK" ]; then
131+
# Create network if it doesn't exist
132+
if ! docker network inspect "$DOCKER_NETWORK" &>/dev/null; then
133+
echo "Creating Docker network: $DOCKER_NETWORK"
134+
docker network create "$DOCKER_NETWORK"
135+
fi
136+
137+
# Check if Redis container exists and is running on this network
138+
if ! docker ps --format '{{.Names}}' | grep -q "^autobox-redis$"; then
139+
# Check if Redis container exists but is stopped
140+
if docker ps -a --format '{{.Names}}' | grep -q "^autobox-redis$"; then
141+
echo "Starting existing Redis container..."
142+
docker start autobox-redis
143+
# Connect to network if not already connected
144+
docker network connect "$DOCKER_NETWORK" autobox-redis 2>/dev/null || true
145+
else
146+
echo "Starting Redis container on network: $DOCKER_NETWORK"
147+
docker run -d \
148+
--name autobox-redis \
149+
--network "$DOCKER_NETWORK" \
150+
-p 6379:6379 \
151+
redis:alpine
152+
fi
153+
else
154+
# Redis is running, ensure it's on the network
155+
docker network connect "$DOCKER_NETWORK" autobox-redis 2>/dev/null || true
156+
fi
157+
158+
# Always wait for Redis to be ready
159+
echo "Waiting for Redis to be ready..."
160+
for i in {1..30}; do
161+
if docker exec autobox-redis redis-cli ping &>/dev/null; then
162+
echo "Redis is ready!"
163+
break
164+
fi
165+
sleep 1
166+
done
167+
168+
# Get Redis container IP for explicit host mapping (bypasses DNS)
169+
REDIS_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' autobox-redis)
170+
if [ -z "$REDIS_IP" ]; then
171+
echo "Error: Could not get Redis container IP"
172+
exit 1
173+
fi
174+
echo "Redis IP: $REDIS_IP"
175+
176+
# Use 'redis' as hostname when on network
177+
if [ "$REDIS_HOST" = "host.docker.internal" ]; then
178+
REDIS_HOST="redis"
179+
fi
180+
echo "Network: $DOCKER_NETWORK (Redis hostname: $REDIS_HOST -> $REDIS_IP)"
181+
182+
# Use explicit host mapping to bypass DNS
183+
NETWORK_ARGS=(--network "$DOCKER_NETWORK" --add-host "redis:$REDIS_IP")
184+
else
185+
NETWORK_ARGS=()
186+
fi
187+
123188
docker run \
124189
--rm \
125190
-it \
126191
"${PORT_ARGS[@]}" \
192+
"${NETWORK_ARGS[@]}" \
127193
-e OPENAI_API_KEY="$OPENAI_API_KEY" \
128194
-e REDIS_HOST="$REDIS_HOST" \
129195
-e REDIS_PORT="$REDIS_PORT" \
@@ -133,6 +199,7 @@ docker run \
133199
-e JWT_EXPIRES_IN="${JWT_EXPIRES_IN:-7d}" \
134200
-v "$(pwd)/examples/simulations:/app/examples/simulations:ro" \
135201
-v "$(pwd)/examples/metrics:/app/examples/metrics:ro" \
202+
-v "$(pwd)/examples/server:/app/examples/server:ro" \
136203
-v "$(pwd)/logs:/app/logs" \
137204
"autobox-engine:${IMAGE_TAG}" \
138205
--config=/app/examples \

bin/docker-stop

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#!/usr/bin/env bash
2+
3+
set -e
4+
5+
echo "Stopping Autobox Engine containers..."
6+
7+
# Stop and remove autobox-engine container(s)
8+
echo "Stopping autobox-engine containers..."
9+
docker ps -a --filter "ancestor=autobox-engine:latest" --format "{{.ID}}" | xargs -r docker stop
10+
docker ps -a --filter "ancestor=autobox-engine:latest" --format "{{.ID}}" | xargs -r docker rm
11+
12+
# Stop and remove autobox-redis container if it exists
13+
if docker ps -a --format '{{.Names}}' | grep -q "^autobox-redis$"; then
14+
echo "Stopping autobox-redis container..."
15+
docker stop autobox-redis
16+
docker rm autobox-redis
17+
fi
18+
19+
# Optional: Remove the network if no other containers are using it
20+
if docker network inspect autobox-network &>/dev/null; then
21+
echo "Removing autobox-network..."
22+
docker network rm autobox-network 2>/dev/null || echo "Network still in use or already removed"
23+
fi
24+
25+
echo "✅ All Autobox containers stopped and removed"

docker-compose.yml

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
services:
2+
redis:
3+
image: redis:alpine
4+
container_name: autobox-redis
5+
ports:
6+
- "6379:6379"
7+
networks:
8+
- autobox-network
9+
healthcheck:
10+
test: ["CMD", "redis-cli", "ping"]
11+
interval: 5s
12+
timeout: 3s
13+
retries: 5
14+
restart: unless-stopped
15+
16+
autobox:
17+
build:
18+
context: .
19+
dockerfile: Dockerfile
20+
image: autobox-engine:latest
21+
container_name: autobox-engine
22+
environment:
23+
- OPENAI_API_KEY=${OPENAI_API_KEY}
24+
- REDIS_HOST=redis
25+
- REDIS_PORT=6379
26+
- PORT=9000
27+
- NODE_ENV=production
28+
- JWT_SECRET=${JWT_SECRET:-autobox-default-secret}
29+
- JWT_EXPIRES_IN=${JWT_EXPIRES_IN:-7d}
30+
volumes:
31+
- ./examples/simulations:/app/examples/simulations:ro
32+
- ./examples/metrics:/app/examples/metrics:ro
33+
- ./examples/server:/app/examples/server:ro
34+
- ./logs:/app/logs
35+
ports:
36+
- "9000:9000"
37+
command: >
38+
--config=/app/examples
39+
--simulation-name=gift_choice
40+
networks:
41+
- autobox-network
42+
depends_on:
43+
redis:
44+
condition: service_healthy
45+
restart: "no"
46+
47+
autobox-dev:
48+
build:
49+
context: .
50+
dockerfile: Dockerfile.dev
51+
image: autobox-engine:dev
52+
container_name: autobox-engine-dev
53+
environment:
54+
- OPENAI_API_KEY=${OPENAI_API_KEY}
55+
volumes:
56+
- .:/app:rw
57+
- ./logs:/app/logs
58+
command: >
59+
uv run python -m autobox.main
60+
--config examples/simulations/summer_vacation.json
61+
--metrics examples/metrics/summer_vacation.json
62+
networks:
63+
- autobox-network
64+
profiles:
65+
- dev
66+
67+
networks:
68+
autobox-network:
69+
driver: bridge

src/index.ts

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,14 @@ import express, { Application } from 'express';
33
import { exit } from 'process';
44
import swaggerUi from 'swagger-ui-express';
55
import routes from './api/routes';
6-
import { startSimulation } from './runner';
6+
import { getConfig, startSimulation } from './runner';
77
// import { initializeRedisClient } from './clients';
88
import { logger } from './config';
9-
import { env } from './config/env';
109
// import { backfill, initializeDatabase, initializeStream } from './lib';
1110
import { errorHandler, requestMetrics, responseHandler } from './middlewares';
12-
import { apiSpec } from './swagger';
11+
import { getApiSpec } from './swagger';
1312

1413
const app: Application = express();
15-
const PORT = env.PORT;
1614

1715
app.use(
1816
cors({
@@ -26,30 +24,42 @@ app.use(express.json());
2624
app.use(express.urlencoded({ extended: true }));
2725
app.use(requestMetrics);
2826
app.use(responseHandler);
29-
app.use('/docs', swaggerUi.serve, swaggerUi.setup(apiSpec));
27+
app.use('/docs', swaggerUi.serve, swaggerUi.setup(getApiSpec()));
3028

3129
app.use('/', routes);
3230
app.use(errorHandler);
3331

34-
async function startServer() {
35-
try {
36-
// await Promise.all([initializeDatabase(), initializeRedisClient()]);
37-
// await Promise.all([backfill(), initializeStream()]);
38-
// logger.info('✅ Database backfilled');
39-
40-
app.listen(PORT, () => {
41-
logger.info(`🚀 Server is running on http://localhost:${PORT}`);
42-
});
43-
} catch (error) {
44-
logger.error('❌ Failed to start server:', error);
45-
exit(1);
46-
}
32+
async function startServer(port: number, host: string): Promise<void> {
33+
return new Promise((resolve, reject) => {
34+
try {
35+
// await Promise.all([initializeDatabase(), initializeRedisClient()]);
36+
// await Promise.all([backfill(), initializeStream()]);
37+
// logger.info('✅ Database backfilled');
38+
39+
app.listen(port, host, () => {
40+
logger.info(`🚀 Server is running on http://${host}:${port}`);
41+
resolve();
42+
});
43+
} catch (error) {
44+
logger.error('❌ Failed to start server:', error);
45+
reject(error);
46+
}
47+
});
4748
}
4849

49-
startServer().then(() => {
50-
logger.info('🌐 Express server started in background');
50+
async function start() {
51+
const { config, isDaemon } = getConfig();
52+
53+
logger.info('🌐 Starting Express server...');
54+
await startServer(config.server.port, config.server.host);
55+
5156
logger.info('🎯 Starting CLI simulation...');
52-
return startSimulation();
57+
await startSimulation(config, isDaemon);
58+
}
59+
60+
start().catch((error) => {
61+
logger.error('❌ Failed to start application:', error);
62+
exit(1);
5363
});
5464

5565
export default app;

src/runner.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Command } from 'commander';
22
import process from 'process';
33
import { loadConfig, logger } from './config';
44
import { runSimulation } from './core';
5+
import { Config } from './schemas';
56

67
const program = new Command();
78

@@ -14,7 +15,7 @@ program
1415
.option('-s, --simulation-name <simulationName>', 'simulation name', 'summer_vacation')
1516
.parse(process.argv);
1617

17-
export const startSimulation = async (): Promise<void> => {
18+
export const getConfig = (): { config: Config; isDaemon: boolean } => {
1819
const options = program.opts();
1920
const configPath = options.config;
2021
const isDaemon = options.daemon;
@@ -33,6 +34,12 @@ export const startSimulation = async (): Promise<void> => {
3334
configPath,
3435
});
3536

37+
logger.info(`🔌 Server will start on ${config.server.host}:${config.server.port}`);
38+
39+
return { config, isDaemon };
40+
};
41+
42+
export const startSimulation = async (config: Config, isDaemon: boolean): Promise<void> => {
3643
try {
3744
await runSimulation(config, { daemon: isDaemon });
3845

src/swagger/apiSpec.ts

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,24 @@
11
import { OpenApiGeneratorV3 } from '@asteasolutions/zod-to-openapi';
2+
import { env } from '../config/env';
23
import { openApiRegistry } from './openApiRegistry';
34

4-
export const apiSpec = new OpenApiGeneratorV3(openApiRegistry.definitions).generateDocument({
5-
openapi: '3.0.3',
6-
info: {
7-
version: '0.0.1',
8-
title: 'Autobox Engine',
9-
description: 'API to interact with Autobox Engine to run AI agents based simulations.',
10-
contact: {
11-
name: 'Autobox Team',
12-
email: 'support@autobox.io',
5+
export const getApiSpec = () => {
6+
return new OpenApiGeneratorV3(openApiRegistry.definitions).generateDocument({
7+
openapi: '3.0.3',
8+
info: {
9+
version: '0.0.1',
10+
title: 'Autobox Engine',
11+
description: 'API to interact with Autobox Engine to run AI agents based simulations.',
12+
contact: {
13+
name: 'Autobox Team',
14+
email: 'support@autobox.ai',
15+
},
1316
},
14-
},
15-
servers: [
16-
{
17-
url: 'http://localhost:4000',
18-
description: 'Development server',
19-
},
20-
],
21-
});
17+
servers: [
18+
{
19+
url: `http://localhost:${env.PORT}`,
20+
description: 'Development server',
21+
},
22+
],
23+
});
24+
};

src/swagger/generateSwagger.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ function getOpenApiDocumentation() {
2121
description: 'API to interact with Autobox Engine to run AI agents based simulations.',
2222
contact: {
2323
name: 'Autobox Team',
24-
email: 'support@autobox.io',
24+
email: 'support@autobox.ai',
2525
},
2626
},
2727
servers: [

src/swagger/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export { apiSpec } from './apiSpec';
1+
export { getApiSpec } from './apiSpec';

0 commit comments

Comments
 (0)