This is an automated email from the ASF dual-hosted git repository. acosentino pushed a commit to branch CAMEL-21597-21598 in repository https://gitbox.apache.org/repos/asf/camel.git
commit 5ca98b17b3c66272f97f042973f5984adae9f7ff Author: Andrea Cosentino <[email protected]> AuthorDate: Tue Sep 23 09:53:39 2025 +0200 CAMEL-21597 - Camel AWS Bedrock: Support Amazon Nova models CAMEL-21598 - Camel AWS Bedrock: Update supported models Signed-off-by: Andrea Cosentino <[email protected]> --- .../camel/catalog/components/aws-bedrock.json | 4 +- .../aws2/bedrock/runtime/aws-bedrock.json | 4 +- .../component/aws2/bedrock/BedrockModels.java | 60 ++- .../aws2/bedrock/runtime/BedrockConfiguration.java | 2 +- .../aws2/bedrock/runtime/BedrockProducer.java | 113 ++++- .../component/aws2/bedrock/BedrockModelsTest.java | 178 ++++++++ .../runtime/integration/BedrockProducerIT.java | 478 +++++++++++++++++++++ 7 files changed, 820 insertions(+), 19 deletions(-) diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/aws-bedrock.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/aws-bedrock.json index 4dc6a29b81b..f840ec3f622 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/aws-bedrock.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/aws-bedrock.json @@ -26,7 +26,7 @@ "componentProperties": { "configuration": { "index": 0, "kind": "property", "displayName": "Configuration", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "org.apache.camel.component.aws2.bedrock.runtime.BedrockConfiguration", "deprecated": false, "autowired": false, "secret": false, "description": "Component configuration" }, "lazyStartProducer": { "index": 1, "kind": "property", "displayName": "Lazy Start Producer", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail [...] - "modelId": { "index": 2, "kind": "property", "displayName": "Model Id", "group": "producer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "enum": [ "amazon.titan-text-express-v1", "amazon.titan-text-lite-v1", "amazon.titan-image-generator-v1", "amazon.titan-embed-text-v1", "ai21.j2-ultra-v1", "ai21.j2-mid-v1", "anthropic.claude-instant-v1", "anthropic.claude-v2", "anthropic.claude-v2:1", "anthropic.claude-3-sonnet-20240229-v1:0", "anthropic.claude- [...] + "modelId": { "index": 2, "kind": "property", "displayName": "Model Id", "group": "producer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "enum": [ "amazon.titan-text-express-v1", "amazon.titan-text-lite-v1", "amazon.titan-image-generator-v1", "amazon.titan-embed-text-v1", "amazon.titan-embed-image-v1", "amazon.titan-text-premier-v1:0", "amazon.titan-embed-text-v2:0", "amazon.titan-image-generator-v2:0", "amazon.nova-canvas-v1:0", "amazon.nova-lite [...] "operation": { "index": 3, "kind": "property", "displayName": "Operation", "group": "producer", "label": "", "required": true, "type": "object", "javaType": "org.apache.camel.component.aws2.bedrock.runtime.BedrockOperations", "enum": [ "invokeTextModel", "invokeImageModel", "invokeEmbeddingsModel" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.aws2.bedrock.runtime.BedrockConfiguration", "configurat [...] "overrideEndpoint": { "index": 4, "kind": "property", "displayName": "Override Endpoint", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.aws2.bedrock.runtime.BedrockConfiguration", "configurationField": "configuration", "description": "Set the need for overriding the endpoint. This option needs to be used in [...] "pojoRequest": { "index": 5, "kind": "property", "displayName": "Pojo Request", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.aws2.bedrock.runtime.BedrockConfiguration", "configurationField": "configuration", "description": "If we want to use a POJO request as body or not" }, @@ -55,7 +55,7 @@ }, "properties": { "label": { "index": 0, "kind": "path", "displayName": "Label", "group": "producer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.aws2.bedrock.runtime.BedrockConfiguration", "configurationField": "configuration", "description": "Logical name" }, - "modelId": { "index": 1, "kind": "parameter", "displayName": "Model Id", "group": "producer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "enum": [ "amazon.titan-text-express-v1", "amazon.titan-text-lite-v1", "amazon.titan-image-generator-v1", "amazon.titan-embed-text-v1", "ai21.j2-ultra-v1", "ai21.j2-mid-v1", "anthropic.claude-instant-v1", "anthropic.claude-v2", "anthropic.claude-v2:1", "anthropic.claude-3-sonnet-20240229-v1:0", "anthropic.claude [...] + "modelId": { "index": 1, "kind": "parameter", "displayName": "Model Id", "group": "producer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "enum": [ "amazon.titan-text-express-v1", "amazon.titan-text-lite-v1", "amazon.titan-image-generator-v1", "amazon.titan-embed-text-v1", "amazon.titan-embed-image-v1", "amazon.titan-text-premier-v1:0", "amazon.titan-embed-text-v2:0", "amazon.titan-image-generator-v2:0", "amazon.nova-canvas-v1:0", "amazon.nova-lit [...] "operation": { "index": 2, "kind": "parameter", "displayName": "Operation", "group": "producer", "label": "", "required": true, "type": "object", "javaType": "org.apache.camel.component.aws2.bedrock.runtime.BedrockOperations", "enum": [ "invokeTextModel", "invokeImageModel", "invokeEmbeddingsModel" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.aws2.bedrock.runtime.BedrockConfiguration", "configura [...] "overrideEndpoint": { "index": 3, "kind": "parameter", "displayName": "Override Endpoint", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.aws2.bedrock.runtime.BedrockConfiguration", "configurationField": "configuration", "description": "Set the need for overriding the endpoint. This option needs to be used i [...] "pojoRequest": { "index": 4, "kind": "parameter", "displayName": "Pojo Request", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.aws2.bedrock.runtime.BedrockConfiguration", "configurationField": "configuration", "description": "If we want to use a POJO request as body or not" }, diff --git a/components/camel-aws/camel-aws-bedrock/src/generated/resources/META-INF/org/apache/camel/component/aws2/bedrock/runtime/aws-bedrock.json b/components/camel-aws/camel-aws-bedrock/src/generated/resources/META-INF/org/apache/camel/component/aws2/bedrock/runtime/aws-bedrock.json index 4dc6a29b81b..f840ec3f622 100644 --- a/components/camel-aws/camel-aws-bedrock/src/generated/resources/META-INF/org/apache/camel/component/aws2/bedrock/runtime/aws-bedrock.json +++ b/components/camel-aws/camel-aws-bedrock/src/generated/resources/META-INF/org/apache/camel/component/aws2/bedrock/runtime/aws-bedrock.json @@ -26,7 +26,7 @@ "componentProperties": { "configuration": { "index": 0, "kind": "property", "displayName": "Configuration", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "org.apache.camel.component.aws2.bedrock.runtime.BedrockConfiguration", "deprecated": false, "autowired": false, "secret": false, "description": "Component configuration" }, "lazyStartProducer": { "index": 1, "kind": "property", "displayName": "Lazy Start Producer", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail [...] - "modelId": { "index": 2, "kind": "property", "displayName": "Model Id", "group": "producer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "enum": [ "amazon.titan-text-express-v1", "amazon.titan-text-lite-v1", "amazon.titan-image-generator-v1", "amazon.titan-embed-text-v1", "ai21.j2-ultra-v1", "ai21.j2-mid-v1", "anthropic.claude-instant-v1", "anthropic.claude-v2", "anthropic.claude-v2:1", "anthropic.claude-3-sonnet-20240229-v1:0", "anthropic.claude- [...] + "modelId": { "index": 2, "kind": "property", "displayName": "Model Id", "group": "producer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "enum": [ "amazon.titan-text-express-v1", "amazon.titan-text-lite-v1", "amazon.titan-image-generator-v1", "amazon.titan-embed-text-v1", "amazon.titan-embed-image-v1", "amazon.titan-text-premier-v1:0", "amazon.titan-embed-text-v2:0", "amazon.titan-image-generator-v2:0", "amazon.nova-canvas-v1:0", "amazon.nova-lite [...] "operation": { "index": 3, "kind": "property", "displayName": "Operation", "group": "producer", "label": "", "required": true, "type": "object", "javaType": "org.apache.camel.component.aws2.bedrock.runtime.BedrockOperations", "enum": [ "invokeTextModel", "invokeImageModel", "invokeEmbeddingsModel" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.aws2.bedrock.runtime.BedrockConfiguration", "configurat [...] "overrideEndpoint": { "index": 4, "kind": "property", "displayName": "Override Endpoint", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.aws2.bedrock.runtime.BedrockConfiguration", "configurationField": "configuration", "description": "Set the need for overriding the endpoint. This option needs to be used in [...] "pojoRequest": { "index": 5, "kind": "property", "displayName": "Pojo Request", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.aws2.bedrock.runtime.BedrockConfiguration", "configurationField": "configuration", "description": "If we want to use a POJO request as body or not" }, @@ -55,7 +55,7 @@ }, "properties": { "label": { "index": 0, "kind": "path", "displayName": "Label", "group": "producer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.aws2.bedrock.runtime.BedrockConfiguration", "configurationField": "configuration", "description": "Logical name" }, - "modelId": { "index": 1, "kind": "parameter", "displayName": "Model Id", "group": "producer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "enum": [ "amazon.titan-text-express-v1", "amazon.titan-text-lite-v1", "amazon.titan-image-generator-v1", "amazon.titan-embed-text-v1", "ai21.j2-ultra-v1", "ai21.j2-mid-v1", "anthropic.claude-instant-v1", "anthropic.claude-v2", "anthropic.claude-v2:1", "anthropic.claude-3-sonnet-20240229-v1:0", "anthropic.claude [...] + "modelId": { "index": 1, "kind": "parameter", "displayName": "Model Id", "group": "producer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "enum": [ "amazon.titan-text-express-v1", "amazon.titan-text-lite-v1", "amazon.titan-image-generator-v1", "amazon.titan-embed-text-v1", "amazon.titan-embed-image-v1", "amazon.titan-text-premier-v1:0", "amazon.titan-embed-text-v2:0", "amazon.titan-image-generator-v2:0", "amazon.nova-canvas-v1:0", "amazon.nova-lit [...] "operation": { "index": 2, "kind": "parameter", "displayName": "Operation", "group": "producer", "label": "", "required": true, "type": "object", "javaType": "org.apache.camel.component.aws2.bedrock.runtime.BedrockOperations", "enum": [ "invokeTextModel", "invokeImageModel", "invokeEmbeddingsModel" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.aws2.bedrock.runtime.BedrockConfiguration", "configura [...] "overrideEndpoint": { "index": 3, "kind": "parameter", "displayName": "Override Endpoint", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.aws2.bedrock.runtime.BedrockConfiguration", "configurationField": "configuration", "description": "Set the need for overriding the endpoint. This option needs to be used i [...] "pojoRequest": { "index": 4, "kind": "parameter", "displayName": "Pojo Request", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.aws2.bedrock.runtime.BedrockConfiguration", "configurationField": "configuration", "description": "If we want to use a POJO request as body or not" }, diff --git a/components/camel-aws/camel-aws-bedrock/src/main/java/org/apache/camel/component/aws2/bedrock/BedrockModels.java b/components/camel-aws/camel-aws-bedrock/src/main/java/org/apache/camel/component/aws2/bedrock/BedrockModels.java index 7b66ca4ff24..3fd4fa4285b 100644 --- a/components/camel-aws/camel-aws-bedrock/src/main/java/org/apache/camel/component/aws2/bedrock/BedrockModels.java +++ b/components/camel-aws/camel-aws-bedrock/src/main/java/org/apache/camel/component/aws2/bedrock/BedrockModels.java @@ -18,6 +18,7 @@ package org.apache.camel.component.aws2.bedrock; public enum BedrockModels { + // Amazon Titan Models TITAN_TEXT_EXPRESS_V1("amazon.titan-text-express-v1"), TITAN_TEXT_LITE_V1("amazon.titan-text-lite-v1"), TITAN_IMAGE_GENERATOR_V1("amazon.titan-image-generator-v1"), @@ -25,8 +26,28 @@ public enum BedrockModels { TITAN_MULTIMODAL_EMBEDDINGS_G1("amazon.titan-embed-image-v1"), TITAN_TEXT_PREMIER_V1("amazon.titan-text-premier-v1:0"), TITAN_TEXT_EMBEDDINGS_V2("amazon.titan-embed-text-v2:0"), + TITAN_IMAGE_GENERATOR_V2("amazon.titan-image-generator-v2:0"), + + // Amazon Nova Models + NOVA_CANVAS_V1("amazon.nova-canvas-v1:0"), + NOVA_LITE_V1("amazon.nova-lite-v1:0"), + NOVA_MICRO_V1("amazon.nova-micro-v1:0"), + NOVA_PREMIER_V1("amazon.nova-premier-v1:0"), + NOVA_PRO_V1("amazon.nova-pro-v1:0"), + NOVA_REEL_V1("amazon.nova-reel-v1:0"), + NOVA_REEL_V1_1("amazon.nova-reel-v1:1"), + NOVA_SONIC_V1("amazon.nova-sonic-v1:0"), + + // Amazon Rerank + RERANK_V1("amazon.rerank-v1:0"), + + // AI21 Labs Models JURASSIC2_ULTRA("ai21.j2-ultra-v1"), JURASSIC2_MID("ai21.j2-mid-v1"), + JAMBA_1_5_LARGE("ai21.jamba-1-5-large-v1:0"), + JAMBA_1_5_MINI("ai21.jamba-1-5-mini-v1:0"), + + // Anthropic Claude Models ANTROPHIC_CLAUDE_INSTANT_V1("anthropic.claude-instant-v1"), ANTROPHIC_CLAUDE_V2("anthropic.claude-v2"), ANTROPHIC_CLAUDE_V2_1("anthropic.claude-v2:1"), @@ -35,9 +56,46 @@ public enum BedrockModels { ANTROPHIC_CLAUDE_V35_2("anthropic.claude-3-5-sonnet-20241022-v2:0"), ANTROPHIC_CLAUDE_HAIKU_V3("anthropic.claude-3-haiku-20240307-v1:0"), ANTROPHIC_CLAUDE_HAIKU_V35("anthropic.claude-3-5-haiku-20241022-v1:0"), + ANTROPHIC_CLAUDE_OPUS_V3("anthropic.claude-3-opus-20240229-v1:0"), + ANTROPHIC_CLAUDE_V37("anthropic.claude-3-7-sonnet-20250219-v1:0"), + ANTROPHIC_CLAUDE_OPUS_V4_1("anthropic.claude-opus-4-1-20250805-v1:0"), + ANTROPHIC_CLAUDE_OPUS_V4("anthropic.claude-opus-4-20250514-v1:0"), + ANTROPHIC_CLAUDE_SONNET_V4("anthropic.claude-sonnet-4-20250514-v1:0"), + + // Cohere Models + COHERE_COMMAND_R_PLUS("cohere.command-r-plus-v1:0"), + COHERE_COMMAND_R("cohere.command-r-v1:0"), + COHERE_EMBED_ENGLISH_V3("cohere.embed-english-v3"), + COHERE_EMBED_MULTILINGUAL_V3("cohere.embed-multilingual-v3"), + COHERE_RERANK_V3_5("cohere.rerank-v3-5:0"), + + // Meta Llama Models + LLAMA3_8B_INSTRUCT("meta.llama3-8b-instruct-v1:0"), + LLAMA3_70B_INSTRUCT("meta.llama3-70b-instruct-v1:0"), + LLAMA3_1_8B_INSTRUCT("meta.llama3-1-8b-instruct-v1:0"), + LLAMA3_1_70B_INSTRUCT("meta.llama3-1-70b-instruct-v1:0"), + LLAMA3_1_405B_INSTRUCT("meta.llama3-1-405b-instruct-v1:0"), + LLAMA3_2_1B_INSTRUCT("meta.llama3-2-1b-instruct-v1:0"), + LLAMA3_2_3B_INSTRUCT("meta.llama3-2-3b-instruct-v1:0"), + LLAMA3_2_11B_INSTRUCT("meta.llama3-2-11b-instruct-v1:0"), + LLAMA3_2_90B_INSTRUCT("meta.llama3-2-90b-instruct-v1:0"), + LLAMA3_3_70B_INSTRUCT("meta.llama3-3-70b-instruct-v1:0"), + LLAMA4_MAVERICK_17B_INSTRUCT("meta.llama4-maverick-17b-instruct-v1:0"), + LLAMA4_SCOUT_17B_INSTRUCT("meta.llama4-scout-17b-instruct-v1:0"), + + // Mistral AI Models MISTRAL_7B_INSTRUCT("mistral.mistral-7b-instruct-v0:2"), MISTRAL_8x7B_INSTRUCT("mistral.mixtral-8x7b-instruct-v0:1"), - MISTRAL_LARGE("mistral.mistral-large-2402-v1:0"); + MISTRAL_LARGE("mistral.mistral-large-2402-v1:0"), + MISTRAL_LARGE_2407("mistral.mistral-large-2407-v1:0"), + MISTRAL_SMALL_2402("mistral.mistral-small-2402-v1:0"), + PIXTRAL_LARGE("mistral.pixtral-large-2502-v1:0"), + + // Stability AI Models + STABLE_DIFFUSION_3_5_LARGE("stability.sd3-5-large-v1:0"), + STABLE_IMAGE_CONTROL_SKETCH("stability.stable-image-control-sketch-v1:0"), + STABLE_IMAGE_CONTROL_STRUCTURE("stability.stable-image-control-structure-v1:0"), + STABLE_IMAGE_CORE("stability.stable-image-core-v1:1"); public final String model; diff --git a/components/camel-aws/camel-aws-bedrock/src/main/java/org/apache/camel/component/aws2/bedrock/runtime/BedrockConfiguration.java b/components/camel-aws/camel-aws-bedrock/src/main/java/org/apache/camel/component/aws2/bedrock/runtime/BedrockConfiguration.java index 6557c616834..6aebf42a015 100644 --- a/components/camel-aws/camel-aws-bedrock/src/main/java/org/apache/camel/component/aws2/bedrock/runtime/BedrockConfiguration.java +++ b/components/camel-aws/camel-aws-bedrock/src/main/java/org/apache/camel/component/aws2/bedrock/runtime/BedrockConfiguration.java @@ -39,7 +39,7 @@ public class BedrockConfiguration implements Cloneable { private String secretKey; @UriParam(label = "security", secret = true) private String sessionToken; - @UriParam(enums = "amazon.titan-text-express-v1,amazon.titan-text-lite-v1,amazon.titan-image-generator-v1,amazon.titan-embed-text-v1,ai21.j2-ultra-v1,ai21.j2-mid-v1,anthropic.claude-instant-v1,anthropic.claude-v2,anthropic.claude-v2:1,anthropic.claude-3-sonnet-20240229-v1:0,anthropic.claude-3-haiku-20240307-v1:0,amazon.titan-text-premier-v1:0,amazon.titan-embed-text-v2:0") + @UriParam(enums = "amazon.titan-text-express-v1,amazon.titan-text-lite-v1,amazon.titan-image-generator-v1,amazon.titan-embed-text-v1,amazon.titan-embed-image-v1,amazon.titan-text-premier-v1:0,amazon.titan-embed-text-v2:0,amazon.titan-image-generator-v2:0,amazon.nova-canvas-v1:0,amazon.nova-lite-v1:0,amazon.nova-micro-v1:0,amazon.nova-premier-v1:0,amazon.nova-pro-v1:0,amazon.nova-reel-v1:0,amazon.nova-reel-v1:1,amazon.nova-sonic-v1:0,amazon.rerank-v1:0,ai21.j2-ultra-v1,ai21.j2-mid-v1, [...] @Metadata(required = true) private String modelId; @UriParam diff --git a/components/camel-aws/camel-aws-bedrock/src/main/java/org/apache/camel/component/aws2/bedrock/runtime/BedrockProducer.java b/components/camel-aws/camel-aws-bedrock/src/main/java/org/apache/camel/component/aws2/bedrock/runtime/BedrockProducer.java index 99f32f3c10a..b700b1f1163 100644 --- a/components/camel-aws/camel-aws-bedrock/src/main/java/org/apache/camel/component/aws2/bedrock/runtime/BedrockProducer.java +++ b/components/camel-aws/camel-aws-bedrock/src/main/java/org/apache/camel/component/aws2/bedrock/runtime/BedrockProducer.java @@ -233,40 +233,115 @@ public class BedrockProducer extends DefaultProducer { } protected void setResponseText(InvokeModelResponse result, Message message) { - switch (getConfiguration().getModelId()) { - case "amazon.titan-text-express-v1", "amazon.titan-text-lite-v1", "amazon.titan-text-premier-v1:0", - "amazon.titan-embed-text-v2:0" -> + String modelId = getConfiguration().getModelId(); + switch (modelId) { + // Amazon Titan Models + case "amazon.titan-text-express-v1": + case "amazon.titan-text-lite-v1": + case "amazon.titan-text-premier-v1:0": + case "amazon.titan-embed-text-v2:0": setTitanText(result, message); - case "ai21.j2-ultra-v1", "ai21.j2-mid-v1" -> { + break; + + // AI21 Labs Models + case "ai21.j2-ultra-v1": + case "ai21.j2-mid-v1": + case "ai21.jamba-1-5-large-v1:0": + case "ai21.jamba-1-5-mini-v1:0": try { setAi21Text(result, message); } catch (JsonProcessingException e) { throw new RuntimeException(e); } - } - case "anthropic.claude-instant-v1", "anthropic.claude-v2", "anthropic.claude-v2:1" -> { + break; + + // Anthropic Claude Models (legacy format) + case "anthropic.claude-instant-v1": + case "anthropic.claude-v2": + case "anthropic.claude-v2:1": try { setAnthropicText(result, message); } catch (JsonProcessingException e) { throw new RuntimeException(e); } - } - case "anthropic.claude-3-sonnet-20240229-v1:0", "anthropic.claude-3-haiku-20240307-v1:0" -> { + break; + + // Anthropic Claude Models (v3+ format) + case "anthropic.claude-3-sonnet-20240229-v1:0": + case "anthropic.claude-3-5-sonnet-20240620-v1:0": + case "anthropic.claude-3-5-sonnet-20241022-v2:0": + case "anthropic.claude-3-haiku-20240307-v1:0": + case "anthropic.claude-3-5-haiku-20241022-v1:0": + case "anthropic.claude-3-opus-20240229-v1:0": + case "anthropic.claude-3-7-sonnet-20250219-v1:0": + case "anthropic.claude-opus-4-1-20250805-v1:0": + case "anthropic.claude-opus-4-20250514-v1:0": + case "anthropic.claude-sonnet-4-20250514-v1:0": try { setAnthropicV3Text(result, message); } catch (JsonProcessingException e) { throw new RuntimeException(e); } - } - case "mistral.mistral-7b-instruct-v0:2", "mistral.mixtral-8x7b-instruct-v0:1", - "mistral.mistral-large-2402-v1:0" -> { + break; + + // Mistral AI Models + case "mistral.mistral-7b-instruct-v0:2": + case "mistral.mixtral-8x7b-instruct-v0:1": + case "mistral.mistral-large-2402-v1:0": + case "mistral.mistral-large-2407-v1:0": + case "mistral.mistral-small-2402-v1:0": + case "mistral.pixtral-large-2502-v1:0": try { setMistralText(result, message); } catch (JsonProcessingException e) { throw new RuntimeException(e); } - } - default -> throw new IllegalStateException("Unexpected value: " + getConfiguration().getModelId()); + break; + + // Amazon Nova Models (using v3 format) + case "amazon.nova-lite-v1:0": + case "amazon.nova-micro-v1:0": + case "amazon.nova-premier-v1:0": + case "amazon.nova-pro-v1:0": + try { + setAnthropicV3Text(result, message); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + break; + + // Cohere Models + case "cohere.command-r-plus-v1:0": + case "cohere.command-r-v1:0": + try { + setCohereText(result, message); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + break; + + // Meta Llama Models + case "meta.llama3-8b-instruct-v1:0": + case "meta.llama3-70b-instruct-v1:0": + case "meta.llama3-1-8b-instruct-v1:0": + case "meta.llama3-1-70b-instruct-v1:0": + case "meta.llama3-1-405b-instruct-v1:0": + case "meta.llama3-2-1b-instruct-v1:0": + case "meta.llama3-2-3b-instruct-v1:0": + case "meta.llama3-2-11b-instruct-v1:0": + case "meta.llama3-2-90b-instruct-v1:0": + case "meta.llama3-3-70b-instruct-v1:0": + case "meta.llama4-maverick-17b-instruct-v1:0": + case "meta.llama4-scout-17b-instruct-v1:0": + try { + setLlamaText(result, message); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + break; + + default: + throw new IllegalStateException("Unexpected model: " + modelId); } } @@ -298,6 +373,18 @@ public class BedrockProducer extends DefaultProducer { message.setBody(jsonString); } + private void setCohereText(InvokeModelResponse result, Message message) throws JsonProcessingException { + ObjectMapper mapper = new ObjectMapper(); + JsonNode jsonString = mapper.readTree(result.body().asUtf8String()); + message.setBody(jsonString.get("text")); + } + + private void setLlamaText(InvokeModelResponse result, Message message) throws JsonProcessingException { + ObjectMapper mapper = new ObjectMapper(); + JsonNode jsonString = mapper.readTree(result.body().asUtf8String()); + message.setBody(jsonString.get("generation")); + } + public static Message getMessageForResponse(final Exchange exchange) { return exchange.getMessage(); } diff --git a/components/camel-aws/camel-aws-bedrock/src/test/java/org/apache/camel/component/aws2/bedrock/BedrockModelsTest.java b/components/camel-aws/camel-aws-bedrock/src/test/java/org/apache/camel/component/aws2/bedrock/BedrockModelsTest.java new file mode 100644 index 00000000000..bb021d781f6 --- /dev/null +++ b/components/camel-aws/camel-aws-bedrock/src/test/java/org/apache/camel/component/aws2/bedrock/BedrockModelsTest.java @@ -0,0 +1,178 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.component.aws2.bedrock; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; + +import static org.junit.jupiter.api.Assertions.*; + +class BedrockModelsTest { + + @ParameterizedTest + @EnumSource(BedrockModels.class) + void testAllModelsHaveValidModelIds(BedrockModels model) { + assertNotNull(model.model, "Model ID should not be null for " + model.name()); + assertFalse(model.model.isEmpty(), "Model ID should not be empty for " + model.name()); + assertTrue(model.model.contains("."), "Model ID should contain provider prefix for " + model.name()); + } + + @Test + void testAmazonTitanModels() { + assertEquals("amazon.titan-text-express-v1", BedrockModels.TITAN_TEXT_EXPRESS_V1.model); + assertEquals("amazon.titan-text-lite-v1", BedrockModels.TITAN_TEXT_LITE_V1.model); + assertEquals("amazon.titan-image-generator-v1", BedrockModels.TITAN_IMAGE_GENERATOR_V1.model); + assertEquals("amazon.titan-embed-text-v1", BedrockModels.TITAN_EMBEDDINGS_G1.model); + assertEquals("amazon.titan-embed-image-v1", BedrockModels.TITAN_MULTIMODAL_EMBEDDINGS_G1.model); + assertEquals("amazon.titan-text-premier-v1:0", BedrockModels.TITAN_TEXT_PREMIER_V1.model); + assertEquals("amazon.titan-embed-text-v2:0", BedrockModels.TITAN_TEXT_EMBEDDINGS_V2.model); + assertEquals("amazon.titan-image-generator-v2:0", BedrockModels.TITAN_IMAGE_GENERATOR_V2.model); + } + + @Test + void testAmazonNovaModels() { + assertEquals("amazon.nova-canvas-v1:0", BedrockModels.NOVA_CANVAS_V1.model); + assertEquals("amazon.nova-lite-v1:0", BedrockModels.NOVA_LITE_V1.model); + assertEquals("amazon.nova-micro-v1:0", BedrockModels.NOVA_MICRO_V1.model); + assertEquals("amazon.nova-premier-v1:0", BedrockModels.NOVA_PREMIER_V1.model); + assertEquals("amazon.nova-pro-v1:0", BedrockModels.NOVA_PRO_V1.model); + assertEquals("amazon.nova-reel-v1:0", BedrockModels.NOVA_REEL_V1.model); + assertEquals("amazon.nova-reel-v1:1", BedrockModels.NOVA_REEL_V1_1.model); + assertEquals("amazon.nova-sonic-v1:0", BedrockModels.NOVA_SONIC_V1.model); + } + + @Test + void testAmazonRerankModel() { + assertEquals("amazon.rerank-v1:0", BedrockModels.RERANK_V1.model); + } + + @Test + void testAI21LabsModels() { + assertEquals("ai21.j2-ultra-v1", BedrockModels.JURASSIC2_ULTRA.model); + assertEquals("ai21.j2-mid-v1", BedrockModels.JURASSIC2_MID.model); + assertEquals("ai21.jamba-1-5-large-v1:0", BedrockModels.JAMBA_1_5_LARGE.model); + assertEquals("ai21.jamba-1-5-mini-v1:0", BedrockModels.JAMBA_1_5_MINI.model); + } + + @Test + void testAnthropicClaudeModels() { + assertEquals("anthropic.claude-instant-v1", BedrockModels.ANTROPHIC_CLAUDE_INSTANT_V1.model); + assertEquals("anthropic.claude-v2", BedrockModels.ANTROPHIC_CLAUDE_V2.model); + assertEquals("anthropic.claude-v2:1", BedrockModels.ANTROPHIC_CLAUDE_V2_1.model); + assertEquals("anthropic.claude-3-sonnet-20240229-v1:0", BedrockModels.ANTROPHIC_CLAUDE_V3.model); + assertEquals("anthropic.claude-3-5-sonnet-20240620-v1:0", BedrockModels.ANTROPHIC_CLAUDE_V35.model); + assertEquals("anthropic.claude-3-5-sonnet-20241022-v2:0", BedrockModels.ANTROPHIC_CLAUDE_V35_2.model); + assertEquals("anthropic.claude-3-haiku-20240307-v1:0", BedrockModels.ANTROPHIC_CLAUDE_HAIKU_V3.model); + assertEquals("anthropic.claude-3-5-haiku-20241022-v1:0", BedrockModels.ANTROPHIC_CLAUDE_HAIKU_V35.model); + assertEquals("anthropic.claude-3-opus-20240229-v1:0", BedrockModels.ANTROPHIC_CLAUDE_OPUS_V3.model); + assertEquals("anthropic.claude-3-7-sonnet-20250219-v1:0", BedrockModels.ANTROPHIC_CLAUDE_V37.model); + assertEquals("anthropic.claude-opus-4-1-20250805-v1:0", BedrockModels.ANTROPHIC_CLAUDE_OPUS_V4_1.model); + assertEquals("anthropic.claude-opus-4-20250514-v1:0", BedrockModels.ANTROPHIC_CLAUDE_OPUS_V4.model); + assertEquals("anthropic.claude-sonnet-4-20250514-v1:0", BedrockModels.ANTROPHIC_CLAUDE_SONNET_V4.model); + } + + @Test + void testCohereModels() { + assertEquals("cohere.command-r-plus-v1:0", BedrockModels.COHERE_COMMAND_R_PLUS.model); + assertEquals("cohere.command-r-v1:0", BedrockModels.COHERE_COMMAND_R.model); + assertEquals("cohere.embed-english-v3", BedrockModels.COHERE_EMBED_ENGLISH_V3.model); + assertEquals("cohere.embed-multilingual-v3", BedrockModels.COHERE_EMBED_MULTILINGUAL_V3.model); + assertEquals("cohere.rerank-v3-5:0", BedrockModels.COHERE_RERANK_V3_5.model); + } + + @Test + void testMetaLlamaModels() { + assertEquals("meta.llama3-8b-instruct-v1:0", BedrockModels.LLAMA3_8B_INSTRUCT.model); + assertEquals("meta.llama3-70b-instruct-v1:0", BedrockModels.LLAMA3_70B_INSTRUCT.model); + assertEquals("meta.llama3-1-8b-instruct-v1:0", BedrockModels.LLAMA3_1_8B_INSTRUCT.model); + assertEquals("meta.llama3-1-70b-instruct-v1:0", BedrockModels.LLAMA3_1_70B_INSTRUCT.model); + assertEquals("meta.llama3-1-405b-instruct-v1:0", BedrockModels.LLAMA3_1_405B_INSTRUCT.model); + assertEquals("meta.llama3-2-1b-instruct-v1:0", BedrockModels.LLAMA3_2_1B_INSTRUCT.model); + assertEquals("meta.llama3-2-3b-instruct-v1:0", BedrockModels.LLAMA3_2_3B_INSTRUCT.model); + assertEquals("meta.llama3-2-11b-instruct-v1:0", BedrockModels.LLAMA3_2_11B_INSTRUCT.model); + assertEquals("meta.llama3-2-90b-instruct-v1:0", BedrockModels.LLAMA3_2_90B_INSTRUCT.model); + assertEquals("meta.llama3-3-70b-instruct-v1:0", BedrockModels.LLAMA3_3_70B_INSTRUCT.model); + assertEquals("meta.llama4-maverick-17b-instruct-v1:0", BedrockModels.LLAMA4_MAVERICK_17B_INSTRUCT.model); + assertEquals("meta.llama4-scout-17b-instruct-v1:0", BedrockModels.LLAMA4_SCOUT_17B_INSTRUCT.model); + } + + @Test + void testMistralAIModels() { + assertEquals("mistral.mistral-7b-instruct-v0:2", BedrockModels.MISTRAL_7B_INSTRUCT.model); + assertEquals("mistral.mixtral-8x7b-instruct-v0:1", BedrockModels.MISTRAL_8x7B_INSTRUCT.model); + assertEquals("mistral.mistral-large-2402-v1:0", BedrockModels.MISTRAL_LARGE.model); + assertEquals("mistral.mistral-large-2407-v1:0", BedrockModels.MISTRAL_LARGE_2407.model); + assertEquals("mistral.mistral-small-2402-v1:0", BedrockModels.MISTRAL_SMALL_2402.model); + assertEquals("mistral.pixtral-large-2502-v1:0", BedrockModels.PIXTRAL_LARGE.model); + } + + @Test + void testStabilityAIModels() { + assertEquals("stability.sd3-5-large-v1:0", BedrockModels.STABLE_DIFFUSION_3_5_LARGE.model); + assertEquals("stability.stable-image-control-sketch-v1:0", BedrockModels.STABLE_IMAGE_CONTROL_SKETCH.model); + assertEquals("stability.stable-image-control-structure-v1:0", BedrockModels.STABLE_IMAGE_CONTROL_STRUCTURE.model); + assertEquals("stability.stable-image-core-v1:1", BedrockModels.STABLE_IMAGE_CORE.model); + } + + @Test + void testModelCategorization() { + assertTrue(BedrockModels.TITAN_TEXT_EXPRESS_V1.model.startsWith("amazon.")); + assertTrue(BedrockModels.JAMBA_1_5_LARGE.model.startsWith("ai21.")); + assertTrue(BedrockModels.ANTROPHIC_CLAUDE_V3.model.startsWith("anthropic.")); + assertTrue(BedrockModels.COHERE_COMMAND_R.model.startsWith("cohere.")); + assertTrue(BedrockModels.LLAMA3_8B_INSTRUCT.model.startsWith("meta.")); + assertTrue(BedrockModels.MISTRAL_7B_INSTRUCT.model.startsWith("mistral.")); + assertTrue(BedrockModels.STABLE_DIFFUSION_3_5_LARGE.model.startsWith("stability.")); + } + + @Test + void testEnumCountAndNewModels() { + BedrockModels[] allModels = BedrockModels.values(); + assertEquals(61, allModels.length, "Should have exactly 61 models including new ones"); + + boolean hasNovaModels = false; + boolean hasJambaModels = false; + boolean hasLlamaModels = false; + boolean hasCohereModels = false; + boolean hasStabilityModels = false; + + for (BedrockModels model : allModels) { + if (model.model.contains("nova")) { + hasNovaModels = true; + } + if (model.model.contains("jamba")) { + hasJambaModels = true; + } + if (model.model.contains("llama")) { + hasLlamaModels = true; + } + if (model.model.startsWith("cohere.")) { + hasCohereModels = true; + } + if (model.model.startsWith("stability.")) { + hasStabilityModels = true; + } + } + + assertTrue(hasNovaModels, "Should include Amazon Nova models"); + assertTrue(hasJambaModels, "Should include AI21 Jamba models"); + assertTrue(hasLlamaModels, "Should include Meta Llama models"); + assertTrue(hasCohereModels, "Should include Cohere models"); + assertTrue(hasStabilityModels, "Should include Stability AI models"); + } +} diff --git a/components/camel-aws/camel-aws-bedrock/src/test/java/org/apache/camel/component/aws2/bedrock/runtime/integration/BedrockProducerIT.java b/components/camel-aws/camel-aws-bedrock/src/test/java/org/apache/camel/component/aws2/bedrock/runtime/integration/BedrockProducerIT.java index 8a7bc51730a..96c2a67f4dc 100644 --- a/components/camel-aws/camel-aws-bedrock/src/test/java/org/apache/camel/component/aws2/bedrock/runtime/integration/BedrockProducerIT.java +++ b/components/camel-aws/camel-aws-bedrock/src/test/java/org/apache/camel/component/aws2/bedrock/runtime/integration/BedrockProducerIT.java @@ -507,6 +507,400 @@ class BedrockProducerIT extends CamelTestSupport { MockEndpoint.assertIsSatisfied(context); } + @Test + public void testInvokeNovaLiteModel() throws InterruptedException { + + result.expectedMessageCount(1); + final Exchange result = template.send("direct:send_nova_lite", exchange -> { + ObjectMapper mapper = new ObjectMapper(); + ObjectNode rootNode = mapper.createObjectNode(); + + ArrayNode messages = mapper.createArrayNode(); + + ObjectNode element = mapper.createObjectNode(); + element.putIfAbsent("role", new TextNode("user")); + + ArrayNode content = mapper.createArrayNode(); + + ObjectNode textContent = mapper.createObjectNode(); + + textContent.putIfAbsent("type", new TextNode("text")); + textContent.putIfAbsent("text", new TextNode("Can you tell the history of Mayflower?")); + + content.add(textContent); + + element.putIfAbsent("content", content); + + messages.add(element); + + rootNode.putIfAbsent("messages", messages); + rootNode.putIfAbsent("max_tokens", new IntNode(1000)); + + exchange.getMessage().setBody(mapper.writer().writeValueAsString(rootNode)); + exchange.getMessage().setHeader(BedrockConstants.MODEL_CONTENT_TYPE, "application/json"); + exchange.getMessage().setHeader(BedrockConstants.MODEL_ACCEPT_CONTENT_TYPE, "application/json"); + }); + + MockEndpoint.assertIsSatisfied(context); + } + + @Test + public void testInvokeJamba15LargeModel() throws InterruptedException { + + result.expectedMessageCount(1); + final Exchange result = template.send("direct:send_jamba_15_large", exchange -> { + ObjectMapper mapper = new ObjectMapper(); + ObjectNode rootNode = mapper.createObjectNode(); + + ArrayNode messages = mapper.createArrayNode(); + + ObjectNode element = mapper.createObjectNode(); + element.putIfAbsent("role", new TextNode("user")); + element.putIfAbsent("content", new TextNode("Can you tell the history of Mayflower?")); + + messages.add(element); + + rootNode.putIfAbsent("messages", messages); + rootNode.putIfAbsent("max_tokens", new IntNode(1000)); + rootNode.putIfAbsent("temperature", new DoubleNode(0.7)); + rootNode.putIfAbsent("top_p", new DoubleNode(0.9)); + + exchange.getMessage().setBody(mapper.writer().writeValueAsString(rootNode)); + exchange.getMessage().setHeader(BedrockConstants.MODEL_CONTENT_TYPE, "application/json"); + exchange.getMessage().setHeader(BedrockConstants.MODEL_ACCEPT_CONTENT_TYPE, "application/json"); + }); + + MockEndpoint.assertIsSatisfied(context); + } + + @Test + public void testInvokeCohereCommandRModel() throws InterruptedException { + + result.expectedMessageCount(1); + final Exchange result = template.send("direct:send_cohere_command_r", exchange -> { + ObjectMapper mapper = new ObjectMapper(); + ObjectNode rootNode = mapper.createObjectNode(); + rootNode.putIfAbsent("message", new TextNode("Can you tell the history of Mayflower?")); + rootNode.putIfAbsent("max_tokens", new IntNode(1000)); + rootNode.putIfAbsent("temperature", new DoubleNode(0.7)); + rootNode.putIfAbsent("p", new DoubleNode(0.9)); + + exchange.getMessage().setBody(mapper.writer().writeValueAsString(rootNode)); + exchange.getMessage().setHeader(BedrockConstants.MODEL_CONTENT_TYPE, "application/json"); + exchange.getMessage().setHeader(BedrockConstants.MODEL_ACCEPT_CONTENT_TYPE, "application/json"); + }); + + MockEndpoint.assertIsSatisfied(context); + } + + @Test + public void testInvokeLlama38BInstructModel() throws InterruptedException { + + result.expectedMessageCount(1); + final Exchange result = template.send("direct:send_llama3_8b_instruct", exchange -> { + ObjectMapper mapper = new ObjectMapper(); + ObjectNode rootNode = mapper.createObjectNode(); + rootNode.putIfAbsent("prompt", new TextNode( + "<|begin_of_text|><|start_header_id|>user<|end_header_id|>\n\nCan you tell the history of Mayflower?<|eot_id|><|start_header_id|>assistant<|end_header_id|>\n\n")); + rootNode.putIfAbsent("max_gen_len", new IntNode(1000)); + rootNode.putIfAbsent("temperature", new DoubleNode(0.7)); + rootNode.putIfAbsent("top_p", new DoubleNode(0.9)); + + exchange.getMessage().setBody(mapper.writer().writeValueAsString(rootNode)); + exchange.getMessage().setHeader(BedrockConstants.MODEL_CONTENT_TYPE, "application/json"); + exchange.getMessage().setHeader(BedrockConstants.MODEL_ACCEPT_CONTENT_TYPE, "application/json"); + }); + + MockEndpoint.assertIsSatisfied(context); + } + + @Test + public void testInvokeMistralLarge2407Model() throws InterruptedException { + + result.expectedMessageCount(1); + final Exchange result = template.send("direct:send_mistral_large_2407", exchange -> { + ObjectMapper mapper = new ObjectMapper(); + ObjectNode rootNode = mapper.createObjectNode(); + rootNode.putIfAbsent("prompt", + new TextNode("\"<s>[INST] Can you tell the history of Mayflower? [/INST]\\\"")); + + rootNode.putIfAbsent("max_tokens", new IntNode(300)); + rootNode.putIfAbsent("temperature", new DoubleNode(0.5)); + rootNode.putIfAbsent("top_p", new DoubleNode(0.9)); + rootNode.putIfAbsent("top_k", new IntNode(50)); + + exchange.getMessage().setBody(mapper.writer().writeValueAsString(rootNode)); + exchange.getMessage().setHeader(BedrockConstants.MODEL_CONTENT_TYPE, "application/json"); + exchange.getMessage().setHeader(BedrockConstants.MODEL_ACCEPT_CONTENT_TYPE, "application/json"); + }); + + MockEndpoint.assertIsSatisfied(context); + } + + @Test + public void testInvokeNovaMicroModel() throws InterruptedException { + + result.expectedMessageCount(1); + final Exchange result = template.send("direct:send_nova_micro", exchange -> { + ObjectMapper mapper = new ObjectMapper(); + ObjectNode rootNode = mapper.createObjectNode(); + + ArrayNode messages = mapper.createArrayNode(); + + ObjectNode element = mapper.createObjectNode(); + element.putIfAbsent("role", new TextNode("user")); + + ArrayNode content = mapper.createArrayNode(); + + ObjectNode textContent = mapper.createObjectNode(); + + textContent.putIfAbsent("type", new TextNode("text")); + textContent.putIfAbsent("text", new TextNode("Can you tell the history of Mayflower?")); + + content.add(textContent); + + element.putIfAbsent("content", content); + + messages.add(element); + + rootNode.putIfAbsent("messages", messages); + rootNode.putIfAbsent("max_tokens", new IntNode(1000)); + + exchange.getMessage().setBody(mapper.writer().writeValueAsString(rootNode)); + exchange.getMessage().setHeader(BedrockConstants.MODEL_CONTENT_TYPE, "application/json"); + exchange.getMessage().setHeader(BedrockConstants.MODEL_ACCEPT_CONTENT_TYPE, "application/json"); + }); + + MockEndpoint.assertIsSatisfied(context); + } + + @Test + public void testInvokeNovaProModel() throws InterruptedException { + + result.expectedMessageCount(1); + final Exchange result = template.send("direct:send_nova_pro", exchange -> { + ObjectMapper mapper = new ObjectMapper(); + ObjectNode rootNode = mapper.createObjectNode(); + + ArrayNode messages = mapper.createArrayNode(); + + ObjectNode element = mapper.createObjectNode(); + element.putIfAbsent("role", new TextNode("user")); + + ArrayNode content = mapper.createArrayNode(); + + ObjectNode textContent = mapper.createObjectNode(); + + textContent.putIfAbsent("type", new TextNode("text")); + textContent.putIfAbsent("text", new TextNode("Can you tell the history of Mayflower?")); + + content.add(textContent); + + element.putIfAbsent("content", content); + + messages.add(element); + + rootNode.putIfAbsent("messages", messages); + rootNode.putIfAbsent("max_tokens", new IntNode(1000)); + + exchange.getMessage().setBody(mapper.writer().writeValueAsString(rootNode)); + exchange.getMessage().setHeader(BedrockConstants.MODEL_CONTENT_TYPE, "application/json"); + exchange.getMessage().setHeader(BedrockConstants.MODEL_ACCEPT_CONTENT_TYPE, "application/json"); + }); + + MockEndpoint.assertIsSatisfied(context); + } + + @Test + public void testInvokeJamba15MiniModel() throws InterruptedException { + + result.expectedMessageCount(1); + final Exchange result = template.send("direct:send_jamba_15_mini", exchange -> { + ObjectMapper mapper = new ObjectMapper(); + ObjectNode rootNode = mapper.createObjectNode(); + + ArrayNode messages = mapper.createArrayNode(); + + ObjectNode element = mapper.createObjectNode(); + element.putIfAbsent("role", new TextNode("user")); + element.putIfAbsent("content", new TextNode("Can you tell the history of Mayflower?")); + + messages.add(element); + + rootNode.putIfAbsent("messages", messages); + rootNode.putIfAbsent("max_tokens", new IntNode(1000)); + rootNode.putIfAbsent("temperature", new DoubleNode(0.7)); + rootNode.putIfAbsent("top_p", new DoubleNode(0.9)); + + exchange.getMessage().setBody(mapper.writer().writeValueAsString(rootNode)); + exchange.getMessage().setHeader(BedrockConstants.MODEL_CONTENT_TYPE, "application/json"); + exchange.getMessage().setHeader(BedrockConstants.MODEL_ACCEPT_CONTENT_TYPE, "application/json"); + }); + + MockEndpoint.assertIsSatisfied(context); + } + + @Test + public void testInvokeCohereCommandRPlusModel() throws InterruptedException { + + result.expectedMessageCount(1); + final Exchange result = template.send("direct:send_cohere_command_r_plus", exchange -> { + ObjectMapper mapper = new ObjectMapper(); + ObjectNode rootNode = mapper.createObjectNode(); + rootNode.putIfAbsent("message", new TextNode("Can you tell the history of Mayflower?")); + rootNode.putIfAbsent("max_tokens", new IntNode(1000)); + rootNode.putIfAbsent("temperature", new DoubleNode(0.7)); + rootNode.putIfAbsent("p", new DoubleNode(0.9)); + + exchange.getMessage().setBody(mapper.writer().writeValueAsString(rootNode)); + exchange.getMessage().setHeader(BedrockConstants.MODEL_CONTENT_TYPE, "application/json"); + exchange.getMessage().setHeader(BedrockConstants.MODEL_ACCEPT_CONTENT_TYPE, "application/json"); + }); + + MockEndpoint.assertIsSatisfied(context); + } + + @Test + public void testInvokeLlama31_70BInstructModel() throws InterruptedException { + + result.expectedMessageCount(1); + final Exchange result = template.send("direct:send_llama31_70b_instruct", exchange -> { + ObjectMapper mapper = new ObjectMapper(); + ObjectNode rootNode = mapper.createObjectNode(); + rootNode.putIfAbsent("prompt", new TextNode( + "<|begin_of_text|><|start_header_id|>user<|end_header_id|>\n\nCan you tell the history of Mayflower?<|eot_id|><|start_header_id|>assistant<|end_header_id|>\n\n")); + rootNode.putIfAbsent("max_gen_len", new IntNode(1000)); + rootNode.putIfAbsent("temperature", new DoubleNode(0.7)); + rootNode.putIfAbsent("top_p", new DoubleNode(0.9)); + + exchange.getMessage().setBody(mapper.writer().writeValueAsString(rootNode)); + exchange.getMessage().setHeader(BedrockConstants.MODEL_CONTENT_TYPE, "application/json"); + exchange.getMessage().setHeader(BedrockConstants.MODEL_ACCEPT_CONTENT_TYPE, "application/json"); + }); + + MockEndpoint.assertIsSatisfied(context); + } + + @Test + public void testInvokeLlama32_11BInstructModel() throws InterruptedException { + + result.expectedMessageCount(1); + final Exchange result = template.send("direct:send_llama32_11b_instruct", exchange -> { + ObjectMapper mapper = new ObjectMapper(); + ObjectNode rootNode = mapper.createObjectNode(); + rootNode.putIfAbsent("prompt", new TextNode( + "<|begin_of_text|><|start_header_id|>user<|end_header_id|>\n\nCan you tell the history of Mayflower?<|eot_id|><|start_header_id|>assistant<|end_header_id|>\n\n")); + rootNode.putIfAbsent("max_gen_len", new IntNode(1000)); + rootNode.putIfAbsent("temperature", new DoubleNode(0.7)); + rootNode.putIfAbsent("top_p", new DoubleNode(0.9)); + + exchange.getMessage().setBody(mapper.writer().writeValueAsString(rootNode)); + exchange.getMessage().setHeader(BedrockConstants.MODEL_CONTENT_TYPE, "application/json"); + exchange.getMessage().setHeader(BedrockConstants.MODEL_ACCEPT_CONTENT_TYPE, "application/json"); + }); + + MockEndpoint.assertIsSatisfied(context); + } + + @Test + public void testInvokeAnthropicClaude35Sonnet2Model() throws InterruptedException { + + result.expectedMessageCount(1); + final Exchange result = template.send("direct:send_anthropic_claude_35_sonnet_2", exchange -> { + ObjectMapper mapper = new ObjectMapper(); + ObjectNode rootNode = mapper.createObjectNode(); + + ArrayNode messages = mapper.createArrayNode(); + + ObjectNode element = mapper.createObjectNode(); + element.putIfAbsent("role", new TextNode("user")); + + ArrayNode content = mapper.createArrayNode(); + + ObjectNode textContent = mapper.createObjectNode(); + + textContent.putIfAbsent("type", new TextNode("text")); + textContent.putIfAbsent("text", new TextNode("Can you tell the history of Mayflower?")); + + content.add(textContent); + + element.putIfAbsent("content", content); + + messages.add(element); + + rootNode.putIfAbsent("messages", messages); + rootNode.putIfAbsent("max_tokens", new IntNode(1000)); + rootNode.putIfAbsent("anthropic_version", new TextNode("bedrock-2023-05-31")); + + exchange.getMessage().setBody(mapper.writer().writeValueAsString(rootNode)); + exchange.getMessage().setHeader(BedrockConstants.MODEL_CONTENT_TYPE, "application/json"); + exchange.getMessage().setHeader(BedrockConstants.MODEL_ACCEPT_CONTENT_TYPE, "application/json"); + }); + + MockEndpoint.assertIsSatisfied(context); + } + + @Test + public void testInvokeAnthropicClaude35HaikuModel() throws InterruptedException { + + result.expectedMessageCount(1); + final Exchange result = template.send("direct:send_anthropic_claude_35_haiku", exchange -> { + ObjectMapper mapper = new ObjectMapper(); + ObjectNode rootNode = mapper.createObjectNode(); + + ArrayNode messages = mapper.createArrayNode(); + + ObjectNode element = mapper.createObjectNode(); + element.putIfAbsent("role", new TextNode("user")); + + ArrayNode content = mapper.createArrayNode(); + + ObjectNode textContent = mapper.createObjectNode(); + + textContent.putIfAbsent("type", new TextNode("text")); + textContent.putIfAbsent("text", new TextNode("Can you tell the history of Mayflower?")); + + content.add(textContent); + + element.putIfAbsent("content", content); + + messages.add(element); + + rootNode.putIfAbsent("messages", messages); + rootNode.putIfAbsent("max_tokens", new IntNode(1000)); + rootNode.putIfAbsent("anthropic_version", new TextNode("bedrock-2023-05-31")); + + exchange.getMessage().setBody(mapper.writer().writeValueAsString(rootNode)); + exchange.getMessage().setHeader(BedrockConstants.MODEL_CONTENT_TYPE, "application/json"); + exchange.getMessage().setHeader(BedrockConstants.MODEL_ACCEPT_CONTENT_TYPE, "application/json"); + }); + + MockEndpoint.assertIsSatisfied(context); + } + + @Test + public void testInvokeMistralSmall2402Model() throws InterruptedException { + + result.expectedMessageCount(1); + final Exchange result = template.send("direct:send_mistral_small_2402", exchange -> { + ObjectMapper mapper = new ObjectMapper(); + ObjectNode rootNode = mapper.createObjectNode(); + rootNode.putIfAbsent("prompt", + new TextNode("\"<s>[INST] Can you tell the history of Mayflower? [/INST]\\\"")); + + rootNode.putIfAbsent("max_tokens", new IntNode(300)); + rootNode.putIfAbsent("temperature", new DoubleNode(0.5)); + rootNode.putIfAbsent("top_p", new DoubleNode(0.9)); + rootNode.putIfAbsent("top_k", new IntNode(50)); + + exchange.getMessage().setBody(mapper.writer().writeValueAsString(rootNode)); + exchange.getMessage().setHeader(BedrockConstants.MODEL_CONTENT_TYPE, "application/json"); + exchange.getMessage().setHeader(BedrockConstants.MODEL_ACCEPT_CONTENT_TYPE, "application/json"); + }); + + MockEndpoint.assertIsSatisfied(context); + } + @Override protected RouteBuilder createRouteBuilder() { return new RouteBuilder() { @@ -613,6 +1007,90 @@ class BedrockProducerIT extends CamelTestSupport { + BedrockModels.MISTRAL_LARGE.model) .log("Completions: ${body}") .to(result); + + from("direct:send_nova_lite") + .to("aws-bedrock:label?accessKey=RAW({{aws.manual.access.key}})&secretKey=RAW({{aws.manual.secret.key}})®ion=us-east-1&operation=invokeTextModel&modelId=" + + BedrockModels.NOVA_LITE_V1.model) + .log("Completions: ${body}") + .to(result); + + from("direct:send_jamba_15_large") + .to("aws-bedrock:label?accessKey=RAW({{aws.manual.access.key}})&secretKey=RAW({{aws.manual.secret.key}})®ion=us-east-1&operation=invokeTextModel&modelId=" + + BedrockModels.JAMBA_1_5_LARGE.model) + .log("Completions: ${body}") + .to(result); + + from("direct:send_cohere_command_r") + .to("aws-bedrock:label?accessKey=RAW({{aws.manual.access.key}})&secretKey=RAW({{aws.manual.secret.key}})®ion=us-east-1&operation=invokeTextModel&modelId=" + + BedrockModels.COHERE_COMMAND_R.model) + .log("Completions: ${body}") + .to(result); + + from("direct:send_llama3_8b_instruct") + .to("aws-bedrock:label?accessKey=RAW({{aws.manual.access.key}})&secretKey=RAW({{aws.manual.secret.key}})®ion=us-east-1&operation=invokeTextModel&modelId=" + + BedrockModels.LLAMA3_8B_INSTRUCT.model) + .log("Completions: ${body}") + .to(result); + + from("direct:send_mistral_large_2407") + .to("aws-bedrock:label?accessKey=RAW({{aws.manual.access.key}})&secretKey=RAW({{aws.manual.secret.key}})®ion=us-east-1&operation=invokeTextModel&modelId=" + + BedrockModels.MISTRAL_LARGE_2407.model) + .log("Completions: ${body}") + .to(result); + + from("direct:send_nova_micro") + .to("aws-bedrock:label?accessKey=RAW({{aws.manual.access.key}})&secretKey=RAW({{aws.manual.secret.key}})®ion=us-east-1&operation=invokeTextModel&modelId=" + + BedrockModels.NOVA_MICRO_V1.model) + .log("Completions: ${body}") + .to(result); + + from("direct:send_nova_pro") + .to("aws-bedrock:label?accessKey=RAW({{aws.manual.access.key}})&secretKey=RAW({{aws.manual.secret.key}})®ion=us-east-1&operation=invokeTextModel&modelId=" + + BedrockModels.NOVA_PRO_V1.model) + .log("Completions: ${body}") + .to(result); + + from("direct:send_jamba_15_mini") + .to("aws-bedrock:label?accessKey=RAW({{aws.manual.access.key}})&secretKey=RAW({{aws.manual.secret.key}})®ion=us-east-1&operation=invokeTextModel&modelId=" + + BedrockModels.JAMBA_1_5_MINI.model) + .log("Completions: ${body}") + .to(result); + + from("direct:send_cohere_command_r_plus") + .to("aws-bedrock:label?accessKey=RAW({{aws.manual.access.key}})&secretKey=RAW({{aws.manual.secret.key}})®ion=us-east-1&operation=invokeTextModel&modelId=" + + BedrockModels.COHERE_COMMAND_R_PLUS.model) + .log("Completions: ${body}") + .to(result); + + from("direct:send_llama31_70b_instruct") + .to("aws-bedrock:label?accessKey=RAW({{aws.manual.access.key}})&secretKey=RAW({{aws.manual.secret.key}})®ion=us-east-1&operation=invokeTextModel&modelId=" + + BedrockModels.LLAMA3_1_70B_INSTRUCT.model) + .log("Completions: ${body}") + .to(result); + + from("direct:send_llama32_11b_instruct") + .to("aws-bedrock:label?accessKey=RAW({{aws.manual.access.key}})&secretKey=RAW({{aws.manual.secret.key}})®ion=us-east-1&operation=invokeTextModel&modelId=" + + BedrockModels.LLAMA3_2_11B_INSTRUCT.model) + .log("Completions: ${body}") + .to(result); + + from("direct:send_anthropic_claude_35_sonnet_2") + .to("aws-bedrock:label?accessKey=RAW({{aws.manual.access.key}})&secretKey=RAW({{aws.manual.secret.key}})®ion=us-east-1&operation=invokeTextModel&modelId=" + + BedrockModels.ANTROPHIC_CLAUDE_V35_2.model) + .log("Completions: ${body}") + .to(result); + + from("direct:send_anthropic_claude_35_haiku") + .to("aws-bedrock:label?accessKey=RAW({{aws.manual.access.key}})&secretKey=RAW({{aws.manual.secret.key}})®ion=us-east-1&operation=invokeTextModel&modelId=" + + BedrockModels.ANTROPHIC_CLAUDE_HAIKU_V35.model) + .log("Completions: ${body}") + .to(result); + + from("direct:send_mistral_small_2402") + .to("aws-bedrock:label?accessKey=RAW({{aws.manual.access.key}})&secretKey=RAW({{aws.manual.secret.key}})®ion=us-east-1&operation=invokeTextModel&modelId=" + + BedrockModels.MISTRAL_SMALL_2402.model) + .log("Completions: ${body}") + .to(result); } }; }
