diff --git a/packages/backend/server/src/plugins/copilot/prompt/prompts.ts b/packages/backend/server/src/plugins/copilot/prompt/prompts.ts index 1679519792..e21a9334f2 100644 --- a/packages/backend/server/src/plugins/copilot/prompt/prompts.ts +++ b/packages/backend/server/src/plugins/copilot/prompt/prompts.ts @@ -949,7 +949,7 @@ const chat: Prompt[] = [ role: 'system', content: `You are AFFiNE AI, a professional and humorous copilot within AFFiNE. You are powered by latest GPT model from OpenAI and AFFiNE. AFFiNE is an open source general purposed productivity tool that contains unified building blocks that users can use on any interfaces, including block-based docs editor, infinite canvas based edgeless graphic mode, or multi-dimensional table with multiple transformable views. Your mission is always to try your very best to assist users to use AFFiNE to write docs, draw diagrams or plan things with these abilities. You always think step-by-step and describe your plan for what to build, using well-structured and clear markdown, written out in great detail. Unless otherwise specified, where list, JSON, or code blocks are required for giving the output. Minimize any other prose so that your responses can be directly used and inserted into the docs. You are able to access to API of AFFiNE to finish your job. You always respect the users' privacy and would not leak their info to anyone else. AFFiNE is made by Toeverything .Pte .Ltd, a company registered in Singapore with a diverse and international team. The company also open sourced blocksuite and octobase for building tools similar to Affine. The name AFFiNE comes from the idea of AFFiNE transform, as blocks in affine can all transform in page, edgeless or database mode. AFFiNE team is now having 25 members, an open source company driven by engineers. # Context Documents -The following documents provide relevant context and background information for your reference. +The following user messages provide relevant context and background information for your reference. If the provided documents are relevant to the user's query: - Use them to enrich and support your response - Cite sources using the citation rules below @@ -958,14 +958,6 @@ If the documents are not relevant: - Answer the question directly based on your knowledge - Do not reference or mention the provided documents -{{#docs}} -## Document {{index}} -- document_index: {{index}} -- document_id: {{docId}} -- document_content: -{{markdown}} -{{/docs}} - # Citations Rules: When referencing information from the provided documents in your response: 1. Use markdown footnote format for citations @@ -975,6 +967,17 @@ When referencing information from the provided documents in your response: [^document_index]:{"type":"doc","docId":"document_id"} 5. Ensure citations adhere strictly to the required format to avoid response errors. Do not add extra spaces in citations like [^ document_index] or [ ^document_index].`, }, + { + role: 'user', + content: `# Context Documents +{{#docs}} +## Document {{index}} +- document_index: {{index}} +- document_id: {{docId}} +- document_content: +{{markdown}} +{{/docs}}`, + }, ], }, { diff --git a/packages/backend/server/src/plugins/copilot/resolver.ts b/packages/backend/server/src/plugins/copilot/resolver.ts index 8d725ac087..9491e91314 100644 --- a/packages/backend/server/src/plugins/copilot/resolver.ts +++ b/packages/backend/server/src/plugins/copilot/resolver.ts @@ -151,6 +151,9 @@ class QueryChatHistoriesInput implements Partial { @Field(() => String, { nullable: true }) sessionId: string | undefined; + + @Field(() => Boolean, { nullable: true }) + withPrompt: boolean | undefined; } // ================== Return Types ================== @@ -346,8 +349,7 @@ export class CopilotResolver { user.id, workspaceId, docId, - options, - true + options ); return histories.map(h => ({ diff --git a/packages/backend/server/src/plugins/copilot/session.ts b/packages/backend/server/src/plugins/copilot/session.ts index ffa8c1a9e2..47cb67a24d 100644 --- a/packages/backend/server/src/plugins/copilot/session.ts +++ b/packages/backend/server/src/plugins/copilot/session.ts @@ -2,6 +2,7 @@ import { randomUUID } from 'node:crypto'; import { Injectable, Logger } from '@nestjs/common'; import { AiPromptRole, Prisma, PrismaClient } from '@prisma/client'; +import { omit } from 'lodash-es'; import { CopilotActionTaken, @@ -255,7 +256,7 @@ export class ChatSessionService { data: state.messages.map(m => ({ ...m, attachments: m.attachments || undefined, - params: m.params || undefined, + params: omit(m.params, ['docs']) || undefined, sessionId, })), }); @@ -415,8 +416,7 @@ export class ChatSessionService { userId: string, workspaceId?: string, docId?: string, - options?: ListHistoriesOptions, - withPrompt = false + options?: ListHistoriesOptions ): Promise { const extraCondition = []; @@ -505,7 +505,7 @@ export class ChatSessionService { const ret = ChatMessageSchema.array().safeParse(messages); if (ret.success) { // render system prompt - const preload = withPrompt + const preload = options?.withPrompt ? prompt .finish(ret.data[0]?.params || {}, id) .filter(({ role }) => role !== 'system') diff --git a/packages/backend/server/src/plugins/copilot/types.ts b/packages/backend/server/src/plugins/copilot/types.ts index 2b41793277..9070b406e8 100644 --- a/packages/backend/server/src/plugins/copilot/types.ts +++ b/packages/backend/server/src/plugins/copilot/types.ts @@ -149,6 +149,7 @@ export type ListHistoriesOptions = { sessionOrder: 'asc' | 'desc' | undefined; messageOrder: 'asc' | 'desc' | undefined; sessionId: string | undefined; + withPrompt: boolean | undefined; }; // ======== Provider Interface ======== diff --git a/packages/backend/server/src/schema.gql b/packages/backend/server/src/schema.gql index 65186f2f07..867eec5e8b 100644 --- a/packages/backend/server/src/schema.gql +++ b/packages/backend/server/src/schema.gql @@ -881,6 +881,7 @@ input QueryChatHistoriesInput { sessionId: String sessionOrder: ChatHistoryOrder skip: Int + withPrompt: Boolean } type QueryTooLongDataType { diff --git a/packages/frontend/apps/android/App/service/src/main/graphql/affine/schema.graphqls b/packages/frontend/apps/android/App/service/src/main/graphql/affine/schema.graphqls index c37c48e88b..6cf4c84139 100644 --- a/packages/frontend/apps/android/App/service/src/main/graphql/affine/schema.graphqls +++ b/packages/frontend/apps/android/App/service/src/main/graphql/affine/schema.graphqls @@ -1123,6 +1123,8 @@ input QueryChatHistoriesInput { sessionOrder: ChatHistoryOrder skip: Int + + withPrompt: Boolean } type QuotaQueryType { diff --git a/packages/frontend/core/src/blocksuite/presets/ai/actions/types.ts b/packages/frontend/core/src/blocksuite/presets/ai/actions/types.ts index 2bc38ac97f..8e2b0a0491 100644 --- a/packages/frontend/core/src/blocksuite/presets/ai/actions/types.ts +++ b/packages/frontend/core/src/blocksuite/presets/ai/actions/types.ts @@ -1,4 +1,5 @@ import type { + ChatHistoryOrder, CopilotContextDoc, CopilotContextFile, getCopilotHistoriesQuery, @@ -312,9 +313,10 @@ declare global { chats: ( workspaceId: string, docId?: string, - options?: RequestOptions< - typeof getCopilotHistoriesQuery - >['variables']['options'] + options?: { + sessionId?: string; + messageOrder?: ChatHistoryOrder; + } ) => Promise; cleanup: ( workspaceId: string, diff --git a/packages/frontend/core/src/blocksuite/presets/ai/chat-panel/index.ts b/packages/frontend/core/src/blocksuite/presets/ai/chat-panel/index.ts index 1bc7e02f7a..e7d0602b87 100644 --- a/packages/frontend/core/src/blocksuite/presets/ai/chat-panel/index.ts +++ b/packages/frontend/core/src/blocksuite/presets/ai/chat-panel/index.ts @@ -125,7 +125,7 @@ export class ChatPanel extends WithDisposable(ShadowlessElement) { const currentRequest = ++this._updateHistoryCounter; const [histories, actions] = await Promise.all([ - AIProvider.histories?.chats(doc.workspace.id, doc.id, { fork: false }), + AIProvider.histories?.chats(doc.workspace.id, doc.id), AIProvider.histories?.actions(doc.workspace.id, doc.id), ]); diff --git a/packages/frontend/core/src/components/blocksuite/block-suite-editor/ai/setup-provider.tsx b/packages/frontend/core/src/components/blocksuite/block-suite-editor/ai/setup-provider.tsx index 4f399d64cc..1c220a9b39 100644 --- a/packages/frontend/core/src/components/blocksuite/block-suite-editor/ai/setup-provider.tsx +++ b/packages/frontend/core/src/components/blocksuite/block-suite-editor/ai/setup-provider.tsx @@ -2,6 +2,7 @@ import { AIProvider } from '@affine/core/blocksuite/presets/ai'; import { toggleGeneralAIOnboarding } from '@affine/core/components/affine/ai-onboarding/apis'; import type { GlobalDialogService } from '@affine/core/modules/dialogs'; import { + type ChatHistoryOrder, type getCopilotHistoriesQuery, type RequestOptions, } from '@affine/graphql'; @@ -469,15 +470,17 @@ Could you make a new website based on these notes and send back just the html fi return ( (await client.getHistories(workspaceId, docId, { action: true, + withPrompt: true, })) ?? [] ); }, chats: async ( workspaceId: string, docId?: string, - options?: RequestOptions< - typeof getCopilotHistoriesQuery - >['variables']['options'] + options?: { + sessionId?: string; + messageOrder?: ChatHistoryOrder; + } ): Promise => { // @ts-expect-error - 'action' is missing in server impl return (await client.getHistories(workspaceId, docId, options)) ?? []; diff --git a/packages/frontend/graphql/src/schema.ts b/packages/frontend/graphql/src/schema.ts index c5278a5616..8c496660cf 100644 --- a/packages/frontend/graphql/src/schema.ts +++ b/packages/frontend/graphql/src/schema.ts @@ -1298,6 +1298,7 @@ export interface QueryChatHistoriesInput { sessionId?: InputMaybe; sessionOrder?: InputMaybe; skip?: InputMaybe; + withPrompt?: InputMaybe; } export interface QueryTooLongDataType {