AI components
Cameleer calls large language models from routes with four components:
openai(OpenAI and any OpenAI-compatible server such as Ollama or vLLM),azure-openai,aws-bedrockandgoogle-vertexai; they cover chat, embeddings and, depending on the provider, streaming, tool calls and images.
Every AI component is a producer: a step in a route that sends the message body to a model and replaces the body with the answer. Everything else (where the text comes from, what happens with the answer, retries, filtering, splitting) is ordinary Camel routing, so an "AI agent" in Cameleer is a route: a trigger, one or more model calls, and the systems the agent acts on.
Which component to use
| openai | azure-openai | aws-bedrock | google-vertexai | |
|---|---|---|---|---|
| Providers | OpenAI, Ollama, vLLM, LM Studio, LiteLLM, any /v1/chat/completions server | Azure OpenAI deployments | Models on Amazon Bedrock (Anthropic, Amazon Titan, Meta, Mistral, ...) | Gemini; partner models (Anthropic, Meta, Mistral) via rawPredict |
| Chat | chat-completion | chatCompletion | converse, invokeTextModel | generateText, generateChat, generateCode, generateMultimodal |
| Embeddings | embeddings | embeddings | invokeEmbeddingsModel | generateEmbeddings |
| Streaming | no | yes (streaming=true) | yes (converseStream, invoke*Streaming) | yes (generate*Streaming, streamRawPredict) |
| Tool calls | pass-through only: send tools in a request map, read the raw response with outputType=full | the model's tool calls in the CamelAzureOpenAIToolCalls header | CamelAwsBedrockConverseToolConfig header; tool-use blocks in CamelAwsBedrockConverseOutputMessage | pass-through only: send tools in a request map, read the raw response in CamelGoogleVertexAIResponse |
| Images | no | imageGeneration (DALL-E) | invokeImageModel | generateImage (Imagen), image input with generateMultimodal |
| Extras | Assistants API (runAssistant, threads, runs) | Guardrails, knowledge bases and agents (aws-bedrock-agent-runtime) | token counting (countTokens) | |
| Auth | API key (Bearer) or none | API key or Entra ID client credentials | AWS credentials (SigV4) | Google service account / workload identity |
All four give the answer text as the body and token usage and finish reason as headers. Check each component page for the exact option and header names.
What is not supported
Be aware of these limits before you design around them:
- No agent loop. No component executes tools for you: when a model asks for a tool, your
route must call the tool and send the result back in a new request. Server-side agents are the
exception: Bedrock Agents (
aws-bedrock-agent-runtime,invokeAgent) and Azure OpenAI Assistants (runAssistant) run the loop in the cloud service. openaihas no streaming and no tool-call options. It waits for the complete answer.- No vector store component. Embeddings are lists of numbers in the body; store and search them with the data store you already run (mongodb, elasticsearch, spring-redis, files).
- No LangChain4j components (
langchain4j-chat,langchain4j-embeddings, ...), no MCP client or server, and no direct Anthropic or Gemini API component: use Claude through Bedrock or Vertex AI, and Gemini through Vertex AI. - No native Ollama API. Ollama, vLLM and LM Studio are used through their OpenAI-compatible
/v1endpoint withopenai:andbaseUrl.
Classify and summarise with an LLM, then act
An agent that triages incidents: the model writes a one-line summary, which becomes the Jira issue summary, while the original text stays the issue description.
- route:
id: ai-triage-to-jira
from:
uri: "direct:incident"
steps:
- setProperty:
name: report
simple: "${body}"
- to: "openai:chat-completion?model={{env:OPENAI_MODEL:gpt-4o-mini}}&apiKey={{env:OPENAI_API_KEY}}&baseUrl={{env:OPENAI_BASE_URL:https://api.openai.com/v1}}&temperature=0&maxTokens=40&systemMessage=Write a one-line Jira summary of this incident report."
- setHeader:
name: IssueSummary
simple: "${body}"
- setBody:
simple: "${exchangeProperty.report}"
- setHeader:
name: ProjectKey
constant: "OPS"
- setHeader:
name: IssueTypeName
constant: "Bug"
- to: "jira:addIssue?jiraUrl={{env:JIRA_URL:https://your-site.atlassian.net}}&username={{env:JIRA_USER}}&password={{env:JIRA_API_TOKEN}}"
- log: "opened ${header.IssueKey}: ${header.IssueSummary}"The same shape works with any target: post the answer to Slack, reply
on Telegram, comment a GitHub pull
request, or use the answer in a choice to route the message (see the
ticket triage example).
Embeddings into a store
Embed every text file dropped into a folder and write the vector next to it as JSON, ready to be loaded into a vector index. Swap the last step for a database component to store it directly.
- route:
id: embed-documents
from:
uri: "file:{{env:DOCS_DIR:docs-in}}?include=.*\\.txt&delay=2000"
steps:
- convertBodyTo:
type: String
- to: "openai:embeddings?model={{env:OPENAI_EMBEDDING_MODEL:text-embedding-3-small}}&apiKey={{env:OPENAI_API_KEY}}&baseUrl={{env:OPENAI_BASE_URL:https://api.openai.com/v1}}"
- marshal:
json: {}
- to: "file:{{env:VECTOR_DIR:vectors}}?fileName=${file:onlyname.noext}.json"Local models: Ollama, vLLM, LM Studio
Point baseUrl at the server's OpenAI-compatible endpoint; nothing else changes. This keeps the
data on the machine, which matters on edge devices and in regulated networks.
| server | baseUrl |
|---|---|
| Ollama | http://localhost:11434/v1 |
| vLLM | http://localhost:8000/v1 |
| LM Studio | http://localhost:1234/v1 |
The openai page has a complete local-model route.
Tool calls
With Azure OpenAI, send the request as a map with tools; when the model decides to call a tool,
the finish reason is tool_calls and the calls (function name and JSON arguments) are in the
CamelAzureOpenAIToolCalls header. Your route runs the tool and sends a follow-up request.
- route:
id: weather-tool-call
from:
uri: "direct:ask-weather"
steps:
- setBody:
constant: '{"messages":[{"role":"user","content":"What is the weather in Oslo?"}],"tools":[{"type":"function","function":{"name":"get_weather","parameters":{"type":"object","properties":{"city":{"type":"string"}}}}}]}'
- unmarshal:
json: {}
- to: "azure-openai://{{env:AZURE_OPENAI_RESOURCE:my-resource}}/{{env:AZURE_OPENAI_DEPLOYMENT:gpt-4o}}?apiKey=RAW({{env:AZURE_OPENAI_KEY}})"
- log: "finish=${header.CamelAzureOpenAIFinishReason} calls=${header.CamelAzureOpenAIToolCalls}"With openai: the same request map is sent unchanged; add outputType=full to get the whole
response, including choices[0].message.tool_calls, as the body:
- route:
id: openai-tools-passthrough
from:
uri: "direct:ask-with-tools"
steps:
- setBody:
constant: '{"messages":[{"role":"user","content":"What is the weather in Oslo?"}],"tools":[{"type":"function","function":{"name":"get_weather","parameters":{"type":"object","properties":{"city":{"type":"string"}}}}}]}'
- unmarshal:
json: {}
- to: "openai:chat-completion?model={{env:OPENAI_MODEL:gpt-4o-mini}}&apiKey={{env:OPENAI_API_KEY}}&baseUrl={{env:OPENAI_BASE_URL:https://api.openai.com/v1}}&outputType=full"
- log: "finish reason: ${header.CamelOpenAIFinishReason}"With Bedrock, put the tool configuration map in the CamelAwsBedrockConverseToolConfig header
of a converse call; see aws-bedrock.
Google Vertex AI (Gemini)
- route:
id: gemini-answer
from:
uri: "direct:ask-gemini"
steps:
- to: "google-vertexai:{{env:GCP_PROJECT:my-project}}:us-central1:gemini-1.5-flash?temperature=0.2&maxOutputTokens=256&systemInstruction=Answer in one paragraph."
- log: "finish=${header.CamelGoogleVertexAIFinishReason} tokens=${header.CamelGoogleVertexAITotalTokenCount}"Credentials come from serviceAccountKey or the GOOGLE_APPLICATION_CREDENTIALS environment
variable. Embeddings use the same component with an embedding model:
- route:
id: gemini-embeddings
from:
uri: "direct:embed-gemini"
steps:
- to: "google-vertexai:{{env:GCP_PROJECT:my-project}}:us-central1:text-embedding-004?operation=generateEmbeddings&taskType=RETRIEVAL_DOCUMENT"More options (streaming, multimodal input, Imagen, Claude on Vertex) are on the google-vertexai page.
Amazon Bedrock
The converse operation works the same way for every Bedrock chat model:
- route:
id: bedrock-converse
from:
uri: "direct:ask-bedrock"
steps:
- setHeader:
name: CamelAwsBedrockConverseSystem
constant: "Answer in one sentence."
- to: "aws-bedrock://chat?modelId=anthropic.claude-3-haiku-20240307-v1:0&operation=converse®ion={{env:AWS_REGION:us-east-1}}&useDefaultCredentialsProvider=true"
- log: "stop=${header.CamelAwsBedrockConverseStopReason}"See aws-bedrock for streaming, embeddings, guardrails, knowledge bases and Bedrock Agents.
Rust DSL
ctx.add_routes(|b| {
b.from("direct:summarise")
.to("openai:chat-completion?model=gpt-4o-mini&systemMessage=Summarise in one sentence.")
.to("slack:#ops?webhookUrl={{env:SLACK_WEBHOOK_URL}}");
})?;Run it with Docker
export OPENAI_API_KEY=sk-... JIRA_USER=bot@example.com JIRA_API_TOKEN=... JIRA_URL=https://your-site.atlassian.net
docker run --rm -e OPENAI_API_KEY -e JIRA_URL -e JIRA_USER -e JIRA_API_TOKEN \
-v "$PWD:/routes:ro" pmsudhi/cameleer run /routes/ai-triage-to-jira.camel.yamlCommon questions
How do I keep the original message after calling the model?
Save it first (setProperty with ${body}), call the model, move the answer into a header,
then restore the body from the property, as in the triage example above.
How do I make model calls reliable?
Treat them like any remote call: onException with a redelivery policy for timeouts and 429
responses, a circuitBreaker around the call, and temperature=0 for classification so the
answers are stable.
Can the same route run against OpenAI in the cloud and Ollama at the edge?
Yes. Put baseUrl, model and apiKey in {{env:...}} placeholders, as in the examples, and
set them per deployment.
How are the examples tested?
Every example on this page runs in the test suite against embedded mock servers of the OpenAI, Azure OpenAI, Bedrock, Vertex AI and Jira APIs, so the option names and the request formats are checked on every build.
Related
- openai, azure-openai, aws-bedrock, google-vertexai
- slack, jira, github, telegram
- EIPs
- Apache Camel AI components
Found a problem?
If something on this page doesn't match what Cameleer does, or you hit a bug while building
with it, please help fix it, whether you are a person or a coding agent:
open an issue that describes what you saw,
then fork the repository, fix it, add or update a test, run cargo test --workspace, and open
a pull request. CONTRIBUTING.md
explains the ground rules.