You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ld...@apache.org on 2026/10/01 07:22:37 UTC
(camel) branch main updated (8045e74b042c -> f4f63bb77a50)
This is an automated email from the ASF dual-hosted git repository.
luigidemasi pushed a change to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
from 8045e74b042c CAMEL-25208: camel-jbang - Camel validation, quick doc and completion for Java and XML DSL routes (#27172)
new a054da0bc932 CAMEL-25201: Make the TypeSafe AI API path configurable
new f4f63bb77a50 CAMEL-25201: Tighten invalid API path assertions
The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.../camel/catalog/components/typesafe-ai.json | 58 ++++-----
.../camel/catalog/docs/typesafe-ai-component.adoc | 24 +++-
.../typesafeai/TypeSafeAiComponentConfigurer.java | 6 +
.../typesafeai/TypeSafeAiEndpointConfigurer.java | 6 +
.../typesafeai/TypeSafeAiEndpointUriFactory.java | 3 +-
.../camel/component/typesafeai/typesafe-ai.json | 58 ++++-----
.../src/main/docs/typesafe-ai-component.adoc | 24 +++-
.../component/typesafeai/TypeSafeAiClient.java | 4 +-
.../typesafeai/TypeSafeAiConfiguration.java | 25 +++-
.../typesafeai/TypeSafeAiApiPathTest.java | 138 +++++++++++++++++++++
.../typesafeai/TypeSafeAiTestSupport.java | 2 +-
.../dsl/TypesafeAiComponentBuilderFactory.java | 25 +++-
.../dsl/TypeSafeAiEndpointBuilderFactory.java | 22 +++-
13 files changed, 328 insertions(+), 67 deletions(-)
create mode 100644 components/camel-ai/camel-typesafe-ai/src/test/java/org/apache/camel/component/typesafeai/TypeSafeAiApiPathTest.java
(camel) 02/02: CAMEL-25201: Tighten invalid API path assertions
Posted by ld...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
luigidemasi pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
commit f4f63bb77a50068cd40b3b8c62675752f00efbd8
Author: Luigi De Masi <ld...@redhat.com>
AuthorDate: Thu Oct 1 08:52:32 2026 +0200
CAMEL-25201: Tighten invalid API path assertions
Require IllegalArgumentException as the endpoint failure's direct cause
so unrelated exceptions do not satisfy the invalid-path test. Check the
direct cause because malformed URI inputs retain URISyntaxException as
the deepest cause.
Co-authored-by: Codex <no...@openai.com>
Signed-off-by: Luigi De Masi <ld...@redhat.com>
---
.../org/apache/camel/component/typesafeai/TypeSafeAiApiPathTest.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/components/camel-ai/camel-typesafe-ai/src/test/java/org/apache/camel/component/typesafeai/TypeSafeAiApiPathTest.java b/components/camel-ai/camel-typesafe-ai/src/test/java/org/apache/camel/component/typesafeai/TypeSafeAiApiPathTest.java
index 38248cd7131a..ce01b1ed80f1 100644
--- a/components/camel-ai/camel-typesafe-ai/src/test/java/org/apache/camel/component/typesafeai/TypeSafeAiApiPathTest.java
+++ b/components/camel-ai/camel-typesafe-ai/src/test/java/org/apache/camel/component/typesafeai/TypeSafeAiApiPathTest.java
@@ -132,7 +132,7 @@ class TypeSafeAiApiPathTest extends TypeSafeAiTestSupport {
void rejectsInvalidPathsBeforeSending(String apiPath) {
context.getComponent("typesafe-ai", TypeSafeAiComponent.class).getConfiguration().setApiPath(apiPath);
- assertThatThrownBy(() -> context.getEndpoint("typesafe-ai:invalid")).isInstanceOf(Exception.class);
+ assertThatThrownBy(() -> context.getEndpoint("typesafe-ai:invalid")).hasCauseInstanceOf(IllegalArgumentException.class);
assertThat(paths).isEmpty();
}
}
(camel) 01/02: CAMEL-25201: Make the TypeSafe AI API path configurable
Posted by ld...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
luigidemasi pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
commit a054da0bc932b006fe7e379c72e8f90018d4c93d
Author: Luigi De Masi <ld...@redhat.com>
AuthorDate: Wed Sep 30 20:29:42 2026 +0200
CAMEL-25201: Make the TypeSafe AI API path configurable
Expose apiPath on component and endpoint configuration with /v1/systemone
as the default. Preserve base URL prefixes and reuse the managed endpoint
configuration for semantic routes. Cover URL construction and configuration
inheritance with HTTP tests, and update documentation and generated metadata.
Co-authored-by: Codex <no...@openai.com>
Signed-off-by: Luigi De Masi <ld...@redhat.com>
---
.../camel/catalog/components/typesafe-ai.json | 58 ++++-----
.../camel/catalog/docs/typesafe-ai-component.adoc | 24 +++-
.../typesafeai/TypeSafeAiComponentConfigurer.java | 6 +
.../typesafeai/TypeSafeAiEndpointConfigurer.java | 6 +
.../typesafeai/TypeSafeAiEndpointUriFactory.java | 3 +-
.../camel/component/typesafeai/typesafe-ai.json | 58 ++++-----
.../src/main/docs/typesafe-ai-component.adoc | 24 +++-
.../component/typesafeai/TypeSafeAiClient.java | 4 +-
.../typesafeai/TypeSafeAiConfiguration.java | 25 +++-
.../typesafeai/TypeSafeAiApiPathTest.java | 138 +++++++++++++++++++++
.../typesafeai/TypeSafeAiTestSupport.java | 2 +-
.../dsl/TypesafeAiComponentBuilderFactory.java | 25 +++-
.../dsl/TypeSafeAiEndpointBuilderFactory.java | 22 +++-
13 files changed, 328 insertions(+), 67 deletions(-)
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/typesafe-ai.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/typesafe-ai.json
index 96a854019318..18c9bff96dd6 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/typesafe-ai.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/typesafe-ai.json
@@ -24,36 +24,38 @@
"remote": true
},
"componentProperties": {
- "baseUrl": { "index": 0, "kind": "property", "displayName": "Base Url", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "https:\/\/api.typesafe.ai", "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "The API base URL. The client appends \/v1\/systemone. Redirects [...]
- "configuration": { "index": 1, "kind": "property", "displayName": "Configuration", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "deprecated": false, "autowired": false, "secret": false, "description": "Default configuration shared by TypeSafe AI endpoints." },
- "maxConcurrentRequests": { "index": 2, "kind": "property", "displayName": "Max Concurrent Requests", "group": "common", "label": "common", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 64, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "Maximum concurrent evaluations per endpoint, shared by producers and pre [...]
- "model": { "index": 3, "kind": "property", "displayName": "Model", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "jev-latest", "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "The model ID or alias. Use a versioned ID to pin decision behavior." },
- "questions": { "index": 4, "kind": "property", "displayName": "Questions", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "A JSON object mapping question names to Noul, Choice or Score question objects. When set, producers evalua [...]
- "questionsResource": { "index": 5, "kind": "property", "displayName": "Questions Resource", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "Camel resource URI for a UTF-8 JSON object mapping question names to Noul, Choice or Scor [...]
- "requestTimeout": { "index": 6, "kind": "property", "displayName": "Request Timeout", "group": "common", "label": "common", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 30000, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "The timeout in milliseconds for the complete HTTP request and response body. Must b [...]
- "state": { "index": 7, "kind": "property", "displayName": "State", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "The Simple expression selecting state for configured producer questions and the TypeSafe AI language. If not set, [...]
- "lazyStartProducer": { "index": 8, "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 [...]
- "resultProperty": { "index": 9, "kind": "property", "displayName": "Result Property", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "Store the producer response in this exchange property, preserving the original message body." },
- "autowiredEnabled": { "index": 10, "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching [...]
- "threshold": { "index": 11, "kind": "property", "displayName": "Threshold", "group": "advanced", "label": "advanced", "required": false, "type": "number", "javaType": "double", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 0.5, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "Default inclusive probability threshold for the TypeSafe AI language. Must be within 0,1." },
- "uncertainty": { "index": 12, "kind": "property", "displayName": "Uncertainty", "group": "advanced", "label": "advanced", "required": false, "type": "number", "javaType": "double", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 0.0, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "Default half-width of the inclusive uncertainty band for the TypeSafe AI language. Zer [...]
- "uncertaintyPolicy": { "index": 13, "kind": "property", "displayName": "Uncertainty Policy", "group": "advanced", "label": "advanced", "required": false, "type": "enum", "javaType": "org.apache.camel.language.typesafeai.TypeSafeAiLanguage.UncertaintyPolicy", "enum": [ "NonMatch", "Fail" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "NonMatch", "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "c [...]
- "apiKey": { "index": 14, "kind": "property", "displayName": "Api Key", "group": "security", "label": "security", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": true, "security": "secret", "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "The API key used for Bearer authentication." }
+ "apiPath": { "index": 0, "kind": "property", "displayName": "Api Path", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "\/v1\/systemone", "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "The API path appended to baseUrl. A leading slash is optional. Must be a n [...]
+ "baseUrl": { "index": 1, "kind": "property", "displayName": "Base Url", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "https:\/\/api.typesafe.ai", "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "The API base URL. The client appends apiPath, preserving any bas [...]
+ "configuration": { "index": 2, "kind": "property", "displayName": "Configuration", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "deprecated": false, "autowired": false, "secret": false, "description": "Default configuration shared by TypeSafe AI endpoints." },
+ "maxConcurrentRequests": { "index": 3, "kind": "property", "displayName": "Max Concurrent Requests", "group": "common", "label": "common", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 64, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "Maximum concurrent evaluations per endpoint, shared by producers and pre [...]
+ "model": { "index": 4, "kind": "property", "displayName": "Model", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "jev-latest", "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "The model ID or alias. Use a versioned ID to pin decision behavior." },
+ "questions": { "index": 5, "kind": "property", "displayName": "Questions", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "A JSON object mapping question names to Noul, Choice or Score question objects. When set, producers evalua [...]
+ "questionsResource": { "index": 6, "kind": "property", "displayName": "Questions Resource", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "Camel resource URI for a UTF-8 JSON object mapping question names to Noul, Choice or Scor [...]
+ "requestTimeout": { "index": 7, "kind": "property", "displayName": "Request Timeout", "group": "common", "label": "common", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 30000, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "The timeout in milliseconds for the complete HTTP request and response body. Must b [...]
+ "state": { "index": 8, "kind": "property", "displayName": "State", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "The Simple expression selecting state for configured producer questions and the TypeSafe AI language. If not set, [...]
+ "lazyStartProducer": { "index": 9, "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 [...]
+ "resultProperty": { "index": 10, "kind": "property", "displayName": "Result Property", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "Store the producer response in this exchange property, preserving the original message body." },
+ "autowiredEnabled": { "index": 11, "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching [...]
+ "threshold": { "index": 12, "kind": "property", "displayName": "Threshold", "group": "advanced", "label": "advanced", "required": false, "type": "number", "javaType": "double", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 0.5, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "Default inclusive probability threshold for the TypeSafe AI language. Must be within 0,1." },
+ "uncertainty": { "index": 13, "kind": "property", "displayName": "Uncertainty", "group": "advanced", "label": "advanced", "required": false, "type": "number", "javaType": "double", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 0.0, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "Default half-width of the inclusive uncertainty band for the TypeSafe AI language. Zer [...]
+ "uncertaintyPolicy": { "index": 14, "kind": "property", "displayName": "Uncertainty Policy", "group": "advanced", "label": "advanced", "required": false, "type": "enum", "javaType": "org.apache.camel.language.typesafeai.TypeSafeAiLanguage.UncertaintyPolicy", "enum": [ "NonMatch", "Fail" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "NonMatch", "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "c [...]
+ "apiKey": { "index": 15, "kind": "property", "displayName": "Api Key", "group": "security", "label": "security", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": true, "security": "secret", "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "The API key used for Bearer authentication." }
},
"properties": {
"name": { "index": 0, "kind": "path", "displayName": "Name", "group": "producer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "A logical name for the evaluation endpoint." },
- "baseUrl": { "index": 1, "kind": "parameter", "displayName": "Base Url", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "https:\/\/api.typesafe.ai", "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "The API base URL. The client appends \/v1\/systemone. Redirects [...]
- "maxConcurrentRequests": { "index": 2, "kind": "parameter", "displayName": "Max Concurrent Requests", "group": "common", "label": "common", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 64, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "Maximum concurrent evaluations per endpoint, shared by producers and pr [...]
- "model": { "index": 3, "kind": "parameter", "displayName": "Model", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "jev-latest", "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "The model ID or alias. Use a versioned ID to pin decision behavior." },
- "questions": { "index": 4, "kind": "parameter", "displayName": "Questions", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "A JSON object mapping question names to Noul, Choice or Score question objects. When set, producers evalu [...]
- "questionsResource": { "index": 5, "kind": "parameter", "displayName": "Questions Resource", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "Camel resource URI for a UTF-8 JSON object mapping question names to Noul, Choice or Sco [...]
- "requestTimeout": { "index": 6, "kind": "parameter", "displayName": "Request Timeout", "group": "common", "label": "common", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 30000, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "The timeout in milliseconds for the complete HTTP request and response body. Must [...]
- "state": { "index": 7, "kind": "parameter", "displayName": "State", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "The Simple expression selecting state for configured producer questions and the TypeSafe AI language. If not set, [...]
- "resultProperty": { "index": 8, "kind": "parameter", "displayName": "Result Property", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "Store the producer response in this exchange property, preserving the original message body." },
- "lazyStartProducer": { "index": 9, "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer (advanced)", "label": "producer,advanced", "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 produc [...]
- "threshold": { "index": 10, "kind": "parameter", "displayName": "Threshold", "group": "advanced", "label": "advanced", "required": false, "type": "number", "javaType": "double", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 0.5, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "Default inclusive probability threshold for the TypeSafe AI language. Must be within 0,1." },
- "uncertainty": { "index": 11, "kind": "parameter", "displayName": "Uncertainty", "group": "advanced", "label": "advanced", "required": false, "type": "number", "javaType": "double", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 0.0, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "Default half-width of the inclusive uncertainty band for the TypeSafe AI language. Ze [...]
- "uncertaintyPolicy": { "index": 12, "kind": "parameter", "displayName": "Uncertainty Policy", "group": "advanced", "label": "advanced", "required": false, "type": "enum", "javaType": "org.apache.camel.language.typesafeai.TypeSafeAiLanguage.UncertaintyPolicy", "enum": [ "NonMatch", "Fail" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "NonMatch", "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": " [...]
- "apiKey": { "index": 13, "kind": "parameter", "displayName": "Api Key", "group": "security", "label": "security", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": true, "security": "secret", "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "The API key used for Bearer authentication." }
+ "apiPath": { "index": 1, "kind": "parameter", "displayName": "Api Path", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "\/v1\/systemone", "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "The API path appended to baseUrl. A leading slash is optional. Must be a [...]
+ "baseUrl": { "index": 2, "kind": "parameter", "displayName": "Base Url", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "https:\/\/api.typesafe.ai", "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "The API base URL. The client appends apiPath, preserving any ba [...]
+ "maxConcurrentRequests": { "index": 3, "kind": "parameter", "displayName": "Max Concurrent Requests", "group": "common", "label": "common", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 64, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "Maximum concurrent evaluations per endpoint, shared by producers and pr [...]
+ "model": { "index": 4, "kind": "parameter", "displayName": "Model", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "jev-latest", "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "The model ID or alias. Use a versioned ID to pin decision behavior." },
+ "questions": { "index": 5, "kind": "parameter", "displayName": "Questions", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "A JSON object mapping question names to Noul, Choice or Score question objects. When set, producers evalu [...]
+ "questionsResource": { "index": 6, "kind": "parameter", "displayName": "Questions Resource", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "Camel resource URI for a UTF-8 JSON object mapping question names to Noul, Choice or Sco [...]
+ "requestTimeout": { "index": 7, "kind": "parameter", "displayName": "Request Timeout", "group": "common", "label": "common", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 30000, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "The timeout in milliseconds for the complete HTTP request and response body. Must [...]
+ "state": { "index": 8, "kind": "parameter", "displayName": "State", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "The Simple expression selecting state for configured producer questions and the TypeSafe AI language. If not set, [...]
+ "resultProperty": { "index": 9, "kind": "parameter", "displayName": "Result Property", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "Store the producer response in this exchange property, preserving the original message body." },
+ "lazyStartProducer": { "index": 10, "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer (advanced)", "label": "producer,advanced", "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 produ [...]
+ "threshold": { "index": 11, "kind": "parameter", "displayName": "Threshold", "group": "advanced", "label": "advanced", "required": false, "type": "number", "javaType": "double", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 0.5, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "Default inclusive probability threshold for the TypeSafe AI language. Must be within 0,1." },
+ "uncertainty": { "index": 12, "kind": "parameter", "displayName": "Uncertainty", "group": "advanced", "label": "advanced", "required": false, "type": "number", "javaType": "double", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 0.0, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "Default half-width of the inclusive uncertainty band for the TypeSafe AI language. Ze [...]
+ "uncertaintyPolicy": { "index": 13, "kind": "parameter", "displayName": "Uncertainty Policy", "group": "advanced", "label": "advanced", "required": false, "type": "enum", "javaType": "org.apache.camel.language.typesafeai.TypeSafeAiLanguage.UncertaintyPolicy", "enum": [ "NonMatch", "Fail" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "NonMatch", "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": " [...]
+ "apiKey": { "index": 14, "kind": "parameter", "displayName": "Api Key", "group": "security", "label": "security", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": true, "security": "secret", "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "The API key used for Bearer authentication." }
}
}
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/typesafe-ai-component.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/typesafe-ai-component.adoc
index 2d1e0cd0ec58..942b64630924 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/typesafe-ai-component.adoc
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/typesafe-ai-component.adoc
@@ -66,7 +66,8 @@ camel.component.typesafe-ai.max-concurrent-requests=64
----
`apiKey` is a secret option. `baseUrl` defaults to `https://api.typesafe.ai`; the client
-appends `/v1/systemone`. A base path can address a proxy. HTTP redirects are not followed.
+appends `apiPath`, which defaults to `/v1/systemone`. A base path can address a proxy.
+HTTP redirects are not followed.
Use HTTPS for the service; HTTP is useful for local testing.
`model` defaults to `jev-latest`. Pin a version when tuning thresholds; aliases can change
@@ -74,6 +75,27 @@ behavior without a route change. The request map may explicitly override the end
model for that request. The response's `model` identifies the model that answered.
See https://docs.typesafe.ai/models[available models].
+=== Custom API path
+
+Set `apiPath` when a service or gateway exposes the supported TypeSafe AI request and
+response schema at a different path:
+
+[source,properties]
+----
+camel.component.typesafe-ai.base-url=http://localhost:8100
+camel.component.typesafe-ai.api-path=/v1/decision
+----
+
+The resulting request URL is `http://localhost:8100/v1/decision`. A path prefix in `baseUrl`
+is preserved: `http://localhost:8100/gateway/` produces
+`http://localhost:8100/gateway/v1/decision`. The leading slash in `apiPath` is optional.
+The path must not be blank or include a scheme, authority, query or fragment.
+
+An endpoint can override the component setting with `typesafe-ai:refund?apiPath=/v1/decision`.
+The Semantic language's TypeSafe AI adapter inherits the component setting even when the
+route contains no `typesafe-ai` endpoint. Changing the path does not change the required
+request and response schema.
+
=== Concurrent requests
`maxConcurrentRequests` limits concurrent evaluations to 64 per endpoint by default and must
diff --git a/components/camel-ai/camel-typesafe-ai/src/generated/java/org/apache/camel/component/typesafeai/TypeSafeAiComponentConfigurer.java b/components/camel-ai/camel-typesafe-ai/src/generated/java/org/apache/camel/component/typesafeai/TypeSafeAiComponentConfigurer.java
index 049cc2d4a171..dc15e479b3ce 100644
--- a/components/camel-ai/camel-typesafe-ai/src/generated/java/org/apache/camel/component/typesafeai/TypeSafeAiComponentConfigurer.java
+++ b/components/camel-ai/camel-typesafe-ai/src/generated/java/org/apache/camel/component/typesafeai/TypeSafeAiComponentConfigurer.java
@@ -32,6 +32,8 @@ public class TypeSafeAiComponentConfigurer extends PropertyConfigurerSupport imp
switch (ignoreCase ? name.toLowerCase() : name) {
case "apikey":
case "apiKey": getOrCreateConfiguration(target).setApiKey(property(camelContext, java.lang.String.class, value)); return true;
+ case "apipath":
+ case "apiPath": getOrCreateConfiguration(target).setApiPath(property(camelContext, java.lang.String.class, value)); return true;
case "autowiredenabled":
case "autowiredEnabled": target.setAutowiredEnabled(property(camelContext, boolean.class, value)); return true;
case "baseurl":
@@ -63,6 +65,8 @@ public class TypeSafeAiComponentConfigurer extends PropertyConfigurerSupport imp
switch (ignoreCase ? name.toLowerCase() : name) {
case "apikey":
case "apiKey": return java.lang.String.class;
+ case "apipath":
+ case "apiPath": return java.lang.String.class;
case "autowiredenabled":
case "autowiredEnabled": return boolean.class;
case "baseurl":
@@ -95,6 +99,8 @@ public class TypeSafeAiComponentConfigurer extends PropertyConfigurerSupport imp
switch (ignoreCase ? name.toLowerCase() : name) {
case "apikey":
case "apiKey": return getOrCreateConfiguration(target).getApiKey();
+ case "apipath":
+ case "apiPath": return getOrCreateConfiguration(target).getApiPath();
case "autowiredenabled":
case "autowiredEnabled": return target.isAutowiredEnabled();
case "baseurl":
diff --git a/components/camel-ai/camel-typesafe-ai/src/generated/java/org/apache/camel/component/typesafeai/TypeSafeAiEndpointConfigurer.java b/components/camel-ai/camel-typesafe-ai/src/generated/java/org/apache/camel/component/typesafeai/TypeSafeAiEndpointConfigurer.java
index 463d1e8a473f..4b4d65371980 100644
--- a/components/camel-ai/camel-typesafe-ai/src/generated/java/org/apache/camel/component/typesafeai/TypeSafeAiEndpointConfigurer.java
+++ b/components/camel-ai/camel-typesafe-ai/src/generated/java/org/apache/camel/component/typesafeai/TypeSafeAiEndpointConfigurer.java
@@ -25,6 +25,8 @@ public class TypeSafeAiEndpointConfigurer extends PropertyConfigurerSupport impl
switch (ignoreCase ? name.toLowerCase() : name) {
case "apikey":
case "apiKey": target.getConfiguration().setApiKey(property(camelContext, java.lang.String.class, value)); return true;
+ case "apipath":
+ case "apiPath": target.getConfiguration().setApiPath(property(camelContext, java.lang.String.class, value)); return true;
case "baseurl":
case "baseUrl": target.getConfiguration().setBaseUrl(property(camelContext, java.lang.String.class, value)); return true;
case "lazystartproducer":
@@ -53,6 +55,8 @@ public class TypeSafeAiEndpointConfigurer extends PropertyConfigurerSupport impl
switch (ignoreCase ? name.toLowerCase() : name) {
case "apikey":
case "apiKey": return java.lang.String.class;
+ case "apipath":
+ case "apiPath": return java.lang.String.class;
case "baseurl":
case "baseUrl": return java.lang.String.class;
case "lazystartproducer":
@@ -82,6 +86,8 @@ public class TypeSafeAiEndpointConfigurer extends PropertyConfigurerSupport impl
switch (ignoreCase ? name.toLowerCase() : name) {
case "apikey":
case "apiKey": return target.getConfiguration().getApiKey();
+ case "apipath":
+ case "apiPath": return target.getConfiguration().getApiPath();
case "baseurl":
case "baseUrl": return target.getConfiguration().getBaseUrl();
case "lazystartproducer":
diff --git a/components/camel-ai/camel-typesafe-ai/src/generated/java/org/apache/camel/component/typesafeai/TypeSafeAiEndpointUriFactory.java b/components/camel-ai/camel-typesafe-ai/src/generated/java/org/apache/camel/component/typesafeai/TypeSafeAiEndpointUriFactory.java
index f31f10cb2caa..b086097ca430 100644
--- a/components/camel-ai/camel-typesafe-ai/src/generated/java/org/apache/camel/component/typesafeai/TypeSafeAiEndpointUriFactory.java
+++ b/components/camel-ai/camel-typesafe-ai/src/generated/java/org/apache/camel/component/typesafeai/TypeSafeAiEndpointUriFactory.java
@@ -24,8 +24,9 @@ public class TypeSafeAiEndpointUriFactory extends org.apache.camel.support.compo
private static final Set<String> ENDPOINT_IDENTITY_PROPERTY_NAMES;
private static final Map<String, String> MULTI_VALUE_PREFIXES;
static {
- Set<String> props = new HashSet<>(14);
+ Set<String> props = new HashSet<>(15);
props.add("apiKey");
+ props.add("apiPath");
props.add("baseUrl");
props.add("lazyStartProducer");
props.add("maxConcurrentRequests");
diff --git a/components/camel-ai/camel-typesafe-ai/src/generated/resources/META-INF/org/apache/camel/component/typesafeai/typesafe-ai.json b/components/camel-ai/camel-typesafe-ai/src/generated/resources/META-INF/org/apache/camel/component/typesafeai/typesafe-ai.json
index 96a854019318..18c9bff96dd6 100644
--- a/components/camel-ai/camel-typesafe-ai/src/generated/resources/META-INF/org/apache/camel/component/typesafeai/typesafe-ai.json
+++ b/components/camel-ai/camel-typesafe-ai/src/generated/resources/META-INF/org/apache/camel/component/typesafeai/typesafe-ai.json
@@ -24,36 +24,38 @@
"remote": true
},
"componentProperties": {
- "baseUrl": { "index": 0, "kind": "property", "displayName": "Base Url", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "https:\/\/api.typesafe.ai", "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "The API base URL. The client appends \/v1\/systemone. Redirects [...]
- "configuration": { "index": 1, "kind": "property", "displayName": "Configuration", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "deprecated": false, "autowired": false, "secret": false, "description": "Default configuration shared by TypeSafe AI endpoints." },
- "maxConcurrentRequests": { "index": 2, "kind": "property", "displayName": "Max Concurrent Requests", "group": "common", "label": "common", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 64, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "Maximum concurrent evaluations per endpoint, shared by producers and pre [...]
- "model": { "index": 3, "kind": "property", "displayName": "Model", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "jev-latest", "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "The model ID or alias. Use a versioned ID to pin decision behavior." },
- "questions": { "index": 4, "kind": "property", "displayName": "Questions", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "A JSON object mapping question names to Noul, Choice or Score question objects. When set, producers evalua [...]
- "questionsResource": { "index": 5, "kind": "property", "displayName": "Questions Resource", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "Camel resource URI for a UTF-8 JSON object mapping question names to Noul, Choice or Scor [...]
- "requestTimeout": { "index": 6, "kind": "property", "displayName": "Request Timeout", "group": "common", "label": "common", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 30000, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "The timeout in milliseconds for the complete HTTP request and response body. Must b [...]
- "state": { "index": 7, "kind": "property", "displayName": "State", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "The Simple expression selecting state for configured producer questions and the TypeSafe AI language. If not set, [...]
- "lazyStartProducer": { "index": 8, "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 [...]
- "resultProperty": { "index": 9, "kind": "property", "displayName": "Result Property", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "Store the producer response in this exchange property, preserving the original message body." },
- "autowiredEnabled": { "index": 10, "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching [...]
- "threshold": { "index": 11, "kind": "property", "displayName": "Threshold", "group": "advanced", "label": "advanced", "required": false, "type": "number", "javaType": "double", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 0.5, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "Default inclusive probability threshold for the TypeSafe AI language. Must be within 0,1." },
- "uncertainty": { "index": 12, "kind": "property", "displayName": "Uncertainty", "group": "advanced", "label": "advanced", "required": false, "type": "number", "javaType": "double", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 0.0, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "Default half-width of the inclusive uncertainty band for the TypeSafe AI language. Zer [...]
- "uncertaintyPolicy": { "index": 13, "kind": "property", "displayName": "Uncertainty Policy", "group": "advanced", "label": "advanced", "required": false, "type": "enum", "javaType": "org.apache.camel.language.typesafeai.TypeSafeAiLanguage.UncertaintyPolicy", "enum": [ "NonMatch", "Fail" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "NonMatch", "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "c [...]
- "apiKey": { "index": 14, "kind": "property", "displayName": "Api Key", "group": "security", "label": "security", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": true, "security": "secret", "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "The API key used for Bearer authentication." }
+ "apiPath": { "index": 0, "kind": "property", "displayName": "Api Path", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "\/v1\/systemone", "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "The API path appended to baseUrl. A leading slash is optional. Must be a n [...]
+ "baseUrl": { "index": 1, "kind": "property", "displayName": "Base Url", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "https:\/\/api.typesafe.ai", "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "The API base URL. The client appends apiPath, preserving any bas [...]
+ "configuration": { "index": 2, "kind": "property", "displayName": "Configuration", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "deprecated": false, "autowired": false, "secret": false, "description": "Default configuration shared by TypeSafe AI endpoints." },
+ "maxConcurrentRequests": { "index": 3, "kind": "property", "displayName": "Max Concurrent Requests", "group": "common", "label": "common", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 64, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "Maximum concurrent evaluations per endpoint, shared by producers and pre [...]
+ "model": { "index": 4, "kind": "property", "displayName": "Model", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "jev-latest", "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "The model ID or alias. Use a versioned ID to pin decision behavior." },
+ "questions": { "index": 5, "kind": "property", "displayName": "Questions", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "A JSON object mapping question names to Noul, Choice or Score question objects. When set, producers evalua [...]
+ "questionsResource": { "index": 6, "kind": "property", "displayName": "Questions Resource", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "Camel resource URI for a UTF-8 JSON object mapping question names to Noul, Choice or Scor [...]
+ "requestTimeout": { "index": 7, "kind": "property", "displayName": "Request Timeout", "group": "common", "label": "common", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 30000, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "The timeout in milliseconds for the complete HTTP request and response body. Must b [...]
+ "state": { "index": 8, "kind": "property", "displayName": "State", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "The Simple expression selecting state for configured producer questions and the TypeSafe AI language. If not set, [...]
+ "lazyStartProducer": { "index": 9, "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 [...]
+ "resultProperty": { "index": 10, "kind": "property", "displayName": "Result Property", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "Store the producer response in this exchange property, preserving the original message body." },
+ "autowiredEnabled": { "index": 11, "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching [...]
+ "threshold": { "index": 12, "kind": "property", "displayName": "Threshold", "group": "advanced", "label": "advanced", "required": false, "type": "number", "javaType": "double", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 0.5, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "Default inclusive probability threshold for the TypeSafe AI language. Must be within 0,1." },
+ "uncertainty": { "index": 13, "kind": "property", "displayName": "Uncertainty", "group": "advanced", "label": "advanced", "required": false, "type": "number", "javaType": "double", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 0.0, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "Default half-width of the inclusive uncertainty band for the TypeSafe AI language. Zer [...]
+ "uncertaintyPolicy": { "index": 14, "kind": "property", "displayName": "Uncertainty Policy", "group": "advanced", "label": "advanced", "required": false, "type": "enum", "javaType": "org.apache.camel.language.typesafeai.TypeSafeAiLanguage.UncertaintyPolicy", "enum": [ "NonMatch", "Fail" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "NonMatch", "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "c [...]
+ "apiKey": { "index": 15, "kind": "property", "displayName": "Api Key", "group": "security", "label": "security", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": true, "security": "secret", "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "The API key used for Bearer authentication." }
},
"properties": {
"name": { "index": 0, "kind": "path", "displayName": "Name", "group": "producer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "A logical name for the evaluation endpoint." },
- "baseUrl": { "index": 1, "kind": "parameter", "displayName": "Base Url", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "https:\/\/api.typesafe.ai", "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "The API base URL. The client appends \/v1\/systemone. Redirects [...]
- "maxConcurrentRequests": { "index": 2, "kind": "parameter", "displayName": "Max Concurrent Requests", "group": "common", "label": "common", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 64, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "Maximum concurrent evaluations per endpoint, shared by producers and pr [...]
- "model": { "index": 3, "kind": "parameter", "displayName": "Model", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "jev-latest", "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "The model ID or alias. Use a versioned ID to pin decision behavior." },
- "questions": { "index": 4, "kind": "parameter", "displayName": "Questions", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "A JSON object mapping question names to Noul, Choice or Score question objects. When set, producers evalu [...]
- "questionsResource": { "index": 5, "kind": "parameter", "displayName": "Questions Resource", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "Camel resource URI for a UTF-8 JSON object mapping question names to Noul, Choice or Sco [...]
- "requestTimeout": { "index": 6, "kind": "parameter", "displayName": "Request Timeout", "group": "common", "label": "common", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 30000, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "The timeout in milliseconds for the complete HTTP request and response body. Must [...]
- "state": { "index": 7, "kind": "parameter", "displayName": "State", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "The Simple expression selecting state for configured producer questions and the TypeSafe AI language. If not set, [...]
- "resultProperty": { "index": 8, "kind": "parameter", "displayName": "Result Property", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "Store the producer response in this exchange property, preserving the original message body." },
- "lazyStartProducer": { "index": 9, "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer (advanced)", "label": "producer,advanced", "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 produc [...]
- "threshold": { "index": 10, "kind": "parameter", "displayName": "Threshold", "group": "advanced", "label": "advanced", "required": false, "type": "number", "javaType": "double", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 0.5, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "Default inclusive probability threshold for the TypeSafe AI language. Must be within 0,1." },
- "uncertainty": { "index": 11, "kind": "parameter", "displayName": "Uncertainty", "group": "advanced", "label": "advanced", "required": false, "type": "number", "javaType": "double", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 0.0, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "Default half-width of the inclusive uncertainty band for the TypeSafe AI language. Ze [...]
- "uncertaintyPolicy": { "index": 12, "kind": "parameter", "displayName": "Uncertainty Policy", "group": "advanced", "label": "advanced", "required": false, "type": "enum", "javaType": "org.apache.camel.language.typesafeai.TypeSafeAiLanguage.UncertaintyPolicy", "enum": [ "NonMatch", "Fail" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "NonMatch", "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": " [...]
- "apiKey": { "index": 13, "kind": "parameter", "displayName": "Api Key", "group": "security", "label": "security", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": true, "security": "secret", "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "The API key used for Bearer authentication." }
+ "apiPath": { "index": 1, "kind": "parameter", "displayName": "Api Path", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "\/v1\/systemone", "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "The API path appended to baseUrl. A leading slash is optional. Must be a [...]
+ "baseUrl": { "index": 2, "kind": "parameter", "displayName": "Base Url", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "https:\/\/api.typesafe.ai", "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "The API base URL. The client appends apiPath, preserving any ba [...]
+ "maxConcurrentRequests": { "index": 3, "kind": "parameter", "displayName": "Max Concurrent Requests", "group": "common", "label": "common", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 64, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "Maximum concurrent evaluations per endpoint, shared by producers and pr [...]
+ "model": { "index": 4, "kind": "parameter", "displayName": "Model", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "jev-latest", "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "The model ID or alias. Use a versioned ID to pin decision behavior." },
+ "questions": { "index": 5, "kind": "parameter", "displayName": "Questions", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "A JSON object mapping question names to Noul, Choice or Score question objects. When set, producers evalu [...]
+ "questionsResource": { "index": 6, "kind": "parameter", "displayName": "Questions Resource", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "Camel resource URI for a UTF-8 JSON object mapping question names to Noul, Choice or Sco [...]
+ "requestTimeout": { "index": 7, "kind": "parameter", "displayName": "Request Timeout", "group": "common", "label": "common", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 30000, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "The timeout in milliseconds for the complete HTTP request and response body. Must [...]
+ "state": { "index": 8, "kind": "parameter", "displayName": "State", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "The Simple expression selecting state for configured producer questions and the TypeSafe AI language. If not set, [...]
+ "resultProperty": { "index": 9, "kind": "parameter", "displayName": "Result Property", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "Store the producer response in this exchange property, preserving the original message body." },
+ "lazyStartProducer": { "index": 10, "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer (advanced)", "label": "producer,advanced", "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 produ [...]
+ "threshold": { "index": 11, "kind": "parameter", "displayName": "Threshold", "group": "advanced", "label": "advanced", "required": false, "type": "number", "javaType": "double", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 0.5, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "Default inclusive probability threshold for the TypeSafe AI language. Must be within 0,1." },
+ "uncertainty": { "index": 12, "kind": "parameter", "displayName": "Uncertainty", "group": "advanced", "label": "advanced", "required": false, "type": "number", "javaType": "double", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 0.0, "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "Default half-width of the inclusive uncertainty band for the TypeSafe AI language. Ze [...]
+ "uncertaintyPolicy": { "index": 13, "kind": "parameter", "displayName": "Uncertainty Policy", "group": "advanced", "label": "advanced", "required": false, "type": "enum", "javaType": "org.apache.camel.language.typesafeai.TypeSafeAiLanguage.UncertaintyPolicy", "enum": [ "NonMatch", "Fail" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "NonMatch", "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": " [...]
+ "apiKey": { "index": 14, "kind": "parameter", "displayName": "Api Key", "group": "security", "label": "security", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": true, "security": "secret", "configurationClass": "org.apache.camel.component.typesafeai.TypeSafeAiConfiguration", "configurationField": "configuration", "description": "The API key used for Bearer authentication." }
}
}
diff --git a/components/camel-ai/camel-typesafe-ai/src/main/docs/typesafe-ai-component.adoc b/components/camel-ai/camel-typesafe-ai/src/main/docs/typesafe-ai-component.adoc
index 2d1e0cd0ec58..942b64630924 100644
--- a/components/camel-ai/camel-typesafe-ai/src/main/docs/typesafe-ai-component.adoc
+++ b/components/camel-ai/camel-typesafe-ai/src/main/docs/typesafe-ai-component.adoc
@@ -66,7 +66,8 @@ camel.component.typesafe-ai.max-concurrent-requests=64
----
`apiKey` is a secret option. `baseUrl` defaults to `https://api.typesafe.ai`; the client
-appends `/v1/systemone`. A base path can address a proxy. HTTP redirects are not followed.
+appends `apiPath`, which defaults to `/v1/systemone`. A base path can address a proxy.
+HTTP redirects are not followed.
Use HTTPS for the service; HTTP is useful for local testing.
`model` defaults to `jev-latest`. Pin a version when tuning thresholds; aliases can change
@@ -74,6 +75,27 @@ behavior without a route change. The request map may explicitly override the end
model for that request. The response's `model` identifies the model that answered.
See https://docs.typesafe.ai/models[available models].
+=== Custom API path
+
+Set `apiPath` when a service or gateway exposes the supported TypeSafe AI request and
+response schema at a different path:
+
+[source,properties]
+----
+camel.component.typesafe-ai.base-url=http://localhost:8100
+camel.component.typesafe-ai.api-path=/v1/decision
+----
+
+The resulting request URL is `http://localhost:8100/v1/decision`. A path prefix in `baseUrl`
+is preserved: `http://localhost:8100/gateway/` produces
+`http://localhost:8100/gateway/v1/decision`. The leading slash in `apiPath` is optional.
+The path must not be blank or include a scheme, authority, query or fragment.
+
+An endpoint can override the component setting with `typesafe-ai:refund?apiPath=/v1/decision`.
+The Semantic language's TypeSafe AI adapter inherits the component setting even when the
+route contains no `typesafe-ai` endpoint. Changing the path does not change the required
+request and response schema.
+
=== Concurrent requests
`maxConcurrentRequests` limits concurrent evaluations to 64 per endpoint by default and must
diff --git a/components/camel-ai/camel-typesafe-ai/src/main/java/org/apache/camel/component/typesafeai/TypeSafeAiClient.java b/components/camel-ai/camel-typesafe-ai/src/main/java/org/apache/camel/component/typesafeai/TypeSafeAiClient.java
index 393d79053a23..8b4d3c4712cb 100644
--- a/components/camel-ai/camel-typesafe-ai/src/main/java/org/apache/camel/component/typesafeai/TypeSafeAiClient.java
+++ b/components/camel-ai/camel-typesafe-ai/src/main/java/org/apache/camel/component/typesafeai/TypeSafeAiClient.java
@@ -32,6 +32,7 @@ import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import org.apache.camel.util.URISupport;
import org.apache.camel.util.json.JsonObject;
import org.apache.camel.util.json.Jsoner;
@@ -52,8 +53,7 @@ final class TypeSafeAiClient implements AutoCloseable {
maxConcurrentRequests = configuration.getMaxConcurrentRequests();
apiKey = configuration.getApiKey();
model = configuration.getModel();
- String baseUrl = configuration.getBaseUrl();
- uri = URI.create((baseUrl.endsWith("/") ? baseUrl.substring(0, baseUrl.length() - 1) : baseUrl) + "/v1/systemone");
+ uri = URI.create(URISupport.joinPaths(configuration.getBaseUrl(), configuration.getApiPath()));
http = HttpClient.newBuilder().connectTimeout(timeout).followRedirects(HttpClient.Redirect.NEVER).build();
}
diff --git a/components/camel-ai/camel-typesafe-ai/src/main/java/org/apache/camel/component/typesafeai/TypeSafeAiConfiguration.java b/components/camel-ai/camel-typesafe-ai/src/main/java/org/apache/camel/component/typesafeai/TypeSafeAiConfiguration.java
index b23be21886cc..9b814600fd82 100644
--- a/components/camel-ai/camel-typesafe-ai/src/main/java/org/apache/camel/component/typesafeai/TypeSafeAiConfiguration.java
+++ b/components/camel-ai/camel-typesafe-ai/src/main/java/org/apache/camel/component/typesafeai/TypeSafeAiConfiguration.java
@@ -31,6 +31,8 @@ public class TypeSafeAiConfiguration implements Cloneable {
private String apiKey;
@UriParam(label = "common", defaultValue = "https://api.typesafe.ai")
private String baseUrl = "https://api.typesafe.ai";
+ @UriParam(label = "common", defaultValue = "/v1/systemone")
+ private String apiPath = "/v1/systemone";
@UriParam(label = "common", defaultValue = "jev-latest")
private String model = "jev-latest";
@UriParam(label = "common", defaultValue = "30000")
@@ -147,11 +149,23 @@ public class TypeSafeAiConfiguration implements Cloneable {
return baseUrl;
}
- /** The API base URL. The client appends /v1/systemone. Redirects are not followed. */
+ /** The API base URL. The client appends apiPath, preserving any base path. Redirects are not followed. */
public void setBaseUrl(String baseUrl) {
this.baseUrl = baseUrl;
}
+ public String getApiPath() {
+ return apiPath;
+ }
+
+ /**
+ * The API path appended to baseUrl. A leading slash is optional. Must be a non-blank path without a scheme,
+ * authority, query or fragment. The service must implement the supported TypeSafe AI request and response schema.
+ */
+ public void setApiPath(String apiPath) {
+ this.apiPath = apiPath;
+ }
+
public String getModel() {
return model;
}
@@ -200,6 +214,7 @@ public class TypeSafeAiConfiguration implements Cloneable {
ObjectHelper.notNull(apiKey, "apiKey");
ObjectHelper.notNull(model, "model");
ObjectHelper.notNull(baseUrl, "baseUrl");
+ ObjectHelper.notNull(apiPath, "apiPath");
if (apiKey.isBlank() || model.isBlank()) {
throw new IllegalArgumentException("apiKey and model must not be blank");
}
@@ -211,6 +226,14 @@ public class TypeSafeAiConfiguration implements Cloneable {
|| uri.getHost() == null || uri.getUserInfo() != null || uri.getFragment() != null || uri.getQuery() != null) {
throw new IllegalArgumentException("baseUrl must be an HTTP(S) URL without user information, query or fragment");
}
+ if (apiPath.isBlank()) {
+ throw new IllegalArgumentException("apiPath must not be blank");
+ }
+ URI path = URI.create(apiPath);
+ if (path.getScheme() != null || path.getRawAuthority() != null || path.getQuery() != null
+ || path.getFragment() != null) {
+ throw new IllegalArgumentException("apiPath must be a path without a scheme, authority, query or fragment");
+ }
if (requestTimeout <= 0) {
throw new IllegalArgumentException("requestTimeout must be positive");
}
diff --git a/components/camel-ai/camel-typesafe-ai/src/test/java/org/apache/camel/component/typesafeai/TypeSafeAiApiPathTest.java b/components/camel-ai/camel-typesafe-ai/src/test/java/org/apache/camel/component/typesafeai/TypeSafeAiApiPathTest.java
new file mode 100644
index 000000000000..38248cd7131a
--- /dev/null
+++ b/components/camel-ai/camel-typesafe-ai/src/test/java/org/apache/camel/component/typesafeai/TypeSafeAiApiPathTest.java
@@ -0,0 +1,138 @@
+/*
+ * 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.typesafeai;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.main.Main;
+import org.apache.camel.semantic.SemanticQuestion;
+import org.apache.camel.semantic.SemanticQuestions;
+import org.apache.camel.util.json.JsonObject;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
+import org.junit.jupiter.params.provider.NullAndEmptySource;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+class TypeSafeAiApiPathTest extends TypeSafeAiTestSupport {
+ private final ConcurrentLinkedQueue<String> paths = new ConcurrentLinkedQueue<>();
+
+ @BeforeEach
+ void capturePaths() {
+ server.removeContext("/v1/systemone");
+ server.createContext("/", exchange -> {
+ paths.add(exchange.getRequestURI().toString());
+ handle(exchange);
+ });
+ }
+
+ @ParameterizedTest
+ @CsvSource({
+ "'', /v1/systemone", "/, /v1/systemone",
+ "/gateway, /gateway/v1/systemone", "/gateway/, /gateway/v1/systemone",
+ "/gateway//, /gateway//v1/systemone" })
+ void defaultPathPreservesBaseUrlBehavior(String prefix, String expectedPath) {
+ TypeSafeAiConfiguration configuration
+ = context.getComponent("typesafe-ai", TypeSafeAiComponent.class).getConfiguration();
+ configuration.setBaseUrl(configuration.getBaseUrl() + prefix);
+
+ JsonObject response = template.requestBody("typesafe-ai:default", request("Refund"), JsonObject.class);
+
+ assertThat(response.getJsonObject("answers")).containsKey("predicate");
+ assertThat(paths).containsExactly(expectedPath);
+ }
+
+ @ParameterizedTest
+ @CsvSource({
+ "'', /v1/decision, /v1/decision", "'', v1/decision, /v1/decision",
+ "/, /v1/decision, /v1/decision", "/, v1/decision, /v1/decision",
+ "/gateway, /v1/decision, /gateway/v1/decision", "/gateway, v1/decision, /gateway/v1/decision",
+ "/gateway/, /v1/decision, /gateway/v1/decision", "/gateway/, v1/decision, /gateway/v1/decision",
+ "/gateway/, /v1/decision/, /gateway/v1/decision/", "/gateway/, /, /gateway/" })
+ void endpointPathOverridesComponentDefault(String prefix, String apiPath, String expectedPath) {
+ TypeSafeAiConfiguration configuration
+ = context.getComponent("typesafe-ai", TypeSafeAiComponent.class).getConfiguration();
+ configuration.setBaseUrl(configuration.getBaseUrl() + prefix);
+ configuration.setApiPath("/component");
+
+ JsonObject response
+ = template.requestBody("typesafe-ai:custom?apiPath=" + apiPath, request("Refund"), JsonObject.class);
+
+ assertThat(response.getJsonObject("answers")).containsKey("predicate");
+ assertThat(paths).containsExactly(expectedPath);
+ assertThat(configuration.getApiPath()).isEqualTo("/component");
+ }
+
+ @ParameterizedTest
+ @ValueSource(booleans = { false, true })
+ void componentPropertiesConfigureProducerAndSemanticAdapter(boolean semantic) throws Exception {
+ Main main = new Main();
+ main.addProperty("camel.component.typesafe-ai.api-key", "test-key");
+ main.addProperty("camel.component.typesafe-ai.base-url",
+ "http://127.0.0.1:" + server.getAddress().getPort() + "/gateway/");
+ main.addProperty("camel.component.typesafe-ai.api-path", "/v1/decision");
+ main.configure().addRoutesBuilder(new RouteBuilder() {
+ @Override
+ public void configure() {
+ if (semantic) {
+ SemanticQuestions.get(getContext()).replace("test", Map.of("refund", new SemanticQuestion(
+ SemanticQuestion.Type.BOOLEAN, "Refund requested?", null,
+ Map.of(), List.of(), 0.5, 0, SemanticQuestion.UncertaintyPolicy.FAIL)));
+ from("direct:request").setBody().language("semantic", "ref:refund");
+ } else {
+ from("direct:request").to("typesafe-ai:configured");
+ }
+ }
+ });
+ respond = request -> result(Map.of(semantic ? "question" : "predicate", Map.of("type", "noul", "noul", 0.9)));
+ try {
+ main.start();
+ try (var producer = main.getCamelContext().createProducerTemplate()) {
+ Object response = producer.requestBody("direct:request", semantic ? "Refund" : request("Refund"));
+ if (semantic) {
+ assertThat(response).isEqualTo(true);
+ } else {
+ assertThat(((JsonObject) response).getJsonObject("answers")).containsKey("predicate");
+ }
+ assertThat(paths).containsExactly("/gateway/v1/decision");
+ assertThat(requests).hasSize(1);
+ assertThat(requests.peek().get("state")).isEqualTo("Refund");
+ assertThat(authorization).containsExactly("Bearer test-key");
+ }
+ } finally {
+ main.stop();
+ }
+ }
+
+ @ParameterizedTest
+ @NullAndEmptySource
+ @ValueSource(strings = {
+ " ", "https://example.com/v1/decision", "//example.com/v1/decision", "/v1/decision?query=value",
+ "/v1/decision#fragment", "/invalid path", "/invalid%path" })
+ void rejectsInvalidPathsBeforeSending(String apiPath) {
+ context.getComponent("typesafe-ai", TypeSafeAiComponent.class).getConfiguration().setApiPath(apiPath);
+
+ assertThatThrownBy(() -> context.getEndpoint("typesafe-ai:invalid")).isInstanceOf(Exception.class);
+ assertThat(paths).isEmpty();
+ }
+}
diff --git a/components/camel-ai/camel-typesafe-ai/src/test/java/org/apache/camel/component/typesafeai/TypeSafeAiTestSupport.java b/components/camel-ai/camel-typesafe-ai/src/test/java/org/apache/camel/component/typesafeai/TypeSafeAiTestSupport.java
index e32f630421e5..a282ee45dd3a 100644
--- a/components/camel-ai/camel-typesafe-ai/src/test/java/org/apache/camel/component/typesafeai/TypeSafeAiTestSupport.java
+++ b/components/camel-ai/camel-typesafe-ai/src/test/java/org/apache/camel/component/typesafeai/TypeSafeAiTestSupport.java
@@ -87,7 +87,7 @@ abstract class TypeSafeAiTestSupport {
}
}
- private void handle(HttpExchange exchange) throws IOException {
+ void handle(HttpExchange exchange) throws IOException {
try {
JsonObject request = (JsonObject) Jsoner.deserialize(
new String(exchange.getRequestBody().readAllBytes(), StandardCharsets.UTF_8));
diff --git a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/TypesafeAiComponentBuilderFactory.java b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/TypesafeAiComponentBuilderFactory.java
index 0b906952c114..cec32d2b62f6 100644
--- a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/TypesafeAiComponentBuilderFactory.java
+++ b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/TypesafeAiComponentBuilderFactory.java
@@ -52,8 +52,28 @@ public interface TypesafeAiComponentBuilderFactory {
/**
- * The API base URL. The client appends /v1/systemone. Redirects are not
- * followed.
+ * The API path appended to baseUrl. A leading slash is optional. Must
+ * be a non-blank path without a scheme, authority, query or fragment.
+ * The service must implement the supported TypeSafe AI request and
+ * response schema.
+ *
+ * The option is a: <code>java.lang.String</code> type.
+ *
+ * Default: /v1/systemone
+ * Group: common
+ *
+ * @param apiPath the value to set
+ * @return the dsl builder
+ */
+ default TypesafeAiComponentBuilder apiPath(java.lang.String apiPath) {
+ doSetProperty("apiPath", apiPath);
+ return this;
+ }
+
+
+ /**
+ * The API base URL. The client appends apiPath, preserving any base
+ * path. Redirects are not followed.
*
* The option is a: <code>java.lang.String</code> type.
*
@@ -341,6 +361,7 @@ public interface TypesafeAiComponentBuilderFactory {
String name,
Object value) {
switch (name) {
+ case "apiPath": getOrCreateConfiguration((TypeSafeAiComponent) component).setApiPath((java.lang.String) value); return true;
case "baseUrl": getOrCreateConfiguration((TypeSafeAiComponent) component).setBaseUrl((java.lang.String) value); return true;
case "configuration": ((TypeSafeAiComponent) component).setConfiguration((org.apache.camel.component.typesafeai.TypeSafeAiConfiguration) value); return true;
case "maxConcurrentRequests": getOrCreateConfiguration((TypeSafeAiComponent) component).setMaxConcurrentRequests((int) value); return true;
diff --git a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/TypeSafeAiEndpointBuilderFactory.java b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/TypeSafeAiEndpointBuilderFactory.java
index 3ed41187a75d..d43faded2df3 100644
--- a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/TypeSafeAiEndpointBuilderFactory.java
+++ b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/TypeSafeAiEndpointBuilderFactory.java
@@ -45,8 +45,26 @@ public interface TypeSafeAiEndpointBuilderFactory {
}
/**
- * The API base URL. The client appends /v1/systemone. Redirects are not
- * followed.
+ * The API path appended to baseUrl. A leading slash is optional. Must
+ * be a non-blank path without a scheme, authority, query or fragment.
+ * The service must implement the supported TypeSafe AI request and
+ * response schema.
+ *
+ * The option is a: <code>java.lang.String</code> type.
+ *
+ * Default: /v1/systemone
+ * Group: common
+ *
+ * @param apiPath the value to set
+ * @return the dsl builder
+ */
+ default TypeSafeAiEndpointBuilder apiPath(String apiPath) {
+ doSetProperty("apiPath", apiPath);
+ return this;
+ }
+ /**
+ * The API base URL. The client appends apiPath, preserving any base
+ * path. Redirects are not followed.
*
* The option is a: <code>java.lang.String</code> type.
*