diff --git a/config/ai_config.env.template b/config/ai_config.env.template index 5c172a85..6778b9f9 100644 --- a/config/ai_config.env.template +++ b/config/ai_config.env.template @@ -1,5 +1,8 @@ # Copy this template to config/ai_config.env and adjust the following variables: +LOCAL_AI_SCHEME="http" LOCAL_AI_IP="192.168.111.1" +LOCAL_AI_PORT="8080" +LOCAL_AI_API_ENDPOINT="/v1" # EMBA checks if this model is available for chatting # if this model is not available EMBA can't proceed LOCAL_AI_MODEL="Qwen2.5-Coder-7B-Instruct-GGUF" diff --git a/helpers/helpers_emba_defaults.sh b/helpers/helpers_emba_defaults.sh index 21c6f67a..55e938f8 100755 --- a/helpers/helpers_emba_defaults.sh +++ b/helpers/helpers_emba_defaults.sh @@ -94,6 +94,9 @@ set_defaults() { # Todo: remove option 1 and 2 and GPT_QUESTION usage # Todo: rename all GPT to AI areas export AI_OPTION=0 # 0 -> off, 1-> unpaid plan, 2 -> no rate-limit, 3 -> LocalAI Server + export LOCAL_AI_SCHEME="http" + export LOCAL_AI_PORT="8080" + export LOCAL_AI_API_ENDPOINT="/v1" export GPT_QUESTION="" export MINIMUM_GPT_PRIO=1 # everything above this value gets checked diff --git a/modules/Q03_localai_connector.sh b/modules/Q03_localai_connector.sh index 0cef0f1a..85cb5a7a 100755 --- a/modules/Q03_localai_connector.sh +++ b/modules/Q03_localai_connector.sh @@ -22,6 +22,14 @@ : "${DEBUG:=0}" export DEBUG +local_ai_api_url() { + local lAPI_PATH="${1#/}" + local lAPI_ENDPOINT="/${LOCAL_AI_API_ENDPOINT#/}" + lAPI_ENDPOINT="${lAPI_ENDPOINT%/}" + + printf '%s://%s:%s%s/%s' "${LOCAL_AI_SCHEME}" "${LOCAL_AI_IP}" "${LOCAL_AI_PORT}" "${lAPI_ENDPOINT}" "${lAPI_PATH}" +} + Q03_localai_connector() { module_log_init "${FUNCNAME[0]}" export AI_RESULT_CNT=0 @@ -289,7 +297,7 @@ ask_localai() { '{model: $model, messages: [{role: "user", content: $prompt}], temperature: 0.2}') lstart_time=$(date +%s) - lHTTP_CODE=$(curl --connect-timeout 10 --max-time 600 -s http://"${LOCAL_AI_IP}":8080/v1/chat/completions \ + lHTTP_CODE=$(curl --connect-timeout 10 --max-time 600 -s "$(local_ai_api_url "chat/completions")" \ -H "Content-Type: application/json" \ -d "${lJSON_PAYLOAD}" -o "${lAI_LOG_FILE}.json" --write-out "%{http_code}" || true) @@ -416,7 +424,7 @@ identify_ai_model() { local lMODEL_LOCALAI="" local lCNT=1 while [[ -z "${lMODEL_LOCALAI}" ]]; do - lMODEL_LOCALAI=$(curl --connect-timeout 10 --max-time 30 http://"${LOCAL_AI_IP}":8080/v1/models 2>/dev/null | jq -r .data[].id || true) + lMODEL_LOCALAI=$(curl --connect-timeout 10 --max-time 30 "$(local_ai_api_url "models")" 2>/dev/null | jq -r .data[].id || true) [[ -n "${lMODEL_LOCALAI}" ]] && break sleep 5 lCNT=$((lCNT + 1)) diff --git a/tests/helpers/defaults_helpers.bats b/tests/helpers/defaults_helpers.bats index a4361677..9b49e8e4 100644 --- a/tests/helpers/defaults_helpers.bats +++ b/tests/helpers/defaults_helpers.bats @@ -75,6 +75,13 @@ teardown() { [ "${SHELLCHECK}" -eq 1 ] } +@test "set_defaults sets local connector endpoint defaults" { + set_defaults + [ "${LOCAL_AI_SCHEME}" = "http" ] + [ "${LOCAL_AI_PORT}" = "8080" ] + [ "${LOCAL_AI_API_ENDPOINT}" = "/v1" ] +} + @test "set_defaults sets SBOM_MAX_FILE_LOG" { set_defaults [ "${SBOM_MAX_FILE_LOG}" -eq 200 ] diff --git a/tests/modules/localai_connector.bats b/tests/modules/localai_connector.bats new file mode 100644 index 00000000..d2be6ca3 --- /dev/null +++ b/tests/modules/localai_connector.bats @@ -0,0 +1,51 @@ +# EMBA - EMBEDDED LINUX ANALYZER +# +# Copyright 2026-2026 Siemens Energy AG +# +# EMBA comes with ABSOLUTELY NO WARRANTY. This is free software, and you are +# welcome to redistribute it under the terms of the GNU General Public License. +# See LICENSE file for usage of this software. +# +# EMBA is licensed under GPLv3 +# SPDX-License-Identifier: GPL-3.0-only + +load ../setup.bash + +setup() { + setup_emba_test_env + # shellcheck source=helpers/helpers_emba_defaults.sh + source "${HELP_DIR}/helpers_emba_defaults.sh" + # shellcheck source=modules/Q03_localai_connector.sh + source "${MOD_DIR}/Q03_localai_connector.sh" + set_defaults + export LOCAL_AI_IP="192.0.2.1" +} + +teardown() { + teardown_emba_test_env +} + +@test "local_ai_api_url preserves the default endpoint" { + result="$(local_ai_api_url "models")" + [ "${result}" = "http://192.0.2.1:8080/v1/models" ] +} + +@test "configuration template preserves the scan profile AI mode" { + export AI_OPTION=2 + # shellcheck source=config/ai_config.env.template + source "${CONFIG_DIR}/ai_config.env.template" + + [ "${AI_OPTION}" -eq 2 ] + result="$(local_ai_api_url "models")" + [ "${result}" = "http://192.168.111.1:8080/v1/models" ] +} + +@test "local_ai_api_url uses configured endpoint components" { + result="$(LOCAL_AI_SCHEME="https" LOCAL_AI_PORT="9443" LOCAL_AI_API_ENDPOINT="/custom" local_ai_api_url "chat/completions")" + [ "${result}" = "https://192.0.2.1:9443/custom/chat/completions" ] +} + +@test "local_ai_api_url normalizes endpoint separators" { + result="$(LOCAL_AI_API_ENDPOINT="custom/" local_ai_api_url "/models")" + [ "${result}" = "http://192.0.2.1:8080/custom/models" ] +}