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

@@ -1066,6 +1066,7 @@ const chat: Prompt[] = [
name: 'Chat With AFFiNE AI',
model: 'gpt-4.1',
optionalModels: [
'gpt-4.1',
'o3',
'o4-mini',
'claude-3-7-sonnet-20250219',

View File

@@ -135,7 +135,7 @@ export class AnthropicProvider
messages: msgs,
abortSignal: options.signal,
providerOptions: {
anthropic: this.getAnthropicOptions(options),
anthropic: this.getAnthropicOptions(options, model),
},
tools: this.getTools(),
maxSteps: this.MAX_STEPS,
@@ -167,7 +167,7 @@ export class AnthropicProvider
messages: msgs,
abortSignal: options.signal,
providerOptions: {
anthropic: this.getAnthropicOptions(options),
anthropic: this.getAnthropicOptions(options, model),
},
tools: this.getTools(),
maxSteps: this.MAX_STEPS,
@@ -256,9 +256,9 @@ export class AnthropicProvider
};
}
private getAnthropicOptions(options: CopilotChatOptions) {
private getAnthropicOptions(options: CopilotChatOptions, model: string) {
const result: AnthropicProviderOptions = {};
if (options?.reasoning) {
if (options?.reasoning && this.isThinkingModel(model)) {
result.thinking = {
type: 'enabled',
budgetTokens: 12000,
@@ -282,4 +282,8 @@ export class AnthropicProvider
private markAsCallout(text: string) {
return text.replaceAll('\n', '\n> ');
}
private isThinkingModel(model: string) {
return model.startsWith('claude-3-7-sonnet');
}
}

View File

@@ -200,8 +200,7 @@ export class OpenAIProvider
for (const tool of options.tools) {
switch (tool) {
case 'webSearch': {
// o series reasoning models
if (model.startsWith('o')) {
if (this.isReasoningModel(model)) {
tools.web_search_exa = createExaSearchTool(this.AFFiNEConfig);
tools.web_crawl_exa = createExaCrawlTool(this.AFFiNEConfig);
} else {
@@ -251,7 +250,7 @@ export class OpenAIProvider
: await generateText({
...commonParams,
providerOptions: {
openai: this.getOpenAIOptions(options),
openai: this.getOpenAIOptions(options, model),
},
tools: this.getTools(options, model),
maxSteps: this.MAX_STEPS,
@@ -284,7 +283,7 @@ export class OpenAIProvider
system,
messages: msgs,
providerOptions: {
openai: this.getOpenAIOptions(options),
openai: this.getOpenAIOptions(options, model),
},
tools: tools as OpenAITools,
maxSteps: this.MAX_STEPS,
@@ -454,9 +453,9 @@ export class OpenAIProvider
}
}
private getOpenAIOptions(options: CopilotChatOptions) {
private getOpenAIOptions(options: CopilotChatOptions, model: string) {
const result: OpenAIResponsesProviderOptions = {};
if (options?.reasoning) {
if (options?.reasoning && this.isReasoningModel(model)) {
result.reasoningEffort = 'medium';
result.reasoningSummary = 'detailed';
}
@@ -481,4 +480,9 @@ export class OpenAIProvider
private markAsCallout(text: string) {
return text.replaceAll('\n', '\n> ');
}
private isReasoningModel(model: string) {
// o series reasoning models
return model.startsWith('o');
}
}