mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-09-08 01:39:22 +08:00
@@ -4,6 +4,118 @@ The actual snapshot is saved in `copilot.spec.ts.snap`.
|
|||||||
|
|
||||||
Generated by [AVA](https://avajs.dev).
|
Generated by [AVA](https://avajs.dev).
|
||||||
|
|
||||||
|
## should be able to manage chat session
|
||||||
|
|
||||||
|
> should generate the final message
|
||||||
|
|
||||||
|
[
|
||||||
|
{
|
||||||
|
content: 'hello world',
|
||||||
|
params: {
|
||||||
|
word: 'world',
|
||||||
|
},
|
||||||
|
role: 'system',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: 'hello',
|
||||||
|
role: 'user',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
> should generate different message with another params
|
||||||
|
|
||||||
|
[
|
||||||
|
{
|
||||||
|
content: 'hello world',
|
||||||
|
params: {
|
||||||
|
word: 'world',
|
||||||
|
},
|
||||||
|
role: 'system',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: 'hello',
|
||||||
|
role: 'user',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
## should be able to fork chat session
|
||||||
|
|
||||||
|
> should generate the final message
|
||||||
|
|
||||||
|
[
|
||||||
|
{
|
||||||
|
content: 'hello world',
|
||||||
|
params: {
|
||||||
|
word: 'world',
|
||||||
|
},
|
||||||
|
role: 'system',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: 'hello',
|
||||||
|
params: {},
|
||||||
|
role: 'user',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: 'world',
|
||||||
|
params: {},
|
||||||
|
role: 'assistant',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
> should generate the final message
|
||||||
|
|
||||||
|
[
|
||||||
|
{
|
||||||
|
content: 'hello world',
|
||||||
|
params: {
|
||||||
|
word: 'world',
|
||||||
|
},
|
||||||
|
role: 'system',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: 'hello',
|
||||||
|
params: {},
|
||||||
|
role: 'user',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: 'world',
|
||||||
|
params: {},
|
||||||
|
role: 'assistant',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
> should generate the final message
|
||||||
|
|
||||||
|
[
|
||||||
|
{
|
||||||
|
content: 'hello world',
|
||||||
|
params: {
|
||||||
|
word: 'world',
|
||||||
|
},
|
||||||
|
role: 'system',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: 'hello',
|
||||||
|
params: {},
|
||||||
|
role: 'user',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: 'world',
|
||||||
|
params: {},
|
||||||
|
role: 'assistant',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: 'aaa',
|
||||||
|
params: {},
|
||||||
|
role: 'user',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: 'bbb',
|
||||||
|
params: {},
|
||||||
|
role: 'assistant',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
## should revert message correctly
|
## should revert message correctly
|
||||||
|
|
||||||
> should have three messages before revert
|
> should have three messages before revert
|
||||||
|
|||||||
Binary file not shown.
@@ -241,35 +241,31 @@ test('should be able to manage chat session', async t => {
|
|||||||
t.is(s.config.promptName, 'prompt', 'should have prompt name');
|
t.is(s.config.promptName, 'prompt', 'should have prompt name');
|
||||||
t.is(s.model, 'model', 'should have model');
|
t.is(s.model, 'model', 'should have model');
|
||||||
|
|
||||||
|
const cleanObject = (obj: any[]) =>
|
||||||
|
JSON.parse(
|
||||||
|
JSON.stringify(obj, (k, v) =>
|
||||||
|
['id', 'attachments', 'createdAt'].includes(k) ||
|
||||||
|
v === null ||
|
||||||
|
(typeof v === 'object' && !Object.keys(v).length)
|
||||||
|
? undefined
|
||||||
|
: v
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
s.push({ role: 'user', content: 'hello', createdAt: new Date() });
|
s.push({ role: 'user', content: 'hello', createdAt: new Date() });
|
||||||
// @ts-expect-error
|
|
||||||
const finalMessages = s.finish(params).map(({ createdAt: _, ...m }) => m);
|
const finalMessages = cleanObject(s.finish(params));
|
||||||
t.deepEqual(
|
t.snapshot(finalMessages, 'should generate the final message');
|
||||||
finalMessages,
|
|
||||||
[
|
|
||||||
{ content: 'hello world', params, role: 'system' },
|
|
||||||
{ content: 'hello', role: 'user' },
|
|
||||||
],
|
|
||||||
'should generate the final message'
|
|
||||||
);
|
|
||||||
await s.save();
|
await s.save();
|
||||||
|
|
||||||
const s1 = (await session.get(sessionId))!;
|
const s1 = (await session.get(sessionId))!;
|
||||||
t.deepEqual(
|
t.deepEqual(
|
||||||
s1
|
cleanObject(s1.finish(params)),
|
||||||
.finish(params)
|
|
||||||
// @ts-expect-error
|
|
||||||
.map(({ id: _, attachments: __, createdAt: ___, ...m }) => m),
|
|
||||||
finalMessages,
|
finalMessages,
|
||||||
'should same as before message'
|
'should same as before message'
|
||||||
);
|
);
|
||||||
t.deepEqual(
|
t.snapshot(
|
||||||
// @ts-expect-error
|
cleanObject(s1.finish(params)),
|
||||||
s1.finish({}).map(({ id: _, attachments: __, createdAt: ___, ...m }) => m),
|
|
||||||
[
|
|
||||||
{ content: 'hello ', params: {}, role: 'system' },
|
|
||||||
{ content: 'hello', role: 'user' },
|
|
||||||
],
|
|
||||||
'should generate different message with another params'
|
'should generate different message with another params'
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -366,22 +362,19 @@ test('should be able to fork chat session', async t => {
|
|||||||
'should fork new session with same params'
|
'should fork new session with same params'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const cleanObject = (obj: any[]) =>
|
||||||
|
JSON.parse(
|
||||||
|
JSON.stringify(obj, (k, v) =>
|
||||||
|
['id', 'createdAt'].includes(k) || v === null ? undefined : v
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
// check forked session messages
|
// check forked session messages
|
||||||
{
|
{
|
||||||
const s2 = (await session.get(forkedSessionId1))!;
|
const s2 = (await session.get(forkedSessionId1))!;
|
||||||
|
|
||||||
const finalMessages = s2
|
const finalMessages = s2.finish(params);
|
||||||
.finish(params) // @ts-expect-error
|
t.snapshot(cleanObject(finalMessages), 'should generate the final message');
|
||||||
.map(({ id: _, attachments: __, createdAt: ___, ...m }) => m);
|
|
||||||
t.deepEqual(
|
|
||||||
finalMessages,
|
|
||||||
[
|
|
||||||
{ role: 'system', content: 'hello world', params },
|
|
||||||
{ role: 'user', content: 'hello' },
|
|
||||||
{ role: 'assistant', content: 'world' },
|
|
||||||
],
|
|
||||||
'should generate the final message'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check second times forked session
|
// check second times forked session
|
||||||
@@ -391,38 +384,16 @@ test('should be able to fork chat session', async t => {
|
|||||||
// should overwrite user id
|
// should overwrite user id
|
||||||
t.is(s2.config.userId, newUser.id, 'should have same user id');
|
t.is(s2.config.userId, newUser.id, 'should have same user id');
|
||||||
|
|
||||||
const finalMessages = s2
|
const finalMessages = s2.finish(params);
|
||||||
.finish(params) // @ts-expect-error
|
t.snapshot(cleanObject(finalMessages), 'should generate the final message');
|
||||||
.map(({ id: _, attachments: __, createdAt: ___, ...m }) => m);
|
|
||||||
t.deepEqual(
|
|
||||||
finalMessages,
|
|
||||||
[
|
|
||||||
{ role: 'system', content: 'hello world', params },
|
|
||||||
{ role: 'user', content: 'hello' },
|
|
||||||
{ role: 'assistant', content: 'world' },
|
|
||||||
],
|
|
||||||
'should generate the final message'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check original session messages
|
// check original session messages
|
||||||
{
|
{
|
||||||
const s3 = (await session.get(sessionId))!;
|
const s3 = (await session.get(sessionId))!;
|
||||||
|
|
||||||
const finalMessages = s3
|
const finalMessages = s3.finish(params);
|
||||||
.finish(params) // @ts-expect-error
|
t.snapshot(cleanObject(finalMessages), 'should generate the final message');
|
||||||
.map(({ id: _, attachments: __, createdAt: ___, ...m }) => m);
|
|
||||||
t.deepEqual(
|
|
||||||
finalMessages,
|
|
||||||
[
|
|
||||||
{ role: 'system', content: 'hello world', params },
|
|
||||||
{ role: 'user', content: 'hello' },
|
|
||||||
{ role: 'assistant', content: 'world' },
|
|
||||||
{ role: 'user', content: 'aaa' },
|
|
||||||
{ role: 'assistant', content: 'bbb' },
|
|
||||||
],
|
|
||||||
'should generate the final message'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// should get main session after fork if re-create a chat session for same docId and workspaceId
|
// should get main session after fork if re-create a chat session for same docId and workspaceId
|
||||||
@@ -612,7 +583,11 @@ test('should revert message correctly', async t => {
|
|||||||
const cleanObject = (obj: any[]) =>
|
const cleanObject = (obj: any[]) =>
|
||||||
JSON.parse(
|
JSON.parse(
|
||||||
JSON.stringify(obj, (k, v) =>
|
JSON.stringify(obj, (k, v) =>
|
||||||
['id', 'createdAt'].includes(k) || v === null ? undefined : v
|
['id', 'createdAt'].includes(k) ||
|
||||||
|
v === null ||
|
||||||
|
(typeof v === 'object' && !Object.keys(v).length)
|
||||||
|
? undefined
|
||||||
|
: v
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ import { CurrentUser, Public } from '../../core/auth';
|
|||||||
import { CopilotProviderService } from './providers';
|
import { CopilotProviderService } from './providers';
|
||||||
import { ChatSession, ChatSessionService } from './session';
|
import { ChatSession, ChatSessionService } from './session';
|
||||||
import { CopilotStorage } from './storage';
|
import { CopilotStorage } from './storage';
|
||||||
import { CopilotCapability, CopilotTextProvider } from './types';
|
import { ChatMessage, CopilotCapability, CopilotTextProvider } from './types';
|
||||||
import { CopilotWorkflowService, GraphExecutorState } from './workflow';
|
import { CopilotWorkflowService, GraphExecutorState } from './workflow';
|
||||||
|
|
||||||
export interface ChatEvent {
|
export interface ChatEvent {
|
||||||
@@ -141,24 +141,28 @@ export class CopilotController implements BeforeApplicationShutdown {
|
|||||||
sessionId: string,
|
sessionId: string,
|
||||||
messageId?: string,
|
messageId?: string,
|
||||||
retry = false
|
retry = false
|
||||||
): Promise<ChatSession> {
|
): Promise<[ChatMessage | undefined, ChatSession]> {
|
||||||
const session = await this.chatSession.get(sessionId);
|
const session = await this.chatSession.get(sessionId);
|
||||||
if (!session) {
|
if (!session) {
|
||||||
throw new CopilotSessionNotFound();
|
throw new CopilotSessionNotFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let latestMessage = undefined;
|
||||||
if (!messageId || retry) {
|
if (!messageId || retry) {
|
||||||
// revert the latest message generated by the assistant
|
// revert the latest message generated by the assistant
|
||||||
// if messageId is provided, we will also revert latest user message
|
// if messageId is provided, we will also revert latest user message
|
||||||
await this.chatSession.revertLatestMessage(sessionId, !!messageId);
|
await this.chatSession.revertLatestMessage(sessionId, !!messageId);
|
||||||
session.revertLatestMessage(!!messageId);
|
session.revertLatestMessage(!!messageId);
|
||||||
|
if (!messageId) {
|
||||||
|
latestMessage = session.latestUserMessage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (messageId) {
|
if (messageId) {
|
||||||
await session.pushByMessageId(messageId);
|
await session.pushByMessageId(messageId);
|
||||||
}
|
}
|
||||||
|
|
||||||
return session;
|
return [latestMessage, session];
|
||||||
}
|
}
|
||||||
|
|
||||||
private prepareParams(params: Record<string, string | string[]>) {
|
private prepareParams(params: Record<string, string | string[]>) {
|
||||||
@@ -226,7 +230,7 @@ export class CopilotController implements BeforeApplicationShutdown {
|
|||||||
messageId
|
messageId
|
||||||
);
|
);
|
||||||
|
|
||||||
const session = await this.appendSessionMessage(
|
const [latestMessage, session] = await this.appendSessionMessage(
|
||||||
sessionId,
|
sessionId,
|
||||||
messageId,
|
messageId,
|
||||||
retry
|
retry
|
||||||
@@ -234,6 +238,14 @@ export class CopilotController implements BeforeApplicationShutdown {
|
|||||||
|
|
||||||
info.model = session.model;
|
info.model = session.model;
|
||||||
metrics.ai.counter('chat_calls').add(1, { model: session.model });
|
metrics.ai.counter('chat_calls').add(1, { model: session.model });
|
||||||
|
|
||||||
|
if (latestMessage) {
|
||||||
|
params = Object.assign({}, params, latestMessage.params, {
|
||||||
|
content: latestMessage.content,
|
||||||
|
attachments: latestMessage.attachments,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const finalMessage = session.finish(params);
|
const finalMessage = session.finish(params);
|
||||||
info.finalMessage = finalMessage;
|
info.finalMessage = finalMessage;
|
||||||
|
|
||||||
@@ -281,14 +293,22 @@ export class CopilotController implements BeforeApplicationShutdown {
|
|||||||
messageId
|
messageId
|
||||||
);
|
);
|
||||||
|
|
||||||
const session = await this.appendSessionMessage(
|
const [latestMessage, session] = await this.appendSessionMessage(
|
||||||
sessionId,
|
sessionId,
|
||||||
messageId,
|
messageId,
|
||||||
retry
|
retry
|
||||||
);
|
);
|
||||||
info.model = session.model;
|
|
||||||
|
|
||||||
|
info.model = session.model;
|
||||||
metrics.ai.counter('chat_stream_calls').add(1, { model: session.model });
|
metrics.ai.counter('chat_stream_calls').add(1, { model: session.model });
|
||||||
|
|
||||||
|
if (latestMessage) {
|
||||||
|
params = Object.assign({}, params, latestMessage.params, {
|
||||||
|
content: latestMessage.content,
|
||||||
|
attachments: latestMessage.attachments,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this.ongoingStreamCount$.next(this.ongoingStreamCount$.value + 1);
|
this.ongoingStreamCount$.next(this.ongoingStreamCount$.value + 1);
|
||||||
const finalMessage = session.finish(params);
|
const finalMessage = session.finish(params);
|
||||||
info.finalMessage = finalMessage;
|
info.finalMessage = finalMessage;
|
||||||
@@ -349,10 +369,11 @@ export class CopilotController implements BeforeApplicationShutdown {
|
|||||||
try {
|
try {
|
||||||
const { messageId } = this.prepareParams(params);
|
const { messageId } = this.prepareParams(params);
|
||||||
|
|
||||||
const session = await this.appendSessionMessage(sessionId, messageId);
|
const [, session] = await this.appendSessionMessage(sessionId, messageId);
|
||||||
info.model = session.model;
|
info.model = session.model;
|
||||||
|
|
||||||
metrics.ai.counter('workflow_calls').add(1, { model: session.model });
|
metrics.ai.counter('workflow_calls').add(1, { model: session.model });
|
||||||
|
|
||||||
const latestMessage = session.stashMessages.findLast(
|
const latestMessage = session.stashMessages.findLast(
|
||||||
m => m.role === 'user'
|
m => m.role === 'user'
|
||||||
);
|
);
|
||||||
@@ -463,12 +484,22 @@ export class CopilotController implements BeforeApplicationShutdown {
|
|||||||
throw new NoCopilotProviderAvailable();
|
throw new NoCopilotProviderAvailable();
|
||||||
}
|
}
|
||||||
|
|
||||||
const session = await this.appendSessionMessage(sessionId, messageId);
|
const [latestMessage, session] = await this.appendSessionMessage(
|
||||||
|
sessionId,
|
||||||
|
messageId
|
||||||
|
);
|
||||||
info.model = session.model;
|
info.model = session.model;
|
||||||
|
|
||||||
metrics.ai
|
metrics.ai
|
||||||
.counter('images_stream_calls')
|
.counter('images_stream_calls')
|
||||||
.add(1, { model: session.model });
|
.add(1, { model: session.model });
|
||||||
|
|
||||||
|
if (latestMessage) {
|
||||||
|
params = Object.assign({}, params, latestMessage.params, {
|
||||||
|
content: latestMessage.content,
|
||||||
|
attachments: latestMessage.attachments,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const handleRemoteLink = this.storage.handleRemoteLink.bind(
|
const handleRemoteLink = this.storage.handleRemoteLink.bind(
|
||||||
this.storage,
|
this.storage,
|
||||||
user.id,
|
user.id,
|
||||||
|
|||||||
@@ -63,6 +63,10 @@ export class ChatSession implements AsyncDisposable {
|
|||||||
return this.state.messages.slice(-this.stashMessageCount);
|
return this.state.messages.slice(-this.stashMessageCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get latestUserMessage() {
|
||||||
|
return this.state.messages.findLast(m => m.role === 'user');
|
||||||
|
}
|
||||||
|
|
||||||
push(message: ChatMessage) {
|
push(message: ChatMessage) {
|
||||||
if (
|
if (
|
||||||
this.state.prompt.action &&
|
this.state.prompt.action &&
|
||||||
@@ -313,6 +317,7 @@ export class ChatSessionService {
|
|||||||
role: true,
|
role: true,
|
||||||
content: true,
|
content: true,
|
||||||
attachments: true,
|
attachments: true,
|
||||||
|
params: true,
|
||||||
createdAt: true,
|
createdAt: true,
|
||||||
},
|
},
|
||||||
orderBy: { createdAt: 'asc' },
|
orderBy: { createdAt: 'asc' },
|
||||||
|
|||||||
Reference in New Issue
Block a user