SPB Git

spb/poche Public

Agent personnel 100 % on-device — SwiftUI + Apple Foundation Models + EventKit + SwiftData. Aucune API, aucun serveur.

Swift 100%
1.6 KB · 48 lines swift
Raw Blame History
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