Skip to content

Commit

Permalink
#29281: Applying feedback and fixing dirty models bug
Browse files Browse the repository at this point in the history
  • Loading branch information
victoralfaro-dotcms committed Aug 5, 2024
1 parent 152f7e6 commit e608069
Show file tree
Hide file tree
Showing 21 changed files with 321 additions and 285 deletions.
31 changes: 28 additions & 3 deletions core-web/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -22128,7 +22128,7 @@ string-length@^4.0.1:
char-regex "^1.0.2"
strip-ansi "^6.0.0"

"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
"string-width-cjs@npm:string-width@^4.2.0":
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
Expand All @@ -22146,6 +22146,15 @@ string-width@^1.0.1:
is-fullwidth-code-point "^1.0.0"
strip-ansi "^3.0.0"

"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"

string-width@^2.0.0, string-width@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
Expand Down Expand Up @@ -22242,7 +22251,7 @@ stringify-package@^1.0.0, stringify-package@^1.0.1:
resolved "https://registry.yarnpkg.com/stringify-package/-/stringify-package-1.0.1.tgz#e5aa3643e7f74d0f28628b72f3dad5cecfc3ba85"
integrity sha512-sa4DUQsYciMP1xhKWGuFM04fB0LG/9DlluZoSVywUMRNvzid6XucHK0/90xGxRoHrAaROrcHK1aPKaijCtSrhg==

"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
Expand Down Expand Up @@ -22270,6 +22279,13 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0:
dependencies:
ansi-regex "^4.1.0"

strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"

strip-ansi@^7.0.1:
version "7.1.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
Expand Down Expand Up @@ -24269,7 +24285,7 @@ worker-farm@^1.6.0, worker-farm@^1.7.0:
dependencies:
errno "~0.1.7"

"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
Expand Down Expand Up @@ -24304,6 +24320,15 @@ wrap-ansi@^6.2.0:
string-width "^4.1.0"
strip-ansi "^6.0.0"

wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"

wrap-ansi@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ private String getTextPrompt(final String prompt, final String supportingContent
}

