feat(context,infra): compression pipeline stages 4-5 — token pruning + relevance + ToMe
- build() is now suspend: pipeline runs all stages in fixed order, each gated by level - TOKEN_PRUNE (level 3): TokenPruner interface + LLMLingua-2 sidecar (sidecars/llmlingua) + HttpTokenPruner adapter (fails open if sidecar down); prunes freeform, preserves protected spans, skips tier-0 turns when TIER_SPLIT on - TOME_MERGE (level 8): ToMeMerger collapses near-duplicate freeform turns (Jaccard) - Stage 5 selection: RelevanceScorer + EmbeddingRelevanceScorer (cosine over Embedder); query-conditioned reorder so least-relevant freeform drops first under budget - [orchestration] compression_level + token_pruner_url config, wired in Main - suspend ripple fixed across builder callers/stubs
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
package com.correx.core.inference
|
||||
|
||||
import com.correx.core.context.compression.RelevanceScorer
|
||||
import kotlin.math.sqrt
|
||||
|
||||
/**
|
||||
* [RelevanceScorer] backed by an [Embedder]: cosine similarity of each candidate to the current
|
||||
* query, rescaled from [-1,1] to [0,1]. Feeds the pipeline's query-conditioned selection — under
|
||||
* budget pressure the least query-relevant turns are dropped first, regardless of age (pipeline §6).
|
||||
* Embeds the query once, then each candidate; a failed embedding scores 0.5 (neutral) so one bad
|
||||
* call doesn't distort the ranking.
|
||||
*/
|
||||
class EmbeddingRelevanceScorer(private val embedder: Embedder) : RelevanceScorer {
|
||||
|
||||
override suspend fun score(query: String, candidates: List<String>): List<Double> {
|
||||
val q = runCatching { embedder.embed(query) }.getOrNull() ?: return candidates.map { NEUTRAL }
|
||||
return candidates.map { c ->
|
||||
runCatching { cosine(q, embedder.embed(c)) }.getOrDefault(NEUTRAL)
|
||||
}
|
||||
}
|
||||
|
||||
private fun cosine(a: FloatArray, b: FloatArray): Double {
|
||||
if (a.isEmpty() || b.isEmpty() || a.size != b.size) return NEUTRAL
|
||||
var dot = 0.0; var na = 0.0; var nb = 0.0
|
||||
for (i in a.indices) { dot += a[i] * b[i]; na += a[i] * a[i]; nb += b[i] * b[i] }
|
||||
if (na == 0.0 || nb == 0.0) return NEUTRAL
|
||||
val cos = dot / (sqrt(na) * sqrt(nb))
|
||||
return ((cos + 1.0) / 2.0).coerceIn(0.0, 1.0)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val NEUTRAL = 0.5
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user