Skip to content

Commit 0b923c3

Browse files
committed
Add render as markdown param, bump ChatGPTSwift to 2.3.1
1 parent 46446b2 commit 0b923c3

File tree

6 files changed

+74
-55
lines changed

6 files changed

+74
-55
lines changed

Package.resolved

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ let package = Package(
1515
targets: ["ChatGPTUI"]),
1616
],
1717
dependencies: [
18-
.package(url: "https://github.com/alfianlosari/ChatGPTSwift.git", from: "2.2.5"),
18+
.package(url: "https://github.com/alfianlosari/ChatGPTSwift.git", from: "2.3.1"),
1919
.package(url: "https://github.com/apple/swift-markdown.git", from: "0.3.0"),
2020
.package(url: "https://github.com/alfianlosari/HighlighterSwift.git", from: "1.0.0"),
2121
.package(url: "https://github.com/alfianlosari/SiriWaveView.git", from: "1.1.0")

Sources/ChatGPTUI/ViewModels/TextChatViewModel.swift

Lines changed: 54 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,22 @@ open class TextChatViewModel<CustomContent: View> {
1919
public var senderImage: String?
2020
public var botImage: String?
2121
public var useStreaming = true
22+
public var renderAsMarkdown = true
2223

2324
public let api: ChatGPTAPI
2425
public var model: ChatGPTModel
2526
public var systemText: String
2627
public var temperature: Double
2728

28-
public init(messages: [MessageRow<CustomContent>] = [], senderImage: String? = nil, botImage: String? = nil, useStreaming: Bool = true, model: ChatGPTModel = .gpt_hyphen_3_period_5_hyphen_turbo, systemText: String = "You're a helpful assistant", temperature: Double = 0.6, apiKey: String) {
29+
public init(messages: [MessageRow<CustomContent>] = [], senderImage: String? = nil, botImage: String? = nil, useStreaming: Bool = true, model: ChatGPTModel = .gpt_hyphen_3_period_5_hyphen_turbo, systemText: String = "You're a helpful assistant", temperature: Double = 0.6, renderAsMarkdown: Bool = true, apiKey: String) {
2930
self.messages = messages
3031
self.senderImage = senderImage
3132
self.botImage = botImage
3233
self.useStreaming = useStreaming
3334
self.model = model
3435
self.api = ChatGPTAPI(apiKey: apiKey)
3536
self.systemText = systemText
37+
self.renderAsMarkdown = renderAsMarkdown
3638
self.temperature = temperature
3739
}
3840

@@ -76,9 +78,13 @@ open class TextChatViewModel<CustomContent: View> {
7678
var streamText = ""
7779
do {
7880
let parsingTask = ResponseParsingTask()
79-
let attributedSend = await parsingTask.parse(text: text)
80-
try Task.checkCancellation()
81-
messageRow.send = .attributed(attributedSend)
81+
if renderAsMarkdown {
82+
let attributedSend = await parsingTask.parse(text: text)
83+
try Task.checkCancellation()
84+
messageRow.send = .attributed(attributedSend)
85+
} else {
86+
messageRow.send = .rawText(text)
87+
}
8288

8389
self.messages.append(messageRow)
8490
let parserThresholdTextCount = 64
@@ -88,42 +94,48 @@ open class TextChatViewModel<CustomContent: View> {
8894
let stream = try await api.sendMessageStream(text: text, model: model, systemText: systemText, temperature: temperature)
8995
for try await text in stream {
9096
streamText += text
91-
currentTextCount += text.count
92-
93-
if currentTextCount >= parserThresholdTextCount || text.contains("```") {
94-
currentOutput = await parsingTask.parse(text: streamText)
95-
try Task.checkCancellation()
96-
currentTextCount = 0
97-
}
97+
if renderAsMarkdown {
98+
currentTextCount += text.count
99+
100+
if currentTextCount >= parserThresholdTextCount || text.contains("```") {
101+
currentOutput = await parsingTask.parse(text: streamText)
102+
try Task.checkCancellation()
103+
currentTextCount = 0
104+
}
98105

99-
if let currentOutput = currentOutput, !currentOutput.results.isEmpty {
100-
let suffixText = streamText.trimmingPrefix(currentOutput.string)
101-
var results = currentOutput.results
102-
let lastResult = results[results.count - 1]
103-
var lastAttrString = lastResult.attributedString
104-
if lastResult.isCodeBlock {
105-
#if os(macOS)
106-
lastAttrString.append(AttributedString(String(suffixText), attributes: .init([.font: NSFont.systemFont(ofSize: 12).apply(newTraits: .monoSpace), .foregroundColor: NSColor.white])))
107-
#else
108-
lastAttrString.append(AttributedString(String(suffixText), attributes: .init([.font: UIFont.systemFont(ofSize: 12).apply(newTraits: .traitMonoSpace), .foregroundColor: UIColor.white])))
109-
#endif
110-
106+
if let currentOutput = currentOutput, !currentOutput.results.isEmpty {
107+
let suffixText = streamText.trimmingPrefix(currentOutput.string)
108+
var results = currentOutput.results
109+
let lastResult = results[results.count - 1]
110+
var lastAttrString = lastResult.attributedString
111+
if lastResult.isCodeBlock {
112+
#if os(macOS)
113+
lastAttrString.append(AttributedString(String(suffixText), attributes: .init([.font: NSFont.systemFont(ofSize: 12).apply(newTraits: .monoSpace), .foregroundColor: NSColor.white])))
114+
#else
115+
lastAttrString.append(AttributedString(String(suffixText), attributes: .init([.font: UIFont.systemFont(ofSize: 12).apply(newTraits: .traitMonoSpace), .foregroundColor: UIColor.white])))
116+
#endif
117+
118+
} else {
119+
lastAttrString.append(AttributedString(String(suffixText)))
120+
}
121+
results[results.count - 1] = ParserResult(attributedString: lastAttrString, isCodeBlock: lastResult.isCodeBlock, codeBlockLanguage: lastResult.codeBlockLanguage)
122+
messageRow.response = .attributed(.init(string: streamText, results: results))
111123
} else {
112-
lastAttrString.append(AttributedString(String(suffixText)))
124+
messageRow.response = .attributed(.init(string: streamText, results: [
125+
ParserResult(attributedString: AttributedString(stringLiteral: streamText), isCodeBlock: false, codeBlockLanguage: nil)
126+
]))
113127
}
114-
results[results.count - 1] = ParserResult(attributedString: lastAttrString, isCodeBlock: lastResult.isCodeBlock, codeBlockLanguage: lastResult.codeBlockLanguage)
115-
messageRow.response = .attributed(.init(string: streamText, results: results))
128+
116129
} else {
117-
messageRow.response = .attributed(.init(string: streamText, results: [
118-
ParserResult(attributedString: AttributedString(stringLiteral: streamText), isCodeBlock: false, codeBlockLanguage: nil)
119-
]))
130+
messageRow.response = .rawText(streamText)
120131
}
121-
122132
self.messages[self.messages.count - 1] = messageRow
123-
if let currentString = currentOutput?.string, currentString != streamText {
124-
let output = await parsingTask.parse(text: streamText)
125-
try Task.checkCancellation()
126-
messageRow.response = .attributed(output)
133+
if renderAsMarkdown {
134+
if let currentString = currentOutput?.string, currentString != streamText {
135+
let output = await parsingTask.parse(text: streamText)
136+
try Task.checkCancellation()
137+
messageRow.response = .attributed(output)
138+
}
127139
}
128140
}
129141
} catch is CancellationError {
@@ -158,12 +170,14 @@ open class TextChatViewModel<CustomContent: View> {
158170
let responseText = try await api.sendMessage(text: text, model: model, systemText: systemText, temperature: temperature)
159171
try Task.checkCancellation()
160172

161-
let parsingTask = ResponseParsingTask()
162-
let output = await parsingTask.parse(text: responseText)
163-
try Task.checkCancellation()
164-
165-
messageRow.response = .attributed(output)
166-
173+
if renderAsMarkdown {
174+
let parsingTask = ResponseParsingTask()
175+
let output = await parsingTask.parse(text: responseText)
176+
try Task.checkCancellation()
177+
messageRow.response = .attributed(output)
178+
} else {
179+
messageRow.response = .rawText(responseText)
180+
}
167181
} catch {
168182
messageRow.responseError = error.localizedDescription
169183
}

Sources/ChatGPTUI/ViewModels/VoiceChatViewModel.swift

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ open class VoiceChatViewModel<CustomContent: View>: NSObject, AVAudioRecorderDel
1313
public var model: ChatGPTModel
1414
public var systemText: String
1515
public var temperature: Double
16+
public var renderAsMarkdown = true
1617

1718
public var state: VoiceChatState<CustomContent> = .idle(nil) {
1819
didSet {
@@ -43,11 +44,12 @@ open class VoiceChatViewModel<CustomContent: View>: NSObject, AVAudioRecorderDel
4344
.first!.appendingPathComponent("recording.m4a")
4445
}
4546

46-
public init(voiceType: VoiceType = .alloy, model: ChatGPTModel = .gpt_hyphen_4o, systemText: String = "You're a helpful assistant", temperature: Double = 0.6, apiKey: String) {
47+
public init(voiceType: VoiceType = .alloy, model: ChatGPTModel = .gpt_hyphen_4o, systemText: String = "You're a helpful assistant", temperature: Double = 0.6, renderAsMarkdown: Bool = true, apiKey: String) {
4748
self.selectedVoice = voiceType
4849
self.model = model
4950
self.systemText = systemText
5051
self.temperature = temperature
52+
self.renderAsMarkdown = renderAsMarkdown
5153
self.api = ChatGPTAPI(apiKey: apiKey)
5254
super.init()
5355
#if !os(macOS)
@@ -134,15 +136,18 @@ open class VoiceChatViewModel<CustomContent: View>: NSObject, AVAudioRecorderDel
134136
let response = try await api.sendMessage(text: prompt, model: model, systemText: systemText, temperature: temperature)
135137
try Task.checkCancellation()
136138

137-
let parsingTask = ResponseParsingTask()
138-
let output = await parsingTask.parse(text: response)
139-
try Task.checkCancellation()
140-
141139
let data = try await api.generateSpeechFrom(input: response, voice:
142140
.init(rawValue: selectedVoice.rawValue) ?? .alloy)
143141
try Task.checkCancellation()
144142

145-
try self.playAudio(data: data, response: .attributed(output))
143+
if self.renderAsMarkdown {
144+
let parsingTask = ResponseParsingTask()
145+
let output = await parsingTask.parse(text: response)
146+
try Task.checkCancellation()
147+
try self.playAudio(data: data, response: .attributed(output))
148+
} else {
149+
try self.playAudio(data: data, response: .rawText(response))
150+
}
146151
} catch {
147152
if Task.isCancelled { return }
148153
state = .error(error)

Sources/ChatGPTUI/Views/TextChatView.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ public struct TextChatView<CustomContent: View>: View {
99
@State var vm: TextChatViewModel<CustomContent>
1010
@FocusState var isTextFieldFocused: Bool
1111

12-
public init(senderImage: String? = nil, botImage: String? = nil, useStreaming: Bool = true, model: ChatGPTModel = .gpt_hyphen_3_period_5_hyphen_turbo, systemText: String = "You're a helpful assistant", temperature: Double = 0.6, apiKey: String) where CustomContent == Text {
13-
self.vm = .init(senderImage: senderImage, botImage: botImage, useStreaming: useStreaming, model: model, systemText: systemText, temperature: temperature, apiKey: apiKey)
12+
public init(senderImage: String? = nil, botImage: String? = nil, useStreaming: Bool = true, model: ChatGPTModel = .gpt_hyphen_3_period_5_hyphen_turbo, systemText: String = "You're a helpful assistant", temperature: Double = 0.6, renderAsMarkdown: Bool = true, apiKey: String) where CustomContent == Text {
13+
self.vm = .init(senderImage: senderImage, botImage: botImage, useStreaming: useStreaming, model: model, systemText: systemText, temperature: temperature, renderAsMarkdown: renderAsMarkdown, apiKey: apiKey)
1414
}
1515

1616
public init(customContentVM: TextChatViewModel<CustomContent>) {

Sources/ChatGPTUI/Views/VoiceChatView.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ public struct VoiceChatView<CustomContent: View>: View {
88
@State var isSymbolAnimating = false
99
var loadingImageSystemName = "circle.dotted.circle"
1010

11-
public init(voiceType: VoiceType = .alloy, model: ChatGPTModel = .gpt_hyphen_4o, systemText: String = "You're a helpful assistant", temperature: Double = 0.6, apiKey: String) where CustomContent == Text {
12-
self.vm = .init(voiceType: voiceType, model: model, systemText: systemText, temperature: temperature, apiKey: apiKey)
11+
public init(voiceType: VoiceType = .alloy, model: ChatGPTModel = .gpt_hyphen_4o, systemText: String = "You're a helpful assistant", temperature: Double = 0.6, renderAsMarkdown: Bool = true, apiKey: String) where CustomContent == Text {
12+
self.vm = .init(voiceType: voiceType, model: model, systemText: systemText, temperature: temperature, renderAsMarkdown: renderAsMarkdown, apiKey: apiKey)
1313
}
1414

1515
public init(customContentVM: VoiceChatViewModel<CustomContent>) {
@@ -36,7 +36,7 @@ public struct VoiceChatView<CustomContent: View>: View {
3636
}
3737
}
3838
}
39-
.contentMargins(.top, 16, for: .scrollContent)
39+
.contentMargins(.vertical, 16, for: .scrollContent)
4040
.frame(maxWidth: .infinity)
4141
.padding(.horizontal)
4242
.overlay { overlayView }

0 commit comments

Comments
 (0)