feat(core): add ai playground feature flag and remove model switch feature flag (#12934)

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

## Summary by CodeRabbit

* **New Features**
* Added support for the "AI Playground" experimental feature, including
new settings and localization entries.

* **Refactor**
* Renamed configuration and service references from "Model Switch" to
"Playground" across the AI chat and playground interfaces.
* Updated feature flag from "enable_ai_model_switch" to
"enable_ai_playground" for consistency.

* **Bug Fixes**
* The "Model" submenu in AI chat preferences is now always visible,
simplifying menu options.

* **Chores**
  * Removed outdated Claude model options from the chat prompt.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Wu Yue 2025-06-26 17:04:57 +08:00 committed by GitHub
parent 5e193b58c0
commit 2171d1bfe2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 52 additions and 64 deletions

View File

@ -1650,10 +1650,6 @@ const CHAT_PROMPT: Omit<Prompt, 'name'> = {
'gpt-4.1',
'o3',
'o4-mini',
'claude-opus-4-20250514',
'claude-sonnet-4-20250514',
'claude-3-7-sonnet-20250219',
'claude-3-5-sonnet-20241022',
'gemini-2.5-flash',
'gemini-2.5-pro',
'claude-opus-4@20250514',

View File

@ -20,8 +20,8 @@ import type {
SearchMenuConfig,
} from '../components/ai-chat-chips';
import type {
AIModelSwitchConfig,
AINetworkSearchConfig,
AIPlaygroundConfig,
AIReasoningConfig,
} from '../components/ai-chat-input';
import {
@ -224,7 +224,7 @@ export class ChatPanel extends SignalWatcher(
accessor reasoningConfig!: AIReasoningConfig;
@property({ attribute: false })
accessor modelSwitchConfig!: AIModelSwitchConfig;
accessor playgroundConfig!: AIPlaygroundConfig;
@property({ attribute: false })
accessor appSidebarConfig!: AppSidebarConfig;
@ -298,7 +298,7 @@ export class ChatPanel extends SignalWatcher(
.doc=${this.doc}
.networkSearchConfig=${this.networkSearchConfig}
.reasoningConfig=${this.reasoningConfig}
.modelSwitchConfig=${this.modelSwitchConfig}
.playgroundConfig=${this.playgroundConfig}
.appSidebarConfig=${this.appSidebarConfig}
.searchMenuConfig=${this.searchMenuConfig}
.docDisplayConfig=${this.docDisplayConfig}
@ -437,7 +437,7 @@ export class ChatPanel extends SignalWatcher(
>`
: 'AFFiNE AI'}
</div>
${this.modelSwitchConfig.visible.value
${this.playgroundConfig.visible.value
? html`
<div class="chat-panel-playground" @click=${this._openPlayground}>
${CenterPeekIcon()}
@ -478,7 +478,7 @@ export class ChatPanel extends SignalWatcher(
.isVisible=${this._isSidebarOpen}
.networkSearchConfig=${this.networkSearchConfig}
.reasoningConfig=${this.reasoningConfig}
.modelSwitchConfig=${this.modelSwitchConfig}
.playgroundConfig=${this.playgroundConfig}
.docDisplayConfig=${this.docDisplayConfig}
.searchMenuConfig=${this.searchMenuConfig}
.trackOptions=${{

View File

@ -29,7 +29,6 @@ import type {
import { isCollectionChip, isDocChip, isTagChip } from '../ai-chat-chips';
import type {
AIChatInputContext,
AIModelSwitchConfig,
AINetworkSearchConfig,
AIReasoningConfig,
} from '../ai-chat-input';
@ -93,9 +92,6 @@ export class AIChatComposer extends SignalWatcher(
@property({ attribute: false })
accessor searchMenuConfig!: SearchMenuConfig;
@property({ attribute: false })
accessor modelSwitchConfig!: AIModelSwitchConfig;
@property({ attribute: false })
accessor onChatSuccess: (() => void) | undefined;
@ -151,7 +147,6 @@ export class AIChatComposer extends SignalWatcher(
.updateContext=${this.updateContext}
.networkSearchConfig=${this.networkSearchConfig}
.reasoningConfig=${this.reasoningConfig}
.modelSwitchConfig=${this.modelSwitchConfig}
.docDisplayConfig=${this.docDisplayConfig}
.onChatSuccess=${this.onChatSuccess}
.trackOptions=${this.trackOptions}

View File

@ -27,7 +27,6 @@ import {
import { MAX_IMAGE_COUNT } from './const';
import type {
AIChatInputContext,
AIModelSwitchConfig,
AINetworkSearchConfig,
AIReasoningConfig,
} from './type';
@ -320,9 +319,6 @@ export class AIChatInput extends SignalWatcher(
@property({ attribute: false })
accessor reasoningConfig!: AIReasoningConfig;
@property({ attribute: false })
accessor modelSwitchConfig: AIModelSwitchConfig | undefined = undefined;
@property({ attribute: false })
accessor docDisplayConfig!: DocDisplayConfig;
@ -442,7 +438,6 @@ export class AIChatInput extends SignalWatcher(
</div>
<div class="chat-input-footer-spacer"></div>
<chat-input-preference
.modelSwitchConfig=${this.modelSwitchConfig}
.session=${this.session}
.onModelChange=${this._handleModelChange}
.modelId=${this.modelId}

View File

@ -15,8 +15,6 @@ import { ShadowlessElement } from '@blocksuite/std';
import { css, html } from 'lit';
import { property } from 'lit/decorators.js';
import type { AIModelSwitchConfig } from './type';
export class ChatInputPreference extends SignalWatcher(
WithDisposable(ShadowlessElement)
) {
@ -51,10 +49,6 @@ export class ChatInputPreference extends SignalWatcher(
@property({ attribute: false })
accessor session!: CopilotSessionType | undefined;
// --------- model props start ---------
@property({ attribute: false })
accessor modelSwitchConfig: AIModelSwitchConfig | undefined = undefined;
@property({ attribute: false })
accessor onModelChange: ((modelId: string) => void) | undefined;
@ -96,22 +90,20 @@ export class ChatInputPreference extends SignalWatcher(
const searchItems = [];
// model switch
if (this.modelSwitchConfig?.visible.value) {
modelItems.push(
menu.subMenu({
name: 'Model',
prefix: AiOutlineIcon(),
options: {
items: (this.session?.optionalModels ?? []).map(modelId => {
return menu.action({
name: modelId,
select: () => this._onModelChange(modelId),
});
}),
},
})
);
}
modelItems.push(
menu.subMenu({
name: 'Model',
prefix: AiOutlineIcon(),
options: {
items: (this.session?.optionalModels ?? []).map(modelId => {
return menu.action({
name: modelId,
select: () => this._onModelChange(modelId),
});
}),
},
})
);
modelItems.push(
menu.toggleSwitch({

View File

@ -14,7 +14,7 @@ export interface AIReasoningConfig {
setEnabled: (state: boolean) => void;
}
export interface AIModelSwitchConfig {
export interface AIPlaygroundConfig {
visible: Signal<boolean | undefined>;
}

View File

@ -18,8 +18,8 @@ import type { ChatPanelMessages } from '../../chat-panel/chat-panel-messages';
import { AIProvider } from '../../provider';
import type { DocDisplayConfig, SearchMenuConfig } from '../ai-chat-chips';
import type {
AIModelSwitchConfig,
AINetworkSearchConfig,
AIPlaygroundConfig,
AIReasoningConfig,
} from '../ai-chat-input';
import {
@ -136,7 +136,7 @@ export class PlaygroundChat extends SignalWatcher(
accessor reasoningConfig!: AIReasoningConfig;
@property({ attribute: false })
accessor modelSwitchConfig!: AIModelSwitchConfig;
accessor playgroundConfig!: AIPlaygroundConfig;
@property({ attribute: false })
accessor appSidebarConfig!: AppSidebarConfig;
@ -320,7 +320,7 @@ export class PlaygroundChat extends SignalWatcher(
.isVisible=${this._isVisible}
.networkSearchConfig=${this.networkSearchConfig}
.reasoningConfig=${this.reasoningConfig}
.modelSwitchConfig=${this.modelSwitchConfig}
.playgroundConfig=${this.playgroundConfig}
.docDisplayConfig=${this.docDisplayConfig}
.searchMenuConfig=${this.searchMenuConfig}
></ai-chat-composer>

View File

@ -12,8 +12,8 @@ import type { AppSidebarConfig } from '../../chat-panel/chat-config';
import { AIProvider } from '../../provider';
import type { DocDisplayConfig, SearchMenuConfig } from '../ai-chat-chips';
import type {
AIModelSwitchConfig,
AINetworkSearchConfig,
AIPlaygroundConfig,
AIReasoningConfig,
} from '../ai-chat-input';
@ -66,7 +66,7 @@ export class PlaygroundContent extends SignalWatcher(
accessor reasoningConfig!: AIReasoningConfig;
@property({ attribute: false })
accessor modelSwitchConfig!: AIModelSwitchConfig;
accessor playgroundConfig!: AIPlaygroundConfig;
@property({ attribute: false })
accessor appSidebarConfig!: AppSidebarConfig;
@ -329,7 +329,7 @@ export class PlaygroundContent extends SignalWatcher(
.doc=${this.doc}
.networkSearchConfig=${this.networkSearchConfig}
.reasoningConfig=${this.reasoningConfig}
.modelSwitchConfig=${this.modelSwitchConfig}
.playgroundConfig=${this.playgroundConfig}
.appSidebarConfig=${this.appSidebarConfig}
.searchMenuConfig=${this.searchMenuConfig}
.docDisplayConfig=${this.docDisplayConfig}

View File

@ -1,6 +1,6 @@
// packages/frontend/core/src/blocksuite/ai/hooks/useChatPanelConfig.ts
import { AIModelSwitchService } from '@affine/core/modules/ai-button/services/model-switch';
import { AINetworkSearchService } from '@affine/core/modules/ai-button/services/network-search';
import { AIPlaygroundService } from '@affine/core/modules/ai-button/services/playground';
import { AIReasoningService } from '@affine/core/modules/ai-button/services/reasoning';
import { CollectionService } from '@affine/core/modules/collection';
import { DocsService } from '@affine/core/modules/doc';
@ -22,7 +22,7 @@ export function useAIChatConfig() {
const searchService = framework.get(AINetworkSearchService);
const reasoningService = framework.get(AIReasoningService);
const modelSwitchService = framework.get(AIModelSwitchService);
const playgroundService = framework.get(AIPlaygroundService);
const docDisplayMetaService = framework.get(DocDisplayMetaService);
const workspaceService = framework.get(WorkspaceService);
const searchMenuService = framework.get(SearchMenuService);
@ -42,8 +42,8 @@ export function useAIChatConfig() {
setEnabled: reasoningService.setEnabled,
};
const modelSwitchConfig = {
visible: modelSwitchService.visible,
const playgroundConfig = {
visible: playgroundService.visible,
};
const docDisplayConfig = {
@ -130,6 +130,6 @@ export function useAIChatConfig() {
reasoningConfig,
docDisplayConfig,
searchMenuConfig,
modelSwitchConfig,
playgroundConfig,
};
}

View File

@ -47,7 +47,7 @@ export const EditorChatPanel = forwardRef(function EditorChatPanel(
searchMenuConfig,
networkSearchConfig,
reasoningConfig,
modelSwitchConfig,
playgroundConfig,
} = useAIChatConfig();
useEffect(() => {
@ -74,7 +74,7 @@ export const EditorChatPanel = forwardRef(function EditorChatPanel(
chatPanelRef.current.searchMenuConfig = searchMenuConfig;
chatPanelRef.current.networkSearchConfig = networkSearchConfig;
chatPanelRef.current.reasoningConfig = reasoningConfig;
chatPanelRef.current.modelSwitchConfig = modelSwitchConfig;
chatPanelRef.current.playgroundConfig = playgroundConfig;
chatPanelRef.current.extensions = editor.host.std
.get(ViewExtensionManagerIdentifier)
.get('preview-page');
@ -109,7 +109,7 @@ export const EditorChatPanel = forwardRef(function EditorChatPanel(
networkSearchConfig,
searchMenuConfig,
reasoningConfig,
modelSwitchConfig,
playgroundConfig,
]);
return <div className={styles.root} ref={containerRef} />;

View File

@ -7,8 +7,8 @@ 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 { AIPlaygroundService } from './services/playground';
import { AIReasoningService } from './services/reasoning';
export const configureAIButtonModule = (framework: Framework) => {
@ -28,6 +28,6 @@ export function configureAIReasoningModule(framework: Framework) {
framework.service(AIReasoningService, [GlobalStateService]);
}
export function configureAIModelSwitchModule(framework: Framework) {
framework.service(AIModelSwitchService, [FeatureFlagService]);
export function configureAIPlaygroundModule(framework: Framework) {
framework.service(AIPlaygroundService, [FeatureFlagService]);
}

View File

@ -6,7 +6,7 @@ import { Service } from '@toeverything/infra';
import type { FeatureFlagService } from '../../feature-flag';
export class AIModelSwitchService extends Service {
export class AIPlaygroundService extends Service {
constructor(private readonly featureFlagService: FeatureFlagService) {
super();
@ -22,5 +22,5 @@ export class AIModelSwitchService extends Service {
visible: Signal<boolean | undefined>;
private readonly _visible$ =
this.featureFlagService.flags.enable_ai_model_switch.$;
this.featureFlagService.flags.enable_ai_playground.$;
}

View File

@ -26,7 +26,7 @@ export const AFFINE_FLAGS = {
configurable: false,
defaultState: true,
},
enable_ai_model_switch: {
enable_ai_playground: {
category: 'affine',
displayName:
'com.affine.settings.workspace.experimental-features.enable-ai-model-switch.name',

View File

@ -3,8 +3,8 @@ import { type Framework } from '@toeverything/infra';
import {
configureAIButtonModule,
configureAIModelSwitchModule,
configureAINetworkSearchModule,
configureAIPlaygroundModule,
configureAIReasoningModule,
} from './ai-button';
import { configureAppSidebarModule } from './app-sidebar';
@ -107,7 +107,7 @@ export function configureCommonModules(framework: Framework) {
configureCommonGlobalStorageImpls(framework);
configureAINetworkSearchModule(framework);
configureAIReasoningModule(framework);
configureAIModelSwitchModule(framework);
configureAIPlaygroundModule(framework);
configureAIButtonModule(framework);
configureTemplateDocModule(framework);
configureBlobManagementModule(framework);

View File

@ -5692,6 +5692,14 @@ export function useAFFiNEI18N(): {
* `Enable or disable AI model switch feature.`
*/
["com.affine.settings.workspace.experimental-features.enable-ai-model-switch.description"](): string;
/**
* `Enable AI Playground`
*/
["com.affine.settings.workspace.experimental-features.enable-ai-playground.name"](): string;
/**
* `Enable or disable AI playground feature.`
*/
["com.affine.settings.workspace.experimental-features.enable-ai-playground.description"](): string;
/**
* `Database Full Width`
*/

View File

@ -1423,6 +1423,8 @@
"com.affine.settings.workspace.experimental-features.enable-ai-network-search.description": "Enable or disable AI Network Search feature.",
"com.affine.settings.workspace.experimental-features.enable-ai-model-switch.name": "Enable AI Model Switch",
"com.affine.settings.workspace.experimental-features.enable-ai-model-switch.description": "Enable or disable AI model switch feature.",
"com.affine.settings.workspace.experimental-features.enable-ai-playground.name": "Enable AI Playground",
"com.affine.settings.workspace.experimental-features.enable-ai-playground.description": "Enable or disable AI playground feature.",
"com.affine.settings.workspace.experimental-features.enable-database-full-width.name": "Database Full Width",
"com.affine.settings.workspace.experimental-features.enable-database-full-width.description": "The database will be displayed in full-width mode.",
"com.affine.settings.workspace.experimental-features.enable-database-attachment-note.name": "Database Attachment Note",