diff --git a/.prettierignore b/.prettierignore index 677edc7d61..fb739dc28e 100644 --- a/.prettierignore +++ b/.prettierignore @@ -29,6 +29,7 @@ test-results tools/cli/src/webpack/error-handler.js packages/backend/native/index.d.ts packages/backend/server/src/__tests__/__snapshots__ +packages/common/native/fixtures/** packages/frontend/native/index.d.ts packages/frontend/native/index.js packages/frontend/graphql/src/graphql/index.ts diff --git a/Cargo.toml b/Cargo.toml index fb16f4bd03..66762ae2c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ chrono = "0.4" criterion2 = { version = "2", default-features = false } dotenvy = "0.15" file-format = { version = "0.26", features = ["reader"] } +homedir = "0.3" mimalloc = "0.1" napi = { version = "3.0.0-alpha.12", features = ["async", "chrono_date", "error_anyhow", "napi9", "serde"] } napi-build = { version = "2" } @@ -28,7 +29,6 @@ objc2 = "0.6" objc2-foundation = "0.3" once_cell = "1" parking_lot = "0.12" -homedir = "0.3" rand = "0.9" rayon = "1.10" serde = "1" diff --git a/packages/backend/server/migrations/20250210083416_ai_context/migration.sql b/packages/backend/server/migrations/20250210083416_ai_context/migration.sql new file mode 100644 index 0000000000..31115677e1 --- /dev/null +++ b/packages/backend/server/migrations/20250210083416_ai_context/migration.sql @@ -0,0 +1,13 @@ +-- CreateTable +CREATE TABLE "ai_contexts" ( + "id" VARCHAR NOT NULL, + "session_id" VARCHAR NOT NULL, + "config" JSON NOT NULL, + "created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMPTZ(3) NOT NULL, + + CONSTRAINT "ai_contexts_pkey" PRIMARY KEY ("id") +); + +-- AddForeignKey +ALTER TABLE "ai_contexts" ADD CONSTRAINT "ai_contexts_session_id_fkey" FOREIGN KEY ("session_id") REFERENCES "ai_sessions_metadata"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/packages/backend/server/package.json b/packages/backend/server/package.json index 6bcb16a389..3a005ef291 100644 --- a/packages/backend/server/package.json +++ b/packages/backend/server/package.json @@ -82,7 +82,7 @@ "nestjs-cls": "^5.0.0", "nodemailer": "^6.9.16", "on-headers": "^1.0.2", - "openai": "^4.76.2", + "openai": "^4.83.0", "piscina": "^5.0.0-alpha.0", "prisma": "^5.22.0", "react": "19.0.0", diff --git a/packages/backend/server/schema.prisma b/packages/backend/server/schema.prisma index 043c9ccb0a..b5f06fa4d8 100644 --- a/packages/backend/server/schema.prisma +++ b/packages/backend/server/schema.prisma @@ -402,12 +402,26 @@ model AiSession { user User @relation(fields: [userId], references: [id], onDelete: Cascade) prompt AiPrompt @relation(fields: [promptName], references: [name], onDelete: Cascade) messages AiSessionMessage[] + context AiContext[] @@index([userId]) @@index([userId, workspaceId]) @@map("ai_sessions_metadata") } +model AiContext { + id String @id @default(uuid()) @db.VarChar + sessionId String @map("session_id") @db.VarChar + config Json @db.Json + + createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3) + updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3) + + session AiSession @relation(fields: [sessionId], references: [id], onDelete: Cascade) + + @@map("ai_contexts") +} + model DataMigration { id String @id @default(uuid()) @db.VarChar name String @unique @db.VarChar diff --git a/packages/backend/server/src/__tests__/__snapshots__/copilot.e2e.ts.md b/packages/backend/server/src/__tests__/__snapshots__/copilot.e2e.ts.md new file mode 100644 index 0000000000..523f9e64c9 --- /dev/null +++ b/packages/backend/server/src/__tests__/__snapshots__/copilot.e2e.ts.md @@ -0,0 +1,26 @@ +# Snapshot report for `src/__tests__/copilot.e2e.ts` + +The actual snapshot is saved in `copilot.e2e.ts.snap`. + +Generated by [AVA](https://avajs.dev). + +## should be able to manage context + +> should list context files + + [ + { + id: 'docId1', + }, + ] + +> should list context docs + + [ + { + blobId: 'fileId1', + chunkSize: 3, + name: 'sample.pdf', + status: 'finished', + }, + ] diff --git a/packages/backend/server/src/__tests__/__snapshots__/copilot.e2e.ts.snap b/packages/backend/server/src/__tests__/__snapshots__/copilot.e2e.ts.snap new file mode 100644 index 0000000000..2452b58a46 Binary files /dev/null and b/packages/backend/server/src/__tests__/__snapshots__/copilot.e2e.ts.snap differ diff --git a/packages/backend/server/src/__tests__/copilot.e2e.ts b/packages/backend/server/src/__tests__/copilot.e2e.ts index fad4e36bde..99cfc1f70a 100644 --- a/packages/backend/server/src/__tests__/copilot.e2e.ts +++ b/packages/backend/server/src/__tests__/copilot.e2e.ts @@ -8,6 +8,7 @@ import { ConfigModule } from '../base/config'; import { AuthService } from '../core/auth'; import { WorkspaceModule } from '../core/workspaces'; import { CopilotModule } from '../plugins/copilot'; +import { CopilotContextService } from '../plugins/copilot/context'; import { prompts, PromptService } from '../plugins/copilot/prompt'; import { CopilotProviderService, @@ -27,15 +28,19 @@ import { TestUser, } from './utils'; import { + addContextDoc, array2sse, chatWithImages, chatWithText, chatWithTextStream, chatWithWorkflow, + createCopilotContext, createCopilotMessage, createCopilotSession, forkCopilotSession, getHistories, + listContext, + listContextFiles, MockCopilotTestProvider, sse2array, textToEventStream, @@ -46,6 +51,7 @@ import { const test = ava as TestFn<{ auth: AuthService; app: TestingApp; + context: CopilotContextService; prompt: PromptService; provider: CopilotProviderService; storage: CopilotStorage; @@ -77,11 +83,13 @@ test.before(async t => { }); const auth = app.get(AuthService); + const context = app.get(CopilotContextService); const prompt = app.get(PromptService); const storage = app.get(CopilotStorage); t.context.app = app; t.context.auth = auth; + t.context.context = context; t.context.prompt = prompt; t.context.storage = storage; }); @@ -678,3 +686,46 @@ test('should be able to search image from unsplash', async t => { const resp = await unsplashSearch(app); t.not(resp.status, 404, 'route should be exists'); }); + +test('should be able to manage context', async t => { + const { app } = t.context; + + const { id: workspaceId } = await createWorkspace(app); + const sessionId = await createCopilotSession( + app, + workspaceId, + randomUUID(), + promptName + ); + + { + await t.throwsAsync( + createCopilotContext(app, workspaceId, randomUUID()), + { instanceOf: Error }, + 'should throw error if create context with invalid session id' + ); + + const context = createCopilotContext(app, workspaceId, sessionId); + await t.notThrowsAsync(context, 'should create context with chat session'); + + const list = await listContext(app, workspaceId, sessionId); + t.deepEqual( + list.map(f => ({ id: f.id })), + [{ id: await context }], + 'should list context' + ); + } + + { + const contextId = await createCopilotContext(app, workspaceId, sessionId); + + await addContextDoc(app, contextId, 'docId1'); + + const { docs } = + (await listContextFiles(app, workspaceId, sessionId, contextId)) || {}; + t.snapshot( + docs?.map(({ createdAt: _, ...d }) => d), + 'should list context files' + ); + } +}); diff --git a/packages/backend/server/src/__tests__/copilot.spec.ts b/packages/backend/server/src/__tests__/copilot.spec.ts index 684c9a8435..976e4e2de1 100644 --- a/packages/backend/server/src/__tests__/copilot.spec.ts +++ b/packages/backend/server/src/__tests__/copilot.spec.ts @@ -1,3 +1,5 @@ +import { randomUUID } from 'node:crypto'; + import type { TestFn } from 'ava'; import ava from 'ava'; import Sinon from 'sinon'; @@ -6,6 +8,7 @@ import { ConfigModule } from '../base/config'; import { AuthService } from '../core/auth'; import { QuotaModule } from '../core/quota'; import { CopilotModule } from '../plugins/copilot'; +import { CopilotContextService } from '../plugins/copilot/context'; import { prompts, PromptService } from '../plugins/copilot/prompt'; import { CopilotProviderService, @@ -44,6 +47,7 @@ import { MockCopilotTestProvider, WorkflowTestCases } from './utils/copilot'; const test = ava as TestFn<{ auth: AuthService; module: TestingModule; + context: CopilotContextService; prompt: PromptService; provider: CopilotProviderService; session: ChatSessionService; @@ -81,6 +85,7 @@ test.before(async t => { }); const auth = module.get(AuthService); + const context = module.get(CopilotContextService); const prompt = module.get(PromptService); const provider = module.get(CopilotProviderService); const session = module.get(ChatSessionService); @@ -88,6 +93,7 @@ test.before(async t => { t.context.module = module; t.context.auth = auth; + t.context.context = context; t.context.prompt = prompt; t.context.provider = provider; t.context.session = session; @@ -1247,3 +1253,52 @@ test('CitationParser should not replace chunks of citation already with URLs', t ].join('\n'); t.is(result, expected); }); + +// ==================== context ==================== +test('should be able to manage context', async t => { + const { context, prompt, session } = t.context; + + await prompt.set('prompt', 'model', [ + { role: 'system', content: 'hello {{word}}' }, + ]); + const chatSession = await session.create({ + docId: 'test', + workspaceId: 'test', + userId, + promptName: 'prompt', + }); + + { + await t.throwsAsync( + context.create(randomUUID()), + { instanceOf: Error }, + 'should throw error if create context with invalid session id' + ); + + const session = context.create(chatSession); + await t.notThrowsAsync(session, 'should create context with chat session'); + + await t.notThrowsAsync( + context.get((await session).id), + 'should get context after create' + ); + + await t.throwsAsync( + context.get(randomUUID()), + { instanceOf: Error }, + 'should throw error if get context with invalid id' + ); + } + + { + const session = await context.create(chatSession); + + const docId = randomUUID(); + await session.addDocRecord(docId); + const docs = session.listDocs().map(d => d.id); + t.deepEqual(docs, [docId], 'should list doc id'); + + await session.removeDocRecord(docId); + t.deepEqual(session.listDocs(), [], 'should remove doc id'); + } +}); diff --git a/packages/backend/server/src/__tests__/utils/copilot.ts b/packages/backend/server/src/__tests__/utils/copilot.ts index 3b4d3f9224..f1ad92805e 100644 --- a/packages/backend/server/src/__tests__/utils/copilot.ts +++ b/packages/backend/server/src/__tests__/utils/copilot.ts @@ -23,6 +23,7 @@ import { WorkflowNodeType, WorkflowParams, } from '../../plugins/copilot/workflow/types'; +import { gql } from './common'; import { TestingApp } from './testing-app'; import { sleep } from './utils'; @@ -209,6 +210,216 @@ export async function forkCopilotSession( return res.forkCopilotSession; } +export async function createCopilotContext( + app: TestingApp, + workspaceId: string, + sessionId: string +): Promise { + const res = await app.gql(` + mutation { + createCopilotContext(workspaceId: "${workspaceId}", sessionId: "${sessionId}") + } + `); + + return res.createCopilotContext; +} + +export async function matchContext( + app: TestingApp, + contextId: string, + content: string, + limit: number +): Promise< + | { + fileId: string; + chunk: number; + content: string; + distance: number | null; + }[] + | undefined +> { + const res = await app.gql( + ` + mutation matchContext($content: String!, $contextId: String!, $limit: SafeInt) { + matchContext(content: $content, contextId: $contextId, limit: $limit) { + fileId + chunk + content + distance + } + } + `, + { contextId, content, limit } + ); + + return res.matchContext; +} + +export async function listContext( + app: TestingApp, + workspaceId: string, + sessionId: string +): Promise< + { + id: string; + workspaceId: string; + }[] +> { + const res = await app.gql(` + query { + currentUser { + copilot(workspaceId: "${workspaceId}") { + contexts(sessionId: "${sessionId}") { + id + workspaceId + } + } + } + } + `); + + return res.currentUser?.copilot?.contexts; +} + +export async function addContextFile( + app: TestingApp, + contextId: string, + blobId: string, + fileName: string, + content: Buffer +): Promise<{ id: string }[]> { + const res = await app + .POST(gql) + .set({ 'x-request-id': 'test', 'x-operation-name': 'test' }) + .field( + 'operations', + JSON.stringify({ + query: ` + mutation addContextFile($options: AddContextFileInput!, $content: Upload!) { + addContextFile(content: $content, options: $options) { + id + } + } + `, + variables: { + content: null, + options: { contextId, blobId, fileName }, + }, + }) + ) + .field('map', JSON.stringify({ '0': ['variables.content'] })) + .attach('0', content, { + filename: fileName, + contentType: 'application/octet-stream', + }) + .expect(200); + + return res.body.data.addContextFile; +} + +export async function removeContextFile( + app: TestingApp, + contextId: string, + fileId: string +): Promise { + const res = await app.gql( + ` + mutation removeContextFile($options: RemoveContextFileInput!) { + removeContextFile(options: $options) + } + `, + { options: { contextId, fileId } } + ); + + return res.removeContextFile; +} + +export async function addContextDoc( + app: TestingApp, + contextId: string, + docId: string +): Promise<{ id: string }[]> { + const res = await app.gql( + ` + mutation addContextDoc($options: AddContextDocInput!) { + addContextDoc(options: $options) { + id + } + } + `, + { options: { contextId, docId } } + ); + + return res.addContextDoc; +} + +export async function removeContextDoc( + app: TestingApp, + contextId: string, + docId: string +): Promise { + const res = await app.gql( + ` + mutation removeContextDoc($options: RemoveContextFileInput!) { + removeContextDoc(options: $options) + } + `, + { options: { contextId, docId } } + ); + + return res.removeContextDoc; +} + +export async function listContextFiles( + app: TestingApp, + workspaceId: string, + sessionId: string, + contextId: string +): Promise< + | { + docs: { + id: string; + createdAt: number; + }[]; + files: { + id: string; + name: string; + blobId: string; + chunkSize: number; + status: string; + createdAt: number; + }[]; + } + | undefined +> { + const res = await app.gql(` + query { + currentUser { + copilot(workspaceId: "${workspaceId}") { + contexts(sessionId: "${sessionId}", contextId: "${contextId}") { + docs { + id + createdAt + } + files { + id + name + blobId + chunkSize + status + createdAt + } + } + } + } + } + `); + + const { docs, files } = res.currentUser?.copilot?.contexts?.[0] || {}; + + return { docs, files }; +} + export async function createCopilotMessage( app: TestingApp, sessionId: string, diff --git a/packages/backend/server/src/base/error/def.ts b/packages/backend/server/src/base/error/def.ts index 155ecdb5f3..e147188a3e 100644 --- a/packages/backend/server/src/base/error/def.ts +++ b/packages/backend/server/src/base/error/def.ts @@ -1,4 +1,5 @@ import { STATUS_CODES } from 'node:http'; +import { escape } from 'node:querystring'; import { HttpStatus, Logger } from '@nestjs/common'; import { ClsServiceManager } from 'nestjs-cls'; @@ -605,6 +606,29 @@ export const USER_FRIENDLY_ERRORS = { message: ({ provider, kind, message }) => `Provider ${provider} failed with ${kind} error: ${message || 'unknown'}`, }, + copilot_invalid_context: { + type: 'invalid_input', + args: { contextId: 'string' }, + message: ({ contextId }) => `Invalid copilot context ${contextId}.`, + }, + copilot_context_file_not_supported: { + type: 'bad_request', + args: { fileName: 'string', message: 'string' }, + message: ({ fileName, message }) => + `File ${fileName} is not supported to use as context: ${message}`, + }, + copilot_failed_to_modify_context: { + type: 'internal_server_error', + args: { contextId: 'string', message: 'string' }, + message: ({ contextId, message }) => + `Failed to modify context ${contextId}: ${message}`, + }, + copilot_failed_to_match_context: { + type: 'internal_server_error', + args: { contextId: 'string', content: 'string', message: 'string' }, + message: ({ contextId, content, message }) => + `Failed to match context ${contextId} with "${escape(content)}": ${message}`, + }, // Quota & Limit errors blob_quota_exceeded: { diff --git a/packages/backend/server/src/base/error/errors.gen.ts b/packages/backend/server/src/base/error/errors.gen.ts index fab04144b6..fca1390412 100644 --- a/packages/backend/server/src/base/error/errors.gen.ts +++ b/packages/backend/server/src/base/error/errors.gen.ts @@ -608,6 +608,50 @@ export class CopilotProviderSideError extends UserFriendlyError { super('internal_server_error', 'copilot_provider_side_error', message, args); } } +@ObjectType() +class CopilotInvalidContextDataType { + @Field() contextId!: string +} + +export class CopilotInvalidContext extends UserFriendlyError { + constructor(args: CopilotInvalidContextDataType, message?: string | ((args: CopilotInvalidContextDataType) => string)) { + super('invalid_input', 'copilot_invalid_context', message, args); + } +} +@ObjectType() +class CopilotContextFileNotSupportedDataType { + @Field() fileName!: string + @Field() message!: string +} + +export class CopilotContextFileNotSupported extends UserFriendlyError { + constructor(args: CopilotContextFileNotSupportedDataType, message?: string | ((args: CopilotContextFileNotSupportedDataType) => string)) { + super('bad_request', 'copilot_context_file_not_supported', message, args); + } +} +@ObjectType() +class CopilotFailedToModifyContextDataType { + @Field() contextId!: string + @Field() message!: string +} + +export class CopilotFailedToModifyContext extends UserFriendlyError { + constructor(args: CopilotFailedToModifyContextDataType, message?: string | ((args: CopilotFailedToModifyContextDataType) => string)) { + super('internal_server_error', 'copilot_failed_to_modify_context', message, args); + } +} +@ObjectType() +class CopilotFailedToMatchContextDataType { + @Field() contextId!: string + @Field() content!: string + @Field() message!: string +} + +export class CopilotFailedToMatchContext extends UserFriendlyError { + constructor(args: CopilotFailedToMatchContextDataType, message?: string | ((args: CopilotFailedToMatchContextDataType) => string)) { + super('internal_server_error', 'copilot_failed_to_match_context', message, args); + } +} export class BlobQuotaExceeded extends UserFriendlyError { constructor(message?: string) { @@ -801,6 +845,10 @@ export enum ErrorNames { COPILOT_PROMPT_NOT_FOUND, COPILOT_PROMPT_INVALID, COPILOT_PROVIDER_SIDE_ERROR, + COPILOT_INVALID_CONTEXT, + COPILOT_CONTEXT_FILE_NOT_SUPPORTED, + COPILOT_FAILED_TO_MODIFY_CONTEXT, + COPILOT_FAILED_TO_MATCH_CONTEXT, BLOB_QUOTA_EXCEEDED, MEMBER_QUOTA_EXCEEDED, COPILOT_QUOTA_EXCEEDED, @@ -825,5 +873,5 @@ registerEnumType(ErrorNames, { export const ErrorDataUnionType = createUnionType({ name: 'ErrorDataUnion', types: () => - [QueryTooLongDataType, WrongSignInCredentialsDataType, UnknownOauthProviderDataType, MissingOauthQueryParameterDataType, InvalidEmailDataType, InvalidPasswordLengthDataType, WorkspacePermissionNotFoundDataType, SpaceNotFoundDataType, MemberNotFoundInSpaceDataType, NotInSpaceDataType, AlreadyInSpaceDataType, SpaceAccessDeniedDataType, SpaceOwnerNotFoundDataType, SpaceShouldHaveOnlyOneOwnerDataType, DocNotFoundDataType, DocAccessDeniedDataType, VersionRejectedDataType, InvalidHistoryTimestampDataType, DocHistoryNotFoundDataType, BlobNotFoundDataType, ExpectToGrantDocUserRolesDataType, ExpectToRevokeDocUserRolesDataType, ExpectToUpdateDocUserRoleDataType, UnsupportedSubscriptionPlanDataType, SubscriptionAlreadyExistsDataType, SubscriptionNotExistsDataType, SameSubscriptionRecurringDataType, SubscriptionPlanNotFoundDataType, CopilotMessageNotFoundDataType, CopilotPromptNotFoundDataType, CopilotProviderSideErrorDataType, RuntimeConfigNotFoundDataType, InvalidRuntimeConfigTypeDataType, InvalidLicenseUpdateParamsDataType, WorkspaceMembersExceedLimitToDowngradeDataType] as const, + [QueryTooLongDataType, WrongSignInCredentialsDataType, UnknownOauthProviderDataType, MissingOauthQueryParameterDataType, InvalidEmailDataType, InvalidPasswordLengthDataType, WorkspacePermissionNotFoundDataType, SpaceNotFoundDataType, MemberNotFoundInSpaceDataType, NotInSpaceDataType, AlreadyInSpaceDataType, SpaceAccessDeniedDataType, SpaceOwnerNotFoundDataType, SpaceShouldHaveOnlyOneOwnerDataType, DocNotFoundDataType, DocAccessDeniedDataType, VersionRejectedDataType, InvalidHistoryTimestampDataType, DocHistoryNotFoundDataType, BlobNotFoundDataType, ExpectToGrantDocUserRolesDataType, ExpectToRevokeDocUserRolesDataType, ExpectToUpdateDocUserRoleDataType, UnsupportedSubscriptionPlanDataType, SubscriptionAlreadyExistsDataType, SubscriptionNotExistsDataType, SameSubscriptionRecurringDataType, SubscriptionPlanNotFoundDataType, CopilotMessageNotFoundDataType, CopilotPromptNotFoundDataType, CopilotProviderSideErrorDataType, CopilotInvalidContextDataType, CopilotContextFileNotSupportedDataType, CopilotFailedToModifyContextDataType, CopilotFailedToMatchContextDataType, RuntimeConfigNotFoundDataType, InvalidRuntimeConfigTypeDataType, InvalidLicenseUpdateParamsDataType, WorkspaceMembersExceedLimitToDowngradeDataType] as const, }); diff --git a/packages/backend/server/src/plugins/copilot/context/index.ts b/packages/backend/server/src/plugins/copilot/context/index.ts new file mode 100644 index 0000000000..d89a9c9b7c --- /dev/null +++ b/packages/backend/server/src/plugins/copilot/context/index.ts @@ -0,0 +1,3 @@ +export { CopilotContextResolver, CopilotContextRootResolver } from './resolver'; +export { CopilotContextService } from './service'; +export { type ContextFile, ContextFileStatus } from './types'; diff --git a/packages/backend/server/src/plugins/copilot/context/resolver.ts b/packages/backend/server/src/plugins/copilot/context/resolver.ts new file mode 100644 index 0000000000..14bd94071e --- /dev/null +++ b/packages/backend/server/src/plugins/copilot/context/resolver.ts @@ -0,0 +1,260 @@ +import { + Args, + Field, + ID, + InputType, + Mutation, + ObjectType, + Parent, + registerEnumType, + ResolveField, + Resolver, +} from '@nestjs/graphql'; +import { SafeIntResolver } from 'graphql-scalars'; + +import { + CallMetric, + CopilotFailedToModifyContext, + CopilotSessionNotFound, + RequestMutex, + Throttle, + TooManyRequest, +} from '../../../base'; +import { CurrentUser } from '../../../core/auth'; +import { COPILOT_LOCKER, CopilotType } from '../resolver'; +import { ChatSessionService } from '../session'; +import { CopilotContextService } from './service'; +import { ContextDoc, type ContextFile, ContextFileStatus } from './types'; + +@InputType() +class AddContextDocInput { + @Field(() => String) + contextId!: string; + + @Field(() => String) + docId!: string; +} + +@InputType() +class RemoveContextFileInput { + @Field(() => String) + contextId!: string; + + @Field(() => String) + fileId!: string; +} + +@ObjectType('CopilotContext') +export class CopilotContextType { + @Field(() => ID) + id!: string; + + @Field(() => String) + workspaceId!: string; +} + +registerEnumType(ContextFileStatus, { name: 'ContextFileStatus' }); + +@ObjectType() +class CopilotContextDoc implements ContextDoc { + @Field(() => ID) + id!: string; + + @Field(() => SafeIntResolver) + createdAt!: number; +} + +@ObjectType() +class CopilotContextFile implements ContextFile { + @Field(() => ID) + id!: string; + + @Field(() => String) + name!: string; + + @Field(() => SafeIntResolver) + chunkSize!: number; + + @Field(() => ContextFileStatus) + status!: ContextFileStatus; + + @Field(() => String) + blobId!: string; + + @Field(() => SafeIntResolver) + createdAt!: number; +} + +@ObjectType() +class CopilotContextListItem { + @Field(() => ID) + id!: string; + + @Field(() => SafeIntResolver) + createdAt!: number; + + @Field(() => String, { nullable: true }) + name!: string; + + @Field(() => SafeIntResolver, { nullable: true }) + chunkSize!: number; + + @Field(() => ContextFileStatus, { nullable: true }) + status!: ContextFileStatus; + + @Field(() => String, { nullable: true }) + blobId!: string; +} + +@Throttle() +@Resolver(() => CopilotType) +export class CopilotContextRootResolver { + constructor( + private readonly mutex: RequestMutex, + private readonly chatSession: ChatSessionService, + private readonly context: CopilotContextService + ) {} + + private async checkChatSession( + user: CurrentUser, + sessionId: string, + workspaceId?: string + ): Promise { + const session = await this.chatSession.get(sessionId); + if ( + !session || + session.config.workspaceId !== workspaceId || + session.config.userId !== user.id + ) { + throw new CopilotSessionNotFound(); + } + } + + @ResolveField(() => [CopilotContextType], { + description: 'Get the context list of a session', + complexity: 2, + }) + @CallMetric('ai', 'context_create') + async contexts( + @Parent() copilot: CopilotType, + @CurrentUser() user: CurrentUser, + @Args('sessionId') sessionId: string, + @Args('contextId', { nullable: true }) contextId?: string + ) { + const lockFlag = `${COPILOT_LOCKER}:context:${sessionId}`; + await using lock = await this.mutex.acquire(lockFlag); + if (!lock) { + return new TooManyRequest('Server is busy'); + } + await this.checkChatSession(user, sessionId, copilot.workspaceId); + + if (contextId) { + const context = await this.context.get(contextId); + if (context) return [context]; + } else { + const context = await this.context.getBySessionId(sessionId); + if (context) return [context]; + } + return []; + } + + @Mutation(() => String, { + description: 'Create a context session', + }) + @CallMetric('ai', 'context_create') + async createCopilotContext( + @CurrentUser() user: CurrentUser, + @Args('workspaceId') workspaceId: string, + @Args('sessionId') sessionId: string + ) { + const lockFlag = `${COPILOT_LOCKER}:context:${sessionId}`; + await using lock = await this.mutex.acquire(lockFlag); + if (!lock) { + return new TooManyRequest('Server is busy'); + } + await this.checkChatSession(user, sessionId, workspaceId); + + const context = await this.context.create(sessionId); + return context.id; + } +} + +@Throttle() +@Resolver(() => CopilotContextType) +export class CopilotContextResolver { + constructor( + private readonly mutex: RequestMutex, + + private readonly context: CopilotContextService + ) {} + + @ResolveField(() => [CopilotContextDoc], { + description: 'list files in context', + }) + @CallMetric('ai', 'context_file_list') + async docs(@Parent() context: CopilotContextType): Promise { + const session = await this.context.get(context.id); + return session.listDocs(); + } + + @Mutation(() => [CopilotContextListItem], { + description: 'add a doc to context', + }) + @CallMetric('ai', 'context_doc_add') + async addContextDoc( + @Args({ name: 'options', type: () => AddContextDocInput }) + options: AddContextDocInput + ) { + const lockFlag = `${COPILOT_LOCKER}:context:${options.contextId}`; + await using lock = await this.mutex.acquire(lockFlag); + if (!lock) { + return new TooManyRequest('Server is busy'); + } + const session = await this.context.get(options.contextId); + + try { + return await session.addDocRecord(options.docId); + } catch (e: any) { + throw new CopilotFailedToModifyContext({ + contextId: options.contextId, + message: e.message, + }); + } + } + + @Mutation(() => Boolean, { + description: 'remove a doc from context', + }) + @CallMetric('ai', 'context_doc_remove') + async removeContextDoc( + @Args({ name: 'options', type: () => RemoveContextFileInput }) + options: RemoveContextFileInput + ) { + const lockFlag = `${COPILOT_LOCKER}:context:${options.contextId}`; + await using lock = await this.mutex.acquire(lockFlag); + if (!lock) { + return new TooManyRequest('Server is busy'); + } + const session = await this.context.get(options.contextId); + + try { + return await session.removeDocRecord(options.fileId); + } catch (e: any) { + throw new CopilotFailedToModifyContext({ + contextId: options.contextId, + message: e.message, + }); + } + } + + @ResolveField(() => [CopilotContextFile], { + description: 'list files in context', + }) + @CallMetric('ai', 'context_file_list') + async files( + @Parent() context: CopilotContextType + ): Promise { + const session = await this.context.get(context.id); + return session.listFiles(); + } +} diff --git a/packages/backend/server/src/plugins/copilot/context/service.ts b/packages/backend/server/src/plugins/copilot/context/service.ts new file mode 100644 index 0000000000..ec3fd459b3 --- /dev/null +++ b/packages/backend/server/src/plugins/copilot/context/service.ts @@ -0,0 +1,113 @@ +import { Injectable } from '@nestjs/common'; +import { PrismaClient } from '@prisma/client'; + +import { + Cache, + CopilotInvalidContext, + CopilotSessionNotFound, +} from '../../../base'; +import { ContextSession } from './session'; +import { ContextConfig, ContextConfigSchema } from './types'; + +const CONTEXT_SESSION_KEY = 'context-session'; + +@Injectable() +export class CopilotContextService { + constructor( + private readonly cache: Cache, + private readonly db: PrismaClient + ) {} + + private async saveConfig( + contextId: string, + config: ContextConfig, + refreshCache = false + ): Promise { + if (!refreshCache) { + await this.db.aiContext.update({ + where: { id: contextId }, + data: { config }, + }); + } + await this.cache.set(`${CONTEXT_SESSION_KEY}:${contextId}`, config); + } + + private async getCachedSession( + contextId: string + ): Promise { + const cachedSession = await this.cache.get( + `${CONTEXT_SESSION_KEY}:${contextId}` + ); + if (cachedSession) { + const config = ContextConfigSchema.safeParse(cachedSession); + if (config.success) { + return new ContextSession( + contextId, + config.data, + this.saveConfig.bind(this, contextId) + ); + } + } + return undefined; + } + + // NOTE: we only cache config to avoid frequent database queries + // but we do not need to cache session instances because a distributed + // lock is already apply to mutation operation for the same context in + // the resolver, so there will be no simultaneous writing to the config + private async cacheSession( + contextId: string, + config: ContextConfig + ): Promise { + const dispatcher = this.saveConfig.bind(this, contextId); + await dispatcher(config, true); + return new ContextSession(contextId, config, dispatcher); + } + + async create(sessionId: string): Promise { + const session = await this.db.aiSession.findFirst({ + where: { id: sessionId }, + select: { workspaceId: true }, + }); + if (!session) { + throw new CopilotSessionNotFound(); + } + + // keep the context unique per session + const existsContext = await this.getBySessionId(sessionId); + if (existsContext) return existsContext; + + const context = await this.db.aiContext.create({ + data: { + sessionId, + config: { workspaceId: session.workspaceId, docs: [], files: [] }, + }, + }); + + const config = ContextConfigSchema.parse(context.config); + return await this.cacheSession(context.id, config); + } + + async get(id: string): Promise { + const context = await this.getCachedSession(id); + if (context) return context; + const ret = await this.db.aiContext.findUnique({ + where: { id }, + select: { config: true }, + }); + if (ret) { + const config = ContextConfigSchema.safeParse(ret.config); + if (config.success) return this.cacheSession(id, config.data); + } + throw new CopilotInvalidContext({ contextId: id }); + } + + async getBySessionId(sessionId: string): Promise { + const existsContext = await this.db.aiContext.findFirst({ + where: { sessionId }, + select: { id: true }, + }); + if (existsContext) return this.get(existsContext.id); + return null; + } +} diff --git a/packages/backend/server/src/plugins/copilot/context/session.ts b/packages/backend/server/src/plugins/copilot/context/session.ts new file mode 100644 index 0000000000..46a9bfd3e9 --- /dev/null +++ b/packages/backend/server/src/plugins/copilot/context/session.ts @@ -0,0 +1,58 @@ +import { ContextConfig, ContextDoc, ContextList } from './types'; + +export class ContextSession implements AsyncDisposable { + constructor( + private readonly contextId: string, + private readonly config: ContextConfig, + private readonly dispatcher?: (config: ContextConfig) => Promise + ) {} + + get id() { + return this.contextId; + } + + get workspaceId() { + return this.config.workspaceId; + } + + listDocs(): ContextDoc[] { + return [...this.config.docs]; + } + + listFiles() { + return this.config.files.map(f => ({ ...f })); + } + + get sortedList(): ContextList { + const { docs, files } = this.config; + return [...docs, ...files].toSorted( + (a, b) => a.createdAt - b.createdAt + ) as ContextList; + } + + async addDocRecord(docId: string): Promise { + if (!this.config.docs.some(f => f.id === docId)) { + this.config.docs.push({ id: docId, createdAt: Date.now() }); + await this.save(); + } + return this.sortedList; + } + + async removeDocRecord(docId: string): Promise { + const index = this.config.docs.findIndex(f => f.id === docId); + if (index >= 0) { + this.config.docs.splice(index, 1); + await this.save(); + return true; + } + return false; + } + + async save() { + await this.dispatcher?.(this.config); + } + + async [Symbol.asyncDispose]() { + await this.save(); + } +} diff --git a/packages/backend/server/src/plugins/copilot/context/types.ts b/packages/backend/server/src/plugins/copilot/context/types.ts new file mode 100644 index 0000000000..90bcd37f48 --- /dev/null +++ b/packages/backend/server/src/plugins/copilot/context/types.ts @@ -0,0 +1,69 @@ +import { z } from 'zod'; + +declare global { + interface Events { + 'workspace.doc.embedding': { + workspaceId: string; + docId: string; + }; + } +} + +export enum ContextFileStatus { + processing = 'processing', + finished = 'finished', + failed = 'failed', +} + +export const ContextConfigSchema = z.object({ + workspaceId: z.string(), + files: z + .object({ + id: z.string(), + chunkSize: z.number(), + name: z.string(), + status: z.enum([ + ContextFileStatus.processing, + ContextFileStatus.finished, + ContextFileStatus.failed, + ]), + blobId: z.string(), + createdAt: z.number(), + }) + .array(), + docs: z + .object({ + id: z.string(), + createdAt: z.number(), + }) + .array(), +}); + +export type ContextConfig = z.infer; +export type ContextDoc = z.infer['docs'][number]; +export type ContextFile = z.infer['files'][number]; +export type ContextListItem = ContextDoc | ContextFile; +export type ContextList = ContextListItem[]; + +export type ChunkSimilarity = { + chunk: number; + content: string; + distance: number | null; +}; + +export type FileChunkSimilarity = ChunkSimilarity & { + fileId: string; +}; + +export type DocChunkSimilarity = ChunkSimilarity & { + docId: string; +}; + +export type Embedding = { + /** + * The index of the embedding in the list of embeddings. + */ + index: number; + content: string; + embedding: Array; +}; diff --git a/packages/backend/server/src/plugins/copilot/context/utils.ts b/packages/backend/server/src/plugins/copilot/context/utils.ts new file mode 100644 index 0000000000..451280d68d --- /dev/null +++ b/packages/backend/server/src/plugins/copilot/context/utils.ts @@ -0,0 +1,11 @@ +export class GqlSignal implements AsyncDisposable { + readonly abortController = new AbortController(); + + get signal() { + return this.abortController.signal; + } + + async [Symbol.asyncDispose]() { + this.abortController.abort(); + } +} diff --git a/packages/backend/server/src/plugins/copilot/index.ts b/packages/backend/server/src/plugins/copilot/index.ts index 2940044c4a..739100056a 100644 --- a/packages/backend/server/src/plugins/copilot/index.ts +++ b/packages/backend/server/src/plugins/copilot/index.ts @@ -5,6 +5,11 @@ import { FeatureModule } from '../../core/features'; import { PermissionModule } from '../../core/permission'; import { QuotaModule } from '../../core/quota'; import { Plugin } from '../registry'; +import { + CopilotContextResolver, + CopilotContextRootResolver, + CopilotContextService, +} from './context'; import { CopilotController } from './controller'; import { ChatMessageCache } from './message'; import { PromptService } from './prompt'; @@ -41,8 +46,13 @@ registerCopilotProvider(PerplexityProvider); CopilotProviderService, CopilotStorage, PromptsManagementResolver, + // workflow CopilotWorkflowService, ...CopilotWorkflowExecutors, + // context + CopilotContextRootResolver, + CopilotContextResolver, + CopilotContextService, ], controllers: [CopilotController], contributesTo: ServerFeature.Copilot, diff --git a/packages/backend/server/src/plugins/copilot/providers/openai.ts b/packages/backend/server/src/plugins/copilot/providers/openai.ts index c8a8fa62e4..5057d33878 100644 --- a/packages/backend/server/src/plugins/copilot/providers/openai.ts +++ b/packages/backend/server/src/plugins/copilot/providers/openai.ts @@ -59,6 +59,7 @@ export class OpenAIProvider private readonly logger = new Logger(OpenAIProvider.type); private readonly instance: OpenAI; + private existsModels: string[] | undefined; constructor(config: ClientOptions) { diff --git a/packages/backend/server/src/plugins/copilot/resolver.ts b/packages/backend/server/src/plugins/copilot/resolver.ts index afd5d89a43..8d725ac087 100644 --- a/packages/backend/server/src/plugins/copilot/resolver.ts +++ b/packages/backend/server/src/plugins/copilot/resolver.ts @@ -23,7 +23,7 @@ import { CallMetric, CopilotFailedToCreateMessage, CopilotSessionNotFound, - FileUpload, + type FileUpload, RequestMutex, Throttle, TooManyRequest, diff --git a/packages/backend/server/src/plugins/copilot/types.ts b/packages/backend/server/src/plugins/copilot/types.ts index ec6e2b0711..2b41793277 100644 --- a/packages/backend/server/src/plugins/copilot/types.ts +++ b/packages/backend/server/src/plugins/copilot/types.ts @@ -198,6 +198,13 @@ const CopilotImageOptionsSchema = CopilotProviderOptionsSchema.merge( export type CopilotImageOptions = z.infer; +export type CopilotContextFile = { + id: string; // fileId + created_at: number; + // embedding status + status: 'in_progress' | 'completed' | 'failed'; +}; + export interface CopilotProvider { readonly type: CopilotProviderType; getCapabilities(): CopilotCapability[]; diff --git a/packages/backend/server/src/schema.gql b/packages/backend/server/src/schema.gql index 5d83e2ba79..3a213c47e0 100644 --- a/packages/backend/server/src/schema.gql +++ b/packages/backend/server/src/schema.gql @@ -2,6 +2,11 @@ # THIS FILE WAS AUTOMATICALLY GENERATED (DO NOT MODIFY) # ------------------------------------------------------ +input AddContextDocInput { + contextId: String! + docId: String! +} + type AlreadyInSpaceDataType { spaceId: String! } @@ -25,12 +30,21 @@ type ChatMessage { role: String! } +enum ContextFileStatus { + failed + finished + processing +} + type Copilot { """Get the session list of actions in the workspace""" actions: [String!]! """Get the session list of chats in the workspace""" chats: [String!]! + + """Get the context list of a session""" + contexts(contextId: String, sessionId: String!): [CopilotContext!]! histories(docId: String, options: QueryChatHistoriesInput): [CopilotHistories!]! """Get the quota of the user in the workspace""" @@ -38,6 +52,55 @@ type Copilot { workspaceId: ID } +type CopilotContext { + """list files in context""" + docs: [CopilotContextDoc!]! + + """list files in context""" + files: [CopilotContextFile!]! + id: ID! + workspaceId: String! +} + +type CopilotContextDoc { + createdAt: SafeInt! + id: ID! +} + +type CopilotContextFile { + blobId: String! + chunkSize: SafeInt! + createdAt: SafeInt! + id: ID! + name: String! + status: ContextFileStatus! +} + +type CopilotContextFileNotSupportedDataType { + fileName: String! + message: String! +} + +type CopilotContextListItem { + blobId: String + chunkSize: SafeInt + createdAt: SafeInt! + id: ID! + name: String + status: ContextFileStatus +} + +type CopilotFailedToMatchContextDataType { + content: String! + contextId: String! + message: String! +} + +type CopilotFailedToModifyContextDataType { + contextId: String! + message: String! +} + type CopilotHistories { """An mark identifying which view to use to display the session""" action: String @@ -49,6 +112,10 @@ type CopilotHistories { tokens: Int! } +type CopilotInvalidContextDataType { + contextId: String! +} + type CopilotMessageNotFoundDataType { messageId: String! } @@ -244,7 +311,7 @@ type EditorType { name: String! } -union ErrorDataUnion = AlreadyInSpaceDataType | BlobNotFoundDataType | CopilotMessageNotFoundDataType | CopilotPromptNotFoundDataType | CopilotProviderSideErrorDataType | DocAccessDeniedDataType | DocHistoryNotFoundDataType | DocNotFoundDataType | ExpectToGrantDocUserRolesDataType | ExpectToRevokeDocUserRolesDataType | ExpectToUpdateDocUserRoleDataType | InvalidEmailDataType | InvalidHistoryTimestampDataType | InvalidLicenseUpdateParamsDataType | InvalidPasswordLengthDataType | InvalidRuntimeConfigTypeDataType | MemberNotFoundInSpaceDataType | MissingOauthQueryParameterDataType | NotInSpaceDataType | QueryTooLongDataType | RuntimeConfigNotFoundDataType | SameSubscriptionRecurringDataType | SpaceAccessDeniedDataType | SpaceNotFoundDataType | SpaceOwnerNotFoundDataType | SpaceShouldHaveOnlyOneOwnerDataType | SubscriptionAlreadyExistsDataType | SubscriptionNotExistsDataType | SubscriptionPlanNotFoundDataType | UnknownOauthProviderDataType | UnsupportedSubscriptionPlanDataType | VersionRejectedDataType | WorkspaceMembersExceedLimitToDowngradeDataType | WorkspacePermissionNotFoundDataType | WrongSignInCredentialsDataType +union ErrorDataUnion = AlreadyInSpaceDataType | BlobNotFoundDataType | CopilotContextFileNotSupportedDataType | CopilotFailedToMatchContextDataType | CopilotFailedToModifyContextDataType | CopilotInvalidContextDataType | CopilotMessageNotFoundDataType | CopilotPromptNotFoundDataType | CopilotProviderSideErrorDataType | DocAccessDeniedDataType | DocHistoryNotFoundDataType | DocNotFoundDataType | ExpectToGrantDocUserRolesDataType | ExpectToRevokeDocUserRolesDataType | ExpectToUpdateDocUserRoleDataType | InvalidEmailDataType | InvalidHistoryTimestampDataType | InvalidLicenseUpdateParamsDataType | InvalidPasswordLengthDataType | InvalidRuntimeConfigTypeDataType | MemberNotFoundInSpaceDataType | MissingOauthQueryParameterDataType | NotInSpaceDataType | QueryTooLongDataType | RuntimeConfigNotFoundDataType | SameSubscriptionRecurringDataType | SpaceAccessDeniedDataType | SpaceNotFoundDataType | SpaceOwnerNotFoundDataType | SpaceShouldHaveOnlyOneOwnerDataType | SubscriptionAlreadyExistsDataType | SubscriptionNotExistsDataType | SubscriptionPlanNotFoundDataType | UnknownOauthProviderDataType | UnsupportedSubscriptionPlanDataType | VersionRejectedDataType | WorkspaceMembersExceedLimitToDowngradeDataType | WorkspacePermissionNotFoundDataType | WrongSignInCredentialsDataType enum ErrorNames { ACCESS_DENIED @@ -260,8 +327,12 @@ enum ErrorNames { CAN_NOT_BATCH_GRANT_DOC_OWNER_PERMISSIONS CAPTCHA_VERIFICATION_FAILED COPILOT_ACTION_TAKEN + COPILOT_CONTEXT_FILE_NOT_SUPPORTED COPILOT_FAILED_TO_CREATE_MESSAGE COPILOT_FAILED_TO_GENERATE_TEXT + COPILOT_FAILED_TO_MATCH_CONTEXT + COPILOT_FAILED_TO_MODIFY_CONTEXT + COPILOT_INVALID_CONTEXT COPILOT_MESSAGE_NOT_FOUND COPILOT_PROMPT_INVALID COPILOT_PROMPT_NOT_FOUND @@ -574,6 +645,9 @@ type MissingOauthQueryParameterDataType { type Mutation { acceptInviteById(inviteId: String!, sendAcceptMail: Boolean, workspaceId: String!): Boolean! activateLicense(license: String!, workspaceId: String!): License! + + """add a doc to context""" + addContextDoc(options: AddContextDocInput!): [CopilotContextListItem!]! addWorkspaceFeature(feature: FeatureType!, workspaceId: String!): Boolean! approveMember(userId: String!, workspaceId: String!): String! cancelSubscription(idempotencyKey: String @deprecated(reason: "use header `Idempotency-Key`"), plan: SubscriptionPlan = Pro, workspaceId: String): SubscriptionType! @@ -589,6 +663,9 @@ type Mutation { """Create a subscription checkout link of stripe""" createCheckoutSession(input: CreateCheckoutSessionInput!): String! + """Create a context session""" + createCopilotContext(sessionId: String!, workspaceId: String!): String! + """Create a chat message""" createCopilotMessage(options: CreateChatMessageInput!): String! @@ -631,6 +708,9 @@ type Mutation { """Remove user avatar""" removeAvatar: RemoveAvatar! + + """remove a doc from context""" + removeContextDoc(options: RemoveContextFileInput!): Boolean! removeWorkspaceFeature(feature: FeatureType!, workspaceId: String!): Boolean! resumeSubscription(idempotencyKey: String @deprecated(reason: "use header `Idempotency-Key`"), plan: SubscriptionPlan = Pro, workspaceId: String): SubscriptionType! revoke(userId: String!, workspaceId: String!): Boolean! @@ -808,6 +888,11 @@ type RemoveAvatar { success: Boolean! } +input RemoveContextFileInput { + contextId: String! + fileId: String! +} + input RevokeDocUserRoleInput { docId: String! userId: String! diff --git a/packages/frontend/graphql/src/graphql/copilot-context-create.gql b/packages/frontend/graphql/src/graphql/copilot-context-create.gql new file mode 100644 index 0000000000..19d9f735fc --- /dev/null +++ b/packages/frontend/graphql/src/graphql/copilot-context-create.gql @@ -0,0 +1,3 @@ +mutation createCopilotContext($workspaceId: String!, $sessionId: String!) { + createCopilotContext(workspaceId: $workspaceId, sessionId: $sessionId) +} diff --git a/packages/frontend/graphql/src/graphql/copilot-context-doc-add.gql b/packages/frontend/graphql/src/graphql/copilot-context-doc-add.gql new file mode 100644 index 0000000000..f515335800 --- /dev/null +++ b/packages/frontend/graphql/src/graphql/copilot-context-doc-add.gql @@ -0,0 +1,10 @@ +mutation addContextDoc($options: AddContextDocInput!) { + addContextDoc(options: $options) { + id + createdAt + name + chunkSize + status + blobId + } +} diff --git a/packages/frontend/graphql/src/graphql/copilot-context-doc-remove.gql b/packages/frontend/graphql/src/graphql/copilot-context-doc-remove.gql new file mode 100644 index 0000000000..e7bf3750f2 --- /dev/null +++ b/packages/frontend/graphql/src/graphql/copilot-context-doc-remove.gql @@ -0,0 +1,3 @@ +mutation removeContextDoc($options: RemoveContextFileInput!) { + removeContextDoc(options: $options) +} diff --git a/packages/frontend/graphql/src/graphql/copilot-context-list.gql b/packages/frontend/graphql/src/graphql/copilot-context-list.gql new file mode 100644 index 0000000000..049c4c3e7f --- /dev/null +++ b/packages/frontend/graphql/src/graphql/copilot-context-list.gql @@ -0,0 +1,9 @@ +query listContext($workspaceId: String!, $sessionId: String!) { + currentUser { + copilot(workspaceId: $workspaceId) { + contexts(sessionId: $sessionId) { + id + } + } + } +} diff --git a/packages/frontend/graphql/src/graphql/get-copilot-history-ids.gql b/packages/frontend/graphql/src/graphql/copilot-history-get-ids.gql similarity index 100% rename from packages/frontend/graphql/src/graphql/get-copilot-history-ids.gql rename to packages/frontend/graphql/src/graphql/copilot-history-get-ids.gql diff --git a/packages/frontend/graphql/src/graphql/get-copilot-histories.gql b/packages/frontend/graphql/src/graphql/copilot-history-list.gql similarity index 100% rename from packages/frontend/graphql/src/graphql/get-copilot-histories.gql rename to packages/frontend/graphql/src/graphql/copilot-history-list.gql diff --git a/packages/frontend/graphql/src/graphql/create-copilot-message.gql b/packages/frontend/graphql/src/graphql/copilot-message-create.gql similarity index 100% rename from packages/frontend/graphql/src/graphql/create-copilot-message.gql rename to packages/frontend/graphql/src/graphql/copilot-message-create.gql diff --git a/packages/frontend/graphql/src/graphql/get-prompts.gql b/packages/frontend/graphql/src/graphql/copilot-prompt-list.gql similarity index 100% rename from packages/frontend/graphql/src/graphql/get-prompts.gql rename to packages/frontend/graphql/src/graphql/copilot-prompt-list.gql diff --git a/packages/frontend/graphql/src/graphql/update-prompt.gql b/packages/frontend/graphql/src/graphql/copilot-prompt-update.gql similarity index 100% rename from packages/frontend/graphql/src/graphql/update-prompt.gql rename to packages/frontend/graphql/src/graphql/copilot-prompt-update.gql diff --git a/packages/frontend/graphql/src/graphql/create-checkout-link copy.gql b/packages/frontend/graphql/src/graphql/copilot-session-cleanup.gql similarity index 100% rename from packages/frontend/graphql/src/graphql/create-checkout-link copy.gql rename to packages/frontend/graphql/src/graphql/copilot-session-cleanup.gql diff --git a/packages/frontend/graphql/src/graphql/create-copilot-session.gql b/packages/frontend/graphql/src/graphql/copilot-session-create.gql similarity index 100% rename from packages/frontend/graphql/src/graphql/create-copilot-session.gql rename to packages/frontend/graphql/src/graphql/copilot-session-create.gql diff --git a/packages/frontend/graphql/src/graphql/fork-copilot-session.gql b/packages/frontend/graphql/src/graphql/copilot-session-fork.gql similarity index 100% rename from packages/frontend/graphql/src/graphql/fork-copilot-session.gql rename to packages/frontend/graphql/src/graphql/copilot-session-fork.gql diff --git a/packages/frontend/graphql/src/graphql/update-copilot-session.gql b/packages/frontend/graphql/src/graphql/copilot-session-update.gql similarity index 100% rename from packages/frontend/graphql/src/graphql/update-copilot-session.gql rename to packages/frontend/graphql/src/graphql/copilot-session-update.gql diff --git a/packages/frontend/graphql/src/graphql/get-copilot-sessions.gql b/packages/frontend/graphql/src/graphql/copilot-sessions-get.gql similarity index 100% rename from packages/frontend/graphql/src/graphql/get-copilot-sessions.gql rename to packages/frontend/graphql/src/graphql/copilot-sessions-get.gql diff --git a/packages/frontend/graphql/src/graphql/index.ts b/packages/frontend/graphql/src/graphql/index.ts index fa2779ea7e..84723aac7c 100644 --- a/packages/frontend/graphql/src/graphql/index.ts +++ b/packages/frontend/graphql/src/graphql/index.ts @@ -158,6 +158,177 @@ mutation changePassword($token: String!, $userId: String!, $newPassword: String! }`, }; +export const createCopilotContextMutation = { + id: 'createCopilotContextMutation' as const, + operationName: 'createCopilotContext', + definitionName: 'createCopilotContext', + containsFile: false, + query: ` +mutation createCopilotContext($workspaceId: String!, $sessionId: String!) { + createCopilotContext(workspaceId: $workspaceId, sessionId: $sessionId) +}`, +}; + +export const addContextDocMutation = { + id: 'addContextDocMutation' as const, + operationName: 'addContextDoc', + definitionName: 'addContextDoc', + containsFile: false, + query: ` +mutation addContextDoc($options: AddContextDocInput!) { + addContextDoc(options: $options) { + id + createdAt + name + chunkSize + status + blobId + } +}`, +}; + +export const removeContextDocMutation = { + id: 'removeContextDocMutation' as const, + operationName: 'removeContextDoc', + definitionName: 'removeContextDoc', + containsFile: false, + query: ` +mutation removeContextDoc($options: RemoveContextFileInput!) { + removeContextDoc(options: $options) +}`, +}; + +export const listContextQuery = { + id: 'listContextQuery' as const, + operationName: 'listContext', + definitionName: 'currentUser', + containsFile: false, + query: ` +query listContext($workspaceId: String!, $sessionId: String!) { + currentUser { + copilot(workspaceId: $workspaceId) { + contexts(sessionId: $sessionId) { + id + } + } + } +}`, +}; + +export const getCopilotHistoryIdsQuery = { + id: 'getCopilotHistoryIdsQuery' as const, + operationName: 'getCopilotHistoryIds', + definitionName: 'currentUser', + containsFile: false, + query: ` +query getCopilotHistoryIds($workspaceId: String!, $docId: String, $options: QueryChatHistoriesInput) { + currentUser { + copilot(workspaceId: $workspaceId) { + histories(docId: $docId, options: $options) { + sessionId + messages { + id + role + createdAt + } + } + } + } +}`, +}; + +export const getCopilotHistoriesQuery = { + id: 'getCopilotHistoriesQuery' as const, + operationName: 'getCopilotHistories', + definitionName: 'currentUser', + containsFile: false, + query: ` +query getCopilotHistories($workspaceId: String!, $docId: String, $options: QueryChatHistoriesInput) { + currentUser { + copilot(workspaceId: $workspaceId) { + histories(docId: $docId, options: $options) { + sessionId + tokens + action + createdAt + messages { + id + role + content + attachments + createdAt + } + } + } + } +}`, +}; + +export const createCopilotMessageMutation = { + id: 'createCopilotMessageMutation' as const, + operationName: 'createCopilotMessage', + definitionName: 'createCopilotMessage', + containsFile: true, + query: ` +mutation createCopilotMessage($options: CreateChatMessageInput!) { + createCopilotMessage(options: $options) +}`, +}; + +export const getPromptsQuery = { + id: 'getPromptsQuery' as const, + operationName: 'getPrompts', + definitionName: 'listCopilotPrompts', + containsFile: false, + query: ` +query getPrompts { + listCopilotPrompts { + name + model + action + config { + jsonMode + frequencyPenalty + presencePenalty + temperature + topP + } + messages { + role + content + params + } + } +}`, +}; + +export const updatePromptMutation = { + id: 'updatePromptMutation' as const, + operationName: 'updatePrompt', + definitionName: 'updateCopilotPrompt', + containsFile: false, + query: ` +mutation updatePrompt($name: String!, $messages: [CopilotPromptMessageInput!]!) { + updateCopilotPrompt(name: $name, messages: $messages) { + name + model + action + config { + jsonMode + frequencyPenalty + presencePenalty + temperature + topP + } + messages { + role + content + params + } + } +}`, +}; + export const copilotQuotaQuery = { id: 'copilotQuotaQuery' as const, operationName: 'copilotQuota', @@ -187,28 +358,6 @@ mutation cleanupCopilotSession($input: DeleteSessionInput!) { }`, }; -export const createCheckoutSessionMutation = { - id: 'createCheckoutSessionMutation' as const, - operationName: 'createCheckoutSession', - definitionName: 'createCheckoutSession', - containsFile: false, - query: ` -mutation createCheckoutSession($input: CreateCheckoutSessionInput!) { - createCheckoutSession(input: $input) -}`, -}; - -export const createCopilotMessageMutation = { - id: 'createCopilotMessageMutation' as const, - operationName: 'createCopilotMessage', - definitionName: 'createCopilotMessage', - containsFile: true, - query: ` -mutation createCopilotMessage($options: CreateChatMessageInput!) { - createCopilotMessage(options: $options) -}`, -}; - export const createCopilotSessionMutation = { id: 'createCopilotSessionMutation' as const, operationName: 'createCopilotSession', @@ -220,6 +369,55 @@ mutation createCopilotSession($options: CreateChatSessionInput!) { }`, }; +export const forkCopilotSessionMutation = { + id: 'forkCopilotSessionMutation' as const, + operationName: 'forkCopilotSession', + definitionName: 'forkCopilotSession', + containsFile: false, + query: ` +mutation forkCopilotSession($options: ForkChatSessionInput!) { + forkCopilotSession(options: $options) +}`, +}; + +export const updateCopilotSessionMutation = { + id: 'updateCopilotSessionMutation' as const, + operationName: 'updateCopilotSession', + definitionName: 'updateCopilotSession', + containsFile: false, + query: ` +mutation updateCopilotSession($options: UpdateChatSessionInput!) { + updateCopilotSession(options: $options) +}`, +}; + +export const getCopilotSessionsQuery = { + id: 'getCopilotSessionsQuery' as const, + operationName: 'getCopilotSessions', + definitionName: 'currentUser', + containsFile: false, + query: ` +query getCopilotSessions($workspaceId: String!) { + currentUser { + copilot(workspaceId: $workspaceId) { + actions + chats + } + } +}`, +}; + +export const createCheckoutSessionMutation = { + id: 'createCheckoutSessionMutation' as const, + operationName: 'createCheckoutSession', + definitionName: 'createCheckoutSession', + containsFile: false, + query: ` +mutation createCheckoutSession($input: CreateCheckoutSessionInput!) { + createCheckoutSession(input: $input) +}`, +}; + export const createCustomerPortalMutation = { id: 'createCustomerPortalMutation' as const, operationName: 'createCustomerPortal', @@ -347,17 +545,6 @@ query getDocRolePermissions($workspaceId: String!, $docId: String!) { }`, }; -export const forkCopilotSessionMutation = { - id: 'forkCopilotSessionMutation' as const, - operationName: 'forkCopilotSession', - definitionName: 'forkCopilotSession', - containsFile: false, - query: ` -mutation forkCopilotSession($options: ForkChatSessionInput!) { - forkCopilotSession(options: $options) -}`, -}; - export const generateLicenseKeyMutation = { id: 'generateLicenseKeyMutation' as const, operationName: 'generateLicenseKey', @@ -369,71 +556,6 @@ mutation generateLicenseKey($sessionId: String!) { }`, }; -export const getCopilotHistoriesQuery = { - id: 'getCopilotHistoriesQuery' as const, - operationName: 'getCopilotHistories', - definitionName: 'currentUser', - containsFile: false, - query: ` -query getCopilotHistories($workspaceId: String!, $docId: String, $options: QueryChatHistoriesInput) { - currentUser { - copilot(workspaceId: $workspaceId) { - histories(docId: $docId, options: $options) { - sessionId - tokens - action - createdAt - messages { - id - role - content - attachments - createdAt - } - } - } - } -}`, -}; - -export const getCopilotHistoryIdsQuery = { - id: 'getCopilotHistoryIdsQuery' as const, - operationName: 'getCopilotHistoryIds', - definitionName: 'currentUser', - containsFile: false, - query: ` -query getCopilotHistoryIds($workspaceId: String!, $docId: String, $options: QueryChatHistoriesInput) { - currentUser { - copilot(workspaceId: $workspaceId) { - histories(docId: $docId, options: $options) { - sessionId - messages { - id - role - createdAt - } - } - } - } -}`, -}; - -export const getCopilotSessionsQuery = { - id: 'getCopilotSessionsQuery' as const, - operationName: 'getCopilotSessions', - definitionName: 'currentUser', - containsFile: false, - query: ` -query getCopilotSessions($workspaceId: String!) { - currentUser { - copilot(workspaceId: $workspaceId) { - actions - chats - } - } -}`, -}; - export const getCurrentUserFeaturesQuery = { id: 'getCurrentUserFeaturesQuery' as const, operationName: 'getCurrentUserFeatures', @@ -616,33 +738,6 @@ query getPageGrantedUsersList($pagination: PaginationInput!, $docId: String!, $w }`, }; -export const getPromptsQuery = { - id: 'getPromptsQuery' as const, - operationName: 'getPrompts', - definitionName: 'listCopilotPrompts', - containsFile: false, - query: ` -query getPrompts { - listCopilotPrompts { - name - model - action - config { - jsonMode - frequencyPenalty - presencePenalty - temperature - topP - } - messages { - role - content - params - } - } -}`, -}; - export const getServerRuntimeConfigQuery = { id: 'getServerRuntimeConfigQuery' as const, operationName: 'getServerRuntimeConfig', @@ -1268,17 +1363,6 @@ mutation updateAccount($id: String!, $input: ManageUserInput!) { }`, }; -export const updateCopilotSessionMutation = { - id: 'updateCopilotSessionMutation' as const, - operationName: 'updateCopilotSession', - definitionName: 'updateCopilotSession', - containsFile: false, - query: ` -mutation updateCopilotSession($options: UpdateChatSessionInput!) { - updateCopilotSession(options: $options) -}`, -}; - export const updateDocUserRoleMutation = { id: 'updateDocUserRoleMutation' as const, operationName: 'updateDocUserRole', @@ -1290,33 +1374,6 @@ mutation updateDocUserRole($input: UpdateDocUserRoleInput!) { }`, }; -export const updatePromptMutation = { - id: 'updatePromptMutation' as const, - operationName: 'updatePrompt', - definitionName: 'updateCopilotPrompt', - containsFile: false, - query: ` -mutation updatePrompt($name: String!, $messages: [CopilotPromptMessageInput!]!) { - updateCopilotPrompt(name: $name, messages: $messages) { - name - model - action - config { - jsonMode - frequencyPenalty - presencePenalty - temperature - topP - } - messages { - role - content - params - } - } -}`, -}; - export const updateServerRuntimeConfigsMutation = { id: 'updateServerRuntimeConfigsMutation' as const, operationName: 'updateServerRuntimeConfigs', diff --git a/packages/frontend/graphql/src/schema.ts b/packages/frontend/graphql/src/schema.ts index ae1a437bc1..e85572927e 100644 --- a/packages/frontend/graphql/src/schema.ts +++ b/packages/frontend/graphql/src/schema.ts @@ -37,6 +37,11 @@ export interface Scalars { Upload: { input: File; output: File }; } +export interface AddContextDocInput { + contextId: Scalars['String']['input']; + docId: Scalars['String']['input']; +} + export interface AlreadyInSpaceDataType { __typename?: 'AlreadyInSpaceDataType'; spaceId: Scalars['String']['output']; @@ -63,23 +68,91 @@ export interface ChatMessage { role: Scalars['String']['output']; } +export enum ContextFileStatus { + failed = 'failed', + finished = 'finished', + processing = 'processing', +} + export interface Copilot { __typename?: 'Copilot'; /** Get the session list of actions in the workspace */ actions: Array; /** Get the session list of chats in the workspace */ chats: Array; + /** Get the context list of a session */ + contexts: Array; histories: Array; /** Get the quota of the user in the workspace */ quota: CopilotQuota; workspaceId: Maybe; } +export interface CopilotContextsArgs { + contextId?: InputMaybe; + sessionId: Scalars['String']['input']; +} + export interface CopilotHistoriesArgs { docId?: InputMaybe; options?: InputMaybe; } +export interface CopilotContext { + __typename?: 'CopilotContext'; + /** list files in context */ + docs: Array; + /** list files in context */ + files: Array; + id: Scalars['ID']['output']; + workspaceId: Scalars['String']['output']; +} + +export interface CopilotContextDoc { + __typename?: 'CopilotContextDoc'; + createdAt: Scalars['SafeInt']['output']; + id: Scalars['ID']['output']; +} + +export interface CopilotContextFile { + __typename?: 'CopilotContextFile'; + blobId: Scalars['String']['output']; + chunkSize: Scalars['SafeInt']['output']; + createdAt: Scalars['SafeInt']['output']; + id: Scalars['ID']['output']; + name: Scalars['String']['output']; + status: ContextFileStatus; +} + +export interface CopilotContextFileNotSupportedDataType { + __typename?: 'CopilotContextFileNotSupportedDataType'; + fileName: Scalars['String']['output']; + message: Scalars['String']['output']; +} + +export interface CopilotContextListItem { + __typename?: 'CopilotContextListItem'; + blobId: Maybe; + chunkSize: Maybe; + createdAt: Scalars['SafeInt']['output']; + id: Scalars['ID']['output']; + name: Maybe; + status: Maybe; +} + +export interface CopilotFailedToMatchContextDataType { + __typename?: 'CopilotFailedToMatchContextDataType'; + content: Scalars['String']['output']; + contextId: Scalars['String']['output']; + message: Scalars['String']['output']; +} + +export interface CopilotFailedToModifyContextDataType { + __typename?: 'CopilotFailedToModifyContextDataType'; + contextId: Scalars['String']['output']; + message: Scalars['String']['output']; +} + export interface CopilotHistories { __typename?: 'CopilotHistories'; /** An mark identifying which view to use to display the session */ @@ -91,6 +164,11 @@ export interface CopilotHistories { tokens: Scalars['Int']['output']; } +export interface CopilotInvalidContextDataType { + __typename?: 'CopilotInvalidContextDataType'; + contextId: Scalars['String']['output']; +} + export interface CopilotMessageNotFoundDataType { __typename?: 'CopilotMessageNotFoundDataType'; messageId: Scalars['String']['output']; @@ -303,6 +381,10 @@ export interface EditorType { export type ErrorDataUnion = | AlreadyInSpaceDataType | BlobNotFoundDataType + | CopilotContextFileNotSupportedDataType + | CopilotFailedToMatchContextDataType + | CopilotFailedToModifyContextDataType + | CopilotInvalidContextDataType | CopilotMessageNotFoundDataType | CopilotPromptNotFoundDataType | CopilotProviderSideErrorDataType @@ -351,8 +433,12 @@ export enum ErrorNames { CAN_NOT_BATCH_GRANT_DOC_OWNER_PERMISSIONS = 'CAN_NOT_BATCH_GRANT_DOC_OWNER_PERMISSIONS', CAPTCHA_VERIFICATION_FAILED = 'CAPTCHA_VERIFICATION_FAILED', COPILOT_ACTION_TAKEN = 'COPILOT_ACTION_TAKEN', + COPILOT_CONTEXT_FILE_NOT_SUPPORTED = 'COPILOT_CONTEXT_FILE_NOT_SUPPORTED', COPILOT_FAILED_TO_CREATE_MESSAGE = 'COPILOT_FAILED_TO_CREATE_MESSAGE', COPILOT_FAILED_TO_GENERATE_TEXT = 'COPILOT_FAILED_TO_GENERATE_TEXT', + COPILOT_FAILED_TO_MATCH_CONTEXT = 'COPILOT_FAILED_TO_MATCH_CONTEXT', + COPILOT_FAILED_TO_MODIFY_CONTEXT = 'COPILOT_FAILED_TO_MODIFY_CONTEXT', + COPILOT_INVALID_CONTEXT = 'COPILOT_INVALID_CONTEXT', COPILOT_MESSAGE_NOT_FOUND = 'COPILOT_MESSAGE_NOT_FOUND', COPILOT_PROMPT_INVALID = 'COPILOT_PROMPT_INVALID', COPILOT_PROMPT_NOT_FOUND = 'COPILOT_PROMPT_NOT_FOUND', @@ -668,6 +754,8 @@ export interface Mutation { __typename?: 'Mutation'; acceptInviteById: Scalars['Boolean']['output']; activateLicense: License; + /** add a doc to context */ + addContextDoc: Array; addWorkspaceFeature: Scalars['Boolean']['output']; approveMember: Scalars['String']['output']; cancelSubscription: SubscriptionType; @@ -679,6 +767,8 @@ export interface Mutation { createChangePasswordUrl: Scalars['String']['output']; /** Create a subscription checkout link of stripe */ createCheckoutSession: Scalars['String']['output']; + /** Create a context session */ + createCopilotContext: Scalars['String']['output']; /** Create a chat message */ createCopilotMessage: Scalars['String']['output']; /** Create a copilot prompt */ @@ -714,6 +804,8 @@ export interface Mutation { releaseDeletedBlobs: Scalars['Boolean']['output']; /** Remove user avatar */ removeAvatar: RemoveAvatar; + /** remove a doc from context */ + removeContextDoc: Scalars['Boolean']['output']; removeWorkspaceFeature: Scalars['Boolean']['output']; resumeSubscription: SubscriptionType; revoke: Scalars['Boolean']['output']; @@ -762,6 +854,10 @@ export interface MutationActivateLicenseArgs { workspaceId: Scalars['String']['input']; } +export interface MutationAddContextDocArgs { + options: AddContextDocInput; +} + export interface MutationAddWorkspaceFeatureArgs { feature: FeatureType; workspaceId: Scalars['String']['input']; @@ -802,6 +898,11 @@ export interface MutationCreateCheckoutSessionArgs { input: CreateCheckoutSessionInput; } +export interface MutationCreateCopilotContextArgs { + sessionId: Scalars['String']['input']; + workspaceId: Scalars['String']['input']; +} + export interface MutationCreateCopilotMessageArgs { options: CreateChatMessageInput; } @@ -909,6 +1010,10 @@ export interface MutationReleaseDeletedBlobsArgs { workspaceId: Scalars['String']['input']; } +export interface MutationRemoveContextDocArgs { + options: RemoveContextFileInput; +} + export interface MutationRemoveWorkspaceFeatureArgs { feature: FeatureType; workspaceId: Scalars['String']['input']; @@ -1203,6 +1308,11 @@ export interface RemoveAvatar { success: Scalars['Boolean']['output']; } +export interface RemoveContextFileInput { + contextId: Scalars['String']['input']; + fileId: Scalars['String']['input']; +} + export interface RevokeDocUserRoleInput { docId: Scalars['String']['input']; userId: Scalars['String']['input']; @@ -1832,6 +1942,179 @@ export type ChangePasswordMutation = { changePassword: boolean; }; +export type CreateCopilotContextMutationVariables = Exact<{ + workspaceId: Scalars['String']['input']; + sessionId: Scalars['String']['input']; +}>; + +export type CreateCopilotContextMutation = { + __typename?: 'Mutation'; + createCopilotContext: string; +}; + +export type AddContextDocMutationVariables = Exact<{ + options: AddContextDocInput; +}>; + +export type AddContextDocMutation = { + __typename?: 'Mutation'; + addContextDoc: Array<{ + __typename?: 'CopilotContextListItem'; + id: string; + createdAt: number; + name: string | null; + chunkSize: number | null; + status: ContextFileStatus | null; + blobId: string | null; + }>; +}; + +export type RemoveContextDocMutationVariables = Exact<{ + options: RemoveContextFileInput; +}>; + +export type RemoveContextDocMutation = { + __typename?: 'Mutation'; + removeContextDoc: boolean; +}; + +export type ListContextQueryVariables = Exact<{ + workspaceId: Scalars['String']['input']; + sessionId: Scalars['String']['input']; +}>; + +export type ListContextQuery = { + __typename?: 'Query'; + currentUser: { + __typename?: 'UserType'; + copilot: { + __typename?: 'Copilot'; + contexts: Array<{ __typename?: 'CopilotContext'; id: string }>; + }; + } | null; +}; + +export type GetCopilotHistoryIdsQueryVariables = Exact<{ + workspaceId: Scalars['String']['input']; + docId?: InputMaybe; + options?: InputMaybe; +}>; + +export type GetCopilotHistoryIdsQuery = { + __typename?: 'Query'; + currentUser: { + __typename?: 'UserType'; + copilot: { + __typename?: 'Copilot'; + histories: Array<{ + __typename?: 'CopilotHistories'; + sessionId: string; + messages: Array<{ + __typename?: 'ChatMessage'; + id: string | null; + role: string; + createdAt: string; + }>; + }>; + }; + } | null; +}; + +export type GetCopilotHistoriesQueryVariables = Exact<{ + workspaceId: Scalars['String']['input']; + docId?: InputMaybe; + options?: InputMaybe; +}>; + +export type GetCopilotHistoriesQuery = { + __typename?: 'Query'; + currentUser: { + __typename?: 'UserType'; + copilot: { + __typename?: 'Copilot'; + histories: Array<{ + __typename?: 'CopilotHistories'; + sessionId: string; + tokens: number; + action: string | null; + createdAt: string; + messages: Array<{ + __typename?: 'ChatMessage'; + id: string | null; + role: string; + content: string; + attachments: Array | null; + createdAt: string; + }>; + }>; + }; + } | null; +}; + +export type CreateCopilotMessageMutationVariables = Exact<{ + options: CreateChatMessageInput; +}>; + +export type CreateCopilotMessageMutation = { + __typename?: 'Mutation'; + createCopilotMessage: string; +}; + +export type GetPromptsQueryVariables = Exact<{ [key: string]: never }>; + +export type GetPromptsQuery = { + __typename?: 'Query'; + listCopilotPrompts: Array<{ + __typename?: 'CopilotPromptType'; + name: string; + model: string; + action: string | null; + config: { + __typename?: 'CopilotPromptConfigType'; + jsonMode: boolean | null; + frequencyPenalty: number | null; + presencePenalty: number | null; + temperature: number | null; + topP: number | null; + } | null; + messages: Array<{ + __typename?: 'CopilotPromptMessageType'; + role: CopilotPromptMessageRole; + content: string; + params: Record | null; + }>; + }>; +}; + +export type UpdatePromptMutationVariables = Exact<{ + name: Scalars['String']['input']; + messages: Array | CopilotPromptMessageInput; +}>; + +export type UpdatePromptMutation = { + __typename?: 'Mutation'; + updateCopilotPrompt: { + __typename?: 'CopilotPromptType'; + name: string; + model: string; + action: string | null; + config: { + __typename?: 'CopilotPromptConfigType'; + jsonMode: boolean | null; + frequencyPenalty: number | null; + presencePenalty: number | null; + temperature: number | null; + topP: number | null; + } | null; + messages: Array<{ + __typename?: 'CopilotPromptMessageType'; + role: CopilotPromptMessageRole; + content: string; + params: Record | null; + }>; + }; +}; + export type CopilotQuotaQueryVariables = Exact<{ [key: string]: never }>; export type CopilotQuotaQuery = { @@ -1858,24 +2141,6 @@ export type CleanupCopilotSessionMutation = { cleanupCopilotSession: Array; }; -export type CreateCheckoutSessionMutationVariables = Exact<{ - input: CreateCheckoutSessionInput; -}>; - -export type CreateCheckoutSessionMutation = { - __typename?: 'Mutation'; - createCheckoutSession: string; -}; - -export type CreateCopilotMessageMutationVariables = Exact<{ - options: CreateChatMessageInput; -}>; - -export type CreateCopilotMessageMutation = { - __typename?: 'Mutation'; - createCopilotMessage: string; -}; - export type CreateCopilotSessionMutationVariables = Exact<{ options: CreateChatSessionInput; }>; @@ -1885,6 +2150,49 @@ export type CreateCopilotSessionMutation = { createCopilotSession: string; }; +export type ForkCopilotSessionMutationVariables = Exact<{ + options: ForkChatSessionInput; +}>; + +export type ForkCopilotSessionMutation = { + __typename?: 'Mutation'; + forkCopilotSession: string; +}; + +export type UpdateCopilotSessionMutationVariables = Exact<{ + options: UpdateChatSessionInput; +}>; + +export type UpdateCopilotSessionMutation = { + __typename?: 'Mutation'; + updateCopilotSession: string; +}; + +export type GetCopilotSessionsQueryVariables = Exact<{ + workspaceId: Scalars['String']['input']; +}>; + +export type GetCopilotSessionsQuery = { + __typename?: 'Query'; + currentUser: { + __typename?: 'UserType'; + copilot: { + __typename?: 'Copilot'; + actions: Array; + chats: Array; + }; + } | null; +}; + +export type CreateCheckoutSessionMutationVariables = Exact<{ + input: CreateCheckoutSessionInput; +}>; + +export type CreateCheckoutSessionMutation = { + __typename?: 'Mutation'; + createCheckoutSession: string; +}; + export type CreateCustomerPortalMutationVariables = Exact<{ [key: string]: never; }>; @@ -1989,15 +2297,6 @@ export type GetDocRolePermissionsQuery = { }; }; -export type ForkCopilotSessionMutationVariables = Exact<{ - options: ForkChatSessionInput; -}>; - -export type ForkCopilotSessionMutation = { - __typename?: 'Mutation'; - forkCopilotSession: string; -}; - export type CredentialsRequirementsFragment = { __typename?: 'CredentialsRequirementType'; password: { @@ -2022,79 +2321,6 @@ export type GenerateLicenseKeyMutation = { generateLicenseKey: string; }; -export type GetCopilotHistoriesQueryVariables = Exact<{ - workspaceId: Scalars['String']['input']; - docId?: InputMaybe; - options?: InputMaybe; -}>; - -export type GetCopilotHistoriesQuery = { - __typename?: 'Query'; - currentUser: { - __typename?: 'UserType'; - copilot: { - __typename?: 'Copilot'; - histories: Array<{ - __typename?: 'CopilotHistories'; - sessionId: string; - tokens: number; - action: string | null; - createdAt: string; - messages: Array<{ - __typename?: 'ChatMessage'; - id: string | null; - role: string; - content: string; - attachments: Array | null; - createdAt: string; - }>; - }>; - }; - } | null; -}; - -export type GetCopilotHistoryIdsQueryVariables = Exact<{ - workspaceId: Scalars['String']['input']; - docId?: InputMaybe; - options?: InputMaybe; -}>; - -export type GetCopilotHistoryIdsQuery = { - __typename?: 'Query'; - currentUser: { - __typename?: 'UserType'; - copilot: { - __typename?: 'Copilot'; - histories: Array<{ - __typename?: 'CopilotHistories'; - sessionId: string; - messages: Array<{ - __typename?: 'ChatMessage'; - id: string | null; - role: string; - createdAt: string; - }>; - }>; - }; - } | null; -}; - -export type GetCopilotSessionsQueryVariables = Exact<{ - workspaceId: Scalars['String']['input']; -}>; - -export type GetCopilotSessionsQuery = { - __typename?: 'Query'; - currentUser: { - __typename?: 'UserType'; - copilot: { - __typename?: 'Copilot'; - actions: Array; - chats: Array; - }; - } | null; -}; - export type GetCurrentUserFeaturesQueryVariables = Exact<{ [key: string]: never; }>; @@ -2265,32 +2491,6 @@ export type GetPageGrantedUsersListQuery = { }; }; -export type GetPromptsQueryVariables = Exact<{ [key: string]: never }>; - -export type GetPromptsQuery = { - __typename?: 'Query'; - listCopilotPrompts: Array<{ - __typename?: 'CopilotPromptType'; - name: string; - model: string; - action: string | null; - config: { - __typename?: 'CopilotPromptConfigType'; - jsonMode: boolean | null; - frequencyPenalty: number | null; - presencePenalty: number | null; - temperature: number | null; - topP: number | null; - } | null; - messages: Array<{ - __typename?: 'CopilotPromptMessageType'; - role: CopilotPromptMessageRole; - content: string; - params: Record | null; - }>; - }>; -}; - export type GetServerRuntimeConfigQueryVariables = Exact<{ [key: string]: never; }>; @@ -2855,15 +3055,6 @@ export type UpdateAccountMutation = { }; }; -export type UpdateCopilotSessionMutationVariables = Exact<{ - options: UpdateChatSessionInput; -}>; - -export type UpdateCopilotSessionMutation = { - __typename?: 'Mutation'; - updateCopilotSession: string; -}; - export type UpdateDocUserRoleMutationVariables = Exact<{ input: UpdateDocUserRoleInput; }>; @@ -2873,35 +3064,6 @@ export type UpdateDocUserRoleMutation = { updateDocUserRole: boolean; }; -export type UpdatePromptMutationVariables = Exact<{ - name: Scalars['String']['input']; - messages: Array | CopilotPromptMessageInput; -}>; - -export type UpdatePromptMutation = { - __typename?: 'Mutation'; - updateCopilotPrompt: { - __typename?: 'CopilotPromptType'; - name: string; - model: string; - action: string | null; - config: { - __typename?: 'CopilotPromptConfigType'; - jsonMode: boolean | null; - frequencyPenalty: number | null; - presencePenalty: number | null; - temperature: number | null; - topP: number | null; - } | null; - messages: Array<{ - __typename?: 'CopilotPromptMessageType'; - role: CopilotPromptMessageRole; - content: string; - params: Record | null; - }>; - }; -}; - export type UpdateServerRuntimeConfigsMutationVariables = Exact<{ updates: Scalars['JSONObject']['input']; }>; @@ -3191,30 +3353,40 @@ export type Queries = response: ListBlobsQuery; } | { - name: 'copilotQuotaQuery'; - variables: CopilotQuotaQueryVariables; - response: CopilotQuotaQuery; - } - | { - name: 'getDocRolePermissionsQuery'; - variables: GetDocRolePermissionsQueryVariables; - response: GetDocRolePermissionsQuery; - } - | { - name: 'getCopilotHistoriesQuery'; - variables: GetCopilotHistoriesQueryVariables; - response: GetCopilotHistoriesQuery; + name: 'listContextQuery'; + variables: ListContextQueryVariables; + response: ListContextQuery; } | { name: 'getCopilotHistoryIdsQuery'; variables: GetCopilotHistoryIdsQueryVariables; response: GetCopilotHistoryIdsQuery; } + | { + name: 'getCopilotHistoriesQuery'; + variables: GetCopilotHistoriesQueryVariables; + response: GetCopilotHistoriesQuery; + } + | { + name: 'getPromptsQuery'; + variables: GetPromptsQueryVariables; + response: GetPromptsQuery; + } + | { + name: 'copilotQuotaQuery'; + variables: CopilotQuotaQueryVariables; + response: CopilotQuotaQuery; + } | { name: 'getCopilotSessionsQuery'; variables: GetCopilotSessionsQueryVariables; response: GetCopilotSessionsQuery; } + | { + name: 'getDocRolePermissionsQuery'; + variables: GetDocRolePermissionsQueryVariables; + response: GetDocRolePermissionsQuery; + } | { name: 'getCurrentUserFeaturesQuery'; variables: GetCurrentUserFeaturesQueryVariables; @@ -3265,11 +3437,6 @@ export type Queries = variables: GetPageGrantedUsersListQueryVariables; response: GetPageGrantedUsersListQuery; } - | { - name: 'getPromptsQuery'; - variables: GetPromptsQueryVariables; - response: GetPromptsQuery; - } | { name: 'getServerRuntimeConfigQuery'; variables: GetServerRuntimeConfigQueryVariables; @@ -3443,25 +3610,55 @@ export type Mutations = response: ChangePasswordMutation; } | { - name: 'cleanupCopilotSessionMutation'; - variables: CleanupCopilotSessionMutationVariables; - response: CleanupCopilotSessionMutation; + name: 'createCopilotContextMutation'; + variables: CreateCopilotContextMutationVariables; + response: CreateCopilotContextMutation; } | { - name: 'createCheckoutSessionMutation'; - variables: CreateCheckoutSessionMutationVariables; - response: CreateCheckoutSessionMutation; + name: 'addContextDocMutation'; + variables: AddContextDocMutationVariables; + response: AddContextDocMutation; + } + | { + name: 'removeContextDocMutation'; + variables: RemoveContextDocMutationVariables; + response: RemoveContextDocMutation; } | { name: 'createCopilotMessageMutation'; variables: CreateCopilotMessageMutationVariables; response: CreateCopilotMessageMutation; } + | { + name: 'updatePromptMutation'; + variables: UpdatePromptMutationVariables; + response: UpdatePromptMutation; + } + | { + name: 'cleanupCopilotSessionMutation'; + variables: CleanupCopilotSessionMutationVariables; + response: CleanupCopilotSessionMutation; + } | { name: 'createCopilotSessionMutation'; variables: CreateCopilotSessionMutationVariables; response: CreateCopilotSessionMutation; } + | { + name: 'forkCopilotSessionMutation'; + variables: ForkCopilotSessionMutationVariables; + response: ForkCopilotSessionMutation; + } + | { + name: 'updateCopilotSessionMutation'; + variables: UpdateCopilotSessionMutationVariables; + response: UpdateCopilotSessionMutation; + } + | { + name: 'createCheckoutSessionMutation'; + variables: CreateCheckoutSessionMutationVariables; + response: CreateCheckoutSessionMutation; + } | { name: 'createCustomerPortalMutation'; variables: CreateCustomerPortalMutationVariables; @@ -3502,11 +3699,6 @@ export type Mutations = variables: DeleteWorkspaceMutationVariables; response: DeleteWorkspaceMutation; } - | { - name: 'forkCopilotSessionMutation'; - variables: ForkCopilotSessionMutationVariables; - response: ForkCopilotSessionMutation; - } | { name: 'generateLicenseKeyMutation'; variables: GenerateLicenseKeyMutationVariables; @@ -3597,21 +3789,11 @@ export type Mutations = variables: UpdateAccountMutationVariables; response: UpdateAccountMutation; } - | { - name: 'updateCopilotSessionMutation'; - variables: UpdateCopilotSessionMutationVariables; - response: UpdateCopilotSessionMutation; - } | { name: 'updateDocUserRoleMutation'; variables: UpdateDocUserRoleMutationVariables; response: UpdateDocUserRoleMutation; } - | { - name: 'updatePromptMutation'; - variables: UpdatePromptMutationVariables; - response: UpdatePromptMutation; - } | { name: 'updateServerRuntimeConfigsMutation'; variables: UpdateServerRuntimeConfigsMutationVariables; diff --git a/packages/frontend/native/nbstore/Cargo.toml b/packages/frontend/native/nbstore/Cargo.toml index 8cdc424466..aeaa701943 100644 --- a/packages/frontend/native/nbstore/Cargo.toml +++ b/packages/frontend/native/nbstore/Cargo.toml @@ -15,8 +15,8 @@ anyhow = { workspace = true } chrono = { workspace = true } napi = { workspace = true } napi-derive = { workspace = true } -thiserror = { workspace = true } sqlx = { workspace = true, default-features = false, features = ["chrono", "macros", "migrate", "runtime-tokio", "sqlite", "tls-rustls"] } +thiserror = { workspace = true } tokio = { workspace = true, features = ["full"] } [target.'cfg(any(target_os = "ios", target_os = "android"))'.dependencies] diff --git a/yarn.lock b/yarn.lock index 63fbbe0516..1d4ff0f3c4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -852,7 +852,7 @@ __metadata: nodemailer: "npm:^6.9.16" nodemon: "npm:^3.1.7" on-headers: "npm:^1.0.2" - openai: "npm:^4.76.2" + openai: "npm:^4.83.0" piscina: "npm:^5.0.0-alpha.0" prisma: "npm:^5.22.0" react: "npm:19.0.0" @@ -28122,7 +28122,7 @@ __metadata: languageName: node linkType: hard -"openai@npm:^4.76.2": +"openai@npm:^4.83.0": version: 4.83.0 resolution: "openai@npm:4.83.0" dependencies: