mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-13 21:05:19 +00:00
feat(server): remove context prefetch & integrate context search (#12956)
fix AI-173
This commit is contained in:
@@ -197,34 +197,52 @@ export class CopilotContextService implements OnApplicationBootstrap {
|
||||
async matchWorkspaceAll(
|
||||
workspaceId: string,
|
||||
content: string,
|
||||
topK: number = 5,
|
||||
topK: number,
|
||||
signal?: AbortSignal,
|
||||
threshold: number = 0.5
|
||||
threshold: number = 0.8,
|
||||
docIds?: string[],
|
||||
scopedThreshold: number = 0.85
|
||||
) {
|
||||
if (!this.embeddingClient) return [];
|
||||
const embedding = await this.embeddingClient.getEmbedding(content, signal);
|
||||
if (!embedding) return [];
|
||||
|
||||
const [fileChunks, workspaceChunks] = await Promise.all([
|
||||
this.models.copilotWorkspace.matchFileEmbedding(
|
||||
workspaceId,
|
||||
embedding,
|
||||
topK * 2,
|
||||
threshold
|
||||
),
|
||||
this.models.copilotContext.matchWorkspaceEmbedding(
|
||||
embedding,
|
||||
workspaceId,
|
||||
topK * 2,
|
||||
threshold
|
||||
),
|
||||
]);
|
||||
const [fileChunks, workspaceChunks, scopedWorkspaceChunks] =
|
||||
await Promise.all([
|
||||
this.models.copilotWorkspace.matchFileEmbedding(
|
||||
workspaceId,
|
||||
embedding,
|
||||
topK * 2,
|
||||
threshold
|
||||
),
|
||||
|
||||
if (!fileChunks.length && !workspaceChunks.length) return [];
|
||||
this.models.copilotContext.matchWorkspaceEmbedding(
|
||||
embedding,
|
||||
workspaceId,
|
||||
topK * 2,
|
||||
threshold
|
||||
),
|
||||
docIds
|
||||
? this.models.copilotContext.matchWorkspaceEmbedding(
|
||||
embedding,
|
||||
workspaceId,
|
||||
topK * 2,
|
||||
scopedThreshold,
|
||||
docIds
|
||||
)
|
||||
: null,
|
||||
]);
|
||||
|
||||
if (
|
||||
!fileChunks.length &&
|
||||
!workspaceChunks.length &&
|
||||
!scopedWorkspaceChunks?.length
|
||||
)
|
||||
return [];
|
||||
|
||||
return await this.embeddingClient.reRank(
|
||||
content,
|
||||
[...fileChunks, ...workspaceChunks],
|
||||
[...fileChunks, ...workspaceChunks, ...(scopedWorkspaceChunks || [])],
|
||||
topK,
|
||||
signal
|
||||
);
|
||||
|
||||
@@ -257,6 +257,7 @@ export class CopilotController implements BeforeApplicationShutdown {
|
||||
...session.config.promptConfig,
|
||||
signal: this.getSignal(req),
|
||||
user: user.id,
|
||||
session: session.config.sessionId,
|
||||
workspace: session.config.workspaceId,
|
||||
reasoning,
|
||||
webSearch,
|
||||
@@ -311,6 +312,7 @@ export class CopilotController implements BeforeApplicationShutdown {
|
||||
...session.config.promptConfig,
|
||||
signal: this.getSignal(req),
|
||||
user: user.id,
|
||||
session: session.config.sessionId,
|
||||
workspace: session.config.workspaceId,
|
||||
reasoning,
|
||||
webSearch,
|
||||
@@ -384,6 +386,7 @@ export class CopilotController implements BeforeApplicationShutdown {
|
||||
...session.config.promptConfig,
|
||||
signal: this.getSignal(req),
|
||||
user: user.id,
|
||||
session: session.config.sessionId,
|
||||
workspace: session.config.workspaceId,
|
||||
reasoning,
|
||||
webSearch,
|
||||
@@ -463,6 +466,7 @@ export class CopilotController implements BeforeApplicationShutdown {
|
||||
...session.config.promptConfig,
|
||||
signal: this.getSignal(req),
|
||||
user: user.id,
|
||||
session: session.config.sessionId,
|
||||
workspace: session.config.workspaceId,
|
||||
})
|
||||
).pipe(
|
||||
@@ -586,6 +590,7 @@ export class CopilotController implements BeforeApplicationShutdown {
|
||||
seed: this.parseNumber(params.seed),
|
||||
signal: this.getSignal(req),
|
||||
user: user.id,
|
||||
session: session.config.sessionId,
|
||||
workspace: session.config.workspaceId,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -1670,6 +1670,11 @@ Your mission is to do your utmost to help users leverage AFFiNE's capabilities f
|
||||
AFFiNE is developed by Toeverything Pte. Ltd., a Singapore-registered company with a diverse international team. The company has also open-sourced BlockSuite and OctoBase to support the creation of tools similar to AFFiNE. The name "AFFiNE" is inspired by the concept of affine transformation, as blocks within AFFiNE can move freely across page, edgeless, and database modes. Currently, the AFFiNE team consists of 25 members and is an engineer-driven open-source company.
|
||||
|
||||
<response_guide>
|
||||
<tool_usage_guide>
|
||||
- When searching for information, prioritize searching the user's Workspace information.
|
||||
- Depending on the complexity of the question and the information returned by the search tools, you can call different tools multiple times to search.
|
||||
</tool_usage_guide>
|
||||
|
||||
<real_world_info>
|
||||
Today is: {{affine::date}}.
|
||||
User's preferred language is {{affine::language}}.
|
||||
|
||||
@@ -141,7 +141,11 @@ export abstract class CopilotProvider<C = any> {
|
||||
const context = this.moduleRef.get(CopilotContextService, {
|
||||
strict: false,
|
||||
});
|
||||
const searchDocs = buildDocSearchGetter(ac, context);
|
||||
|
||||
const docContext = options.session
|
||||
? await context.getBySessionId(options.session)
|
||||
: null;
|
||||
const searchDocs = buildDocSearchGetter(ac, context, docContext);
|
||||
tools.doc_semantic_search = createDocSemanticSearchTool(
|
||||
searchDocs.bind(null, options)
|
||||
);
|
||||
|
||||
@@ -161,6 +161,7 @@ export type StreamObject = z.infer<typeof StreamObjectSchema>;
|
||||
const CopilotProviderOptionsSchema = z.object({
|
||||
signal: z.instanceof(AbortSignal).optional(),
|
||||
user: z.string().optional(),
|
||||
session: z.string().optional(),
|
||||
workspace: z.string().optional(),
|
||||
});
|
||||
|
||||
|
||||
@@ -4,14 +4,20 @@ import { z } from 'zod';
|
||||
import type { AccessController } from '../../../core/permission';
|
||||
import type { ChunkSimilarity } from '../../../models';
|
||||
import type { CopilotContextService } from '../context';
|
||||
import type { ContextSession } from '../context/session';
|
||||
import type { CopilotChatOptions } from '../providers';
|
||||
import { toolError } from './error';
|
||||
|
||||
export const buildDocSearchGetter = (
|
||||
ac: AccessController,
|
||||
context: CopilotContextService
|
||||
context: CopilotContextService,
|
||||
docContext: ContextSession | null
|
||||
) => {
|
||||
const searchDocs = async (options: CopilotChatOptions, query?: string) => {
|
||||
const searchDocs = async (
|
||||
options: CopilotChatOptions,
|
||||
query?: string,
|
||||
abortSignal?: AbortSignal
|
||||
) => {
|
||||
if (!options || !query?.trim() || !options.user || !options.workspace) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -20,7 +26,11 @@ export const buildDocSearchGetter = (
|
||||
.workspace(options.workspace)
|
||||
.can('Workspace.Read');
|
||||
if (!canAccess) return undefined;
|
||||
const chunks = await context.matchWorkspaceAll(options.workspace, query);
|
||||
const [chunks, contextChunks] = await Promise.all([
|
||||
context.matchWorkspaceAll(options.workspace, query, 10, abortSignal),
|
||||
docContext?.matchFiles(query, 10, abortSignal) ?? [],
|
||||
]);
|
||||
|
||||
const docChunks = await ac
|
||||
.user(options.user)
|
||||
.workspace(options.workspace)
|
||||
@@ -29,6 +39,9 @@ export const buildDocSearchGetter = (
|
||||
'Doc.Read'
|
||||
);
|
||||
const fileChunks = chunks.filter(c => 'fileId' in c);
|
||||
if (contextChunks.length) {
|
||||
fileChunks.push(...contextChunks);
|
||||
}
|
||||
if (!docChunks.length && !fileChunks.length) return undefined;
|
||||
return [...fileChunks, ...docChunks];
|
||||
};
|
||||
@@ -36,17 +49,24 @@ export const buildDocSearchGetter = (
|
||||
};
|
||||
|
||||
export const createDocSemanticSearchTool = (
|
||||
searchDocs: (query: string) => Promise<ChunkSimilarity[] | undefined>
|
||||
searchDocs: (
|
||||
query: string,
|
||||
abortSignal?: AbortSignal
|
||||
) => Promise<ChunkSimilarity[] | undefined>
|
||||
) => {
|
||||
return tool({
|
||||
description:
|
||||
'Semantic search for relevant documents in the current workspace',
|
||||
parameters: z.object({
|
||||
query: z.string().describe('The query to search for.'),
|
||||
query: z
|
||||
.string()
|
||||
.describe(
|
||||
'The query statement to search for, e.g. "What is the capital of France?"'
|
||||
),
|
||||
}),
|
||||
execute: async ({ query }) => {
|
||||
execute: async ({ query }, options) => {
|
||||
try {
|
||||
return await searchDocs(query);
|
||||
return await searchDocs(query, options.abortSignal);
|
||||
} catch (e: any) {
|
||||
return toolError('Doc Semantic Search Failed', e.message);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user