private int countTokens(final String testString) {
return EncodingUtil.REGISTRY
return EncodingUtil.get().registry
.getEncodingForModel(config.get().getModel().getCurrentModel())
.map(enc -> enc.countTokens(testString))
.orElseThrow(() -> new DotRuntimeException("Encoder not found"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,10 @@ public Tuple2<Integer, List<Float>> pullOrGenerateEmbeddings(final String conten
return cachedEmbeddings;
}

final List<Integer> tokens = EncodingUtil.ENCODING.get().encode(content);
final List<Integer> tokens = EncodingUtil.get()
.getEncoding()
.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));
return Tuple.of(0, List.of());
Expand Down
5 changes: 4 additions & 1 deletion dotCMS/src/main/java/com/dotcms/ai/api/EmbeddingsRunner.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,10 @@ public void run() {
int totalTokens = 0;
for (int end = iterator.next(); end != BreakIterator.DONE; start = end, end = iterator.next()) {
final String sentence = cleanContent.substring(start, end);
final int tokenCount = EncodingUtil.ENCODING.get().countTokens(sentence);
final int tokenCount = EncodingUtil.get()
.getEncoding()
.map(encoding -> encoding.countTokens(sentence))
.orElse(0);
totalTokens += tokenCount;

if (totalTokens < splitAtTokens) {
Expand Down
2 changes: 1 addition & 1 deletion dotCMS/src/main/java/com/dotcms/ai/app/AIAppUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ public String discoverSecret(final Map<String, Secret> secrets, final AppKeys ke
* @return the list of split secret values
*/
public List<String> splitDiscoveredSecret(final Map<String, Secret> secrets, final AppKeys key) {
return Arrays.stream(discoverSecret(secrets, key).split(","))
return Arrays.stream(Optional.ofNullable(discoverSecret(secrets, key)).orElse(StringPool.BLANK).split(","))
.map(String::trim)
.map(String::toLowerCase)
.collect(Collectors.toList());
Expand Down
68 changes: 32 additions & 36 deletions dotCMS/src/main/java/com/dotcms/ai/app/AIModels.java
Original file line number Diff line number Diff line change
Expand Up @@ -131,18 +131,16 @@ public Optional<AIModel> findModel(final String host, final AIModelType type) {
*/
public void resetModels(final Host host) {
final String hostKey = host.getHostname();
synchronized (AIModels.class) {
Optional.ofNullable(internalModels.get(hostKey)).ifPresent(models -> {
models.clear();
internalModels.remove(hostKey);
});
modelsByName.keySet()
.stream()
.filter(key -> key._1.equals(hostKey))
.collect(Collectors.toSet())
.forEach(modelsByName::remove);
ConfigService.INSTANCE.config(host);
}
Optional.ofNullable(internalModels.get(hostKey)).ifPresent(models -> {
models.clear();
internalModels.remove(hostKey);
});
modelsByName.keySet()
.stream()
.filter(key -> key._1.equals(hostKey))
.collect(Collectors.toSet())
.forEach(modelsByName::remove);
ConfigService.INSTANCE.config(host);
}

/**
Expand All @@ -152,31 +150,29 @@ public void resetModels(final Host host) {
* @return a list of supported model names
*/
public List<String> getOrPullSupportedModels() {
synchronized (supportedModelsCache) {
final List<String> cached = supportedModelsCache.getIfPresent(SUPPORTED_MODELS_KEY);
if (CollectionUtils.isNotEmpty(cached)) {
return cached;
}

final AppConfig appConfig = appConfigSupplier.get();
if (!appConfig.isEnabled()) {
Logger.debug(this, "OpenAI is not enabled, returning empty list of supported models");
return List.of();
}

final List<String> supported = Try.of(() ->
fetchOpenAIModels(appConfig)
.getResponse()
.getData()
.stream()
.map(OpenAIModel::getId)
.map(String::toLowerCase)
.collect(Collectors.toList()))
.getOrElse(Optional.ofNullable(cached).orElse(List.of()));
supportedModelsCache.put(SUPPORTED_MODELS_KEY, supported);

return supported;
final List<String> cached = supportedModelsCache.getIfPresent(SUPPORTED_MODELS_KEY);
if (CollectionUtils.isNotEmpty(cached)) {
return cached;
}

final AppConfig appConfig = appConfigSupplier.get();
if (!appConfig.isEnabled()) {
Logger.debug(this, "OpenAI is not enabled, returning empty list of supported models");
return List.of();
}

final List<String> supported = Try.of(() ->
fetchOpenAIModels(appConfig)
.getResponse()
.getData()
.stream()
.map(OpenAIModel::getId)
.map(String::toLowerCase)
.collect(Collectors.toList()))
.getOrElse(Optional.ofNullable(cached).orElse(List.of()));
supportedModelsCache.put(SUPPORTED_MODELS_KEY, supported);

return supported;
}

/**
Expand Down
33 changes: 16 additions & 17 deletions dotCMS/src/main/java/com/dotcms/ai/app/AppConfig.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.dotcms.ai.app;

import com.dotcms.ai.util.OpenAIRequest;
import com.dotcms.security.apps.Secret;
import com.dotmarketing.exception.DotRuntimeException;
import com.dotmarketing.util.Config;
Expand Down Expand Up @@ -44,7 +43,6 @@ public AppConfig(final String host, final Map<String, Secret> secrets) {
this.host = host;

final AIAppUtil aiAppUtil = AIAppUtil.get();
apiKey = aiAppUtil.discoverEnvSecret(secrets, AppKeys.API_KEY);

AIModels.get().loadModels(
this.host,
Expand All @@ -53,13 +51,14 @@ public AppConfig(final String host, final Map<String, Secret> secrets) {
aiAppUtil.createImageModel(secrets),
aiAppUtil.createEmbeddingsModel(secrets)));

model = resolveModel(AIModelType.TEXT);
imageModel = resolveModel(AIModelType.IMAGE);
embeddingsModel = resolveModel(AIModelType.EMBEDDINGS);

apiUrl = aiAppUtil.discoverEnvSecret(secrets, AppKeys.API_URL);
apiImageUrl = aiAppUtil.discoverEnvSecret(secrets, AppKeys.API_IMAGE_URL);
apiEmbeddingsUrl = discoverEmbeddingsApiUrl(secrets);
apiKey = aiAppUtil.discoverEnvSecret(secrets, AppKeys.API_KEY);

model = resolveModel(AIModelType.TEXT);
imageModel = resolveModel(AIModelType.IMAGE);
embeddingsModel = resolveModel(AIModelType.EMBEDDINGS);

rolePrompt = aiAppUtil.discoverSecret(secrets, AppKeys.ROLE_PROMPT);
textPrompt = aiAppUtil.discoverSecret(secrets, AppKeys.TEXT_PROMPT);
Expand All @@ -73,13 +72,13 @@ public AppConfig(final String host, final Map<String, Secret> secrets) {
Logger.debug(getClass(), () -> "apiUrl: " + apiUrl);
Logger.debug(getClass(), () -> "apiImageUrl: " + apiImageUrl);
Logger.debug(getClass(), () -> "embeddingsUrl: " + apiEmbeddingsUrl);
Logger.debug(getClass(), () -> "model: " + model);
Logger.debug(getClass(), () -> "imageModel: " + imageModel);
Logger.debug(getClass(), () -> "embeddingsModel: " + embeddingsModel);
Logger.debug(getClass(), () -> "rolePrompt: " + rolePrompt);
Logger.debug(getClass(), () -> "textPrompt: " + textPrompt);
Logger.debug(getClass(), () -> "model: " + model);
Logger.debug(getClass(), () -> "imagePrompt: " + imagePrompt);
Logger.debug(getClass(), () -> "imageModel: " + imageModel);
Logger.debug(getClass(), () -> "imageSize: " + imageSize);
Logger.debug(getClass(), () -> "embeddingsModel: " + embeddingsModel);
Logger.debug(getClass(), () -> "listerIndexer: " + listenerIndexer);
}

Expand Down Expand Up @@ -263,24 +262,24 @@ public AIModel resolveModel(final AIModelType type) {
* @param modelName the name of the model to find
*/
public AIModel resolveModelOrThrow(final String modelName) {
final AIModel model = AIModels.get()
final AIModel aiModel = AIModels.get()
.findModel(host, modelName)
.orElseThrow(() -> {
final String supported = String.join(", ", AIModels.get().getOrPullSupportedModels());
return new DotRuntimeException(
"Unable to find model: [" + modelName + "]. Only [" + supported + "] are supported ");
});

if (!model.isOperational()) {
Logger.debug(
OpenAIRequest.class,
String.format(
if (!aiModel.isOperational()) {
debugLogger(
AppConfig.class,
() -> String.format(
"Resolved model [%s] is not operational, avoiding its usage",
model.getCurrentModel()));
throw new DotRuntimeException(String.format("Model [%s] is not operational", model.getCurrentModel()));
aiModel.getCurrentModel()));
throw new DotRuntimeException(String.format("Model [%s] is not operational", aiModel.getCurrentModel()));
}

return model;
return aiModel;
}

/**
Expand Down
24 changes: 12 additions & 12 deletions dotCMS/src/main/java/com/dotcms/ai/app/AppKeys.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,20 @@ public enum AppKeys {
IMAGE_PROMPT("imagePrompt", "Use 16:9 aspect ratio."),
IMAGE_SIZE("imageSize", "1024x1024"),
TEXT_MODEL_NAMES("textModelNames", "gpt-3.5-turbo-16k"),
TEXT_MODEL_TOKENS_PER_MINUTE("textModelTokensPerMinute", "1000"),
TEXT_MODEL_API_PER_MINUTE("textModelApiPerMinute", "1000"),
TEXT_MODEL_MAX_TOKENS("textModelMaxTokens", "1000"),
TEXT_MODEL_TOKENS_PER_MINUTE("textModelTokensPerMinute", "180000"),
TEXT_MODEL_API_PER_MINUTE("textModelApiPerMinute", "3500"),
TEXT_MODEL_MAX_TOKENS("textModelMaxTokens", "16384"),
TEXT_MODEL_COMPLETION("textModelCompletion", "true"),
IMAGE_MODEL_NAMES("imageModelNames", "dall-e-3"),
IMAGE_MODEL_TOKENS_PER_MINUTE("imageModelTokensPerMinute", "1000"),
IMAGE_MODEL_API_PER_MINUTE("imageModelApiPerMinute", "1000"),
IMAGE_MODEL_MAX_TOKENS("imageModelMaxTokens", "1000"),
IMAGE_MODEL_COMPLETION("imageModelCompletion", "true"),
IMAGE_MODEL_TOKENS_PER_MINUTE("imageModelTokensPerMinute", "0"),
IMAGE_MODEL_API_PER_MINUTE("imageModelApiPerMinute", "50"),
IMAGE_MODEL_MAX_TOKENS("imageModelMaxTokens", "0"),
IMAGE_MODEL_COMPLETION("imageModelCompletion", "false"),
EMBEDDINGS_MODEL_NAMES("embeddingsModelNames", "text-embedding-ada-002"),
EMBEDDINGS_MODEL_TOKENS_PER_MINUTE("embeddingsModelTokensPerMinute", "1000"),
EMBEDDINGS_MODEL_API_PER_MINUTE("embeddingsModelApiPerMinute", "1000"),
EMBEDDINGS_MODEL_MAX_TOKENS("embeddingsModelMaxTokens", "1000"),
EMBEDDINGS_MODEL_COMPLETION("embeddingsModelCompletion", "true"),
EMBEDDINGS_MODEL_TOKENS_PER_MINUTE("embeddingsModelTokensPerMinute", "1000000"),
EMBEDDINGS_MODEL_API_PER_MINUTE("embeddingsModelApiPerMinute", "3000"),
EMBEDDINGS_MODEL_MAX_TOKENS("embeddingsModelMaxTokens", "8191"),
EMBEDDINGS_MODEL_COMPLETION("embeddingsModelCompletion", "false"),
EMBEDDINGS_SPLIT_AT_TOKENS("com.dotcms.ai.embeddings.split.at.tokens", "512"),
EMBEDDINGS_MINIMUM_TEXT_LENGTH_TO_INDEX("com.dotcms.ai.embeddings.minimum.text.length", "64"),
EMBEDDINGS_MINIMUM_FILE_SIZE_TO_INDEX("com.dotcms.ai.embeddings.minimum.file.size", "1024"),
Expand Down Expand Up @@ -57,7 +57,7 @@ public enum AppKeys {
public final String key;
public final String defaultValue;

AppKeys(String key, String defaultValue) {
AppKeys(final String key, final String defaultValue) {
this.key = key;
this.defaultValue = defaultValue;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public void notify(final AppSecretSavedEvent event) {
final String hostId = event.getHostIdentifier();
final Host host = Try.of(() -> hostAPI.find(hostId, APILocator.systemUser(), false)).getOrNull();

Optional.ofNullable(host).ifPresent(found -> AIModels.get().resetModels(found));
Optional.ofNullable(host).ifPresent(found -> AIModels.get().resetModels(found));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import com.dotcms.ai.api.EmbeddingsAPI;
import com.dotcms.ai.app.AppConfig;
import com.dotcms.ai.app.AppKeys;
import com.dotcms.ai.app.ConfigService;
import com.dotcms.ai.db.EmbeddingsDTO;
import com.dotcms.content.elasticsearch.business.event.ContentletArchiveEvent;
Expand All @@ -18,7 +17,6 @@
import com.dotmarketing.util.Logger;
import com.dotmarketing.util.json.JSONObject;
import io.vavr.control.Try;
import org.apache.commons.lang3.StringUtils;

import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -85,7 +83,7 @@ private AppConfig getAppConfig(final String hostId) {
.getOrElse(APILocator.systemHost());

final AppConfig appConfig = ConfigService.INSTANCE.config(host);
if (StringUtils.isBlank(appConfig.getApiKey())) {
if (!appConfig.isEnabled()) {
throw new DotRuntimeException("No API key found in app config");
}

Expand Down Expand Up @@ -127,10 +125,7 @@ private void addToIndexesIfNeeded(final Contentlet contentlet) {
.stream()
.filter(typeFields -> contentType.equalsIgnoreCase(typeFields.getKey()))
.forEach(e -> EmbeddingsAPI.impl()
.generateEmbeddingsForContent(
contentlet,
e.getValue(),
indexName));
.generateEmbeddingsForContent(contentlet, e.getValue(), indexName));
}
}

Expand Down
Loading

0 comments on commit e608069

Please sign in to comment.