Skip to content

Commit bdbe84e

Browse files
YousefEDnperez0111
andauthored
fix: AI system messages should always be at start of prompt (#1741)
* fix alternating system / user messages for anthropic / gemini * also update json tool * add USER_MESSAGE Co-authored-by: Nick Perez <nick@blocknotejs.org> * snapshots --------- Co-authored-by: Nick Perez <nick@blocknotejs.org>
1 parent f4b2e7a commit bdbe84e

File tree

199 files changed

+883
-466
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

199 files changed

+883
-466
lines changed

packages/xl-ai/package.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,6 @@
6464
"email": "email dev"
6565
},
6666
"dependencies": {
67-
"@ai-sdk/groq": "^1.2.9",
68-
"@ai-sdk/mistral": "^1.2.8",
69-
"@ai-sdk/openai": "^1.3.22",
70-
"@ai-sdk/openai-compatible": "^0.2.14",
7167
"@blocknote/core": "0.31.2",
7268
"@blocknote/mantine": "0.31.2",
7369
"@blocknote/prosemirror-suggest-changes": "^0.1.3",
@@ -92,6 +88,11 @@
9288
"zustand": "^5.0.3"
9389
},
9490
"devDependencies": {
91+
"@ai-sdk/groq": "^1.2.9",
92+
"@ai-sdk/mistral": "^1.2.8",
93+
"@ai-sdk/openai": "^1.3.22",
94+
"@ai-sdk/openai-compatible": "^0.2.14",
95+
"@ai-sdk/anthropic": "^1.2.12",
9596
"@mswjs/interceptors": "^0.37.5",
9697
"@types/diff": "^6.0.0",
9798
"@types/json-diff": "^1.0.3",

packages/xl-ai/src/api/LLMRequest.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,18 @@ export async function doLLMRequest(
166166
let previousMessages: CoreMessage[] | undefined = undefined;
167167

168168
if (previousResponse) {
169-
previousMessages = previousResponse.messages;
169+
previousMessages = previousResponse.messages.map((m) => {
170+
// Some models, like Gemini and Anthropic don't support mixing system and user messages.
171+
// Therefore, we convert all user messages to system messages.
172+
// (also see comment below on a possibly better approach that might also address this)
173+
if (m.role === "user" && typeof m.content === "string") {
174+
return {
175+
role: "system",
176+
content: `USER_MESSAGE: ${m.content}`,
177+
};
178+
}
179+
return m;
180+
});
170181
/*
171182
We currently insert these messages as "assistant" string messages.
172183
When using Tools, the "official" pattern for this is to use a "tool_result" message.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"request": {
3+
"method": "POST",
4+
"url": "https://localhost:3000/ai?provider=anthropic&url=https%3A%2F%2Fapi.anthropic.com%2Fv1%2Fmessages",
5+
"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\"}}",
6+
"headers": [],
7+
"cookies": []
8+
},
9+
"response": {
10+
"status": 200,
11+
"statusText": "",
12+
"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\"}}",
13+
"headers": []
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"request": {
3+
"method": "POST",
4+
"url": "https://localhost:3000/ai?provider=anthropic&url=https%3A%2F%2Fapi.anthropic.com%2Fv1%2Fmessages",
5+
"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\"}}",
6+
"headers": [],
7+
"cookies": []
8+
},
9+
"response": {
10+
"status": 200,
11+
"statusText": "",
12+
"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\"}}",
13+
"headers": []
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"request": {
3+
"method": "POST",
4+
"url": "https://localhost:3000/ai?provider=anthropic&url=https%3A%2F%2Fapi.anthropic.com%2Fv1%2Fmessages",
5+
"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\"}}",
6+
"headers": [],
7+
"cookies": []
8+
},
9+
"response": {
10+
"status": 200,
11+
"statusText": "",
12+
"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\"}}",
13+
"headers": []
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"request": {
3+
"method": "POST",
4+
"url": "https://localhost:3000/ai?provider=anthropic&url=https%3A%2F%2Fapi.anthropic.com%2Fv1%2Fmessages",
5+
"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\"}}",
6+
"headers": [],
7+
"cookies": []
8+
},
9+
"response": {
10+
"status": 200,
11+
"statusText": "",
12+
"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\"}}",
13+
"headers": []
14+
}
15+
}

0 commit comments

Comments
 (0)