Files
wehub-resource-sync a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:33:42 +08:00

165 lines
5.0 KiB
Go

package rerank
import (
"unicode"
"github.com/zzet/gortex/internal/graph"
)
// OverloadProminenceSignal breaks ties among same-named candidates. When
// a query is ambiguous — several distinct symbols answer to one
// identifier (Go interface methods like BindBody across every binding,
// or a `decode` on each decoder class) — no ranker can know which
// overload the caller meant, but the likelier ones can be floated toward
// the top-5 by intrinsic prominence: an exported, non-test, callable
// definition is a better default answer than an unexported field or a
// test helper of the same name.
//
// The signal fires ONLY on a genuine same-name collision in the current
// batch (nameGroupCount > 1); a candidate whose name is unique
// contributes 0, so a non-ambiguous query is never perturbed. Every
// input — exported shape, test path, node kind — is an AST-tier fact
// available at index time with no enrichment, so the ordering is
// identical whether or not semantic enrichment has run.
type OverloadProminenceSignal struct{}
func (OverloadProminenceSignal) Name() string { return SignalOverload }
func (OverloadProminenceSignal) Contribute(query string, c *Candidate, ctx *Context) float64 {
if c == nil || c.Node == nil || ctx == nil {
return 0
}
nm := lowerName(c.Node.Name)
if nm == "" {
return 0
}
// Identifier query: fire only on a genuine same-name collision —
// an exact-name lookup where overload disambiguation is the whole
// problem. On any other class the batch's incidental same-name
// pairs are noise, and a blanket prominence boost would fight the
// semantic channel that is doing the real work.
if ctx.QueryClass == QueryClassSymbol {
if ctx.nameGroupCount[nm] <= 1 {
return 0
}
return prominenceScore(c.Node)
}
// Degraded-identifier profile: a multi-word query whose tokens
// concatenate to exactly this candidate's name is a case-split
// identifier lookup ("should bind body with" → ShouldBindBodyWith),
// not a concept query, for THIS candidate. Longer same-stem
// variants (ShouldBindBodyWithJSON) share every query token and can
// out-BM25 the exact match; the exact-match floor puts the queried
// identifier back on top, with prominence breaking ties among
// several exact matches (the overload group).
if exactDegradedNameMatch(query, nm) {
return 0.55 + 0.45*prominenceScore(c.Node)
}
return 0
}
// prominenceScore rates a symbol's intrinsic answer-worthiness in
// [0, 1] from AST-tier facts only.
func prominenceScore(n *graph.Node) float64 {
var s float64
if isLikelyExported(n) {
s += 0.5
}
if !isTestPath(n.FilePath) {
s += 0.3
}
switch n.Kind {
case graph.KindFunction, graph.KindMethod, graph.KindType, graph.KindInterface:
s += 0.2
}
if s > 1 {
s = 1
}
return s
}
// exactDegradedNameMatch reports whether a multi-word query's tokens
// concatenate to exactly the (already-lowercased) candidate name,
// comparing alphanumeric runes only so snake_case names match their
// space-split degraded form too. Single-token queries return false —
// they are the symbol class's territory.
func exactDegradedNameMatch(query, lowerNodeName string) bool {
qa := alnumFold(query, true)
if qa == "" {
return false
}
return qa == alnumFold(lowerNodeName, false)
}
// alnumFold lowercases and strips every non-alphanumeric rune. When
// requireMultiToken is set it returns "" unless the input had 2+
// whitespace-separated tokens (the degraded-identifier shape).
func alnumFold(s string, requireMultiToken bool) string {
var b []rune
tokens := 0
inTok := false
for _, r := range s {
if unicode.IsSpace(r) {
inTok = false
continue
}
if !inTok {
tokens++
inTok = true
}
switch {
case r >= 'a' && r <= 'z' || r >= '0' && r <= '9':
b = append(b, r)
case r >= 'A' && r <= 'Z':
b = append(b, unicode.ToLower(r))
case unicode.IsLetter(r) || unicode.IsDigit(r):
b = append(b, unicode.ToLower(r))
}
}
if requireMultiToken && tokens < 2 {
return ""
}
return string(b)
}
// lowerName lowercases a symbol name without pulling in strings for a
// one-liner on the hot path. ASCII-fast, rune-correct for the rest.
func lowerName(s string) string {
hasUpper := false
for _, r := range s {
if r >= 'A' && r <= 'Z' || unicode.IsUpper(r) {
hasUpper = true
break
}
}
if !hasUpper {
return s
}
out := make([]rune, 0, len(s))
for _, r := range s {
out = append(out, unicode.ToLower(r))
}
return string(out)
}
// isLikelyExported reports whether a symbol is part of its module's
// public surface, using only the name shape and language — no
// enrichment. A leading underscore marks a private member in Python / JS
// conventions; Go (and the other case-visibility languages) key
// export on an uppercase initial; everything else is treated as public
// unless underscore-prefixed.
func isLikelyExported(n *graph.Node) bool {
if n == nil || n.Name == "" {
return false
}
first := []rune(n.Name)[0]
if first == '_' {
return false
}
switch n.Language {
case "go", "java", "kotlin", "scala", "csharp":
return unicode.IsUpper(first)
}
return true
}