diff --git a/.gitlab/ci/container-boot.gitlab-ci.yml b/.gitlab/ci/container-boot.gitlab-ci.yml index ea7f440..1ab2af4 100644 --- a/.gitlab/ci/container-boot.gitlab-ci.yml +++ b/.gitlab/ci/container-boot.gitlab-ci.yml @@ -158,3 +158,38 @@ container:boot:sculptor: - docker ps --all - docker compose logs sculptor -f & - docker compose run curl-sculptor + +container:boot:velorum: + extends: + - .container:boot + needs: + - generate-environment + - manifest:velorum + script: + - bundle install + - docker compose up velorum -d + - docker compose logs velorum -f & + - support/grpc_check_health --host docker:50052 --service liveness --retries 20 + - support/grpc_check_health --host docker:50052 --service readiness --retries 20 + +container:boot:velorum:offline: + extends: + - .container:boot + needs: + - generate-environment + - manifest:velorum + script: + - docker compose up velorum-offline -d + - docker compose logs velorum-offline -f & + - | + for i in $(seq 1 30); do + STATUS=$(docker inspect --format='{{.State.Health.Status}}' $(docker compose ps -q velorum-offline) 2>/dev/null || echo "starting") + echo "Attempt $i/30: $STATUS" + if [ "$STATUS" = "healthy" ]; then + echo "Velorum booted successfully without network access" + exit 0 + fi + sleep 1 + done + echo "Timed out waiting for healthy status" + exit 1 diff --git a/.gitlab/ci/container-build.gitlab-ci.yml b/.gitlab/ci/container-build.gitlab-ci.yml index e44110a..843aabb 100644 --- a/.gitlab/ci/container-build.gitlab-ci.yml +++ b/.gitlab/ci/container-build.gitlab-ci.yml @@ -326,3 +326,18 @@ manifest:sculptor: PLATFORM: - amd64 - arm64 + +container:velorum: + extends: + - .single-image-build-base + needs: + - generate-environment + variables: + NEED_PROJECT_DOWNLOAD: 'true' + +manifest:velorum: + extends: + - .manifest-create-base + needs: + - generate-environment + - container:velorum diff --git a/container/config-generator/generate.rb b/container/config-generator/generate.rb index b566db2..cf0b0af 100644 --- a/container/config-generator/generate.rb +++ b/container/config-generator/generate.rb @@ -83,6 +83,21 @@ def env?(key) def env_set?(key) @env.fetch(key, nil) != nil end + + def find_envs(pattern, group: false) + if group + @env.each_with_object({}) do |(key, value), groups| + match = key.match(pattern) + next unless match + + group_key = match[1] + suffix = key.sub(/.*#{Regexp.escape(group_key)}_?/, '') + (groups[group_key] ||= {})[suffix] = value + end + else + @env.select { |key, _| key =~ pattern } + end + end end # Run the generator diff --git a/container/config-generator/templates/sagittarius.sagittarius.yml.erb b/container/config-generator/templates/sagittarius.sagittarius.yml.erb index c6a4ab1..1354775 100644 --- a/container/config-generator/templates/sagittarius.sagittarius.yml.erb +++ b/container/config-generator/templates/sagittarius.sagittarius.yml.erb @@ -6,3 +6,7 @@ rails: db: host: <%= env('POSTGRES_HOST') %> port: <%= env('POSTGRES_PORT') %> +velorum: + enabled: <%= env_set?('VELORUM_ENABLED') ? env('VELORUM_ENABLED') : env('COMPOSE_PROFILES').include?('ide_velorum') %> + host: <%= env('VELORUM_HOST') %>:<%= env('VELORUM_PORT') %> + jwt_secret: <%= env('VELORUM_JWT_SECRET') %> diff --git a/container/config-generator/templates/velorum.models.configuration.json.erb b/container/config-generator/templates/velorum.models.configuration.json.erb new file mode 100644 index 0000000..df81762 --- /dev/null +++ b/container/config-generator/templates/velorum.models.configuration.json.erb @@ -0,0 +1,16 @@ +[ +<% find_envs(/VELORUM_MODEL_(\S+?)_.+/, group: true).each_with_index do |(_, envs), i| %> +<%= ',' if i > 0 %> + { + "identifier": "<%= envs.fetch('IDENTIFIER', '') %>", + "name": "<%= envs.fetch('NAME', '') %>", + "capabilities": [ + <%= envs.fetch('CAPABILITIES', '').split(',').map { |c| "\"#{c}\"" }.join(',') %> + ], + "provider": "<%= envs.fetch('PROVIDER', '') %>", + "api": "<%= envs.fetch('API', '') %>", + "auth": "<%= envs.fetch('AUTH', '') %>", + "token_cost": <%= envs.fetch('TOKEN_COST', '').to_f %> + } +<% end %> +] diff --git a/container/velorum/Dockerfile b/container/velorum/Dockerfile new file mode 100644 index 0000000..35e6110 --- /dev/null +++ b/container/velorum/Dockerfile @@ -0,0 +1,27 @@ +ARG PYTHON_VERSION=3.12 +ARG UV_VERSION=0.11.16 + +FROM ghcr.io/astral-sh/uv:${UV_VERSION} AS uv +FROM python:${PYTHON_VERSION}-slim + +COPY --from=uv /uv /uvx /usr/local/bin/ + +WORKDIR /velorum + +COPY projects/velorum/pyproject.toml projects/velorum/uv.lock ./ +RUN uv sync --frozen +COPY projects/velorum/src/model.py ./src/model.py +RUN uv run python -c "from src.model import load_vector_model; load_vector_model()" + +COPY projects/velorum/cli ./cli +COPY projects/velorum/src ./src +COPY projects/velorum/main.py ./main.py + +ENV HF_HUB_OFFLINE=1 +ENV PYTHONUNBUFFERED=1 + +HEALTHCHECK --interval=5s --timeout=3s --retries=3 \ + CMD uv run python -c "import grpc; from grpc_health.v1 import health_pb2, health_pb2_grpc; ch = grpc.insecure_channel('localhost:50051'); stub = health_pb2_grpc.HealthStub(ch); stub.Check(health_pb2.HealthCheckRequest(service='liveness'))" || exit 1 + +EXPOSE 50051 +CMD ["uv", "run", "main.py"] diff --git a/docker-compose/.env b/docker-compose/.env index fbf2398..214cc6f 100644 --- a/docker-compose/.env +++ b/docker-compose/.env @@ -21,6 +21,7 @@ DRACO_REST_AQUILA_TOKEN=draco-rest DRACO_CRON_AQUILA_TOKEN=draco-cron # Active services +# Available services: ide, runtime, ide_velorum (AI orchestrator) COMPOSE_PROFILES=ide,runtime # Image config @@ -28,6 +29,20 @@ IMAGE_REGISTRY=registry.gitlab.com/code0-tech/packages IMAGE_TAG= IMAGE_EDITION= # ce or ee +# Velorum config +# VELORUM_ENABLED=false (override, default checks if COMPOSE_PROFILES contains ide_velorum) +VELORUM_HOST=velorum +VELORUM_PORT=50051 +VELORUM_JWT_SECRET=088cfc7a7fc2b07696d8ee5e1ea9d642 # change to a random value + +# example, GPT5 in the variable name can be changed to other values. This set of variables can be repeated multiple times +# VELORUM_MODEL_GPT5_IDENTIFIER=gpt-5 +# VELORUM_MODEL_GPT5_NAME=GPT 5 +# VELORUM_MODEL_GPT5_CAPABILITIES=explain,generate +# VELORUM_MODEL_GPT5_API= # if using a non-default base-url +# VELORUM_MODEL_GPT5_AUTH= # authentication token +# VELORUM_MODEL_GPT5_TOKEN_COST=1 + # Internal config options SAGITTARIUS_RAILS_HOST=sagittarius-rails-web SAGITTARIUS_RAILS_PORT=3000 diff --git a/docker-compose/docker-compose.yml b/docker-compose/docker-compose.yml index 47966a5..1850ac1 100644 --- a/docker-compose/docker-compose.yml +++ b/docker-compose/docker-compose.yml @@ -110,6 +110,24 @@ services: profiles: - ide + velorum: + image: ${IMAGE_REGISTRY}/velorum:${IMAGE_TAG} + depends_on: + config-generator: + condition: service_completed_successfully + restart: unless-stopped + environment: + SECURITY_TOKEN: $VELORUM_JWT_SECRET + volumes: + - generated-configs:/tmp/generated-configs:ro + entrypoint: | + sh -c " + cp /tmp/generated-configs/velorum.models.configuration.json models.configuration.json + exec uv run main.py + " + profiles: + - ide_velorum + nginx: image: nginx:1.31.0-alpine-slim depends_on: diff --git a/support/config/velorum.models.configuration.json b/support/config/velorum.models.configuration.json new file mode 100644 index 0000000..fe51488 --- /dev/null +++ b/support/config/velorum.models.configuration.json @@ -0,0 +1 @@ +[] diff --git a/support/docker-compose.yml b/support/docker-compose.yml index 33609e3..dc5d81a 100644 --- a/support/docker-compose.yml +++ b/support/docker-compose.yml @@ -147,5 +147,20 @@ services: - --retry-connrefused - http://sculptor:3000/ + velorum: + image: ghcr.io/code0-tech/reticulum/ci-builds/velorum:${RETICULUM_CONTAINER_VERSION} + networks: + - boot + volumes: + - ./config/velorum.models.configuration.json:/velorum/models.configuration.json + ports: + - "50052:50051" + + velorum-offline: + image: ghcr.io/code0-tech/reticulum/ci-builds/velorum:${RETICULUM_CONTAINER_VERSION} + network_mode: "none" + volumes: + - ./config/velorum.models.configuration.json:/velorum/models.configuration.json + networks: boot: diff --git a/support/download_projects b/support/download_projects index 4d62075..03ff61a 100644 --- a/support/download_projects +++ b/support/download_projects @@ -10,4 +10,6 @@ mkdir -p projects download_project aquila download_project draco download_project sagittarius +download_project sculptor download_project taurus +download_project velorum diff --git a/versions/velorum b/versions/velorum new file mode 100644 index 0000000..83a4271 --- /dev/null +++ b/versions/velorum @@ -0,0 +1 @@ +6f34f272aa434826459b76f315f9f33c70d969f4 \ No newline at end of file