donteatfriedrice
2024-08-02 13:14:15 +08:00
committed by Chen
parent f7798a00c1
commit 62fc7e2f4d
2 changed files with 103 additions and 66 deletions
@@ -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
); );
@@ -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 {