diff --git a/dotCMS/src/main/java/com/dotcms/ai/api/CompletionsAPIImpl.java b/dotCMS/src/main/java/com/dotcms/ai/api/CompletionsAPIImpl.java index 4b33ca0bb2b3..7decdb7c9fa8 100644 --- a/dotCMS/src/main/java/com/dotcms/ai/api/CompletionsAPIImpl.java +++ b/dotCMS/src/main/java/com/dotcms/ai/api/CompletionsAPIImpl.java @@ -122,10 +122,10 @@ public void summarizeStream(final CompletionsForm summaryRequest, final OutputSt @Override public JSONObject raw(final JSONObject json, final String userId) { - AppConfig.debugLogger(this.getClass(), () -> "OpenAI request:" + json.toString(2)); + config.debugLogger(this.getClass(), () -> "OpenAI request:" + json.toString(2)); final String response = sendRequest(config, json, userId).getResponse(); - AppConfig.debugLogger(this.getClass(), () -> "OpenAI response:" + response); + config.debugLogger(this.getClass(), () -> "OpenAI response:" + response); return new JSONObject(response); } diff --git a/dotCMS/src/main/java/com/dotcms/ai/api/EmbeddingsAPIImpl.java b/dotCMS/src/main/java/com/dotcms/ai/api/EmbeddingsAPIImpl.java index a02d3e8fdf8f..36490040fa89 100644 --- a/dotCMS/src/main/java/com/dotcms/ai/api/EmbeddingsAPIImpl.java +++ b/dotCMS/src/main/java/com/dotcms/ai/api/EmbeddingsAPIImpl.java @@ -54,7 +54,6 @@ import java.util.Optional; import java.util.stream.Collectors; -import static com.dotcms.ai.app.AppConfig.debugLogger; import static com.liferay.util.StringPool.BLANK; /** @@ -335,7 +334,7 @@ public Tuple2> pullOrGenerateEmbeddings(final String conten .map(encoding -> encoding.encode(content)) .orElse(List.of()); if (tokens.isEmpty()) { - debugLogger(this.getClass(), () -> String.format("No tokens for content ID '%s' were encoded: %s", contentId, content)); + config.debugLogger(this.getClass(), () -> String.format("No tokens for content ID '%s' were encoded: %s", contentId, content)); return Tuple.of(0, List.of()); } @@ -432,15 +431,15 @@ private List sendTokensToOpenAI(final String contentId, final JSONObject json = new JSONObject(); json.put(AiKeys.MODEL, config.getEmbeddingsModel().getCurrentModel()); json.put(AiKeys.INPUT, tokens); - debugLogger(this.getClass(), () -> String.format("Content tokens for content ID '%s': %s", contentId, tokens)); + config.debugLogger(this.getClass(), () -> String.format("Content tokens for content ID '%s': %s", contentId, tokens)); final String responseString = AIProxyClient.get() .callToAI(JSONObjectAIRequest.quickEmbeddings(config, json, userId)) .getResponse(); - debugLogger(this.getClass(), () -> String.format("OpenAI Response for content ID '%s': %s", + config.debugLogger(this.getClass(), () -> String.format("OpenAI Response for content ID '%s': %s", contentId, responseString.replace("\n", BLANK))); final JSONObject jsonResponse = Try.of(() -> new JSONObject(responseString)).getOrElseThrow(e -> { Logger.error(this, "OpenAI Response String is not a valid JSON", e); - debugLogger(this.getClass(), () -> String.format("Invalid JSON Response: %s", responseString)); + config.debugLogger(this.getClass(), () -> String.format("Invalid JSON Response: %s", responseString)); return new DotCorruptedDataException(e); }); if (jsonResponse.containsKey(AiKeys.ERROR)) { diff --git a/dotCMS/src/main/java/com/dotcms/ai/api/EmbeddingsRunner.java b/dotCMS/src/main/java/com/dotcms/ai/api/EmbeddingsRunner.java index 91d3e9a01723..f8a054a4ece0 100644 --- a/dotCMS/src/main/java/com/dotcms/ai/api/EmbeddingsRunner.java +++ b/dotCMS/src/main/java/com/dotcms/ai/api/EmbeddingsRunner.java @@ -1,5 +1,6 @@ package com.dotcms.ai.api; +import com.dotcms.ai.app.AppConfig; import com.dotcms.ai.app.AppKeys; import com.dotcms.ai.app.ConfigService; import com.dotcms.ai.db.EmbeddingsDTO; @@ -17,7 +18,6 @@ import java.util.List; import java.util.Locale; -import static com.dotcms.ai.app.AppConfig.debugLogger; import static com.liferay.util.StringPool.SPACE; /** @@ -86,9 +86,9 @@ public void run() { } if (buffer.toString().split("\\s+").length > 0) { - debugLogger(this.getClass(), () -> String.format("Saving embeddings for contentlet ID '%s'", this.contentlet.getIdentifier())); + AppConfig.debugLogger(embeddingsAPI.config, this.getClass(), () -> String.format("Saving embeddings for contentlet ID '%s'", this.contentlet.getIdentifier())); this.saveEmbedding(buffer.toString()); - debugLogger(this.getClass(), () -> String.format("Embeddings for contentlet ID '%s' were saved", this.contentlet.getIdentifier())); + AppConfig.debugLogger(embeddingsAPI.config, this.getClass(), () -> String.format("Embeddings for contentlet ID '%s' were saved", this.contentlet.getIdentifier())); } } catch (final Exception e) { final String errorMsg = String.format("Failed to generate embeddings for contentlet ID " + diff --git a/dotCMS/src/main/java/com/dotcms/ai/app/AIModels.java b/dotCMS/src/main/java/com/dotcms/ai/app/AIModels.java index acead42c48d5..5e51779b1136 100644 --- a/dotCMS/src/main/java/com/dotcms/ai/app/AIModels.java +++ b/dotCMS/src/main/java/com/dotcms/ai/app/AIModels.java @@ -59,19 +59,19 @@ public static AIModels get() { return INSTANCE.get(); } - private static CircuitBreakerUrl.Response fetchOpenAIModels(final String apiKey) { + private static CircuitBreakerUrl.Response fetchOpenAIModels(final AppConfig appConfig) { final CircuitBreakerUrl.Response response = CircuitBreakerUrl.builder() .setMethod(CircuitBreakerUrl.Method.GET) .setUrl(AI_MODELS_API_URL) .setTimeout(AI_MODELS_FETCH_TIMEOUT) .setTryAgainAttempts(AI_MODELS_FETCH_ATTEMPTS) - .setHeaders(CircuitBreakerUrl.authHeaders("Bearer " + apiKey)) + .setHeaders(CircuitBreakerUrl.authHeaders("Bearer " + appConfig.getApiKey())) .setThrowWhenNot2xx(true) .build() .doResponse(OpenAIModels.class); if (!CircuitBreakerUrl.isSuccessResponse(response)) { - AppConfig.debugLogger( + appConfig.debugLogger( AIModels.class, () -> String.format( "Error fetching OpenAI supported models from [%s] (status code: [%d])", @@ -98,10 +98,11 @@ private AIModels() { * are already loaded, this method does nothing. It also maps model names to their * corresponding AIModel instances. * - * @param host the host for which the models are being loaded + * @param appConfig app config * @param loading the list of AI models to load */ - public void loadModels(final String host, final List loading) { + public void loadModels(final AppConfig appConfig, final List loading) { + final String host = appConfig.getHost(); final List> added = internalModels.putIfAbsent( host, loading.stream() @@ -112,7 +113,7 @@ public void loadModels(final String host, final List loading) { .forEach(model -> { final Tuple3 key = Tuple.of(host, model, aiModel.getType()); if (modelsByName.containsKey(key)) { - AppConfig.debugLogger( + appConfig.debugLogger( getClass(), () -> String.format( "Model [%s] already exists for host [%s], ignoring it", @@ -230,11 +231,11 @@ public Set getOrPullSupportedModels(final AppConfig appConfig) { } if (!appConfig.isEnabled()) { - AppConfig.debugLogger(getClass(), () -> "dotAI is not enabled, returning empty set of supported models"); + appConfig.debugLogger(getClass(), () -> "dotAI is not enabled, returning empty set of supported models"); return Set.of(); } - final CircuitBreakerUrl.Response response = fetchOpenAIModels(appConfig.getApiKey()); + final CircuitBreakerUrl.Response response = fetchOpenAIModels(appConfig); if (Objects.nonNull(response.getResponse().getError())) { throw new DotRuntimeException("Found error in AI response: " + response.getResponse().getError().getMessage()); } diff --git a/dotCMS/src/main/java/com/dotcms/ai/app/AppConfig.java b/dotCMS/src/main/java/com/dotcms/ai/app/AppConfig.java index 0ab3b83b4273..69cbad32958e 100644 --- a/dotCMS/src/main/java/com/dotcms/ai/app/AppConfig.java +++ b/dotCMS/src/main/java/com/dotcms/ai/app/AppConfig.java @@ -2,6 +2,7 @@ import com.dotcms.ai.domain.Model; import com.dotcms.security.apps.Secret; +import com.dotmarketing.util.Config; import com.dotmarketing.util.Logger; import com.dotmarketing.util.UtilMethods; import com.liferay.util.StringPool; @@ -12,9 +13,7 @@ import java.io.Serializable; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Optional; -import java.util.concurrent.atomic.AtomicReference; import java.util.function.Supplier; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -29,8 +28,7 @@ public class AppConfig implements Serializable { private static final String AI_API_URL_KEY = "AI_API_URL"; private static final String AI_IMAGE_API_URL_KEY = "AI_IMAGE_API_URL"; private static final String AI_EMBEDDINGS_API_URL_KEY = "AI_EMBEDDINGS_API_URL"; - private static final String SYSTEM_HOST = "System Host"; - private static final AtomicReference SYSTEM_HOST_CONFIG = new AtomicReference<>(); + private static final String AI_DEBUG_LOGGING_KEY = "AI_DEBUG_LOGGING"; public static final Pattern SPLITTER = Pattern.compile("\\s?,\\s?"); @@ -51,9 +49,6 @@ public class AppConfig implements Serializable { public AppConfig(final String host, final Map secrets) { this.host = host; - if (SYSTEM_HOST.equalsIgnoreCase(host)) { - setSystemHostConfig(this); - } final AIAppUtil aiAppUtil = AIAppUtil.get(); apiKey = aiAppUtil.discoverSecret(secrets, AppKeys.API_KEY); @@ -63,7 +58,7 @@ public AppConfig(final String host, final Map secrets) { if (!secrets.isEmpty() || isEnabled()) { AIModels.get().loadModels( - this.host, + this, List.of( aiAppUtil.createTextModel(secrets), aiAppUtil.createImageModel(secrets), @@ -85,35 +80,25 @@ public AppConfig(final String host, final Map secrets) { Logger.debug(this, this::toString); } - /** - * Retrieves the system host configuration. - * - * @return the system host configuration - */ - public static AppConfig getSystemHostConfig() { - if (Objects.isNull(SYSTEM_HOST_CONFIG.get())) { - setSystemHostConfig(ConfigService.INSTANCE.config()); - } - return SYSTEM_HOST_CONFIG.get(); - } - /** * Prints a specific error message to the log, based on the {@link AppKeys#DEBUG_LOGGING} * property instead of the usual Log4j configuration. * + * @param appConfig The {#link AppConfig} to be used when logging. * @param clazz The {@link Class} to log the message for. * @param message The {@link Supplier} with the message to log. */ - public static void debugLogger(final Class clazz, final Supplier message) { - if (getSystemHostConfig().getConfigBoolean(AppKeys.DEBUG_LOGGING)) { + public static void debugLogger(final AppConfig appConfig, final Class clazz, final Supplier message) { + if (appConfig == null) { + Logger.debug(clazz, message); + return; + } + if (appConfig.getConfigBoolean(AppKeys.DEBUG_LOGGING) + || Config.getBooleanProperty(AI_DEBUG_LOGGING_KEY, false)) { Logger.info(clazz, message.get()); } } - public static void setSystemHostConfig(final AppConfig systemHostConfig) { - AppConfig.SYSTEM_HOST_CONFIG.set(systemHostConfig); - } - /** * Retrieves the host. * @@ -318,6 +303,10 @@ public boolean isEnabled() { return Stream.of(apiUrl, apiImageUrl, apiEmbeddingsUrl, apiKey).allMatch(StringUtils::isNotBlank); } + public void debugLogger(final Class clazz, final Supplier message) { + debugLogger(this, clazz, message); + } + @Override public String toString() { return "AppConfig{\n" + diff --git a/dotCMS/src/main/java/com/dotcms/ai/client/AIModelFallbackStrategy.java b/dotCMS/src/main/java/com/dotcms/ai/client/AIModelFallbackStrategy.java index c763a50c43ed..9480faabfe3a 100644 --- a/dotCMS/src/main/java/com/dotcms/ai/client/AIModelFallbackStrategy.java +++ b/dotCMS/src/main/java/com/dotcms/ai/client/AIModelFallbackStrategy.java @@ -56,9 +56,9 @@ public class AIModelFallbackStrategy implements AIClientStrategy { */ @Override public AIResponseData applyStrategy(final AIClient client, - final AIResponseEvaluator handler, - final AIRequest request, - final OutputStream incoming) { + final AIResponseEvaluator handler, + final AIRequest request, + final OutputStream incoming) { final JSONObjectAIRequest jsonRequest = AIClient.useRequestOrThrow(request); final Tuple2 modelTuple = resolveModel(jsonRequest); @@ -95,9 +95,9 @@ private static boolean isSameAsFirst(final Model firstAttempt, final Model model return firstAttempt.equals(model); } - private static boolean isOperational(final Model model) { + private static boolean isOperational(final Model model, final AppConfig config) { if (!model.isOperational()) { - AppConfig.debugLogger( + config.debugLogger( AIModelFallbackStrategy.class, () -> String.format("Model [%s] is not operational. Skipping.", model.getName())); return false; @@ -117,7 +117,7 @@ private static AIResponseData doSend(final AIClient client, } private static void notifyFailure(final AIModel aiModel, final JSONObjectAIRequest request) { - AIAppValidator.get().validateModelsUsage(aiModel, request.getUserId()); + AIAppValidator.get().validateModelsUsage(aiModel, request); } private static void handleFailure(final Tuple2 modelTuple, @@ -127,7 +127,7 @@ private static void handleFailure(final Tuple2 modelTuple, final Model model = modelTuple._2; if (!responseData.getStatus().doesNeedToThrow()) { - AppConfig.debugLogger( + request.getConfig().debugLogger( AIModelFallbackStrategy.class, () -> String.format( "Model [%s] failed then setting its status to [%s].", @@ -138,7 +138,7 @@ private static void handleFailure(final Tuple2 modelTuple, if (model.getIndex() == aiModel.getModels().size() - 1) { aiModel.setCurrentModelIndex(AIModel.NOOP_INDEX); - AppConfig.debugLogger( + request.getConfig().debugLogger( AIModelFallbackStrategy.class, () -> String.format( "Model [%s] is the last one. Cannot fallback anymore.", @@ -167,7 +167,7 @@ private static AIResponseData sendRequest(final AIClient client, if (!responseData.isSuccess()) { if (responseData.getStatus().doesNeedToThrow()) { if (!modelTuple._1.isOperational()) { - AppConfig.debugLogger( + request.getConfig().debugLogger( AIModelFallbackStrategy.class, () -> String.format( "All models from type [%s] are not operational. Throwing exception.", @@ -181,11 +181,11 @@ private static AIResponseData sendRequest(final AIClient client, } if (responseData.isSuccess()) { - AppConfig.debugLogger( + request.getConfig().debugLogger( AIModelFallbackStrategy.class, () -> String.format("Model [%s] succeeded. No need to fallback.", modelTuple._2.getName())); } else { - logFailure(modelTuple, responseData); + logFailure(modelTuple, request, responseData); handleFailure(modelTuple, request, responseData); } @@ -198,18 +198,20 @@ private static AIResponseData sendRequest(final AIClient client, return responseData; } - private static void logFailure(final Tuple2 modelTuple, final AIResponseData responseData) { + private static void logFailure(final Tuple2 modelTuple, + final JSONObjectAIRequest request, + final AIResponseData responseData) { Optional .ofNullable(responseData.getResponse()) .ifPresentOrElse( - response -> AppConfig.debugLogger( + response -> request.getConfig().debugLogger( AIModelFallbackStrategy.class, () -> String.format( "Model [%s] failed with response:%s%sTrying next model.", modelTuple._2.getName(), System.lineSeparator(), response)), - () -> AppConfig.debugLogger( + () -> request.getConfig().debugLogger( AIModelFallbackStrategy.class, () -> String.format( "Model [%s] failed with error: [%s]. Trying next model.", @@ -229,7 +231,7 @@ private static AIResponseData runFallbacks(final AIClient client, final OutputStream output, final Tuple2 modelTuple) { for(final Model model : modelTuple._1.getModels()) { - if (isSameAsFirst(modelTuple._2, model) || !isOperational(model)) { + if (isSameAsFirst(modelTuple._2, model) || !isOperational(model, request.getConfig())) { continue; } diff --git a/dotCMS/src/main/java/com/dotcms/ai/client/openai/OpenAIClient.java b/dotCMS/src/main/java/com/dotcms/ai/client/openai/OpenAIClient.java index 499a219379cc..c80eae336afc 100644 --- a/dotCMS/src/main/java/com/dotcms/ai/client/openai/OpenAIClient.java +++ b/dotCMS/src/main/java/com/dotcms/ai/client/openai/OpenAIClient.java @@ -82,7 +82,7 @@ public void sendRequest(final AIRequest request, fin final JSONObjectAIRequest jsonRequest = AIClient.useRequestOrThrow(request); final AppConfig appConfig = jsonRequest.getConfig(); - AppConfig.debugLogger( + request.getConfig().debugLogger( OpenAIClient.class, () -> String.format( "Posting to [%s] with method [%s]%s with app config:%s%s the payload: %s", @@ -94,7 +94,7 @@ public void sendRequest(final AIRequest request, fin jsonRequest.payloadToString())); if (!appConfig.isEnabled()) { - AppConfig.debugLogger(OpenAIClient.class, () -> "App dotAI is not enabled and will not send request."); + request.getConfig().debugLogger(OpenAIClient.class, () -> "App dotAI is not enabled and will not send request."); throw new DotAIAppConfigDisabledException("App dotAI config without API urls or API key"); } @@ -106,7 +106,7 @@ public void sendRequest(final AIRequest request, fin final AIModel aiModel = modelTuple._1; if (!modelTuple._2.isOperational()) { - AppConfig.debugLogger( + request.getConfig().debugLogger( getClass(), () -> String.format("Resolved model [%s] is not operational, avoiding its usage", modelName)); throw new DotAIModelNotOperationalException(String.format("Model [%s] is not operational", modelName)); diff --git a/dotCMS/src/main/java/com/dotcms/ai/listener/EmbeddingContentListener.java b/dotCMS/src/main/java/com/dotcms/ai/listener/EmbeddingContentListener.java index 5c5a7b24d5ef..0c315c7541a6 100644 --- a/dotCMS/src/main/java/com/dotcms/ai/listener/EmbeddingContentListener.java +++ b/dotCMS/src/main/java/com/dotcms/ai/listener/EmbeddingContentListener.java @@ -83,7 +83,7 @@ private AppConfig getAppConfig(final String hostId) { final AppConfig appConfig = ConfigService.INSTANCE.config(host); if (!appConfig.isEnabled()) { - AppConfig.debugLogger( + appConfig.debugLogger( getClass(), () -> "dotAI is not enabled since no API urls or API key found in app config"); throw new DotAIAppConfigDisabledException("App dotAI config without API urls or API key"); diff --git a/dotCMS/src/main/java/com/dotcms/ai/util/ContentToStringUtil.java b/dotCMS/src/main/java/com/dotcms/ai/util/ContentToStringUtil.java index b08c7ed54f57..300d4fd74bd4 100644 --- a/dotCMS/src/main/java/com/dotcms/ai/util/ContentToStringUtil.java +++ b/dotCMS/src/main/java/com/dotcms/ai/util/ContentToStringUtil.java @@ -1,6 +1,7 @@ package com.dotcms.ai.util; +import com.dotcms.ai.app.AppConfig; import com.dotcms.ai.app.AppKeys; import com.dotcms.ai.app.ConfigService; import com.dotcms.contenttype.model.field.BinaryField; @@ -40,7 +41,6 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; -import static com.dotcms.ai.app.AppConfig.debugLogger; import static com.liferay.util.StringPool.BLANK; import static com.liferay.util.StringPool.SPACE; @@ -194,7 +194,8 @@ public List guessWhatFieldsToIndex(@NotNull Contentlet contentlet) { .filter(f -> f.dataType().equals(DataTypes.LONG_TEXT) ).collect(Collectors.toUnmodifiableList()); - debugLogger(this.getClass(), () -> String.format("Found %d indexable field(s) for Contentlet ID '%s': %s", + final AppConfig config = ConfigService.INSTANCE.config(); + config.debugLogger(this.getClass(), () -> String.format("Found %d indexable field(s) for Contentlet ID '%s': %s", indexableFields.size(), contentlet.getIdentifier(), indexableFields.stream().map(Field::variable).collect(Collectors.toSet()))); return indexableFields; } @@ -253,10 +254,10 @@ public Optional parseFields(@NotNull final Contentlet contentlet, @N parseField(contentlet, field) .ifPresent(s -> builder.append(s).append(SPACE)); } - final int embeddingsMinimumLength = - ConfigService.INSTANCE.config().getConfigInteger(AppKeys.EMBEDDINGS_MINIMUM_TEXT_LENGTH_TO_INDEX); + final AppConfig config = ConfigService.INSTANCE.config(); + final int embeddingsMinimumLength = config.getConfigInteger(AppKeys.EMBEDDINGS_MINIMUM_TEXT_LENGTH_TO_INDEX); if (builder.length() < embeddingsMinimumLength) { - debugLogger(this.getClass(), () -> String.format("Parseable fields for Contentlet ID " + + config.debugLogger(this.getClass(), () -> String.format("Parseable fields for Contentlet ID " + "'%s' don't meet the minimum length requirement of %d characters. Skipping indexing.", contentlet.getIdentifier(), embeddingsMinimumLength)); return Optional.empty(); diff --git a/dotCMS/src/main/java/com/dotcms/ai/util/EncodingUtil.java b/dotCMS/src/main/java/com/dotcms/ai/util/EncodingUtil.java index 9aed5869213a..a427cfa76955 100644 --- a/dotCMS/src/main/java/com/dotcms/ai/util/EncodingUtil.java +++ b/dotCMS/src/main/java/com/dotcms/ai/util/EncodingUtil.java @@ -37,7 +37,7 @@ public Optional getEncoding(final AppConfig appConfig, final AIModelTy final Model currentModel = aiModel.getCurrent(); if (Objects.isNull(currentModel)) { - AppConfig.debugLogger( + appConfig.debugLogger( getClass(), () -> String.format( "No current model found for type [%s], meaning the are all are exhausted", @@ -47,16 +47,17 @@ public Optional getEncoding(final AppConfig appConfig, final AIModelTy return registry .getEncodingForModel(currentModel.getName()) - .or(() -> modelFallback(aiModel, currentModel)); + .or(() -> modelFallback(appConfig, aiModel, currentModel)); } public Optional getEncoding() { return getEncoding(ConfigService.INSTANCE.config(), AIModelType.EMBEDDINGS); } - private Optional modelFallback(final AIModel aiModel, + private Optional modelFallback(final AppConfig appConfig, + final AIModel aiModel, final Model currentModel) { - AppConfig.debugLogger( + appConfig.debugLogger( getClass(), () -> String.format( "Model [%s] is not suitable for encoding, marking it as invalid and falling back to other models", @@ -74,7 +75,7 @@ private Optional modelFallback(final AIModel aiModel, final Optional encoding = registry.getEncodingForModel(model.getName()); if (encoding.isEmpty()) { model.setStatus(ModelStatus.INVALID); - AppConfig.debugLogger( + appConfig.debugLogger( getClass(), () -> String.format( "Model [%s] is not suitable for encoding, marking as invalid", @@ -83,7 +84,7 @@ private Optional modelFallback(final AIModel aiModel, } aiModel.setCurrentModelIndex(model.getIndex()); - AppConfig.debugLogger( + appConfig.debugLogger( getClass(), () -> "Model [" + model.getName() + "] found, setting as current model"); return encoding.get(); diff --git a/dotCMS/src/main/java/com/dotcms/ai/validator/AIAppValidator.java b/dotCMS/src/main/java/com/dotcms/ai/validator/AIAppValidator.java index 7c51f40c883e..14299eac93c9 100644 --- a/dotCMS/src/main/java/com/dotcms/ai/validator/AIAppValidator.java +++ b/dotCMS/src/main/java/com/dotcms/ai/validator/AIAppValidator.java @@ -3,6 +3,7 @@ import com.dotcms.ai.app.AIModel; import com.dotcms.ai.app.AIModels; import com.dotcms.ai.app.AppConfig; +import com.dotcms.ai.client.JSONObjectAIRequest; import com.dotcms.ai.domain.Model; import com.dotcms.api.system.event.message.MessageSeverity; import com.dotcms.api.system.event.message.SystemMessageEventUtil; @@ -52,7 +53,7 @@ public static AIAppValidator get() { */ public void validateAIConfig(final AppConfig appConfig, final String userId) { if (Objects.isNull(userId)) { - AppConfig.debugLogger(getClass(), () -> "User Id is null, skipping AI configuration validation"); + appConfig.debugLogger(getClass(), () -> "User Id is null, skipping AI configuration validation"); return; } @@ -89,11 +90,11 @@ public void validateAIConfig(final AppConfig appConfig, final String userId) { * If any exhausted or invalid models are found, a warning message is pushed to the user. * * @param aiModel the AI model - * @param userId the user ID + * @param request the ai request */ - public void validateModelsUsage(final AIModel aiModel, final String userId) { - if (Objects.isNull(userId)) { - AppConfig.debugLogger(getClass(), () -> "User Id is null, skipping AI models usage validation"); + public void validateModelsUsage(final AIModel aiModel, final JSONObjectAIRequest request) { + if (Objects.isNull(request.getUserId())) { + request.getConfig().debugLogger(getClass(), () -> "User Id is null, skipping AI models usage validation"); return; } @@ -114,7 +115,7 @@ public void validateModelsUsage(final AIModel aiModel, final String userId) { .setLife(DateUtil.SEVEN_SECOND_MILLIS) .create(); - systemMessageEventUtil.pushMessage(systemMessage, Collections.singletonList(userId)); + systemMessageEventUtil.pushMessage(systemMessage, Collections.singletonList(request.getUserId())); } @VisibleForTesting diff --git a/dotcms-integration/src/test/java/com/dotcms/ai/validator/AIAppValidatorTest.java b/dotcms-integration/src/test/java/com/dotcms/ai/validator/AIAppValidatorTest.java index 8c7379ab6313..a429c08c415f 100644 --- a/dotcms-integration/src/test/java/com/dotcms/ai/validator/AIAppValidatorTest.java +++ b/dotcms-integration/src/test/java/com/dotcms/ai/validator/AIAppValidatorTest.java @@ -3,6 +3,7 @@ import com.dotcms.ai.AiTest; import com.dotcms.ai.app.AppConfig; import com.dotcms.ai.app.ConfigService; +import com.dotcms.ai.client.JSONObjectAIRequest; import com.dotcms.api.system.event.message.SystemMessageEventUtil; import com.dotcms.api.system.event.message.builder.SystemMessage; import com.dotcms.datagen.SiteDataGen; @@ -101,7 +102,8 @@ public void test_validateModelsUsage() throws Exception { AiTest.aiAppSecrets(host, invalidModels, "dall-e-3", "text-embedding-ada-002"); appConfig = ConfigService.INSTANCE.config(host); - validator.validateModelsUsage(appConfig.getModel(), user.getUserId()); + final JSONObjectAIRequest request = JSONObjectAIRequest.builder().withUserId("jon.snow").build(); + validator.validateModelsUsage(appConfig.getModel(), request); verify(systemMessageEventUtil, atLeast(2)) .pushMessage(any(SystemMessage.class), anyList()); diff --git a/justfile b/justfile index 378842ddb929..de5b8380e59a 100644 --- a/justfile +++ b/justfile @@ -39,6 +39,9 @@ build-test: build-quick: ./mvnw -DskipTests install +build-quicker: + ./mvnw -pl :dotcms-core -DskipTests install + # Builds the project for production, skipping tests build-prod: ./mvnw -DskipTests clean install -Pprod @@ -85,7 +88,7 @@ dev-stop: dev-clean-volumes: ./mvnw -pl :dotcms-core -Pdocker-clean-volumes -# Starts the dotCMS application in a Tomcat container on port 8080, running in the foreground +# Starts the dotCMS application in a Tomcat container on port 8087, running in the foreground dev-tomcat-run port="8087": ./mvnw -pl :dotcms-core -Ptomcat-run -Pdebug -Dservlet.port={{ port }}