version: '3'
services:
  api_server:
    image: danswer/danswer-ee-backend:latest
    build:
      context: ../../backend
      dockerfile: Dockerfile
    command: >
      /bin/sh -c "alembic upgrade head &&
      echo \"Starting Danswer Api Server\" &&
      uvicorn ee.danswer.main:app --host 0.0.0.0 --port 8080"
    depends_on:
      - relational_db
      - index
      - inference_model_server
    restart: always
    ports:
      - "8080:8080"
    environment:
      # Auth Settings
      - AUTH_TYPE=${AUTH_TYPE:-disabled}
      - SESSION_EXPIRE_TIME_SECONDS=${SESSION_EXPIRE_TIME_SECONDS:-86400}
      - ENCRYPTION_KEY_SECRET=${ENCRYPTION_KEY_SECRET:-}
      - VALID_EMAIL_DOMAINS=${VALID_EMAIL_DOMAINS:-}
      - GOOGLE_OAUTH_CLIENT_ID=${GOOGLE_OAUTH_CLIENT_ID:-}
      - GOOGLE_OAUTH_CLIENT_SECRET=${GOOGLE_OAUTH_CLIENT_SECRET:-}
      - REQUIRE_EMAIL_VERIFICATION=${REQUIRE_EMAIL_VERIFICATION:-}
      - SMTP_SERVER=${SMTP_SERVER:-}  # For sending verification emails, if unspecified then defaults to 'smtp.gmail.com'
      - SMTP_PORT=${SMTP_PORT:-587}  # For sending verification emails, if unspecified then defaults to '587'
      - SMTP_USER=${SMTP_USER:-}
      - SMTP_PASS=${SMTP_PASS:-}
      - EMAIL_FROM=${EMAIL_FROM:-}
      - OAUTH_CLIENT_ID=${OAUTH_CLIENT_ID:-}
      - OAUTH_CLIENT_SECRET=${OAUTH_CLIENT_SECRET:-}
      - OPENID_CONFIG_URL=${OPENID_CONFIG_URL:-}
      # Gen AI Settings
      - GEN_AI_MODEL_PROVIDER=${GEN_AI_MODEL_PROVIDER:-}
      - GEN_AI_MODEL_VERSION=${GEN_AI_MODEL_VERSION:-}
      - FAST_GEN_AI_MODEL_VERSION=${FAST_GEN_AI_MODEL_VERSION:-}
      - GEN_AI_API_KEY=${GEN_AI_API_KEY:-}
      - GEN_AI_API_ENDPOINT=${GEN_AI_API_ENDPOINT:-}
      - GEN_AI_API_VERSION=${GEN_AI_API_VERSION:-}
      - GEN_AI_LLM_PROVIDER_TYPE=${GEN_AI_LLM_PROVIDER_TYPE:-}
      - GEN_AI_MAX_TOKENS=${GEN_AI_MAX_TOKENS:-}
      - QA_TIMEOUT=${QA_TIMEOUT:-}
      - MAX_CHUNKS_FED_TO_CHAT=${MAX_CHUNKS_FED_TO_CHAT:-}
      - DISABLE_LLM_FILTER_EXTRACTION=${DISABLE_LLM_FILTER_EXTRACTION:-}
      - DISABLE_LLM_CHUNK_FILTER=${DISABLE_LLM_CHUNK_FILTER:-}
      - DISABLE_LLM_CHOOSE_SEARCH=${DISABLE_LLM_CHOOSE_SEARCH:-}
      - DISABLE_LLM_QUERY_REPHRASE=${DISABLE_LLM_QUERY_REPHRASE:-}
      - DISABLE_GENERATIVE_AI=${DISABLE_GENERATIVE_AI:-}
      - DISABLE_LITELLM_STREAMING=${DISABLE_LITELLM_STREAMING:-}
      - LITELLM_EXTRA_HEADERS=${LITELLM_EXTRA_HEADERS:-}
      - BING_API_KEY=${BING_API_KEY:-}
      # if set, allows for the use of the token budget system
      - TOKEN_BUDGET_GLOBALLY_ENABLED=${TOKEN_BUDGET_GLOBALLY_ENABLED:-}
      # Enables the use of bedrock models
      - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID:-}
      - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY:-}
      - AWS_REGION_NAME=${AWS_REGION_NAME:-}
      # Query Options
      - DOC_TIME_DECAY=${DOC_TIME_DECAY:-}  # Recency Bias for search results, decay at 1 / (1 + DOC_TIME_DECAY * x years)
      - HYBRID_ALPHA=${HYBRID_ALPHA:-}  # Hybrid Search Alpha (0 for entirely keyword, 1 for entirely vector)
      - EDIT_KEYWORD_QUERY=${EDIT_KEYWORD_QUERY:-}
      - MULTILINGUAL_QUERY_EXPANSION=${MULTILINGUAL_QUERY_EXPANSION:-}
      - QA_PROMPT_OVERRIDE=${QA_PROMPT_OVERRIDE:-}
      # Other services
      - POSTGRES_HOST=relational_db
      - VESPA_HOST=index
      - WEB_DOMAIN=${WEB_DOMAIN:-}  # For frontend redirect auth purpose
      # Don't change the NLP model configs unless you know what you're doing
      - DOCUMENT_ENCODER_MODEL=${DOCUMENT_ENCODER_MODEL:-}
      - DOC_EMBEDDING_DIM=${DOC_EMBEDDING_DIM:-}
      - NORMALIZE_EMBEDDINGS=${NORMALIZE_EMBEDDINGS:-}
      - ASYM_QUERY_PREFIX=${ASYM_QUERY_PREFIX:-}
      - ENABLE_RERANKING_REAL_TIME_FLOW=${ENABLE_RERANKING_REAL_TIME_FLOW:-}
      - ENABLE_RERANKING_ASYNC_FLOW=${ENABLE_RERANKING_ASYNC_FLOW:-}
      - MODEL_SERVER_HOST=${MODEL_SERVER_HOST:-inference_model_server}
      - MODEL_SERVER_PORT=${MODEL_SERVER_PORT:-}
      # Leave this on pretty please? Nothing sensitive is collected!
      # https://docs.danswer.dev/more/telemetry
      - DISABLE_TELEMETRY=${DISABLE_TELEMETRY:-}
      - LOG_LEVEL=${LOG_LEVEL:-info}  # Set to debug to get more fine-grained logs
      - LOG_ALL_MODEL_INTERACTIONS=${LOG_ALL_MODEL_INTERACTIONS:-}  # Log all of the prompts to the LLM
      # If set to `true` will enable additional logs about Vespa query performance
      # (time spent on finding the right docs + time spent fetching summaries from disk)
      - LOG_VESPA_TIMING_INFORMATION=${LOG_VESPA_TIMING_INFORMATION:-}
      - LOG_ENDPOINT_LATENCY=${LOG_ENDPOINT_LATENCY:-}
      # Enterprise Edition only
      - API_KEY_HASH_ROUNDS=${API_KEY_HASH_ROUNDS:-}
    volumes:
      - local_dynamic_storage:/home/storage
      - file_connector_tmp_storage:/home/file_connector_storage
    extra_hosts:
      - "host.docker.internal:host-gateway"
    logging:
      driver: json-file
      options:
        max-size: "50m"
        max-file: "6"


  background:
    image: danswer/danswer-ee-backend:latest
    build:
      context: ../../backend
      dockerfile: Dockerfile
    command: /usr/bin/supervisord -c /etc/supervisor/conf.d/ee.supervisord.conf
    depends_on:
      - relational_db
      - index
      - inference_model_server
      - indexing_model_server
    restart: always
    environment:
      - ENCRYPTION_KEY_SECRET=${ENCRYPTION_KEY_SECRET:-}
      # Gen AI Settings (Needed by DanswerBot)
      - GEN_AI_MODEL_PROVIDER=${GEN_AI_MODEL_PROVIDER:-}
      - GEN_AI_MODEL_VERSION=${GEN_AI_MODEL_VERSION:-}
      - FAST_GEN_AI_MODEL_VERSION=${FAST_GEN_AI_MODEL_VERSION:-}
      - GEN_AI_API_KEY=${GEN_AI_API_KEY:-}
      - GEN_AI_API_ENDPOINT=${GEN_AI_API_ENDPOINT:-}
      - GEN_AI_API_VERSION=${GEN_AI_API_VERSION:-}
      - GEN_AI_LLM_PROVIDER_TYPE=${GEN_AI_LLM_PROVIDER_TYPE:-}
      - GEN_AI_MAX_TOKENS=${GEN_AI_MAX_TOKENS:-}
      - QA_TIMEOUT=${QA_TIMEOUT:-}
      - MAX_CHUNKS_FED_TO_CHAT=${MAX_CHUNKS_FED_TO_CHAT:-}
      - DISABLE_LLM_FILTER_EXTRACTION=${DISABLE_LLM_FILTER_EXTRACTION:-}
      - DISABLE_LLM_CHUNK_FILTER=${DISABLE_LLM_CHUNK_FILTER:-}
      - DISABLE_LLM_CHOOSE_SEARCH=${DISABLE_LLM_CHOOSE_SEARCH:-}
      - DISABLE_LLM_QUERY_REPHRASE=${DISABLE_LLM_QUERY_REPHRASE:-}
      - DISABLE_GENERATIVE_AI=${DISABLE_GENERATIVE_AI:-}
      - GENERATIVE_MODEL_ACCESS_CHECK_FREQ=${GENERATIVE_MODEL_ACCESS_CHECK_FREQ:-}
      - DISABLE_LITELLM_STREAMING=${DISABLE_LITELLM_STREAMING:-}
      - LITELLM_EXTRA_HEADERS=${LITELLM_EXTRA_HEADERS:-}
      - BING_API_KEY=${BING_API_KEY:-}
      # Query Options
      - DOC_TIME_DECAY=${DOC_TIME_DECAY:-}  # Recency Bias for search results, decay at 1 / (1 + DOC_TIME_DECAY * x years)
      - HYBRID_ALPHA=${HYBRID_ALPHA:-}  # Hybrid Search Alpha (0 for entirely keyword, 1 for entirely vector)
      - EDIT_KEYWORD_QUERY=${EDIT_KEYWORD_QUERY:-}
      - MULTILINGUAL_QUERY_EXPANSION=${MULTILINGUAL_QUERY_EXPANSION:-}
      - QA_PROMPT_OVERRIDE=${QA_PROMPT_OVERRIDE:-}
      # Other Services
      - POSTGRES_HOST=relational_db
      - POSTGRES_USER=${POSTGRES_USER:-}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-}
      - POSTGRES_DB=${POSTGRES_DB:-}
      - VESPA_HOST=index
      - WEB_DOMAIN=${WEB_DOMAIN:-}  # For frontend redirect auth purpose for OAuth2 connectors
      # Don't change the NLP model configs unless you know what you're doing
      - DOCUMENT_ENCODER_MODEL=${DOCUMENT_ENCODER_MODEL:-}
      - DOC_EMBEDDING_DIM=${DOC_EMBEDDING_DIM:-}
      - NORMALIZE_EMBEDDINGS=${NORMALIZE_EMBEDDINGS:-}
      - ASYM_QUERY_PREFIX=${ASYM_QUERY_PREFIX:-}  # Needed by DanswerBot
      - ASYM_PASSAGE_PREFIX=${ASYM_PASSAGE_PREFIX:-}
      - MODEL_SERVER_HOST=${MODEL_SERVER_HOST:-inference_model_server}
      - MODEL_SERVER_PORT=${MODEL_SERVER_PORT:-}
      - INDEXING_MODEL_SERVER_HOST=${INDEXING_MODEL_SERVER_HOST:-indexing_model_server}
      # Indexing Configs
      - NUM_INDEXING_WORKERS=${NUM_INDEXING_WORKERS:-}
      - ENABLED_CONNECTOR_TYPES=${ENABLED_CONNECTOR_TYPES:-}
      - DISABLE_INDEX_UPDATE_ON_SWAP=${DISABLE_INDEX_UPDATE_ON_SWAP:-}
      - DASK_JOB_CLIENT_ENABLED=${DASK_JOB_CLIENT_ENABLED:-}
      - CONTINUE_ON_CONNECTOR_FAILURE=${CONTINUE_ON_CONNECTOR_FAILURE:-}
      - EXPERIMENTAL_CHECKPOINTING_ENABLED=${EXPERIMENTAL_CHECKPOINTING_ENABLED:-}
      - CONFLUENCE_CONNECTOR_LABELS_TO_SKIP=${CONFLUENCE_CONNECTOR_LABELS_TO_SKIP:-}
      - JIRA_CONNECTOR_LABELS_TO_SKIP=${JIRA_CONNECTOR_LABELS_TO_SKIP:-}
      - WEB_CONNECTOR_VALIDATE_URLS=${WEB_CONNECTOR_VALIDATE_URLS:-}
      - JIRA_API_VERSION=${JIRA_API_VERSION:-}
      - GONG_CONNECTOR_START_TIME=${GONG_CONNECTOR_START_TIME:-}
      - NOTION_CONNECTOR_ENABLE_RECURSIVE_PAGE_LOOKUP=${NOTION_CONNECTOR_ENABLE_RECURSIVE_PAGE_LOOKUP:-}
      - GITHUB_CONNECTOR_BASE_URL=${GITHUB_CONNECTOR_BASE_URL:-}
      # Danswer SlackBot Configs
      - DANSWER_BOT_SLACK_APP_TOKEN=${DANSWER_BOT_SLACK_APP_TOKEN:-}
      - DANSWER_BOT_SLACK_BOT_TOKEN=${DANSWER_BOT_SLACK_BOT_TOKEN:-}
      - DANSWER_BOT_DISABLE_DOCS_ONLY_ANSWER=${DANSWER_BOT_DISABLE_DOCS_ONLY_ANSWER:-}
      - DANSWER_BOT_FEEDBACK_VISIBILITY=${DANSWER_BOT_FEEDBACK_VISIBILITY:-}
      - DANSWER_BOT_DISPLAY_ERROR_MSGS=${DANSWER_BOT_DISPLAY_ERROR_MSGS:-}
      - DANSWER_BOT_RESPOND_EVERY_CHANNEL=${DANSWER_BOT_RESPOND_EVERY_CHANNEL:-}
      - DANSWER_BOT_DISABLE_COT=${DANSWER_BOT_DISABLE_COT:-}  # Currently unused
      - NOTIFY_SLACKBOT_NO_ANSWER=${NOTIFY_SLACKBOT_NO_ANSWER:-}
      - DANSWER_BOT_MAX_QPM=${DANSWER_BOT_MAX_QPM:-}
      - DANSWER_BOT_MAX_WAIT_TIME=${DANSWER_BOT_MAX_WAIT_TIME:-}
      # Logging
      # Leave this on pretty please? Nothing sensitive is collected!
      # https://docs.danswer.dev/more/telemetry
      - DISABLE_TELEMETRY=${DISABLE_TELEMETRY:-}
      - LOG_LEVEL=${LOG_LEVEL:-info}  # Set to debug to get more fine-grained logs
      - LOG_ALL_MODEL_INTERACTIONS=${LOG_ALL_MODEL_INTERACTIONS:-}  # Log all of the prompts to the LLM
      - LOG_VESPA_TIMING_INFORMATION=${LOG_VESPA_TIMING_INFORMATION:-}
    extra_hosts:
      - "host.docker.internal:host-gateway"
    logging:
      driver: json-file
      options:
        max-size: "50m"
        max-file: "6"


  web_server:
    image: danswer/danswer-ee-web-server:latest
    build:
      context: ../../web
      dockerfile: Dockerfile
      args:
        - NEXT_PUBLIC_DISABLE_STREAMING=${NEXT_PUBLIC_DISABLE_STREAMING:-false}
        - NEXT_PUBLIC_NEW_CHAT_DIRECTS_TO_SAME_PERSONA=${NEXT_PUBLIC_NEW_CHAT_DIRECTS_TO_SAME_PERSONA:-false}
        - NEXT_PUBLIC_POSITIVE_PREDEFINED_FEEDBACK_OPTIONS=${NEXT_PUBLIC_POSITIVE_PREDEFINED_FEEDBACK_OPTIONS:-}
        - NEXT_PUBLIC_NEGATIVE_PREDEFINED_FEEDBACK_OPTIONS=${NEXT_PUBLIC_NEGATIVE_PREDEFINED_FEEDBACK_OPTIONS:-}
        - NEXT_PUBLIC_DISABLE_LOGOUT=${NEXT_PUBLIC_DISABLE_LOGOUT:-}
        - NEXT_PUBLIC_THEME=${NEXT_PUBLIC_THEME:-}
        - NEXT_PUBLIC_EE_ENABLED=${NEXT_PUBLIC_EE_ENABLED:-true}
    depends_on:
      - api_server
    restart: always
    environment:
      - INTERNAL_URL=http://api_server:8080
      - WEB_DOMAIN=${WEB_DOMAIN:-}
      - THEME_IS_DARK=${THEME_IS_DARK:-}


  inference_model_server:
    image: danswer/danswer-model-server:latest
    build:
      context: ../../backend
      dockerfile: Dockerfile.model_server
    command: >
      /bin/sh -c "if [ \"${DISABLE_MODEL_SERVER:-false}\" = \"True\" ]; then
        echo 'Skipping service...';
        exit 0;
      else
        exec uvicorn model_server.main:app --host 0.0.0.0 --port 9000;
      fi"
    restart: on-failure
    environment:
      - MIN_THREADS_ML_MODELS=${MIN_THREADS_ML_MODELS:-}
      # Set to debug to get more fine-grained logs
      - LOG_LEVEL=${LOG_LEVEL:-info}
    volumes:
      # Not necessary, this is just to reduce download time during startup
      - model_cache_huggingface:/root/.cache/huggingface/
    logging:
      driver: json-file
      options:
        max-size: "50m"
        max-file: "6"


  indexing_model_server:
    image: danswer/danswer-model-server:latest
    build:
      context: ../../backend
      dockerfile: Dockerfile.model_server
    command: >
      /bin/sh -c "if [ \"${DISABLE_MODEL_SERVER:-false}\" = \"True\" ]; then
        echo 'Skipping service...';
        exit 0;
      else
        exec uvicorn model_server.main:app --host 0.0.0.0 --port 9000;
      fi"
    restart: on-failure
    environment:
      - MIN_THREADS_ML_MODELS=${MIN_THREADS_ML_MODELS:-}
      - INDEXING_ONLY=True
      # Set to debug to get more fine-grained logs
      - LOG_LEVEL=${LOG_LEVEL:-info}
    volumes:
      # Not necessary, this is just to reduce download time during startup
      - model_cache_huggingface:/root/.cache/huggingface/
    logging:
      driver: json-file
      options:
        max-size: "50m"
        max-file: "6"

  relational_db:
    image: postgres:15.2-alpine
    restart: always
    environment:
      - POSTGRES_USER=${POSTGRES_USER:-postgres}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-password}
    ports:
      - "5432:5432"
    volumes:
      - db_volume:/var/lib/postgresql/data


  # This container name cannot have an underscore in it due to Vespa expectations of the URL
  index:
    image: vespaengine/vespa:8.277.17
    restart: always
    ports:
      - "19071:19071"
      - "8081:8081"
    volumes:
      - vespa_volume:/opt/vespa/var
    logging:
      driver: json-file
      options:
        max-size: "50m"
        max-file: "6"


  nginx:
    image: nginx:1.23.4-alpine
    restart: always
    # nginx will immediately crash with `nginx: [emerg] host not found in upstream`
    # if api_server / web_server are not up 
    depends_on:
      - api_server
      - web_server
    environment:
      - DOMAIN=localhost
    ports:
      - "80:80"
      - "3000:80"  # allow for localhost:3000 usage, since that is the norm
    volumes:
      - ../data/nginx:/etc/nginx/conf.d
    logging:
      driver: json-file
      options:
        max-size: "50m"
        max-file: "6"
    # the specified script waits for the api_server to start up. 
    # Without this we've seen issues where nginx shows no error logs but 
    # does not recieve any traffic
    # NOTE: we have to use dos2unix to remove Carriage Return chars from the file
    # in order to make this work on both Unix-like systems and windows
    command: > 
      /bin/sh -c "dos2unix /etc/nginx/conf.d/run-nginx.sh 
      && /etc/nginx/conf.d/run-nginx.sh app.conf.template.dev"
        

volumes:
  db_volume:
  vespa_volume:
  # Created by the container itself
  model_cache_huggingface:
