mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-18 02:21:51 +08:00
feat(server): improve context management (#15448)
#### PR Dependency Tree * **PR #15448** 👈 This tree was auto-generated by [Charcoal](https://github.com/danerwilliams/charcoal) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added workspace artifact upload, browsing, removal, deduplication, and library ownership support. * Copilot now supports scoped document and artifact search, canvas reading, live editor context, and frontend tools. * Added scope and focus selectors with source-resolution receipts in chat. * Added embedding health, progress, synchronization, and retrieval capabilities. * Added BYOK policy visibility, provider restrictions, endpoint dialect selection, and validation. * Added delegated editor interactions and userdata document authorization. * **Bug Fixes** * Improved attachment handling, cancellation, access control, retrieval fallbacks, workspace synchronization, and configuration validation. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -191,7 +191,6 @@ model Workspace {
|
||||
docs WorkspaceDoc[]
|
||||
blobs Blob[]
|
||||
ignoredDocs AiWorkspaceIgnoredDocs[]
|
||||
embedFiles AiWorkspaceFiles[]
|
||||
byokConfigs AiWorkspaceByokConfig[]
|
||||
aiUsageEvents AiUsageEvent[]
|
||||
comments Comment[]
|
||||
@@ -209,6 +208,7 @@ model Workspace {
|
||||
docAccessPolicies DocAccessPolicy[]
|
||||
docGrants DocGrant[]
|
||||
mcpCredentials McpCredential[]
|
||||
artifacts WorkspaceArtifact[]
|
||||
|
||||
@@index([lastCheckEmbeddings])
|
||||
@@index([createdAt])
|
||||
@@ -652,8 +652,6 @@ model Snapshot {
|
||||
// we need to clear all hanging updates and snapshots before enable the foreign key on workspaceId
|
||||
// workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
||||
|
||||
embedding AiWorkspaceEmbedding[]
|
||||
|
||||
@@id([workspaceId, id])
|
||||
@@index([workspaceId, updatedAt])
|
||||
@@map("snapshots")
|
||||
@@ -710,6 +708,11 @@ enum AiSessionMessageRole {
|
||||
system
|
||||
assistant
|
||||
user
|
||||
|
||||
// the database type keeps the legacy name so the previous release, whose
|
||||
// Prisma client casts enum values as "AiPromptRole", can keep writing
|
||||
// ai_sessions_messages while it runs against the same database
|
||||
@@map("AiPromptRole")
|
||||
}
|
||||
|
||||
model AiSessionMessage {
|
||||
@@ -721,10 +724,12 @@ model AiSessionMessage {
|
||||
streamObjects Json? @db.Json
|
||||
attachments Json? @db.Json
|
||||
params Json? @db.Json
|
||||
scopeSnapshot Json? @map("scope_snapshot") @db.JsonB
|
||||
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)
|
||||
session AiSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
|
||||
artifacts AiMessageArtifact[]
|
||||
|
||||
@@index([sessionId])
|
||||
@@index([sessionId, compatSubmissionId])
|
||||
@@ -747,10 +752,10 @@ model AiSession {
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
|
||||
updatedAt DateTime @default(now()) @updatedAt @map("updated_at") @db.Timestamptz(3)
|
||||
deletedAt DateTime? @map("deleted_at") @db.Timestamptz(3)
|
||||
focus Json? @db.JsonB
|
||||
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
messages AiSessionMessage[]
|
||||
context AiContext[]
|
||||
actionRuns AiActionRun[]
|
||||
|
||||
//NOTE:
|
||||
@@ -764,6 +769,50 @@ model AiSession {
|
||||
@@map("ai_sessions_metadata")
|
||||
}
|
||||
|
||||
model WorkspaceArtifact {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
workspaceId String @map("workspace_id") @db.VarChar
|
||||
contentHash String @map("content_hash") @db.VarChar
|
||||
displayName String? @map("display_name") @db.VarChar
|
||||
fileName String? @map("file_name") @db.VarChar
|
||||
canonicalMediaType String @map("canonical_media_type") @db.VarChar
|
||||
sizeBytes BigInt @map("size_bytes")
|
||||
storageScope String @map("storage_scope") @db.VarChar
|
||||
storageKey String @map("storage_key") @db.Text
|
||||
status String @db.VarChar
|
||||
libraryOwned Boolean @default(false) @map("library_owned")
|
||||
reservationExpiresAt DateTime? @map("reservation_expires_at") @db.Timestamptz(3)
|
||||
readyAt DateTime? @map("ready_at") @db.Timestamptz(3)
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
|
||||
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3)
|
||||
|
||||
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
||||
messages AiMessageArtifact[]
|
||||
|
||||
@@unique([workspaceId, contentHash])
|
||||
@@unique([workspaceId, id])
|
||||
@@index([workspaceId, status])
|
||||
@@index([status, reservationExpiresAt])
|
||||
@@map("workspace_artifacts")
|
||||
}
|
||||
|
||||
model AiMessageArtifact {
|
||||
messageId String @map("message_id") @db.VarChar
|
||||
workspaceId String @map("workspace_id") @db.VarChar
|
||||
artifactId String @map("artifact_id") @db.Uuid
|
||||
role String @db.VarChar
|
||||
displayName String? @map("display_name") @db.VarChar
|
||||
metadata Json? @db.JsonB
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
|
||||
|
||||
message AiSessionMessage @relation(fields: [messageId], references: [id], onDelete: Cascade)
|
||||
artifact WorkspaceArtifact @relation(fields: [workspaceId, artifactId], references: [workspaceId, id], onDelete: Cascade)
|
||||
|
||||
@@id([messageId, artifactId, role])
|
||||
@@index([workspaceId, artifactId])
|
||||
@@map("ai_message_artifacts")
|
||||
}
|
||||
|
||||
model AiActionRun {
|
||||
id String @id @default(uuid()) @db.VarChar
|
||||
userId String @map("user_id") @db.VarChar
|
||||
@@ -821,59 +870,6 @@ model AiTranscriptTask {
|
||||
@@map("ai_transcript_tasks")
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
embeddings AiContextEmbedding[]
|
||||
session AiSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@map("ai_contexts")
|
||||
}
|
||||
|
||||
model AiContextEmbedding {
|
||||
id String @id @default(uuid()) @db.VarChar
|
||||
contextId String @map("context_id") @db.VarChar
|
||||
fileId String @map("file_id") @db.VarChar
|
||||
// a file can be divided into multiple chunks and embedded separately.
|
||||
chunk Int @db.Integer
|
||||
content String @db.VarChar
|
||||
embedding Unsupported("vector(1024)")
|
||||
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
|
||||
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3)
|
||||
|
||||
context AiContext @relation(fields: [contextId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([contextId, fileId, chunk])
|
||||
@@index([embedding], map: "ai_context_embeddings_idx")
|
||||
@@map("ai_context_embeddings")
|
||||
}
|
||||
|
||||
model AiWorkspaceEmbedding {
|
||||
workspaceId String @map("workspace_id") @db.VarChar
|
||||
docId String @map("doc_id") @db.VarChar
|
||||
// a doc can be divided into multiple chunks and embedded separately.
|
||||
chunk Int @db.Integer
|
||||
content String @db.VarChar
|
||||
embedding Unsupported("vector(1024)")
|
||||
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
|
||||
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3)
|
||||
|
||||
// workspace level search not available for non-cloud workspaces
|
||||
// so we can match this record with the snapshot one by one
|
||||
snapshot Snapshot @relation(fields: [workspaceId, docId], references: [workspaceId, id], onDelete: Cascade)
|
||||
|
||||
@@id([workspaceId, docId, chunk])
|
||||
@@index([embedding], map: "ai_workspace_embeddings_idx")
|
||||
@@map("ai_workspace_embeddings")
|
||||
}
|
||||
|
||||
model AiWorkspaceIgnoredDocs {
|
||||
workspaceId String @map("workspace_id") @db.VarChar
|
||||
docId String @map("doc_id") @db.VarChar
|
||||
@@ -886,78 +882,26 @@ model AiWorkspaceIgnoredDocs {
|
||||
@@map("ai_workspace_ignored_docs")
|
||||
}
|
||||
|
||||
model AiWorkspaceFiles {
|
||||
workspaceId String @map("workspace_id") @db.VarChar
|
||||
fileId String @map("file_id") @db.VarChar
|
||||
blobId String @default("") @map("blob_id") @db.VarChar
|
||||
fileName String @map("file_name") @db.VarChar
|
||||
mimeType String @map("mime_type") @db.VarChar
|
||||
size Int @db.Integer
|
||||
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
|
||||
|
||||
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
||||
|
||||
embeddings AiWorkspaceFileEmbedding[]
|
||||
|
||||
@@id([workspaceId, fileId])
|
||||
@@map("ai_workspace_files")
|
||||
}
|
||||
|
||||
model AiWorkspaceFileEmbedding {
|
||||
workspaceId String @map("workspace_id") @db.VarChar
|
||||
fileId String @map("file_id") @db.VarChar
|
||||
// a file can be divided into multiple chunks and embedded separately.
|
||||
chunk Int @db.Integer
|
||||
content String @db.VarChar
|
||||
embedding Unsupported("vector(1024)")
|
||||
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
|
||||
|
||||
file AiWorkspaceFiles @relation(fields: [workspaceId, fileId], references: [workspaceId, fileId], onDelete: Cascade)
|
||||
|
||||
@@id([workspaceId, fileId, chunk])
|
||||
@@index([embedding], map: "ai_workspace_file_embeddings_idx")
|
||||
@@map("ai_workspace_file_embeddings")
|
||||
}
|
||||
|
||||
model AiWorkspaceBlobEmbedding {
|
||||
workspaceId String @map("workspace_id") @db.VarChar
|
||||
blobId String @map("blob_id") @db.VarChar
|
||||
// a file can be divided into multiple chunks and embedded separately.
|
||||
chunk Int @db.Integer
|
||||
content String @db.VarChar
|
||||
embedding Unsupported("vector(1024)")
|
||||
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
|
||||
|
||||
blob Blob @relation(fields: [workspaceId, blobId], references: [workspaceId, key], onDelete: Cascade)
|
||||
|
||||
@@id([workspaceId, blobId, chunk])
|
||||
@@index([embedding], map: "ai_workspace_blob_embeddings_idx")
|
||||
@@map("ai_workspace_blob_embeddings")
|
||||
}
|
||||
|
||||
model AiWorkspaceByokConfig {
|
||||
id String @id @default(uuid()) @db.VarChar
|
||||
workspaceId String @map("workspace_id") @db.VarChar
|
||||
provider String @db.VarChar
|
||||
name String @db.VarChar
|
||||
description String? @db.VarChar
|
||||
encryptedApiKey String @map("encrypted_api_key") @db.Text
|
||||
definition Json @db.JsonB
|
||||
revision Int @default(1)
|
||||
credentialGeneration Int @default(1) @map("credential_generation")
|
||||
validation Json? @db.JsonB
|
||||
sortOrder Int @default(0) @map("sort_order")
|
||||
enabled Boolean @default(true)
|
||||
lastUsedAt DateTime? @map("last_used_at") @db.Timestamptz(3)
|
||||
lastErrorAt DateTime? @map("last_error_at") @db.Timestamptz(3)
|
||||
lastError String? @map("last_error") @db.Text
|
||||
createdBy String? @map("created_by") @db.VarChar
|
||||
updatedBy String? @map("updated_by") @db.VarChar
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
|
||||
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3)
|
||||
id String @id @default(uuid()) @db.VarChar
|
||||
workspaceId String @map("workspace_id") @db.VarChar
|
||||
provider String @db.VarChar
|
||||
name String @db.VarChar
|
||||
description String? @db.VarChar
|
||||
encryptedApiKey String @map("encrypted_api_key") @db.Text
|
||||
definition Json @db.JsonB
|
||||
revision Int @default(1)
|
||||
credentialGeneration Int @default(1) @map("credential_generation")
|
||||
validation Json? @db.JsonB
|
||||
sortOrder Int @default(0) @map("sort_order")
|
||||
enabled Boolean @default(true)
|
||||
lastUsedAt DateTime? @map("last_used_at") @db.Timestamptz(3)
|
||||
lastErrorAt DateTime? @map("last_error_at") @db.Timestamptz(3)
|
||||
lastError String? @map("last_error") @db.Text
|
||||
createdBy String? @map("created_by") @db.VarChar
|
||||
updatedBy String? @map("updated_by") @db.VarChar
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
|
||||
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3)
|
||||
|
||||
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@ -1209,8 +1153,7 @@ model Blob {
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
|
||||
deletedAt DateTime? @map("deleted_at") @db.Timestamptz(3)
|
||||
|
||||
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
||||
AiWorkspaceBlobEmbedding AiWorkspaceBlobEmbedding[]
|
||||
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@id([workspaceId, key])
|
||||
@@index([workspaceId, status, deletedAt])
|
||||
|
||||
Reference in New Issue
Block a user