Overview
Copied from #4 in #3554026: Canvas AI: Refine the orchestrator prompt
Currently, only the last two messages are sent as history to the orchestrator, which affects the overall user experience.
See the attached video for reference. When asked to create a CTA for a tech journal website, the orchestrator asks whether a code component should be created or an existing component should be added (meaning the page builder should be used). When asked to create a code component, the component agent fails. So I asked the agent to "add an existing component then." The agent then responds with:
"Please let me know what type of existing component or section you would like to add to your page, and any specific details or placements you have in mind."
This happens because it doesn’t have access to the complete chat history.
Another issue is that intermediate status messages from sub-agents are also included in the history. For example:
When you ask the agent to "Create a button code component with a red background," the component agent is invoked. Once the component is created, it outputs:
"I have created a new 'Red Button' component with customizable button text and a red background. Let me know if you need any further adjustments!"
When the orchestrator receives this message, it also outputs something like:
"A new 'Red Button' component has been created with customizable button text and a red background. You can now use and adjust the button text as needed."
Now, when the user asks to change the color to blue, the message history sent to the orchestrator looks like this:
This includes the outputs of both the component agent and the orchestrator, but not the initial user message
[
{
"role": "ai",
"html": "<div style=\"margin-top: 10px;\"><div style=\"display: flex; align-items: center; padding: 8px; background-color: white;\">\n <span style=\"margin-right: 8px;\"><span class=\"aiCompletedIcon\"></span></span>\n <span style=\"font-weight: 400;\">Thinking</span>\n </div><div style=\"padding: 8px; background-color: white; font-size: 14px; line-height: 1.26;\">\n I have created a new \"Red Button\" component with a customizable button text and a red background. Let me know if you need any further adjustments!\n </div><div style=\"display: flex; align-items: center; padding: 8px; background-color: white;\">\n <span style=\"margin-right: 8px;\"><span class=\"aiCompletedIcon\"></span></span>\n <span style=\"font-weight: 400;\">Generate a component</span>\n </div><div style=\"padding: 8px; background-color: white; font-size: 14px; line-height: 1.26;\">\n A new \"Red Button\" component has been created with a customizable button text and a red background. You can now use and adjust the button text as needed.\n </div></div>"
},
{
"role": "ai",
"text": "A new \"Red Button\" component has been created with a customizable button text and a red background. You can now use and adjust the button text as needed."
},
{
"role": "user",
"text": "change its color to blue"
}
]
I think the sub-agent output can be ignored in the history since the orchestrator already returns a similar message. Instead, the history should look like this:
[
{
"role": "user",
"text": "Add a button with red color"
},
{
"role": "ai",
"text": "A new 'Red Button' component has been created with a customizable button text and a red background. You can now use and adjust the button text as needed."
},
{
"role": "user",
"text": "change its color to blue"
}
]--------------------------------------------------------
The above observation is partially incorrect. Even though the front end sends sub agent responses to backend, the code only considers user inputs and the orchestrator responses for chat history
if (!empty($message['text'])) {
// Sub agent responses contain $message['html'] instead of $message['text']
$messages[] = new ChatMessage($message['role'] === 'user' ? 'user' : 'assistant', $message['text']);
}
The real issue is thatCurrently only the last 3 messages are sent to backend due to this setting in web/modules/contrib/canvas/ui/src/components/aiExtension/AiWizard.tsx
requestBodyLimits={{
maxMessages: 3,
}}We should remove this setting and allow all messages to be sent to the backend. For that, as per the deepchat documentation, the maxMessages value should be set 0 or less.
https://deepchat.dev/docs/connect/#requestBodyLimits
maxMessages is the maximum number of messages counting from the most recent one. If this is set to a number higher than 0 such as 1 - the outgoing request will only include the new user message, if it is 2 - it will also include the message before the latest one (from AI or the user) and so on... If the number is 0 or below - the request will include all messages in the chat. If it is undefined, the request will only include the input text/files.
Proposed resolution
The AIWizard.tsx file had this comment
@todo Revisit once https://www.drupal.org/node/3528730 is in.
That issue suggests using the short-term memory plugin from the AI module to control chat history. The "last_n" plugin provided by AI core could be used, but it simply returns a fixed number of messages from the complete history. In this issue, custom logic is used for the same purpose, as it is more efficient.
Set request body limit in Deep Chat
- Set
requestBodyLimitsto-1so the full conversation history is sent to the backend with each request
Make chat history length configurable
- Created
src/CanvasAiChatHelper.phpwith agetFilteredChatHistorymethod - This method takes the entire chat history sent from the UI to the backend controller and returns only the number of messages equal to
chat_history_max_messages - Fixes a bug in the previous logic where, if an image message was encountered, a
breakwas triggered in the loop after fetching the image file. This prevented all subsequent messages from being included in the history. Thebreakhas been removed. - Removed the logic where the text accompanying an uploaded image was removed (See: https://git.drupalcode.org/project/canvas/-/merge_requests/687#note_819457)
- Added a
chat_history_max_messagessetting with default10 0means no history,-1means full history, positive values limit to last N messages- Kept the default history length as 10. Added an update hook to set the history limit as 3 for existing users (the current limit is 3)
Test coverage
- Added kernel tests for the service.
User interface changes
| Comment | File | Size | Author |
|---|---|---|---|
| #26 | Screenshot 2026-03-31 at 2.50.02 PM.png | 166.12 KB | joshua1234511 |
| #26 | Screenshot 2026-03-31 at 2.50.08 PM.png | 181.25 KB | joshua1234511 |
| #26 | Screenshot 2026-03-31 at 2.50.15 PM.png | 79.76 KB | joshua1234511 |
| #2 | Image- 1.png | 82 KB | akhil babu |
Issue fork canvas-3555239
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
akhil babuThe final message from the orchestrator is currently displayed at the top. The message from the last sub-agent that was invoked is being added to the chat history, but ideally, the orchestrator’s response should appear at the bottom and be added to the history instead.
Comment #3
rakhimandhania commentedComment #4
rakhimandhania commentedComment #5
rakhimandhania commentedhi @afoster - Tagging you for sharing applicable use cases, prompts and screenshots (starting state and expected outcome after the prompt is run).
Comment #6
lbesenyei commentedComment #8
rakhimandhania commentedComment #9
lbesenyei commentedI've removed the duplicated message from the chat, and implemented the ShortTermMemoryPlugin as it was hinted in the code. @see https://www.drupal.org/node/3528730.
The history limit I'm not sure what to set.
Maybe it could be configurable.
Comment #10
akhil babuThanks a lot for working on this! I haven't had a chance to test it yet, but while looking through the merge request I noticed there are several changes to the UI files.
I was wondering if those changes are required for this issue. For example, in ui/src/components/aiExtension/AiWizard.tsx a element is introduced. As far as I know, UI changes like this often require a UI review or design discussion first.
Are these UI updates necessary for fixing the issue?
The history limit is now set as 3 in https://git.drupalcode.org/project/canvas/-/blob/1.x/ui/src/components/a.... I think we should increase the limit to maybe 10
Comment #11
lbesenyei commentedRegarding the UI, yes it needed the change to remove the duplicated agent message from the history.
Please tag someone from the UI team so we can discuss this.
The history limit of 3 set in the UI should no longer be necessary, the ShortTermMemoryPlugin implementation handles that on the backend with limit set to 20.
Comment #12
lbesenyei commentedI have removed unnecessary UI changes.
Currently the message duplication seems to have been solved.
Comment #13
akhil babuThanks, I will review this
Comment #14
akhil babuThanks for pushing this forward.
Using the 'last_n' short term memory plugin makes sense. But in the current approach, we are storing the chat history in tempstore, which is overkill and also adds the burden of writing extra code to delete the history. The better approach would be:
Currently only the last 3 messages are sent to backend due to this setting in
web/modules/contrib/canvas/ui/src/components/aiExtension/AiWizard.tsxWe should remove this setting and allow all messages to be sent to the backend. For that, as per the deepchat documentation, the maxMessages value should be set 0 or less.
https://deepchat.dev/docs/connect/#requestBodyLimits
These message contain outputs from both the orchestrator agents and sub agents. But, the backend only considers user inputs and the orchestrator responses for chat history (Which is intentional)
These filtered messages can be set as input to the short term memory plugin, and the messages returned by the plugin can be set as chat history of the agent
Comment #15
akhil babuI will be able to work on this today
Comment #16
akhil babuComment #17
akhil babuComment #18
akhil babuComment #19
akhil babuComment #20
akhil babuComment #21
akhil babuComment #22
akhil babuComment #23
akhil babuPHPstan reports errors in files that are not changed in the current MR.
Apart from that, this issue is ready for review.
The issue mentioned in comment #2 has been already fixed in #3548718: Orchestrator and sub-agents produce duplicate final messages
Comment #24
joshua1234511Comment #25
marcus_johansson commentedAdded some initial comments, will set it to needs work. But joshua1234511 should add on top of it.
Comment #26
joshua1234511UI Manual testing
Tested on branch 3555239-canvas-ai-chat-history (commit 93014880) with Drupal 11.x / DDEV.
Points:
New "Maximum chat history messages" field appears with default value of 20.
Multi-turn conversation context: Opened Canvas Page → AI panel and ran the exact scenario from the issue summary:
Turn 1: "Add a button with red background color"
AI: Responded that button component isn't available, offered to create a custom JS/React button component with red background or use an existing component.
Turn 2: "Change its color to blue instead"
AI: "Do you mean: 1. Change the button's background from red to blue, or 2. Change the button text color to blue? Also, are we updating a custom JavaScript/React button component you already created..."
The orchestrator correctly resolved "its" back to the button from Turn 1 and remembered both the red background and the JS/React component type. This confirms the full conversation history is being passed to the backend.
Chat reset on panel reopen: Toggling the AI panel off/on correctly clears the history — no stale data from previous sessions.
Comment #27
akhil babuComment #28
akhil babuThanks Marcus and Joshua. I've updated the code based on some of the suggestions, and I’ve added comments for the remaining ones.
Ready for review!
Comment #29
lbesenyei commentedThanks for the code improvements, as well as for adding the tests and configuration.
I’ve also tested everything manually, and it looks good to me.
Comment #31
akhil babuBack to needs work
Comment #32
akhil babuPer https://git.drupalcode.org/project/canvas/-/merge_requests/687#note_737465
Comment #33
marcus_johansson commentedComment #34
marcus_johansson commentedWrote some comments, could you please have a look.
Comment #35
akhil babuComment #36
akhil babuComment #37
akhil babuComment #38
akhil babuThanks for the review. I have updated the code based on feedback. Please review
Comment #39
marcus_johansson commentedLooks good to me now