Skip to content

Commit 9602bbb

Browse files
committed
Merge branch 'release/0.3.0'
2 parents d4a69c5 + 788d1e4 commit 9602bbb

29 files changed

+597
-94
lines changed

Core/Package.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ let package = Package(
6666
]
6767
),
6868

69+
.testTarget(
70+
name: "CodeCompletionServiceTests",
71+
dependencies: ["CodeCompletionService"]
72+
),
6973
.testTarget(
7074
name: "FundamentalTests",
7175
dependencies: ["Fundamental"]

Core/Sources/CodeCompletionService/AzureOpenAIService.swift renamed to Core/Sources/CodeCompletionService/API/AzureOpenAIService.swift

File renamed without changes.

Core/Sources/CodeCompletionService/GoogleGeminiService.swift renamed to Core/Sources/CodeCompletionService/API/GoogleGeminiService.swift

File renamed without changes.

Core/Sources/CodeCompletionService/OllamaService.swift renamed to Core/Sources/CodeCompletionService/API/OllamaService.swift

File renamed without changes.

Core/Sources/CodeCompletionService/OpenAIService.swift renamed to Core/Sources/CodeCompletionService/API/OpenAIService.swift

File renamed without changes.

Core/Sources/CodeCompletionService/TabbyService.swift renamed to Core/Sources/CodeCompletionService/API/TabbyService.swift

File renamed without changes.

Core/Sources/CodeCompletionService/CodeCompletionService.swift

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,20 @@ protocol CodeCompletionServiceType {
1111
extension CodeCompletionServiceType {
1212
func getCompletions(
1313
_ request: PromptStrategy,
14+
streamStopStrategy: StreamStopStrategy,
1415
count: Int
1516
) async throws -> [String] {
1617
try await withThrowingTaskGroup(of: String.self) { group in
1718
for _ in 0..<max(1, count) {
1819
_ = group.addTaskUnlessCancelled {
19-
var result = ""
20+
let limiter = StreamLineLimiter(strategy: streamStopStrategy)
2021
let stream = try await getCompletion(request)
2122
for try await response in stream {
22-
result.append(response)
23+
if case let .finish(result) = limiter.push(response) {
24+
return result
25+
}
2326
}
24-
return result
27+
return limiter.result
2528
}
2629
}
2730

@@ -53,6 +56,7 @@ public struct CodeCompletionService {
5356

5457
public func getCompletions(
5558
_ prompt: PromptStrategy,
59+
streamStopStrategy: StreamStopStrategy,
5660
model: TabbyModel,
5761
count: Int
5862
) async throws -> [String] {
@@ -71,13 +75,18 @@ public struct CodeCompletionService {
7175
}
7276
}())
7377

74-
let result = try await service.getCompletions(prompt, count: count)
78+
let result = try await service.getCompletions(
79+
prompt,
80+
streamStopStrategy: streamStopStrategy,
81+
count: count
82+
)
7583
try Task.checkCancellation()
7684
return result
7785
}
7886

7987
public func getCompletions(
8088
_ prompt: PromptStrategy,
89+
streamStopStrategy: StreamStopStrategy,
8190
model: ChatModel,
8291
count: Int
8392
) async throws -> [String] {
@@ -92,7 +101,11 @@ public struct CodeCompletionService {
92101
stopWords: prompt.stopWords,
93102
apiKey: apiKey
94103
)
95-
let result = try await service.getCompletions(prompt, count: count)
104+
let result = try await service.getCompletions(
105+
prompt,
106+
streamStopStrategy: streamStopStrategy,
107+
count: count
108+
)
96109
try Task.checkCancellation()
97110
return result
98111
case .azureOpenAI:
@@ -103,7 +116,11 @@ public struct CodeCompletionService {
103116
stopWords: prompt.stopWords,
104117
apiKey: apiKey
105118
)
106-
let result = try await service.getCompletions(prompt, count: count)
119+
let result = try await service.getCompletions(
120+
prompt,
121+
streamStopStrategy: streamStopStrategy,
122+
count: count
123+
)
107124
try Task.checkCancellation()
108125
return result
109126
case .googleAI:
@@ -112,7 +129,11 @@ public struct CodeCompletionService {
112129
maxToken: model.info.maxTokens,
113130
apiKey: apiKey
114131
)
115-
let result = try await service.getCompletions(prompt, count: count)
132+
let result = try await service.getCompletions(
133+
prompt,
134+
streamStopStrategy: streamStopStrategy,
135+
count: count
136+
)
116137
try Task.checkCancellation()
117138
return result
118139
case .ollama:
@@ -124,7 +145,11 @@ public struct CodeCompletionService {
124145
keepAlive: model.info.ollamaInfo.keepAlive,
125146
format: .none
126147
)
127-
let result = try await service.getCompletions(prompt, count: count)
148+
let result = try await service.getCompletions(
149+
prompt,
150+
streamStopStrategy: streamStopStrategy,
151+
count: count
152+
)
128153
try Task.checkCancellation()
129154
return result
130155
case .unknown:
@@ -134,6 +159,7 @@ public struct CodeCompletionService {
134159

135160
public func getCompletions(
136161
_ prompt: PromptStrategy,
162+
streamStopStrategy: StreamStopStrategy,
137163
model: CompletionModel,
138164
count: Int
139165
) async throws -> [String] {
@@ -148,7 +174,11 @@ public struct CodeCompletionService {
148174
stopWords: prompt.stopWords,
149175
apiKey: apiKey
150176
)
151-
let result = try await service.getCompletions(prompt, count: count)
177+
let result = try await service.getCompletions(
178+
prompt,
179+
streamStopStrategy: streamStopStrategy,
180+
count: count
181+
)
152182
try Task.checkCancellation()
153183
return result
154184
case .azureOpenAI:
@@ -159,7 +189,11 @@ public struct CodeCompletionService {
159189
stopWords: prompt.stopWords,
160190
apiKey: apiKey
161191
)
162-
let result = try await service.getCompletions(prompt, count: count)
192+
let result = try await service.getCompletions(
193+
prompt,
194+
streamStopStrategy: streamStopStrategy,
195+
count: count
196+
)
163197
try Task.checkCancellation()
164198
return result
165199
case .ollama:
@@ -171,7 +205,11 @@ public struct CodeCompletionService {
171205
keepAlive: model.info.ollamaInfo.keepAlive,
172206
format: .none
173207
)
174-
let result = try await service.getCompletions(prompt, count: count)
208+
let result = try await service.getCompletions(
209+
prompt,
210+
streamStopStrategy: streamStopStrategy,
211+
count: count
212+
)
175213
try Task.checkCancellation()
176214
return result
177215
case .unknown:
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import Foundation
2+
3+
final class StreamLineLimiter {
4+
public private(set) var result = ""
5+
private var currentLine = ""
6+
private var existedLines = [String]()
7+
private let lineLimit: Int
8+
private let strategy: any StreamStopStrategy
9+
10+
enum PushResult: Equatable {
11+
case `continue`
12+
case finish(String)
13+
}
14+
15+
init(
16+
lineLimit: Int = UserDefaults.shared.value(for: \.maxNumberOfLinesOfSuggestion),
17+
strategy: any StreamStopStrategy
18+
) {
19+
self.lineLimit = lineLimit
20+
self.strategy = strategy
21+
}
22+
23+
func push(_ token: String) -> PushResult {
24+
currentLine.append(token)
25+
if let newLine = currentLine.last(where: { $0.isNewline }) {
26+
let lines = currentLine
27+
.breakLines(proposedLineEnding: String(newLine), appendLineBreakToLastLine: false)
28+
let (newLines, lastLine) = lines.headAndTail
29+
existedLines.append(contentsOf: newLines)
30+
currentLine = lastLine ?? ""
31+
}
32+
33+
let stopResult = if lineLimit <= 0 {
34+
StreamStopStrategyResult.continue
35+
} else {
36+
strategy.shouldStop(
37+
existedLines: existedLines,
38+
currentLine: currentLine,
39+
proposedLineLimit: lineLimit
40+
)
41+
}
42+
43+
switch stopResult {
44+
case .continue:
45+
result.append(token)
46+
return .continue
47+
case let .stop(appendingNewContent):
48+
if appendingNewContent {
49+
result.append(token)
50+
}
51+
return .finish(result)
52+
}
53+
}
54+
}
55+
56+
extension Array {
57+
var headAndTail: ([Element], Element?) {
58+
guard let tail = last else { return ([], nil) }
59+
return (Array(dropLast()), tail)
60+
}
61+
}
62+
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
public struct DefaultStreamStopStrategy: StreamStopStrategy {
2+
public init() {}
3+
4+
public func shouldStop(
5+
existedLines: [String],
6+
currentLine: String,
7+
proposedLineLimit: Int
8+
) -> StreamStopStrategyResult {
9+
if existedLines.count >= proposedLineLimit {
10+
return .stop(appendingNewContent: true)
11+
}
12+
return .continue
13+
}
14+
}
15+
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
public struct NeverStreamStopStrategy: StreamStopStrategy {
2+
public init() {}
3+
4+
public func shouldStop(
5+
existedLines: [String],
6+
currentLine: String,
7+
proposedLineLimit: Int
8+
) -> StreamStopStrategyResult {
9+
.continue
10+
}
11+
}
12+

0 commit comments

Comments
 (0)