Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,144 @@ Reloading a resource replaces its complete set of questions, including removing
no longer present. Development-mode route reload also removes definitions from deleted or renamed files
before parsing replacements. This replacement does not make the surrounding route reload transactional.
Existing expressions resolve the current definition on their next evaluation.
Loading declarations does not perform inference. Java applications can register immutable
Loading declarations does not perform inference.

=== Java declarations

Use the fluent helper from `camel-semantic` inside an ordinary `RouteBuilder`:

[source,java]
----
import static org.apache.camel.semantic.SemanticQuestionsBuilder.semanticQuestions;

@Override
public void configure() {
semanticQuestions(this)
.question("department")
.type("choice")
.state("${header.myState}")
.instructions("Which department should handle this message?")
.criterion("billing", "Invoices, payments, and refunds")
.criterion("technical", "Bugs, outages, and technical problems")
.criterion("other", "Everything else")
.register();

from("direct:tickets")
.setProperty("department").language("semantic", "ref:department")
.to("direct:dispatch");
}
----

Call `.end().question("anotherName")` to add another question to the group, then `.register()`
once to validate and install the entire group atomically. Register before creating expressions
that refer to these names. When adding builders to a running context, add the builder declaring
shared questions before builders whose routes reference them.

Use `.type("boolean")` with optional `.threshold(0.8)`, `.uncertainty(0.1)` and
`.uncertaintyPolicy("non-match")` for boolean questions. Use `.type("score")` and successive
`.level("description")` calls for ordered score levels. Applications can also register immutable
`SemanticQuestion` definitions using `SemanticQuestions.get(context).replace(source, questions)`.

Each Java resource owns one group. Register all of its questions together; registering again
replaces that resource's previous group. Reloading a Java resource without the helper removes
its declarations. Embedded builders without a resource receive distinct generated source keys.
The helper reserves `"java:" + resource.getLocation()` as the resource source key; passing that
key and an empty map to `replace` removes the resource's questions.

=== XML declarations

The XML extension belongs to `camel-semantic`. Include `camel-semantic` and
xref:others:java-xml-io-dsl.adoc[XML DSL] (`camel-xml-io-dsl`). Camel discovers the extension
automatically at startup: use ordinary `*.xml` files without registering a loader or changing
the Camel core model. XML documents without semantic declarations are handled by the standard
XML loader, including its bean and route configuration support. XML loaders registered by the
application before route resource discovery retain precedence over automatic discovery.
Loader discovery is refreshed when the context starts or routes are reloaded.
Filenames may contain dots, such as `my.tickets.xml`. Routes retain their original resource
locations and line numbers for debugging and error messages.

Both layouts are supported. To keep declarations alongside routes, use `tickets.xml`:

[source,xml]
----
<routes>
<semantic>
<question name="department" type="choice" state="${header.myState}">
<instructions>Which department should handle this message?</instructions>
<criterion key="billing" value="Invoices, payments, and refunds"/>
<criterion key="technical" value="Bugs, outages, and technical problems"/>
<criterion key="other" value="Everything else"/>
</question>
</semantic>
<route id="classify-ticket">
<from uri="direct:tickets"/>
<setProperty name="department">
<language language="semantic">ref:department</language>
</setProperty>
<to uri="direct:dispatch"/>
</route>
</routes>
----

To share declarations across route files, use a standalone `questions.xml`:

[source,xml]
----
<semantic>
<question name="department" type="choice" state="${header.myState}">
<instructions>Which department should handle this message?</instructions>
<criterion key="billing" value="Invoices, payments, and refunds"/>
<criterion key="technical" value="Bugs, outages, and technical problems"/>
<criterion key="other" value="Everything else"/>
</question>
</semantic>
----

Load this together with ordinary `*.xml`, Java or YAML route resources that use `ref:department`.
For example, with Camel Main, set
`camel.main.routes-include-pattern=classpath:questions.xml,classpath:routes.xml`.
Declarations are registered before consuming routes are configured. Reload replaces the source's
questions; removing the `semantic` block, using an empty block, or deleting the resource removes
obsolete definitions. XML source keys are the resource locations.

The combined format supports a `routes` root with one optional `semantic` block and ordinary
`route` elements. The standalone format uses a `semantic` root. Namespace-free documents are
supported, as are documents consistently using `http://camel.apache.org/schema/semantic`,
`http://camel.apache.org/schema/xml-io`, or `http://camel.apache.org/schema/spring`.
A `semantic` block can also declare `xmlns="http://camel.apache.org/schema/semantic"` inside a
standard `routes` document; its question elements inherit that namespace. The extension detects
the declaration block or semantic root namespace automatically.

These extensions do not validate against Camel's standard core XSDs. Automatic discovery applies
to Camel's route resource loader; it does not extend Spring's XML application-context parser
or direct JAXB unmarshalling.

