Files
AzooKeyKanaKanjiConverter/Sources/KanaKanjiConverterModule/ConversionAlgorithms/Zenzai/Zenz/Zenz.swift
2025-07-25 01:33:17 +09:00

92 lines
3.4 KiB
Swift

package import Foundation
import SwiftUtils
import EfficientNGram
private func debugLog(_ items: Any..., separator: String = " ", terminator: String = "\n") {
let message = items.map { "\($0)" }.joined(separator: separator) + terminator
let timestamp = ISO8601DateFormatter().string(from: Date())
let logEntry = "[\(timestamp)] \(message)"
debug(message)
let debugLogFile = URL(fileURLWithPath: "zenz.log")
if let data = logEntry.data(using: .utf8) {
if !FileManager.default.fileExists(atPath: debugLogFile.path) {
FileManager.default.createFile(atPath: debugLogFile.path, contents: data)
} else {
if let fileHandle = try? FileHandle(forWritingTo: debugLogFile) {
fileHandle.seekToEndOfFile()
fileHandle.write(data)
fileHandle.closeFile()
}
}
}
}
@MainActor package final class Zenz {
package var resourceURL: URL
package let ngl: Int
private var zenzContext: ZenzContext?
init(resourceURL: URL, ngl: Int = 0) throws {
self.resourceURL = resourceURL
self.ngl = ngl
debugLog("[Zenz] init called with resourceURL: \(resourceURL), ngl: \(ngl)")
do {
#if canImport(Darwin)
if #available(iOS 16, macOS 13, *) {
self.zenzContext = try ZenzContext.createContext(path: resourceURL.path(percentEncoded: false), ngl: ngl)
} else {
// this is not percent-encoded
self.zenzContext = try ZenzContext.createContext(path: resourceURL.path, ngl: ngl)
}
#else
// this is not percent-encoded
self.zenzContext = try ZenzContext.createContext(path: resourceURL.path, ngl: ngl)
#endif
debugLog("[Zenz] Loaded model \(resourceURL.lastPathComponent) successfully with ngl=\(ngl)")
} catch {
throw error
}
}
package func endSession() {
try? self.zenzContext?.reset_context()
}
func candidateEvaluate(
convertTarget: String,
candidates: [Candidate],
requestRichCandidates: Bool,
personalizationMode: (mode: ConvertRequestOptions.ZenzaiMode.PersonalizationMode, base: EfficientNGram, personal: EfficientNGram)?,
versionDependentConfig: ConvertRequestOptions.ZenzaiVersionDependentMode
) -> ZenzContext.CandidateEvaluationResult {
guard let zenzContext else {
return .error
}
for candidate in candidates {
let result = zenzContext.evaluate_candidate(
input: convertTarget.toKatakana(),
candidate: candidate,
requestRichCandidates: requestRichCandidates,
personalizationMode: personalizationMode,
versionDependentConfig: versionDependentConfig
)
return result
}
return .error
}
func predictNextCharacter(leftSideContext: String, count: Int) -> [(character: Character, value: Float)] {
guard let zenzContext else {
return []
}
let result = zenzContext.predict_next_character(leftSideContext: leftSideContext, count: count)
return result
}
package func pureGreedyDecoding(pureInput: String, maxCount: Int = .max) -> String {
return self.zenzContext?.pure_greedy_decoding(leftSideContext: pureInput, maxCount: maxCount) ?? ""
}
}