Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Anthropic AI Plugin #29095

Merged
merged 26 commits into from
Dec 5, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
ba1a96a
feat: Anthropic AI Plugin
vivonk Nov 24, 2023
c403a1f
Change the design fror the FieldArrayControl
dvj1988 Nov 24, 2023
e55e2f2
Add the setFirstOptionAsDefault option in Dropdowncontrol
dvj1988 Nov 25, 2023
ac62ae2
Add condition for anthropic chat fetchDynamicValues
dvj1988 Nov 25, 2023
3b10547
Move temperature field to the bottom
dvj1988 Nov 25, 2023
23b4dd0
Merge branch 'release' of github.com:appsmithorg/appsmith into feat/a…
vivonk Nov 27, 2023
88cc259
adding caching on AI plugins
vivonk Nov 28, 2023
6afe759
Trigger the onChange call dropdown after the first option is selected…
dvj1988 Nov 29, 2023
2354035
Merge branch 'feat/anthropic-ai-plugin' of github.com:appsmithorg/app…
dvj1988 Nov 29, 2023
cc5507c
improving anthropic plugin experience
vivonk Nov 29, 2023
39dc45c
Merge branch 'feat/anthropic-ai-plugin' of github.com:appsmithorg/app…
vivonk Nov 29, 2023
dab0f51
test cases
vivonk Nov 29, 2023
4d28a6f
adding connection provider
vivonk Nov 30, 2023
e3af695
Fix for auto additions for ARRAY_FIELD object on initial render and o…
dvj1988 Dec 1, 2023
c5f1fb6
plugin fixes
vivonk Dec 1, 2023
3b5fe95
Merge branch 'feat/anthropic-ai-plugin' of github.com:appsmithorg/app…
vivonk Dec 1, 2023
c614e69
check fix
vivonk Dec 1, 2023
363fbbf
Add appsmithErrorMessage as fallback in response tab error message
dvj1988 Dec 1, 2023
35191f9
Merge branch 'feat/anthropic-ai-plugin' of github.com:appsmithorg/app…
dvj1988 Dec 1, 2023
1850d5b
Remove initial values of all Array field control types
dvj1988 Dec 4, 2023
8b8a172
remove unused css
dvj1988 Dec 4, 2023
b86a653
code changes
vivonk Dec 5, 2023
3034ab6
Merge branch 'feat/anthropic-ai-plugin' of github.com:appsmithorg/app…
vivonk Dec 5, 2023
844e4be
provider name change, disable one test
vivonk Dec 5, 2023
f2d0a7f
refactor
vivonk Dec 5, 2023
04337a7
small refactor
vivonk Dec 5, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions app/server/appsmith-plugins/anthropicPlugin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@
<artifactId>jackson-datatype-jsr310</artifactId>
<version>${jackson-bom.version}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>32.0.1-jre</version>
</dependency>

<!--
Ideally this dependency should have been added with 'compile' scope here. But that is causing 'java.lang
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import com.appsmith.external.models.ActionExecutionResult;
import com.appsmith.external.models.ApiKeyAuth;
import com.appsmith.external.models.DatasourceConfiguration;
import com.appsmith.external.models.DatasourceTestResult;
import com.appsmith.external.models.TriggerRequestDTO;
import com.appsmith.external.models.TriggerResultDTO;
import com.appsmith.external.plugins.BasePlugin;
Expand All @@ -20,13 +19,16 @@
import com.external.plugins.models.AnthropicRequestDTO;
import com.external.plugins.utils.AnthropicMethodStrategy;
import com.external.plugins.utils.RequestUtils;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.json.JSONArray;
import org.json.JSONObject;
import org.pf4j.PluginWrapper;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatusCode;
import org.springframework.util.StringUtils;
import org.springframework.web.reactive.function.BodyInserters;
import reactor.core.publisher.Mono;

