From bc3d46d8c147a8a4dc42501d6510fa34125e7515 Mon Sep 17 00:00:00 2001
From: Nirmal Sarswat <25587962+vivonk@users.noreply.github.com>
Date: Tue, 16 Apr 2024 19:11:28 +0530
Subject: [PATCH] feat: Vision models support in Anthropic (#32103)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## Description
Adding new vision models support in Anthropic
Fixes https://github.com/appsmithorg/appsmith-ee/issues/3681
## Automation
/ok-to-test tags="@tag.Datasources"
### :mag: Cypress test results
> [!CAUTION]
> 🔴 🔴 🔴 Some tests have failed.
> Workflow run:
> Commit: 2c393e7ffaf3d08fd8e945761206de76d3a13845
> Cypress dashboard: Click here!
> The following are new failures, please fix them before merging the PR:
> - cypress/e2e/Regression/ServerSide/GenerateCRUD/MySQL2_Spec.ts
> To know the list of identified flaky tests - Refer here
## Summary by CodeRabbit
- **New Features**
- Enhanced chat functionality with version upgrade and refined message
creation process.
- Added vision-related commands for handling image and text data in the
Anthropic plugin.
- Expanded constants to support new messaging and vision features.
- Introduced new fields in request models to support system prompts and
messages, while deprecating older fields.
- Implemented a `VisionCommand` in method strategy for better handling
of vision tasks.
- Improved utility functions for message handling and configuration
value extraction.
- **Refactor**
- Streamlined request URI construction to support new vision
functionality alongside chat commands.
- Removed unused constants and methods to clean up the codebase.
---
.../com/external/plugins/AnthropicPlugin.java | 53 +++++-
.../plugins/commands/ChatCommand.java | 123 ++++--------
.../plugins/commands/VisionCommand.java | 179 ++++++++++++++++++
.../plugins/constants/AnthropicConstants.java | 26 ++-
.../plugins/models/AnthropicRequestDTO.java | 14 ++
.../plugins/models/CompletionDTO.java | 18 ++
.../com/external/plugins/models/Message.java | 62 ++++++
.../external/plugins/models/MessageDTO.java | 37 ++++
.../utils/AnthropicMethodStrategy.java | 3 +
.../external/plugins/utils/CommandUtils.java | 92 +++++++++
.../external/plugins/utils/RequestUtils.java | 6 +-
.../src/main/resources/editor/chat.json | 16 +-
.../src/main/resources/editor/root.json | 7 +-
.../src/main/resources/editor/vision.json | 127 +++++++++++++
.../external/plugins/AnthropicPluginTest.java | 39 ----
.../com/external/plugins/ChatCommandTest.java | 4 +-
...050MoveAnthropicLegacyModelsInQueries.java | 101 ++++++++++
17 files changed, 766 insertions(+), 141 deletions(-)
create mode 100644 app/server/appsmith-plugins/anthropicPlugin/src/main/java/com/external/plugins/commands/VisionCommand.java
create mode 100644 app/server/appsmith-plugins/anthropicPlugin/src/main/java/com/external/plugins/models/CompletionDTO.java
create mode 100644 app/server/appsmith-plugins/anthropicPlugin/src/main/java/com/external/plugins/models/Message.java
create mode 100644 app/server/appsmith-plugins/anthropicPlugin/src/main/java/com/external/plugins/models/MessageDTO.java
create mode 100644 app/server/appsmith-plugins/anthropicPlugin/src/main/java/com/external/plugins/utils/CommandUtils.java
create mode 100644 app/server/appsmith-plugins/anthropicPlugin/src/main/resources/editor/vision.json
create mode 100644 app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/db/ce/Migration050MoveAnthropicLegacyModelsInQueries.java
diff --git a/app/server/appsmith-plugins/anthropicPlugin/src/main/java/com/external/plugins/AnthropicPlugin.java b/app/server/appsmith-plugins/anthropicPlugin/src/main/java/com/external/plugins/AnthropicPlugin.java
index c256ee336a..7d8559d931 100644
--- a/app/server/appsmith-plugins/anthropicPlugin/src/main/java/com/external/plugins/AnthropicPlugin.java
+++ b/app/server/appsmith-plugins/anthropicPlugin/src/main/java/com/external/plugins/AnthropicPlugin.java
@@ -19,8 +19,11 @@ import com.appsmith.external.services.SharedConfig;
import com.external.plugins.commands.AnthropicCommand;
import com.external.plugins.constants.AnthropicConstants;
import com.external.plugins.models.AnthropicRequestDTO;
+import com.external.plugins.models.CompletionDTO;
+import com.external.plugins.models.MessageDTO;
import com.external.plugins.utils.AnthropicMethodStrategy;
import com.external.plugins.utils.RequestUtils;
+import com.fasterxml.jackson.annotation.JsonInclude;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.gson.Gson;
@@ -45,6 +48,7 @@ import java.util.stream.Collectors;
import static com.external.plugins.constants.AnthropicConstants.ANTHROPIC_MODELS;
import static com.external.plugins.constants.AnthropicConstants.BODY;
+import static com.external.plugins.constants.AnthropicConstants.CLAUDE3_PREFIX;
import static com.external.plugins.constants.AnthropicConstants.LABEL;
import static com.external.plugins.constants.AnthropicConstants.TEST_MODEL;
import static com.external.plugins.constants.AnthropicConstants.TEST_PROMPT;
@@ -137,7 +141,21 @@ public class AnthropicPlugin extends BasePlugin {
return Mono.just(apiKeyNotPresentErrorResult);
}
- return RequestUtils.makeRequest(httpMethod, uri, apiKeyAuth, BodyInserters.fromValue(anthropicRequestDTO))
+ String model = anthropicRequestDTO.getModel();
+
+ // we don't want to serialise null values as Anthropic throws bad request otherwise
+ objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
+ String requestBody;
+ try {
+ requestBody = objectMapper.writeValueAsString(anthropicRequestDTO);
+ } catch (Exception e) {
+ errorResult.setIsExecutionSuccess(false);
+ errorResult.setErrorInfo(
+ new AppsmithPluginException(AppsmithPluginError.PLUGIN_JSON_PARSE_ERROR, e.getMessage()));
+ return Mono.just(errorResult);
+ }
+
+ return RequestUtils.makeRequest(httpMethod, uri, apiKeyAuth, BodyInserters.fromValue(requestBody))
.flatMap(responseEntity -> {
HttpStatusCode statusCode = responseEntity.getStatusCode();
@@ -171,7 +189,12 @@ public class AnthropicPlugin extends BasePlugin {
Object body;
try {
body = objectMapper.readValue(responseEntity.getBody(), Object.class);
- actionExecutionResult.setBody(body);
+ if (model.contains(CLAUDE3_PREFIX)) {
+ actionExecutionResult.setBody(body);
+ } else {
+ actionExecutionResult.setBody(
+ formatResponseBodyAsCompletionAPI(model, responseEntity.getBody()));
+ }
} catch (IOException ex) {
actionExecutionResult.setIsExecutionSuccess(false);
actionExecutionResult.setErrorInfo(new AppsmithPluginException(
@@ -204,6 +227,24 @@ public class AnthropicPlugin extends BasePlugin {
});
}
+ /**
+ * To keep things backward compatible, if model doesn't belong to claude 3, format response in form of claude completion API
+ */
+ private Object formatResponseBodyAsCompletionAPI(String model, byte[] response) {
+ try {
+ MessageDTO messageDTO = objectMapper.readValue(response, MessageDTO.class);
+ CompletionDTO completionDTO = new CompletionDTO();
+ completionDTO.setId(messageDTO.getId());
+ completionDTO.setType("completion");
+ completionDTO.setStopReason(messageDTO.getStopReason());
+ completionDTO.setModel(model);
+ completionDTO.setCompletion(messageDTO.getFirstMessage());
+ return completionDTO;
+ } catch (IOException e) {
+ throw new AppsmithPluginException(AppsmithPluginError.PLUGIN_JSON_PARSE_ERROR, new String(response));
+ }
+ }
+
@Override
public Mono trigger(
APIConnection connection, DatasourceConfiguration datasourceConfiguration, TriggerRequestDTO request) {
@@ -252,7 +293,11 @@ public class AnthropicPlugin extends BasePlugin {
})
.onErrorResume(error -> {
log.debug("Error while fetching Anthropic models list", error);
- return Mono.just(getDataToMap(ANTHROPIC_MODELS));
+ if (ANTHROPIC_MODELS.containsKey(requestType)) {
+ return Mono.just(getDataToMap(ANTHROPIC_MODELS.get(requestType)));
+ }
+ return Mono.error(new AppsmithPluginException(
+ AppsmithPluginError.PLUGIN_EXECUTE_ARGUMENT_ERROR, error.getMessage()));
})
.map(trigger -> {
TriggerResultDTO triggerResult = new TriggerResultDTO(trigger);
@@ -268,7 +313,7 @@ public class AnthropicPlugin extends BasePlugin {
}
private List