Arya
Software Engineer / AI & ML
Software Engineer / AI & ML


static func makeSearchableItem(from profile: BeanProfile) -> CSSearchableItem { let attrs = CSSearchableItemAttributeSet(contentType: .text) attrs.title = profile.name attrs.keywords = profile.flavorNotes.map(\.rawValue) + [profile.island.rawValue, profile.island.label, profile.subregion, profile.processingMethod.rawValue, profile.processingMethod.label, profile.acidity.rawValue + " acidity", profile.body.rawValue + " body", "moka " + profile.mokaPotSuitability.rawValue] + (profile.roastRecommendation.map { [$0.rawValue, $0.label] } ?? []) attrs.textContent = profile.searchableText return CSSearchableItem( uniqueIdentifier: profile.id, domainIdentifier: "bean-profile", attributeSet: attrs ) } func donate(_ profiles: [BeanProfile]) async throws { let items = profiles.map { profile -> CSSearchableItem in let item = Self.makeSearchableItem(from: profile) item.expirationDate = .distantFuture return item } try await index.indexSearchableItems(items) }
CoffeeAgent.init(resource:) -> JSONDecoder().decode([BeanProfile].self) -> profiles (source of truth, unchanged) -> BeanIndexer.donate(profiles) -> BeanIndexer.makeSearchableItem(from:), one per profile -> CSSearchableItemAttributeSet title, keywords, textContent -> index.indexSearchableItems(items) -> Spotlight's on-device index (a donated copy only)
session.respond(to: "fruity beans above 1400 meters") -> LanguageModelSession (model decides searchBeanCorpus is needed) -> BeanCorpusTool.call(arguments:) -> CSSearchQuery(queryString: spotlightQuery(for:), queryContext:) -> Spotlight's on-device index -> matches title + keywords + textContent, token by token -> for try await result in query.results -> ids -> ids handed to synthesis for hydration
static func spotlightQuery(for needle: String) -> String { let fields = ["title", "keywords", "textContent"] return needle .split(whereSeparator: \.isWhitespace) .map { token in let escaped = token .replacingOccurrences(of: "\\", with: "\\\\") .replacingOccurrences(of: "\"", with: "\\\"") let clauses = fields.map { "\($0) == \"\(escaped)*\"cdw" } return "(" + clauses.joined(separator: " || ") + ")" } .joined(separator: " && ") }
func call(arguments: Arguments) async throws -> BeanSearchOutcome { let needle = arguments.query.trimmingCharacters(in: .whitespacesAndNewlines) guard !needle.isEmpty else { return BeanSearchOutcome(status: .noMatchesInIndex, beans: []) } guard CSSearchableIndex.isIndexingAvailable() else { return BeanSearchOutcome(status: .indexUnavailable, beans: []) } let context = CSSearchQueryContext() context.fetchAttributes = [] var ids: [String] = [] let query = CSSearchQuery(queryString: Self.spotlightQuery(for: needle), queryContext: context) defer { query.cancel() } do { for try await result in query.results { ids.append(result.item.uniqueIdentifier) if ids.count >= arguments.limit { break } } } catch { return BeanSearchOutcome(status: .indexUnavailable, beans: []) } guard !ids.isEmpty else { return BeanSearchOutcome(status: .noMatchesInIndex, beans: []) } let profiles = await store.profiles(for: ids) guard !profiles.isEmpty else { return BeanSearchOutcome(status: .indexStale, beans: []) } return BeanSearchOutcome(status: .matchesFound, beans: profiles.map(Self.hit)) }
final class BeanIndexDelegate: NSObject, CSSearchableIndexDelegate { let store: BeanProfileStore func searchableIndex( _ index: CSSearchableIndex, reindexSearchableItemsWithIdentifiers identifiers: [String], acknowledgementHandler ack: @escaping () -> Void ) { Task { try? await BeanIndexer().donate(await store.profiles(for: identifiers)) ack() } } func searchableIndex( _ index: CSSearchableIndex, reindexAllSearchableItemsWithAcknowledgementHandler ack: @escaping () -> Void ) { Task { try? await BeanIndexer().donate(await store.allProfiles()) ack() } } }
@Generable struct BeanHit { var name: String var island: String var subregion: String var processing: String var flavors: String var acidity: String var body: String var roastRecommendation: String? var mokaPotSuitability: String var cuppingScore: Double? var dataSource: String } @Generable struct BeanSearchOutcome { var status: BeanSearchStatus var beans: [BeanHit] } static func hit(for profile: BeanProfile) -> BeanHit { BeanHit( name: profile.name, island: profile.island.label, subregion: profile.subregion, processing: profile.processingMethod.label, flavors: profile.flavorNotes.map(\.label).joined(separator: ", "), acidity: profile.acidity.label, body: profile.body.label, roastRecommendation: profile.roastRecommendation?.label, mokaPotSuitability: profile.mokaPotSuitability.label, cuppingScore: profile.cuppingScore, dataSource: profile.dataSource.label ) }
ids from retrieval -> BeanProfileStore.profiles(for: ids), hydrated directly, no delegate callback -> empty means indexStale, the two stores disagreed -> BeanSearchOutcome(status: .matchesFound, beans: profiles.map(Self.hit)) -> model reasons over named BeanHit fields -> response.content, cited by bean name
spotlightQuery(for:), and opens a CustomStage for scoring hits before the model sees them during synthesis. That is a different enough tool contract, built on a different enough set of types, GuidanceProfile, CoreSpotlightSource, SearchReply.