perf(sessions): cache search projections
- commit
- 1bc97e8414b7244f5d5f965590873110b2069c9f
- author
- brice · · 2026-08-19 05:50 UTC
- parent
- caa47a7
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
2 files · +22 -9
M packages/agent/modes/session_dialog.go +14 -2
| @@ -544,9 +544,21 @@ func matchSessionSearchSegments(ctx context.Context, query string, segments []co | |||
| 544 | 544 | if query == "" { | |
| 545 | 545 | return nil | |
| 546 | 546 | } | |
| 547 | + | if ctx.Err() != nil { | |
| 548 | + | return nil | |
| 549 | + | } | |
| 547 | 550 | texts := make([]string, len(segments)) | |
| 548 | 551 | for index, segment := range segments { | |
| 549 | - | texts[index] = core.NormalizeSessionSearchText(segment.Text) | |
| 552 | + | if ctx.Err() != nil { | |
| 553 | + | return nil | |
| 554 | + | } | |
| 555 | + | texts[index] = segment.Normalized | |
| 556 | + | if texts[index] == "" { | |
| 557 | + | texts[index] = core.NormalizeSessionSearchText(segment.Text) | |
| 558 | + | } | |
| 559 | + | } | |
| 560 | + | if ctx.Err() != nil { | |
| 561 | + | return nil | |
| 550 | 562 | } | |
| 551 | 563 | ranked := fuzzy.Find(query, texts) | |
| 552 | 564 | matches := make(map[string]sessionSearchMatch) | |
| @@ -558,7 +570,7 @@ func matchSessionSearchSegments(ctx context.Context, query string, segments []co | |||
| 558 | 570 | current := matches[segment.Path] | |
| 559 | 571 | current.count++ | |
| 560 | 572 | if current.excerpt == "" || match.Score > current.score { | |
| 561 | - | current.excerpt = segment.Text | |
| 573 | + | current.excerpt = texts[match.Index] | |
| 562 | 574 | current.indexes = append([]int(nil), match.MatchedIndexes...) | |
| 563 | 575 | current.score = match.Score | |
| 564 | 576 | } | |
| @@ -544,9 +544,21 @@ func matchSessionSearchSegments(ctx context.Context, query string, segments []co | |||||
| 544 | if query == "" { | 544 | if query == "" { | ||
| 545 | return nil | 545 | return nil | ||
| 546 | } | 546 | } | ||
| 547 | + | if ctx.Err() != nil { | |||
| 548 | + | return nil | |||
| 549 | + | } | |||
| 547 | texts := make([]string, len(segments)) | 550 | texts := make([]string, len(segments)) | ||
| 548 | for index, segment := range segments { | 551 | for index, segment := range segments { | ||
| 549 | - | texts[index] = core.NormalizeSessionSearchText(segment.Text) | 552 | + | if ctx.Err() != nil { |
| 553 | + | return nil | |||
| 554 | + | } | |||
| 555 | + | texts[index] = segment.Normalized | |||
| 556 | + | if texts[index] == "" { | |||
| 557 | + | texts[index] = core.NormalizeSessionSearchText(segment.Text) | |||
| 558 | + | } | |||
| 559 | + | } | |||
| 560 | + | if ctx.Err() != nil { | |||
| 561 | + | return nil | |||
| 550 | } | 562 | } | ||
| 551 | ranked := fuzzy.Find(query, texts) | 563 | ranked := fuzzy.Find(query, texts) | ||
| 552 | matches := make(map[string]sessionSearchMatch) | 564 | matches := make(map[string]sessionSearchMatch) | ||
| @@ -558,7 +570,7 @@ func matchSessionSearchSegments(ctx context.Context, query string, segments []co | |||||
| 558 | current := matches[segment.Path] | 570 | current := matches[segment.Path] | ||
| 559 | current.count++ | 571 | current.count++ | ||
| 560 | if current.excerpt == "" || match.Score > current.score { | 572 | if current.excerpt == "" || match.Score > current.score { | ||
| 561 | - | current.excerpt = segment.Text | 573 | + | current.excerpt = texts[match.Index] |
| 562 | current.indexes = append([]int(nil), match.MatchedIndexes...) | 574 | current.indexes = append([]int(nil), match.MatchedIndexes...) | ||
| 563 | current.score = match.Score | 575 | current.score = match.Score | ||
| 564 | } | 576 | } | ||
M packages/core/session_picker.go +8 -7
| @@ -27,11 +27,12 @@ const ( | |||
| 27 | 27 | // excerpt from a persisted session. It deliberately never contains tool, | |
| 28 | 28 | // developer, image, or raw JSON data. | |
| 29 | 29 | type SessionSearchSegment struct { | |
| 30 | - | Path string | |
| 31 | - | Text string | |
| 32 | - | Role provider.Role | |
| 33 | - | Time time.Time | |
| 34 | - | Order int | |
| 30 | + | Path string | |
| 31 | + | Text string | |
| 32 | + | Normalized string | |
| 33 | + | Role provider.Role | |
| 34 | + | Time time.Time | |
| 35 | + | Order int | |
| 35 | 36 | } | |
| 36 | 37 | ||
| 37 | 38 | // ListSessionPathsContext lists eligible session files for cwd or, when all is | |
| @@ -191,7 +192,7 @@ func ReadSessionSearchSegments(ctx context.Context, path string) ([]SessionSearc | |||
| 191 | 192 | if message.Role == provider.RoleUser || message.Role == provider.RoleAssistant { | |
| 192 | 193 | for _, rawText := range message.Texts { | |
| 193 | 194 | for _, text := range splitSessionSearchText(rawText) { | |
| 194 | - | segments = append(segments, SessionSearchSegment{Path: path, Text: text, Role: message.Role, Time: message.Time, Order: order}) | |
| 195 | + | segments = append(segments, SessionSearchSegment{Path: path, Text: text, Normalized: NormalizeSessionSearchText(text), Role: message.Role, Time: message.Time, Order: order}) | |
| 195 | 196 | order++ | |
| 196 | 197 | } | |
| 197 | 198 | } | |
| @@ -284,7 +285,7 @@ func splitSessionSearchText(text string) []string { | |||
| 284 | 285 | for len(runes) > 0 { | |
| 285 | 286 | end := min(len(runes), maxSessionSearchRunes) | |
| 286 | 287 | if end < len(runes) { | |
| 287 | - | for split := end; split > end/2; split-- { | |
| 288 | + | for split := end - 1; split > end/2; split-- { | |
| 288 | 289 | if unicode.IsSpace(runes[split]) || unicode.Is(unicode.Sentence_Terminal, runes[split]) { | |
| 289 | 290 | end = split + 1 | |
| 290 | 291 | break | |
| @@ -27,11 +27,12 @@ const ( | |||||
| 27 | // excerpt from a persisted session. It deliberately never contains tool, | 27 | // excerpt from a persisted session. It deliberately never contains tool, | ||
| 28 | // developer, image, or raw JSON data. | 28 | // developer, image, or raw JSON data. | ||
| 29 | type SessionSearchSegment struct { | 29 | type SessionSearchSegment struct { | ||
| 30 | - | Path string | 30 | + | Path string |
| 31 | - | Text string | 31 | + | Text string |
| 32 | - | Role provider.Role | 32 | + | Normalized string |
| 33 | - | Time time.Time | 33 | + | Role provider.Role |
| 34 | - | Order int | 34 | + | Time time.Time |
| 35 | + | Order int | |||
| 35 | } | 36 | } | ||
| 36 | 37 | ||||
| 37 | // ListSessionPathsContext lists eligible session files for cwd or, when all is | 38 | // ListSessionPathsContext lists eligible session files for cwd or, when all is | ||
| @@ -191,7 +192,7 @@ func ReadSessionSearchSegments(ctx context.Context, path string) ([]SessionSearc | |||||
| 191 | if message.Role == provider.RoleUser || message.Role == provider.RoleAssistant { | 192 | if message.Role == provider.RoleUser || message.Role == provider.RoleAssistant { | ||
| 192 | for _, rawText := range message.Texts { | 193 | for _, rawText := range message.Texts { | ||
| 193 | for _, text := range splitSessionSearchText(rawText) { | 194 | for _, text := range splitSessionSearchText(rawText) { | ||
| 194 | - | segments = append(segments, SessionSearchSegment{Path: path, Text: text, Role: message.Role, Time: message.Time, Order: order}) | 195 | + | segments = append(segments, SessionSearchSegment{Path: path, Text: text, Normalized: NormalizeSessionSearchText(text), Role: message.Role, Time: message.Time, Order: order}) |
| 195 | order++ | 196 | order++ | ||
| 196 | } | 197 | } | ||
| 197 | } | 198 | } | ||
| @@ -284,7 +285,7 @@ func splitSessionSearchText(text string) []string { | |||||
| 284 | for len(runes) > 0 { | 285 | for len(runes) > 0 { | ||
| 285 | end := min(len(runes), maxSessionSearchRunes) | 286 | end := min(len(runes), maxSessionSearchRunes) | ||
| 286 | if end < len(runes) { | 287 | if end < len(runes) { | ||
| 287 | - | for split := end; split > end/2; split-- { | 288 | + | for split := end - 1; split > end/2; split-- { |
| 288 | if unicode.IsSpace(runes[split]) || unicode.Is(unicode.Sentence_Terminal, runes[split]) { | 289 | if unicode.IsSpace(runes[split]) || unicode.Is(unicode.Sentence_Terminal, runes[split]) { | ||
| 289 | end = split + 1 | 290 | end = split + 1 | ||
| 290 | break | 291 | break | ||