For boolean questions, `threshold`, `uncertainty` and `uncertaintyPolicy` are optional question
attributes. For score questions, replace the named `criterion` elements with ordered `level`
elements, such as `<level>Routine</level><level>Urgent</level><level>Critical</level>`.

The numeric `threshold` and `uncertainty` options accept property placeholders in all three
DSLs. For example, Java accepts `.threshold("{{semantic.threshold:0.5}}")`, and XML accepts
`threshold="{{semantic.threshold:0.5}}"`. Values are resolved and validated when declarations
are registered.

Java and XML declarations use the same context-wide registry, validation, defaults and adapters
as YAML. They do not require `camel-yaml-dsl`. Their questions can also be selected together
using `refs:name1,name2`, as described below.

=== Exporting routes

Question declarations live outside Camel's core route model. Generic model exports and runtime
route dumps such as `camel.main.dumpRoutes=yaml` contain only routes; keep declarations separately
and load them before reloading an exported route.

The MCP route conversion tool rejects Java or YAML inputs containing semantic declarations
because a generic route export would lose those declarations. Convert the routes separately.
Extended XML documents must also be separated into declarations and ordinary routes before
using the generic converter.

=== Selected state

A question's optional `state` Simple expression overrides
`camel.language.semantic.default-state`, whose default is `$\{body}`. Selectors are compiled
before evaluation; selected strings, maps and lists are passed as data and are never evaluated recursively.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,15 +108,17 @@ class DocExamplesXmlSchemaTest {
* tag, a Spring bean next to route fragments, and the {@code <namespace>} child of an expression, which the model
* and the xml-io parser support but no generated schema can express next to the expression text (JAXB
* {@code @XmlValue}), so Spring XML declares the namespaces as {@code xmlns:} attributes instead; and the endpoint
* page's unescaped {@code &}, which is there to show the error it causes.
* page's unescaped {@code &}, which is there to show the error it causes. Semantic declaration documents use a
* component-owned loader outside the core schemas and are covered by camel-semantic's loader tests.
*/
private static final Map<String, String> EXAMPLES_SKIPPED = Map.of(
"xmlsecurity-sign-component", "<bean id=\"xadesProperties\"",
"xmlsecurity-verify-component", "<bean id=\"xadesProperties\"",
"spring-summary", "<beans xmlns=\"http://www.springframework.org/schema/beans\"",
"split-eip", "<namespace key=",
"xtokenize-language", "<namespace key=",
"endpoint", "paramA=1&paramB=2");
"endpoint", "paramA=1&paramB=2",
"semantic-language", "<semantic>");

private static CamelCatalog catalog;
private static Schema springSchema;
Expand Down
15 changes: 15 additions & 0 deletions components/camel-ai/camel-semantic/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@
<groupId>org.apache.camel</groupId>
<artifactId>camel-core-languages</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-core-model</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-xml-io</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-yaml-dsl-common</artifactId>
Expand All @@ -47,6 +57,11 @@
<artifactId>camel-test-junit6</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-xml-io-dsl</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-main</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Generated by camel build tools - do NOT edit this file!
class=org.apache.camel.semantic.SemanticXmlRoutesBuilderLoader
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,144 @@ Reloading a resource replaces its complete set of questions, including removing
no longer present. Development-mode route reload also removes definitions from deleted or renamed files
before parsing replacements. This replacement does not make the surrounding route reload transactional.
Existing expressions resolve the current definition on their next evaluation.
Loading declarations does not perform inference. Java applications can register immutable
Loading declarations does not perform inference.

=== Java declarations

Use the fluent helper from `camel-semantic` inside an ordinary `RouteBuilder`:

[source,java]
----
import static org.apache.camel.semantic.SemanticQuestionsBuilder.semanticQuestions;

@Override
public void configure() {
semanticQuestions(this)
.question("department")
.type("choice")
.state("${header.myState}")
.instructions("Which department should handle this message?")
.criterion("billing", "Invoices, payments, and refunds")
.criterion("technical", "Bugs, outages, and technical problems")
.criterion("other", "Everything else")
.register();

from("direct:tickets")
.setProperty("department").language("semantic", "ref:department")
.to("direct:dispatch");
}
----

Call `.end().question("anotherName")` to add another question to the group, then `.register()`
once to validate and install the entire group atomically. Register before creating expressions
that refer to these names. When adding builders to a running context, add the builder declaring
shared questions before builders whose routes reference them.

Use `.type("boolean")` with optional `.threshold(0.8)`, `.uncertainty(0.1)` and
`.uncertaintyPolicy("non-match")` for boolean questions. Use `.type("score")` and successive
`.level("description")` calls for ordered score levels. Applications can also register immutable
`SemanticQuestion` definitions using `SemanticQuestions.get(context).replace(source, questions)`.

