feat: add vocabulary-aware Transcriber and shared correction/result types
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -24,6 +24,63 @@ public protocol AudioRecording {
|
||||
|
||||
public protocol Transcriber {
|
||||
func transcribe(_ samples: [Float]) async throws -> String
|
||||
/// Biases decoding towards `vocabulary`. Engines without bias support ignore the list.
|
||||
func transcribe(_ samples: [Float], vocabulary: [String]) async throws -> String
|
||||
}
|
||||
|
||||
public extension Transcriber {
|
||||
func transcribe(_ samples: [Float], vocabulary: [String]) async throws -> String {
|
||||
try await transcribe(samples)
|
||||
}
|
||||
}
|
||||
|
||||
/// One replacement made by the dictionary correction pass.
|
||||
/// `start`/`length` are UTF-16 offsets into the corrected text.
|
||||
public struct AppliedCorrection: Equatable, Codable, Sendable {
|
||||
public var heard: String
|
||||
public var wrote: String
|
||||
public var rule: String
|
||||
public var start: Int
|
||||
public var length: Int
|
||||
|
||||
public init(heard: String, wrote: String, rule: String, start: Int, length: Int) {
|
||||
self.heard = heard
|
||||
self.wrote = wrote
|
||||
self.rule = rule
|
||||
self.start = start
|
||||
self.length = length
|
||||
}
|
||||
}
|
||||
|
||||
public struct CorrectionResult: Equatable, Sendable {
|
||||
public var text: String
|
||||
public var corrections: [AppliedCorrection]
|
||||
|
||||
public init(text: String, corrections: [AppliedCorrection]) {
|
||||
self.text = text
|
||||
self.corrections = corrections
|
||||
}
|
||||
}
|
||||
|
||||
public protocol TextCorrecting {
|
||||
func correct(_ text: String) -> CorrectionResult
|
||||
}
|
||||
|
||||
/// A finished dictation, emitted just before the text is inserted.
|
||||
public struct DictationResult: Equatable, Sendable {
|
||||
public var date: Date
|
||||
public var duration: TimeInterval
|
||||
public var rawText: String
|
||||
public var finalText: String
|
||||
public var corrections: [AppliedCorrection]
|
||||
|
||||
public init(date: Date, duration: TimeInterval, rawText: String, finalText: String, corrections: [AppliedCorrection]) {
|
||||
self.date = date
|
||||
self.duration = duration
|
||||
self.rawText = rawText
|
||||
self.finalText = finalText
|
||||
self.corrections = corrections
|
||||
}
|
||||
}
|
||||
|
||||
public protocol Cleaner {
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import Foundation
|
||||
import Testing
|
||||
@testable import SstCore
|
||||
|
||||
private struct PlainTranscriber: Transcriber {
|
||||
func transcribe(_ samples: [Float]) async throws -> String { "plain \(samples.count)" }
|
||||
}
|
||||
|
||||
struct TranscriberVocabularyTests {
|
||||
@Test func vocabularyVariantFallsBackToPlainTranscribe() async throws {
|
||||
let result = try await PlainTranscriber().transcribe([0, 0, 0], vocabulary: ["Anthropic"])
|
||||
#expect(result == "plain 3")
|
||||
}
|
||||
|
||||
@Test func appliedCorrectionRoundTripsThroughJSON() throws {
|
||||
let original = AppliedCorrection(heard: "cloud code", wrote: "Claude Code", rule: "cloud code", start: 12, length: 11)
|
||||
let data = try JSONEncoder().encode(original)
|
||||
let decoded = try JSONDecoder().decode(AppliedCorrection.self, from: data)
|
||||
#expect(decoded == original)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user