volknode 0.2.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. volknode-0.2.5/.gitignore +21 -0
  2. volknode-0.2.5/.netlify/netlify.toml +26 -0
  3. volknode-0.2.5/.python-version +1 -0
  4. volknode-0.2.5/=0.20.0 +0 -0
  5. volknode-0.2.5/PKG-INFO +11 -0
  6. volknode-0.2.5/README.md +0 -0
  7. volknode-0.2.5/Taskfile.yml +44 -0
  8. volknode-0.2.5/gitlab/ci.yml +121 -0
  9. volknode-0.2.5/gitlab/scripts/run-tests-on-gcloud.sh +252 -0
  10. volknode-0.2.5/gitlab/scripts/test-installsh.sh +345 -0
  11. volknode-0.2.5/installsh/install.sh +283 -0
  12. volknode-0.2.5/pyproject.toml +33 -0
  13. volknode-0.2.5/pyrightconfig.json +6 -0
  14. volknode-0.2.5/scripts/generate-code-from-openapi.sh +14 -0
  15. volknode-0.2.5/uv.lock +845 -0
  16. volknode-0.2.5/volknode/__init__.py +0 -0
  17. volknode-0.2.5/volknode/main.py +214 -0
  18. volknode-0.2.5/volknode/src/core/__init__.py +0 -0
  19. volknode-0.2.5/volknode/src/core/result.py +26 -0
  20. volknode-0.2.5/volknode/src/core/validation.py +20 -0
  21. volknode-0.2.5/volknode/src/csr/__init__.py +0 -0
  22. volknode-0.2.5/volknode/src/csr/csr_service.py +47 -0
  23. volknode-0.2.5/volknode/src/csr/key_pair_generator.py +34 -0
  24. volknode-0.2.5/volknode/src/dependency_resolver.py +33 -0
  25. volknode-0.2.5/volknode/src/dependency_resolver_test.py +56 -0
  26. volknode-0.2.5/volknode/src/domain/__init__.py +0 -0
  27. volknode-0.2.5/volknode/src/domain/csr_credentials.py +7 -0
  28. volknode-0.2.5/volknode/src/domain/llm_model.py +12 -0
  29. volknode-0.2.5/volknode/src/domain/llm_node_certificate.py +6 -0
  30. volknode-0.2.5/volknode/src/domain/nexus_certificate.py +6 -0
  31. volknode-0.2.5/volknode/src/domain/running_vllm_config.py +7 -0
  32. volknode-0.2.5/volknode/src/http_client/.gitignore +23 -0
  33. volknode-0.2.5/volknode/src/http_client/README.md +124 -0
  34. volknode-0.2.5/volknode/src/http_client/ai_suite_client/__init__.py +8 -0
  35. volknode-0.2.5/volknode/src/http_client/ai_suite_client/api/__init__.py +1 -0
  36. volknode-0.2.5/volknode/src/http_client/ai_suite_client/api/default/__init__.py +1 -0
  37. volknode-0.2.5/volknode/src/http_client/ai_suite_client/api/default/change_password.py +197 -0
  38. volknode-0.2.5/volknode/src/http_client/ai_suite_client/api/default/converse.py +178 -0
  39. volknode-0.2.5/volknode/src/http_client/ai_suite_client/api/default/create_personal_access_token.py +193 -0
  40. volknode-0.2.5/volknode/src/http_client/ai_suite_client/api/default/create_user.py +207 -0
  41. volknode-0.2.5/volknode/src/http_client/ai_suite_client/api/default/delay_converse_indefinitely_for_prompt.py +127 -0
  42. volknode-0.2.5/volknode/src/http_client/ai_suite_client/api/default/delete_conversation.py +185 -0
  43. volknode-0.2.5/volknode/src/http_client/ai_suite_client/api/default/delete_user.py +189 -0
  44. volknode-0.2.5/volknode/src/http_client/ai_suite_client/api/default/dislike_message.py +182 -0
  45. volknode-0.2.5/volknode/src/http_client/ai_suite_client/api/default/edit_sent_message.py +196 -0
  46. volknode-0.2.5/volknode/src/http_client/ai_suite_client/api/default/get_app_context.py +150 -0
  47. volknode-0.2.5/volknode/src/http_client/ai_suite_client/api/default/get_conversation_by_id.py +182 -0
  48. volknode-0.2.5/volknode/src/http_client/ai_suite_client/api/default/get_conversations.py +190 -0
  49. volknode-0.2.5/volknode/src/http_client/ai_suite_client/api/default/get_my_profile.py +158 -0
  50. volknode-0.2.5/volknode/src/http_client/ai_suite_client/api/default/is_live.py +141 -0
  51. volknode-0.2.5/volknode/src/http_client/ai_suite_client/api/default/is_ready.py +141 -0
  52. volknode-0.2.5/volknode/src/http_client/ai_suite_client/api/default/like_message.py +182 -0
  53. volknode-0.2.5/volknode/src/http_client/ai_suite_client/api/default/list_personal_access_tokens.py +158 -0
  54. volknode-0.2.5/volknode/src/http_client/ai_suite_client/api/default/login_with_credentials.py +193 -0
  55. volknode-0.2.5/volknode/src/http_client/ai_suite_client/api/default/register_llm_node.py +208 -0
  56. volknode-0.2.5/volknode/src/http_client/ai_suite_client/api/default/rename_conversation.py +206 -0
  57. volknode-0.2.5/volknode/src/http_client/ai_suite_client/api/default/reset_app.py +107 -0
  58. volknode-0.2.5/volknode/src/http_client/ai_suite_client/api/default/retry_ai_response.py +175 -0
  59. volknode-0.2.5/volknode/src/http_client/ai_suite_client/api/default/revoke_personal_access_token.py +196 -0
  60. volknode-0.2.5/volknode/src/http_client/ai_suite_client/api/default/stop_indefinite_converse_answer_for_prompt.py +127 -0
  61. volknode-0.2.5/volknode/src/http_client/ai_suite_client/api/default/view_llm_nodes.py +201 -0
  62. volknode-0.2.5/volknode/src/http_client/ai_suite_client/api/default/view_users.py +190 -0
  63. volknode-0.2.5/volknode/src/http_client/ai_suite_client/client.py +271 -0
  64. volknode-0.2.5/volknode/src/http_client/ai_suite_client/errors.py +14 -0
  65. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/__init__.py +109 -0
  66. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/ai_assistant_started_typing_event.py +76 -0
  67. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/ai_retry_started_event.py +76 -0
  68. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/app_context_response.py +122 -0
  69. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/available_model.py +92 -0
  70. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/business_constraints.py +76 -0
  71. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/change_password_request.py +92 -0
  72. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/conversation.py +84 -0
  73. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/conversation_with_messages.py +110 -0
  74. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/conversations_response.py +118 -0
  75. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/converse_request.py +86 -0
  76. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/converse_response_server_event.py +182 -0
  77. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/converse_response_server_event_event_type.py +12 -0
  78. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/create_personal_access_token_request.py +101 -0
  79. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/create_user_request.py +120 -0
  80. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/delay_converse_indefinitely_for_prompt_request.py +76 -0
  81. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/edit_sent_message_request.py +76 -0
  82. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/edit_sent_message_response_server_event.py +166 -0
  83. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/edit_sent_message_response_server_event_event_type.py +11 -0
  84. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/error.py +101 -0
  85. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/error_event.py +88 -0
  86. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/list_personal_access_tokens_response.py +94 -0
  87. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/logged_in_user.py +100 -0
  88. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/login_with_credentials_request.py +84 -0
  89. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/message.py +120 -0
  90. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/message_author.py +8 -0
  91. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/message_edited_event.py +76 -0
  92. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/my_profile_response.py +122 -0
  93. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/new_conversation_started_event.py +76 -0
  94. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/no_available_models_event.py +84 -0
  95. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/pagination.py +84 -0
  96. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/permissions.py +92 -0
  97. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/personal_access_token_created_successfully.py +92 -0
  98. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/personal_access_token_list_item.py +120 -0
  99. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/prompted_message_saved_event.py +76 -0
  100. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/register_llm_node_request.py +124 -0
  101. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/register_llm_node_response.py +100 -0
  102. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/rename_conversation_request.py +76 -0
  103. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/reply_chunk.py +100 -0
  104. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/reply_chunk_content.py +88 -0
  105. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/reply_chunk_content_content_type.py +7 -0
  106. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/retry_ai_response_response_server_event.py +166 -0
  107. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/retry_ai_response_response_server_event_event_type.py +11 -0
  108. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/stop_indefinite_converse_answer_for_prompt_request.py +76 -0
  109. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/successful_login_response.py +84 -0
  110. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/user_created_successfully.py +108 -0
  111. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/user_resource_operations.py +84 -0
  112. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/user_role.py +88 -0
  113. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/user_role_name.py +8 -0
  114. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/view_llm_nodes_response.py +108 -0
  115. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/view_one_llm_node.py +100 -0
  116. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/view_one_user.py +132 -0
  117. volknode-0.2.5/volknode/src/http_client/ai_suite_client/models/view_users_response.py +108 -0
  118. volknode-0.2.5/volknode/src/http_client/ai_suite_client/py.typed +1 -0
  119. volknode-0.2.5/volknode/src/http_client/ai_suite_client/types.py +53 -0
  120. volknode-0.2.5/volknode/src/http_client/pyproject.toml +26 -0
  121. volknode-0.2.5/volknode/src/llm/__init__.py +16 -0
  122. volknode-0.2.5/volknode/src/llm/allowed_models.py +35 -0
  123. volknode-0.2.5/volknode/src/llm/fake_llm_runner.py +32 -0
  124. volknode-0.2.5/volknode/src/llm/fake_vllm_server.py +154 -0
  125. volknode-0.2.5/volknode/src/llm/llm_integration_test.py +331 -0
  126. volknode-0.2.5/volknode/src/llm/llm_runner.py +15 -0
  127. volknode-0.2.5/volknode/src/llm/pulled_model_info.py +9 -0
  128. volknode-0.2.5/volknode/src/llm/real_llm_runner.py +84 -0
  129. volknode-0.2.5/volknode/src/mtls/__init__.py +0 -0
  130. volknode-0.2.5/volknode/src/mtls/mtls_client.py +37 -0
  131. volknode-0.2.5/volknode/src/mtls/mtls_reverse_proxy.py +76 -0
  132. volknode-0.2.5/volknode/src/nexus/__init__.py +0 -0
  133. volknode-0.2.5/volknode/src/nexus/create_personal_access_token_test.py +77 -0
  134. volknode-0.2.5/volknode/src/nexus/fake_certificate_authority.py +94 -0
  135. volknode-0.2.5/volknode/src/nexus/fake_nexus.py +105 -0
  136. volknode-0.2.5/volknode/src/nexus/login_integration_test.py +67 -0
  137. volknode-0.2.5/volknode/src/nexus/nexus.py +22 -0
  138. volknode-0.2.5/volknode/src/nexus/real_nexus.py +48 -0
  139. volknode-0.2.5/volknode/src/nexus/register_llm_node_test.py +420 -0
  140. volknode-0.2.5/volknode/src/repository/__init__.py +7 -0
  141. volknode-0.2.5/volknode/src/repository/csr_credentials_repository.py +44 -0
  142. volknode-0.2.5/volknode/src/repository/llm_model_repository.py +70 -0
  143. volknode-0.2.5/volknode/src/repository/llm_node_certificate_repository.py +43 -0
  144. volknode-0.2.5/volknode/src/repository/nexus_certificate_repository.py +43 -0
  145. volknode-0.2.5/volknode/src/repository/running_vllm_config_repository.py +44 -0
  146. volknode-0.2.5/volknode/src/usecases/pull_model/__init__.py +0 -0
  147. volknode-0.2.5/volknode/src/usecases/pull_model/input.py +21 -0
  148. volknode-0.2.5/volknode/src/usecases/pull_model/interactor.py +20 -0
  149. volknode-0.2.5/volknode/src/usecases/pull_model/interactor_test.py +77 -0
  150. volknode-0.2.5/volknode/src/usecases/register_llm_node/__init__.py +0 -0
  151. volknode-0.2.5/volknode/src/usecases/register_llm_node/input.py +52 -0
  152. volknode-0.2.5/volknode/src/usecases/register_llm_node/interactor.py +62 -0
  153. volknode-0.2.5/volknode/src/usecases/register_llm_node/interactor_test.py +285 -0
  154. volknode-0.2.5/volknode/src/usecases/run_llm_node/__init__.py +0 -0
  155. volknode-0.2.5/volknode/src/usecases/run_llm_node/input.py +22 -0
  156. volknode-0.2.5/volknode/src/usecases/run_llm_node/interactor.py +33 -0
  157. volknode-0.2.5/volknode/src/usecases/run_llm_node/interactor_test.py +119 -0
  158. volknode-0.2.5/volknode/src/usecases/serve_llm/__init__.py +1 -0
  159. volknode-0.2.5/volknode/src/usecases/serve_llm/input.py +38 -0
  160. volknode-0.2.5/volknode/src/usecases/serve_llm/input_test.py +42 -0
  161. volknode-0.2.5/volknode/src/usecases/serve_llm/interactor.py +45 -0
  162. volknode-0.2.5/volknode/src/usecases/serve_llm/interactor_test.py +174 -0