Each Java resource owns one group. Register all of its questions together; registering again
replaces that resource's previous group. Reloading a Java resource without the helper removes
its declarations. Embedded builders without a resource receive distinct generated source keys.
The helper reserves `"java:" + resource.getLocation()` as the resource source key; passing that
key and an empty map to `replace` removes the resource's questions.

=== XML declarations

The XML extension belongs to `camel-semantic`. Include `camel-semantic` and
xref:others:java-xml-io-dsl.adoc[XML DSL] (`camel-xml-io-dsl`). Camel discovers the extension
automatically at startup: use ordinary `*.xml` files without registering a loader or changing
the Camel core model. XML documents without semantic declarations are handled by the standard
XML loader, including its bean and route configuration support. XML loaders registered by the
application before route resource discovery retain precedence over automatic discovery.
Loader discovery is refreshed when the context starts or routes are reloaded.
Filenames may contain dots, such as `my.tickets.xml`. Routes retain their original resource
locations and line numbers for debugging and error messages.

Both layouts are supported. To keep declarations alongside routes, use `tickets.xml`:

[source,xml]
----
<routes>
<semantic>
<question name="department" type="choice" state="${header.myState}">
<instructions>Which department should handle this message?</instructions>
<criterion key="billing" value="Invoices, payments, and refunds"/>
<criterion key="technical" value="Bugs, outages, and technical problems"/>
<criterion key="other" value="Everything else"/>
</question>
</semantic>
<route id="classify-ticket">
<from uri="direct:tickets"/>
<setProperty name="department">
<language language="semantic">ref:department</language>
</setProperty>
<to uri="direct:dispatch"/>
</route>
</routes>
----

To share declarations across route files, use a standalone `questions.xml`:

[source,xml]
----
<semantic>
<question name="department" type="choice" state="${header.myState}">
<instructions>Which department should handle this message?</instructions>
<criterion key="billing" value="Invoices, payments, and refunds"/>
<criterion key="technical" value="Bugs, outages, and technical problems"/>
<criterion key="other" value="Everything else"/>
</question>
</semantic>
----

Load this together with ordinary `*.xml`, Java or YAML route resources that use `ref:department`.
For example, with Camel Main, set
`camel.main.routes-include-pattern=classpath:questions.xml,classpath:routes.xml`.
Declarations are registered before consuming routes are configured. Reload replaces the source's
questions; removing the `semantic` block, using an empty block, or deleting the resource removes
obsolete definitions. XML source keys are the resource locations.

The combined format supports a `routes` root with one optional `semantic` block and ordinary
`route` elements. The standalone format uses a `semantic` root. Namespace-free documents are
supported, as are documents consistently using `http://camel.apache.org/schema/semantic`,
`http://camel.apache.org/schema/xml-io`, or `http://camel.apache.org/schema/spring`.
A `semantic` block can also declare `xmlns="http://camel.apache.org/schema/semantic"` inside a
standard `routes` document; its question elements inherit that namespace. The extension detects
the declaration block or semantic root namespace automatically.

These extensions do not validate against Camel's standard core XSDs. Automatic discovery applies
to Camel's route resource loader; it does not extend Spring's XML application-context parser
or direct JAXB unmarshalling.

For boolean questions, `threshold`, `uncertainty` and `uncertaintyPolicy` are optional question
attributes. For score questions, replace the named `criterion` elements with ordered `level`
elements, such as `<level>Routine</level><level>Urgent</level><level>Critical</level>`.

The numeric `threshold` and `uncertainty` options accept property placeholders in all three
DSLs. For example, Java accepts `.threshold("{{semantic.threshold:0.5}}")`, and XML accepts
`threshold="{{semantic.threshold:0.5}}"`. Values are resolved and validated when declarations
are registered.

Java and XML declarations use the same context-wide registry, validation, defaults and adapters
as YAML. They do not require `camel-yaml-dsl`. Their questions can also be selected together
using `refs:name1,name2`, as described below.

=== Exporting routes

Question declarations live outside Camel's core route model. Generic model exports and runtime
route dumps such as `camel.main.dumpRoutes=yaml` contain only routes; keep declarations separately
and load them before reloading an exported route.

The MCP route conversion tool rejects Java or YAML inputs containing semantic declarations
because a generic route export would lose those declarations. Convert the routes separately.
Extended XML documents must also be separated into declarations and ordinary routes before
using the generic converter.

=== Selected state

A question's optional `state` Simple expression overrides
`camel.language.semantic.default-state`, whose default is `$\{body}`. Selectors are compiled
before evaluation; selected strings, maps and lists are passed as data and are never evaluated recursively.
Expand Down
Loading
Loading