This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new 289ff4b81dd2 CAMEL-23394: Limit OpenAI conversation history size
289ff4b81dd2 is described below

commit 289ff4b81dd2adeaec13d2f461a0714b52328790
Author: Omar Atie <[email protected]>
AuthorDate: Tue Jul 21 00:01:58 2026 -0700

    CAMEL-23394: Limit OpenAI conversation history size
    
    Add maxHistoryMessages and maxHistoryTokens options to camel-openai to
    bound conversation history growth. Uses segment-based trimming that
    preserves tool-call/tool-result message pairs, with long-based token
    estimation that handles image payloads. Always retains the most recent
    segment even if it alone exceeds the token budget.
    
    Closes #24901
    
    Co-Authored-By: Cursor <[email protected]>
---
 .../apache/camel/catalog/components/openai.json    |  64 ++---
 .../component/openai/OpenAIEndpointConfigurer.java |  12 +
 .../component/openai/OpenAIEndpointUriFactory.java |   4 +-
 .../org/apache/camel/component/openai/openai.json  |  64 ++---
 .../src/main/docs/openai-component.adoc            |   2 +
 .../component/openai/OpenAIConfiguration.java      |  32 +++
 .../openai/OpenAIConversationHistoryTrimmer.java   | 248 +++++++++++++++++++
 .../camel/component/openai/OpenAIProducer.java     |  22 +-
 .../openai/OpenAIToolExecutionProducer.java        |   2 +-
 .../openai/OpenAIConversationHistorySizeTest.java  | 104 ++++++++
 .../OpenAIConversationHistoryTokenLimitTest.java   | 110 +++++++++
 .../OpenAIConversationHistoryTrimmerTest.java      | 274 +++++++++++++++++++++
 .../endpoint/dsl/OpenAIEndpointBuilderFactory.java |  82 ++++++
 13 files changed, 949 insertions(+), 71 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/openai.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/openai.json
