support zenz-v3

This commit is contained in:
Miwa / Ensan
2025-02-06 23:01:51 +09:00
parent fe3fe96e9b
commit 9142f3e493
6 changed files with 100 additions and 24 deletions

View File

@ -8,7 +8,7 @@ let options = ConvertRequestOptions.withDefaultDictionary(
zenzaiMode: .on(
weight: url,
inferenceLimit: 1,
versionDependentMode: .v2(.init(profile: "三輪/azooKeyの開発者", leftSideContext: "私の名前は"))
versionDependentMode: .v3(.init(profile: "三輪/azooKeyの開発者", leftSideContext: "私の名前は"))
)
// ...
)

View File

@ -38,7 +38,7 @@ extension Subcommands {
shouldResetMemory: false,
memoryDirectoryURL: URL(fileURLWithPath: ""),
sharedContainerURL: URL(fileURLWithPath: ""),
zenzaiMode: self.zenzWeightPath.isEmpty ? .off : .on(weight: URL(string: self.zenzWeightPath)!, inferenceLimit: .max, versionDependentMode: .v2(.init())),
zenzaiMode: self.zenzWeightPath.isEmpty ? .off : .on(weight: URL(string: self.zenzWeightPath)!, inferenceLimit: .max, versionDependentMode: .v3(.init())),
metadata: .init(versionString: "anco for debugging")
)
}

View File

