mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-16 13:57:02 +08:00
feat(server): add typed list session gql (#12979)
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Introduced new API endpoints and GraphQL queries to retrieve Copilot chat sessions by workspace, document, and pinned status, with detailed session and message information. * Added support for filtering and querying Copilot chat histories with new options such as pinned status and message ordering. * **Bug Fixes** * Improved filtering logic for listing and retrieving chat sessions, ensuring accurate results for workspace, document, and pinned session queries. * **Tests** * Expanded and refactored test coverage for session listing, filtering, and new query options to ensure reliability and correctness of Copilot session retrieval. * Updated snapshot data to reflect new session types and filtering capabilities. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -16,6 +16,7 @@ Generated by [AVA](https://avajs.dev).
|
|||||||
role: 'assistant',
|
role: 'assistant',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
pinned: false,
|
||||||
tokens: 8,
|
tokens: 8,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
@@ -30,6 +31,7 @@ Generated by [AVA](https://avajs.dev).
|
|||||||
role: 'assistant',
|
role: 'assistant',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
pinned: false,
|
||||||
tokens: 8,
|
tokens: 8,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|||||||
Binary file not shown.
@@ -53,7 +53,10 @@ import {
|
|||||||
createWorkspaceCopilotSession,
|
createWorkspaceCopilotSession,
|
||||||
forkCopilotSession,
|
forkCopilotSession,
|
||||||
getCopilotSession,
|
getCopilotSession,
|
||||||
|
getDocSessions,
|
||||||
getHistories,
|
getHistories,
|
||||||
|
getPinnedSessions,
|
||||||
|
getWorkspaceSessions,
|
||||||
listContext,
|
listContext,
|
||||||
listContextDocAndFiles,
|
listContextDocAndFiles,
|
||||||
matchFiles,
|
matchFiles,
|
||||||
@@ -1140,31 +1143,94 @@ test('should list histories for different session types correctly', async t => {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
const testHistoryQuery = async (
|
const testHistoryQuery = async (
|
||||||
queryDocId: string | undefined,
|
queryFn: () => Promise<any[]>,
|
||||||
expectedSessionId: string,
|
opts: {
|
||||||
|
sessionIds?: string[];
|
||||||
|
sessionId?: string;
|
||||||
|
pinned?: boolean;
|
||||||
|
isEmpty?: boolean;
|
||||||
|
},
|
||||||
description: string
|
description: string
|
||||||
) => {
|
) => {
|
||||||
const histories = await getHistories(app, {
|
const s = await queryFn();
|
||||||
workspaceId,
|
|
||||||
docId: queryDocId,
|
if (opts.isEmpty) {
|
||||||
});
|
t.is(s.length, 0, `should return ${description}`);
|
||||||
t.is(histories.length, 1, `should return ${description}`);
|
return;
|
||||||
t.is(
|
}
|
||||||
histories[0].sessionId,
|
|
||||||
expectedSessionId,
|
if (opts.sessionIds) {
|
||||||
`should return correct ${description}`
|
t.is(s.length, opts.sessionIds.length, `should return ${description}`);
|
||||||
);
|
const ids = s.map(h => h.sessionId).sort((a, b) => a.localeCompare(b));
|
||||||
|
const expectedIds = opts.sessionIds.sort((a, b) => a.localeCompare(b));
|
||||||
|
t.deepEqual(ids, expectedIds, `should return correct ${description}`);
|
||||||
|
} else if (opts.sessionId) {
|
||||||
|
t.is(s.length, 1, `should return ${description}`);
|
||||||
|
t.is(
|
||||||
|
s[0].sessionId,
|
||||||
|
opts.sessionId,
|
||||||
|
`should return correct ${description}`
|
||||||
|
);
|
||||||
|
if (opts.pinned !== undefined) {
|
||||||
|
t.is(s[0].pinned, opts.pinned, `pinned status for ${description}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// test for getHistories
|
||||||
await testHistoryQuery(
|
await testHistoryQuery(
|
||||||
undefined,
|
() => getHistories(app, { workspaceId, docId: null }),
|
||||||
workspaceSessionId,
|
{ sessionId: workspaceSessionId },
|
||||||
'workspace session history'
|
'workspace session history'
|
||||||
);
|
);
|
||||||
await testHistoryQuery(
|
await testHistoryQuery(
|
||||||
pinnedDocId,
|
() => getHistories(app, { workspaceId, docId: pinnedDocId }),
|
||||||
pinnedSessionId,
|
{ sessionId: pinnedSessionId },
|
||||||
'pinned session history'
|
'pinned session history'
|
||||||
);
|
);
|
||||||
await testHistoryQuery(docId, docSessionId, 'doc session history');
|
await testHistoryQuery(
|
||||||
|
() => getHistories(app, { workspaceId, docId }),
|
||||||
|
{ sessionId: docSessionId },
|
||||||
|
'doc session history'
|
||||||
|
);
|
||||||
|
|
||||||
|
// test for getWorkspaceSessions
|
||||||
|
await testHistoryQuery(
|
||||||
|
() => getWorkspaceSessions(app, { workspaceId }),
|
||||||
|
{ sessionId: workspaceSessionId, pinned: false },
|
||||||
|
'workspace-level sessions'
|
||||||
|
);
|
||||||
|
|
||||||
|
// test for getDocSessions
|
||||||
|
await testHistoryQuery(
|
||||||
|
() =>
|
||||||
|
getDocSessions(app, { workspaceId, docId, options: { pinned: false } }),
|
||||||
|
{ sessionId: docSessionId, pinned: false },
|
||||||
|
'doc sessions'
|
||||||
|
);
|
||||||
|
|
||||||
|
await testHistoryQuery(
|
||||||
|
() => getDocSessions(app, { workspaceId, docId: pinnedDocId }),
|
||||||
|
{ sessionId: pinnedSessionId, pinned: true },
|
||||||
|
'pinned doc sessions'
|
||||||
|
);
|
||||||
|
|
||||||
|
// test for getPinnedSessions
|
||||||
|
await testHistoryQuery(
|
||||||
|
() => getPinnedSessions(app, { workspaceId }),
|
||||||
|
{ sessionId: pinnedSessionId, pinned: true },
|
||||||
|
'pinned sessions'
|
||||||
|
);
|
||||||
|
|
||||||
|
await testHistoryQuery(
|
||||||
|
() => getPinnedSessions(app, { workspaceId, docId: pinnedDocId }),
|
||||||
|
{ sessionId: pinnedSessionId, pinned: true },
|
||||||
|
'pinned session for specific doc'
|
||||||
|
);
|
||||||
|
|
||||||
|
await testHistoryQuery(
|
||||||
|
() => getPinnedSessions(app, { workspaceId, docId }),
|
||||||
|
{ isEmpty: true },
|
||||||
|
'no pinned sessions for non-pinned doc'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -262,16 +262,53 @@ Generated by [AVA](https://avajs.dev).
|
|||||||
|
|
||||||
{
|
{
|
||||||
all_workspace_sessions: {
|
all_workspace_sessions: {
|
||||||
count: 2,
|
count: 7,
|
||||||
sessionTypes: [
|
sessionTypes: [
|
||||||
{
|
{
|
||||||
hasMessages: false,
|
hasMessages: false,
|
||||||
|
isAction: false,
|
||||||
|
isFork: true,
|
||||||
|
messageCount: 0,
|
||||||
|
type: 'doc',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hasMessages: false,
|
||||||
|
isAction: false,
|
||||||
|
isFork: false,
|
||||||
|
messageCount: 0,
|
||||||
|
type: 'doc',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hasMessages: false,
|
||||||
|
isAction: false,
|
||||||
|
isFork: false,
|
||||||
|
messageCount: 0,
|
||||||
|
type: 'doc',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hasMessages: false,
|
||||||
|
isAction: true,
|
||||||
|
isFork: false,
|
||||||
|
messageCount: 0,
|
||||||
|
type: 'doc',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hasMessages: false,
|
||||||
|
isAction: false,
|
||||||
|
isFork: false,
|
||||||
|
messageCount: 0,
|
||||||
|
type: 'doc',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hasMessages: false,
|
||||||
|
isAction: false,
|
||||||
isFork: false,
|
isFork: false,
|
||||||
messageCount: 0,
|
messageCount: 0,
|
||||||
type: 'pinned',
|
type: 'pinned',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
hasMessages: false,
|
hasMessages: false,
|
||||||
|
isAction: false,
|
||||||
isFork: false,
|
isFork: false,
|
||||||
messageCount: 0,
|
messageCount: 0,
|
||||||
type: 'workspace',
|
type: 'workspace',
|
||||||
@@ -283,30 +320,35 @@ Generated by [AVA](https://avajs.dev).
|
|||||||
sessionTypes: [
|
sessionTypes: [
|
||||||
{
|
{
|
||||||
hasMessages: false,
|
hasMessages: false,
|
||||||
|
isAction: false,
|
||||||
isFork: true,
|
isFork: true,
|
||||||
messageCount: 0,
|
messageCount: 0,
|
||||||
type: 'doc',
|
type: 'doc',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
hasMessages: true,
|
hasMessages: true,
|
||||||
|
isAction: false,
|
||||||
isFork: false,
|
isFork: false,
|
||||||
messageCount: 1,
|
messageCount: 1,
|
||||||
type: 'doc',
|
type: 'doc',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
hasMessages: true,
|
hasMessages: true,
|
||||||
|
isAction: false,
|
||||||
isFork: false,
|
isFork: false,
|
||||||
messageCount: 1,
|
messageCount: 1,
|
||||||
type: 'doc',
|
type: 'doc',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
hasMessages: false,
|
hasMessages: false,
|
||||||
|
isAction: true,
|
||||||
isFork: false,
|
isFork: false,
|
||||||
messageCount: 0,
|
messageCount: 0,
|
||||||
type: 'doc',
|
type: 'doc',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
hasMessages: true,
|
hasMessages: true,
|
||||||
|
isAction: false,
|
||||||
isFork: false,
|
isFork: false,
|
||||||
messageCount: 1,
|
messageCount: 1,
|
||||||
type: 'doc',
|
type: 'doc',
|
||||||
@@ -318,6 +360,7 @@ Generated by [AVA](https://avajs.dev).
|
|||||||
sessionTypes: [
|
sessionTypes: [
|
||||||
{
|
{
|
||||||
hasMessages: false,
|
hasMessages: false,
|
||||||
|
isAction: false,
|
||||||
isFork: false,
|
isFork: false,
|
||||||
messageCount: 0,
|
messageCount: 0,
|
||||||
type: 'doc',
|
type: 'doc',
|
||||||
@@ -325,28 +368,39 @@ Generated by [AVA](https://avajs.dev).
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
non_action_sessions: {
|
non_action_sessions: {
|
||||||
count: 4,
|
count: 5,
|
||||||
sessionTypes: [
|
sessionTypes: [
|
||||||
{
|
{
|
||||||
hasMessages: false,
|
hasMessages: false,
|
||||||
|
isAction: false,
|
||||||
isFork: true,
|
isFork: true,
|
||||||
messageCount: 0,
|
messageCount: 0,
|
||||||
type: 'doc',
|
type: 'doc',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
hasMessages: false,
|
hasMessages: false,
|
||||||
|
isAction: false,
|
||||||
isFork: false,
|
isFork: false,
|
||||||
messageCount: 0,
|
messageCount: 0,
|
||||||
type: 'doc',
|
type: 'doc',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
hasMessages: false,
|
hasMessages: false,
|
||||||
|
isAction: false,
|
||||||
isFork: false,
|
isFork: false,
|
||||||
messageCount: 0,
|
messageCount: 0,
|
||||||
type: 'doc',
|
type: 'doc',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
hasMessages: false,
|
hasMessages: false,
|
||||||
|
isAction: true,
|
||||||
|
isFork: false,
|
||||||
|
messageCount: 0,
|
||||||
|
type: 'doc',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hasMessages: false,
|
||||||
|
isAction: false,
|
||||||
isFork: false,
|
isFork: false,
|
||||||
messageCount: 0,
|
messageCount: 0,
|
||||||
type: 'doc',
|
type: 'doc',
|
||||||
@@ -354,28 +408,25 @@ Generated by [AVA](https://avajs.dev).
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
non_fork_sessions: {
|
non_fork_sessions: {
|
||||||
count: 4,
|
count: 3,
|
||||||
sessionTypes: [
|
sessionTypes: [
|
||||||
{
|
{
|
||||||
hasMessages: false,
|
hasMessages: false,
|
||||||
|
isAction: false,
|
||||||
isFork: false,
|
isFork: false,
|
||||||
messageCount: 0,
|
messageCount: 0,
|
||||||
type: 'doc',
|
type: 'doc',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
hasMessages: false,
|
hasMessages: false,
|
||||||
|
isAction: false,
|
||||||
isFork: false,
|
isFork: false,
|
||||||
messageCount: 0,
|
messageCount: 0,
|
||||||
type: 'doc',
|
type: 'doc',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
hasMessages: false,
|
hasMessages: false,
|
||||||
isFork: false,
|
isAction: false,
|
||||||
messageCount: 0,
|
|
||||||
type: 'doc',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
hasMessages: false,
|
|
||||||
isFork: false,
|
isFork: false,
|
||||||
messageCount: 0,
|
messageCount: 0,
|
||||||
type: 'doc',
|
type: 'doc',
|
||||||
@@ -383,16 +434,44 @@ Generated by [AVA](https://avajs.dev).
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
recent_top3_sessions: {
|
recent_top3_sessions: {
|
||||||
|
count: 3,
|
||||||
|
sessionTypes: [
|
||||||
|
{
|
||||||
|
hasMessages: false,
|
||||||
|
isAction: false,
|
||||||
|
isFork: true,
|
||||||
|
messageCount: 0,
|
||||||
|
type: 'doc',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hasMessages: false,
|
||||||
|
isAction: false,
|
||||||
|
isFork: false,
|
||||||
|
messageCount: 0,
|
||||||
|
type: 'doc',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hasMessages: false,
|
||||||
|
isAction: false,
|
||||||
|
isFork: false,
|
||||||
|
messageCount: 0,
|
||||||
|
type: 'doc',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
workspace_sessions_with_messages: {
|
||||||
count: 2,
|
count: 2,
|
||||||
sessionTypes: [
|
sessionTypes: [
|
||||||
{
|
{
|
||||||
hasMessages: false,
|
hasMessages: false,
|
||||||
|
isAction: false,
|
||||||
isFork: false,
|
isFork: false,
|
||||||
messageCount: 0,
|
messageCount: 0,
|
||||||
type: 'pinned',
|
type: 'pinned',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
hasMessages: false,
|
hasMessages: false,
|
||||||
|
isAction: false,
|
||||||
isFork: false,
|
isFork: false,
|
||||||
messageCount: 0,
|
messageCount: 0,
|
||||||
type: 'workspace',
|
type: 'workspace',
|
||||||
@@ -486,102 +565,3 @@ Generated by [AVA](https://avajs.dev).
|
|||||||
workspaceSessionExists: true,
|
workspaceSessionExists: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
## should handle session updates and validations
|
|
||||||
|
|
||||||
> should unpin existing when pinning new session
|
|
||||||
|
|
||||||
[
|
|
||||||
{
|
|
||||||
docId: null,
|
|
||||||
id: 'session-update-id',
|
|
||||||
pinned: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
docId: null,
|
|
||||||
id: 'existing-pinned-session-id',
|
|
||||||
pinned: false,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
> session type conversion steps
|
|
||||||
|
|
||||||
[
|
|
||||||
{
|
|
||||||
session: {
|
|
||||||
docId: 'doc-update-id',
|
|
||||||
pinned: false,
|
|
||||||
},
|
|
||||||
step: 'workspace_to_doc',
|
|
||||||
type: 'doc',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
session: {
|
|
||||||
docId: null,
|
|
||||||
pinned: false,
|
|
||||||
},
|
|
||||||
step: 'doc_to_workspace',
|
|
||||||
type: 'workspace',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
session: {
|
|
||||||
docId: null,
|
|
||||||
pinned: true,
|
|
||||||
},
|
|
||||||
step: 'workspace_to_pinned',
|
|
||||||
type: 'pinned',
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
## should create multiple doc sessions and query latest
|
|
||||||
|
|
||||||
> multiple doc sessions for same document with order verification
|
|
||||||
|
|
||||||
[
|
|
||||||
{
|
|
||||||
docId: 'multi-session-doc',
|
|
||||||
hasMessages: true,
|
|
||||||
isFirstSession: false,
|
|
||||||
isSecondSession: false,
|
|
||||||
isThirdSession: true,
|
|
||||||
messageCount: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
docId: 'multi-session-doc',
|
|
||||||
hasMessages: true,
|
|
||||||
isFirstSession: false,
|
|
||||||
isSecondSession: true,
|
|
||||||
isThirdSession: false,
|
|
||||||
messageCount: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
docId: 'multi-session-doc',
|
|
||||||
hasMessages: true,
|
|
||||||
isFirstSession: true,
|
|
||||||
isSecondSession: false,
|
|
||||||
isThirdSession: false,
|
|
||||||
messageCount: 1,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
## should query recent topK sessions of different types
|
|
||||||
|
|
||||||
> should include different session types in recent topK query
|
|
||||||
|
|
||||||
[
|
|
||||||
{
|
|
||||||
docId: null,
|
|
||||||
pinned: false,
|
|
||||||
type: 'workspace',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
docId: null,
|
|
||||||
pinned: true,
|
|
||||||
type: 'pinned',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
docId: null,
|
|
||||||
pinned: false,
|
|
||||||
type: 'workspace',
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|||||||
Binary file not shown.
@@ -169,6 +169,7 @@ test('should list and filter session type', async t => {
|
|||||||
const workspaceSessions = await copilotSession.list({
|
const workspaceSessions = await copilotSession.list({
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
workspaceId: workspace.id,
|
workspaceId: workspace.id,
|
||||||
|
docId: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
t.snapshot(
|
t.snapshot(
|
||||||
@@ -575,6 +576,10 @@ test('should handle session queries, ordering, and filtering', async t => {
|
|||||||
const docParams = { ...baseParams, docId };
|
const docParams = { ...baseParams, docId };
|
||||||
const queryTestCases = [
|
const queryTestCases = [
|
||||||
{ name: 'all_workspace_sessions', params: baseParams },
|
{ name: 'all_workspace_sessions', params: baseParams },
|
||||||
|
{
|
||||||
|
name: 'workspace_sessions_with_messages',
|
||||||
|
params: { ...baseParams, docId: null, withMessages: true },
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'doc_sessions_with_messages',
|
name: 'doc_sessions_with_messages',
|
||||||
params: { ...docParams, withMessages: true },
|
params: { ...docParams, withMessages: true },
|
||||||
@@ -609,6 +614,7 @@ test('should handle session queries, ordering, and filtering', async t => {
|
|||||||
type: copilotSession.getSessionType(s),
|
type: copilotSession.getSessionType(s),
|
||||||
hasMessages: !!s.messages?.length,
|
hasMessages: !!s.messages?.length,
|
||||||
messageCount: s.messages?.length || 0,
|
messageCount: s.messages?.length || 0,
|
||||||
|
isAction: s.promptName === TEST_PROMPTS.ACTION,
|
||||||
isFork: !!s.parentSessionId,
|
isFork: !!s.parentSessionId,
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -709,26 +709,30 @@ type ChatMessage = {
|
|||||||
|
|
||||||
type History = {
|
type History = {
|
||||||
sessionId: string;
|
sessionId: string;
|
||||||
|
pinned: boolean;
|
||||||
tokens: number;
|
tokens: number;
|
||||||
action: string | null;
|
action: string | null;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
messages: ChatMessage[];
|
messages: ChatMessage[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type HistoryOptions = {
|
||||||
|
action?: boolean;
|
||||||
|
fork?: boolean;
|
||||||
|
pinned?: boolean;
|
||||||
|
limit?: number;
|
||||||
|
skip?: number;
|
||||||
|
sessionOrder?: 'asc' | 'desc';
|
||||||
|
messageOrder?: 'asc' | 'desc';
|
||||||
|
sessionId?: string;
|
||||||
|
};
|
||||||
|
|
||||||
export async function getHistories(
|
export async function getHistories(
|
||||||
app: TestingApp,
|
app: TestingApp,
|
||||||
variables: {
|
variables: {
|
||||||
workspaceId: string;
|
workspaceId: string;
|
||||||
docId?: string;
|
docId?: string | null;
|
||||||
options?: {
|
options?: HistoryOptions;
|
||||||
action?: boolean;
|
|
||||||
fork?: boolean;
|
|
||||||
limit?: number;
|
|
||||||
skip?: number;
|
|
||||||
sessionOrder?: 'asc' | 'desc';
|
|
||||||
messageOrder?: 'asc' | 'desc';
|
|
||||||
sessionId?: string;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
): Promise<History[]> {
|
): Promise<History[]> {
|
||||||
const res = await app.gql(
|
const res = await app.gql(
|
||||||
@@ -742,6 +746,7 @@ export async function getHistories(
|
|||||||
copilot(workspaceId: $workspaceId) {
|
copilot(workspaceId: $workspaceId) {
|
||||||
histories(docId: $docId, options: $options) {
|
histories(docId: $docId, options: $options) {
|
||||||
sessionId
|
sessionId
|
||||||
|
pinned
|
||||||
tokens
|
tokens
|
||||||
action
|
action
|
||||||
createdAt
|
createdAt
|
||||||
@@ -763,6 +768,152 @@ export async function getHistories(
|
|||||||
return res.currentUser?.copilot?.histories || [];
|
return res.currentUser?.copilot?.histories || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getWorkspaceSessions(
|
||||||
|
app: TestingApp,
|
||||||
|
variables: {
|
||||||
|
workspaceId: string;
|
||||||
|
options?: HistoryOptions;
|
||||||
|
}
|
||||||
|
): Promise<History[]> {
|
||||||
|
const res = await app.gql(
|
||||||
|
`query getCopilotWorkspaceSessions(
|
||||||
|
$workspaceId: String!
|
||||||
|
$options: QueryChatHistoriesInput
|
||||||
|
) {
|
||||||
|
currentUser {
|
||||||
|
copilot(workspaceId: $workspaceId) {
|
||||||
|
histories(docId: null, options: $options) {
|
||||||
|
sessionId
|
||||||
|
pinned
|
||||||
|
tokens
|
||||||
|
action
|
||||||
|
createdAt
|
||||||
|
messages {
|
||||||
|
id
|
||||||
|
role
|
||||||
|
content
|
||||||
|
streamObjects {
|
||||||
|
type
|
||||||
|
textDelta
|
||||||
|
toolCallId
|
||||||
|
toolName
|
||||||
|
args
|
||||||
|
result
|
||||||
|
}
|
||||||
|
attachments
|
||||||
|
createdAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
variables
|
||||||
|
);
|
||||||
|
|
||||||
|
return res.currentUser?.copilot?.histories || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getDocSessions(
|
||||||
|
app: TestingApp,
|
||||||
|
variables: {
|
||||||
|
workspaceId: string;
|
||||||
|
docId: string;
|
||||||
|
options?: HistoryOptions;
|
||||||
|
}
|
||||||
|
): Promise<History[]> {
|
||||||
|
const res = await app.gql(
|
||||||
|
`query getCopilotDocSessions(
|
||||||
|
$workspaceId: String!
|
||||||
|
$docId: String!
|
||||||
|
$options: QueryChatHistoriesInput
|
||||||
|
) {
|
||||||
|
currentUser {
|
||||||
|
copilot(workspaceId: $workspaceId) {
|
||||||
|
histories(docId: $docId, options: $options) {
|
||||||
|
sessionId
|
||||||
|
pinned
|
||||||
|
tokens
|
||||||
|
action
|
||||||
|
createdAt
|
||||||
|
messages {
|
||||||
|
id
|
||||||
|
role
|
||||||
|
content
|
||||||
|
streamObjects {
|
||||||
|
type
|
||||||
|
textDelta
|
||||||
|
toolCallId
|
||||||
|
toolName
|
||||||
|
args
|
||||||
|
result
|
||||||
|
}
|
||||||
|
attachments
|
||||||
|
createdAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
variables
|
||||||
|
);
|
||||||
|
|
||||||
|
return res.currentUser?.copilot?.histories || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getPinnedSessions(
|
||||||
|
app: TestingApp,
|
||||||
|
variables: {
|
||||||
|
workspaceId: string;
|
||||||
|
docId?: string;
|
||||||
|
messageOrder?: 'asc' | 'desc';
|
||||||
|
withPrompt?: boolean;
|
||||||
|
}
|
||||||
|
): Promise<History[]> {
|
||||||
|
const res = await app.gql(
|
||||||
|
`query getCopilotPinnedSessions(
|
||||||
|
$workspaceId: String!
|
||||||
|
$docId: String
|
||||||
|
$messageOrder: ChatHistoryOrder
|
||||||
|
$withPrompt: Boolean
|
||||||
|
) {
|
||||||
|
currentUser {
|
||||||
|
copilot(workspaceId: $workspaceId) {
|
||||||
|
histories(docId: $docId, options: {
|
||||||
|
limit: 1,
|
||||||
|
pinned: true,
|
||||||
|
messageOrder: $messageOrder,
|
||||||
|
withPrompt: $withPrompt
|
||||||
|
}) {
|
||||||
|
sessionId
|
||||||
|
pinned
|
||||||
|
tokens
|
||||||
|
action
|
||||||
|
createdAt
|
||||||
|
messages {
|
||||||
|
id
|
||||||
|
role
|
||||||
|
content
|
||||||
|
streamObjects {
|
||||||
|
type
|
||||||
|
textDelta
|
||||||
|
toolCallId
|
||||||
|
toolName
|
||||||
|
args
|
||||||
|
result
|
||||||
|
}
|
||||||
|
attachments
|
||||||
|
createdAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
variables
|
||||||
|
);
|
||||||
|
|
||||||
|
return res.currentUser?.copilot?.histories || [];
|
||||||
|
}
|
||||||
|
|
||||||
type Prompt = {
|
type Prompt = {
|
||||||
name: string;
|
name: string;
|
||||||
model: string;
|
model: string;
|
||||||
|
|||||||
@@ -285,38 +285,44 @@ export class CopilotSessionModel extends BaseModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async list(options: ListSessionOptions) {
|
async list(options: ListSessionOptions) {
|
||||||
const { userId, sessionId, workspaceId, docId } = options;
|
const { userId, sessionId, workspaceId, docId, action, fork } = options;
|
||||||
|
|
||||||
|
function getNullCond<T>(
|
||||||
|
maybeBool: boolean | undefined,
|
||||||
|
wrap: (ret: { not: null } | null) => T = ret => ret as T
|
||||||
|
): T | undefined {
|
||||||
|
return maybeBool === true
|
||||||
|
? wrap({ not: null })
|
||||||
|
: maybeBool === false
|
||||||
|
? wrap(null)
|
||||||
|
: undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getEqCond<T>(maybeValue: T | undefined): T | undefined {
|
||||||
|
return maybeValue !== undefined ? maybeValue : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
const conditions: Prisma.AiSessionWhereInput['OR'] = [
|
const conditions: Prisma.AiSessionWhereInput['OR'] = [
|
||||||
{
|
{
|
||||||
userId,
|
userId,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
docId: docId ?? null,
|
docId: getEqCond(docId),
|
||||||
id: sessionId ? { equals: sessionId } : undefined,
|
id: getEqCond(sessionId),
|
||||||
deletedAt: null,
|
deletedAt: null,
|
||||||
prompt:
|
pinned: getEqCond(options.pinned),
|
||||||
typeof options.action === 'boolean'
|
prompt: getNullCond(fork, ret => ({ action: ret })),
|
||||||
? options.action
|
parentSessionId: getNullCond(fork),
|
||||||
? { action: { not: null } }
|
|
||||||
: { action: null }
|
|
||||||
: undefined,
|
|
||||||
parentSessionId:
|
|
||||||
typeof options.fork === 'boolean'
|
|
||||||
? options.fork
|
|
||||||
? { not: null }
|
|
||||||
: null
|
|
||||||
: undefined,
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
if (!options?.action && options?.fork) {
|
if (!action && fork) {
|
||||||
// query forked sessions from other users
|
// query forked sessions from other users
|
||||||
// only query forked session if fork == true and action == false
|
// only query forked session if fork == true and action == false
|
||||||
conditions.push({
|
conditions.push({
|
||||||
userId: { not: userId },
|
userId: { not: userId },
|
||||||
workspaceId: workspaceId,
|
workspaceId: workspaceId,
|
||||||
docId: docId ?? null,
|
docId: docId ?? null,
|
||||||
id: sessionId ? { equals: sessionId } : undefined,
|
id: getEqCond(sessionId),
|
||||||
prompt: { action: null },
|
prompt: { action: null },
|
||||||
// should only find forked session
|
// should only find forked session
|
||||||
parentSessionId: { not: null },
|
parentSessionId: { not: null },
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
query getCopilotDocSessions(
|
||||||
|
$workspaceId: String!
|
||||||
|
$docId: String!
|
||||||
|
$options: QueryChatHistoriesInput
|
||||||
|
) {
|
||||||
|
currentUser {
|
||||||
|
copilot(workspaceId: $workspaceId) {
|
||||||
|
histories(docId: $docId, options: $options) {
|
||||||
|
sessionId
|
||||||
|
pinned
|
||||||
|
tokens
|
||||||
|
action
|
||||||
|
createdAt
|
||||||
|
messages {
|
||||||
|
id
|
||||||
|
role
|
||||||
|
content
|
||||||
|
streamObjects {
|
||||||
|
type
|
||||||
|
textDelta
|
||||||
|
toolCallId
|
||||||
|
toolName
|
||||||
|
args
|
||||||
|
result
|
||||||
|
}
|
||||||
|
attachments
|
||||||
|
createdAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
query getCopilotPinnedSessions(
|
||||||
|
$workspaceId: String!
|
||||||
|
$docId: String
|
||||||
|
$messageOrder: ChatHistoryOrder
|
||||||
|
$withPrompt: Boolean
|
||||||
|
) {
|
||||||
|
currentUser {
|
||||||
|
copilot(workspaceId: $workspaceId) {
|
||||||
|
histories(docId: $docId, options: {
|
||||||
|
limit: 1,
|
||||||
|
pinned: true,
|
||||||
|
messageOrder: $messageOrder,
|
||||||
|
withPrompt: $withPrompt
|
||||||
|
}) {
|
||||||
|
sessionId
|
||||||
|
pinned
|
||||||
|
tokens
|
||||||
|
action
|
||||||
|
createdAt
|
||||||
|
messages {
|
||||||
|
id
|
||||||
|
role
|
||||||
|
content
|
||||||
|
streamObjects {
|
||||||
|
type
|
||||||
|
textDelta
|
||||||
|
toolCallId
|
||||||
|
toolName
|
||||||
|
args
|
||||||
|
result
|
||||||
|
}
|
||||||
|
attachments
|
||||||
|
createdAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
query getCopilotWorkspaceSessions(
|
||||||
|
$workspaceId: String!
|
||||||
|
$options: QueryChatHistoriesInput
|
||||||
|
) {
|
||||||
|
currentUser {
|
||||||
|
copilot(workspaceId: $workspaceId) {
|
||||||
|
histories(docId: null, options: $options) {
|
||||||
|
sessionId
|
||||||
|
pinned
|
||||||
|
tokens
|
||||||
|
action
|
||||||
|
createdAt
|
||||||
|
messages {
|
||||||
|
id
|
||||||
|
role
|
||||||
|
content
|
||||||
|
streamObjects {
|
||||||
|
type
|
||||||
|
textDelta
|
||||||
|
toolCallId
|
||||||
|
toolName
|
||||||
|
args
|
||||||
|
result
|
||||||
|
}
|
||||||
|
attachments
|
||||||
|
createdAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -604,6 +604,108 @@ export const getCopilotHistoryIdsQuery = {
|
|||||||
}`,
|
}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getCopilotDocSessionsQuery = {
|
||||||
|
id: 'getCopilotDocSessionsQuery' as const,
|
||||||
|
op: 'getCopilotDocSessions',
|
||||||
|
query: `query getCopilotDocSessions($workspaceId: String!, $docId: String!, $options: QueryChatHistoriesInput) {
|
||||||
|
currentUser {
|
||||||
|
copilot(workspaceId: $workspaceId) {
|
||||||
|
histories(docId: $docId, options: $options) {
|
||||||
|
sessionId
|
||||||
|
pinned
|
||||||
|
tokens
|
||||||
|
action
|
||||||
|
createdAt
|
||||||
|
messages {
|
||||||
|
id
|
||||||
|
role
|
||||||
|
content
|
||||||
|
streamObjects {
|
||||||
|
type
|
||||||
|
textDelta
|
||||||
|
toolCallId
|
||||||
|
toolName
|
||||||
|
args
|
||||||
|
result
|
||||||
|
}
|
||||||
|
attachments
|
||||||
|
createdAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getCopilotPinnedSessionsQuery = {
|
||||||
|
id: 'getCopilotPinnedSessionsQuery' as const,
|
||||||
|
op: 'getCopilotPinnedSessions',
|
||||||
|
query: `query getCopilotPinnedSessions($workspaceId: String!, $docId: String, $messageOrder: ChatHistoryOrder, $withPrompt: Boolean) {
|
||||||
|
currentUser {
|
||||||
|
copilot(workspaceId: $workspaceId) {
|
||||||
|
histories(
|
||||||
|
docId: $docId
|
||||||
|
options: {limit: 1, pinned: true, messageOrder: $messageOrder, withPrompt: $withPrompt}
|
||||||
|
) {
|
||||||
|
sessionId
|
||||||
|
pinned
|
||||||
|
tokens
|
||||||
|
action
|
||||||
|
createdAt
|
||||||
|
messages {
|
||||||
|
id
|
||||||
|
role
|
||||||
|
content
|
||||||
|
streamObjects {
|
||||||
|
type
|
||||||
|
textDelta
|
||||||
|
toolCallId
|
||||||
|
toolName
|
||||||
|
args
|
||||||
|
result
|
||||||
|
}
|
||||||
|
attachments
|
||||||
|
createdAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getCopilotWorkspaceSessionsQuery = {
|
||||||
|
id: 'getCopilotWorkspaceSessionsQuery' as const,
|
||||||
|
op: 'getCopilotWorkspaceSessions',
|
||||||
|
query: `query getCopilotWorkspaceSessions($workspaceId: String!, $options: QueryChatHistoriesInput) {
|
||||||
|
currentUser {
|
||||||
|
copilot(workspaceId: $workspaceId) {
|
||||||
|
histories(docId: null, options: $options) {
|
||||||
|
sessionId
|
||||||
|
pinned
|
||||||
|
tokens
|
||||||
|
action
|
||||||
|
createdAt
|
||||||
|
messages {
|
||||||
|
id
|
||||||
|
role
|
||||||
|
content
|
||||||
|
streamObjects {
|
||||||
|
type
|
||||||
|
textDelta
|
||||||
|
toolCallId
|
||||||
|
toolName
|
||||||
|
args
|
||||||
|
result
|
||||||
|
}
|
||||||
|
attachments
|
||||||
|
createdAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
};
|
||||||
|
|
||||||
export const getCopilotHistoriesQuery = {
|
export const getCopilotHistoriesQuery = {
|
||||||
id: 'getCopilotHistoriesQuery' as const,
|
id: 'getCopilotHistoriesQuery' as const,
|
||||||
op: 'getCopilotHistories',
|
op: 'getCopilotHistories',
|
||||||
|
|||||||
@@ -3390,6 +3390,129 @@ export type GetCopilotHistoryIdsQuery = {
|
|||||||
} | null;
|
} | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type GetCopilotDocSessionsQueryVariables = Exact<{
|
||||||
|
workspaceId: Scalars['String']['input'];
|
||||||
|
docId: Scalars['String']['input'];
|
||||||
|
options?: InputMaybe<QueryChatHistoriesInput>;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export type GetCopilotDocSessionsQuery = {
|
||||||
|
__typename?: 'Query';
|
||||||
|
currentUser: {
|
||||||
|
__typename?: 'UserType';
|
||||||
|
copilot: {
|
||||||
|
__typename?: 'Copilot';
|
||||||
|
histories: Array<{
|
||||||
|
__typename?: 'CopilotHistories';
|
||||||
|
sessionId: string;
|
||||||
|
pinned: boolean;
|
||||||
|
tokens: number;
|
||||||
|
action: string | null;
|
||||||
|
createdAt: string;
|
||||||
|
messages: Array<{
|
||||||
|
__typename?: 'ChatMessage';
|
||||||
|
id: string | null;
|
||||||
|
role: string;
|
||||||
|
content: string;
|
||||||
|
attachments: Array<string> | null;
|
||||||
|
createdAt: string;
|
||||||
|
streamObjects: Array<{
|
||||||
|
__typename?: 'StreamObject';
|
||||||
|
type: string;
|
||||||
|
textDelta: string | null;
|
||||||
|
toolCallId: string | null;
|
||||||
|
toolName: string | null;
|
||||||
|
args: Record<string, string> | null;
|
||||||
|
result: Record<string, string> | null;
|
||||||
|
}> | null;
|
||||||
|
}>;
|
||||||
|
}>;
|
||||||
|
};
|
||||||
|
} | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GetCopilotPinnedSessionsQueryVariables = Exact<{
|
||||||
|
workspaceId: Scalars['String']['input'];
|
||||||
|
docId?: InputMaybe<Scalars['String']['input']>;
|
||||||
|
messageOrder?: InputMaybe<ChatHistoryOrder>;
|
||||||
|
withPrompt?: InputMaybe<Scalars['Boolean']['input']>;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export type GetCopilotPinnedSessionsQuery = {
|
||||||
|
__typename?: 'Query';
|
||||||
|
currentUser: {
|
||||||
|
__typename?: 'UserType';
|
||||||
|
copilot: {
|
||||||
|
__typename?: 'Copilot';
|
||||||
|
histories: Array<{
|
||||||
|
__typename?: 'CopilotHistories';
|
||||||
|
sessionId: string;
|
||||||
|
pinned: boolean;
|
||||||
|
tokens: number;
|
||||||
|
action: string | null;
|
||||||
|
createdAt: string;
|
||||||
|
messages: Array<{
|
||||||
|
__typename?: 'ChatMessage';
|
||||||
|
id: string | null;
|
||||||
|
role: string;
|
||||||
|
content: string;
|
||||||
|
attachments: Array<string> | null;
|
||||||
|
createdAt: string;
|
||||||
|
streamObjects: Array<{
|
||||||
|
__typename?: 'StreamObject';
|
||||||
|
type: string;
|
||||||
|
textDelta: string | null;
|
||||||
|
toolCallId: string | null;
|
||||||
|
toolName: string | null;
|
||||||
|
args: Record<string, string> | null;
|
||||||
|
result: Record<string, string> | null;
|
||||||
|
}> | null;
|
||||||
|
}>;
|
||||||
|
}>;
|
||||||
|
};
|
||||||
|
} | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GetCopilotWorkspaceSessionsQueryVariables = Exact<{
|
||||||
|
workspaceId: Scalars['String']['input'];
|
||||||
|
options?: InputMaybe<QueryChatHistoriesInput>;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export type GetCopilotWorkspaceSessionsQuery = {
|
||||||
|
__typename?: 'Query';
|
||||||
|
currentUser: {
|
||||||
|
__typename?: 'UserType';
|
||||||
|
copilot: {
|
||||||
|
__typename?: 'Copilot';
|
||||||
|
histories: Array<{
|
||||||
|
__typename?: 'CopilotHistories';
|
||||||
|
sessionId: string;
|
||||||
|
pinned: boolean;
|
||||||
|
tokens: number;
|
||||||
|
action: string | null;
|
||||||
|
createdAt: string;
|
||||||
|
messages: Array<{
|
||||||
|
__typename?: 'ChatMessage';
|
||||||
|
id: string | null;
|
||||||
|
role: string;
|
||||||
|
content: string;
|
||||||
|
attachments: Array<string> | null;
|
||||||
|
createdAt: string;
|
||||||
|
streamObjects: Array<{
|
||||||
|
__typename?: 'StreamObject';
|
||||||
|
type: string;
|
||||||
|
textDelta: string | null;
|
||||||
|
toolCallId: string | null;
|
||||||
|
toolName: string | null;
|
||||||
|
args: Record<string, string> | null;
|
||||||
|
result: Record<string, string> | null;
|
||||||
|
}> | null;
|
||||||
|
}>;
|
||||||
|
}>;
|
||||||
|
};
|
||||||
|
} | null;
|
||||||
|
};
|
||||||
|
|
||||||
export type GetCopilotHistoriesQueryVariables = Exact<{
|
export type GetCopilotHistoriesQueryVariables = Exact<{
|
||||||
workspaceId: Scalars['String']['input'];
|
workspaceId: Scalars['String']['input'];
|
||||||
docId?: InputMaybe<Scalars['String']['input']>;
|
docId?: InputMaybe<Scalars['String']['input']>;
|
||||||
@@ -5252,6 +5375,21 @@ export type Queries =
|
|||||||
variables: GetCopilotHistoryIdsQueryVariables;
|
variables: GetCopilotHistoryIdsQueryVariables;
|
||||||
response: GetCopilotHistoryIdsQuery;
|
response: GetCopilotHistoryIdsQuery;
|
||||||
}
|
}
|
||||||
|
| {
|
||||||
|
name: 'getCopilotDocSessionsQuery';
|
||||||
|
variables: GetCopilotDocSessionsQueryVariables;
|
||||||
|
response: GetCopilotDocSessionsQuery;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
name: 'getCopilotPinnedSessionsQuery';
|
||||||
|
variables: GetCopilotPinnedSessionsQueryVariables;
|
||||||
|
response: GetCopilotPinnedSessionsQuery;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
name: 'getCopilotWorkspaceSessionsQuery';
|
||||||
|
variables: GetCopilotWorkspaceSessionsQueryVariables;
|
||||||
|
response: GetCopilotWorkspaceSessionsQuery;
|
||||||
|
}
|
||||||
| {
|
| {
|
||||||
name: 'getCopilotHistoriesQuery';
|
name: 'getCopilotHistoriesQuery';
|
||||||
variables: GetCopilotHistoriesQueryVariables;
|
variables: GetCopilotHistoriesQueryVariables;
|
||||||
|
|||||||
Reference in New Issue
Block a user