mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-26 10:45:57 +08:00
feat(core): support ai network search (#9357)
### What Changed?
- Add `PerplexityProvider` in backend.
- Update session prompt name if user toggle network search mode in chat panel.
- Add experimental flag for AI network search feature.
- Add unit tests and e2e tests.
Search results are streamed and appear word for word:
<div class='graphite__hidden'>
<div>🎥 Video uploaded on Graphite:</div>
<a href="https://app.graphite.dev/media/video/sJGviKxfE3Ap685cl5bj/56f6ec7b-4b21-405f-9612-43e083f6fb84.mov">
<img src="https://app.graphite.dev/api/v1/graphite/video/thumbnail/sJGviKxfE3Ap685cl5bj/56f6ec7b-4b21-405f-9612-43e083f6fb84.mov">
</a>
</div>
<video src="https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/sJGviKxfE3Ap685cl5bj/56f6ec7b-4b21-405f-9612-43e083f6fb84.mov">录屏2024-12-27 18.58.40.mov</video>
Click the little globe icon to manually turn on/off Internet search:
<div class='graphite__hidden'>
<div>🎥 Video uploaded on Graphite:</div>
<a href="https://app.graphite.dev/media/video/sJGviKxfE3Ap685cl5bj/778f1406-bf29-498e-a90d-7dad813392d1.mov">
<img src="https://app.graphite.dev/api/v1/graphite/video/thumbnail/sJGviKxfE3Ap685cl5bj/778f1406-bf29-498e-a90d-7dad813392d1.mov">
</a>
</div>
<video src="https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/sJGviKxfE3Ap685cl5bj/778f1406-bf29-498e-a90d-7dad813392d1.mov">录屏2024-12-27 19.01.16.mov</video>
When there is an image, it will automatically switch to the openai model:
<div class='graphite__hidden'>
<div>🎥 Video uploaded on Graphite:</div>
<a href="https://app.graphite.dev/media/video/sJGviKxfE3Ap685cl5bj/56431d8e-75e1-4d84-ab4a-b6636042cc6a.mov">
<img src="https://app.graphite.dev/api/v1/graphite/video/thumbnail/sJGviKxfE3Ap685cl5bj/56431d8e-75e1-4d84-ab4a-b6636042cc6a.mov">
</a>
</div>
<video src="https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/sJGviKxfE3Ap685cl5bj/56431d8e-75e1-4d84-ab4a-b6636042cc6a.mov">录屏2024-12-27 19.02.13.mov</video>
This commit is contained in:
@@ -10,6 +10,7 @@ import {
|
||||
CopilotQuotaExceeded,
|
||||
CopilotSessionDeleted,
|
||||
CopilotSessionNotFound,
|
||||
PrismaTransaction,
|
||||
} from '../../base';
|
||||
import { FeatureManagementService } from '../../core/features';
|
||||
import { QuotaService } from '../../core/quota';
|
||||
@@ -22,6 +23,7 @@ import {
|
||||
ChatMessageSchema,
|
||||
ChatSessionForkOptions,
|
||||
ChatSessionOptions,
|
||||
ChatSessionPromptUpdateOptions,
|
||||
ChatSessionState,
|
||||
getTokenEncoder,
|
||||
ListHistoriesOptions,
|
||||
@@ -198,6 +200,22 @@ export class ChatSessionService {
|
||||
private readonly prompt: PromptService
|
||||
) {}
|
||||
|
||||
private async haveSession(
|
||||
sessionId: string,
|
||||
userId: string,
|
||||
tx?: PrismaTransaction
|
||||
) {
|
||||
const executor = tx ?? this.db;
|
||||
return await executor.aiSession
|
||||
.count({
|
||||
where: {
|
||||
id: sessionId,
|
||||
userId,
|
||||
},
|
||||
})
|
||||
.then(c => c > 0);
|
||||
}
|
||||
|
||||
private async setSession(state: ChatSessionState): Promise<string> {
|
||||
return await this.db.$transaction(async tx => {
|
||||
let sessionId = state.sessionId;
|
||||
@@ -226,15 +244,7 @@ export class ChatSessionService {
|
||||
if (id) sessionId = id;
|
||||
}
|
||||
|
||||
const haveSession = await tx.aiSession
|
||||
.count({
|
||||
where: {
|
||||
id: sessionId,
|
||||
userId: state.userId,
|
||||
},
|
||||
})
|
||||
.then(c => c > 0);
|
||||
|
||||
const haveSession = await this.haveSession(sessionId, state.userId, tx);
|
||||
if (haveSession) {
|
||||
// message will only exists when setSession call by session.save
|
||||
if (state.messages.length) {
|
||||
@@ -570,6 +580,27 @@ export class ChatSessionService {
|
||||
});
|
||||
}
|
||||
|
||||
async updateSessionPrompt(
|
||||
options: ChatSessionPromptUpdateOptions
|
||||
): Promise<string> {
|
||||
const prompt = await this.prompt.get(options.promptName);
|
||||
if (!prompt) {
|
||||
this.logger.error(`Prompt not found: ${options.promptName}`);
|
||||
throw new CopilotPromptNotFound({ name: options.promptName });
|
||||
}
|
||||
return await this.db.$transaction(async tx => {
|
||||
let sessionId = options.sessionId;
|
||||
const haveSession = await this.haveSession(sessionId, options.userId, tx);
|
||||
if (haveSession) {
|
||||
await tx.aiSession.update({
|
||||
where: { id: sessionId },
|
||||
data: { promptName: prompt.name },
|
||||
});
|
||||
}
|
||||
return sessionId;
|
||||
});
|
||||
}
|
||||
|
||||
async fork(options: ChatSessionForkOptions): Promise<string> {
|
||||
const state = await this.getSession(options.sessionId);
|
||||
if (!state) {
|
||||
|
||||
Reference in New Issue
Block a user