Cameleer

OpenAI component

The openai component calls any OpenAI-compatible API (OpenAI, Ollama, vLLM, LM Studio, LiteLLM and other gateways) for chat completions and embeddings.

Scheme: openai: · Crate: cameleer-saas · Cargo feature: saas · Consumer: no · Producer: yes · Apache Camel: no direct equivalent; closest are LangChain4j Chat and LangChain4j Embeddings

When to use it

  • Classify, route or tag messages with an LLM (ticket priority, sentiment, language, intent).
  • Summarise or rewrite text inside a route (incident reports, e-mails, logs) before sending it on.
  • Create embeddings for semantic search or retrieval-augmented generation (RAG) and store them.
  • Run against a local model (Ollama, vLLM) on an edge box or an air-gapped network with the same route.

URI format

text
openai:chat-completion[?options]
openai:embeddings[?options]

chat, chat-completions and embedding are accepted as aliases. Any other operation fails when the endpoint is created.

Example

Classify a support ticket. A text body is the user message; systemMessage is the system prompt. The body becomes the assistant's answer; the token usage is in headers.

yaml example: openai-classify
- route:
    id: classify-ticket
    from:
      uri: "direct:classify"
      steps:
        - 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=5&systemMessage=Classify the ticket as billing, bug or question. Reply with one word."
        - setHeader:
            name: category
            simple: "${body}"
        - log: "category=${header.category} tokens=${header.CamelOpenAITotalTokens}"

Create an embedding. A text body becomes one vector (a list of numbers); a list body becomes a list of vectors, in the same order.

yaml example: openai-embeddings
- route:
    id: embed-text
    from:
      uri: "direct:embed"
      steps:
        - 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}}"
        - log: "vector with ${body.size()} dimensions"

Use a local model with Ollama (or vLLM, LM Studio): only baseUrl and model change. Ollama does not check the API key, so none is set. A list body is a conversation: maps with role and content, plain strings become user messages.

yaml example: openai-ollama-local
- route:
    id: local-llm
    from:
      uri: "direct:ask-local"
      steps:
        - to: "openai:chat-completion?model={{env:OLLAMA_MODEL:llama3.2}}&baseUrl={{env:OLLAMA_URL:http://localhost:11434/v1}}&systemMessage=Answer briefly."

Rust DSL

rust
ctx.add_routes(|b| {
    b.from("direct:classify")
        .to("openai:chat-completion?model=gpt-4o-mini&temperature=0&systemMessage=Classify the ticket as billing, bug or question. Reply with one word.")
        .set_header("category", simple("${body}"));
})?;

Options

optiondefaultdescription
modelModel name (gpt-4o-mini, text-embedding-3-small, llama3.2, ...). The CamelOpenAIModel header overrides it.
apiKeyOPENAI_API_KEY envSent as Authorization: Bearer ....
organizationSent as the OpenAI-Organization header.
baseUrlhttps://api.openai.com/v1API base URL: any OpenAI-compatible server.
systemMessageChat: system prompt (the CamelOpenAISystemMessage header overrides it).
temperatureChat: sampling temperature.
maxTokensChat: max_tokens.
topPChat: top_p.
userChat: end-user id sent as user.
outputTypefull keeps the whole JSON response as the body instead of the answer text (or the vectors).
timeout120sHTTP timeout.

Values in a request-map body win over the options (an option is only added when the key is missing).

Message headers

headerdirectiondescription
CamelOpenAISystemMessageinChat: system prompt.
CamelOpenAIModelinModel for this call.
CamelOpenAIResponseIdoutChat: response id.
CamelOpenAIResponseModeloutChat: model that answered.
CamelOpenAIFinishReasonoutChat: stop, length, tool_calls, ...
CamelOpenAIPromptTokens, CamelOpenAICompletionTokens, CamelOpenAITotalTokensoutChat: token usage.

Chat bodies: a text body is one user message (after the system message); a list body is the message list; a map body is sent as the whole request (model and the options are added when missing), which lets you pass fields the component has no option for, such as response_format, seed or tools.

Run it with Docker

sh
export OPENAI_API_KEY=sk-...
docker run --rm -e OPENAI_API_KEY -v "$PWD:/routes:ro" pmsudhi/cameleer run /routes/classify-ticket.camel.yaml

With Ollama on the host (Docker Desktop):

sh
docker run --rm -e OLLAMA_URL=http://host.docker.internal:11434/v1 -v "$PWD:/routes:ro" pmsudhi/cameleer run /routes/local-llm.camel.yaml

Common questions

Does it support streaming?

No. The component sends one request and waits for the complete answer (timeout, default 120 seconds). For streaming, use azure-openai (streaming=true), aws-bedrock or google-vertexai.

Does it support tool calls (function calling)?

Not natively: there is no tool option, no tool-call header and no agent loop. You can send a request map with tools and outputType=full, then read choices[0].message.tool_calls from the response yourself. The AI components guide shows this and the components with built-in tool-call support.

How do I get JSON output?

Put "response_format": {"type": "json_object"} in a request-map body (or ask for JSON in the system message) and unmarshal: json the answer.

What happens on an API error?

A non-2xx response fails with HttpOperationFailedException; the message contains the status and the API's error.message (for example Incorrect API key provided). Rate limits (429) can be retried with onException and a redelivery policy.

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.

Edit this page on GitHub