mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-09-22 19:53:48 +08:00
fix: support chat in different doc (#7693)
fix: [BS-990](https://linear.app/affine-design/issue/BS-990/避免centerpeek中发起的会话,等待ai返回时,页面失去响应) [BS-1005](https://linear.app/affine-design/issue/BS-1005/chat-block无法被copy-paste到别的文档中,duplicate全篇文档后,可以center-peek)
This commit is contained in:
@@ -48,16 +48,16 @@ export type ChatAction = {
|
|||||||
) => Promise<boolean>;
|
) => Promise<boolean>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function queryHistoryMessages(doc: Doc, forkSessionId: string) {
|
export async function queryHistoryMessages(
|
||||||
|
workspaceId: string,
|
||||||
|
docId: string,
|
||||||
|
forkSessionId: string
|
||||||
|
) {
|
||||||
// Get fork session messages
|
// Get fork session messages
|
||||||
const histories = await AIProvider.histories?.chats(
|
const histories = await AIProvider.histories?.chats(workspaceId, docId, {
|
||||||
doc.collection.id,
|
sessionId: forkSessionId,
|
||||||
doc.id,
|
messageOrder: ChatHistoryOrder.asc,
|
||||||
{
|
});
|
||||||
sessionId: forkSessionId,
|
|
||||||
messageOrder: ChatHistoryOrder.asc,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!histories || !histories.length) {
|
if (!histories || !histories.length) {
|
||||||
return [];
|
return [];
|
||||||
@@ -98,7 +98,11 @@ export async function constructRootChatBlockMessages(
|
|||||||
) {
|
) {
|
||||||
// Convert chat messages to AI chat block messages
|
// Convert chat messages to AI chat block messages
|
||||||
const userInfo = await AIProvider.userInfo;
|
const userInfo = await AIProvider.userInfo;
|
||||||
const forkMessages = await queryHistoryMessages(doc, forkSessionId);
|
const forkMessages = await queryHistoryMessages(
|
||||||
|
doc.collection.id,
|
||||||
|
doc.id,
|
||||||
|
forkSessionId
|
||||||
|
);
|
||||||
return constructUserInfoWithMessages(forkMessages, userInfo);
|
return constructUserInfoWithMessages(forkMessages, userInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,6 +167,8 @@ function addAIChatBlock(
|
|||||||
messages: JSON.stringify(messages),
|
messages: JSON.stringify(messages),
|
||||||
index: layer.generateIndex('affine:embed-ai-chat'),
|
index: layer.generateIndex('affine:embed-ai-chat'),
|
||||||
sessionId,
|
sessionId,
|
||||||
|
rootWorkspaceId: doc.collection.id,
|
||||||
|
rootDocId: doc.id,
|
||||||
},
|
},
|
||||||
surfaceBlock.id
|
surfaceBlock.id
|
||||||
);
|
);
|
||||||
|
|||||||
+87
-56
@@ -15,7 +15,6 @@ import {
|
|||||||
type ChatMessage,
|
type ChatMessage,
|
||||||
ChatMessagesSchema,
|
ChatMessagesSchema,
|
||||||
} from '@blocksuite/presets';
|
} from '@blocksuite/presets';
|
||||||
import type { Doc } from '@blocksuite/store';
|
|
||||||
import { html, LitElement, nothing } from 'lit';
|
import { html, LitElement, nothing } from 'lit';
|
||||||
import { customElement, property, query, state } from 'lit/decorators.js';
|
import { customElement, property, query, state } from 'lit/decorators.js';
|
||||||
import { classMap } from 'lit/directives/class-map.js';
|
import { classMap } from 'lit/directives/class-map.js';
|
||||||
@@ -57,6 +56,14 @@ export class AIChatBlockPeekView extends LitElement {
|
|||||||
return this.parentModel.id;
|
return this.parentModel.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private get parentRootDocId() {
|
||||||
|
return this.parentModel.rootDocId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private get parentRootWorkspaceId() {
|
||||||
|
return this.parentModel.rootWorkspaceId;
|
||||||
|
}
|
||||||
|
|
||||||
private readonly _deserializeHistoryChatMessages = (
|
private readonly _deserializeHistoryChatMessages = (
|
||||||
historyMessagesString: string
|
historyMessagesString: string
|
||||||
) => {
|
) => {
|
||||||
@@ -75,11 +82,16 @@ export class AIChatBlockPeekView extends LitElement {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private readonly _constructBranchChatBlockMessages = async (
|
private readonly _constructBranchChatBlockMessages = async (
|
||||||
doc: Doc,
|
rootWorkspaceId: string,
|
||||||
|
rootDocId: string,
|
||||||
forkSessionId: string
|
forkSessionId: string
|
||||||
) => {
|
) => {
|
||||||
const currentUserInfo = await AIProvider.userInfo;
|
const currentUserInfo = await AIProvider.userInfo;
|
||||||
const forkMessages = await queryHistoryMessages(doc, forkSessionId);
|
const forkMessages = await queryHistoryMessages(
|
||||||
|
rootWorkspaceId,
|
||||||
|
rootDocId,
|
||||||
|
forkSessionId
|
||||||
|
);
|
||||||
const forkLength = forkMessages.length;
|
const forkLength = forkMessages.length;
|
||||||
const historyLength = this._historyMessages.length;
|
const historyLength = this._historyMessages.length;
|
||||||
|
|
||||||
@@ -153,8 +165,10 @@ export class AIChatBlockPeekView extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get fork session messages
|
// Get fork session messages
|
||||||
|
const { parentRootWorkspaceId, parentRootDocId } = this;
|
||||||
const messages = await this._constructBranchChatBlockMessages(
|
const messages = await this._constructBranchChatBlockMessages(
|
||||||
doc,
|
parentRootWorkspaceId,
|
||||||
|
parentRootDocId,
|
||||||
this.chatContext.currentSessionId
|
this.chatContext.currentSessionId
|
||||||
);
|
);
|
||||||
if (!messages.length) {
|
if (!messages.length) {
|
||||||
@@ -169,6 +183,8 @@ export class AIChatBlockPeekView extends LitElement {
|
|||||||
xywh: bound.serialize(),
|
xywh: bound.serialize(),
|
||||||
messages: JSON.stringify(messages),
|
messages: JSON.stringify(messages),
|
||||||
sessionId: this.chatContext.currentSessionId,
|
sessionId: this.chatContext.currentSessionId,
|
||||||
|
rootWorkspaceId: parentRootWorkspaceId,
|
||||||
|
rootDocId: parentRootDocId,
|
||||||
},
|
},
|
||||||
surfaceBlock.id
|
surfaceBlock.id
|
||||||
);
|
);
|
||||||
@@ -212,8 +228,10 @@ export class AIChatBlockPeekView extends LitElement {
|
|||||||
const chatBlock = doc.getBlock(this.chatContext.currentChatBlockId);
|
const chatBlock = doc.getBlock(this.chatContext.currentChatBlockId);
|
||||||
|
|
||||||
// Get fork session messages
|
// Get fork session messages
|
||||||
|
const { parentRootWorkspaceId, parentRootDocId } = this;
|
||||||
const messages = await this._constructBranchChatBlockMessages(
|
const messages = await this._constructBranchChatBlockMessages(
|
||||||
doc,
|
parentRootWorkspaceId,
|
||||||
|
parentRootDocId,
|
||||||
this.chatContext.currentSessionId
|
this.chatContext.currentSessionId
|
||||||
);
|
);
|
||||||
if (!messages.length) {
|
if (!messages.length) {
|
||||||
@@ -346,58 +364,66 @@ export class AIChatBlockPeekView extends LitElement {
|
|||||||
const { host } = this;
|
const { host } = this;
|
||||||
const actions = ChatBlockPeekViewActions;
|
const actions = ChatBlockPeekViewActions;
|
||||||
|
|
||||||
return html`${repeat(
|
return html`${repeat(currentMessages, (message, idx) => {
|
||||||
currentMessages,
|
const { status, error } = this.chatContext;
|
||||||
message => message.createdAt + message.content,
|
const isAssistantMessage = message.role === 'assistant';
|
||||||
(message, idx) => {
|
const isLastReply =
|
||||||
const { status, error } = this.chatContext;
|
idx === currentMessages.length - 1 && isAssistantMessage;
|
||||||
const isAssistantMessage = message.role === 'assistant';
|
const messageState =
|
||||||
const isLastReply =
|
isLastReply && (status === 'transmitting' || status === 'loading')
|
||||||
idx === currentMessages.length - 1 && isAssistantMessage;
|
? 'generating'
|
||||||
const messageState =
|
: 'finished';
|
||||||
isLastReply && status === 'transmitting' ? 'generating' : 'finished';
|
const shouldRenderError = isLastReply && status === 'error' && !!error;
|
||||||
const shouldRenderError = isLastReply && status === 'error' && !!error;
|
const isNotReady = status === 'transmitting' || status === 'loading';
|
||||||
const isNotReady = status === 'transmitting' || status === 'loading';
|
const shouldRenderCopyMore =
|
||||||
const shouldRenderCopyMore =
|
isAssistantMessage && !(isLastReply && isNotReady);
|
||||||
isAssistantMessage && !(isLastReply && isNotReady);
|
const shouldRenderActions =
|
||||||
const shouldRenderActions =
|
isLastReply && !!message.content && !isNotReady;
|
||||||
isLastReply && !!message.content && !isNotReady;
|
|
||||||
|
|
||||||
const messageClasses = classMap({
|
const messageClasses = classMap({
|
||||||
'assistant-message-container': isAssistantMessage,
|
'assistant-message-container': isAssistantMessage,
|
||||||
});
|
});
|
||||||
|
|
||||||
return html`<div class=${messageClasses}>
|
const { attachments, role, content } = message;
|
||||||
<ai-chat-message
|
const userInfo = {
|
||||||
.host=${host}
|
userId: message.userId,
|
||||||
.message=${message}
|
userName: message.userName,
|
||||||
.state=${messageState}
|
avatarUrl: message.avatarUrl,
|
||||||
></ai-chat-message>
|
};
|
||||||
${shouldRenderError ? AIChatErrorRenderer(host, error) : nothing}
|
|
||||||
${shouldRenderCopyMore
|
return html`<div class=${messageClasses}>
|
||||||
? html` <chat-copy-more
|
<ai-chat-message
|
||||||
.host=${host}
|
.host=${host}
|
||||||
.actions=${actions}
|
.state=${messageState}
|
||||||
.content=${message.content}
|
.content=${content}
|
||||||
.isLast=${isLastReply}
|
.attachments=${attachments}
|
||||||
.chatSessionId=${this.chatContext.currentSessionId ?? undefined}
|
.role=${role}
|
||||||
.messageId=${message.id ?? undefined}
|
.userInfo=${userInfo}
|
||||||
.retry=${() => this.retry()}
|
></ai-chat-message>
|
||||||
></chat-copy-more>`
|
${shouldRenderError ? AIChatErrorRenderer(host, error) : nothing}
|
||||||
: nothing}
|
${shouldRenderCopyMore
|
||||||
${shouldRenderActions
|
? html` <chat-copy-more
|
||||||
? html`<chat-action-list
|
.host=${host}
|
||||||
.host=${host}
|
.actions=${actions}
|
||||||
.actions=${actions}
|
.content=${message.content}
|
||||||
.content=${message.content}
|
.isLast=${isLastReply}
|
||||||
.chatSessionId=${this.chatContext.currentSessionId ?? undefined}
|
.chatSessionId=${this.chatContext.currentSessionId ?? undefined}
|
||||||
.messageId=${message.id ?? undefined}
|
.messageId=${message.id ?? undefined}
|
||||||
.layoutDirection=${'horizontal'}
|
.retry=${() => this.retry()}
|
||||||
></chat-action-list>`
|
></chat-copy-more>`
|
||||||
: nothing}
|
: nothing}
|
||||||
</div>`;
|
${shouldRenderActions
|
||||||
}
|
? html`<chat-action-list
|
||||||
)}`;
|
.host=${host}
|
||||||
|
.actions=${actions}
|
||||||
|
.content=${message.content}
|
||||||
|
.chatSessionId=${this.chatContext.currentSessionId ?? undefined}
|
||||||
|
.messageId=${message.id ?? undefined}
|
||||||
|
.layoutDirection=${'horizontal'}
|
||||||
|
></chat-action-list>`
|
||||||
|
: nothing}
|
||||||
|
</div>`;
|
||||||
|
})}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
override connectedCallback() {
|
override connectedCallback() {
|
||||||
@@ -405,7 +431,12 @@ export class AIChatBlockPeekView extends LitElement {
|
|||||||
this._historyMessages = this._deserializeHistoryChatMessages(
|
this._historyMessages = this._deserializeHistoryChatMessages(
|
||||||
this.historyMessagesString
|
this.historyMessagesString
|
||||||
);
|
);
|
||||||
queryHistoryMessages(this.host.doc, this.parentSessionId)
|
const { parentRootWorkspaceId, parentRootDocId, parentSessionId } = this;
|
||||||
|
queryHistoryMessages(
|
||||||
|
parentRootWorkspaceId,
|
||||||
|
parentRootDocId,
|
||||||
|
parentSessionId
|
||||||
|
)
|
||||||
.then(messages => {
|
.then(messages => {
|
||||||
this._historyMessages = this._historyMessages.map((message, idx) => {
|
this._historyMessages = this._historyMessages.map((message, idx) => {
|
||||||
return {
|
return {
|
||||||
|
|||||||
Reference in New Issue
Block a user