Skip to content

Commit ae689e0

Browse files
committed
Update WebChatContextCollector
1 parent 8ff7696 commit ae689e0

3 files changed

Lines changed: 197 additions & 124 deletions

File tree

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import Foundation
2+
import OpenAIService
3+
import Preferences
4+
5+
struct QueryWebsiteFunction: ChatGPTFunction {
6+
struct Arguments: Codable {
7+
var query: String
8+
var urlString: String
9+
}
10+
11+
struct Result: ChatGPTFunctionResult {
12+
var relevantTrunks: [String]
13+
14+
var botReadableContent: String {
15+
// don't forget to remove overlaps
16+
return ""
17+
}
18+
}
19+
20+
var name: String {
21+
"queryWebsite"
22+
}
23+
24+
var description: String {
25+
"Useful for when you need to answer a question using information from a website."
26+
}
27+
28+
var argumentSchema: JSONSchemaValue {
29+
return [
30+
.type: "object",
31+
.properties: [
32+
"query": [
33+
.type: "string",
34+
.description: "things you want to know about the website",
35+
],
36+
"urlString": [
37+
.type: "string",
38+
.description: "the url of the website"
39+
]
40+
],
41+
.required: ["query", "urlString"]
42+
]
43+
}
44+
45+
func message(at phase: OpenAIService.ChatGPTFunctionCallPhase) -> String {
46+
return ""
47+
}
48+
49+
func call(arguments: Arguments) async throws -> Result {
50+
// 1. grab the website content
51+
// 2. trunk the content
52+
// 3. embedding and store in memory
53+
// 4. embedding on the query, then search for relevant trunks, choose the 3 most relevant
54+
// 5. return the thunks
55+
56+
return .init(relevantTrunks: [])
57+
}
58+
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import BingSearchService
2+
import Foundation
3+
import OpenAIService
4+
import Preferences
5+
6+
struct SearchFunction: ChatGPTFunction {
7+
static let dateFormatter = {
8+
let it = DateFormatter()
9+
it.dateFormat = "yyyy-MM-dd"
10+
return it
11+
}()
12+
13+
struct Arguments: Codable {
14+
var query: String
15+
var freshness: String?
16+
}
17+
18+
struct Result: ChatGPTFunctionResult {
19+
var result: BingSearchResult
20+
21+
var botReadableContent: String {
22+
result.webPages.value.enumerated().map {
23+
let (index, page) = $0
24+
return """
25+
\(index + 1). \(page.name) \(page.url)
26+
\(page.snippet)
27+
"""
28+
}.joined(separator: "\n")
29+
}
30+
}
31+
32+
var name: String {
33+
"searchWeb"
34+
}
35+
36+
var description: String {
37+
"Useful for when you need to answer questions about latest information."
38+
}
39+
40+
var argumentSchema: JSONSchemaValue {
41+
let today = Self.dateFormatter.string(from: Date())
42+
return [
43+
.type: "object",
44+
.properties: [
45+
"query": [
46+
.type: "string",
47+
.description: "the search query",
48+
],
49+
"freshness": [
50+
.type: "string",
51+
.description: .string(
52+
"limit the search result to a specific range, use only when user ask the question about current events. Today is \(today). Format: yyyy-MM-dd..yyyy-MM-dd"
53+
),
54+
.examples: ["1919-10-20..1988-10-20"],
55+
],
56+
],
57+
.required: ["query"],
58+
]
59+
}
60+
61+
func message(at phase: ChatGPTFunctionCallPhase) -> String {
62+
func parseArgument(_ string: String) throws -> Arguments {
63+
try JSONDecoder().decode(Arguments.self, from: string.data(using: .utf8) ?? Data())
64+
}
65+
66+
switch phase {
67+
case .detected:
68+
return "Searching.."
69+
case let .processing(argumentsJsonString):
70+
do {
71+
let arguments = try parseArgument(argumentsJsonString)
72+
return "Searching \(arguments.query)"
73+
} catch {
74+
return "Searching.."
75+
}
76+
case let .ended(argumentsJsonString, result):
77+
do {
78+
let arguments = try parseArgument(argumentsJsonString)
79+
if let result = result as? Result {
80+
return """
81+
Finish searching \(arguments.query)
82+
\(
83+
result.result.webPages.value
84+
.map { "- [\($0.name)](\($0.url))" }
85+
.joined(separator: "\n")
86+
)
87+
"""
88+
}
89+
return "Finish searching \(arguments.query)"
90+
} catch {
91+
return "Finish searching"
92+
}
93+
case let .error(argumentsJsonString, _):
94+
do {
95+
let arguments = try parseArgument(argumentsJsonString)
96+
return "Failed searching \(arguments.query)"
97+
} catch {
98+
return "Failed searching"
99+
}
100+
}
101+
}
102+
103+
func call(arguments: Arguments) async throws -> Result {
104+
let bingSearch = BingSearchService(
105+
subscriptionKey: UserDefaults.shared.value(for: \.bingSearchSubscriptionKey),
106+
searchURL: UserDefaults.shared.value(for: \.bingSearchEndpoint)
107+
)
108+
let result = try await bingSearch.search(
109+
query: arguments.query,
110+
numberOfResult: UserDefaults.shared.value(for: \.chatGPTMaxToken) > 5000 ? 5 : 3,
111+
freshness: arguments.freshness
112+
)
113+
114+
let content = result.webPages.value.enumerated().map {
115+
let (index, page) = $0
116+
return """
117+
\(index + 1). \(page.name) \(page.url)
118+
\(page.snippet)
119+
"""
120+
}.joined(separator: "\n")
121+
122+
return .init(result: result)
123+
}
124+
}
125+
Lines changed: 14 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
import BingSearchService
21
import ChatContextCollector
32
import Foundation
43
import OpenAIService
5-
import Preferences
64
import SuggestionModel
75

8-
public struct WebChatContextCollector: ChatContextCollector {
6+
public final class WebChatContextCollector: ChatContextCollector {
7+
var recentLinks = [String]()
8+
99
public init() {}
1010

1111
public func generateContext(
@@ -14,132 +14,22 @@ public struct WebChatContextCollector: ChatContextCollector {
1414
content: String
1515
) -> ChatContext? {
1616
guard scopes.contains("web") else { return nil }
17+
let links = Self.detectLinks(from: history)
18+
let functions: [(any ChatGPTFunction)?] = [
19+
SearchFunction(),
20+
// allow this function only when there is a link in the memory.
21+
links.isEmpty ? nil : QueryWebsiteFunction(),
22+
]
1723
return .init(
1824
systemPrompt: "You prefer to answer questions with latest content on the internet.",
19-
functions: [
20-
SearchFunction(),
21-
]
25+
functions: functions.compactMap { $0 }
2226
)
2327
}
28+
}
2429

25-
struct SearchFunction: ChatGPTFunction {
26-
static let dateFormatter = {
27-
let it = DateFormatter()
28-
it.dateFormat = "yyyy-MM-dd"
29-
return it
30-
}()
31-
32-
struct Arguments: Codable {
33-
var query: String
34-
var freshness: String?
35-
}
36-
37-
struct Result: ChatGPTFunctionResult {
38-
var result: BingSearchResult
39-
40-
var botReadableContent: String {
41-
result.webPages.value.enumerated().map {
42-
let (index, page) = $0
43-
return """
44-
\(index + 1). \(page.name) \(page.url)
45-
\(page.snippet)
46-
"""
47-
}.joined(separator: "\n")
48-
}
49-
}
50-
51-
var name: String {
52-
"searchWeb"
53-
}
54-
55-
var description: String {
56-
"Useful for when you need to answer questions about latest information."
57-
}
58-
59-
var argumentSchema: JSONSchemaValue {
60-
let today = Self.dateFormatter.string(from: Date())
61-
return [
62-
.type: "object",
63-
.properties: [
64-
"query": [
65-
.type: "string",
66-
.description: "the search query",
67-
],
68-
"freshness": [
69-
.type: "string",
70-
.description: .string(
71-
"limit the search result to a specific range, use only when user ask the question about current events. Today is \(today). Format: yyyy-MM-dd..yyyy-MM-dd"
72-
),
73-
.examples: ["1919-10-20..1988-10-20"],
74-
],
75-
],
76-
.required: ["query"],
77-
]
78-
}
79-
80-
func message(at phase: ChatGPTFunctionCallPhase) -> String {
81-
func parseArgument(_ string: String) throws -> Arguments {
82-
try JSONDecoder().decode(Arguments.self, from: string.data(using: .utf8) ?? Data())
83-
}
84-
85-
switch phase {
86-
case .detected:
87-
return "Searching.."
88-
case let .processing(argumentsJsonString):
89-
do {
90-
let arguments = try parseArgument(argumentsJsonString)
91-
return "Searching \(arguments.query)"
92-
} catch {
93-
return "Searching.."
94-
}
95-
case let .ended(argumentsJsonString, result):
96-
do {
97-
let arguments = try parseArgument(argumentsJsonString)
98-
if let result = result as? Result {
99-
return """
100-
Finish searching \(arguments.query)
101-
\(
102-
result.result.webPages.value
103-
.map { "- [\($0.name)](\($0.url))" }
104-
.joined(separator: "\n")
105-
)
106-
"""
107-
}
108-
return "Finish searching \(arguments.query)"
109-
} catch {
110-
return "Finish searching"
111-
}
112-
case let .error(argumentsJsonString, _):
113-
do {
114-
let arguments = try parseArgument(argumentsJsonString)
115-
return "Failed searching \(arguments.query)"
116-
} catch {
117-
return "Failed searching"
118-
}
119-
}
120-
}
121-
122-
func call(arguments: Arguments) async throws -> Result {
123-
let bingSearch = BingSearchService(
124-
subscriptionKey: UserDefaults.shared.value(for: \.bingSearchSubscriptionKey),
125-
searchURL: UserDefaults.shared.value(for: \.bingSearchEndpoint)
126-
)
127-
let result = try await bingSearch.search(
128-
query: arguments.query,
129-
numberOfResult: UserDefaults.shared.value(for: \.chatGPTMaxToken) > 5000 ? 5 : 3,
130-
freshness: arguments.freshness
131-
)
132-
133-
let content = result.webPages.value.enumerated().map {
134-
let (index, page) = $0
135-
return """
136-
\(index + 1). \(page.name) \(page.url)
137-
\(page.snippet)
138-
"""
139-
}.joined(separator: "\n")
140-
141-
return .init(result: result)
142-
}
30+
extension WebChatContextCollector {
31+
static func detectLinks(from: [ChatMessage]) -> [String] {
32+
return []
14333
}
14434
}
14535

0 commit comments

Comments
 (0)