# dclint disable top-level-properties-order services-alphabetical-order service-image-require-explicit-tag
name: gai

## GenAI common variables
x-image:
  api_server_image: &api_server_image ${API_SERVER_IMAGE-quay.io/eggplantsoftware/gai-api-server}:${API_SERVER_IMAGE_TAG:-26.2.2-7}
  processingd_image_cuda: &processingd_image_cuda ${PROCESSINGD_CUDA_IMAGE-quay.io/eggplantsoftware/gai-processingd-cuda}:${PROCESSINGD_CUDA_IMAGE_TAG:-26.2.2-7}
  processingd_image_cpu: &processingd_image_cpu ${PROCESSINGD_CPU_IMAGE-quay.io/eggplantsoftware/gai-processingd-cpu}:${PROCESSINGD_CPU_IMAGE_TAG:-26.2.2-7}
  postgres_image: &postgres_image pgvector/pgvector:pg17
  nginx_image: &nginx_image nginx:1.27.3
  busybox_image: &busybox_image busybox:latest
  curl_image: &curl_image curlimages/curl:latest
  kc_realm_config_image: &kc_realm_config_image ${KC_REALM_CONFIG_IMAGE:-quay.io/eggplantsoftware/keycloak-realm-config}:${KC_REALM_CONFIG_TAG:-6.3.331}

x-database:
  db_user: &db_user ${POSTGRES_USER-postgres}
  db_password: &db_password ${POSTGRES_PASSWORD-eggplant}
  db_name: &db_name ${POSTGRES_DB-eggplant_skill}

x-healthcheck:
  api_server_healthheck_url: &api_server_healthheck_url http://gai-api-server:8000/docs

x-volumes:
  gai_downloads: &gai_downloads ${GAI_VOLUME_PATH-storage}/downloads
  gai_requirements: &gai_requirements ${GAI_VOLUME_PATH-storage}/requirement
  gai_context_docs: &gai_context_docs ${GAI_VOLUME_PATH-storage}/upload_docs
  gai_spacy: &gai_spacy ${GAI_VOLUME_PATH-storage}/spacy

x-tls:
  tls_cert: &tls_cert ${TLS_CERT-tls.crt}
  tls_key: &tls_key ${TLS_KEY-tls.key}

x-keycloak:
  keycloak_base_url: &keycloak_base_url ${KEYCLOAK_BASE_URL:?}
  keycloak_realm: &keycloak_realm ${KEYCLOAK_REALM-eggplant}
  keycloak_frontend_url: &keycloak_frontend_url ${KEYCLOAK_FRONTEND_URL:?}
  keycloak_referrer: &keycloak_referrer ${GENERATOR_URL:?}

x-model-parms:
  similarity_threshold: &similarity_threshold ${SIMILARITY_THRESHOLD:-10}
  similarity_top_k: &similarity_top_k ${SIMILARITY_TOP_K:-4}
  llm_temperature: &llm_temperature ${LLM_TEMPERATURE:-0.2}
  llm_top_p: &llm_top_p ${LLM_TOP_P:-0.05}

x-realm-config-env-vars: &realm-config-env-vars
  KEYCLOAK_URL: *keycloak_frontend_url
  KEYCLOAK_USER: ${KC_ADMIN_USER:?}
  KEYCLOAK_PASSWORD: ${KC_ADMIN_PASSWORD:?}
  KEYCLOAK_SSLVERIFY: ${KEYCLOAK_SSLVERIFY:-true}
  KEYCLOAK_REALM: ${KEYCLOAK_REALM-eggplant}
  IMPORT_VARSUBSTITUTION_ENABLED: true
  IMPORT_REMOTESTATE_ENABLED: true
  IMPORT_MANAGED_GROUP: no-delete
  IMPORT_MANAGED_CLIENTSCOPE: no-delete
  IMPORT_MANAGED_COMPONENT: no-delete
  IMPORT_MANAGED_SUBCOMPONENT: no-delete
  IMPORT_MANAGED_AUTHENTICATIONFLOW: no-delete
  IMPORT_MANAGED_IDENTITYPROVIDER: no-delete
  IMPORT_MANAGED_IDENTITYPROVIDERMAPPER: no-delete
  IMPORT_MANAGED_CLIENT: no-delete
  IMPORT_MANAGED_MESSAGEBUNDLES: no-delete
  INITIAL_USER_USERNAME: ${INITIAL_USER_USERNAME:?}
  INITIAL_USER_PASSWORD: ${INITIAL_USER_PASSWORD:?}
  INITIAL_USER_GIVEN_NAME: ${INITIAL_USER_GIVEN_NAME:?}
  INITIAL_USER_FAMILY_NAME: ${INITIAL_USER_FAMILY_NAME:?}
  INITIAL_USER_EMAIL: ${INITIAL_USER_EMAIL:?}
  INITIAL_USER_IS_PASSWORD_TEMPORARY: ${INITIAL_USER_IS_PASSWORD_TEMPORARY:-true}
  SMTP_START_TLS: ${SMTP_START_TLS:-true}
  SMTP_SSL: ${SMTP_SSL:-true}
  SMTP_HOST: ${SMTP_HOST:-localhost}
  SMTP_PORT: ${SMTP_PORT:-25}
  SMTP_FROM: ${SMTP_FROM:-noreply@localhost}
  SMTP_AUTH: ${SMTP_AUTH:-true}
  SMTP_USERNAME: ${SMTP_USERNAME:-}
  SMTP_PASSWORD: ${SMTP_PASSWORD:-}
  TERMS_AND_CONDITIONS_REQUIRED: ${TERMS_AND_CONDITIONS_REQUIRED:-true}

