mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-26 10:45:57 +08:00
refactor(core): use the websearch parameters passed in by the front-end (#11989)
Support [AI-60](https://linear.app/affine-design/issue/AI-60). <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Refactor** - Updated naming conventions for the web search tool to ensure consistency across AI chat features. - Simplified internal handling of web search tool options for a more streamlined experience. - Unified parameter naming from "mustSearch" to "webSearch" across AI chat inputs and actions. - **Chores** - Removed unused configuration options related to web search from prompt settings. - Added support for enabling or disabling web search via new parameters in chat requests. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -179,12 +179,16 @@ export class CopilotController implements BeforeApplicationShutdown {
|
|||||||
const reasoning = Array.isArray(params.reasoning)
|
const reasoning = Array.isArray(params.reasoning)
|
||||||
? Boolean(params.reasoning[0])
|
? Boolean(params.reasoning[0])
|
||||||
: Boolean(params.reasoning);
|
: Boolean(params.reasoning);
|
||||||
|
const webSearch = Array.isArray(params.webSearch)
|
||||||
|
? Boolean(params.webSearch[0])
|
||||||
|
: Boolean(params.webSearch);
|
||||||
|
|
||||||
delete params.messageId;
|
delete params.messageId;
|
||||||
delete params.retry;
|
delete params.retry;
|
||||||
delete params.reasoning;
|
delete params.reasoning;
|
||||||
|
delete params.webSearch;
|
||||||
|
|
||||||
return { messageId, retry, reasoning, params };
|
return { messageId, retry, reasoning, webSearch, params };
|
||||||
}
|
}
|
||||||
|
|
||||||
private getSignal(req: Request) {
|
private getSignal(req: Request) {
|
||||||
@@ -232,7 +236,8 @@ export class CopilotController implements BeforeApplicationShutdown {
|
|||||||
const info: any = { sessionId, params };
|
const info: any = { sessionId, params };
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { messageId, retry, reasoning } = this.prepareParams(params);
|
const { messageId, retry, reasoning, webSearch } =
|
||||||
|
this.prepareParams(params);
|
||||||
|
|
||||||
const provider = await this.chooseTextProvider(
|
const provider = await this.chooseTextProvider(
|
||||||
user.id,
|
user.id,
|
||||||
@@ -264,6 +269,7 @@ export class CopilotController implements BeforeApplicationShutdown {
|
|||||||
signal: this.getSignal(req),
|
signal: this.getSignal(req),
|
||||||
user: user.id,
|
user: user.id,
|
||||||
reasoning,
|
reasoning,
|
||||||
|
webSearch,
|
||||||
});
|
});
|
||||||
|
|
||||||
session.push({
|
session.push({
|
||||||
@@ -296,7 +302,8 @@ export class CopilotController implements BeforeApplicationShutdown {
|
|||||||
const info: any = { sessionId, params, throwInStream: false };
|
const info: any = { sessionId, params, throwInStream: false };
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { messageId, retry, reasoning } = this.prepareParams(params);
|
const { messageId, retry, reasoning, webSearch } =
|
||||||
|
this.prepareParams(params);
|
||||||
|
|
||||||
const provider = await this.chooseTextProvider(
|
const provider = await this.chooseTextProvider(
|
||||||
user.id,
|
user.id,
|
||||||
@@ -330,6 +337,7 @@ export class CopilotController implements BeforeApplicationShutdown {
|
|||||||
signal: this.getSignal(req),
|
signal: this.getSignal(req),
|
||||||
user: user.id,
|
user: user.id,
|
||||||
reasoning,
|
reasoning,
|
||||||
|
webSearch,
|
||||||
})
|
})
|
||||||
).pipe(
|
).pipe(
|
||||||
connect(shared$ =>
|
connect(shared$ =>
|
||||||
|
|||||||
@@ -1044,9 +1044,9 @@ const chat: Prompt[] = [
|
|||||||
content: `You are AFFiNE AI, a professional and humorous copilot within AFFiNE. You are powered by latest GPT model from OpenAI and AFFiNE. AFFiNE is an open source general purposed productivity tool that contains unified building blocks that users can use on any interfaces, including block-based docs editor, infinite canvas based edgeless graphic mode, or multi-dimensional table with multiple transformable views. Your mission is always to try your very best to assist users to use AFFiNE to write docs, draw diagrams or plan things with these abilities. You always think step-by-step and describe your plan for what to build, using well-structured and clear markdown, written out in great detail. Unless otherwise specified, where list, JSON, or code blocks are required for giving the output. Minimize any other prose so that your responses can be directly used and inserted into the docs. You are able to access to API of AFFiNE to finish your job. You always respect the users' privacy and would not leak their info to anyone else. AFFiNE is made by Toeverything .Pte .Ltd, a company registered in Singapore with a diverse and international team. The company also open sourced blocksuite and octobase for building tools similar to Affine. The name AFFiNE comes from the idea of AFFiNE transform, as blocks in affine can all transform in page, edgeless or database mode. AFFiNE team is now having 25 members, an open source company driven by engineers. Today is: {{affine::date}}, User's preferred language is {{affine::language}}.
|
content: `You are AFFiNE AI, a professional and humorous copilot within AFFiNE. You are powered by latest GPT model from OpenAI and AFFiNE. AFFiNE is an open source general purposed productivity tool that contains unified building blocks that users can use on any interfaces, including block-based docs editor, infinite canvas based edgeless graphic mode, or multi-dimensional table with multiple transformable views. Your mission is always to try your very best to assist users to use AFFiNE to write docs, draw diagrams or plan things with these abilities. You always think step-by-step and describe your plan for what to build, using well-structured and clear markdown, written out in great detail. Unless otherwise specified, where list, JSON, or code blocks are required for giving the output. Minimize any other prose so that your responses can be directly used and inserted into the docs. You are able to access to API of AFFiNE to finish your job. You always respect the users' privacy and would not leak their info to anyone else. AFFiNE is made by Toeverything .Pte .Ltd, a company registered in Singapore with a diverse and international team. The company also open sourced blocksuite and octobase for building tools similar to Affine. The name AFFiNE comes from the idea of AFFiNE transform, as blocks in affine can all transform in page, edgeless or database mode. AFFiNE team is now having 25 members, an open source company driven by engineers. Today is: {{affine::date}}, User's preferred language is {{affine::language}}.
|
||||||
|
|
||||||
# Response Guide
|
# Response Guide
|
||||||
Use the webSearch tool to gather information from the web. There are two modes for web searching:
|
Use the web search tool to gather information from the web. There are two modes for web searching:
|
||||||
- MUST: Means you always need to use the webSearch tool to gather information from the web, no matter what the user's query is.
|
- MUST: Means you always need to use the web search tool to gather information from the web, no matter what the user's query is.
|
||||||
- CAN: Indicates that web searching is optional - you may use the webSearch tool at your discretion when you determine it would provide valuable information for answering the user's query.
|
- CAN: Indicates that web searching is optional - you may use the web search tool at your discretion when you determine it would provide valuable information for answering the user's query.
|
||||||
Currently, you are in the {{searchMode}} web searching mode.
|
Currently, you are in the {{searchMode}} web searching mode.
|
||||||
|
|
||||||
I will provide you with some content fragments. There are two types of content fragments:
|
I will provide you with some content fragments. There are two types of content fragments:
|
||||||
@@ -1115,9 +1115,6 @@ Below is the user's query. Please respond in the user's language without treatin
|
|||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
config: {
|
|
||||||
webSearch: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Search With AFFiNE AI',
|
name: 'Search With AFFiNE AI',
|
||||||
|
|||||||
@@ -134,9 +134,7 @@ export class AnthropicProvider
|
|||||||
providerOptions: {
|
providerOptions: {
|
||||||
anthropic: this.getAnthropicOptions(options),
|
anthropic: this.getAnthropicOptions(options),
|
||||||
},
|
},
|
||||||
tools: {
|
tools: this.getTools(options),
|
||||||
webSearch: createExaTool(this.AFFiNEConfig),
|
|
||||||
},
|
|
||||||
maxSteps: this.MAX_STEPS,
|
maxSteps: this.MAX_STEPS,
|
||||||
experimental_continueSteps: true,
|
experimental_continueSteps: true,
|
||||||
});
|
});
|
||||||
@@ -168,9 +166,7 @@ export class AnthropicProvider
|
|||||||
providerOptions: {
|
providerOptions: {
|
||||||
anthropic: this.getAnthropicOptions(options),
|
anthropic: this.getAnthropicOptions(options),
|
||||||
},
|
},
|
||||||
tools: {
|
tools: this.getTools(options),
|
||||||
webSearch: createExaTool(this.AFFiNEConfig),
|
|
||||||
},
|
|
||||||
maxSteps: this.MAX_STEPS,
|
maxSteps: this.MAX_STEPS,
|
||||||
experimental_continueSteps: true,
|
experimental_continueSteps: true,
|
||||||
});
|
});
|
||||||
@@ -182,7 +178,7 @@ export class AnthropicProvider
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'tool-result': {
|
case 'tool-result': {
|
||||||
if (message.toolName === 'webSearch') {
|
if (message.toolName === 'web_search') {
|
||||||
this.toolResults.push(this.getWebSearchLinks(message.result));
|
this.toolResults.push(this.getWebSearchLinks(message.result));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -214,6 +210,15 @@ export class AnthropicProvider
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getTools(options: CopilotChatOptions) {
|
||||||
|
if (options?.webSearch) {
|
||||||
|
return {
|
||||||
|
web_search: createExaTool(this.AFFiNEConfig),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
private getAnthropicOptions(
|
private getAnthropicOptions(
|
||||||
options: CopilotChatOptions
|
options: CopilotChatOptions
|
||||||
): AnthropicProviderOptions {
|
): AnthropicProviderOptions {
|
||||||
|
|||||||
@@ -178,8 +178,8 @@ export class OpenAIProvider
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private getToolUse(options: CopilotChatOptions = {}) {
|
private getTools(options: CopilotChatOptions) {
|
||||||
if (options.webSearch) {
|
if (options?.webSearch) {
|
||||||
return {
|
return {
|
||||||
web_search_preview: openai.tools.webSearchPreview(),
|
web_search_preview: openai.tools.webSearchPreview(),
|
||||||
};
|
};
|
||||||
@@ -224,6 +224,7 @@ export class OpenAIProvider
|
|||||||
providerOptions: {
|
providerOptions: {
|
||||||
openai: options.user ? { user: options.user } : {},
|
openai: options.user ? { user: options.user } : {},
|
||||||
},
|
},
|
||||||
|
tools: this.getTools(options),
|
||||||
});
|
});
|
||||||
|
|
||||||
return text.trim();
|
return text.trim();
|
||||||
@@ -245,18 +246,16 @@ export class OpenAIProvider
|
|||||||
|
|
||||||
const [system, msgs] = await chatToGPTMessage(messages);
|
const [system, msgs] = await chatToGPTMessage(messages);
|
||||||
|
|
||||||
const modelInstance = options.webSearch
|
const modelInstance = this.#instance(model, {
|
||||||
? this.#instance.responses(model)
|
structuredOutputs: Boolean(options.jsonMode),
|
||||||
: this.#instance(model, {
|
user: options.user,
|
||||||
structuredOutputs: Boolean(options.jsonMode),
|
});
|
||||||
user: options.user,
|
|
||||||
});
|
|
||||||
|
|
||||||
const { fullStream } = streamText({
|
const { fullStream } = streamText({
|
||||||
model: modelInstance,
|
model: modelInstance,
|
||||||
system,
|
system,
|
||||||
messages: msgs,
|
messages: msgs,
|
||||||
tools: this.getToolUse(options),
|
tools: this.getTools(options),
|
||||||
frequencyPenalty: options.frequencyPenalty || 0,
|
frequencyPenalty: options.frequencyPenalty || 0,
|
||||||
presencePenalty: options.presencePenalty || 0,
|
presencePenalty: options.presencePenalty || 0,
|
||||||
temperature: options.temperature || 0,
|
temperature: options.temperature || 0,
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ export enum CopilotCapability {
|
|||||||
export const PromptConfigStrictSchema = z.object({
|
export const PromptConfigStrictSchema = z.object({
|
||||||
// openai
|
// openai
|
||||||
jsonMode: z.boolean().nullable().optional(),
|
jsonMode: z.boolean().nullable().optional(),
|
||||||
webSearch: z.boolean().nullable().optional(),
|
|
||||||
frequencyPenalty: z.number().nullable().optional(),
|
frequencyPenalty: z.number().nullable().optional(),
|
||||||
presencePenalty: z.number().nullable().optional(),
|
presencePenalty: z.number().nullable().optional(),
|
||||||
temperature: z.number().nullable().optional(),
|
temperature: z.number().nullable().optional(),
|
||||||
@@ -81,6 +80,7 @@ const CopilotChatOptionsSchema = CopilotProviderOptionsSchema.merge(
|
|||||||
)
|
)
|
||||||
.extend({
|
.extend({
|
||||||
reasoning: z.boolean().optional(),
|
reasoning: z.boolean().optional(),
|
||||||
|
webSearch: z.boolean().optional(),
|
||||||
})
|
})
|
||||||
.optional();
|
.optional();
|
||||||
|
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ function actionToStream<T extends keyof BlockSuitePresets.AIActions>(
|
|||||||
where,
|
where,
|
||||||
docId: host.doc.id,
|
docId: host.doc.id,
|
||||||
workspaceId: host.doc.workspace.id,
|
workspaceId: host.doc.workspace.id,
|
||||||
mustSearch: visible?.value && enabled?.value,
|
webSearch: visible?.value && enabled?.value,
|
||||||
} as Parameters<typeof action>[0];
|
} as Parameters<typeof action>[0];
|
||||||
// @ts-expect-error TODO(@Peng): maybe fix this
|
// @ts-expect-error TODO(@Peng): maybe fix this
|
||||||
stream = await action(options);
|
stream = await action(options);
|
||||||
|
|||||||
@@ -199,7 +199,7 @@ function actionToStream<T extends keyof BlockSuitePresets.AIActions>(
|
|||||||
host,
|
host,
|
||||||
docId: host.doc.id,
|
docId: host.doc.id,
|
||||||
workspaceId: host.doc.workspace.id,
|
workspaceId: host.doc.workspace.id,
|
||||||
mustSearch: visible?.value && enabled?.value,
|
webSearch: visible?.value && enabled?.value,
|
||||||
} as Parameters<typeof action>[0];
|
} as Parameters<typeof action>[0];
|
||||||
|
|
||||||
const content = ctx.get().content;
|
const content = ctx.get().content;
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ declare global {
|
|||||||
interface ChatOptions extends AITextActionOptions {
|
interface ChatOptions extends AITextActionOptions {
|
||||||
sessionId?: string;
|
sessionId?: string;
|
||||||
isRootSession?: boolean;
|
isRootSession?: boolean;
|
||||||
mustSearch?: boolean;
|
webSearch?: boolean;
|
||||||
reasoning?: boolean;
|
reasoning?: boolean;
|
||||||
contexts?: {
|
contexts?: {
|
||||||
docs: AIDocContextOption[];
|
docs: AIDocContextOption[];
|
||||||
|
|||||||
@@ -560,7 +560,7 @@ export class AIChatInput extends SignalWatcher(WithDisposable(LitElement)) {
|
|||||||
isRootSession: this.isRootSession,
|
isRootSession: this.isRootSession,
|
||||||
where: this.trackOptions.where,
|
where: this.trackOptions.where,
|
||||||
control: this.trackOptions.control,
|
control: this.trackOptions.control,
|
||||||
mustSearch: this._isNetworkActive,
|
webSearch: this._isNetworkActive,
|
||||||
reasoning: this._isReasoningActive,
|
reasoning: this._isReasoningActive,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -351,17 +351,20 @@ export class CopilotClient {
|
|||||||
sessionId,
|
sessionId,
|
||||||
messageId,
|
messageId,
|
||||||
reasoning,
|
reasoning,
|
||||||
|
webSearch,
|
||||||
signal,
|
signal,
|
||||||
}: {
|
}: {
|
||||||
sessionId: string;
|
sessionId: string;
|
||||||
messageId?: string;
|
messageId?: string;
|
||||||
reasoning?: boolean;
|
reasoning?: boolean;
|
||||||
|
webSearch?: boolean;
|
||||||
signal?: AbortSignal;
|
signal?: AbortSignal;
|
||||||
}) {
|
}) {
|
||||||
let url = `/api/copilot/chat/${sessionId}`;
|
let url = `/api/copilot/chat/${sessionId}`;
|
||||||
const queryString = this.paramsToQueryString({
|
const queryString = this.paramsToQueryString({
|
||||||
messageId,
|
messageId,
|
||||||
reasoning,
|
reasoning,
|
||||||
|
webSearch,
|
||||||
});
|
});
|
||||||
if (queryString) {
|
if (queryString) {
|
||||||
url += `?${queryString}`;
|
url += `?${queryString}`;
|
||||||
@@ -376,10 +379,12 @@ export class CopilotClient {
|
|||||||
sessionId,
|
sessionId,
|
||||||
messageId,
|
messageId,
|
||||||
reasoning,
|
reasoning,
|
||||||
|
webSearch,
|
||||||
}: {
|
}: {
|
||||||
sessionId: string;
|
sessionId: string;
|
||||||
messageId?: string;
|
messageId?: string;
|
||||||
reasoning?: boolean;
|
reasoning?: boolean;
|
||||||
|
webSearch?: boolean;
|
||||||
},
|
},
|
||||||
endpoint = 'stream'
|
endpoint = 'stream'
|
||||||
) {
|
) {
|
||||||
@@ -387,6 +392,7 @@ export class CopilotClient {
|
|||||||
const queryString = this.paramsToQueryString({
|
const queryString = this.paramsToQueryString({
|
||||||
messageId,
|
messageId,
|
||||||
reasoning,
|
reasoning,
|
||||||
|
webSearch,
|
||||||
});
|
});
|
||||||
if (queryString) {
|
if (queryString) {
|
||||||
url += `?${queryString}`;
|
url += `?${queryString}`;
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export type TextToTextOptions = {
|
|||||||
isRootSession?: boolean;
|
isRootSession?: boolean;
|
||||||
postfix?: (text: string) => string;
|
postfix?: (text: string) => string;
|
||||||
reasoning?: boolean;
|
reasoning?: boolean;
|
||||||
|
webSearch?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ToImageOptions = TextToTextOptions & {
|
export type ToImageOptions = TextToTextOptions & {
|
||||||
@@ -115,6 +116,7 @@ export function textToText({
|
|||||||
workflow = false,
|
workflow = false,
|
||||||
postfix,
|
postfix,
|
||||||
reasoning,
|
reasoning,
|
||||||
|
webSearch,
|
||||||
}: TextToTextOptions) {
|
}: TextToTextOptions) {
|
||||||
let messageId: string | undefined;
|
let messageId: string | undefined;
|
||||||
|
|
||||||
@@ -135,6 +137,7 @@ export function textToText({
|
|||||||
sessionId,
|
sessionId,
|
||||||
messageId,
|
messageId,
|
||||||
reasoning,
|
reasoning,
|
||||||
|
webSearch,
|
||||||
},
|
},
|
||||||
workflow ? 'workflow' : undefined
|
workflow ? 'workflow' : undefined
|
||||||
);
|
);
|
||||||
@@ -195,6 +198,7 @@ export function textToText({
|
|||||||
sessionId,
|
sessionId,
|
||||||
messageId,
|
messageId,
|
||||||
reasoning,
|
reasoning,
|
||||||
|
webSearch,
|
||||||
});
|
});
|
||||||
})(),
|
})(),
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ export function setupAIProvider(
|
|||||||
|
|
||||||
//#region actions
|
//#region actions
|
||||||
AIProvider.provide('chat', async options => {
|
AIProvider.provide('chat', async options => {
|
||||||
const { input, contexts, mustSearch } = options;
|
const { input, contexts, webSearch } = options;
|
||||||
|
|
||||||
const sessionId = await createSession({
|
const sessionId = await createSession({
|
||||||
promptName: 'Chat With AFFiNE AI',
|
promptName: 'Chat With AFFiNE AI',
|
||||||
@@ -96,7 +96,7 @@ export function setupAIProvider(
|
|||||||
params: {
|
params: {
|
||||||
docs: contexts?.docs,
|
docs: contexts?.docs,
|
||||||
files: contexts?.files,
|
files: contexts?.files,
|
||||||
searchMode: mustSearch ? 'MUST' : 'CAN',
|
searchMode: webSearch ? 'MUST' : 'CAN',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user