diff --git a/bundle/default-bundle/pom.xml b/bundle/default-bundle/pom.xml index 2732e047b7..43c871371b 100644 --- a/bundle/default-bundle/pom.xml +++ b/bundle/default-bundle/pom.xml @@ -33,6 +33,10 @@ io.camunda.connector connector-aws-lambda + + io.camunda.connector + connector-aws-bedrock + io.camunda.connector connector-google-drive diff --git a/bundle/pom.xml b/bundle/pom.xml index 563d961086..af08c6cd8a 100644 --- a/bundle/pom.xml +++ b/bundle/pom.xml @@ -59,6 +59,11 @@ connector-sendgrid ${project.version} + + io.camunda.connector + connector-aws-bedrock + ${project.version} + io.camunda.connector connector-aws-sns diff --git a/connectors/aws/aws-base/pom.xml b/connectors/aws/aws-base/pom.xml index 5eaa3f4f4d..91dd145e1d 100644 --- a/connectors/aws/aws-base/pom.xml +++ b/connectors/aws/aws-base/pom.xml @@ -18,7 +18,9 @@ Camunda Self-Managed Free Edition license - https://camunda.com/legal/terms/cloud-terms-and-conditions/camunda-cloud-self-managed-free-edition-terms/ + + https://camunda.com/legal/terms/cloud-terms-and-conditions/camunda-cloud-self-managed-free-edition-terms/ + Camunda Self-Managed Enterprise Edition license @@ -31,7 +33,13 @@ aws-java-sdk-core ${version.aws-java-sdk} - + + + software.amazon.awssdk + auth + ${version.software-aws-java-sdk} + + com.amazonaws aws-java-sdk-sagemakerruntime diff --git a/connectors/aws/aws-base/src/main/java/io/camunda/connector/aws/CredentialsProviderSupportV2.java b/connectors/aws/aws-base/src/main/java/io/camunda/connector/aws/CredentialsProviderSupportV2.java new file mode 100644 index 0000000000..4a8e79fa8f --- /dev/null +++ b/connectors/aws/aws-base/src/main/java/io/camunda/connector/aws/CredentialsProviderSupportV2.java @@ -0,0 +1,29 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.aws; + +import io.camunda.connector.aws.model.impl.AwsAuthentication; +import io.camunda.connector.aws.model.impl.AwsBaseRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import software.amazon.awssdk.auth.credentials.*; + +public class CredentialsProviderSupportV2 { + + private static final Logger LOGGER = LoggerFactory.getLogger(CredentialsProviderSupportV2.class); + + public static AwsCredentialsProvider credentialsProvider(AwsBaseRequest request) { + AwsAuthentication authentication = request.getAuthentication(); + if (authentication instanceof AwsAuthentication.AwsStaticCredentialsAuthentication sca) { + LOGGER.debug("Using StaticCredentialsProvider for AWS authentication (using aws sdk v2)"); + return StaticCredentialsProvider.create( + AwsBasicCredentials.create(sca.accessKey(), sca.secretKey())); + } + LOGGER.debug("Falling to DefaultCredentialsProvider for AWS authentication (using aws sdk v2)"); + return DefaultCredentialsProvider.create(); + } +} diff --git a/connectors/aws/aws-bedrock/LICENSE.txt b/connectors/aws/aws-bedrock/LICENSE.txt new file mode 100644 index 0000000000..85fdd16e79 --- /dev/null +++ b/connectors/aws/aws-bedrock/LICENSE.txt @@ -0,0 +1,5 @@ +Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH under one or more contributor license agreements and licensed to you under a proprietary license. +You may not use this file except in compliance with the proprietary license. +The proprietary license can be either the Camunda Self-Managed Free Edition license (available on Camunda’s website) or the Camunda Self-Managed Enterprise Edition license (a copy you obtain when you contact Camunda). +The Camunda Self-Managed Free Edition comes for free but only allows for usage of the software (file) in non-production environments. +If you want to use the software (file) in production, you need to purchase the Camunda Self-Managed Enterprise Edition. \ No newline at end of file diff --git a/connectors/aws/aws-bedrock/element-templates/aws-bedrock-outbound-connector.json b/connectors/aws/aws-bedrock/element-templates/aws-bedrock-outbound-connector.json new file mode 100644 index 0000000000..26c2ba8411 --- /dev/null +++ b/connectors/aws/aws-bedrock/element-templates/aws-bedrock-outbound-connector.json @@ -0,0 +1,349 @@ +{ + "$schema" : "https://unpkg.com/@camunda/zeebe-element-templates-json-schema/resources/schema.json", + "name" : "AWS BedRock Outbound Connector", + "id" : "io.camunda.connectors.aws.bedrock.v1", + "description" : "Execute bedrock requests", + "documentationRef" : "https://docs.camunda.io/docs/", + "version" : 1, + "category" : { + "id" : "connectors", + "name" : "Connectors" + }, + "appliesTo" : [ "bpmn:Task" ], + "elementType" : { + "value" : "bpmn:ServiceTask" + }, + "groups" : [ { + "id" : "authentication", + "label" : "Authentication" + }, { + "id" : "configuration", + "label" : "Configuration" + }, { + "id" : "action", + "label" : "Action" + }, { + "id" : "invokeModel", + "label" : "Invoke Model" + }, { + "id" : "converse", + "label" : "Converse" + }, { + "id" : "output", + "label" : "Output mapping" + }, { + "id" : "error", + "label" : "Error handling" + }, { + "id" : "retries", + "label" : "Retries" + } ], + "properties" : [ { + "value" : "io.camunda:aws-bedrock:1", + "binding" : { + "property" : "type", + "type" : "zeebe:taskDefinition" + }, + "type" : "Hidden" + }, { + "id" : "authentication.type", + "label" : "Authentication", + "description" : "Specify AWS authentication strategy. Learn more at the documentation page", + "value" : "credentials", + "group" : "authentication", + "binding" : { + "name" : "authentication.type", + "type" : "zeebe:input" + }, + "type" : "Dropdown", + "choices" : [ { + "name" : "Default Credentials Chain (Hybrid/Self-Managed only)", + "value" : "defaultCredentialsChain" + }, { + "name" : "Credentials", + "value" : "credentials" + } ] + }, { + "id" : "authentication.accessKey", + "label" : "Access key", + "description" : "Provide an IAM access key tailored to a user, equipped with the necessary permissions", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "authentication", + "binding" : { + "name" : "authentication.accessKey", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "authentication.type", + "equals" : "credentials", + "type" : "simple" + }, + "type" : "String" + }, { + "id" : "authentication.secretKey", + "label" : "Secret key", + "description" : "Provide a secret key of a user with permissions to invoke specified AWS Lambda function", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "authentication", + "binding" : { + "name" : "authentication.secretKey", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "authentication.type", + "equals" : "credentials", + "type" : "simple" + }, + "type" : "String" + }, { + "id" : "configuration.region", + "label" : "Region", + "description" : "Specify the AWS region", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "configuration", + "binding" : { + "name" : "configuration.region", + "type" : "zeebe:input" + }, + "type" : "String" + }, { + "id" : "configuration.endpoint", + "label" : "Endpoint", + "description" : "Specify endpoint if need to use custom endpoint", + "optional" : true, + "group" : "configuration", + "binding" : { + "name" : "configuration.endpoint", + "type" : "zeebe:input" + }, + "type" : "Hidden" + }, { + "id" : "action", + "label" : "Action", + "value" : "invokeModel", + "group" : "action", + "binding" : { + "name" : "action", + "type" : "zeebe:input" + }, + "type" : "Dropdown", + "choices" : [ { + "name" : "Invoke Model", + "value" : "invokeModel" + }, { + "name" : "Converse", + "value" : "converse" + } ] + }, { + "id" : "data.modelId0", + "label" : "Model id", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "invokeModel", + "binding" : { + "name" : "data.modelId", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "action", + "equals" : "invokeModel", + "type" : "simple" + }, + "type" : "String" + }, { + "id" : "data.payload", + "label" : "Payload", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "required", + "group" : "invokeModel", + "binding" : { + "name" : "data.payload", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "action", + "equals" : "invokeModel", + "type" : "simple" + }, + "type" : "String" + }, { + "id" : "data.modelId1", + "label" : "Model id", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "converse", + "binding" : { + "name" : "data.modelId", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "action", + "equals" : "converse", + "type" : "simple" + }, + "type" : "String" + }, { + "id" : "data.nextMessage", + "label" : "New Message", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "converse", + "binding" : { + "name" : "data.nextMessage", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "action", + "equals" : "converse", + "type" : "simple" + }, + "type" : "String" + }, { + "id" : "data.messages", + "label" : "Messages History", + "optional" : true, + "feel" : "optional", + "group" : "converse", + "binding" : { + "name" : "data.messages", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "action", + "equals" : "converse", + "type" : "simple" + }, + "type" : "String" + }, { + "id" : "data.maxTokens", + "label" : "Max token returned", + "optional" : true, + "feel" : "optional", + "group" : "converse", + "binding" : { + "name" : "data.maxTokens", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "action", + "equals" : "converse", + "type" : "simple" + }, + "type" : "String" + }, { + "id" : "data.temperature", + "label" : "Temperature", + "optional" : true, + "feel" : "optional", + "group" : "converse", + "binding" : { + "name" : "data.temperature", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "action", + "equals" : "converse", + "type" : "simple" + }, + "type" : "String" + }, { + "id" : "data.topP", + "label" : "top P", + "optional" : true, + "feel" : "optional", + "group" : "converse", + "binding" : { + "name" : "data.topP", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "action", + "equals" : "converse", + "type" : "simple" + }, + "type" : "String" + }, { + "id" : "resultVariable", + "label" : "Result variable", + "description" : "Name of variable to store the response in", + "group" : "output", + "binding" : { + "key" : "resultVariable", + "type" : "zeebe:taskHeader" + }, + "type" : "String" + }, { + "id" : "resultExpression", + "label" : "Result expression", + "description" : "Expression to map the response into process variables", + "feel" : "required", + "group" : "output", + "binding" : { + "key" : "resultExpression", + "type" : "zeebe:taskHeader" + }, + "type" : "Text" + }, { + "id" : "errorExpression", + "label" : "Error expression", + "description" : "Expression to handle errors. Details in the documentation.", + "feel" : "required", + "group" : "error", + "binding" : { + "key" : "errorExpression", + "type" : "zeebe:taskHeader" + }, + "type" : "Text" + }, { + "id" : "retryCount", + "label" : "Retries", + "description" : "Number of retries", + "value" : "3", + "feel" : "optional", + "group" : "retries", + "binding" : { + "property" : "retries", + "type" : "zeebe:taskDefinition" + }, + "type" : "String" + }, { + "id" : "retryBackoff", + "label" : "Retry backoff", + "description" : "ISO-8601 duration to wait between retries", + "value" : "PT0S", + "feel" : "optional", + "group" : "retries", + "binding" : { + "key" : "retryBackoff", + "type" : "zeebe:taskHeader" + }, + "type" : "String" + } ], + "icon" : { + "contents" : "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iODBweCIgaGVpZ2h0PSI4MHB4IiB2aWV3Qm94PSIwIDAgODAgODAiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8dGl0bGU+SWNvbi1BcmNoaXRlY3R1cmUvNjQvQXJjaF9BbWF6b24tQmVkcm9ja182NDwvdGl0bGU+CiAgICA8ZyBpZD0iSWNvbi1BcmNoaXRlY3R1cmUvNjQvQXJjaF9BbWF6b24tQmVkcm9ja182NCIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+CiAgICAgICAgPGcgaWQ9Ikljb24tQXJjaGl0ZWN0dXJlLUJHLzY0L01hY2hpbmUtTGVhcm5pbmciIGZpbGw9IiMwMUE4OEQiPgogICAgICAgICAgICA8cmVjdCBpZD0iUmVjdGFuZ2xlIiB4PSIwIiB5PSIwIiB3aWR0aD0iODAiIGhlaWdodD0iODAiPjwvcmVjdD4KICAgICAgICA8L2c+CiAgICAgICAgPGcgaWQ9Ikljb24tU2VydmljZS82NC9BbWF6b24tQmVkcm9ja182NCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTIuMDAwMDAwLCAxMi4wMDAwMDApIiBmaWxsPSIjRkZGRkZGIj4KICAgICAgICAgICAgPHBhdGggZD0iTTUyLDI2Ljk5OTg5MTggQzUwLjg5NywyNi45OTk4OTE4IDUwLDI2LjEwMjg5MTggNTAsMjQuOTk5ODkxOCBDNTAsMjMuODk2ODkxOCA1MC44OTcsMjIuOTk5ODkxOCA1MiwyMi45OTk4OTE4IEM1My4xMDMsMjIuOTk5ODkxOCA1NCwyMy44OTY4OTE4IDU0LDI0Ljk5OTg5MTggQzU0LDI2LjEwMjg5MTggNTMuMTAzLDI2Ljk5OTg5MTggNTIsMjYuOTk5ODkxOCBMNTIsMjYuOTk5ODkxOCBaIE0yMC4xMTMsNTMuOTA3ODkxOCBMMTYuODY1LDUyLjAxMzg5MTggTDIzLjUzLDQ3Ljg0Nzg5MTggTDIyLjQ3LDQ2LjE1MTg5MTggTDE0LjkxMyw1MC44NzQ4OTE4IEw5LDQ3LjQyNTg5MTggTDksMzguNTM0ODkxOCBMMTQuNTU1LDM0LjgzMTg5MTggTDEzLjQ0NSwzMy4xNjc4OTE4IEw3Ljk1OSwzNi44MjQ4OTE4IEwyLDMzLjQxOTg5MTggTDIsMjguNTc5ODkxOCBMOC40OTYsMjQuODY3ODkxOCBMNy41MDQsMjMuMTMxODkxOCBMMiwyNi4yNzY4OTE4IEwyLDIyLjU3OTg5MTggTDgsMTkuMTUxODkxOCBMMTQsMjIuNTc5ODkxOCBMMTQsMjYuNDMzODkxOCBMOS40ODUsMjkuMTQyODkxOCBMMTAuNTE1LDMwLjg1Njg5MTggTDE1LDI4LjE2NTg5MTggTDE5LjQ4NSwzMC44NTY4OTE4IEwyMC41MTUsMjkuMTQyODkxOCBMMTYsMjYuNDMzODkxOCBMMTYsMjIuNTM0ODkxOCBMMjEuNTU1LDE4LjgzMTg5MTggQzIxLjgzMywxOC42NDU4OTE4IDIyLDE4LjMzMzg5MTggMjIsMTcuOTk5ODkxOCBMMjIsMTAuOTk5ODkxOCBMMjAsMTAuOTk5ODkxOCBMMjAsMTcuNDY0ODkxOCBMMTQuOTU5LDIwLjgyNDg5MTggTDksMTcuNDE5ODkxOCBMOSw4LjU3Mzg5MTgxIEwxNCw1LjY1Nzg5MTgxIEwxNCwxMy45OTk4OTE4IEwxNiwxMy45OTk4OTE4IEwxNiw0LjQ5MDg5MTgxIEwyMC4xMTMsMi4wOTE4OTE4MSBMMjgsNC43MjA4OTE4MSBMMjgsMzMuNDMzODkxOCBMMTMuNDg1LDQyLjE0Mjg5MTggTDE0LjUxNSw0My44NTY4OTE4IEwyOCwzNS43NjU4OTE4IEwyOCw1MS4yNzg4OTE4IEwyMC4xMTMsNTMuOTA3ODkxOCBaIE01MCwzNy45OTk4OTE4IEM1MCwzOS4xMDI4OTE4IDQ5LjEwMywzOS45OTk4OTE4IDQ4LDM5Ljk5OTg5MTggQzQ2Ljg5NywzOS45OTk4OTE4IDQ2LDM5LjEwMjg5MTggNDYsMzcuOTk5ODkxOCBDNDYsMzYuODk2ODkxOCA0Ni44OTcsMzUuOTk5ODkxOCA0OCwzNS45OTk4OTE4IEM0OS4xMDMsMzUuOTk5ODkxOCA1MCwzNi44OTY4OTE4IDUwLDM3Ljk5OTg5MTggTDUwLDM3Ljk5OTg5MTggWiBNNDAsNDcuOTk5ODkxOCBDNDAsNDkuMTAyODkxOCAzOS4xMDMsNDkuOTk5ODkxOCAzOCw0OS45OTk4OTE4IEMzNi44OTcsNDkuOTk5ODkxOCAzNiw0OS4xMDI4OTE4IDM2LDQ3Ljk5OTg5MTggQzM2LDQ2Ljg5Njg5MTggMzYuODk3LDQ1Ljk5OTg5MTggMzgsNDUuOTk5ODkxOCBDMzkuMTAzLDQ1Ljk5OTg5MTggNDAsNDYuODk2ODkxOCA0MCw0Ny45OTk4OTE4IEw0MCw0Ny45OTk4OTE4IFogTTM5LDcuOTk5ODkxODEgQzM5LDYuODk2ODkxODEgMzkuODk3LDUuOTk5ODkxODEgNDEsNS45OTk4OTE4MSBDNDIuMTAzLDUuOTk5ODkxODEgNDMsNi44OTY4OTE4MSA0Myw3Ljk5OTg5MTgxIEM0Myw5LjEwMjg5MTgxIDQyLjEwMyw5Ljk5OTg5MTgxIDQxLDkuOTk5ODkxODEgQzM5Ljg5Nyw5Ljk5OTg5MTgxIDM5LDkuMTAyODkxODEgMzksNy45OTk4OTE4MSBMMzksNy45OTk4OTE4MSBaIE01MiwyMC45OTk4OTE4IEM1MC4xNDEsMjAuOTk5ODkxOCA0OC41ODksMjIuMjc5ODkxOCA0OC4xNDIsMjMuOTk5ODkxOCBMMzAsMjMuOTk5ODkxOCBMMzAsMTguOTk5ODkxOCBMNDEsMTguOTk5ODkxOCBDNDEuNTUzLDE4Ljk5OTg5MTggNDIsMTguNTUxODkxOCA0MiwxNy45OTk4OTE4IEw0MiwxMS44NTc4OTE4IEM0My43MiwxMS40MTA4OTE4IDQ1LDkuODU3ODkxODEgNDUsNy45OTk4OTE4MSBDNDUsNS43OTM4OTE4MSA0My4yMDYsMy45OTk4OTE4MSA0MSwzLjk5OTg5MTgxIEMzOC43OTQsMy45OTk4OTE4MSAzNyw1Ljc5Mzg5MTgxIDM3LDcuOTk5ODkxODEgQzM3LDkuODU3ODkxODEgMzguMjgsMTEuNDEwODkxOCA0MCwxMS44NTc4OTE4IEw0MCwxNi45OTk4OTE4IEwzMCwxNi45OTk4OTE4IEwzMCwzLjk5OTg5MTgxIEMzMCwzLjU2ODg5MTgxIDI5LjcyNSwzLjE4Nzg5MTgxIDI5LjMxNiwzLjA1MDg5MTgxIEwyMC4zMTYsMC4wNTA4OTE4MTEgQzIwLjA0MiwtMC4wMzkxMDgxODkgMTkuNzQ0LC0wLjAwOTEwODE4OTA0IDE5LjQ5NiwwLjEzNTg5MTgxMSBMNy40OTYsNy4xMzU4OTE4MSBDNy4xODgsNy4zMTQ4OTE4MSA3LDcuNjQ0ODkxODEgNyw3Ljk5OTg5MTgxIEw3LDE3LjQxOTg5MTggTDAuNTA0LDIxLjEzMTg5MTggQzAuMTkyLDIxLjMwOTg5MTggMCwyMS42NDA4OTE4IDAsMjEuOTk5ODkxOCBMMCwzMy45OTk4OTE4IEMwLDM0LjM1ODg5MTggMC4xOTIsMzQuNjg5ODkxOCAwLjUwNCwzNC44Njc4OTE4IEw3LDM4LjU3OTg5MTggTDcsNDcuOTk5ODkxOCBDNyw0OC4zNTQ4OTE4IDcuMTg4LDQ4LjY4NDg5MTggNy40OTYsNDguODYzODkxOCBMMTkuNDk2LDU1Ljg2Mzg5MTggQzE5LjY1LDU1Ljk1Mzg5MTggMTkuODI1LDU1Ljk5OTg5MTggMjAsNTUuOTk5ODkxOCBDMjAuMTA2LDU1Ljk5OTg5MTggMjAuMjEzLDU1Ljk4Mjg5MTggMjAuMzE2LDU1Ljk0ODg5MTggTDI5LjMxNiw1Mi45NDg4OTE4IEMyOS43MjUsNTIuODExODkxOCAzMCw1Mi40MzA4OTE4IDMwLDUxLjk5OTg5MTggTDMwLDM5Ljk5OTg5MTggTDM3LDM5Ljk5OTg5MTggTDM3LDQ0LjE0MTg5MTggQzM1LjI4LDQ0LjU4ODg5MTggMzQsNDYuMTQxODkxOCAzNCw0Ny45OTk4OTE4IEMzNCw1MC4yMDU4OTE4IDM1Ljc5NCw1MS45OTk4OTE4IDM4LDUxLjk5OTg5MTggQzQwLjIwNiw1MS45OTk4OTE4IDQyLDUwLjIwNTg5MTggNDIsNDcuOTk5ODkxOCBDNDIsNDYuMTQxODkxOCA0MC43Miw0NC41ODg4OTE4IDM5LDQ0LjE0MTg5MTggTDM5LDM4Ljk5OTg5MTggQzM5LDM4LjQ0Nzg5MTggMzguNTUzLDM3Ljk5OTg5MTggMzgsMzcuOTk5ODkxOCBMMzAsMzcuOTk5ODkxOCBMMzAsMzIuOTk5ODkxOCBMNDIuNSwzMi45OTk4OTE4IEw0NC42MzgsMzUuODQ5ODkxOCBDNDQuMjM5LDM2LjQ3MTg5MTggNDQsMzcuMjA2ODkxOCA0NCwzNy45OTk4OTE4IEM0NCw0MC4yMDU4OTE4IDQ1Ljc5NCw0MS45OTk4OTE4IDQ4LDQxLjk5OTg5MTggQzUwLjIwNiw0MS45OTk4OTE4IDUyLDQwLjIwNTg5MTggNTIsMzcuOTk5ODkxOCBDNTIsMzUuNzkzODkxOCA1MC4yMDYsMzMuOTk5ODkxOCA0OCwzMy45OTk4OTE4IEM0Ny4zMTYsMzMuOTk5ODkxOCA0Ni42ODIsMzQuMTg3ODkxOCA0Ni4xMTksMzQuNDkxODkxOCBMNDMuOCwzMS4zOTk4OTE4IEM0My42MTEsMzEuMTQ3ODkxOCA0My4zMTQsMzAuOTk5ODkxOCA0MywzMC45OTk4OTE4IEwzMCwzMC45OTk4OTE4IEwzMCwyNS45OTk4OTE4IEw0OC4xNDIsMjUuOTk5ODkxOCBDNDguNTg5LDI3LjcxOTg5MTggNTAuMTQxLDI4Ljk5OTg5MTggNTIsMjguOTk5ODkxOCBDNTQuMjA2LDI4Ljk5OTg5MTggNTYsMjcuMjA1ODkxOCA1NiwyNC45OTk4OTE4IEM1NiwyMi43OTM4OTE4IDU0LjIwNiwyMC45OTk4OTE4IDUyLDIwLjk5OTg5MTggTDUyLDIwLjk5OTg5MTggWiIgaWQ9IkZpbGwtMSI+PC9wYXRoPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+" + } +} \ No newline at end of file diff --git a/connectors/aws/aws-bedrock/element-templates/hybrid/aws-bedrock-outbound-connector-hybrid.json b/connectors/aws/aws-bedrock/element-templates/hybrid/aws-bedrock-outbound-connector-hybrid.json new file mode 100644 index 0000000000..e4e504572f --- /dev/null +++ b/connectors/aws/aws-bedrock/element-templates/hybrid/aws-bedrock-outbound-connector-hybrid.json @@ -0,0 +1,354 @@ +{ + "$schema" : "https://unpkg.com/@camunda/zeebe-element-templates-json-schema/resources/schema.json", + "name" : "Hybrid AWS BedRock Outbound Connector", + "id" : "io.camunda.connectors.aws.bedrock.v1-hybrid", + "description" : "Execute bedrock requests", + "documentationRef" : "https://docs.camunda.io/docs/", + "version" : 1, + "category" : { + "id" : "connectors", + "name" : "Connectors" + }, + "appliesTo" : [ "bpmn:Task" ], + "elementType" : { + "value" : "bpmn:ServiceTask" + }, + "groups" : [ { + "id" : "taskDefinitionType", + "label" : "Task definition type" + }, { + "id" : "authentication", + "label" : "Authentication" + }, { + "id" : "configuration", + "label" : "Configuration" + }, { + "id" : "action", + "label" : "Action" + }, { + "id" : "invokeModel", + "label" : "Invoke Model" + }, { + "id" : "converse", + "label" : "Converse" + }, { + "id" : "output", + "label" : "Output mapping" + }, { + "id" : "error", + "label" : "Error handling" + }, { + "id" : "retries", + "label" : "Retries" + } ], + "properties" : [ { + "id" : "taskDefinitionType", + "value" : "io.camunda:aws-bedrock:1", + "group" : "taskDefinitionType", + "binding" : { + "property" : "type", + "type" : "zeebe:taskDefinition" + }, + "type" : "String" + }, { + "id" : "authentication.type", + "label" : "Authentication", + "description" : "Specify AWS authentication strategy. Learn more at the documentation page", + "value" : "credentials", + "group" : "authentication", + "binding" : { + "name" : "authentication.type", + "type" : "zeebe:input" + }, + "type" : "Dropdown", + "choices" : [ { + "name" : "Default Credentials Chain (Hybrid/Self-Managed only)", + "value" : "defaultCredentialsChain" + }, { + "name" : "Credentials", + "value" : "credentials" + } ] + }, { + "id" : "authentication.accessKey", + "label" : "Access key", + "description" : "Provide an IAM access key tailored to a user, equipped with the necessary permissions", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "authentication", + "binding" : { + "name" : "authentication.accessKey", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "authentication.type", + "equals" : "credentials", + "type" : "simple" + }, + "type" : "String" + }, { + "id" : "authentication.secretKey", + "label" : "Secret key", + "description" : "Provide a secret key of a user with permissions to invoke specified AWS Lambda function", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "authentication", + "binding" : { + "name" : "authentication.secretKey", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "authentication.type", + "equals" : "credentials", + "type" : "simple" + }, + "type" : "String" + }, { + "id" : "configuration.region", + "label" : "Region", + "description" : "Specify the AWS region", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "configuration", + "binding" : { + "name" : "configuration.region", + "type" : "zeebe:input" + }, + "type" : "String" + }, { + "id" : "configuration.endpoint", + "label" : "Endpoint", + "description" : "Specify endpoint if need to use custom endpoint", + "optional" : true, + "group" : "configuration", + "binding" : { + "name" : "configuration.endpoint", + "type" : "zeebe:input" + }, + "type" : "Hidden" + }, { + "id" : "action", + "label" : "Action", + "value" : "invokeModel", + "group" : "action", + "binding" : { + "name" : "action", + "type" : "zeebe:input" + }, + "type" : "Dropdown", + "choices" : [ { + "name" : "Invoke Model", + "value" : "invokeModel" + }, { + "name" : "Converse", + "value" : "converse" + } ] + }, { + "id" : "data.modelId0", + "label" : "Model id", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "invokeModel", + "binding" : { + "name" : "data.modelId", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "action", + "equals" : "invokeModel", + "type" : "simple" + }, + "type" : "String" + }, { + "id" : "data.payload", + "label" : "Payload", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "required", + "group" : "invokeModel", + "binding" : { + "name" : "data.payload", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "action", + "equals" : "invokeModel", + "type" : "simple" + }, + "type" : "String" + }, { + "id" : "data.modelId1", + "label" : "Model id", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "converse", + "binding" : { + "name" : "data.modelId", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "action", + "equals" : "converse", + "type" : "simple" + }, + "type" : "String" + }, { + "id" : "data.nextMessage", + "label" : "New Message", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "converse", + "binding" : { + "name" : "data.nextMessage", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "action", + "equals" : "converse", + "type" : "simple" + }, + "type" : "String" + }, { + "id" : "data.messages", + "label" : "Messages History", + "optional" : true, + "feel" : "optional", + "group" : "converse", + "binding" : { + "name" : "data.messages", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "action", + "equals" : "converse", + "type" : "simple" + }, + "type" : "String" + }, { + "id" : "data.maxTokens", + "label" : "Max token returned", + "optional" : true, + "feel" : "optional", + "group" : "converse", + "binding" : { + "name" : "data.maxTokens", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "action", + "equals" : "converse", + "type" : "simple" + }, + "type" : "String" + }, { + "id" : "data.temperature", + "label" : "Temperature", + "optional" : true, + "feel" : "optional", + "group" : "converse", + "binding" : { + "name" : "data.temperature", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "action", + "equals" : "converse", + "type" : "simple" + }, + "type" : "String" + }, { + "id" : "data.topP", + "label" : "top P", + "optional" : true, + "feel" : "optional", + "group" : "converse", + "binding" : { + "name" : "data.topP", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "action", + "equals" : "converse", + "type" : "simple" + }, + "type" : "String" + }, { + "id" : "resultVariable", + "label" : "Result variable", + "description" : "Name of variable to store the response in", + "group" : "output", + "binding" : { + "key" : "resultVariable", + "type" : "zeebe:taskHeader" + }, + "type" : "String" + }, { + "id" : "resultExpression", + "label" : "Result expression", + "description" : "Expression to map the response into process variables", + "feel" : "required", + "group" : "output", + "binding" : { + "key" : "resultExpression", + "type" : "zeebe:taskHeader" + }, + "type" : "Text" + }, { + "id" : "errorExpression", + "label" : "Error expression", + "description" : "Expression to handle errors. Details in the documentation.", + "feel" : "required", + "group" : "error", + "binding" : { + "key" : "errorExpression", + "type" : "zeebe:taskHeader" + }, + "type" : "Text" + }, { + "id" : "retryCount", + "label" : "Retries", + "description" : "Number of retries", + "value" : "3", + "feel" : "optional", + "group" : "retries", + "binding" : { + "property" : "retries", + "type" : "zeebe:taskDefinition" + }, + "type" : "String" + }, { + "id" : "retryBackoff", + "label" : "Retry backoff", + "description" : "ISO-8601 duration to wait between retries", + "value" : "PT0S", + "feel" : "optional", + "group" : "retries", + "binding" : { + "key" : "retryBackoff", + "type" : "zeebe:taskHeader" + }, + "type" : "String" + } ], + "icon" : { + "contents" : "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iODBweCIgaGVpZ2h0PSI4MHB4IiB2aWV3Qm94PSIwIDAgODAgODAiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8dGl0bGU+SWNvbi1BcmNoaXRlY3R1cmUvNjQvQXJjaF9BbWF6b24tQmVkcm9ja182NDwvdGl0bGU+CiAgICA8ZyBpZD0iSWNvbi1BcmNoaXRlY3R1cmUvNjQvQXJjaF9BbWF6b24tQmVkcm9ja182NCIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+CiAgICAgICAgPGcgaWQ9Ikljb24tQXJjaGl0ZWN0dXJlLUJHLzY0L01hY2hpbmUtTGVhcm5pbmciIGZpbGw9IiMwMUE4OEQiPgogICAgICAgICAgICA8cmVjdCBpZD0iUmVjdGFuZ2xlIiB4PSIwIiB5PSIwIiB3aWR0aD0iODAiIGhlaWdodD0iODAiPjwvcmVjdD4KICAgICAgICA8L2c+CiAgICAgICAgPGcgaWQ9Ikljb24tU2VydmljZS82NC9BbWF6b24tQmVkcm9ja182NCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTIuMDAwMDAwLCAxMi4wMDAwMDApIiBmaWxsPSIjRkZGRkZGIj4KICAgICAgICAgICAgPHBhdGggZD0iTTUyLDI2Ljk5OTg5MTggQzUwLjg5NywyNi45OTk4OTE4IDUwLDI2LjEwMjg5MTggNTAsMjQuOTk5ODkxOCBDNTAsMjMuODk2ODkxOCA1MC44OTcsMjIuOTk5ODkxOCA1MiwyMi45OTk4OTE4IEM1My4xMDMsMjIuOTk5ODkxOCA1NCwyMy44OTY4OTE4IDU0LDI0Ljk5OTg5MTggQzU0LDI2LjEwMjg5MTggNTMuMTAzLDI2Ljk5OTg5MTggNTIsMjYuOTk5ODkxOCBMNTIsMjYuOTk5ODkxOCBaIE0yMC4xMTMsNTMuOTA3ODkxOCBMMTYuODY1LDUyLjAxMzg5MTggTDIzLjUzLDQ3Ljg0Nzg5MTggTDIyLjQ3LDQ2LjE1MTg5MTggTDE0LjkxMyw1MC44NzQ4OTE4IEw5LDQ3LjQyNTg5MTggTDksMzguNTM0ODkxOCBMMTQuNTU1LDM0LjgzMTg5MTggTDEzLjQ0NSwzMy4xNjc4OTE4IEw3Ljk1OSwzNi44MjQ4OTE4IEwyLDMzLjQxOTg5MTggTDIsMjguNTc5ODkxOCBMOC40OTYsMjQuODY3ODkxOCBMNy41MDQsMjMuMTMxODkxOCBMMiwyNi4yNzY4OTE4IEwyLDIyLjU3OTg5MTggTDgsMTkuMTUxODkxOCBMMTQsMjIuNTc5ODkxOCBMMTQsMjYuNDMzODkxOCBMOS40ODUsMjkuMTQyODkxOCBMMTAuNTE1LDMwLjg1Njg5MTggTDE1LDI4LjE2NTg5MTggTDE5LjQ4NSwzMC44NTY4OTE4IEwyMC41MTUsMjkuMTQyODkxOCBMMTYsMjYuNDMzODkxOCBMMTYsMjIuNTM0ODkxOCBMMjEuNTU1LDE4LjgzMTg5MTggQzIxLjgzMywxOC42NDU4OTE4IDIyLDE4LjMzMzg5MTggMjIsMTcuOTk5ODkxOCBMMjIsMTAuOTk5ODkxOCBMMjAsMTAuOTk5ODkxOCBMMjAsMTcuNDY0ODkxOCBMMTQuOTU5LDIwLjgyNDg5MTggTDksMTcuNDE5ODkxOCBMOSw4LjU3Mzg5MTgxIEwxNCw1LjY1Nzg5MTgxIEwxNCwxMy45OTk4OTE4IEwxNiwxMy45OTk4OTE4IEwxNiw0LjQ5MDg5MTgxIEwyMC4xMTMsMi4wOTE4OTE4MSBMMjgsNC43MjA4OTE4MSBMMjgsMzMuNDMzODkxOCBMMTMuNDg1LDQyLjE0Mjg5MTggTDE0LjUxNSw0My44NTY4OTE4IEwyOCwzNS43NjU4OTE4IEwyOCw1MS4yNzg4OTE4IEwyMC4xMTMsNTMuOTA3ODkxOCBaIE01MCwzNy45OTk4OTE4IEM1MCwzOS4xMDI4OTE4IDQ5LjEwMywzOS45OTk4OTE4IDQ4LDM5Ljk5OTg5MTggQzQ2Ljg5NywzOS45OTk4OTE4IDQ2LDM5LjEwMjg5MTggNDYsMzcuOTk5ODkxOCBDNDYsMzYuODk2ODkxOCA0Ni44OTcsMzUuOTk5ODkxOCA0OCwzNS45OTk4OTE4IEM0OS4xMDMsMzUuOTk5ODkxOCA1MCwzNi44OTY4OTE4IDUwLDM3Ljk5OTg5MTggTDUwLDM3Ljk5OTg5MTggWiBNNDAsNDcuOTk5ODkxOCBDNDAsNDkuMTAyODkxOCAzOS4xMDMsNDkuOTk5ODkxOCAzOCw0OS45OTk4OTE4IEMzNi44OTcsNDkuOTk5ODkxOCAzNiw0OS4xMDI4OTE4IDM2LDQ3Ljk5OTg5MTggQzM2LDQ2Ljg5Njg5MTggMzYuODk3LDQ1Ljk5OTg5MTggMzgsNDUuOTk5ODkxOCBDMzkuMTAzLDQ1Ljk5OTg5MTggNDAsNDYuODk2ODkxOCA0MCw0Ny45OTk4OTE4IEw0MCw0Ny45OTk4OTE4IFogTTM5LDcuOTk5ODkxODEgQzM5LDYuODk2ODkxODEgMzkuODk3LDUuOTk5ODkxODEgNDEsNS45OTk4OTE4MSBDNDIuMTAzLDUuOTk5ODkxODEgNDMsNi44OTY4OTE4MSA0Myw3Ljk5OTg5MTgxIEM0Myw5LjEwMjg5MTgxIDQyLjEwMyw5Ljk5OTg5MTgxIDQxLDkuOTk5ODkxODEgQzM5Ljg5Nyw5Ljk5OTg5MTgxIDM5LDkuMTAyODkxODEgMzksNy45OTk4OTE4MSBMMzksNy45OTk4OTE4MSBaIE01MiwyMC45OTk4OTE4IEM1MC4xNDEsMjAuOTk5ODkxOCA0OC41ODksMjIuMjc5ODkxOCA0OC4xNDIsMjMuOTk5ODkxOCBMMzAsMjMuOTk5ODkxOCBMMzAsMTguOTk5ODkxOCBMNDEsMTguOTk5ODkxOCBDNDEuNTUzLDE4Ljk5OTg5MTggNDIsMTguNTUxODkxOCA0MiwxNy45OTk4OTE4IEw0MiwxMS44NTc4OTE4IEM0My43MiwxMS40MTA4OTE4IDQ1LDkuODU3ODkxODEgNDUsNy45OTk4OTE4MSBDNDUsNS43OTM4OTE4MSA0My4yMDYsMy45OTk4OTE4MSA0MSwzLjk5OTg5MTgxIEMzOC43OTQsMy45OTk4OTE4MSAzNyw1Ljc5Mzg5MTgxIDM3LDcuOTk5ODkxODEgQzM3LDkuODU3ODkxODEgMzguMjgsMTEuNDEwODkxOCA0MCwxMS44NTc4OTE4IEw0MCwxNi45OTk4OTE4IEwzMCwxNi45OTk4OTE4IEwzMCwzLjk5OTg5MTgxIEMzMCwzLjU2ODg5MTgxIDI5LjcyNSwzLjE4Nzg5MTgxIDI5LjMxNiwzLjA1MDg5MTgxIEwyMC4zMTYsMC4wNTA4OTE4MTEgQzIwLjA0MiwtMC4wMzkxMDgxODkgMTkuNzQ0LC0wLjAwOTEwODE4OTA0IDE5LjQ5NiwwLjEzNTg5MTgxMSBMNy40OTYsNy4xMzU4OTE4MSBDNy4xODgsNy4zMTQ4OTE4MSA3LDcuNjQ0ODkxODEgNyw3Ljk5OTg5MTgxIEw3LDE3LjQxOTg5MTggTDAuNTA0LDIxLjEzMTg5MTggQzAuMTkyLDIxLjMwOTg5MTggMCwyMS42NDA4OTE4IDAsMjEuOTk5ODkxOCBMMCwzMy45OTk4OTE4IEMwLDM0LjM1ODg5MTggMC4xOTIsMzQuNjg5ODkxOCAwLjUwNCwzNC44Njc4OTE4IEw3LDM4LjU3OTg5MTggTDcsNDcuOTk5ODkxOCBDNyw0OC4zNTQ4OTE4IDcuMTg4LDQ4LjY4NDg5MTggNy40OTYsNDguODYzODkxOCBMMTkuNDk2LDU1Ljg2Mzg5MTggQzE5LjY1LDU1Ljk1Mzg5MTggMTkuODI1LDU1Ljk5OTg5MTggMjAsNTUuOTk5ODkxOCBDMjAuMTA2LDU1Ljk5OTg5MTggMjAuMjEzLDU1Ljk4Mjg5MTggMjAuMzE2LDU1Ljk0ODg5MTggTDI5LjMxNiw1Mi45NDg4OTE4IEMyOS43MjUsNTIuODExODkxOCAzMCw1Mi40MzA4OTE4IDMwLDUxLjk5OTg5MTggTDMwLDM5Ljk5OTg5MTggTDM3LDM5Ljk5OTg5MTggTDM3LDQ0LjE0MTg5MTggQzM1LjI4LDQ0LjU4ODg5MTggMzQsNDYuMTQxODkxOCAzNCw0Ny45OTk4OTE4IEMzNCw1MC4yMDU4OTE4IDM1Ljc5NCw1MS45OTk4OTE4IDM4LDUxLjk5OTg5MTggQzQwLjIwNiw1MS45OTk4OTE4IDQyLDUwLjIwNTg5MTggNDIsNDcuOTk5ODkxOCBDNDIsNDYuMTQxODkxOCA0MC43Miw0NC41ODg4OTE4IDM5LDQ0LjE0MTg5MTggTDM5LDM4Ljk5OTg5MTggQzM5LDM4LjQ0Nzg5MTggMzguNTUzLDM3Ljk5OTg5MTggMzgsMzcuOTk5ODkxOCBMMzAsMzcuOTk5ODkxOCBMMzAsMzIuOTk5ODkxOCBMNDIuNSwzMi45OTk4OTE4IEw0NC42MzgsMzUuODQ5ODkxOCBDNDQuMjM5LDM2LjQ3MTg5MTggNDQsMzcuMjA2ODkxOCA0NCwzNy45OTk4OTE4IEM0NCw0MC4yMDU4OTE4IDQ1Ljc5NCw0MS45OTk4OTE4IDQ4LDQxLjk5OTg5MTggQzUwLjIwNiw0MS45OTk4OTE4IDUyLDQwLjIwNTg5MTggNTIsMzcuOTk5ODkxOCBDNTIsMzUuNzkzODkxOCA1MC4yMDYsMzMuOTk5ODkxOCA0OCwzMy45OTk4OTE4IEM0Ny4zMTYsMzMuOTk5ODkxOCA0Ni42ODIsMzQuMTg3ODkxOCA0Ni4xMTksMzQuNDkxODkxOCBMNDMuOCwzMS4zOTk4OTE4IEM0My42MTEsMzEuMTQ3ODkxOCA0My4zMTQsMzAuOTk5ODkxOCA0MywzMC45OTk4OTE4IEwzMCwzMC45OTk4OTE4IEwzMCwyNS45OTk4OTE4IEw0OC4xNDIsMjUuOTk5ODkxOCBDNDguNTg5LDI3LjcxOTg5MTggNTAuMTQxLDI4Ljk5OTg5MTggNTIsMjguOTk5ODkxOCBDNTQuMjA2LDI4Ljk5OTg5MTggNTYsMjcuMjA1ODkxOCA1NiwyNC45OTk4OTE4IEM1NiwyMi43OTM4OTE4IDU0LjIwNiwyMC45OTk4OTE4IDUyLDIwLjk5OTg5MTggTDUyLDIwLjk5OTg5MTggWiIgaWQ9IkZpbGwtMSI+PC9wYXRoPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+" + } +} \ No newline at end of file diff --git a/connectors/aws/aws-bedrock/pom.xml b/connectors/aws/aws-bedrock/pom.xml new file mode 100644 index 0000000000..60d512493f --- /dev/null +++ b/connectors/aws/aws-bedrock/pom.xml @@ -0,0 +1,70 @@ + + + 4.0.0 + + io.camunda.connector + connector-aws-parent + 8.6.0-SNAPSHOT + + + + connector-aws-bedrock + Camunda AWS Bedrock Connector + connector-aws-bedrock + jar + + + + Camunda Self-Managed Free Edition license + https://camunda.com/legal/terms/cloud-terms-and-conditions/camunda-cloud-self-managed-free-edition-terms/ + + + Camunda Self-Managed Enterprise Edition license + + + + + + io.camunda.connector + connector-aws-base + ${project.version} + + + + software.amazon.awssdk + bedrockruntime + ${version.software-aws-java-sdk} + + + + + + + io.camunda.connector + element-template-generator-maven-plugin + ${project.version} + + + + io.camunda.connector.aws.bedrock.BedrockConnectorFunction + + + io.camunda.connectors.aws.bedrock.v1 + aws-bedrock-outbound-connector.json + + + true + + + + io.camunda.connector:connector-aws-base + + + + + + + + \ No newline at end of file diff --git a/connectors/aws/aws-bedrock/src/main/java/io/camunda/connector/aws/bedrock/BedrockConnectorFunction.java b/connectors/aws/aws-bedrock/src/main/java/io/camunda/connector/aws/bedrock/BedrockConnectorFunction.java new file mode 100644 index 0000000000..5991241b51 --- /dev/null +++ b/connectors/aws/aws-bedrock/src/main/java/io/camunda/connector/aws/bedrock/BedrockConnectorFunction.java @@ -0,0 +1,44 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.aws.bedrock; + +import io.camunda.connector.api.annotation.OutboundConnector; +import io.camunda.connector.api.outbound.OutboundConnectorContext; +import io.camunda.connector.api.outbound.OutboundConnectorFunction; +import io.camunda.connector.aws.bedrock.core.BedrockExecutor; +import io.camunda.connector.aws.bedrock.model.BedrockRequest; +import io.camunda.connector.generator.java.annotation.ElementTemplate; + +@OutboundConnector( + name = "AWS BedRock", + inputVariables = {"authentication", "configuration", "action", "data"}, + type = "io.camunda:aws-bedrock:1") +@ElementTemplate( + id = "io.camunda.connectors.aws.bedrock.v1", + name = "AWS BedRock Outbound Connector", + description = "Execute bedrock requests", + inputDataClass = BedrockRequest.class, + version = 1, + propertyGroups = { + @ElementTemplate.PropertyGroup(id = "authentication", label = "Authentication"), + @ElementTemplate.PropertyGroup(id = "configuration", label = "Configuration"), + @ElementTemplate.PropertyGroup(id = "action", label = "Action"), + @ElementTemplate.PropertyGroup(id = "invokeModel", label = "Invoke Model"), + @ElementTemplate.PropertyGroup(id = "converse", label = "Converse"), + }, + documentationRef = "https://docs.camunda.io/docs/", + icon = "icon.svg") +public class BedrockConnectorFunction implements OutboundConnectorFunction { + + public BedrockConnectorFunction() {} + + @Override + public Object execute(OutboundConnectorContext context) { + BedrockRequest bedrockRequest = context.bindVariables(BedrockRequest.class); + return BedrockExecutor.create(bedrockRequest).execute(); + } +} diff --git a/connectors/aws/aws-bedrock/src/main/java/io/camunda/connector/aws/bedrock/core/BedrockExecutor.java b/connectors/aws/aws-bedrock/src/main/java/io/camunda/connector/aws/bedrock/core/BedrockExecutor.java new file mode 100644 index 0000000000..ddf4d4f64c --- /dev/null +++ b/connectors/aws/aws-bedrock/src/main/java/io/camunda/connector/aws/bedrock/core/BedrockExecutor.java @@ -0,0 +1,39 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.aws.bedrock.core; + +import io.camunda.connector.aws.CredentialsProviderSupportV2; +import io.camunda.connector.aws.ObjectMapperSupplier; +import io.camunda.connector.aws.bedrock.model.BedrockRequest; +import io.camunda.connector.aws.bedrock.model.BedrockResponse; +import io.camunda.connector.aws.bedrock.model.RequestData; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.bedrockruntime.BedrockRuntimeClient; + +public class BedrockExecutor { + + private final BedrockRuntimeClient bedrockRuntimeClient; + private final RequestData requestData; + + public BedrockExecutor(BedrockRuntimeClient bedrockRuntimeClient, RequestData requestData) { + this.bedrockRuntimeClient = bedrockRuntimeClient; + this.requestData = requestData; + } + + public static BedrockExecutor create(BedrockRequest bedrockRequest) { + return new BedrockExecutor( + BedrockRuntimeClient.builder() + .credentialsProvider(CredentialsProviderSupportV2.credentialsProvider(bedrockRequest)) + .region(Region.of(bedrockRequest.getConfiguration().region())) + .build(), + bedrockRequest.getData()); + } + + public BedrockResponse execute() { + return this.requestData.execute(bedrockRuntimeClient, ObjectMapperSupplier.getMapperInstance()); + } +} diff --git a/connectors/aws/aws-bedrock/src/main/java/io/camunda/connector/aws/bedrock/model/BedrockRequest.java b/connectors/aws/aws-bedrock/src/main/java/io/camunda/connector/aws/bedrock/model/BedrockRequest.java new file mode 100644 index 0000000000..7f7197d4db --- /dev/null +++ b/connectors/aws/aws-bedrock/src/main/java/io/camunda/connector/aws/bedrock/model/BedrockRequest.java @@ -0,0 +1,38 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.aws.bedrock.model; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import io.camunda.connector.aws.model.impl.AwsBaseRequest; +import io.camunda.connector.generator.java.annotation.NestedProperties; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; + +public class BedrockRequest extends AwsBaseRequest { + @JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.EXTERNAL_PROPERTY, + property = "action") + @JsonSubTypes( + value = { + @JsonSubTypes.Type(value = ConverseData.class, name = "converse"), + @JsonSubTypes.Type(value = InvokeModelData.class, name = "invokeModel"), + }) + @Valid + @NotNull + @NestedProperties(addNestedPath = false) + private RequestData data; + + public BedrockRequest() {} + + @Valid + @NotNull + public RequestData getData() { + return data; + } +} diff --git a/connectors/aws/aws-bedrock/src/main/java/io/camunda/connector/aws/bedrock/model/BedrockResponse.java b/connectors/aws/aws-bedrock/src/main/java/io/camunda/connector/aws/bedrock/model/BedrockResponse.java new file mode 100644 index 0000000000..fdd984a83b --- /dev/null +++ b/connectors/aws/aws-bedrock/src/main/java/io/camunda/connector/aws/bedrock/model/BedrockResponse.java @@ -0,0 +1,9 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.aws.bedrock.model; + +public interface BedrockResponse {} diff --git a/connectors/aws/aws-bedrock/src/main/java/io/camunda/connector/aws/bedrock/model/ConverseData.java b/connectors/aws/aws-bedrock/src/main/java/io/camunda/connector/aws/bedrock/model/ConverseData.java new file mode 100644 index 0000000000..f7daeb50f4 --- /dev/null +++ b/connectors/aws/aws-bedrock/src/main/java/io/camunda/connector/aws/bedrock/model/ConverseData.java @@ -0,0 +1,176 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.aws.bedrock.model; + +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.camunda.connector.generator.dsl.Property; +import io.camunda.connector.generator.java.annotation.TemplateProperty; +import io.camunda.connector.generator.java.annotation.TemplateSubType; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import software.amazon.awssdk.services.bedrockruntime.BedrockRuntimeClient; +import software.amazon.awssdk.services.bedrockruntime.model.ContentBlock; +import software.amazon.awssdk.services.bedrockruntime.model.ConversationRole; +import software.amazon.awssdk.services.bedrockruntime.model.ConverseResponse; +import software.amazon.awssdk.services.bedrockruntime.model.Message; + +@TemplateSubType(id = "converse", label = "Converse") +public final class ConverseData implements RequestData { + + @TemplateProperty( + label = "Model id", + group = "converse", + id = "data.modelId1", + feel = Property.FeelMode.optional, + binding = @TemplateProperty.PropertyBinding(name = "data.modelId")) + @Valid + @NotNull + private String modelId; + + @TemplateProperty( + label = "New Message", + group = "converse", + id = "data.nextMessage", + feel = Property.FeelMode.optional, + binding = @TemplateProperty.PropertyBinding(name = "data.nextMessage")) + @Valid + @NotBlank + private String nextMessage; + + @TemplateProperty( + label = "Messages History", + group = "converse", + id = "data.messages", + feel = Property.FeelMode.optional, + optional = true, + binding = @TemplateProperty.PropertyBinding(name = "data.messages")) + @Valid + @JsonSetter(nulls = Nulls.SKIP) + private List messages = new ArrayList<>(); + + @TemplateProperty( + label = "Max token returned", + group = "converse", + id = "data.maxTokens", + feel = Property.FeelMode.optional, + optional = true, + binding = @TemplateProperty.PropertyBinding(name = "data.maxTokens")) + private Integer maxTokens = 512; + + @TemplateProperty( + label = "Temperature", + group = "converse", + id = "data.temperature", + feel = Property.FeelMode.optional, + optional = true, + binding = @TemplateProperty.PropertyBinding(name = "data.temperature")) + private Float temperature = 0.5f; + + @TemplateProperty( + label = "top P", + group = "converse", + id = "data.topP", + feel = Property.FeelMode.optional, + optional = true, + binding = @TemplateProperty.PropertyBinding(name = "data.topP")) + private Float topP = 0.9f; + + @Override + public BedrockResponse execute( + BedrockRuntimeClient bedrockRuntimeClient, ObjectMapper mapperInstance) { + this.messages.add(new PreviousMessage(this.nextMessage, ConversationRole.USER.name())); + Message.Builder messageBuilder = Message.builder(); + List messages = + this.messages.stream() + .map( + message -> + messageBuilder + .role(ConversationRole.valueOf(message.role())) + .content(ContentBlock.fromText(message.message())) + .build()) + .toList(); + ConverseResponse converseResponse = + bedrockRuntimeClient.converse( + builder -> + builder + .modelId(this.modelId) + .messages(messages) + .inferenceConfig( + config -> + config + .temperature(this.temperature) + .maxTokens(this.maxTokens) + .topP(this.topP) + .build())); + String newMessage = converseResponse.output().message().content().getFirst().text(); + this.messages.add(new PreviousMessage(newMessage, ConversationRole.ASSISTANT.name())); + return new ConverseWrapperResponse(this.messages, newMessage); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ConverseData that = (ConverseData) o; + return Objects.equals(modelId, that.modelId) + && Objects.equals(nextMessage, that.nextMessage) + && Objects.equals(messages, that.messages) + && Objects.equals(maxTokens, that.maxTokens) + && Objects.equals(temperature, that.temperature) + && Objects.equals(topP, that.topP); + } + + @Override + public int hashCode() { + return Objects.hash(modelId, nextMessage, messages, maxTokens, temperature, topP); + } + + public void setModelId(@Valid @NotNull String modelId) { + this.modelId = modelId; + } + + public void setNextMessage(@Valid @NotBlank String nextMessage) { + this.nextMessage = nextMessage; + } + + public void setMessages(@Valid List messages) { + this.messages = messages; + } + + public void setMaxTokens(Integer maxTokens) { + this.maxTokens = maxTokens; + } + + public void setTemperature(Float temperature) { + this.temperature = temperature; + } + + public void setTopP(Float topP) { + this.topP = topP; + } + + @Override + public String toString() { + return "ConverseData{" + + "modelId='" + + modelId + + '\'' + + ", maxTokens=" + + maxTokens + + ", temperature=" + + temperature + + ", topP=" + + topP + + '}'; + } +} diff --git a/connectors/aws/aws-bedrock/src/main/java/io/camunda/connector/aws/bedrock/model/ConverseWrapperResponse.java b/connectors/aws/aws-bedrock/src/main/java/io/camunda/connector/aws/bedrock/model/ConverseWrapperResponse.java new file mode 100644 index 0000000000..d3f0659d83 --- /dev/null +++ b/connectors/aws/aws-bedrock/src/main/java/io/camunda/connector/aws/bedrock/model/ConverseWrapperResponse.java @@ -0,0 +1,12 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.aws.bedrock.model; + +import java.util.List; + +public record ConverseWrapperResponse(List messagesHistory, String newMessage) + implements BedrockResponse {} diff --git a/connectors/aws/aws-bedrock/src/main/java/io/camunda/connector/aws/bedrock/model/InvokeModelData.java b/connectors/aws/aws-bedrock/src/main/java/io/camunda/connector/aws/bedrock/model/InvokeModelData.java new file mode 100644 index 0000000000..2dcc25d620 --- /dev/null +++ b/connectors/aws/aws-bedrock/src/main/java/io/camunda/connector/aws/bedrock/model/InvokeModelData.java @@ -0,0 +1,85 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.aws.bedrock.model; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.camunda.connector.generator.dsl.Property; +import io.camunda.connector.generator.java.annotation.TemplateProperty; +import io.camunda.connector.generator.java.annotation.TemplateSubType; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import java.util.Objects; +import software.amazon.awssdk.core.SdkBytes; +import software.amazon.awssdk.services.bedrockruntime.BedrockRuntimeClient; + +@TemplateSubType(id = "invokeModel", label = "Invoke Model") +public final class InvokeModelData implements RequestData { + + @TemplateProperty( + label = "Model id", + group = "invokeModel", + id = "data.modelId0", + feel = Property.FeelMode.optional, + binding = @TemplateProperty.PropertyBinding(name = "data.modelId")) + @Valid + @NotNull + String modelId; + + @TemplateProperty( + label = "Payload", + group = "invokeModel", + id = "data.payload", + feel = Property.FeelMode.required, + binding = @TemplateProperty.PropertyBinding(name = "data.payload")) + @Valid + @NotNull + Object payload; + + @Override + public BedrockResponse execute( + BedrockRuntimeClient bedrockRuntimeClient, ObjectMapper mapperInstance) { + try { + String body = mapperInstance.writeValueAsString(this.payload); + String response = + bedrockRuntimeClient + .invokeModel( + builder -> builder.body(SdkBytes.fromUtf8String(body)).modelId(this.modelId)) + .body() + .asUtf8String(); + return new InvokeModelWrappedResponse(mapperInstance.readValue(response, Object.class)); + } catch (JsonProcessingException e) { + throw new RuntimeException(e.getOriginalMessage(), e); + } + } + + public void setModelId(@Valid @NotNull String modelId) { + this.modelId = modelId; + } + + public void setPayload(@Valid @NotNull Object payload) { + this.payload = payload; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + InvokeModelData that = (InvokeModelData) o; + return Objects.equals(modelId, that.modelId) && Objects.equals(payload, that.payload); + } + + @Override + public int hashCode() { + return Objects.hash(modelId, payload); + } + + @Override + public String toString() { + return "InvokeModelData{" + "modelId='" + modelId + '\'' + '}'; + } +} diff --git a/connectors/aws/aws-bedrock/src/main/java/io/camunda/connector/aws/bedrock/model/InvokeModelWrappedResponse.java b/connectors/aws/aws-bedrock/src/main/java/io/camunda/connector/aws/bedrock/model/InvokeModelWrappedResponse.java new file mode 100644 index 0000000000..10635930c7 --- /dev/null +++ b/connectors/aws/aws-bedrock/src/main/java/io/camunda/connector/aws/bedrock/model/InvokeModelWrappedResponse.java @@ -0,0 +1,9 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.aws.bedrock.model; + +public record InvokeModelWrappedResponse(Object body) implements BedrockResponse {} diff --git a/connectors/aws/aws-bedrock/src/main/java/io/camunda/connector/aws/bedrock/model/PreviousMessage.java b/connectors/aws/aws-bedrock/src/main/java/io/camunda/connector/aws/bedrock/model/PreviousMessage.java new file mode 100644 index 0000000000..1c6c0aacfb --- /dev/null +++ b/connectors/aws/aws-bedrock/src/main/java/io/camunda/connector/aws/bedrock/model/PreviousMessage.java @@ -0,0 +1,9 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.aws.bedrock.model; + +public record PreviousMessage(String message, String role) {} diff --git a/connectors/aws/aws-bedrock/src/main/java/io/camunda/connector/aws/bedrock/model/RequestData.java b/connectors/aws/aws-bedrock/src/main/java/io/camunda/connector/aws/bedrock/model/RequestData.java new file mode 100644 index 0000000000..5b2e1edb79 --- /dev/null +++ b/connectors/aws/aws-bedrock/src/main/java/io/camunda/connector/aws/bedrock/model/RequestData.java @@ -0,0 +1,22 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.aws.bedrock.model; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.camunda.connector.generator.java.annotation.TemplateDiscriminatorProperty; +import io.camunda.connector.generator.java.annotation.TemplateSubType; +import software.amazon.awssdk.services.bedrockruntime.BedrockRuntimeClient; + +@TemplateDiscriminatorProperty( + label = "Action", + group = "action", + name = "action", + defaultValue = "invokeModel") +@TemplateSubType(id = "action", label = "Action") +public sealed interface RequestData permits InvokeModelData, ConverseData { + BedrockResponse execute(BedrockRuntimeClient bedrockRuntimeClient, ObjectMapper mapperInstance); +} diff --git a/connectors/aws/aws-bedrock/src/main/resources/META-INF/services/io.camunda.connector.api.outbound.OutboundConnectorFunction b/connectors/aws/aws-bedrock/src/main/resources/META-INF/services/io.camunda.connector.api.outbound.OutboundConnectorFunction new file mode 100644 index 0000000000..ae6fbf7303 --- /dev/null +++ b/connectors/aws/aws-bedrock/src/main/resources/META-INF/services/io.camunda.connector.api.outbound.OutboundConnectorFunction @@ -0,0 +1 @@ +io.camunda.connector.aws.bedrock.BedrockConnectorFunction \ No newline at end of file diff --git a/connectors/aws/aws-bedrock/src/main/resources/icon.svg b/connectors/aws/aws-bedrock/src/main/resources/icon.svg new file mode 100644 index 0000000000..2e5077ba4e --- /dev/null +++ b/connectors/aws/aws-bedrock/src/main/resources/icon.svg @@ -0,0 +1,12 @@ + + + Icon-Architecture/64/Arch_Amazon-Bedrock_64 + + + + + + + + + \ No newline at end of file diff --git a/connectors/aws/aws-bedrock/src/test/java/io/camunda/connector/aws/bedrock/BaseTest.java b/connectors/aws/aws-bedrock/src/test/java/io/camunda/connector/aws/bedrock/BaseTest.java new file mode 100644 index 0000000000..1a3138a855 --- /dev/null +++ b/connectors/aws/aws-bedrock/src/test/java/io/camunda/connector/aws/bedrock/BaseTest.java @@ -0,0 +1,57 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.aws.bedrock; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static java.nio.file.Files.readString; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.camunda.connector.api.json.ConnectorsObjectMapperSupplier; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.stream.Stream; +import org.junit.jupiter.params.provider.Arguments; + +public class BaseTest { + + public static Stream loadInvokeModelVariables() { + try { + return loadTestCasesFromResourceFile( + "src/test/resources/invokemodel/invokeModelExample.json"); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static Stream loadConverseVariables() { + try { + return loadTestCasesFromResourceFile("src/test/resources/converse/converseExample.json"); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @SuppressWarnings("unchecked") + protected static Stream loadTestCasesFromResourceFile(final String fileWithTestCasesUri) + throws IOException { + final String cases = readString(new File(fileWithTestCasesUri).toPath(), UTF_8); + final ObjectMapper mapper = ConnectorsObjectMapperSupplier.getCopy(); + var array = mapper.readValue(cases, ArrayList.class); + return array.stream() + .map( + value -> { + try { + return mapper.writeValueAsString(value); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + }) + .map(Arguments::of); + } +} diff --git a/connectors/aws/aws-bedrock/src/test/java/io/camunda/connector/aws/bedrock/BedrockConnectorFunctionTest.java b/connectors/aws/aws-bedrock/src/test/java/io/camunda/connector/aws/bedrock/BedrockConnectorFunctionTest.java new file mode 100644 index 0000000000..ba2f9ebc5b --- /dev/null +++ b/connectors/aws/aws-bedrock/src/test/java/io/camunda/connector/aws/bedrock/BedrockConnectorFunctionTest.java @@ -0,0 +1,64 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.aws.bedrock; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +import io.camunda.connector.aws.bedrock.core.BedrockExecutor; +import io.camunda.connector.aws.bedrock.model.InvokeModelWrappedResponse; +import io.camunda.connector.test.outbound.OutboundConnectorContextBuilder; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.MockedStatic; +import org.mockito.Mockito; + +class BedrockConnectorFunctionTest extends BaseTest { + + @ParameterizedTest + @MethodSource("loadInvokeModelVariables") + void executeInvokeModelReturnsCorrectResult(String variables) { + + var bedrockConnectorFunction = new BedrockConnectorFunction(); + var context = OutboundConnectorContextBuilder.create().variables(variables).build(); + + var bedrockExecutor = Mockito.mock(BedrockExecutor.class); + + try (MockedStatic bedrockExecutorMockedStatic = + Mockito.mockStatic(BedrockExecutor.class)) { + bedrockExecutorMockedStatic + .when(() -> BedrockExecutor.create(any())) + .thenReturn(bedrockExecutor); + when(bedrockExecutor.execute()).thenReturn(new InvokeModelWrappedResponse("Hello")); + var response = bedrockConnectorFunction.execute(context); + Assertions.assertNotNull(response); + Assertions.assertInstanceOf(InvokeModelWrappedResponse.class, response); + } + } + + @ParameterizedTest + @MethodSource("loadConverseVariables") + void executeConverseReturnsCorrectResult(String variables) { + + var bedrockConnectorFunction = new BedrockConnectorFunction(); + var context = OutboundConnectorContextBuilder.create().variables(variables).build(); + + var bedrockExecutor = Mockito.mock(BedrockExecutor.class); + + try (MockedStatic bedrockExecutorMockedStatic = + Mockito.mockStatic(BedrockExecutor.class)) { + bedrockExecutorMockedStatic + .when(() -> BedrockExecutor.create(any())) + .thenReturn(bedrockExecutor); + when(bedrockExecutor.execute()).thenReturn(new InvokeModelWrappedResponse("Hello")); + var response = bedrockConnectorFunction.execute(context); + Assertions.assertNotNull(response); + Assertions.assertInstanceOf(InvokeModelWrappedResponse.class, response); + } + } +} diff --git a/connectors/aws/aws-bedrock/src/test/java/io/camunda/connector/aws/bedrock/model/ConverseDataTest.java b/connectors/aws/aws-bedrock/src/test/java/io/camunda/connector/aws/bedrock/model/ConverseDataTest.java new file mode 100644 index 0000000000..e80ed0bbc7 --- /dev/null +++ b/connectors/aws/aws-bedrock/src/test/java/io/camunda/connector/aws/bedrock/model/ConverseDataTest.java @@ -0,0 +1,52 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.aws.bedrock.model; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import software.amazon.awssdk.services.bedrockruntime.BedrockRuntimeClient; +import software.amazon.awssdk.services.bedrockruntime.model.ConversationRole; +import software.amazon.awssdk.services.bedrockruntime.model.ConverseResponse; + +class ConverseDataTest { + + ObjectMapper mapper = new ObjectMapper(); + BedrockRuntimeClient bedrockRuntimeClient = mock(BedrockRuntimeClient.class); + ConverseResponse converseResponse = mock(ConverseResponse.class, Mockito.RETURNS_DEEP_STUBS); + + @Test + void execute_success() { + + ConverseData converseData = new ConverseData(); + converseData.setModelId("random-model-id"); + + List previousMessages = new ArrayList<>(); + previousMessages.add(new PreviousMessage("Hey", ConversationRole.USER.name())); + previousMessages.add(new PreviousMessage("How are you?", ConversationRole.ASSISTANT.name())); + converseData.setMessages(previousMessages); + converseData.setNextMessage("I am good thanks, and you?"); + + when(bedrockRuntimeClient.converse(any(Consumer.class))).thenReturn(converseResponse); + when(converseResponse.output().message().content().getFirst().text()) + .thenReturn("I am also good"); + BedrockResponse bedrockResponse = converseData.execute(bedrockRuntimeClient, mapper); + + Assertions.assertInstanceOf(ConverseWrapperResponse.class, bedrockResponse); + Assertions.assertEquals( + "I am also good", + ((ConverseWrapperResponse) bedrockResponse).messagesHistory().getLast().message()); + } +} diff --git a/connectors/aws/aws-bedrock/src/test/java/io/camunda/connector/aws/bedrock/model/InvokeModelDataTest.java b/connectors/aws/aws-bedrock/src/test/java/io/camunda/connector/aws/bedrock/model/InvokeModelDataTest.java new file mode 100644 index 0000000000..ee156d85c8 --- /dev/null +++ b/connectors/aws/aws-bedrock/src/test/java/io/camunda/connector/aws/bedrock/model/InvokeModelDataTest.java @@ -0,0 +1,65 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.aws.bedrock.model; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.camunda.connector.aws.bedrock.BaseTest; +import java.util.function.Consumer; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import software.amazon.awssdk.core.SdkBytes; +import software.amazon.awssdk.services.bedrockruntime.BedrockRuntimeClient; +import software.amazon.awssdk.services.bedrockruntime.model.InvokeModelResponse; + +class InvokeModelDataTest extends BaseTest { + + ObjectMapper mapper = new ObjectMapper(); + BedrockRuntimeClient bedrockRuntimeClient = mock(BedrockRuntimeClient.class); + InvokeModelResponse invokeModelResponse = mock(InvokeModelResponse.class); + + @Test + void execute_success() throws JsonProcessingException { + String payload = + "{\"messages\":[{\"role\":\"user\", \"content\":\"Hello\"}], \"max_tokens\":256, \"top_p\":0.8, \"temperature\":0.7}"; + InvokeModelData invokeModelData = new InvokeModelData(); + invokeModelData.setModelId("random-model-id"); + invokeModelData.setPayload(mapper.readTree(payload)); + + when(bedrockRuntimeClient.invokeModel(any(Consumer.class))).thenReturn(invokeModelResponse); + when(invokeModelResponse.body()).thenReturn(SdkBytes.fromUtf8String("{\"result\" : \"Hey\"}")); + BedrockResponse invokeModelWrappedResponse = + invokeModelData.execute(bedrockRuntimeClient, mapper); + + Assertions.assertInstanceOf(InvokeModelWrappedResponse.class, invokeModelWrappedResponse); + } + + @Test + void execute_wrongReturn() throws JsonProcessingException { + String payload = + "{\"messages\":[{\"role\":\"user\", \"content\":\"Hello\"}], \"max_tokens\":256, \"top_p\":0.8, \"temperature\":0.7}"; + InvokeModelData invokeModelData = new InvokeModelData(); + invokeModelData.setModelId("random-model-id"); + invokeModelData.setPayload(mapper.readTree(payload)); + + when(bedrockRuntimeClient.invokeModel(any(Consumer.class))).thenReturn(invokeModelResponse); + when(invokeModelResponse.body()).thenReturn(SdkBytes.fromUtf8String("Hey")); + + RuntimeException runtimeException = + Assertions.assertThrows( + RuntimeException.class, () -> invokeModelData.execute(bedrockRuntimeClient, mapper)); + + assertEquals( + "Unrecognized token 'Hey': was expecting (JSON String, Number, Array, Object or token 'null', 'true' or 'false')", + runtimeException.getMessage()); + } +} diff --git a/connectors/aws/aws-bedrock/src/test/resources/converse/converseExample.json b/connectors/aws/aws-bedrock/src/test/resources/converse/converseExample.json new file mode 100644 index 0000000000..b17a2a2967 --- /dev/null +++ b/connectors/aws/aws-bedrock/src/test/resources/converse/converseExample.json @@ -0,0 +1,20 @@ +[ + { + "data":{ + "modelId":"amazon.titan-text-premier-v1:0", + "nextMessage":"ok", + "messages":[ + {"message":"Hey", "role":"USER"}, {"message":"Hello there! How can I assist you today?", "role":"ASSISTANT"}, + {"message":"You feel good ?", "role":"USER"}, { + "message":"Thank you for asking! As an AI, I don't have personal feelings or experiences, but I'm always here to help with any questions or information you may need. How can I assist you today?", + "role":"ASSISTANT" + } + ] + }, + "action":"converse", + "configuration":{"region":"us-east-1"}, + "authentication":{ + "type":"credentials", "accessKey":"access", "secretKey":"secret" + } + } +] \ No newline at end of file diff --git a/connectors/aws/aws-bedrock/src/test/resources/invokemodel/invokeModelExample.json b/connectors/aws/aws-bedrock/src/test/resources/invokemodel/invokeModelExample.json new file mode 100644 index 0000000000..a4883efe5f --- /dev/null +++ b/connectors/aws/aws-bedrock/src/test/resources/invokemodel/invokeModelExample.json @@ -0,0 +1,10 @@ +[ + { + "data":{ + "modelId":"ai21.jamba-instruct-v1:0", + "payload":{"messages":[{"role":"user", "content":"Hello"}], "max_tokens":256, "top_p":0.8, "temperature":0.7} + }, "action":"invokeModel", "configuration":{"region":"us-east-1"}, "authentication":{ + "type":"credentials", "accessKey":"access", "secretKey":"secret" + } + } +] \ No newline at end of file diff --git a/connectors/aws/pom.xml b/connectors/aws/pom.xml index caeca95e68..461acc64d6 100644 --- a/connectors/aws/pom.xml +++ b/connectors/aws/pom.xml @@ -24,6 +24,7 @@ aws-sns aws-sqs aws-sagemaker + aws-bedrock diff --git a/parent/pom.xml b/parent/pom.xml index 760077b5d5..49133a968e 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -91,6 +91,7 @@ limitations under the License. 1.5.6 1.12.745 + 2.26.16 3.11.6 1.2.3