feat(core): add ai model switch ui (#12266)

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

![截屏2025-05-14 11.32.41.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/sJGviKxfE3Ap685cl5bj/b92d5c32-fa5a-4afd-93e6-3699347575be.png)

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

## Summary by CodeRabbit

- **New Features**
  - Introduced AI model switching in chat, allowing users to select from multiple AI models during conversations.
  - Added a floating menu for easy AI model selection within the chat interface.
  - Enabled visibility of the AI model switcher through a new experimental feature flag, configurable in workspace settings (canary builds only).

- **Enhancements**
  - Improved session management in the chat panel for smoother model switching and state handling.
  - Updated localization to support the new AI model switch feature in settings.

- **Bug Fixes**
  - None.

- **Chores**
  - Registered new components and services to support AI model switching functionality.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
akumatus
2025-05-15 06:29:37 +00:00
parent 6a13d69dea
commit 9fee8147cb
18 changed files with 271 additions and 25 deletions

View File

@@ -7,6 +7,7 @@ import { FeatureFlagService } from '../feature-flag';
import { GlobalStateService } from '../storage';
import { AIButtonProvider } from './provider/ai-button';
import { AIButtonService } from './services/ai-button';
import { AIModelSwitchService } from './services/model-switch';
import { AINetworkSearchService } from './services/network-search';
import { AIReasoningService } from './services/reasoning';
@@ -26,3 +27,7 @@ export function configureAINetworkSearchModule(framework: Framework) {
export function configureAIReasoningModule(framework: Framework) {
framework.service(AIReasoningService, [GlobalStateService]);
}
export function configureAIModelSwitchModule(framework: Framework) {
framework.service(AIModelSwitchService, [FeatureFlagService]);
}

View File

@@ -0,0 +1,26 @@
import {
createSignalFromObservable,
type Signal,
} from '@blocksuite/affine/shared/utils';
import { Service } from '@toeverything/infra';
import type { FeatureFlagService } from '../../feature-flag';
export class AIModelSwitchService extends Service {
constructor(private readonly featureFlagService: FeatureFlagService) {
super();
const { signal: visible, cleanup: visibleCleanup } =
createSignalFromObservable<boolean | undefined>(
this._visible$,
undefined
);
this.visible = visible;
this.disposables.push(visibleCleanup);
}
visible: Signal<boolean | undefined>;
private readonly _visible$ =
this.featureFlagService.flags.enable_ai_model_switch.$;
}

View File

@@ -26,6 +26,15 @@ export const AFFINE_FLAGS = {
configurable: false,
defaultState: true,
},
enable_ai_model_switch: {
category: 'affine',
displayName:
'com.affine.settings.workspace.experimental-features.enable-ai-model-switch.name',
description:
'com.affine.settings.workspace.experimental-features.enable-ai-model-switch.description',
configurable: isCanaryBuild,
defaultState: isCanaryBuild,
},
enable_edgeless_text: {
category: 'blocksuite',
bsFlag: 'enable_edgeless_text',

View File

@@ -3,6 +3,7 @@ import { type Framework } from '@toeverything/infra';
import {
configureAIButtonModule,
configureAIModelSwitchModule,
configureAINetworkSearchModule,
configureAIReasoningModule,
} from './ai-button';
@@ -105,6 +106,7 @@ export function configureCommonModules(framework: Framework) {
configureCommonGlobalStorageImpls(framework);
configureAINetworkSearchModule(framework);
configureAIReasoningModule(framework);
configureAIModelSwitchModule(framework);
configureAIButtonModule(framework);
configureTemplateDocModule(framework);
configureBlobManagementModule(framework);