spb/poche Public
Agent personnel 100 % on-device — SwiftUI + Apple Foundation Models + EventKit + SwiftData. Aucune API, aucun serveur.
Swift 100%
1//2// SemanticIndex.swift3// Poche4//5// Author: Simon-Pierre Boucher6// Contact: contact@spboucher.ai7//89import Foundation10import NaturalLanguage1112/// Local semantic ranking over the user's data. NLEmbedding runs entirely13/// on-device — no service is involved, not even for indexing (CLAUDE.md §7).14///15/// Constraint: distances are computed per query over the whole corpus.16/// Fine at personal-notes scale; precompute stored vectors before the17/// corpus grows past a few thousand documents.18@MainActor19final class SemanticIndex {20 private let embedding = NLEmbedding.sentenceEmbedding(for: .french)2122 func rank(query: String, in documents: [SearchDocument], limit: Int = 3) -> [SearchDocument] {23 guard !documents.isEmpty else { return [] }2425 if let embedding {26 let scored = documents.map { document in27 (document, embedding.distance(between: query, and: document.text, distanceType: .cosine))28 }29 return scored30 .sorted { $0.1 < $1.1 }31 .prefix(limit)32 .map(\.0)33 }3435 // Keyword fallback when the French sentence embedding asset is36 // not present on the device.37 let needles = query.lowercased().split(separator: " ").map(String.init)38 let scored = documents.map { document in39 (document, needles.count(where: { document.text.lowercased().contains($0) }))40 }41 return scored42 .filter { $0.1 > 0 }43 .sorted { $0.1 > $1.1 }44 .prefix(limit)45 .map(\.0)46 }47}48