Skip to content

Commit

Permalink
feat(presets): support chat retry (#7085)
Browse files Browse the repository at this point in the history
  • Loading branch information
regischen committed May 17, 2024
1 parent 5b10dd6 commit 01f8131
Show file tree
Hide file tree
Showing 7 changed files with 143 additions and 8 deletions.
22 changes: 22 additions & 0 deletions packages/presets/src/ai/_common/icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -891,6 +891,28 @@ export const CopyIcon = html`<svg
</defs>
</svg> `;

export const RetryIcon = html`<svg
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g clip-path="url(#clip0_4361_69948)">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M2.70837 9.99998C2.70837 5.9729 5.97296 2.70831 10 2.70831C11.8672 2.70831 13.5717 3.41091 14.8613 4.56514L14.8633 4.56693L16.0417 5.63077V3.33331C16.0417 2.98814 16.3215 2.70831 16.6667 2.70831C17.0119 2.70831 17.2917 2.98814 17.2917 3.33331V7.03702C17.2917 7.38219 17.0119 7.66202 16.6667 7.66202H12.963C12.6178 7.66202 12.338 7.38219 12.338 7.03702C12.338 6.69184 12.6178 6.41202 12.963 6.41202H15.0417L14.0277 5.49656C14.0273 5.49626 14.027 5.49597 14.0267 5.49567C12.9575 4.53915 11.5473 3.95831 10 3.95831C6.66332 3.95831 3.95837 6.66326 3.95837 9.99998C3.95837 13.3367 6.66332 16.0416 10 16.0416C12.8745 16.0416 15.2815 14.0336 15.892 11.3432C15.9684 11.0065 16.3032 10.7956 16.6398 10.872C16.9764 10.9483 17.1874 11.2831 17.111 11.6198C16.3742 14.8671 13.471 17.2916 10 17.2916C5.97296 17.2916 2.70837 14.0271 2.70837 9.99998Z"
fill="#77757D"
/>
</g>
<defs>
<clipPath id="clip0_4361_69948">
<rect width="20" height="20" fill="white" />
</clipPath>
</defs>
</svg> `;

export const MoreIcon = html`<svg
width="20"
height="20"
Expand Down
3 changes: 2 additions & 1 deletion packages/presets/src/ai/actions/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,11 @@ export const textTones = [
declare global {
namespace BlockSuitePresets {
interface AITextActionOptions {
input: string;
input?: string;
stream?: boolean;
attachments?: (string | File | Blob)[]; // blob could only be strings for the moments (url or data urls)
signal?: AbortSignal;
retry?: boolean;

// action's context
docId: string;
Expand Down
76 changes: 74 additions & 2 deletions packages/presets/src/ai/chat-panel/actions/copy-more.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@ import type {
TextSelection,
} from '@blocksuite/block-std';
import { WithDisposable } from '@blocksuite/block-std';
import { createButtonPopper, Tooltip } from '@blocksuite/blocks';
import { type AIError, createButtonPopper, Tooltip } from '@blocksuite/blocks';
import { noop } from '@blocksuite/global/utils';
import { css, html, LitElement, nothing, type PropertyValues } from 'lit';
import { customElement, property, query, state } from 'lit/decorators.js';
import { repeat } from 'lit/directives/repeat.js';

import { CopyIcon, MoreIcon } from '../../_common/icons.js';
import { CopyIcon, MoreIcon, RetryIcon } from '../../_common/icons.js';
import { AIProvider } from '../../provider.js';
import { copyText } from '../../utils/editor-actions.js';
import type { ChatItem, ChatMessage, ChatStatus } from '../index.js';
import { PageEditorActions } from './actions-handle.js';

noop(Tooltip);
Expand Down Expand Up @@ -83,6 +85,24 @@ export class ChatCopyMore extends WithDisposable(LitElement) {
@property({ attribute: false })
curBlockSelections?: BlockSelection[];

@property({ attribute: false })
items!: ChatItem[];

@property({ attribute: false })
updateItems!: (items: ChatItem[]) => void;

@property({ attribute: false })
updateStatus!: (status: ChatStatus) => void;

@property({ attribute: false })
updateError!: (error: AIError | null) => void;

@property({ attribute: false })
abortController!: AbortController | null;

@property({ attribute: false })
updateAbortController!: (abortController: AbortController | null) => void;

@state()
private _showMoreMenu = false;

Expand Down Expand Up @@ -111,6 +131,52 @@ export class ChatCopyMore extends WithDisposable(LitElement) {
}
}

private async _retry() {
const { doc } = this.host;
try {
const abortController = new AbortController();
const items = [...this.items];
const last = items[items.length - 1];
if ('content' in last) {
last.content = '';
last.createdAt = new Date().toISOString();
}

this.updateItems(items);
this.updateStatus('loading');
this.updateError(null);

const stream = AIProvider.actions.chat?.({
retry: true,
docId: doc.id,
workspaceId: doc.collection.id,
host: this.host,
stream: true,
signal: abortController.signal,
where: 'chat-panel',
control: 'chat-send',
});

if (stream) {
this.updateAbortController(abortController);
for await (const text of stream) {
this.updateStatus('transmitting');
const items = [...this.items];
const last = items[items.length - 1] as ChatMessage;
last.content += text;
this.updateItems(items);
}

this.updateStatus('success');
}
} catch (error) {
this.updateStatus('error');
this.updateError(error as AIError);
} finally {
this.updateAbortController(null);
}
}

override render() {
const { host, content, isLast } = this;
return html`<style>
Expand All @@ -123,6 +189,12 @@ export class ChatCopyMore extends WithDisposable(LitElement) {
${CopyIcon}
<affine-tooltip>Copy</affine-tooltip>
</div>
${isLast
? html`<div @click=${() => this._retry()}>
${RetryIcon}
<affine-tooltip>Retry</affine-tooltip>
</div>`
: nothing}
${isLast
? nothing
: html`<div class="more-button" @click=${this._toggle}>
Expand Down
11 changes: 7 additions & 4 deletions packages/presets/src/ai/chat-panel/chat-panel-input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,11 @@ export class ChatPanelInput extends WithDisposable(LitElement) {
@state()
focused = false;

@state()
abortController?: AbortController;
@property({ attribute: false })
abortController!: AbortController | null;

@property({ attribute: false })
updateAbortController!: (abortController: AbortController | null) => void;

send = async () => {
if (this.status === 'loading' || this.status === 'transmitting') return;
Expand Down Expand Up @@ -205,7 +208,7 @@ export class ChatPanelInput extends WithDisposable(LitElement) {
});

if (stream) {
this.abortController = abortController;
this.updateAbortController(abortController);

for await (const text of stream) {
this.updateStatus('transmitting');
Expand All @@ -221,7 +224,7 @@ export class ChatPanelInput extends WithDisposable(LitElement) {
this.updateStatus('error');
this.updateError(error as AIError);
} finally {
this.abortController = undefined;
this.updateAbortController(null);
}
};

Expand Down
21 changes: 21 additions & 0 deletions packages/presets/src/ai/chat-panel/chat-panel-messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,21 @@ export class ChatPanelMessages extends WithDisposable(ShadowlessElement) {
@property({ attribute: false })
isLoading!: boolean;

@property({ attribute: false })
updateItems!: (items: ChatItem[]) => void;

@property({ attribute: false })
updateStatus!: (status: ChatStatus) => void;

@property({ attribute: false })
updateError!: (error: AIError | null) => void;

@property({ attribute: false })
abortController!: AbortController | null;

@property({ attribute: false })
updateAbortController!: (abortController: AbortController | null) => void;

@query('.chat-panel-messages')
messagesContainer!: HTMLDivElement;

Expand Down Expand Up @@ -359,6 +374,12 @@ export class ChatPanelMessages extends WithDisposable(ShadowlessElement) {
.isLast=${isLast}
.curTextSelection=${this._currentTextSelection}
.curBlockSelections=${this._currentBlockSelections}
.items=${this.items}
.abortController=${this.abortController}
.updateItems=${this.updateItems}
.updateStatus=${this.updateStatus}
.updateError=${this.updateError}
.updateAbortController=${this.updateAbortController}
></chat-copy-more>
${isLast
? html`<div class="actions-container">
Expand Down
16 changes: 15 additions & 1 deletion packages/presets/src/ai/chat-panel/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ export class ChatPanel extends WithDisposable(ShadowlessElement) {
@state()
isLoading = false;

@state()
abortController: AbortController | null = null;

private _chatMessages: Ref<ChatPanelMessages> =
createRef<ChatPanelMessages>();

Expand Down Expand Up @@ -192,6 +195,10 @@ export class ChatPanel extends WithDisposable(ShadowlessElement) {
this.error = error;
};

updateAbortController = (abortController: AbortController | null) => {
this.abortController = abortController;
};

scrollToDown() {
requestAnimationFrame(() => this._chatMessages.value?.scrollToDown());
}
Expand All @@ -206,14 +213,21 @@ export class ChatPanel extends WithDisposable(ShadowlessElement) {
.status=${this.status}
.error=${this.error}
.isLoading=${this.isLoading}
.updateItems=${this.updateItems}
.updateStatus=${this.updateStatus}
.updateError=${this.updateError}
.abortController=${this.abortController}
.updateAbortController=${this.updateAbortController}
></chat-panel-messages>
<chat-panel-input
.host=${this.host}
.items=${this.items}
.updateItems=${this.updateItems}
.updateStatus=${this.updateStatus}
.abortController=${this.abortController}
.updateAbortController=${this.updateAbortController}
.addToItems=${this.addToItems}
.status=${this.status}
.updateStatus=${this.updateStatus}
.error=${this.error}
.updateError=${this.updateError}
></chat-panel-input>
Expand Down
2 changes: 2 additions & 0 deletions packages/presets/src/ai/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ export class AIProvider {
// add more if needed
};

static LAST_ACTION_SESSIONID = '';

static MAX_LOCAL_HISTORY = 10;
// track the history of triggered actions (in memory only)
private readonly actionHistory: {
Expand Down

1 comment on commit 01f8131

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Size Report

Bundles

Entry Size Gzip Brotli
examples/basic 1.38 kB 769 B (-2 B) 659 B (-4 B)

Packages

Name Size Gzip Brotli
blocks 2.42 MB (-109 B) 569 kB (-7 B) 411 kB (+163 B)
editor 84 B 89 B 63 B
store 83 B 88 B 63 B
inline 84 B 88 B 63 B

Please sign in to comment.