This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new 1452a4594383 CAMEL-26071: camel-aws-bedrock -
aws-bedrock-agent-runtime: invokeFlow converts the body, and the operation
header works on an endpoint of another operation (#28894)
1452a4594383 is described below
commit 1452a45943835fa41391339a982e008af73957b6
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon Nov 2 20:36:38 2026 +0100
CAMEL-26071: camel-aws-bedrock - aws-bedrock-agent-runtime: invokeFlow
converts the body, and the operation header works on an endpoint of another
operation (#28894)
…w converts the body, and the operation header works on an endpoint of
another operation
invokeFlow failed for a byte[], stream or file body; it now converts the
body
to the text of the input node, as invokeAgent does. The async client that
invokeFlow, invokeAgent and invokeInlineAgent need was only created for an
endpoint of these operations, so picking one with the operation header
failed;
it is now created on first use. No operation fails with a message instead
of a
NullPointerException. modelId and knowledgeBaseId are no longer marked
required,
the citations header type is List<Citation>, and the doc names the right
client.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: Claus Ibsen <[email protected]>
---
.../components/aws-bedrock-agent-runtime.json | 12 +-
.../docs/aws-bedrock-agent-runtime-component.adoc | 12 +-
.../agentruntime/aws-bedrock-agent-runtime.json | 12 +-
.../docs/aws-bedrock-agent-runtime-component.adoc | 12 +-
.../BedrockAgentRuntimeConfiguration.java | 6 +-
.../agentruntime/BedrockAgentRuntimeConstants.java | 2 +-
.../agentruntime/BedrockAgentRuntimeEndpoint.java | 37 +++++-
.../agentruntime/BedrockAgentRuntimeProducer.java | 51 +++----
.../BedrockAgentRuntimeEndpointTest.java | 38 ++++++
.../BedrockAgentRuntimeProducerInvokeFlowTest.java | 35 +++++
.../BedrockAgentRuntimeProducerOperationTest.java | 146 +++++++++++++++++++++
...BedrockAgentRuntimeComponentBuilderFactory.java | 6 +-
.../BedrockAgentRuntimeEndpointBuilderFactory.java | 11 +-
13 files changed, 314 insertions(+), 66 deletions(-)
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/aws-bedrock-agent-runtime.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/aws-bedrock-agent-runtime.json
index 3b6d1f8fa0cd..346346285fbe 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/aws-bedrock-agent-runtime.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/aws-bedrock-agent-runtime.json
@@ -25,9 +25,9 @@
},
"componentProperties": {
"configuration": { "index": 0, "kind": "property", "displayName":
"Configuration", "group": "producer", "label": "", "required": false, "type":
"object", "javaType":
"org.apache.camel.component.aws2.bedrock.agentruntime.BedrockAgentRuntimeConfiguration",
"deprecated": false, "autowired": false, "secret": false, "description":
"Component configuration" },
- "knowledgeBaseId": { "index": 1, "kind": "property", "displayName":
"Knowledge Base Id", "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.agentruntime.BedrockAgentRuntimeConfiguration",
"configurationField": "configuration", "description": "Define the Knowledge
Base Id we are going to use" },
+ "knowledgeBaseId": { "index": 1, "kind": "property", "displayName":
"Knowledge Base Id", "group": "producer", "label": "", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.aws2.bedrock.agentruntime.BedrockAgentRuntimeConfiguration",
"configurationField": "configuration", "description": "Define the Knowledge
Base Id we are going to use (used by the retrieve a [...]
"lazyStartProducer": { "index": 2, "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": 3, "kind": "property", "displayName": "Model Id",
"group": "producer", "label": "", "required": true, "type": "enum", "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-v [...]
+ "modelId": { "index": 3, "kind": "property", "displayName": "Model Id",
"group": "producer", "label": "", "required": false, "type": "enum",
"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": 4, "kind": "property", "displayName": "Operation",
"group": "producer", "label": "", "required": true, "type": "enum", "javaType":
"org.apache.camel.component.aws2.bedrock.agentruntime.BedrockAgentRuntimeOperations",
"enum": [ "retrieveAndGenerate", "invokeFlow", "invokeAgent",
"invokeInlineAgent", "retrieve" ], "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.aws2.bedrock.agentr [...]
"overrideEndpoint": { "index": 5, "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.agentruntime.BedrockAgentRuntimeConfiguration",
"configurationField": "configuration", "description": "Set the need for
overriding the endpoint. This option ne [...]
"pojoRequest": { "index": 6, "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.agentruntime.BedrockAgentRuntimeConfiguration",
"configurationField": "configuration", "description": "If we want to use a
POJO request as body or not" },
@@ -61,7 +61,7 @@
},
"headers": {
"CamelAwsBedrockAgentRuntimeOperation": { "index": 0, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The operation we want to perform",
"constantName":
"org.apache.camel.component.aws2.bedrock.agentruntime.BedrockAgentRuntimeConstants#OPERATION"
},
- "CamelAwsBedrockAgentRuntimeCitations": { "index": 1, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "When retrieving and generating a
response, this header will contain the citations", "constantName":
"org.apache.camel.component.aws2.bedrock.agentruntime.BedrockAgentRuntimeConstants#CITATIONS"
},
+ "CamelAwsBedrockAgentRuntimeCitations": { "index": 1, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType":
"java.util.List<software.amazon.awssdk.services.bedrockagentruntime.model.Citation>",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "When retrieving and generating a response, this header
will contain the citations", "constantName":
"org.apache.camel.component.aws2.bedrock.agentrun [...]
"CamelAwsBedrockAgentRuntimeSessionId": { "index": 2, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "When retrieving and generating a
response, this header will contain he unique identifier of the session. Reuse
the same value to continue the same session with the knowledge base.",
"constantName": "org.apache.camel.component [...]
"CamelAwsBedrockAgentRuntimeAgentId": { "index": 3, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The unique identifier of the agent to
invoke. Overrides the agentId configured on the endpoint.", "constantName":
"org.apache.camel.component.aws2.bedrock.agentruntime.BedrockAgentRuntimeConstants#AGENT_ID"
},
"CamelAwsBedrockAgentRuntimeAgentAliasId": { "index": 4, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The unique identifier of the agent
alias to invoke. Overrides the agentAliasId configured on the endpoint.",
"constantName":
"org.apache.camel.component.aws2.bedrock.agentruntime.BedrockAgentRuntimeConstants#AGENT_ALIAS_ID"
},
@@ -88,13 +88,13 @@
"CamelAwsBedrockAgentRuntimeRetrieveGuardrailAction": { "index": 25,
"kind": "header", "displayName": "", "group": "producer", "label": "",
"required": false, "javaType": "String", "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false, "description":
"When performing a retrieve operation, this header will contain the guardrail
action (if any) applied by the knowledge base.", "constantName":
"org.apache.camel.component.aws2.bedrock.agentruntime.BedrockAgentRu [...]
},
"bodyTypes": {
- "producerIn": { "carriers": [ "text", "bytes", "stream", "file" ],
"byOption": [ { "option": "operation", "value": "invokeFlow", "carriers": [
"text", "list", "pojo" ] } ], "description": "The question, the query or the
input of the agent as text for retrieveAndGenerate, retrieve, invokeAgent and
invokeInlineAgent: bytes, a stream or a file are read as text, a Map or a POJO
is sent as its toString(), so take the text out of it first, and no body fails.
invokeFlow takes a String, whic [...]
+ "producerIn": { "carriers": [ "text", "bytes", "stream", "file" ],
"byOption": [ { "option": "operation", "value": "invokeFlow", "carriers": [
"text", "bytes", "stream", "file", "list", "pojo" ] } ], "description": "The
question, the query or the input of the agent as text for retrieveAndGenerate,
retrieve, invokeAgent and invokeInlineAgent: bytes, a stream or a file are read
as text, a Map or a POJO is sent as its toString(), so take the text out of it
first, and no body fails. invo [...]
"producerOut": { "carriers": [ "text", "list" ], "byOption": [ { "option":
"operation", "value": "retrieveAndGenerate", "carriers": [ "text" ],
"javaType": "java.lang.String" }, { "option": "operation", "value": "retrieve",
"carriers": [ "list" ], "javaType":
"java.util.List<software.amazon.awssdk.services.bedrockagentruntime.model.KnowledgeBaseRetrievalResult>"
}, { "option": "operation", "value": "invokeFlow", "carriers": [ "text",
"pojo", "unchanged" ] } ], "description": "The ans [...]
},
"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.agentruntime.BedrockAgentRuntimeConfiguration",
"configurationField": "configuration", "description": "Logical name" },
- "knowledgeBaseId": { "index": 1, "kind": "parameter", "displayName":
"Knowledge Base Id", "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.agentruntime.BedrockAgentRuntimeConfiguration",
"configurationField": "configuration", "description": "Define the Knowledge
Base Id we are going to use" },
- "modelId": { "index": 2, "kind": "parameter", "displayName": "Model Id",
"group": "producer", "label": "", "required": true, "type": "enum", "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- [...]
+ "knowledgeBaseId": { "index": 1, "kind": "parameter", "displayName":
"Knowledge Base Id", "group": "producer", "label": "", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.aws2.bedrock.agentruntime.BedrockAgentRuntimeConfiguration",
"configurationField": "configuration", "description": "Define the Knowledge
Base Id we are going to use (used by the retrieve [...]
+ "modelId": { "index": 2, "kind": "parameter", "displayName": "Model Id",
"group": "producer", "label": "", "required": false, "type": "enum",
"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": "parameter", "displayName":
"Operation", "group": "producer", "label": "", "required": true, "type":
"enum", "javaType":
"org.apache.camel.component.aws2.bedrock.agentruntime.BedrockAgentRuntimeOperations",
"enum": [ "retrieveAndGenerate", "invokeFlow", "invokeAgent",
"invokeInlineAgent", "retrieve" ], "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.aws2.bedrock.agent [...]
"overrideEndpoint": { "index": 4, "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.agentruntime.BedrockAgentRuntimeConfiguration",
"configurationField": "configuration", "description": "Set the need for
overriding the endpoint. This option n [...]
"pojoRequest": { "index": 5, "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.agentruntime.BedrockAgentRuntimeConfiguration",
"configurationField": "configuration", "description": "If we want to use a
POJO request as body or not" },
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/aws-bedrock-agent-runtime-component.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/aws-bedrock-agent-runtime-component.adoc
index 2bc52892e234..5fbb5407c4e5 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/aws-bedrock-agent-runtime-component.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/aws-bedrock-agent-runtime-component.adoc
@@ -44,7 +44,7 @@ include::partial$component-endpoint-headers.adoc[]
Required Bedrock component options
-You have to provide the bedrockRuntimeClient in the
+You have to provide the bedrockAgentRuntimeClient in the
Registry or your accessKey and secretKey to access
the https://aws.amazon.com/bedrock/[Amazon Bedrock] service.
@@ -60,6 +60,9 @@ The component supports the following operations on the
`operation` URI option:
* `invokeAgent` — Invoke a deployed Bedrock Agent by id/alias and stream the
response back.
* `invokeInlineAgent` — Invoke an agent defined inline at invocation time
(foundation model plus instruction).
+The `CamelAwsBedrockAgentRuntimeOperation` header overrides the `operation`
option per exchange. With neither set,
+the exchange fails with an `IllegalArgumentException`.
+
=== Retrieving from a Bedrock Knowledge Base
The `retrieve` operation performs a semantic search against a Bedrock
Knowledge Base and returns the matching chunks
@@ -89,7 +92,9 @@ following headers:
=== Invoking a Bedrock Flow
The `invokeFlow` operation streams flow events using the AWS SDK async client;
the producer transparently allocates a
-`BedrockAgentRuntimeAsyncClient` when the endpoint is configured with
`operation=invokeFlow`.
+`BedrockAgentRuntimeAsyncClient` when the endpoint is configured with
`operation=invokeFlow`. When the
+`CamelAwsBedrockAgentRuntimeOperation` header picks `invokeFlow`,
`invokeAgent` or `invokeInlineAgent` on an endpoint
+of another operation, the async client is created when the first exchange
needs it.
Mandatory options:
@@ -107,7 +112,8 @@ The body of the in-message can be:
* a `String` — wrapped in a single `FlowInput` targeting node `FlowInputNode`,
output `document`;
* a `FlowInput` instance — used as the sole input to the flow;
-* a `List<FlowInput>` — passed verbatim to the request (use this form to chain
multiple flow inputs).
+* a `List<FlowInput>` — passed verbatim to the request (use this form to chain
multiple flow inputs);
+* another body, such as a `byte[]`, an `InputStream` or a file, is converted
to a `String` and used as above.
When `pojoRequest=true`, the body must be an `InvokeFlowRequest` and is sent
unchanged.
diff --git
a/components/camel-aws/camel-aws-bedrock/src/generated/resources/META-INF/org/apache/camel/component/aws2/bedrock/agentruntime/aws-bedrock-agent-runtime.json
b/components/camel-aws/camel-aws-bedrock/src/generated/resources/META-INF/org/apache/camel/component/aws2/bedrock/agentruntime/aws-bedrock-agent-runtime.json
index 3b6d1f8fa0cd..346346285fbe 100644
---
a/components/camel-aws/camel-aws-bedrock/src/generated/resources/META-INF/org/apache/camel/component/aws2/bedrock/agentruntime/aws-bedrock-agent-runtime.json
+++
b/components/camel-aws/camel-aws-bedrock/src/generated/resources/META-INF/org/apache/camel/component/aws2/bedrock/agentruntime/aws-bedrock-agent-runtime.json
@@ -25,9 +25,9 @@
},
"componentProperties": {
"configuration": { "index": 0, "kind": "property", "displayName":
"Configuration", "group": "producer", "label": "", "required": false, "type":
"object", "javaType":
"org.apache.camel.component.aws2.bedrock.agentruntime.BedrockAgentRuntimeConfiguration",
"deprecated": false, "autowired": false, "secret": false, "description":
"Component configuration" },
- "knowledgeBaseId": { "index": 1, "kind": "property", "displayName":
"Knowledge Base Id", "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.agentruntime.BedrockAgentRuntimeConfiguration",
"configurationField": "configuration", "description": "Define the Knowledge
Base Id we are going to use" },
+ "knowledgeBaseId": { "index": 1, "kind": "property", "displayName":
"Knowledge Base Id", "group": "producer", "label": "", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.aws2.bedrock.agentruntime.BedrockAgentRuntimeConfiguration",
"configurationField": "configuration", "description": "Define the Knowledge
Base Id we are going to use (used by the retrieve a [...]
"lazyStartProducer": { "index": 2, "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": 3, "kind": "property", "displayName": "Model Id",
"group": "producer", "label": "", "required": true, "type": "enum", "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-v [...]
+ "modelId": { "index": 3, "kind": "property", "displayName": "Model Id",
"group": "producer", "label": "", "required": false, "type": "enum",
"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": 4, "kind": "property", "displayName": "Operation",
"group": "producer", "label": "", "required": true, "type": "enum", "javaType":
"org.apache.camel.component.aws2.bedrock.agentruntime.BedrockAgentRuntimeOperations",
"enum": [ "retrieveAndGenerate", "invokeFlow", "invokeAgent",
"invokeInlineAgent", "retrieve" ], "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.aws2.bedrock.agentr [...]
"overrideEndpoint": { "index": 5, "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.agentruntime.BedrockAgentRuntimeConfiguration",
"configurationField": "configuration", "description": "Set the need for
overriding the endpoint. This option ne [...]
"pojoRequest": { "index": 6, "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.agentruntime.BedrockAgentRuntimeConfiguration",
"configurationField": "configuration", "description": "If we want to use a
POJO request as body or not" },
@@ -61,7 +61,7 @@
},
"headers": {
"CamelAwsBedrockAgentRuntimeOperation": { "index": 0, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The operation we want to perform",
"constantName":
"org.apache.camel.component.aws2.bedrock.agentruntime.BedrockAgentRuntimeConstants#OPERATION"
},
- "CamelAwsBedrockAgentRuntimeCitations": { "index": 1, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "When retrieving and generating a
response, this header will contain the citations", "constantName":
"org.apache.camel.component.aws2.bedrock.agentruntime.BedrockAgentRuntimeConstants#CITATIONS"
},
+ "CamelAwsBedrockAgentRuntimeCitations": { "index": 1, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType":
"java.util.List<software.amazon.awssdk.services.bedrockagentruntime.model.Citation>",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "When retrieving and generating a response, this header
will contain the citations", "constantName":
"org.apache.camel.component.aws2.bedrock.agentrun [...]
"CamelAwsBedrockAgentRuntimeSessionId": { "index": 2, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "When retrieving and generating a
response, this header will contain he unique identifier of the session. Reuse
the same value to continue the same session with the knowledge base.",
"constantName": "org.apache.camel.component [...]
"CamelAwsBedrockAgentRuntimeAgentId": { "index": 3, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The unique identifier of the agent to
invoke. Overrides the agentId configured on the endpoint.", "constantName":
"org.apache.camel.component.aws2.bedrock.agentruntime.BedrockAgentRuntimeConstants#AGENT_ID"
},
"CamelAwsBedrockAgentRuntimeAgentAliasId": { "index": 4, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The unique identifier of the agent
alias to invoke. Overrides the agentAliasId configured on the endpoint.",
"constantName":
"org.apache.camel.component.aws2.bedrock.agentruntime.BedrockAgentRuntimeConstants#AGENT_ALIAS_ID"
},
@@ -88,13 +88,13 @@
"CamelAwsBedrockAgentRuntimeRetrieveGuardrailAction": { "index": 25,
"kind": "header", "displayName": "", "group": "producer", "label": "",
"required": false, "javaType": "String", "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false, "description":
"When performing a retrieve operation, this header will contain the guardrail
action (if any) applied by the knowledge base.", "constantName":
"org.apache.camel.component.aws2.bedrock.agentruntime.BedrockAgentRu [...]
},
"bodyTypes": {
- "producerIn": { "carriers": [ "text", "bytes", "stream", "file" ],
"byOption": [ { "option": "operation", "value": "invokeFlow", "carriers": [
"text", "list", "pojo" ] } ], "description": "The question, the query or the
input of the agent as text for retrieveAndGenerate, retrieve, invokeAgent and
invokeInlineAgent: bytes, a stream or a file are read as text, a Map or a POJO
is sent as its toString(), so take the text out of it first, and no body fails.
invokeFlow takes a String, whic [...]
+ "producerIn": { "carriers": [ "text", "bytes", "stream", "file" ],
"byOption": [ { "option": "operation", "value": "invokeFlow", "carriers": [
"text", "bytes", "stream", "file", "list", "pojo" ] } ], "description": "The
question, the query or the input of the agent as text for retrieveAndGenerate,
retrieve, invokeAgent and invokeInlineAgent: bytes, a stream or a file are read
as text, a Map or a POJO is sent as its toString(), so take the text out of it
first, and no body fails. invo [...]
"producerOut": { "carriers": [ "text", "list" ], "byOption": [ { "option":
"operation", "value": "retrieveAndGenerate", "carriers": [ "text" ],
"javaType": "java.lang.String" }, { "option": "operation", "value": "retrieve",
"carriers": [ "list" ], "javaType":
"java.util.List<software.amazon.awssdk.services.bedrockagentruntime.model.KnowledgeBaseRetrievalResult>"
}, { "option": "operation", "value": "invokeFlow", "carriers": [ "text",
"pojo", "unchanged" ] } ], "description": "The ans [...]
},
"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.agentruntime.BedrockAgentRuntimeConfiguration",
"configurationField": "configuration", "description": "Logical name" },
- "knowledgeBaseId": { "index": 1, "kind": "parameter", "displayName":
"Knowledge Base Id", "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.agentruntime.BedrockAgentRuntimeConfiguration",
"configurationField": "configuration", "description": "Define the Knowledge
Base Id we are going to use" },
- "modelId": { "index": 2, "kind": "parameter", "displayName": "Model Id",
"group": "producer", "label": "", "required": true, "type": "enum", "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- [...]
+ "knowledgeBaseId": { "index": 1, "kind": "parameter", "displayName":
"Knowledge Base Id", "group": "producer", "label": "", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.aws2.bedrock.agentruntime.BedrockAgentRuntimeConfiguration",
"configurationField": "configuration", "description": "Define the Knowledge
Base Id we are going to use (used by the retrieve [...]
+ "modelId": { "index": 2, "kind": "parameter", "displayName": "Model Id",
"group": "producer", "label": "", "required": false, "type": "enum",
"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": "parameter", "displayName":
"Operation", "group": "producer", "label": "", "required": true, "type":
"enum", "javaType":
"org.apache.camel.component.aws2.bedrock.agentruntime.BedrockAgentRuntimeOperations",
"enum": [ "retrieveAndGenerate", "invokeFlow", "invokeAgent",
"invokeInlineAgent", "retrieve" ], "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.aws2.bedrock.agent [...]
"overrideEndpoint": { "index": 4, "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.agentruntime.BedrockAgentRuntimeConfiguration",
"configurationField": "configuration", "description": "Set the need for
overriding the endpoint. This option n [...]
"pojoRequest": { "index": 5, "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.agentruntime.BedrockAgentRuntimeConfiguration",
"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/docs/aws-bedrock-agent-runtime-component.adoc
b/components/camel-aws/camel-aws-bedrock/src/main/docs/aws-bedrock-agent-runtime-component.adoc
index 2bc52892e234..5fbb5407c4e5 100644
---
a/components/camel-aws/camel-aws-bedrock/src/main/docs/aws-bedrock-agent-runtime-component.adoc
+++
b/components/camel-aws/camel-aws-bedrock/src/main/docs/aws-bedrock-agent-runtime-component.adoc
@@ -44,7 +44,7 @@ include::partial$component-endpoint-headers.adoc[]
Required Bedrock component options
-You have to provide the bedrockRuntimeClient in the
+You have to provide the bedrockAgentRuntimeClient in the
Registry or your accessKey and secretKey to access
the https://aws.amazon.com/bedrock/[Amazon Bedrock] service.
@@ -60,6 +60,9 @@ The component supports the following operations on the
`operation` URI option:
* `invokeAgent` — Invoke a deployed Bedrock Agent by id/alias and stream the
response back.
* `invokeInlineAgent` — Invoke an agent defined inline at invocation time
(foundation model plus instruction).
+The `CamelAwsBedrockAgentRuntimeOperation` header overrides the `operation`
option per exchange. With neither set,
+the exchange fails with an `IllegalArgumentException`.
+
=== Retrieving from a Bedrock Knowledge Base
The `retrieve` operation performs a semantic search against a Bedrock
Knowledge Base and returns the matching chunks
@@ -89,7 +92,9 @@ following headers:
=== Invoking a Bedrock Flow
The `invokeFlow` operation streams flow events using the AWS SDK async client;
the producer transparently allocates a
-`BedrockAgentRuntimeAsyncClient` when the endpoint is configured with
`operation=invokeFlow`.
+`BedrockAgentRuntimeAsyncClient` when the endpoint is configured with
`operation=invokeFlow`. When the
+`CamelAwsBedrockAgentRuntimeOperation` header picks `invokeFlow`,
`invokeAgent` or `invokeInlineAgent` on an endpoint
+of another operation, the async client is created when the first exchange
needs it.
Mandatory options:
@@ -107,7 +112,8 @@ The body of the in-message can be:
* a `String` — wrapped in a single `FlowInput` targeting node `FlowInputNode`,
output `document`;
* a `FlowInput` instance — used as the sole input to the flow;
-* a `List<FlowInput>` — passed verbatim to the request (use this form to chain
multiple flow inputs).
+* a `List<FlowInput>` — passed verbatim to the request (use this form to chain
multiple flow inputs);
+* another body, such as a `byte[]`, an `InputStream` or a file, is converted
to a `String` and used as above.
When `pojoRequest=true`, the body must be an `InvokeFlowRequest` and is sent
unchanged.
diff --git
a/components/camel-aws/camel-aws-bedrock/src/main/java/org/apache/camel/component/aws2/bedrock/agentruntime/BedrockAgentRuntimeConfiguration.java
b/components/camel-aws/camel-aws-bedrock/src/main/java/org/apache/camel/component/aws2/bedrock/agentruntime/BedrockAgentRuntimeConfiguration.java
index 425342694ac4..bd33cabf6b5f 100644
---
a/components/camel-aws/camel-aws-bedrock/src/main/java/org/apache/camel/component/aws2/bedrock/agentruntime/BedrockAgentRuntimeConfiguration.java
+++
b/components/camel-aws/camel-aws-bedrock/src/main/java/org/apache/camel/component/aws2/bedrock/agentruntime/BedrockAgentRuntimeConfiguration.java
@@ -45,10 +45,8 @@ public class BedrockAgentRuntimeConfiguration implements
Cloneable, AwsCommonCon
@UriParam(label = "security", security = "secret")
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,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.jamba-1-5-large-v1:0,ai21.j
[...]
- @Metadata(required = true)
private String modelId;
@UriParam
- @Metadata(required = true)
private String knowledgeBaseId;
@UriParam
@Metadata(required = true)
@@ -306,7 +304,7 @@ public class BedrockAgentRuntimeConfiguration implements
Cloneable, AwsCommonCon
}
/**
- * Define the model Id we are going to use
+ * Define the model Id we are going to use (used by the
retrieveAndGenerate operation)
*/
public void setModelId(String modelId) {
this.modelId = modelId;
@@ -317,7 +315,7 @@ public class BedrockAgentRuntimeConfiguration implements
Cloneable, AwsCommonCon
}
/**
- * Define the Knowledge Base Id we are going to use
+ * Define the Knowledge Base Id we are going to use (used by the retrieve
and retrieveAndGenerate operations)
*/
public void setKnowledgeBaseId(String knowledgeBaseId) {
this.knowledgeBaseId = knowledgeBaseId;
diff --git
a/components/camel-aws/camel-aws-bedrock/src/main/java/org/apache/camel/component/aws2/bedrock/agentruntime/BedrockAgentRuntimeConstants.java
b/components/camel-aws/camel-aws-bedrock/src/main/java/org/apache/camel/component/aws2/bedrock/agentruntime/BedrockAgentRuntimeConstants.java
index a3172395527e..beaa1b2ded10 100644
---
a/components/camel-aws/camel-aws-bedrock/src/main/java/org/apache/camel/component/aws2/bedrock/agentruntime/BedrockAgentRuntimeConstants.java
+++
b/components/camel-aws/camel-aws-bedrock/src/main/java/org/apache/camel/component/aws2/bedrock/agentruntime/BedrockAgentRuntimeConstants.java
@@ -26,7 +26,7 @@ public interface BedrockAgentRuntimeConstants {
String OPERATION = "CamelAwsBedrockAgentRuntimeOperation";
@Metadata(description = "When retrieving and generating a response, this
header will contain the citations",
- javaType = "String")
+ javaType =
"java.util.List<software.amazon.awssdk.services.bedrockagentruntime.model.Citation>")
String CITATIONS = "CamelAwsBedrockAgentRuntimeCitations";
@Metadata(description = "When retrieving and generating a response, this
header will contain he unique identifier of the session. "
diff --git
a/components/camel-aws/camel-aws-bedrock/src/main/java/org/apache/camel/component/aws2/bedrock/agentruntime/BedrockAgentRuntimeEndpoint.java
b/components/camel-aws/camel-aws-bedrock/src/main/java/org/apache/camel/component/aws2/bedrock/agentruntime/BedrockAgentRuntimeEndpoint.java
index e9c18b44be5c..070b0d16fed5 100644
---
a/components/camel-aws/camel-aws-bedrock/src/main/java/org/apache/camel/component/aws2/bedrock/agentruntime/BedrockAgentRuntimeEndpoint.java
+++
b/components/camel-aws/camel-aws-bedrock/src/main/java/org/apache/camel/component/aws2/bedrock/agentruntime/BedrockAgentRuntimeEndpoint.java
@@ -18,6 +18,8 @@ package org.apache.camel.component.aws2.bedrock.agentruntime;
import java.util.HashMap;
import java.util.Map;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
import org.apache.camel.*;
import
org.apache.camel.component.aws2.bedrock.agentruntime.client.BedrockAgentRuntimeClientFactory;
@@ -40,13 +42,16 @@ import
software.amazon.awssdk.services.bedrockagentruntime.BedrockAgentRuntimeCl
headersClass = BedrockAgentRuntimeConstants.class)
@BodyTypes(producerIn = @BodyType(value = { Carrier.TEXT, Carrier.BYTES,
Carrier.STREAM, Carrier.FILE },
byOption = @BodyType.When(option =
"operation", value = "invokeFlow",
- carriers = {
Carrier.TEXT, Carrier.LIST, Carrier.POJO }),
+ carriers = {
+
Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE,
+
Carrier.LIST, Carrier.POJO }),
description = "The question, the query or
the input of the agent as text for"
+ " retrieveAndGenerate,
retrieve, invokeAgent and invokeInlineAgent: bytes, a"
+ " stream or a file are read
as text, a Map or a POJO is sent as its"
+ " toString(), so take the
text out of it first, and no body fails. invokeFlow"
+ " takes a String, which
becomes the document of the input node FlowInputNode,"
- + " or a FlowInput or a List
of FlowInput of the AWS SDK: another body fails."
+ + " or a FlowInput or a List
of FlowInput of the AWS SDK: another body is"
+ + " read as text, as for the
other operations."
+ " With pojoRequest=true the
request of the AWS SDK for the operation, such as"
+ " a
RetrieveAndGenerateRequest: another body fails"),
producerOut = @BodyType(value = { Carrier.TEXT, Carrier.LIST },
@@ -69,7 +74,8 @@ import
software.amazon.awssdk.services.bedrockagentruntime.BedrockAgentRuntimeCl
public class BedrockAgentRuntimeEndpoint extends ScheduledPollEndpoint
implements EndpointServiceLocation {
private BedrockAgentRuntimeClient bedrockAgentRuntimeClient;
- private BedrockAgentRuntimeAsyncClient bedrockAgentRuntimeAsyncClient;
+ private volatile BedrockAgentRuntimeAsyncClient
bedrockAgentRuntimeAsyncClient;
+ private final Lock asyncClientLock = new ReentrantLock();
@UriParam
private BedrockAgentRuntimeConfiguration configuration;
@@ -104,7 +110,8 @@ public class BedrockAgentRuntimeEndpoint extends
ScheduledPollEndpoint implement
// Only build the async client when the operation needs event-stream
support (invokeFlow, invokeAgent,
// invokeInlineAgent), or when the user explicitly supplied one. This
avoids creating a Netty-based async HTTP
- // client for users that only need synchronous operations like
retrieveAndGenerate.
+ // client for users that only need synchronous operations like
retrieveAndGenerate. When the operation header
+ // picks one of these operations on an endpoint of another operation,
the client is created on first use.
if
(ObjectHelper.isNotEmpty(configuration.getBedrockAgentRuntimeAsyncClient())) {
bedrockAgentRuntimeAsyncClient =
configuration.getBedrockAgentRuntimeAsyncClient();
} else if (configuration.getOperation() ==
BedrockAgentRuntimeOperations.invokeFlow
@@ -127,6 +134,7 @@ public class BedrockAgentRuntimeEndpoint extends
ScheduledPollEndpoint implement
bedrockAgentRuntimeAsyncClient.close();
}
}
+ bedrockAgentRuntimeAsyncClient = null;
super.doStop();
}
@@ -142,6 +150,27 @@ public class BedrockAgentRuntimeEndpoint extends
ScheduledPollEndpoint implement
return bedrockAgentRuntimeAsyncClient;
}
+ /**
+ * The async client that invokeFlow, invokeAgent and invokeInlineAgent
need. It is created on first use when the
+ * operation of the endpoint does not need it, but the operation header
picks one that does.
+ */
+ public BedrockAgentRuntimeAsyncClient
getOrCreateBedrockAgentRuntimeAsyncClient() {
+ BedrockAgentRuntimeAsyncClient client = bedrockAgentRuntimeAsyncClient;
+ if (client == null) {
+ asyncClientLock.lock();
+ try {
+ if (bedrockAgentRuntimeAsyncClient == null) {
+ bedrockAgentRuntimeAsyncClient
+ =
BedrockAgentRuntimeClientFactory.getBedrockAgentRuntimeAsyncClient(configuration);
+ }
+ client = bedrockAgentRuntimeAsyncClient;
+ } finally {
+ asyncClientLock.unlock();
+ }
+ }
+ return client;
+ }
+
@Override
public String getServiceUrl() {
if (!configuration.isOverrideEndpoint()) {
diff --git
a/components/camel-aws/camel-aws-bedrock/src/main/java/org/apache/camel/component/aws2/bedrock/agentruntime/BedrockAgentRuntimeProducer.java
b/components/camel-aws/camel-aws-bedrock/src/main/java/org/apache/camel/component/aws2/bedrock/agentruntime/BedrockAgentRuntimeProducer.java
index a68b54bd764b..1d022aaa868d 100644
---
a/components/camel-aws/camel-aws-bedrock/src/main/java/org/apache/camel/component/aws2/bedrock/agentruntime/BedrockAgentRuntimeProducer.java
+++
b/components/camel-aws/camel-aws-bedrock/src/main/java/org/apache/camel/component/aws2/bedrock/agentruntime/BedrockAgentRuntimeProducer.java
@@ -81,7 +81,13 @@ public class BedrockAgentRuntimeProducer extends
DefaultProducer {
@Override
public void process(Exchange exchange) throws Exception {
- switch (determineOperation(exchange)) {
+ BedrockAgentRuntimeOperations operation = determineOperation(exchange);
+ if (operation == null) {
+ throw new IllegalArgumentException(
+ "Operation must be specified with the operation option or
the "
+ +
BedrockAgentRuntimeConstants.OPERATION + " header");
+ }
+ switch (operation) {
case retrieveAndGenerate:
retrieveAndGenerate(getEndpoint().getBedrockAgentRuntimeClient(), exchange);
break;
@@ -273,12 +279,7 @@ public class BedrockAgentRuntimeProducer extends
DefaultProducer {
}
private void invokeFlow(Exchange exchange) throws InvalidPayloadException {
- BedrockAgentRuntimeAsyncClient asyncClient =
getEndpoint().getBedrockAgentRuntimeAsyncClient();
- if (asyncClient == null) {
- throw new IllegalStateException(
- "BedrockAgentRuntimeAsyncClient is not available. The
invokeFlow operation requires an async client; "
- + "set operation=invokeFlow on the
endpoint or autowire a BedrockAgentRuntimeAsyncClient.");
- }
+ BedrockAgentRuntimeAsyncClient asyncClient =
getEndpoint().getOrCreateBedrockAgentRuntimeAsyncClient();
InvokeFlowRequest request;
if (getConfiguration().isPojoRequest()) {
@@ -386,18 +387,15 @@ public class BedrockAgentRuntimeProducer extends
DefaultProducer {
return Collections.singletonList(singleInput);
}
- if (body instanceof String text) {
- FlowInput input = FlowInput.builder()
- .nodeName("FlowInputNode")
- .nodeOutputName("document")
-
.content(FlowInputContent.builder().document(Document.fromString(text)).build())
- .build();
- return Collections.singletonList(input);
- }
-
- throw new IllegalArgumentException(
- "invokeFlow expects the body to be a List<FlowInput>, a single
FlowInput or a String when pojoRequest=false. "
- + "Got: " +
body.getClass().getName());
+ // another body, such as bytes, a stream or a file, is converted to
the text of the input node, as invokeAgent
+ // does with its input text
+ String text = resolveInputText(exchange, "invokeFlow");
+ FlowInput input = FlowInput.builder()
+ .nodeName("FlowInputNode")
+ .nodeOutputName("document")
+
.content(FlowInputContent.builder().document(Document.fromString(text)).build())
+ .build();
+ return Collections.singletonList(input);
}
private void prepareFlowResponse(
@@ -462,7 +460,7 @@ public class BedrockAgentRuntimeProducer extends
DefaultProducer {
}
private void invokeAgent(Exchange exchange) throws InvalidPayloadException
{
- BedrockAgentRuntimeAsyncClient asyncClient =
requireAsyncClient("invokeAgent");
+ BedrockAgentRuntimeAsyncClient asyncClient =
getEndpoint().getOrCreateBedrockAgentRuntimeAsyncClient();
InvokeAgentRequest request;
if (getConfiguration().isPojoRequest()) {
@@ -493,7 +491,7 @@ public class BedrockAgentRuntimeProducer extends
DefaultProducer {
}
private void invokeInlineAgent(Exchange exchange) throws
InvalidPayloadException {
- BedrockAgentRuntimeAsyncClient asyncClient =
requireAsyncClient("invokeInlineAgent");
+ BedrockAgentRuntimeAsyncClient asyncClient =
getEndpoint().getOrCreateBedrockAgentRuntimeAsyncClient();
InvokeInlineAgentRequest request;
if (getConfiguration().isPojoRequest()) {
@@ -523,17 +521,6 @@ public class BedrockAgentRuntimeProducer extends
DefaultProducer {
prepareAgentResponse(result, request.sessionId(), exchange);
}
- private BedrockAgentRuntimeAsyncClient requireAsyncClient(String
operation) {
- BedrockAgentRuntimeAsyncClient asyncClient =
getEndpoint().getBedrockAgentRuntimeAsyncClient();
- if (asyncClient == null) {
- throw new IllegalStateException(
- "BedrockAgentRuntimeAsyncClient is not available. The " +
operation
- + " operation requires an async
client; set operation=" + operation
- + " on the endpoint or autowire a
BedrockAgentRuntimeAsyncClient.");
- }
- return asyncClient;
- }
-
/**
* Awaits an agent invocation, unwrapping the CompletionException that
CompletableFuture.join() puts around AWS
* service exceptions so callers see the same behavior as synchronous AWS
calls.
diff --git
a/components/camel-aws/camel-aws-bedrock/src/test/java/org/apache/camel/component/aws2/bedrock/agentruntime/BedrockAgentRuntimeEndpointTest.java
b/components/camel-aws/camel-aws-bedrock/src/test/java/org/apache/camel/component/aws2/bedrock/agentruntime/BedrockAgentRuntimeEndpointTest.java
index 7ad5c2278f9a..ae5eb93277c2 100644
---
a/components/camel-aws/camel-aws-bedrock/src/test/java/org/apache/camel/component/aws2/bedrock/agentruntime/BedrockAgentRuntimeEndpointTest.java
+++
b/components/camel-aws/camel-aws-bedrock/src/test/java/org/apache/camel/component/aws2/bedrock/agentruntime/BedrockAgentRuntimeEndpointTest.java
@@ -17,12 +17,14 @@
package org.apache.camel.component.aws2.bedrock.agentruntime;
import org.apache.camel.CamelContext;
+import
org.apache.camel.component.aws2.bedrock.agentruntime.client.BedrockAgentRuntimeClientFactory;
import org.apache.camel.impl.DefaultCamelContext;
import org.apache.camel.support.SimpleRegistry;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
+import org.mockito.MockedStatic;
import org.mockito.junit.jupiter.MockitoExtension;
import
software.amazon.awssdk.services.bedrockagentruntime.BedrockAgentRuntimeAsyncClient;
import
software.amazon.awssdk.services.bedrockagentruntime.BedrockAgentRuntimeClient;
@@ -32,6 +34,10 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
@ExtendWith(MockitoExtension.class)
public class BedrockAgentRuntimeEndpointTest {
@@ -122,4 +128,36 @@ public class BedrockAgentRuntimeEndpointTest {
localContext.stop();
}
}
+
+ @Test
+ public void retrieveEndpointCreatesAsyncClientOnFirstUse() throws
Exception {
+ // the operation header can pick invokeFlow, invokeAgent or
invokeInlineAgent on an endpoint of retrieve, so the
+ // async client is created when an exchange first needs it, and closed
when the endpoint stops
+ SimpleRegistry localRegistry = new SimpleRegistry();
+ localRegistry.bind("syncClient", syncClient);
+ CamelContext localContext = new DefaultCamelContext(localRegistry);
+
+ BedrockAgentRuntimeEndpoint endpoint = (BedrockAgentRuntimeEndpoint)
localContext.getEndpoint(
+
"aws-bedrock-agent-runtime://label?bedrockAgentRuntimeClient=#syncClient&operation=retrieve"
+
+
"&knowledgeBaseId=kb-1®ion=us-east-1&accessKey=unused&secretKey=unused");
+
+ try (MockedStatic<BedrockAgentRuntimeClientFactory> factory =
mockStatic(BedrockAgentRuntimeClientFactory.class)) {
+ factory.when(() ->
BedrockAgentRuntimeClientFactory.getBedrockAgentRuntimeAsyncClient(any()))
+ .thenReturn(asyncClient);
+
+ endpoint.start();
+ try {
+ assertNull(endpoint.getBedrockAgentRuntimeAsyncClient());
+ assertSame(asyncClient,
endpoint.getOrCreateBedrockAgentRuntimeAsyncClient());
+ assertSame(asyncClient,
endpoint.getOrCreateBedrockAgentRuntimeAsyncClient());
+ assertSame(asyncClient,
endpoint.getBedrockAgentRuntimeAsyncClient());
+ factory.verify(() ->
BedrockAgentRuntimeClientFactory.getBedrockAgentRuntimeAsyncClient(any()),
times(1));
+ } finally {
+ endpoint.stop();
+ localContext.stop();
+ }
+ }
+ verify(asyncClient).close();
+ assertNull(endpoint.getBedrockAgentRuntimeAsyncClient());
+ }
}
diff --git
a/components/camel-aws/camel-aws-bedrock/src/test/java/org/apache/camel/component/aws2/bedrock/agentruntime/BedrockAgentRuntimeProducerInvokeFlowTest.java
b/components/camel-aws/camel-aws-bedrock/src/test/java/org/apache/camel/component/aws2/bedrock/agentruntime/BedrockAgentRuntimeProducerInvokeFlowTest.java
index 90653dd49d55..4b616b728199 100644
---
a/components/camel-aws/camel-aws-bedrock/src/test/java/org/apache/camel/component/aws2/bedrock/agentruntime/BedrockAgentRuntimeProducerInvokeFlowTest.java
+++
b/components/camel-aws/camel-aws-bedrock/src/test/java/org/apache/camel/component/aws2/bedrock/agentruntime/BedrockAgentRuntimeProducerInvokeFlowTest.java
@@ -16,6 +16,8 @@
*/
package org.apache.camel.component.aws2.bedrock.agentruntime;
+import java.io.ByteArrayInputStream;
+import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicReference;
@@ -33,6 +35,7 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
+import software.amazon.awssdk.core.document.Document;
import
software.amazon.awssdk.services.bedrockagentruntime.BedrockAgentRuntimeAsyncClient;
import software.amazon.awssdk.services.bedrockagentruntime.model.FlowInput;
import
software.amazon.awssdk.services.bedrockagentruntime.model.InvokeFlowRequest;
@@ -41,6 +44,7 @@ import
software.amazon.awssdk.services.bedrockagentruntime.model.InvokeFlowRespo
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -135,6 +139,37 @@ public class BedrockAgentRuntimeProducerInvokeFlowTest {
assertTrue(((List<?>) outputs).isEmpty(), "FLOW_OUTPUTS header should
be an empty list when no events were emitted");
}
+ @Test
+ public void invokeFlowConvertsBytesBodyToText() {
+ Exchange result = template.send("direct:flow-config-default",
+ exchange -> exchange.getMessage().setBody("hello
bytes".getBytes(StandardCharsets.UTF_8)));
+
+ assertNull(result.getException(), "Exchange should not fail");
+ assertEquals("hello bytes", inputDocument().asString());
+ }
+
+ @Test
+ public void invokeFlowConvertsStreamBodyToText() {
+ Exchange result = template.send("direct:flow-config-default",
+ exchange -> exchange.getMessage()
+ .setBody(new ByteArrayInputStream("hello
stream".getBytes(StandardCharsets.UTF_8))));
+
+ assertNull(result.getException(), "Exchange should not fail");
+ assertEquals("hello stream", inputDocument().asString());
+ }
+
+ private Document inputDocument() {
+ InvokeFlowRequest request = capturedRequest.get();
+ assertNotNull(request, "Producer must have invoked the async client");
+ assertEquals(1, request.inputs().size());
+ FlowInput input = request.inputs().get(0);
+ assertEquals("FlowInputNode", input.nodeName());
+ assertEquals("document", input.nodeOutputName());
+ Document document = input.content().document();
+ assertTrue(document.isString(), "Document should be a string");
+ return document;
+ }
+
@Test
public void invokeFlowAllowsHeaderOverridesForFlowIdentifiers() {
template.send("direct:flow-config-default", exchange -> {
diff --git
a/components/camel-aws/camel-aws-bedrock/src/test/java/org/apache/camel/component/aws2/bedrock/agentruntime/BedrockAgentRuntimeProducerOperationTest.java
b/components/camel-aws/camel-aws-bedrock/src/test/java/org/apache/camel/component/aws2/bedrock/agentruntime/BedrockAgentRuntimeProducerOperationTest.java
new file mode 100644
index 000000000000..acf4d0aa0131
--- /dev/null
+++
b/components/camel-aws/camel-aws-bedrock/src/test/java/org/apache/camel/component/aws2/bedrock/agentruntime/BedrockAgentRuntimeProducerOperationTest.java
@@ -0,0 +1,146 @@
+/*
+ * 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.agentruntime;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.CamelExecutionException;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import
org.apache.camel.component.aws2.bedrock.agentruntime.client.BedrockAgentRuntimeClientFactory;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.support.SimpleRegistry;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.MockedStatic;
+import org.mockito.junit.jupiter.MockitoExtension;
+import
software.amazon.awssdk.services.bedrockagentruntime.BedrockAgentRuntimeAsyncClient;
+import
software.amazon.awssdk.services.bedrockagentruntime.BedrockAgentRuntimeClient;
+import
software.amazon.awssdk.services.bedrockagentruntime.model.InvokeAgentRequest;
+import
software.amazon.awssdk.services.bedrockagentruntime.model.InvokeAgentResponseHandler;
+import
software.amazon.awssdk.services.bedrockagentruntime.model.InvokeFlowRequest;
+import
software.amazon.awssdk.services.bedrockagentruntime.model.InvokeFlowResponseHandler;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.times;
+
+/**
+ * The operation of an exchange: no operation, and an operation picked by the
header that needs the async client on an
+ * endpoint of an operation that does not.
+ */
+@ExtendWith(MockitoExtension.class)
+class BedrockAgentRuntimeProducerOperationTest {
+
+ @Mock
+ private BedrockAgentRuntimeClient syncClient;
+
+ @Mock
+ private BedrockAgentRuntimeAsyncClient asyncClient;
+
+ private CamelContext camelContext;
+ private ProducerTemplate template;
+ private AtomicReference<InvokeFlowRequest> capturedFlowRequest;
+ private AtomicReference<InvokeAgentRequest> capturedAgentRequest;
+
+ @BeforeEach
+ void setup() throws Exception {
+ capturedFlowRequest = new AtomicReference<>();
+ capturedAgentRequest = new AtomicReference<>();
+ lenient().doAnswer(invocation -> {
+ capturedFlowRequest.set(invocation.getArgument(0,
InvokeFlowRequest.class));
+ return CompletableFuture.completedFuture(null);
+ }).when(asyncClient).invokeFlow(any(InvokeFlowRequest.class),
any(InvokeFlowResponseHandler.class));
+ lenient().doAnswer(invocation -> {
+ capturedAgentRequest.set(invocation.getArgument(0,
InvokeAgentRequest.class));
+ return CompletableFuture.completedFuture(null);
+ }).when(asyncClient).invokeAgent(any(InvokeAgentRequest.class),
any(InvokeAgentResponseHandler.class));
+
+ // only the sync client is in the registry, so the async client is not
autowired
+ SimpleRegistry registry = new SimpleRegistry();
+ registry.bind("syncClient", syncClient);
+ camelContext = new DefaultCamelContext(registry);
+ camelContext.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() {
+ String common =
"?bedrockAgentRuntimeClient=#syncClient®ion=us-east-1&accessKey=unused&secretKey=unused";
+
+ from("direct:retrieve")
+ .to("aws-bedrock-agent-runtime://label" + common
+ + "&operation=retrieve&knowledgeBaseId=kb-1"
+ +
"&flowIdentifier=flow-1&flowAliasIdentifier=alias-1"
+ + "&agentId=agent-1&agentAliasId=agent-alias-1");
+
+ from("direct:no-operation")
+ .to("aws-bedrock-agent-runtime://label2" + common);
+ }
+ });
+ camelContext.start();
+ template = camelContext.createProducerTemplate();
+ }
+
+ @AfterEach
+ void teardown() {
+ if (template != null) {
+ template.stop();
+ }
+ if (camelContext != null) {
+ camelContext.stop();
+ }
+ }
+
+ @Test
+ void noOperationFailsWithMessage() {
+ assertThatThrownBy(() -> template.sendBody("direct:no-operation",
"hi"))
+ .isInstanceOf(CamelExecutionException.class)
+ .cause()
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessageContaining("operation option")
+ .hasMessageContaining(BedrockAgentRuntimeConstants.OPERATION);
+ }
+
+ @Test
+ void operationHeaderPicksInvokeFlowOnRetrieveEndpoint() {
+ try (MockedStatic<BedrockAgentRuntimeClientFactory> factory =
mockStatic(BedrockAgentRuntimeClientFactory.class)) {
+ factory.when(() ->
BedrockAgentRuntimeClientFactory.getBedrockAgentRuntimeAsyncClient(any()))
+ .thenReturn(asyncClient);
+
+ template.sendBodyAndHeader("direct:retrieve", "hello flow",
BedrockAgentRuntimeConstants.OPERATION,
+ BedrockAgentRuntimeOperations.invokeFlow);
+ assertThat(capturedFlowRequest.get()).isNotNull();
+
assertThat(capturedFlowRequest.get().flowIdentifier()).isEqualTo("flow-1");
+
assertThat(capturedFlowRequest.get().inputs().get(0).content().document().asString()).isEqualTo("hello
flow");
+
+ template.sendBodyAndHeader("direct:retrieve", "hello agent",
BedrockAgentRuntimeConstants.OPERATION,
+ "invokeAgent");
+ assertThat(capturedAgentRequest.get()).isNotNull();
+
assertThat(capturedAgentRequest.get().agentId()).isEqualTo("agent-1");
+
assertThat(capturedAgentRequest.get().inputText()).isEqualTo("hello agent");
+
+ // the async client is created once, on first use, and kept for
the next exchanges
+ factory.verify(() ->
BedrockAgentRuntimeClientFactory.getBedrockAgentRuntimeAsyncClient(any()),
times(1));
+ }
+ }
+}
diff --git
a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/AwsBedrockAgentRuntimeComponentBuilderFactory.java
b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/AwsBedrockAgentRuntimeComponentBuilderFactory.java
index 95b76a5b0faa..a4b9d4bd6a8b 100644
---
a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/AwsBedrockAgentRuntimeComponentBuilderFactory.java
+++
b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/AwsBedrockAgentRuntimeComponentBuilderFactory.java
@@ -67,7 +67,8 @@ public interface
AwsBedrockAgentRuntimeComponentBuilderFactory {
}
/**
- * Define the Knowledge Base Id we are going to use.
+ * Define the Knowledge Base Id we are going to use (used by the
+ * retrieve and retrieveAndGenerate operations).
*
* The option is a: <code>java.lang.String</code> type.
*
@@ -107,7 +108,8 @@ public interface
AwsBedrockAgentRuntimeComponentBuilderFactory {
}
/**
- * Define the model Id we are going to use.
+ * Define the model Id we are going to use (used by the
+ * retrieveAndGenerate operation).
*
* The option is a: <code>java.lang.String</code> type.
*
diff --git
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/BedrockAgentRuntimeEndpointBuilderFactory.java
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/BedrockAgentRuntimeEndpointBuilderFactory.java
index 5142a159b5d8..f435a9abe3af 100644
---
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/BedrockAgentRuntimeEndpointBuilderFactory.java
+++
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/BedrockAgentRuntimeEndpointBuilderFactory.java
@@ -45,11 +45,11 @@ public interface BedrockAgentRuntimeEndpointBuilderFactory {
}
/**
- * Define the Knowledge Base Id we are going to use.
+ * Define the Knowledge Base Id we are going to use (used by the
+ * retrieve and retrieveAndGenerate operations).
*
* The option is a: <code>java.lang.String</code> type.
*
- * Required: true
* Group: producer
*
* @param knowledgeBaseId the value to set
@@ -60,11 +60,11 @@ public interface BedrockAgentRuntimeEndpointBuilderFactory {
return this;
}
/**
- * Define the model Id we are going to use.
+ * Define the model Id we are going to use (used by the
+ * retrieveAndGenerate operation).
*
* The option is a: <code>java.lang.String</code> type.
*
- * Required: true
* Group: producer
*
* @param modelId the value to set
@@ -837,7 +837,8 @@ public interface BedrockAgentRuntimeEndpointBuilderFactory {
* When retrieving and generating a response, this header will contain
* the citations.
*
- * The option is a: {@code String} type.
+ * The option is a: {@code
+ *
java.util.List<software.amazon.awssdk.services.bedrockagentruntime.model.Citation>}
type.
*
* Group: producer
*