Commit e508c338 authored by Steven杜宇's avatar Steven杜宇

// AI

parent 0f5e2488
...@@ -114,26 +114,29 @@ class YHAIMainChatViewController: YHBaseViewController { ...@@ -114,26 +114,29 @@ class YHAIMainChatViewController: YHBaseViewController {
func reloadAndScrollToBottom(_ forceScrollToBottom: Bool = false, _ isNeedAccurate: Bool = false) { func reloadAndScrollToBottom(_ forceScrollToBottom: Bool = false, _ isNeedAccurate: Bool = false) {
self.tableView.reloadData() self.tableView.reloadData()
if !forceScrollToBottom && !canTriggerProgrammaticScroll() { if !forceScrollToBottom && !canTriggerProgrammaticScroll() {
return return
} }
DispatchQueue.main.asyncAfter(deadline: .now()+0.5) { DispatchQueue.main.async {
[weak self] in
guard let self = self else { return }
if isNeedAccurate { // 使用 UIView.animate 实现更丝滑的滚动效果
// self.tableView.setContentOffset(.zero, animated: true) UIView.animate(withDuration: 0.5,
// self.tableView.setContentOffset(CGPointMake(0, 90000), animated: true) delay: 0,
} usingSpringWithDamping: 1.0, // 弹簧阻尼,控制回弹效果
initialSpringVelocity: 0.3, // 初始速度
options: [.curveEaseOut, .allowUserInteraction],
// 使用 performBatchUpdates 来确保所有布局更新完成后再滚动 animations: {
self.tableView.performBatchUpdates(nil) { _ in
// 在布局更新完成后执行滚动
if self.messages.count > 0 { if self.messages.count > 0 {
let lastIndexPath = IndexPath(row: self.messages.count-1, section: 0) let lastIndexPath = IndexPath(row: self.messages.count-1, section: 0)
self.tableView.scrollToRow(at: lastIndexPath, at: .bottom, animated: true) self.tableView.scrollToRow(at: lastIndexPath, at: .bottom, animated: false)
} }
}
}, completion: nil)
} }
} }
...@@ -148,21 +151,16 @@ class YHAIMainChatViewController: YHBaseViewController { ...@@ -148,21 +151,16 @@ class YHAIMainChatViewController: YHBaseViewController {
self.reloadAndScrollToBottom(forceScrollToBottom) self.reloadAndScrollToBottom(forceScrollToBottom)
self.bottomInputView.status = .loading self.bottomInputView.status = .loading
self.chatConfig.disableHandleMessage = false self.chatConfig.disableHandleMessage = false
self.manager.disableHandleMessage = false
self.manager.requestAI(botId: self.robotId, conversationId: self.conversationId, question:text) { self.manager.requestAI(botId: self.robotId, conversationId: self.conversationId, question:text) {
[weak self] res, done in [weak self] res, done in
guard let self = self else { return } guard let self = self else { return }
self.chatConfig.handleReceiveMessage(res, done, &messages)
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { if done {
[weak self] in print("RESPONSE-DONE")
guard let self = self else { return } self.bottomInputView.status = self.bottomInputView.textView.text.isEmpty ? .disableSend : .enableSend
self.chatConfig.handleReceiveMessage(res, done, &messages)
if done {
print("RESPONSE-DONE")
self.bottomInputView.status = self.bottomInputView.textView.text.isEmpty ? .disableSend : .enableSend
}
self.reloadAndScrollToBottom(forceScrollToBottom)
} }
self.reloadAndScrollToBottom(forceScrollToBottom)
} }
} }
} }
...@@ -202,19 +200,15 @@ class YHAIMainChatViewController: YHBaseViewController { ...@@ -202,19 +200,15 @@ class YHAIMainChatViewController: YHBaseViewController {
} }
func stopAutoResponse(completion:((Bool)->())? = nil) { func stopAutoResponse(completion:((Bool)->())? = nil) {
self.manager.cancelCurrentRequest()
self.reloadAndScrollToBottom()
self.chatConfig.disableHandleMessage = true
self.manager.disableHandleMessage = true
self.chatConfig.removeThinkingMessageFromChatList(&self.messages)
self.bottomInputView.status = self.bottomInputView.textView.text.isEmpty ? .disableSend : .enableSend
completion?(true)
self.manager.stopChat(chatId: self.manager.chatId, conversationId: self.conversationId) { success, error in self.manager.stopChat(chatId: self.manager.chatId, conversationId: self.conversationId) { success, error in
self.bottomInputView.status = self.bottomInputView.textView.text.isEmpty ? .disableSend : .enableSend
if success {
self.reloadAndScrollToBottom()
self.manager.request?.cancel()
self.chatConfig.disableHandleMessage = true
DispatchQueue.main.asyncAfter(deadline: .now()+0.5) {
self.chatConfig.removeThinkingMessageFromChatList(&self.messages)
self.bottomInputView.status = self.bottomInputView.textView.text.isEmpty ? .disableSend : .enableSend
self.tableView.reloadData()
completion?(success)
}
}
} }
} }
...@@ -272,9 +266,7 @@ extension YHAIMainChatViewController: UITableViewDelegate, UITableViewDataSource ...@@ -272,9 +266,7 @@ extension YHAIMainChatViewController: UITableViewDelegate, UITableViewDataSource
guard let self = self else { return } guard let self = self else { return }
if self.isNeedStopResonse() { if self.isNeedStopResonse() {
self.stopAutoResponse { success in self.stopAutoResponse { success in
if success { self.sendMessage(text)
self.sendMessage(text)
}
} }
} else { } else {
self.sendMessage(text) self.sendMessage(text)
...@@ -354,7 +346,44 @@ extension YHAIMainChatViewController: UITableViewDelegate, UITableViewDataSource ...@@ -354,7 +346,44 @@ extension YHAIMainChatViewController: UITableViewDelegate, UITableViewDataSource
} }
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
let message = messages[indexPath.row]
let msgType = message.getType()
if !message.isTextMessage() {
return UITableView.automaticDimension
}
let text = message.body.contentText // 要显示的文本内容
let font = UIFont.PFSC_R(ofSize: 14) // 字体大小
let maxWidth = KScreenWidth-20*2-16*2 // 最大宽度限制
let attributes = [NSAttributedString.Key.font : font] as [NSAttributedString.Key : Any]
let size = (text as NSString).boundingRect(with: CGSize(width: maxWidth, height: .greatestFiniteMagnitude), options: .usesLineFragmentOrigin, attributes: attributes, context: nil).size
var textHeight = ceil(size.height)
if textHeight < 20.0 {
textHeight = 20.0
}
if message.isSelf || message.getType() == .recommendText {
return textHeight + 16.0*2 + 16.0
}
let complete = message.isDone
let isNeedShowCopyView = complete
let isNeedShowLoadingView = !complete
var resultHeight = textHeight+16.0*2.0
if isNeedShowLoadingView {
resultHeight += (18+6)
}
if isNeedShowCopyView {
resultHeight += (16+37)
} else {
resultHeight += 16
}
return resultHeight
} }
......
...@@ -27,6 +27,9 @@ class YHAIRequestManager: NSObject { ...@@ -27,6 +27,9 @@ class YHAIRequestManager: NSObject {
var uuid = UUID().uuidString + NSDate().timeIntervalSince1970.description var uuid = UUID().uuidString + NSDate().timeIntervalSince1970.description
var chatId = "" var chatId = ""
var disableHandleMessage: Bool = false
func getCommonHeaders() -> HTTPHeaders { func getCommonHeaders() -> HTTPHeaders {
var requestHeader = HTTPHeaders() var requestHeader = HTTPHeaders()
...@@ -58,10 +61,19 @@ class YHAIRequestManager: NSObject { ...@@ -58,10 +61,19 @@ class YHAIRequestManager: NSObject {
} }
return requestHeader return requestHeader
} }
func cancelCurrentRequest() {
self.request?.responseStreamString {
stream in
}
self.request?.cancel()
self.request = nil
}
func requestAI(botId: String, conversationId: String, question: String = "", callback: ((_ msg: YHAIChatMessage?, _ done: Bool) -> Void)?) { func requestAI(botId: String, conversationId: String, question: String = "", callback: ((_ msg: YHAIChatMessage?, _ done: Bool) -> Void)?) {
self.disableHandleMessage = false
let url = YHBaseUrlManager.shared.curURL() + YHAllApiName.AIChat.agentChat let url = YHBaseUrlManager.shared.curURL() + YHAllApiName.AIChat.agentChat
self.uuid = UUID().uuidString + NSDate().timeIntervalSince1970.description self.uuid = UUID().uuidString + NSDate().timeIntervalSince1970.description
...@@ -86,6 +98,10 @@ class YHAIRequestManager: NSObject { ...@@ -86,6 +98,10 @@ class YHAIRequestManager: NSObject {
[weak self] stream in [weak self] stream in
guard let self = self else { return } guard let self = self else { return }
if self.disableHandleMessage {
return
}
switch stream.event { switch stream.event {
case let .stream(result): case let .stream(result):
...@@ -100,15 +116,15 @@ class YHAIRequestManager: NSObject { ...@@ -100,15 +116,15 @@ class YHAIRequestManager: NSObject {
case let .failure(error): case let .failure(error):
print("\(error)") print("\(error)")
// 一段话结束需要重新生成uuid 来 // 一段话结束需要重新生成uuid 来
self.uuid = UUID().uuidString + NSDate().timeIntervalSince1970.description // self.uuid = UUID().uuidString + NSDate().timeIntervalSince1970.description
callback?(nil, true) // callback?(nil, true)
} }
case let .complete(completion): case let .complete(completion):
print("COMPLETE") print("COMPLETE")
// 一段话结束需要重新生成uuid 来 // 一段话结束需要重新生成uuid 来
self.uuid = UUID().uuidString + NSDate().timeIntervalSince1970.description // self.uuid = UUID().uuidString + NSDate().timeIntervalSince1970.description
callback?(nil, true) // callback?(nil, true)
if let af_error = completion.error { if let af_error = completion.error {
...@@ -166,7 +182,7 @@ class YHAIRequestManager: NSObject { ...@@ -166,7 +182,7 @@ class YHAIRequestManager: NSObject {
} }
// 处理data字符串 // 处理data字符串
func handle(dataString: String, completion:((_ : YHAIChatMessage, _ done: Bool) ->())?) { func handle(dataString: String, completion:((_ : YHAIChatMessage?, _ done: Bool) ->())?) {
let arr = dataString.components(separatedBy: "\n\n") let arr = dataString.components(separatedBy: "\n\n")
if arr.count <= 0 { if arr.count <= 0 {
...@@ -229,36 +245,40 @@ class YHAIRequestManager: NSObject { ...@@ -229,36 +245,40 @@ class YHAIRequestManager: NSObject {
self.uuid = UUID().uuidString + NSDate().timeIntervalSince1970.description self.uuid = UUID().uuidString + NSDate().timeIntervalSince1970.description
} }
// if receiveMessage.isUserfulMessage() {
// completion?(receiveMessage, sessionDone)
// }
if receiveMessage.isUserfulMessage() { if receiveMessage.isUserfulMessage() {
if receiveMessage.isNeedSpiceMessage() { if receiveMessage.body.isDone() {
let text = receiveMessage.body.contentText completion?(nil, sessionDone)
if text.count > 1 {
let textArray = Array(text)
for (index, character) in textArray.enumerated() {
let msg = receiveMessage.copy() as! YHAIChatMessage
msg.body.contentText = String(character)
msg.body.status = ""
if index == textArray.count-1 {
msg.body.status = receiveMessage.body.status
}
msg.updateBodyToData()
printLog("AAAAA- \(msg)")
sessionDone = receiveMessage.body.isDone()
completion?(msg, sessionDone)
}
} else {
completion?(receiveMessage, sessionDone)
}
} else { } else {
completion?(receiveMessage, sessionDone) completion?(receiveMessage, sessionDone)
} }
} }
// if receiveMessage.isUserfulMessage() {
// if receiveMessage.isNeedSpiceMessage() {
// let text = receiveMessage.body.contentText
// if text.count > 1 {
// let textArray = Array(text)
// for (index, character) in textArray.enumerated() {
// let msg = receiveMessage.copy() as! YHAIChatMessage
// msg.body.contentText = String(character)
// msg.body.status = ""
// if index == textArray.count-1 {
// msg.body.status = receiveMessage.body.status
// }
// msg.updateBodyToData()
// printLog("AAAAA- \(msg)")
// sessionDone = receiveMessage.body.isDone()
// completion?(msg, sessionDone)
// }
//
// } else {
// completion?(receiveMessage, sessionDone)
// }
//
// } else {
// completion?(receiveMessage, sessionDone)
// }
// }
} }
} // arr 结束 } // arr 结束
} }
......
...@@ -31,7 +31,6 @@ class YHAIRobotChatViewController: YHBaseViewController { ...@@ -31,7 +31,6 @@ class YHAIRobotChatViewController: YHBaseViewController {
}() }()
lazy var tableView: UITableView = { lazy var tableView: UITableView = {
let tableView = UITableView(frame: CGRectMake(0, 0, k_Height_NavigationtBarAndStatuBar, KScreenHeight-k_Height_NavigationtBarAndStatuBar-64-k_Height_safeAreaInsetsBottom()), style:.grouped) let tableView = UITableView(frame: CGRectMake(0, 0, k_Height_NavigationtBarAndStatuBar, KScreenHeight-k_Height_NavigationtBarAndStatuBar-64-k_Height_safeAreaInsetsBottom()), style:.grouped)
if #available(iOS 11.0, *) { if #available(iOS 11.0, *) {
...@@ -47,7 +46,7 @@ class YHAIRobotChatViewController: YHBaseViewController { ...@@ -47,7 +46,7 @@ class YHAIRobotChatViewController: YHBaseViewController {
chatConfig.registerMessageGroupCells(tableView) chatConfig.registerMessageGroupCells(tableView)
return tableView return tableView
}() }()
lazy var bgImgView: UIImageView = { lazy var bgImgView: UIImageView = {
let v = UIImageView(image: UIImage(named: "ai_chat_bg")) let v = UIImageView(image: UIImage(named: "ai_chat_bg"))
return v return v
...@@ -94,9 +93,7 @@ class YHAIRobotChatViewController: YHBaseViewController { ...@@ -94,9 +93,7 @@ class YHAIRobotChatViewController: YHBaseViewController {
guard let self = self else { return } guard let self = self else { return }
if self.isNeedStopResonse() { if self.isNeedStopResonse() {
self.stopAutoResponse { success in self.stopAutoResponse { success in
if success { self.sendMessage(text, true)
self.sendMessage(text, true)
}
} }
} else { } else {
self.sendMessage(text, true) self.sendMessage(text, true)
...@@ -115,9 +112,7 @@ class YHAIRobotChatViewController: YHBaseViewController { ...@@ -115,9 +112,7 @@ class YHAIRobotChatViewController: YHBaseViewController {
if self.isNeedStopResonse() { if self.isNeedStopResonse() {
self.stopAutoResponse { success in self.stopAutoResponse { success in
if success { self.sendMessage(text, true)
self.sendMessage(text, true)
}
} }
} else { } else {
self.sendMessage(text, true) self.sendMessage(text, true)
...@@ -189,25 +184,29 @@ class YHAIRobotChatViewController: YHBaseViewController { ...@@ -189,25 +184,29 @@ class YHAIRobotChatViewController: YHBaseViewController {
func reloadAndScrollToBottom(_ forceScrollToBottom: Bool = false, _ isNeedAccurate: Bool = false) { func reloadAndScrollToBottom(_ forceScrollToBottom: Bool = false, _ isNeedAccurate: Bool = false) {
self.tableView.reloadData() self.tableView.reloadData()
if !forceScrollToBottom && !canTriggerProgrammaticScroll() { if !forceScrollToBottom && !canTriggerProgrammaticScroll() {
return return
} }
DispatchQueue.main.asyncAfter(deadline: .now()+0.5) { DispatchQueue.main.async {
[weak self] in
guard let self = self else { return }
if isNeedAccurate { // 使用 UIView.animate 实现更丝滑的滚动效果
// self.tableView.setContentOffset(.zero, animated: true) UIView.animate(withDuration: 0.5,
// self.tableView.setContentOffset(CGPointMake(0, 90000), animated: true) delay: 0,
} usingSpringWithDamping: 1.0, // 弹簧阻尼,控制回弹效果
initialSpringVelocity: 0.3, // 初始速度
// 使用 performBatchUpdates 来确保所有布局更新完成后再滚动 options: [.curveEaseOut, .allowUserInteraction],
self.tableView.performBatchUpdates(nil) { _ in animations: {
// 在布局更新完成后执行滚动
if self.messages.count > 0 { if self.messages.count > 0 {
let lastIndexPath = IndexPath(row: self.messages.count-1, section: 0) let lastIndexPath = IndexPath(row: self.messages.count-1, section: 0)
self.tableView.scrollToRow(at: lastIndexPath, at: .bottom, animated: true) self.tableView.scrollToRow(at: lastIndexPath, at: .bottom, animated: false)
} }
}
}, completion: nil)
} }
} }
...@@ -219,22 +218,18 @@ class YHAIRobotChatViewController: YHBaseViewController { ...@@ -219,22 +218,18 @@ class YHAIRobotChatViewController: YHBaseViewController {
messages.append(thinkingMessage) messages.append(thinkingMessage)
self.bottomInputView.status = .loading self.bottomInputView.status = .loading
self.chatConfig.disableHandleMessage = false self.chatConfig.disableHandleMessage = false
self.manager.disableHandleMessage = false
self.reloadAndScrollToBottom(forceScrollToBottom) self.reloadAndScrollToBottom(forceScrollToBottom)
self.manager.requestAI(botId: self.robotId, conversationId: self.conversationId, question:text) { self.manager.requestAI(botId: self.robotId, conversationId: self.conversationId, question:text) {
[weak self] res, done in [weak self] res, done in
guard let self = self else { return } guard let self = self else { return }
self.chatConfig.handleReceiveMessage(res, done, &messages)
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { if done {
[weak self] in print("RESPONSE-DONE")
guard let self = self else { return } self.bottomInputView.status = self.bottomInputView.textView.text.isEmpty ? .disableSend : .enableSend
self.chatConfig.handleReceiveMessage(res, done, &messages)
if done {
print("RESPONSE-DONE")
self.bottomInputView.status = self.bottomInputView.textView.text.isEmpty ? .disableSend : .enableSend
}
self.reloadAndScrollToBottom(forceScrollToBottom)
} }
self.reloadAndScrollToBottom(forceScrollToBottom)
} }
} }
...@@ -292,19 +287,15 @@ class YHAIRobotChatViewController: YHBaseViewController { ...@@ -292,19 +287,15 @@ class YHAIRobotChatViewController: YHBaseViewController {
} }
func stopAutoResponse(completion:((Bool)->())? = nil) { func stopAutoResponse(completion:((Bool)->())? = nil) {
self.manager.cancelCurrentRequest()
self.reloadAndScrollToBottom()
self.chatConfig.disableHandleMessage = true
self.manager.disableHandleMessage = true
self.chatConfig.removeThinkingMessageFromChatList(&self.messages)
self.bottomInputView.status = self.bottomInputView.textView.text.isEmpty ? .disableSend : .enableSend
completion?(true)
self.manager.stopChat(chatId: self.manager.chatId, conversationId: self.conversationId) { success, error in self.manager.stopChat(chatId: self.manager.chatId, conversationId: self.conversationId) { success, error in
self.bottomInputView.status = self.bottomInputView.textView.text.isEmpty ? .disableSend : .enableSend
if success {
self.reloadAndScrollToBottom()
self.manager.request?.cancel()
self.chatConfig.disableHandleMessage = true
DispatchQueue.main.asyncAfter(deadline: .now()+0.5) {
self.chatConfig.removeThinkingMessageFromChatList(&self.messages)
self.bottomInputView.status = self.bottomInputView.textView.text.isEmpty ? .disableSend : .enableSend
self.tableView.reloadData()
completion?(success)
}
}
} }
} }
...@@ -415,9 +406,7 @@ extension YHAIRobotChatViewController: UITableViewDelegate, UITableViewDataSourc ...@@ -415,9 +406,7 @@ extension YHAIRobotChatViewController: UITableViewDelegate, UITableViewDataSourc
if self.isNeedStopResonse() { if self.isNeedStopResonse() {
self.stopAutoResponse { success in self.stopAutoResponse { success in
if success { self.sendMessage(text, true)
self.sendMessage(text, true)
}
} }
} else { } else {
self.sendMessage(text, true) self.sendMessage(text, true)
...@@ -497,6 +486,47 @@ extension YHAIRobotChatViewController: UITableViewDelegate, UITableViewDataSourc ...@@ -497,6 +486,47 @@ extension YHAIRobotChatViewController: UITableViewDelegate, UITableViewDataSourc
} }
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if 0 <= indexPath.row, indexPath.row < messages.count {
let message = messages[indexPath.row]
let msgType = message.getType()
if !message.isTextMessage() {
return UITableView.automaticDimension
}
let text = message.body.contentText // 要显示的文本内容
let font = UIFont.PFSC_R(ofSize: 14) // 字体大小
let maxWidth = KScreenWidth-20*2-16*2 // 最大宽度限制
let attributes = [NSAttributedString.Key.font : font] as [NSAttributedString.Key : Any]
let size = (text as NSString).boundingRect(with: CGSize(width: maxWidth, height: .greatestFiniteMagnitude), options: .usesLineFragmentOrigin, attributes: attributes, context: nil).size
var textHeight = ceil(size.height)
if textHeight < 20.0 {
textHeight = 20.0
}
if message.isSelf || msgType == .recommendText {
return textHeight + 16.0*2 + 16.0
}
let complete = message.isDone
let isNeedShowCopyView = complete
let isNeedShowLoadingView = !complete
var resultHeight = textHeight+16.0*2.0
if isNeedShowLoadingView {
resultHeight += (18+6)
}
if isNeedShowCopyView {
resultHeight += (16+37)
} else {
resultHeight += 16
}
return resultHeight
}
return UITableView.automaticDimension return UITableView.automaticDimension
} }
......
...@@ -154,7 +154,7 @@ class YHAIChatMessage: CustomStringConvertible, NSCopying { ...@@ -154,7 +154,7 @@ class YHAIChatMessage: CustomStringConvertible, NSCopying {
// //
func isUserfulMessage() -> Bool { func isUserfulMessage() -> Bool {
if !self.body.isStart() && !self.body.isDone() { if !self.body.isStart() {
return true return true
} }
return false return false
......
...@@ -30,7 +30,7 @@ class YHAITextMessageCell: UITableViewCell { ...@@ -30,7 +30,7 @@ class YHAITextMessageCell: UITableViewCell {
whiteContentView.snp.remakeConstraints { make in whiteContentView.snp.remakeConstraints { make in
make.left.greaterThanOrEqualTo(20) make.left.greaterThanOrEqualTo(20)
make.right.equalTo(-20) make.right.equalTo(-20)
make.top.equalTo(20) make.top.equalTo(16)
make.bottom.equalTo(0) make.bottom.equalTo(0)
} }
...@@ -46,11 +46,6 @@ class YHAITextMessageCell: UITableViewCell { ...@@ -46,11 +46,6 @@ class YHAITextMessageCell: UITableViewCell {
} else { } else {
messageLabel.text = message.body.contentText messageLabel.text = message.body.contentText
// let text = message.body.contentText
// if text.count >= message.body.contentTextIndex {
// messageLabel.text = String(text.prefix(message.body.contentTextIndex))
// }
whiteContentView.backgroundColor = .white whiteContentView.backgroundColor = .white
messageLabel.textColor = .mainTextColor messageLabel.textColor = .mainTextColor
...@@ -58,7 +53,7 @@ class YHAITextMessageCell: UITableViewCell { ...@@ -58,7 +53,7 @@ class YHAITextMessageCell: UITableViewCell {
whiteContentView.snp.remakeConstraints { make in whiteContentView.snp.remakeConstraints { make in
make.left.equalTo(20) make.left.equalTo(20)
make.right.equalTo(-20) make.right.equalTo(-20)
make.top.equalTo(20) make.top.equalTo(16)
make.bottom.equalTo(0) make.bottom.equalTo(0)
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment