feat(core): extract selected docs (#13426)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

## Summary by CodeRabbit

* **New Features**
* Added support for handling and extracting embedded document references
within selected content in AI chat features.
* Documents associated with selected context chips are now properly
managed alongside attachments, improving context handling in AI chat
interactions.

* **Bug Fixes**
* Ensured that the state of context chips accurately reflects the
presence of attachments and documents.

* **Documentation**
* Updated type definitions to include support for document references in
relevant AI chat contexts.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

> CLOSE AF-2783
This commit is contained in:
德布劳外 · 贾贵
2025-08-07 10:53:59 +08:00
committed by GitHub
parent 542c8e2c1d
commit a6c78dbcce
7 changed files with 38 additions and 6 deletions

View File

@@ -39,6 +39,7 @@ export interface CollectionChip extends BaseChip {
export interface SelectedContextChip extends BaseChip { export interface SelectedContextChip extends BaseChip {
uuid: string; uuid: string;
attachments: { sourceId: string; name: string }[]; attachments: { sourceId: string; name: string }[];
docs: string[];
snapshot: string | null; snapshot: string | null;
combinedElementsMarkdown: string | null; combinedElementsMarkdown: string | null;
} }

View File

@@ -412,15 +412,16 @@ export class AIChatComposer extends SignalWatcher(
}; };
private readonly addSelectedContextChip = async () => { private readonly addSelectedContextChip = async () => {
const { attachments, snapshot, combinedElementsMarkdown } = const { attachments, snapshot, combinedElementsMarkdown, docs } =
this.chatContextValue; this.chatContextValue;
await this.removeSelectedContextChip(); await this.removeSelectedContextChip();
const chip: SelectedContextChip = { const chip: SelectedContextChip = {
uuid: uuidv4(), uuid: uuidv4(),
attachments, attachments,
docs,
snapshot, snapshot,
combinedElementsMarkdown, combinedElementsMarkdown,
state: 'processing', state: attachments.length > 0 ? 'processing' : 'finished',
}; };
await this.addChip(chip, true); await this.addChip(chip, true);
}; };
@@ -545,7 +546,7 @@ export class AIChatComposer extends SignalWatcher(
private readonly addSelectedContextChipToContext = async ( private readonly addSelectedContextChipToContext = async (
chip: SelectedContextChip chip: SelectedContextChip
) => { ) => {
const { attachments } = chip; const { attachments, docs } = chip;
const contextId = await this.createContextId(); const contextId = await this.createContextId();
if (!contextId || !AIProvider.context) { if (!contextId || !AIProvider.context) {
throw new Error('Context not found'); throw new Error('Context not found');
@@ -554,6 +555,14 @@ export class AIChatComposer extends SignalWatcher(
blobIds: attachments.map(attachment => attachment.sourceId), blobIds: attachments.map(attachment => attachment.sourceId),
contextId, contextId,
}); });
await Promise.all(
docs.map(docId =>
AIProvider.context?.addContextDoc({
contextId,
docId,
})
)
);
}; };
private readonly removeFromContext = async ( private readonly removeFromContext = async (
@@ -589,11 +598,19 @@ export class AIChatComposer extends SignalWatcher(
}); });
} }
if (isSelectedContextChip(chip)) { if (isSelectedContextChip(chip)) {
const { attachments } = chip; const { attachments, docs } = chip;
return await AIProvider.context.removeContextBlobs({ await AIProvider.context.removeContextBlobs({
contextId, contextId,
blobIds: attachments.map(attachment => attachment.sourceId), blobIds: attachments.map(attachment => attachment.sourceId),
}); });
await Promise.all(
docs.map(docId =>
AIProvider.context?.removeContextDoc({
contextId,
docId,
})
)
);
} }
return true; return true;
} catch { } catch {

View File

@@ -52,6 +52,7 @@ const DEFAULT_CHAT_CONTEXT_VALUE: ChatContextValue = {
snapshot: null, snapshot: null,
attachments: [], attachments: [],
combinedElementsMarkdown: null, combinedElementsMarkdown: null,
docs: [],
}; };
export class AIChatContent extends SignalWatcher( export class AIChatContent extends SignalWatcher(

View File

@@ -18,5 +18,7 @@ export type ChatContextValue = {
attachments: { sourceId: string; name: string }[]; attachments: { sourceId: string; name: string }[];
// combined markdown of the selected elements // combined markdown of the selected elements
combinedElementsMarkdown: string | null; combinedElementsMarkdown: string | null;
// docs of the selected content
docs: string[];
abortController: AbortController | null; abortController: AbortController | null;
}; };

View File

@@ -30,5 +30,5 @@ export type AIChatInputContext = {
abortController: AbortController | null; abortController: AbortController | null;
} & Pick< } & Pick<
ChatContextValue, ChatContextValue,
'snapshot' | 'combinedElementsMarkdown' | 'attachments' 'snapshot' | 'combinedElementsMarkdown' | 'attachments' | 'docs'
>; >;

View File

@@ -48,6 +48,7 @@ const DEFAULT_CHAT_CONTEXT_VALUE: ChatContextValue = {
snapshot: null, snapshot: null,
attachments: [], attachments: [],
combinedElementsMarkdown: null, combinedElementsMarkdown: null,
docs: [],
}; };
export class PlaygroundChat extends SignalWatcher( export class PlaygroundChat extends SignalWatcher(

View File

@@ -2,6 +2,8 @@ import { WorkspaceImpl } from '@affine/core/modules/workspace/impls/workspace';
import { getSurfaceBlock } from '@blocksuite/affine/blocks/surface'; import { getSurfaceBlock } from '@blocksuite/affine/blocks/surface';
import { import {
DatabaseBlockModel, DatabaseBlockModel,
EmbedLinkedDocModel,
EmbedSyncedDocModel,
ImageBlockModel, ImageBlockModel,
NoteBlockModel, NoteBlockModel,
NoteDisplayMode, NoteDisplayMode,
@@ -65,6 +67,7 @@ async function extractEdgelessSelected(
let markdown = ''; let markdown = '';
const attachments: ChatContextValue['attachments'] = []; const attachments: ChatContextValue['attachments'] = [];
const images: File[] = []; const images: File[] = [];
const docs: ChatContextValue['docs'] = [];
if (selectedElements.length) { if (selectedElements.length) {
const transformer = host.store.getTransformer(); const transformer = host.store.getTransformer();
@@ -117,6 +120,12 @@ async function extractEdgelessSelected(
needSnapshot = true; needSnapshot = true;
const props = getElementProps(element, new Map()); const props = getElementProps(element, new Map());
surface.addElement(props); surface.addElement(props);
} else if (
element instanceof EmbedSyncedDocModel ||
element instanceof EmbedLinkedDocModel
) {
const docId = element.props.pageId;
docs.push(docId);
} }
} }
@@ -144,6 +153,7 @@ async function extractEdgelessSelected(
snapshot: snapshot ? JSON.stringify(snapshot) : null, snapshot: snapshot ? JSON.stringify(snapshot) : null,
combinedElementsMarkdown: markdown.length ? markdown : null, combinedElementsMarkdown: markdown.length ? markdown : null,
attachments, attachments,
docs,
}; };
} }