refactor(core): remove copilot client from presets (#6546)
depends on https://github.com/toeverything/blocksuite/pull/6748
This commit is contained in:
@@ -52,7 +52,9 @@ export class CopilotClient {
|
|||||||
async getHistories(
|
async getHistories(
|
||||||
workspaceId: string,
|
workspaceId: string,
|
||||||
docId?: string,
|
docId?: string,
|
||||||
options?: OptionsField<typeof getCopilotHistoriesQuery>
|
options?: RequestOptions<
|
||||||
|
typeof getCopilotHistoriesQuery
|
||||||
|
>['variables']['options']
|
||||||
) {
|
) {
|
||||||
const res = await fetcher({
|
const res = await fetcher({
|
||||||
query: getCopilotHistoriesQuery,
|
query: getCopilotHistoriesQuery,
|
||||||
@@ -66,20 +68,6 @@ export class CopilotClient {
|
|||||||
return res.currentUser?.copilot?.histories;
|
return res.currentUser?.copilot?.histories;
|
||||||
}
|
}
|
||||||
|
|
||||||
async textToText(message: string, sessionId: string) {
|
|
||||||
const res = await fetch(
|
|
||||||
`${this.backendUrl}/api/copilot/chat/${sessionId}?message=${encodeURIComponent(message)}`
|
|
||||||
);
|
|
||||||
if (!res.ok) return;
|
|
||||||
return res.text();
|
|
||||||
}
|
|
||||||
|
|
||||||
textToTextStream(message: string, sessionId: string) {
|
|
||||||
return new EventSource(
|
|
||||||
`${this.backendUrl}/api/copilot/chat/${sessionId}/stream?message=${encodeURIComponent(message)}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
chatText({
|
chatText({
|
||||||
sessionId,
|
sessionId,
|
||||||
messageId,
|
messageId,
|
||||||
|
|||||||
@@ -1,17 +1,39 @@
|
|||||||
|
import { assertExists } from '@blocksuite/global/utils';
|
||||||
import { AIProvider } from '@blocksuite/presets';
|
import { AIProvider } from '@blocksuite/presets';
|
||||||
|
|
||||||
import { textToText } from './request';
|
import { createChatSession, listHistories, textToText } from './request';
|
||||||
|
|
||||||
export function setupAIProvider() {
|
export function setupAIProvider() {
|
||||||
AIProvider.provideAction('chat', options => {
|
// a single workspace should have only a single chat session
|
||||||
|
// workspace-id:doc-id -> chat session id
|
||||||
|
const chatSessions = new Map<string, Promise<string>>();
|
||||||
|
|
||||||
|
async function getChatSessionId(workspaceId: string, docId: string) {
|
||||||
|
const storeKey = `${workspaceId}:${docId}`;
|
||||||
|
if (!chatSessions.has(storeKey)) {
|
||||||
|
chatSessions.set(
|
||||||
|
storeKey,
|
||||||
|
createChatSession({
|
||||||
|
workspaceId,
|
||||||
|
docId,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const sessionId = await chatSessions.get(storeKey);
|
||||||
|
assertExists(sessionId);
|
||||||
|
return sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
AIProvider.provide('chat', options => {
|
||||||
|
const sessionId = getChatSessionId(options.workspaceId, options.docId);
|
||||||
return textToText({
|
return textToText({
|
||||||
...options,
|
...options,
|
||||||
content: options.input,
|
content: options.input,
|
||||||
promptName: 'debug:chat:gpt4',
|
sessionId,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
AIProvider.provideAction('summary', options => {
|
AIProvider.provide('summary', options => {
|
||||||
return textToText({
|
return textToText({
|
||||||
...options,
|
...options,
|
||||||
content: options.input,
|
content: options.input,
|
||||||
@@ -19,7 +41,7 @@ export function setupAIProvider() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
AIProvider.provideAction('translate', options => {
|
AIProvider.provide('translate', options => {
|
||||||
return textToText({
|
return textToText({
|
||||||
...options,
|
...options,
|
||||||
promptName: 'Translate to',
|
promptName: 'Translate to',
|
||||||
@@ -30,7 +52,7 @@ export function setupAIProvider() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
AIProvider.provideAction('changeTone', options => {
|
AIProvider.provide('changeTone', options => {
|
||||||
return textToText({
|
return textToText({
|
||||||
...options,
|
...options,
|
||||||
content: options.input,
|
content: options.input,
|
||||||
@@ -38,7 +60,7 @@ export function setupAIProvider() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
AIProvider.provideAction('improveWriting', options => {
|
AIProvider.provide('improveWriting', options => {
|
||||||
return textToText({
|
return textToText({
|
||||||
...options,
|
...options,
|
||||||
content: options.input,
|
content: options.input,
|
||||||
@@ -46,7 +68,7 @@ export function setupAIProvider() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
AIProvider.provideAction('improveGrammar', options => {
|
AIProvider.provide('improveGrammar', options => {
|
||||||
return textToText({
|
return textToText({
|
||||||
...options,
|
...options,
|
||||||
content: options.input,
|
content: options.input,
|
||||||
@@ -54,7 +76,7 @@ export function setupAIProvider() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
AIProvider.provideAction('fixSpelling', options => {
|
AIProvider.provide('fixSpelling', options => {
|
||||||
return textToText({
|
return textToText({
|
||||||
...options,
|
...options,
|
||||||
content: options.input,
|
content: options.input,
|
||||||
@@ -62,7 +84,7 @@ export function setupAIProvider() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
AIProvider.provideAction('createHeadings', options => {
|
AIProvider.provide('createHeadings', options => {
|
||||||
return textToText({
|
return textToText({
|
||||||
...options,
|
...options,
|
||||||
content: options.input,
|
content: options.input,
|
||||||
@@ -70,7 +92,7 @@ export function setupAIProvider() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
AIProvider.provideAction('makeLonger', options => {
|
AIProvider.provide('makeLonger', options => {
|
||||||
return textToText({
|
return textToText({
|
||||||
...options,
|
...options,
|
||||||
content: options.input,
|
content: options.input,
|
||||||
@@ -78,7 +100,7 @@ export function setupAIProvider() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
AIProvider.provideAction('makeShorter', options => {
|
AIProvider.provide('makeShorter', options => {
|
||||||
return textToText({
|
return textToText({
|
||||||
...options,
|
...options,
|
||||||
content: options.input,
|
content: options.input,
|
||||||
@@ -86,7 +108,7 @@ export function setupAIProvider() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
AIProvider.provideAction('checkCodeErrors', options => {
|
AIProvider.provide('checkCodeErrors', options => {
|
||||||
return textToText({
|
return textToText({
|
||||||
...options,
|
...options,
|
||||||
content: options.input,
|
content: options.input,
|
||||||
@@ -94,7 +116,7 @@ export function setupAIProvider() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
AIProvider.provideAction('explainCode', options => {
|
AIProvider.provide('explainCode', options => {
|
||||||
return textToText({
|
return textToText({
|
||||||
...options,
|
...options,
|
||||||
content: options.input,
|
content: options.input,
|
||||||
@@ -102,7 +124,7 @@ export function setupAIProvider() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
AIProvider.provideAction('writeArticle', options => {
|
AIProvider.provide('writeArticle', options => {
|
||||||
return textToText({
|
return textToText({
|
||||||
...options,
|
...options,
|
||||||
content: options.input,
|
content: options.input,
|
||||||
@@ -110,7 +132,7 @@ export function setupAIProvider() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
AIProvider.provideAction('writeTwitterPost', options => {
|
AIProvider.provide('writeTwitterPost', options => {
|
||||||
return textToText({
|
return textToText({
|
||||||
...options,
|
...options,
|
||||||
content: options.input,
|
content: options.input,
|
||||||
@@ -118,7 +140,7 @@ export function setupAIProvider() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
AIProvider.provideAction('writePoem', options => {
|
AIProvider.provide('writePoem', options => {
|
||||||
return textToText({
|
return textToText({
|
||||||
...options,
|
...options,
|
||||||
content: options.input,
|
content: options.input,
|
||||||
@@ -126,7 +148,7 @@ export function setupAIProvider() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
AIProvider.provideAction('writeOutline', options => {
|
AIProvider.provide('writeOutline', options => {
|
||||||
return textToText({
|
return textToText({
|
||||||
...options,
|
...options,
|
||||||
content: options.input,
|
content: options.input,
|
||||||
@@ -134,7 +156,7 @@ export function setupAIProvider() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
AIProvider.provideAction('writeBlogPost', options => {
|
AIProvider.provide('writeBlogPost', options => {
|
||||||
return textToText({
|
return textToText({
|
||||||
...options,
|
...options,
|
||||||
content: options.input,
|
content: options.input,
|
||||||
@@ -142,7 +164,7 @@ export function setupAIProvider() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
AIProvider.provideAction('brainstorm', options => {
|
AIProvider.provide('brainstorm', options => {
|
||||||
return textToText({
|
return textToText({
|
||||||
...options,
|
...options,
|
||||||
content: options.input,
|
content: options.input,
|
||||||
@@ -150,7 +172,7 @@ export function setupAIProvider() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
AIProvider.provideAction('findActions', options => {
|
AIProvider.provide('findActions', options => {
|
||||||
return textToText({
|
return textToText({
|
||||||
...options,
|
...options,
|
||||||
content: options.input,
|
content: options.input,
|
||||||
@@ -158,7 +180,7 @@ export function setupAIProvider() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
AIProvider.provideAction('brainstormMindmap', options => {
|
AIProvider.provide('brainstormMindmap', options => {
|
||||||
return textToText({
|
return textToText({
|
||||||
...options,
|
...options,
|
||||||
content: options.input,
|
content: options.input,
|
||||||
@@ -166,7 +188,7 @@ export function setupAIProvider() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
AIProvider.provideAction('explain', options => {
|
AIProvider.provide('explain', options => {
|
||||||
return textToText({
|
return textToText({
|
||||||
...options,
|
...options,
|
||||||
content: options.input,
|
content: options.input,
|
||||||
@@ -174,7 +196,7 @@ export function setupAIProvider() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
AIProvider.provideAction('explainImage', options => {
|
AIProvider.provide('explainImage', options => {
|
||||||
return textToText({
|
return textToText({
|
||||||
...options,
|
...options,
|
||||||
content: options.input,
|
content: options.input,
|
||||||
@@ -182,7 +204,7 @@ export function setupAIProvider() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
AIProvider.provideAction('makeItReal', options => {
|
AIProvider.provide('makeItReal', options => {
|
||||||
return textToText({
|
return textToText({
|
||||||
...options,
|
...options,
|
||||||
promptName: 'Make it real',
|
promptName: 'Make it real',
|
||||||
@@ -192,4 +214,25 @@ export function setupAIProvider() {
|
|||||||
'Here are the latest wireframes. Could you make a new website based on these wireframes and notes and send back just the html file?',
|
'Here are the latest wireframes. Could you make a new website based on these wireframes and notes and send back just the html file?',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
AIProvider.provide('histories', {
|
||||||
|
actions: async (
|
||||||
|
workspaceId: string,
|
||||||
|
docId?: string
|
||||||
|
): Promise<BlockSuitePresets.AIHistory[]> => {
|
||||||
|
// @ts-expect-error - 'action' is missing in server impl
|
||||||
|
return (
|
||||||
|
(await listHistories(workspaceId, docId, {
|
||||||
|
action: true,
|
||||||
|
})) ?? []
|
||||||
|
);
|
||||||
|
},
|
||||||
|
chats: async (
|
||||||
|
workspaceId: string,
|
||||||
|
docId?: string
|
||||||
|
): Promise<BlockSuitePresets.AIHistory[]> => {
|
||||||
|
// @ts-expect-error - 'action' is missing in server impl
|
||||||
|
return (await listHistories(workspaceId, docId)) ?? [];
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ const TIMEOUT = 50000;
|
|||||||
|
|
||||||
const client = new CopilotClient();
|
const client = new CopilotClient();
|
||||||
|
|
||||||
function readBlobAsURL(blob: Blob) {
|
function readBlobAsURL(blob: Blob | File) {
|
||||||
return new Promise<string>((resolve, reject) => {
|
return new Promise<string>((resolve, reject) => {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.onload = e => {
|
reader.onload = e => {
|
||||||
@@ -25,28 +25,48 @@ function readBlobAsURL(blob: Blob) {
|
|||||||
export type TextToTextOptions = {
|
export type TextToTextOptions = {
|
||||||
docId: string;
|
docId: string;
|
||||||
workspaceId: string;
|
workspaceId: string;
|
||||||
promptName: PromptKey;
|
promptName?: PromptKey;
|
||||||
|
sessionId?: string | Promise<string>;
|
||||||
content?: string;
|
content?: string;
|
||||||
attachments?: (string | Blob)[];
|
attachments?: (string | Blob | File)[];
|
||||||
params?: Record<string, string>;
|
params?: Record<string, string>;
|
||||||
timeout?: number;
|
timeout?: number;
|
||||||
stream?: boolean;
|
stream?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function createChatSession({
|
||||||
|
workspaceId,
|
||||||
|
docId,
|
||||||
|
}: {
|
||||||
|
workspaceId: string;
|
||||||
|
docId: string;
|
||||||
|
}) {
|
||||||
|
return client.createSession({
|
||||||
|
workspaceId,
|
||||||
|
docId,
|
||||||
|
promptName: 'debug:chat:gpt4',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async function createSessionMessage({
|
async function createSessionMessage({
|
||||||
docId,
|
docId,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
promptName,
|
promptName,
|
||||||
content,
|
content,
|
||||||
|
sessionId: providedSessionId,
|
||||||
attachments,
|
attachments,
|
||||||
params,
|
params,
|
||||||
}: TextToTextOptions) {
|
}: TextToTextOptions) {
|
||||||
const hasAttachments = attachments && attachments.length > 0;
|
const hasAttachments = attachments && attachments.length > 0;
|
||||||
const session = await client.createSession({
|
if (!promptName && !providedSessionId) {
|
||||||
workspaceId,
|
throw new Error('promptName or sessionId is required');
|
||||||
docId,
|
}
|
||||||
promptName,
|
const sessionId = await (providedSessionId ??
|
||||||
});
|
client.createSession({
|
||||||
|
workspaceId,
|
||||||
|
docId,
|
||||||
|
promptName: promptName as string,
|
||||||
|
}));
|
||||||
if (hasAttachments) {
|
if (hasAttachments) {
|
||||||
const normalizedAttachments = await Promise.all(
|
const normalizedAttachments = await Promise.all(
|
||||||
attachments.map(async attachment => {
|
attachments.map(async attachment => {
|
||||||
@@ -58,19 +78,19 @@ async function createSessionMessage({
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
const messageId = await client.createMessage({
|
const messageId = await client.createMessage({
|
||||||
sessionId: session,
|
sessionId: sessionId,
|
||||||
content,
|
content,
|
||||||
attachments: normalizedAttachments,
|
attachments: normalizedAttachments,
|
||||||
params,
|
params,
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
messageId,
|
messageId,
|
||||||
session,
|
sessionId,
|
||||||
};
|
};
|
||||||
} else if (content) {
|
} else if (content) {
|
||||||
return {
|
return {
|
||||||
message: content,
|
message: content,
|
||||||
session,
|
sessionId,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
throw new Error('No content or attachments provided');
|
throw new Error('No content or attachments provided');
|
||||||
@@ -84,6 +104,7 @@ export function textToText({
|
|||||||
content,
|
content,
|
||||||
attachments,
|
attachments,
|
||||||
params,
|
params,
|
||||||
|
sessionId,
|
||||||
stream,
|
stream,
|
||||||
timeout = TIMEOUT,
|
timeout = TIMEOUT,
|
||||||
}: TextToTextOptions) {
|
}: TextToTextOptions) {
|
||||||
@@ -97,10 +118,11 @@ export function textToText({
|
|||||||
content,
|
content,
|
||||||
attachments,
|
attachments,
|
||||||
params,
|
params,
|
||||||
|
sessionId,
|
||||||
});
|
});
|
||||||
|
|
||||||
const eventSource = client.chatTextStream({
|
const eventSource = client.chatTextStream({
|
||||||
sessionId: message.session,
|
sessionId: message.sessionId,
|
||||||
messageId: message.messageId,
|
messageId: message.messageId,
|
||||||
message: message.message,
|
message: message.message,
|
||||||
});
|
});
|
||||||
@@ -123,9 +145,10 @@ export function textToText({
|
|||||||
content,
|
content,
|
||||||
attachments,
|
attachments,
|
||||||
params,
|
params,
|
||||||
}).then(message => {
|
sessionId,
|
||||||
return client.chatText({
|
}).then(async message => {
|
||||||
sessionId: message.session,
|
return await client.chatText({
|
||||||
|
sessionId: message.sessionId,
|
||||||
messageId: message.messageId,
|
messageId: message.messageId,
|
||||||
message: message.message,
|
message: message.message,
|
||||||
});
|
});
|
||||||
@@ -133,3 +156,5 @@ export function textToText({
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const listHistories = client.getHistories;
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ export function useCurrentUser(): CheckedUser {
|
|||||||
const user = session.user;
|
const user = session.user;
|
||||||
dispatcher({ type: 'update', payload: user });
|
dispatcher({ type: 'update', payload: user });
|
||||||
// todo: move this to a better place!
|
// todo: move this to a better place!
|
||||||
AIProvider.provideUserInfo(() => {
|
AIProvider.provide('userInfo', () => {
|
||||||
return user;
|
return user;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -76,9 +76,9 @@ test('create multi workspace in the workspace list', async ({
|
|||||||
await page.waitForTimeout(1000);
|
await page.waitForTimeout(1000);
|
||||||
|
|
||||||
{
|
{
|
||||||
//check workspace list length
|
// check workspace list length
|
||||||
const workspaceCards = await page.$$('data-testid=workspace-card');
|
const workspaceCards = page.getByTestId('workspace-card');
|
||||||
expect(workspaceCards.length).toBe(3);
|
await expect(workspaceCards).toHaveCount(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
await page.reload();
|
await page.reload();
|
||||||
@@ -118,7 +118,20 @@ test('create multi workspace in the workspace list', async ({
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
await page.mouse.up();
|
await page.mouse.up();
|
||||||
await page.waitForTimeout(1000);
|
|
||||||
|
// check workspace list order
|
||||||
|
await page.waitForFunction(
|
||||||
|
() => {
|
||||||
|
const cards = document.querySelectorAll('[data-testid="workspace-card"]');
|
||||||
|
return (
|
||||||
|
cards[1].textContent?.includes('New Workspace 3') &&
|
||||||
|
cards[2].textContent?.includes('New Workspace 2')
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
{ timeout: 5000 }
|
||||||
|
);
|
||||||
|
|
||||||
await page.reload();
|
await page.reload();
|
||||||
await openWorkspaceListModal(page);
|
await openWorkspaceListModal(page);
|
||||||
|
|
||||||
@@ -127,7 +140,7 @@ test('create multi workspace in the workspace list', async ({
|
|||||||
{
|
{
|
||||||
await page.waitForTimeout(1000);
|
await page.waitForTimeout(1000);
|
||||||
const workspaceCards = page.getByTestId('workspace-card');
|
const workspaceCards = page.getByTestId('workspace-card');
|
||||||
expect(await workspaceCards.count()).toBe(3);
|
await expect(workspaceCards).toHaveCount(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
const workspaceChangePromise = page.evaluate(() => {
|
const workspaceChangePromise = page.evaluate(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user