mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-09-23 20:18:42 +08:00
fix(core): can not clear chat-panel history (#10634)
Close [BS-2754](https://linear.app/affine-design/issue/BS-2754). ### What Changed? Use the latest session id and display the corresponding historical messages.
This commit is contained in:
@@ -2,6 +2,7 @@ import type {
|
|||||||
ChatHistoryOrder,
|
ChatHistoryOrder,
|
||||||
CopilotContextDoc,
|
CopilotContextDoc,
|
||||||
CopilotContextFile,
|
CopilotContextFile,
|
||||||
|
CopilotSessionType,
|
||||||
getCopilotHistoriesQuery,
|
getCopilotHistoriesQuery,
|
||||||
RequestOptions,
|
RequestOptions,
|
||||||
} from '@affine/graphql';
|
} from '@affine/graphql';
|
||||||
@@ -305,7 +306,7 @@ declare global {
|
|||||||
workspaceId: string,
|
workspaceId: string,
|
||||||
docId?: string,
|
docId?: string,
|
||||||
options?: { action?: boolean }
|
options?: { action?: boolean }
|
||||||
) => Promise<{ id: string; promptName: string }[] | undefined>;
|
) => Promise<CopilotSessionType[] | undefined>;
|
||||||
updateSession: (sessionId: string, promptName: string) => Promise<string>;
|
updateSession: (sessionId: string, promptName: string) => Promise<string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ import type {
|
|||||||
DocSearchMenuConfig,
|
DocSearchMenuConfig,
|
||||||
} from './chat-config';
|
} from './chat-config';
|
||||||
import type {
|
import type {
|
||||||
ChatAction,
|
|
||||||
ChatContextValue,
|
ChatContextValue,
|
||||||
ChatItem,
|
ChatItem,
|
||||||
DocChip,
|
DocChip,
|
||||||
@@ -150,9 +149,10 @@ export class ChatPanel extends SignalWatcher(
|
|||||||
|
|
||||||
const items: ChatItem[] = actions ? [...actions] : [];
|
const items: ChatItem[] = actions ? [...actions] : [];
|
||||||
|
|
||||||
if (histories?.at(-1)) {
|
const history = histories?.find(
|
||||||
const history = histories.at(-1);
|
history => history.sessionId === this._chatSessionId
|
||||||
if (!history) return;
|
);
|
||||||
|
if (history) {
|
||||||
items.push(...history.messages);
|
items.push(...history.messages);
|
||||||
AIProvider.LAST_ROOT_SESSION_ID = history.sessionId;
|
AIProvider.LAST_ROOT_SESSION_ID = history.sessionId;
|
||||||
}
|
}
|
||||||
@@ -286,13 +286,12 @@ export class ChatPanel extends SignalWatcher(
|
|||||||
cancelText: 'Cancel',
|
cancelText: 'Cancel',
|
||||||
})
|
})
|
||||||
) {
|
) {
|
||||||
|
const actionIds = this.chatContextValue.items
|
||||||
|
.filter(item => 'sessionId' in item)
|
||||||
|
.map(item => item.sessionId);
|
||||||
await AIProvider.histories?.cleanup(this.doc.workspace.id, this.doc.id, [
|
await AIProvider.histories?.cleanup(this.doc.workspace.id, this.doc.id, [
|
||||||
this._chatSessionId ?? '',
|
...(this._chatSessionId ? [this._chatSessionId] : []),
|
||||||
...(
|
...(actionIds || []),
|
||||||
this.chatContextValue.items.filter(
|
|
||||||
item => 'sessionId' in item
|
|
||||||
) as ChatAction[]
|
|
||||||
).map(item => item.sessionId),
|
|
||||||
]);
|
]);
|
||||||
notification.toast('History cleared');
|
notification.toast('History cleared');
|
||||||
await this._updateHistory();
|
await this._updateHistory();
|
||||||
@@ -308,12 +307,16 @@ export class ChatPanel extends SignalWatcher(
|
|||||||
if (!userId) return;
|
if (!userId) return;
|
||||||
|
|
||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
const sessions = await AIProvider.session?.getSessions(
|
const sessions = (
|
||||||
this.doc.workspace.id,
|
(await AIProvider.session?.getSessions(
|
||||||
this.doc.id
|
this.doc.workspace.id,
|
||||||
);
|
this.doc.id,
|
||||||
if (sessions?.length) {
|
{ action: false }
|
||||||
this._chatSessionId = sessions?.[0].id;
|
)) || []
|
||||||
|
).filter(session => !session.parentSessionId);
|
||||||
|
|
||||||
|
if (sessions && sessions.length) {
|
||||||
|
this._chatSessionId = sessions.at(-1)?.id;
|
||||||
await this._updateHistory();
|
await this._updateHistory();
|
||||||
}
|
}
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
|
|||||||
@@ -103,6 +103,19 @@ const clearChat = async (page: Page) => {
|
|||||||
await page.waitForTimeout(500);
|
await page.waitForTimeout(500);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const collectHistory = async (page: Page) => {
|
||||||
|
const chatPanel = await page.waitForSelector('.chat-panel-messages');
|
||||||
|
return Promise.all(
|
||||||
|
Array.from(await chatPanel.$$('.message')).map(async m => ({
|
||||||
|
name: await m.$('.user-info').then(i => i?.innerText()),
|
||||||
|
content: await m
|
||||||
|
.$('chat-text')
|
||||||
|
.then(t => t?.$('editor-host'))
|
||||||
|
.then(e => e?.innerText()),
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const collectChat = async (page: Page) => {
|
const collectChat = async (page: Page) => {
|
||||||
await page.waitForTimeout(ONE_SECOND);
|
await page.waitForTimeout(ONE_SECOND);
|
||||||
const chatPanel = await page.waitForSelector('.chat-panel-messages');
|
const chatPanel = await page.waitForSelector('.chat-panel-messages');
|
||||||
@@ -117,15 +130,7 @@ const collectChat = async (page: Page) => {
|
|||||||
const lastMessage = await chatPanel.$$('.message').then(m => m[m.length - 1]);
|
const lastMessage = await chatPanel.$$('.message').then(m => m[m.length - 1]);
|
||||||
await lastMessage.waitForSelector('chat-copy-more');
|
await lastMessage.waitForSelector('chat-copy-more');
|
||||||
await page.waitForTimeout(200);
|
await page.waitForTimeout(200);
|
||||||
return Promise.all(
|
return collectHistory(page);
|
||||||
Array.from(await chatPanel.$$('.message')).map(async m => ({
|
|
||||||
name: await m.$('.user-info').then(i => i?.innerText()),
|
|
||||||
content: await m
|
|
||||||
.$('chat-text')
|
|
||||||
.then(t => t?.$('editor-host'))
|
|
||||||
.then(e => e?.innerText()),
|
|
||||||
}))
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const focusToEditor = async (page: Page) => {
|
const focusToEditor = async (page: Page) => {
|
||||||
@@ -372,6 +377,18 @@ test.describe('chat panel', () => {
|
|||||||
).toStrictEqual(contents);
|
).toStrictEqual(contents);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('can save chat to block and clear history', async ({ page }) => {
|
||||||
|
await collectChat(page);
|
||||||
|
expect(await getPageMode(page)).toBe('page');
|
||||||
|
await page.getByTestId('action-save-chat-to-block').click();
|
||||||
|
await page.waitForSelector('affine-edgeless-ai-chat');
|
||||||
|
|
||||||
|
await page.reload();
|
||||||
|
await page.waitForTimeout(200);
|
||||||
|
await clearChat(page);
|
||||||
|
expect((await collectChat(page)).length).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
test('chat in center peek', async ({ page }) => {
|
test('chat in center peek', async ({ page }) => {
|
||||||
const contents = (await collectChat(page)).map(m => m.content);
|
const contents = (await collectChat(page)).map(m => m.content);
|
||||||
await page.getByTestId('action-save-chat-to-block').click();
|
await page.getByTestId('action-save-chat-to-block').click();
|
||||||
@@ -678,6 +695,7 @@ test.describe('chat with block', () => {
|
|||||||
await createLocalWorkspace({ name: 'test' }, page);
|
await createLocalWorkspace({ name: 'test' }, page);
|
||||||
await clickNewPageButton(page);
|
await clickNewPageButton(page);
|
||||||
await pasteTextToPageEditor(page, 'Mac Mini');
|
await pasteTextToPageEditor(page, 'Mac Mini');
|
||||||
|
await openChat(page);
|
||||||
});
|
});
|
||||||
|
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
@@ -750,6 +768,19 @@ test.describe('chat with block', () => {
|
|||||||
} else {
|
} else {
|
||||||
expect(await collectTextAnswer(page)).toBeTruthy();
|
expect(await collectTextAnswer(page)).toBeTruthy();
|
||||||
}
|
}
|
||||||
|
// TODO some actions do not have history yet
|
||||||
|
if (
|
||||||
|
option !== 'Generate presentation' &&
|
||||||
|
option !== 'Brainstorm ideas with mind map'
|
||||||
|
) {
|
||||||
|
const history = await collectHistory(page);
|
||||||
|
expect(history.length).toBe(1);
|
||||||
|
expect(history[0].name).toBe('AFFiNE AI');
|
||||||
|
const discard = await page.waitForSelector('.ai-item-discard');
|
||||||
|
await discard.click();
|
||||||
|
await clearChat(page);
|
||||||
|
expect((await collectHistory(page)).length).toBe(0);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -887,6 +918,43 @@ test.describe('chat with block', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('clear history', async ({ page }) => {
|
||||||
|
await page.reload();
|
||||||
|
await clickSideBarAllPageButton(page);
|
||||||
|
await page.waitForTimeout(200);
|
||||||
|
await createLocalWorkspace({ name: 'test' }, page);
|
||||||
|
await clickNewPageButton(page);
|
||||||
|
await focusToEditor(page);
|
||||||
|
await page.keyboard.type('Mac Mini');
|
||||||
|
await openChat(page);
|
||||||
|
|
||||||
|
await makeChat(page, 'hello');
|
||||||
|
await collectHistory(page);
|
||||||
|
|
||||||
|
await page.waitForSelector('affine-paragraph').then(i => i.click());
|
||||||
|
await page.keyboard.press('ControlOrMeta+A');
|
||||||
|
await page
|
||||||
|
.waitForSelector('page-editor editor-toolbar ask-ai-icon', {
|
||||||
|
state: 'attached',
|
||||||
|
timeout: 10000,
|
||||||
|
})
|
||||||
|
.then(b => b.click());
|
||||||
|
await disableEditorBlank(page);
|
||||||
|
await page
|
||||||
|
.waitForSelector(
|
||||||
|
`.ai-item-${`Fix spelling`.replaceAll(' ', '-').toLowerCase()}`
|
||||||
|
)
|
||||||
|
.then(i => i.click());
|
||||||
|
await collectTextAnswer(page);
|
||||||
|
|
||||||
|
await page.reload();
|
||||||
|
await page.waitForTimeout(1000);
|
||||||
|
const history = await collectHistory(page);
|
||||||
|
expect(history.length).toBe(3);
|
||||||
|
await clearChat(page);
|
||||||
|
expect((await collectHistory(page)).length).toBe(0);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test.describe('chat with doc', () => {
|
test.describe('chat with doc', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user