diff --git a/DSL/Resql/rag-search/POST/get-all-llm-connections-paginated.sql b/DSL/Resql/rag-search/POST/get-all-llm-connections-paginated.sql new file mode 100644 index 00000000..cb1e2394 --- /dev/null +++ b/DSL/Resql/rag-search/POST/get-all-llm-connections-paginated.sql @@ -0,0 +1,45 @@ +SELECT + id, + vault_uuid, + connection_name, + llm_platform, + llm_model, + embedding_platform, + embedding_model, + monthly_budget, + warn_budget_threshold, + stop_budget_threshold, + disconnect_on_budget_exceed, + used_budget, + environment, + connection_status, + created_at, + CEIL(COUNT(*) OVER() / :page_size::DECIMAL) AS totalPages, + CASE + WHEN used_budget IS NULL OR used_budget = 0 OR (used_budget::DECIMAL / monthly_budget::DECIMAL) < (warn_budget_threshold::DECIMAL / 100.0) THEN 'within_budget' + WHEN stop_budget_threshold != 0 AND (used_budget::DECIMAL / monthly_budget::DECIMAL) >= (stop_budget_threshold::DECIMAL / 100.0) THEN 'over_budget' + WHEN stop_budget_threshold = 0 AND (used_budget::DECIMAL / monthly_budget::DECIMAL) >= 1 THEN 'over_budget' + WHEN (used_budget::DECIMAL / monthly_budget::DECIMAL) >= (warn_budget_threshold::DECIMAL / 100.0) THEN 'close_to_exceed' + ELSE 'within_budget' + END AS budget_status +FROM rag_search.llm_connections +WHERE connection_status <> 'deleted' + -- AND environment = 'testing' + AND (:llm_platform IS NULL OR :llm_platform = '' OR llm_platform = :llm_platform) + AND (:llm_model IS NULL OR :llm_model = '' OR llm_model = :llm_model) + AND (:environment IS NULL OR :environment = '' OR environment = :environment) +ORDER BY + CASE WHEN :sorting = 'connection_name asc' THEN connection_name END ASC, + CASE WHEN :sorting = 'connection_name desc' THEN connection_name END DESC, + CASE WHEN :sorting = 'llm_platform asc' THEN llm_platform END ASC, + CASE WHEN :sorting = 'llm_platform desc' THEN llm_platform END DESC, + CASE WHEN :sorting = 'llm_model asc' THEN llm_model END ASC, + CASE WHEN :sorting = 'llm_model desc' THEN llm_model END DESC, + CASE WHEN :sorting = 'monthly_budget asc' THEN monthly_budget END ASC, + CASE WHEN :sorting = 'monthly_budget desc' THEN monthly_budget END DESC, + CASE WHEN :sorting = 'environment asc' THEN environment END ASC, + CASE WHEN :sorting = 'environment desc' THEN environment END DESC, + CASE WHEN :sorting = 'created_at asc' THEN created_at END ASC, + CASE WHEN :sorting = 'created_at desc' THEN created_at END DESC, + created_at DESC -- Default fallback sorting +OFFSET ((GREATEST(:page, 1) - 1) * :page_size) LIMIT :page_size; \ No newline at end of file diff --git a/DSL/Ruuter.private/rag-search/GET/llm-connections/all.yml b/DSL/Ruuter.private/rag-search/GET/llm-connections/all.yml new file mode 100644 index 00000000..3b69aebd --- /dev/null +++ b/DSL/Ruuter.private/rag-search/GET/llm-connections/all.yml @@ -0,0 +1,84 @@ +declaration: + call: declare + version: 0.1 + description: "Get paginated list of LLM connections" + method: get + accepts: json + returns: json + namespace: rag-search + allowlist: + params: + - field: pageNumber + type: number + description: "Page number (1-based)" + - field: pageSize + type: number + description: "Number of items per page" + - field: sortBy + type: string + description: "Field to sort by (e.g. 'llm_platform', 'created_at')" + - field: sortOrder + type: string + description: "Sort order: 'asc' or 'desc'" + - field: llmPlatform + type: string + description: "Filter by LLM platform" + - field: llmModel + type: string + description: "Filter by LLM model" + - field: environment + type: string + description: "Filter by deployment environment" + +extract_request_data: + assign: + pageNumber: ${Number(incoming.params.pageNumber) ?? 1} + pageSize: ${Number(incoming.params.pageSize) ?? 10} + sortBy: ${incoming.params.sortBy ?? "created_at"} + sortOrder: ${incoming.params.sortOrder ?? "desc"} + sorting: ${sortBy + " " + sortOrder} + llmPlatform: ${incoming.params.llmPlatform ?? ""} + llmModel: ${incoming.params.llmModel ?? ""} + environment: ${incoming.params.environment ?? ""} + next: validate_page_params + +validate_page_params: + switch: + - condition: ${pageNumber < 1} + next: return_invalid_page + - condition: ${pageSize < 1 || pageSize > 100} + next: return_invalid_page_size + next: get_llm_connections + +get_llm_connections: + call: http.post + args: + url: "[#RAG_SEARCH_RESQL]/get-all-llm-connections-paginated" + body: + page: ${pageNumber} + page_size: ${pageSize} + sorting: ${sorting} + llm_platform: ${llmPlatform} + llm_model: ${llmModel} + environment: ${environment} + result: connections_result + next: transform_response + +transform_response: + assign: + response_data: ${connections_result.response.body} + next: return_success + +return_success: + return: ${response_data} + next: end + +return_invalid_page: + status: 400 + return: "Page number must be greater than 0" + next: end + +return_invalid_page_size: + status: 400 + return: "Page size must be between 1 and 100" + next: end \ No newline at end of file diff --git a/Dockerfile.vault-init b/Dockerfile.vault-init new file mode 100644 index 00000000..7743fa6e --- /dev/null +++ b/Dockerfile.vault-init @@ -0,0 +1,10 @@ +FROM hashicorp/vault:1.20.3 + +# Bake the only CLI tools vault-init.sh actually needs (jq + openssl) so container +# startup never depends on the Alpine CDN. Previously these were installed via +# `apk add` on every boot, which failed intermittently on EC2. Retry guards the +# one-time build against a transient mirror hiccup. +RUN for i in 1 2 3; do \ + apk add --no-cache jq openssl && break; \ + echo "apk add failed (attempt $i), retrying..."; sleep 3; \ + done diff --git a/GUI/src/pages/TestProductionLLM/index.tsx b/GUI/src/pages/TestProductionLLM/index.tsx index b9ba6be7..5f22d714 100644 --- a/GUI/src/pages/TestProductionLLM/index.tsx +++ b/GUI/src/pages/TestProductionLLM/index.tsx @@ -8,7 +8,7 @@ import { ChoiceButton } from 'services/inference'; import './TestProductionLLM.scss'; import MessageContent from 'components/MessageContent'; import { llmConnectionsQueryKeys } from 'utils/queryKeys'; -import { fetchLLMConnectionsPaginated } from 'services/llmConnections'; +import { fetchAllLLMConnectionsPaginated } from 'services/llmConnections'; interface Message { @@ -45,7 +45,7 @@ const TestProductionLLM: FC = () => { pageSize: 100, // Get all connections for dropdown sorting: 'created_at desc', }), - queryFn: () => fetchLLMConnectionsPaginated({ + queryFn: () => fetchAllLLMConnectionsPaginated({ pageNumber: 1, pageSize: 100, sortBy: 'created_at desc', diff --git a/GUI/src/services/llmConnections.ts b/GUI/src/services/llmConnections.ts index 647addfc..cd07324e 100644 --- a/GUI/src/services/llmConnections.ts +++ b/GUI/src/services/llmConnections.ts @@ -378,3 +378,19 @@ export async function updateLLMConnectionStatus( }); return data?.response; } + +export async function fetchAllLLMConnectionsPaginated(filters: LLMConnectionFilters): Promise { + const queryParams = new URLSearchParams(); + + if (filters.pageNumber) queryParams.append('pageNumber', filters.pageNumber.toString()); + if (filters.pageSize) queryParams.append('pageSize', filters.pageSize.toString()); + if (filters.sortBy) queryParams.append('sortBy', filters.sortBy); + if (filters.sortOrder) queryParams.append('sortOrder', filters.sortOrder); + if (filters.llmPlatform) queryParams.append('llmPlatform', filters.llmPlatform); + if (filters.llmModel) queryParams.append('llmModel', filters.llmModel); + if (filters.environment) queryParams.append('environment', filters.environment); + + const url = `${llmConnectionsEndpoints.FETCH_ALL_LLM_CONNECTIONS_PAGINATED()}?${queryParams.toString()}`; + const { data } = await apiDev.get(url); + return data?.response; +} \ No newline at end of file diff --git a/GUI/src/utils/endpoints.ts b/GUI/src/utils/endpoints.ts index 386db296..30624914 100644 --- a/GUI/src/utils/endpoints.ts +++ b/GUI/src/utils/endpoints.ts @@ -15,6 +15,7 @@ export const authEndpoints = { export const llmConnectionsEndpoints = { FETCH_LLM_CONNECTIONS_PAGINATED: (): string => `/rag-search/llm-connections/list`, + FETCH_ALL_LLM_CONNECTIONS_PAGINATED: (): string => `/rag-search/llm-connections/all`, GET_LLM_CONNECTION: (): string => `/rag-search/llm-connections/get`, GET_PRODUCTION_CONNECTION: (): string => `/rag-search/llm-connections/production`, CREATE_LLM_CONNECTION: (): string => `/rag-search/llm-connections/add`, diff --git a/docker-compose-ec2.yml b/docker-compose-ec2.yml index e6152dbe..1176e3dd 100644 --- a/docker-compose-ec2.yml +++ b/docker-compose-ec2.yml @@ -517,7 +517,10 @@ services: start_period: 10s vault-init: - image: hashicorp/vault:1.20.3 + build: + context: . + dockerfile: Dockerfile.vault-init + image: rag-vault-init:1.20.3 container_name: vault-init user: "0" depends_on: @@ -539,7 +542,6 @@ services: command: - -c - | - apk add --no-cache curl jq uuidgen openssl # Create and set permissions for all agent directories mkdir -p /agent/credentials /agent/gui-token /agent/cron-token /agent/llm-token /agent/out chown -R vault:vault /agent/credentials /agent/gui-token /agent/cron-token /agent/llm-token /agent/out diff --git a/docker-compose.yml b/docker-compose.yml index 0befd6d7..b64224f1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -465,7 +465,10 @@ services: start_period: 10s vault-init: - image: hashicorp/vault:1.20.3 + build: + context: . + dockerfile: Dockerfile.vault-init + image: rag-vault-init:1.20.3 container_name: vault-init user: "0" depends_on: @@ -487,7 +490,6 @@ services: command: - -c - | - apk add --no-cache curl jq uuidgen openssl # Create and set permissions for all agent directories mkdir -p /agent/credentials /agent/gui-token /agent/cron-token /agent/llm-token /agent/out chown -R vault:vault /agent/credentials /agent/gui-token /agent/cron-token /agent/llm-token /agent/out