From 0e8f19b92ca879841717fe20497a1913e4c3498b Mon Sep 17 00:00:00 2001 From: yoyoyohamapi <8338436+yoyoyohamapi@users.noreply.github.com> Date: Mon, 26 May 2025 07:17:38 +0000 Subject: [PATCH] refactor(core): workspace embedding entities (#12490) ### TL;DR refactor: split workspace embedding module entities into: * additional-attachments * ignored-docs * embedding-enabled * embedding-progress --- .../entities/additional-attachments.ts | 153 +++++++++ .../entities/embedding-enabled.ts | 63 ++++ .../entities/embedding-progress.ts | 86 ++++++ .../entities/embedding.ts | 290 ------------------ .../entities/ignored-docs.ts | 70 +++++ .../workspace-indexer-embedding/index.ts | 10 +- .../services/embedding.ts | 10 +- .../workspace-indexer-embedding/utils.ts | 4 + .../view/embedding-settings.tsx | 89 +++--- 9 files changed, 442 insertions(+), 333 deletions(-) create mode 100644 packages/frontend/core/src/modules/workspace-indexer-embedding/entities/additional-attachments.ts create mode 100644 packages/frontend/core/src/modules/workspace-indexer-embedding/entities/embedding-enabled.ts create mode 100644 packages/frontend/core/src/modules/workspace-indexer-embedding/entities/embedding-progress.ts delete mode 100644 packages/frontend/core/src/modules/workspace-indexer-embedding/entities/embedding.ts create mode 100644 packages/frontend/core/src/modules/workspace-indexer-embedding/entities/ignored-docs.ts diff --git a/packages/frontend/core/src/modules/workspace-indexer-embedding/entities/additional-attachments.ts b/packages/frontend/core/src/modules/workspace-indexer-embedding/entities/additional-attachments.ts new file mode 100644 index 000000000..cd056827f --- /dev/null +++ b/packages/frontend/core/src/modules/workspace-indexer-embedding/entities/additional-attachments.ts @@ -0,0 +1,153 @@ +import type { WorkspaceService } from '@affine/core/modules/workspace'; +import type { PaginationInput } from '@affine/graphql'; +import { + catchErrorInto, + effect, + Entity, + fromPromise, + LiveData, + onComplete, + onStart, + smartRetry, +} from '@toeverything/infra'; +import { EMPTY } from 'rxjs'; +import { exhaustMap, mergeMap } from 'rxjs/operators'; + +import { COUNT_PER_PAGE } from '../constants'; +import type { EmbeddingStore } from '../stores/embedding'; +import type { LocalAttachmentFile, PersistedAttachmentFile } from '../types'; +import { logger } from '../utils'; + +interface Attachments { + totalCount: number; + pageInfo: { + endCursor: string | null; + hasNextPage: boolean; + }; + edges: { + node: PersistedAttachmentFile; + }[]; +} + +export class AdditionalAttachments extends Entity { + error$ = new LiveData(null); + attachments$ = new LiveData({ + edges: [], + pageInfo: { + endCursor: null, + hasNextPage: false, + }, + totalCount: 0, + }); + loading$ = new LiveData(true); + + uploadingAttachments$ = new LiveData([]); + + constructor( + private readonly workspaceService: WorkspaceService, + private readonly store: EmbeddingStore + ) { + super(); + } + + mergedAttachments$ = LiveData.computed(get => { + const uploading = get(this.uploadingAttachments$); + const uploaded = get(this.attachments$).edges.map(edge => edge.node); + return [...uploading, ...uploaded].slice(0, 10); + }); + + getAttachments = effect( + exhaustMap((pagination: PaginationInput) => { + return fromPromise(signal => + this.store.getEmbeddingFiles( + this.workspaceService.workspace.id, + pagination, + signal + ) + ).pipe( + smartRetry(), + mergeMap(value => { + const patched = { + ...value, + edges: value.edges.map(edge => ({ + ...edge, + node: { + ...edge.node, + status: 'uploaded' as const, + }, + })), + }; + this.attachments$.next(patched); + return EMPTY; + }), + catchErrorInto(this.error$, error => { + logger.error( + 'Failed to fetch workspace doc embedding attachments', + error + ); + }), + onStart(() => this.loading$.setValue(true)), + onComplete(() => this.loading$.setValue(false)) + ); + }) + ); + + addAttachments = (files: File[]) => { + const generateLocalId = () => + Math.random().toString(36).slice(2) + Date.now(); + const localAttachments: LocalAttachmentFile[] = files.map(file => ({ + localId: generateLocalId(), + fileName: file.name, + mimeType: file.type, + size: file.size, + createdAt: file.lastModified, + status: 'uploading', + })); + + this.uploadingAttachments$.next([ + ...localAttachments, + ...this.uploadingAttachments$.value, + ]); + + this.store + .addEmbeddingFiles(this.workspaceService.workspace.id, files) + .then(() => { + this.uploadingAttachments$.next( + this.uploadingAttachments$.value.filter( + att => !localAttachments.some(l => l.localId === att.localId) + ) + ); + this.getAttachments({ first: COUNT_PER_PAGE, after: null }); + }) + .catch(error => { + this.uploadingAttachments$.next( + this.uploadingAttachments$.value.map(att => + localAttachments.some(l => l.localId === att.localId) + ? { ...att, status: 'error', errorMessage: String(error) } + : att + ) + ); + }); + }; + + removeAttachment = (id: string) => { + const localIndex = this.uploadingAttachments$.value.findIndex( + att => att.localId === id + ); + if (localIndex !== -1) { + this.uploadingAttachments$.next( + this.uploadingAttachments$.value.filter(att => att.localId !== id) + ); + return Promise.resolve(); + } + return this.store + .removeEmbeddingFile(this.workspaceService.workspace.id, id) + .then(() => { + this.getAttachments({ first: COUNT_PER_PAGE, after: null }); + }); + }; + + override dispose(): void { + this.getAttachments.unsubscribe(); + } +} diff --git a/packages/frontend/core/src/modules/workspace-indexer-embedding/entities/embedding-enabled.ts b/packages/frontend/core/src/modules/workspace-indexer-embedding/entities/embedding-enabled.ts new file mode 100644 index 000000000..7a232eb30 --- /dev/null +++ b/packages/frontend/core/src/modules/workspace-indexer-embedding/entities/embedding-enabled.ts @@ -0,0 +1,63 @@ +import type { WorkspaceService } from '@affine/core/modules/workspace'; +import { logger } from '@sentry/react'; +import { + catchErrorInto, + effect, + Entity, + fromPromise, + LiveData, + onComplete, + onStart, + smartRetry, +} from '@toeverything/infra'; +import { EMPTY } from 'rxjs'; +import { exhaustMap, mergeMap } from 'rxjs/operators'; + +import type { EmbeddingStore } from '../stores/embedding'; + +export class EmbeddingEnabled extends Entity { + enabled$ = new LiveData(null); + loading$ = new LiveData(true); + error$ = new LiveData(null); + + constructor( + private readonly workspaceService: WorkspaceService, + private readonly store: EmbeddingStore + ) { + super(); + } + + getEnabled = effect( + exhaustMap(() => { + return fromPromise(signal => + this.store.getEnabled(this.workspaceService.workspace.id, signal) + ).pipe( + smartRetry(), + mergeMap(value => { + this.enabled$.next(value); + return EMPTY; + }), + catchErrorInto(this.error$, error => { + logger.error( + 'Failed to fetch workspace doc embedding enabled', + error + ); + }), + onStart(() => this.loading$.setValue(true)), + onComplete(() => this.loading$.setValue(false)) + ); + }) + ); + + setEnabled = (enabled: boolean) => { + return this.store + .updateEnabled(this.workspaceService.workspace.id, enabled) + .then(() => { + this.getEnabled(); + }); + }; + + override dispose(): void { + this.getEnabled.unsubscribe(); + } +} diff --git a/packages/frontend/core/src/modules/workspace-indexer-embedding/entities/embedding-progress.ts b/packages/frontend/core/src/modules/workspace-indexer-embedding/entities/embedding-progress.ts new file mode 100644 index 000000000..7e66e9c4c --- /dev/null +++ b/packages/frontend/core/src/modules/workspace-indexer-embedding/entities/embedding-progress.ts @@ -0,0 +1,86 @@ +import type { WorkspaceService } from '@affine/core/modules/workspace'; +import { logger } from '@sentry/react'; +import { + catchErrorInto, + effect, + Entity, + fromPromise, + LiveData, + onComplete, + onStart, + smartRetry, +} from '@toeverything/infra'; +import { EMPTY, interval, Subject } from 'rxjs'; +import { exhaustMap, mergeMap, switchMap, takeUntil } from 'rxjs/operators'; + +import type { EmbeddingStore } from '../stores/embedding'; +import type { LocalAttachmentFile } from '../types'; + +interface Progress { + embedded: number; + total: number; +} + +export class EmbeddingProgress extends Entity { + progress$ = new LiveData(null); + error$ = new LiveData(null); + loading$ = new LiveData(true); + + private readonly EMBEDDING_PROGRESS_POLL_INTERVAL = 3000; + private readonly stopEmbeddingProgress$ = new Subject(); + uploadingAttachments$ = new LiveData([]); + + constructor( + private readonly workspaceService: WorkspaceService, + private readonly store: EmbeddingStore + ) { + super(); + } + + startEmbeddingProgressPolling() { + this.stopEmbeddingProgressPolling(); + this.getEmbeddingProgress(); + } + + stopEmbeddingProgressPolling() { + this.stopEmbeddingProgress$.next(); + } + + getEmbeddingProgress = effect( + exhaustMap(() => { + return interval(this.EMBEDDING_PROGRESS_POLL_INTERVAL).pipe( + takeUntil(this.stopEmbeddingProgress$), + switchMap(() => + fromPromise(signal => + this.store.getEmbeddingProgress( + this.workspaceService.workspace.id, + signal + ) + ).pipe( + smartRetry(), + mergeMap(value => { + this.progress$.next(value); + if (value && value.embedded === value.total) { + this.stopEmbeddingProgressPolling(); + } + return EMPTY; + }), + catchErrorInto(this.error$, error => { + logger.error( + 'Failed to fetch workspace embedding progress', + error + ); + }), + onStart(() => this.loading$.setValue(true)), + onComplete(() => this.loading$.setValue(false)) + ) + ) + ); + }) + ); + + override dispose(): void { + this.stopEmbeddingProgress$.next(); + this.getEmbeddingProgress.unsubscribe(); + } +} diff --git a/packages/frontend/core/src/modules/workspace-indexer-embedding/entities/embedding.ts b/packages/frontend/core/src/modules/workspace-indexer-embedding/entities/embedding.ts deleted file mode 100644 index d3f49401b..000000000 --- a/packages/frontend/core/src/modules/workspace-indexer-embedding/entities/embedding.ts +++ /dev/null @@ -1,290 +0,0 @@ -import type { WorkspaceService } from '@affine/core/modules/workspace'; -import { DebugLogger } from '@affine/debug'; -import type { PaginationInput } from '@affine/graphql'; -import { - catchErrorInto, - effect, - Entity, - fromPromise, - LiveData, - onComplete, - onStart, - smartRetry, -} from '@toeverything/infra'; -import { EMPTY, interval, Subject } from 'rxjs'; -import { exhaustMap, mergeMap, switchMap, takeUntil } from 'rxjs/operators'; - -import { COUNT_PER_PAGE } from '../constants'; -import type { EmbeddingStore } from '../stores/embedding'; -import type { - IgnoredDoc, - LocalAttachmentFile, - PersistedAttachmentFile, -} from '../types'; - -const logger = new DebugLogger('WorkspaceEmbedding'); - -export interface EmbeddingConfig { - enabled: boolean; -} - -interface Attachments { - totalCount: number; - pageInfo: { - endCursor: string | null; - hasNextPage: boolean; - }; - edges: { - node: PersistedAttachmentFile; - }[]; -} - -type IgnoredDocs = IgnoredDoc[]; - -interface EmbeddingProgress { - embedded: number; - total: number; -} - -export class Embedding extends Entity { - enabled$ = new LiveData(null); - error$ = new LiveData(null); - attachments$ = new LiveData({ - edges: [], - pageInfo: { - endCursor: null, - hasNextPage: false, - }, - totalCount: 0, - }); - ignoredDocs$ = new LiveData([]); - isEnabledLoading$ = new LiveData(true); - isAttachmentsLoading$ = new LiveData(true); - isIgnoredDocsLoading$ = new LiveData(true); - embeddingProgress$ = new LiveData(null); - isEmbeddingProgressLoading$ = new LiveData(true); - - private readonly EMBEDDING_PROGRESS_POLL_INTERVAL = 3000; - private readonly stopEmbeddingProgress$ = new Subject(); - uploadingAttachments$ = new LiveData([]); - - constructor( - private readonly workspaceService: WorkspaceService, - private readonly store: EmbeddingStore - ) { - super(); - } - - mergedAttachments$ = LiveData.computed(get => { - const uploading = get(this.uploadingAttachments$); - const uploaded = get(this.attachments$).edges.map(edge => edge.node); - return [...uploading, ...uploaded].slice(0, 10); - }); - - getEnabled = effect( - exhaustMap(() => { - return fromPromise(signal => - this.store.getEnabled(this.workspaceService.workspace.id, signal) - ).pipe( - smartRetry(), - mergeMap(value => { - this.enabled$.next(value); - return EMPTY; - }), - catchErrorInto(this.error$, error => { - logger.error( - 'Failed to fetch workspace doc embedding enabled', - error - ); - }), - onStart(() => this.isEnabledLoading$.setValue(true)), - onComplete(() => this.isEnabledLoading$.setValue(false)) - ); - }) - ); - - setEnabled = (enabled: boolean) => { - return this.store - .updateEnabled(this.workspaceService.workspace.id, enabled) - .then(() => { - this.getEnabled(); - }); - }; - - getIgnoredDocs = effect( - exhaustMap(() => { - return fromPromise(signal => - this.store.getIgnoredDocs(this.workspaceService.workspace.id, signal) - ).pipe( - smartRetry(), - mergeMap(value => { - this.ignoredDocs$.next(value); - return EMPTY; - }), - catchErrorInto(this.error$, error => { - logger.error( - 'Failed to fetch workspace doc embedding ignored docs', - error - ); - }), - onStart(() => this.isIgnoredDocsLoading$.setValue(true)), - onComplete(() => this.isIgnoredDocsLoading$.setValue(false)) - ); - }) - ); - - updateIgnoredDocs = ({ - add, - remove, - }: { - add: string[]; - remove: string[]; - }) => { - return this.store - .updateIgnoredDocs(this.workspaceService.workspace.id, add, remove) - .then(() => { - this.getIgnoredDocs(); - }); - }; - - getAttachments = effect( - exhaustMap((pagination: PaginationInput) => { - return fromPromise(signal => - this.store.getEmbeddingFiles( - this.workspaceService.workspace.id, - pagination, - signal - ) - ).pipe( - smartRetry(), - mergeMap(value => { - const patched = { - ...value, - edges: value.edges.map(edge => ({ - ...edge, - node: { - ...edge.node, - status: 'uploaded' as const, - }, - })), - }; - this.attachments$.next(patched); - return EMPTY; - }), - catchErrorInto(this.error$, error => { - logger.error( - 'Failed to fetch workspace doc embedding attachments', - error - ); - }), - onStart(() => this.isAttachmentsLoading$.setValue(true)), - onComplete(() => this.isAttachmentsLoading$.setValue(false)) - ); - }) - ); - - addAttachments = (files: File[]) => { - const generateLocalId = () => - Math.random().toString(36).slice(2) + Date.now(); - const localAttachments: LocalAttachmentFile[] = files.map(file => ({ - localId: generateLocalId(), - fileName: file.name, - mimeType: file.type, - size: file.size, - createdAt: file.lastModified, - status: 'uploading', - })); - - this.uploadingAttachments$.next([ - ...localAttachments, - ...this.uploadingAttachments$.value, - ]); - - this.store - .addEmbeddingFiles(this.workspaceService.workspace.id, files) - .then(() => { - this.uploadingAttachments$.next( - this.uploadingAttachments$.value.filter( - att => !localAttachments.some(l => l.localId === att.localId) - ) - ); - this.getAttachments({ first: COUNT_PER_PAGE, after: null }); - }) - .catch(error => { - this.uploadingAttachments$.next( - this.uploadingAttachments$.value.map(att => - localAttachments.some(l => l.localId === att.localId) - ? { ...att, status: 'error', errorMessage: String(error) } - : att - ) - ); - }); - }; - - removeAttachment = (id: string) => { - const localIndex = this.uploadingAttachments$.value.findIndex( - att => att.localId === id - ); - if (localIndex !== -1) { - this.uploadingAttachments$.next( - this.uploadingAttachments$.value.filter(att => att.localId !== id) - ); - return Promise.resolve(); - } - return this.store - .removeEmbeddingFile(this.workspaceService.workspace.id, id) - .then(() => { - this.getAttachments({ first: COUNT_PER_PAGE, after: null }); - }); - }; - - startEmbeddingProgressPolling() { - this.stopEmbeddingProgressPolling(); - this.getEmbeddingProgress(); - } - - stopEmbeddingProgressPolling() { - this.stopEmbeddingProgress$.next(); - } - - getEmbeddingProgress = effect( - exhaustMap(() => { - return interval(this.EMBEDDING_PROGRESS_POLL_INTERVAL).pipe( - takeUntil(this.stopEmbeddingProgress$), - switchMap(() => - fromPromise(signal => - this.store.getEmbeddingProgress( - this.workspaceService.workspace.id, - signal - ) - ).pipe( - smartRetry(), - mergeMap(value => { - this.embeddingProgress$.next(value); - if (value && value.embedded === value.total) { - this.stopEmbeddingProgressPolling(); - } - return EMPTY; - }), - catchErrorInto(this.error$, error => { - logger.error( - 'Failed to fetch workspace embedding progress', - error - ); - }), - onStart(() => this.isEmbeddingProgressLoading$.setValue(true)), - onComplete(() => this.isEmbeddingProgressLoading$.setValue(false)) - ) - ) - ); - }) - ); - - override dispose(): void { - this.getEnabled.unsubscribe(); - this.getAttachments.unsubscribe(); - this.getIgnoredDocs.unsubscribe(); - this.stopEmbeddingProgress$.next(); - this.getEmbeddingProgress.unsubscribe(); - } -} diff --git a/packages/frontend/core/src/modules/workspace-indexer-embedding/entities/ignored-docs.ts b/packages/frontend/core/src/modules/workspace-indexer-embedding/entities/ignored-docs.ts new file mode 100644 index 000000000..61c2a4823 --- /dev/null +++ b/packages/frontend/core/src/modules/workspace-indexer-embedding/entities/ignored-docs.ts @@ -0,0 +1,70 @@ +import type { WorkspaceService } from '@affine/core/modules/workspace'; +import { + catchErrorInto, + effect, + Entity, + fromPromise, + LiveData, + onComplete, + onStart, + smartRetry, +} from '@toeverything/infra'; +import { EMPTY } from 'rxjs'; +import { exhaustMap, mergeMap } from 'rxjs/operators'; + +import type { EmbeddingStore } from '../stores/embedding'; +import type { IgnoredDoc } from '../types'; +import { logger } from '../utils'; + +export class IgnoredDocs extends Entity { + docs$ = new LiveData([]); + error$ = new LiveData(null); + loading$ = new LiveData(true); + + constructor( + private readonly workspaceService: WorkspaceService, + private readonly store: EmbeddingStore + ) { + super(); + } + + getIgnoredDocs = effect( + exhaustMap(() => { + return fromPromise(signal => + this.store.getIgnoredDocs(this.workspaceService.workspace.id, signal) + ).pipe( + smartRetry(), + mergeMap(value => { + this.docs$.next(value); + return EMPTY; + }), + catchErrorInto(this.error$, error => { + logger.error( + 'Failed to fetch workspace doc embedding ignored docs', + error + ); + }), + onStart(() => this.loading$.setValue(true)), + onComplete(() => this.loading$.setValue(false)) + ); + }) + ); + + updateIgnoredDocs = ({ + add, + remove, + }: { + add: string[]; + remove: string[]; + }) => { + return this.store + .updateIgnoredDocs(this.workspaceService.workspace.id, add, remove) + .then(() => { + this.getIgnoredDocs(); + }); + }; + + override dispose(): void { + this.getIgnoredDocs.unsubscribe(); + } +} diff --git a/packages/frontend/core/src/modules/workspace-indexer-embedding/index.ts b/packages/frontend/core/src/modules/workspace-indexer-embedding/index.ts index d8939077a..fdb3321b1 100644 --- a/packages/frontend/core/src/modules/workspace-indexer-embedding/index.ts +++ b/packages/frontend/core/src/modules/workspace-indexer-embedding/index.ts @@ -5,7 +5,10 @@ import { } from '@affine/core/modules/workspace'; import { type Framework } from '@toeverything/infra'; -import { Embedding } from './entities/embedding'; +import { AdditionalAttachments } from './entities/additional-attachments'; +import { EmbeddingEnabled } from './entities/embedding-enabled'; +import { EmbeddingProgress } from './entities/embedding-progress'; +import { IgnoredDocs } from './entities/ignored-docs'; import { EmbeddingService } from './services/embedding'; import { EmbeddingStore } from './stores/embedding'; @@ -14,7 +17,10 @@ export function configureIndexerEmbeddingModule(framework: Framework) { .scope(WorkspaceScope) .service(EmbeddingService) .store(EmbeddingStore, [WorkspaceServerService]) - .entity(Embedding, [WorkspaceService, EmbeddingStore]); + .entity(EmbeddingEnabled, [WorkspaceService, EmbeddingStore]) + .entity(AdditionalAttachments, [WorkspaceService, EmbeddingStore]) + .entity(IgnoredDocs, [WorkspaceService, EmbeddingStore]) + .entity(EmbeddingProgress, [WorkspaceService, EmbeddingStore]); } export { EmbeddingSettings } from './view'; diff --git a/packages/frontend/core/src/modules/workspace-indexer-embedding/services/embedding.ts b/packages/frontend/core/src/modules/workspace-indexer-embedding/services/embedding.ts index fe3876d1e..3bb3a1ec7 100644 --- a/packages/frontend/core/src/modules/workspace-indexer-embedding/services/embedding.ts +++ b/packages/frontend/core/src/modules/workspace-indexer-embedding/services/embedding.ts @@ -1,7 +1,13 @@ import { Service } from '@toeverything/infra'; -import { Embedding } from '../entities/embedding'; +import { AdditionalAttachments } from '../entities/additional-attachments'; +import { EmbeddingEnabled } from '../entities/embedding-enabled'; +import { EmbeddingProgress } from '../entities/embedding-progress'; +import { IgnoredDocs } from '../entities/ignored-docs'; export class EmbeddingService extends Service { - embedding = this.framework.createEntity(Embedding); + embeddingEnabled = this.framework.createEntity(EmbeddingEnabled); + additionalAttachments = this.framework.createEntity(AdditionalAttachments); + ignoredDocs = this.framework.createEntity(IgnoredDocs); + embeddingProgress = this.framework.createEntity(EmbeddingProgress); } diff --git a/packages/frontend/core/src/modules/workspace-indexer-embedding/utils.ts b/packages/frontend/core/src/modules/workspace-indexer-embedding/utils.ts index 927e9fdc5..5a64da7f1 100644 --- a/packages/frontend/core/src/modules/workspace-indexer-embedding/utils.ts +++ b/packages/frontend/core/src/modules/workspace-indexer-embedding/utils.ts @@ -1,3 +1,5 @@ +import { DebugLogger } from '@affine/debug'; + import type { AttachmentFile, ErrorAttachmentFile, @@ -36,3 +38,5 @@ export function getAttachmentId(attachment: AttachmentFile): string { } return attachment.localId; } + +export const logger = new DebugLogger('WorkspaceEmbedding'); diff --git a/packages/frontend/core/src/modules/workspace-indexer-embedding/view/embedding-settings.tsx b/packages/frontend/core/src/modules/workspace-indexer-embedding/view/embedding-settings.tsx index 9f4352777..3598bf66f 100644 --- a/packages/frontend/core/src/modules/workspace-indexer-embedding/view/embedding-settings.tsx +++ b/packages/frontend/core/src/modules/workspace-indexer-embedding/view/embedding-settings.tsx @@ -24,22 +24,26 @@ interface EmbeddingSettingsProps {} export const EmbeddingSettings: React.FC = () => { const t = useI18n(); const embeddingService = useService(EmbeddingService); - const embeddingEnabled = useLiveData(embeddingService.embedding.enabled$); - const { totalCount } = useLiveData(embeddingService.embedding.attachments$); - const attachments = useLiveData( - embeddingService.embedding.mergedAttachments$ - ); - const ignoredDocs = useLiveData(embeddingService.embedding.ignoredDocs$); - const embeddingProgress = useLiveData( - embeddingService.embedding.embeddingProgress$ - ); - - const isIgnoredDocsLoading = useLiveData( - embeddingService.embedding.isIgnoredDocsLoading$ - ); const workspaceDialogService = useService(WorkspaceDialogService); + + const embeddingEnabled = useLiveData( + embeddingService.embeddingEnabled.enabled$ + ); + const attachments = useLiveData( + embeddingService.additionalAttachments.mergedAttachments$ + ); + const ignoredDocs = useLiveData(embeddingService.ignoredDocs.docs$); + const embeddingProgress = useLiveData( + embeddingService.embeddingProgress.progress$ + ); + const { totalCount } = useLiveData( + embeddingService.additionalAttachments.attachments$ + ); + const isIgnoredDocsLoading = useLiveData( + embeddingService.ignoredDocs.loading$ + ); const isEnabledLoading = useLiveData( - embeddingService.embedding.isEnabledLoading$ + embeddingService.embeddingEnabled.loading$ ); const handleEmbeddingToggle = useCallback( @@ -50,7 +54,7 @@ export const EmbeddingSettings: React.FC = () => { option: checked ? 'on' : 'off', }); - embeddingService.embedding.setEnabled(checked).catch(error => { + embeddingService.embeddingEnabled.setEnabled(checked).catch(error => { const err = UserFriendlyError.fromAny(error); notify.error({ title: @@ -61,7 +65,7 @@ export const EmbeddingSettings: React.FC = () => { }); }); }, - [embeddingService.embedding, t] + [embeddingService.embeddingEnabled, t] ); const handleAttachmentUpload = useCallback( @@ -71,34 +75,36 @@ export const EmbeddingSettings: React.FC = () => { control: 'Select doc', docType: file.type, }); - embeddingService.embedding.addAttachments([file]); + embeddingService.additionalAttachments.addAttachments([file]); }, - [embeddingService.embedding] + [embeddingService.additionalAttachments] ); const handleAttachmentsDelete = useCallback( (fileId: string) => { - embeddingService.embedding.removeAttachment(fileId).catch(error => { - const err = UserFriendlyError.fromAny(error); - notify.error({ - title: - t[ - 'com.affine.settings.workspace.indexer-embedding.embedding.remove-attachment.error' - ](), - message: t[`error.${err.name}`](err.data), + embeddingService.additionalAttachments + .removeAttachment(fileId) + .catch(error => { + const err = UserFriendlyError.fromAny(error); + notify.error({ + title: + t[ + 'com.affine.settings.workspace.indexer-embedding.embedding.remove-attachment.error' + ](), + message: t[`error.${err.name}`](err.data), + }); }); - }); }, - [embeddingService.embedding, t] + [embeddingService.additionalAttachments, t] ); const handleAttachmentsPageChange = useCallback( (offset: number) => { - embeddingService.embedding.getAttachments({ + embeddingService.additionalAttachments.getAttachments({ offset, }); }, - [embeddingService.embedding] + [embeddingService.additionalAttachments] ); const handleSelectDoc = useCallback(() => { @@ -122,7 +128,7 @@ export const EmbeddingSettings: React.FC = () => { }); const add = selectedIds.filter(id => !initialIds?.includes(id)); const remove = initialIds?.filter(id => !selectedIds.includes(id)); - embeddingService.embedding + embeddingService.ignoredDocs .updateIgnoredDocs({ add, remove }) .catch(error => { const err = UserFriendlyError.fromAny(error); @@ -140,24 +146,29 @@ export const EmbeddingSettings: React.FC = () => { ignoredDocs, isIgnoredDocsLoading, workspaceDialogService, - embeddingService.embedding, + embeddingService.ignoredDocs, t, ]); useEffect(() => { - embeddingService.embedding.startEmbeddingProgressPolling(); - embeddingService.embedding.getEnabled(); - embeddingService.embedding.getAttachments({ + embeddingService.embeddingProgress.startEmbeddingProgressPolling(); + embeddingService.embeddingEnabled.getEnabled(); + embeddingService.additionalAttachments.getAttachments({ first: COUNT_PER_PAGE, after: null, }); - embeddingService.embedding.getIgnoredDocs(); - embeddingService.embedding.getEmbeddingProgress(); + embeddingService.ignoredDocs.getIgnoredDocs(); + embeddingService.embeddingProgress.getEmbeddingProgress(); return () => { - embeddingService.embedding.stopEmbeddingProgressPolling(); + embeddingService.embeddingProgress.stopEmbeddingProgressPolling(); }; - }, [embeddingService.embedding]); + }, [ + embeddingService.embeddingProgress, + embeddingService.embeddingEnabled, + embeddingService.additionalAttachments, + embeddingService.ignoredDocs, + ]); return ( <>