@@ -0,0 +1,21 @@
1
+ node_modules
2
+ *.env
3
+ ai-suite/llm-node/.venv
4
+ !*.example.env
5
+ !devcontainer.config.env
6
+ temp
7
+ ai-suite/nexus/src/temp/*
8
+ !ai-suite/nexus/src/temp
9
+ !ai-suite/nexus/src/temp/temp
10
+
11
+ ai-suite/bin
12
+ secrets
13
+ inventory.ini
14
+
15
+ .claude/
16
+ dist/
17
+ gcloud_admin_service_account_key.json
18
+
19
+ __pycache__/
20
+
21
+ .pytest_cache
@@ -0,0 +1,26 @@
1
+ plugins = []
2
+ headers = []
3
+ redirects = []
4
+
5
+ [functions]
6
+
7
+ [functions."*"]
8
+
9
+ [build]
10
+ publish = "/monorepo/ai-suite/llm-node"
11
+ publishOrigin = "default"
12
+ base = "/monorepo/ai-suite/llm-node"
13
+
14
+ [build.environment]
15
+
16
+ [build.processing]
17
+
18
+ [build.processing.css]
19
+
20
+ [build.processing.html]
21
+
22
+ [build.processing.images]
23
+
24
+ [build.processing.js]
25
+
26
+ [build.services]
@@ -0,0 +1 @@
1
+ 3.12
volknode-0.2.5/=0.20.0 ADDED
File without changes
@@ -0,0 +1,11 @@
1
+ Metadata-Version: 2.4
2
+ Name: volknode
3
+ Version: 0.2.5
4
+ Summary: Add your description here
5
+ Requires-Python: >=3.12
6
+ Requires-Dist: attrs>=22.2.0
7
+ Requires-Dist: cryptography>=44.0.0
8
+ Requires-Dist: httpx<0.29.0,>=0.23.0
9
+ Requires-Dist: huggingface-hub<1.0,>=0.34.0
10
+ Requires-Dist: python-dateutil>=2.8.0
11
+ Requires-Dist: typer>=0.20.0
File without changes
@@ -0,0 +1,44 @@
1
+ version: "3"
2
+ vars:
3
+ GITLAB_PROJECT_ID:
4
+ sh: cat ../../global-configuration.json | jq -r ".gitlabProjectId"
5
+
6
+ tasks:
7
+ test:nexus:integration:
8
+ desc: |
9
+ Run integration tests against the nexus backend.
10
+ Requires the nexus backend to be running (use task ai-suite:nexus:containers:keep-up-with-fake-llm-real-ca).
11
+ dir: ./
12
+ cmds:
13
+ - uv run pytest volknode/src/nexus/ -v
14
+
15
+ test:unit:
16
+ desc: Run unit tests
17
+ dir: ./
18
+ cmds:
19
+ - uv run pytest volknode/src/usecases/ -v
20
+
21
+ volknode-with-fake-llm:
22
+ desc: Run volknode with a fake LLM runner
23
+ dir: ./
24
+ cmds:
25
+ - USE_FAKE_LLM=true uv run python -m volknode.main {{.CLI_ARGS}}
26
+
27
+ volknode-with-fake-llm-and-nexus:
28
+ desc: Run volknode with a fake LLM runner
29
+ dir: ./
30
+ cmds:
31
+ - USE_FAKE_LLM=true USE_FAKE_NEXUS=true uv run python -m volknode.main {{.CLI_ARGS}}
32
+
33
+ volknode:
34
+ desc: Run volknode
35
+ dir: ./
36
+ cmds:
37
+ - USE_FAKE_LLM=false uv run python -m volknode.main {{.CLI_ARGS}}
38
+
39
+ build:
40
+ desc: Build volknode executable with PyInstaller
41
+ dir: ./
42
+ cmds:
43
+ - mkdir -p ./installsh/dist
44
+ - cp ./installsh/install.sh ./installsh/dist/
@@ -0,0 +1,121 @@
1
+ stages:
2
+ - commit-stage
3
+ - deploy-to-test
4
+
5
+ variables:
6
+ ENVIRONMENT: CI
7
+
8
+ llm-node-nexus-integration-tests:
9
+ stage: commit-stage
10
+ image:
11
+ name: gitlab.internal.volksai.io:5050/volksai/monorepo/generic-ci-image:latest
12
+ entrypoint: [""]
13
+ pull_policy: if-not-present
14
+ variables:
15
+ TESTCONTAINERS_HOST_OVERRIDE: host.docker.internal
16
+ TESTCONTAINERS_RYUK_DISABLED: "true"
17
+ NEXUS_HOST: host.docker.internal
18
+ NEXUS_CONTAINER_NAME: nexus-$CI_JOB_ID
19
+ LOCAL_ENV_NEXUS_DB_HOST_INTERNALLY_DOCKER: dev-postgres-$CI_JOB_ID
20
+ LOCAL_ENV_NEXUS_DB_PORT_INTERNALLY_DOCKER: "5432"
21
+ LOCAL_ENV_NEXUS_DB_USER: "username"
22
+ LOCAL_ENV_NEXUS_DB_PASSWORD: "password"
23
+ LOCAL_ENV_NEXUS_DB_NAME: "database"
24
+ POSTGRES_VERSION: "17.4"
25
+ rules:
26
+ - if: '$RUN_ALL_TESTS == "true"'
27
+ - if: '$CI_PIPELINE_SOURCE == "push"'
28
+ changes:
29
+ - ai-suite/llm-node/**/*
30
+ - ai-suite/nexus/**/*
31
+ - ai-suite/openapi/**/*
32
+ tags:
33
+ - docker-executor
34
+ before_script:
35
+ - ln -sfn $CI_PROJECT_DIR /monorepo
36
+ - cd $CI_PROJECT_DIR/ai-suite/nexus/local-development/devcontainers && go mod download
37
+ - cd $CI_PROJECT_DIR
38
+ - cd $CI_PROJECT_DIR/ai-suite/llm-node && uv sync & UV_SYNC_PID=$!
39
+ - nohup task ai-suite:nexus:containers:keep-up-with-fake-llm-real-ca > backend-dev.log 2>&1 &
40
+ - $CI_PROJECT_DIR/scripts/wait-for-container.sh $NEXUS_CONTAINER_NAME
41
+ - nexus_port=$(docker inspect --format='{{(index (index .NetworkSettings.Ports "8080/tcp") 0).HostPort}}' $NEXUS_CONTAINER_NAME)
42
+ - $CI_PROJECT_DIR/scripts/wait-for-http-status-200.sh http://host.docker.internal:$nexus_port/chat-backend/is-live
43
+ - echo "NEXUS_PORT=$nexus_port" >> $CI_PROJECT_DIR/nexus.env
44
+ - wait $UV_SYNC_PID
45
+ script:
46
+ - source $CI_PROJECT_DIR/nexus.env
47
+ - NEXUS_PORT=$NEXUS_PORT task ai-suite:llm-node:test:nexus:integration
48
+ after_script:
49
+ - echo "--- backend-dev.log ---" && cat $CI_PROJECT_DIR/backend-dev.log 2>/dev/null || true
50
+ - echo "--- docker ps -a ---" && docker ps -a 2>/dev/null || true
51
+ - echo "Cleaning up job containers"
52
+ - docker stop $NEXUS_CONTAINER_NAME $LOCAL_ENV_NEXUS_DB_HOST_INTERNALLY_DOCKER 2>/dev/null || true
53
+ - docker rm $NEXUS_CONTAINER_NAME $LOCAL_ENV_NEXUS_DB_HOST_INTERNALLY_DOCKER 2>/dev/null || true
54
+ - docker network prune -f 2>/dev/null || true
55
+
56
+ deploy-volksnode-to-pypi:
57
+ stage: deploy-to-test
58
+ needs: []
59
+ allow_failure: true
60
+ retry: 2
61
+ image:
62
+ name: gitlab.internal.volksai.io:5050/volksai/monorepo/generic-ci-image:latest
63
+ entrypoint: [""]
64
+ pull_policy: if-not-present
65
+ rules:
66
+ - if: '$RUN_ALL_TESTS == "true"'
67
+ - if: '$CI_PIPELINE_SOURCE == "push"'
68
+ changes:
69
+ - ai-suite/llm-node/**/*
70
+ tags:
71
+ - docker-executor
72
+ variables:
73
+ UV_PUBLISH_TOKEN: $PYPI_TOKEN
74
+ script:
75
+ - cd $CI_PROJECT_DIR/ai-suite/llm-node
76
+ - uv build
77
+ - uv publish
78
+
79
+ test-llm-node-installsh:
80
+ stage: slow-tests
81
+ resource_group: test-llm-node-installsh
82
+ needs: []
83
+ image:
84
+ name: gitlab.internal.volksai.io:5050/volksai/monorepo/generic-ci-image:latest
85
+ entrypoint: [""]
86
+ pull_policy: if-not-present
87
+ rules:
88
+ - if: '$RUN_ALL_TESTS == "true"'
89
+ - if: '$CI_PIPELINE_SOURCE == "push"'
90
+ changes:
91
+ - ai-suite/llm-node/**/*
92
+ tags:
93
+ - docker-executor
94
+ variables:
95
+ NETLIFY_AUTH_TOKEN: $NETLIFY_AUTH_TOKEN
96
+ NETLIFY_LLM_NODE_INSTALLSH_TEST_SITE_ID: $NETLIFY_LLM_NODE_INSTALLSH_TEST_SITE_ID
97
+ GCLOUD_SERVICE_ACCOUNT_KEY: $GCLOUD_SERVICE_ACCOUNT_KEY
98
+ script:
99
+ - $CI_PROJECT_DIR/gitlab/scripts/configure-resource-group-newest-first.sh test-llm-node-installsh
100
+ - chmod +x $CI_PROJECT_DIR/ai-suite/llm-node/gitlab/scripts/test-installsh.sh
101
+ - $CI_PROJECT_DIR/ai-suite/llm-node/gitlab/scripts/test-installsh.sh
102
+
103
+ llm-node-vllm-integration-tests:
104
+ stage: slow-tests
105
+ resource_group: llm-node-vllm-integration-tests
106
+ needs: []
107
+ image:
108
+ name: gitlab.internal.volksai.io:5050/volksai/monorepo/generic-ci-image:latest
109
+ entrypoint: [""]
110
+ pull_policy: if-not-present
111
+ rules:
112
+ - if: '$RUN_ALL_TESTS == "true"'
113
+ - if: '$CI_PIPELINE_SOURCE == "push"'
114
+ changes:
115
+ - ai-suite/llm-node/**/*
116
+ tags:
117
+ - docker-executor
118
+ script:
119
+ - $CI_PROJECT_DIR/gitlab/scripts/configure-resource-group-newest-first.sh llm-node-vllm-integration-tests
120
+ - chmod +x $CI_PROJECT_DIR/ai-suite/llm-node/gitlab/scripts/run-tests-on-gcloud.sh
121
+ - $CI_PROJECT_DIR/ai-suite/llm-node/gitlab/scripts/run-tests-on-gcloud.sh
@@ -0,0 +1,252 @@
1
+ #!/bin/bash
2
+ set -e
3
+
4
+ # GCLOUD_PROJECT=${GCLOUD_PROJECT:?"\$GCLOUD_PROJECT must be set"}
5
+ GCLOUD_PROJECT=${GCLOUD_PROJECT:-"llm-node-testing-479012-f6"}
6
+ GCLOUD_ZONES=${GCLOUD_ZONES:-"us-central1-a us-central1-b us-central1-c us-central1-f us-east1-b us-east1-c us-east4-a us-east4-b us-east4-c us-west1-a us-west1-b us-west4-a us-west4-b europe-west1-b europe-west1-c europe-west4-a europe-west4-b europe-west4-c"}
7
+ GCLOUD_MACHINE_TYPE=${GCLOUD_MACHINE_TYPE:-"g2-standard-8"}
8
+ GCLOUD_GPU_TYPE=${GCLOUD_GPU_TYPE:-"nvidia-l4"}
9
+ GCLOUD_GPU_COUNT=${GCLOUD_GPU_COUNT:-1}
10
+ GCLOUD_DISK_SIZE_GB=${GCLOUD_DISK_SIZE_GB:-200}
11
+ GCLOUD_IMAGE_FAMILY=${GCLOUD_IMAGE_FAMILY:-"ubuntu-2204-lts"}
12
+ GCLOUD_IMAGE_PROJECT=${GCLOUD_IMAGE_PROJECT:-"ubuntu-os-cloud"}
13
+
14
+ GCLOUD_SERVICE_ACCOUNT_KEY=${GCLOUD_SERVICE_ACCOUNT_KEY:-""}
15
+
16
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
17
+ REPO_ROOT="${CI_PROJECT_DIR:-$(cd "$SCRIPT_DIR/../../../.." && pwd)}"
18
+
19
+ # Authenticate with gcloud
20
+ echo "=== Authenticating with gcloud ==="
21
+ if [[ -n "$GCLOUD_SERVICE_ACCOUNT_KEY" ]]; then
22
+ echo "$GCLOUD_SERVICE_ACCOUNT_KEY" > /tmp/gcloud-sa-key.json
23
+ gcloud auth activate-service-account --key-file=/tmp/gcloud-sa-key.json
24
+ rm -f /tmp/gcloud-sa-key.json
25
+ else
26
+ echo "Warning: \$GCLOUD_SERVICE_ACCOUNT_KEY not set, assuming gcloud is already authenticated"
27
+ fi
28
+ gcloud config set project "$GCLOUD_PROJECT" --quiet
29
+
30
+ instance_name=""
31
+ GCLOUD_ZONE=""
32
+
33
+ cleanup() {
34
+ echo "=== Cleaning up ==="
35
+ if [[ -n "$instance_name" && -n "$GCLOUD_ZONE" ]]; then
36
+ echo "Deleting GCP instance: $instance_name (zone: $GCLOUD_ZONE)"
37
+ gcloud compute instances delete "$instance_name" \
38
+ --project="$GCLOUD_PROJECT" \
39
+ --zone="$GCLOUD_ZONE" \
40
+ --quiet || true
41
+ echo "Instance deleted"
42
+ fi
43
+ if [[ -n "$gpu_instance_provision_tmp_dir" ]]; then
44
+ rm -rf "$gpu_instance_provision_tmp_dir" || true
45
+ fi
46
+ }
47
+
48
+ trap cleanup EXIT
49
+
50
+ random_unique_tmp_dir_name=$(tr -dc 'a-zA-Z' </dev/urandom | head -c 16)
51
+ gpu_instance_provision_tmp_dir=/tmp/vllm-integration-tests/$random_unique_tmp_dir_name
52
+ gpu_instance_user_private_key_path=$gpu_instance_provision_tmp_dir/gpu_instance_key
53
+ gpu_instance_user_public_key_path=$gpu_instance_provision_tmp_dir/gpu_instance_key.pub
54
+
55
+ rm -rf $gpu_instance_provision_tmp_dir
56
+ mkdir -p $gpu_instance_provision_tmp_dir
57
+
58
+ # Generate SSH key pair
59
+ ssh-keygen -b 2048 -t rsa -f $gpu_instance_user_private_key_path -q -N ""
60
+ llmnode_runner_user_public_key=$(cat $gpu_instance_user_public_key_path)
61
+ chmod 600 $gpu_instance_user_private_key_path
62
+
63
+ instance_name="vllm-integration-test-$(tr -dc 'a-z0-9' </dev/urandom | head -c 8)"
64
+
65
+ # Create startup script that installs NVIDIA drivers, CUDA, and sets up the user.
66
+ # GCP startup scripts run on every boot. After installing NVIDIA drivers, a reboot
67
+ # is required to load the kernel module. The script uses a marker file to track
68
+ # whether the driver install + reboot has already happened.
69
+ startup_script=$(cat <<'STARTUP'
70
+ #!/bin/bash
71
+ exec > /var/log/startup-script.log 2>&1
72
+ set -x
73
+
74
+ # Phase 1: Install NVIDIA drivers and reboot
75
+ if [[ ! -f /var/log/nvidia-driver-installed ]]; then
76
+ apt-get update
77
+ apt-get install -y build-essential linux-headers-$(uname -r) openssh-server sudo curl python3 python3-pip python3-venv
78
+
79
+ curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
80
+ distribution=$(. /etc/os-release; echo $ID$VERSION_ID)
81
+ curl -sL "https://nvidia.github.io/libnvidia-container/$distribution/libnvidia-container.list" | \
82
+ sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
83
+ tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
84
+
85
+ apt-get update
86
+ apt-get install -y nvidia-driver-550
87
+ apt-get install -y cuda-toolkit-12-8
88
+
89
+ echo 'export PATH=/usr/local/cuda/bin:$PATH' >> /etc/profile.d/cuda.sh
90
+ echo 'export LD_LIBRARY_PATH=/usr/local/cuda/lib64:$LD_LIBRARY_PATH' >> /etc/profile.d/cuda.sh
91
+
92
+ touch /var/log/nvidia-driver-installed
93
+ echo "NVIDIA driver installed, rebooting to load kernel module..."
94
+ reboot
95
+ exit 0
96
+ fi
97
+
98
+ # Phase 2: After reboot — set up user and signal readiness
99
+ if ! id llmnode-runner &>/dev/null; then
100
+ useradd -m -s /bin/bash llmnode-runner
101
+ fi
102
+ echo 'llmnode-runner ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
103
+
104
+ mkdir -p /home/llmnode-runner/.ssh
105
+ echo 'PUBLIC_KEY_PLACEHOLDER' >> /home/llmnode-runner/.ssh/authorized_keys
106
+ chmod 700 /home/llmnode-runner/.ssh
107
+ chmod 600 /home/llmnode-runner/.ssh/authorized_keys
108
+ chown -R llmnode-runner:llmnode-runner /home/llmnode-runner/.ssh
109
+
110
+ nvidia-smi
111
+ echo "GPU setup complete"
112
+ touch /var/log/startup-script-done
113
+ STARTUP
114
+ )
115
+
116
+ startup_script="${startup_script//PUBLIC_KEY_PLACEHOLDER/$llmnode_runner_user_public_key}"
117
+
118
+ echo "$startup_script" > $gpu_instance_provision_tmp_dir/startup-script.sh
119
+
120
+ # Create the instance, trying each zone until one succeeds
121
+ echo "=== Creating GCP GPU instance ==="
122
+ echo " Project: $GCLOUD_PROJECT"
123
+ echo " Machine type: $GCLOUD_MACHINE_TYPE"
124
+ echo " GPU: ${GCLOUD_GPU_COUNT}x $GCLOUD_GPU_TYPE"
125
+ echo " Disk size: ${GCLOUD_DISK_SIZE_GB}GB"
126
+ echo " Instance name: $instance_name"
127
+ echo " Zones to try: $GCLOUD_ZONES"
128
+
129
+ for zone in $GCLOUD_ZONES; do
130
+ echo "Trying zone: $zone..."
131
+ if gcloud compute instances create "$instance_name" \
132
+ --project="$GCLOUD_PROJECT" \
133
+ --zone="$zone" \
134
+ --machine-type="$GCLOUD_MACHINE_TYPE" \
135
+ --accelerator="type=$GCLOUD_GPU_TYPE,count=$GCLOUD_GPU_COUNT" \
136
+ --maintenance-policy=TERMINATE \
137
+ --boot-disk-size="${GCLOUD_DISK_SIZE_GB}GB" \
138
+ --image-family="$GCLOUD_IMAGE_FAMILY" \
139
+ --image-project="$GCLOUD_IMAGE_PROJECT" \
140
+ --metadata-from-file=startup-script=$gpu_instance_provision_tmp_dir/startup-script.sh 2>&1; then
141
+ GCLOUD_ZONE="$zone"
142
+ echo "Created instance: $instance_name in zone: $GCLOUD_ZONE"
143
+ break
144
+ else
145
+ echo "Zone $zone unavailable, trying next..."
146
+ fi
147
+ done
148
+
149
+ if [[ -z "$GCLOUD_ZONE" ]]; then
150
+ echo "Error: Could not create instance in any zone"
151
+ exit 1
152
+ fi
153
+
154
+ # Wait for instance to be running
155
+ echo "=== Waiting for instance to be running ==="
156
+ max_attempts=60
157
+ attempt=0
158
+ while [ $attempt -lt $max_attempts ]; do
159
+ status=$(gcloud compute instances describe "$instance_name" \
160
+ --project="$GCLOUD_PROJECT" \
161
+ --zone="$GCLOUD_ZONE" \
162
+ --format="value(status)")
163
+
164
+ if [[ "$status" == "RUNNING" ]]; then
165
+ echo "Instance is running"
166
+ break
167
+ fi
168
+
169
+ echo "Instance status: $status (attempt $((attempt+1))/$max_attempts)"
170
+ sleep 10
171
+ attempt=$((attempt+1))
172
+ done
173
+
174
+ if [[ "$status" != "RUNNING" ]]; then
175
+ echo "Error: Instance did not become ready"
176
+ exit 1
177
+ fi
178
+
179
+ ssh_host=$(gcloud compute instances describe "$instance_name" \
180
+ --project="$GCLOUD_PROJECT" \
181
+ --zone="$GCLOUD_ZONE" \
182
+ --format="value(networkInterfaces[0].accessConfigs[0].natIP)")
183
+
184
+ echo "Instance external IP: $ssh_host"
185
+
186
+ # Wait for SSH
187
+ echo "=== Waiting for SSH to be available ==="
188
+ attempt=0
189
+ while [ $attempt -lt 30 ]; do
190
+ if ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
191
+ -o ConnectTimeout=5 -i $gpu_instance_user_private_key_path \
192
+ llmnode-runner@$ssh_host "echo 'SSH ready'" 2>/dev/null; then
193
+ echo "SSH is ready"
194
+ break
195
+ fi
196
+ echo "Waiting for SSH... (attempt $((attempt+1))/30)"
197
+ sleep 10
198
+ attempt=$((attempt+1))
199
+ done
200
+
201
+ # Wait for startup script to finish (NVIDIA drivers, CUDA)
202
+ echo "=== Waiting for startup script to complete ==="
203
+ attempt=0
204
+ while [ $attempt -lt 60 ]; do
205
+ if ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
206
+ -o ConnectTimeout=5 -i $gpu_instance_user_private_key_path \
207
+ llmnode-runner@$ssh_host "test -f /var/log/startup-script-done" 2>/dev/null; then
208
+ echo "Startup script completed"
209
+ break
210
+ fi
211
+ echo "Waiting for startup script to finish... (attempt $((attempt+1))/60)"
212
+ sleep 15
213
+ attempt=$((attempt+1))
214
+ done
215
+
216
+ if ! ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
217
+ -o ConnectTimeout=5 -i $gpu_instance_user_private_key_path \
218
+ llmnode-runner@$ssh_host "test -f /var/log/startup-script-done" 2>/dev/null; then
219
+ echo "Error: Startup script did not complete"
220
+ echo "=== Startup script log ==="
221
+ ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
222
+ -i $gpu_instance_user_private_key_path \
223
+ llmnode-runner@$ssh_host "sudo cat /var/log/startup-script.log" 2>/dev/null || true
224
+ exit 1
225
+ fi
226
+
227
+ # Copy test files to instance
228
+ echo "=== Copying test files to GPU instance ==="
229
+ rsync -az --progress \
230
+ -e "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i $gpu_instance_user_private_key_path" \
231
+ --exclude='.venv' \
232
+ --exclude='__pycache__' \
233
+ --exclude='.pytest_cache' \
234
+ --exclude='.mypy_cache' \
235
+ --exclude='gitlab' \
236
+ $REPO_ROOT/ai-suite/llm-node/ \
237
+ llmnode-runner@$ssh_host:~/llm-node
238
+
239
+ # Run integration tests
240
+ echo "=== Running vLLM integration tests ==="
241
+ ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
242
+ -i $gpu_instance_user_private_key_path \
243
+ llmnode-runner@$ssh_host \
244
+ "pip install uv && \
245
+ export PATH=\$HOME/.local/bin:/usr/local/cuda/bin:\$PATH && \
246
+ export LD_LIBRARY_PATH=/usr/local/cuda/lib64:\$LD_LIBRARY_PATH && \
247
+ cd ~/llm-node && \
248
+ uv sync && \
249
+ uv pip install vllm && \
250
+ uv run pytest volknode/src/llm/llm_integration_test.py -v"
251
+
252
+ echo "=== vLLM integration tests completed successfully ==="