mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-09-22 19:53:48 +08:00
feat(core): ai intelligence track (#13187)
Close [AI-335](https://linear.app/affine-design/issue/AI-335) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added support for an "independent mode" and document-specific context in AI chat components, allowing enhanced context handling and tracking. * Introduced new tracking options to distinguish between current document and general document actions in chat interactions. * **Improvements** * More flexible property handling for independent mode and document context across chat-related components for consistent behavior and tracking. * Enhanced tracking system to support additional event categories and methods for more granular analytics. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -80,7 +80,7 @@ export class ChatMessageAssistant extends WithDisposable(ShadowlessElement) {
|
|||||||
accessor notificationService!: NotificationService;
|
accessor notificationService!: NotificationService;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor independentMode!: boolean;
|
accessor independentMode: boolean | undefined;
|
||||||
|
|
||||||
get state() {
|
get state() {
|
||||||
const { isLast, status } = this;
|
const { isLast, status } = this;
|
||||||
|
|||||||
+8
@@ -23,6 +23,12 @@ export class AIChatAddContext extends SignalWatcher(
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
accessor docId: string | undefined;
|
||||||
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
accessor independentMode: boolean | undefined;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor addChip!: (chip: ChatChip) => Promise<void>;
|
accessor addChip!: (chip: ChatChip) => Promise<void>;
|
||||||
|
|
||||||
@@ -69,6 +75,8 @@ export class AIChatAddContext extends SignalWatcher(
|
|||||||
createLitPortal({
|
createLitPortal({
|
||||||
template: html`
|
template: html`
|
||||||
<chat-panel-add-popover
|
<chat-panel-add-popover
|
||||||
|
.docId=${this.docId}
|
||||||
|
.independentMode=${this.independentMode}
|
||||||
.addChip=${this.addChip}
|
.addChip=${this.addChip}
|
||||||
.addImages=${this.addImages}
|
.addImages=${this.addImages}
|
||||||
.searchMenuConfig=${this.searchMenuConfig}
|
.searchMenuConfig=${this.searchMenuConfig}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { toast } from '@affine/component';
|
import { toast } from '@affine/component';
|
||||||
import type { TagMeta } from '@affine/core/components/page-list';
|
import type { TagMeta } from '@affine/core/components/page-list';
|
||||||
import type { CollectionMeta } from '@affine/core/modules/collection';
|
import type { CollectionMeta } from '@affine/core/modules/collection';
|
||||||
import track from '@affine/track';
|
import track, { type EventArgs } from '@affine/track';
|
||||||
import { SignalWatcher, WithDisposable } from '@blocksuite/affine/global/lit';
|
import { SignalWatcher, WithDisposable } from '@blocksuite/affine/global/lit';
|
||||||
import { scrollbarStyle } from '@blocksuite/affine/shared/styles';
|
import { scrollbarStyle } from '@blocksuite/affine/shared/styles';
|
||||||
import { unsafeCSSVar, unsafeCSSVarV2 } from '@blocksuite/affine/shared/theme';
|
import { unsafeCSSVar, unsafeCSSVarV2 } from '@blocksuite/affine/shared/theme';
|
||||||
@@ -120,6 +120,12 @@ export class ChatPanelAddPopover extends SignalWatcher(
|
|||||||
|
|
||||||
private accessor _query = '';
|
private accessor _query = '';
|
||||||
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
accessor independentMode: boolean | undefined;
|
||||||
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
accessor docId: string | undefined;
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
private accessor _searchGroups: MenuGroup[] = [];
|
private accessor _searchGroups: MenuGroup[] = [];
|
||||||
|
|
||||||
@@ -497,7 +503,8 @@ export class ChatPanelAddPopover extends SignalWatcher(
|
|||||||
state: 'processing',
|
state: 'processing',
|
||||||
});
|
});
|
||||||
const mode = this.docDisplayConfig.getDocPrimaryMode(meta.id);
|
const mode = this.docDisplayConfig.getDocPrimaryMode(meta.id);
|
||||||
this._track('doc', mode);
|
const method = meta.id === this.docId ? 'cur-doc' : 'doc';
|
||||||
|
this._track(method, mode);
|
||||||
};
|
};
|
||||||
|
|
||||||
private readonly _addTagChip = async (tag: TagMeta) => {
|
private readonly _addTagChip = async (tag: TagMeta) => {
|
||||||
@@ -561,10 +568,13 @@ export class ChatPanelAddPopover extends SignalWatcher(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _track(
|
private _track(
|
||||||
method: 'doc' | 'file' | 'tags' | 'collections',
|
method: EventArgs['addEmbeddingDoc']['method'],
|
||||||
type?: 'page' | 'edgeless'
|
type?: 'page' | 'edgeless'
|
||||||
) {
|
) {
|
||||||
track.$.chatPanel.chatPanelInput.addEmbeddingDoc({
|
const page = this.independentMode
|
||||||
|
? track.$.intelligence
|
||||||
|
: track.$.chatPanel;
|
||||||
|
page.chatPanelInput.addEmbeddingDoc({
|
||||||
control: 'addButton',
|
control: 'addButton',
|
||||||
method,
|
method,
|
||||||
type,
|
type,
|
||||||
|
|||||||
@@ -81,6 +81,9 @@ export class ChatPanelChips extends SignalWatcher(
|
|||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor isCollapsed!: boolean;
|
accessor isCollapsed!: boolean;
|
||||||
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
accessor independentMode: boolean | undefined;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor addChip!: (chip: ChatChip) => Promise<void>;
|
accessor addChip!: (chip: ChatChip) => Promise<void>;
|
||||||
|
|
||||||
@@ -142,6 +145,7 @@ export class ChatPanelChips extends SignalWatcher(
|
|||||||
if (isDocChip(chip)) {
|
if (isDocChip(chip)) {
|
||||||
return html`<chat-panel-doc-chip
|
return html`<chat-panel-doc-chip
|
||||||
.chip=${chip}
|
.chip=${chip}
|
||||||
|
.independentMode=${this.independentMode}
|
||||||
.addChip=${this.addChip}
|
.addChip=${this.addChip}
|
||||||
.updateChip=${this.updateChip}
|
.updateChip=${this.updateChip}
|
||||||
.removeChip=${this.removeChip}
|
.removeChip=${this.removeChip}
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ export class ChatPanelDocChip extends SignalWatcher(
|
|||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor chip!: DocChip;
|
accessor chip!: DocChip;
|
||||||
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
accessor independentMode: boolean | undefined;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor addChip!: (chip: DocChip) => void;
|
accessor addChip!: (chip: DocChip) => void;
|
||||||
|
|
||||||
@@ -81,7 +84,10 @@ export class ChatPanelDocChip extends SignalWatcher(
|
|||||||
state: 'processing',
|
state: 'processing',
|
||||||
});
|
});
|
||||||
const mode = this.docDisplayConfig.getDocPrimaryMode(this.chip.docId);
|
const mode = this.docDisplayConfig.getDocPrimaryMode(this.chip.docId);
|
||||||
track.$.chatPanel.chatPanelInput.addEmbeddingDoc({
|
const page = this.independentMode
|
||||||
|
? track.$.intelligence
|
||||||
|
: track.$.chatPanel;
|
||||||
|
page.chatPanelInput.addEmbeddingDoc({
|
||||||
control: 'addButton',
|
control: 'addButton',
|
||||||
method: 'suggestion',
|
method: 'suggestion',
|
||||||
type: mode,
|
type: mode,
|
||||||
|
|||||||
+6
-5
@@ -59,7 +59,7 @@ export class AIChatComposer extends SignalWatcher(
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor independentMode!: boolean;
|
accessor independentMode: boolean | undefined;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor host: EditorHost | null | undefined;
|
accessor host: EditorHost | null | undefined;
|
||||||
@@ -85,9 +85,9 @@ export class AIChatComposer extends SignalWatcher(
|
|||||||
accessor updateContext!: (context: Partial<AIChatInputContext>) => void;
|
accessor updateContext!: (context: Partial<AIChatInputContext>) => void;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor onEmbeddingProgressChange!: (
|
accessor onEmbeddingProgressChange:
|
||||||
count: Record<ContextEmbedStatus, number>
|
| ((count: Record<ContextEmbedStatus, number>) => void)
|
||||||
) => void;
|
| undefined;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor docDisplayConfig!: DocDisplayConfig;
|
accessor docDisplayConfig!: DocDisplayConfig;
|
||||||
@@ -136,6 +136,7 @@ export class AIChatComposer extends SignalWatcher(
|
|||||||
<chat-panel-chips
|
<chat-panel-chips
|
||||||
.chips=${this.chips}
|
.chips=${this.chips}
|
||||||
.isCollapsed=${this.isChipsCollapsed}
|
.isCollapsed=${this.isChipsCollapsed}
|
||||||
|
.independentMode=${this.independentMode}
|
||||||
.addChip=${this.addChip}
|
.addChip=${this.addChip}
|
||||||
.updateChip=${this.updateChip}
|
.updateChip=${this.updateChip}
|
||||||
.removeChip=${this.removeChip}
|
.removeChip=${this.removeChip}
|
||||||
@@ -603,7 +604,7 @@ export class AIChatComposer extends SignalWatcher(
|
|||||||
return chip;
|
return chip;
|
||||||
});
|
});
|
||||||
this.updateChips(nextChips);
|
this.updateChips(nextChips);
|
||||||
this.onEmbeddingProgressChange(count);
|
this.onEmbeddingProgressChange?.(count);
|
||||||
if (count.processing === 0) {
|
if (count.processing === 0) {
|
||||||
this._abortPoll();
|
this._abortPoll();
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-5
@@ -124,7 +124,7 @@ export class AIChatContent extends SignalWatcher(
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor independentMode!: boolean;
|
accessor independentMode: boolean | undefined;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor onboardingOffsetY!: number;
|
accessor onboardingOffsetY!: number;
|
||||||
@@ -177,9 +177,9 @@ export class AIChatContent extends SignalWatcher(
|
|||||||
accessor notificationService!: NotificationService;
|
accessor notificationService!: NotificationService;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor onEmbeddingProgressChange!: (
|
accessor onEmbeddingProgressChange:
|
||||||
count: Record<ContextEmbedStatus, number>
|
| ((count: Record<ContextEmbedStatus, number>) => void)
|
||||||
) => void;
|
| undefined;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor onContextChange!: (context: Partial<ChatContextValue>) => void;
|
accessor onContextChange!: (context: Partial<ChatContextValue>) => void;
|
||||||
@@ -403,7 +403,7 @@ export class AIChatContent extends SignalWatcher(
|
|||||||
<ai-chat-messages
|
<ai-chat-messages
|
||||||
class=${classMap({
|
class=${classMap({
|
||||||
'ai-chat-messages': true,
|
'ai-chat-messages': true,
|
||||||
'independent-mode': this.independentMode,
|
'independent-mode': !!this.independentMode,
|
||||||
'no-message': this.messages.length === 0,
|
'no-message': this.messages.length === 0,
|
||||||
})}
|
})}
|
||||||
${ref(this.chatMessagesRef)}
|
${ref(this.chatMessagesRef)}
|
||||||
|
|||||||
@@ -290,7 +290,7 @@ export class AIChatInput extends SignalWatcher(
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor independentMode!: boolean;
|
accessor independentMode: boolean | undefined;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor host: EditorHost | null | undefined;
|
accessor host: EditorHost | null | undefined;
|
||||||
@@ -458,6 +458,8 @@ export class AIChatInput extends SignalWatcher(
|
|||||||
<div class="chat-panel-input-actions">
|
<div class="chat-panel-input-actions">
|
||||||
<div class="chat-input-icon">
|
<div class="chat-input-icon">
|
||||||
<ai-chat-add-context
|
<ai-chat-add-context
|
||||||
|
.docId=${this.docId}
|
||||||
|
.independentMode=${this.independentMode}
|
||||||
.addChip=${this.addChip}
|
.addChip=${this.addChip}
|
||||||
.addImages=${this.addImages}
|
.addImages=${this.addImages}
|
||||||
.docDisplayConfig=${this.docDisplayConfig}
|
.docDisplayConfig=${this.docDisplayConfig}
|
||||||
|
|||||||
+2
-2
@@ -150,7 +150,7 @@ export class AIChatMessages extends WithDisposable(ShadowlessElement) {
|
|||||||
accessor avatarUrl = '';
|
accessor avatarUrl = '';
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor independentMode!: boolean;
|
accessor independentMode: boolean | undefined;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor messages!: HistoryMessage[];
|
accessor messages!: HistoryMessage[];
|
||||||
@@ -275,7 +275,7 @@ export class AIChatMessages extends WithDisposable(ShadowlessElement) {
|
|||||||
<div
|
<div
|
||||||
class=${classMap({
|
class=${classMap({
|
||||||
'chat-panel-messages-container': true,
|
'chat-panel-messages-container': true,
|
||||||
'independent-mode': this.independentMode,
|
'independent-mode': !!this.independentMode,
|
||||||
})}
|
})}
|
||||||
data-testid="chat-panel-messages-container"
|
data-testid="chat-panel-messages-container"
|
||||||
@scroll=${() => this._debouncedOnScroll()}
|
@scroll=${() => this._debouncedOnScroll()}
|
||||||
|
|||||||
@@ -442,6 +442,9 @@ interface PageEvents extends PageDivision {
|
|||||||
chatPanel: {
|
chatPanel: {
|
||||||
chatPanelInput: ['addEmbeddingDoc'];
|
chatPanelInput: ['addEmbeddingDoc'];
|
||||||
};
|
};
|
||||||
|
intelligence: {
|
||||||
|
chatPanelInput: ['addEmbeddingDoc'];
|
||||||
|
};
|
||||||
commentPanel: {
|
commentPanel: {
|
||||||
$: ['createComment', 'editComment', 'deleteComment', 'resolveComment'];
|
$: ['createComment', 'editComment', 'deleteComment', 'resolveComment'];
|
||||||
};
|
};
|
||||||
@@ -720,7 +723,7 @@ export type EventArgs = {
|
|||||||
addEmbeddingDoc: {
|
addEmbeddingDoc: {
|
||||||
type?: 'page' | 'edgeless';
|
type?: 'page' | 'edgeless';
|
||||||
control: 'addButton' | 'atMenu';
|
control: 'addButton' | 'atMenu';
|
||||||
method: 'doc' | 'file' | 'tags' | 'collections' | 'suggestion';
|
method: 'doc' | 'cur-doc' | 'file' | 'tags' | 'collections' | 'suggestion';
|
||||||
};
|
};
|
||||||
openAttachmentInFullscreen: AttachmentEventArgs;
|
openAttachmentInFullscreen: AttachmentEventArgs;
|
||||||
openAttachmentInNewTab: AttachmentEventArgs;
|
openAttachmentInNewTab: AttachmentEventArgs;
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { enableAutoTrack, makeTracker } from './auto';
|
import { enableAutoTrack, makeTracker } from './auto';
|
||||||
import { type Events } from './events';
|
import { type EventArgs, type Events } from './events';
|
||||||
import { mixpanel } from './mixpanel';
|
import { mixpanel } from './mixpanel';
|
||||||
import { sentry } from './sentry';
|
import { sentry } from './sentry';
|
||||||
export const track = makeTracker((event, props) => {
|
export const track = makeTracker((event, props) => {
|
||||||
mixpanel.track(event, props);
|
mixpanel.track(event, props);
|
||||||
});
|
});
|
||||||
|
|
||||||
export { enableAutoTrack, type Events, mixpanel, sentry };
|
export { enableAutoTrack, type EventArgs, type Events, mixpanel, sentry };
|
||||||
export default track;
|
export default track;
|
||||||
|
|||||||
Reference in New Issue
Block a user