Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Patch for <macOS 15 build systems #283

Merged
merged 21 commits into from
Dec 21, 2024
Merged
Changes from 1 commit
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
4915574
Prevent units tests from succeeding on error
ZachNagengast Dec 20, 2024
2eff5b0
Use compiler flags for mltensor sampling
ZachNagengast Dec 20, 2024
936e4ed
Add flag for mltensor utils
ZachNagengast Dec 20, 2024
c39f052
Update platform versions for development and pre-release tests workflows
ZachNagengast Dec 20, 2024
f1f5dbe
Differentiate artifact name in unit-tests.yml workflow
ZachNagengast Dec 20, 2024
b43015f
Stop unit tests early if run condition is false
ZachNagengast Dec 20, 2024
13cf628
Fix macos 14 runner ios version
ZachNagengast Dec 20, 2024
9020e30
Use success error code for expected test skipping
ZachNagengast Dec 20, 2024
ac83405
Lower priority of early stopping task, cleanup
ZachNagengast Dec 20, 2024
2a8b95b
Formatting
ZachNagengast Dec 20, 2024
5a5517d
Fix tests, attempt to lower early stopping task priority further
ZachNagengast Dec 20, 2024
87d1720
Fix inverted action run condition logic
ZachNagengast Dec 20, 2024
cf0b880
Use detached lower priority for early stopping to resolve priority in…
ZachNagengast Dec 20, 2024
f052eac
Fix tests
ZachNagengast Dec 20, 2024
cb589e4
Set test priority for early stopping, fix correctness test on macos 14
ZachNagengast Dec 20, 2024
59ef54f
Upgrade unit test task priority
ZachNagengast Dec 20, 2024
f2d3c22
Specify device for older iOS simulators
ZachNagengast Dec 20, 2024
024049c
Fix workflow for ios-device
ZachNagengast Dec 20, 2024
1d3b1f4
Disable ealy stopping test on watchos
ZachNagengast Dec 20, 2024
250ed9d
Set xcode version on CI
ZachNagengast Dec 20, 2024
75bf130
Make sure test simulator is available on runner
ZachNagengast Dec 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Lower priority of early stopping task, cleanup
ZachNagengast committed Dec 20, 2024

Verified

This commit was signed with the committer’s verified signature.
so1ve Ray
commit ac834058843ea9a36f910fe7e517fe33feba6ee4
4 changes: 2 additions & 2 deletions Sources/WhisperKit/Core/TextDecoder.swift
Original file line number Diff line number Diff line change
@@ -213,7 +213,7 @@ public extension TextDecoding {
throw WhisperError.tokenizerUnavailable()
}

var prefilledDecoderInputs = decoderInputs
let prefilledDecoderInputs = decoderInputs

