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 requestBodyLimits to -1 so the full conversation history is sent to the backend with each request

Make chat history length configurable

  • Created src/CanvasAiChatHelper.php with a getFilteredChatHistory method
  • 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 break was triggered in the loop after fetching the image file. This prevented all subsequent messages from being included in the history. The break has 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_messages setting with default 10
  • 0 means no history, -1 means 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

Issue fork canvas-3555239

Command icon 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

akhil babu created an issue. See original summary.

akhil babu’s picture

StatusFileSize
new82 KB

The 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.

/files/issues/2025-11-10/Image-%201.png

rakhimandhania’s picture

Issue tags: +AI Innovation
rakhimandhania’s picture

Issue tags: +AI Initiative Sprint
rakhimandhania’s picture

hi @afoster - Tagging you for sharing applicable use cases, prompts and screenshots (starting state and expected outcome after the prompt is run).

lbesenyei’s picture

Assigned: Unassigned » lbesenyei

rakhimandhania’s picture

Issue tags: +AI Page Generation
lbesenyei’s picture

Status: Active » Needs review

I'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.

akhil babu’s picture

Status: Needs review » Needs work

Thanks 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

lbesenyei’s picture

Regarding 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.

lbesenyei’s picture

Assigned: lbesenyei » Unassigned
Status: Needs work » Needs review

I have removed unnecessary UI changes.
Currently the message duplication seems to have been solved.

akhil babu’s picture

Assigned: Unassigned » akhil babu

Thanks, I will review this

akhil babu’s picture

Assigned: akhil babu » Unassigned
Status: Needs review » Needs work

Thanks 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.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.

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)

  if (!empty($message['text'])) {
      // Sub agent responses contain $message['html'] instead of $message['text']
        $messages[] = new ChatMessage($message['role'] === 'user' ? 'user' : 'assistant', $message['text']);
      }

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

akhil babu’s picture

Assigned: Unassigned » akhil babu
Status: Needs work » Active

I will be able to work on this today

akhil babu’s picture

Issue summary: View changes
akhil babu’s picture

Issue summary: View changes
akhil babu’s picture

Issue summary: View changes
akhil babu’s picture

Issue summary: View changes
akhil babu’s picture

Issue summary: View changes
akhil babu’s picture

Issue summary: View changes
akhil babu’s picture

Issue summary: View changes
akhil babu’s picture

Assigned: akhil babu » Unassigned
Status: Active » Needs review

PHPstan 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

joshua1234511’s picture

Assigned: Unassigned » joshua1234511
marcus_johansson’s picture

Status: Needs review » Needs work

Added some initial comments, will set it to needs work. But joshua1234511 should add on top of it.

joshua1234511’s picture

UI 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..."

Test
Test
Test

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.

akhil babu’s picture

Assigned: Unassigned » akhil babu
Status: Needs work » Active
akhil babu’s picture

Status: Active » Needs review

Thanks 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!

lbesenyei’s picture

Status: Needs review » Reviewed & tested by the community

Thanks 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.

narendrar made their first commit to this issue’s fork.

akhil babu’s picture

Status: Reviewed & tested by the community » Needs work

Back to needs work

akhil babu’s picture

Assigned: akhil babu » Unassigned
Status: Needs work » Needs review
marcus_johansson’s picture

Assigned: Unassigned » marcus_johansson
marcus_johansson’s picture

Assigned: marcus_johansson » Unassigned
Status: Needs review » Needs work

Wrote some comments, could you please have a look.

akhil babu’s picture

Assigned: Unassigned » akhil babu
akhil babu’s picture

Title: Canvas AI: Orchestrator missing previous conversation context » Canvas AI: Make chat history length configurable
Issue summary: View changes
akhil babu’s picture

Issue summary: View changes
akhil babu’s picture

Assigned: akhil babu » Unassigned
Status: Needs work » Needs review

Thanks for the review. I have updated the code based on feedback. Please review

marcus_johansson’s picture

Status: Needs review » Reviewed & tested by the community

Looks good to me now