Skip to content

Commit d239573

Browse files
committed
Merge branch 'release/0.36.0'
2 parents 7840206 + a346816 commit d239573

File tree

44 files changed

+583
-278
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+583
-278
lines changed

ChatPlugins/Sources/ShortcutChatPlugin/ShortcutChatPlugin.swift

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,36 @@ public final class ShortcutChatPlugin: ChatPlugin {
2020
terminal = Terminal()
2121
}
2222

23-
public func send(_ request: Request) async -> AsyncThrowingStream<Response, any Error> {
23+
public func sendForTextResponse(_ request: Request) async
24+
-> AsyncThrowingStream<String, any Error>
25+
{
26+
let stream = await sendForComplicatedResponse(request)
27+
return .init { continuation in
28+
let task = Task {
29+
do {
30+
for try await response in stream {
31+
switch response {
32+
case let .content(.text(content)):
33+
continuation.yield(content)
34+
default:
35+
break
36+
}
37+
}
38+
continuation.finish()
39+
} catch {
40+
continuation.finish(throwing: error)
41+
}
42+
}
43+
44+
continuation.onTermination = { _ in
45+
task.cancel()
46+
}
47+
}
48+
}
49+
50+
public func sendForComplicatedResponse(_ request: Request) async
51+
-> AsyncThrowingStream<Response, any Error>
52+
{
2453
return .init { continuation in
2554
let task = Task {
2655
let id = "\(Self.command)-\(UUID().uuidString)"

ChatPlugins/Sources/TerminalChatPlugin/TerminalChatPlugin.swift

Lines changed: 95 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ public final class TerminalChatPlugin: ChatPlugin {
99
public static var name: String { "Terminal" }
1010
public static var description: String { """
1111
Run the command in the message from terminal.
12-
12+
1313
You can use environment variable `$FILE_PATH` and `$PROJECT_ROOT` to access the current file path and project root.
1414
""" }
1515

@@ -23,41 +23,11 @@ public final class TerminalChatPlugin: ChatPlugin {
2323
terminal = Terminal()
2424
}
2525

26-
public func formatContent(_ content: Response.Content) -> Response.Content {
27-
switch content {
28-
case let .text(content):
29-
return .text("""
30-
```sh
31-
\(content)
32-
```
33-
""")
34-
}
35-
}
36-
37-
public func send(_ request: Request) async -> AsyncThrowingStream<Response, any Error> {
26+
public func getTextContent(from request: Request) async
27+
-> AsyncStream<String>
28+
{
3829
return .init { continuation in
3930
let task = Task {
40-
var updateTime = Date()
41-
42-
func streamOutput(_ content: String) {
43-
defer { updateTime = Date() }
44-
if Date().timeIntervalSince(updateTime) > 60 * 2 {
45-
continuation.yield(.startNewMessage)
46-
continuation.yield(.startAction(
47-
id: "run",
48-
task: "Continue `\(request.text)`"
49-
))
50-
continuation.yield(.finishAction(
51-
id: "run",
52-
result: .success("Executed.")
53-
))
54-
continuation.yield(.content(.text("[continue]\n")))
55-
continuation.yield(.content(.text(content)))
56-
} else {
57-
continuation.yield(.content(.text(content)))
58-
}
59-
}
60-
6131
do {
6232
let fileURL = XcodeInspector.shared.realtimeActiveDocumentURL
6333
let projectURL = XcodeInspector.shared.realtimeActiveProjectURL
@@ -75,34 +45,25 @@ public final class TerminalChatPlugin: ChatPlugin {
7545
let env = ProcessInfo.processInfo.environment
7646
let shell = env["SHELL"] ?? "/bin/bash"
7747

78-
continuation.yield(.startAction(id: "run", task: "Run `\(request.text)`"))
79-
8048
let output = terminal.streamCommand(
8149
shell,
8250
arguments: ["-i", "-l", "-c", request.text],
8351
currentDirectoryURL: projectURL,
8452
environment: environment
8553
)
8654

87-
continuation.yield(.finishAction(
88-
id: "run",
89-
result: .success("Executed.")
90-
))
91-
55+
var accumulatedOutput = ""
9256
for try await content in output {
9357
try Task.checkCancellation()
94-
streamOutput(content)
58+
accumulatedOutput += content
59+
continuation.yield(accumulatedOutput)
9560
}
9661
} catch let error as Terminal.TerminationError {
97-
continuation.yield(.content(.text("""
98-
99-
[error: \(error.reason)]
100-
""")))
62+
let errorMessage = "\n\n[error: \(error.reason)]"
63+
continuation.yield(errorMessage)
10164
} catch {
102-
continuation.yield(.content(.text("""
103-
104-
[error: \(error.localizedDescription)]
105-
""")))
65+
let errorMessage = "\n\n[error: \(error.localizedDescription)]"
66+
continuation.yield(errorMessage)
10667
}
10768

10869
continuation.finish()
@@ -116,5 +77,89 @@ public final class TerminalChatPlugin: ChatPlugin {
11677
}
11778
}
11879
}
80+
81+
public func sendForTextResponse(_ request: Request) async
82+
-> AsyncThrowingStream<String, any Error>
83+
{
84+
let stream = await getTextContent(from: request)
85+
return .init { continuation in
86+
let task = Task {
87+
continuation.yield("Executing command: `\(request.text)`\n\n")
88+
continuation.yield("```console\n")
89+
for await text in stream {
90+
try Task.checkCancellation()
91+
continuation.yield(text)
92+
}
93+
continuation.yield("\n```\n")
94+
continuation.finish()
95+
}
96+
97+
continuation.onTermination = { _ in
98+
task.cancel()
99+
}
100+
}
101+
}
102+
103+
public func formatContent(_ content: Response.Content) -> Response.Content {
104+
switch content {
105+
case let .text(content):
106+
return .text("""
107+
```console
108+
\(content)
109+
```
110+
""")
111+
}
112+
}
113+
114+
public func sendForComplicatedResponse(_ request: Request) async
115+
-> AsyncThrowingStream<Response, any Error>
116+
{
117+
return .init { continuation in
118+
let task = Task {
119+
var updateTime = Date()
120+
121+
continuation.yield(.startAction(id: "run", task: "Run `\(request.text)`"))
122+
123+
let textStream = await getTextContent(from: request)
124+
var previousOutput = ""
125+
126+
continuation.yield(.finishAction(
127+
id: "run",
128+
result: .success("Executed.")
129+
))
130+
131+
for await accumulatedOutput in textStream {
132+
try Task.checkCancellation()
133+
134+
let newContent = accumulatedOutput.dropFirst(previousOutput.count)
135+
previousOutput = accumulatedOutput
136+
137+
if !newContent.isEmpty {
138+
if Date().timeIntervalSince(updateTime) > 60 * 2 {
139+
continuation.yield(.startNewMessage)
140+
continuation.yield(.startAction(
141+
id: "run",
142+
task: "Continue `\(request.text)`"
143+
))
144+
continuation.yield(.finishAction(
145+
id: "run",
146+
result: .success("Executed.")
147+
))
148+
continuation.yield(.content(.text("[continue]\n")))
149+
updateTime = Date()
150+
}
151+
152+
continuation.yield(.content(.text(String(newContent))))
153+
}
154+
}
155+
156+
continuation.finish()
157+
}
158+
159+
continuation.onTermination = { _ in
160+
task.cancel()
161+
}
162+
}
163+
}
119164
}
120165

Core/Package.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ let package = Package(
6464
.product(name: "SuggestionBasic", package: "Tool"),
6565
.product(name: "Logger", package: "Tool"),
6666
.product(name: "Preferences", package: "Tool"),
67-
].pro([
68-
"ProClient",
67+
].proCore([
68+
"LicenseManagement",
6969
])
7070
),
7171
.target(
@@ -348,7 +348,7 @@ extension [Target.Dependency] {
348348
extension [Package.Dependency] {
349349
var pro: [Package.Dependency] {
350350
if isProIncluded {
351-
return self + [.package(path: "../../Pro")]
351+
return self + [.package(path: "../../Pro"), .package(path: "../../Pro/ProCore")]
352352
}
353353
return self
354354
}

Core/Sources/ChatGPTChatTab/Views/ThemedMarkdownText.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,10 @@ extension MarkdownUI.Theme {
7171
}
7272
.codeBlock { configuration in
7373
let wrapCode = UserDefaults.shared.value(for: \.wrapCodeInChatCodeBlock)
74-
|| ["plaintext", "text", "markdown", "sh", "bash", "shell", "latex", "tex"]
74+
|| [
75+
"plaintext", "text", "markdown", "sh", "console", "bash", "shell", "latex",
76+
"tex"
77+
]
7578
.contains(configuration.language)
7679

7780
if wrapCode {

Core/Sources/ChatService/AllPlugins.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ final class LegacyChatPluginWrapper<Plugin: ChatPlugin>: LegacyChatPlugin {
5656

5757
let plugin = Plugin()
5858

59-
let stream = await plugin.send(.init(
59+
let stream = await plugin.sendForComplicatedResponse(.init(
6060
text: content,
6161
arguments: [],
6262
history: chatGPTService.memory.history

Core/Sources/HostApp/AccountSettings/ChatModelManagement/ChatModelEdit.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ struct ChatModelEdit {
3535
var openAICompatibleSupportsMultipartMessageContent = true
3636
var requiresBeginWithUserMessage = false
3737
var customBody: String = ""
38+
var supportsImages: Bool = true
3839
}
3940

4041
enum Action: Equatable, BindableAction {
@@ -290,7 +291,9 @@ extension ChatModel {
290291
return state.supportsFunctionCalling
291292
}
292293
}(),
293-
modelName: state.modelName.trimmingCharacters(in: .whitespacesAndNewlines),
294+
supportsImage: state.supportsImages,
295+
modelName: state.modelName
296+
.trimmingCharacters(in: .whitespacesAndNewlines),
294297
openAIInfo: .init(
295298
organizationID: state.openAIOrganizationID,
296299
projectID: state.openAIProjectID
@@ -331,7 +334,8 @@ extension ChatModel {
331334
openAICompatibleSupportsMultipartMessageContent: info.openAICompatibleInfo
332335
.supportsMultipartMessageContent,
333336
requiresBeginWithUserMessage: info.openAICompatibleInfo.requiresBeginWithUserMessage,
334-
customBody: info.customBodyInfo.jsonBody
337+
customBody: info.customBodyInfo.jsonBody,
338+
supportsImages: info.supportsImage
335339
)
336340
}
337341
}

Core/Sources/HostApp/AccountSettings/ChatModelManagement/ChatModelEditView.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,10 @@ struct ChatModelEditView: View {
358358
TextField(text: $store.openAIProjectID, prompt: Text("Optional")) {
359359
Text("Project ID")
360360
}
361+
362+
Toggle(isOn: $store.supportsImages) {
363+
Text("Supports Images")
364+
}
361365

362366
VStack(alignment: .leading, spacing: 8) {
363367
Text(Image(systemName: "exclamationmark.triangle.fill")) + Text(
@@ -386,6 +390,10 @@ struct ChatModelEditView: View {
386390

387391
MaxTokensTextField(store: store)
388392
SupportsFunctionCallingToggle(store: store)
393+
394+
Toggle(isOn: $store.supportsImages) {
395+
Text("Supports Images")
396+
}
389397
}
390398
}
391399
}
@@ -435,6 +443,10 @@ struct ChatModelEditView: View {
435443
Toggle(isOn: $store.requiresBeginWithUserMessage) {
436444
Text("Requires the first message to be from the user")
437445
}
446+
447+
Toggle(isOn: $store.supportsImages) {
448+
Text("Supports Images")
449+
}
438450
}
439451
}
440452
}
@@ -473,6 +485,10 @@ struct ChatModelEditView: View {
473485
MaxTokensTextField(store: store)
474486

475487
TextField("API Version", text: $store.apiVersion, prompt: Text("v1"))
488+
489+
Toggle(isOn: $store.supportsImages) {
490+
Text("Supports Images")
491+
}
476492
}
477493
}
478494
}
@@ -496,6 +512,10 @@ struct ChatModelEditView: View {
496512
Text("Keep Alive")
497513
}
498514

515+
Toggle(isOn: $store.supportsImages) {
516+
Text("Supports Images")
517+
}
518+
499519
VStack(alignment: .leading, spacing: 8) {
500520
Text(Image(systemName: "exclamationmark.triangle.fill")) + Text(
501521
" For more details, please visit [https://ollama.com](https://ollama.com)."
@@ -539,6 +559,10 @@ struct ChatModelEditView: View {
539559
}
540560

541561
MaxTokensTextField(store: store)
562+
563+
Toggle(isOn: $store.supportsImages) {
564+
Text("Supports Images")
565+
}
542566

543567
VStack(alignment: .leading, spacing: 8) {
544568
Text(Image(systemName: "exclamationmark.triangle.fill")) + Text(
@@ -572,6 +596,10 @@ struct ChatModelEditView: View {
572596
Toggle(isOn: $store.openAICompatibleSupportsMultipartMessageContent) {
573597
Text("Support multi-part message content")
574598
}
599+
600+
Toggle(isOn: $store.supportsImages) {
601+
Text("Supports Images")
602+
}
575603

576604
VStack(alignment: .leading, spacing: 8) {
577605
Text(Image(systemName: "exclamationmark.triangle.fill")) + Text(

Core/Sources/HostApp/FeatureSettings/Suggestion/SuggestionFeatureDisabledLanguageListView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ struct SuggestionFeatureDisabledLanguageListView: View {
7474
if settings.suggestionFeatureDisabledLanguageList.isEmpty {
7575
Text("""
7676
Empty
77-
Disable the language of a file by right clicking the circular widget.
77+
Disable the language of a file by right clicking the indicator widget.
7878
""")
7979
.multilineTextAlignment(.center)
8080
.padding()

Core/Sources/HostApp/FeatureSettings/Suggestion/SuggestionFeatureEnabledProjectListView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ struct SuggestionFeatureEnabledProjectListView: View {
8282
Text("""
8383
Empty
8484
Add project with "+" button
85-
Or right clicking the circular widget
85+
Or right clicking the indicator widget
8686
""")
8787
.multilineTextAlignment(.center)
8888
}

0 commit comments

Comments
 (0)