WeKnora deployment source with the reviewed wiki identity, summary and recovery fixes
Anydoc / anydoc parser engine (push) Canceled after 0s
App / Format, vet, test, and build (push) Canceled after 0s
CLI / test (macos-latest / go 1.26) (push) Canceled after 0s
CLI / test (ubuntu-latest / go 1.26) (push) Canceled after 0s
CLI / test (windows-latest / go 1.26) (push) Canceled after 0s
Build and Push Docker Image / build-ui (push) Canceled after 0s
Build and Push Docker Image / build-docreader (push) Canceled after 0s
Build and Push Docker Image / build-sandbox (push) Canceled after 0s
Build and Push Docker Image / build-app (amd64, linux/amd64, ubuntu-latest) (push) Canceled after 0s
Build and Push Docker Image / build-app (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
DocReader / Python tests and Go client tests (push) Canceled after 0s
Build & Publish (dsh-weknora) / Typecheck, test, pack (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (pinned) (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (latest, advisory) (push) Canceled after 0s
Frontend / Test, type-check, and build (push) Canceled after 0s
Go Lint Cache / warm lint cache (.) (push) Canceled after 0s
Go Lint Cache / warm lint cache (cli) (push) Canceled after 0s
Go Lint Cache / warm lint cache (client) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.10) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.11) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.12) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.13) (push) Canceled after 0s
Build and Push Docker Image / merge (push) Canceled after 0s
Build & Publish (dsh-weknora) / Publish to npm (push) Canceled after 0s
Build & Publish (mcp-server) / Build sdist + wheel (push) Canceled after 0s
Build & Publish (mcp-server) / Publish to PyPI (push) Canceled after 0s
Anydoc / anydoc parser engine (push) Canceled after 0s
App / Format, vet, test, and build (push) Canceled after 0s
CLI / test (macos-latest / go 1.26) (push) Canceled after 0s
CLI / test (ubuntu-latest / go 1.26) (push) Canceled after 0s
CLI / test (windows-latest / go 1.26) (push) Canceled after 0s
Build and Push Docker Image / build-ui (push) Canceled after 0s
Build and Push Docker Image / build-docreader (push) Canceled after 0s
Build and Push Docker Image / build-sandbox (push) Canceled after 0s
Build and Push Docker Image / build-app (amd64, linux/amd64, ubuntu-latest) (push) Canceled after 0s
Build and Push Docker Image / build-app (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
DocReader / Python tests and Go client tests (push) Canceled after 0s
Build & Publish (dsh-weknora) / Typecheck, test, pack (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (pinned) (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (latest, advisory) (push) Canceled after 0s
Frontend / Test, type-check, and build (push) Canceled after 0s
Go Lint Cache / warm lint cache (.) (push) Canceled after 0s
Go Lint Cache / warm lint cache (cli) (push) Canceled after 0s
Go Lint Cache / warm lint cache (client) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.10) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.11) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.12) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.13) (push) Canceled after 0s
Build and Push Docker Image / merge (push) Canceled after 0s
Build & Publish (dsh-weknora) / Publish to npm (push) Canceled after 0s
Build & Publish (mcp-server) / Build sdist + wheel (push) Canceled after 0s
Build & Publish (mcp-server) / Publish to PyPI (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
package searchutil
|
||||
|
||||
import "sort"
|
||||
|
||||
// KeywordScoreCallbacks allows callers to hook into normalization telemetry.
|
||||
type KeywordScoreCallbacks struct {
|
||||
OnNoVariance func(count int, score float64)
|
||||
OnNormalized func(count int, rawMin, rawMax, normalizeMin, normalizeMax float64)
|
||||
}
|
||||
|
||||
// NormalizeKeywordScores normalizes keyword match scores in-place using robust percentile bounds.
|
||||
func NormalizeKeywordScores[T any](
|
||||
results []T,
|
||||
isKeyword func(T) bool,
|
||||
getScore func(T) float64,
|
||||
setScore func(T, float64),
|
||||
callbacks KeywordScoreCallbacks,
|
||||
) {
|
||||
keywordResults := make([]T, 0, len(results))
|
||||
for _, result := range results {
|
||||
if isKeyword(result) {
|
||||
keywordResults = append(keywordResults, result)
|
||||
}
|
||||
}
|
||||
|
||||
if len(keywordResults) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if len(keywordResults) == 1 {
|
||||
setScore(keywordResults[0], 1.0)
|
||||
return
|
||||
}
|
||||
|
||||
minS := getScore(keywordResults[0])
|
||||
maxS := minS
|
||||
for _, r := range keywordResults[1:] {
|
||||
score := getScore(r)
|
||||
if score < minS {
|
||||
minS = score
|
||||
}
|
||||
if score > maxS {
|
||||
maxS = score
|
||||
}
|
||||
}
|
||||
|
||||
if maxS <= minS {
|
||||
for _, r := range keywordResults {
|
||||
setScore(r, 1.0)
|
||||
}
|
||||
if callbacks.OnNoVariance != nil {
|
||||
callbacks.OnNoVariance(len(keywordResults), minS)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
normalizeMin := minS
|
||||
normalizeMax := maxS
|
||||
|
||||
if len(keywordResults) >= 10 {
|
||||
scores := make([]float64, len(keywordResults))
|
||||
for i, r := range keywordResults {
|
||||
scores[i] = getScore(r)
|
||||
}
|
||||
sort.Float64s(scores)
|
||||
p5Idx := len(scores) * 5 / 100
|
||||
p95Idx := len(scores) * 95 / 100
|
||||
if p5Idx < len(scores) {
|
||||
normalizeMin = scores[p5Idx]
|
||||
}
|
||||
if p95Idx < len(scores) {
|
||||
normalizeMax = scores[p95Idx]
|
||||
}
|
||||
}
|
||||
|
||||
rangeSize := normalizeMax - normalizeMin
|
||||
if rangeSize > 0 {
|
||||
for _, r := range keywordResults {
|
||||
clamped := getScore(r)
|
||||
if clamped < normalizeMin {
|
||||
clamped = normalizeMin
|
||||
} else if clamped > normalizeMax {
|
||||
clamped = normalizeMax
|
||||
}
|
||||
ns := (clamped - normalizeMin) / rangeSize
|
||||
if ns < 0 {
|
||||
ns = 0
|
||||
} else if ns > 1 {
|
||||
ns = 1
|
||||
}
|
||||
setScore(r, ns)
|
||||
}
|
||||
if callbacks.OnNormalized != nil {
|
||||
callbacks.OnNormalized(
|
||||
len(keywordResults),
|
||||
minS,
|
||||
maxS,
|
||||
normalizeMin,
|
||||
normalizeMax,
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Fallback when percentile filtering collapses the range.
|
||||
for _, r := range keywordResults {
|
||||
setScore(r, 1.0)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user