Files
ai-contract-platform/yuqei-infra/compose/docker-compose.yml
T
yuqei 5f89c11226
Security Quality Gate / npm audit, Bandit, pip-audit, detect-secrets (pull_request) Failing after 11m27s
fix: separate AI liveness from readiness
2026-07-13 12:55:29 +08:00

348 lines
16 KiB
YAML

name: yuqei-contract-v2
services:
postgres:
image: ${POSTGRES_IMAGE:-pgvector/pgvector:pg16}
restart: unless-stopped
environment:
POSTGRES_USER: ${POSTGRES_USER:-yuqei}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-yuqei-local}
POSTGRES_DB: ${POSTGRES_DB:-postgres}
ports:
- "${POSTGRES_HOST_PORT:-5432}:5432"
volumes:
- postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER:-yuqei} -d $${POSTGRES_DB:-postgres}"]
interval: 10s
timeout: 5s
retries: 5
postgres-init-databases:
image: postgres:16-alpine
depends_on:
postgres:
condition: service_healthy
entrypoint: ["/bin/sh", "-c"]
command:
- >
for db in "$${CONTRACT_POSTGRES_DB:-yuqei_contract}" "$${AI_PLATFORM_POSTGRES_DB:-yuqei_ai}" "$${OPS_POSTGRES_DB:-yuqei_ops}"; do
if ! psql -h postgres -U "$${POSTGRES_USER:-yuqei}" -d "$${POSTGRES_DB:-postgres}" -tAc "SELECT 1 FROM pg_database WHERE datname = '$${db}'" | grep -q 1; then
psql -h postgres -U "$${POSTGRES_USER:-yuqei}" -d "$${POSTGRES_DB:-postgres}" -c "CREATE DATABASE \"$${db}\"";
fi;
done
environment:
PGPASSWORD: ${POSTGRES_PASSWORD:-yuqei-local}
POSTGRES_USER: ${POSTGRES_USER:-yuqei}
POSTGRES_DB: ${POSTGRES_DB:-postgres}
CONTRACT_POSTGRES_DB: ${CONTRACT_POSTGRES_DB:-yuqei_contract}
AI_PLATFORM_POSTGRES_DB: ${AI_PLATFORM_POSTGRES_DB:-yuqei_ai}
OPS_POSTGRES_DB: ${OPS_POSTGRES_DB:-yuqei_ops}
restart: "no"
redis:
image: redis:7-alpine
restart: unless-stopped
ports:
- "${REDIS_HOST_PORT:-6379}:6379"
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
minio:
image: ${MINIO_IMAGE:-minio/minio:RELEASE.2023-06-23T20-26-00Z}
restart: unless-stopped
command: server /data --console-address ":9001"
environment:
MINIO_ROOT_USER: ${MINIO_ROOT_USER:-yuqei-minio}
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-yuqei-minio-local}
ports:
- "${MINIO_API_HOST_PORT:-9000}:9000"
- "${MINIO_CONSOLE_HOST_PORT:-9001}:9001"
volumes:
- minio-data:/data
healthcheck:
test: ["CMD-SHELL", "test -f /data/.minio.sys/format.json"]
interval: 10s
timeout: 5s
retries: 12
start_period: 45s
contract-api-migrate:
build:
context: ../..
dockerfile: yuqei-infra/docker/contract-api.Dockerfile
args:
PIP_INDEX_URL: ${PYPI_INDEX_URL:-https://pypi.org/simple}
working_dir: /app/yuqei-contract-platform/services/contract-api
command: ["python", "-m", "alembic", "upgrade", "head"]
environment:
CONTRACT_API_ENVIRONMENT: ${CONTRACT_API_ENVIRONMENT:-local}
CONTRACT_API_DATABASE_URL: ${CONTRACT_API_DATABASE_URL:-postgresql+psycopg://yuqei:yuqei-local@postgres:5432/yuqei_contract}
CONTRACT_API_DATABASE_AUTO_CREATE: ${CONTRACT_API_DATABASE_AUTO_CREATE:-false}
CONTRACT_API_SEED_DEMO_DATA: "false"
CONTRACT_API_FILE_STORAGE_DIR: ${CONTRACT_API_FILE_STORAGE_DIR:-/data/contract-files}
CONTRACT_API_REDIS_URL: ${CONTRACT_API_REDIS_URL:-redis://redis:6379/0}
CONTRACT_API_AI_PLATFORM_BASE_URL: ${CONTRACT_API_AI_PLATFORM_BASE_URL:-http://ai-platform-api:8101}
CONTRACT_API_DB_POOL_SIZE: ${CONTRACT_API_DB_POOL_SIZE:-10}
CONTRACT_API_DB_MAX_OVERFLOW: ${CONTRACT_API_DB_MAX_OVERFLOW:-20}
CONTRACT_API_DB_POOL_TIMEOUT_SECONDS: ${CONTRACT_API_DB_POOL_TIMEOUT_SECONDS:-30}
CONTRACT_API_DB_POOL_RECYCLE_SECONDS: ${CONTRACT_API_DB_POOL_RECYCLE_SECONDS:-1800}
volumes:
- contract-files:/data/contract-files
depends_on:
postgres-init-databases:
condition: service_completed_successfully
restart: "no"
contract-api:
build:
context: ../..
dockerfile: yuqei-infra/docker/contract-api.Dockerfile
args:
PIP_INDEX_URL: ${PYPI_INDEX_URL:-https://pypi.org/simple}
restart: unless-stopped
environment:
CONTRACT_API_ENVIRONMENT: ${CONTRACT_API_ENVIRONMENT:-local}
CONTRACT_API_DATABASE_URL: ${CONTRACT_API_DATABASE_URL:-postgresql+psycopg://yuqei:yuqei-local@postgres:5432/yuqei_contract}
CONTRACT_API_DATABASE_AUTO_CREATE: ${CONTRACT_API_DATABASE_AUTO_CREATE:-false}
CONTRACT_API_SEED_DEMO_DATA: ${CONTRACT_API_SEED_DEMO_DATA:-true}
CONTRACT_API_FILE_STORAGE_DIR: ${CONTRACT_API_FILE_STORAGE_DIR:-/data/contract-files}
CONTRACT_API_REDIS_URL: ${CONTRACT_API_REDIS_URL:-redis://redis:6379/0}
CONTRACT_API_AI_PLATFORM_BASE_URL: ${CONTRACT_API_AI_PLATFORM_BASE_URL:-http://ai-platform-api:8101}
CONTRACT_API_AI_PLATFORM_SERVICE_KEY: ${AI_PLATFORM_SERVICE_SHARED_SECRET:-local-only-ai-service-secret-change-me-32}
CONTRACT_API_WEB_PROXY_SHARED_SECRET: ${CONTRACT_API_WEB_PROXY_SHARED_SECRET:-local-only-proxy-secret-change-me-32}
CONTRACT_API_REQUIRE_WEB_PROXY_AUTH: ${CONTRACT_API_REQUIRE_WEB_PROXY_AUTH:-false}
CONTRACT_API_UVICORN_WORKERS: ${CONTRACT_API_UVICORN_WORKERS:-1}
CONTRACT_API_DB_POOL_SIZE: ${CONTRACT_API_DB_POOL_SIZE:-10}
CONTRACT_API_DB_MAX_OVERFLOW: ${CONTRACT_API_DB_MAX_OVERFLOW:-20}
CONTRACT_API_DB_POOL_TIMEOUT_SECONDS: ${CONTRACT_API_DB_POOL_TIMEOUT_SECONDS:-30}
CONTRACT_API_DB_POOL_RECYCLE_SECONDS: ${CONTRACT_API_DB_POOL_RECYCLE_SECONDS:-1800}
CONTRACT_API_READ_CACHE_TTL_SECONDS: ${CONTRACT_API_READ_CACHE_TTL_SECONDS:-2}
ports:
- "${CONTRACT_API_HOST_PORT:-8100}:8100"
volumes:
- contract-files:/data/contract-files
healthcheck:
test: ["CMD", "python", "-c", "import json, urllib.request; data=json.load(urllib.request.urlopen('http://127.0.0.1:8100/api/v1/health', timeout=5)); assert data['status'] == 'ok'"]
interval: 10s
timeout: 5s
retries: 6
depends_on:
postgres:
condition: service_healthy
postgres-init-databases:
condition: service_completed_successfully
redis:
condition: service_healthy
contract-api-migrate:
condition: service_completed_successfully
ai-platform-api:
condition: service_healthy
contract-review-worker:
build:
context: ../..
dockerfile: yuqei-infra/docker/contract-api.Dockerfile
args:
PIP_INDEX_URL: ${PYPI_INDEX_URL:-https://pypi.org/simple}
restart: unless-stopped
command: ["python", "-m", "yuqei_contract_api.review_worker"]
environment:
CONTRACT_API_ENVIRONMENT: ${CONTRACT_API_ENVIRONMENT:-local}
CONTRACT_API_DATABASE_URL: ${CONTRACT_API_DATABASE_URL:-postgresql+psycopg://yuqei:yuqei-local@postgres:5432/yuqei_contract}
CONTRACT_API_DATABASE_AUTO_CREATE: ${CONTRACT_API_DATABASE_AUTO_CREATE:-false}
CONTRACT_API_SEED_DEMO_DATA: ${CONTRACT_REVIEW_WORKER_SEED_DEMO_DATA:-false}
CONTRACT_API_FILE_STORAGE_DIR: ${CONTRACT_API_FILE_STORAGE_DIR:-/data/contract-files}
CONTRACT_API_REDIS_URL: ${CONTRACT_API_REDIS_URL:-redis://redis:6379/0}
CONTRACT_API_AI_PLATFORM_BASE_URL: ${CONTRACT_API_AI_PLATFORM_BASE_URL:-http://ai-platform-api:8101}
CONTRACT_API_AI_PLATFORM_SERVICE_KEY: ${AI_PLATFORM_SERVICE_SHARED_SECRET:-local-only-ai-service-secret-change-me-32}
CONTRACT_API_REVIEW_WORKER_NAME: ${CONTRACT_API_REVIEW_WORKER_NAME:-contract-review-worker-1}
CONTRACT_API_REVIEW_WORKER_POLL_INTERVAL_SECONDS: ${CONTRACT_API_REVIEW_WORKER_POLL_INTERVAL_SECONDS:-5}
CONTRACT_API_REVIEW_WORKER_BATCH_LIMIT: ${CONTRACT_API_REVIEW_WORKER_BATCH_LIMIT:-5}
volumes:
- contract-files:/data/contract-files
healthcheck:
test: ["CMD-SHELL", "tr '\\0' ' ' < /proc/1/cmdline | grep -Fq 'yuqei_contract_api.review_worker'"]
interval: 30s
timeout: 10s
retries: 3
depends_on:
postgres:
condition: service_healthy
postgres-init-databases:
condition: service_completed_successfully
redis:
condition: service_healthy
contract-api:
condition: service_healthy
ai-platform-api:
condition: service_healthy
ai-platform-api-migrate:
build:
context: ../..
dockerfile: yuqei-infra/docker/ai-platform-api.Dockerfile
args:
PIP_INDEX_URL: ${PYPI_INDEX_URL:-https://pypi.org/simple}
working_dir: /app/yuqei-ai-platform/services/ai-platform-api
command: ["python", "-m", "alembic", "upgrade", "head"]
environment:
AI_PLATFORM_ENVIRONMENT: ${AI_PLATFORM_ENVIRONMENT:-local}
AI_PLATFORM_DATABASE_URL: ${AI_PLATFORM_DATABASE_URL:-postgresql+psycopg://yuqei:yuqei-local@postgres:5432/yuqei_ai}
AI_PLATFORM_DATABASE_AUTO_CREATE: ${AI_PLATFORM_DATABASE_AUTO_CREATE:-false}
AI_PLATFORM_REPOSITORY_BACKEND: ${AI_PLATFORM_REPOSITORY_BACKEND:-sqlalchemy}
AI_PLATFORM_DB_POOL_SIZE: ${AI_PLATFORM_DB_POOL_SIZE:-10}
AI_PLATFORM_DB_MAX_OVERFLOW: ${AI_PLATFORM_DB_MAX_OVERFLOW:-20}
AI_PLATFORM_DB_POOL_TIMEOUT_SECONDS: ${AI_PLATFORM_DB_POOL_TIMEOUT_SECONDS:-30}
AI_PLATFORM_DB_POOL_RECYCLE_SECONDS: ${AI_PLATFORM_DB_POOL_RECYCLE_SECONDS:-1800}
depends_on:
postgres-init-databases:
condition: service_completed_successfully
restart: "no"
ai-platform-prompt-assets-import:
build:
context: ../..
dockerfile: yuqei-infra/docker/ai-platform-api.Dockerfile
args:
PIP_INDEX_URL: ${PYPI_INDEX_URL:-https://pypi.org/simple}
working_dir: /app/yuqei-ai-platform/services/ai-platform-api
command: ["python", "-m", "yuqei_ai_platform_api.v1_prompt_assets_import", "--apply"]
environment:
AI_PLATFORM_ENVIRONMENT: ${AI_PLATFORM_ENVIRONMENT:-local}
AI_PLATFORM_DATABASE_URL: ${AI_PLATFORM_DATABASE_URL:-postgresql+psycopg://yuqei:yuqei-local@postgres:5432/yuqei_ai}
AI_PLATFORM_DATABASE_AUTO_CREATE: ${AI_PLATFORM_DATABASE_AUTO_CREATE:-false}
depends_on:
ai-platform-api-migrate:
condition: service_completed_successfully
restart: "no"
ai-platform-api:
build:
context: ../..
dockerfile: yuqei-infra/docker/ai-platform-api.Dockerfile
args:
PIP_INDEX_URL: ${PYPI_INDEX_URL:-https://pypi.org/simple}
restart: unless-stopped
environment:
AI_PLATFORM_ENVIRONMENT: ${AI_PLATFORM_ENVIRONMENT:-local}
AI_PLATFORM_DATABASE_URL: ${AI_PLATFORM_DATABASE_URL:-postgresql+psycopg://yuqei:yuqei-local@postgres:5432/yuqei_ai}
AI_PLATFORM_DATABASE_AUTO_CREATE: ${AI_PLATFORM_DATABASE_AUTO_CREATE:-false}
AI_PLATFORM_REPOSITORY_BACKEND: ${AI_PLATFORM_REPOSITORY_BACKEND:-sqlalchemy}
AI_PLATFORM_REDIS_URL: ${AI_PLATFORM_REDIS_URL:-redis://redis:6379/1}
AI_PLATFORM_PROVIDER_CONFIG_SOURCE: ${AI_PLATFORM_PROVIDER_CONFIG_SOURCE:-database}
AI_PLATFORM_PROVIDER_CALL_MODE: ${AI_PLATFORM_PROVIDER_CALL_MODE:-disabled}
AI_PLATFORM_PROVIDER_FALLBACK_ENABLED: ${AI_PLATFORM_PROVIDER_FALLBACK_ENABLED:-true}
AI_PLATFORM_SERVICE_SHARED_SECRET: ${AI_PLATFORM_SERVICE_SHARED_SECRET:-local-only-ai-service-secret-change-me-32}
AI_PLATFORM_REQUIRE_SERVICE_AUTH: ${AI_PLATFORM_REQUIRE_SERVICE_AUTH:-false}
AI_PLATFORM_UVICORN_WORKERS: ${AI_PLATFORM_UVICORN_WORKERS:-1}
AI_PLATFORM_DB_POOL_SIZE: ${AI_PLATFORM_DB_POOL_SIZE:-10}
AI_PLATFORM_DB_MAX_OVERFLOW: ${AI_PLATFORM_DB_MAX_OVERFLOW:-20}
AI_PLATFORM_DB_POOL_TIMEOUT_SECONDS: ${AI_PLATFORM_DB_POOL_TIMEOUT_SECONDS:-30}
AI_PLATFORM_DB_POOL_RECYCLE_SECONDS: ${AI_PLATFORM_DB_POOL_RECYCLE_SECONDS:-1800}
AI_PLATFORM_OBJECT_STORAGE_BACKEND: s3
AI_PLATFORM_OBJECT_STORAGE_BUCKET: ${AI_PLATFORM_OBJECT_STORAGE_BUCKET:-ai-platform-uploads}
AI_PLATFORM_OBJECT_STORAGE_ENDPOINT_URL: http://minio:9000
AI_PLATFORM_OBJECT_STORAGE_ACCESS_KEY_ID: ${MINIO_ROOT_USER:-yuqei-minio}
AI_PLATFORM_OBJECT_STORAGE_SECRET_ACCESS_KEY: ${MINIO_ROOT_PASSWORD:-yuqei-minio-local}
AI_PLATFORM_OBJECT_STORAGE_REGION_NAME: us-east-1
ports:
- "${AI_PLATFORM_API_HOST_PORT:-8101}:8101"
healthcheck:
test: ["CMD", "python", "-c", "import json, urllib.request; data=json.load(urllib.request.urlopen('http://127.0.0.1:8101/health', timeout=5)); assert data['status'] == 'ok'"]
interval: 10s
timeout: 5s
retries: 6
depends_on:
postgres:
condition: service_healthy
postgres-init-databases:
condition: service_completed_successfully
redis:
condition: service_healthy
minio:
condition: service_healthy
ai-platform-api-migrate:
condition: service_completed_successfully
ai-platform-prompt-assets-import:
condition: service_completed_successfully
worker:
build:
context: ../..
dockerfile: yuqei-infra/docker/worker.Dockerfile
args:
PIP_INDEX_URL: ${PYPI_INDEX_URL:-https://pypi.org/simple}
restart: unless-stopped
command: ["python", "-m", "yuqei_worker.main"]
environment:
YUQEI_WORKER_ENVIRONMENT: ${YUQEI_WORKER_ENVIRONMENT:-local}
YUQEI_WORKER_DATABASE_URL: ${YUQEI_WORKER_DATABASE_URL:-postgresql+psycopg://yuqei:yuqei-local@postgres:5432/yuqei_ai}
YUQEI_WORKER_DATABASE_AUTO_CREATE: ${YUQEI_WORKER_DATABASE_AUTO_CREATE:-false}
YUQEI_WORKER_REDIS_URL: ${YUQEI_WORKER_REDIS_URL:-redis://redis:6379/2}
YUQEI_WORKER_OBJECT_STORAGE_BACKEND: s3
YUQEI_WORKER_OBJECT_STORAGE_BUCKET: ${AI_PLATFORM_OBJECT_STORAGE_BUCKET:-ai-platform-uploads}
YUQEI_WORKER_OBJECT_STORAGE_ENDPOINT_URL: http://minio:9000
YUQEI_WORKER_OBJECT_STORAGE_ACCESS_KEY_ID: ${MINIO_ROOT_USER:-yuqei-minio}
YUQEI_WORKER_OBJECT_STORAGE_SECRET_ACCESS_KEY: ${MINIO_ROOT_PASSWORD:-yuqei-minio-local}
YUQEI_WORKER_OBJECT_STORAGE_REGION_NAME: us-east-1
YUQEI_WORKER_WORKER_NAME: knowledge-worker-1
YUQEI_WORKER_TENANT_ID: ${YUQEI_WORKER_TENANT_ID:-default}
YUQEI_WORKER_POLL_INTERVAL_SECONDS: "5"
YUQEI_WORKER_BATCH_LIMIT: "10"
YUQEI_WORKER_TRACE_GOVERNANCE_ENABLED: ${YUQEI_WORKER_TRACE_GOVERNANCE_ENABLED:-false}
YUQEI_WORKER_TRACE_GOVERNANCE_INTERVAL_SECONDS: ${YUQEI_WORKER_TRACE_GOVERNANCE_INTERVAL_SECONDS:-300}
YUQEI_WORKER_TRACE_GOVERNANCE_BATCH_LIMIT: ${YUQEI_WORKER_TRACE_GOVERNANCE_BATCH_LIMIT:-100}
YUQEI_WORKER_TRACE_GOVERNANCE_WORKER_NAME: ${YUQEI_WORKER_TRACE_GOVERNANCE_WORKER_NAME:-trace-governance-worker}
healthcheck:
test: ["CMD-SHELL", "tr '\\0' ' ' < /proc/1/cmdline | grep -Fq 'yuqei_worker.main'"]
interval: 30s
timeout: 10s
retries: 3
depends_on:
postgres:
condition: service_healthy
postgres-init-databases:
condition: service_completed_successfully
redis:
condition: service_healthy
minio:
condition: service_healthy
ai-platform-api:
condition: service_healthy
contract-web:
build:
context: ../..
dockerfile: yuqei-infra/docker/contract-web.Dockerfile
args:
NPM_REGISTRY: ${NPM_REGISTRY:-https://registry.npmjs.org}
restart: unless-stopped
environment:
CONTRACT_API_BASE_URL: http://contract-api:8100
CONTRACT_API_PROXY_SECRET: ${CONTRACT_API_WEB_PROXY_SHARED_SECRET:-local-only-proxy-secret-change-me-32}
CONTRACT_WEB_SESSION_SECRET: ${CONTRACT_WEB_SESSION_SECRET:-local-only-session-secret-change-me-32}
CONTRACT_WEB_SESSION_SECURE: ${CONTRACT_WEB_SESSION_SECURE:-false}
NEXT_PUBLIC_CONTRACT_API_BASE_URL: http://localhost:8100
AI_PLATFORM_API_BASE_URL: http://ai-platform-api:8101
AI_PLATFORM_PROXY_SECRET: ${AI_PLATFORM_SERVICE_SHARED_SECRET:-local-only-ai-service-secret-change-me-32}
NEXT_PUBLIC_AI_PLATFORM_API_BASE_URL: http://localhost:8101
ports:
- "${CONTRACT_WEB_HOST_PORT:-3000}:3000"
healthcheck:
test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:3000/health').then((response) => { if (!response.ok) process.exit(1); }).catch(() => process.exit(1));"]
interval: 10s
timeout: 5s
retries: 6
depends_on:
contract-api:
condition: service_healthy
volumes:
postgres-data:
minio-data:
contract-files: