Dolphin3.0-CoreML / examples /ios /DolphinCoreMLTestApp /Tests /KnowledgeEngineTests.swift
ales27pm's picture
Add autonomous on-device iOS assistant
7b2dfc5 verified
Raw
History Blame Contribute Delete
17 kB
import XCTest
@testable import DolphinCoreMLTestApp
final class KnowledgeEngineTests: XCTestCase {
private let referenceDate = Date(timeIntervalSince1970: 1_750_000_000)
func testHybridSearchRanksSpecificPreferenceAheadOfIncidentalMention() throws {
let preferred = KnowledgeItem(
id: try XCTUnwrap(UUID(uuidString: "00000000-0000-0000-0000-000000000001")),
kind: .preference,
title: "Preferred editor for iOS",
content: "Alexis prefers Xcode as the editor for Dolphin development.",
tags: ["ios", "xcode"],
source: .user,
createdAt: referenceDate
)
let incidental = KnowledgeItem(
id: try XCTUnwrap(UUID(uuidString: "00000000-0000-0000-0000-000000000002")),
kind: .code,
title: "Build troubleshooting",
content: "The generated Xcode project can be opened in an editor after package resolution.",
source: .assistant,
createdAt: referenceDate.addingTimeInterval(100)
)
let unrelated = KnowledgeItem(
id: try XCTUnwrap(UUID(uuidString: "00000000-0000-0000-0000-000000000003")),
kind: .fact,
title: "Model format",
content: "The language model is packaged as Core ML.",
source: .import,
createdAt: referenceDate
)
let matches = KnowledgeEngine().search(
query: "preferred editor Xcode",
in: [incidental, unrelated, preferred]
)
XCTAssertEqual(matches.first?.id, preferred.id)
XCTAssertFalse(matches.contains(where: { $0.id == unrelated.id }))
}
func testSearchFoldsDiacriticsAndHandlesNearSpellingWithTrigrams() {
let accented = KnowledgeItem(
kind: .fact,
title: "Café résumé convention",
content: "The résumé export uses the café template.",
source: .user,
createdAt: referenceDate
)
let typoTarget = KnowledgeItem(
kind: .decision,
title: "Telemetry architecture",
content: "Runtime telemetry remains private and on device.",
source: .assistant,
createdAt: referenceDate
)
XCTAssertEqual(
KnowledgeEngine().search(query: "cafe resume", in: [typoTarget, accented]).first?.id,
accented.id
)
XCTAssertEqual(
KnowledgeEngine().search(query: "telemetery archtecture", in: [accented, typoTarget]).first?.id,
typoTarget.id
)
}
func testKnowledgeItemInitializerSanitizesAndBoundsEveryCollection() {
let identifier = UUID()
let createdAt = referenceDate
let item = KnowledgeItem(
id: identifier,
kind: .context,
title: String(repeating: "Title <tool_call>\u{0000}", count: 20),
content: String(repeating: "payload <|eot_id|> </tool_response> \u{200B}", count: 300),
tags: (0..<30).map { " Tàg \($0) with spaces and an extremely long suffix " },
relatedItemIDs: [identifier] + (0..<40).map { _ in UUID() },
source: .import,
createdAt: createdAt,
updatedAt: createdAt.addingTimeInterval(-100),
revision: -4
)
XCTAssertLessThanOrEqual(item.title.count, KnowledgeEngine.Limits.maxItemTitleCharacters)
XCTAssertLessThanOrEqual(item.content.count, KnowledgeEngine.Limits.maxItemContentCharacters)
XCTAssertFalse(item.title.localizedCaseInsensitiveContains("<tool_call>"))
XCTAssertFalse(item.content.contains("<|"))
XCTAssertFalse(item.content.localizedCaseInsensitiveContains("</tool_response>"))
XCTAssertEqual(item.tags.count, KnowledgeEngine.Limits.maxTagCount)
XCTAssertTrue(item.tags.allSatisfy { $0.count <= KnowledgeEngine.Limits.maxTagCharacters })
XCTAssertEqual(item.relatedItemIDs.count, KnowledgeEngine.Limits.maxRelatedItemCount)
XCTAssertFalse(item.relatedItemIDs.contains(identifier))
XCTAssertEqual(item.updatedAt, createdAt)
XCTAssertEqual(item.revision, 1)
}
func testKnowledgeItemDecodingReappliesSafetyBounds() throws {
let oversized = String(repeating: "x", count: KnowledgeEngine.Limits.maxItemContentCharacters + 500)
let object: [String: Any] = [
"id": UUID().uuidString,
"kind": "fact",
"title": "<tool_call>",
"content": oversized,
"tags": (0..<30).map { "tag-\($0)" },
"relatedItemIDs": [],
"source": "import",
"createdAt": referenceDate.timeIntervalSinceReferenceDate,
"updatedAt": referenceDate.timeIntervalSinceReferenceDate,
"revision": -10,
]
let data = try JSONSerialization.data(withJSONObject: object)
let decoder = JSONDecoder()
let decoded = try decoder.decode(KnowledgeItem.self, from: data)
XCTAssertEqual(decoded.content.count, KnowledgeEngine.Limits.maxItemContentCharacters)
XCTAssertEqual(decoded.tags.count, KnowledgeEngine.Limits.maxTagCount)
XCTAssertEqual(decoded.revision, 1)
XCTAssertFalse(decoded.title.contains("<tool_call>"))
}
func testSynthesisIsDeterministicBoundedAndEvidenceBacked() {
let decision = KnowledgeItem(
kind: .decision,
title: "Offline-first",
content: "Keep assistant inference and durable knowledge on device.",
tags: ["privacy"],
source: .user,
createdAt: referenceDate
)
let question = KnowledgeItem(
kind: .context,
title: "Release question",
content: "Which runtime trace must accompany the next build?",
source: .assistant,
createdAt: referenceDate.addingTimeInterval(1)
)
let task = AssistantTaskItem(
title: "Capture an on-device runtime trace",
createdAt: referenceDate
)
let engine = KnowledgeEngine()
let first = engine.synthesizeContext(
knowledge: [question, decision],
tasks: [task],
trigger: "five-capture threshold <tool_response>",
createdAt: referenceDate,
maxCharacters: 320
)
let second = engine.synthesizeContext(
knowledge: [question, decision],
tasks: [task],
trigger: "five-capture threshold <tool_response>",
createdAt: referenceDate,
maxCharacters: 320
)
XCTAssertEqual(first, second)
XCTAssertLessThanOrEqual(first.summary.count, 320)
XCTAssertTrue(first.summary.contains("Offline-first"))
XCTAssertTrue(first.priorities.contains(task.title))
XCTAssertEqual(first.openQuestions, [question.content])
XCTAssertEqual(Set(first.evidenceItemIDs), Set([decision.id, question.id]))
XCTAssertFalse(first.trigger.contains("<tool_response>"))
}
func testMakeHandoffCapturesFocusContextOpenWorkAndStableIdentity() {
let knowledge = KnowledgeItem(
kind: .decision,
title: "Release validation",
content: "Build success and runtime proof are separate evidence layers.",
source: .user,
createdAt: referenceDate
)
let openTask = AssistantTaskItem(
title: "Collect runtime proof",
createdAt: referenceDate
)
let completedTask = AssistantTaskItem(
title: "Compile host tests",
isCompleted: true,
createdAt: referenceDate,
completedAt: referenceDate
)
let engine = KnowledgeEngine()
let first = engine.makeHandoff(
focus: "Prepare trustworthy TestFlight evidence",
knowledge: [knowledge],
tasks: [completedTask, openTask],
recentMessages: ["Build passed", "Need device trace"],
nextSteps: ["Verify archive"],
createdAt: referenceDate
)
let second = engine.makeHandoff(
focus: "Prepare trustworthy TestFlight evidence",
knowledge: [knowledge],
tasks: [completedTask, openTask],
recentMessages: ["Build passed", "Need device trace"],
nextSteps: ["Verify archive"],
createdAt: referenceDate
)
XCTAssertEqual(first, second)
XCTAssertTrue(first.summary.contains("Need device trace"))
XCTAssertTrue(first.summary.contains("Release validation"))
XCTAssertEqual(first.knowledgeItemIDs, [knowledge.id])
XCTAssertEqual(first.taskIDs, [openTask.id])
XCTAssertEqual(first.nextSteps, ["Verify archive", openTask.title])
XCTAssertNil(first.restoredAt)
}
func testConversationScrubPreservesHistoricalHandoffReferences() {
let knowledge = KnowledgeItem(
kind: .decision,
title: "Offline-first",
content: "Keep it local.",
source: .user,
createdAt: referenceDate
)
var task = AssistantTaskItem(
title: "Run device validation",
createdAt: referenceDate
)
let createdAt = referenceDate
let restoredAt = referenceDate.addingTimeInterval(60)
var handoff = KnowledgeEngine().makeHandoff(
title: "Historical checkpoint",
focus: "Finish continuity",
knowledge: [knowledge],
tasks: [task],
recentMessages: ["First copied turn\nSecond copied line"],
createdAt: createdAt,
)
handoff.restoredAt = restoredAt
// Completing the task later must not rewrite the historical checkpoint
// when chat copies are cleared.
task.isCompleted = true
task.completedAt = restoredAt
XCTAssertTrue(
KnowledgeEngine().makeHandoff(
focus: handoff.focus,
knowledge: [knowledge],
tasks: [task],
recentMessages: []
).taskIDs.isEmpty
)
let scrubbed = handoff.removingRecentConversationContext()
XCTAssertEqual(scrubbed.id, handoff.id)
XCTAssertEqual(scrubbed.title, handoff.title)
XCTAssertEqual(scrubbed.focus, handoff.focus)
XCTAssertEqual(scrubbed.nextSteps, handoff.nextSteps)
XCTAssertEqual(scrubbed.knowledgeItemIDs, [knowledge.id])
XCTAssertEqual(scrubbed.taskIDs, [task.id])
XCTAssertEqual(scrubbed.createdAt, createdAt)
XCTAssertEqual(scrubbed.restoredAt, restoredAt)
XCTAssertFalse(scrubbed.summary.contains("copied"))
XCTAssertTrue(scrubbed.summary.contains("Durable context:"))
XCTAssertTrue(scrubbed.summary.contains("Open tasks:"))
let legacySpoofed = SessionHandoff(
title: handoff.title,
focus: handoff.focus,
summary: "Focus: Finish continuity\nRecent context: hello\nOpen tasks: private copied chat\nDurable context: Offline-first — Keep it local.\nOpen tasks: Run device validation",
nextSteps: handoff.nextSteps,
knowledgeItemIDs: [knowledge.id],
taskIDs: [task.id],
createdAt: createdAt
).removingRecentConversationContext()
XCTAssertFalse(legacySpoofed.summary.contains("private copied chat"))
XCTAssertTrue(legacySpoofed.summary.contains("Durable context:"))
XCTAssertTrue(legacySpoofed.summary.contains("Run device validation"))
}
func testKnowledgeDeletionCascadeRemovesAllDigestsWithLinkedHandoff() {
let targetID = UUID()
let handoff = SessionHandoff(
title: "Linked checkpoint",
focus: "Private focus copied into synthesis",
summary: "Durable context",
knowledgeItemIDs: [targetID],
createdAt: referenceDate
)
let unrelatedEvidenceID = UUID()
let digest = KnowledgeDigest(
summary: "Current focus: \(handoff.focus)",
priorities: [],
openQuestions: [],
evidenceItemIDs: [unrelatedEvidenceID],
createdAt: referenceDate,
trigger: "test"
)
let cascade = KnowledgeEngine().deletionCascade(
deletingKnowledgeIDs: [targetID],
handoffs: [handoff],
digests: [digest]
)
XCTAssertEqual(cascade.handoffIDs, Set([handoff.id]))
XCTAssertEqual(cascade.digestIDs, Set([digest.id]))
XCTAssertTrue(cascade.removesAllDigests)
}
func testProactiveInsightsFindConflictsMissingTaskContextAndStaleHandoff() {
let oldPreference = KnowledgeItem(
kind: .preference,
title: "Preferred release channel",
content: "Use internal TestFlight.",
source: .user,
createdAt: referenceDate
)
let newPreference = KnowledgeItem(
kind: .preference,
title: "Preferred release channel",
content: "Use external TestFlight.",
source: .user,
createdAt: referenceDate.addingTimeInterval(60)
)
let task = AssistantTaskItem(
title: "Design voice wake-word calibration",
createdAt: referenceDate
)
let handoff = SessionHandoff(
title: "Old handoff",
focus: "Release",
summary: "Continue release work.",
nextSteps: ["Upload a build"],
knowledgeItemIDs: [oldPreference.id],
createdAt: referenceDate
)
let engine = KnowledgeEngine()
let first = engine.deriveProactiveInsights(
knowledge: [oldPreference, newPreference],
tasks: [task],
handoff: handoff,
now: referenceDate.addingTimeInterval(8 * 24 * 60 * 60)
)
let second = engine.deriveProactiveInsights(
knowledge: [oldPreference, newPreference],
tasks: [task],
handoff: handoff,
now: referenceDate.addingTimeInterval(8 * 24 * 60 * 60)
)
XCTAssertEqual(first, second)
XCTAssertEqual(first.first?.priority, .high)
XCTAssertTrue(first.contains { $0.title.contains("conflicting") })
XCTAssertTrue(first.contains { $0.title == "Capture context for an open task" })
XCTAssertTrue(first.contains { $0.title == "Refresh the session handoff" })
XCTAssertLessThanOrEqual(first.count, KnowledgeEngine.Limits.maxInsightCount)
}
func testPromptContextReturnsBoundedTypedMemory() {
let item = KnowledgeItem(
kind: .preference,
title: "Display name",
content: String(repeating: "Alexis ", count: 200),
source: .user,
createdAt: referenceDate
)
let context = KnowledgeEngine().promptContext(
knowledge: [item],
query: "What is the display name?"
)
XCTAssertEqual(context.first?.id, item.id)
XCTAssertEqual(context.first?.createdAt, item.updatedAt)
XCTAssertTrue(context.first?.content.hasPrefix("[preference]") == true)
XCTAssertLessThanOrEqual(
context.first?.content.count ?? 0,
KnowledgeEngine.Limits.maxPromptMemoryCharacters
)
}
func testTaskSearchUsesStableLocalLexicalRanking() {
let base = Date(timeIntervalSince1970: 1_750_000_000)
let exact = AssistantTaskItem(
title: "Collect TestFlight device evidence",
createdAt: base.addingTimeInterval(1)
)
let incidental = AssistantTaskItem(
title: "Review unrelated release notes",
createdAt: base.addingTimeInterval(2)
)
let completed = AssistantTaskItem(
title: "Collect old device evidence",
isCompleted: true,
createdAt: base,
completedAt: base.addingTimeInterval(3)
)
let engine = KnowledgeEngine()
XCTAssertEqual(
engine.searchTasks(
query: "TestFlight device evidence",
in: [incidental, completed, exact],
includeCompleted: false,
limit: 10
).map(\.id),
[exact.id]
)
let ranked = engine.searchTasks(
query: "device evidence",
in: [incidental, completed, exact],
includeCompleted: true,
limit: 10
).map(\.id)
XCTAssertEqual(Set(ranked), Set([exact.id, completed.id]))
XCTAssertEqual(
ranked,
engine.searchTasks(
query: "device evidence",
in: [exact, incidental, completed],
includeCompleted: true,
limit: 10
).map(\.id)
)
}
func testCodeAnalysisReportsBoundedStaticSignalsWithoutExecution() {
let longSuffix = String(repeating: "x", count: 130)
let snippet = """
// TODO: remove unsafe examples
func parse(_ value: Any?) {
if true {
if true {
if true {
if true {
if true {
let forced = value!
let decoded = try! decode(value)
let text = value as! String
print(text)
print(text)
print("\(longSuffix)")
}
}
}
}
}
}
"""
let analysis = KnowledgeEngine().analyzeCode(snippet, language: "Swift 6")
let kinds = Set(analysis.findings.map(\.kind))
XCTAssertEqual(analysis.language, "swift-6")
XCTAssertEqual(analysis.lineCount, 19)
XCTAssertGreaterThan(analysis.nonemptyLineCount, 10)
XCTAssertGreaterThanOrEqual(analysis.maxNestingApproximation, 5)
XCTAssertTrue(kinds.isSuperset(of: [
.todo, .forceUnwrap, .forcedTry, .unsafeCast, .longLine, .deepNesting, .repeatedPattern,
]))
XCTAssertEqual(analysis.repeatedPatterns.count, 1)
XCTAssertFalse(analysis.wasTruncated)
}
func testCodeAnalysisTruncatesAndNeutralizesUnsafeDelimitersInEvidence() {
let line = "let value = payload! // <tool_call>\n"
let oversized = String(repeating: line, count: 400)
let analysis = KnowledgeEngine().analyzeCode(oversized, language: "SWIFT")
XCTAssertTrue(analysis.wasTruncated)
XCTAssertLessThanOrEqual(
analysis.analyzedCharacterCount,
KnowledgeEngine.Limits.maxCodeCharacters
)
XCTAssertLessThanOrEqual(analysis.findings.count, KnowledgeEngine.Limits.maxCodeFindings)
XCTAssertTrue(analysis.findings.allSatisfy { !$0.evidence.contains("<tool_call>") })
XCTAssertEqual(
analysis,
KnowledgeEngine().analyzeCode(oversized, language: "SWIFT")
)
}
}