Skip to content

Commit b5b47a7

Browse files
committed
Merge branch 'feature/widget-read-cache-from-filespace' into develop
2 parents 5e09050 + d0d1245 commit b5b47a7

18 files changed

Lines changed: 501 additions & 483 deletions

Core/Sources/ChatPlugins/ChatPlugin.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ public protocol ChatPlugin {
66
static var command: String { get }
77
var name: String { get }
88

9-
init(inside chatGPTService: ChatGPTServiceType, delegate: ChatPluginDelegate)
9+
init(inside chatGPTService: any ChatGPTServiceType, delegate: ChatPluginDelegate)
1010
func send(content: String) async
1111
func cancel() async
1212
func stopResponding() async

Core/Sources/ChatPlugins/TerminalChatPlugin.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@ public actor TerminalChatPlugin: ChatPlugin {
77
public static var command: String { "run" }
88
public nonisolated var name: String { "Terminal" }
99

10-
let chatGPTService: ChatGPTServiceType
10+
let chatGPTService: any ChatGPTServiceType
1111
var terminal: TerminalType = Terminal()
1212
var isCancelled = false
1313
weak var delegate: ChatPluginDelegate?
1414

15-
public init(inside chatGPTService: ChatGPTServiceType, delegate: ChatPluginDelegate) {
15+
public init(inside chatGPTService: any ChatGPTServiceType, delegate: ChatPluginDelegate) {
1616
self.chatGPTService = chatGPTService
1717
self.delegate = delegate
1818
}

Core/Sources/ChatService/ChatService.swift

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,24 @@
11
import ChatPlugins
2+
import Combine
23
import Foundation
34
import OpenAIService
45

56
public final class ChatService: ObservableObject {
6-
let chatGPTService: ChatGPTServiceType
7+
public let chatGPTService: any ChatGPTServiceType
78
let plugins = registerPlugins(
89
TerminalChatPlugin.self
910
)
1011
var runningPlugin: ChatPlugin?
12+
var cancellable = Set<AnyCancellable>()
1113

12-
public init(chatGPTService: ChatGPTServiceType) {
14+
public init<T: ChatGPTServiceType>(chatGPTService: T) {
1315
self.chatGPTService = chatGPTService
16+
17+
chatGPTService.objectWillChange.sink { [weak self] _ in
18+
self?.objectWillChange.send()
19+
}.store(in: &cancellable)
1420
}
15-
21+
1622
public func send(content: String) async throws {
1723
// look for the prefix of content, see if there is something like /command.
1824
// If there is, then we need to find the plugin that can handle this command.
@@ -43,26 +49,30 @@ public final class ChatService: ObservableObject {
4349
}
4450
await chatGPTService.clearHistory()
4551
}
52+
53+
public func mutateSystemPrompt(_ newPrompt: String) async {
54+
await chatGPTService.mutateSystemPrompt(newPrompt)
55+
}
4656
}
4757

4858
extension ChatService: ChatPluginDelegate {
49-
public func pluginDidStartResponding(_ plugin: ChatPlugins.ChatPlugin) {
59+
public func pluginDidStartResponding(_: ChatPlugins.ChatPlugin) {
5060
Task {
5161
await chatGPTService.markReceivingMessage(true)
5262
}
5363
}
54-
55-
public func pluginDidEndResponding(_ plugin: ChatPlugins.ChatPlugin) {
64+
65+
public func pluginDidEndResponding(_: ChatPlugins.ChatPlugin) {
5666
Task {
5767
await chatGPTService.markReceivingMessage(false)
5868
}
5969
}
60-
70+
6171
public func pluginDidStart(_ plugin: ChatPlugin) {
6272
runningPlugin = plugin
6373
}
64-
65-
public func pluginDidEnd(_ plugin: ChatPlugin) {
74+
75+
public func pluginDidEnd(_: ChatPlugin) {
6676
runningPlugin = nil
6777
}
6878
}

Core/Sources/OpenAIService/ChatGPTService.swift

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import AsyncAlgorithms
22
import Foundation
3+
import Preferences
34

4-
public protocol ChatGPTServiceType {
5+
public protocol ChatGPTServiceType: ObservableObject {
6+
var isReceivingMessage: Bool { get async }
7+
var history: [ChatMessage] { get async }
58
func send(content: String, summary: String?) async throws -> AsyncThrowingStream<String, Error>
69
func stopReceivingMessage() async
710
func clearHistory() async
@@ -49,13 +52,31 @@ public struct ChatGPTError: Error, Codable, LocalizedError {
4952
}
5053
}
5154

52-
public actor ChatGPTService: ChatGPTServiceType, ObservableObject {
53-
public var temperature: Double
54-
public var model: String
55-
public var endpoint: String
56-
public var apiKey: String
55+
public actor ChatGPTService: ChatGPTServiceType {
5756
public var systemPrompt: String
58-
public var maxToken: Int
57+
public var temperature: Double
58+
59+
public var model: String {
60+
let value = UserDefaults.shared.value(for: \.chatGPTModel)
61+
if value.isEmpty { return "gpt-3.5-turbo" }
62+
return value
63+
}
64+
65+
public var endpoint: String {
66+
let value = UserDefaults.shared.value(for: \.chatGPTEndpoint)
67+
if value.isEmpty { return "https://api.openai.com/v1/chat/completions" }
68+
69+
return value
70+
}
71+
72+
public var apiKey: String {
73+
UserDefaults.shared.value(for: \.openAIAPIKey)
74+
}
75+
76+
public var maxToken: Int {
77+
UserDefaults.shared.value(for: \.chatGPTMaxToken)
78+
}
79+
5980
public var history: [ChatMessage] = [] {
6081
didSet { objectWillChange.send() }
6182
}
@@ -69,19 +90,11 @@ public actor ChatGPTService: ChatGPTServiceType, ObservableObject {
6990
var buildCompletionStreamAPI: CompletionStreamAPIBuilder = OpenAICompletionStreamAPI.init
7091

7192
public init(
72-
systemPrompt: String,
73-
apiKey: String,
74-
endpoint: String? = nil,
75-
model: String? = nil,
76-
temperature: Double = 1,
77-
maxToken: Int = 2048
93+
systemPrompt: String = "",
94+
temperature: Double = 1
7895
) {
7996
self.systemPrompt = systemPrompt
80-
self.apiKey = apiKey
81-
self.model = model ?? "gpt-3.5-turbo"
8297
self.temperature = temperature
83-
self.maxToken = maxToken
84-
self.endpoint = endpoint ?? "https://api.openai.com/v1/chat/completions"
8598
}
8699

87100
public func send(
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import ChatService
2+
import Foundation
3+
import OpenAIService
4+
import SuggestionWidget
5+
6+
extension ChatProvider {
7+
convenience init(service: ChatService, fileURL: URL, onCloseChat: @escaping () -> Void) {
8+
self.init()
9+
let cancellable = service.objectWillChange.sink { [weak self] in
10+
guard let self else { return }
11+
Task { @MainActor in
12+
self.history = (await service.chatGPTService.history).map { message in
13+
.init(
14+
id: message.id,
15+
isUser: message.role == .user,
16+
text: message.summary ?? message.content
17+
)
18+
}
19+
self.isReceivingMessage = await service.chatGPTService.isReceivingMessage
20+
}
21+
}
22+
23+
service.objectWillChange.send()
24+
25+
onMessageSend = { [cancellable] message in
26+
_ = cancellable
27+
Task {
28+
do {
29+
_ = try await service.send(content: message)
30+
} catch {
31+
PresentInWindowSuggestionPresenter().presentError(error)
32+
}
33+
}
34+
}
35+
onStop = {
36+
Task {
37+
await service.stopReceivingMessage()
38+
}
39+
}
40+
41+
onClear = {
42+
Task {
43+
await service.clearHistory()
44+
}
45+
}
46+
47+
onClose = {
48+
Task {
49+
await service.stopReceivingMessage()
50+
PresentInWindowSuggestionPresenter().closeChatRoom(fileURL: fileURL)
51+
onCloseChat()
52+
}
53+
}
54+
}
55+
}

Core/Sources/Service/GUI/GraphicalUserInterfaceController.swift.swift

Lines changed: 1 addition & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -9,33 +9,7 @@ public final class GraphicalUserInterfaceController {
99
nonisolated let suggestionWidget = SuggestionWidgetController()
1010
private nonisolated init() {
1111
Task { @MainActor in
12-
suggestionWidget.onNextButtonTapped = {
13-
Task { @ServiceActor in
14-
let handler = PseudoCommandHandler()
15-
await handler.presentNextSuggestion()
16-
}
17-
}
18-
19-
suggestionWidget.onPreviousButtonTapped = {
20-
Task { @ServiceActor in
21-
let handler = PseudoCommandHandler()
22-
await handler.presentPreviousSuggestion()
23-
}
24-
}
25-
26-
suggestionWidget.onRejectButtonTapped = {
27-
Task { @ServiceActor in
28-
let handler = PseudoCommandHandler()
29-
await handler.rejectSuggestions()
30-
}
31-
}
32-
33-
suggestionWidget.onAcceptButtonTapped = {
34-
Task { @ServiceActor in
35-
let handler = PseudoCommandHandler()
36-
await handler.acceptSuggestion()
37-
}
38-
}
12+
suggestionWidget.dataSource = WidgetDataSource.shared
3913
}
4014
}
4115
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import ChatService
2+
import Foundation
3+
import SuggestionWidget
4+
5+
final class WidgetDataSource: SuggestionWidgetDataSource {
6+
static let shared = WidgetDataSource()
7+
8+
private init() {}
9+
10+
func suggestionForFile(at url: URL) async -> SuggestionProvider? {
11+
for workspace in await workspaces.values {
12+
if let filespace = await workspace.filespaces[url],
13+
let suggestion = await filespace.presentingSuggestion
14+
{
15+
return .init(
16+
code: suggestion.text,
17+
language: await filespace.language,
18+
startLineIndex: suggestion.position.line,
19+
suggestionCount: await filespace.suggestions.count,
20+
currentSuggestionIndex: await filespace.suggestionIndex,
21+
onSelectPreviousSuggestionTapped: {
22+
Task { @ServiceActor in
23+
let handler = PseudoCommandHandler()
24+
await handler.presentPreviousSuggestion()
25+
}
26+
},
27+
onSelectNextSuggestionTapped: {
28+
Task { @ServiceActor in
29+
let handler = PseudoCommandHandler()
30+
await handler.presentNextSuggestion()
31+
}
32+
},
33+
onRejectSuggestionTapped: {
34+
Task { @ServiceActor in
35+
let handler = PseudoCommandHandler()
36+
await handler.rejectSuggestions()
37+
}
38+
},
39+
onAcceptSuggestionTapped: {
40+
Task { @ServiceActor in
41+
let handler = PseudoCommandHandler()
42+
await handler.acceptSuggestion()
43+
}
44+
}
45+
)
46+
}
47+
}
48+
return nil
49+
}
50+
51+
func chatForFile(at url: URL) async -> ChatProvider? {
52+
for workspace in await workspaces.values {
53+
if let filespace = await workspace.filespaces[url],
54+
let service = await filespace.chatService
55+
{
56+
return .init(
57+
service: service,
58+
fileURL: url,
59+
onCloseChat: { [weak filespace] in
60+
Task { @ServiceActor [weak filespace] in
61+
filespace?.chatService = nil
62+
}
63+
}
64+
)
65+
}
66+
}
67+
return nil
68+
}
69+
70+
func chatServiceForFile(at url: URL) async -> ChatService? {
71+
for workspace in await workspaces.values {
72+
if let filespace = await workspace.filespaces[url] {
73+
return await filespace.chatService
74+
}
75+
}
76+
return nil
77+
}
78+
}

0 commit comments

Comments
 (0)