index 3784ba5f8579..08defe1693e5 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/openai.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/openai.json
@@ -96,36 +96,38 @@
     "embeddingModel": { "index": 17, "kind": "parameter", "displayName": 
"Embedding Model", "group": "producer", "label": "", "required": false, "type": 
"string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "The model to use for 
embeddings" },
     "encodingFormat": { "index": 18, "kind": "parameter", "displayName": 
"Encoding Format", "group": "producer", "label": "", "required": false, "type": 
"enum", "javaType": "java.lang.String", "enum": [ "float", "base64" ], 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "defaultValue": "base64", "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "The format for embedding ou [...]
     "jsonSchema": { "index": 19, "kind": "parameter", "displayName": "Json 
Schema", "group": "producer", "label": "", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "supportFileReference": true, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "JSON schema for 
structured output validation" },
-    "maxRetries": { "index": 20, "kind": "parameter", "displayName": "Max 
Retries", "group": "producer", "label": "", "required": false, "type": 
"integer", "javaType": "int", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "defaultValue": 2, "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "Maximum number of times the OpenAI SDK client 
retries failed requests. The SDK retr [...]
-    "maxTokens": { "index": 21, "kind": "parameter", "displayName": "Max 
Tokens", "group": "producer", "label": "", "required": false, "type": 
"integer", "javaType": "java.lang.Integer", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "Maximum number of tokens 
to generate" },
-    "maxToolIterations": { "index": 22, "kind": "parameter", "displayName": 
"Max Tool Iterations", "group": "producer", "label": "", "required": false, 
"type": "integer", "javaType": "int", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "defaultValue": 50, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "Maximum number of tool 
call loop iterations to prevent infinite loops" },
-    "mcpProtocolVersions": { "index": 23, "kind": "parameter", "displayName": 
"Mcp Protocol Versions", "group": "producer", "label": "", "required": false, 
"type": "string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "Comma-separated list of 
MCP protocol versions to advertise when connect [...]
-    "mcpReconnect": { "index": 24, "kind": "parameter", "displayName": "Mcp 
Reconnect", "group": "producer", "label": "", "required": false, "type": 
"boolean", "javaType": "boolean", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "defaultValue": true, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "Automatically reconnect 
to MCP servers when a tool call fails due to a t [...]
-    "mcpServer": { "index": 25, "kind": "parameter", "displayName": "Mcp 
Server", "group": "producer", "label": "", "required": false, "type": "object", 
"javaType": "java.util.Map<java.lang.String, java.lang.Object>", "prefix": 
"mcpServer.", "multiValue": true, "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "MCP (Model Cont [...]
-    "mcpTimeout": { "index": 26, "kind": "parameter", "displayName": "Mcp 
Timeout", "group": "producer", "label": "", "required": false, "type": 
"integer", "javaType": "int", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "defaultValue": 20, "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "Timeout in seconds for MCP tool call requests. 
Applies to all MCP operations inclu [...]
-    "model": { "index": 27, "kind": "parameter", "displayName": "Model", 
"group": "producer", "label": "", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "The model to use for chat completion" },
-    "outputClass": { "index": 28, "kind": "parameter", "displayName": "Output 
Class", "group": "producer", "label": "", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "Fully qualified class name for structured 
output using response format" },
-    "requestTimeout": { "index": 29, "kind": "parameter", "displayName": 
"Request Timeout", "group": "producer", "label": "", "required": false, "type": 
"integer", "javaType": "long", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "defaultValue": 0, "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "HTTP request timeout in milliseconds for the 
OpenAI SDK client. When 0 or  [...]
-    "storeFullResponse": { "index": 30, "kind": "parameter", "displayName": 
"Store Full Response", "group": "producer", "label": "", "required": false, 
"type": "boolean", "javaType": "boolean", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "defaultValue": 
false, "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "Store the full response in the exchange 
property 'CamelOpenA [...]
-    "streaming": { "index": 31, "kind": "parameter", "displayName": 
"Streaming", "group": "producer", "label": "", "required": false, "type": 
"boolean", "javaType": "boolean", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "defaultValue": false, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "Enable streaming 
responses" },
-    "stripThinking": { "index": 32, "kind": "parameter", "displayName": "Strip 
Thinking", "group": "producer", "label": "", "required": false, "type": 
"boolean", "javaType": "boolean", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "defaultValue": false, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "Strip ... blocks from 
model responses (used by reasoning models like  [...]
-    "systemMessage": { "index": 33, "kind": "parameter", "displayName": 
"System Message", "group": "producer", "label": "", "required": false, "type": 
"string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "System message to 
prepend. When set and conversationMemory is enabled, the conversat [...]
-    "temperature": { "index": 34, "kind": "parameter", "displayName": 
"Temperature", "group": "producer", "label": "", "required": false, "type": 
"number", "javaType": "java.lang.Double", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "Temperature for response 
generation (0.0 to 2.0)" },
-    "topP": { "index": 35, "kind": "parameter", "displayName": "Top P", 
"group": "producer", "label": "", "required": false, "type": "number", 
"javaType": "java.lang.Double", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "Top P for response generation (0.0 to 1.0)" },
-    "userMessage": { "index": 36, "kind": "parameter", "displayName": "User 
Message", "group": "producer", "label": "", "required": false, "type": 
"string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "Default user message 
text to use when no prompt is provided" },
-    "lazyStartProducer": { "index": 37, "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 [...]
-    "oauthProfile": { "index": 38, "kind": "parameter", "displayName": "Oauth 
Profile", "group": "security", "label": "security", "required": false, "type": 
"string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "OAuth profile name for 
obtaining an access token via the OAuth 2.0 Client Cred [...]
-    "sslContextParameters": { "index": 39, "kind": "parameter", "displayName": 
"Ssl Context Parameters", "group": "security", "label": "security", "required": 
false, "type": "object", "javaType": 
"org.apache.camel.support.jsse.SSLContextParameters", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "SSLContextParameters to 
use [...]
-    "sslEndpointAlgorithm": { "index": 40, "kind": "parameter", "displayName": 
"Ssl Endpoint Algorithm", "group": "security", "label": "security", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "security": 
"insecure:ssl", "insecureValue": "none", "defaultValue": "https", 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", [...]
-    "sslKeymanagerAlgorithm": { "index": 41, "kind": "parameter", 
"displayName": "Ssl Keymanager Algorithm", "group": "security", "label": 
"security", "required": false, "type": "string", "javaType": 
"java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "defaultValue": "SunX509", "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "The algorithm used by the key  [...]
-    "sslKeyPassword": { "index": 42, "kind": "parameter", "displayName": "Ssl 
Key Password", "group": "security", "label": "security", "required": false, 
"type": "string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": true, "security": 
"secret", "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "The password of the private key in the key 
store file" },
-    "sslKeystoreLocation": { "index": 43, "kind": "parameter", "displayName": 
"Ssl Keystore Location", "group": "security", "label": "security", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "The location of the key 
store file. This is optional and can be [...]
-    "sslKeystorePassword": { "index": 44, "kind": "parameter", "displayName": 
"Ssl Keystore Password", "group": "security", "label": "security", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": true, "security": 
"secret", "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "The store password for the key store file" },
-    "sslKeystoreType": { "index": 45, "kind": "parameter", "displayName": "Ssl 
Keystore Type", "group": "security", "label": "security", "required": false, 
"type": "string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "defaultValue": 
"JKS", "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "The file format of the key store file" },
-    "sslProtocol": { "index": 46, "kind": "parameter", "displayName": "Ssl 
Protocol", "group": "security", "label": "security", "required": false, "type": 
"string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "defaultValue": 
"TLSv1.3", "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "The SSL protocol used to generate the 
SSLContext" },
-    "sslTrustmanagerAlgorithm": { "index": 47, "kind": "parameter", 
"displayName": "Ssl Trustmanager Algorithm", "group": "security", "label": 
"security", "required": false, "type": "string", "javaType": 
"java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "defaultValue": "PKIX", "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "The algorithm used by the tru [...]
-    "sslTruststoreLocation": { "index": 48, "kind": "parameter", 
"displayName": "Ssl Truststore Location", "group": "security", "label": 
"security", "required": false, "type": "string", "javaType": 
"java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "The location of the trust store file, used to 
validate the  [...]
-    "sslTruststorePassword": { "index": 49, "kind": "parameter", 
"displayName": "Ssl Truststore Password", "group": "security", "label": 
"security", "required": false, "type": "string", "javaType": 
"java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": true, "security": "secret", "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "The password for the trust store file. [...]
-    "sslTruststoreType": { "index": 50, "kind": "parameter", "displayName": 
"Ssl Truststore Type", "group": "security", "label": "security", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "defaultValue": 
"JKS", "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "The file format of the trust store file" }
+    "maxHistoryMessages": { "index": 20, "kind": "parameter", "displayName": 
"Max History Messages", "group": "producer", "label": "", "required": false, 
"type": "integer", "javaType": "int", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "defaultValue": 0, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "When conversationMemory 
is enabled, retain at most this many messa [...]
+    "maxHistoryTokens": { "index": 21, "kind": "parameter", "displayName": 
"Max History Tokens", "group": "producer", "label": "", "required": false, 
"type": "integer", "javaType": "int", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "defaultValue": 0, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "When conversationMemory 
is enabled, trim conversation history using a  [...]
+    "maxRetries": { "index": 22, "kind": "parameter", "displayName": "Max 
Retries", "group": "producer", "label": "", "required": false, "type": 
"integer", "javaType": "int", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "defaultValue": 2, "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "Maximum number of times the OpenAI SDK client 
retries failed requests. The SDK retr [...]
+    "maxTokens": { "index": 23, "kind": "parameter", "displayName": "Max 
Tokens", "group": "producer", "label": "", "required": false, "type": 
"integer", "javaType": "java.lang.Integer", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "Maximum number of tokens 
to generate" },
+    "maxToolIterations": { "index": 24, "kind": "parameter", "displayName": 
"Max Tool Iterations", "group": "producer", "label": "", "required": false, 
"type": "integer", "javaType": "int", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "defaultValue": 50, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "Maximum number of tool 
call loop iterations to prevent infinite loops" },
+    "mcpProtocolVersions": { "index": 25, "kind": "parameter", "displayName": 
"Mcp Protocol Versions", "group": "producer", "label": "", "required": false, 
"type": "string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "Comma-separated list of 
MCP protocol versions to advertise when connect [...]
+    "mcpReconnect": { "index": 26, "kind": "parameter", "displayName": "Mcp 
Reconnect", "group": "producer", "label": "", "required": false, "type": 
"boolean", "javaType": "boolean", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "defaultValue": true, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "Automatically reconnect 
to MCP servers when a tool call fails due to a t [...]
+    "mcpServer": { "index": 27, "kind": "parameter", "displayName": "Mcp 
Server", "group": "producer", "label": "", "required": false, "type": "object", 
"javaType": "java.util.Map<java.lang.String, java.lang.Object>", "prefix": 
"mcpServer.", "multiValue": true, "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "MCP (Model Cont [...]
+    "mcpTimeout": { "index": 28, "kind": "parameter", "displayName": "Mcp 
Timeout", "group": "producer", "label": "", "required": false, "type": 
"integer", "javaType": "int", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "defaultValue": 20, "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "Timeout in seconds for MCP tool call requests. 
Applies to all MCP operations inclu [...]
+    "model": { "index": 29, "kind": "parameter", "displayName": "Model", 
"group": "producer", "label": "", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "The model to use for chat completion" },
+    "outputClass": { "index": 30, "kind": "parameter", "displayName": "Output 
Class", "group": "producer", "label": "", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "Fully qualified class name for structured 
output using response format" },
+    "requestTimeout": { "index": 31, "kind": "parameter", "displayName": 
"Request Timeout", "group": "producer", "label": "", "required": false, "type": 
"integer", "javaType": "long", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "defaultValue": 0, "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "HTTP request timeout in milliseconds for the 
OpenAI SDK client. When 0 or  [...]
+    "storeFullResponse": { "index": 32, "kind": "parameter", "displayName": 
"Store Full Response", "group": "producer", "label": "", "required": false, 
"type": "boolean", "javaType": "boolean", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "defaultValue": 
false, "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "Store the full response in the exchange 
property 'CamelOpenA [...]
+    "streaming": { "index": 33, "kind": "parameter", "displayName": 
"Streaming", "group": "producer", "label": "", "required": false, "type": 
"boolean", "javaType": "boolean", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "defaultValue": false, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "Enable streaming 
responses" },
+    "stripThinking": { "index": 34, "kind": "parameter", "displayName": "Strip 
Thinking", "group": "producer", "label": "", "required": false, "type": 
"boolean", "javaType": "boolean", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "defaultValue": false, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "Strip ... blocks from 
model responses (used by reasoning models like  [...]
+    "systemMessage": { "index": 35, "kind": "parameter", "displayName": 
"System Message", "group": "producer", "label": "", "required": false, "type": 
"string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "System message to 
prepend. When set and conversationMemory is enabled, the conversat [...]
+    "temperature": { "index": 36, "kind": "parameter", "displayName": 
"Temperature", "group": "producer", "label": "", "required": false, "type": 
"number", "javaType": "java.lang.Double", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "Temperature for response 
generation (0.0 to 2.0)" },
+    "topP": { "index": 37, "kind": "parameter", "displayName": "Top P", 
"group": "producer", "label": "", "required": false, "type": "number", 
"javaType": "java.lang.Double", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "Top P for response generation (0.0 to 1.0)" },
+    "userMessage": { "index": 38, "kind": "parameter", "displayName": "User 
Message", "group": "producer", "label": "", "required": false, "type": 
"string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "Default user message 
text to use when no prompt is provided" },
+    "lazyStartProducer": { "index": 39, "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 [...]
+    "oauthProfile": { "index": 40, "kind": "parameter", "displayName": "Oauth 
Profile", "group": "security", "label": "security", "required": false, "type": 
"string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "OAuth profile name for 
obtaining an access token via the OAuth 2.0 Client Cred [...]
+    "sslContextParameters": { "index": 41, "kind": "parameter", "displayName": 
"Ssl Context Parameters", "group": "security", "label": "security", "required": 
false, "type": "object", "javaType": 
"org.apache.camel.support.jsse.SSLContextParameters", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "SSLContextParameters to 
use [...]
+    "sslEndpointAlgorithm": { "index": 42, "kind": "parameter", "displayName": 
"Ssl Endpoint Algorithm", "group": "security", "label": "security", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "security": 
"insecure:ssl", "insecureValue": "none", "defaultValue": "https", 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", [...]
+    "sslKeymanagerAlgorithm": { "index": 43, "kind": "parameter", 
"displayName": "Ssl Keymanager Algorithm", "group": "security", "label": 
"security", "required": false, "type": "string", "javaType": 
"java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "defaultValue": "SunX509", "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "The algorithm used by the key  [...]
+    "sslKeyPassword": { "index": 44, "kind": "parameter", "displayName": "Ssl 
Key Password", "group": "security", "label": "security", "required": false, 
"type": "string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": true, "security": 
"secret", "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "The password of the private key in the key 
store file" },
+    "sslKeystoreLocation": { "index": 45, "kind": "parameter", "displayName": 
"Ssl Keystore Location", "group": "security", "label": "security", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "The location of the key 
store file. This is optional and can be [...]
+    "sslKeystorePassword": { "index": 46, "kind": "parameter", "displayName": 
"Ssl Keystore Password", "group": "security", "label": "security", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": true, "security": 
"secret", "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "The store password for the key store file" },
+    "sslKeystoreType": { "index": 47, "kind": "parameter", "displayName": "Ssl 
Keystore Type", "group": "security", "label": "security", "required": false, 
"type": "string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "defaultValue": 
"JKS", "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "The file format of the key store file" },
+    "sslProtocol": { "index": 48, "kind": "parameter", "displayName": "Ssl 
Protocol", "group": "security", "label": "security", "required": false, "type": 
"string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "defaultValue": 
"TLSv1.3", "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "The SSL protocol used to generate the 
SSLContext" },
+    "sslTrustmanagerAlgorithm": { "index": 49, "kind": "parameter", 
"displayName": "Ssl Trustmanager Algorithm", "group": "security", "label": 
"security", "required": false, "type": "string", "javaType": 
"java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "defaultValue": "PKIX", "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "The algorithm used by the tru [...]
+    "sslTruststoreLocation": { "index": 50, "kind": "parameter", 
"displayName": "Ssl Truststore Location", "group": "security", "label": 
"security", "required": false, "type": "string", "javaType": 
"java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "The location of the trust store file, used to 
validate the  [...]
+    "sslTruststorePassword": { "index": 51, "kind": "parameter", 
"displayName": "Ssl Truststore Password", "group": "security", "label": 
"security", "required": false, "type": "string", "javaType": 
"java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": true, "security": "secret", "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "The password for the trust store file. [...]
+    "sslTruststoreType": { "index": 52, "kind": "parameter", "displayName": 
"Ssl Truststore Type", "group": "security", "label": "security", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "defaultValue": 
"JKS", "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "The file format of the trust store file" }
   }
 }
diff --git 
a/components/camel-ai/camel-openai/src/generated/java/org/apache/camel/component/openai/OpenAIEndpointConfigurer.java
 
b/components/camel-ai/camel-openai/src/generated/java/org/apache/camel/component/openai/OpenAIEndpointConfigurer.java
index 4b16f943cc3a..59cbe85688bf 100644
--- 
a/components/camel-ai/camel-openai/src/generated/java/org/apache/camel/component/openai/OpenAIEndpointConfigurer.java
+++ 
b/components/camel-ai/camel-openai/src/generated/java/org/apache/camel/component/openai/OpenAIEndpointConfigurer.java
@@ -62,6 +62,10 @@ public class OpenAIEndpointConfigurer extends 
PropertyConfigurerSupport implemen
         case "jsonSchema": 
target.getConfiguration().setJsonSchema(property(camelContext, 
java.lang.String.class, value)); return true;
         case "lazystartproducer":
         case "lazyStartProducer": 
target.setLazyStartProducer(property(camelContext, boolean.class, value)); 
return true;
+        case "maxhistorymessages":
+        case "maxHistoryMessages": 
target.getConfiguration().setMaxHistoryMessages(property(camelContext, 
int.class, value)); return true;
+        case "maxhistorytokens":
+        case "maxHistoryTokens": 
target.getConfiguration().setMaxHistoryTokens(property(camelContext, int.class, 
value)); return true;
         case "maxretries":
         case "maxRetries": 
target.getConfiguration().setMaxRetries(property(camelContext, int.class, 
value)); return true;
         case "maxtokens":
@@ -165,6 +169,10 @@ public class OpenAIEndpointConfigurer extends 
PropertyConfigurerSupport implemen
         case "jsonSchema": return java.lang.String.class;
         case "lazystartproducer":
         case "lazyStartProducer": return boolean.class;
+        case "maxhistorymessages":
+        case "maxHistoryMessages": return int.class;
+        case "maxhistorytokens":
+        case "maxHistoryTokens": return int.class;
         case "maxretries":
         case "maxRetries": return int.class;
         case "maxtokens":
@@ -269,6 +277,10 @@ public class OpenAIEndpointConfigurer extends 
PropertyConfigurerSupport implemen
         case "jsonSchema": return target.getConfiguration().getJsonSchema();
         case "lazystartproducer":
         case "lazyStartProducer": return target.isLazyStartProducer();
+        case "maxhistorymessages":
+        case "maxHistoryMessages": return 
target.getConfiguration().getMaxHistoryMessages();
+        case "maxhistorytokens":
+        case "maxHistoryTokens": return 
target.getConfiguration().getMaxHistoryTokens();
         case "maxretries":
         case "maxRetries": return target.getConfiguration().getMaxRetries();
         case "maxtokens":
diff --git 
a/components/camel-ai/camel-openai/src/generated/java/org/apache/camel/component/openai/OpenAIEndpointUriFactory.java
 
b/components/camel-ai/camel-openai/src/generated/java/org/apache/camel/component/openai/OpenAIEndpointUriFactory.java
index 98171fe606ea..75f9eae5ba4b 100644
--- 
a/components/camel-ai/camel-openai/src/generated/java/org/apache/camel/component/openai/OpenAIEndpointUriFactory.java
+++ 
b/components/camel-ai/camel-openai/src/generated/java/org/apache/camel/component/openai/OpenAIEndpointUriFactory.java
@@ -24,7 +24,7 @@ public class OpenAIEndpointUriFactory extends 
org.apache.camel.support.component
     private static final Set<String> ENDPOINT_IDENTITY_PROPERTY_NAMES;
     private static final Map<String, String> MULTI_VALUE_PREFIXES;
     static {
-        Set<String> props = new HashSet<>(51);
+        Set<String> props = new HashSet<>(53);
         props.add("additionalBodyProperty");
         props.add("additionalHeader");
         props.add("additionalResponseHeader");
@@ -45,6 +45,8 @@ public class OpenAIEndpointUriFactory extends 
org.apache.camel.support.component
         props.add("encodingFormat");
         props.add("jsonSchema");
         props.add("lazyStartProducer");
+        props.add("maxHistoryMessages");
+        props.add("maxHistoryTokens");
         props.add("maxRetries");
         props.add("maxTokens");
         props.add("maxToolIterations");
diff --git 
a/components/camel-ai/camel-openai/src/generated/resources/META-INF/org/apache/camel/component/openai/openai.json
 
b/components/camel-ai/camel-openai/src/generated/resources/META-INF/org/apache/camel/component/openai/openai.json
index 3784ba5f8579..08defe1693e5 100644
--- 
a/components/camel-ai/camel-openai/src/generated/resources/META-INF/org/apache/camel/component/openai/openai.json
+++ 
b/components/camel-ai/camel-openai/src/generated/resources/META-INF/org/apache/camel/component/openai/openai.json
@@ -96,36 +96,38 @@
     "embeddingModel": { "index": 17, "kind": "parameter", "displayName": 
"Embedding Model", "group": "producer", "label": "", "required": false, "type": 
"string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "The model to use for 
embeddings" },
     "encodingFormat": { "index": 18, "kind": "parameter", "displayName": 
"Encoding Format", "group": "producer", "label": "", "required": false, "type": 
"enum", "javaType": "java.lang.String", "enum": [ "float", "base64" ], 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "defaultValue": "base64", "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "The format for embedding ou [...]
     "jsonSchema": { "index": 19, "kind": "parameter", "displayName": "Json 
Schema", "group": "producer", "label": "", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "supportFileReference": true, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "JSON schema for 
structured output validation" },
-    "maxRetries": { "index": 20, "kind": "parameter", "displayName": "Max 
Retries", "group": "producer", "label": "", "required": false, "type": 
"integer", "javaType": "int", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "defaultValue": 2, "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "Maximum number of times the OpenAI SDK client 
retries failed requests. The SDK retr [...]
-    "maxTokens": { "index": 21, "kind": "parameter", "displayName": "Max 
Tokens", "group": "producer", "label": "", "required": false, "type": 
"integer", "javaType": "java.lang.Integer", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "Maximum number of tokens 
to generate" },
-    "maxToolIterations": { "index": 22, "kind": "parameter", "displayName": 
"Max Tool Iterations", "group": "producer", "label": "", "required": false, 
"type": "integer", "javaType": "int", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "defaultValue": 50, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "Maximum number of tool 
call loop iterations to prevent infinite loops" },
-    "mcpProtocolVersions": { "index": 23, "kind": "parameter", "displayName": 
"Mcp Protocol Versions", "group": "producer", "label": "", "required": false, 
"type": "string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "Comma-separated list of 
MCP protocol versions to advertise when connect [...]
-    "mcpReconnect": { "index": 24, "kind": "parameter", "displayName": "Mcp 
Reconnect", "group": "producer", "label": "", "required": false, "type": 
"boolean", "javaType": "boolean", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "defaultValue": true, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "Automatically reconnect 
to MCP servers when a tool call fails due to a t [...]
-    "mcpServer": { "index": 25, "kind": "parameter", "displayName": "Mcp 
Server", "group": "producer", "label": "", "required": false, "type": "object", 
"javaType": "java.util.Map<java.lang.String, java.lang.Object>", "prefix": 
"mcpServer.", "multiValue": true, "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "MCP (Model Cont [...]
-    "mcpTimeout": { "index": 26, "kind": "parameter", "displayName": "Mcp 
Timeout", "group": "producer", "label": "", "required": false, "type": 
"integer", "javaType": "int", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "defaultValue": 20, "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "Timeout in seconds for MCP tool call requests. 
Applies to all MCP operations inclu [...]
-    "model": { "index": 27, "kind": "parameter", "displayName": "Model", 
"group": "producer", "label": "", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "The model to use for chat completion" },
-    "outputClass": { "index": 28, "kind": "parameter", "displayName": "Output 
Class", "group": "producer", "label": "", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "Fully qualified class name for structured 
output using response format" },
-    "requestTimeout": { "index": 29, "kind": "parameter", "displayName": 
"Request Timeout", "group": "producer", "label": "", "required": false, "type": 
"integer", "javaType": "long", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "defaultValue": 0, "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "HTTP request timeout in milliseconds for the 
OpenAI SDK client. When 0 or  [...]
-    "storeFullResponse": { "index": 30, "kind": "parameter", "displayName": 
"Store Full Response", "group": "producer", "label": "", "required": false, 
"type": "boolean", "javaType": "boolean", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "defaultValue": 
false, "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "Store the full response in the exchange 
property 'CamelOpenA [...]
-    "streaming": { "index": 31, "kind": "parameter", "displayName": 
"Streaming", "group": "producer", "label": "", "required": false, "type": 
"boolean", "javaType": "boolean", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "defaultValue": false, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "Enable streaming 
responses" },
-    "stripThinking": { "index": 32, "kind": "parameter", "displayName": "Strip 
Thinking", "group": "producer", "label": "", "required": false, "type": 
"boolean", "javaType": "boolean", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "defaultValue": false, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "Strip ... blocks from 
model responses (used by reasoning models like  [...]
-    "systemMessage": { "index": 33, "kind": "parameter", "displayName": 
"System Message", "group": "producer", "label": "", "required": false, "type": 
"string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "System message to 
prepend. When set and conversationMemory is enabled, the conversat [...]
-    "temperature": { "index": 34, "kind": "parameter", "displayName": 
"Temperature", "group": "producer", "label": "", "required": false, "type": 
"number", "javaType": "java.lang.Double", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "Temperature for response 
generation (0.0 to 2.0)" },
-    "topP": { "index": 35, "kind": "parameter", "displayName": "Top P", 
"group": "producer", "label": "", "required": false, "type": "number", 
"javaType": "java.lang.Double", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "Top P for response generation (0.0 to 1.0)" },
-    "userMessage": { "index": 36, "kind": "parameter", "displayName": "User 
Message", "group": "producer", "label": "", "required": false, "type": 
"string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "Default user message 
text to use when no prompt is provided" },
-    "lazyStartProducer": { "index": 37, "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 [...]
-    "oauthProfile": { "index": 38, "kind": "parameter", "displayName": "Oauth 
Profile", "group": "security", "label": "security", "required": false, "type": 
"string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "OAuth profile name for 
obtaining an access token via the OAuth 2.0 Client Cred [...]
-    "sslContextParameters": { "index": 39, "kind": "parameter", "displayName": 
"Ssl Context Parameters", "group": "security", "label": "security", "required": 
false, "type": "object", "javaType": 
"org.apache.camel.support.jsse.SSLContextParameters", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "SSLContextParameters to 
use [...]
-    "sslEndpointAlgorithm": { "index": 40, "kind": "parameter", "displayName": 
"Ssl Endpoint Algorithm", "group": "security", "label": "security", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "security": 
"insecure:ssl", "insecureValue": "none", "defaultValue": "https", 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", [...]
-    "sslKeymanagerAlgorithm": { "index": 41, "kind": "parameter", 
"displayName": "Ssl Keymanager Algorithm", "group": "security", "label": 
"security", "required": false, "type": "string", "javaType": 
"java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "defaultValue": "SunX509", "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "The algorithm used by the key  [...]
-    "sslKeyPassword": { "index": 42, "kind": "parameter", "displayName": "Ssl 
Key Password", "group": "security", "label": "security", "required": false, 
"type": "string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": true, "security": 
"secret", "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "The password of the private key in the key 
store file" },
-    "sslKeystoreLocation": { "index": 43, "kind": "parameter", "displayName": 
"Ssl Keystore Location", "group": "security", "label": "security", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "The location of the key 
store file. This is optional and can be [...]
-    "sslKeystorePassword": { "index": 44, "kind": "parameter", "displayName": 
"Ssl Keystore Password", "group": "security", "label": "security", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": true, "security": 
"secret", "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "The store password for the key store file" },
-    "sslKeystoreType": { "index": 45, "kind": "parameter", "displayName": "Ssl 
Keystore Type", "group": "security", "label": "security", "required": false, 
"type": "string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "defaultValue": 
"JKS", "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "The file format of the key store file" },
-    "sslProtocol": { "index": 46, "kind": "parameter", "displayName": "Ssl 
Protocol", "group": "security", "label": "security", "required": false, "type": 
"string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "defaultValue": 
"TLSv1.3", "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "The SSL protocol used to generate the 
SSLContext" },
-    "sslTrustmanagerAlgorithm": { "index": 47, "kind": "parameter", 
"displayName": "Ssl Trustmanager Algorithm", "group": "security", "label": 
"security", "required": false, "type": "string", "javaType": 
"java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "defaultValue": "PKIX", "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "The algorithm used by the tru [...]
-    "sslTruststoreLocation": { "index": 48, "kind": "parameter", 
"displayName": "Ssl Truststore Location", "group": "security", "label": 
"security", "required": false, "type": "string", "javaType": 
"java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "The location of the trust store file, used to 
validate the  [...]
-    "sslTruststorePassword": { "index": 49, "kind": "parameter", 
"displayName": "Ssl Truststore Password", "group": "security", "label": 
"security", "required": false, "type": "string", "javaType": 
"java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": true, "security": "secret", "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "The password for the trust store file. [...]
-    "sslTruststoreType": { "index": 50, "kind": "parameter", "displayName": 
"Ssl Truststore Type", "group": "security", "label": "security", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "defaultValue": 
"JKS", "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "The file format of the trust store file" }
+    "maxHistoryMessages": { "index": 20, "kind": "parameter", "displayName": 
"Max History Messages", "group": "producer", "label": "", "required": false, 
"type": "integer", "javaType": "int", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "defaultValue": 0, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "When conversationMemory 
is enabled, retain at most this many messa [...]
+    "maxHistoryTokens": { "index": 21, "kind": "parameter", "displayName": 
"Max History Tokens", "group": "producer", "label": "", "required": false, 
"type": "integer", "javaType": "int", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "defaultValue": 0, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "When conversationMemory 
is enabled, trim conversation history using a  [...]
+    "maxRetries": { "index": 22, "kind": "parameter", "displayName": "Max 
Retries", "group": "producer", "label": "", "required": false, "type": 
"integer", "javaType": "int", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "defaultValue": 2, "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "Maximum number of times the OpenAI SDK client 
retries failed requests. The SDK retr [...]
+    "maxTokens": { "index": 23, "kind": "parameter", "displayName": "Max 
Tokens", "group": "producer", "label": "", "required": false, "type": 
"integer", "javaType": "java.lang.Integer", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "Maximum number of tokens 
to generate" },
+    "maxToolIterations": { "index": 24, "kind": "parameter", "displayName": 
"Max Tool Iterations", "group": "producer", "label": "", "required": false, 
"type": "integer", "javaType": "int", "deprecated": false, "deprecationNote": 
"", "autowired": false, "secret": false, "defaultValue": 50, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "Maximum number of tool 
call loop iterations to prevent infinite loops" },
+    "mcpProtocolVersions": { "index": 25, "kind": "parameter", "displayName": 
"Mcp Protocol Versions", "group": "producer", "label": "", "required": false, 
"type": "string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "Comma-separated list of 
MCP protocol versions to advertise when connect [...]
+    "mcpReconnect": { "index": 26, "kind": "parameter", "displayName": "Mcp 
Reconnect", "group": "producer", "label": "", "required": false, "type": 
"boolean", "javaType": "boolean", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "defaultValue": true, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "Automatically reconnect 
to MCP servers when a tool call fails due to a t [...]
+    "mcpServer": { "index": 27, "kind": "parameter", "displayName": "Mcp 
Server", "group": "producer", "label": "", "required": false, "type": "object", 
"javaType": "java.util.Map<java.lang.String, java.lang.Object>", "prefix": 
"mcpServer.", "multiValue": true, "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "MCP (Model Cont [...]
+    "mcpTimeout": { "index": 28, "kind": "parameter", "displayName": "Mcp 
Timeout", "group": "producer", "label": "", "required": false, "type": 
"integer", "javaType": "int", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "defaultValue": 20, "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "Timeout in seconds for MCP tool call requests. 
Applies to all MCP operations inclu [...]
+    "model": { "index": 29, "kind": "parameter", "displayName": "Model", 
"group": "producer", "label": "", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "The model to use for chat completion" },
+    "outputClass": { "index": 30, "kind": "parameter", "displayName": "Output 
Class", "group": "producer", "label": "", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "Fully qualified class name for structured 
output using response format" },
+    "requestTimeout": { "index": 31, "kind": "parameter", "displayName": 
"Request Timeout", "group": "producer", "label": "", "required": false, "type": 
"integer", "javaType": "long", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "defaultValue": 0, "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "HTTP request timeout in milliseconds for the 
OpenAI SDK client. When 0 or  [...]
+    "storeFullResponse": { "index": 32, "kind": "parameter", "displayName": 
"Store Full Response", "group": "producer", "label": "", "required": false, 
"type": "boolean", "javaType": "boolean", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "defaultValue": 
false, "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "Store the full response in the exchange 
property 'CamelOpenA [...]
+    "streaming": { "index": 33, "kind": "parameter", "displayName": 
"Streaming", "group": "producer", "label": "", "required": false, "type": 
"boolean", "javaType": "boolean", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "defaultValue": false, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "Enable streaming 
responses" },
+    "stripThinking": { "index": 34, "kind": "parameter", "displayName": "Strip 
Thinking", "group": "producer", "label": "", "required": false, "type": 
"boolean", "javaType": "boolean", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "defaultValue": false, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "Strip ... blocks from 
model responses (used by reasoning models like  [...]
+    "systemMessage": { "index": 35, "kind": "parameter", "displayName": 
"System Message", "group": "producer", "label": "", "required": false, "type": 
"string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "System message to 
prepend. When set and conversationMemory is enabled, the conversat [...]
+    "temperature": { "index": 36, "kind": "parameter", "displayName": 
"Temperature", "group": "producer", "label": "", "required": false, "type": 
"number", "javaType": "java.lang.Double", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "Temperature for response 
generation (0.0 to 2.0)" },
+    "topP": { "index": 37, "kind": "parameter", "displayName": "Top P", 
"group": "producer", "label": "", "required": false, "type": "number", 
"javaType": "java.lang.Double", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "Top P for response generation (0.0 to 1.0)" },
+    "userMessage": { "index": 38, "kind": "parameter", "displayName": "User 
Message", "group": "producer", "label": "", "required": false, "type": 
"string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "Default user message 
text to use when no prompt is provided" },
+    "lazyStartProducer": { "index": 39, "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 [...]
+    "oauthProfile": { "index": 40, "kind": "parameter", "displayName": "Oauth 
Profile", "group": "security", "label": "security", "required": false, "type": 
"string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "OAuth profile name for 
obtaining an access token via the OAuth 2.0 Client Cred [...]
+    "sslContextParameters": { "index": 41, "kind": "parameter", "displayName": 
"Ssl Context Parameters", "group": "security", "label": "security", "required": 
false, "type": "object", "javaType": 
"org.apache.camel.support.jsse.SSLContextParameters", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "SSLContextParameters to 
use [...]
+    "sslEndpointAlgorithm": { "index": 42, "kind": "parameter", "displayName": 
"Ssl Endpoint Algorithm", "group": "security", "label": "security", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "security": 
"insecure:ssl", "insecureValue": "none", "defaultValue": "https", 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", [...]
+    "sslKeymanagerAlgorithm": { "index": 43, "kind": "parameter", 
"displayName": "Ssl Keymanager Algorithm", "group": "security", "label": 
"security", "required": false, "type": "string", "javaType": 
"java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "defaultValue": "SunX509", "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "The algorithm used by the key  [...]
+    "sslKeyPassword": { "index": 44, "kind": "parameter", "displayName": "Ssl 
Key Password", "group": "security", "label": "security", "required": false, 
"type": "string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": true, "security": 
"secret", "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "The password of the private key in the key 
store file" },
+    "sslKeystoreLocation": { "index": 45, "kind": "parameter", "displayName": 
"Ssl Keystore Location", "group": "security", "label": "security", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, 
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration", 
"configurationField": "configuration", "description": "The location of the key 
store file. This is optional and can be [...]
+    "sslKeystorePassword": { "index": 46, "kind": "parameter", "displayName": 
"Ssl Keystore Password", "group": "security", "label": "security", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": true, "security": 
"secret", "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "The store password for the key store file" },
+    "sslKeystoreType": { "index": 47, "kind": "parameter", "displayName": "Ssl 
Keystore Type", "group": "security", "label": "security", "required": false, 
"type": "string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "defaultValue": 
"JKS", "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "The file format of the key store file" },
+    "sslProtocol": { "index": 48, "kind": "parameter", "displayName": "Ssl 
Protocol", "group": "security", "label": "security", "required": false, "type": 
"string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "defaultValue": 
"TLSv1.3", "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "The SSL protocol used to generate the 
SSLContext" },
+    "sslTrustmanagerAlgorithm": { "index": 49, "kind": "parameter", 
"displayName": "Ssl Trustmanager Algorithm", "group": "security", "label": 
"security", "required": false, "type": "string", "javaType": 
"java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "defaultValue": "PKIX", "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "The algorithm used by the tru [...]
+    "sslTruststoreLocation": { "index": 50, "kind": "parameter", 
"displayName": "Ssl Truststore Location", "group": "security", "label": 
"security", "required": false, "type": "string", "javaType": 
"java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "The location of the trust store file, used to 
validate the  [...]
+    "sslTruststorePassword": { "index": 51, "kind": "parameter", 
"displayName": "Ssl Truststore Password", "group": "security", "label": 
"security", "required": false, "type": "string", "javaType": 
"java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": true, "security": "secret", "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "The password for the trust store file. [...]
+    "sslTruststoreType": { "index": 52, "kind": "parameter", "displayName": 
"Ssl Truststore Type", "group": "security", "label": "security", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "defaultValue": 
"JKS", "configurationClass": 
"org.apache.camel.component.openai.OpenAIConfiguration", "configurationField": 
"configuration", "description": "The file format of the trust store file" }
   }
 }
diff --git 
a/components/camel-ai/camel-openai/src/main/docs/openai-component.adoc 
b/components/camel-ai/camel-openai/src/main/docs/openai-component.adoc
index ee3cac44cb51..9a6c87ccf537 100644
--- a/components/camel-ai/camel-openai/src/main/docs/openai-component.adoc
+++ b/components/camel-ai/camel-openai/src/main/docs/openai-component.adoc
@@ -672,6 +672,8 @@ The JSON schema must be a valid JSON object. Invalid schema 
strings will result
 
 When `conversationMemory=true`, the component maintains conversation history 
in the `CamelOpenAIConversationHistory` exchange property (configurable via 
`conversationHistoryProperty` option). This history is scoped to a single 
Exchange and allows multi-turn conversations within a route.
 
+Use `maxHistoryMessages` and `maxHistoryTokens` to bound how much history is 
retained in that property. Oldest conversation segments are dropped first; 
assistant tool-call blocks are always removed together with their tool results. 
The most recent segment is always kept, even when it alone exceeds 
`maxHistoryMessages` or `maxHistoryTokens`. Token limits use a character-count 
/ 4 estimate (including image payload size for multi-modal user messages).
+
 IMPORTANT:
 
 * Conversation history is automatically updated with each assistant response 
for **non-streaming** responses only
diff --git 
a/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIConfiguration.java
 
b/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIConfiguration.java
index 21ca68ffcc4a..66dc4ee206af 100644
--- 
a/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIConfiguration.java
+++ 
b/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIConfiguration.java
@@ -101,6 +101,22 @@ public class OpenAIConfiguration implements Cloneable {
     @Metadata(description = "Exchange property name for storing conversation 
history")
     private String conversationHistoryProperty = 
"CamelOpenAIConversationHistory";
 
+    @UriParam(defaultValue = "0")
+    @Metadata(description = "When conversationMemory is enabled, retain at 
most this many messages in the exchange "
+                            + "conversation history. System and developer 
messages are prepended separately and are not "
+                            + "stored in history. Assistant tool-call blocks 
are kept intact and may retain slightly "
+                            + "more than this limit to preserve tool result 
pairing. When 0, no message limit is applied.")
+    private int maxHistoryMessages;
+
+    @UriParam(defaultValue = "0")
+    @Metadata(description = "When conversationMemory is enabled, trim 
conversation history using a token estimate "
+                            + "(character count / 4, including image payload 
size for multi-modal user messages). "
+                            + "Oldest segments are dropped first until the 
estimated tokens are within this limit. "
+                            + "Assistant tool-call blocks are removed as a 
unit with their tool results. The most recent "
+                            + "segment is always retained, even when it alone 
exceeds this limit. When 0, "
+                            + "no token limit is applied.")
+    private int maxHistoryTokens;
+
     @UriParam
     @Metadata(description = "Default user message text to use when no prompt 
is provided", largeInput = true)
     private String userMessage;
@@ -382,6 +398,22 @@ public class OpenAIConfiguration implements Cloneable {
         this.conversationHistoryProperty = conversationHistoryProperty;
     }
 
+    public int getMaxHistoryMessages() {
+        return maxHistoryMessages;
+    }
+
+    public void setMaxHistoryMessages(int maxHistoryMessages) {
+        this.maxHistoryMessages = maxHistoryMessages;
+    }
+
+    public int getMaxHistoryTokens() {
+        return maxHistoryTokens;
+    }
+
+    public void setMaxHistoryTokens(int maxHistoryTokens) {
+        this.maxHistoryTokens = maxHistoryTokens;
+    }
+
     public String getUserMessage() {
         return userMessage;
     }
diff --git 
a/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIConversationHistoryTrimmer.java
 
b/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIConversationHistoryTrimmer.java
new file mode 100644
index 000000000000..39f63d1489ab
--- /dev/null
+++ 
b/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIConversationHistoryTrimmer.java
@@ -0,0 +1,248 @@
+/*
+ * 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.openai;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.openai.models.chat.completions.ChatCompletionAssistantMessageParam;
+import com.openai.models.chat.completions.ChatCompletionContentPart;
+import com.openai.models.chat.completions.ChatCompletionMessageParam;
+import com.openai.models.chat.completions.ChatCompletionMessageToolCall;
+import com.openai.models.chat.completions.ChatCompletionToolMessageParam;
+import com.openai.models.chat.completions.ChatCompletionUserMessageParam;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Applies sliding-window limits to exchange-scoped OpenAI conversation 
history. Trimming removes whole segments from
+ * the oldest side so assistant tool-call blocks stay paired with their tool 
result messages.
+ */
+final class OpenAIConversationHistoryTrimmer {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(OpenAIConversationHistoryTrimmer.class);
+    private static final int CHARS_PER_TOKEN = 4;
+
+    private OpenAIConversationHistoryTrimmer() {
+    }
+
+    static List<ChatCompletionMessageParam> trim(
+            List<ChatCompletionMessageParam> history, OpenAIConfiguration 
config) {
+        if (history == null) {
+            return null;
+        }
+        if (history.isEmpty()) {
+            return new ArrayList<>();
+        }
+
+        int maxMessages = config.getMaxHistoryMessages();
+        int maxTokens = config.getMaxHistoryTokens();
+        if (maxMessages <= 0 && maxTokens <= 0) {
+            return history;
+        }
+
+        int originalSize = history.size();
+        List<Segment> segments = buildSegments(history);
+        int firstSegment = 0;
+
+        if (maxMessages > 0) {
+            firstSegment = findFirstSegmentForMessageLimit(segments, 
maxMessages);
+        }
+
+        if (maxTokens > 0) {
+            firstSegment = Math.max(firstSegment,
+                    findFirstSegmentForTokenLimit(history, segments, 
maxTokens));
+        }
+
+        List<ChatCompletionMessageParam> trimmed;
+        if (firstSegment >= segments.size()) {
+            trimmed = new ArrayList<>();
+        } else {
+            Segment first = segments.get(firstSegment);
+            Segment last = segments.get(segments.size() - 1);
+            trimmed = new ArrayList<>(history.subList(first.start, last.end + 
1));
+        }
+
+        int dropped = originalSize - trimmed.size();
+        if (dropped > 0) {
+            LOG.debug("Trimmed conversation history: dropped {} message(s), 
retained {}", dropped, trimmed.size());
+        }
+
+        return trimmed;
+    }
+
+    static int estimateTokens(List<ChatCompletionMessageParam> messages) {
+        return estimateTokens(messages, 0, messages.size());
+    }
+
+    private static int estimateTokens(List<ChatCompletionMessageParam> 
messages, int from, int to) {
+        long chars = 0;
+        for (int i = from; i < to; i++) {
+            chars += estimateMessageChars(messages.get(i));
+        }
+        return tokensFromChars(chars);
+    }
+
+    private static int tokensFromChars(long chars) {
+        if (chars <= 0) {
+            return 0;
+        }
+        long maxChars = (long) Integer.MAX_VALUE * CHARS_PER_TOKEN;
+        if (chars > maxChars) {
+            return Integer.MAX_VALUE;
+        }
+        return (int) ((chars + CHARS_PER_TOKEN - 1) / CHARS_PER_TOKEN);
+    }
+
+    private static int findFirstSegmentForMessageLimit(List<Segment> segments, 
int maxMessages) {
+        int firstSegment = segments.size() - 1;
+        int messageCount = segments.get(firstSegment).messageCount();
+        while (firstSegment > 0) {
+            Segment previous = segments.get(firstSegment - 1);
+            int nextCount = messageCount + previous.messageCount();
+            if (nextCount > maxMessages) {
+                break;
+            }
+            firstSegment--;
+            messageCount = nextCount;
+        }
+        return firstSegment;
+    }
+
+    private static int findFirstSegmentForTokenLimit(
+            List<ChatCompletionMessageParam> history, List<Segment> segments, 
int maxTokens) {
+        int lastSegment = segments.size() - 1;
+        Segment last = segments.get(lastSegment);
+        int lastSegmentTokens = estimateTokens(history, last.start, last.end + 
1);
+        if (lastSegmentTokens > maxTokens) {
+            LOG.warn(
+                    "Last conversation segment alone exceeds maxHistoryTokens 
(estimated {} > {}); "
+                     + "retaining it anyway",
+                    lastSegmentTokens, maxTokens);
+            return lastSegment;
+        }
+
+        for (int firstSegment = 0; firstSegment < segments.size(); 
firstSegment++) {
+            Segment first = segments.get(firstSegment);
+            if (estimateTokens(history, first.start, last.end + 1) <= 
maxTokens) {
+                return firstSegment;
+            }
+        }
+        return lastSegment;
+    }
+
+    private static List<Segment> 
buildSegments(List<ChatCompletionMessageParam> history) {
+        List<Segment> segments = new ArrayList<>();
+        int index = 0;
+        while (index < history.size()) {
+            ChatCompletionMessageParam message = history.get(index);
+            if (isAssistantWithToolCalls(message)) {
+                int end = index;
+                while (end + 1 < history.size() && history.get(end + 
1).tool().isPresent()) {
+                    end++;
+                }
+                segments.add(new Segment(index, end));
+                index = end + 1;
+            } else {
+                segments.add(new Segment(index, index));
+                index++;
+            }
+        }
+        return segments;
+    }
+
+    private static boolean isAssistantWithToolCalls(ChatCompletionMessageParam 
message) {
+        return message.assistant()
+                .map(assistant -> 
!assistant.toolCalls().orElse(List.of()).isEmpty())
+                .orElse(false);
+    }
+
+    private static int estimateMessageChars(ChatCompletionMessageParam 
message) {
+        return message.user()
+                
.map(OpenAIConversationHistoryTrimmer::estimateUserMessageChars)
+                .orElseGet(() -> message.assistant()
+                        
.map(OpenAIConversationHistoryTrimmer::estimateAssistantMessageChars)
+                        .orElseGet(() -> message.tool()
+                                
.map(OpenAIConversationHistoryTrimmer::estimateToolMessageChars)
+                                .orElse(0)));
+    }
+
+    private static int estimateUserMessageChars(ChatCompletionUserMessageParam 
user) {
+        ChatCompletionUserMessageParam.Content content = user.content();
+        if (content.isText()) {
+            return content.asText().length();
+        }
+        if (content.isArrayOfContentParts()) {
+            return content.asArrayOfContentParts().stream()
+                    
.mapToInt(OpenAIConversationHistoryTrimmer::estimateContentPartChars)
+                    .sum();
+        }
+        return 0;
+    }
+
+    private static int 
estimateAssistantMessageChars(ChatCompletionAssistantMessageParam assistant) {
+        int chars = assistant.content()
+                .filter(ChatCompletionAssistantMessageParam.Content::isText)
+                .map(content -> content.asText().length())
+                .orElse(0);
+
+        for (ChatCompletionMessageToolCall toolCall : 
assistant.toolCalls().orElse(List.of())) {
+            chars += toolCall.asFunction().function().name().length();
+            chars += toolCall.asFunction().function().arguments().length();
+        }
+
+        return chars;
+    }
+
+    private static int estimateToolMessageChars(ChatCompletionToolMessageParam 
tool) {
+        ChatCompletionToolMessageParam.Content content = tool.content();
+        if (content.isText()) {
+            return content.asText().length();
+        }
+        if (content.isArrayOfContentParts()) {
+            return content.asArrayOfContentParts().stream()
+                    .mapToInt(part -> part.text().length())
+                    .sum();
+        }
+        return 0;
+    }
+
+    private static int estimateContentPartChars(ChatCompletionContentPart 
part) {
+        int chars = 0;
+        if (part.text().isPresent()) {
+            chars += part.asText().text().length();
+        }
+        if (part.imageUrl().isPresent()) {
+            chars += part.asImageUrl().imageUrl().url().length();
+        }
+        return chars;
+    }
+
+    private static final class Segment {
+        private final int start;
+        private final int end;
+
+        private Segment(int start, int end) {
+            this.start = start;
+            this.end = end;
+        }
+
+        private int messageCount() {
+            return end - start + 1;
+        }
+    }
+}
diff --git 
a/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIProducer.java
 
b/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIProducer.java
index 37a79794a6ab..a9493c2d50dc 100644
--- 
a/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIProducer.java
+++ 
b/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIProducer.java
@@ -272,11 +272,16 @@ public class OpenAIProducer extends DefaultAsyncProducer {
             return;
         }
 
-        List<ChatCompletionMessageParam> history = 
in.getExchange().getProperty(
+        Exchange exchange = in.getExchange();
+        List<ChatCompletionMessageParam> history = exchange.getProperty(
                 config.getConversationHistoryProperty(),
                 List.class);
         if (history != null) {
-            messages.addAll(history);
+            List<ChatCompletionMessageParam> trimmed = 
OpenAIConversationHistoryTrimmer.trim(history, config);
+            if (trimmed.size() != history.size()) {
+                exchange.setProperty(config.getConversationHistoryProperty(), 
trimmed);
+            }
+            messages.addAll(trimmed);
         }
     }
 
@@ -726,7 +731,8 @@ public class OpenAIProducer extends DefaultAsyncProducer {
                         .build());
 
         history.add(assistantMessage);
-        exchange.setProperty(config.getConversationHistoryProperty(), history);
+        exchange.setProperty(config.getConversationHistoryProperty(),
+                OpenAIConversationHistoryTrimmer.trim(history, config));
     }
 
     private void updateConversationHistory(
@@ -758,9 +764,10 @@ public class OpenAIProducer extends DefaultAsyncProducer {
                         .build());
         history.add(assistantMessage);
 
-        exchange.setProperty(config.getConversationHistoryProperty(), history);
+        List<ChatCompletionMessageParam> trimmed = 
OpenAIConversationHistoryTrimmer.trim(history, config);
+        exchange.setProperty(config.getConversationHistoryProperty(), trimmed);
         LOG.debug("Updated conversation history with {} agentic messages + 
final response, total entries: {}",
-                agenticMessages.size(), history.size());
+                agenticMessages.size(), trimmed.size());
     }
 
     private void updateConversationHistory(
@@ -791,9 +798,10 @@ public class OpenAIProducer extends DefaultAsyncProducer {
                         .build());
         history.add(assistantMessage);
 
-        exchange.setProperty(config.getConversationHistoryProperty(), history);
+        List<ChatCompletionMessageParam> trimmed = 
OpenAIConversationHistoryTrimmer.trim(history, config);
+        exchange.setProperty(config.getConversationHistoryProperty(), trimmed);
         LOG.debug("Updated conversation history with {} agentic messages + 
returnDirect result, total entries: {}",
-                agenticMessages.size(), history.size());
+                agenticMessages.size(), trimmed.size());
     }
 
     private ResponseFormatJsonSchema.JsonSchema.Schema 
buildSchemaFromJson(String jsonSchemaString) throws Exception {
diff --git 
a/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIToolExecutionProducer.java
 
b/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIToolExecutionProducer.java
index 857e0e04f1f1..e5a01352dddf 100644
--- 
a/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIToolExecutionProducer.java
+++ 
b/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIToolExecutionProducer.java
@@ -173,7 +173,7 @@ public class OpenAIToolExecutionProducer extends 
DefaultProducer {
         }
 
         // Update conversation history and clear body for the next 
chat-completion call
-        exchange.setProperty(historyProperty, history);
+        exchange.setProperty(historyProperty, 
OpenAIConversationHistoryTrimmer.trim(history, config));
         exchange.getMessage().setBody(null);
         exchange.getMessage().setHeader(OpenAIConstants.TOOL_ITERATIONS, 
executedCount);
     }
diff --git 
a/components/camel-ai/camel-openai/src/test/java/org/apache/camel/component/openai/OpenAIConversationHistorySizeTest.java
 
b/components/camel-ai/camel-openai/src/test/java/org/apache/camel/component/openai/OpenAIConversationHistorySizeTest.java
new file mode 100644
index 000000000000..9055b6b76232
--- /dev/null
+++ 
b/components/camel-ai/camel-openai/src/test/java/org/apache/camel/component/openai/OpenAIConversationHistorySizeTest.java
@@ -0,0 +1,104 @@
+/*
+ * 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.openai;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.infra.openai.mock.OpenAIMock;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class OpenAIConversationHistorySizeTest extends CamelTestSupport {
+
+    private static final ObjectMapper MAPPER = new ObjectMapper();
+
+    private final AtomicReference<String> fourthRequestBody = new 
AtomicReference<>();
+
+    @RegisterExtension
+    public OpenAIMock openAIMock = new OpenAIMock().builder()
+            .when("turn-1")
+            .replyWith("reply-1")
+            .end()
+            .when("turn-2")
+            .replyWith("reply-2")
+            .end()
+            .when("turn-3")
+            .replyWith("reply-3")
+            .end()
+            .when("turn-4")
+            .assertRequest(fourthRequestBody::set)
+            .replyWith("reply-4")
+            .end()
+            .build();
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        String endpoint = 
"openai:chat-completion?model=gpt-5&apiKey=dummy&conversationMemory=true"
+                          + "&maxHistoryMessages=2&baseUrl=" + 
openAIMock.getBaseUrl() + "/v1";
+
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                from("direct:multi-turn")
+                        .to(endpoint)
+                        .setBody(constant("turn-2"))
+                        .to(endpoint)
+                        .setBody(constant("turn-3"))
+                        .to(endpoint)
+                        .setBody(constant("turn-4"))
+                        .to(endpoint);
+            }
+        };
+    }
+
+    @Test
+    void 
maxHistoryMessagesShouldRetainOnlyRecentHistoryInRequestsAndExchangeProperty() 
throws Exception {
+        Exchange result = template.request("direct:multi-turn", e -> 
e.getIn().setBody("turn-1"));
+
+        assertThat(result.getException()).isNull();
+        
assertThat(result.getMessage().getBody(String.class)).isEqualTo("reply-4");
+
+        @SuppressWarnings("unchecked")
+        List<Object> history = 
result.getProperty("CamelOpenAIConversationHistory", List.class);
+        assertThat(history).hasSize(2);
+
+        String request = fourthRequestBody.get();
+        assertThat(request).isNotNull();
+
+        JsonNode messages = MAPPER.readTree(request).get("messages");
+        assertThat(messages).isNotNull();
+        assertThat(messages)
+                .noneMatch(message -> 
"turn-1".equals(message.path("content").asText())
+                        || "reply-1".equals(message.path("content").asText())
+                        || "turn-2".equals(message.path("content").asText())
+                        || "reply-2".equals(message.path("content").asText()));
+        assertThat(messages)
+                .anyMatch(message -> 
"turn-3".equals(message.path("content").asText()));
+        assertThat(messages)
+                .anyMatch(message -> 
"reply-3".equals(message.path("content").asText()));
+        assertThat(messages)
+                .anyMatch(message -> 
"turn-4".equals(message.path("content").asText()));
+    }
+}
diff --git 
a/components/camel-ai/camel-openai/src/test/java/org/apache/camel/component/openai/OpenAIConversationHistoryTokenLimitTest.java
 
b/components/camel-ai/camel-openai/src/test/java/org/apache/camel/component/openai/OpenAIConversationHistoryTokenLimitTest.java
new file mode 100644
index 000000000000..207227183703
--- /dev/null
+++ 
b/components/camel-ai/camel-openai/src/test/java/org/apache/camel/component/openai/OpenAIConversationHistoryTokenLimitTest.java
@@ -0,0 +1,110 @@
+/*
+ * 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.openai;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.openai.models.chat.completions.ChatCompletionMessageParam;
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.infra.openai.mock.OpenAIMock;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class OpenAIConversationHistoryTokenLimitTest extends CamelTestSupport {
+
+    private static final ObjectMapper MAPPER = new ObjectMapper();
+    private static final String TURN_1 = "turn-1" + "a".repeat(34);
+    private static final String TURN_2 = "turn-2" + "b".repeat(34);
+    private static final String TURN_3 = "turn-3" + "c".repeat(34);
+    private static final String TURN_4 = "turn-4" + "d".repeat(34);
+
+    private final AtomicReference<String> fourthRequestBody = new 
AtomicReference<>();
+
+    @RegisterExtension
+    public OpenAIMock openAIMock = new OpenAIMock().builder()
+            .when(TURN_1)
+            .replyWith("reply-1")
+            .end()
+            .when(TURN_2)
+            .replyWith("reply-2")
+            .end()
+            .when(TURN_3)
+            .replyWith("reply-3")
+            .end()
+            .when(TURN_4)
+            .assertRequest(fourthRequestBody::set)
+            .replyWith("reply-4")
+            .end()
+            .build();
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        String endpoint = 
"openai:chat-completion?model=gpt-5&apiKey=dummy&conversationMemory=true"
+                          + "&maxHistoryTokens=15&baseUrl=" + 
openAIMock.getBaseUrl() + "/v1";
+
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                from("direct:token-limited")
+                        .to(endpoint)
+                        .setBody(constant(TURN_2))
+                        .to(endpoint)
+                        .setBody(constant(TURN_3))
+                        .to(endpoint)
+                        .setBody(constant(TURN_4))
+                        .to(endpoint);
+            }
+        };
+    }
+
+    @Test
+    void maxHistoryTokensShouldTrimHistoryAndAllowSubsequentCalls() throws 
Exception {
+        Exchange result = template.request("direct:token-limited", e -> 
e.getIn().setBody(TURN_1));
+
+        assertThat(result.getException()).isNull();
+        
assertThat(result.getMessage().getBody(String.class)).isEqualTo("reply-4");
+
+        @SuppressWarnings("unchecked")
+        List<ChatCompletionMessageParam> history
+                = result.getProperty("CamelOpenAIConversationHistory", 
List.class);
+        assertThat(history).isNotEmpty();
+        
assertThat(OpenAIConversationHistoryTrimmer.estimateTokens(history)).isLessThanOrEqualTo(15);
+
+        String request = fourthRequestBody.get();
+        assertThat(request).isNotNull();
+
+        JsonNode messages = MAPPER.readTree(request).get("messages");
+        assertThat(messages).isNotNull();
+        assertThat(messages)
+                .noneMatch(message -> 
TURN_1.equals(message.path("content").asText())
+                        || "reply-1".equals(message.path("content").asText())
+                        || TURN_2.equals(message.path("content").asText()));
+        assertThat(messages)
+                .anyMatch(message -> 
TURN_3.equals(message.path("content").asText()));
+        assertThat(messages)
+                .anyMatch(message -> 
"reply-3".equals(message.path("content").asText()));
+        assertThat(messages)
+                .anyMatch(message -> 
TURN_4.equals(message.path("content").asText()));
+    }
+}
diff --git 
a/components/camel-ai/camel-openai/src/test/java/org/apache/camel/component/openai/OpenAIConversationHistoryTrimmerTest.java
 
b/components/camel-ai/camel-openai/src/test/java/org/apache/camel/component/openai/OpenAIConversationHistoryTrimmerTest.java
new file mode 100644
index 000000000000..78f5a64ebbe4
--- /dev/null
+++ 
b/components/camel-ai/camel-openai/src/test/java/org/apache/camel/component/openai/OpenAIConversationHistoryTrimmerTest.java
@@ -0,0 +1,274 @@
+/*
+ * 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.openai;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.openai.models.chat.completions.ChatCompletionAssistantMessageParam;
+import com.openai.models.chat.completions.ChatCompletionContentPart;
+import com.openai.models.chat.completions.ChatCompletionContentPartImage;
+import com.openai.models.chat.completions.ChatCompletionContentPartText;
+import 
com.openai.models.chat.completions.ChatCompletionMessageFunctionToolCall;
+import com.openai.models.chat.completions.ChatCompletionMessageParam;
+import com.openai.models.chat.completions.ChatCompletionMessageToolCall;
+import com.openai.models.chat.completions.ChatCompletionToolMessageParam;
+import com.openai.models.chat.completions.ChatCompletionUserMessageParam;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class OpenAIConversationHistoryTrimmerTest {
+
+    @Test
+    void trimShouldReturnSameListWhenNoLimitsConfigured() {
+        List<ChatCompletionMessageParam> history = List.of(
+                userMessage("one"),
+                assistantMessage("two"));
+
+        List<ChatCompletionMessageParam> trimmed = 
OpenAIConversationHistoryTrimmer.trim(history, config(0, 0));
+
+        assertThat(trimmed).isSameAs(history);
+    }
+
+    @Test
+    void trimShouldRetainMostRecentMessagesWhenMaxHistoryMessagesIsSet() {
+        List<ChatCompletionMessageParam> history = new ArrayList<>();
+        history.add(userMessage("msg-1"));
+        history.add(assistantMessage("reply-1"));
+        history.add(userMessage("msg-2"));
+        history.add(assistantMessage("reply-2"));
+        history.add(userMessage("msg-3"));
+        history.add(assistantMessage("reply-3"));
+
+        List<ChatCompletionMessageParam> trimmed = 
OpenAIConversationHistoryTrimmer.trim(history, config(2, 0));
+
+        assertThat(trimmed).hasSize(2);
+        assertThat(extractText(trimmed.get(0))).isEqualTo("msg-3");
+        assertThat(extractText(trimmed.get(1))).isEqualTo("reply-3");
+    }
+
+    @Test
+    void trimShouldDropOldestMessagesWhenMaxHistoryTokensIsExceeded() {
+        List<ChatCompletionMessageParam> history = List.of(
+                userMessage(repeat('a', 40)),
+                assistantMessage(repeat('b', 40)),
+                userMessage(repeat('c', 40)));
+
+        List<ChatCompletionMessageParam> trimmed = 
OpenAIConversationHistoryTrimmer.trim(history, config(0, 15));
+
+        
assertThat(OpenAIConversationHistoryTrimmer.estimateTokens(trimmed)).isLessThanOrEqualTo(15);
+        assertThat(trimmed).hasSize(1);
+        assertThat(extractText(trimmed.get(0))).isEqualTo(repeat('c', 40));
+    }
+
+    @Test
+    void trimShouldApplyBothMessageAndTokenLimits() {
+        List<ChatCompletionMessageParam> history = new ArrayList<>();
+        for (int i = 1; i <= 6; i++) {
+            history.add(userMessage("user-" + i + repeat('x', 36)));
+            history.add(assistantMessage("assistant-" + i));
+        }
+
+        List<ChatCompletionMessageParam> trimmed = 
OpenAIConversationHistoryTrimmer.trim(history, config(4, 20));
+
+        assertThat(trimmed.size()).isLessThanOrEqualTo(4);
+        
assertThat(OpenAIConversationHistoryTrimmer.estimateTokens(trimmed)).isLessThanOrEqualTo(20);
+        
assertThat(trimmed.stream().map(OpenAIConversationHistoryTrimmerTest::extractText))
+                .noneMatch(text -> text.contains("user-1") || 
text.contains("assistant-1"));
+        
assertThat(trimmed.stream().map(OpenAIConversationHistoryTrimmerTest::extractText))
+                .anyMatch(text -> text.contains("user-6"));
+    }
+
+    @Test
+    void trimShouldCountToolCallArgumentsTowardTokenEstimate() {
+        ChatCompletionMessageParam assistantWithToolCall = 
ChatCompletionMessageParam.ofAssistant(
+                ChatCompletionAssistantMessageParam.builder()
+                        .toolCalls(List.of(
+                                ChatCompletionMessageToolCall.ofFunction(
+                                        
ChatCompletionMessageFunctionToolCall.builder()
+                                                .id("call-1")
+                                                
.function(ChatCompletionMessageFunctionToolCall.Function.builder()
+                                                        .name("lookup")
+                                                        
.arguments("{\"query\":\"" + repeat('z', 80) + "\"}")
+                                                        .build())
+                                                .build())))
+                        .build());
+
+        List<ChatCompletionMessageParam> history = List.of(
+                userMessage("short"),
+                assistantWithToolCall,
+                toolMessage("tool-result"));
+
+        List<ChatCompletionMessageParam> trimmed = 
OpenAIConversationHistoryTrimmer.trim(history, config(0, 10));
+
+        assertThat(trimmed).hasSize(2);
+        assertThat(trimmed.get(0).assistant()).isPresent();
+        assertThat(trimmed.get(1).tool()).isPresent();
+    }
+
+    @Test
+    void trimShouldReturnMutableListThatSupportsSubsequentAdds() {
+        List<ChatCompletionMessageParam> history = List.of(
+                userMessage(repeat('a', 40)),
+                assistantMessage(repeat('b', 40)),
+                userMessage(repeat('c', 40)));
+
+        List<ChatCompletionMessageParam> trimmed = 
OpenAIConversationHistoryTrimmer.trim(history, config(0, 15));
+
+        trimmed.add(userMessage("next"));
+
+        assertThat(trimmed).hasSize(2);
+        assertThat(extractText(trimmed.get(0))).isEqualTo(repeat('c', 40));
+        assertThat(extractText(trimmed.get(1))).isEqualTo("next");
+    }
+
+    @Test
+    void trimShouldReturnMutableEmptyListForEmptyHistory() {
+        List<ChatCompletionMessageParam> trimmed
+                = OpenAIConversationHistoryTrimmer.trim(new ArrayList<>(), 
config(0, 10));
+
+        trimmed.add(userMessage("one"));
+
+        assertThat(trimmed).hasSize(1);
+    }
+
+    @Test
+    void trimShouldRetainLastSegmentWhenItAloneExceedsTokenLimit() {
+        List<ChatCompletionMessageParam> history = List.of(
+                userMessage("older"),
+                assistantMessage("older-reply"),
+                userMessage(repeat('x', 80)));
+
+        List<ChatCompletionMessageParam> trimmed = 
OpenAIConversationHistoryTrimmer.trim(history, config(0, 10));
+
+        assertThat(trimmed).hasSize(1);
+        assertThat(extractText(trimmed.get(0))).isEqualTo(repeat('x', 80));
+    }
+
+    @Test
+    void 
trimShouldKeepAssistantToolCallBlockIntactWhenMessageLimitWouldSplitIt() {
+        List<ChatCompletionMessageParam> history = List.of(
+                userMessage("older"),
+                assistantMessage("older-reply"),
+                userMessage("latest"),
+                assistantWithToolCall("call-1"),
+                toolMessage("tool-result"),
+                assistantMessage("final-reply"));
+
+        List<ChatCompletionMessageParam> trimmed = 
OpenAIConversationHistoryTrimmer.trim(history, config(2, 0));
+
+        assertThat(trimmed).hasSize(1);
+        assertThat(trimmed.get(0).assistant()).isPresent();
+        assertThat(extractText(trimmed.get(0))).isEqualTo("final-reply");
+        assertThat(trimmed.stream().noneMatch(message -> 
message.tool().isPresent())).isTrue();
+    }
+
+    @Test
+    void trimShouldRetainAssistantAndToolResultsAsOneSegment() {
+        List<ChatCompletionMessageParam> history = List.of(
+                assistantWithToolCall("call-1"),
+                toolMessage("tool-result"));
+
+        List<ChatCompletionMessageParam> trimmed = 
OpenAIConversationHistoryTrimmer.trim(history, config(2, 0));
+
+        assertThat(trimmed).hasSize(2);
+        assertThat(trimmed.get(0).assistant()).isPresent();
+        assertThat(trimmed.get(1).tool()).isPresent();
+    }
+
+    @Test
+    void estimateTokensShouldIncludeImagePayloadSize() {
+        String imageUrl = "data:image/png;base64," + repeat('x', 400);
+        ChatCompletionMessageParam imageMessage = 
ChatCompletionMessageParam.ofUser(
+                ChatCompletionUserMessageParam.builder()
+                        
.content(ChatCompletionUserMessageParam.Content.ofArrayOfContentParts(List.of(
+                                ChatCompletionContentPart.ofText(
+                                        
ChatCompletionContentPartText.builder().text("describe").build()),
+                                ChatCompletionContentPart.ofImageUrl(
+                                        
ChatCompletionContentPartImage.builder()
+                                                
.imageUrl(ChatCompletionContentPartImage.ImageUrl.builder()
+                                                        .url(imageUrl)
+                                                        .build())
+                                                .build()))))
+                        .build());
+
+        int tokens = 
OpenAIConversationHistoryTrimmer.estimateTokens(List.of(imageMessage));
+
+        assertThat(tokens).isGreaterThan(100);
+    }
+
+    private static OpenAIConfiguration config(int maxHistoryMessages, int 
maxHistoryTokens) {
+        OpenAIConfiguration configuration = new OpenAIConfiguration();
+        configuration.setMaxHistoryMessages(maxHistoryMessages);
+        configuration.setMaxHistoryTokens(maxHistoryTokens);
+        return configuration;
+    }
+
+    private static ChatCompletionMessageParam userMessage(String text) {
+        return ChatCompletionMessageParam.ofUser(
+                ChatCompletionUserMessageParam.builder()
+                        
.content(ChatCompletionUserMessageParam.Content.ofText(text))
+                        .build());
+    }
+
+    private static ChatCompletionMessageParam assistantMessage(String text) {
+        return ChatCompletionMessageParam.ofAssistant(
+                ChatCompletionAssistantMessageParam.builder()
+                        
.content(ChatCompletionAssistantMessageParam.Content.ofText(text))
+                        .build());
+    }
+
+    private static ChatCompletionMessageParam assistantWithToolCall(String 
toolCallId) {
+        return ChatCompletionMessageParam.ofAssistant(
+                ChatCompletionAssistantMessageParam.builder()
+                        .toolCalls(List.of(
+                                ChatCompletionMessageToolCall.ofFunction(
+                                        
ChatCompletionMessageFunctionToolCall.builder()
+                                                .id(toolCallId)
+                                                
.function(ChatCompletionMessageFunctionToolCall.Function.builder()
+                                                        .name("lookup")
+                                                        .arguments("{}")
+                                                        .build())
+                                                .build())))
+                        .build());
+    }
+
+    private static ChatCompletionMessageParam toolMessage(String text) {
+        return ChatCompletionMessageParam.ofTool(
+                ChatCompletionToolMessageParam.builder()
+                        .toolCallId("call-1")
+                        .content(text)
+                        .build());
+    }
+
+    private static String extractText(ChatCompletionMessageParam message) {
+        return message.user()
+                .map(user -> user.content().isText() ? user.content().asText() 
: "")
+                .orElseGet(() -> message.assistant()
+                        .flatMap(assistant -> assistant.content()
+                                
.filter(ChatCompletionAssistantMessageParam.Content::isText)
+                                
.map(ChatCompletionAssistantMessageParam.Content::asText))
+                        .orElseGet(() -> message.tool()
+                                .map(tool -> tool.content().isText() ? 
tool.content().asText() : "")
+                                .orElse("")));
+    }
+
+    private static String repeat(char value, int count) {
+        return String.valueOf(value).repeat(count);
+    }
+}
diff --git 
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/OpenAIEndpointBuilderFactory.java
 
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/OpenAIEndpointBuilderFactory.java
index fc175c550c1b..901192e02a69 100644
--- 
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/OpenAIEndpointBuilderFactory.java
+++ 
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/OpenAIEndpointBuilderFactory.java
@@ -480,6 +480,88 @@ public interface OpenAIEndpointBuilderFactory {
             doSetProperty("jsonSchema", jsonSchema);
             return this;
         }
+        /**
+         * When conversationMemory is enabled, retain at most this many 
messages
+         * in the exchange conversation history. System and developer messages
+         * are prepended separately and are not stored in history. Assistant
+         * tool-call blocks are kept intact and may retain slightly more than
+         * this limit to preserve tool result pairing. When 0, no message limit
+         * is applied.
+         * 
+         * The option is a: <code>int</code> type.
+         * 
+         * Default: 0
+         * Group: producer
+         * 
+         * @param maxHistoryMessages the value to set
+         * @return the dsl builder
+         */
+        default OpenAIEndpointBuilder maxHistoryMessages(int 
maxHistoryMessages) {
+            doSetProperty("maxHistoryMessages", maxHistoryMessages);
+            return this;
+        }
+        /**
+         * When conversationMemory is enabled, retain at most this many 
messages
+         * in the exchange conversation history. System and developer messages
+         * are prepended separately and are not stored in history. Assistant
+         * tool-call blocks are kept intact and may retain slightly more than
+         * this limit to preserve tool result pairing. When 0, no message limit
+         * is applied.
+         * 
+         * The option will be converted to a <code>int</code> type.
+         * 
+         * Default: 0
+         * Group: producer
+         * 
+         * @param maxHistoryMessages the value to set
+         * @return the dsl builder
+         */
+        default OpenAIEndpointBuilder maxHistoryMessages(String 
maxHistoryMessages) {
+            doSetProperty("maxHistoryMessages", maxHistoryMessages);
+            return this;
+        }
+        /**
+         * When conversationMemory is enabled, trim conversation history using 
a
+         * token estimate (character count / 4, including image payload size 
for
+         * multi-modal user messages). Oldest segments are dropped first until
+         * the estimated tokens are within this limit. Assistant tool-call
+         * blocks are removed as a unit with their tool results. The most 
recent
+         * segment is always retained, even when it alone exceeds this limit.
+         * When 0, no token limit is applied.
+         * 
+         * The option is a: <code>int</code> type.
+         * 
+         * Default: 0
+         * Group: producer
+         * 
+         * @param maxHistoryTokens the value to set
+         * @return the dsl builder
+         */
+        default OpenAIEndpointBuilder maxHistoryTokens(int maxHistoryTokens) {
+            doSetProperty("maxHistoryTokens", maxHistoryTokens);
+            return this;
+        }
+        /**
+         * When conversationMemory is enabled, trim conversation history using 
a
+         * token estimate (character count / 4, including image payload size 
for
+         * multi-modal user messages). Oldest segments are dropped first until
+         * the estimated tokens are within this limit. Assistant tool-call
+         * blocks are removed as a unit with their tool results. The most 
recent
+         * segment is always retained, even when it alone exceeds this limit.
+         * When 0, no token limit is applied.
+         * 
+         * The option will be converted to a <code>int</code> type.
+         * 
+         * Default: 0
+         * Group: producer
+         * 
+         * @param maxHistoryTokens the value to set
+         * @return the dsl builder
+         */
+        default OpenAIEndpointBuilder maxHistoryTokens(String 
maxHistoryTokens) {
+            doSetProperty("maxHistoryTokens", maxHistoryTokens);
+            return this;
+        }
         /**
          * Maximum number of times the OpenAI SDK client retries failed
          * requests. The SDK retry is rate-limit aware (honors Retry-After on

Reply via email to