fix(history): ranking (#46)
- I removed scoring system in history and it now only works in spec mode - Also fix the AI suggestion placed in 3rd place instead of the first in history mode ad7e2886950dc4e58b1cfac6fe15e4bb7a298865 - Clean up suggestions code
This commit is contained in:
+44
-1
@@ -225,7 +225,50 @@ func SearchHistory(query string, aliases map[string]string) ([]HistResult, error
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
matches := searcherCache.SearchWithScores(q, &fuzzy.SearchOptions{Limit: 200})
|
// extract pure prefix matches based strictly on recency order (historyCache is newest-first)
|
||||||
|
strictMatches := 0
|
||||||
|
for _, cmd := range historyCache {
|
||||||
|
if seenCmds[cmd] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fields := strings.Fields(cmd)
|
||||||
|
firstWordLow := ""
|
||||||
|
if len(fields) > 0 {
|
||||||
|
firstWordLow = strings.ToLower(fields[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
if queryFirstWord != "" {
|
||||||
|
if firstWordLow != queryFirstWord {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if subcmdFilter && querySecondWord != "" {
|
||||||
|
if len(fields) < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
secondWordLow := strings.ToLower(fields[1])
|
||||||
|
if !strings.HasPrefix(secondWordLow, querySecondWord) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if !strings.HasPrefix(firstWordLow, qLow) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
seenCmds[cmd] = true
|
||||||
|
results = append(results, HistResult{
|
||||||
|
ID: idMapCache[cmd],
|
||||||
|
Cmd: cmd,
|
||||||
|
FuzzyScore: 10000,
|
||||||
|
})
|
||||||
|
strictMatches++
|
||||||
|
if strictMatches >= 200 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
matches := searcherCache.SearchWithScores(q, &fuzzy.SearchOptions{Limit: 1000})
|
||||||
for _, m := range matches {
|
for _, m := range matches {
|
||||||
if seenCmds[m.Str] {
|
if seenCmds[m.Str] {
|
||||||
continue
|
continue
|
||||||
|
|||||||
+59
-56
@@ -2,6 +2,7 @@ package root
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@@ -19,28 +20,12 @@ func MergeResults(query string, mode string) []spec.Suggestion {
|
|||||||
maxSugg := config.Get().UI.MaxSuggestions
|
maxSugg := config.Get().UI.MaxSuggestions
|
||||||
seen := make(map[string]bool)
|
seen := make(map[string]bool)
|
||||||
deduped := []spec.Suggestion{}
|
deduped := []spec.Suggestion{}
|
||||||
|
|
||||||
// always call lookup to scan aliases and get spec suggestions
|
|
||||||
logger.Debugf("Merge Calling Lookup for '%s'", query)
|
|
||||||
cmdResults := spec.Lookup(query)
|
|
||||||
|
|
||||||
// search history if in history mode
|
|
||||||
var histResults []integration.HistResult
|
|
||||||
if mode == "history" {
|
|
||||||
aliases := spec.GetAliasesCopy()
|
|
||||||
histResults, _ = integration.SearchHistory(query, aliases)
|
|
||||||
}
|
|
||||||
|
|
||||||
normalizedQuery := strings.TrimSpace(query)
|
normalizedQuery := strings.TrimSpace(query)
|
||||||
|
|
||||||
// add suggestion helper to deduplicate
|
// add suggestion helper to deduplicate
|
||||||
addSuggestion := func(s spec.Suggestion) {
|
addSuggestion := func(s spec.Suggestion) {
|
||||||
normalizedCmd := strings.TrimSpace(s.Cmd)
|
normalizedCmd := strings.TrimSpace(s.Cmd)
|
||||||
if normalizedCmd == "" {
|
if normalizedCmd == "" || normalizedCmd == normalizedQuery {
|
||||||
return
|
|
||||||
}
|
|
||||||
// filter exact match to avoid loops and redundant suggestions
|
|
||||||
if normalizedCmd == normalizedQuery {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if s.Source == "" {
|
if s.Source == "" {
|
||||||
@@ -55,46 +40,86 @@ func MergeResults(query string, mode string) []spec.Suggestion {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// always call lookup to scan aliases and get spec suggestions
|
||||||
|
logger.Debugf("Merge Calling Lookup for '%s'", query)
|
||||||
|
cmdResults := spec.Lookup(query)
|
||||||
|
|
||||||
if mode == "history" {
|
if mode == "history" {
|
||||||
// history mode: history first, then spec/alias
|
aliases := spec.GetAliasesCopy()
|
||||||
for _, h := range histResults {
|
histResults, _ := integration.SearchHistory(query, aliases)
|
||||||
|
|
||||||
|
// scale confidence based on recency (index in histResults) so the most recent commands stay on top
|
||||||
|
baseConf := 75
|
||||||
|
for i, h := range histResults {
|
||||||
|
conf := max(baseConf-(i*2), 60)
|
||||||
addSuggestion(spec.Suggestion{
|
addSuggestion(spec.Suggestion{
|
||||||
Cmd: h.Cmd,
|
Cmd: h.Cmd,
|
||||||
Desc: "history",
|
Desc: "history",
|
||||||
Icon: "history",
|
Icon: "history",
|
||||||
Source: "history",
|
Source: "history",
|
||||||
Confidence: 70,
|
Confidence: conf,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
for _, s := range cmdResults {
|
}
|
||||||
addSuggestion(s)
|
|
||||||
}
|
for _, s := range cmdResults {
|
||||||
} else {
|
addSuggestion(s)
|
||||||
// spec mode: spec/alias only
|
|
||||||
for _, s := range cmdResults {
|
|
||||||
addSuggestion(s)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if mode == "history" && normalizedQuery == "" {
|
if mode == "history" && normalizedQuery == "" {
|
||||||
if len(deduped) > maxSugg {
|
if len(deduped) > maxSugg {
|
||||||
deduped = deduped[:maxSugg]
|
return deduped[:maxSugg]
|
||||||
}
|
}
|
||||||
return deduped
|
return deduped
|
||||||
}
|
}
|
||||||
|
|
||||||
|
injectAISuggestion(&deduped, seen, normalizedQuery)
|
||||||
|
|
||||||
|
var finalResults []spec.Suggestion
|
||||||
|
if mode == "history" {
|
||||||
|
sort.SliceStable(deduped, func(i, j int) bool {
|
||||||
|
return deduped[i].Confidence > deduped[j].Confidence
|
||||||
|
})
|
||||||
|
finalResults = deduped
|
||||||
|
} else {
|
||||||
|
cwd := spec.GetCWD()
|
||||||
|
tokens := spec.Tokenize(query)
|
||||||
|
rootCmd := ""
|
||||||
|
if len(tokens) > 0 {
|
||||||
|
rootCmd = tokens[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
ctxTimeout, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
|
||||||
|
defer cancel()
|
||||||
|
store, _ := scoring.GetFrecencyStore()
|
||||||
|
signals := scoring.CollectSignals(ctxTimeout, cwd, query, rootCmd, store, getPrevSkeleton())
|
||||||
|
scored := scoring.Score(deduped, signals)
|
||||||
|
|
||||||
|
finalResults = make([]spec.Suggestion, 0, len(scored))
|
||||||
|
for _, sc := range scored {
|
||||||
|
finalResults = append(finalResults, sc.Suggestion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(finalResults) > maxSugg {
|
||||||
|
return finalResults[:maxSugg]
|
||||||
|
}
|
||||||
|
return finalResults
|
||||||
|
}
|
||||||
|
|
||||||
|
func injectAISuggestion(deduped *[]spec.Suggestion, seen map[string]bool, normalizedQuery string) {
|
||||||
if aiSugg := GetCurrentAISuggestion(); aiSugg != nil {
|
if aiSugg := GetCurrentAISuggestion(); aiSugg != nil {
|
||||||
normalizedCmd := strings.TrimSpace(aiSugg.Cmd)
|
normalizedCmd := strings.TrimSpace(aiSugg.Cmd)
|
||||||
if normalizedCmd != "" && normalizedCmd != normalizedQuery && strings.HasPrefix(strings.ToLower(normalizedCmd), strings.ToLower(normalizedQuery)) {
|
if normalizedCmd != "" && normalizedCmd != normalizedQuery && strings.HasPrefix(strings.ToLower(normalizedCmd), strings.ToLower(normalizedQuery)) {
|
||||||
if !seen[aiSugg.Cmd] {
|
if !seen[aiSugg.Cmd] {
|
||||||
seen[aiSugg.Cmd] = true
|
seen[aiSugg.Cmd] = true
|
||||||
deduped = append(deduped, *aiSugg)
|
*deduped = append(*deduped, *aiSugg)
|
||||||
} else {
|
} else {
|
||||||
for i, item := range deduped {
|
for i, item := range *deduped {
|
||||||
if item.Cmd == aiSugg.Cmd && aiSugg.Confidence > item.Confidence {
|
if item.Cmd == aiSugg.Cmd && aiSugg.Confidence > item.Confidence {
|
||||||
deduped[i].Confidence = aiSugg.Confidence
|
(*deduped)[i].Confidence = aiSugg.Confidence
|
||||||
if deduped[i].Source == "" || deduped[i].Source == "spec" || deduped[i].Source == "history" {
|
if (*deduped)[i].Source == "" || (*deduped)[i].Source == "spec" || (*deduped)[i].Source == "history" {
|
||||||
deduped[i].Source = "ai"
|
(*deduped)[i].Source = "ai"
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -102,28 +127,6 @@ func MergeResults(query string, mode string) []spec.Suggestion {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cwd := spec.GetCWD()
|
|
||||||
tokens := spec.Tokenize(query)
|
|
||||||
rootCmd := ""
|
|
||||||
if len(tokens) > 0 {
|
|
||||||
rootCmd = tokens[0]
|
|
||||||
}
|
|
||||||
ctxTimeout, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
|
|
||||||
defer cancel()
|
|
||||||
store, _ := scoring.GetFrecencyStore()
|
|
||||||
signals := scoring.CollectSignals(ctxTimeout, cwd, query, rootCmd, store, getPrevSkeleton())
|
|
||||||
scored := scoring.Score(deduped, signals)
|
|
||||||
|
|
||||||
finalResults := make([]spec.Suggestion, 0, len(scored))
|
|
||||||
for _, sc := range scored {
|
|
||||||
finalResults = append(finalResults, sc.Suggestion)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(finalResults) > maxSugg {
|
|
||||||
finalResults = finalResults[:maxSugg]
|
|
||||||
}
|
|
||||||
return finalResults
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|||||||
Reference in New Issue
Block a user