feat(core): add ai workspace all docs switch (#13345)
Close [AI-397](https://linear.app/affine-design/issue/AI-397) <img width="272" height="186" alt="截屏2025-07-29 11 54 20" src="https://github.com/user-attachments/assets/e171fb57-66cf-4244-894d-c27b18cbe83a" /> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Introduced an AI tools configuration service, allowing users to customize AI tool usage (e.g., workspace search, reading docs) in chat and AI features. * Added a toggle in chat preferences for enabling or disabling workspace-wide document search. * AI chat components now respect user-configured tool settings across chat, retry, and playground scenarios. * **Improvements** * Enhanced chat and AI interfaces to propagate and honor user tool configuration throughout the frontend and backend. * Made draft and tool configuration services optional and safely handled their absence in chat components. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -56,7 +56,7 @@ import { StreamObjectParser } from './providers/utils';
|
|||||||
import { ChatSession, ChatSessionService } from './session';
|
import { ChatSession, ChatSessionService } from './session';
|
||||||
import { CopilotStorage } from './storage';
|
import { CopilotStorage } from './storage';
|
||||||
import { ChatMessage, ChatQuerySchema } from './types';
|
import { ChatMessage, ChatQuerySchema } from './types';
|
||||||
import { getSignal } from './utils';
|
import { getSignal, getTools } from './utils';
|
||||||
import { CopilotWorkflowService, GraphExecutorState } from './workflow';
|
import { CopilotWorkflowService, GraphExecutorState } from './workflow';
|
||||||
|
|
||||||
export interface ChatEvent {
|
export interface ChatEvent {
|
||||||
@@ -244,7 +244,8 @@ export class CopilotController implements BeforeApplicationShutdown {
|
|||||||
info.finalMessage = finalMessage.filter(m => m.role !== 'system');
|
info.finalMessage = finalMessage.filter(m => m.role !== 'system');
|
||||||
metrics.ai.counter('chat_calls').add(1, { model });
|
metrics.ai.counter('chat_calls').add(1, { model });
|
||||||
|
|
||||||
const { reasoning, webSearch } = ChatQuerySchema.parse(query);
|
const { reasoning, webSearch, toolsConfig } =
|
||||||
|
ChatQuerySchema.parse(query);
|
||||||
const content = await provider.text({ modelId: model }, finalMessage, {
|
const content = await provider.text({ modelId: model }, finalMessage, {
|
||||||
...session.config.promptConfig,
|
...session.config.promptConfig,
|
||||||
signal: getSignal(req).signal,
|
signal: getSignal(req).signal,
|
||||||
@@ -253,6 +254,7 @@ export class CopilotController implements BeforeApplicationShutdown {
|
|||||||
workspace: session.config.workspaceId,
|
workspace: session.config.workspaceId,
|
||||||
reasoning,
|
reasoning,
|
||||||
webSearch,
|
webSearch,
|
||||||
|
tools: getTools(session.config.promptConfig?.tools, toolsConfig),
|
||||||
});
|
});
|
||||||
|
|
||||||
session.push({
|
session.push({
|
||||||
@@ -306,7 +308,8 @@ export class CopilotController implements BeforeApplicationShutdown {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const { messageId, reasoning, webSearch } = ChatQuerySchema.parse(query);
|
const { messageId, reasoning, webSearch, toolsConfig } =
|
||||||
|
ChatQuerySchema.parse(query);
|
||||||
|
|
||||||
const source$ = from(
|
const source$ = from(
|
||||||
provider.streamText({ modelId: model }, finalMessage, {
|
provider.streamText({ modelId: model }, finalMessage, {
|
||||||
@@ -317,6 +320,7 @@ export class CopilotController implements BeforeApplicationShutdown {
|
|||||||
workspace: session.config.workspaceId,
|
workspace: session.config.workspaceId,
|
||||||
reasoning,
|
reasoning,
|
||||||
webSearch,
|
webSearch,
|
||||||
|
tools: getTools(session.config.promptConfig?.tools, toolsConfig),
|
||||||
})
|
})
|
||||||
).pipe(
|
).pipe(
|
||||||
connect(shared$ =>
|
connect(shared$ =>
|
||||||
@@ -398,7 +402,8 @@ export class CopilotController implements BeforeApplicationShutdown {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const { messageId, reasoning, webSearch } = ChatQuerySchema.parse(query);
|
const { messageId, reasoning, webSearch, toolsConfig } =
|
||||||
|
ChatQuerySchema.parse(query);
|
||||||
|
|
||||||
const source$ = from(
|
const source$ = from(
|
||||||
provider.streamObject({ modelId: model }, finalMessage, {
|
provider.streamObject({ modelId: model }, finalMessage, {
|
||||||
@@ -409,6 +414,7 @@ export class CopilotController implements BeforeApplicationShutdown {
|
|||||||
workspace: session.config.workspaceId,
|
workspace: session.config.workspaceId,
|
||||||
reasoning,
|
reasoning,
|
||||||
webSearch,
|
webSearch,
|
||||||
|
tools: getTools(session.config.promptConfig?.tools, toolsConfig),
|
||||||
})
|
})
|
||||||
).pipe(
|
).pipe(
|
||||||
connect(shared$ =>
|
connect(shared$ =>
|
||||||
|
|||||||
@@ -57,28 +57,28 @@ export const VertexSchema: JSONSchema = {
|
|||||||
|
|
||||||
// ========== prompt ==========
|
// ========== prompt ==========
|
||||||
|
|
||||||
|
export const PromptToolsSchema = z
|
||||||
|
.enum([
|
||||||
|
'codeArtifact',
|
||||||
|
'conversationSummary',
|
||||||
|
// work with morph
|
||||||
|
'docEdit',
|
||||||
|
// work with indexer
|
||||||
|
'docRead',
|
||||||
|
'docKeywordSearch',
|
||||||
|
// work with embeddings
|
||||||
|
'docSemanticSearch',
|
||||||
|
// work with exa/model internal tools
|
||||||
|
'webSearch',
|
||||||
|
// artifact tools
|
||||||
|
'docCompose',
|
||||||
|
// section editing
|
||||||
|
'sectionEdit',
|
||||||
|
])
|
||||||
|
.array();
|
||||||
|
|
||||||
export const PromptConfigStrictSchema = z.object({
|
export const PromptConfigStrictSchema = z.object({
|
||||||
tools: z
|
tools: PromptToolsSchema.nullable().optional(),
|
||||||
.enum([
|
|
||||||
'codeArtifact',
|
|
||||||
'conversationSummary',
|
|
||||||
// work with morph
|
|
||||||
'docEdit',
|
|
||||||
// work with indexer
|
|
||||||
'docRead',
|
|
||||||
'docKeywordSearch',
|
|
||||||
// work with embeddings
|
|
||||||
'docSemanticSearch',
|
|
||||||
// work with exa/model internal tools
|
|
||||||
'webSearch',
|
|
||||||
// artifact tools
|
|
||||||
'docCompose',
|
|
||||||
// section editing
|
|
||||||
'sectionEdit',
|
|
||||||
])
|
|
||||||
.array()
|
|
||||||
.nullable()
|
|
||||||
.optional(),
|
|
||||||
// params requirements
|
// params requirements
|
||||||
requireContent: z.boolean().nullable().optional(),
|
requireContent: z.boolean().nullable().optional(),
|
||||||
requireAttachment: z.boolean().nullable().optional(),
|
requireAttachment: z.boolean().nullable().optional(),
|
||||||
@@ -107,6 +107,8 @@ export const PromptConfigSchema =
|
|||||||
|
|
||||||
export type PromptConfig = z.infer<typeof PromptConfigSchema>;
|
export type PromptConfig = z.infer<typeof PromptConfigSchema>;
|
||||||
|
|
||||||
|
export type PromptTools = z.infer<typeof PromptToolsSchema>;
|
||||||
|
|
||||||
// ========== message ==========
|
// ========== message ==========
|
||||||
|
|
||||||
export const EmbeddingMessage = z.array(z.string().trim().min(1)).min(1);
|
export const EmbeddingMessage = z.array(z.string().trim().min(1)).min(1);
|
||||||
|
|||||||
@@ -16,6 +16,23 @@ const zMaybeString = z.preprocess(val => {
|
|||||||
return s === '' || s == null ? undefined : s;
|
return s === '' || s == null ? undefined : s;
|
||||||
}, z.string().min(1).optional());
|
}, z.string().min(1).optional());
|
||||||
|
|
||||||
|
const ToolsConfigSchema = z.preprocess(
|
||||||
|
val => {
|
||||||
|
// if val is a string, try to parse it as JSON
|
||||||
|
if (typeof val === 'string') {
|
||||||
|
try {
|
||||||
|
return JSON.parse(val);
|
||||||
|
} catch {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return val || {};
|
||||||
|
},
|
||||||
|
z.record(z.enum(['searchWorkspace', 'readingDocs']), z.boolean()).default({})
|
||||||
|
);
|
||||||
|
|
||||||
|
export type ToolsConfig = z.infer<typeof ToolsConfigSchema>;
|
||||||
|
|
||||||
export const ChatQuerySchema = z
|
export const ChatQuerySchema = z
|
||||||
.object({
|
.object({
|
||||||
messageId: zMaybeString,
|
messageId: zMaybeString,
|
||||||
@@ -23,15 +40,25 @@ export const ChatQuerySchema = z
|
|||||||
retry: zBool,
|
retry: zBool,
|
||||||
reasoning: zBool,
|
reasoning: zBool,
|
||||||
webSearch: zBool,
|
webSearch: zBool,
|
||||||
|
toolsConfig: ToolsConfigSchema,
|
||||||
})
|
})
|
||||||
.catchall(z.string())
|
.catchall(z.string())
|
||||||
.transform(
|
.transform(
|
||||||
({ messageId, modelId, retry, reasoning, webSearch, ...params }) => ({
|
({
|
||||||
messageId,
|
messageId,
|
||||||
modelId,
|
modelId,
|
||||||
retry,
|
retry,
|
||||||
reasoning,
|
reasoning,
|
||||||
webSearch,
|
webSearch,
|
||||||
|
toolsConfig,
|
||||||
|
...params
|
||||||
|
}) => ({
|
||||||
|
messageId,
|
||||||
|
modelId,
|
||||||
|
retry,
|
||||||
|
reasoning,
|
||||||
|
webSearch,
|
||||||
|
toolsConfig,
|
||||||
params,
|
params,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ import { Readable } from 'node:stream';
|
|||||||
import type { Request } from 'express';
|
import type { Request } from 'express';
|
||||||
|
|
||||||
import { readBufferWithLimit } from '../../base';
|
import { readBufferWithLimit } from '../../base';
|
||||||
import { MAX_EMBEDDABLE_SIZE } from './types';
|
import { PromptTools } from './providers';
|
||||||
|
import { MAX_EMBEDDABLE_SIZE, ToolsConfig } from './types';
|
||||||
|
|
||||||
export function readStream(
|
export function readStream(
|
||||||
readable: Readable,
|
readable: Readable,
|
||||||
@@ -49,3 +50,33 @@ export function getSignal(req: Request): SignalReturnType {
|
|||||||
onConnectionClosed: cb => (callback = cb),
|
onConnectionClosed: cb => (callback = cb),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getTools(
|
||||||
|
tools?: PromptTools | null,
|
||||||
|
toolsConfig?: ToolsConfig
|
||||||
|
) {
|
||||||
|
if (!tools || !toolsConfig) {
|
||||||
|
return tools;
|
||||||
|
}
|
||||||
|
let result: PromptTools = tools;
|
||||||
|
(Object.keys(toolsConfig) as Array<keyof ToolsConfig>).forEach(key => {
|
||||||
|
const value = toolsConfig[key];
|
||||||
|
switch (key) {
|
||||||
|
case 'searchWorkspace':
|
||||||
|
if (value === false) {
|
||||||
|
result = result.filter(tool => {
|
||||||
|
return tool !== 'docKeywordSearch' && tool !== 'docSemanticSearch';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'readingDocs':
|
||||||
|
if (value === false) {
|
||||||
|
result = result.filter(tool => {
|
||||||
|
return tool !== 'docRead';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import type { AIToolsConfig } from '@affine/core/modules/ai-button';
|
||||||
import type {
|
import type {
|
||||||
AddContextFileInput,
|
AddContextFileInput,
|
||||||
ContextMatchedDocChunk,
|
ContextMatchedDocChunk,
|
||||||
@@ -142,6 +143,7 @@ declare global {
|
|||||||
webSearch?: boolean;
|
webSearch?: boolean;
|
||||||
reasoning?: boolean;
|
reasoning?: boolean;
|
||||||
modelId?: string;
|
modelId?: string;
|
||||||
|
toolsConfig?: AIToolsConfig | undefined;
|
||||||
contexts?: {
|
contexts?: {
|
||||||
docs: AIDocContextOption[];
|
docs: AIDocContextOption[];
|
||||||
files: AIFileContextOption[];
|
files: AIFileContextOption[];
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import type { AIToolsConfigService } from '@affine/core/modules/ai-button';
|
||||||
import type { WorkspaceDialogService } from '@affine/core/modules/dialogs';
|
import type { WorkspaceDialogService } from '@affine/core/modules/dialogs';
|
||||||
import type { FeatureFlagService } from '@affine/core/modules/feature-flag';
|
import type { FeatureFlagService } from '@affine/core/modules/feature-flag';
|
||||||
import type { AppThemeService } from '@affine/core/modules/theme';
|
import type { AppThemeService } from '@affine/core/modules/theme';
|
||||||
@@ -105,6 +106,9 @@ export class AIChatPanelTitle extends SignalWatcher(
|
|||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor notificationService!: NotificationService;
|
accessor notificationService!: NotificationService;
|
||||||
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
accessor aiToolsConfigService!: AIToolsConfigService;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor session!: CopilotChatHistoryFragment | null | undefined;
|
accessor session!: CopilotChatHistoryFragment | null | undefined;
|
||||||
|
|
||||||
@@ -142,6 +146,7 @@ export class AIChatPanelTitle extends SignalWatcher(
|
|||||||
.affineThemeService=${this.affineThemeService}
|
.affineThemeService=${this.affineThemeService}
|
||||||
.notificationService=${this.notificationService}
|
.notificationService=${this.notificationService}
|
||||||
.affineWorkspaceDialogService=${this.affineWorkspaceDialogService}
|
.affineWorkspaceDialogService=${this.affineWorkspaceDialogService}
|
||||||
|
.aiToolsConfigService=${this.aiToolsConfigService}
|
||||||
></playground-content>
|
></playground-content>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
import type { AIDraftService } from '@affine/core/modules/ai-button';
|
import type {
|
||||||
|
AIDraftService,
|
||||||
|
AIToolsConfigService,
|
||||||
|
} from '@affine/core/modules/ai-button';
|
||||||
import type { WorkspaceDialogService } from '@affine/core/modules/dialogs';
|
import type { WorkspaceDialogService } from '@affine/core/modules/dialogs';
|
||||||
import type { FeatureFlagService } from '@affine/core/modules/feature-flag';
|
import type { FeatureFlagService } from '@affine/core/modules/feature-flag';
|
||||||
import type { AppThemeService } from '@affine/core/modules/theme';
|
import type { AppThemeService } from '@affine/core/modules/theme';
|
||||||
@@ -119,6 +122,9 @@ export class ChatPanel extends SignalWatcher(
|
|||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor aiDraftService!: AIDraftService;
|
accessor aiDraftService!: AIDraftService;
|
||||||
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
accessor aiToolsConfigService!: AIToolsConfigService;
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
accessor session: CopilotChatHistoryFragment | null | undefined;
|
accessor session: CopilotChatHistoryFragment | null | undefined;
|
||||||
|
|
||||||
@@ -387,6 +393,7 @@ export class ChatPanel extends SignalWatcher(
|
|||||||
.affineWorkspaceDialogService=${this.affineWorkspaceDialogService}
|
.affineWorkspaceDialogService=${this.affineWorkspaceDialogService}
|
||||||
.affineThemeService=${this.affineThemeService}
|
.affineThemeService=${this.affineThemeService}
|
||||||
.notificationService=${this.notificationService}
|
.notificationService=${this.notificationService}
|
||||||
|
.aiToolsConfigService=${this.aiToolsConfigService}
|
||||||
.session=${this.session}
|
.session=${this.session}
|
||||||
.status=${this.status}
|
.status=${this.status}
|
||||||
.embeddingProgress=${this.embeddingProgress}
|
.embeddingProgress=${this.embeddingProgress}
|
||||||
@@ -413,6 +420,7 @@ export class ChatPanel extends SignalWatcher(
|
|||||||
.affineThemeService=${this.affineThemeService}
|
.affineThemeService=${this.affineThemeService}
|
||||||
.notificationService=${this.notificationService}
|
.notificationService=${this.notificationService}
|
||||||
.aiDraftService=${this.aiDraftService}
|
.aiDraftService=${this.aiDraftService}
|
||||||
|
.aiToolsConfigService=${this.aiToolsConfigService}
|
||||||
.onEmbeddingProgressChange=${this.onEmbeddingProgressChange}
|
.onEmbeddingProgressChange=${this.onEmbeddingProgressChange}
|
||||||
.onContextChange=${this.onContextChange}
|
.onContextChange=${this.onContextChange}
|
||||||
.width=${this.sidebarWidth}
|
.width=${this.sidebarWidth}
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import './ai-chat-composer-tip';
|
import './ai-chat-composer-tip';
|
||||||
|
|
||||||
import type { AIDraftService } from '@affine/core/modules/ai-button';
|
import type {
|
||||||
|
AIDraftService,
|
||||||
|
AIToolsConfigService,
|
||||||
|
} from '@affine/core/modules/ai-button';
|
||||||
import type { WorkspaceDialogService } from '@affine/core/modules/dialogs';
|
import type { WorkspaceDialogService } from '@affine/core/modules/dialogs';
|
||||||
import type {
|
import type {
|
||||||
ContextEmbedStatus,
|
ContextEmbedStatus,
|
||||||
@@ -118,7 +121,10 @@ export class AIChatComposer extends SignalWatcher(
|
|||||||
accessor notificationService!: NotificationService;
|
accessor notificationService!: NotificationService;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor aiDraftService!: AIDraftService;
|
accessor aiDraftService: AIDraftService | undefined;
|
||||||
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
accessor aiToolsConfigService!: AIToolsConfigService;
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
accessor chips: ChatChip[] = [];
|
accessor chips: ChatChip[] = [];
|
||||||
@@ -166,6 +172,7 @@ export class AIChatComposer extends SignalWatcher(
|
|||||||
.docDisplayConfig=${this.docDisplayConfig}
|
.docDisplayConfig=${this.docDisplayConfig}
|
||||||
.searchMenuConfig=${this.searchMenuConfig}
|
.searchMenuConfig=${this.searchMenuConfig}
|
||||||
.aiDraftService=${this.aiDraftService}
|
.aiDraftService=${this.aiDraftService}
|
||||||
|
.aiToolsConfigService=${this.aiToolsConfigService}
|
||||||
.portalContainer=${this.portalContainer}
|
.portalContainer=${this.portalContainer}
|
||||||
.onChatSuccess=${this.onChatSuccess}
|
.onChatSuccess=${this.onChatSuccess}
|
||||||
.trackOptions=${this.trackOptions}
|
.trackOptions=${this.trackOptions}
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
import type { AIDraftService } from '@affine/core/modules/ai-button';
|
import type {
|
||||||
|
AIDraftService,
|
||||||
|
AIToolsConfigService,
|
||||||
|
} from '@affine/core/modules/ai-button';
|
||||||
import type { AIDraftState } from '@affine/core/modules/ai-button/services/ai-draft';
|
import type { AIDraftState } from '@affine/core/modules/ai-button/services/ai-draft';
|
||||||
import type { WorkspaceDialogService } from '@affine/core/modules/dialogs';
|
import type { WorkspaceDialogService } from '@affine/core/modules/dialogs';
|
||||||
import type { FeatureFlagService } from '@affine/core/modules/feature-flag';
|
import type { FeatureFlagService } from '@affine/core/modules/feature-flag';
|
||||||
@@ -153,7 +156,10 @@ export class AIChatContent extends SignalWatcher(
|
|||||||
accessor notificationService!: NotificationService;
|
accessor notificationService!: NotificationService;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor aiDraftService!: AIDraftService;
|
accessor aiDraftService: AIDraftService | undefined;
|
||||||
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
accessor aiToolsConfigService!: AIToolsConfigService;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor onEmbeddingProgressChange:
|
accessor onEmbeddingProgressChange:
|
||||||
@@ -273,6 +279,9 @@ export class AIChatContent extends SignalWatcher(
|
|||||||
};
|
};
|
||||||
|
|
||||||
private readonly updateDraft = async (context: Partial<ChatContextValue>) => {
|
private readonly updateDraft = async (context: Partial<ChatContextValue>) => {
|
||||||
|
if (!this.aiDraftService) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const draft: Partial<AIDraftState> = pick(context, [
|
const draft: Partial<AIDraftState> = pick(context, [
|
||||||
'quote',
|
'quote',
|
||||||
'images',
|
'images',
|
||||||
@@ -344,15 +353,17 @@ export class AIChatContent extends SignalWatcher(
|
|||||||
|
|
||||||
this.initChatContent().catch(console.error);
|
this.initChatContent().catch(console.error);
|
||||||
|
|
||||||
this.aiDraftService
|
if (this.aiDraftService) {
|
||||||
.getDraft()
|
this.aiDraftService
|
||||||
.then(draft => {
|
.getDraft()
|
||||||
this.chatContextValue = {
|
.then(draft => {
|
||||||
...this.chatContextValue,
|
this.chatContextValue = {
|
||||||
...draft,
|
...this.chatContextValue,
|
||||||
};
|
...draft,
|
||||||
})
|
};
|
||||||
.catch(console.error);
|
})
|
||||||
|
.catch(console.error);
|
||||||
|
}
|
||||||
|
|
||||||
this._disposables.add(
|
this._disposables.add(
|
||||||
AIProvider.slots.actions.subscribe(({ event }) => {
|
AIProvider.slots.actions.subscribe(({ event }) => {
|
||||||
@@ -405,6 +416,7 @@ export class AIChatContent extends SignalWatcher(
|
|||||||
.affineFeatureFlagService=${this.affineFeatureFlagService}
|
.affineFeatureFlagService=${this.affineFeatureFlagService}
|
||||||
.affineThemeService=${this.affineThemeService}
|
.affineThemeService=${this.affineThemeService}
|
||||||
.notificationService=${this.notificationService}
|
.notificationService=${this.notificationService}
|
||||||
|
.aiToolsConfigService=${this.aiToolsConfigService}
|
||||||
.networkSearchConfig=${this.networkSearchConfig}
|
.networkSearchConfig=${this.networkSearchConfig}
|
||||||
.reasoningConfig=${this.reasoningConfig}
|
.reasoningConfig=${this.reasoningConfig}
|
||||||
.width=${this.width}
|
.width=${this.width}
|
||||||
@@ -434,6 +446,7 @@ export class AIChatContent extends SignalWatcher(
|
|||||||
.affineWorkspaceDialogService=${this.affineWorkspaceDialogService}
|
.affineWorkspaceDialogService=${this.affineWorkspaceDialogService}
|
||||||
.notificationService=${this.notificationService}
|
.notificationService=${this.notificationService}
|
||||||
.aiDraftService=${this.aiDraftService}
|
.aiDraftService=${this.aiDraftService}
|
||||||
|
.aiToolsConfigService=${this.aiToolsConfigService}
|
||||||
.trackOptions=${{
|
.trackOptions=${{
|
||||||
where: 'chat-panel',
|
where: 'chat-panel',
|
||||||
control: 'chat-send',
|
control: 'chat-send',
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
import type { AIDraftService } from '@affine/core/modules/ai-button';
|
import type {
|
||||||
|
AIDraftService,
|
||||||
|
AIToolsConfigService,
|
||||||
|
} from '@affine/core/modules/ai-button';
|
||||||
import type { CopilotChatHistoryFragment } from '@affine/graphql';
|
import type { CopilotChatHistoryFragment } from '@affine/graphql';
|
||||||
import { SignalWatcher, WithDisposable } from '@blocksuite/affine/global/lit';
|
import { SignalWatcher, WithDisposable } from '@blocksuite/affine/global/lit';
|
||||||
import { unsafeCSSVar, unsafeCSSVarV2 } from '@blocksuite/affine/shared/theme';
|
import { unsafeCSSVar, unsafeCSSVarV2 } from '@blocksuite/affine/shared/theme';
|
||||||
@@ -353,7 +356,10 @@ export class AIChatInput extends SignalWatcher(
|
|||||||
accessor searchMenuConfig!: SearchMenuConfig;
|
accessor searchMenuConfig!: SearchMenuConfig;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor aiDraftService!: AIDraftService;
|
accessor aiDraftService: AIDraftService | undefined;
|
||||||
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
accessor aiToolsConfigService!: AIToolsConfigService;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor isRootSession: boolean = true;
|
accessor isRootSession: boolean = true;
|
||||||
@@ -406,13 +412,15 @@ export class AIChatInput extends SignalWatcher(
|
|||||||
|
|
||||||
protected override firstUpdated(changedProperties: PropertyValues): void {
|
protected override firstUpdated(changedProperties: PropertyValues): void {
|
||||||
super.firstUpdated(changedProperties);
|
super.firstUpdated(changedProperties);
|
||||||
this.aiDraftService
|
if (this.aiDraftService) {
|
||||||
.getDraft()
|
this.aiDraftService
|
||||||
.then(draft => {
|
.getDraft()
|
||||||
this.textarea.value = draft.input;
|
.then(draft => {
|
||||||
this.isInputEmpty = !this.textarea.value.trim();
|
this.textarea.value = draft.input;
|
||||||
})
|
this.isInputEmpty = !this.textarea.value.trim();
|
||||||
.catch(console.error);
|
})
|
||||||
|
.catch(console.error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override render() {
|
protected override render() {
|
||||||
@@ -493,6 +501,7 @@ export class AIChatInput extends SignalWatcher(
|
|||||||
.networkSearchVisible=${!!this.networkSearchConfig.visible.value}
|
.networkSearchVisible=${!!this.networkSearchConfig.visible.value}
|
||||||
.isNetworkActive=${this._isNetworkActive}
|
.isNetworkActive=${this._isNetworkActive}
|
||||||
.onNetworkActiveChange=${this._toggleNetworkSearch}
|
.onNetworkActiveChange=${this._toggleNetworkSearch}
|
||||||
|
.toolsConfigService=${this.aiToolsConfigService}
|
||||||
></chat-input-preference>
|
></chat-input-preference>
|
||||||
${status === 'transmitting' || status === 'loading'
|
${status === 'transmitting' || status === 'loading'
|
||||||
? html`<button
|
? html`<button
|
||||||
@@ -536,9 +545,11 @@ export class AIChatInput extends SignalWatcher(
|
|||||||
textarea.style.overflowY = 'scroll';
|
textarea.style.overflowY = 'scroll';
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.aiDraftService.setDraft({
|
if (this.aiDraftService) {
|
||||||
input: value,
|
await this.aiDraftService.setDraft({
|
||||||
});
|
input: value,
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private readonly _handleKeyDown = async (evt: KeyboardEvent) => {
|
private readonly _handleKeyDown = async (evt: KeyboardEvent) => {
|
||||||
@@ -593,9 +604,11 @@ export class AIChatInput extends SignalWatcher(
|
|||||||
this.isInputEmpty = true;
|
this.isInputEmpty = true;
|
||||||
this.textarea.style.height = 'unset';
|
this.textarea.style.height = 'unset';
|
||||||
|
|
||||||
await this.aiDraftService.setDraft({
|
if (this.aiDraftService) {
|
||||||
input: '',
|
await this.aiDraftService.setDraft({
|
||||||
});
|
input: '',
|
||||||
|
});
|
||||||
|
}
|
||||||
await this.send(value);
|
await this.send(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -647,6 +660,7 @@ export class AIChatInput extends SignalWatcher(
|
|||||||
control: this.trackOptions?.control,
|
control: this.trackOptions?.control,
|
||||||
webSearch: this._isNetworkActive,
|
webSearch: this._isNetworkActive,
|
||||||
reasoning: this._isReasoningActive,
|
reasoning: this._isReasoningActive,
|
||||||
|
toolsConfig: this.aiToolsConfigService.config.value,
|
||||||
modelId: this.modelId,
|
modelId: this.modelId,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import type { AIToolsConfigService } from '@affine/core/modules/ai-button';
|
||||||
import type { CopilotChatHistoryFragment } from '@affine/graphql';
|
import type { CopilotChatHistoryFragment } from '@affine/graphql';
|
||||||
import {
|
import {
|
||||||
menu,
|
menu,
|
||||||
@@ -7,6 +8,7 @@ import {
|
|||||||
import { SignalWatcher, WithDisposable } from '@blocksuite/affine/global/lit';
|
import { SignalWatcher, WithDisposable } from '@blocksuite/affine/global/lit';
|
||||||
import {
|
import {
|
||||||
ArrowDownSmallIcon,
|
ArrowDownSmallIcon,
|
||||||
|
CloudWorkspaceIcon,
|
||||||
ThinkingIcon,
|
ThinkingIcon,
|
||||||
WebIcon,
|
WebIcon,
|
||||||
} from '@blocksuite/icons/lit';
|
} from '@blocksuite/icons/lit';
|
||||||
@@ -81,6 +83,9 @@ export class ChatInputPreference extends SignalWatcher(
|
|||||||
| undefined;
|
| undefined;
|
||||||
// --------- search props end ---------
|
// --------- search props end ---------
|
||||||
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
accessor toolsConfigService!: AIToolsConfigService;
|
||||||
|
|
||||||
// private readonly _onModelChange = (modelId: string) => {
|
// private readonly _onModelChange = (modelId: string) => {
|
||||||
// this.onModelChange?.(modelId);
|
// this.onModelChange?.(modelId);
|
||||||
// };
|
// };
|
||||||
@@ -126,6 +131,19 @@ export class ChatInputPreference extends SignalWatcher(
|
|||||||
onChange: (value: boolean) => this.onNetworkActiveChange?.(value),
|
onChange: (value: boolean) => this.onNetworkActiveChange?.(value),
|
||||||
class: { 'preference-action': true },
|
class: { 'preference-action': true },
|
||||||
testId: 'chat-network-search',
|
testId: 'chat-network-search',
|
||||||
|
}),
|
||||||
|
menu.toggleSwitch({
|
||||||
|
name: 'Workspace All Docs',
|
||||||
|
prefix: CloudWorkspaceIcon(),
|
||||||
|
on:
|
||||||
|
!!this.toolsConfigService.config.value.searchWorkspace &&
|
||||||
|
!!this.toolsConfigService.config.value.readingDocs,
|
||||||
|
onChange: (value: boolean) =>
|
||||||
|
this.toolsConfigService.setConfig({
|
||||||
|
searchWorkspace: value,
|
||||||
|
readingDocs: value,
|
||||||
|
}),
|
||||||
|
class: { 'preference-action': true },
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import type { AIToolsConfigService } from '@affine/core/modules/ai-button';
|
||||||
import type { AppThemeService } from '@affine/core/modules/theme';
|
import type { AppThemeService } from '@affine/core/modules/theme';
|
||||||
import type { CopilotChatHistoryFragment } from '@affine/graphql';
|
import type { CopilotChatHistoryFragment } from '@affine/graphql';
|
||||||
import { WithDisposable } from '@blocksuite/affine/global/lit';
|
import { WithDisposable } from '@blocksuite/affine/global/lit';
|
||||||
@@ -206,6 +207,9 @@ export class AIChatMessages extends WithDisposable(ShadowlessElement) {
|
|||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor docDisplayService!: DocDisplayConfig;
|
accessor docDisplayService!: DocDisplayConfig;
|
||||||
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
accessor aiToolsConfigService!: AIToolsConfigService;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor onOpenDoc!: (docId: string, sessionId?: string) => void;
|
accessor onOpenDoc!: (docId: string, sessionId?: string) => void;
|
||||||
|
|
||||||
@@ -467,6 +471,7 @@ export class AIChatMessages extends WithDisposable(ShadowlessElement) {
|
|||||||
isRootSession: true,
|
isRootSession: true,
|
||||||
reasoning: this._isReasoningActive,
|
reasoning: this._isReasoningActive,
|
||||||
webSearch: this._isNetworkActive,
|
webSearch: this._isNetworkActive,
|
||||||
|
toolsConfig: this.aiToolsConfigService.config.value,
|
||||||
});
|
});
|
||||||
|
|
||||||
for await (const text of stream) {
|
for await (const text of stream) {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import type { AIToolsConfigService } from '@affine/core/modules/ai-button';
|
||||||
import type { WorkspaceDialogService } from '@affine/core/modules/dialogs';
|
import type { WorkspaceDialogService } from '@affine/core/modules/dialogs';
|
||||||
import type { FeatureFlagService } from '@affine/core/modules/feature-flag';
|
import type { FeatureFlagService } from '@affine/core/modules/feature-flag';
|
||||||
import type { AppThemeService } from '@affine/core/modules/theme';
|
import type { AppThemeService } from '@affine/core/modules/theme';
|
||||||
@@ -173,6 +174,9 @@ export class PlaygroundChat extends SignalWatcher(
|
|||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor notificationService!: NotificationService;
|
accessor notificationService!: NotificationService;
|
||||||
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
accessor aiToolsConfigService!: AIToolsConfigService;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor addChat!: () => Promise<void>;
|
accessor addChat!: () => Promise<void>;
|
||||||
|
|
||||||
@@ -338,6 +342,7 @@ export class PlaygroundChat extends SignalWatcher(
|
|||||||
.affineFeatureFlagService=${this.affineFeatureFlagService}
|
.affineFeatureFlagService=${this.affineFeatureFlagService}
|
||||||
.affineThemeService=${this.affineThemeService}
|
.affineThemeService=${this.affineThemeService}
|
||||||
.notificationService=${this.notificationService}
|
.notificationService=${this.notificationService}
|
||||||
|
.aiToolsConfigService=${this.aiToolsConfigService}
|
||||||
.networkSearchConfig=${this.networkSearchConfig}
|
.networkSearchConfig=${this.networkSearchConfig}
|
||||||
.reasoningConfig=${this.reasoningConfig}
|
.reasoningConfig=${this.reasoningConfig}
|
||||||
.messages=${this.messages}
|
.messages=${this.messages}
|
||||||
@@ -357,6 +362,7 @@ export class PlaygroundChat extends SignalWatcher(
|
|||||||
.docDisplayConfig=${this.docDisplayConfig}
|
.docDisplayConfig=${this.docDisplayConfig}
|
||||||
.searchMenuConfig=${this.searchMenuConfig}
|
.searchMenuConfig=${this.searchMenuConfig}
|
||||||
.notificationService=${this.notificationService}
|
.notificationService=${this.notificationService}
|
||||||
|
.aiToolsConfigService=${this.aiToolsConfigService}
|
||||||
.affineWorkspaceDialogService=${this.affineWorkspaceDialogService}
|
.affineWorkspaceDialogService=${this.affineWorkspaceDialogService}
|
||||||
></ai-chat-composer>
|
></ai-chat-composer>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import type { AIToolsConfigService } from '@affine/core/modules/ai-button';
|
||||||
import type { FeatureFlagService } from '@affine/core/modules/feature-flag';
|
import type { FeatureFlagService } from '@affine/core/modules/feature-flag';
|
||||||
import type { AppThemeService } from '@affine/core/modules/theme';
|
import type { AppThemeService } from '@affine/core/modules/theme';
|
||||||
import type { CopilotChatHistoryFragment } from '@affine/graphql';
|
import type { CopilotChatHistoryFragment } from '@affine/graphql';
|
||||||
@@ -92,6 +93,9 @@ export class PlaygroundContent extends SignalWatcher(
|
|||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor notificationService!: NotificationService;
|
accessor notificationService!: NotificationService;
|
||||||
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
accessor aiToolsConfigService!: AIToolsConfigService;
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
accessor sessions: CopilotChatHistoryFragment[] = [];
|
accessor sessions: CopilotChatHistoryFragment[] = [];
|
||||||
|
|
||||||
@@ -347,6 +351,7 @@ export class PlaygroundContent extends SignalWatcher(
|
|||||||
.affineFeatureFlagService=${this.affineFeatureFlagService}
|
.affineFeatureFlagService=${this.affineFeatureFlagService}
|
||||||
.affineThemeService=${this.affineThemeService}
|
.affineThemeService=${this.affineThemeService}
|
||||||
.notificationService=${this.notificationService}
|
.notificationService=${this.notificationService}
|
||||||
|
.aiToolsConfigService=${this.aiToolsConfigService}
|
||||||
.addChat=${this.addChat}
|
.addChat=${this.addChat}
|
||||||
></playground-chat>
|
></playground-chat>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
import type {
|
||||||
|
AIDraftService,
|
||||||
|
AIToolsConfigService,
|
||||||
|
} from '@affine/core/modules/ai-button';
|
||||||
import type { WorkspaceDialogService } from '@affine/core/modules/dialogs';
|
import type { WorkspaceDialogService } from '@affine/core/modules/dialogs';
|
||||||
import type { FeatureFlagService } from '@affine/core/modules/feature-flag';
|
import type { FeatureFlagService } from '@affine/core/modules/feature-flag';
|
||||||
import type {
|
import type {
|
||||||
@@ -393,6 +397,7 @@ export class AIChatBlockPeekView extends LitElement {
|
|||||||
control: 'chat-send',
|
control: 'chat-send',
|
||||||
reasoning: this._isReasoningActive,
|
reasoning: this._isReasoningActive,
|
||||||
webSearch: this._isNetworkActive,
|
webSearch: this._isNetworkActive,
|
||||||
|
toolsConfig: this.aiToolsConfigService.config.value,
|
||||||
});
|
});
|
||||||
|
|
||||||
for await (const text of stream) {
|
for await (const text of stream) {
|
||||||
@@ -608,6 +613,7 @@ export class AIChatBlockPeekView extends LitElement {
|
|||||||
.searchMenuConfig=${this.searchMenuConfig}
|
.searchMenuConfig=${this.searchMenuConfig}
|
||||||
.affineWorkspaceDialogService=${this.affineWorkspaceDialogService}
|
.affineWorkspaceDialogService=${this.affineWorkspaceDialogService}
|
||||||
.notificationService=${notificationService}
|
.notificationService=${notificationService}
|
||||||
|
.aiToolsConfigService=${this.aiToolsConfigService}
|
||||||
.onChatSuccess=${this._onChatSuccess}
|
.onChatSuccess=${this._onChatSuccess}
|
||||||
.trackOptions=${{
|
.trackOptions=${{
|
||||||
where: 'ai-chat-block',
|
where: 'ai-chat-block',
|
||||||
@@ -646,6 +652,12 @@ export class AIChatBlockPeekView extends LitElement {
|
|||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor affineWorkspaceDialogService!: WorkspaceDialogService;
|
accessor affineWorkspaceDialogService!: WorkspaceDialogService;
|
||||||
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
accessor aiDraftService!: AIDraftService;
|
||||||
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
accessor aiToolsConfigService!: AIToolsConfigService;
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
accessor _historyMessages: ChatMessage[] = [];
|
accessor _historyMessages: ChatMessage[] = [];
|
||||||
|
|
||||||
@@ -682,7 +694,9 @@ export const AIChatBlockPeekViewTemplate = (
|
|||||||
networkSearchConfig: AINetworkSearchConfig,
|
networkSearchConfig: AINetworkSearchConfig,
|
||||||
reasoningConfig: AIReasoningConfig,
|
reasoningConfig: AIReasoningConfig,
|
||||||
affineFeatureFlagService: FeatureFlagService,
|
affineFeatureFlagService: FeatureFlagService,
|
||||||
affineWorkspaceDialogService: WorkspaceDialogService
|
affineWorkspaceDialogService: WorkspaceDialogService,
|
||||||
|
aiDraftService: AIDraftService,
|
||||||
|
aiToolsConfigService: AIToolsConfigService
|
||||||
) => {
|
) => {
|
||||||
return html`<ai-chat-block-peek-view
|
return html`<ai-chat-block-peek-view
|
||||||
.blockModel=${blockModel}
|
.blockModel=${blockModel}
|
||||||
@@ -693,5 +707,7 @@ export const AIChatBlockPeekViewTemplate = (
|
|||||||
.reasoningConfig=${reasoningConfig}
|
.reasoningConfig=${reasoningConfig}
|
||||||
.affineFeatureFlagService=${affineFeatureFlagService}
|
.affineFeatureFlagService=${affineFeatureFlagService}
|
||||||
.affineWorkspaceDialogService=${affineWorkspaceDialogService}
|
.affineWorkspaceDialogService=${affineWorkspaceDialogService}
|
||||||
|
.aiDraftService=${aiDraftService}
|
||||||
|
.aiToolsConfigService=${aiToolsConfigService}
|
||||||
></ai-chat-block-peek-view>`;
|
></ai-chat-block-peek-view>`;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { showAILoginRequiredAtom } from '@affine/core/components/affine/auth/ai-login-required';
|
import { showAILoginRequiredAtom } from '@affine/core/components/affine/auth/ai-login-required';
|
||||||
|
import type { AIToolsConfig } from '@affine/core/modules/ai-button';
|
||||||
import type { UserFriendlyError } from '@affine/error';
|
import type { UserFriendlyError } from '@affine/error';
|
||||||
import {
|
import {
|
||||||
addContextCategoryMutation,
|
addContextCategoryMutation,
|
||||||
@@ -415,6 +416,7 @@ export class CopilotClient {
|
|||||||
reasoning,
|
reasoning,
|
||||||
webSearch,
|
webSearch,
|
||||||
modelId,
|
modelId,
|
||||||
|
toolsConfig,
|
||||||
signal,
|
signal,
|
||||||
}: {
|
}: {
|
||||||
sessionId: string;
|
sessionId: string;
|
||||||
@@ -422,6 +424,7 @@ export class CopilotClient {
|
|||||||
reasoning?: boolean;
|
reasoning?: boolean;
|
||||||
webSearch?: boolean;
|
webSearch?: boolean;
|
||||||
modelId?: string;
|
modelId?: string;
|
||||||
|
toolsConfig?: AIToolsConfig;
|
||||||
signal?: AbortSignal;
|
signal?: AbortSignal;
|
||||||
}) {
|
}) {
|
||||||
let url = `/api/copilot/chat/${sessionId}`;
|
let url = `/api/copilot/chat/${sessionId}`;
|
||||||
@@ -430,6 +433,7 @@ export class CopilotClient {
|
|||||||
reasoning,
|
reasoning,
|
||||||
webSearch,
|
webSearch,
|
||||||
modelId,
|
modelId,
|
||||||
|
toolsConfig,
|
||||||
});
|
});
|
||||||
if (queryString) {
|
if (queryString) {
|
||||||
url += `?${queryString}`;
|
url += `?${queryString}`;
|
||||||
@@ -446,12 +450,14 @@ export class CopilotClient {
|
|||||||
reasoning,
|
reasoning,
|
||||||
webSearch,
|
webSearch,
|
||||||
modelId,
|
modelId,
|
||||||
|
toolsConfig,
|
||||||
}: {
|
}: {
|
||||||
sessionId: string;
|
sessionId: string;
|
||||||
messageId?: string;
|
messageId?: string;
|
||||||
reasoning?: boolean;
|
reasoning?: boolean;
|
||||||
webSearch?: boolean;
|
webSearch?: boolean;
|
||||||
modelId?: string;
|
modelId?: string;
|
||||||
|
toolsConfig?: AIToolsConfig;
|
||||||
},
|
},
|
||||||
endpoint = Endpoint.Stream
|
endpoint = Endpoint.Stream
|
||||||
) {
|
) {
|
||||||
@@ -461,6 +467,7 @@ export class CopilotClient {
|
|||||||
reasoning,
|
reasoning,
|
||||||
webSearch,
|
webSearch,
|
||||||
modelId,
|
modelId,
|
||||||
|
toolsConfig,
|
||||||
});
|
});
|
||||||
if (queryString) {
|
if (queryString) {
|
||||||
url += `?${queryString}`;
|
url += `?${queryString}`;
|
||||||
@@ -486,7 +493,9 @@ export class CopilotClient {
|
|||||||
return this.eventSource(url);
|
return this.eventSource(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
paramsToQueryString(params: Record<string, string | boolean | undefined>) {
|
paramsToQueryString(
|
||||||
|
params: Record<string, string | boolean | undefined | Record<string, any>>
|
||||||
|
) {
|
||||||
const queryString = new URLSearchParams();
|
const queryString = new URLSearchParams();
|
||||||
Object.entries(params).forEach(([key, value]) => {
|
Object.entries(params).forEach(([key, value]) => {
|
||||||
if (typeof value === 'boolean') {
|
if (typeof value === 'boolean') {
|
||||||
@@ -495,6 +504,8 @@ export class CopilotClient {
|
|||||||
}
|
}
|
||||||
} else if (typeof value === 'string') {
|
} else if (typeof value === 'string') {
|
||||||
queryString.append(key, value);
|
queryString.append(key, value);
|
||||||
|
} else if (typeof value === 'object' && value !== null) {
|
||||||
|
queryString.append(key, JSON.stringify(value));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return queryString.toString();
|
return queryString.toString();
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import type { AIToolsConfig } from '@affine/core/modules/ai-button';
|
||||||
import { partition } from 'lodash-es';
|
import { partition } from 'lodash-es';
|
||||||
|
|
||||||
import { AIProvider } from './ai-provider';
|
import { AIProvider } from './ai-provider';
|
||||||
@@ -22,6 +23,7 @@ export type TextToTextOptions = {
|
|||||||
reasoning?: boolean;
|
reasoning?: boolean;
|
||||||
webSearch?: boolean;
|
webSearch?: boolean;
|
||||||
modelId?: string;
|
modelId?: string;
|
||||||
|
toolsConfig?: AIToolsConfig;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ToImageOptions = TextToTextOptions & {
|
export type ToImageOptions = TextToTextOptions & {
|
||||||
@@ -119,6 +121,7 @@ export function textToText({
|
|||||||
reasoning,
|
reasoning,
|
||||||
webSearch,
|
webSearch,
|
||||||
modelId,
|
modelId,
|
||||||
|
toolsConfig,
|
||||||
}: TextToTextOptions) {
|
}: TextToTextOptions) {
|
||||||
let messageId: string | undefined;
|
let messageId: string | undefined;
|
||||||
|
|
||||||
@@ -141,6 +144,7 @@ export function textToText({
|
|||||||
reasoning,
|
reasoning,
|
||||||
webSearch,
|
webSearch,
|
||||||
modelId,
|
modelId,
|
||||||
|
toolsConfig,
|
||||||
},
|
},
|
||||||
endpoint
|
endpoint
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -11,7 +11,10 @@ import { getViewManager } from '@affine/core/blocksuite/manager/view';
|
|||||||
import { NotificationServiceImpl } from '@affine/core/blocksuite/view-extensions/editor-view/notification-service';
|
import { NotificationServiceImpl } from '@affine/core/blocksuite/view-extensions/editor-view/notification-service';
|
||||||
import { useAIChatConfig } from '@affine/core/components/hooks/affine/use-ai-chat-config';
|
import { useAIChatConfig } from '@affine/core/components/hooks/affine/use-ai-chat-config';
|
||||||
import { useAISpecs } from '@affine/core/components/hooks/affine/use-ai-specs';
|
import { useAISpecs } from '@affine/core/components/hooks/affine/use-ai-specs';
|
||||||
import { AIDraftService } from '@affine/core/modules/ai-button';
|
import {
|
||||||
|
AIDraftService,
|
||||||
|
AIToolsConfigService,
|
||||||
|
} from '@affine/core/modules/ai-button';
|
||||||
import {
|
import {
|
||||||
EventSourceService,
|
EventSourceService,
|
||||||
FetchService,
|
FetchService,
|
||||||
@@ -223,6 +226,7 @@ export const Component = () => {
|
|||||||
confirmModal.openConfirmModal
|
confirmModal.openConfirmModal
|
||||||
);
|
);
|
||||||
content.aiDraftService = framework.get(AIDraftService);
|
content.aiDraftService = framework.get(AIDraftService);
|
||||||
|
content.aiToolsConfigService = framework.get(AIToolsConfigService);
|
||||||
content.createSession = createSession;
|
content.createSession = createSession;
|
||||||
content.onOpenDoc = onOpenDoc;
|
content.onOpenDoc = onOpenDoc;
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,10 @@ import type { AffineEditorContainer } from '@affine/core/blocksuite/block-suite-
|
|||||||
import { NotificationServiceImpl } from '@affine/core/blocksuite/view-extensions/editor-view/notification-service';
|
import { NotificationServiceImpl } from '@affine/core/blocksuite/view-extensions/editor-view/notification-service';
|
||||||
import { useAIChatConfig } from '@affine/core/components/hooks/affine/use-ai-chat-config';
|
import { useAIChatConfig } from '@affine/core/components/hooks/affine/use-ai-chat-config';
|
||||||
import { useAISpecs } from '@affine/core/components/hooks/affine/use-ai-specs';
|
import { useAISpecs } from '@affine/core/components/hooks/affine/use-ai-specs';
|
||||||
import { AIDraftService } from '@affine/core/modules/ai-button';
|
import {
|
||||||
|
AIDraftService,
|
||||||
|
AIToolsConfigService,
|
||||||
|
} from '@affine/core/modules/ai-button';
|
||||||
import { WorkspaceDialogService } from '@affine/core/modules/dialogs';
|
import { WorkspaceDialogService } from '@affine/core/modules/dialogs';
|
||||||
import { FeatureFlagService } from '@affine/core/modules/feature-flag';
|
import { FeatureFlagService } from '@affine/core/modules/feature-flag';
|
||||||
import { AppThemeService } from '@affine/core/modules/theme';
|
import { AppThemeService } from '@affine/core/modules/theme';
|
||||||
@@ -97,6 +100,8 @@ export const EditorChatPanel = forwardRef(function EditorChatPanel(
|
|||||||
confirmModal.openConfirmModal
|
confirmModal.openConfirmModal
|
||||||
);
|
);
|
||||||
chatPanelRef.current.aiDraftService = framework.get(AIDraftService);
|
chatPanelRef.current.aiDraftService = framework.get(AIDraftService);
|
||||||
|
chatPanelRef.current.aiToolsConfigService =
|
||||||
|
framework.get(AIToolsConfigService);
|
||||||
|
|
||||||
containerRef.current?.append(chatPanelRef.current);
|
containerRef.current?.append(chatPanelRef.current);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
export { AIButtonProvider } from './provider/ai-button';
|
export { AIButtonProvider } from './provider/ai-button';
|
||||||
export { AIButtonService } from './services/ai-button';
|
export { AIButtonService } from './services/ai-button';
|
||||||
export { AIDraftService } from './services/ai-draft';
|
export { AIDraftService } from './services/ai-draft';
|
||||||
|
export {
|
||||||
|
type AIToolsConfig,
|
||||||
|
AIToolsConfigService,
|
||||||
|
} from './services/tools-config';
|
||||||
|
|
||||||
import type { Framework } from '@toeverything/infra';
|
import type { Framework } from '@toeverything/infra';
|
||||||
|
|
||||||
@@ -13,6 +17,7 @@ import { AIDraftService } from './services/ai-draft';
|
|||||||
import { AINetworkSearchService } from './services/network-search';
|
import { AINetworkSearchService } from './services/network-search';
|
||||||
import { AIPlaygroundService } from './services/playground';
|
import { AIPlaygroundService } from './services/playground';
|
||||||
import { AIReasoningService } from './services/reasoning';
|
import { AIReasoningService } from './services/reasoning';
|
||||||
|
import { AIToolsConfigService } from './services/tools-config';
|
||||||
|
|
||||||
export const configureAIButtonModule = (framework: Framework) => {
|
export const configureAIButtonModule = (framework: Framework) => {
|
||||||
framework.service(AIButtonService, container => {
|
framework.service(AIButtonService, container => {
|
||||||
@@ -40,3 +45,7 @@ export function configureAIDraftModule(framework: Framework) {
|
|||||||
.scope(WorkspaceScope)
|
.scope(WorkspaceScope)
|
||||||
.service(AIDraftService, [GlobalStateService, CacheStorage]);
|
.service(AIDraftService, [GlobalStateService, CacheStorage]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function configureAIToolsConfigModule(framework: Framework) {
|
||||||
|
framework.service(AIToolsConfigService, [GlobalStateService]);
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
import {
|
||||||
|
createSignalFromObservable,
|
||||||
|
type Signal,
|
||||||
|
} from '@blocksuite/affine/shared/utils';
|
||||||
|
import { LiveData, Service } from '@toeverything/infra';
|
||||||
|
import { map } from 'rxjs';
|
||||||
|
|
||||||
|
import type { GlobalStateService } from '../../storage';
|
||||||
|
|
||||||
|
const AI_TOOLS_CONFIG_KEY = 'AIToolsConfig';
|
||||||
|
|
||||||
|
export interface AIToolsConfig {
|
||||||
|
searchWorkspace?: boolean;
|
||||||
|
readingDocs?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AIToolsConfigService extends Service {
|
||||||
|
constructor(private readonly globalStateService: GlobalStateService) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
const { signal, cleanup: enabledCleanup } =
|
||||||
|
createSignalFromObservable<AIToolsConfig>(this.config$, {
|
||||||
|
searchWorkspace: true,
|
||||||
|
readingDocs: true,
|
||||||
|
});
|
||||||
|
this.config = signal;
|
||||||
|
this.disposables.push(enabledCleanup);
|
||||||
|
}
|
||||||
|
|
||||||
|
config: Signal<AIToolsConfig>;
|
||||||
|
|
||||||
|
private readonly config$ = LiveData.from(
|
||||||
|
this.globalStateService.globalState.watch<AIToolsConfig>(
|
||||||
|
AI_TOOLS_CONFIG_KEY
|
||||||
|
),
|
||||||
|
undefined
|
||||||
|
).pipe(
|
||||||
|
map(config => ({
|
||||||
|
searchWorkspace: config?.searchWorkspace ?? true,
|
||||||
|
readingDocs: config?.readingDocs ?? true,
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
|
||||||
|
setConfig = (data: Partial<AIToolsConfig>) => {
|
||||||
|
this.globalStateService.globalState.set(AI_TOOLS_CONFIG_KEY, {
|
||||||
|
...this.config.value,
|
||||||
|
...data,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
configureAINetworkSearchModule,
|
configureAINetworkSearchModule,
|
||||||
configureAIPlaygroundModule,
|
configureAIPlaygroundModule,
|
||||||
configureAIReasoningModule,
|
configureAIReasoningModule,
|
||||||
|
configureAIToolsConfigModule,
|
||||||
} 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';
|
||||||
@@ -112,6 +113,7 @@ export function configureCommonModules(framework: Framework) {
|
|||||||
configureAIPlaygroundModule(framework);
|
configureAIPlaygroundModule(framework);
|
||||||
configureAIButtonModule(framework);
|
configureAIButtonModule(framework);
|
||||||
configureAIDraftModule(framework);
|
configureAIDraftModule(framework);
|
||||||
|
configureAIToolsConfigModule(framework);
|
||||||
configureTemplateDocModule(framework);
|
configureTemplateDocModule(framework);
|
||||||
configureBlobManagementModule(framework);
|
configureBlobManagementModule(framework);
|
||||||
configureMediaModule(framework);
|
configureMediaModule(framework);
|
||||||
|
|||||||
@@ -2,6 +2,10 @@ import { toReactNode } from '@affine/component';
|
|||||||
import { AIChatBlockPeekViewTemplate } from '@affine/core/blocksuite/ai';
|
import { AIChatBlockPeekViewTemplate } from '@affine/core/blocksuite/ai';
|
||||||
import type { AIChatBlockModel } from '@affine/core/blocksuite/ai/blocks/ai-chat-block/model/ai-chat-model';
|
import type { AIChatBlockModel } from '@affine/core/blocksuite/ai/blocks/ai-chat-block/model/ai-chat-model';
|
||||||
import { useAIChatConfig } from '@affine/core/components/hooks/affine/use-ai-chat-config';
|
import { useAIChatConfig } from '@affine/core/components/hooks/affine/use-ai-chat-config';
|
||||||
|
import {
|
||||||
|
AIDraftService,
|
||||||
|
AIToolsConfigService,
|
||||||
|
} from '@affine/core/modules/ai-button';
|
||||||
import { WorkspaceDialogService } from '@affine/core/modules/dialogs';
|
import { WorkspaceDialogService } from '@affine/core/modules/dialogs';
|
||||||
import { FeatureFlagService } from '@affine/core/modules/feature-flag';
|
import { FeatureFlagService } from '@affine/core/modules/feature-flag';
|
||||||
import type { EditorHost } from '@blocksuite/affine/std';
|
import type { EditorHost } from '@blocksuite/affine/std';
|
||||||
@@ -27,6 +31,8 @@ export const AIChatBlockPeekView = ({
|
|||||||
const framework = useFramework();
|
const framework = useFramework();
|
||||||
const affineFeatureFlagService = framework.get(FeatureFlagService);
|
const affineFeatureFlagService = framework.get(FeatureFlagService);
|
||||||
const affineWorkspaceDialogService = framework.get(WorkspaceDialogService);
|
const affineWorkspaceDialogService = framework.get(WorkspaceDialogService);
|
||||||
|
const aiDraftService = framework.get(AIDraftService);
|
||||||
|
const aiToolsConfigService = framework.get(AIToolsConfigService);
|
||||||
|
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
const template = AIChatBlockPeekViewTemplate(
|
const template = AIChatBlockPeekViewTemplate(
|
||||||
@@ -37,7 +43,9 @@ export const AIChatBlockPeekView = ({
|
|||||||
networkSearchConfig,
|
networkSearchConfig,
|
||||||
reasoningConfig,
|
reasoningConfig,
|
||||||
affineFeatureFlagService,
|
affineFeatureFlagService,
|
||||||
affineWorkspaceDialogService
|
affineWorkspaceDialogService,
|
||||||
|
aiDraftService,
|
||||||
|
aiToolsConfigService
|
||||||
);
|
);
|
||||||
return toReactNode(template);
|
return toReactNode(template);
|
||||||
}, [
|
}, [
|
||||||
@@ -49,5 +57,7 @@ export const AIChatBlockPeekView = ({
|
|||||||
reasoningConfig,
|
reasoningConfig,
|
||||||
affineFeatureFlagService,
|
affineFeatureFlagService,
|
||||||
affineWorkspaceDialogService,
|
affineWorkspaceDialogService,
|
||||||
|
aiDraftService,
|
||||||
|
aiToolsConfigService,
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user