services:
  gai-init:
    image: *busybox_image
    command:
      - sh
      - -c
      - |
        set -e;

        echo "Creating required folders"
        mkdir -p host/storage/context_store \
            host/storage/db \
            host/storage/upload_docs \
            host/storage/requirement \
            host/storage/downloads \
            host/storage/spacy \
            host/storage/local_models

        echo "Created folders successfully"
    volumes:
      - ${GAI_VOLUME_PATH-.}:/host
    restart: "no"
    profiles:
      - init

  nginx:
    image: *nginx_image
    ports:
      - "0.0.0.0:${GENERATOR_PORT:-8443}:443"
    configs:
      - source: nginx_tls_config
        target: /etc/nginx/nginx.conf
    secrets:
      - source: tls_cert
        target: /etc/nginx/certs/tls.crt
      - source: tls_key
        target: /etc/nginx/certs/tls.key
    networks:
      - eggplant_nw
    depends_on:
      gai-api-server:
        condition: service_started
        required: true
    profiles:
      - default
      - cpu

  gai-api-server:
    command:
      - uvicorn
      - api_server_main:app
      - --host
      - 0.0.0.0
      - --port
      - "8000"
    container_name: gai-api-server
    depends_on:
      postgres-db:
        condition: service_started
        required: true
    environment:
      KEYCLOAK_BASE_URL: *keycloak_base_url
      KEYCLOAK_REALM: *keycloak_realm
      KEYCLOAK_FRONTEND_URL: *keycloak_frontend_url
      KEYCLOAK_REFERRER: *keycloak_referrer
      POSTGRES_HOST: postgres-db
      POSTGRES_PASSWORD: *db_password
      POSTGRES_USER: *db_user
      POSTGRES_DB: *db_name
    expose:
      - "8000"
    healthcheck:
      test:
        - CMD
        - curl
        - -f
        - *api_server_healthheck_url
      timeout: 20s
      interval: 10m0s
      retries: 2
    image: *api_server_image
    networks:
      eggplant_nw:
        ipv4_address: 10.5.0.3
    platform: linux/amd64
    ports:
      - mode: ingress
        target: 8000
        published: "8000"
        protocol: tcp
    configs:
      - source: keycloak_settings
        target: /app/static/settings.json
    restart: unless-stopped
    volumes:
      - type: volume
        source: gai_downloads
        target: /etc/sde/downloads
        volume: {}
      - type: volume
        source: gai_requirements
        target: /etc/sde/requirement
        volume: {}
      - type: volume
        source: gai_context_docs
        target: /etc/sde/upload_docs
        volume: {}
    profiles:
      - default
      - notls
      - notls-cpu
      - cpu

  gai-processingd: &gai-processingd-base
    command:
      - python
      - ./processingd_main.py
      - ">>"
      - out_file.log
    container_name: gai-processingd
    depends_on:
      postgres-db:
        condition: service_started
        required: true
    environment:
      KEYCLOAK_BASE_URL: *keycloak_base_url
      KEYCLOAK_REALM: *keycloak_realm
      KEYCLOAK_FRONTEND_URL: *keycloak_frontend_url
      KEYCLOAK_REFERRER: *keycloak_referrer
      POSTGRES_HOST: postgres-db
      POSTGRES_PASSWORD: *db_password
      POSTGRES_USER: *db_user
      POSTGRES_DB: *db_name
      HF_HUB_OFFLINE: 1
      LLAMA_INDEX_CACHE_DIR: /root/.cache/huggingface/hub/
      SIMILARITY_THRESHOLD: *similarity_threshold
      SIMILARITY_TOP_K: *similarity_top_k
      LLM_TEMPERATURE: *llm_temperature
      LLM_TOP_P: *llm_top_p
    image: *processingd_image_cuda
    networks:
      eggplant_nw:
        ipv4_address: 10.5.0.4
    platform: linux/amd64
    restart: unless-stopped
    volumes:
      - type: volume
        source: gai_spacy
        target: /etc/sde/spacy
        volume: {}
      - type: volume
        source: gai_context_docs
        target: /etc/sde/upload_docs
        volume: {}
    deploy:
      resources:
        reservations:
          devices:
            - capabilities:
                - gpu
              driver: nvidia
              count: all
    profiles:
      - default
      - notls

  gai-processingd-cpu:
    <<: *gai-processingd-base
    container_name: gai-processingd-cpu
    image: *processingd_image_cpu
    deploy: ~
    profiles:
      - cpu
      - notls-cpu

  postgres-db:
    container_name: postgres-db
    environment:
      POSTGRES_DB: *db_name
      POSTGRES_PASSWORD: *db_password
      POSTGRES_USER: *db_user
    image: *postgres_image
    networks:
      eggplant_nw:
        ipv4_address: 10.5.0.7
    restart: always
    shm_size: "134217728"
    volumes:
      - type: volume
        source: postgres_pgdata
        target: /var/lib/postgresql/data
        volume: {}
    profiles:
      - default
      - notls
      - notls-cpu
      - cpu

  kc-realm-config-reset: &kc-realm-config-base
    image: *kc_realm_config_image
    container_name: kc-realm-config-reset
    environment:
      <<: *realm-config-env-vars
      IMPORT_MANAGED_REQUIREDACTION: full
      IMPORT_MANAGED_SCOPEMAPPING: full
      IMPORT_MANAGED_CLIENTSCOPEMAPPING: full
      IMPORT_MANAGED_ROLE: full
      IMPORT_MANAGED_CLIENTAUTHORIZATIONRESOURCES: full
      IMPORT_MANAGED_CLIENTAUTHORIZATIONPOLICIES: full
      IMPORT_MANAGED_CLIENTAUTHORIZATIONSCOPES: full
    entrypoint: /app/keycloak-config-cli.sh
    command: reset
    profiles:
      - realm-config
      - realm-config-reset

  kc-realm-config-installer-iam:
    <<: *kc-realm-config-base
    container_name: kc-realm-config-installer-iam
    environment:
      <<: *realm-config-env-vars
      IMPORT_MANAGED_REQUIREDACTION: no-delete
      IMPORT_MANAGED_SCOPEMAPPING: no-delete
      IMPORT_MANAGED_CLIENTSCOPEMAPPING: no-delete
      IMPORT_MANAGED_ROLE: no-delete
      IMPORT_MANAGED_CLIENTAUTHORIZATIONRESOURCES: no-delete
      IMPORT_MANAGED_CLIENTAUTHORIZATIONPOLICIES: no-delete
      IMPORT_MANAGED_CLIENTAUTHORIZATIONSCOPES: no-delete
      KC_DAI_REDIRECT_URI: http://localhost/*
      REALM_INSTALLER_CLIENT_SECRET: ${REALM_INSTALLER_CLIENT_SECRET:?}
    entrypoint: /app/keycloak-config-cli.sh
    command: configure -e dev -e smtp -e initial_user
    profiles:
      - realm-config
    depends_on:
      kc-realm-config-reset:
        condition: service_completed_successfully

  kc-realm-config-installer-generator: &kc-realm-config-installer
    container_name: kc-realm-config-installer-generator
    image: *kc_realm_config_image
    networks:
      - eggplant_nw
    environment:
      KEYCLOAK_URL: *keycloak_frontend_url
      KEYCLOAK_USER: ${KC_ADMIN_USER:?}
      KEYCLOAK_PASSWORD: ${KC_ADMIN_PASSWORD:?}
      KEYCLOAK_SSLVERIFY: ${KEYCLOAK_SSLVERIFY:-true}
      KEYCLOAK_REALM: *keycloak_realm
      REALM_INSTALLER_CLIENT_SECRET: ${REALM_INSTALLER_CLIENT_SECRET:?}
      IMPORT_VARSUBSTITUTION_ENABLED: true
      IMPORT_REMOTESTATE_ENABLED: true
      IMPORT_MANAGED_GROUP: no-delete
      IMPORT_MANAGED_REQUIREDACTION: no-delete
      IMPORT_MANAGED_CLIENTSCOPE: no-delete
      IMPORT_MANAGED_SCOPEMAPPING: no-delete
      IMPORT_MANAGED_CLIENTSCOPEMAPPING: no-delete
      IMPORT_MANAGED_ROLE: no-delete
      IMPORT_MANAGED_COMPONENT: no-delete
      IMPORT_MANAGED_SUBCOMPONENT: no-delete
      IMPORT_MANAGED_AUTHENTICATIONFLOW: no-delete
      IMPORT_MANAGED_IDENTITYPROVIDER: no-delete
      IMPORT_MANAGED_IDENTITYPROVIDERMAPPER: no-delete
      IMPORT_MANAGED_CLIENT: no-delete
      IMPORT_MANAGED_CLIENTAUTHORIZATIONRESOURCES: no-delete
      IMPORT_MANAGED_CLIENTAUTHORIZATIONPOLICIES: no-delete
      IMPORT_MANAGED_CLIENTAUTHORIZATIONSCOPES: no-delete
      IMPORT_MANAGED_MESSAGEBUNDLES: no-delete
      KC_DAI_REDIRECT_URI: http://localhost/*
      GENERATOR_REDIRECT_URI: ${GENERATOR_URL:?}/*
    entrypoint: /app/keycloak-config-cli.sh
    command: configure -a generator -e dev -e on_prem
    profiles:
      - realm-config
    depends_on:
      kc-realm-config-installer-iam:
        condition: service_completed_successfully

networks:
  eggplant_nw:
    name: compose_eggplant_nw
    driver: bridge
    ipam:
      config:
        - subnet: 10.5.0.0/24
          gateway: 10.5.0.1

volumes:
  gai_context_docs:
    name: compose_gai_context_docs
    driver: local
    driver_opts:
      device: *gai_context_docs
      o: bind
      type: none
  gai_downloads:
    name: compose_gai_downloads
    driver: local
    driver_opts:
      device: *gai_downloads
      o: bind
      type: none
  gai_requirements:
    name: compose_gai_requirements
    driver: local
    driver_opts:
      device: *gai_requirements
      o: bind
      type: none
  gai_spacy:
    name: compose_gai_spacy
    driver: local
    driver_opts:
      device: *gai_spacy
      o: bind
      type: none
  postgres_pgdata:
    name: compose_postgres_pgdata

configs:
  nginx_tls_config:
    content: |
      events {}

      http {
          server {
              listen 80;
              server_name localhost;

              client_max_body_size ${MAX_FILE_UPLOAD_SIZE-10m};

              return 301 https://$$host$$request_uri;
          }

          server {
              listen 443 ssl;
              server_name localhost;

              client_max_body_size ${MAX_FILE_UPLOAD_SIZE-10m};

              ssl_certificate /etc/nginx/certs/tls.crt;
              ssl_certificate_key /etc/nginx/certs/tls.key;

              location / {
                  proxy_pass http://gai-api-server:8000;
                  proxy_set_header Host $$host;
                  proxy_set_header X-Real-IP $$remote_addr;
                  proxy_set_header X-Forwarded-For $$proxy_add_x_forwarded_for;
                  proxy_set_header X-Forwarded-Proto $$scheme;
              }
          }
      }

  keycloak_settings:
    content: |
      {
        "services": [
          {
            "name": "iam",
            "version": "v1.0.0",
            "public": {
              "settings": {},
              "options": [
                {
                  "name": "keycloak",
                  "enabled": true,
                  "settings": {
                    "keycloak_base_url": "${KEYCLOAK_FRONTEND_URL:?}",
                    "keycloak_issuer": "${KEYCLOAK_FRONTEND_URL:?}realms/${KEYCLOAK_REALM-eggplant}",
                    "keycloak_realm": "${KEYCLOAK_REALM-eggplant}",
                    "keycloak_referrer": "${GENERATOR_URL:?}",
                    "keycloak_clientId": "generator:web"
                  }
                }
              ]
            }
          }
        ]
      }

secrets:
  tls_cert:
    file: *tls_cert
  tls_key:
    file: *tls_key
