fixup! ️(frontend) improve prompt of some actions

This commit is contained in:
Anthony LC
2026-01-27 17:53:21 +01:00
parent 610a469a08
commit 2c4c65b05c
@@ -1,19 +1,95 @@
import { Block } from '@blocknote/core';
import { llmFormats } from '@blocknote/xl-ai';
import { CoreMessage } from 'ai';
import { TFunction } from 'i18next';
import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { DocsBlockNoteEditor } from '../../types';
import {
DocsBlockNoteEditor,
DocsBlockSchema,
DocsInlineContentSchema,
DocsStyleSchema,
} from '../../types';
export type PromptBuilderInput = {
userPrompt: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
selectedBlocks?: Block<any, any, any>[];
selectedBlocks?: Block<
DocsBlockSchema,
DocsInlineContentSchema,
DocsStyleSchema
>[];
excludeBlockIds?: string[];
previousMessages?: Array<CoreMessage>;
};
type SystemPromptKey =
| 'add-edit-instruction'
| 'add-formatting'
| 'add-markdown'
| 'assistant'
| 'language'
| 'referenceId';
// Constants for prompt content matching
const PROMPT_INDICATORS = {
HTML_BLOCKS: "You're manipulating a text document using HTML blocks.",
EDIT_INSTRUCTION:
'First, determine what part of the document the user is talking about.',
ASSISTANT_IDENTIFIER: 'You are an AI assistant that edits user documents.',
EMPTY_CONTENT: '[]',
} as const;
const createSystemPrompts = (
t: TFunction<'translation', undefined>,
): Record<SystemPromptKey, CoreMessage> => ({
assistant: {
role: 'system',
content: t('You are an AI assistant that edits user documents.'),
},
referenceId: {
role: 'system',
content: t(
'Keep block IDs exactly as provided when referencing them (including the trailing "$").',
),
},
'add-markdown': {
role: 'system',
content: t('Answer the user prompt in markdown format.'),
},
'add-formatting': {
role: 'system',
content: t('Add formatting to the text to make it more readable.'),
},
'add-edit-instruction': {
role: 'system',
content: t(
'Add content; do not delete or alter existing blocks unless explicitly told.',
),
},
language: {
role: 'system',
content: t(
'Detect the dominant language inside the provided blocks. YOU MUST PROVIDE AN ANSWER IN THE DETECTED LANGUAGE.',
),
},
});
const createUserPrompts = (
t: TFunction<'translation', undefined>,
): Record<string, string> => ({
'continue writing': t(
'Keep writing about the content sent in the prompt, expanding on the ideas.',
),
'improve writing': t(
'Improve the writing of the selected text. Make it more professional and clear.',
),
summarize: t('Summarize the document into a concise paragraph.'),
'fix spelling': t(
'Fix the spelling and grammar mistakes in the selected text.',
),
});
/**
* Custom implementation of the PromptBuilder that allows for using predefined prompts.
*
@@ -23,132 +99,145 @@ export type PromptBuilderInput = {
export const usePromptAI = () => {
const { t } = useTranslation();
const parseCustomPrompt = useCallback(
(userPrompt: string, userPrompts: Record<string, string>) => {
const customPromptMatch = userPrompt.match(/^([^:]+)(?=[:]|$)/);
const promptKey = customPromptMatch?.[0].trim().toLowerCase();
if (promptKey && userPrompts[promptKey]) {
return {
promptKey,
modifiedPrompt: userPrompts[promptKey],
};
}
return {
promptKey,
modifiedPrompt: userPrompt,
};
},
[],
);
/**
* Fix a bug when the initial content is empty
* TODO: Remove this when the bug is fixed in BlockNote
*/
const fixEmptyContentBug = useCallback(
(prompt: CoreMessage, editor: DocsBlockNoteEditor): CoreMessage => {
const lastBlockId = editor.document[editor.document.length - 1]?.id;
return {
role: prompt.role,
content: `[{"id":"${lastBlockId}$","block":"<p></p>"}]`,
} as CoreMessage;
},
[],
);
const transformPromptContent = useCallback(
(
prompt: CoreMessage,
systemPrompts: Record<SystemPromptKey, CoreMessage>,
editor: DocsBlockNoteEditor,
): CoreMessage => {
if (!prompt.content || typeof prompt.content !== 'string') {
return prompt;
}
// Fix empty content bug
if (prompt.content.includes(PROMPT_INDICATORS.EMPTY_CONTENT)) {
return fixEmptyContentBug(prompt, editor);
}
// Replace specific prompt content with system prompts
if (prompt.content.includes(PROMPT_INDICATORS.HTML_BLOCKS)) {
return systemPrompts['add-markdown'];
}
if (prompt.content.includes(PROMPT_INDICATORS.EDIT_INSTRUCTION)) {
return systemPrompts['add-edit-instruction'];
}
return prompt;
},
[fixEmptyContentBug],
);
const addSystemPrompts = useCallback(
(
prompts: CoreMessage[],
systemPrompts: Record<SystemPromptKey, CoreMessage>,
hasAssistantPrompt: boolean,
promptKey?: string,
): CoreMessage[] => {
if (hasAssistantPrompt) {
return prompts;
}
const newPrompts = [
systemPrompts.assistant,
...prompts,
systemPrompts.referenceId,
];
// Add language prompt except when translating
if (!promptKey?.includes('Translate into')) {
newPrompts.push(systemPrompts.language);
}
return newPrompts;
},
[],
);
return useCallback(
async (
editor: DocsBlockNoteEditor,
opts: PromptBuilderInput,
): Promise<Array<CoreMessage>> => {
const systemPrompts: Record<
| 'add-edit-instruction'
| 'add-formatting'
| 'add-markdown'
| 'assistant'
| 'language'
| 'referenceId',
CoreMessage
> = {
assistant: {
role: 'system',
content: t(`You are an AI assistant that edits user documents.`),
},
referenceId: {
role: 'system',
content: t(
`Keep block IDs exactly as provided when referencing them (including the trailing "$").`,
),
},
'add-markdown': {
role: 'system',
content: t(`Answer the user prompt in markdown format.`),
},
'add-formatting': {
role: 'system',
content: t(`Add formatting to the text to make it more readable.`),
},
'add-edit-instruction': {
role: 'system',
content: t(
`Add content; do not delete or alter existing blocks unless explicitly told.`,
),
},
language: {
role: 'system',
content: t(
`Detect the dominant language inside the provided blocks. YOU MUST PROVIDE AN ANSWER IN THE DETECTED LANGUAGE.`,
),
},
};
const systemPrompts = createSystemPrompts(t);
const userPrompts = createUserPrompts(t);
const userPrompts: Record<string, string> = {
'continue writing': t(
'Keep writing about the content sent in the prompt, expanding on the ideas.',
),
'improve writing': t(
'Improve the writing of the selected text. Make it more professional and clear.',
),
summarize: t('Summarize the document into a concise paragraph.'),
'fix spelling': t(
'Fix the spelling and grammar mistakes in the selected text.',
),
};
// Parse and modify user prompt if it matches a custom prompt
const { promptKey, modifiedPrompt } = parseCustomPrompt(
opts.userPrompt,
userPrompts,
);
const modifiedOpts = { ...opts, userPrompt: modifiedPrompt };
// Modify userPrompt if it matches a custom prompt
const customPromptMatch = opts.userPrompt.match(/^([^:]+)(?=[:]|$)/);
let modifiedOpts = opts;
const promptKey = customPromptMatch?.[0].trim().toLowerCase();
if (promptKey) {
if (userPrompts[promptKey]) {
modifiedOpts = {
...opts,
userPrompt: userPrompts[promptKey],
};
}
}
// Get initial prompts from BlockNote
let prompts = await llmFormats.html.defaultPromptBuilder(
editor,
modifiedOpts,
);
const isTransformExistingContent = !!opts.selectedBlocks?.length;
if (!isTransformExistingContent) {
prompts = prompts.map((prompt) => {
if (!prompt.content || typeof prompt.content !== 'string') {
return prompt;
}
/**
* Fix a bug when the initial content is empty
* TODO: Remove this when the bug is fixed in BlockNote
*/
if (prompt.content === '[]') {
const lastBlockId = editor.document[editor.document.length - 1].id;
const hasAssistantPrompt = prompts.some(
(prompt) => prompt.content === PROMPT_INDICATORS.ASSISTANT_IDENTIFIER,
);
prompt.content = `[{\"id\":\"${lastBlockId}$\",\"block\":\"<p></p>\"}]`;
return prompt;
}
const hasSelectedBlocks = !!opts.selectedBlocks?.length;
// Transform prompts for new content creation (no selected blocks)
if (!hasSelectedBlocks) {
prompts = prompts.map((prompt) =>
transformPromptContent(prompt, systemPrompts, editor),
);
if (
prompt.content.includes(
"You're manipulating a text document using HTML blocks.",
)
) {
prompt = systemPrompts['add-markdown'];
return prompt;
}
if (
prompt.content.includes(
'First, determine what part of the document the user is talking about.',
)
) {
prompt = systemPrompts['add-edit-instruction'];
}
return prompt;
});
prompts.push(systemPrompts['add-formatting']);
// Add formatting prompt if no assistant prompt exists
if (!hasAssistantPrompt) {
prompts.push(systemPrompts['add-formatting']);
}
}
prompts.unshift(systemPrompts['assistant']);
prompts.push(systemPrompts['referenceId']);
// Try to keep the language of the document except when we are translating
if (!promptKey?.includes('Translate into')) {
prompts.push(systemPrompts['language']);
}
// Add system prompts if they don't exist
prompts = addSystemPrompts(
prompts,
systemPrompts,
hasAssistantPrompt,
promptKey,
);
return prompts;
},
[t],
[parseCustomPrompt, transformPromptContent, addSystemPrompts, t],
);
};