diff --git a/packages/backend/server/src/__tests__/copilot.spec.ts b/packages/backend/server/src/__tests__/copilot.spec.ts index a567275920..b44ca678f1 100644 --- a/packages/backend/server/src/__tests__/copilot.spec.ts +++ b/packages/backend/server/src/__tests__/copilot.spec.ts @@ -1891,7 +1891,7 @@ test('should handle generateSessionTitle correctly under various conditions', as await session.generateSessionTitle({ sessionId }); if (testCase.expectSnapshot) { - const sessionState = await session.getSession(sessionId); + const sessionState = await session.getSessionInfo(sessionId); t.snapshot( { chatWithPromptCalled: testCase.expectNotCalled diff --git a/packages/backend/server/src/models/copilot-session.ts b/packages/backend/server/src/models/copilot-session.ts index d623cf3d62..f64b81fefd 100644 --- a/packages/backend/server/src/models/copilot-session.ts +++ b/packages/backend/server/src/models/copilot-session.ts @@ -265,26 +265,31 @@ export class CopilotSessionModel extends BaseModel { userId: true, workspaceId: true, docId: true, - pinned: true, parentSessionId: true, + pinned: true, title: true, + promptName: true, + tokenCost: true, + createdAt: true, + updatedAt: true, messages: { select: { id: true, role: true, content: true, - streamObjects: true, attachments: true, + streamObjects: true, params: true, createdAt: true, }, orderBy: { createdAt: 'asc' }, }, - promptName: true, }); } - async list(options: ListSessionOptions) { + private getListConditions( + options: ListSessionOptions + ): Prisma.AiSessionWhereInput { const { userId, sessionId, workspaceId, docId, action, fork } = options; function getNullCond( @@ -330,8 +335,18 @@ export class CopilotSessionModel extends BaseModel { }); } + return { OR: conditions }; + } + + async count(options: ListSessionOptions) { + return await this.db.aiSession.count({ + where: this.getListConditions(options), + }); + } + + async list(options: ListSessionOptions) { return await this.db.aiSession.findMany({ - where: { OR: conditions }, + where: this.getListConditions(options), select: { id: true, userId: true, @@ -351,8 +366,8 @@ export class CopilotSessionModel extends BaseModel { role: true, content: true, attachments: true, - params: true, streamObjects: true, + params: true, createdAt: true, }, orderBy: { diff --git a/packages/backend/server/src/plugins/copilot/resolver.ts b/packages/backend/server/src/plugins/copilot/resolver.ts index f1317998ae..f658254dc7 100644 --- a/packages/backend/server/src/plugins/copilot/resolver.ts +++ b/packages/backend/server/src/plugins/copilot/resolver.ts @@ -25,6 +25,9 @@ import { CopilotFailedToCreateMessage, CopilotSessionNotFound, type FileUpload, + paginate, + Paginated, + PaginationInput, RequestMutex, Throttle, TooManyRequest, @@ -38,12 +41,7 @@ import { PromptService } from './prompt'; import { PromptMessage, StreamObject } from './providers'; import { ChatSessionService } from './session'; import { CopilotStorage } from './storage'; -import { - type ChatHistory, - type ChatMessage, - type ChatSessionState, - SubmittedMessage, -} from './types'; +import { type ChatHistory, type ChatMessage, SubmittedMessage } from './types'; export const COPILOT_LOCKER = 'copilot'; @@ -186,6 +184,9 @@ class QueryChatHistoriesInput @Field(() => String, { nullable: true }) sessionId: string | undefined; + @Field(() => Boolean, { nullable: true }) + withMessages: boolean | undefined; + @Field(() => Boolean, { nullable: true }) withPrompt: boolean | undefined; } @@ -239,7 +240,7 @@ class ChatMessageType implements Partial { } @ObjectType('CopilotHistories') -class CopilotHistoriesType implements Partial { +class CopilotHistoriesType implements Omit { @Field(() => String) sessionId!: string; @@ -249,8 +250,17 @@ class CopilotHistoriesType implements Partial { @Field(() => String, { nullable: true }) docId!: string | null; - @Field(() => Boolean) - pinned!: boolean; + @Field(() => String, { nullable: true }) + parentSessionId!: string | null; + + @Field(() => String) + promptName!: string; + + @Field(() => String) + model!: string; + + @Field(() => [String]) + optionalModels!: string[]; @Field(() => String, { description: 'An mark identifying which view to use to display the session', @@ -258,6 +268,12 @@ class CopilotHistoriesType implements Partial { }) action!: string | null; + @Field(() => Boolean) + pinned!: boolean; + + @Field(() => String, { nullable: true }) + title!: string | null; + @Field(() => Number, { description: 'The number of tokens used in the session', }) @@ -273,6 +289,11 @@ class CopilotHistoriesType implements Partial { updatedAt!: Date; } +@ObjectType() +export class PaginatedCopilotHistoriesType extends Paginated( + CopilotHistoriesType +) {} + @ObjectType('CopilotQuota') class CopilotQuotaType { @Field(() => SafeIntResolver, { nullable: true }) @@ -421,7 +442,7 @@ export class CopilotResolver { @Args('sessionId') sessionId: string ): Promise { await this.assertPermission(user, copilot); - const session = await this.chatSession.getSession(sessionId); + const session = await this.chatSession.getSessionInfo(sessionId); if (!session) { throw new NotFoundException('Session not found'); } @@ -430,6 +451,7 @@ export class CopilotResolver { @ResolveField(() => [CopilotSessionType], { description: 'Get the session list in the workspace', + deprecationReason: 'use `chats` instead', complexity: 2, }) async sessions( @@ -447,11 +469,12 @@ export class CopilotResolver { Object.assign({}, copilot, { docId: maybeDocId }) ); - const sessions = await this.chatSession.listSessions( - Object.assign({}, options, appendOptions) + const sessions = await this.chatSession.list( + Object.assign({}, options, appendOptions), + false ); if (appendOptions.docId) { - type Session = Omit & { docId: string }; + type Session = ChatHistory & { docId: string }; const filtered = sessions.filter((s): s is Session => !!s.docId); const accessible = await this.ac .user(user.id) @@ -463,7 +486,9 @@ export class CopilotResolver { } } - @ResolveField(() => [CopilotHistoriesType], {}) + @ResolveField(() => [CopilotHistoriesType], { + deprecationReason: 'use `chats` instead', + }) @CallMetric('ai', 'histories') async histories( @Parent() copilot: CopilotType, @@ -478,8 +503,9 @@ export class CopilotResolver { await this.assertPermission(user, { workspaceId, docId }); } - const histories = await this.chatSession.listHistories( - Object.assign({}, options, { userId: user.id, workspaceId, docId }) + const histories = await this.chatSession.list( + Object.assign({}, options, { userId: user.id, workspaceId, docId }), + true ); return histories.map(h => ({ @@ -491,6 +517,48 @@ export class CopilotResolver { })); } + @ResolveField(() => PaginatedCopilotHistoriesType, {}) + @CallMetric('ai', 'histories') + async chats( + @Parent() copilot: CopilotType, + @CurrentUser() user: CurrentUser, + @Args('pagination', PaginationInput.decode) pagination: PaginationInput, + @Args('docId', { nullable: true }) docId?: string, + @Args('options', { nullable: true }) options?: QueryChatHistoriesInput + ): Promise { + const workspaceId = copilot.workspaceId; + if (!workspaceId) { + return paginate([], 'updatedAt', pagination, 0); + } else { + await this.assertPermission(user, { workspaceId, docId }); + } + + const finalOptions = Object.assign( + {}, + options, + { userId: user.id, workspaceId, docId }, + { skip: pagination.offset, limit: pagination.first } + ); + const totalCount = await this.chatSession.count(finalOptions); + const histories = await this.chatSession.list( + finalOptions, + !!options?.withMessages + ); + + return paginate( + histories.map(h => ({ + ...h, + // filter out empty messages + messages: h.messages?.filter( + m => m.content || m.attachments?.length + ) as ChatMessageType[], + })), + 'updatedAt', + pagination, + totalCount + ); + } + @Mutation(() => String, { description: 'Create a chat session', }) @@ -657,18 +725,9 @@ export class CopilotResolver { } private transformToSessionType( - session: Omit + session: Omit ): CopilotSessionType { - return { - id: session.sessionId, - parentSessionId: session.parentSessionId, - docId: session.docId, - pinned: session.pinned, - title: session.title, - promptName: session.prompt.name, - model: session.prompt.model, - optionalModels: session.prompt.optionalModels, - }; + return { id: session.sessionId, ...session }; } } diff --git a/packages/backend/server/src/plugins/copilot/session.ts b/packages/backend/server/src/plugins/copilot/session.ts index 68b080e9e6..3efbf6da0f 100644 --- a/packages/backend/server/src/plugins/copilot/session.ts +++ b/packages/backend/server/src/plugins/copilot/session.ts @@ -4,6 +4,7 @@ import { Injectable, Logger } from '@nestjs/common'; import { ModuleRef } from '@nestjs/core'; import { Transactional } from '@nestjs-cls/transactional'; import { AiPromptRole } from '@prisma/client'; +import { pick } from 'lodash-es'; import { CopilotActionTaken, @@ -25,7 +26,7 @@ import { UpdateChatSessionOptions, } from '../../models'; import { ChatMessageCache } from './message'; -import { PromptService } from './prompt'; +import { ChatPrompt, PromptService } from './prompt'; import { CopilotProviderFactory, ModelOutputType, @@ -240,6 +241,14 @@ export class ChatSession implements AsyncDisposable { } } +type Session = NonNullable< + Awaited> +>; + +type SessionHistory = ChatHistory & { + prompt: ChatPrompt; +}; + @Injectable() export class ChatSessionService { private readonly logger = new Logger(ChatSessionService.name); @@ -253,27 +262,55 @@ export class ChatSessionService { private readonly prompt: PromptService ) {} - async getSession(sessionId: string): Promise { - const session = await this.models.copilotSession.get(sessionId); - if (!session) return; + private getMessage(session: Session): ChatMessage[] { + if (!Array.isArray(session.messages) || !session.messages.length) { + return []; + } + const messages = ChatMessageSchema.array().safeParse(session.messages); + if (!messages.success) { + this.logger.error( + `Unexpected message schema: ${JSON.stringify(messages.error)}` + ); + return []; + } + return messages.data; + } + + private async getHistory(session: Session): Promise { const prompt = await this.prompt.get(session.promptName); if (!prompt) throw new CopilotPromptNotFound({ name: session.promptName }); - const messages = ChatMessageSchema.array().safeParse(session.messages); - return { + ...pick(session, [ + 'userId', + 'workspaceId', + 'docId', + 'parentSessionId', + 'pinned', + 'title', + 'createdAt', + 'updatedAt', + ]), sessionId: session.id, - userId: session.userId, - workspaceId: session.workspaceId, - docId: session.docId, - pinned: session.pinned, - title: session.title, - parentSessionId: session.parentSessionId, + tokens: session.tokenCost, + messages: this.getMessage(session), + + // prompt info prompt, - messages: messages.success ? messages.data : [], + action: prompt.action || null, + model: prompt.model, + optionalModels: prompt.optionalModels || null, + promptName: prompt.name, }; } + async getSessionInfo(sessionId: string): Promise { + const session = await this.models.copilotSession.get(sessionId); + if (!session) return; + + return await this.getHistory(session); + } + // revert the latest messages not generate by user // after revert, we can retry the action async revertLatestMessage( @@ -286,116 +323,70 @@ export class ChatSessionService { ); } - async listSessions( - options: ListSessionOptions - ): Promise[]> { - const sessions = await this.models.copilotSession.list({ - ...options, - withMessages: false, - }); - - return Promise.all( - sessions.map(async session => { - const prompt = await this.prompt.get(session.promptName); - if (!prompt) - throw new CopilotPromptNotFound({ name: session.promptName }); - - return { - sessionId: session.id, - userId: session.userId, - workspaceId: session.workspaceId, - docId: session.docId, - pinned: session.pinned, - title: session.title, - parentSessionId: session.parentSessionId, - prompt, - }; - }) - ); + async count(options: ListSessionOptions): Promise { + return await this.models.copilotSession.count(options); } - async listHistories(options: ListSessionOptions): Promise { - const { userId } = options; + async list( + options: ListSessionOptions, + withMessages: boolean + ): Promise { + const { userId: reqUserId } = options; const sessions = await this.models.copilotSession.list({ ...options, - withMessages: true, + withMessages, }); const histories = await Promise.all( - sessions.map( - async ({ - userId: uid, - id, - workspaceId, - docId, - pinned, - title, - promptName, - tokenCost, - messages, - createdAt, - updatedAt, - }) => { - try { - const prompt = await this.prompt.get(promptName); - if (!prompt) { - throw new CopilotPromptNotFound({ name: promptName }); - } + sessions.map(async session => { + const { userId, id: sessionId, createdAt } = session; + try { + const { prompt, messages, ...baseHistory } = + await this.getHistory(session); + + if (withMessages) { if ( // filter out the user's session that not match the action option - (uid === userId && !!options?.action !== !!prompt.action) || + (userId === reqUserId && !!options?.action !== !!prompt.action) || // filter out the non chat session from other user - (uid !== userId && !!prompt.action) + (userId !== reqUserId && !!prompt.action) ) { return undefined; } - const ret = ChatMessageSchema.array().safeParse(messages); - if (ret.success) { - // render system prompt - const preload = ( - options?.withPrompt - ? prompt - .finish(ret.data[0]?.params || {}, id) - .filter(({ role }) => role !== 'system') - : [] - ) as ChatMessage[]; + // render system prompt + const preload = ( + options?.withPrompt + ? prompt + .finish(messages[0]?.params || {}, sessionId) + .filter(({ role }) => role !== 'system') + : [] + ) as ChatMessage[]; - // `createdAt` is required for history sorting in frontend - // let's fake the creating time of prompt messages - preload.forEach((msg, i) => { - msg.createdAt = new Date( - createdAt.getTime() - preload.length - i - 1 - ); - }); - - return { - sessionId: id, - workspaceId, - docId, - pinned, - title, - action: prompt.action || null, - tokens: tokenCost, - createdAt, - updatedAt, - messages: preload.concat(ret.data).map(m => ({ - ...m, - attachments: m.attachments - ?.map(a => (typeof a === 'string' ? a : a.attachment)) - .filter(a => !!a), - })), - }; - } else { - this.logger.error( - `Unexpected message schema: ${JSON.stringify(ret.error)}` + // `createdAt` is required for history sorting in frontend + // let's fake the creating time of prompt messages + preload.forEach((msg, i) => { + msg.createdAt = new Date( + createdAt.getTime() - preload.length - i - 1 ); - } - } catch (e) { - this.logger.error('Unexpected error in listHistories', e); + }); + + return { + ...baseHistory, + messages: preload.concat(messages).map(m => ({ + ...m, + attachments: m.attachments + ?.map(a => (typeof a === 'string' ? a : a.attachment)) + .filter(a => !!a), + })), + }; + } else { + return { ...baseHistory, messages: [] }; } - return undefined; + } catch (e) { + this.logger.error('Unexpected error in list ChatHistories', e); } - ) + return undefined; + }) ); return histories.filter((v): v is NonNullable => !!v); @@ -461,7 +452,7 @@ export class ChatSessionService { @Transactional() async update(options: UpdateChatSession): Promise { - const session = await this.getSession(options.sessionId); + const session = await this.getSessionInfo(options.sessionId); if (!session) { throw new CopilotSessionNotFound(); } @@ -494,14 +485,14 @@ export class ChatSessionService { @Transactional() async fork(options: ChatSessionForkOptions): Promise { - const state = await this.getSession(options.sessionId); - if (!state) { + const session = await this.getSessionInfo(options.sessionId); + if (!session) { throw new CopilotSessionNotFound(); } - let messages = state.messages.map(m => ({ ...m, id: undefined })); + let messages = session.messages.map(m => ({ ...m, id: undefined })); if (options.latestMessageId) { - const lastMessageIdx = state.messages.findLastIndex( + const lastMessageIdx = session.messages.findLastIndex( ({ id, role }) => role === AiPromptRole.assistant && id === options.latestMessageId ); @@ -514,7 +505,7 @@ export class ChatSessionService { } return await this.models.copilotSession.fork({ - ...state, + ...session, userId: options.userId, sessionId: randomUUID(), parentSessionId: options.sessionId, @@ -544,7 +535,7 @@ export class ChatSessionService { * @returns */ async get(sessionId: string): Promise { - const state = await this.getSession(sessionId); + const state = await this.getSessionInfo(sessionId); if (state) { return new ChatSession(this.messageCache, state, async state => { await this.models.copilotSession.updateMessages(state); diff --git a/packages/backend/server/src/plugins/copilot/types.ts b/packages/backend/server/src/plugins/copilot/types.ts index b34beeb84a..dc8db5e18b 100644 --- a/packages/backend/server/src/plugins/copilot/types.ts +++ b/packages/backend/server/src/plugins/copilot/types.ts @@ -46,12 +46,19 @@ export type ChatMessage = z.infer; export const ChatHistorySchema = z .object({ + userId: z.string(), sessionId: z.string(), workspaceId: z.string(), docId: z.string().nullable(), + parentSessionId: z.string().nullable(), pinned: z.boolean(), title: z.string().nullable(), + action: z.string().nullable(), + model: z.string(), + optionalModels: z.array(z.string()), + promptName: z.string(), + tokens: z.number(), messages: z.array(ChatMessageSchema), createdAt: z.date(), @@ -69,32 +76,26 @@ export type SubmittedMessage = z.infer; // ======== Chat Session ======== -export interface ChatSessionOptions { - // connect ids - userId: string; - workspaceId: string; - docId: string | null; - promptName: string; - pinned: boolean; +export type ChatSessionOptions = Pick< + ChatHistory, + 'userId' | 'workspaceId' | 'docId' | 'promptName' | 'pinned' +> & { reuseLatestChat?: boolean; -} +}; -export interface ChatSessionForkOptions - extends Omit { - sessionId: string; +export type ChatSessionForkOptions = Pick< + ChatHistory, + 'userId' | 'sessionId' | 'workspaceId' | 'docId' +> & { latestMessageId?: string; -} +}; -export interface ChatSessionState - extends Omit { - title: string | null; - // connect ids - sessionId: string; - parentSessionId: string | null; - // states +export type ChatSessionState = Pick< + ChatHistory, + 'userId' | 'sessionId' | 'workspaceId' | 'docId' | 'messages' +> & { prompt: ChatPrompt; - messages: ChatMessage[]; -} +}; export type CopilotContextFile = { id: string; // fileId diff --git a/packages/backend/server/src/schema.gql b/packages/backend/server/src/schema.gql index 7c8858389a..c05ab923e8 100644 --- a/packages/backend/server/src/schema.gql +++ b/packages/backend/server/src/schema.gql @@ -208,10 +208,11 @@ type ContextWorkspaceEmbeddingStatus { type Copilot { audioTranscription(blobId: String, jobId: String): TranscriptionResultType + chats(docId: String, options: QueryChatHistoriesInput, pagination: PaginationInput!): PaginatedCopilotHistoriesType! """Get the context list of a session""" contexts(contextId: String, sessionId: String): [CopilotContext!]! - histories(docId: String, options: QueryChatHistoriesInput): [CopilotHistories!]! + histories(docId: String, options: QueryChatHistoriesInput): [CopilotHistories!]! @deprecated(reason: "use `chats` instead") """Get the quota of the user in the workspace""" quota: CopilotQuota! @@ -220,7 +221,7 @@ type Copilot { session(sessionId: String!): CopilotSessionType! """Get the session list in the workspace""" - sessions(docId: String, options: QueryChatSessionsInput): [CopilotSessionType!]! + sessions(docId: String, options: QueryChatSessionsInput): [CopilotSessionType!]! @deprecated(reason: "use `chats` instead") workspaceId: ID } @@ -313,8 +314,13 @@ type CopilotHistories { createdAt: DateTime! docId: String messages: [ChatMessage!]! + model: String! + optionalModels: [String!]! + parentSessionId: String pinned: Boolean! + promptName: String! sessionId: String! + title: String """The number of tokens used in the session""" tokens: Int! @@ -322,6 +328,11 @@ type CopilotHistories { workspaceId: String! } +type CopilotHistoriesTypeEdge { + cursor: String! + node: CopilotHistories! +} + type CopilotInvalidContextDataType { contextId: String! } @@ -1421,6 +1432,12 @@ type PaginatedCommentObjectType { totalCount: Int! } +type PaginatedCopilotHistoriesType { + edges: [CopilotHistoriesTypeEdge!]! + pageInfo: PageInfo! + totalCount: Int! +} + type PaginatedCopilotWorkspaceFileType { edges: [CopilotWorkspaceFileTypeEdge!]! pageInfo: PageInfo! @@ -1552,6 +1569,7 @@ input QueryChatHistoriesInput { sessionId: String sessionOrder: ChatHistoryOrder skip: Int + withMessages: Boolean withPrompt: Boolean } diff --git a/packages/common/graphql/src/graphql/copilot-history-get-ids.gql b/packages/common/graphql/src/graphql/copilot-history-get-ids.gql index b458262083..67b6cec390 100644 --- a/packages/common/graphql/src/graphql/copilot-history-get-ids.gql +++ b/packages/common/graphql/src/graphql/copilot-history-get-ids.gql @@ -1,17 +1,29 @@ query getCopilotHistoryIds( $workspaceId: String! + $pagination: PaginationInput! $docId: String $options: QueryChatHistoriesInput ) { currentUser { copilot(workspaceId: $workspaceId) { - histories(docId: $docId, options: $options) { - sessionId - pinned - messages { - id - role - createdAt + chats(pagination: $pagination, docId: $docId, options: $options) { + pageInfo { + hasNextPage + hasPreviousPage + startCursor + endCursor + } + edges { + cursor + node { + sessionId + pinned + messages { + id + role + createdAt + } + } } } } diff --git a/packages/common/graphql/src/graphql/copilot-history-list-doc-sessions.gql b/packages/common/graphql/src/graphql/copilot-history-list-doc-sessions.gql index cb57c859c4..31dc7bd097 100644 --- a/packages/common/graphql/src/graphql/copilot-history-list-doc-sessions.gql +++ b/packages/common/graphql/src/graphql/copilot-history-list-doc-sessions.gql @@ -1,31 +1,15 @@ +#import "./fragments/copilot.gql" + query getCopilotDocSessions( $workspaceId: String! $docId: String! + $pagination: PaginationInput! $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 - } + chats(pagination: $pagination, docId: $docId, options: $options) { + ...PaginatedCopilotChats } } } diff --git a/packages/common/graphql/src/graphql/copilot-history-list-pinned-session.gql b/packages/common/graphql/src/graphql/copilot-history-list-pinned-session.gql index 6c390721e8..19aac215cf 100644 --- a/packages/common/graphql/src/graphql/copilot-history-list-pinned-session.gql +++ b/packages/common/graphql/src/graphql/copilot-history-list-pinned-session.gql @@ -1,3 +1,5 @@ +#import "./fragments/copilot.gql" + query getCopilotPinnedSessions( $workspaceId: String! $docId: String @@ -6,32 +8,12 @@ query getCopilotPinnedSessions( ) { currentUser { copilot(workspaceId: $workspaceId) { - histories(docId: $docId, options: { - limit: 1, + chats(pagination: { first: 1 }, docId: $docId, options: { pinned: true, messageOrder: $messageOrder, withPrompt: $withPrompt }) { - sessionId - pinned - tokens - action - createdAt - messages { - id - role - content - streamObjects { - type - textDelta - toolCallId - toolName - args - result - } - attachments - createdAt - } + ...PaginatedCopilotChats } } } diff --git a/packages/common/graphql/src/graphql/copilot-history-list-workspace-sessions.gql b/packages/common/graphql/src/graphql/copilot-history-list-workspace-sessions.gql index eaa74dd7a0..aeaddf21fa 100644 --- a/packages/common/graphql/src/graphql/copilot-history-list-workspace-sessions.gql +++ b/packages/common/graphql/src/graphql/copilot-history-list-workspace-sessions.gql @@ -1,30 +1,14 @@ +#import "./fragments/copilot.gql" + query getCopilotWorkspaceSessions( $workspaceId: String! + $pagination: PaginationInput! $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 - } + chats(pagination: $pagination, docId: null, options: $options) { + ...PaginatedCopilotChats } } } diff --git a/packages/common/graphql/src/graphql/copilot-history-list.gql b/packages/common/graphql/src/graphql/copilot-history-list.gql index 8095f77b71..cc2708e50a 100644 --- a/packages/common/graphql/src/graphql/copilot-history-list.gql +++ b/packages/common/graphql/src/graphql/copilot-history-list.gql @@ -1,31 +1,15 @@ +#import "./fragments/copilot.gql" + query getCopilotHistories( $workspaceId: String! + $pagination: PaginationInput! $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 - } + chats(pagination: $pagination, docId: $docId, options: $options) { + ...PaginatedCopilotChats } } } diff --git a/packages/common/graphql/src/graphql/copilot-session-get-latest-doc.gql b/packages/common/graphql/src/graphql/copilot-session-get-latest-doc.gql index 43b9c0c14d..382c1f6ee8 100644 --- a/packages/common/graphql/src/graphql/copilot-session-get-latest-doc.gql +++ b/packages/common/graphql/src/graphql/copilot-session-get-latest-doc.gql @@ -1,34 +1,22 @@ +#import "./fragments/copilot.gql" + query getCopilotLatestDocSession( $workspaceId: String! $docId: String! ) { currentUser { copilot(workspaceId: $workspaceId) { - histories( + chats( + pagination: { first: 1 } docId: $docId options: { - limit: 1 sessionOrder: desc action: false fork: false + withMessages: true } ) { - sessionId - workspaceId - docId - pinned - action - tokens - createdAt - updatedAt - messages { - id - role - content - attachments - params - createdAt - } + ...PaginatedCopilotChats } } } diff --git a/packages/common/graphql/src/graphql/copilot-session-get.gql b/packages/common/graphql/src/graphql/copilot-session-get.gql index b331aa1bc4..129dc109af 100644 --- a/packages/common/graphql/src/graphql/copilot-session-get.gql +++ b/packages/common/graphql/src/graphql/copilot-session-get.gql @@ -1,18 +1,16 @@ +#import "./fragments/copilot.gql" + query getCopilotSession( $workspaceId: String! $sessionId: String! ) { currentUser { copilot(workspaceId: $workspaceId) { - session(sessionId: $sessionId) { - id - parentSessionId - docId - pinned - title - promptName - model - optionalModels + chats( + pagination: { first: 1 } + options: { sessionId: $sessionId } + ) { + ...PaginatedCopilotChats } } } diff --git a/packages/common/graphql/src/graphql/copilot-session-list-recent.gql b/packages/common/graphql/src/graphql/copilot-session-list-recent.gql index 34caa79e07..4c013d5dcf 100644 --- a/packages/common/graphql/src/graphql/copilot-session-list-recent.gql +++ b/packages/common/graphql/src/graphql/copilot-session-list-recent.gql @@ -1,23 +1,20 @@ +#import "./fragments/copilot.gql" + query getCopilotRecentSessions( $workspaceId: String! $limit: Int = 10 ) { currentUser { copilot(workspaceId: $workspaceId) { - histories( + chats( + pagination: { first: $limit } options: { - limit: $limit + fork: false sessionOrder: desc + withMessages: true } ) { - sessionId - workspaceId - docId - pinned - action - tokens - createdAt - updatedAt + ...PaginatedCopilotChats } } } diff --git a/packages/common/graphql/src/graphql/copilot-sessions-get.gql b/packages/common/graphql/src/graphql/copilot-sessions-get.gql index 3ee9b4ad5c..8ec80bfc4e 100644 --- a/packages/common/graphql/src/graphql/copilot-sessions-get.gql +++ b/packages/common/graphql/src/graphql/copilot-sessions-get.gql @@ -1,19 +1,15 @@ +#import "./fragments/copilot.gql" + query getCopilotSessions( $workspaceId: String! + $pagination: PaginationInput! $docId: String - $options: QueryChatSessionsInput + $options: QueryChatHistoriesInput ) { currentUser { copilot(workspaceId: $workspaceId) { - sessions(docId: $docId, options: $options) { - id - parentSessionId - docId - pinned - title - promptName - model - optionalModels + chats(pagination: $pagination, docId: $docId, options: $options) { + ...PaginatedCopilotChats } } } diff --git a/packages/common/graphql/src/graphql/fragments/copilot.gql b/packages/common/graphql/src/graphql/fragments/copilot.gql new file mode 100644 index 0000000000..3884cdcd0c --- /dev/null +++ b/packages/common/graphql/src/graphql/fragments/copilot.gql @@ -0,0 +1,49 @@ +fragment CopilotChatMessage on ChatMessage { + id + role + content + attachments + streamObjects { + type + textDelta + toolCallId + toolName + args + result + } + createdAt +} + +fragment CopilotChatHistory on CopilotHistories { + sessionId + workspaceId + docId + parentSessionId + promptName + model + optionalModels + action + pinned + title + tokens + messages { + ...CopilotChatMessage + } + createdAt + updatedAt +} + +fragment PaginatedCopilotChats on PaginatedCopilotHistoriesType { + pageInfo { + hasNextPage + hasPreviousPage + startCursor + endCursor + } + edges { + cursor + node { + ...CopilotChatHistory + } + } +} diff --git a/packages/common/graphql/src/graphql/index.ts b/packages/common/graphql/src/graphql/index.ts index 557a280566..dbf149fb90 100644 --- a/packages/common/graphql/src/graphql/index.ts +++ b/packages/common/graphql/src/graphql/index.ts @@ -6,6 +6,53 @@ export interface GraphQLQuery { file?: boolean; deprecations?: string[]; } +export const copilotChatMessageFragment = `fragment CopilotChatMessage on ChatMessage { + id + role + content + attachments + streamObjects { + type + textDelta + toolCallId + toolName + args + result + } + createdAt +}`; +export const copilotChatHistoryFragment = `fragment CopilotChatHistory on CopilotHistories { + sessionId + workspaceId + docId + parentSessionId + promptName + model + optionalModels + action + pinned + title + tokens + messages { + ...CopilotChatMessage + } + createdAt + updatedAt +}`; +export const paginatedCopilotChatsFragment = `fragment PaginatedCopilotChats on PaginatedCopilotHistoriesType { + pageInfo { + hasNextPage + hasPreviousPage + startCursor + endCursor + } + edges { + cursor + node { + ...CopilotChatHistory + } + } +}`; export const credentialsRequirementsFragment = `fragment CredentialsRequirements on CredentialsRequirementType { password { ...PasswordLimits @@ -762,16 +809,27 @@ export const queueWorkspaceEmbeddingMutation = { export const getCopilotHistoryIdsQuery = { id: 'getCopilotHistoryIdsQuery' as const, op: 'getCopilotHistoryIds', - query: `query getCopilotHistoryIds($workspaceId: String!, $docId: String, $options: QueryChatHistoriesInput) { + query: `query getCopilotHistoryIds($workspaceId: String!, $pagination: PaginationInput!, $docId: String, $options: QueryChatHistoriesInput) { currentUser { copilot(workspaceId: $workspaceId) { - histories(docId: $docId, options: $options) { - sessionId - pinned - messages { - id - role - createdAt + chats(pagination: $pagination, docId: $docId, options: $options) { + pageInfo { + hasNextPage + hasPreviousPage + startCursor + endCursor + } + edges { + cursor + node { + sessionId + pinned + messages { + id + role + createdAt + } + } } } } @@ -782,34 +840,18 @@ export const getCopilotHistoryIdsQuery = { export const getCopilotDocSessionsQuery = { id: 'getCopilotDocSessionsQuery' as const, op: 'getCopilotDocSessions', - query: `query getCopilotDocSessions($workspaceId: String!, $docId: String!, $options: QueryChatHistoriesInput) { + query: `query getCopilotDocSessions($workspaceId: String!, $docId: String!, $pagination: PaginationInput!, $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 - } + chats(pagination: $pagination, docId: $docId, options: $options) { + ...PaginatedCopilotChats } } } -}`, +} +${copilotChatMessageFragment} +${copilotChatHistoryFragment} +${paginatedCopilotChatsFragment}`, }; export const getCopilotPinnedSessionsQuery = { @@ -818,100 +860,53 @@ export const getCopilotPinnedSessionsQuery = { query: `query getCopilotPinnedSessions($workspaceId: String!, $docId: String, $messageOrder: ChatHistoryOrder, $withPrompt: Boolean) { currentUser { copilot(workspaceId: $workspaceId) { - histories( + chats( + pagination: {first: 1} docId: $docId - options: {limit: 1, pinned: true, messageOrder: $messageOrder, withPrompt: $withPrompt} + options: {pinned: true, messageOrder: $messageOrder, withPrompt: $withPrompt} ) { - sessionId - pinned - tokens - action - createdAt - messages { - id - role - content - streamObjects { - type - textDelta - toolCallId - toolName - args - result - } - attachments - createdAt - } + ...PaginatedCopilotChats } } } -}`, +} +${copilotChatMessageFragment} +${copilotChatHistoryFragment} +${paginatedCopilotChatsFragment}`, }; export const getCopilotWorkspaceSessionsQuery = { id: 'getCopilotWorkspaceSessionsQuery' as const, op: 'getCopilotWorkspaceSessions', - query: `query getCopilotWorkspaceSessions($workspaceId: String!, $options: QueryChatHistoriesInput) { + query: `query getCopilotWorkspaceSessions($workspaceId: String!, $pagination: PaginationInput!, $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 - } + chats(pagination: $pagination, docId: null, options: $options) { + ...PaginatedCopilotChats } } } -}`, +} +${copilotChatMessageFragment} +${copilotChatHistoryFragment} +${paginatedCopilotChatsFragment}`, }; export const getCopilotHistoriesQuery = { id: 'getCopilotHistoriesQuery' as const, op: 'getCopilotHistories', - query: `query getCopilotHistories($workspaceId: String!, $docId: String, $options: QueryChatHistoriesInput) { + query: `query getCopilotHistories($workspaceId: String!, $pagination: PaginationInput!, $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 - } + chats(pagination: $pagination, docId: $docId, options: $options) { + ...PaginatedCopilotChats } } } -}`, +} +${copilotChatMessageFragment} +${copilotChatHistoryFragment} +${paginatedCopilotChatsFragment}`, }; export const submitAudioTranscriptionMutation = { @@ -1039,30 +1034,19 @@ export const getCopilotLatestDocSessionQuery = { query: `query getCopilotLatestDocSession($workspaceId: String!, $docId: String!) { currentUser { copilot(workspaceId: $workspaceId) { - histories( + chats( + pagination: {first: 1} docId: $docId - options: {limit: 1, sessionOrder: desc, action: false, fork: false} + options: {sessionOrder: desc, action: false, fork: false, withMessages: true} ) { - sessionId - workspaceId - docId - pinned - action - tokens - createdAt - updatedAt - messages { - id - role - content - attachments - params - createdAt - } + ...PaginatedCopilotChats } } } -}`, +} +${copilotChatMessageFragment} +${copilotChatHistoryFragment} +${paginatedCopilotChatsFragment}`, }; export const getCopilotSessionQuery = { @@ -1071,19 +1055,15 @@ export const getCopilotSessionQuery = { query: `query getCopilotSession($workspaceId: String!, $sessionId: String!) { currentUser { copilot(workspaceId: $workspaceId) { - session(sessionId: $sessionId) { - id - parentSessionId - docId - pinned - title - promptName - model - optionalModels + chats(pagination: {first: 1}, options: {sessionId: $sessionId}) { + ...PaginatedCopilotChats } } } -}`, +} +${copilotChatMessageFragment} +${copilotChatHistoryFragment} +${paginatedCopilotChatsFragment}`, }; export const getCopilotRecentSessionsQuery = { @@ -1092,19 +1072,18 @@ export const getCopilotRecentSessionsQuery = { query: `query getCopilotRecentSessions($workspaceId: String!, $limit: Int = 10) { currentUser { copilot(workspaceId: $workspaceId) { - histories(options: {limit: $limit, sessionOrder: desc}) { - sessionId - workspaceId - docId - pinned - action - tokens - createdAt - updatedAt + chats( + pagination: {first: $limit} + options: {fork: false, sessionOrder: desc, withMessages: true} + ) { + ...PaginatedCopilotChats } } } -}`, +} +${copilotChatMessageFragment} +${copilotChatHistoryFragment} +${paginatedCopilotChatsFragment}`, }; export const updateCopilotSessionMutation = { @@ -1118,22 +1097,18 @@ export const updateCopilotSessionMutation = { export const getCopilotSessionsQuery = { id: 'getCopilotSessionsQuery' as const, op: 'getCopilotSessions', - query: `query getCopilotSessions($workspaceId: String!, $docId: String, $options: QueryChatSessionsInput) { + query: `query getCopilotSessions($workspaceId: String!, $pagination: PaginationInput!, $docId: String, $options: QueryChatHistoriesInput) { currentUser { copilot(workspaceId: $workspaceId) { - sessions(docId: $docId, options: $options) { - id - parentSessionId - docId - pinned - title - promptName - model - optionalModels + chats(pagination: $pagination, docId: $docId, options: $options) { + ...PaginatedCopilotChats } } } -}`, +} +${copilotChatMessageFragment} +${copilotChatHistoryFragment} +${paginatedCopilotChatsFragment}`, }; export const addWorkspaceEmbeddingFilesMutation = { diff --git a/packages/common/graphql/src/schema.ts b/packages/common/graphql/src/schema.ts index 1714e37f38..1d4348de01 100644 --- a/packages/common/graphql/src/schema.ts +++ b/packages/common/graphql/src/schema.ts @@ -245,14 +245,19 @@ export interface ContextWorkspaceEmbeddingStatus { export interface Copilot { __typename?: 'Copilot'; audioTranscription: Maybe; + chats: PaginatedCopilotHistoriesType; /** Get the context list of a session */ contexts: Array; + /** @deprecated use `chats` instead */ histories: Array; /** Get the quota of the user in the workspace */ quota: CopilotQuota; /** Get the session by id */ session: CopilotSessionType; - /** Get the session list in the workspace */ + /** + * Get the session list in the workspace + * @deprecated use `chats` instead + */ sessions: Array; workspaceId: Maybe; } @@ -262,6 +267,12 @@ export interface CopilotAudioTranscriptionArgs { jobId?: InputMaybe; } +export interface CopilotChatsArgs { + docId?: InputMaybe; + options?: InputMaybe; + pagination: PaginationInput; +} + export interface CopilotContextsArgs { contextId?: InputMaybe; sessionId?: InputMaybe; @@ -391,14 +402,25 @@ export interface CopilotHistories { createdAt: Scalars['DateTime']['output']; docId: Maybe; messages: Array; + model: Scalars['String']['output']; + optionalModels: Array; + parentSessionId: Maybe; pinned: Scalars['Boolean']['output']; + promptName: Scalars['String']['output']; sessionId: Scalars['String']['output']; + title: Maybe; /** The number of tokens used in the session */ tokens: Scalars['Int']['output']; updatedAt: Scalars['DateTime']['output']; workspaceId: Scalars['String']['output']; } +export interface CopilotHistoriesTypeEdge { + __typename?: 'CopilotHistoriesTypeEdge'; + cursor: Scalars['String']['output']; + node: CopilotHistories; +} + export interface CopilotInvalidContextDataType { __typename?: 'CopilotInvalidContextDataType'; contextId: Scalars['String']['output']; @@ -1954,6 +1976,13 @@ export interface PaginatedCommentObjectType { totalCount: Scalars['Int']['output']; } +export interface PaginatedCopilotHistoriesType { + __typename?: 'PaginatedCopilotHistoriesType'; + edges: Array; + pageInfo: PageInfo; + totalCount: Scalars['Int']['output']; +} + export interface PaginatedCopilotWorkspaceFileType { __typename?: 'PaginatedCopilotWorkspaceFileType'; edges: Array; @@ -2133,6 +2162,7 @@ export interface QueryChatHistoriesInput { sessionId?: InputMaybe; sessionOrder?: InputMaybe; skip?: InputMaybe; + withMessages?: InputMaybe; withPrompt?: InputMaybe; } @@ -3757,6 +3787,7 @@ export type QueueWorkspaceEmbeddingMutation = { export type GetCopilotHistoryIdsQueryVariables = Exact<{ workspaceId: Scalars['String']['input']; + pagination: PaginationInput; docId?: InputMaybe; options?: InputMaybe; }>; @@ -3767,17 +3798,31 @@ export type GetCopilotHistoryIdsQuery = { __typename?: 'UserType'; copilot: { __typename?: 'Copilot'; - histories: Array<{ - __typename?: 'CopilotHistories'; - sessionId: string; - pinned: boolean; - messages: Array<{ - __typename?: 'ChatMessage'; - id: string | null; - role: string; - createdAt: string; + chats: { + __typename?: 'PaginatedCopilotHistoriesType'; + pageInfo: { + __typename?: 'PageInfo'; + hasNextPage: boolean; + hasPreviousPage: boolean; + startCursor: string | null; + endCursor: string | null; + }; + edges: Array<{ + __typename?: 'CopilotHistoriesTypeEdge'; + cursor: string; + node: { + __typename?: 'CopilotHistories'; + sessionId: string; + pinned: boolean; + messages: Array<{ + __typename?: 'ChatMessage'; + id: string | null; + role: string; + createdAt: string; + }>; + }; }>; - }>; + }; }; } | null; }; @@ -3785,6 +3830,7 @@ export type GetCopilotHistoryIdsQuery = { export type GetCopilotDocSessionsQueryVariables = Exact<{ workspaceId: Scalars['String']['input']; docId: Scalars['String']['input']; + pagination: PaginationInput; options?: InputMaybe; }>; @@ -3794,31 +3840,53 @@ export type GetCopilotDocSessionsQuery = { __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 | null; - createdAt: string; - streamObjects: Array<{ - __typename?: 'StreamObject'; - type: string; - textDelta: string | null; - toolCallId: string | null; - toolName: string | null; - args: Record | null; - result: Record | null; - }> | null; + chats: { + __typename?: 'PaginatedCopilotHistoriesType'; + pageInfo: { + __typename?: 'PageInfo'; + hasNextPage: boolean; + hasPreviousPage: boolean; + startCursor: string | null; + endCursor: string | null; + }; + edges: Array<{ + __typename?: 'CopilotHistoriesTypeEdge'; + cursor: string; + node: { + __typename?: 'CopilotHistories'; + sessionId: string; + workspaceId: string; + docId: string | null; + parentSessionId: string | null; + promptName: string; + model: string; + optionalModels: Array; + action: string | null; + pinned: boolean; + title: string | null; + tokens: number; + createdAt: string; + updatedAt: string; + messages: Array<{ + __typename?: 'ChatMessage'; + id: string | null; + role: string; + content: string; + attachments: Array | null; + createdAt: string; + streamObjects: Array<{ + __typename?: 'StreamObject'; + type: string; + textDelta: string | null; + toolCallId: string | null; + toolName: string | null; + args: Record | null; + result: Record | null; + }> | null; + }>; + }; }>; - }>; + }; }; } | null; }; @@ -3836,37 +3904,60 @@ export type GetCopilotPinnedSessionsQuery = { __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 | null; - createdAt: string; - streamObjects: Array<{ - __typename?: 'StreamObject'; - type: string; - textDelta: string | null; - toolCallId: string | null; - toolName: string | null; - args: Record | null; - result: Record | null; - }> | null; + chats: { + __typename?: 'PaginatedCopilotHistoriesType'; + pageInfo: { + __typename?: 'PageInfo'; + hasNextPage: boolean; + hasPreviousPage: boolean; + startCursor: string | null; + endCursor: string | null; + }; + edges: Array<{ + __typename?: 'CopilotHistoriesTypeEdge'; + cursor: string; + node: { + __typename?: 'CopilotHistories'; + sessionId: string; + workspaceId: string; + docId: string | null; + parentSessionId: string | null; + promptName: string; + model: string; + optionalModels: Array; + action: string | null; + pinned: boolean; + title: string | null; + tokens: number; + createdAt: string; + updatedAt: string; + messages: Array<{ + __typename?: 'ChatMessage'; + id: string | null; + role: string; + content: string; + attachments: Array | null; + createdAt: string; + streamObjects: Array<{ + __typename?: 'StreamObject'; + type: string; + textDelta: string | null; + toolCallId: string | null; + toolName: string | null; + args: Record | null; + result: Record | null; + }> | null; + }>; + }; }>; - }>; + }; }; } | null; }; export type GetCopilotWorkspaceSessionsQueryVariables = Exact<{ workspaceId: Scalars['String']['input']; + pagination: PaginationInput; options?: InputMaybe; }>; @@ -3876,37 +3967,60 @@ export type GetCopilotWorkspaceSessionsQuery = { __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 | null; - createdAt: string; - streamObjects: Array<{ - __typename?: 'StreamObject'; - type: string; - textDelta: string | null; - toolCallId: string | null; - toolName: string | null; - args: Record | null; - result: Record | null; - }> | null; + chats: { + __typename?: 'PaginatedCopilotHistoriesType'; + pageInfo: { + __typename?: 'PageInfo'; + hasNextPage: boolean; + hasPreviousPage: boolean; + startCursor: string | null; + endCursor: string | null; + }; + edges: Array<{ + __typename?: 'CopilotHistoriesTypeEdge'; + cursor: string; + node: { + __typename?: 'CopilotHistories'; + sessionId: string; + workspaceId: string; + docId: string | null; + parentSessionId: string | null; + promptName: string; + model: string; + optionalModels: Array; + action: string | null; + pinned: boolean; + title: string | null; + tokens: number; + createdAt: string; + updatedAt: string; + messages: Array<{ + __typename?: 'ChatMessage'; + id: string | null; + role: string; + content: string; + attachments: Array | null; + createdAt: string; + streamObjects: Array<{ + __typename?: 'StreamObject'; + type: string; + textDelta: string | null; + toolCallId: string | null; + toolName: string | null; + args: Record | null; + result: Record | null; + }> | null; + }>; + }; }>; - }>; + }; }; } | null; }; export type GetCopilotHistoriesQueryVariables = Exact<{ workspaceId: Scalars['String']['input']; + pagination: PaginationInput; docId?: InputMaybe; options?: InputMaybe; }>; @@ -3917,31 +4031,53 @@ export type GetCopilotHistoriesQuery = { __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 | null; - createdAt: string; - streamObjects: Array<{ - __typename?: 'StreamObject'; - type: string; - textDelta: string | null; - toolCallId: string | null; - toolName: string | null; - args: Record | null; - result: Record | null; - }> | null; + chats: { + __typename?: 'PaginatedCopilotHistoriesType'; + pageInfo: { + __typename?: 'PageInfo'; + hasNextPage: boolean; + hasPreviousPage: boolean; + startCursor: string | null; + endCursor: string | null; + }; + edges: Array<{ + __typename?: 'CopilotHistoriesTypeEdge'; + cursor: string; + node: { + __typename?: 'CopilotHistories'; + sessionId: string; + workspaceId: string; + docId: string | null; + parentSessionId: string | null; + promptName: string; + model: string; + optionalModels: Array; + action: string | null; + pinned: boolean; + title: string | null; + tokens: number; + createdAt: string; + updatedAt: string; + messages: Array<{ + __typename?: 'ChatMessage'; + id: string | null; + role: string; + content: string; + attachments: Array | null; + createdAt: string; + streamObjects: Array<{ + __typename?: 'StreamObject'; + type: string; + textDelta: string | null; + toolCallId: string | null; + toolName: string | null; + args: Record | null; + result: Record | null; + }> | null; + }>; + }; }>; - }>; + }; }; } | null; }; @@ -4095,26 +4231,53 @@ export type GetCopilotLatestDocSessionQuery = { __typename?: 'UserType'; copilot: { __typename?: 'Copilot'; - histories: Array<{ - __typename?: 'CopilotHistories'; - sessionId: string; - workspaceId: string; - docId: string | null; - pinned: boolean; - action: string | null; - tokens: number; - createdAt: string; - updatedAt: string; - messages: Array<{ - __typename?: 'ChatMessage'; - id: string | null; - role: string; - content: string; - attachments: Array | null; - params: Record | null; - createdAt: string; + chats: { + __typename?: 'PaginatedCopilotHistoriesType'; + pageInfo: { + __typename?: 'PageInfo'; + hasNextPage: boolean; + hasPreviousPage: boolean; + startCursor: string | null; + endCursor: string | null; + }; + edges: Array<{ + __typename?: 'CopilotHistoriesTypeEdge'; + cursor: string; + node: { + __typename?: 'CopilotHistories'; + sessionId: string; + workspaceId: string; + docId: string | null; + parentSessionId: string | null; + promptName: string; + model: string; + optionalModels: Array; + action: string | null; + pinned: boolean; + title: string | null; + tokens: number; + createdAt: string; + updatedAt: string; + messages: Array<{ + __typename?: 'ChatMessage'; + id: string | null; + role: string; + content: string; + attachments: Array | null; + createdAt: string; + streamObjects: Array<{ + __typename?: 'StreamObject'; + type: string; + textDelta: string | null; + toolCallId: string | null; + toolName: string | null; + args: Record | null; + result: Record | null; + }> | null; + }>; + }; }>; - }>; + }; }; } | null; }; @@ -4130,16 +4293,52 @@ export type GetCopilotSessionQuery = { __typename?: 'UserType'; copilot: { __typename?: 'Copilot'; - session: { - __typename?: 'CopilotSessionType'; - id: string; - parentSessionId: string | null; - docId: string | null; - pinned: boolean; - title: string | null; - promptName: string; - model: string; - optionalModels: Array; + chats: { + __typename?: 'PaginatedCopilotHistoriesType'; + pageInfo: { + __typename?: 'PageInfo'; + hasNextPage: boolean; + hasPreviousPage: boolean; + startCursor: string | null; + endCursor: string | null; + }; + edges: Array<{ + __typename?: 'CopilotHistoriesTypeEdge'; + cursor: string; + node: { + __typename?: 'CopilotHistories'; + sessionId: string; + workspaceId: string; + docId: string | null; + parentSessionId: string | null; + promptName: string; + model: string; + optionalModels: Array; + action: string | null; + pinned: boolean; + title: string | null; + tokens: number; + createdAt: string; + updatedAt: string; + messages: Array<{ + __typename?: 'ChatMessage'; + id: string | null; + role: string; + content: string; + attachments: Array | null; + createdAt: string; + streamObjects: Array<{ + __typename?: 'StreamObject'; + type: string; + textDelta: string | null; + toolCallId: string | null; + toolName: string | null; + args: Record | null; + result: Record | null; + }> | null; + }>; + }; + }>; }; }; } | null; @@ -4156,17 +4355,53 @@ export type GetCopilotRecentSessionsQuery = { __typename?: 'UserType'; copilot: { __typename?: 'Copilot'; - histories: Array<{ - __typename?: 'CopilotHistories'; - sessionId: string; - workspaceId: string; - docId: string | null; - pinned: boolean; - action: string | null; - tokens: number; - createdAt: string; - updatedAt: string; - }>; + chats: { + __typename?: 'PaginatedCopilotHistoriesType'; + pageInfo: { + __typename?: 'PageInfo'; + hasNextPage: boolean; + hasPreviousPage: boolean; + startCursor: string | null; + endCursor: string | null; + }; + edges: Array<{ + __typename?: 'CopilotHistoriesTypeEdge'; + cursor: string; + node: { + __typename?: 'CopilotHistories'; + sessionId: string; + workspaceId: string; + docId: string | null; + parentSessionId: string | null; + promptName: string; + model: string; + optionalModels: Array; + action: string | null; + pinned: boolean; + title: string | null; + tokens: number; + createdAt: string; + updatedAt: string; + messages: Array<{ + __typename?: 'ChatMessage'; + id: string | null; + role: string; + content: string; + attachments: Array | null; + createdAt: string; + streamObjects: Array<{ + __typename?: 'StreamObject'; + type: string; + textDelta: string | null; + toolCallId: string | null; + toolName: string | null; + args: Record | null; + result: Record | null; + }> | null; + }>; + }; + }>; + }; }; } | null; }; @@ -4182,8 +4417,9 @@ export type UpdateCopilotSessionMutation = { export type GetCopilotSessionsQueryVariables = Exact<{ workspaceId: Scalars['String']['input']; + pagination: PaginationInput; docId?: InputMaybe; - options?: InputMaybe; + options?: InputMaybe; }>; export type GetCopilotSessionsQuery = { @@ -4192,17 +4428,53 @@ export type GetCopilotSessionsQuery = { __typename?: 'UserType'; copilot: { __typename?: 'Copilot'; - sessions: Array<{ - __typename?: 'CopilotSessionType'; - id: string; - parentSessionId: string | null; - docId: string | null; - pinned: boolean; - title: string | null; - promptName: string; - model: string; - optionalModels: Array; - }>; + chats: { + __typename?: 'PaginatedCopilotHistoriesType'; + pageInfo: { + __typename?: 'PageInfo'; + hasNextPage: boolean; + hasPreviousPage: boolean; + startCursor: string | null; + endCursor: string | null; + }; + edges: Array<{ + __typename?: 'CopilotHistoriesTypeEdge'; + cursor: string; + node: { + __typename?: 'CopilotHistories'; + sessionId: string; + workspaceId: string; + docId: string | null; + parentSessionId: string | null; + promptName: string; + model: string; + optionalModels: Array; + action: string | null; + pinned: boolean; + title: string | null; + tokens: number; + createdAt: string; + updatedAt: string; + messages: Array<{ + __typename?: 'ChatMessage'; + id: string | null; + role: string; + content: string; + attachments: Array | null; + createdAt: string; + streamObjects: Array<{ + __typename?: 'StreamObject'; + type: string; + textDelta: string | null; + toolCallId: string | null; + toolName: string | null; + args: Record | null; + result: Record | null; + }> | null; + }>; + }; + }>; + }; }; } | null; }; @@ -4438,6 +4710,106 @@ export type GetDocRolePermissionsQuery = { }; }; +export type CopilotChatMessageFragment = { + __typename?: 'ChatMessage'; + id: string | null; + role: string; + content: string; + attachments: Array | null; + createdAt: string; + streamObjects: Array<{ + __typename?: 'StreamObject'; + type: string; + textDelta: string | null; + toolCallId: string | null; + toolName: string | null; + args: Record | null; + result: Record | null; + }> | null; +}; + +export type CopilotChatHistoryFragment = { + __typename?: 'CopilotHistories'; + sessionId: string; + workspaceId: string; + docId: string | null; + parentSessionId: string | null; + promptName: string; + model: string; + optionalModels: Array; + action: string | null; + pinned: boolean; + title: string | null; + tokens: number; + createdAt: string; + updatedAt: string; + messages: Array<{ + __typename?: 'ChatMessage'; + id: string | null; + role: string; + content: string; + attachments: Array | null; + createdAt: string; + streamObjects: Array<{ + __typename?: 'StreamObject'; + type: string; + textDelta: string | null; + toolCallId: string | null; + toolName: string | null; + args: Record | null; + result: Record | null; + }> | null; + }>; +}; + +export type PaginatedCopilotChatsFragment = { + __typename?: 'PaginatedCopilotHistoriesType'; + pageInfo: { + __typename?: 'PageInfo'; + hasNextPage: boolean; + hasPreviousPage: boolean; + startCursor: string | null; + endCursor: string | null; + }; + edges: Array<{ + __typename?: 'CopilotHistoriesTypeEdge'; + cursor: string; + node: { + __typename?: 'CopilotHistories'; + sessionId: string; + workspaceId: string; + docId: string | null; + parentSessionId: string | null; + promptName: string; + model: string; + optionalModels: Array; + action: string | null; + pinned: boolean; + title: string | null; + tokens: number; + createdAt: string; + updatedAt: string; + messages: Array<{ + __typename?: 'ChatMessage'; + id: string | null; + role: string; + content: string; + attachments: Array | null; + createdAt: string; + streamObjects: Array<{ + __typename?: 'StreamObject'; + type: string; + textDelta: string | null; + toolCallId: string | null; + toolName: string | null; + args: Record | null; + result: Record | null; + }> | null; + }>; + }; + }>; +}; + export type CredentialsRequirementsFragment = { __typename?: 'CredentialsRequirementType'; password: { diff --git a/packages/frontend/core/src/blocksuite/ai/actions/types.ts b/packages/frontend/core/src/blocksuite/ai/actions/types.ts index 87a2b6208e..8de72a3650 100644 --- a/packages/frontend/core/src/blocksuite/ai/actions/types.ts +++ b/packages/frontend/core/src/blocksuite/ai/actions/types.ts @@ -2,13 +2,13 @@ import type { ContextMatchedDocChunk, ContextMatchedFileChunk, ContextWorkspaceEmbeddingStatus, + CopilotChatHistoryFragment, CopilotContextCategory, CopilotContextDoc, CopilotContextFile, CopilotHistories, - CopilotSessionType, getCopilotHistoriesQuery, - QueryChatSessionsInput, + QueryChatHistoriesInput, RequestOptions, StreamObject, UpdateChatSessionInput, @@ -356,10 +356,10 @@ declare global { interface AIHistory { sessionId: string; tokens: number; - action: string; + action: string | null; createdAt: string; messages: { - id: string; // message id + id: string | null; // message id content: string; createdAt: string; role: MessageRole; @@ -392,19 +392,19 @@ declare global { interface AISessionService { createSession: (options: AICreateSessionOptions) => Promise; + getSession: ( + workspaceId: string, + sessionId: string + ) => Promise; getSessions: ( workspaceId: string, docId?: string, - options?: QueryChatSessionsInput - ) => Promise; + options?: QueryChatHistoriesInput + ) => Promise; getRecentSessions: ( workspaceId: string, limit?: number ) => Promise; - getSession: ( - workspaceId: string, - sessionId: string - ) => Promise; updateSession: (options: UpdateChatSessionInput) => Promise; } diff --git a/packages/frontend/core/src/blocksuite/ai/chat-panel/index.ts b/packages/frontend/core/src/blocksuite/ai/chat-panel/index.ts index 5797aad916..09563a0c59 100644 --- a/packages/frontend/core/src/blocksuite/ai/chat-panel/index.ts +++ b/packages/frontend/core/src/blocksuite/ai/chat-panel/index.ts @@ -3,7 +3,7 @@ import type { FeatureFlagService } from '@affine/core/modules/feature-flag'; import type { WorkbenchService } from '@affine/core/modules/workbench'; import type { ContextEmbedStatus, - CopilotSessionType, + CopilotChatHistoryFragment, UpdateChatSessionInput, } from '@affine/graphql'; import { SignalWatcher, WithDisposable } from '@blocksuite/affine/global/lit'; @@ -103,7 +103,7 @@ export class ChatPanel extends SignalWatcher( accessor affineWorkbenchService!: WorkbenchService; @state() - accessor session: CopilotSessionType | null | undefined; + accessor session: CopilotChatHistoryFragment | null | undefined; @state() accessor embeddingProgress: [number, number] = [0, 0]; @@ -170,7 +170,7 @@ export class ChatPanel extends SignalWatcher( }; private readonly setSession = ( - session: CopilotSessionType | null | undefined + session: CopilotChatHistoryFragment | null | undefined ) => { this.session = session ?? null; }; @@ -250,7 +250,7 @@ export class ChatPanel extends SignalWatcher( }; private readonly openSession = async (sessionId: string) => { - if (this.session?.id === sessionId) { + if (this.session?.sessionId === sessionId) { return; } this.resetPanel(); @@ -263,7 +263,7 @@ export class ChatPanel extends SignalWatcher( private readonly openDoc = async (docId: string, sessionId: string) => { if (this.doc.id === docId) { - if (this.session?.id === sessionId || this.session?.pinned) { + if (this.session?.sessionId === sessionId || this.session?.pinned) { return; } await this.openSession(sessionId); @@ -284,7 +284,7 @@ export class ChatPanel extends SignalWatcher( await this.createSession({ pinned }); } else { await this.updateSession({ - sessionId: this.session.id, + sessionId: this.session.sessionId, pinned, }); } @@ -296,7 +296,7 @@ export class ChatPanel extends SignalWatcher( } if (this.session.docId !== this.doc.id) { await this.updateSession({ - sessionId: this.session.id, + sessionId: this.session.sessionId, docId: this.doc.id, }); } @@ -399,7 +399,7 @@ export class ChatPanel extends SignalWatcher( return html`
${keyed( - this.hasPinned ? this.session?.id : this.doc.id, + this.hasPinned ? this.session?.sessionId : this.doc.id, html` void; diff --git a/packages/frontend/core/src/blocksuite/ai/components/ai-chat-composer/ai-chat-composer.ts b/packages/frontend/core/src/blocksuite/ai/components/ai-chat-composer/ai-chat-composer.ts index c4ec0a644c..12625835e5 100644 --- a/packages/frontend/core/src/blocksuite/ai/components/ai-chat-composer/ai-chat-composer.ts +++ b/packages/frontend/core/src/blocksuite/ai/components/ai-chat-composer/ai-chat-composer.ts @@ -4,10 +4,10 @@ import type { WorkspaceDialogService } from '@affine/core/modules/dialogs'; import type { ContextEmbedStatus, ContextWorkspaceEmbeddingStatus, + CopilotChatHistoryFragment, CopilotContextDoc, CopilotContextFile, CopilotDocType, - CopilotSessionType, } from '@affine/graphql'; import { SignalWatcher, WithDisposable } from '@blocksuite/affine/global/lit'; import type { EditorHost } from '@blocksuite/affine/std'; @@ -63,10 +63,12 @@ export class AIChatComposer extends SignalWatcher( accessor docId: string | undefined; @property({ attribute: false }) - accessor session!: CopilotSessionType | null | undefined; + accessor session!: CopilotChatHistoryFragment | null | undefined; @property({ attribute: false }) - accessor createSession!: () => Promise; + accessor createSession!: () => Promise< + CopilotChatHistoryFragment | undefined + >; @property({ attribute: false }) accessor chatContextValue!: AIChatInputContext; @@ -178,7 +180,7 @@ export class AIChatComposer extends SignalWatcher( return this._contextId; } - const sessionId = this.session?.id; + const sessionId = this.session?.sessionId; if (!sessionId) return; const contextId = await AIProvider.context?.getContextId( @@ -194,7 +196,7 @@ export class AIChatComposer extends SignalWatcher( return this._contextId; } - const sessionId = (await this.createSession())?.id; + const sessionId = (await this.createSession())?.sessionId; if (!sessionId) return; this._contextId = await AIProvider.context?.createContext( @@ -206,7 +208,7 @@ export class AIChatComposer extends SignalWatcher( private readonly _initChips = async () => { // context not initialized - const sessionId = this.session?.id; + const sessionId = this.session?.sessionId; const contextId = await this._getContextId(); if (!sessionId || !contextId) { return; @@ -282,7 +284,7 @@ export class AIChatComposer extends SignalWatcher( }; private readonly _pollContextDocsAndFiles = async () => { - const sessionId = this.session?.id; + const sessionId = this.session?.sessionId; const contextId = await this._getContextId(); if (!sessionId || !contextId || !AIProvider.context) { return; diff --git a/packages/frontend/core/src/blocksuite/ai/components/ai-chat-content/ai-chat-content.ts b/packages/frontend/core/src/blocksuite/ai/components/ai-chat-content/ai-chat-content.ts index 67832613be..507aa3d2c0 100644 --- a/packages/frontend/core/src/blocksuite/ai/components/ai-chat-content/ai-chat-content.ts +++ b/packages/frontend/core/src/blocksuite/ai/components/ai-chat-content/ai-chat-content.ts @@ -1,6 +1,9 @@ import type { WorkspaceDialogService } from '@affine/core/modules/dialogs'; import type { FeatureFlagService } from '@affine/core/modules/feature-flag'; -import type { ContextEmbedStatus, CopilotSessionType } from '@affine/graphql'; +import type { + ContextEmbedStatus, + CopilotChatHistoryFragment, +} from '@affine/graphql'; import { SignalWatcher, WithDisposable } from '@blocksuite/affine/global/lit'; import type { EditorHost } from '@blocksuite/affine/std'; import { ShadowlessElement } from '@blocksuite/affine/std'; @@ -123,10 +126,12 @@ export class AIChatContent extends SignalWatcher( accessor host: EditorHost | null | undefined; @property({ attribute: false }) - accessor session!: CopilotSessionType | null | undefined; + accessor session!: CopilotChatHistoryFragment | null | undefined; @property({ attribute: false }) - accessor createSession!: () => Promise; + accessor createSession!: () => Promise< + CopilotChatHistoryFragment | undefined + >; @property({ attribute: false }) accessor workspaceId!: string; @@ -214,7 +219,7 @@ export class AIChatContent extends SignalWatcher( return; } - const sessionId = this.session?.id; + const sessionId = this.session?.sessionId; const [histories, actions] = await Promise.all([ sessionId ? AIProvider.histories.chats(this.workspaceId, sessionId) diff --git a/packages/frontend/core/src/blocksuite/ai/components/ai-chat-input/ai-chat-input.ts b/packages/frontend/core/src/blocksuite/ai/components/ai-chat-input/ai-chat-input.ts index 0561ce95ed..46664e90f1 100644 --- a/packages/frontend/core/src/blocksuite/ai/components/ai-chat-input/ai-chat-input.ts +++ b/packages/frontend/core/src/blocksuite/ai/components/ai-chat-input/ai-chat-input.ts @@ -1,5 +1,5 @@ import { toast } from '@affine/component'; -import type { CopilotSessionType } from '@affine/graphql'; +import type { CopilotChatHistoryFragment } from '@affine/graphql'; import { SignalWatcher, WithDisposable } from '@blocksuite/affine/global/lit'; import { unsafeCSSVar, unsafeCSSVarV2 } from '@blocksuite/affine/shared/theme'; import { openFilesWith } from '@blocksuite/affine/shared/utils'; @@ -304,7 +304,7 @@ export class AIChatInput extends SignalWatcher( accessor docId: string | undefined; @property({ attribute: false }) - accessor session!: CopilotSessionType | null | undefined; + accessor session!: CopilotChatHistoryFragment | null | undefined; @query('image-preview-grid') accessor imagePreviewGrid: HTMLDivElement | null = null; @@ -328,7 +328,9 @@ export class AIChatInput extends SignalWatcher( accessor chips: ChatChip[] = []; @property({ attribute: false }) - accessor createSession!: () => Promise; + accessor createSession!: () => Promise< + CopilotChatHistoryFragment | undefined + >; @property({ attribute: false }) accessor updateContext!: (context: Partial) => void; @@ -608,7 +610,7 @@ export class AIChatInput extends SignalWatcher( // optimistic update messages await this._preUpdateMessages(userInput, attachments); - const sessionId = (await this.createSession())?.id; + const sessionId = (await this.createSession())?.sessionId; let contexts = await this._getMatchedContexts(); if (abortController.signal.aborted) { return; @@ -694,7 +696,7 @@ export class AIChatInput extends SignalWatcher( }; private readonly _postUpdateMessages = async () => { - const sessionId = this.session?.id; + const sessionId = this.session?.sessionId; if (!sessionId || !AIProvider.histories) return; const { messages } = this.chatContextValue; @@ -703,7 +705,7 @@ export class AIChatInput extends SignalWatcher( const historyIds = await AIProvider.histories.ids( this.workspaceId, this.docId, - { sessionId } + { sessionId, withMessages: true } ); if (!historyIds || !historyIds[0]) return; last.id = historyIds[0].messages.at(-1)?.id ?? ''; diff --git a/packages/frontend/core/src/blocksuite/ai/components/ai-chat-input/preference-popup.ts b/packages/frontend/core/src/blocksuite/ai/components/ai-chat-input/preference-popup.ts index 6c940ee683..a72686f4a6 100644 --- a/packages/frontend/core/src/blocksuite/ai/components/ai-chat-input/preference-popup.ts +++ b/packages/frontend/core/src/blocksuite/ai/components/ai-chat-input/preference-popup.ts @@ -1,4 +1,4 @@ -import type { CopilotSessionType } from '@affine/graphql'; +import type { CopilotChatHistoryFragment } from '@affine/graphql'; import { menu, popMenu, @@ -49,7 +49,7 @@ export class ChatInputPreference extends SignalWatcher( `; @property({ attribute: false }) - accessor session!: CopilotSessionType | null | undefined; + accessor session!: CopilotChatHistoryFragment | null | undefined; @property({ attribute: false }) accessor onModelChange: ((modelId: string) => void) | undefined; diff --git a/packages/frontend/core/src/blocksuite/ai/components/ai-chat-messages/ai-chat-messages.ts b/packages/frontend/core/src/blocksuite/ai/components/ai-chat-messages/ai-chat-messages.ts index b45358a6ae..7f7adcfef9 100644 --- a/packages/frontend/core/src/blocksuite/ai/components/ai-chat-messages/ai-chat-messages.ts +++ b/packages/frontend/core/src/blocksuite/ai/components/ai-chat-messages/ai-chat-messages.ts @@ -1,4 +1,4 @@ -import type { CopilotSessionType } from '@affine/graphql'; +import type { CopilotChatHistoryFragment } from '@affine/graphql'; import { WithDisposable } from '@blocksuite/affine/global/lit'; import { DocModeProvider, @@ -171,10 +171,12 @@ export class AIChatMessages extends WithDisposable(ShadowlessElement) { accessor chatContextValue!: ChatContextValue; @property({ attribute: false }) - accessor session!: CopilotSessionType | null | undefined; + accessor session!: CopilotChatHistoryFragment | null | undefined; @property({ attribute: false }) - accessor createSession!: () => Promise; + accessor createSession!: () => Promise< + CopilotChatHistoryFragment | undefined + >; @property({ attribute: false }) accessor updateContext!: (context: Partial) => void; @@ -418,7 +420,7 @@ export class AIChatMessages extends WithDisposable(ShadowlessElement) { retry = async () => { try { - const sessionId = (await this.createSession())?.id; + const sessionId = (await this.createSession())?.sessionId; if (!sessionId) return; if (!AIProvider.actions.chat) return; diff --git a/packages/frontend/core/src/blocksuite/ai/components/ai-chat-toolbar/ai-chat-toolbar.ts b/packages/frontend/core/src/blocksuite/ai/components/ai-chat-toolbar/ai-chat-toolbar.ts index ebeef8456e..d5c959aa82 100644 --- a/packages/frontend/core/src/blocksuite/ai/components/ai-chat-toolbar/ai-chat-toolbar.ts +++ b/packages/frontend/core/src/blocksuite/ai/components/ai-chat-toolbar/ai-chat-toolbar.ts @@ -1,4 +1,4 @@ -import type { CopilotSessionType } from '@affine/graphql'; +import type { CopilotChatHistoryFragment } from '@affine/graphql'; import { createLitPortal } from '@blocksuite/affine/components/portal'; import { WithDisposable } from '@blocksuite/affine/global/lit'; import type { NotificationService } from '@blocksuite/affine/shared/services'; @@ -18,7 +18,7 @@ import type { DocDisplayConfig } from '../ai-chat-chips'; export class AIChatToolbar extends WithDisposable(ShadowlessElement) { @property({ attribute: false }) - accessor session!: CopilotSessionType | null | undefined; + accessor session!: CopilotChatHistoryFragment | null | undefined; @property({ attribute: false }) accessor workspaceId!: string; @@ -132,7 +132,7 @@ export class AIChatToolbar extends WithDisposable(ShadowlessElement) { }; private readonly onSessionClick = async (sessionId: string) => { - if (this.session?.id === sessionId) { + if (this.session?.sessionId === sessionId) { this.notification?.toast('You are already in this chat'); return; } @@ -143,7 +143,7 @@ export class AIChatToolbar extends WithDisposable(ShadowlessElement) { }; private readonly onDocClick = async (docId: string, sessionId: string) => { - if (this.docId === docId && this.session?.id === sessionId) { + if (this.docId === docId && this.session?.sessionId === sessionId) { this.notification?.toast('You are already in this chat'); return; } diff --git a/packages/frontend/core/src/blocksuite/ai/components/ai-history-clear/ai-history-clear.ts b/packages/frontend/core/src/blocksuite/ai/components/ai-history-clear/ai-history-clear.ts index c36aa294b8..ae868775e7 100644 --- a/packages/frontend/core/src/blocksuite/ai/components/ai-history-clear/ai-history-clear.ts +++ b/packages/frontend/core/src/blocksuite/ai/components/ai-history-clear/ai-history-clear.ts @@ -1,4 +1,4 @@ -import type { CopilotSessionType } from '@affine/graphql'; +import type { CopilotChatHistoryFragment } from '@affine/graphql'; import { WithDisposable } from '@blocksuite/affine/global/lit'; import { type NotificationService } from '@blocksuite/affine/shared/services'; import { unsafeCSSVarV2 } from '@blocksuite/affine/shared/theme'; @@ -15,7 +15,7 @@ export class AIHistoryClear extends WithDisposable(ShadowlessElement) { accessor chatContextValue!: ChatContextValue; @property({ attribute: false }) - accessor session!: CopilotSessionType | null | undefined; + accessor session!: CopilotChatHistoryFragment | null | undefined; @property({ attribute: false }) accessor notification: NotificationService | null | undefined; @@ -50,7 +50,7 @@ export class AIHistoryClear extends WithDisposable(ShadowlessElement) { if (this._isHistoryClearDisabled || !this.session) { return; } - const sessionId = this.session.id; + const sessionId = this.session.sessionId; try { const confirm = this.notification ? await this.notification.confirm({ diff --git a/packages/frontend/core/src/blocksuite/ai/components/chat-action-list.ts b/packages/frontend/core/src/blocksuite/ai/components/chat-action-list.ts index ab24a79e05..14233e6495 100644 --- a/packages/frontend/core/src/blocksuite/ai/components/chat-action-list.ts +++ b/packages/frontend/core/src/blocksuite/ai/components/chat-action-list.ts @@ -1,4 +1,4 @@ -import type { CopilotSessionType } from '@affine/graphql'; +import type { CopilotChatHistoryFragment } from '@affine/graphql'; import type { ImageSelection } from '@blocksuite/affine/shared/selection'; import { NotificationProvider } from '@blocksuite/affine/shared/services'; import type { @@ -82,7 +82,7 @@ export class ChatActionList extends LitElement { accessor content: string = ''; @property({ attribute: false }) - accessor session!: CopilotSessionType | null | undefined; + accessor session!: CopilotChatHistoryFragment | null | undefined; @property({ attribute: false }) accessor messageId: string | undefined = undefined; @@ -139,7 +139,7 @@ export class ChatActionList extends LitElement { blocks: this._currentBlockSelections, images: this._currentImageSelections, }; - const sessionId = this.session?.id; + const sessionId = this.session?.sessionId; const success = await action.handler( host, content, diff --git a/packages/frontend/core/src/blocksuite/ai/components/copy-more.ts b/packages/frontend/core/src/blocksuite/ai/components/copy-more.ts index f1ed18eb1b..7e8ddb0a50 100644 --- a/packages/frontend/core/src/blocksuite/ai/components/copy-more.ts +++ b/packages/frontend/core/src/blocksuite/ai/components/copy-more.ts @@ -1,4 +1,4 @@ -import type { CopilotSessionType } from '@affine/graphql'; +import type { CopilotChatHistoryFragment } from '@affine/graphql'; import { Tooltip } from '@blocksuite/affine/components/toolbar'; import { WithDisposable } from '@blocksuite/affine/global/lit'; import { noop } from '@blocksuite/affine/global/utils'; @@ -111,7 +111,7 @@ export class ChatCopyMore extends WithDisposable(LitElement) { accessor actions: ChatAction[] = []; @property({ attribute: false }) - accessor session!: CopilotSessionType | null | undefined; + accessor session!: CopilotChatHistoryFragment | null | undefined; @property({ attribute: false }) accessor content!: string; @@ -224,7 +224,7 @@ export class ChatCopyMore extends WithDisposable(LitElement) { }; return html`
{ - const sessionId = this.session?.id; + const sessionId = this.session?.sessionId; const success = await action.handler( host, content, diff --git a/packages/frontend/core/src/blocksuite/ai/components/playground/chat.ts b/packages/frontend/core/src/blocksuite/ai/components/playground/chat.ts index e0a68ee269..a87a1b7a34 100644 --- a/packages/frontend/core/src/blocksuite/ai/components/playground/chat.ts +++ b/packages/frontend/core/src/blocksuite/ai/components/playground/chat.ts @@ -1,5 +1,8 @@ import type { FeatureFlagService } from '@affine/core/modules/feature-flag'; -import type { ContextEmbedStatus, CopilotSessionType } from '@affine/graphql'; +import type { + ContextEmbedStatus, + CopilotChatHistoryFragment, +} from '@affine/graphql'; import { SignalWatcher, WithDisposable } from '@blocksuite/affine/global/lit'; import { NotificationProvider } from '@blocksuite/affine/shared/services'; import { unsafeCSSVarV2 } from '@blocksuite/affine/shared/theme'; @@ -130,7 +133,7 @@ export class PlaygroundChat extends SignalWatcher( accessor doc!: Store; @property({ attribute: false }) - accessor session!: CopilotSessionType | null | undefined; + accessor session!: CopilotChatHistoryFragment | null | undefined; @property({ attribute: false }) accessor networkSearchConfig!: AINetworkSearchConfig; @@ -194,7 +197,7 @@ export class PlaygroundChat extends SignalWatcher( const currentRequest = ++this._updateHistoryCounter; - const sessionId = this.session?.id; + const sessionId = this.session?.sessionId; const [histories, actions] = await Promise.all([ sessionId ? AIProvider.histories.chats( diff --git a/packages/frontend/core/src/blocksuite/ai/components/playground/content.ts b/packages/frontend/core/src/blocksuite/ai/components/playground/content.ts index 2dc48d0ec2..7de6602fed 100644 --- a/packages/frontend/core/src/blocksuite/ai/components/playground/content.ts +++ b/packages/frontend/core/src/blocksuite/ai/components/playground/content.ts @@ -1,5 +1,5 @@ import type { FeatureFlagService } from '@affine/core/modules/feature-flag'; -import type { CopilotSessionType } from '@affine/graphql'; +import type { CopilotChatHistoryFragment } from '@affine/graphql'; import { SignalWatcher, WithDisposable } from '@blocksuite/affine/global/lit'; import type { EditorHost } from '@blocksuite/affine/std'; import { ShadowlessElement } from '@blocksuite/affine/std'; @@ -84,7 +84,7 @@ export class PlaygroundContent extends SignalWatcher( accessor affineFeatureFlagService!: FeatureFlagService; @state() - accessor sessions: CopilotSessionType[] = []; + accessor sessions: CopilotChatHistoryFragment[] = []; @state() accessor sharedInputValue: string = ''; @@ -118,14 +118,14 @@ export class PlaygroundContent extends SignalWatcher( } } } else { - this.rootSessionId = rootSession.id; + this.rootSessionId = rootSession.sessionId; const childSessions = sessions.filter( - session => session.parentSessionId === rootSession.id + session => session.parentSessionId === rootSession.sessionId ); if (childSessions.length > 0) { this.sessions = childSessions; } else { - const forkSession = await this.forkSession(rootSession.id); + const forkSession = await this.forkSession(rootSession.sessionId); if (forkSession) { this.sessions = [forkSession]; } @@ -321,7 +321,7 @@ export class PlaygroundContent extends SignalWatcher(
${repeat( this.sessions, - session => session.id, + session => session.sessionId, session => html`
{ - const forkSessionId = this.forkSession?.id; + const forkSessionId = this.forkSession?.sessionId; if (!this._forkBlockId || !forkSessionId) { return; } @@ -360,7 +363,7 @@ export class AIChatBlockPeekView extends LitElement { */ retry = async () => { try { - const forkSessionId = this.forkSession?.id; + const forkSessionId = this.forkSession?.sessionId; if (!this._forkBlockId || !forkSessionId) return; if (!AIProvider.actions.chat) return; @@ -656,10 +659,10 @@ export class AIChatBlockPeekView extends LitElement { accessor embeddingProgress: [number, number] = [0, 0]; @state() - accessor session: CopilotSessionType | null | undefined; + accessor session: CopilotChatHistoryFragment | null | undefined; @state() - accessor forkSession: CopilotSessionType | null | undefined; + accessor forkSession: CopilotChatHistoryFragment | null | undefined; } declare global { diff --git a/packages/frontend/core/src/blocksuite/ai/provider/copilot-client.ts b/packages/frontend/core/src/blocksuite/ai/provider/copilot-client.ts index 140c589480..2866d00386 100644 --- a/packages/frontend/core/src/blocksuite/ai/provider/copilot-client.ts +++ b/packages/frontend/core/src/blocksuite/ai/provider/copilot-client.ts @@ -19,6 +19,7 @@ import { listContextObjectQuery, listContextQuery, matchContextQuery, + type PaginationInput, type QueryOptions, type QueryResponse, removeContextCategoryMutation, @@ -152,7 +153,7 @@ export class CopilotClient { query: getCopilotSessionQuery, variables: { sessionId, workspaceId }, }); - return res.currentUser?.copilot?.session; + return res.currentUser?.copilot?.chats?.edges?.[0]?.node; } catch (err) { throw resolveError(err); } @@ -160,6 +161,7 @@ export class CopilotClient { async getSessions( workspaceId: string, + pagination: PaginationInput, docId?: string, options?: RequestOptions< typeof getCopilotSessionsQuery @@ -170,11 +172,12 @@ export class CopilotClient { query: getCopilotSessionsQuery, variables: { workspaceId, + pagination, docId, options, }, }); - return res.currentUser?.copilot?.sessions; + return res.currentUser?.copilot?.chats.edges.map(e => e.node); } catch (err) { throw resolveError(err); } @@ -189,7 +192,7 @@ export class CopilotClient { limit, }, }); - return res.currentUser?.copilot?.histories; + return res.currentUser?.copilot?.chats.edges.map(e => e.node); } catch (err) { throw resolveError(err); } @@ -197,6 +200,7 @@ export class CopilotClient { async getHistories( workspaceId: string, + pagination: PaginationInput, docId?: string, options?: RequestOptions< typeof getCopilotHistoriesQuery @@ -207,12 +211,13 @@ export class CopilotClient { query: getCopilotHistoriesQuery, variables: { workspaceId, + pagination, docId, options, }, }); - return res.currentUser?.copilot?.histories; + return res.currentUser?.copilot?.chats.edges.map(e => e.node); } catch (err) { throw resolveError(err); } @@ -220,9 +225,10 @@ export class CopilotClient { async getHistoryIds( workspaceId: string, + pagination: PaginationInput, docId?: string, options?: RequestOptions< - typeof getCopilotHistoriesQuery + typeof getCopilotHistoryIdsQuery >['variables']['options'] ) { try { @@ -230,12 +236,13 @@ export class CopilotClient { query: getCopilotHistoryIdsQuery, variables: { workspaceId, + pagination, docId, options, }, }); - return res.currentUser?.copilot?.histories; + return res.currentUser?.copilot?.chats.edges.map(e => e.node); } catch (err) { throw resolveError(err); } diff --git a/packages/frontend/core/src/blocksuite/ai/provider/setup-provider.tsx b/packages/frontend/core/src/blocksuite/ai/provider/setup-provider.tsx index 2c26f4dbbe..8b6713ef1d 100644 --- a/packages/frontend/core/src/blocksuite/ai/provider/setup-provider.tsx +++ b/packages/frontend/core/src/blocksuite/ai/provider/setup-provider.tsx @@ -586,7 +586,7 @@ Could you make a new website based on these notes and send back just the html fi docId?: string, options?: QueryChatSessionsInput ) => { - return client.getSessions(workspaceId, docId, options); + return client.getSessions(workspaceId, {}, docId, options); }, getRecentSessions: async (workspaceId: string, limit?: number) => { return client.getRecentSessions(workspaceId, limit); @@ -744,9 +744,10 @@ Could you make a new website based on these notes and send back just the html fi ): Promise => { // @ts-expect-error - 'action' is missing in server impl return ( - (await client.getHistories(workspaceId, docId, { + (await client.getHistories(workspaceId, {}, docId, { action: true, withPrompt: true, + withMessages: true, })) ?? [] ); }, @@ -757,8 +758,9 @@ Could you make a new website based on these notes and send back just the html fi ): Promise => { // @ts-expect-error - 'action' is missing in server impl return ( - (await client.getHistories(workspaceId, docId, { + (await client.getHistories(workspaceId, {}, docId, { sessionId, + withMessages: true, })) ?? [] ); }, @@ -776,8 +778,8 @@ Could you make a new website based on these notes and send back just the html fi typeof getCopilotHistoriesQuery >['variables']['options'] ): Promise => { - // @ts-expect-error - 'role' is missing type in server impl - return await client.getHistoryIds(workspaceId, docId, options); + // @ts-expect-error - 'action' is missing in server impl + return await client.getHistoryIds(workspaceId, {}, docId, options); }, }); diff --git a/packages/frontend/core/src/desktop/pages/workspace/chat/index.tsx b/packages/frontend/core/src/desktop/pages/workspace/chat/index.tsx index f72c5323f1..c10c97c31a 100644 --- a/packages/frontend/core/src/desktop/pages/workspace/chat/index.tsx +++ b/packages/frontend/core/src/desktop/pages/workspace/chat/index.tsx @@ -98,11 +98,14 @@ export const Component = () => { await createSession({ pinned }); } else { await client.updateSession({ - sessionId: currentSession.id, + sessionId: currentSession.sessionId, pinned, }); // retrieve the latest session and update the state - const session = await client.getSession(workspaceId, currentSession.id); + const session = await client.getSession( + workspaceId, + currentSession.sessionId + ); setCurrentSession(session); } } finally {