@ -25,12 +25,16 @@ extension Subcommands {
var roman2kana = false
@Option(name: [.customLong("config_zenzai_inference_limit")], help: "inference limit for zenzai.")
var configZenzaiInferenceLimit: Int = .max
@Flag(name: [.customLong("config_zenzai_rich_n_best")], help: "enable profile prompting for zenz-v2.")
@Flag(name: [.customLong("config_zenzai_rich_n_best")], help: "enable rich n_best generation for zenzai.")
var configRequestRichCandidates = false
@Option(name: [.customLong("config_profile")], help: "enable rich n_best generation for zenzai.")
var configZenzV2Profile: String?
@Option(name: [.customLong("config_profile")], help: "enable profile prompting for zenz-v2 and later.")
var configZenzaiProfile: String?
@Option(name: [.customLong("config_topic")], help: "enable topic prompting for zenz-v3 and later.")
var configZenzaiTopic: String?
@Flag(name: [.customLong("zenz_v1")], help: "Use zenz_v1 model.")
var zenzV1 = false
@Flag(name: [.customLong("zenz_v2")], help: "Use zenz_v2 model.")
var zenzV2 = false
static let configuration = CommandConfiguration(commandName: "session", abstract: "Start session for incremental input.")
@ -192,8 +196,10 @@ extension Subcommands {
func requestOptions(memoryDirectory: URL, leftSideContext: String) -> ConvertRequestOptions {
let zenzaiVersionDependentMode: ConvertRequestOptions.ZenzaiVersionDependentMode = if self.zenzV1 {
.v1
} else if self.zenzV2 {
.v2(.init(profile: self.configZenzaiProfile, leftSideContext: leftSideContext))
} else {
.v2(.init(profile: self.configZenzV2Profile, leftSideContext: leftSideContext))
.v3(.init(profile: self.configZenzaiProfile, topic: self.configZenzaiTopic, leftSideContext: leftSideContext))
}
var option: ConvertRequestOptions = .withDefaultDictionary(
N_best: self.onlyWholeConversion ? max(self.configNBest, self.displayTopN) : self.configNBest,

View File

@ -154,14 +154,37 @@ public struct ConvertRequestOptions: Sendable {
public var leftSideContext: String?
}
public struct ZenzaiV3DependentMode: Sendable, Equatable, Hashable {
public init(profile: String? = nil, topic: String? = nil, style: String? = nil, preference: String? = nil, leftSideContext: String? = nil) {
self.profile = profile
self.topic = topic
self.style = style
self.preference = preference
self.leftSideContext = leftSideContext
}
/// 1020
public var profile: String?
/// topic1020
public var topic: String?
/// style1020
public var style: String?
/// preferencepreference1020
public var preference: String?
///
public var leftSideContext: String?
}
public enum ZenzVersion: Sendable, Equatable, Hashable {
case v1
case v2
case v3
}
public enum ZenzaiVersionDependentMode: Sendable, Equatable, Hashable {
case v1
case v2(ZenzaiV2DependentMode)
case v3(ZenzaiV3DependentMode)
public var version: ZenzVersion {
switch self {
@ -169,6 +192,8 @@ public struct ConvertRequestOptions: Sendable {
return .v1
case .v2:
return .v2
case .v3:
return .v3
}
}
}
@ -179,7 +204,7 @@ public struct ConvertRequestOptions: Sendable {
weightURL: URL(fileURLWithPath: ""),
inferenceLimit: 10,
requestRichCandidates: false,
versionDependentMode: .v2(.init())
versionDependentMode: .v3(.init())
)
/// activate *Zenzai* - Neural Kana-Kanji Conversiion Engine
@ -188,7 +213,7 @@ public struct ConvertRequestOptions: Sendable {
/// - inferenceLimit: applying inference count limitation. Smaller limit makes conversion faster but quality will be worse. (Default: 10)
/// - requestRichCandidates: when this flag is true, the converter spends more time but generate richer N-Best candidates for candidate list view. Usually this option is not recommended for live conversion.
/// - versionDependentMode: specify zenz model version and its configuration.
public static func on(weight: URL, inferenceLimit: Int = 10, requestRichCandidates: Bool = false, versionDependentMode: ZenzaiVersionDependentMode = .v2(.init())) -> Self {
public static func on(weight: URL, inferenceLimit: Int = 10, requestRichCandidates: Bool = false, versionDependentMode: ZenzaiVersionDependentMode = .v3(.init())) -> Self {
ZenzaiMode(
enabled: true,
weightURL: weight,

View File

@ -62,7 +62,7 @@ import SwiftUtils
return []
}
guard options.zenzaiMode.versionDependentMode.version == .v2 else {
print("next character prediction requires zenz-v2 models, not zenz-v1")
print("next character prediction requires zenz-v2 models, not zenz-v1 nor zenz-v3 and later")
return []
}
let results = zenz.predictNextCharacter(leftSideContext: leftSideContext, count: count)

View File

@ -263,30 +263,75 @@ class ZenzContext {
conditions.append("辞書:\(userDictionaryPrompt)")
}
//
if case .v2(let mode) = versionDependentConfig, let profile = mode.profile, !profile.isEmpty {
let pf = profile.suffix(25)
conditions.append("プロフィール:\(profile)")
switch versionDependentConfig {
case .v1: break
case .v2(let mode):
if let profile = mode.profile, !profile.isEmpty {
let pf = profile.suffix(25)
conditions.append("プロフィール:\(pf)")
}
case .v3(let mode):
if let profile = mode.profile, !profile.isEmpty {
let pf = profile.suffix(25)
conditions.append("\u{EE03}\(pf)")
}
if let topic = mode.topic, !topic.isEmpty {
let tp = topic.suffix(25)
conditions.append("\u{EE04}\(tp)")
}
if let style = mode.style, !style.isEmpty {
let st = style.suffix(25)
conditions.append("\u{EE05}\(st)")
}
if let preference = mode.preference, !preference.isEmpty {
let pr = preference.suffix(25)
conditions.append("\u{EE06}\(pr)")
}
}
//
//
let leftSideContext = if case .v2(let mode) = versionDependentConfig, let leftSideContext = mode.leftSideContext {
String(leftSideContext.suffix(40))
} else {
""
let leftSideContext: String = switch versionDependentConfig {
case .v1: ""
case .v2(let mode):
if let leftSideContext = mode.leftSideContext {
String(leftSideContext.suffix(40))
} else {
""
}
case .v3(let mode):
if let leftSideContext = mode.leftSideContext {
String(leftSideContext.suffix(40))
} else {
""
}
}
let inputTag = "\u{EE00}"
let outputTag = "\u{EE01}"
let contextTag = "\u{EE02}"
//
let prompt: String = if !conditions.isEmpty {
// empty:
inputTag + input + contextTag + conditions.joined(separator: "") + "・発言:\(leftSideContext)" + outputTag
} else if !leftSideContext.isEmpty {
// emptyleftSideContext
inputTag + input + contextTag + leftSideContext + outputTag
} else {
//
let prompt: String = switch versionDependentConfig {
case .v1:
inputTag + input + outputTag
case .v2:
if !conditions.isEmpty {
// empty:
inputTag + input + contextTag + conditions.joined(separator: "") + "・発言:\(leftSideContext)" + outputTag
} else if !leftSideContext.isEmpty {
// emptyleftSideContext
inputTag + input + contextTag + leftSideContext + outputTag
} else {
//
inputTag + input + outputTag
}
case .v3:
if !leftSideContext.isEmpty {
// leftSideContextEmpty
// contextinput(KV-caching)
conditions.joined(separator: "") + contextTag + leftSideContext + inputTag + input + outputTag
} else {
//
conditions.joined(separator: "") + inputTag + input + outputTag
}
}
// Therefore, tokens = prompt_tokens + candidate_tokens is an appropriate operation.
let prompt_tokens = self.tokenize(text: prompt, add_bos: true, add_eos: false)