Skip to content

SemanticSearchManager.kt

src/jvmMain/kotlin/afield/ai/retrieval/SemanticSearchManager.kt · 251 lines

@file:afield.runtime.completion.CompletionCategory("Data")

package afield.ai.retrieval

import afield.data.collections.get
import afield.data.collections.invoke
import afield.data.collections.s
import afield.data.collections.times
import afield.document.graph.minusAssign
import afield.document.graph.plusAssign
import afield.document.properties.compareTo
import afield.document.workspace.file
import afield.graphics.render.rangeTo
import afield.math.autodiff.compareTo
import afield.math.autodiff.div
import afield.math.autodiff.minus
import afield.math.autodiff.plus
import afield.math.autodiff.times
import afield.math.linalg.invoke
import afield.math.linalg.remAssign
import afield.math.linalg.set
import afield.math.linalg.times
import afield.math.units.div
import afield.math.units.minus
import afield.math.units.plus
import afield.math.units.times

import afield.audio.synth.*
import afield.document.workspace.path
import afield.ai.retrieval.SemanticRetrievalBuildResult
import afield.ai.retrieval.SemanticRetrievalConfigSaveRequest
import afield.ai.retrieval.SemanticRetrievalDeleteCacheRequest
import afield.ai.retrieval.SemanticRetrievalDownloadRequest
import afield.ai.retrieval.SemanticRetrievalQueryRequest
import afield.ai.retrieval.SemanticRetrievalService
import afield.ai.retrieval.SemanticRetrievalSettingsStatus
import afield.ai.retrieval.SemanticRetrievalStatus
import java.util.Locale

Box-facing wrapper around afield.ai.retrieval.SemanticRetrievalService, Field's embedding-based search over source, docs and workspace boxes. Make a SemanticSearchManager and call SemanticSearchManager.find for the full SemanticSearchResult (message, status, hits) or SemanticSearchManager.findTopK / SemanticSearchManager.findWorkspaceTopK when you just want a List<SemanticSearchHit>; each hit carries score, kind, corpus, path and line range, and prints as one readable line. Queries can be narrowed by corpus, path prefix, kind, document, box or property, and note that find passes rebuildIfEmpty = true by default.

The remaining methods administer the service rather than query it: SemanticSearchManager.status, SemanticSearchManager.settings, SemanticSearchManager.enable, SemanticSearchManager.saveSettings, SemanticSearchManager.downloadPreset, SemanticSearchManager.rebuild, SemanticSearchManager.deleteIndex and SemanticSearchManager.deleteModels. Every call delegates straight through and blocks; the only state kept here is SemanticSearchManager.lastResult.

data class SemanticSearchResult

data class SemanticSearchResult(
    val success: Boolean,
    val message: String,
    val hits: List<SemanticSearchHit>,
    val status: SemanticRetrievalStatus
)

data class SemanticSearchHit

data class SemanticSearchHit(
    val title: String,
    val symbol: String,
    val kind: String,
    val corpus: String,
    val path: String,
    val line: Int,
    val endLine: Int,
    val score: Double,
    val id: String,
    val scale: String,
    val documentName: String = "",
    val boxName: String = "",
    val boxId: String = "",
    val property: String = "",
    val language: String = "",
    val capsule: String = "",
    val sourcePreview: String = ""
) {

val location: String

    val location: String
        get() = if (line > 0) "$path:$line" else path

override fun toString(): String

    override fun toString(): String {
        val name = symbol.ifBlank { title.ifBlank { path } }
        val owner = when {
            documentName.isNotBlank() && boxName.isNotBlank() -> "$documentName / $boxName"
            documentName.isNotBlank() -> documentName
            else -> ""
        }
        val ownerText = if (owner.isBlank()) "" else " [$owner]"
        return "${String.format(Locale.ROOT, "%.3f", score)} $name ($kind)$ownerText $location"
    }
}

class SemanticSearchManager

var lastResult: SemanticSearchResult?

    var lastResult: SemanticSearchResult? = null
        private set

fun status(): SemanticRetrievalStatus

    fun status(): SemanticRetrievalStatus =
        SemanticRetrievalService.status()

fun settings(): SemanticRetrievalSettingsStatus

    fun settings(): SemanticRetrievalSettingsStatus =
        SemanticRetrievalService.settingsStatus()

fun enable(enabled: Boolean = true): SemanticRetrievalSettingsStatus

    fun enable(enabled: Boolean = true): SemanticRetrievalSettingsStatus =
        SemanticRetrievalService.saveConfig(SemanticRetrievalConfigSaveRequest(enabled = enabled))

