mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-18 10:36:22 +08:00
feat(core): support ai network search (#9357)
### What Changed?
- Add `PerplexityProvider` in backend.
- Update session prompt name if user toggle network search mode in chat panel.
- Add experimental flag for AI network search feature.
- Add unit tests and e2e tests.
Search results are streamed and appear word for word:
<div class='graphite__hidden'>
<div>🎥 Video uploaded on Graphite:</div>
<a href="https://app.graphite.dev/media/video/sJGviKxfE3Ap685cl5bj/56f6ec7b-4b21-405f-9612-43e083f6fb84.mov">
<img src="https://app.graphite.dev/api/v1/graphite/video/thumbnail/sJGviKxfE3Ap685cl5bj/56f6ec7b-4b21-405f-9612-43e083f6fb84.mov">
</a>
</div>
<video src="https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/sJGviKxfE3Ap685cl5bj/56f6ec7b-4b21-405f-9612-43e083f6fb84.mov">录屏2024-12-27 18.58.40.mov</video>
Click the little globe icon to manually turn on/off Internet search:
<div class='graphite__hidden'>
<div>🎥 Video uploaded on Graphite:</div>
<a href="https://app.graphite.dev/media/video/sJGviKxfE3Ap685cl5bj/778f1406-bf29-498e-a90d-7dad813392d1.mov">
<img src="https://app.graphite.dev/api/v1/graphite/video/thumbnail/sJGviKxfE3Ap685cl5bj/778f1406-bf29-498e-a90d-7dad813392d1.mov">
</a>
</div>
<video src="https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/sJGviKxfE3Ap685cl5bj/778f1406-bf29-498e-a90d-7dad813392d1.mov">录屏2024-12-27 19.01.16.mov</video>
When there is an image, it will automatically switch to the openai model:
<div class='graphite__hidden'>
<div>🎥 Video uploaded on Graphite:</div>
<a href="https://app.graphite.dev/media/video/sJGviKxfE3Ap685cl5bj/56431d8e-75e1-4d84-ab4a-b6636042cc6a.mov">
<img src="https://app.graphite.dev/api/v1/graphite/video/thumbnail/sJGviKxfE3Ap685cl5bj/56431d8e-75e1-4d84-ab4a-b6636042cc6a.mov">
</a>
</div>
<video src="https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/sJGviKxfE3Ap685cl5bj/56431d8e-75e1-4d84-ab4a-b6636042cc6a.mov">录屏2024-12-27 19.02.13.mov</video>
This commit is contained in:
@@ -3,11 +3,21 @@ export { AIButtonService } from './services/ai-button';
|
||||
|
||||
import type { Framework } from '@toeverything/infra';
|
||||
|
||||
import { FeatureFlagService } from '../feature-flag';
|
||||
import { GlobalStateService } from '../storage';
|
||||
import { AIButtonProvider } from './provider/ai-button';
|
||||
import { AIButtonService } from './services/ai-button';
|
||||
import { AINetworkSearchService } from './services/network-search';
|
||||
|
||||
export const configureAIButtonModule = (framework: Framework) => {
|
||||
framework.service(AIButtonService, container => {
|
||||
return new AIButtonService(container.getOptional(AIButtonProvider));
|
||||
});
|
||||
};
|
||||
|
||||
export function configureAINetworkSearchModule(framework: Framework) {
|
||||
framework.service(AINetworkSearchService, [
|
||||
GlobalStateService,
|
||||
FeatureFlagService,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import {
|
||||
createSignalFromObservable,
|
||||
type Signal,
|
||||
} from '@blocksuite/affine-shared/utils';
|
||||
import { LiveData, Service } from '@toeverything/infra';
|
||||
|
||||
import type { FeatureFlagService } from '../../feature-flag';
|
||||
import type { GlobalStateService } from '../../storage';
|
||||
|
||||
const AI_NETWORK_SEARCH_KEY = 'AINetworkSearch';
|
||||
|
||||
export class AINetworkSearchService extends Service {
|
||||
constructor(
|
||||
private readonly globalStateService: GlobalStateService,
|
||||
private readonly featureFlagService: FeatureFlagService
|
||||
) {
|
||||
super();
|
||||
|
||||
const { signal: enabled, cleanup: enabledCleanup } =
|
||||
createSignalFromObservable<boolean | undefined>(this._enabled$, false);
|
||||
this.enabled = enabled;
|
||||
this.disposables.push(enabledCleanup);
|
||||
|
||||
const { signal: visible, cleanup: visibleCleanup } =
|
||||
createSignalFromObservable<boolean | undefined>(this._visible$, false);
|
||||
this.visible = visible;
|
||||
this.disposables.push(visibleCleanup);
|
||||
}
|
||||
|
||||
visible: Signal<boolean | undefined>;
|
||||
|
||||
enabled: Signal<boolean | undefined>;
|
||||
|
||||
private readonly _visible$ =
|
||||
this.featureFlagService.flags.enable_ai_network_search.$;
|
||||
|
||||
private readonly _enabled$ = LiveData.from(
|
||||
this.globalStateService.globalState.watch<boolean>(AI_NETWORK_SEARCH_KEY),
|
||||
false
|
||||
);
|
||||
|
||||
setEnabled = (enabled: boolean) => {
|
||||
this.globalStateService.globalState.set(AI_NETWORK_SEARCH_KEY, enabled);
|
||||
};
|
||||
}
|
||||
@@ -16,6 +16,15 @@ export const AFFINE_FLAGS = {
|
||||
configurable: true,
|
||||
defaultState: true,
|
||||
},
|
||||
enable_ai_network_search: {
|
||||
category: 'affine',
|
||||
displayName:
|
||||
'com.affine.settings.workspace.experimental-features.enable-ai-network-search.name',
|
||||
description:
|
||||
'com.affine.settings.workspace.experimental-features.enable-ai-network-search.description',
|
||||
configurable: true,
|
||||
defaultState: false,
|
||||
},
|
||||
enable_database_full_width: {
|
||||
category: 'blocksuite',
|
||||
bsFlag: 'enable_database_full_width',
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { configureQuotaModule } from '@affine/core/modules/quota';
|
||||
import { type Framework } from '@toeverything/infra';
|
||||
|
||||
import { configureAIButtonModule } from './ai-button';
|
||||
import {
|
||||
configureAIButtonModule,
|
||||
configureAINetworkSearchModule,
|
||||
} from './ai-button';
|
||||
import { configureAppSidebarModule } from './app-sidebar';
|
||||
import { configAtMenuConfigModule } from './at-menu-config';
|
||||
import { configureCloudModule } from './cloud';
|
||||
@@ -89,5 +92,6 @@ export function configureCommonModules(framework: Framework) {
|
||||
configAtMenuConfigModule(framework);
|
||||
configureDndModule(framework);
|
||||
configureCommonGlobalStorageImpls(framework);
|
||||
configureAINetworkSearchModule(framework);
|
||||
configureAIButtonModule(framework);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user