SPB Git

spb/poche Public

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

Swift 100%
2.0 KB · 67 lines swift
Raw Blame History
1//2//  Condenser.swift3//  Poche4//5//  Author:  Simon-Pierre Boucher6//  Contact: contact@spboucher.ai7//89import Foundation10import FoundationModels1112/// One exchange in the agent's private log, used for condensation and13/// session recycling.14struct AgentExchange: Sendable {15    enum Role: String, Sendable {16        case user17        case assistant18        case event19    }2021    let role: Role22    let text: String23}2425/// Summarizes old turns in a separate, tool-free session (CLAUDE.md §5).26struct Condenser: Sendable {27    /// Each turn is clipped before summarizing: the condensation prompt must28    /// itself fit in a fresh window.29    private static let perTurnClip = 2803031    func condense(_ exchanges: [AgentExchange]) async throws -> String {32        let rendered = exchanges.map { exchange in33            "\(label(for: exchange.role)) : \(String(exchange.text.prefix(Self.perTurnClip)))"34        }35        .joined(separator: "\n")3637        let session = LanguageModelSession(38            model: .default,39            instructions: "Tu résumes une conversation entre un utilisateur et son assistant. Sois dense, fidèle, factuel."40        )41        let response = try await session.respond(42            to: "Résume fidèlement cette conversation :\n\n\(rendered)",43            generating: CondensedSummary.self44        )45        return response.content.summary46    }4748    /// Last-resort summary when the model itself cannot condense49    /// (e.g. the condensation call is refused). Losing detail is acceptable;50    /// visibly losing the thread is not.51    func fallbackSummary(for exchanges: [AgentExchange]) -> String {52        exchanges53            .filter { $0.role == .user || $0.role == .event }54            .suffix(6)55            .map { "- \(String($0.text.prefix(120)))" }56            .joined(separator: "\n")57    }5859    private func label(for role: AgentExchange.Role) -> String {60        switch role {61        case .user: "Utilisateur"62        case .assistant: "Assistant"63        case .event: "Événement"64        }65    }66}67