Expand All @@ -35,6 +37,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.external.plugins.constants.AnthropicConstants.ANTHROPIC_MODELS;
Expand All @@ -51,16 +54,13 @@ public AnthropicPlugin(PluginWrapper wrapper) {

public static class AnthropicPluginExecutor extends BaseRestApiPluginExecutor {
private static final Gson gson = new Gson();
private static final Cache<String, TriggerResultDTO> triggerResponseCache =
vivonk marked this conversation as resolved.
Show resolved Hide resolved
CacheBuilder.newBuilder().expireAfterWrite(1, TimeUnit.DAYS).build();

protected AnthropicPluginExecutor(SharedConfig sharedConfig) {
vivonk marked this conversation as resolved.
Show resolved Hide resolved
super(sharedConfig);
}

@Override
public Mono<DatasourceTestResult> testDatasource(APIConnection connection) {
return super.testDatasource(connection);
}

@Override
public Mono<ActionExecutionResult> executeParameterized(
APIConnection connection,
Expand Down Expand Up @@ -99,6 +99,14 @@ public Mono<ActionExecutionResult> executeParameterized(
actionExecutionResult.setRequest(actionExecutionRequest);
actionExecutionResult.setStatusCode(statusCode.toString());

/**
* DownstreamErrorMessage
* Caching
* Test cases
* Migrate to Use CS
* Implement Test Configuration
* Sort model order
*/
if (HttpStatusCode.valueOf(401).isSameCodeAs(statusCode)) {
actionExecutionResult.setIsExecutionSuccess(false);
String errorMessage = "";
Expand Down Expand Up @@ -163,10 +171,20 @@ public Mono<TriggerResultDTO> trigger(
APIConnection connection, DatasourceConfiguration datasourceConfiguration, TriggerRequestDTO request) {
final ApiKeyAuth apiKeyAuth = (ApiKeyAuth) datasourceConfiguration.getAuthentication();
assert apiKeyAuth.getValue() != null;
if (!StringUtils.hasText(request.getRequestType())) {
throw new AppsmithPluginException(
AppsmithPluginError.PLUGIN_EXECUTE_ARGUMENT_ERROR, "request type is missing");
}
String requestType = request.getRequestType();

AnthropicCommand anthropicCommand = AnthropicMethodStrategy.selectTriggerMethod(request, gson);
HttpMethod httpMethod = anthropicCommand.getTriggerHTTPMethod();
URI uri = anthropicCommand.createTriggerUri();

TriggerResultDTO triggerResultDTO = triggerResponseCache.getIfPresent(requestType);
if (triggerResultDTO != null) {
return Mono.just(triggerResultDTO);
}
return RequestUtils.makeRequest(httpMethod, uri, apiKeyAuth, BodyInserters.empty())
.flatMap(responseEntity -> {
if (responseEntity.getStatusCode().is4xxClientError()) {
Expand Down Expand Up @@ -195,11 +213,16 @@ public Mono<TriggerResultDTO> trigger(
log.debug("Error while fetching Anthropic models list", error);
return Mono.just(getDataToMap(ANTHROPIC_MODELS));
})
.map(TriggerResultDTO::new);
.map(trigger -> {
TriggerResultDTO triggerResult = new TriggerResultDTO(trigger);
// saving response on request type
triggerResponseCache.put(requestType, triggerResult);
return triggerResult;
});
}

private List<Map<String, String>> getDataToMap(List<String> data) {
return data.stream().map(x -> Map.of(LABEL, x, VALUE, x)).collect(Collectors.toList());
return data.stream().sorted().map(x -> Map.of(LABEL, x, VALUE, x)).collect(Collectors.toList());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import static com.external.plugins.constants.AnthropicConstants.CHAT_MODEL_SELECTOR;
import static com.external.plugins.constants.AnthropicConstants.CONTENT;
import static com.external.plugins.constants.AnthropicConstants.DATA;
import static com.external.plugins.constants.AnthropicConstants.DEFAULT_MAX_TOKEN;
import static com.external.plugins.constants.AnthropicConstants.MAX_TOKENS;
import static com.external.plugins.constants.AnthropicConstants.MESSAGES;
Expand All @@ -42,7 +43,7 @@ public HttpMethod getExecutionMethod() {

@Override
public URI createTriggerUri() {
return URI.create("https://cs-1467-customer.dp.appsmith.com/api/v1/ai/models?provider=anthropic");
return URI.create("https://cs.appsmith.com/api/v1/ai/models?provider=anthropic");
}

@Override
Expand Down Expand Up @@ -84,9 +85,9 @@ public AnthropicRequestDTO makeRequestBody(ActionConfiguration actionConfigurati
*/
private String createPrompt(Map<String, Object> formData) {
StringBuilder stringBuilder = new StringBuilder();
formData.get(MESSAGES);
if (formData.containsKey(MESSAGES) && ((List) formData.get(MESSAGES)).size() > 0) {
List<Map<String, String>> messagesMap = (List<Map<String, String>>) formData.get(MESSAGES);
if (formData.containsKey(MESSAGES) && ((Map) formData.get(MESSAGES)).containsKey(DATA)) {
List<Map<String, String>> messagesMap =
(List<Map<String, String>>) ((Map<?, ?>) formData.get(MESSAGES)).get(DATA);
for (Map<String, String> message : messagesMap) {
if (message.containsKey(ROLE) && message.containsKey(CONTENT)) {
stringBuilder
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
plugin.id=anthropic-plugin
plugin.class=com.external.plugins.AnthropicPlugin
plugin.version=1.0-SNAPSHOTQ12Q21 ??
plugin.version=1.0-SNAPSHOT
plugin.provider=tech@appsmith.com
plugin.dependencies=
5 changes: 5 additions & 0 deletions app/server/appsmith-plugins/openAiPlugin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,11 @@
<artifactId>reactor-netty-http</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>32.0.1-jre</version>
</dependency>

</dependencies>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import com.external.plugins.models.OpenAIRequestDTO;
import com.external.plugins.utils.OpenAIMethodStrategy;
import com.external.plugins.utils.RequestUtils;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.json.JSONArray;
Expand All @@ -33,10 +35,15 @@

import java.io.IOException;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.external.plugins.constants.OpenAIConstants.BODY;
import static com.external.plugins.constants.OpenAIConstants.DATA;
Expand All @@ -54,6 +61,8 @@ public OpenAiPlugin(PluginWrapper wrapper) {
public static class OpenAiPluginExecutor extends BaseRestApiPluginExecutor {

private static final Gson gson = new Gson();
private static final Cache<String, JSONObject> modelResponseCache =
CacheBuilder.newBuilder().expireAfterWrite(1, TimeUnit.DAYS).build();

public OpenAiPluginExecutor(SharedConfig config) {
super(config);
Expand Down Expand Up @@ -163,6 +172,28 @@ public Mono<ActionExecutionResult> executeCommon(
});
}

private String cacheKey(String bearerToken) {
return sha256(bearerToken);
}

private String sha256(String base) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(base.getBytes(StandardCharsets.UTF_8));
StringBuilder hexString = new StringBuilder();

for (byte b : hash) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) hexString.append('0');
hexString.append(hex);
}

return hexString.toString();
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}

@Override
public Set<String> validateDatasource(DatasourceConfiguration datasourceConfiguration) {
return RequestUtils.validateBearerTokenDatasource(datasourceConfiguration);
Expand All @@ -175,34 +206,45 @@ public Mono<TriggerResultDTO> trigger(
// Authentication will already be valid at this point
final BearerTokenAuth bearerTokenAuth = (BearerTokenAuth) datasourceConfiguration.getAuthentication();
assert (bearerTokenAuth.getBearerToken() != null);

OpenAICommand openAICommand = OpenAIMethodStrategy.selectTriggerMethod(request, gson);
HttpMethod httpMethod = openAICommand.getTriggerHTTPMethod();
URI uri = openAICommand.createTriggerUri();

return RequestUtils.makeRequest(httpMethod, uri, bearerTokenAuth, BodyInserters.empty())
.flatMap(responseEntity -> {
if (responseEntity.getStatusCode().is4xxClientError()) {
return Mono.error(new AppsmithPluginException(
AppsmithPluginError.PLUGIN_DATASOURCE_AUTHENTICATION_ERROR));
}

if (!responseEntity.getStatusCode().is2xxSuccessful()) {
return Mono.error(
new AppsmithPluginException(AppsmithPluginError.PLUGIN_GET_STRUCTURE_ERROR));
}
String cacheKey = cacheKey(bearerTokenAuth.getBearerToken());
JSONObject modelsResponse = modelResponseCache.getIfPresent(cacheKey);

Mono<JSONObject> responseMono;
if (modelsResponse != null) {
responseMono = Mono.just(modelsResponse);
} else {
responseMono = RequestUtils.makeRequest(httpMethod, uri, bearerTokenAuth, BodyInserters.empty())
.flatMap(responseEntity -> {
if (responseEntity.getStatusCode().is4xxClientError()) {
return Mono.error(new AppsmithPluginException(
AppsmithPluginError.PLUGIN_DATASOURCE_AUTHENTICATION_ERROR));
}

// link to get response data https://platform.openai.com/docs/api-reference/models/list
return Mono.just(new JSONObject(new String(responseEntity.getBody())));
})
if (!responseEntity.getStatusCode().is2xxSuccessful()) {
return Mono.error(
new AppsmithPluginException(AppsmithPluginError.PLUGIN_GET_STRUCTURE_ERROR));
}
JSONObject responseObject = new JSONObject(new String(responseEntity.getBody()));
modelResponseCache.put(cacheKey, responseObject);
// link to get response data https://platform.openai.com/docs/api-reference/models/list
return Mono.just(responseObject);
});
}

return responseMono
.map(jsonObject -> {
if (!jsonObject.has(DATA) && jsonObject.get(DATA) instanceof JSONArray) {
// let's throw some error.
throw Exceptions.propagate(
new AppsmithPluginException(AppsmithPluginError.PLUGIN_GET_STRUCTURE_ERROR));
}

List<Map<String, String>> triggerModelList = new ArrayList<>();
List<String> compatibleModels = new ArrayList<>();
Map<String, JSONObject> modelsMap = new HashMap<>();
JSONArray modelList = jsonObject.getJSONArray(DATA);
int modelListIndex = 0;
while (modelListIndex < modelList.length()) {
Expand All @@ -212,13 +254,18 @@ public Mono<TriggerResultDTO> trigger(
}

if (openAICommand.isModelCompatible(model)) {
triggerModelList.add(openAICommand.getModelMap(model));
String id = model.getString(ID);
compatibleModels.add(id);
modelsMap.put(id, model);
}

modelListIndex += 1;
}

return triggerModelList;
// sort models alphabetically
return compatibleModels.stream()
.sorted()
.map(model -> openAICommand.getModelMap(modelsMap.get(model)))
.collect(Collectors.toList());
})
.map(TriggerResultDTO::new);
}
Expand Down
Loading