feat(core): add reasoning icon button (#11941)
Close [AI-58](https://linear.app/affine-design/issue/AI-58). <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced a toggleable AI reasoning feature in the chat interface, allowing users to enable or disable advanced reasoning during AI chat interactions. - Added a new reasoning button to the chat input for quick access and control. - **Enhancements** - Improved chat configuration options to include reasoning settings, providing more flexibility for AI responses. - Streamlined network search and image upload interactions for a smoother user experience. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -134,6 +134,7 @@ declare global {
|
|||||||
sessionId?: string;
|
sessionId?: string;
|
||||||
isRootSession?: boolean;
|
isRootSession?: boolean;
|
||||||
mustSearch?: boolean;
|
mustSearch?: boolean;
|
||||||
|
reasoning?: boolean;
|
||||||
contexts?: {
|
contexts?: {
|
||||||
docs: AIDocContextOption[];
|
docs: AIDocContextOption[];
|
||||||
files: AIFileContextOption[];
|
files: AIFileContextOption[];
|
||||||
|
|||||||
@@ -17,7 +17,10 @@ import type {
|
|||||||
DocDisplayConfig,
|
DocDisplayConfig,
|
||||||
SearchMenuConfig,
|
SearchMenuConfig,
|
||||||
} from '../components/ai-chat-chips';
|
} from '../components/ai-chat-chips';
|
||||||
import type { AINetworkSearchConfig } from '../components/ai-chat-input';
|
import type {
|
||||||
|
AINetworkSearchConfig,
|
||||||
|
AIReasoningConfig,
|
||||||
|
} from '../components/ai-chat-input';
|
||||||
import { type HistoryMessage } from '../components/ai-chat-messages';
|
import { type HistoryMessage } from '../components/ai-chat-messages';
|
||||||
import { AIProvider } from '../provider';
|
import { AIProvider } from '../provider';
|
||||||
import { extractSelectedContent } from '../utils/extract';
|
import { extractSelectedContent } from '../utils/extract';
|
||||||
@@ -197,6 +200,9 @@ export class ChatPanel extends SignalWatcher(
|
|||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor networkSearchConfig!: AINetworkSearchConfig;
|
accessor networkSearchConfig!: AINetworkSearchConfig;
|
||||||
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
accessor reasoningConfig!: AIReasoningConfig;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor appSidebarConfig!: AppSidebarConfig;
|
accessor appSidebarConfig!: AppSidebarConfig;
|
||||||
|
|
||||||
@@ -415,6 +421,7 @@ export class ChatPanel extends SignalWatcher(
|
|||||||
.onHistoryCleared=${this._updateHistory}
|
.onHistoryCleared=${this._updateHistory}
|
||||||
.isVisible=${this._isSidebarOpen}
|
.isVisible=${this._isSidebarOpen}
|
||||||
.networkSearchConfig=${this.networkSearchConfig}
|
.networkSearchConfig=${this.networkSearchConfig}
|
||||||
|
.reasoningConfig=${this.reasoningConfig}
|
||||||
.docDisplayConfig=${this.docDisplayConfig}
|
.docDisplayConfig=${this.docDisplayConfig}
|
||||||
.searchMenuConfig=${this.searchMenuConfig}
|
.searchMenuConfig=${this.searchMenuConfig}
|
||||||
.trackOptions=${{
|
.trackOptions=${{
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import { isCollectionChip, isDocChip, isTagChip } from '../ai-chat-chips';
|
|||||||
import type {
|
import type {
|
||||||
AIChatInputContext,
|
AIChatInputContext,
|
||||||
AINetworkSearchConfig,
|
AINetworkSearchConfig,
|
||||||
|
AIReasoningConfig,
|
||||||
} from '../ai-chat-input';
|
} from '../ai-chat-input';
|
||||||
|
|
||||||
export class AIChatComposer extends SignalWatcher(
|
export class AIChatComposer extends SignalWatcher(
|
||||||
@@ -81,6 +82,9 @@ export class AIChatComposer extends SignalWatcher(
|
|||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor networkSearchConfig!: AINetworkSearchConfig;
|
accessor networkSearchConfig!: AINetworkSearchConfig;
|
||||||
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
accessor reasoningConfig!: AIReasoningConfig;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor searchMenuConfig!: SearchMenuConfig;
|
accessor searchMenuConfig!: SearchMenuConfig;
|
||||||
|
|
||||||
@@ -125,6 +129,7 @@ export class AIChatComposer extends SignalWatcher(
|
|||||||
.chatContextValue=${this.chatContextValue}
|
.chatContextValue=${this.chatContextValue}
|
||||||
.updateContext=${this.updateContext}
|
.updateContext=${this.updateContext}
|
||||||
.networkSearchConfig=${this.networkSearchConfig}
|
.networkSearchConfig=${this.networkSearchConfig}
|
||||||
|
.reasoningConfig=${this.reasoningConfig}
|
||||||
.docDisplayConfig=${this.docDisplayConfig}
|
.docDisplayConfig=${this.docDisplayConfig}
|
||||||
.cleanupHistories=${this._cleanupHistories}
|
.cleanupHistories=${this._cleanupHistories}
|
||||||
.onChatSuccess=${this.onChatSuccess}
|
.onChatSuccess=${this.onChatSuccess}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
CloseIcon,
|
CloseIcon,
|
||||||
ImageIcon,
|
ImageIcon,
|
||||||
PublishIcon,
|
PublishIcon,
|
||||||
|
ThinkingIcon,
|
||||||
} from '@blocksuite/icons/lit';
|
} from '@blocksuite/icons/lit';
|
||||||
import { css, html, LitElement, nothing } from 'lit';
|
import { css, html, LitElement, nothing } from 'lit';
|
||||||
import { property, query, state } from 'lit/decorators.js';
|
import { property, query, state } from 'lit/decorators.js';
|
||||||
@@ -30,7 +31,11 @@ import {
|
|||||||
isTagChip,
|
isTagChip,
|
||||||
} from '../ai-chat-chips/utils';
|
} from '../ai-chat-chips/utils';
|
||||||
import type { ChatMessage } from '../ai-chat-messages';
|
import type { ChatMessage } from '../ai-chat-messages';
|
||||||
import type { AIChatInputContext, AINetworkSearchConfig } from './type';
|
import type {
|
||||||
|
AIChatInputContext,
|
||||||
|
AINetworkSearchConfig,
|
||||||
|
AIReasoningConfig,
|
||||||
|
} from './type';
|
||||||
|
|
||||||
const MaximumImageCount = 32;
|
const MaximumImageCount = 32;
|
||||||
|
|
||||||
@@ -241,6 +246,9 @@ export class AIChatInput extends SignalWatcher(WithDisposable(LitElement)) {
|
|||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor networkSearchConfig!: AINetworkSearchConfig;
|
accessor networkSearchConfig!: AINetworkSearchConfig;
|
||||||
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
accessor reasoningConfig!: AIReasoningConfig;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor docDisplayConfig!: DocDisplayConfig;
|
accessor docDisplayConfig!: DocDisplayConfig;
|
||||||
|
|
||||||
@@ -259,16 +267,12 @@ export class AIChatInput extends SignalWatcher(WithDisposable(LitElement)) {
|
|||||||
private get _isNetworkActive() {
|
private get _isNetworkActive() {
|
||||||
return (
|
return (
|
||||||
!!this.networkSearchConfig.visible.value &&
|
!!this.networkSearchConfig.visible.value &&
|
||||||
!!this.networkSearchConfig.enabled.value &&
|
!!this.networkSearchConfig.enabled.value
|
||||||
!this._isNetworkDisabled
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private get _isNetworkDisabled() {
|
private get _isReasoningActive() {
|
||||||
return (
|
return !!this.reasoningConfig.enabled.value;
|
||||||
!!this.chatContextValue.images.length ||
|
|
||||||
!!this.chips.filter(chip => chip.state === 'finished').length
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private get _isClearDisabled() {
|
private get _isClearDisabled() {
|
||||||
@@ -299,7 +303,6 @@ export class AIChatInput extends SignalWatcher(WithDisposable(LitElement)) {
|
|||||||
const { images, status } = this.chatContextValue;
|
const { images, status } = this.chatContextValue;
|
||||||
const hasImages = images.length > 0;
|
const hasImages = images.length > 0;
|
||||||
const maxHeight = hasImages ? 272 + 2 : 200 + 2;
|
const maxHeight = hasImages ? 272 + 2 : 200 + 2;
|
||||||
const uploadDisabled = this._isNetworkActive;
|
|
||||||
return html` <div
|
return html` <div
|
||||||
class="chat-panel-input"
|
class="chat-panel-input"
|
||||||
data-if-focused=${this.focused}
|
data-if-focused=${this.focused}
|
||||||
@@ -364,7 +367,6 @@ export class AIChatInput extends SignalWatcher(WithDisposable(LitElement)) {
|
|||||||
<div
|
<div
|
||||||
class="chat-network-search"
|
class="chat-network-search"
|
||||||
data-testid="chat-network-search"
|
data-testid="chat-network-search"
|
||||||
aria-disabled=${this._isNetworkDisabled}
|
|
||||||
data-active=${this._isNetworkActive}
|
data-active=${this._isNetworkActive}
|
||||||
@click=${this._toggleNetworkSearch}
|
@click=${this._toggleNetworkSearch}
|
||||||
@pointerdown=${stopPropagation}
|
@pointerdown=${stopPropagation}
|
||||||
@@ -373,12 +375,20 @@ export class AIChatInput extends SignalWatcher(WithDisposable(LitElement)) {
|
|||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
|
<div
|
||||||
|
class="chat-network-search"
|
||||||
|
data-testid="chat-reasoning"
|
||||||
|
data-active=${this._isReasoningActive}
|
||||||
|
@click=${this._toggleReasoning}
|
||||||
|
@pointerdown=${stopPropagation}
|
||||||
|
>
|
||||||
|
${ThinkingIcon()}
|
||||||
|
</div>
|
||||||
${images.length < MaximumImageCount
|
${images.length < MaximumImageCount
|
||||||
? html`<div
|
? html`<div
|
||||||
data-testid="chat-panel-input-image-upload"
|
data-testid="chat-panel-input-image-upload"
|
||||||
class="image-upload"
|
class="image-upload"
|
||||||
aria-disabled=${uploadDisabled}
|
@click=${this._uploadImageFiles}
|
||||||
@click=${uploadDisabled ? undefined : this._uploadImageFiles}
|
|
||||||
>
|
>
|
||||||
${ImageIcon()}
|
${ImageIcon()}
|
||||||
</div>`
|
</div>`
|
||||||
@@ -457,13 +467,18 @@ export class AIChatInput extends SignalWatcher(WithDisposable(LitElement)) {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
if (this._isNetworkDisabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const enable = this.networkSearchConfig.enabled.value;
|
const enable = this.networkSearchConfig.enabled.value;
|
||||||
this.networkSearchConfig.setEnabled(!enable);
|
this.networkSearchConfig.setEnabled(!enable);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private readonly _toggleReasoning = (e: MouseEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
const enable = this.reasoningConfig.enabled.value;
|
||||||
|
this.reasoningConfig.setEnabled(!enable);
|
||||||
|
};
|
||||||
|
|
||||||
private _addImages(images: File[]) {
|
private _addImages(images: File[]) {
|
||||||
const oldImages = this.chatContextValue.images;
|
const oldImages = this.chatContextValue.images;
|
||||||
this.updateContext({
|
this.updateContext({
|
||||||
@@ -545,6 +560,7 @@ export class AIChatInput extends SignalWatcher(WithDisposable(LitElement)) {
|
|||||||
where: this.trackOptions.where,
|
where: this.trackOptions.where,
|
||||||
control: this.trackOptions.control,
|
control: this.trackOptions.control,
|
||||||
mustSearch: this._isNetworkActive,
|
mustSearch: this._isNetworkActive,
|
||||||
|
reasoning: this._isReasoningActive,
|
||||||
});
|
});
|
||||||
|
|
||||||
for await (const text of stream) {
|
for await (const text of stream) {
|
||||||
|
|||||||
@@ -9,6 +9,11 @@ export interface AINetworkSearchConfig {
|
|||||||
setEnabled: (state: boolean) => void;
|
setEnabled: (state: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AIReasoningConfig {
|
||||||
|
enabled: Signal<boolean | undefined>;
|
||||||
|
setEnabled: (state: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: remove this type
|
// TODO: remove this type
|
||||||
export type AIChatInputContext = {
|
export type AIChatInputContext = {
|
||||||
messages: HistoryMessage[];
|
messages: HistoryMessage[];
|
||||||
|
|||||||
@@ -28,7 +28,10 @@ import type {
|
|||||||
DocDisplayConfig,
|
DocDisplayConfig,
|
||||||
SearchMenuConfig,
|
SearchMenuConfig,
|
||||||
} from '../components/ai-chat-chips';
|
} from '../components/ai-chat-chips';
|
||||||
import type { AINetworkSearchConfig } from '../components/ai-chat-input';
|
import type {
|
||||||
|
AINetworkSearchConfig,
|
||||||
|
AIReasoningConfig,
|
||||||
|
} from '../components/ai-chat-input';
|
||||||
import type { ChatMessage } from '../components/ai-chat-messages';
|
import type { ChatMessage } from '../components/ai-chat-messages';
|
||||||
import { ChatMessagesSchema } from '../components/ai-chat-messages';
|
import { ChatMessagesSchema } from '../components/ai-chat-messages';
|
||||||
import type { TextRendererOptions } from '../components/text-renderer';
|
import type { TextRendererOptions } from '../components/text-renderer';
|
||||||
@@ -519,6 +522,7 @@ export class AIChatBlockPeekView extends LitElement {
|
|||||||
control: 'chat-send',
|
control: 'chat-send',
|
||||||
}}
|
}}
|
||||||
.portalContainer=${this.parentElement}
|
.portalContainer=${this.parentElement}
|
||||||
|
.reasoningConfig=${this.reasoningConfig}
|
||||||
></ai-chat-composer>
|
></ai-chat-composer>
|
||||||
</div> `;
|
</div> `;
|
||||||
}
|
}
|
||||||
@@ -535,6 +539,9 @@ export class AIChatBlockPeekView extends LitElement {
|
|||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor networkSearchConfig!: AINetworkSearchConfig;
|
accessor networkSearchConfig!: AINetworkSearchConfig;
|
||||||
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
accessor reasoningConfig!: AIReasoningConfig;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor docDisplayConfig!: DocDisplayConfig;
|
accessor docDisplayConfig!: DocDisplayConfig;
|
||||||
|
|
||||||
@@ -568,7 +575,8 @@ export const AIChatBlockPeekViewTemplate = (
|
|||||||
host: EditorHost,
|
host: EditorHost,
|
||||||
docDisplayConfig: DocDisplayConfig,
|
docDisplayConfig: DocDisplayConfig,
|
||||||
searchMenuConfig: SearchMenuConfig,
|
searchMenuConfig: SearchMenuConfig,
|
||||||
networkSearchConfig: AINetworkSearchConfig
|
networkSearchConfig: AINetworkSearchConfig,
|
||||||
|
reasoningConfig: AIReasoningConfig
|
||||||
) => {
|
) => {
|
||||||
return html`<ai-chat-block-peek-view
|
return html`<ai-chat-block-peek-view
|
||||||
.blockModel=${blockModel}
|
.blockModel=${blockModel}
|
||||||
@@ -576,5 +584,6 @@ export const AIChatBlockPeekViewTemplate = (
|
|||||||
.networkSearchConfig=${networkSearchConfig}
|
.networkSearchConfig=${networkSearchConfig}
|
||||||
.docDisplayConfig=${docDisplayConfig}
|
.docDisplayConfig=${docDisplayConfig}
|
||||||
.searchMenuConfig=${searchMenuConfig}
|
.searchMenuConfig=${searchMenuConfig}
|
||||||
|
.reasoningConfig=${reasoningConfig}
|
||||||
></ai-chat-block-peek-view>`;
|
></ai-chat-block-peek-view>`;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -350,15 +350,21 @@ export class CopilotClient {
|
|||||||
async chatText({
|
async chatText({
|
||||||
sessionId,
|
sessionId,
|
||||||
messageId,
|
messageId,
|
||||||
|
reasoning,
|
||||||
signal,
|
signal,
|
||||||
}: {
|
}: {
|
||||||
sessionId: string;
|
sessionId: string;
|
||||||
messageId?: string;
|
messageId?: string;
|
||||||
|
reasoning?: boolean;
|
||||||
signal?: AbortSignal;
|
signal?: AbortSignal;
|
||||||
}) {
|
}) {
|
||||||
let url = `/api/copilot/chat/${sessionId}`;
|
let url = `/api/copilot/chat/${sessionId}`;
|
||||||
if (messageId) {
|
const queryString = this.paramsToQueryString({
|
||||||
url += `?messageId=${encodeURIComponent(messageId)}`;
|
messageId,
|
||||||
|
reasoning,
|
||||||
|
});
|
||||||
|
if (queryString) {
|
||||||
|
url += `?${queryString}`;
|
||||||
}
|
}
|
||||||
const response = await this.fetcher(url.toString(), { signal });
|
const response = await this.fetcher(url.toString(), { signal });
|
||||||
return response.text();
|
return response.text();
|
||||||
@@ -369,15 +375,21 @@ export class CopilotClient {
|
|||||||
{
|
{
|
||||||
sessionId,
|
sessionId,
|
||||||
messageId,
|
messageId,
|
||||||
|
reasoning,
|
||||||
}: {
|
}: {
|
||||||
sessionId: string;
|
sessionId: string;
|
||||||
messageId?: string;
|
messageId?: string;
|
||||||
|
reasoning?: boolean;
|
||||||
},
|
},
|
||||||
endpoint = 'stream'
|
endpoint = 'stream'
|
||||||
) {
|
) {
|
||||||
let url = `/api/copilot/chat/${sessionId}/${endpoint}`;
|
let url = `/api/copilot/chat/${sessionId}/${endpoint}`;
|
||||||
if (messageId) {
|
const queryString = this.paramsToQueryString({
|
||||||
url += `?messageId=${encodeURIComponent(messageId)}`;
|
messageId,
|
||||||
|
reasoning,
|
||||||
|
});
|
||||||
|
if (queryString) {
|
||||||
|
url += `?${queryString}`;
|
||||||
}
|
}
|
||||||
return this.eventSource(url);
|
return this.eventSource(url);
|
||||||
}
|
}
|
||||||
@@ -390,17 +402,27 @@ export class CopilotClient {
|
|||||||
endpoint = 'images'
|
endpoint = 'images'
|
||||||
) {
|
) {
|
||||||
let url = `/api/copilot/chat/${sessionId}/${endpoint}`;
|
let url = `/api/copilot/chat/${sessionId}/${endpoint}`;
|
||||||
|
const queryString = this.paramsToQueryString({
|
||||||
if (messageId || seed) {
|
messageId,
|
||||||
url += '?';
|
seed,
|
||||||
url += new URLSearchParams(
|
});
|
||||||
Object.fromEntries(
|
if (queryString) {
|
||||||
Object.entries({ messageId, seed }).filter(
|
url += `?${queryString}`;
|
||||||
([_, v]) => v !== undefined
|
|
||||||
)
|
|
||||||
) as Record<string, string>
|
|
||||||
).toString();
|
|
||||||
}
|
}
|
||||||
return this.eventSource(url);
|
return this.eventSource(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
paramsToQueryString(params: Record<string, string | boolean | undefined>) {
|
||||||
|
const queryString = new URLSearchParams();
|
||||||
|
Object.entries(params).forEach(([key, value]) => {
|
||||||
|
if (typeof value === 'boolean') {
|
||||||
|
if (value) {
|
||||||
|
queryString.append(key, 'true');
|
||||||
|
}
|
||||||
|
} else if (typeof value === 'string') {
|
||||||
|
queryString.append(key, value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return queryString.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ export type TextToTextOptions = {
|
|||||||
workflow?: boolean;
|
workflow?: boolean;
|
||||||
isRootSession?: boolean;
|
isRootSession?: boolean;
|
||||||
postfix?: (text: string) => string;
|
postfix?: (text: string) => string;
|
||||||
|
reasoning?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ToImageOptions = TextToTextOptions & {
|
export type ToImageOptions = TextToTextOptions & {
|
||||||
@@ -113,6 +114,7 @@ export function textToText({
|
|||||||
retry = false,
|
retry = false,
|
||||||
workflow = false,
|
workflow = false,
|
||||||
postfix,
|
postfix,
|
||||||
|
reasoning,
|
||||||
}: TextToTextOptions) {
|
}: TextToTextOptions) {
|
||||||
let messageId: string | undefined;
|
let messageId: string | undefined;
|
||||||
|
|
||||||
@@ -132,6 +134,7 @@ export function textToText({
|
|||||||
{
|
{
|
||||||
sessionId,
|
sessionId,
|
||||||
messageId,
|
messageId,
|
||||||
|
reasoning,
|
||||||
},
|
},
|
||||||
workflow ? 'workflow' : undefined
|
workflow ? 'workflow' : undefined
|
||||||
);
|
);
|
||||||
@@ -191,6 +194,7 @@ export function textToText({
|
|||||||
return client.chatText({
|
return client.chatText({
|
||||||
sessionId,
|
sessionId,
|
||||||
messageId,
|
messageId,
|
||||||
|
reasoning,
|
||||||
});
|
});
|
||||||
})(),
|
})(),
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
// packages/frontend/core/src/blocksuite/ai/hooks/useChatPanelConfig.ts
|
// packages/frontend/core/src/blocksuite/ai/hooks/useChatPanelConfig.ts
|
||||||
import { AINetworkSearchService } from '@affine/core/modules/ai-button/services/network-search';
|
import { AINetworkSearchService } from '@affine/core/modules/ai-button/services/network-search';
|
||||||
|
import { AIReasoningService } from '@affine/core/modules/ai-button/services/reasoning';
|
||||||
import { CollectionService } from '@affine/core/modules/collection';
|
import { CollectionService } from '@affine/core/modules/collection';
|
||||||
import { DocsService } from '@affine/core/modules/doc';
|
import { DocsService } from '@affine/core/modules/doc';
|
||||||
import { DocDisplayMetaService } from '@affine/core/modules/doc-display-meta';
|
import { DocDisplayMetaService } from '@affine/core/modules/doc-display-meta';
|
||||||
@@ -19,6 +20,7 @@ export function useAIChatConfig() {
|
|||||||
const framework = useFramework();
|
const framework = useFramework();
|
||||||
|
|
||||||
const searchService = framework.get(AINetworkSearchService);
|
const searchService = framework.get(AINetworkSearchService);
|
||||||
|
const reasoningService = framework.get(AIReasoningService);
|
||||||
const docDisplayMetaService = framework.get(DocDisplayMetaService);
|
const docDisplayMetaService = framework.get(DocDisplayMetaService);
|
||||||
const workspaceService = framework.get(WorkspaceService);
|
const workspaceService = framework.get(WorkspaceService);
|
||||||
const searchMenuService = framework.get(SearchMenuService);
|
const searchMenuService = framework.get(SearchMenuService);
|
||||||
@@ -33,6 +35,11 @@ export function useAIChatConfig() {
|
|||||||
setEnabled: searchService.setEnabled,
|
setEnabled: searchService.setEnabled,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const reasoningConfig = {
|
||||||
|
enabled: reasoningService.enabled,
|
||||||
|
setEnabled: reasoningService.setEnabled,
|
||||||
|
};
|
||||||
|
|
||||||
const docDisplayConfig = {
|
const docDisplayConfig = {
|
||||||
getIcon: (docId: string) => {
|
getIcon: (docId: string) => {
|
||||||
return docDisplayMetaService.icon$(docId, { type: 'lit' }).value;
|
return docDisplayMetaService.icon$(docId, { type: 'lit' }).value;
|
||||||
@@ -114,6 +121,7 @@ export function useAIChatConfig() {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
networkSearchConfig,
|
networkSearchConfig,
|
||||||
|
reasoningConfig,
|
||||||
docDisplayConfig,
|
docDisplayConfig,
|
||||||
searchMenuConfig,
|
searchMenuConfig,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -41,8 +41,12 @@ export const EditorChatPanel = forwardRef(function EditorChatPanel(
|
|||||||
}
|
}
|
||||||
}, [onLoad, ref]);
|
}, [onLoad, ref]);
|
||||||
|
|
||||||
const { docDisplayConfig, searchMenuConfig, networkSearchConfig } =
|
const {
|
||||||
useAIChatConfig();
|
docDisplayConfig,
|
||||||
|
searchMenuConfig,
|
||||||
|
networkSearchConfig,
|
||||||
|
reasoningConfig,
|
||||||
|
} = useAIChatConfig();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!editor || !editor.host) return;
|
if (!editor || !editor.host) return;
|
||||||
@@ -67,6 +71,7 @@ export const EditorChatPanel = forwardRef(function EditorChatPanel(
|
|||||||
chatPanelRef.current.docDisplayConfig = docDisplayConfig;
|
chatPanelRef.current.docDisplayConfig = docDisplayConfig;
|
||||||
chatPanelRef.current.searchMenuConfig = searchMenuConfig;
|
chatPanelRef.current.searchMenuConfig = searchMenuConfig;
|
||||||
chatPanelRef.current.networkSearchConfig = networkSearchConfig;
|
chatPanelRef.current.networkSearchConfig = networkSearchConfig;
|
||||||
|
chatPanelRef.current.reasoningConfig = reasoningConfig;
|
||||||
chatPanelRef.current.extensions = editor.host.std
|
chatPanelRef.current.extensions = editor.host.std
|
||||||
.get(ViewExtensionManagerIdentifier)
|
.get(ViewExtensionManagerIdentifier)
|
||||||
.get('preview-page');
|
.get('preview-page');
|
||||||
@@ -98,6 +103,7 @@ export const EditorChatPanel = forwardRef(function EditorChatPanel(
|
|||||||
framework,
|
framework,
|
||||||
networkSearchConfig,
|
networkSearchConfig,
|
||||||
searchMenuConfig,
|
searchMenuConfig,
|
||||||
|
reasoningConfig,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return <div className={styles.root} ref={containerRef} />;
|
return <div className={styles.root} ref={containerRef} />;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { GlobalStateService } from '../storage';
|
|||||||
import { AIButtonProvider } from './provider/ai-button';
|
import { AIButtonProvider } from './provider/ai-button';
|
||||||
import { AIButtonService } from './services/ai-button';
|
import { AIButtonService } from './services/ai-button';
|
||||||
import { AINetworkSearchService } from './services/network-search';
|
import { AINetworkSearchService } from './services/network-search';
|
||||||
|
import { AIReasoningService } from './services/reasoning';
|
||||||
|
|
||||||
export const configureAIButtonModule = (framework: Framework) => {
|
export const configureAIButtonModule = (framework: Framework) => {
|
||||||
framework.service(AIButtonService, container => {
|
framework.service(AIButtonService, container => {
|
||||||
@@ -21,3 +22,7 @@ export function configureAINetworkSearchModule(framework: Framework) {
|
|||||||
FeatureFlagService,
|
FeatureFlagService,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function configureAIReasoningModule(framework: Framework) {
|
||||||
|
framework.service(AIReasoningService, [GlobalStateService]);
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import {
|
||||||
|
createSignalFromObservable,
|
||||||
|
type Signal,
|
||||||
|
} from '@blocksuite/affine/shared/utils';
|
||||||
|
import { LiveData, Service } from '@toeverything/infra';
|
||||||
|
|
||||||
|
import type { GlobalStateService } from '../../storage';
|
||||||
|
|
||||||
|
const AI_REASONING_KEY = 'AIReasoning';
|
||||||
|
|
||||||
|
export class AIReasoningService extends Service {
|
||||||
|
constructor(private readonly globalStateService: GlobalStateService) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
const { signal: enabled, cleanup: enabledCleanup } =
|
||||||
|
createSignalFromObservable<boolean | undefined>(
|
||||||
|
this._enabled$,
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
this.enabled = enabled;
|
||||||
|
this.disposables.push(enabledCleanup);
|
||||||
|
}
|
||||||
|
|
||||||
|
enabled: Signal<boolean | undefined>;
|
||||||
|
|
||||||
|
private readonly _enabled$ = LiveData.from(
|
||||||
|
this.globalStateService.globalState.watch<boolean>(AI_REASONING_KEY),
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
|
||||||
|
setEnabled = (enabled: boolean) => {
|
||||||
|
this.globalStateService.globalState.set(AI_REASONING_KEY, enabled);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ import { type Framework } from '@toeverything/infra';
|
|||||||
import {
|
import {
|
||||||
configureAIButtonModule,
|
configureAIButtonModule,
|
||||||
configureAINetworkSearchModule,
|
configureAINetworkSearchModule,
|
||||||
|
configureAIReasoningModule,
|
||||||
} from './ai-button';
|
} from './ai-button';
|
||||||
import { configureAppSidebarModule } from './app-sidebar';
|
import { configureAppSidebarModule } from './app-sidebar';
|
||||||
import { configAtMenuConfigModule } from './at-menu-config';
|
import { configAtMenuConfigModule } from './at-menu-config';
|
||||||
@@ -101,6 +102,7 @@ export function configureCommonModules(framework: Framework) {
|
|||||||
configureDndModule(framework);
|
configureDndModule(framework);
|
||||||
configureCommonGlobalStorageImpls(framework);
|
configureCommonGlobalStorageImpls(framework);
|
||||||
configureAINetworkSearchModule(framework);
|
configureAINetworkSearchModule(framework);
|
||||||
|
configureAIReasoningModule(framework);
|
||||||
configureAIButtonModule(framework);
|
configureAIButtonModule(framework);
|
||||||
configureTemplateDocModule(framework);
|
configureTemplateDocModule(framework);
|
||||||
configureBlobManagementModule(framework);
|
configureBlobManagementModule(framework);
|
||||||
|
|||||||
@@ -14,16 +14,29 @@ export const AIChatBlockPeekView = ({
|
|||||||
model,
|
model,
|
||||||
host,
|
host,
|
||||||
}: AIChatBlockPeekViewProps) => {
|
}: AIChatBlockPeekViewProps) => {
|
||||||
const { docDisplayConfig, searchMenuConfig, networkSearchConfig } =
|
const {
|
||||||
useAIChatConfig();
|
docDisplayConfig,
|
||||||
|
searchMenuConfig,
|
||||||
|
networkSearchConfig,
|
||||||
|
reasoningConfig,
|
||||||
|
} = useAIChatConfig();
|
||||||
|
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
const template = AIChatBlockPeekViewTemplate(
|
const template = AIChatBlockPeekViewTemplate(
|
||||||
model,
|
model,
|
||||||
host,
|
host,
|
||||||
docDisplayConfig,
|
docDisplayConfig,
|
||||||
searchMenuConfig,
|
searchMenuConfig,
|
||||||
networkSearchConfig
|
networkSearchConfig,
|
||||||
|
reasoningConfig
|
||||||
);
|
);
|
||||||
return toReactNode(template);
|
return toReactNode(template);
|
||||||
}, [model, host, docDisplayConfig, searchMenuConfig, networkSearchConfig]);
|
}, [
|
||||||
|
model,
|
||||||
|
host,
|
||||||
|
docDisplayConfig,
|
||||||
|
searchMenuConfig,
|
||||||
|
networkSearchConfig,
|
||||||
|
reasoningConfig,
|
||||||
|
]);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user