mirror of
https://github.com/MustangYM/OSXChatGpt.git
synced 2025-11-25 03:15:08 +08:00
google search
This commit is contained in:
@@ -55,6 +55,10 @@
|
||||
CB1F1DD029DDBA0B008CFD0B /* AIPromptPopView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB1F1DCF29DDBA0B008CFD0B /* AIPromptPopView.swift */; };
|
||||
CB2449F829D721F3006EE829 /* SystemManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB2449F729D721F3006EE829 /* SystemManager.swift */; };
|
||||
CB2449FA29D7FE38006EE829 /* ServerManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB2449F929D7FE38006EE829 /* ServerManager.swift */; };
|
||||
CB26A2DF29FC1DCF001EF861 /* GoogleSearch+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB26A2DB29FC1DCF001EF861 /* GoogleSearch+CoreDataClass.swift */; };
|
||||
CB26A2E029FC1DCF001EF861 /* GoogleSearch+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB26A2DC29FC1DCF001EF861 /* GoogleSearch+CoreDataProperties.swift */; };
|
||||
CB26A2E129FC1DCF001EF861 /* GoogleSearchResult+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB26A2DD29FC1DCF001EF861 /* GoogleSearchResult+CoreDataClass.swift */; };
|
||||
CB26A2E229FC1DCF001EF861 /* GoogleSearchResult+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB26A2DE29FC1DCF001EF861 /* GoogleSearchResult+CoreDataProperties.swift */; };
|
||||
CB27655C29D1C12C00897E0E /* MarkdownContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB27655B29D1C12C00897E0E /* MarkdownContentView.swift */; };
|
||||
CB27656629D1DA9800897E0E /* AIPromptView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB27656529D1DA9800897E0E /* AIPromptView.swift */; };
|
||||
CB27657329D30F1400897E0E /* AIPromptViewMdoel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB27657229D30F1400897E0E /* AIPromptViewMdoel.swift */; };
|
||||
@@ -62,8 +66,6 @@
|
||||
CB28A52229C07BE500F0286A /* KeyboardMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB28A52129C07BE500F0286A /* KeyboardMonitor.swift */; };
|
||||
CB28A52829C1569900F0286A /* ThinkingAnimationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB28A52729C1569900F0286A /* ThinkingAnimationView.swift */; };
|
||||
CB2A943229F828E500D3A048 /* SwiftSoup in Frameworks */ = {isa = PBXBuildFile; productRef = CB2A943129F828E500D3A048 /* SwiftSoup */; };
|
||||
CB2A943529F830B300D3A048 /* GoogleSearch+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB2A943329F830B300D3A048 /* GoogleSearch+CoreDataClass.swift */; };
|
||||
CB2A943629F830B300D3A048 /* GoogleSearch+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB2A943429F830B300D3A048 /* GoogleSearch+CoreDataProperties.swift */; };
|
||||
CB2A943929F830E500D3A048 /* Conversation+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB2A943729F830E500D3A048 /* Conversation+CoreDataClass.swift */; };
|
||||
CB2A943A29F830E500D3A048 /* Conversation+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB2A943829F830E500D3A048 /* Conversation+CoreDataProperties.swift */; };
|
||||
CB2A944029F973F800D3A048 /* GoogleSearchPopView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB2A943F29F973F800D3A048 /* GoogleSearchPopView.swift */; };
|
||||
@@ -133,14 +135,17 @@
|
||||
CB228EA129CD4949006B3559 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
|
||||
CB2449F729D721F3006EE829 /* SystemManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SystemManager.swift; sourceTree = "<group>"; };
|
||||
CB2449F929D7FE38006EE829 /* ServerManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerManager.swift; sourceTree = "<group>"; };
|
||||
CB26A2DB29FC1DCF001EF861 /* GoogleSearch+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GoogleSearch+CoreDataClass.swift"; sourceTree = "<group>"; };
|
||||
CB26A2DC29FC1DCF001EF861 /* GoogleSearch+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GoogleSearch+CoreDataProperties.swift"; sourceTree = "<group>"; };
|
||||
CB26A2DD29FC1DCF001EF861 /* GoogleSearchResult+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GoogleSearchResult+CoreDataClass.swift"; sourceTree = "<group>"; };
|
||||
CB26A2DE29FC1DCF001EF861 /* GoogleSearchResult+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GoogleSearchResult+CoreDataProperties.swift"; sourceTree = "<group>"; };
|
||||
CB26A2E329FC399F001EF861 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
||||
CB27655B29D1C12C00897E0E /* MarkdownContentView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MarkdownContentView.swift; sourceTree = "<group>"; };
|
||||
CB27656529D1DA9800897E0E /* AIPromptView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AIPromptView.swift; sourceTree = "<group>"; };
|
||||
CB27657229D30F1400897E0E /* AIPromptViewMdoel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AIPromptViewMdoel.swift; sourceTree = "<group>"; };
|
||||
CB27657429D33D7A00897E0E /* AIPromptInputView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AIPromptInputView.swift; sourceTree = "<group>"; };
|
||||
CB28A52129C07BE500F0286A /* KeyboardMonitor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyboardMonitor.swift; sourceTree = "<group>"; };
|
||||
CB28A52729C1569900F0286A /* ThinkingAnimationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThinkingAnimationView.swift; sourceTree = "<group>"; };
|
||||
CB2A943329F830B300D3A048 /* GoogleSearch+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GoogleSearch+CoreDataClass.swift"; sourceTree = "<group>"; };
|
||||
CB2A943429F830B300D3A048 /* GoogleSearch+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GoogleSearch+CoreDataProperties.swift"; sourceTree = "<group>"; };
|
||||
CB2A943729F830E500D3A048 /* Conversation+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Conversation+CoreDataClass.swift"; sourceTree = "<group>"; };
|
||||
CB2A943829F830E500D3A048 /* Conversation+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Conversation+CoreDataProperties.swift"; sourceTree = "<group>"; };
|
||||
CB2A943F29F973F800D3A048 /* GoogleSearchPopView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GoogleSearchPopView.swift; sourceTree = "<group>"; };
|
||||
@@ -270,6 +275,7 @@
|
||||
CB1DCAC029B4F09D00B1D4E1 /* OSXChatGPT */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
CB26A2E329FC399F001EF861 /* Info.plist */,
|
||||
CBD5AB6729E7078E007B6625 /* Markdown */,
|
||||
CB0F5A5C29D059C4005B71D2 /* SyntaxHighlighter */,
|
||||
182B436E29BC5D1B00F06778 /* DataProvider */,
|
||||
@@ -355,10 +361,12 @@
|
||||
CBC4B11429B8CB1B00650296 /* Models */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
CB26A2DB29FC1DCF001EF861 /* GoogleSearch+CoreDataClass.swift */,
|
||||
CB26A2DC29FC1DCF001EF861 /* GoogleSearch+CoreDataProperties.swift */,
|
||||
CB26A2DD29FC1DCF001EF861 /* GoogleSearchResult+CoreDataClass.swift */,
|
||||
CB26A2DE29FC1DCF001EF861 /* GoogleSearchResult+CoreDataProperties.swift */,
|
||||
CB2A943729F830E500D3A048 /* Conversation+CoreDataClass.swift */,
|
||||
CB2A943829F830E500D3A048 /* Conversation+CoreDataProperties.swift */,
|
||||
CB2A943329F830B300D3A048 /* GoogleSearch+CoreDataClass.swift */,
|
||||
CB2A943429F830B300D3A048 /* GoogleSearch+CoreDataProperties.swift */,
|
||||
CB2D438729F0183A007742AE /* ChatGPT+CoreDataClass.swift */,
|
||||
CB2D438829F0183A007742AE /* ChatGPT+CoreDataProperties.swift */,
|
||||
CB1F015D29EB9D05009CF942 /* Message+CoreDataClass.swift */,
|
||||
@@ -485,15 +493,16 @@
|
||||
CB2449FA29D7FE38006EE829 /* ServerManager.swift in Sources */,
|
||||
CB1F014829E99B5E009CF942 /* Int+IsOdd.swift in Sources */,
|
||||
CB2F972029CE1ADC004EBD96 /* OSXChatGPT.xcdatamodeld in Sources */,
|
||||
CB26A2E029FC1DCF001EF861 /* GoogleSearch+CoreDataProperties.swift in Sources */,
|
||||
182B436929BC5C8700F06778 /* UserInitializeView.swift in Sources */,
|
||||
CB2449F829D721F3006EE829 /* SystemManager.swift in Sources */,
|
||||
CB2A944229F993A200D3A048 /* GoogleSearchSettingView.swift in Sources */,
|
||||
182B43A429BF730300F06778 /* NSColor.swift in Sources */,
|
||||
CB1F012D29E99982009CF942 /* Grammar.swift in Sources */,
|
||||
CB27656629D1DA9800897E0E /* AIPromptView.swift in Sources */,
|
||||
CB26A2E229FC1DCF001EF861 /* GoogleSearchResult+CoreDataProperties.swift in Sources */,
|
||||
CB1F014929E99B5E009CF942 /* Sequence+Occurrences.swift in Sources */,
|
||||
182B436B29BC5CBA00F06778 /* AppDelegate.swift in Sources */,
|
||||
CB2A943629F830B300D3A048 /* GoogleSearch+CoreDataProperties.swift in Sources */,
|
||||
CB1F015B29EAFBF5009CF942 /* MessageText+CoreDataClass.swift in Sources */,
|
||||
182B437329BC5D1B00F06778 /* CoreDataManager.swift in Sources */,
|
||||
CB2A944029F973F800D3A048 /* GoogleSearchPopView.swift in Sources */,
|
||||
@@ -504,7 +513,6 @@
|
||||
CB2A943A29F830E500D3A048 /* Conversation+CoreDataProperties.swift in Sources */,
|
||||
182B436629BC5C8700F06778 /* View.swift in Sources */,
|
||||
CB1F012929E995BA009CF942 /* MarkdownTextBuilder.swift in Sources */,
|
||||
CB2A943529F830B300D3A048 /* GoogleSearch+CoreDataClass.swift in Sources */,
|
||||
CB1F015F29EB9D05009CF942 /* Message+CoreDataClass.swift in Sources */,
|
||||
182B437B29BC5FBE00F06778 /* EnterAPIView.swift in Sources */,
|
||||
CB0F5A5F29D059C4005B71D2 /* TextOutputFormat.swift in Sources */,
|
||||
@@ -526,6 +534,7 @@
|
||||
CB1F014429E99B5E009CF942 /* String+PrefixChecking.swift in Sources */,
|
||||
CB27657529D33D7A00897E0E /* AIPromptInputView.swift in Sources */,
|
||||
182B437229BC5D1B00F06778 /* HTTPClient.swift in Sources */,
|
||||
CB26A2DF29FC1DCF001EF861 /* GoogleSearch+CoreDataClass.swift in Sources */,
|
||||
CB53A3BF29D48C8F00A5B8FC /* Prompt+CoreDataProperties.swift in Sources */,
|
||||
CB1F012729E9832F009CF942 /* MarkdownTextAttributesReader.swift in Sources */,
|
||||
CB1F012C29E99982009CF942 /* SwiftGrammar.swift in Sources */,
|
||||
@@ -534,6 +543,7 @@
|
||||
CB28A52829C1569900F0286A /* ThinkingAnimationView.swift in Sources */,
|
||||
CBD5AB6629E6EFE3007B6625 /* MarkdownView.swift in Sources */,
|
||||
CBD5AB6B29E707F0007B6625 /* MarkdownTokenType.swift in Sources */,
|
||||
CB26A2E129FC1DCF001EF861 /* GoogleSearchResult+CoreDataClass.swift in Sources */,
|
||||
182B437529BC5D1B00F06778 /* ChatGPTManager.swift in Sources */,
|
||||
CB2D438A29F0183A007742AE /* ChatGPT+CoreDataProperties.swift in Sources */,
|
||||
CB2F972229CED6AE004EBD96 /* ChatRoomInputView.swift in Sources */,
|
||||
@@ -688,6 +698,7 @@
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = OSXChatGPT/Info.plist;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
@@ -716,6 +727,7 @@
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = OSXChatGPT/Info.plist;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftSoup
|
||||
|
||||
|
||||
private let OSXChatGPTKEY = "OSXChatGPT_apiKey_key"
|
||||
private let OSXChatGoogleSearchKEY = "OSXChatGPT_GoogleSearch_apiKey_key"
|
||||
@@ -76,10 +76,14 @@ struct ChatGPTRequest {
|
||||
get {
|
||||
let arr = messages.suffix(contextCount.valyeInt * 2 + 1)
|
||||
var temp: [[String: String]] = []
|
||||
var tokens: Int64 = 0
|
||||
arr.forEach { msg in
|
||||
if msg.type != 1 {
|
||||
//移除错误的回复,不误导gpt
|
||||
if msg.msgType == .normal {
|
||||
//只上传普通消息
|
||||
temp.append(["role": msg.role ?? "user", "content": msg.text ?? ""])
|
||||
tokens += Int64("rolecontent".count)
|
||||
tokens += Int64((msg.role ?? "user").count)
|
||||
tokens += Int64((msg.text ?? "").count)
|
||||
}
|
||||
}
|
||||
if let system = systemMsg {
|
||||
@@ -232,11 +236,11 @@ class ChatGPTManager {
|
||||
let key = UserDefaults.standard.value(forKey: OSXChatGPTKEY) as? String
|
||||
return key ?? ""
|
||||
}()
|
||||
private lazy var googleSearchApiKey : String = {
|
||||
lazy var googleSearchApiKey : String = {
|
||||
let key = UserDefaults.standard.value(forKey: OSXChatGoogleSearchKEY) as? String
|
||||
return key ?? ""
|
||||
}()
|
||||
private lazy var googleSearchEngineID : String = {
|
||||
lazy var googleSearchEngineID : String = {
|
||||
let key = UserDefaults.standard.value(forKey: OSXChatGoogleEngineKEY) as? String
|
||||
return key ?? ""
|
||||
}()
|
||||
@@ -402,6 +406,16 @@ extension ChatGPTManager {
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
/*
|
||||
Web search results:
|
||||
|
||||
{web_results}
|
||||
Current date: {current_date}
|
||||
|
||||
Instructions: Using the provided web search results, write a comprehensive reply to the given query. Make sure to cite results using [[number](URL)] notation after the reference. If the provided search results refer to multiple subjects with the same name, write separate answers for each subject.
|
||||
Query: {query}
|
||||
Reply in 中文
|
||||
*/
|
||||
|
||||
/// 提问
|
||||
func askChatGPT(messages: [Message], complete:(([String: Any]?, String?) -> ())?) {
|
||||
@@ -448,58 +462,19 @@ extension ChatGPTManager {
|
||||
|
||||
// MARK: - search
|
||||
extension ChatGPTManager {
|
||||
func search(_ text: String, callback:@escaping (_ searchResult: GoogleSearchResult?, _ err: String?) -> Void) {
|
||||
httpClient.googleSearch(text, cx: googleSearchEngineID, key: googleSearchApiKey) { [weak self] result, error in
|
||||
guard let model = result else {
|
||||
callback(nil, "error")
|
||||
return
|
||||
}
|
||||
var temp : [GoogleSearch] = []
|
||||
let group = DispatchGroup()
|
||||
let queue = DispatchQueue.global(qos: .userInitiated)
|
||||
|
||||
model.items.forEach { item in
|
||||
if let link = item.link {
|
||||
group.enter()
|
||||
self?.fetchHTML(link, callback: { content, err in
|
||||
if let result = content {
|
||||
|
||||
}
|
||||
group.leave()
|
||||
})
|
||||
}
|
||||
}
|
||||
group.notify(queue: DispatchQueue.main) {
|
||||
|
||||
func search(search: GoogleSearch?, callback:@escaping (_ searchResult: GoogleSearchResponse?, _ err: String?) -> Void) {
|
||||
if let url = search?.url {
|
||||
httpClient.googleSearch(url) { result, error in
|
||||
callback(result,error)
|
||||
}
|
||||
}else {
|
||||
callback(nil,"error")
|
||||
}
|
||||
|
||||
}
|
||||
private func fetchHTML(_ link: String, callback: @escaping (_ content: String?, _ err: String?) -> Void) {
|
||||
func fetchHTML(_ link: String, callback: @escaping (_ content: String?, _ err: String?) -> Void) {
|
||||
httpClient.googleSearchFetchHTML(link) { result, err in
|
||||
if let html = result {
|
||||
do {
|
||||
let doc = try SwiftSoup.parse(html)
|
||||
let pTags = try doc.select("p")
|
||||
var array: [String] = []
|
||||
for p in pTags {
|
||||
let text = try p.text()
|
||||
if !array.contains(text) && !text.isEmpty {
|
||||
array.append(text)
|
||||
}
|
||||
}
|
||||
var string: String = ""
|
||||
array.forEach { str in
|
||||
string.append(str)
|
||||
}
|
||||
callback(string, nil)
|
||||
print("string:\(string)")
|
||||
} catch Exception.Error(let type, let message) {
|
||||
callback(nil, "error")
|
||||
} catch {
|
||||
callback(nil, "error")
|
||||
}
|
||||
|
||||
}
|
||||
callback(result, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import Foundation
|
||||
import SwiftUI
|
||||
import CoreData
|
||||
import Splash
|
||||
import SwiftSoup
|
||||
|
||||
|
||||
|
||||
@@ -32,6 +33,13 @@ import Splash
|
||||
|
||||
@Environment(\.colorScheme) private var colorScheme
|
||||
|
||||
|
||||
private let searchQueue = DispatchQueue(label: "google.search.queue")
|
||||
private let semaphore = DispatchSemaphore(value: 1)
|
||||
private var searchResult: GoogleSearchResponse?
|
||||
private var searchIndex: Int = 0
|
||||
private var searchResultMaxCount: Int16 = 0
|
||||
|
||||
var theme: Splash.Theme {
|
||||
switch colorScheme {
|
||||
case .dark:
|
||||
@@ -268,6 +276,123 @@ extension ViewModel {
|
||||
}
|
||||
}
|
||||
func addNewMessage(sesstionId: String, text: String, role: String, prompt: String?, updateBlock: @escaping(() -> ())) {
|
||||
if sesstionId.isEmpty {
|
||||
return
|
||||
}
|
||||
if ChatGPTManager.shared.chatGPTSpeaking {
|
||||
return
|
||||
}
|
||||
let msg = Message(context: CoreDataManager.shared.container.viewContext)
|
||||
msg.sesstionId = sesstionId
|
||||
msg.text = text
|
||||
msg.role = role
|
||||
msg.id = UUID()
|
||||
msg.createdDate = Date()
|
||||
messages.append(msg)
|
||||
CoreDataManager.shared.saveData()
|
||||
updateConversation(sesstionId: sesstionId, message:messages.last)
|
||||
self.scrollID = msg.id!
|
||||
self.changeMsgText = text
|
||||
print("发送提问:\(text)")
|
||||
var sendMsgs = messages
|
||||
sendMsgs.removeAll(where: {$0.msgType == .waitingReply})
|
||||
|
||||
if let search = currentConversation?.search, search.open, !search.searched {
|
||||
//开启搜索状态
|
||||
search.search = text
|
||||
let result = search.result?.mutableCopy() as? NSMutableOrderedSet
|
||||
result?.removeAllObjects()
|
||||
self.addGptThinkMessage(sesstionId: sesstionId)
|
||||
if let lastId = self.messages.last?.id {
|
||||
self.scrollID = lastId
|
||||
}
|
||||
self.changeMsgText = ""
|
||||
self.search(search: search) { [weak self] searchResult, err in
|
||||
guard let self = self else { return }
|
||||
if let result = searchResult {
|
||||
|
||||
if let index = self.messages.firstIndex(where: { $0.msgType == .searching}) {
|
||||
let message = self.messages[index]
|
||||
message.text = self.createSearchText(result)
|
||||
self.messages[index] = message
|
||||
self.updateConversation(sesstionId: sesstionId, search: result)
|
||||
if self.currentConversation?.sesstionId == sesstionId {
|
||||
self.scrollID = self.messages.last!.id!
|
||||
self.changeMsgText = ""//更新滚动
|
||||
}
|
||||
}else {
|
||||
self.removeGptThinkMessage()
|
||||
let newMsg = Message(context: CoreDataManager.shared.container.viewContext)
|
||||
newMsg.sesstionId = sesstionId
|
||||
newMsg.role = ChatGPTManager.shared.gptRoleString
|
||||
newMsg.id = UUID()
|
||||
newMsg.createdDate = Date()
|
||||
newMsg.msgTextType = .text
|
||||
newMsg.msgType = .searching
|
||||
newMsg.text = self.createSearchText(result)
|
||||
if self.currentConversation?.sesstionId == sesstionId {
|
||||
self.messages.append(newMsg)
|
||||
self.scrollID = self.messages.last!.id!
|
||||
self.changeMsgText = ""//更新滚动
|
||||
}
|
||||
self.addGptThinkMessage(sesstionId: sesstionId)
|
||||
CoreDataManager.shared.saveData()
|
||||
self.updateConversation(sesstionId: sesstionId, search: result)
|
||||
|
||||
}
|
||||
|
||||
}else {
|
||||
self.removeGptThinkMessage()
|
||||
var searchText: String?
|
||||
//没有返回结果,表示错误,或者已经结束
|
||||
if let search = self.currentConversation?.search {
|
||||
search.searched = true//已经结束搜索
|
||||
searchText = self.getSearchText(search)
|
||||
self.updateConversation(sesstionId: sesstionId, search: search)
|
||||
}
|
||||
//开始提问
|
||||
self.sendMessage(sesstionId: sesstionId, messages: sendMsgs, prompt: searchText, updateBlock: updateBlock)
|
||||
}
|
||||
}
|
||||
|
||||
}else {
|
||||
var sendMsgs = messages
|
||||
sendMsgs.removeAll(where: {$0.msgType == .waitingReply})
|
||||
sendMessage(sesstionId: sesstionId, messages: sendMsgs, prompt: prompt, updateBlock: updateBlock)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
private func createSearchText(_ search: GoogleSearch) -> String {
|
||||
var text: String = "#### 搜索结果"
|
||||
search.result?.array.forEach({ item in
|
||||
if let result = item as? GoogleSearchResult {
|
||||
text.append("\n\t")
|
||||
text.append("标题: \(result.title ?? "") \n\t")
|
||||
text.append("链接: \(result.link ?? "") \n\t")
|
||||
text.append("内容: \(result.result ?? "") \n\t")
|
||||
}
|
||||
})
|
||||
return text
|
||||
}
|
||||
private func getSearchText(_ search: GoogleSearch) -> String {
|
||||
var text: String = "{"
|
||||
search.result?.array.forEach({ item in
|
||||
if let result = item as? GoogleSearchResult {
|
||||
text.append("\(result.result ?? "")\n")
|
||||
}
|
||||
})
|
||||
text.append("}")
|
||||
return text
|
||||
}
|
||||
func addSearch(sesstionId: String, text: String, role: String, prompt: String?, updateBlock: @escaping(() -> ())) {
|
||||
search(search: currentConversation?.search) { searchResult, err in
|
||||
|
||||
}
|
||||
|
||||
|
||||
return
|
||||
|
||||
if sesstionId.isEmpty {
|
||||
return
|
||||
}
|
||||
@@ -290,6 +415,8 @@ extension ViewModel {
|
||||
sendMsgs.removeAll(where: {$0.msgType == .waitingReply})
|
||||
sendMessage(sesstionId: sesstionId, messages: sendMsgs, prompt: prompt, updateBlock: updateBlock)
|
||||
}
|
||||
|
||||
|
||||
func cancel() {
|
||||
ChatGPTManager.shared.stopResponse()
|
||||
self.removeGptThinkMessage()
|
||||
@@ -648,7 +775,156 @@ extension ViewModel {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - search
|
||||
extension ViewModel {
|
||||
|
||||
func cerateDefaultSearch() -> GoogleSearch {
|
||||
let search = GoogleSearch(context: CoreDataManager.shared.container.viewContext)
|
||||
search.id = UUID()
|
||||
search.maxSearchResult = 3
|
||||
search.dateType = .unlimited
|
||||
return search
|
||||
}
|
||||
func updateConversation(sesstionId: String, search: GoogleSearch?) {
|
||||
var con = fetchConversation(sesstionId: sesstionId)
|
||||
if con == nil {
|
||||
con = Conversation(context: CoreDataManager.shared.container.viewContext)
|
||||
con?.sesstionId = sesstionId
|
||||
con?.id = UUID()
|
||||
}
|
||||
con!.search = search
|
||||
CoreDataManager.shared.saveData()
|
||||
|
||||
if let index = conversations.firstIndex(where: { $0.sesstionId == sesstionId}) {
|
||||
conversations[index] = con!
|
||||
} else {
|
||||
conversations.insert(con!, at: 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - search
|
||||
extension ViewModel {
|
||||
func search(search: GoogleSearch?, callback:@escaping (_ searchResult: GoogleSearch?, _ err: String?) -> Void) {
|
||||
guard let searchModel = search else {
|
||||
callback(nil, "error")
|
||||
return
|
||||
}
|
||||
ChatGPTManager.shared.search(search: searchModel) { [weak self] searchResult, err in
|
||||
guard let result = searchResult else {
|
||||
DispatchQueue.main.async {
|
||||
callback(nil, "error")
|
||||
}
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
DispatchQueue.main.async {
|
||||
callback(nil, "error")
|
||||
}
|
||||
return
|
||||
}
|
||||
self?.searchResult = result
|
||||
self?.searchIndex = 0
|
||||
self?.searchResultMaxCount = searchModel.maxSearchResult
|
||||
self?.fetchHTML(callback: { content, item, err in
|
||||
if let result = content {
|
||||
let model = GoogleSearchResult(context: CoreDataManager.shared.container.viewContext)
|
||||
model.result = result
|
||||
model.link = item?.link
|
||||
model.snippet = item?.snippet
|
||||
model.title = item?.title
|
||||
searchModel.addToResult(model)
|
||||
CoreDataManager.shared.saveData()
|
||||
DispatchQueue.main.async {
|
||||
callback(searchModel, nil)
|
||||
}
|
||||
}else {
|
||||
DispatchQueue.main.async {
|
||||
callback(nil, "error")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private func fetchHTML(callback: @escaping (_ content: String?, _ item: GoogleSearchItem?, _ err: String?) -> Void) {
|
||||
guard let result = self.searchResult else {
|
||||
print("Not searchResult")
|
||||
callback(nil, nil, "error")
|
||||
return
|
||||
}
|
||||
|
||||
if result.items.count <= self.searchIndex || self.searchIndex >= self.searchResultMaxCount {
|
||||
print("exceed max count")
|
||||
callback(nil, nil, "error")
|
||||
return
|
||||
}
|
||||
let item = result.items[self.searchIndex]
|
||||
searchQueue.async {
|
||||
self.semaphore.wait()
|
||||
self.fetchHTML(item.link) { [weak self] content, err in
|
||||
self?.semaphore.signal()
|
||||
guard let self = self else {
|
||||
print("htmlError Not self")
|
||||
return
|
||||
}
|
||||
guard let html = content else {
|
||||
print("htmlError Not html")
|
||||
callback(nil, item, "error")
|
||||
self.searchIndex += 1
|
||||
self.fetchHTML(callback: callback)
|
||||
return
|
||||
}
|
||||
// print("+++html:\(html)")
|
||||
// print("+++htmlNED")
|
||||
guard let doc = try? SwiftSoup.parse(html), let pTags = try? doc.select("p:lt(2)") else {
|
||||
self.searchIndex += 1
|
||||
print("htmlError Not parse")
|
||||
callback(nil, item, "error")
|
||||
self.fetchHTML(callback: callback)
|
||||
return
|
||||
}
|
||||
var pArray: [String] = []
|
||||
for p in pTags {
|
||||
if let text = try? p.text(), !text.isEmpty {
|
||||
print("+++htmlP:\(text)")
|
||||
if !pArray.contains(text) {
|
||||
pArray.append(text)
|
||||
}
|
||||
}
|
||||
}
|
||||
var string: String = ""
|
||||
pArray.forEach { p in
|
||||
string.append(p)
|
||||
}
|
||||
print("htmlString:\(string)")
|
||||
callback(string, item, nil)
|
||||
self.searchIndex += 1
|
||||
self.fetchHTML(callback: callback)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
private func fetchHTML(_ link: String?, callback: @escaping (_ content: String?, _ err: String?) -> Void) {
|
||||
guard let lin = link, let ur = URL(string: lin) else {
|
||||
callback(nil, "error")
|
||||
return
|
||||
}
|
||||
print("aaaaaaaURL:\(lin)")
|
||||
URLSession.shared.dataTask(with: ur) { (data, response, error) in
|
||||
if let error = error {
|
||||
print("error: \(error.localizedDescription)")
|
||||
callback(nil, error.localizedDescription)
|
||||
return
|
||||
}
|
||||
if let response = response as? HTTPURLResponse {
|
||||
print("statusCode: \(response.statusCode)")
|
||||
}
|
||||
if let da = data, let html = String(data: da, encoding: .utf8) {
|
||||
callback(html, nil)
|
||||
}else {
|
||||
callback(nil, "error")
|
||||
}
|
||||
}.resume()
|
||||
}
|
||||
}
|
||||
|
||||
11
OSXChatGPT/OSXChatGPT/Info.plist
Normal file
11
OSXChatGPT/OSXChatGPT/Info.plist
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -2,14 +2,63 @@
|
||||
// GoogleSearch+CoreDataClass.swift
|
||||
// OSXChatGPT
|
||||
//
|
||||
// Created by CoderChan on 2023/4/26.
|
||||
// Created by CoderChan on 2023/4/28.
|
||||
//
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreData
|
||||
|
||||
enum GoogleSearchDate: Int16 {
|
||||
case unlimited = 0 //无限制
|
||||
case oneWeak // 一周内
|
||||
case oneMonth // 一月内
|
||||
case sixMonth // 六月内
|
||||
case oneYears // 一年内
|
||||
var value: String {
|
||||
switch self {
|
||||
case .unlimited:
|
||||
return ""
|
||||
case .oneWeak:
|
||||
return "w1"
|
||||
case .oneMonth:
|
||||
return "m1"
|
||||
case .sixMonth:
|
||||
return "m6"
|
||||
case .oneYears:
|
||||
return "y1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class GoogleSearch: NSManagedObject {
|
||||
|
||||
var dateType: GoogleSearchDate {
|
||||
get {
|
||||
return GoogleSearchDate(rawValue: dateRestrict) ?? .unlimited
|
||||
}
|
||||
set {
|
||||
dateRestrict = newValue.rawValue
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var baseUrl: String = "https://customsearch.googleapis.com/customsearch/v1"
|
||||
var url: String? {
|
||||
var newUrl = baseUrl
|
||||
if ChatGPTManager.shared.googleSearchEngineID.isEmpty {
|
||||
return nil
|
||||
}
|
||||
newUrl += "?cx=\(ChatGPTManager.shared.googleSearchEngineID)"
|
||||
guard let searchText = search else { return nil }
|
||||
newUrl += "&q=\(searchText)"
|
||||
newUrl += "&dateRestrict=\(dateType.value)"
|
||||
if ChatGPTManager.shared.googleSearchApiKey.isEmpty {
|
||||
return nil
|
||||
}
|
||||
newUrl += "&key=\(ChatGPTManager.shared.googleSearchApiKey)"
|
||||
let allowedCharacters = CharacterSet.urlQueryAllowed
|
||||
guard let encodedStr = newUrl.addingPercentEncoding(withAllowedCharacters: allowedCharacters) else { return nil}
|
||||
return encodedStr
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// GoogleSearch+CoreDataProperties.swift
|
||||
// OSXChatGPT
|
||||
//
|
||||
// Created by CoderChan on 2023/4/26.
|
||||
// Created by CoderChan on 2023/4/28.
|
||||
//
|
||||
//
|
||||
|
||||
@@ -16,12 +16,48 @@ extension GoogleSearch {
|
||||
return NSFetchRequest<GoogleSearch>(entityName: "GoogleSearch")
|
||||
}
|
||||
|
||||
@NSManaged public var maxSearchResult: Int16
|
||||
@NSManaged public var id: UUID?
|
||||
@NSManaged public var link: String?
|
||||
@NSManaged public var title: String?
|
||||
@NSManaged public var result: String?
|
||||
@NSManaged public var snippet: String?
|
||||
@NSManaged public var maxSearchResult: Int16
|
||||
@NSManaged public var dateRestrict: Int16
|
||||
@NSManaged public var result: NSOrderedSet?
|
||||
@NSManaged public var search: String?
|
||||
@NSManaged public var open: Bool
|
||||
@NSManaged public var searched: Bool
|
||||
|
||||
}
|
||||
|
||||
// MARK: Generated accessors for result
|
||||
extension GoogleSearch {
|
||||
|
||||
@objc(insertObject:inResultAtIndex:)
|
||||
@NSManaged public func insertIntoResult(_ value: GoogleSearchResult, at idx: Int)
|
||||
|
||||
@objc(removeObjectFromResultAtIndex:)
|
||||
@NSManaged public func removeFromResult(at idx: Int)
|
||||
|
||||
@objc(insertResult:atIndexes:)
|
||||
@NSManaged public func insertIntoResult(_ values: [GoogleSearchResult], at indexes: NSIndexSet)
|
||||
|
||||
@objc(removeResultAtIndexes:)
|
||||
@NSManaged public func removeFromResult(at indexes: NSIndexSet)
|
||||
|
||||
@objc(replaceObjectInResultAtIndex:withObject:)
|
||||
@NSManaged public func replaceResult(at idx: Int, with value: GoogleSearchResult)
|
||||
|
||||
@objc(replaceResultAtIndexes:withResult:)
|
||||
@NSManaged public func replaceResult(at indexes: NSIndexSet, with values: [GoogleSearchResult])
|
||||
|
||||
@objc(addResultObject:)
|
||||
@NSManaged public func addToResult(_ value: GoogleSearchResult)
|
||||
|
||||
@objc(removeResultObject:)
|
||||
@NSManaged public func removeFromResult(_ value: GoogleSearchResult)
|
||||
|
||||
@objc(addResult:)
|
||||
@NSManaged public func addToResult(_ values: NSOrderedSet)
|
||||
|
||||
@objc(removeResult:)
|
||||
@NSManaged public func removeFromResult(_ values: NSOrderedSet)
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
//
|
||||
// GoogleSearchResult+CoreDataClass.swift
|
||||
// OSXChatGPT
|
||||
//
|
||||
// Created by CoderChan on 2023/4/28.
|
||||
//
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreData
|
||||
|
||||
|
||||
public class GoogleSearchResult: NSManagedObject {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// GoogleSearchResult+CoreDataProperties.swift
|
||||
// OSXChatGPT
|
||||
//
|
||||
// Created by CoderChan on 2023/4/28.
|
||||
//
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreData
|
||||
|
||||
|
||||
extension GoogleSearchResult {
|
||||
|
||||
@nonobjc public class func fetchRequest() -> NSFetchRequest<GoogleSearchResult> {
|
||||
return NSFetchRequest<GoogleSearchResult>(entityName: "GoogleSearchResult")
|
||||
}
|
||||
|
||||
@NSManaged public var title: String?
|
||||
@NSManaged public var snippet: String?
|
||||
@NSManaged public var result: String?
|
||||
@NSManaged public var link: String?
|
||||
@NSManaged public var id: UUID?
|
||||
|
||||
}
|
||||
|
||||
extension GoogleSearchResult : Identifiable {
|
||||
|
||||
}
|
||||
@@ -13,6 +13,7 @@ enum MessageType: Int16 {
|
||||
case normal = 0
|
||||
case waitingReply = 1//等待回复
|
||||
case fialMsg = 2//错误消息
|
||||
case searching = 3//搜索中
|
||||
}
|
||||
|
||||
enum MessageTextType: Int16 {
|
||||
|
||||
@@ -40,7 +40,7 @@ struct HTTPResponseMessage: Decodable {
|
||||
let role: String?
|
||||
}
|
||||
|
||||
struct GoogleSearchResult: Decodable {
|
||||
struct GoogleSearchResponse: Decodable {
|
||||
let kind: String
|
||||
let items: [GoogleSearchItem]
|
||||
}
|
||||
@@ -347,18 +347,12 @@ extension HTTPClient {
|
||||
}
|
||||
// MARK: - google search
|
||||
extension HTTPClient {
|
||||
func googleSearch(_ text: String, cx: String, key: String, callback:@escaping (_ searchResult: GoogleSearchResult?, _ err: String?) -> Void) {
|
||||
let urlStr = "https://customsearch.googleapis.com/customsearch/v1?cx=\(cx)&q=\(text)&key=\(key)"
|
||||
let allowedCharacters = CharacterSet.urlQueryAllowed
|
||||
guard let encodedStr = urlStr.addingPercentEncoding(withAllowedCharacters: allowedCharacters) else {
|
||||
callback(nil, "error")
|
||||
return
|
||||
}
|
||||
let url = URL(string: encodedStr)!
|
||||
func googleSearch(_ urlString: String, callback:@escaping (_ searchResult: GoogleSearchResponse?, _ err: String?) -> Void) {
|
||||
let url = URL(string: urlString)!
|
||||
var request = URLRequest(url: url)
|
||||
request.setValue("application/json", forHTTPHeaderField: "Accept")
|
||||
request.httpMethod = "GET"
|
||||
let task = URLSession.shared.dataTask(with: request) { [weak self] (data, response, error) in
|
||||
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
|
||||
if let error = error {
|
||||
print("error: \(error.localizedDescription)")
|
||||
callback(nil, error.localizedDescription)
|
||||
@@ -368,7 +362,7 @@ extension HTTPClient {
|
||||
print("statusCode: \(response.statusCode)")
|
||||
}
|
||||
if let da = data,
|
||||
let response = try? JSONDecoder().decode(GoogleSearchResult.self, from: da){
|
||||
let response = try? JSONDecoder().decode(GoogleSearchResponse.self, from: da){
|
||||
callback(response, nil)
|
||||
}else {
|
||||
callback(nil, "error")
|
||||
@@ -377,7 +371,11 @@ extension HTTPClient {
|
||||
task.resume()
|
||||
}
|
||||
func googleSearchFetchHTML(_ url: String, callback:@escaping (_ result: String?, _ err: String?) -> Void) {
|
||||
let ur = URL(string: url)!
|
||||
guard let ur = URL(string: url) else {
|
||||
callback(nil, "error")
|
||||
return
|
||||
}
|
||||
print("aaaaaaaURL:\(url)")
|
||||
let task = URLSession.shared.dataTask(with: ur) { (data, response, error) in
|
||||
if let error = error {
|
||||
print("error: \(error.localizedDescription)")
|
||||
|
||||
@@ -17,9 +17,17 @@
|
||||
<relationship name="search" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="GoogleSearch"/>
|
||||
</entity>
|
||||
<entity name="GoogleSearch" representedClassName=".GoogleSearch" syncable="YES">
|
||||
<attribute name="dateRestrict" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="id" attributeType="UUID" usesScalarValueType="NO"/>
|
||||
<attribute name="maxSearchResult" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="open" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="search" optional="YES" attributeType="String"/>
|
||||
<attribute name="searched" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<relationship name="result" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="GoogleSearchResult"/>
|
||||
</entity>
|
||||
<entity name="GoogleSearchResult" representedClassName=".GoogleSearchResult" syncable="YES">
|
||||
<attribute name="id" optional="YES" attributeType="UUID" usesScalarValueType="NO"/>
|
||||
<attribute name="link" optional="YES" attributeType="String"/>
|
||||
<attribute name="maxSearchResult" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="result" optional="YES" attributeType="String"/>
|
||||
<attribute name="snippet" optional="YES" attributeType="String"/>
|
||||
<attribute name="title" optional="YES" attributeType="String"/>
|
||||
|
||||
@@ -83,6 +83,17 @@ struct ChatRoomInputView: View {
|
||||
return
|
||||
}
|
||||
}
|
||||
// if let search = viewModel.currentConversation?.search, search.open, !search.searched {
|
||||
// //开启搜索,并且未搜索过才去谷歌搜索
|
||||
// search.search = msg
|
||||
// let result = search.result?.mutableCopy() as? NSMutableOrderedSet
|
||||
// result?.removeAllObjects()
|
||||
// CoreDataManager.shared.saveData()
|
||||
// viewModel.search(search: search) { searchResult, err in
|
||||
//
|
||||
// }
|
||||
// }else {
|
||||
// }
|
||||
viewModel.addNewMessage(sesstionId: viewModel.currentConversation?.sesstionId ?? "", text: msg, role: "user", prompt: viewModel.currentConversation?.prompt?.prompt) {
|
||||
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ struct ChatRoomToolBar: View {
|
||||
@State private var showInputView = false
|
||||
@State private var showDragView = false
|
||||
@State private var showSearchView = false
|
||||
@State private var showSearchSettingView = false
|
||||
@State private var temperature: String = ""
|
||||
@State private var model: String = ""
|
||||
@State private var context: String = ""
|
||||
@@ -71,17 +72,19 @@ struct ChatRoomToolBar: View {
|
||||
|
||||
Button {
|
||||
showSearchView.toggle()
|
||||
ChatGPTManager.shared.search("中国新冠开放") { searchResult, err in
|
||||
|
||||
}
|
||||
// ChatGPTManager.shared.search("中国新冠开放") { searchResult, err in
|
||||
//
|
||||
// }
|
||||
} label: {
|
||||
Text("谷歌搜索")
|
||||
}
|
||||
.popover(isPresented: $showSearchView) {
|
||||
GoogleSearchPopView(showSearchView: $showSearchView).environmentObject(viewModel)
|
||||
GoogleSearchPopView(showSearchView: $showSearchView, showSearchSettingView: $showSearchSettingView).environmentObject(viewModel)
|
||||
}
|
||||
|
||||
|
||||
.popover(isPresented: $showSearchSettingView) {
|
||||
GoogleSearchSettingView().environmentObject(viewModel)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
if viewModel.showStopAnswerBtn {
|
||||
|
||||
@@ -11,6 +11,7 @@ struct GoogleSearchPopView: View {
|
||||
@Binding var showSearchView: Bool
|
||||
@State private var progress: Float = 0.5
|
||||
@EnvironmentObject var viewModel: ViewModel
|
||||
@Binding var showSearchSettingView : Bool
|
||||
@State private var isOn = false
|
||||
var body: some View {
|
||||
VStack(alignment: .center) {
|
||||
@@ -26,48 +27,127 @@ struct GoogleSearchPopView: View {
|
||||
if isOn {
|
||||
GoogleSearchCountSliderView()
|
||||
Divider()
|
||||
Button {
|
||||
withAnimation {
|
||||
showSearchView.toggle()
|
||||
viewModel.currentConversation = nil
|
||||
viewModel.showUserInitialize = true
|
||||
}
|
||||
} label: {
|
||||
HStack {
|
||||
Text("谷歌APIKey")
|
||||
Text("未配置")
|
||||
}
|
||||
}.buttonStyle(PlainButtonStyle())
|
||||
.padding(EdgeInsets(top: 5, leading: 16, bottom: 5, trailing: 16))
|
||||
Divider()
|
||||
Button {
|
||||
withAnimation {
|
||||
showSearchView.toggle()
|
||||
viewModel.currentConversation = nil
|
||||
viewModel.showUserInitialize = true
|
||||
}
|
||||
} label: {
|
||||
HStack {
|
||||
Text("搜索引擎ID")
|
||||
Text("未配置")
|
||||
}
|
||||
}.buttonStyle(PlainButtonStyle())
|
||||
.padding(EdgeInsets(top: 5, leading: 16, bottom: 5, trailing: 16))
|
||||
GoogleSearchTimeSliderView()
|
||||
}else {
|
||||
Text("1、搜索需要配置APIKey与搜索引擎ID")
|
||||
.lineLimit(3)
|
||||
.padding(EdgeInsets(top: 15, leading: 16, bottom: 5, trailing: 16))
|
||||
Text("2、搜索结果超出最大令牌后会自动截取")
|
||||
.lineLimit(3)
|
||||
.padding(EdgeInsets(top: 0, leading: 16, bottom: 15, trailing: 16))
|
||||
HStack {
|
||||
Text("谷歌APIKey")
|
||||
Button {
|
||||
withAnimation {
|
||||
showSearchView.toggle()
|
||||
showSearchSettingView = true
|
||||
}
|
||||
} label: {
|
||||
HStack {
|
||||
if ChatGPTManager.shared.getGoogleSearchMaskApiKey().isEmpty {
|
||||
Text("[未配置]")
|
||||
.foregroundColor(.red)
|
||||
}else {
|
||||
Text("[更新key]").foregroundColor(.green)
|
||||
}
|
||||
|
||||
}
|
||||
}.buttonStyle(PlainButtonStyle())
|
||||
}
|
||||
.frame(height: 45)
|
||||
Divider()
|
||||
HStack {
|
||||
Text("搜索引擎ID")
|
||||
Button {
|
||||
withAnimation {
|
||||
showSearchView.toggle()
|
||||
showSearchSettingView = true
|
||||
}
|
||||
} label: {
|
||||
HStack {
|
||||
if ChatGPTManager.shared.getGoogleSearchEngineMaskApiKey().isEmpty {
|
||||
Text("[未配置]")
|
||||
.foregroundColor(.red)
|
||||
}else {
|
||||
Text("[更新ID]")
|
||||
.foregroundColor(.green)
|
||||
}
|
||||
|
||||
}
|
||||
}.buttonStyle(PlainButtonStyle())
|
||||
}
|
||||
.frame(height: 45)
|
||||
}
|
||||
Spacer()
|
||||
|
||||
}
|
||||
.frame(width: 160, height: 200)
|
||||
.onChange(of: isOn) { newValue in
|
||||
var search = viewModel.currentConversation?.search
|
||||
if search == nil {
|
||||
search = viewModel.cerateDefaultSearch()
|
||||
}
|
||||
search?.open = newValue
|
||||
viewModel.currentConversation?.search = search
|
||||
viewModel.updateConversation(sesstionId: viewModel.currentConversation!.sesstionId, search: search)
|
||||
}
|
||||
.onAppear {
|
||||
if let search = viewModel.currentConversation?.search, search.open {
|
||||
self.isOn = true
|
||||
}else {
|
||||
self.isOn = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct GoogleSearchTimeSliderView: View {
|
||||
@State private var progress: Float = 0
|
||||
@State private var change: String = "无限"
|
||||
@EnvironmentObject var viewModel: ViewModel
|
||||
var body: some View {
|
||||
VStack {
|
||||
HStack {
|
||||
Text("时间筛选")
|
||||
.padding(0)
|
||||
Text("(\(change))")
|
||||
.padding(0)
|
||||
.frame(width: 40)
|
||||
}.padding(0)
|
||||
Slider(value: $progress, in: 1...5, step: 1) {
|
||||
|
||||
}
|
||||
.frame(height: 10)
|
||||
.padding(.leading, 16)
|
||||
.padding(.trailing, 16)
|
||||
.padding(.top, 0)
|
||||
.padding(.bottom, 0)
|
||||
|
||||
}
|
||||
.frame(height: 45)
|
||||
.onChange(of: progress) { newValue in
|
||||
let formattedValue = String(format: "%.0f", newValue)
|
||||
change = formattedValue
|
||||
if newValue == 2 {
|
||||
change = "1周"
|
||||
}else if newValue == 3 {
|
||||
change = "1月"
|
||||
}else if newValue == 4 {
|
||||
change = "6月"
|
||||
}else if newValue == 5 {
|
||||
change = "1年"
|
||||
}else {
|
||||
change = "无限"
|
||||
}
|
||||
|
||||
}
|
||||
.onAppear {
|
||||
if let search = viewModel.currentConversation?.search {
|
||||
progress = Float(search.dateType.rawValue)
|
||||
}
|
||||
}
|
||||
.onDisappear {
|
||||
let search = viewModel.currentConversation?.search
|
||||
search?.dateType = GoogleSearchDate(rawValue: Int16(progress)) ?? .unlimited
|
||||
viewModel.currentConversation?.search = search
|
||||
viewModel.updateConversation(sesstionId: viewModel.currentConversation!.sesstionId, search: search)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct GoogleSearchCountSliderView: View {
|
||||
@State private var progress: Float = 3
|
||||
@@ -76,52 +156,36 @@ struct GoogleSearchCountSliderView: View {
|
||||
var body: some View {
|
||||
VStack {
|
||||
HStack {
|
||||
Text("获取搜索结果数")
|
||||
Text(change)
|
||||
.frame(width: 10)
|
||||
Text("搜索结果数")
|
||||
Text("(\(change))")
|
||||
.frame(width: 26)
|
||||
}.padding(0)
|
||||
// .background(.orange)
|
||||
Slider(value: $progress, in: 1...10, step: 1) {
|
||||
// Text(change)
|
||||
|
||||
}
|
||||
.frame(height: 10)
|
||||
.padding(.leading, 16)
|
||||
.padding(.trailing, 16)
|
||||
.padding(.top, 0)
|
||||
.padding(.bottom, 0)
|
||||
// .background(.green)
|
||||
// Slider(value: $progress) {
|
||||
// Text(change)
|
||||
// }
|
||||
// Slider(value: $progress) {
|
||||
//
|
||||
// } minimumValueLabel: {
|
||||
// Text("0.0")
|
||||
// } maximumValueLabel: {
|
||||
// Text("2.0")
|
||||
// }.padding(.horizontal)
|
||||
// .frame(width: 160)
|
||||
// .background(.orange)
|
||||
|
||||
}
|
||||
.padding(.top, 0)
|
||||
.padding(.bottom, 0)
|
||||
.frame(height: 45)
|
||||
.onChange(of: progress) { newValue in
|
||||
let formattedValue = String(format: "%.0f", newValue)
|
||||
change = formattedValue
|
||||
|
||||
}
|
||||
.onAppear {
|
||||
// if let gpt = viewModel.currentConversation?.chatGPT {
|
||||
// progress = Float(gpt.temperature / 2)
|
||||
// let formattedValue = String(format: "%.1f", progress * 2)
|
||||
// change = formattedValue
|
||||
// }
|
||||
if let search = viewModel.currentConversation?.search {
|
||||
progress = Float(search.maxSearchResult)
|
||||
}
|
||||
}
|
||||
.onDisappear {
|
||||
// viewModel.currentConversation?.chatGPT?.temperature = Double(progress * 2)
|
||||
// viewModel.updateConversation(sesstion: viewModel.currentConversation)
|
||||
// temperature = change
|
||||
let search = viewModel.currentConversation?.search
|
||||
search?.maxSearchResult = Int16(progress)
|
||||
viewModel.currentConversation?.search = search
|
||||
viewModel.updateConversation(sesstionId: viewModel.currentConversation!.sesstionId, search: search)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user