From 3324f6842653fe959a0cd796c823e078a717028e Mon Sep 17 00:00:00 2001 From: erangi-ar <111747955+erangi-ar@users.noreply.github.com> Date: Thu, 29 Jan 2026 13:54:54 +0530 Subject: [PATCH 1/4] Prompt config module (#218) * updated docker compose ec2 * integrate streaming endpoint with test prodction connection page * formatted response with markdown * fe logic for the encryption * vault secret update after fixing issues * fixed formatting issue * integration with be * update cron manager vault script * tested integration of vault security update * fix security issues * creation success model changes * clean vite config generated files * fixed issue references are not sending with streming tokens * complete #192 and #206 bug fixes * production inference display logic change * change production inference display logic * fixed requested issue * Refactor Docker Compose configuration for vault agents and update CSP settings * Remove obsolete Vite configuration files and associated plugins * Add prompt configuration management feature * Add prompt configuration retrieval and update endpoint paths --------- Co-authored-by: Thiru Dinesh <56014038+Thirunayan22@users.noreply.github.com> Co-authored-by: Thiru Dinesh Co-authored-by: erangi-ar Co-authored-by: nuwangeek Co-authored-by: Charith Nuwan Bimsara <59943919+nuwangeek@users.noreply.github.com> --- .../rag-search-script-v5-prompt-config.sql | 8 ++ DSL/Liquibase/master.yml | 4 +- .../GET/get-prompt-configuration.sql | 5 + .../POST/insert-prompt-configuration.sql | 3 + .../POST/update-prompt-configuration.sql | 4 + .../GET/prompt-configuration/get.yml | 39 ++++++ .../POST/prompt-configuration/save.yml | 58 +++++++++ .../rag-search/POST/vault/secret/create.yml | 2 +- .../rag-search/POST/vault/secret/delete.yml | 2 +- GUI/src/App.tsx | 2 + GUI/src/components/MainNavigation/index.tsx | 16 ++- .../PromptConfigurations.scss | 39 ++++++ GUI/src/pages/PromptConfigurations/index.tsx | 100 +++++++++++++++ GUI/src/services/promptConfiguration.ts | 23 ++++ GUI/src/utils/endpoints.ts | 5 + GUI/src/utils/queryKeys.ts | 5 + GUI/translations/en/common.json | 19 ++- GUI/translations/et/common.json | 19 ++- README.md | 2 +- docker-compose-ec2.yml | 121 ++++++++++++++---- 20 files changed, 442 insertions(+), 34 deletions(-) create mode 100644 DSL/Liquibase/changelog/rag-search-script-v5-prompt-config.sql create mode 100644 DSL/Resql/rag-search/GET/get-prompt-configuration.sql create mode 100644 DSL/Resql/rag-search/POST/insert-prompt-configuration.sql create mode 100644 DSL/Resql/rag-search/POST/update-prompt-configuration.sql create mode 100644 DSL/Ruuter.private/rag-search/GET/prompt-configuration/get.yml create mode 100644 DSL/Ruuter.private/rag-search/POST/prompt-configuration/save.yml create mode 100644 GUI/src/pages/PromptConfigurations/PromptConfigurations.scss create mode 100644 GUI/src/pages/PromptConfigurations/index.tsx create mode 100644 GUI/src/services/promptConfiguration.ts diff --git a/DSL/Liquibase/changelog/rag-search-script-v5-prompt-config.sql b/DSL/Liquibase/changelog/rag-search-script-v5-prompt-config.sql new file mode 100644 index 00000000..8d29f949 --- /dev/null +++ b/DSL/Liquibase/changelog/rag-search-script-v5-prompt-config.sql @@ -0,0 +1,8 @@ +-- liquibase formatted sql + +-- changeset Erangi Ariyasena:rag-script-v5-changeset1 +CREATE TABLE public.prompt_configuration ( + id BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY, + prompt TEXT +); + diff --git a/DSL/Liquibase/master.yml b/DSL/Liquibase/master.yml index a1c31eb1..a17a2485 100644 --- a/DSL/Liquibase/master.yml +++ b/DSL/Liquibase/master.yml @@ -6,4 +6,6 @@ databaseChangeLog: - include: file: changelog/rag-search-script-v3-configuration.sql - include: - file: changelog/rag-search-script-v4-authority-data.xml \ No newline at end of file + file: changelog/rag-search-script-v4-authority-data.xml + - include: + file: changelog/rag-search-script-v5-prompt-config.sql \ No newline at end of file diff --git a/DSL/Resql/rag-search/GET/get-prompt-configuration.sql b/DSL/Resql/rag-search/GET/get-prompt-configuration.sql new file mode 100644 index 00000000..36b3f36b --- /dev/null +++ b/DSL/Resql/rag-search/GET/get-prompt-configuration.sql @@ -0,0 +1,5 @@ +SELECT + id, + prompt +FROM prompt_configuration +LIMIT 1 diff --git a/DSL/Resql/rag-search/POST/insert-prompt-configuration.sql b/DSL/Resql/rag-search/POST/insert-prompt-configuration.sql new file mode 100644 index 00000000..d60bd700 --- /dev/null +++ b/DSL/Resql/rag-search/POST/insert-prompt-configuration.sql @@ -0,0 +1,3 @@ +INSERT INTO prompt_configuration (prompt) +VALUES (:prompt) +RETURNING id, prompt diff --git a/DSL/Resql/rag-search/POST/update-prompt-configuration.sql b/DSL/Resql/rag-search/POST/update-prompt-configuration.sql new file mode 100644 index 00000000..1d3fb205 --- /dev/null +++ b/DSL/Resql/rag-search/POST/update-prompt-configuration.sql @@ -0,0 +1,4 @@ +UPDATE prompt_configuration +SET prompt = :prompt +WHERE id = :id +RETURNING id, prompt diff --git a/DSL/Ruuter.private/rag-search/GET/prompt-configuration/get.yml b/DSL/Ruuter.private/rag-search/GET/prompt-configuration/get.yml new file mode 100644 index 00000000..69e513cb --- /dev/null +++ b/DSL/Ruuter.private/rag-search/GET/prompt-configuration/get.yml @@ -0,0 +1,39 @@ +declaration: + call: declare + version: 0.1 + description: "Get prompt configuration" + method: get + accepts: json + returns: json + namespace: rag-search + +get_prompt_configuration: + call: http.get + args: + url: "[#RAG_SEARCH_RESQL]/get-prompt-configuration" + result: prompt_result + next: check_prompt_exists + +check_prompt_exists: + switch: + - condition: "${prompt_result.response.body.length > 0}" + next: transform_response + next: transform_empty_response + +transform_response: + assign: + data: ${prompt_result.response.body} + next: return_success + +transform_empty_response: + assign: + emptyData: [] + next: return_empty + +return_success: + return: ${data} + next: end + +return_empty: + return: ${emptyData} + next: end diff --git a/DSL/Ruuter.private/rag-search/POST/prompt-configuration/save.yml b/DSL/Ruuter.private/rag-search/POST/prompt-configuration/save.yml new file mode 100644 index 00000000..ad90875d --- /dev/null +++ b/DSL/Ruuter.private/rag-search/POST/prompt-configuration/save.yml @@ -0,0 +1,58 @@ +declaration: + call: declare + version: 0.1 + description: "Update or insert prompt configuration" + method: post + accepts: json + returns: json + namespace: rag-search + allowlist: + body: + - field: prompt + type: string + description: "Prompt text to save" + +extract_request_data: + assign: + prompt: ${incoming.body.prompt ?? ""} + next: get_existing_prompt + +get_existing_prompt: + call: http.get + args: + url: "[#RAG_SEARCH_RESQL]/get-prompt-configuration" + result: existing_prompt + next: check_if_exists + +check_if_exists: + switch: + - condition: "${existing_prompt.response.body.length > 0}" + next: update_prompt + next: insert_prompt + +update_prompt: + call: http.post + args: + url: "[#RAG_SEARCH_RESQL]/update-prompt-configuration" + body: + id: ${existing_prompt.response.body[0].id} + prompt: ${prompt} + result: update_result + next: return_update_success + +insert_prompt: + call: http.post + args: + url: "[#RAG_SEARCH_RESQL]/insert-prompt-configuration" + body: + prompt: ${prompt} + result: insert_result + next: return_insert_success + +return_update_success: + return: ${update_result.response.body[0]} + next: end + +return_insert_success: + return: ${insert_result.response.body[0]} + next: end diff --git a/DSL/Ruuter.private/rag-search/POST/vault/secret/create.yml b/DSL/Ruuter.private/rag-search/POST/vault/secret/create.yml index 3fa2f460..b6a533d9 100644 --- a/DSL/Ruuter.private/rag-search/POST/vault/secret/create.yml +++ b/DSL/Ruuter.private/rag-search/POST/vault/secret/create.yml @@ -5,7 +5,7 @@ declaration: method: post accepts: json returns: json - namespace: classifier + namespace: rag-search allowlist: body: - field: connectionId diff --git a/DSL/Ruuter.private/rag-search/POST/vault/secret/delete.yml b/DSL/Ruuter.private/rag-search/POST/vault/secret/delete.yml index 7cf146f5..f0a72200 100644 --- a/DSL/Ruuter.private/rag-search/POST/vault/secret/delete.yml +++ b/DSL/Ruuter.private/rag-search/POST/vault/secret/delete.yml @@ -5,7 +5,7 @@ declaration: method: post accepts: json returns: json - namespace: classifier + namespace: rag-search allowlist: body: - field: connectionId diff --git a/GUI/src/App.tsx b/GUI/src/App.tsx index ceb8d83e..5839b180 100644 --- a/GUI/src/App.tsx +++ b/GUI/src/App.tsx @@ -13,6 +13,7 @@ import ViewLLMConnection from 'pages/LLMConnections/ViewLLMConnection'; import UserManagement from 'pages/UserManagement'; import TestLLM from 'pages/TestModel'; import TestProductionLLM from 'pages/TestProductionLLM'; +import PromptConfigurations from 'pages/PromptConfigurations'; const App: FC = () => { const navigate = useNavigate(); @@ -62,6 +63,7 @@ const App: FC = () => { } /> } /> } /> + } /> } /> } /> diff --git a/GUI/src/components/MainNavigation/index.tsx b/GUI/src/components/MainNavigation/index.tsx index 90dccb4a..070c4b9a 100644 --- a/GUI/src/components/MainNavigation/index.tsx +++ b/GUI/src/components/MainNavigation/index.tsx @@ -25,9 +25,19 @@ const MainNavigation: FC = () => { }, { id: 'llmConnections', - label: t('menu.llmConnections'), - path: '/llm-connections', + label: t('menu.llmConnections._self'), + path: '', icon: , + children: [ + { + label: t('menu.llmConnections.overview'), + path: '/llm-connections', + }, + { + label: t('menu.llmConnections.promptConfigurations'), + path: '/prompt-configurations', + } + ], }, { id: 'testLLM', @@ -37,7 +47,7 @@ const MainNavigation: FC = () => { }, { id: 'testProductionLLM', - label: 'Test Production LLM', + label: t('menu.testProductionLLM'), path: '/test-production-llm', icon: } diff --git a/GUI/src/pages/PromptConfigurations/PromptConfigurations.scss b/GUI/src/pages/PromptConfigurations/PromptConfigurations.scss new file mode 100644 index 00000000..edbeba13 --- /dev/null +++ b/GUI/src/pages/PromptConfigurations/PromptConfigurations.scss @@ -0,0 +1,39 @@ +.prompt-configurations { + padding: 2rem; + + .container { + max-width: 1200px; + margin: 0 auto; + } + + .title-container { + margin-bottom: 2rem; + + .title { + font-size: 2rem; + font-weight: 600; + margin-bottom: 0.5rem; + color: #1a1a1a; + } + + .subtitle { + font-size: 1rem; + color: #666; + margin: 0; + } + } + + .prompt-form { + background: #fff; + border-radius: 8px; + padding: 2rem; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + + .form-actions { + margin-top: 1.5rem; + display: flex; + justify-content: flex-end; + gap: 1rem; + } + } +} diff --git a/GUI/src/pages/PromptConfigurations/index.tsx b/GUI/src/pages/PromptConfigurations/index.tsx new file mode 100644 index 00000000..0c7b5113 --- /dev/null +++ b/GUI/src/pages/PromptConfigurations/index.tsx @@ -0,0 +1,100 @@ +import { FC, useState, useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { Button, FormTextarea } from 'components'; +import { ButtonAppearanceTypes, ToastTypes } from 'enums/commonEnums'; +import CircularSpinner from 'components/molecules/CircularSpinner/CircularSpinner'; +import { getPromptConfiguration, savePromptConfiguration } from 'services/promptConfiguration'; +import { promptConfigurationQueryKeys } from 'utils/queryKeys'; +import { useToast } from 'hooks/useToast'; +import './PromptConfigurations.scss'; + +const PromptConfigurations: FC = () => { + const { t } = useTranslation(); + const toast = useToast(); + const queryClient = useQueryClient(); + const [promptText, setPromptText] = useState(''); + const [isUpdating, setIsUpdating] = useState(false); + + // Fetch prompt configuration + const { data: promptConfig, isLoading } = useQuery({ + queryKey: promptConfigurationQueryKeys.current(), + queryFn: getPromptConfiguration, + }); + + + // Update promptText when data is loaded + useEffect(() => { + if (promptConfig && promptConfig.length > 0) { + setPromptText(promptConfig[0].prompt || ''); + setIsUpdating(true); + } + }, [promptConfig]); + + // Save prompt mutation + const saveMutation = useMutation({ + mutationFn: savePromptConfiguration, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: promptConfigurationQueryKeys.current() }); + toast.open({ + type: ToastTypes.SUCCESS, + title: t('toast.success.title'), + message: t('promptConfigurations.submitSuccess'), + }); + }, + onError: (error: any) => { + console.error('Error saving prompt:', error); + toast.open({ + type: ToastTypes.ERROR, + title: t('toast.error.title'), + message: t('promptConfigurations.submitError'), + }); + }, + }); + + const handleSubmit = () => { + if (!promptText.trim()) { + return; + } + saveMutation.mutate(promptText); + }; + + if (isLoading) { + return ; + } + + return ( +
+
+
+
{t('promptConfigurations.title')}
+
+ +
+ setPromptText(e.target.value)} + minRows={10} + /> + +
+ +
+
+
+
+ ); +}; + +export default PromptConfigurations; diff --git a/GUI/src/services/promptConfiguration.ts b/GUI/src/services/promptConfiguration.ts new file mode 100644 index 00000000..09dc6d45 --- /dev/null +++ b/GUI/src/services/promptConfiguration.ts @@ -0,0 +1,23 @@ +import apiDev from './api-dev'; +import { promptConfigurationEndpoints } from 'utils/endpoints'; + +export interface PromptConfiguration { + id: number | null; + prompt: string; +} + +export interface PromptConfigurationResponse { + response: PromptConfiguration[]; +} + +export const getPromptConfiguration = async (): Promise => { + const { data } = await apiDev.get(promptConfigurationEndpoints.GET_PROMPT_CONFIGURATION()); + return data?.response || []; +}; + +export const savePromptConfiguration = async (prompt: string): Promise => { + const { data } = await apiDev.post(promptConfigurationEndpoints.SAVE_PROMPT_CONFIGURATION(), { + prompt, + }); + return data?.response; +}; diff --git a/GUI/src/utils/endpoints.ts b/GUI/src/utils/endpoints.ts index a6b203d8..386db296 100644 --- a/GUI/src/utils/endpoints.ts +++ b/GUI/src/utils/endpoints.ts @@ -34,3 +34,8 @@ export const vaultEndpoints = { CREATE_VAULT_SECRET: (): string => `/rag-search/vault/secret/create`, DELETE_VAULT_SECRET: (): string => `/rag-search/vault/secret/delete`, } + +export const promptConfigurationEndpoints = { + GET_PROMPT_CONFIGURATION: (): string => `/rag-search/prompt-configuration/get`, + SAVE_PROMPT_CONFIGURATION: (): string => `/rag-search/prompt-configuration/save`, +} diff --git a/GUI/src/utils/queryKeys.ts b/GUI/src/utils/queryKeys.ts index e10462e8..ebc3c1a4 100644 --- a/GUI/src/utils/queryKeys.ts +++ b/GUI/src/utils/queryKeys.ts @@ -38,3 +38,8 @@ export const inferenceQueryKeys = { results: () => [...inferenceQueryKeys.all(), 'results'] as const, result: (request: InferenceRequest) => [...inferenceQueryKeys.results(), request] as const, }; + +export const promptConfigurationQueryKeys = { + all: () => ['prompt-configuration'] as const, + current: () => [...promptConfigurationQueryKeys.all(), 'current'] as const, +}; diff --git a/GUI/translations/en/common.json b/GUI/translations/en/common.json index 0341108e..abba3913 100644 --- a/GUI/translations/en/common.json +++ b/GUI/translations/en/common.json @@ -62,7 +62,12 @@ "menu": { "userManagement": "User management", "testLLM": "Test LLM", - "llmConnections": "LLM connections" + "testProductionLLM": "Test Production LLM", + "llmConnections": { + "_self": "LLM connections", + "overview": "Overview", + "promptConfigurations": "Prompt Configurations" + } }, "userManagement": { "title": "User management", @@ -399,5 +404,17 @@ "aws": "AWS Bedrock", "azure": "Azure OpenAI" } + }, + "promptConfigurations": { + "title": "Prompt Configurations", + "subtitle": "Configure and manage your prompt templates", + "promptLabel": "Prompt Template", + "promptPlaceholder": "Enter your prompt template here...", + "submitButton": "Save", + "updateButton": "Update", + "saving": "Saving...", + "updating": "Updating...", + "submitSuccess": "Prompt configuration saved successfully", + "submitError": "Failed to save prompt configuration. Please try again." } } \ No newline at end of file diff --git a/GUI/translations/et/common.json b/GUI/translations/et/common.json index bd2d5504..2cf5b3c4 100644 --- a/GUI/translations/et/common.json +++ b/GUI/translations/et/common.json @@ -62,7 +62,12 @@ "menu": { "userManagement": "Kasutajate haldus", "testLLM": "Testi mudelit", - "llmConnections": "Mudelite ühendused" + "testProductionLLM": "Testi toodangu mudelit", + "llmConnections": { + "_self": "Mudelite ühendused", + "overview": "Ülevaade", + "promptConfigurations": "Viiba Seaded" + } }, "userManagement": { "title": "Kasutajate haldus", @@ -399,5 +404,17 @@ "aws": "AWS Bedrock", "azure": "Azure OpenAI" } + }, + "promptConfigurations": { + "title": "Viiba Seaded", + "subtitle": "Seadista ja halda oma viiba malle", + "promptLabel": "Viiba Mall", + "promptPlaceholder": "Sisesta siia oma viiba mall...", + "submitButton": "Salvesta", + "updateButton": "Uuenda", + "saving": "Salvestan...", + "updating": "Uuendan...", + "submitSuccess": "Viiba seadistus salvestati edukalt", + "submitError": "Viiba seadistuse salvestamine ebaõnnestus. Palun proovi uuesti." } } \ No newline at end of file diff --git a/README.md b/README.md index 9e7dd82b..ad5edce9 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ The **BYK-RAG Module** is part of the Burokratt ecosystem, designed to provide * - Models searchable via dropdown with cache-enabled indicators. - **Enhanced Security with RSA Encryption** - - LLM credentials encrypted with RSA-2048 asymmetric encryption before storage. + - LLM credentials encrypted with RSA-2048 asymmetric encryption before storage. - GUI encrypts using public key; CronManager decrypts with private key. - Additional security layer beyond HashiCorp Vault's encryption. diff --git a/docker-compose-ec2.yml b/docker-compose-ec2.yml index 130a3484..26c19068 100644 --- a/docker-compose-ec2.yml +++ b/docker-compose-ec2.yml @@ -128,7 +128,7 @@ services: - REACT_APP_RUUTER_API_URL=https://est-rag-rtc.rootcode.software/ruuter-public - REACT_APP_RUUTER_PRIVATE_API_URL=https://est-rag-rtc.rootcode.software/ruuter-private - REACT_APP_CUSTOMER_SERVICE_LOGIN=https://est-rag-rtc.rootcode.software/authentication-layer/et/dev-auth - - REACT_APP_CSP=upgrade-insecure-requests; default-src 'self'; font-src 'self' data:; img-src 'self' data:; script-src 'self' 'unsafe-eval' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; object-src 'none'; connect-src 'self' http://localhost:8086 http://localhost:8088 http://localhost:3004 http://localhost:3005 ws://localhost https://est-rag-rtc.rootcode.software; + - REACT_APP_CSP=upgrade-insecure-requests; default-src 'self'; font-src 'self' data:; img-src 'self' data:; script-src 'self' 'unsafe-eval' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; object-src 'none'; connect-src 'self' http://localhost:8086 http://localhost:8088 http://localhost:3004 http://localhost:3005 ws://localhost https://vault-agent-gui:8202 https://est-rag-rtc.rootcode.software; - DEBUG_ENABLED=true - CHOKIDAR_USEPOLLING=true - PORT=3001 @@ -174,25 +174,25 @@ services: cron-manager: container_name: cron-manager image: cron-manager-python:latest - user: "root" + user: root volumes: - ./DSL/CronManager/DSL:/DSL - ./DSL/CronManager/script:/app/scripts - ./src/vector_indexer:/app/src/vector_indexer + - ./src/utils/decrypt_vault_secrets.py:/app/src/utils/decrypt_vault_secrets.py:ro # Decryption utility (read-only) - cron_data:/app/data - shared-volume:/app/shared # Access to shared resources for cross-container coordination - ./datasets:/app/datasets # Direct access to datasets folder for diff identifier operations - ./grafana-configs/loki_logger.py:/app/src/vector_indexer/loki_logger.py - ./.env:/app/.env:ro - - vault-agent-token:/agent/out:ro # Mount vault token for accessing vault secrets environment: - server.port=9010 - PYTHONPATH=/app:/app/src/vector_indexer - - VAULT_ADDR=http://vault:8200 + - VAULT_AGENT_URL=http://vault-agent-cron:8203 ports: - 9010:8080 depends_on: - - vault-agent-llm + - vault-agent-cron networks: - bykstack @@ -496,10 +496,8 @@ services: - vault-data:/vault/file - ./vault/config:/vault/config:ro - ./vault/logs:/vault/logs - expose: - - "8200" networks: - - bykstack + - vault-network # Only on vault-network for security restart: unless-stopped healthcheck: test: ["CMD", "sh", "-c", "wget -q -O- http://127.0.0.1:8200/v1/sys/health || exit 0"] @@ -520,14 +518,74 @@ services: volumes: - vault-data:/vault/data - vault-agent-creds:/agent/credentials - - vault-agent-token:/agent/out + - vault-agent-gui-token:/agent/gui-token + - vault-agent-cron-token:/agent/cron-token + - vault-agent-llm-token:/agent/llm-token - ./vault-init.sh:/vault-init.sh:ro networks: - - bykstack + - vault-network # Access vault + - bykstack # Access to write agent tokens entrypoint: ["/bin/sh"] - command: ["-c", "apk add --no-cache curl jq && chmod -R 755 /agent/credentials && chmod -R 770 /agent/out && chown -R vault:vault /agent/credentials /agent/out && su vault -s /bin/sh /vault-init.sh"] + 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 + chmod 755 /agent/credentials /agent/gui-token /agent/cron-token /agent/llm-token /agent/out + # Run vault initialization as vault user + su vault -s /bin/sh /vault-init.sh restart: "no" + vault-agent-gui: + image: hashicorp/vault:1.20.3 + container_name: vault-agent-gui + command: ["vault", "agent", "-config=/agent/config/gui-agent.hcl", "-log-level=info"] + depends_on: + vault-init: + condition: service_completed_successfully + cap_add: + - IPC_LOCK + volumes: + - ./vault/agents/gui/gui-agent.hcl:/agent/config/gui-agent.hcl:ro + - vault-agent-creds:/agent/credentials:ro + - vault-agent-gui-token:/agent/gui-token + networks: + - vault-network # Access vault + - bykstack # Accessible by GUI service + restart: unless-stopped + healthcheck: + test: ["CMD", "sh", "-c", "test -f /agent/gui-token/token && test -s /agent/gui-token/token"] + interval: 10s + timeout: 3s + retries: 3 + start_period: 5s + + vault-agent-cron: + image: hashicorp/vault:1.20.3 + container_name: vault-agent-cron + command: ["vault", "agent", "-config=/agent/config/cron-agent.hcl", "-log-level=info"] + depends_on: + vault-init: + condition: service_completed_successfully + cap_add: + - IPC_LOCK + volumes: + - ./vault/agents/cron/cron-agent.hcl:/agent/config/cron-agent.hcl:ro + - vault-agent-creds:/agent/credentials:ro + - vault-agent-cron-token:/agent/cron-token + networks: + - vault-network # Access vault + - bykstack # Accessible by CronManager service + restart: unless-stopped + healthcheck: + test: ["CMD", "sh", "-c", "test -f /agent/cron-token/token && test -s /agent/cron-token/token"] + interval: 10s + timeout: 3s + retries: 3 + start_period: 5s + vault-agent-llm: image: hashicorp/vault:1.20.3 container_name: vault-agent-llm @@ -540,10 +598,17 @@ services: volumes: - ./vault/agents/llm/agent.hcl:/agent/config/agent.hcl:ro - vault-agent-creds:/agent/credentials:ro - - vault-agent-token:/agent/out + - vault-agent-llm-token:/agent/llm-token networks: - - bykstack + - vault-network # Access vault + - bykstack # Accessible by LLM service restart: unless-stopped + healthcheck: + test: ["CMD", "sh", "-c", "test -f /agent/llm-token/token && test -s /agent/llm-token/token"] + interval: 10s + timeout: 3s + retries: 3 + start_period: 5s # LLM Orchestration Service llm-orchestration-service: @@ -558,24 +623,22 @@ services: - .env environment: - ENVIRONMENT=production - - VAULT_ADDR=http://vault:8200 - - VAULT_TOKEN=/agent/out/token + - VAULT_ADDR=http://vault-agent-llm:8201 + # VAULT_TOKEN not set - vault-agent-llm proxy handles authentication volumes: - ./src/llm_config_module/config:/app/src/llm_config_module/config:ro - ./src/optimization/optimized_modules:/app/src/optimization/optimized_modules - llm_orchestration_logs:/app/logs - - vault-agent-token:/agent/out:ro networks: - bykstack depends_on: - - vault - vault-agent-llm - # healthcheck: - # test: ["CMD", "curl", "-f", "http://llm-orchestration-service:8100/health"] - # interval: 30s - # timeout: 10s - # start_period: 40s - # retries: 3 + healthcheck: + test: ["CMD", "curl", "-f", "http://llm-orchestration-service:8100/health"] + interval: 30s + timeout: 10s + start_period: 40s + retries: 3 volumes: loki-data: @@ -602,8 +665,12 @@ volumes: name: cron_data vault-agent-creds: name: vault-agent-creds - vault-agent-token: - name: vault-agent-token + vault-agent-gui-token: + name: vault-agent-gui-token + vault-agent-cron-token: + name: vault-agent-cron-token + vault-agent-llm-token: + name: vault-agent-llm-token opensearch-data: name: opensearch-data @@ -611,3 +678,7 @@ networks: bykstack: name: bykstack driver: bridge + vault-network: + name: vault-network + driver: bridge + internal: true # No external access - isolated network \ No newline at end of file From d45dc8a645a2b8caeb013328dcb5067878f76d87 Mon Sep 17 00:00:00 2001 From: erangi-ar <111747955+erangi-ar@users.noreply.github.com> Date: Tue, 3 Feb 2026 22:16:15 +0530 Subject: [PATCH 2/4] Show used budget for llm connection (#215) * updated docker compose ec2 * integrate streaming endpoint with test prodction connection page * formatted response with markdown * fe logic for the encryption * vault secret update after fixing issues * fixed formatting issue * integration with be * update cron manager vault script * tested integration of vault security update * fix security issues * fixed issue references are not sending with streming tokens * complete #192 and #206 bug fixes * change production inference display logic * Add budget management features to LLMConnectionCard and translations * Remove obsolete Vite configuration files and associated plugins --------- Co-authored-by: Thiru Dinesh <56014038+Thirunayan22@users.noreply.github.com> Co-authored-by: Thiru Dinesh Co-authored-by: erangi-ar Co-authored-by: nuwangeek Co-authored-by: Charith Nuwan Bimsara <59943919+nuwangeek@users.noreply.github.com> --- .../molecules/LLMConnectionCard/index.tsx | 43 +++++++++++++++++++ GUI/src/pages/LLMConnections/index.tsx | 10 ++++- GUI/translations/en/common.json | 1 + GUI/translations/et/common.json | 1 + 4 files changed, 54 insertions(+), 1 deletion(-) diff --git a/GUI/src/components/molecules/LLMConnectionCard/index.tsx b/GUI/src/components/molecules/LLMConnectionCard/index.tsx index 48342e75..26fe8e11 100644 --- a/GUI/src/components/molecules/LLMConnectionCard/index.tsx +++ b/GUI/src/components/molecules/LLMConnectionCard/index.tsx @@ -21,6 +21,10 @@ type LLMConnectionCardProps = { isActive?: boolean; deploymentEnv?: string; budgetStatus?: string; + usedBudget?: number; + monthlyBudget?: number; + stopBudgetThreshold?: number; + disconnectOnBudgetExceed?: boolean; onStatusChange?: (id: number | string, newStatus: boolean) => void; }; @@ -32,6 +36,10 @@ const LLMConnectionCard: FC> = ({ isActive, deploymentEnv, budgetStatus, + usedBudget, + monthlyBudget, + stopBudgetThreshold, + disconnectOnBudgetExceed, onStatusChange, }) => { const { open, close } = useDialog(); @@ -40,6 +48,31 @@ const LLMConnectionCard: FC> = ({ const toast = useToast(); const queryClient = useQueryClient(); + // Format currency + const formatCurrency = (amount?: number): string => { + if (amount === undefined || amount === null) { + return '0,00 €'; + } + + return new Intl.NumberFormat('et-EE', { + style: 'currency', + currency: 'EUR', + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }).format(amount); + }; + + + // Get the relevant budget threshold + const getRelevantBudget = (): number | undefined => { + // here if disconnect on budget exceed is enabled and stop threshold is set, calculate the actual amount from percentage + if (disconnectOnBudgetExceed && stopBudgetThreshold && stopBudgetThreshold > 0 && monthlyBudget) { + return (monthlyBudget * stopBudgetThreshold) / 100; + } + // Otherwise using monthly budget + return monthlyBudget; + }; + const updateStatusMutation = useMutation({ mutationFn: ({ id, status }: { id: string | number; status: 'active' | 'inactive' }) => updateLLMConnectionStatus(id, status), @@ -145,6 +178,16 @@ const LLMConnectionCard: FC> = ({ {model ?? 'N/A'} + {(usedBudget !== undefined || monthlyBudget !== undefined) && ( +
+ + {t('dataModels.budgetUsage')}: + + + {formatCurrency(usedBudget)} / {formatCurrency(getRelevantBudget())} + +
+ )}
{renderDeploymentEnv(deploymentEnv)} {renderBudgetStatus(budgetStatus)} diff --git a/GUI/src/pages/LLMConnections/index.tsx b/GUI/src/pages/LLMConnections/index.tsx index 2484a82d..2c3542df 100644 --- a/GUI/src/pages/LLMConnections/index.tsx +++ b/GUI/src/pages/LLMConnections/index.tsx @@ -257,7 +257,11 @@ const LLMConnections: FC = () => { deploymentEnv={productionConnection.environment} budgetStatus={productionConnection.budgetStatus} platform={productionConnection.llmPlatform} - model={productionConnection.llmModel} + model={productionConnection.llmModel} + usedBudget={productionConnection.usedBudget} + monthlyBudget={productionConnection.monthlyBudget} + stopBudgetThreshold={productionConnection.stopBudgetThreshold} + disconnectOnBudgetExceed={productionConnection.disconnectOnBudgetExceed} />
@@ -278,6 +282,10 @@ const LLMConnections: FC = () => { budgetStatus={llmConnection.budgetStatus} platform={llmConnection.llmPlatform} model={llmConnection.llmModel} + usedBudget={llmConnection.usedBudget} + monthlyBudget={llmConnection.monthlyBudget} + stopBudgetThreshold={llmConnection.stopBudgetThreshold} + disconnectOnBudgetExceed={llmConnection.disconnectOnBudgetExceed} /> ); })} diff --git a/GUI/translations/en/common.json b/GUI/translations/en/common.json index abba3913..a8d17cef 100644 --- a/GUI/translations/en/common.json +++ b/GUI/translations/en/common.json @@ -163,6 +163,7 @@ "testing": "Testing", "production": "Production" }, + "budgetUsage": "Budget usage", "budgetStatus": { "withinBudget": "Within budget", "overBudget": "Over budget", diff --git a/GUI/translations/et/common.json b/GUI/translations/et/common.json index 2cf5b3c4..615c97aa 100644 --- a/GUI/translations/et/common.json +++ b/GUI/translations/et/common.json @@ -163,6 +163,7 @@ "testing": "Testimine", "production": "Toodang" }, + "budgetUsage": "Eelarve kasutamine", "budgetStatus": { "withinBudget": "Eelarve piires", "overBudget": "Eelarve ületatud", From 1b28136be352e8c584b028ed7694a6eb193a83d5 Mon Sep 17 00:00:00 2001 From: erangi-ar <111747955+erangi-ar@users.noreply.github.com> Date: Tue, 3 Feb 2026 22:16:34 +0530 Subject: [PATCH 3/4] Refactor docker-compose-ec2.ym l file with new vault agent containers (#216) * updated docker compose ec2 * integrate streaming endpoint with test prodction connection page * formatted response with markdown * fe logic for the encryption * vault secret update after fixing issues * fixed formatting issue * integration with be * update cron manager vault script * tested integration of vault security update * fix security issues * creation success model changes * clean vite config generated files * fixed issue references are not sending with streming tokens * complete #192 and #206 bug fixes * production inference display logic change * change production inference display logic * fixed requested issue * Refactor Docker Compose configuration for vault agents and update CSP settings --------- Co-authored-by: Thiru Dinesh <56014038+Thirunayan22@users.noreply.github.com> Co-authored-by: Thiru Dinesh Co-authored-by: erangi-ar Co-authored-by: nuwangeek Co-authored-by: Charith Nuwan Bimsara <59943919+nuwangeek@users.noreply.github.com> From 45132f9ae782f929d70617dae0ea7617fdcac13f Mon Sep 17 00:00:00 2001 From: erangi-ar <111747955+erangi-ar@users.noreply.github.com> Date: Tue, 3 Feb 2026 22:20:57 +0530 Subject: [PATCH 4/4] UI bug fixes (#219) * updated docker compose ec2 * integrate streaming endpoint with test prodction connection page * formatted response with markdown * fe logic for the encryption * vault secret update after fixing issues * fixed formatting issue * integration with be * update cron manager vault script * tested integration of vault security update * fix security issues * creation success model changes * clean vite config generated files * fixed issue references are not sending with streming tokens * complete #192 and #206 bug fixes * production inference display logic change * change production inference display logic * fixed requested issue * Refactor Docker Compose configuration for vault agents and update CSP settings * Remove obsolete Vite configuration files and associated plugins * bug fixes --------- Co-authored-by: Thiru Dinesh <56014038+Thirunayan22@users.noreply.github.com> Co-authored-by: Thiru Dinesh Co-authored-by: erangi-ar Co-authored-by: nuwangeek Co-authored-by: Charith Nuwan Bimsara <59943919+nuwangeek@users.noreply.github.com> --- .dockerignore | 1 + .../rag-search/POST/llm-connections/edit.yml | 33 +++++- .../FormElements/FormSelect/index.tsx | 10 +- .../LLMConnectionForm/LLMConnectionForm.scss | 24 ++-- .../molecules/LLMConnectionForm/index.tsx | 108 +++++++++-------- GUI/src/pages/LLMConnections/index.tsx | 111 +++++++----------- GUI/src/pages/TestModel/index.tsx | 5 + GUI/src/store/index.ts | 35 +++++- GUI/translations/en/common.json | 14 ++- GUI/translations/et/common.json | 11 +- 10 files changed, 202 insertions(+), 150 deletions(-) diff --git a/.dockerignore b/.dockerignore index d25f099d..635b6fa2 100644 --- a/.dockerignore +++ b/.dockerignore @@ -36,6 +36,7 @@ venv/ ENV/ env.bak/ venv.bak/ +myenv/ # IDE .vscode/ diff --git a/DSL/Ruuter.private/rag-search/POST/llm-connections/edit.yml b/DSL/Ruuter.private/rag-search/POST/llm-connections/edit.yml index 84b375d1..6ae6f10e 100644 --- a/DSL/Ruuter.private/rag-search/POST/llm-connections/edit.yml +++ b/DSL/Ruuter.private/rag-search/POST/llm-connections/edit.yml @@ -119,9 +119,38 @@ check_connection_exists: validate_connection_exists: switch: - condition: "${existing_connection.response.body.length > 0}" - next: update_llm_connection + next: check_deployment_environment next: return_not_found +check_deployment_environment: + switch: + - condition: ${environment == "production" && existing_connection.response.body[0].environment == "testing"} + next: get_existing_production_connection + next: update_llm_connection + +get_existing_production_connection: + call: http.post + args: + url: "[#RAG_SEARCH_RESQL]/get-production-connection" + result: existing_production_result + next: update_existing_production_to_testing + +update_existing_production_to_testing: + switch: + - condition: ${existing_production_result.response.body && existing_production_result.response.body.length > 0} + next: update_production_connection + next: update_llm_connection + +update_production_connection: + call: http.post + args: + url: "[#RAG_SEARCH_RESQL]/update-llm-connection-environment" + body: + connection_id: ${existing_production_result.response.body[0].id} + environment: "testing" + result: update_result + next: update_llm_connection + update_llm_connection: call: http.post args: @@ -169,4 +198,4 @@ return_invalid_environment: return_unauthorized: status: 401 return: "error: unauthorized" - next: end + next: end \ No newline at end of file diff --git a/GUI/src/components/FormElements/FormSelect/index.tsx b/GUI/src/components/FormElements/FormSelect/index.tsx index e1187a49..5ad70cd5 100644 --- a/GUI/src/components/FormElements/FormSelect/index.tsx +++ b/GUI/src/components/FormElements/FormSelect/index.tsx @@ -87,8 +87,10 @@ const FormSelect = forwardRef( itemToString, selectedItem, onSelectedItemChange: ({ selectedItem: newSelectedItem }) => { - setSelectedItem(newSelectedItem ?? null); - if (onSelectionChange) onSelectionChange(newSelectedItem ?? null); + if (!disabled) { + setSelectedItem(newSelectedItem ?? null); + if (onSelectionChange) onSelectionChange(newSelectedItem ?? null); + } }, }); @@ -109,7 +111,7 @@ const FormSelect = forwardRef( className={`select__trigger ${ error ? `select__error` : `select__default` }`} - {...getToggleButtonProps()} + {...getToggleButtonProps({ disabled })} > {selectedItem?.label ?? placeholderValue} ( } ); -export default FormSelect; +export default FormSelect; \ No newline at end of file diff --git a/GUI/src/components/molecules/LLMConnectionForm/LLMConnectionForm.scss b/GUI/src/components/molecules/LLMConnectionForm/LLMConnectionForm.scss index c999f4a7..95813268 100644 --- a/GUI/src/components/molecules/LLMConnectionForm/LLMConnectionForm.scss +++ b/GUI/src/components/molecules/LLMConnectionForm/LLMConnectionForm.scss @@ -115,18 +115,16 @@ } .flex-grid { - flex-wrap: wrap; gap: 8px; justify-content: flex-end; button { - flex: 0 1 auto; - - min-width: 60px; - max-width: calc(50% - 4px); - padding: 8px 12px; - font-size: 13px; + flex: 1 1 auto; + padding: 6px 8px; + font-size: 12px; + display: inline-flex; + justify-content: center; } } } @@ -154,12 +152,14 @@ } .flex-grid { - flex-direction: column-reverse; + flex-direction: column; gap: 12px; button { - width: 100%; - min-width: unset; + flex: 0 1 auto; + display: inline-flex; + justify-content: center; + padding: 8px 20px; } } } @@ -172,9 +172,7 @@ button { flex: 1 1 auto; min-width: 70px; - max-width: 200px; - font-size: 14px; - padding: 8px 12px; + padding: 8px 16px; } } } diff --git a/GUI/src/components/molecules/LLMConnectionForm/index.tsx b/GUI/src/components/molecules/LLMConnectionForm/index.tsx index 3662097b..797dd277 100644 --- a/GUI/src/components/molecules/LLMConnectionForm/index.tsx +++ b/GUI/src/components/molecules/LLMConnectionForm/index.tsx @@ -200,7 +200,13 @@ const embeddingModelOptions = toOptions(embeddingModelsData); ( ( ( -

{t('llmConnectionForm.generic.llmApiKey.label') || 'LLM API Key'}

-

{t('llmConnectionForm.generic.llmApiKey.description') || 'The API key of the LLM model'}

- ( - - )} - /> - - ); + return null; } }; @@ -353,7 +352,13 @@ const embeddingModelOptions = toOptions(embeddingModelsData); ( ( ( -

{t('llmConnectionForm.generic.embeddingApiKey.label') || 'Embedding Model API Key'}

-

{t('llmConnectionForm.generic.embeddingApiKey.description') || 'API key of your embedding model'}

- ( - { - setEmbeddingApiKeyReplaceMode(false); - setValue('embeddingModelApiKey', ''); - }} - endButtonText={t('global.change') || "Change"} - {...field} - /> - )} - /> - - ); + return null; } }; @@ -523,13 +514,20 @@ const embeddingModelOptions = toOptions(embeddingModelsData); ( )} @@ -881,4 +879,4 @@ const embeddingModelOptions = toOptions(embeddingModelsData); ); }; -export default LLMConnectionForm; +export default LLMConnectionForm; \ No newline at end of file diff --git a/GUI/src/pages/LLMConnections/index.tsx b/GUI/src/pages/LLMConnections/index.tsx index 2c3542df..0af35601 100644 --- a/GUI/src/pages/LLMConnections/index.tsx +++ b/GUI/src/pages/LLMConnections/index.tsx @@ -15,19 +15,26 @@ import { platforms, trainingStatuses } from 'config/dataModelsConfig'; import LLMConnectionCard from 'components/molecules/LLMConnectionCard'; import { fetchLLMConnectionsPaginated, LLMConnectionFilters, LLMConnection, getProductionConnection, ProductionConnectionFilters } from 'services/llmConnections'; import { llmConnectionsQueryKeys } from 'utils/queryKeys'; +import { useToast } from 'hooks/useToast'; +import { ToastTypes } from 'enums/commonEnums'; +import useStore from 'store'; const LLMConnections: FC = () => { const { t } = useTranslation(); const navigate = useNavigate(); const [searchParams] = useSearchParams(); + const toast = useToast(); - const [pageIndex, setPageIndex] = useState(1); - const [filters, setFilters] = useState({ - pageNumber: 1, - pageSize: 10, - sortBy: 'created_at', - sortOrder: 'desc', - }); + // Use Zustand store for persistent filters + const { + llmConnectionFilters: filters, + llmConnectionPageIndex: pageIndex, + productionConnectionFilters, + setLLMConnectionFilters: setFilters, + setLLMConnectionPageIndex: setPageIndex, + setProductionConnectionFilters, + resetLLMConnectionFilters, + } = useStore(); // Fetch LLM connections using TanStack Query with new paginated endpoint const { data: connectionsResponse, isLoading: isModelDataLoading, error } = useQuery({ @@ -35,17 +42,10 @@ const LLMConnections: FC = () => { queryFn: () => fetchLLMConnectionsPaginated(filters), }); - // Fetch production connection separately with potential filters - const [productionFilters, setProductionFilters] = useState({ - sortBy: 'created_at', - sortOrder: 'desc', - llmPlatform: '', - llmModel: '', - }); - + // Fetch production connection separately with filters from store const { data: productionConnection, isLoading: isProductionLoading } = useQuery({ - queryKey: llmConnectionsQueryKeys.production(productionFilters), - queryFn: () => getProductionConnection(productionFilters), + queryKey: llmConnectionsQueryKeys.production(productionConnectionFilters), + queryFn: () => getProductionConnection(productionConnectionFilters), }); @@ -54,26 +54,35 @@ const LLMConnections: FC = () => { // Update filters when pageIndex changes useEffect(() => { - setFilters(prev => ({ ...prev, pageNumber: pageIndex })); - }, [pageIndex]); + setFilters({ ...filters, pageNumber: pageIndex }); + }, [pageIndex, setFilters]); - // Sync production filters with main filters on component mount + // Sync production filters with main filters useEffect(() => { - setProductionFilters(prev => ({ - ...prev, + setProductionConnectionFilters({ llmPlatform: filters.llmPlatform || '', llmModel: filters.llmModel || '', sortBy: filters.sortBy || 'created_at', sortOrder: filters.sortOrder || 'desc', - })); - }, [filters.llmPlatform, filters.llmModel, filters.sortBy, filters.sortOrder]); + }); + }, [filters.llmPlatform, filters.llmModel, filters.sortBy, filters.sortOrder, setProductionConnectionFilters]); + + // Show toast on error + useEffect(() => { + if (error) { + toast.open({ + type: ToastTypes.ERROR, + title: t('toast.error.title') || 'Error', + message: t('dataModels.errorLoadingConnections') || 'Error loading LLM connections', + }); + } + }, [error, toast, t]); const handleFilterChange = ( name: string, value: string | number | undefined | { name: string; id: string } ) => { let filterUpdate: Partial = {}; - let productionFilterUpdate: Partial = {}; if (name === 'sorting') { // Handle sorting format - no conversion needed, use snake_case directly @@ -84,32 +93,14 @@ const LLMConnections: FC = () => { sortBy: sortBy, sortOrder: sortOrder as 'asc' | 'desc' }; - - productionFilterUpdate = { - sortBy: sortBy, - sortOrder: sortOrder as 'asc' | 'desc' - }; } else { filterUpdate = { [name]: value }; - - // Update production filters for relevant fields - if (name === 'llmPlatform' || name === 'llmModel') { - productionFilterUpdate = { [name]: value as string }; - } } - setFilters((prevFilters) => ({ - ...prevFilters, + setFilters({ + ...filters, ...filterUpdate, - })); - - // Update production filters if relevant - if (Object.keys(productionFilterUpdate).length > 0) { - setProductionFilters((prevFilters) => ({ - ...prevFilters, - ...productionFilterUpdate, - })); - } + }); // Reset to first page when filters change if (name !== 'pageNumber') { @@ -219,24 +210,7 @@ const LLMConnections: FC = () => {
{ ); }; -export default LLMConnections; +export default LLMConnections; \ No newline at end of file diff --git a/GUI/src/pages/TestModel/index.tsx b/GUI/src/pages/TestModel/index.tsx index b6e66e76..c00c3ae1 100644 --- a/GUI/src/pages/TestModel/index.tsx +++ b/GUI/src/pages/TestModel/index.tsx @@ -75,6 +75,10 @@ const TestLLM: FC = () => { }; const handleChange = (key: string, value: string | number) => { + // Prevent changes while inference is loading + if (inferenceMutation.isLoading) { + return; + } setTestLLM((prev) => ({ ...prev, [key]: value, @@ -104,6 +108,7 @@ const TestLLM: FC = () => { }} value={testLLM?.connectionId === null ? t('testModels.connectionNotExist') || 'Connection does not exist' : undefined} defaultValue={testLLM?.connectionId ?? undefined} + disabled={inferenceMutation.isLoading} /> diff --git a/GUI/src/store/index.ts b/GUI/src/store/index.ts index 564d3215..c5fe37db 100644 --- a/GUI/src/store/index.ts +++ b/GUI/src/store/index.ts @@ -1,16 +1,49 @@ import { create } from 'zustand'; import { UserInfo } from 'types/userInfo'; +import { LLMConnectionFilters, ProductionConnectionFilters } from 'services/llmConnections'; interface StoreState { userInfo: UserInfo | null; userId: string; setUserInfo: (info: UserInfo) => void; + llmConnectionFilters: LLMConnectionFilters; + llmConnectionPageIndex: number; + productionConnectionFilters: ProductionConnectionFilters; + setLLMConnectionFilters: (filters: LLMConnectionFilters) => void; + setLLMConnectionPageIndex: (pageIndex: number) => void; + setProductionConnectionFilters: (filters: ProductionConnectionFilters) => void; + resetLLMConnectionFilters: () => void; } +const defaultLLMConnectionFilters: LLMConnectionFilters = { + pageNumber: 1, + pageSize: 10, + sortBy: 'created_at', + sortOrder: 'desc', +}; + +const defaultProductionConnectionFilters: ProductionConnectionFilters = { + sortBy: 'created_at', + sortOrder: 'desc', + llmPlatform: '', + llmModel: '', +}; + const useStore = create((set) => ({ userInfo: null, userId: '', setUserInfo: (data) => set({ userInfo: data, userId: data?.userIdCode || '' }), + llmConnectionFilters: defaultLLMConnectionFilters, + llmConnectionPageIndex: 1, + productionConnectionFilters: defaultProductionConnectionFilters, + setLLMConnectionFilters: (filters) => set({ llmConnectionFilters: filters }), + setLLMConnectionPageIndex: (pageIndex) => set({ llmConnectionPageIndex: pageIndex }), + setProductionConnectionFilters: (filters) => set({ productionConnectionFilters: filters }), + resetLLMConnectionFilters: () => set({ + llmConnectionFilters: defaultLLMConnectionFilters, + llmConnectionPageIndex: 1, + productionConnectionFilters: defaultProductionConnectionFilters, + }), })); -export default useStore; +export default useStore; \ No newline at end of file diff --git a/GUI/translations/en/common.json b/GUI/translations/en/common.json index a8d17cef..8c2cac8a 100644 --- a/GUI/translations/en/common.json +++ b/GUI/translations/en/common.json @@ -127,6 +127,7 @@ "settings": "Settings", "dataModels": "LLM connections", "noModels": "No LLM connections found", + "errorLoadingConnections": "Error loading LLM connections", "createModel": "Create LLM connection", "productionConnections": "Production LLM connection", "otherConnections": "Other LLM connections", @@ -300,6 +301,7 @@ }, "validationMessages": { "connectionNameRequired": "Connection name is required", + "connectionNameMaxLength": "Connection name must not exceed 100 characters", "llmPlatformRequired": "LLM platform is required", "llmModelRequired": "LLM model is required", "embeddingPlatformRequired": "Embedding model platform is required", @@ -328,7 +330,13 @@ "embeddingApiKeyRequired": "Embedding API key is required", "invalidUrl": "Please enter a valid URL starting with http:// or https://", "failedToLoadPlatforms": "Failed to load platforms", - "failedToLoadModels": "Failed to load models" + "failedToLoadModels": "Failed to load models", + "invalidAccessKey": "Access Key cannot contain spaces", + "invalidSecretKey": "Secret Key cannot contain spaces", + "invalidApiKey": "API Key cannot contain spaces", + "invalidEmbeddingAccessKey": "Embedding Access Key cannot contain spaces", + "invalidEmbeddingSecretKey": "Embedding Secret Key cannot contain spaces", + "invalidEmbeddingApiKey": "Embedding API Key cannot contain spaces" }, "buttons": { "deleteConnection": "Delete connection", @@ -348,7 +356,7 @@ "errorDialogMessage": "The connection couldn't be established either due to invalid API credentials or misconfiguration in the deployment platform", "goBackButton": "Go back", "replaceProductionDialogTitle": "Replace production connection", - "replaceProductionDialogMessage": "A production connection \"{connectionName}\" already exists.", + "replaceProductionDialogMessage": "A production connection \"{{connectionName}}\" already exists.", "replaceProductionDialogWarning": "Creating this new production connection will replace the current one. Are you sure you want to proceed?", "cancelButton": "Cancel", "confirmReplaceButton": "Yes, replace production connection" @@ -366,7 +374,7 @@ "goBackButton": "Go back", "confirmEnvironmentChangeTitle": "Confirm production environment change", "confirmEnvironmentChangeMessage": "You are about to change a production connection to testing environment.", - "confirmEnvironmentChangeWarning": "This will affect the current production setup. Are you sure you want to proceed?", + "confirmTestingToProductionEnvironmentChangeMessage": "You are about to change a testing connection to production environment.", "cancelButton": "Cancel", "confirmChangeButton": "Yes, change environment", "cannotDeleteProductionTitle": "Cannot delete production connection", diff --git a/GUI/translations/et/common.json b/GUI/translations/et/common.json index 615c97aa..1c093b6f 100644 --- a/GUI/translations/et/common.json +++ b/GUI/translations/et/common.json @@ -127,6 +127,7 @@ "settings": "Seaded", "dataModels": "Mudelite ühendused", "noModels": "Mudelite ühendusi ei leitud", + "errorLoadingConnections": "Viga mudeli ühenduste laadimisel", "createModel": "Loo mudeli ühendus", "productionConnections": "Mudel toodangukeskkonnas", "otherConnections": "Muud mudeli ühendused", @@ -300,6 +301,7 @@ }, "validationMessages": { "connectionNameRequired": "Ühenduse nimi on kohustuslik", + "connectionNameMaxLength": "Ühenduse nimi ei tohi ületada 100 märki", "llmPlatformRequired": "LLM platvorm on kohustuslik", "llmModelRequired": "LLM mudel on kohustuslik", "embeddingPlatformRequired": "Vektor-teisendusmudeli platvorm on kohustuslik", @@ -328,7 +330,13 @@ "embeddingApiKeyRequired": "Vektor-teisenduse API võti on kohustuslik", "invalidUrl": "Palun sisesta kehtiv URL, mis algab http:// või https://", "failedToLoadPlatforms": "Platvormide laadimine ebaõnnestus", - "failedToLoadModels": "Mudelite laadimine ebaõnnestus" + "failedToLoadModels": "Mudelite laadimine ebaõnnestus", + "invalidAccessKey": "Juurdepääsuvõti ei tohi sisaldada tühikuid", + "invalidSecretKey": "Salavõti ei tohi sisaldada tühikuid", + "invalidApiKey": "API võti ei tohi sisaldada tühikuid", + "invalidEmbeddingAccessKey": "Vektor-teisenduse juurdepääsuvõti ei tohi sisaldada tühikuid", + "invalidEmbeddingSecretKey": "Vektor-teisenduse salavõti ei tohi sisaldada tühikuid", + "invalidEmbeddingApiKey": "Vektor-teisenduse API võti ei tohi sisaldada tühikuid" }, "buttons": { "deleteConnection": "Kustuta ühendus", @@ -366,6 +374,7 @@ "goBackButton": "Mine tagasi", "confirmEnvironmentChangeTitle": "Kinnita toodangukeskkonna muutus", "confirmEnvironmentChangeMessage": "Oled toodanguühendust muutmas testimiskeskkonnaks.", + "confirmTestingToProductionEnvironmentChangeMessage": "Oled testimiskeskkonna ühendust muutmas toodangukeskkonnaks.", "confirmEnvironmentChangeWarning": "See mõjutab praegust toodanguseadistust. Kas oled kindel, et soovid jätkata?", "cancelButton": "Tühista", "confirmChangeButton": "Jah, muuda keskkonda",