|
| 1 | +import ChatPlugin |
1 | 2 | import Foundation |
2 | 3 | import LangChain |
3 | | -import PythonHelper |
4 | | -import PythonKit |
5 | | - |
6 | | -func solveMathProblem(_ problem: String) async throws -> String { |
7 | | - #if DEBUG |
8 | | - let verbose = true |
9 | | - #else |
10 | | - let verbose = false |
11 | | - #endif |
12 | | - |
13 | | - struct E: Error, LocalizedError { |
14 | | - var errorDescription: String? { |
15 | | - "Failed to parse answer." |
| 4 | +import Logger |
| 5 | +import OpenAIService |
| 6 | + |
| 7 | +let systemPrompt = """ |
| 8 | +Translate a math problem into a expression that can be executed using Python's numexpr library. |
| 9 | +Use the output of running this code to answer the question. |
| 10 | +
|
| 11 | +Question: ${{Question with math problem.}} |
| 12 | +```text |
| 13 | +${{single line mathematical expression that solves the problem}} |
| 14 | +``` |
| 15 | +...numexpr.evaluate(text)... |
| 16 | +```output |
| 17 | +${{Output of running the code}} |
| 18 | +``` |
| 19 | +Answer: ${{Answer}} |
| 20 | +
|
| 21 | +Begin. |
| 22 | +
|
| 23 | +Question: What is 37593 * 67? |
| 24 | +```text |
| 25 | +37593 * 67 |
| 26 | +``` |
| 27 | +...numexpr.evaluate("37593 * 67")... |
| 28 | +```output |
| 29 | +2518731 |
| 30 | +``` |
| 31 | +Answer: 2518731 |
| 32 | +
|
| 33 | +Question: 37593^(1/5) |
| 34 | +```text |
| 35 | +37593**(1/5) |
| 36 | +``` |
| 37 | +...numexpr.evaluate("37593**(1/5)")... |
| 38 | +```output |
| 39 | +8.222831614237718 |
| 40 | +``` |
| 41 | +Answer: 8.222831614237718 |
| 42 | +""" |
| 43 | + |
| 44 | +/// Extract the math problem with ChatGPT, and pass it to python to get the result. |
| 45 | +/// |
| 46 | +/// [llm_math in |
| 47 | +/// LangChain](https://github.com/hwchase17/langchain/blob/master/langchain/chains/llm_math/base.py) |
| 48 | +/// |
| 49 | +/// The logic is basically the same as the LLMMathChain provided in LangChain. |
| 50 | +func solveMathProblem(_ question: String) async throws -> String { |
| 51 | + guard let reply = try await askChatGPT( |
| 52 | + systemPrompt: systemPrompt, |
| 53 | + question: "Question: \(question)", |
| 54 | + temperature: 0 |
| 55 | + ) else { return "No answer." } |
| 56 | + |
| 57 | + // parse inside text code block |
| 58 | + let codeBlockRegex = try NSRegularExpression(pattern: "```text\n(.*?)\n```", options: []) |
| 59 | + let codeBlockMatches = codeBlockRegex.matches( |
| 60 | + in: reply, |
| 61 | + options: [], |
| 62 | + range: NSRange(reply.startIndex..<reply.endIndex, in: reply) |
| 63 | + ) |
| 64 | + if let firstMatch = codeBlockMatches.first, let textRange = Range( |
| 65 | + firstMatch.range(at: 1), |
| 66 | + in: reply |
| 67 | + ) { |
| 68 | + let text = reply[textRange] |
| 69 | + let expression = String(text) |
| 70 | + let task = Task { try evaluateWithPython(expression) } |
| 71 | + if let answer = try await task.value { |
| 72 | + return answer |
16 | 73 | } |
17 | 74 | } |
18 | 75 |
|
19 | | - let task = Task { |
20 | | - try runPython { |
21 | | - let langchain = try Python.attemptImportOnPythonThread("langchain") |
22 | | - let LLMMathChain = langchain.LLMMathChain |
23 | | - let llm = try LangChainChatModel.DynamicChatOpenAI(temperature: 0) |
24 | | - let llmMath = LLMMathChain.from_llm(llm, verbose: verbose) |
25 | | - let result = try llmMath.run.throwing.dynamicallyCall(withArguments: problem) |
26 | | - let answer = String(result) |
27 | | - if let answer { return answer } |
28 | | - |
29 | | - throw E() |
30 | | - } |
| 76 | + // parse after Answer: |
| 77 | + let answerRegex = try NSRegularExpression(pattern: "Answer: (.*)", options: []) |
| 78 | + let answerMatches = answerRegex.matches( |
| 79 | + in: reply, |
| 80 | + options: [], |
| 81 | + range: NSRange(reply.startIndex..<reply.endIndex, in: reply) |
| 82 | + ) |
| 83 | + if let firstMatch = answerMatches.first, let answerRange = Range( |
| 84 | + firstMatch.range(at: 1), |
| 85 | + in: reply |
| 86 | + ) { |
| 87 | + let answer = reply[answerRange] |
| 88 | + return String(answer) |
31 | 89 | } |
32 | | - |
33 | | - return try await task.value |
| 90 | + |
| 91 | + return reply |
| 92 | +} |
| 93 | + |
| 94 | +func evaluateWithPython(_ expression: String) throws -> String? { |
| 95 | + let mathExpression = NSExpression(format: expression) |
| 96 | + let value = mathExpression.expressionValue(with: nil, context: nil) |
| 97 | + Logger.service.debug(String(describing: value)) |
| 98 | + return (value as? Int).flatMap(String.init) |
34 | 99 | } |
35 | 100 |
|
0 commit comments