feat(core): add ai workspace all docs switch (#13345)

Close [AI-397](https://linear.app/affine-design/issue/AI-397)

<img width="272" height="186" alt="截屏2025-07-29 11 54 20"
src="https://github.com/user-attachments/assets/e171fb57-66cf-4244-894d-c27b18cbe83a"
/>


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

* **New Features**
* Introduced an AI tools configuration service, allowing users to
customize AI tool usage (e.g., workspace search, reading docs) in chat
and AI features.
* Added a toggle in chat preferences for enabling or disabling
workspace-wide document search.
* AI chat components now respect user-configured tool settings across
chat, retry, and playground scenarios.

* **Improvements**
* Enhanced chat and AI interfaces to propagate and honor user tool
configuration throughout the frontend and backend.
* Made draft and tool configuration services optional and safely handled
their absence in chat components.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Wu Yue
2025-07-30 10:10:39 +08:00
committed by GitHub
parent 091bac1047
commit f7a094053e
23 changed files with 321 additions and 61 deletions
@@ -1,6 +1,10 @@
export { AIButtonProvider } from './provider/ai-button';
export { AIButtonService } from './services/ai-button';
export { AIDraftService } from './services/ai-draft';
export {
type AIToolsConfig,
AIToolsConfigService,
} from './services/tools-config';
import type { Framework } from '@toeverything/infra';
@@ -13,6 +17,7 @@ import { AIDraftService } from './services/ai-draft';
import { AINetworkSearchService } from './services/network-search';
import { AIPlaygroundService } from './services/playground';
import { AIReasoningService } from './services/reasoning';
import { AIToolsConfigService } from './services/tools-config';
export const configureAIButtonModule = (framework: Framework) => {
framework.service(AIButtonService, container => {
@@ -40,3 +45,7 @@ export function configureAIDraftModule(framework: Framework) {
.scope(WorkspaceScope)
.service(AIDraftService, [GlobalStateService, CacheStorage]);
}
export function configureAIToolsConfigModule(framework: Framework) {
framework.service(AIToolsConfigService, [GlobalStateService]);
}
@@ -0,0 +1,50 @@
import {
createSignalFromObservable,
type Signal,
} from '@blocksuite/affine/shared/utils';
import { LiveData, Service } from '@toeverything/infra';
import { map } from 'rxjs';
import type { GlobalStateService } from '../../storage';
const AI_TOOLS_CONFIG_KEY = 'AIToolsConfig';
export interface AIToolsConfig {
searchWorkspace?: boolean;
readingDocs?: boolean;
}
export class AIToolsConfigService extends Service {
constructor(private readonly globalStateService: GlobalStateService) {
super();
const { signal, cleanup: enabledCleanup } =
createSignalFromObservable<AIToolsConfig>(this.config$, {
searchWorkspace: true,
readingDocs: true,
});
this.config = signal;
this.disposables.push(enabledCleanup);
}
config: Signal<AIToolsConfig>;
private readonly config$ = LiveData.from(
this.globalStateService.globalState.watch<AIToolsConfig>(
AI_TOOLS_CONFIG_KEY
),
undefined
).pipe(
map(config => ({
searchWorkspace: config?.searchWorkspace ?? true,
readingDocs: config?.readingDocs ?? true,
}))
);
setConfig = (data: Partial<AIToolsConfig>) => {
this.globalStateService.globalState.set(AI_TOOLS_CONFIG_KEY, {
...this.config.value,
...data,
});
};
}
@@ -7,6 +7,7 @@ import {
configureAINetworkSearchModule,
configureAIPlaygroundModule,
configureAIReasoningModule,
configureAIToolsConfigModule,
} from './ai-button';
import { configureAppSidebarModule } from './app-sidebar';
import { configAtMenuConfigModule } from './at-menu-config';
@@ -112,6 +113,7 @@ export function configureCommonModules(framework: Framework) {
configureAIPlaygroundModule(framework);
configureAIButtonModule(framework);
configureAIDraftModule(framework);
configureAIToolsConfigModule(framework);
configureTemplateDocModule(framework);
configureBlobManagementModule(framework);
configureMediaModule(framework);
@@ -2,6 +2,10 @@ import { toReactNode } from '@affine/component';
import { AIChatBlockPeekViewTemplate } from '@affine/core/blocksuite/ai';
import type { AIChatBlockModel } from '@affine/core/blocksuite/ai/blocks/ai-chat-block/model/ai-chat-model';
import { useAIChatConfig } from '@affine/core/components/hooks/affine/use-ai-chat-config';
import {
AIDraftService,
AIToolsConfigService,
} from '@affine/core/modules/ai-button';
import { WorkspaceDialogService } from '@affine/core/modules/dialogs';
import { FeatureFlagService } from '@affine/core/modules/feature-flag';
import type { EditorHost } from '@blocksuite/affine/std';
@@ -27,6 +31,8 @@ export const AIChatBlockPeekView = ({
const framework = useFramework();
const affineFeatureFlagService = framework.get(FeatureFlagService);
const affineWorkspaceDialogService = framework.get(WorkspaceDialogService);
const aiDraftService = framework.get(AIDraftService);
const aiToolsConfigService = framework.get(AIToolsConfigService);
return useMemo(() => {
const template = AIChatBlockPeekViewTemplate(
@@ -37,7 +43,9 @@ export const AIChatBlockPeekView = ({
networkSearchConfig,
reasoningConfig,
affineFeatureFlagService,
affineWorkspaceDialogService
affineWorkspaceDialogService,
aiDraftService,
aiToolsConfigService
);
return toReactNode(template);
}, [
@@ -49,5 +57,7 @@ export const AIChatBlockPeekView = ({
reasoningConfig,
affineFeatureFlagService,
affineWorkspaceDialogService,
aiDraftService,
aiToolsConfigService,
]);
};