fun saveSettings

    fun saveSettings(
        enabled: Boolean? = null,
        presetId: String? = null,
        modelPath: String? = null,
        tokenizerPath: String? = null,
        dimension: Int? = null,
        maxTokens: Int? = null,
        queryPrefix: String? = null,
        documentPrefix: String? = null
    ): SemanticRetrievalSettingsStatus =
        SemanticRetrievalService.saveConfig(
            SemanticRetrievalConfigSaveRequest(
                enabled = enabled,
                presetId = presetId,
                modelPath = modelPath,
                tokenizerPath = tokenizerPath,
                dimension = dimension,
                maxTokens = maxTokens,
                queryPrefix = queryPrefix,
                documentPrefix = documentPrefix
            )
        )

fun downloadPreset(presetId: String = settings().config.presetId): SemanticRetrievalSettingsStatus

    fun downloadPreset(presetId: String = settings().config.presetId): SemanticRetrievalSettingsStatus =
        SemanticRetrievalService.downloadPreset(SemanticRetrievalDownloadRequest(presetId = presetId))

fun rebuild(): SemanticRetrievalBuildResult

    fun rebuild(): SemanticRetrievalBuildResult =
        SemanticRetrievalService.rebuild()

fun deleteIndex(): SemanticRetrievalSettingsStatus

    fun deleteIndex(): SemanticRetrievalSettingsStatus =
        SemanticRetrievalService.deleteCache(SemanticRetrievalDeleteCacheRequest("index"))

fun deleteModels(): SemanticRetrievalSettingsStatus

    fun deleteModels(): SemanticRetrievalSettingsStatus =
        SemanticRetrievalService.deleteCache(SemanticRetrievalDeleteCacheRequest("models"))

fun find

body — 59 lines, from line 146
    fun find(
        query: String,
        k: Int = 10,
        corpus: String = "",
        corpora: List<String> = emptyList(),
        pathPrefix: String = "",
        kinds: List<String> = emptyList(),
        documentName: String = "",
        boxId: String = "",
        property: String = "",
        rebuildIfEmpty: Boolean = true,
        includeCapsule: Boolean = false,
        includeSource: Boolean = false
    ): SemanticSearchResult {
        val result = SemanticRetrievalService.query(
            SemanticRetrievalQueryRequest(
                query = query,
                limit = k,
                corpus = corpus,
                corpora = corpora,
                pathPrefix = pathPrefix,
                kinds = kinds,
                documentName = documentName,
                boxId = boxId,
                property = property,
                rebuildIfEmpty = rebuildIfEmpty,
                includeCapsule = includeCapsule,
                includeSource = includeSource
            )
        )
        return SemanticSearchResult(
            success = result.success,
            message = result.message,
            hits = result.results.map {
                SemanticSearchHit(
                    title = it.title,
                    symbol = it.symbol,
                    kind = it.kind,
                    corpus = it.corpus,
                    path = it.path,
                    line = it.line,
                    endLine = it.endLine,
                    score = it.score,
                    id = it.id,
                    scale = it.scale,
                    documentName = it.documentName,
                    boxName = it.boxName,
                    boxId = it.boxId,
                    property = it.property,
                    language = it.language,
                    capsule = it.capsule,
                    sourcePreview = it.sourcePreview
                )
            },
            status = result.status
        ).also {
            lastResult = it
        }
    }

fun findTopK

    fun findTopK(
        query: String,
        k: Int = 10,
        corpus: String = "",
        corpora: List<String> = emptyList(),
        pathPrefix: String = "",
        kinds: List<String> = emptyList(),
        documentName: String = "",
        boxId: String = "",
        property: String = "",
        includeCapsule: Boolean = false,
        includeSource: Boolean = false
    ): List<SemanticSearchHit> =
        find(
            query = query,
            k = k,
            corpus = corpus,
            corpora = corpora,
            pathPrefix = pathPrefix,
            kinds = kinds,
            documentName = documentName,
            boxId = boxId,
            property = property,
            includeCapsule = includeCapsule,
            includeSource = includeSource
        ).hits

fun findWorkspaceTopK

    fun findWorkspaceTopK(
        query: String,
        k: Int = 10,
        documentName: String = "",
        boxId: String = "",
        property: String = "",
        includeSource: Boolean = false
    ): List<SemanticSearchHit> =
        findTopK(
            query = query,
            k = k,
            corpus = "workspace",
            documentName = documentName,
            boxId = boxId,
            property = property,
            includeSource = includeSource
        )
}