feat: update schema & bump deps (#15455)
#### PR Dependency Tree * **PR #15455** 👈 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 management, artifact details, and removal support. - Added workspace BYOK configuration insights, model capabilities, connection checks, and validation status. - Added profile reordering and credential revision safeguards. - **Improvements** - Improved Copilot session creation with server-provided history and metadata. - Streamlined context handling with document scope selections and selected-source synchronization. - **Bug Fixes** - Improved compatibility and reliability across web, Android, and iOS Copilot workflows. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -16,7 +16,7 @@ let package = Package(
|
||||
dependencies: [
|
||||
.package(path: "../AffineGraphQL"),
|
||||
.package(path: "../AffineResources"),
|
||||
.package(url: "https://github.com/apollographql/apollo-ios.git", from: "1.25.4"),
|
||||
.package(url: "https://github.com/apollographql/apollo-ios.git", from: "1.25.7"),
|
||||
.package(url: "https://github.com/apple/swift-collections.git", from: "1.6.0"),
|
||||
.package(url: "https://github.com/SnapKit/SnapKit.git", from: "5.7.1"),
|
||||
.package(url: "https://github.com/SwifterSwift/SwifterSwift.git", from: "6.2.0"),
|
||||
|
||||
@@ -55,10 +55,10 @@ public extension ChatManager {
|
||||
}
|
||||
|
||||
DispatchQueue.global().async {
|
||||
self.prepareContext(
|
||||
self.startCopilotResponse(
|
||||
workspaceId: workspaceId,
|
||||
sessionId: sessionId,
|
||||
editorData: editorData,
|
||||
sessionId: sessionId,
|
||||
viewModelId: viewModelId
|
||||
)
|
||||
}
|
||||
@@ -66,108 +66,15 @@ public extension ChatManager {
|
||||
}
|
||||
|
||||
private extension ChatManager {
|
||||
func prepareContext(
|
||||
workspaceId: String,
|
||||
sessionId: String,
|
||||
editorData: InputBoxData,
|
||||
viewModelId: UUID
|
||||
) {
|
||||
assert(!Thread.isMainThread)
|
||||
let createContext = CreateCopilotContextMutation(
|
||||
workspaceId: workspaceId,
|
||||
sessionId: sessionId
|
||||
)
|
||||
QLService.shared.client.perform(mutation: createContext) { result in
|
||||
DispatchQueue.main.async {
|
||||
switch result {
|
||||
case let .success(graphQLResult):
|
||||
guard let contextId = graphQLResult.data?.createCopilotContext else {
|
||||
self.report(sessionId, ChatError.invalidResponse)
|
||||
return
|
||||
}
|
||||
print("[+] copilot context created: \(contextId)")
|
||||
|
||||
DispatchQueue.global().async {
|
||||
let docAttachGroup = DispatchGroup()
|
||||
for docAttach in editorData.documentAttachments {
|
||||
let addDoc = AddContextDocMutation(
|
||||
options: .init(
|
||||
contextId: contextId,
|
||||
docId: docAttach.documentID
|
||||
)
|
||||
)
|
||||
docAttachGroup.enter()
|
||||
QLService.shared.client.perform(mutation: addDoc) { result in
|
||||
switch result {
|
||||
case .success:
|
||||
print("[+] doc \(docAttach.documentID) added to context")
|
||||
case let .failure(error):
|
||||
print("[-] addContextDoc failed: \(error)")
|
||||
}
|
||||
docAttachGroup.leave()
|
||||
}
|
||||
}
|
||||
|
||||
docAttachGroup.notify(queue: .global()) {
|
||||
var contextSnippet = ""
|
||||
if !editorData.documentAttachments.isEmpty {
|
||||
let sem = DispatchSemaphore(value: 0)
|
||||
let matchQuery = MatchContextQuery(
|
||||
contextId: .some(contextId),
|
||||
workspaceId: .some(workspaceId),
|
||||
content: editorData.text,
|
||||
limit: .none,
|
||||
scopedThreshold: .none,
|
||||
threshold: .none
|
||||
)
|
||||
QLService.shared.client.fetch(query: matchQuery) { result in
|
||||
switch result {
|
||||
case let .success(queryResult):
|
||||
let matches = queryResult.data?.currentUser?.copilot.contexts ?? []
|
||||
let matchDocs = matches.compactMap(\.matchWorkspaceDocs).flatMap(\.self)
|
||||
for context in matchDocs {
|
||||
contextSnippet += "<file docId=\"\(context.docId)\" chunk=\"\(context.chunk)\">\(context.content)</file>\n"
|
||||
}
|
||||
case let .failure(error):
|
||||
print("[-] matchContext failed: \(error)")
|
||||
// self.report(sessionId, error)
|
||||
}
|
||||
sem.signal()
|
||||
}
|
||||
sem.wait()
|
||||
}
|
||||
print("[+] context snippet prepared: \(contextSnippet)")
|
||||
self.startCopilotResponse(
|
||||
editorData: editorData,
|
||||
contextSnippet: contextSnippet,
|
||||
sessionId: sessionId,
|
||||
viewModelId: viewModelId
|
||||
)
|
||||
}
|
||||
}
|
||||
case let .failure(error):
|
||||
self.report(sessionId, error)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func startCopilotResponse(
|
||||
workspaceId: String,
|
||||
editorData: InputBoxData,
|
||||
contextSnippet: String,
|
||||
sessionId: String,
|
||||
viewModelId: UUID
|
||||
) {
|
||||
assert(!Thread.isMainThread)
|
||||
print("[+] starting copilot response for session: \(sessionId)")
|
||||
|
||||
let messageParameters: AffineGraphQL.JSON = [
|
||||
// packages/frontend/core/src/blocksuite/ai/provider/setup-provider.tsx
|
||||
"docs": editorData.documentAttachments.map(\.documentID), // affine doc
|
||||
"files": [String](), // attachment in context, keep nil for now
|
||||
"searchMode": editorData.isSearchEnabled ? "MUST" : "AUTO",
|
||||
]
|
||||
let uploadableAttachments: [CopilotAttachmentUpload] = [
|
||||
editorData.fileAttachments.map { file -> CopilotAttachmentUpload in
|
||||
.init(
|
||||
@@ -185,13 +92,28 @@ private extension ChatManager {
|
||||
},
|
||||
].flatMap(\.self)
|
||||
assert(uploadableAttachments.allSatisfy { !$0.data.isEmpty })
|
||||
let messageContent = contextSnippet.isEmpty ? editorData.text : "\(contextSnippet)\n\(editorData.text)"
|
||||
Task {
|
||||
do {
|
||||
let selectedDocumentIds = editorData.documentAttachments.map(\.documentID)
|
||||
if !selectedDocumentIds.isEmpty {
|
||||
try await IntelligentContext.shared.waitForSelectedSources(selectedDocumentIds)
|
||||
}
|
||||
let messageParameters: AffineGraphQL.JSON = [
|
||||
// packages/frontend/core/src/blocksuite/ai/provider/setup-provider.tsx
|
||||
"scopeSelectors": editorData.documentAttachments.map { attachment in
|
||||
[
|
||||
"kind": "document",
|
||||
"id": attachment.documentID,
|
||||
"name": attachment.title,
|
||||
"source": "draft",
|
||||
]
|
||||
},
|
||||
"searchMode": editorData.isSearchEnabled ? "MUST" : "AUTO",
|
||||
]
|
||||
let messageIdentifier = try await QLService.shared.createCopilotMessage(
|
||||
workspaceId: IntelligentContext.shared.currentWorkspaceId ?? "",
|
||||
workspaceId: workspaceId,
|
||||
sessionId: sessionId,
|
||||
content: messageContent,
|
||||
content: editorData.text,
|
||||
params: messageParameters,
|
||||
attachments: uploadableAttachments
|
||||
)
|
||||
|
||||
@@ -20,10 +20,13 @@ enum BridgedWindowScript: String {
|
||||
case getAiButtonFeatureFlag = "window.getAiButtonFeatureFlag();"
|
||||
case getCurrentI18nLocale = "window.getCurrentI18nLocale();"
|
||||
case createNewDocByMarkdownInCurrentWorkspace = "return await window.createNewDocByMarkdownInCurrentWorkspace(markdown, title);"
|
||||
case waitForSelectedSources = "return await window.waitForSelectedSources(documentIds);"
|
||||
|
||||
var requiresAsyncContext: Bool {
|
||||
switch self {
|
||||
case .getCurrentDocContentInMarkdown, .createNewDocByMarkdownInCurrentWorkspace: true
|
||||
case .getCurrentDocContentInMarkdown,
|
||||
.createNewDocByMarkdownInCurrentWorkspace,
|
||||
.waitForSelectedSources: true
|
||||
default: false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,27 +25,27 @@ public extension IntelligentContext {
|
||||
workspaceId: workspaceId
|
||||
)
|
||||
|
||||
let mutation = CreateCopilotSessionMutation(options: input)
|
||||
let mutation = CreateCopilotSessionWithHistoryMutation(options: input)
|
||||
|
||||
QLService.shared.client.perform(mutation: mutation) { result in
|
||||
switch result {
|
||||
case let .success(graphQLResult):
|
||||
guard let sessionId = graphQLResult.data?.createCopilotSession else {
|
||||
guard let history = graphQLResult.data?.createCopilotSessionWithHistory else {
|
||||
completion(.failure(IntelligentError.sessionCreationFailed("No session ID returned.")))
|
||||
return
|
||||
}
|
||||
|
||||
let session = ChatSessionObject(
|
||||
id: sessionId,
|
||||
workspaceId: workspaceId,
|
||||
docId: docId,
|
||||
promptName: promptName.rawValue,
|
||||
id: history.sessionId,
|
||||
workspaceId: history.workspaceId,
|
||||
docId: history.docId,
|
||||
promptName: history.promptName,
|
||||
model: nil,
|
||||
pinned: pinned,
|
||||
pinned: history.pinned,
|
||||
tokens: 0,
|
||||
createdAt: DateTime(date: Date()),
|
||||
updatedAt: DateTime(date: Date()),
|
||||
parentSessionId: nil
|
||||
createdAt: history.createdAt,
|
||||
updatedAt: history.updatedAt,
|
||||
parentSessionId: history.parentSessionId
|
||||
)
|
||||
completion(.success(session))
|
||||
|
||||
|
||||
@@ -9,6 +9,16 @@ import UIKit
|
||||
import WebKit
|
||||
|
||||
extension IntelligentContext {
|
||||
@MainActor
|
||||
func waitForSelectedSources(_ documentIds: [String]) async throws {
|
||||
_ = try await webView.callAsyncJavaScript(
|
||||
BridgedWindowScript.waitForSelectedSources.rawValue,
|
||||
arguments: ["documentIds": documentIds],
|
||||
in: nil,
|
||||
contentWorld: .page
|
||||
)
|
||||
}
|
||||
|
||||
func prepareMetadataFrom(webView: WKWebView, completion: @escaping ([WebViewMetadataKey: Any]) -> Void) {
|
||||
var newMetadata: [WebViewMetadataKey: Any] = [:]
|
||||
let dispatchGroup = DispatchGroup()
|
||||
|
||||
Reference in New Issue
Block a user