// Setup prefill tokens based on task and language
var prefillTokens: [Int] = [tokenizer.specialTokens.startOfTranscriptToken] // SOT
@@ -828,7 +828,7 @@ open class TextDecoder: TextDecoding, WhisperMLModel {

// Call the callback if it is provided on a background thread
if let callback = callback {
Task.detached { [weak self] in
Task(priority: .utility) { [weak self] in
guard let self = self else { return }
let shouldContinue = callback(result)
if let shouldContinue = shouldContinue, !shouldContinue, !isPrefill {
96 changes: 56 additions & 40 deletions Tests/WhisperKitTests/UnitTests.swift
Original file line number Diff line number Diff line change
@@ -743,46 +743,6 @@ final class UnitTests: XCTestCase {
)
}

func testDecodingEarlyStopping() async throws {
let earlyStopTokenCount = 10
let options = DecodingOptions()
let continuationCallback: TranscriptionCallback = { (progress: TranscriptionProgress) -> Bool? in
// Stop after only 10 tokens (full test audio contains 16)
progress.tokens.count <= earlyStopTokenCount
}

let result = try await XCTUnwrapAsync(
await transcribe(with: .tiny, options: options, callback: continuationCallback).first!,
"Failed to transcribe"
)

XCTAssertNotNil(result)
let tokenCountWithEarlyStop = result.segments.flatMap { $0.tokens }.count
let decodingTimePerTokenWithEarlyStop = result.timings.decodingLoop / Double(tokenCountWithEarlyStop)

// Work done in the callback should not block the decoding loop
let continuationCallbackWithWait: TranscriptionCallback = { (progress: TranscriptionProgress) -> Bool? in
Thread.sleep(forTimeInterval: 2)
return false
}

let resultWithWait = try await XCTUnwrapAsync(
await transcribe(with: .tiny, options: options, callback: continuationCallbackWithWait).first!,
"Failed to transcribe"
)

XCTAssertNotNil(resultWithWait)
let tokenCountWithWait = resultWithWait.segments.flatMap { $0.tokens }.count
let decodingTimePerTokenWithWait = resultWithWait.timings.decodingLoop / Double(tokenCountWithWait)
Logging.debug("Decoding loop without wait: \(result.timings.decodingLoop), with wait: \(resultWithWait.timings.decodingLoop)")

// Assert that the decoding predictions per token are not slower with the waiting
XCTAssertEqual(decodingTimePerTokenWithWait, decodingTimePerTokenWithEarlyStop, accuracy: decodingTimePerTokenWithEarlyStop, "Decoding predictions per token should not be significantly slower with waiting")

// Assert that more tokens are returned in the callback with waiting
XCTAssertGreaterThan(tokenCountWithWait, tokenCountWithEarlyStop, "More tokens should be returned in the callback with waiting")
}

// MARK: - Tokenizer Tests

func testDecoderTokenizer() async throws {
@@ -1300,6 +1260,62 @@ final class UnitTests: XCTestCase {
await fulfillment(of: [modelStateExpectation, segmentDiscoveryExpectation, transcriptionStateExpectation], timeout: 1)
}

func testCallbackWithEarlyStopping() async throws {
let computeOptions = ModelComputeOptions(
melCompute: .cpuOnly,
audioEncoderCompute: .cpuOnly,
textDecoderCompute: .cpuOnly,
prefillCompute: .cpuOnly
)

let config = try WhisperKitConfig(
modelFolder: tinyModelPath(),
computeOptions: computeOptions,
verbose: true,
logLevel: .debug,
load: false
)
let whisperKit = try await WhisperKit(config)

try await whisperKit.loadModels()
let audioFilePath = try XCTUnwrap(
Bundle.current.path(forResource: "jfk", ofType: "wav"),
"Audio file not found"
)

let earlyStopTokenCount = 10
let continuationCallback: TranscriptionCallback = { (progress: TranscriptionProgress) -> Bool? in
// Stop after only 10 tokens (full test audio contains 16)
progress.tokens.count <= earlyStopTokenCount
}

let result = try await whisperKit.transcribe(audioPath: audioFilePath, callback: continuationCallback).first!

XCTAssertNotNil(result)
let tokenCountWithEarlyStop = result.segments.flatMap { $0.tokens }.count
let decodingTimePerTokenWithEarlyStop = result.timings.decodingLoop / Double(tokenCountWithEarlyStop)

// Work done in the callback should not block the decoding loop
let continuationCallbackWithWait: TranscriptionCallback = { (progress: TranscriptionProgress) -> Bool? in
Thread.sleep(forTimeInterval: 5)
return false
}

let resultWithWait = try await whisperKit.transcribe(audioPath: audioFilePath, callback: continuationCallbackWithWait).first!

XCTAssertNotNil(resultWithWait)
let tokenCountWithWait = resultWithWait.segments.flatMap { $0.tokens }.count
let decodingTimePerTokenWithWait = resultWithWait.timings.decodingLoop / Double(tokenCountWithWait)
Logging.debug("Decoding loop without wait: \(result.timings.decodingLoop), with wait: \(resultWithWait.timings.decodingLoop)")

// Assert that the decoding predictions per token are not slower with the waiting
XCTAssertEqual(decodingTimePerTokenWithWait, decodingTimePerTokenWithEarlyStop, accuracy: decodingTimePerTokenWithEarlyStop, "Decoding predictions per token should not be significantly slower with waiting")

// Assert that more tokens are returned in the callback with waiting
XCTAssertEqual(tokenCountWithWait, 30, "Token count should be equal to full audio file with 5 seconds of wait")
XCTAssertGreaterThan(tokenCountWithWait, tokenCountWithEarlyStop, "More tokens should be returned in the callback with waiting")
}

// MARK: - Utils Tests

func testFillIndexesWithValue() throws {