Skip to content

Commit ede7135

Browse files
committed
Add MathChatPlugin
1 parent 513468d commit ede7135

File tree

5 files changed

+112
-8
lines changed

5 files changed

+112
-8
lines changed
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import ChatPlugin
2+
import Environment
3+
import Foundation
4+
import OpenAIService
5+
6+
/// Use Python to solve math problems.
7+
public actor MathChatPlugin: ChatPlugin {
8+
public static var command: String { "math" }
9+
public nonisolated var name: String { "Math" }
10+
11+
let chatGPTService: any ChatGPTServiceType
12+
var isCancelled = false
13+
weak var delegate: ChatPluginDelegate?
14+
15+
public init(inside chatGPTService: any ChatGPTServiceType, delegate: ChatPluginDelegate) {
16+
self.chatGPTService = chatGPTService
17+
self.delegate = delegate
18+
}
19+
20+
public func send(content: String, originalMessage: String) async {
21+
delegate?.pluginDidStart(self)
22+
delegate?.pluginDidStartResponding(self)
23+
24+
let id = "\(Self.command)-\(UUID().uuidString)"
25+
var reply = ChatMessage(id: id, role: .assistant, content: "Calculating...")
26+
27+
await chatGPTService.mutateHistory { history in
28+
history.append(.init(role: .user, content: originalMessage, summary: content))
29+
history.append(reply)
30+
}
31+
32+
do {
33+
let result = try await solveMathProblem(content)
34+
await chatGPTService.mutateHistory { history in
35+
if history.last?.id == id {
36+
history.removeLast()
37+
}
38+
reply.content = result
39+
history.append(reply)
40+
}
41+
} catch {
42+
await chatGPTService.mutateHistory { history in
43+
if history.last?.id == id {
44+
history.removeLast()
45+
}
46+
reply.content = error.localizedDescription
47+
history.append(reply)
48+
}
49+
}
50+
51+
delegate?.pluginDidEndResponding(self)
52+
delegate?.pluginDidEnd(self)
53+
}
54+
55+
public func cancel() async {
56+
isCancelled = true
57+
}
58+
59+
public func stopResponding() async {
60+
isCancelled = true
61+
}
62+
}
63+
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import Foundation
2+
import LangChain
3+
import PythonKit
4+
5+
func solveMathProblem(_ problem: String) async throws -> String {
6+
#if DEBUG
7+
let verbose = true
8+
#else
9+
let verbose = false
10+
#endif
11+
12+
struct E: Error, LocalizedError {
13+
var errorDescription: String? {
14+
"Failed to parse answer."
15+
}
16+
}
17+
18+
let task = Task {
19+
try withReadableThrowingPython {
20+
let langchain = try Python.attemptImport("langchain")
21+
let LLMMathChain = langchain.LLMMathChain
22+
let llm = try LangChainChatModel.DynamicChatOpenAI(temperature: 0)
23+
let llmMath = LLMMathChain.from_llm(llm, verbose: verbose)
24+
let result = try llmMath.run.throwing.dynamicallyCall(withArguments: problem)
25+
let answer = String(result)
26+
if let answer { return answer }
27+
28+
throw E()
29+
}
30+
}
31+
32+
return try await task.value
33+
}
34+
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import ChatPlugin
2+
import MathChatPlugin
3+
4+
let allPlugins: [ChatPlugin.Type] = [
5+
TerminalChatPlugin.self,
6+
AITerminalChatPlugin.self,
7+
MathChatPlugin.self,
8+
]

Core/Sources/ChatService/ChatPluginController.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ final class ChatPluginController {
77
let chatGPTService: any ChatGPTServiceType
88
let plugins: [String: ChatPlugin.Type]
99
var runningPlugin: ChatPlugin?
10-
11-
init(chatGPTService: any ChatGPTServiceType, plugins: ChatPlugin.Type...) {
10+
11+
init(chatGPTService: any ChatGPTServiceType, plugins: [ChatPlugin.Type]) {
1212
self.chatGPTService = chatGPTService
1313
var all = [String: ChatPlugin.Type]()
1414
for plugin in plugins {
@@ -17,6 +17,10 @@ final class ChatPluginController {
1717
self.plugins = all
1818
}
1919

20+
convenience init(chatGPTService: any ChatGPTServiceType, plugins: ChatPlugin.Type...) {
21+
self.init(chatGPTService: chatGPTService, plugins: plugins)
22+
}
23+
2024
/// Handle the message in a plugin if required. Return false if no plugin handles the message.
2125
func handleContent(_ content: String) async throws -> Bool {
2226
// look for the prefix of content, see if there is something like /command.

Core/Sources/ChatService/ChatService.swift

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,7 @@ public final class ChatService: ObservableObject {
2525

2626
public init<T: ChatGPTServiceType>(chatGPTService: T) {
2727
self.chatGPTService = chatGPTService
28-
pluginController = ChatPluginController(
29-
chatGPTService: chatGPTService,
30-
plugins:
31-
TerminalChatPlugin.self,
32-
AITerminalChatPlugin.self
33-
)
28+
pluginController = ChatPluginController(chatGPTService: chatGPTService, plugins: allPlugins)
3429
contextController = DynamicContextController(
3530
chatGPTService: chatGPTService,
3631
contextCollectors: ActiveDocumentChatContextCollector()

0 commit comments

Comments
 (0)