Skip to content

Commit c75ec77

Browse files
committed
save1
1 parent da63ef1 commit c75ec77

File tree

4 files changed

+190
-119
lines changed

4 files changed

+190
-119
lines changed

src/frontend/apps/impress/src/features/docs/doc-editor/components/AI/libAGPL.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ export {
22
AIMenu as AIMenuDefault,
33
AIMenuController,
44
createAIExtension,
5-
createBlockNoteAIClient,
65
getAIExtension,
76
getAISlashMenuItems,
87
getDefaultAIMenuItems,
Lines changed: 14 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,28 @@
11
import { createOpenAI } from '@ai-sdk/openai';
2-
import { CoreMessage } from 'ai';
32
import { useMemo } from 'react';
43

54
import { fetchAPI } from '@/api';
65
import { useConfig } from '@/core';
76
import { Doc } from '@/docs/doc-management';
87

98
import { useModuleAI } from './useModuleAI';
9+
import { usePromptAI } from './usePromptAI';
1010

11-
const systemPrompts: Record<
12-
'add-assistant' | 'add-formatting' | 'add-edit-instruction',
13-
CoreMessage
14-
> = {
15-
'add-assistant': {
16-
role: 'system',
17-
content:
18-
'You are an AI assistant that helps users to edit their documents. Answer the user prompt in markdown format.',
19-
},
20-
'add-formatting': {
21-
role: 'system',
22-
content: `Add formatting to the text to make it more readable.`,
23-
},
24-
'add-edit-instruction': {
25-
role: 'system',
26-
content: `Keep adding to the document, do not delete or modify existing blocks.`,
27-
},
28-
};
29-
30-
const userPrompts: Record<string, string> = {
31-
'continue writing':
32-
'Keep writing about the content send in the prompt, expanding on the ideas.',
33-
'improve writing':
34-
'Improve the writing of the selected text. Make it more professional and clear.',
35-
summarize:
36-
'Summarize the selected text into a concise paragraph. Add a small summarize title above the text.',
37-
};
38-
39-
/**
40-
* Custom implementation of the PromptBuilder that allows for using predefined prompts.
41-
*
42-
* This extends the default HTML promptBuilder from BlockNote to support custom prompt templates.
43-
* Custom prompts can be invoked using the pattern !promptName in the AI input field.
44-
*/
4511
export const useAI = (doc: Doc) => {
4612
const conf = useConfig().data;
4713
const modules = useModuleAI();
4814
const aiAllowed = !!(conf?.AI_FEATURE_ENABLED && doc.abilities?.ai_proxy);
15+
const promptBuilder = usePromptAI();
4916

5017
return useMemo(() => {
5118
if (!aiAllowed || !modules || !conf?.AI_MODEL) {
5219
return;
5320
}
5421

55-
const { llmFormats, createAIExtension, createBlockNoteAIClient } = modules;
56-
57-
const clientAI = createBlockNoteAIClient({
58-
baseURL: ``,
59-
apiKey: '',
60-
});
22+
const { createAIExtension, llmFormats } = modules;
6123

6224
const openai = createOpenAI({
63-
...clientAI.getProviderSettings('openai'),
25+
apiKey: '', // The API key will be set by the AI proxy
6426
fetch: (input, init) => {
6527
// Create a new headers object without the Authorization header
6628
const headers = new Headers(init?.headers);
@@ -78,83 +40,17 @@ export const useAI = (doc: Doc) => {
7840
stream: conf.AI_STREAM,
7941
model,
8042
agentCursor: conf.AI_BOT,
81-
// Create a custom promptBuilder that extends the default one
82-
promptBuilder: async (editor, opts): Promise<Array<CoreMessage>> => {
83-
const defaultPromptBuilder = llmFormats.html.defaultPromptBuilder;
84-
const isTransform = !!opts.selectedBlocks?.length;
85-
86-
// Try to catch the action
87-
const customPromptMatch = opts.userPrompt.match(/^([^:]+)(?=[:]|$)/);
88-
89-
let modifiedOpts = opts;
90-
if (customPromptMatch?.length) {
91-
const promptKey = customPromptMatch[0].trim().toLowerCase();
92-
93-
if (userPrompts[promptKey]) {
94-
modifiedOpts = {
95-
...opts,
96-
userPrompt: userPrompts[promptKey],
97-
};
98-
}
99-
}
100-
101-
let prompts = await defaultPromptBuilder(editor, modifiedOpts);
102-
103-
if (!isTransform) {
104-
prompts = prompts.map((prompt) => {
105-
if (!prompt.content || typeof prompt.content !== 'string') {
106-
return prompt;
107-
}
108-
109-
/**
110-
* Fix a bug when the initial content is empty
111-
* TODO: Remove this when the bug is fixed in BlockNote
112-
*/
113-
if (prompt.content === '[]') {
114-
editor.insertBlocks(
115-
[
116-
{
117-
id: 'base-block',
118-
content: ' ',
119-
type: 'paragraph',
120-
},
121-
],
122-
'initialBlockId',
123-
'after',
124-
);
125-
126-
prompt.content =
127-
'[{\"id\":\"base-block$\",\"block\":\"<p></p>\"}]';
128-
return prompt;
129-
}
130-
131-
if (
132-
prompt.content.includes(
133-
"You're manipulating a text document using HTML blocks.",
134-
)
135-
) {
136-
prompt = systemPrompts['add-assistant'];
137-
return prompt;
138-
}
139-
140-
if (
141-
prompt.content.includes(
142-
'First, determine what part of the document the user is talking about.',
143-
)
144-
) {
145-
prompt = systemPrompts['add-edit-instruction'];
146-
}
147-
148-
return prompt;
149-
});
150-
151-
prompts.push(systemPrompts['add-formatting']);
152-
}
153-
154-
return prompts;
155-
},
43+
promptBuilder: promptBuilder(llmFormats.html.defaultPromptBuilder),
15644
});
15745

15846
return extension;
159-
}, [aiAllowed, conf, doc.id, modules]);
47+
}, [
48+
aiAllowed,
49+
conf?.AI_BOT,
50+
conf?.AI_MODEL,
51+
conf?.AI_STREAM,
52+
doc.id,
53+
modules,
54+
promptBuilder,
55+
]);
16056
};
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
import { Block } from '@blocknote/core';
2+
import { CoreMessage } from 'ai';
3+
import { useCallback } from 'react';
4+
import { useTranslation } from 'react-i18next';
5+
6+
import {
7+
DocsBlockNoteEditor,
8+
DocsBlockSchema,
9+
DocsInlineContentSchema,
10+
DocsStyleSchema,
11+
} from '../../types';
12+
13+
export type PromptBuilderInput = {
14+
userPrompt: string;
15+
selectedBlocks?: Block<
16+
DocsBlockSchema,
17+
DocsInlineContentSchema,
18+
DocsStyleSchema
19+
>[];
20+
excludeBlockIds?: string[];
21+
previousMessages?: Array<CoreMessage>;
22+
};
23+
24+
type PromptBuilder = (
25+
editor: DocsBlockNoteEditor,
26+
opts: PromptBuilderInput,
27+
) => Promise<Array<CoreMessage>>;
28+
29+
/**
30+
* Custom implementation of the PromptBuilder that allows for using predefined prompts.
31+
*
32+
* This extends the default HTML promptBuilder from BlockNote to support custom prompt templates.
33+
* Custom prompts can be invoked using the pattern !promptName in the AI input field.
34+
*/
35+
export const usePromptAI = () => {
36+
const { t } = useTranslation();
37+
38+
return useCallback(
39+
(defaultPromptBuilder: PromptBuilder) =>
40+
async (
41+
editor: DocsBlockNoteEditor,
42+
opts: PromptBuilderInput,
43+
): Promise<Array<CoreMessage>> => {
44+
const systemPrompts: Record<
45+
| 'add-edit-instruction'
46+
| 'add-formatting'
47+
| 'add-markdown'
48+
| 'assistant'
49+
| 'language'
50+
| 'referenceId',
51+
CoreMessage
52+
> = {
53+
assistant: {
54+
role: 'system',
55+
content: t(`You are an AI assistant that edits user documents.`),
56+
},
57+
referenceId: {
58+
role: 'system',
59+
content: t(
60+
`Keep block IDs exactly as provided when referencing them (including the trailing "$").`,
61+
),
62+
},
63+
'add-markdown': {
64+
role: 'system',
65+
content: t(`Answer the user prompt in markdown format.`),
66+
},
67+
'add-formatting': {
68+
role: 'system',
69+
content: t(`Add formatting to the text to make it more readable.`),
70+
},
71+
'add-edit-instruction': {
72+
role: 'system',
73+
content: t(
74+
`Add content; do not delete or alter existing blocks unless explicitly told.`,
75+
),
76+
},
77+
language: {
78+
role: 'system',
79+
content: t(
80+
`Detect the dominant language inside the provided blocks. YOU MUST PROVIDE A ANSWER IN THE DETECTED LANGUAGE.`,
81+
),
82+
},
83+
};
84+
85+
const userPrompts: Record<string, string> = {
86+
'continue writing': t(
87+
'Keep writing about the content send in the prompt, expanding on the ideas.',
88+
),
89+
'improve writing': t(
90+
'Improve the writing of the selected text. Make it more professional and clear.',
91+
),
92+
summarize: t('Summarize the document into a concise paragraph.'),
93+
'fix spelling': t(
94+
'Fix the spelling and grammar mistakes in the selected text.',
95+
),
96+
};
97+
98+
// Modify userPrompt if it matches a custom prompt
99+
const customPromptMatch = opts.userPrompt.match(/^([^:]+)(?=[:]|$)/);
100+
let modifiedOpts = opts;
101+
const promptKey = customPromptMatch?.[0].trim().toLowerCase();
102+
if (promptKey) {
103+
if (userPrompts[promptKey]) {
104+
modifiedOpts = {
105+
...opts,
106+
userPrompt: userPrompts[promptKey],
107+
};
108+
}
109+
}
110+
111+
let prompts = await defaultPromptBuilder(editor, modifiedOpts);
112+
const isTransformExistingContent = !!opts.selectedBlocks?.length;
113+
if (!isTransformExistingContent) {
114+
prompts = prompts.map((prompt) => {
115+
if (!prompt.content || typeof prompt.content !== 'string') {
116+
return prompt;
117+
}
118+
119+
/**
120+
* Fix a bug when the initial content is empty
121+
* TODO: Remove this when the bug is fixed in BlockNote
122+
*/
123+
if (prompt.content === '[]') {
124+
const lastBlockId =
125+
editor.document[editor.document.length - 1].id;
126+
127+
prompt.content = `[{\"id\":\"${lastBlockId}$\",\"block\":\"<p></p>\"}]`;
128+
return prompt;
129+
}
130+
131+
if (
132+
prompt.content.includes(
133+
"You're manipulating a text document using HTML blocks.",
134+
)
135+
) {
136+
prompt = systemPrompts['add-markdown'];
137+
return prompt;
138+
}
139+
140+
if (
141+
prompt.content.includes(
142+
'First, determine what part of the document the user is talking about.',
143+
)
144+
) {
145+
prompt = systemPrompts['add-edit-instruction'];
146+
}
147+
148+
return prompt;
149+
});
150+
151+
prompts.push(systemPrompts['add-formatting']);
152+
}
153+
154+
prompts.unshift(systemPrompts['assistant']);
155+
prompts.push(systemPrompts['referenceId']);
156+
157+
// Try to keep the language of the document except when we are translating
158+
if (!promptKey?.includes('Translate into')) {
159+
prompts.push(systemPrompts['language']);
160+
}
161+
162+
return prompts;
163+
},
164+
[t],
165+
);
166+
};

src/frontend/apps/impress/src/i18n/translations.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,16 @@
590590
"Warning": "Attention",
591591
"Why can't I edit?": "Pourquoi ne puis-je pas éditer ?",
592592
"Write": "Écrire",
593+
"You are an AI assistant that helps users to edit their documents.": "Vous êtes un assistant IA qui aide les utilisateurs à éditer leurs documents.",
594+
"Answer the user prompt in markdown format.": "Répondez à la demande de l'utilisateur au format markdown.",
595+
"Add formatting to the text to make it more readable.": "Ajoutez du formatage au texte pour le rendre plus lisible.",
596+
"Keep adding to the document, do not delete or modify existing blocks.": "Continuez à ajouter au document, ne supprimez ni ne modifiez les blocs existants.",
597+
"Your answer must be in the same language as the document.": "Votre réponse doit être dans la même langue que le document.",
598+
"Fix the spelling and grammar mistakes in the selected text.": "Corrigez les fautes d'orthographe et de grammaire dans le texte sélectionné.",
599+
"Improve the writing of the selected text. Make it more professional and clear.": "Améliorez l'écriture du texte sélectionné. Rendez-le plus professionnel et clair.",
600+
"Summarize the document into a concise paragraph.": "Résumez le document en un paragraphe concis.",
601+
"Keep writing about the content send in the prompt, expanding on the ideas.": "Continuez à écrire sur le contenu envoyé dans la demande, en développant les idées.",
602+
"Important, verified the language of the document! Your answer MUST be in the same language as the document. If the document is in English, your answer MUST be in English. If the document is in Spanish, your answer MUST be in Spanish, etc.": "Important, vérifiez la langue du document ! Votre réponse DOIT être dans la même langue que le document. Si le document est en anglais, votre réponse DOIT être en anglais. Si le document est en espagnol, votre réponse DOIT être en espagnol, etc.",
593603
"You are the sole owner of this group, make another member the group owner before you can change your own role or be removed from your document.": "Vous êtes le seul propriétaire de ce groupe, faites d'un autre membre le propriétaire du groupe, avant de pouvoir modifier votre propre rôle ou vous supprimer du document.",
594604
"You do not have permission to view this document.": "Vous n'avez pas la permission de voir ce document.",
595605
"You do not have permission to view users sharing this document or modify link settings.": "Vous n'avez pas la permission de voir les utilisateurs partageant ce document ou de modifier les paramètres du lien.",

0 commit comments

Comments
 (0)