Skip to content

fix: AI system messages should always be at start of prompt #1741

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jun 12, 2025
9 changes: 5 additions & 4 deletions packages/xl-ai/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,6 @@
"email": "email dev"
},
"dependencies": {
"@ai-sdk/groq": "^1.2.9",
"@ai-sdk/mistral": "^1.2.8",
"@ai-sdk/openai": "^1.3.22",
"@ai-sdk/openai-compatible": "^0.2.14",
"@blocknote/core": "0.31.2",
"@blocknote/mantine": "0.31.2",
"@blocknote/prosemirror-suggest-changes": "^0.1.3",
Expand All @@ -92,6 +88,11 @@
"zustand": "^5.0.3"
},
"devDependencies": {
"@ai-sdk/groq": "^1.2.9",
"@ai-sdk/mistral": "^1.2.8",
"@ai-sdk/openai": "^1.3.22",
"@ai-sdk/openai-compatible": "^0.2.14",
"@ai-sdk/anthropic": "^1.2.12",
"@mswjs/interceptors": "^0.37.5",
"@types/diff": "^6.0.0",
"@types/json-diff": "^1.0.3",
Expand Down
13 changes: 12 additions & 1 deletion packages/xl-ai/src/api/LLMRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,18 @@ export async function doLLMRequest(
let previousMessages: CoreMessage[] | undefined = undefined;

if (previousResponse) {
previousMessages = previousResponse.messages;
previousMessages = previousResponse.messages.map((m) => {
// Some models, like Gemini and Anthropic don't support mixing system and user messages.
// Therefore, we convert all user messages to system messages.
// (also see comment below on a possibly better approach that might also address this)
if (m.role === "user" && typeof m.content === "string") {
return {
role: "system",
content: `USER_MESSAGE: ${m.content}`,
};
}
return m;
});
/*
We currently insert these messages as "assistant" string messages.
When using Tools, the "official" pattern for this is to use a "tool_result" message.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"request": {
"method": "POST",
"url": "https://localhost:3000/ai?provider=anthropic&url=https%3A%2F%2Fapi.anthropic.com%2Fv1%2Fmessages",
"body": "{\"model\":\"claude-3-7-sonnet-latest\",\"max_tokens\":4096,\"temperature\":0,\"system\":[{\"type\":\"text\",\"text\":\"You're manipulating a text document using HTML blocks. \\n Make sure to follow the json schema provided. When referencing ids they MUST be EXACTLY the same (including the trailing $). \\n List items are 1 block with 1 list item each, so block content `<ul><li>item1</li></ul>` is valid, but `<ul><li>item1</li><li>item2</li></ul>` is invalid. We'll merge them automatically.\\n For code blocks, you can use the `data-language` attribute on a code block to specify the language.\\n This is the document as an array of html blocks (the cursor is BETWEEN two blocks as indicated by cursor: true):\"},{\"type\":\"text\",\"text\":\"[{\\\"id\\\":\\\"ref1$\\\",\\\"block\\\":\\\"<p>Hello, world!</p>\\\"},{\\\"cursor\\\":true},{\\\"id\\\":\\\"ref2$\\\",\\\"block\\\":\\\"<p>How are you?</p>\\\"}]\"},{\"type\":\"text\",\"text\":\"First, determine what part of the document the user is talking about. You SHOULD probably take cursor info into account if needed.\\n EXAMPLE: if user says \\\"below\\\" (without pointing to a specific part of the document) he / she probably indicates the block(s) after the cursor. \\n EXAMPLE: If you want to insert content AT the cursor position (UNLESS indicated otherwise by the user), \\n then you need `referenceId` to point to the block before the cursor with position `after` (or block below and `before`).\\n \\n Prefer updating existing blocks over removing and adding (but this also depends on the user's question).\"},{\"type\":\"text\",\"text\":\"The user asks you to do the following:\"}],\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"at the end of doc, add a h1 heading `Code` and a javascript code block with `console.log('hello world');`\"}]}],\"tools\":[{\"name\":\"json\",\"description\":\"Respond with a JSON object.\",\"input_schema\":{\"type\":\"object\",\"properties\":{\"operations\":{\"type\":\"array\",\"items\":{\"anyOf\":[{\"type\":\"object\",\"description\":\"Update a block\",\"properties\":{\"type\":{\"type\":\"string\",\"enum\":[\"update\"]},\"id\":{\"type\":\"string\",\"description\":\"id of block to update\"},\"block\":{\"$ref\":\"#/$defs/block\"}},\"required\":[\"type\",\"id\",\"block\"],\"additionalProperties\":false},{\"type\":\"object\",\"description\":\"Insert new blocks\",\"properties\":{\"type\":{\"type\":\"string\",\"enum\":[\"add\"]},\"referenceId\":{\"type\":\"string\",\"description\":\"MUST be an id of a block in the document\"},\"position\":{\"type\":\"string\",\"enum\":[\"before\",\"after\"],\"description\":\"`after` to add blocks AFTER (below) the block with `referenceId`, `before` to add the block BEFORE (above)\"},\"blocks\":{\"items\":{\"$ref\":\"#/$defs/block\"},\"type\":\"array\"}},\"required\":[\"type\",\"referenceId\",\"position\",\"blocks\"],\"additionalProperties\":false},{\"type\":\"object\",\"description\":\"Delete a block\",\"properties\":{\"type\":{\"type\":\"string\",\"enum\":[\"delete\"]},\"id\":{\"type\":\"string\",\"description\":\"id of block to delete\"}},\"required\":[\"type\",\"id\"],\"additionalProperties\":false}]}}},\"additionalProperties\":false,\"required\":[\"operations\"],\"$defs\":{\"block\":{\"type\":\"string\",\"description\":\"html of block (MUST be a single HTML element)\"}}}}],\"tool_choice\":{\"type\":\"tool\",\"name\":\"json\"}}",
"headers": [],
"cookies": []
},
"response": {
"status": 200,
"statusText": "",
"body": "{\"id\":\"msg_01CvNNDGkyfR1VeWv4sjCDQe\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-3-7-sonnet-20250219\",\"content\":[{\"type\":\"tool_use\",\"id\":\"toolu_01Sfb6t2T6NaHwLGw71Xk6iA\",\"name\":\"json\",\"input\":{\"operations\":[{\"type\":\"add\",\"referenceId\":\"ref2$\",\"position\":\"after\",\"blocks\":[\"<h1>Code</h1>\",\"<pre><code data-language=\\\"javascript\\\">console.log('hello world');</code></pre>\"]}]}}],\"stop_reason\":\"tool_use\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":1141,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":115,\"service_tier\":\"standard\"}}",
"headers": []
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"request": {
"method": "POST",
"url": "https://localhost:3000/ai?provider=anthropic&url=https%3A%2F%2Fapi.anthropic.com%2Fv1%2Fmessages",
"body": "{\"model\":\"claude-3-7-sonnet-latest\",\"max_tokens\":4096,\"temperature\":0,\"system\":[{\"type\":\"text\",\"text\":\"You're manipulating a text document using HTML blocks. \\n Make sure to follow the json schema provided. When referencing ids they MUST be EXACTLY the same (including the trailing $). \\n List items are 1 block with 1 list item each, so block content `<ul><li>item1</li></ul>` is valid, but `<ul><li>item1</li><li>item2</li></ul>` is invalid. We'll merge them automatically.\\n For code blocks, you can use the `data-language` attribute on a code block to specify the language.\\n This is the document as an array of html blocks (the cursor is BETWEEN two blocks as indicated by cursor: true):\"},{\"type\":\"text\",\"text\":\"[{\\\"id\\\":\\\"ref1$\\\",\\\"block\\\":\\\"<p>Hello, world!</p>\\\"},{\\\"cursor\\\":true},{\\\"id\\\":\\\"ref2$\\\",\\\"block\\\":\\\"<p>How are you?</p>\\\"}]\"},{\"type\":\"text\",\"text\":\"First, determine what part of the document the user is talking about. You SHOULD probably take cursor info into account if needed.\\n EXAMPLE: if user says \\\"below\\\" (without pointing to a specific part of the document) he / she probably indicates the block(s) after the cursor. \\n EXAMPLE: If you want to insert content AT the cursor position (UNLESS indicated otherwise by the user), \\n then you need `referenceId` to point to the block before the cursor with position `after` (or block below and `before`).\\n \\n Prefer updating existing blocks over removing and adding (but this also depends on the user's question).\"},{\"type\":\"text\",\"text\":\"The user asks you to do the following:\"}],\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"add a list with the items 'Apples' and 'Bananas' after the last sentence\"}]}],\"tools\":[{\"name\":\"json\",\"description\":\"Respond with a JSON object.\",\"input_schema\":{\"type\":\"object\",\"properties\":{\"operations\":{\"type\":\"array\",\"items\":{\"anyOf\":[{\"type\":\"object\",\"description\":\"Update a block\",\"properties\":{\"type\":{\"type\":\"string\",\"enum\":[\"update\"]},\"id\":{\"type\":\"string\",\"description\":\"id of block to update\"},\"block\":{\"$ref\":\"#/$defs/block\"}},\"required\":[\"type\",\"id\",\"block\"],\"additionalProperties\":false},{\"type\":\"object\",\"description\":\"Insert new blocks\",\"properties\":{\"type\":{\"type\":\"string\",\"enum\":[\"add\"]},\"referenceId\":{\"type\":\"string\",\"description\":\"MUST be an id of a block in the document\"},\"position\":{\"type\":\"string\",\"enum\":[\"before\",\"after\"],\"description\":\"`after` to add blocks AFTER (below) the block with `referenceId`, `before` to add the block BEFORE (above)\"},\"blocks\":{\"items\":{\"$ref\":\"#/$defs/block\"},\"type\":\"array\"}},\"required\":[\"type\",\"referenceId\",\"position\",\"blocks\"],\"additionalProperties\":false},{\"type\":\"object\",\"description\":\"Delete a block\",\"properties\":{\"type\":{\"type\":\"string\",\"enum\":[\"delete\"]},\"id\":{\"type\":\"string\",\"description\":\"id of block to delete\"}},\"required\":[\"type\",\"id\"],\"additionalProperties\":false}]}}},\"additionalProperties\":false,\"required\":[\"operations\"],\"$defs\":{\"block\":{\"type\":\"string\",\"description\":\"html of block (MUST be a single HTML element)\"}}}}],\"tool_choice\":{\"type\":\"tool\",\"name\":\"json\"}}",
"headers": [],
"cookies": []
},
"response": {
"status": 200,
"statusText": "",
"body": "{\"id\":\"msg_01CRWHfETGzBRnoYeagKsc7Q\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-3-7-sonnet-20250219\",\"content\":[{\"type\":\"tool_use\",\"id\":\"toolu_0119L1JcnPAWnLHFoeLMkQA1\",\"name\":\"json\",\"input\":{\"operations\":[{\"type\":\"add\",\"referenceId\":\"ref2$\",\"position\":\"after\",\"blocks\":[\"<ul><li>Apples</li></ul>\",\"<ul><li>Bananas</li></ul>\"]}]}}],\"stop_reason\":\"tool_use\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":1134,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":103,\"service_tier\":\"standard\"}}",
"headers": []
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"request": {
"method": "POST",
"url": "https://localhost:3000/ai?provider=anthropic&url=https%3A%2F%2Fapi.anthropic.com%2Fv1%2Fmessages",
"body": "{\"model\":\"claude-3-7-sonnet-latest\",\"max_tokens\":4096,\"temperature\":0,\"system\":[{\"type\":\"text\",\"text\":\"You're manipulating a text document using HTML blocks. \\n Make sure to follow the json schema provided. When referencing ids they MUST be EXACTLY the same (including the trailing $). \\n List items are 1 block with 1 list item each, so block content `<ul><li>item1</li></ul>` is valid, but `<ul><li>item1</li><li>item2</li></ul>` is invalid. We'll merge them automatically.\\n For code blocks, you can use the `data-language` attribute on a code block to specify the language.\\n This is the document as an array of html blocks (the cursor is BETWEEN two blocks as indicated by cursor: true):\"},{\"type\":\"text\",\"text\":\"[{\\\"id\\\":\\\"ref1$\\\",\\\"block\\\":\\\"<p></p>\\\"},{\\\"cursor\\\":true}]\"},{\"type\":\"text\",\"text\":\"First, determine what part of the document the user is talking about. You SHOULD probably take cursor info into account if needed.\\n EXAMPLE: if user says \\\"below\\\" (without pointing to a specific part of the document) he / she probably indicates the block(s) after the cursor. \\n EXAMPLE: If you want to insert content AT the cursor position (UNLESS indicated otherwise by the user), \\n then you need `referenceId` to point to the block before the cursor with position `after` (or block below and `before`).\\n \\n Because the document is empty, first update the empty block before adding new blocks.\"},{\"type\":\"text\",\"text\":\"The user asks you to do the following:\"}],\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"write a new paragraph with the text 'You look great today!'\"}]}],\"tools\":[{\"name\":\"json\",\"description\":\"Respond with a JSON object.\",\"input_schema\":{\"type\":\"object\",\"properties\":{\"operations\":{\"type\":\"array\",\"items\":{\"anyOf\":[{\"type\":\"object\",\"description\":\"Update a block\",\"properties\":{\"type\":{\"type\":\"string\",\"enum\":[\"update\"]},\"id\":{\"type\":\"string\",\"description\":\"id of block to update\"},\"block\":{\"$ref\":\"#/$defs/block\"}},\"required\":[\"type\",\"id\",\"block\"],\"additionalProperties\":false},{\"type\":\"object\",\"description\":\"Insert new blocks\",\"properties\":{\"type\":{\"type\":\"string\",\"enum\":[\"add\"]},\"referenceId\":{\"type\":\"string\",\"description\":\"MUST be an id of a block in the document\"},\"position\":{\"type\":\"string\",\"enum\":[\"before\",\"after\"],\"description\":\"`after` to add blocks AFTER (below) the block with `referenceId`, `before` to add the block BEFORE (above)\"},\"blocks\":{\"items\":{\"$ref\":\"#/$defs/block\"},\"type\":\"array\"}},\"required\":[\"type\",\"referenceId\",\"position\",\"blocks\"],\"additionalProperties\":false},{\"type\":\"object\",\"description\":\"Delete a block\",\"properties\":{\"type\":{\"type\":\"string\",\"enum\":[\"delete\"]},\"id\":{\"type\":\"string\",\"description\":\"id of block to delete\"}},\"required\":[\"type\",\"id\"],\"additionalProperties\":false}]}}},\"additionalProperties\":false,\"required\":[\"operations\"],\"$defs\":{\"block\":{\"type\":\"string\",\"description\":\"html of block (MUST be a single HTML element)\"}}}}],\"tool_choice\":{\"type\":\"tool\",\"name\":\"json\"}}",
"headers": [],
"cookies": []
},
"response": {
"status": 200,
"statusText": "",
"body": "{\"id\":\"msg_017cBrLzbBcimHZvkBPHaHUS\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-3-7-sonnet-20250219\",\"content\":[{\"type\":\"tool_use\",\"id\":\"toolu_01H2npMHYeXPe4vWmyeeAN6n\",\"name\":\"json\",\"input\":{\"operations\":[{\"type\":\"update\",\"id\":\"ref1$\",\"block\":\"<p>You look great today!</p>\"}]}}],\"stop_reason\":\"tool_use\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":1097,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":74,\"service_tier\":\"standard\"}}",
"headers": []
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"request": {
"method": "POST",
"url": "https://localhost:3000/ai?provider=anthropic&url=https%3A%2F%2Fapi.anthropic.com%2Fv1%2Fmessages",
"body": "{\"model\":\"claude-3-7-sonnet-latest\",\"max_tokens\":4096,\"temperature\":0,\"system\":[{\"type\":\"text\",\"text\":\"You're manipulating a text document using HTML blocks. \\n Make sure to follow the json schema provided. When referencing ids they MUST be EXACTLY the same (including the trailing $). \\n List items are 1 block with 1 list item each, so block content `<ul><li>item1</li></ul>` is valid, but `<ul><li>item1</li><li>item2</li></ul>` is invalid. We'll merge them automatically.\\n For code blocks, you can use the `data-language` attribute on a code block to specify the language.\\n This is the document as an array of html blocks (the cursor is BETWEEN two blocks as indicated by cursor: true):\"},{\"type\":\"text\",\"text\":\"[{\\\"id\\\":\\\"ref1$\\\",\\\"block\\\":\\\"<p>Hello, world!</p>\\\"},{\\\"cursor\\\":true},{\\\"id\\\":\\\"ref2$\\\",\\\"block\\\":\\\"<p>How are you?</p>\\\"}]\"},{\"type\":\"text\",\"text\":\"First, determine what part of the document the user is talking about. You SHOULD probably take cursor info into account if needed.\\n EXAMPLE: if user says \\\"below\\\" (without pointing to a specific part of the document) he / she probably indicates the block(s) after the cursor. \\n EXAMPLE: If you want to insert content AT the cursor position (UNLESS indicated otherwise by the user), \\n then you need `referenceId` to point to the block before the cursor with position `after` (or block below and `before`).\\n \\n Prefer updating existing blocks over removing and adding (but this also depends on the user's question).\"},{\"type\":\"text\",\"text\":\"The user asks you to do the following:\"}],\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"add a new paragraph with the text 'You look great today!' after the last sentence\"}]}],\"tools\":[{\"name\":\"json\",\"description\":\"Respond with a JSON object.\",\"input_schema\":{\"type\":\"object\",\"properties\":{\"operations\":{\"type\":\"array\",\"items\":{\"anyOf\":[{\"type\":\"object\",\"description\":\"Update a block\",\"properties\":{\"type\":{\"type\":\"string\",\"enum\":[\"update\"]},\"id\":{\"type\":\"string\",\"description\":\"id of block to update\"},\"block\":{\"$ref\":\"#/$defs/block\"}},\"required\":[\"type\",\"id\",\"block\"],\"additionalProperties\":false},{\"type\":\"object\",\"description\":\"Insert new blocks\",\"properties\":{\"type\":{\"type\":\"string\",\"enum\":[\"add\"]},\"referenceId\":{\"type\":\"string\",\"description\":\"MUST be an id of a block in the document\"},\"position\":{\"type\":\"string\",\"enum\":[\"before\",\"after\"],\"description\":\"`after` to add blocks AFTER (below) the block with `referenceId`, `before` to add the block BEFORE (above)\"},\"blocks\":{\"items\":{\"$ref\":\"#/$defs/block\"},\"type\":\"array\"}},\"required\":[\"type\",\"referenceId\",\"position\",\"blocks\"],\"additionalProperties\":false},{\"type\":\"object\",\"description\":\"Delete a block\",\"properties\":{\"type\":{\"type\":\"string\",\"enum\":[\"delete\"]},\"id\":{\"type\":\"string\",\"description\":\"id of block to delete\"}},\"required\":[\"type\",\"id\"],\"additionalProperties\":false}]}}},\"additionalProperties\":false,\"required\":[\"operations\"],\"$defs\":{\"block\":{\"type\":\"string\",\"description\":\"html of block (MUST be a single HTML element)\"}}}}],\"tool_choice\":{\"type\":\"tool\",\"name\":\"json\"}}",
"headers": [],
"cookies": []
},
"response": {
"status": 200,
"statusText": "",
"body": "{\"id\":\"msg_01AdgqSQ8TTFkiYMU3VVczc1\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-3-7-sonnet-20250219\",\"content\":[{\"type\":\"tool_use\",\"id\":\"toolu_01KnGeGTMvcmwDG1sraFPvok\",\"name\":\"json\",\"input\":{\"operations\":[{\"type\":\"add\",\"referenceId\":\"ref2$\",\"position\":\"after\",\"blocks\":[\"<p>You look great today!</p>\"]}]}}],\"stop_reason\":\"tool_use\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":1129,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":86,\"service_tier\":\"standard\"}}",
"headers": []
}
}
Loading
Loading