fix(core): slash ask ai returns make it real action result (#10349)

Fix issue [AF-2252](https://linear.app/affine-design/issue/AF-2252).

## What Changed?
- Remove useless `generateAnswer` configuration for inline ask ai
- Refactor the common `updateAIPanelConfig` function
- Use empty string instead of meaningless `placeholder` string
- Remove unnecessary high-order function wrappers
This commit is contained in:
akumatus
2025-02-21 12:52:53 +00:00
parent 55651503df
commit 07cbf5affe
4 changed files with 75 additions and 112 deletions

View File

@@ -278,7 +278,7 @@ export class AffineAIPanelWidget extends WidgetComponent {
input?: string, input?: string,
shouldTriggerCallback?: boolean shouldTriggerCallback?: boolean
) => { ) => {
if (input) { if (typeof input === 'string') {
this._inputText = input; this._inputText = input;
this.generate(); this.generate();
} else { } else {

View File

@@ -1,9 +1,9 @@
import { type EditorHost, TextSelection } from '@blocksuite/affine/block-std'; import { type EditorHost, TextSelection } from '@blocksuite/affine/block-std';
import { import {
type AffineAIPanelWidget, type AffineAIPanelWidget,
type AffineAIPanelWidgetConfig,
type AIError, type AIError,
type AIItemGroupConfig, type AIItemGroupConfig,
AIStarIconWithAnimation,
createLitPortal, createLitPortal,
} from '@blocksuite/affine/blocks'; } from '@blocksuite/affine/blocks';
import { assertExists } from '@blocksuite/affine/global/utils'; import { assertExists } from '@blocksuite/affine/global/utils';
@@ -64,18 +64,20 @@ export function bindTextStream(
}); });
} }
export function actionToStream<T extends keyof BlockSuitePresets.AIActions>( function actionToStream<T extends keyof BlockSuitePresets.AIActions>(
host: EditorHost,
id: T, id: T,
input: string,
signal?: AbortSignal, signal?: AbortSignal,
variants?: Omit< variants?: Omit<
Parameters<BlockSuitePresets.AIActions[T]>[0], Parameters<BlockSuitePresets.AIActions[T]>[0],
keyof BlockSuitePresets.AITextActionOptions keyof BlockSuitePresets.AITextActionOptions
>, >,
trackerOptions?: BlockSuitePresets.TrackerOptions trackerOptions?: BlockSuitePresets.TrackerOptions
) { ): BlockSuitePresets.TextStream | undefined {
const action = AIProvider.actions[id]; const action = AIProvider.actions[id];
if (!action || typeof action !== 'function') return; if (!action || typeof action !== 'function') return;
return (host: EditorHost): BlockSuitePresets.TextStream => {
let stream: BlockSuitePresets.TextStream | undefined; let stream: BlockSuitePresets.TextStream | undefined;
return { return {
async *[Symbol.asyncIterator]() { async *[Symbol.asyncIterator]() {
@@ -93,16 +95,13 @@ export function actionToStream<T extends keyof BlockSuitePresets.AIActions>(
]); ]);
} }
// for now if there are more than one selected blocks, we will not omit the attachments
const sendAttachments =
selectedBlocks?.length === 1 && attachments.length > 0;
const models = selectedBlocks?.map(block => block.model); const models = selectedBlocks?.map(block => block.model);
const control = trackerOptions?.control ?? 'format-bar'; const control = trackerOptions?.control ?? 'format-bar';
const where = trackerOptions?.where ?? 'ai-panel'; const where = trackerOptions?.where ?? 'ai-panel';
const options = { const options = {
...variants, ...variants,
attachments: sendAttachments ? attachments : undefined, attachments,
input: sendAttachments ? '' : markdown, input: input ? `${markdown}\n${input}` : markdown,
stream: true, stream: true,
host, host,
models, models,
@@ -118,12 +117,10 @@ export function actionToStream<T extends keyof BlockSuitePresets.AIActions>(
yield* stream; yield* stream;
}, },
}; };
};
} }
export function actionToGenerateAnswer< function actionToGenerateAnswer<T extends keyof BlockSuitePresets.AIActions>(
T extends keyof BlockSuitePresets.AIActions, host: EditorHost,
>(
id: T, id: T,
variants?: Omit< variants?: Omit<
Parameters<BlockSuitePresets.AIActions[T]>[0], Parameters<BlockSuitePresets.AIActions[T]>[0],
@@ -131,8 +128,8 @@ export function actionToGenerateAnswer<
>, >,
trackerOptions?: BlockSuitePresets.TrackerOptions trackerOptions?: BlockSuitePresets.TrackerOptions
) { ) {
return (host: EditorHost) => {
return ({ return ({
input,
signal, signal,
update, update,
finish, finish,
@@ -145,15 +142,16 @@ export function actionToGenerateAnswer<
const { selectedBlocks: blocks } = getSelections(host); const { selectedBlocks: blocks } = getSelections(host);
if (!blocks || blocks.length === 0) return; if (!blocks || blocks.length === 0) return;
const stream = actionToStream( const stream = actionToStream(
host,
id, id,
input,
signal, signal,
variants, variants,
trackerOptions trackerOptions
)?.(host); );
if (!stream) return; if (!stream) return;
bindTextStream(stream, { update, finish, signal }); bindTextStream(stream, { update, finish, signal });
}; };
};
} }
/** /**
@@ -174,10 +172,11 @@ function updateAIPanelConfig<T extends keyof BlockSuitePresets.AIActions>(
const { config, host } = aiPanel; const { config, host } = aiPanel;
assertExists(config); assertExists(config);
config.generateAnswer = actionToGenerateAnswer( config.generateAnswer = actionToGenerateAnswer(
host,
id, id,
variants, variants,
trackerOptions trackerOptions
)(host); );
const ctx = new AIContext(); const ctx = new AIContext();
config.answerRenderer = actionToAnswerRenderer(id, host, ctx); config.answerRenderer = actionToAnswerRenderer(id, host, ctx);
@@ -206,7 +205,7 @@ export function actionToHandler<T extends keyof BlockSuitePresets.AIActions>(
if (!blocks || blocks.length === 0) return; if (!blocks || blocks.length === 0) return;
const block = blocks.at(-1); const block = blocks.at(-1);
assertExists(block); assertExists(block);
aiPanel.toggle(block, 'placeholder'); aiPanel.toggle(block, '');
}; };
} }
@@ -222,43 +221,12 @@ export function handleInlineAskAIAction(
if (!lastBlockPath) return; if (!lastBlockPath) return;
const block = host.view.getBlock(lastBlockPath); const block = host.view.getBlock(lastBlockPath);
if (!block) return; if (!block) return;
const generateAnswer: AffineAIPanelWidgetConfig['generateAnswer'] = ({
finish,
input,
signal,
update,
}) => {
if (!AIProvider.actions.chat) return;
// recover selection to get content from above blocks
assertExists(selection);
host.selection.set([selection]);
selectAboveBlocks(host)
.then(async context => {
if (!AIProvider.session || !AIProvider.actions.chat) return;
const sessionId = await AIProvider.session.createSession(
host.doc.workspace.id,
host.doc.id
);
const stream = AIProvider.actions.chat({
sessionId,
input: `${context}\n${input}`,
stream: true,
host,
where: 'inline-chat-panel',
control: 'chat-send',
docId: host.doc.id,
workspaceId: host.doc.workspace.id,
});
bindTextStream(stream, { update, finish, signal });
})
.catch(console.error);
};
if (!panel.config) return; if (!panel.config) return;
panel.config.generateAnswer = generateAnswer; updateAIPanelConfig(panel, 'chat', AIStarIconWithAnimation, undefined, {
control: 'chat-send',
where: 'inline-chat-panel',
});
if (!actionGroups) { if (!actionGroups) {
panel.toggle(block); panel.toggle(block);

View File

@@ -451,11 +451,7 @@ export function actionToHandler<T extends keyof BlockSuitePresets.AIActions>(
togglePanel() togglePanel()
.then(isEmpty => { .then(isEmpty => {
aiPanel.toggle( aiPanel.toggle(referenceElement, isEmpty ? undefined : '', false);
referenceElement,
isEmpty ? undefined : 'placeholder',
false
);
}) })
.catch(console.error); .catch(console.error);
}; };

View File

@@ -346,10 +346,9 @@ export class ChatPanel extends WithDisposable(ShadowlessElement) {
if (!this.doc) throw new Error('doc is required'); if (!this.doc) throw new Error('doc is required');
this._disposables.add( this._disposables.add(
AIProvider.slots.actions.on(({ action, event }) => { AIProvider.slots.actions.on(({ event }) => {
const { status } = this.chatContextValue; const { status } = this.chatContextValue;
if ( if (
action !== 'chat' &&
event === 'finished' && event === 'finished' &&
(status === 'idle' || status === 'success') (status === 'idle' || status === 'success')
) { ) {