diff --git a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/ChatManager/ChatManager+Stream.swift b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/ChatManager/ChatManager+Stream.swift index 2662ad5fec..2d453d5e7d 100644 --- a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/ChatManager/ChatManager+Stream.swift +++ b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/ChatManager/ChatManager+Stream.swift @@ -160,6 +160,7 @@ private extension ChatManager { viewModelId: UUID ) { assert(!Thread.isMainThread) + print("[+] starting copilot response for session: \(sessionId)") let messageParameters: [String: AnyHashable] = [ // packages/frontend/core/src/blocksuite/ai/provider/setup-provider.tsx @@ -167,11 +168,11 @@ private extension ChatManager { "files": [String](), // attachment in context, keep nil for now "searchMode": editorData.isSearchEnabled ? "MUST" : "AUTO", ] - let hasMultipleAttachmentBlobs = [ + let attachmentCount = [ editorData.fileAttachments.count, editorData.documentAttachments.count, - ].reduce(0, +) > 1 - let attachmentFieldName = hasMultipleAttachmentBlobs ? "options.blobs" : "options.blob" + ].reduce(0, +) + let attachmentFieldName = attachmentCount > 1 && attachmentCount != 0 ? "options.blobs" : "options.blob" let uploadableAttachments: [GraphQLFile] = [ editorData.fileAttachments.map { file -> GraphQLFile in .init(fieldName: attachmentFieldName, originalName: file.name, data: file.data ?? .init()) @@ -183,8 +184,8 @@ private extension ChatManager { assert(uploadableAttachments.allSatisfy { !($0.data?.isEmpty ?? true) }) guard let input = try? CreateChatMessageInput( attachments: [], - blob: hasMultipleAttachmentBlobs ? .none : "", - blobs: hasMultipleAttachmentBlobs ? .some([]) : .none, + blob: .none, + blobs: attachmentCount > 1 && attachmentCount != 0 ? .some([]) : .none, content: .some(contextSnippet.isEmpty ? editorData.text : "\(contextSnippet)\n\(editorData.text)"), params: .some(AffineGraphQL.JSON(_jsonValue: messageParameters)), sessionId: sessionId @@ -195,11 +196,13 @@ private extension ChatManager { } let mutation = CreateCopilotMessageMutation(options: input) QLService.shared.client.upload(operation: mutation, files: uploadableAttachments) { result in + print("[*] createCopilotMessage result: \(result)") DispatchQueue.main.async { switch result { case let .success(graphQLResult): guard let messageIdentifier = graphQLResult.data?.createCopilotMessage else { self.report(sessionId, ChatError.invalidResponse) + self.delete(sessionId: sessionId, vmId: viewModelId) return } self.startStreamingResponse( @@ -217,6 +220,7 @@ private extension ChatManager { private extension ChatManager { func startStreamingResponse(sessionId: String, messageId: String, applyingTo vmId: UUID) { + print("[+] starting streaming response for session: \(sessionId), message: \(messageId)") let base = IntelligentContext.shared.webViewMetadata[.currentServerBaseUrl] as? String guard let base, let url = URL(string: base) else { report(sessionId, ChatError.invalidServerConfiguration) diff --git a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/ChatManager/ChatManager.swift b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/ChatManager/ChatManager.swift index cc63c25b0f..7b1d640f57 100644 --- a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/ChatManager/ChatManager.swift +++ b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/ChatManager/ChatManager.swift @@ -68,6 +68,10 @@ public class ChatManager: ObservableObject, @unchecked Sendable { } } } + + public func delete(sessionId: String, vmId: UUID) { + with(sessionId: sessionId) { $0.removeValue(forKey: vmId) } + } @discardableResult public func append(sessionId: String, _ viewModel: any ChatCellViewModel) -> UUID { diff --git a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/ChatCell/ErrorCell.swift b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/ChatCell/ErrorCell.swift index 08feaf021c..f84b2856c6 100644 --- a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/ChatCell/ErrorCell.swift +++ b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/ChatCell/ErrorCell.swift @@ -11,24 +11,61 @@ import Then import UIKit class ErrorCell: ChatBaseCell { + let label = UILabel() + override func prepareContentView(inside contentView: UIView) { super.prepareContentView(inside: contentView) + contentView.addSubview(label) } override func prepareForReuse() { super.prepareForReuse() + label.attributedText = nil } override func layoutContentView(bounds: CGRect) { super.layoutContentView(bounds: bounds) + let width = bounds.width * 0.8 + label.frame = .init( + x: (bounds.width - width) / 2, + y: 0, + width: width, + height: bounds.height + ) + } + + override func configure(with viewModel: any ChatCellViewModel) { + super.configure(with: viewModel) + guard let vm = viewModel as? ErrorCellViewModel else { + assertionFailure("Invalid view model type") + return + } + label.attributedText = Self.attributeText(for: vm.errorMessage) } + static func attributeText(for text: String) -> NSAttributedString { + return .init(string: text, attributes: [ + .font: UIFont.preferredFont(forTextStyle: .footnote), + .foregroundColor: UIColor.affineTextSecondary, + .paragraphStyle: NSMutableParagraphStyle().then { + $0.lineBreakMode = .byWordWrapping + $0.alignment = .center + } + ]) + } + override class func heightForContent( for viewModel: any ChatCellViewModel, width: CGFloat ) -> CGFloat { - _ = viewModel - _ = width - return 0 + let vm = viewModel as! ErrorCellViewModel + let text = Self.attributeText(for: vm.errorMessage) + let boundingRect = text.boundingRect( + with: CGSize(width: width * 0.8, height: .greatestFiniteMagnitude), + options: [.usesLineFragmentOrigin, .usesFontLeading], + context: nil + ) + let boundingSize = boundingRect.size + return ceil(boundingSize.height) } }