feat(polish): add context safeguards, layered prompts, and output validation

Use redacted cursor neighborhood and pause-aware chunks for more natural polish,
validate protected terms with retry/local fallback, and structure bilingual prompts
for consistency and provider prefix caching.
This commit is contained in:
Rocky
2026-07-29 17:45:11 +08:00
parent 2d44423f4c
commit 34be2e8dd1
40 changed files with 1827 additions and 183 deletions
@@ -0,0 +1,51 @@
import XCTest
@testable import OSGKeyboardShared
final class PolishOutputValidatorTests: XCTestCase {
func testMissingDictionaryCanonicalTermIsHardViolation() {
let dictionary = PersonalDictionary(entries: [
.init(
term: "Kubernetes",
aliases: ["k8s"],
category: .productName,
source: .manual
),
])
let violations = PolishOutputValidator.validate(
input: "部署 k8s 集群",
output: "部署容器集群",
dictionary: dictionary,
lengthRatio: 0.5...2
)
XCTAssertTrue(violations.contains(.missingDictionaryTerms(["Kubernetes"])))
XCTAssertTrue(violations.contains(where: \.isHard))
}
func testIdentifiersArePreservedExactly() {
let violations = PolishOutputValidator.validate(
input: "send https://example.com/a to dev@example.com using user_id",
output: "send it to the team",
dictionary: .empty,
lengthRatio: 0.5...2
)
XCTAssertTrue(violations.contains { violation in
if case .missingIdentifiers(let values) = violation {
return values.contains("https://example.com/a")
&& values.contains("dev@example.com")
&& values.contains("user_id")
}
return false
})
}
func testNumbersLengthAndLanguageAreObservationOnly() {
let violations = PolishOutputValidator.validate(
input: "项目 123 明天下午交付并通知全部相关成员",
output: "Ship tomorrow.",
dictionary: .empty,
lengthRatio: 0.9...1.1
)
XCTAssertFalse(violations.isEmpty)
XCTAssertTrue(violations.filter(\.isHard).isEmpty)
}
}