swift-skills 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +410 -0
- package/build/cli/auth.d.ts +3 -0
- package/build/cli/auth.d.ts.map +1 -0
- package/build/cli/auth.js +44 -0
- package/build/cli/auth.js.map +1 -0
- package/build/cli/setup.d.ts +2 -0
- package/build/cli/setup.d.ts.map +1 -0
- package/build/cli/setup.js +150 -0
- package/build/cli/setup.js.map +1 -0
- package/build/cli/source-manager.d.ts +3 -0
- package/build/cli/source-manager.d.ts.map +1 -0
- package/build/cli/source-manager.js +117 -0
- package/build/cli/source-manager.js.map +1 -0
- package/build/config/creators.d.ts +11 -0
- package/build/config/creators.d.ts.map +1 -0
- package/build/config/creators.js +32 -0
- package/build/config/creators.js.map +1 -0
- package/build/config/sources.d.ts +91 -0
- package/build/config/sources.d.ts.map +1 -0
- package/build/config/sources.js +231 -0
- package/build/config/sources.js.map +1 -0
- package/build/config/sources.test.d.ts +2 -0
- package/build/config/sources.test.d.ts.map +1 -0
- package/build/config/sources.test.js +199 -0
- package/build/config/sources.test.js.map +1 -0
- package/build/config/swift-keywords.d.ts +29 -0
- package/build/config/swift-keywords.d.ts.map +1 -0
- package/build/config/swift-keywords.js +77 -0
- package/build/config/swift-keywords.js.map +1 -0
- package/build/index.d.ts +3 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +181 -0
- package/build/index.js.map +1 -0
- package/build/integration/cache-behavior.test.d.ts +2 -0
- package/build/integration/cache-behavior.test.d.ts.map +1 -0
- package/build/integration/cache-behavior.test.js +521 -0
- package/build/integration/cache-behavior.test.js.map +1 -0
- package/build/integration/mcp-client.test.d.ts +2 -0
- package/build/integration/mcp-client.test.d.ts.map +1 -0
- package/build/integration/mcp-client.test.js +82 -0
- package/build/integration/mcp-client.test.js.map +1 -0
- package/build/integration/response-quality.test.d.ts +2 -0
- package/build/integration/response-quality.test.d.ts.map +1 -0
- package/build/integration/response-quality.test.js +230 -0
- package/build/integration/response-quality.test.js.map +1 -0
- package/build/integration/test-client.d.ts +25 -0
- package/build/integration/test-client.d.ts.map +1 -0
- package/build/integration/test-client.js +93 -0
- package/build/integration/test-client.js.map +1 -0
- package/build/sources/free/nilcoalescing.d.ts +8 -0
- package/build/sources/free/nilcoalescing.d.ts.map +1 -0
- package/build/sources/free/nilcoalescing.js +26 -0
- package/build/sources/free/nilcoalescing.js.map +1 -0
- package/build/sources/free/nilcoalescing.test.d.ts +2 -0
- package/build/sources/free/nilcoalescing.test.d.ts.map +1 -0
- package/build/sources/free/nilcoalescing.test.js +63 -0
- package/build/sources/free/nilcoalescing.test.js.map +1 -0
- package/build/sources/free/pointfree.d.ts +15 -0
- package/build/sources/free/pointfree.d.ts.map +1 -0
- package/build/sources/free/pointfree.js +175 -0
- package/build/sources/free/pointfree.js.map +1 -0
- package/build/sources/free/pointfree.test.d.ts +2 -0
- package/build/sources/free/pointfree.test.d.ts.map +1 -0
- package/build/sources/free/pointfree.test.js +86 -0
- package/build/sources/free/pointfree.test.js.map +1 -0
- package/build/sources/free/rssPatternSource.d.ts +42 -0
- package/build/sources/free/rssPatternSource.d.ts.map +1 -0
- package/build/sources/free/rssPatternSource.js +109 -0
- package/build/sources/free/rssPatternSource.js.map +1 -0
- package/build/sources/free/rssPatternSource.test.d.ts +2 -0
- package/build/sources/free/rssPatternSource.test.d.ts.map +1 -0
- package/build/sources/free/rssPatternSource.test.js +89 -0
- package/build/sources/free/rssPatternSource.test.js.map +1 -0
- package/build/sources/free/sundell.d.ts +8 -0
- package/build/sources/free/sundell.d.ts.map +1 -0
- package/build/sources/free/sundell.js +17 -0
- package/build/sources/free/sundell.js.map +1 -0
- package/build/sources/free/sundell.test.d.ts +2 -0
- package/build/sources/free/sundell.test.d.ts.map +1 -0
- package/build/sources/free/sundell.test.js +63 -0
- package/build/sources/free/sundell.test.js.map +1 -0
- package/build/sources/free/vanderlee.d.ts +8 -0
- package/build/sources/free/vanderlee.d.ts.map +1 -0
- package/build/sources/free/vanderlee.js +63 -0
- package/build/sources/free/vanderlee.js.map +1 -0
- package/build/sources/free/vanderlee.test.d.ts +2 -0
- package/build/sources/free/vanderlee.test.d.ts.map +1 -0
- package/build/sources/free/vanderlee.test.js +77 -0
- package/build/sources/free/vanderlee.test.js.map +1 -0
- package/build/sources/premium/patreon-dl.d.ts +45 -0
- package/build/sources/premium/patreon-dl.d.ts.map +1 -0
- package/build/sources/premium/patreon-dl.js +189 -0
- package/build/sources/premium/patreon-dl.js.map +1 -0
- package/build/sources/premium/patreon-fetch.d.ts +3 -0
- package/build/sources/premium/patreon-fetch.d.ts.map +1 -0
- package/build/sources/premium/patreon-fetch.js +18 -0
- package/build/sources/premium/patreon-fetch.js.map +1 -0
- package/build/sources/premium/patreon-oauth.d.ts +24 -0
- package/build/sources/premium/patreon-oauth.d.ts.map +1 -0
- package/build/sources/premium/patreon-oauth.js +208 -0
- package/build/sources/premium/patreon-oauth.js.map +1 -0
- package/build/sources/premium/patreon-zip.d.ts +17 -0
- package/build/sources/premium/patreon-zip.d.ts.map +1 -0
- package/build/sources/premium/patreon-zip.js +127 -0
- package/build/sources/premium/patreon-zip.js.map +1 -0
- package/build/sources/premium/patreon.d.ts +48 -0
- package/build/sources/premium/patreon.d.ts.map +1 -0
- package/build/sources/premium/patreon.js +221 -0
- package/build/sources/premium/patreon.js.map +1 -0
- package/build/sources/premium/youtube.d.ts +14 -0
- package/build/sources/premium/youtube.d.ts.map +1 -0
- package/build/sources/premium/youtube.js +92 -0
- package/build/sources/premium/youtube.js.map +1 -0
- package/build/tools/extract-cookie.d.ts +2 -0
- package/build/tools/extract-cookie.d.ts.map +1 -0
- package/build/tools/extract-cookie.js +40 -0
- package/build/tools/extract-cookie.js.map +1 -0
- package/build/tools/handlers/enableSource.d.ts +3 -0
- package/build/tools/handlers/enableSource.d.ts.map +1 -0
- package/build/tools/handlers/enableSource.js +25 -0
- package/build/tools/handlers/enableSource.js.map +1 -0
- package/build/tools/handlers/getPatreonPatterns.d.ts +3 -0
- package/build/tools/handlers/getPatreonPatterns.d.ts.map +1 -0
- package/build/tools/handlers/getPatreonPatterns.js +43 -0
- package/build/tools/handlers/getPatreonPatterns.js.map +1 -0
- package/build/tools/handlers/getSwiftPattern.d.ts +3 -0
- package/build/tools/handlers/getSwiftPattern.d.ts.map +1 -0
- package/build/tools/handlers/getSwiftPattern.js +72 -0
- package/build/tools/handlers/getSwiftPattern.js.map +1 -0
- package/build/tools/handlers/handlers.test.d.ts +2 -0
- package/build/tools/handlers/handlers.test.d.ts.map +1 -0
- package/build/tools/handlers/handlers.test.js +359 -0
- package/build/tools/handlers/handlers.test.js.map +1 -0
- package/build/tools/handlers/listContentSources.d.ts +3 -0
- package/build/tools/handlers/listContentSources.d.ts.map +1 -0
- package/build/tools/handlers/listContentSources.js +34 -0
- package/build/tools/handlers/listContentSources.js.map +1 -0
- package/build/tools/handlers/searchSwiftContent.d.ts +3 -0
- package/build/tools/handlers/searchSwiftContent.d.ts.map +1 -0
- package/build/tools/handlers/searchSwiftContent.js +121 -0
- package/build/tools/handlers/searchSwiftContent.js.map +1 -0
- package/build/tools/handlers/setupPatreon.d.ts +3 -0
- package/build/tools/handlers/setupPatreon.d.ts.map +1 -0
- package/build/tools/handlers/setupPatreon.js +40 -0
- package/build/tools/handlers/setupPatreon.js.map +1 -0
- package/build/tools/index.d.ts +3 -0
- package/build/tools/index.d.ts.map +1 -0
- package/build/tools/index.js +18 -0
- package/build/tools/index.js.map +1 -0
- package/build/tools/registry.d.ts +14 -0
- package/build/tools/registry.d.ts.map +1 -0
- package/build/tools/registry.js +21 -0
- package/build/tools/registry.js.map +1 -0
- package/build/tools/registry.test.d.ts +2 -0
- package/build/tools/registry.test.d.ts.map +1 -0
- package/build/tools/registry.test.js +54 -0
- package/build/tools/registry.test.js.map +1 -0
- package/build/tools/types.d.ts +67 -0
- package/build/tools/types.d.ts.map +1 -0
- package/build/tools/types.js +3 -0
- package/build/tools/types.js.map +1 -0
- package/build/utils/cache.d.ts +20 -0
- package/build/utils/cache.d.ts.map +1 -0
- package/build/utils/cache.js +186 -0
- package/build/utils/cache.js.map +1 -0
- package/build/utils/concurrency.d.ts +13 -0
- package/build/utils/concurrency.d.ts.map +1 -0
- package/build/utils/concurrency.js +33 -0
- package/build/utils/concurrency.js.map +1 -0
- package/build/utils/errors.d.ts +19 -0
- package/build/utils/errors.d.ts.map +1 -0
- package/build/utils/errors.js +35 -0
- package/build/utils/errors.js.map +1 -0
- package/build/utils/fetch.d.ts +6 -0
- package/build/utils/fetch.d.ts.map +1 -0
- package/build/utils/fetch.js +6 -0
- package/build/utils/fetch.js.map +1 -0
- package/build/utils/http.d.ts +21 -0
- package/build/utils/http.d.ts.map +1 -0
- package/build/utils/http.js +53 -0
- package/build/utils/http.js.map +1 -0
- package/build/utils/intent-cache.d.ts +94 -0
- package/build/utils/intent-cache.d.ts.map +1 -0
- package/build/utils/intent-cache.js +164 -0
- package/build/utils/intent-cache.js.map +1 -0
- package/build/utils/intent-cache.test.d.ts +2 -0
- package/build/utils/intent-cache.test.d.ts.map +1 -0
- package/build/utils/intent-cache.test.js +290 -0
- package/build/utils/intent-cache.test.js.map +1 -0
- package/build/utils/logger.d.ts +4 -0
- package/build/utils/logger.d.ts.map +1 -0
- package/build/utils/logger.js +9 -0
- package/build/utils/logger.js.map +1 -0
- package/build/utils/paths.d.ts +27 -0
- package/build/utils/paths.d.ts.map +1 -0
- package/build/utils/paths.js +43 -0
- package/build/utils/paths.js.map +1 -0
- package/build/utils/pattern-formatter.d.ts +40 -0
- package/build/utils/pattern-formatter.d.ts.map +1 -0
- package/build/utils/pattern-formatter.js +124 -0
- package/build/utils/pattern-formatter.js.map +1 -0
- package/build/utils/response-helpers.d.ts +17 -0
- package/build/utils/response-helpers.d.ts.map +1 -0
- package/build/utils/response-helpers.js +34 -0
- package/build/utils/response-helpers.js.map +1 -0
- package/build/utils/search-terms.d.ts +17 -0
- package/build/utils/search-terms.d.ts.map +1 -0
- package/build/utils/search-terms.js +71 -0
- package/build/utils/search-terms.js.map +1 -0
- package/build/utils/search-terms.test.d.ts +2 -0
- package/build/utils/search-terms.test.d.ts.map +1 -0
- package/build/utils/search-terms.test.js +107 -0
- package/build/utils/search-terms.test.js.map +1 -0
- package/build/utils/search.d.ts +48 -0
- package/build/utils/search.d.ts.map +1 -0
- package/build/utils/search.js +158 -0
- package/build/utils/search.js.map +1 -0
- package/build/utils/search.test.d.ts +2 -0
- package/build/utils/search.test.d.ts.map +1 -0
- package/build/utils/search.test.js +199 -0
- package/build/utils/search.test.js.map +1 -0
- package/build/utils/semantic-recall.d.ts +38 -0
- package/build/utils/semantic-recall.d.ts.map +1 -0
- package/build/utils/semantic-recall.js +134 -0
- package/build/utils/semantic-recall.js.map +1 -0
- package/build/utils/semantic-recall.test.d.ts +2 -0
- package/build/utils/semantic-recall.test.d.ts.map +1 -0
- package/build/utils/semantic-recall.test.js +326 -0
- package/build/utils/semantic-recall.test.js.map +1 -0
- package/build/utils/source-registry.d.ts +45 -0
- package/build/utils/source-registry.d.ts.map +1 -0
- package/build/utils/source-registry.js +113 -0
- package/build/utils/source-registry.js.map +1 -0
- package/build/utils/source-registry.test.d.ts +2 -0
- package/build/utils/source-registry.test.d.ts.map +1 -0
- package/build/utils/source-registry.test.js +206 -0
- package/build/utils/source-registry.test.js.map +1 -0
- package/build/utils/swift-analysis.d.ts +61 -0
- package/build/utils/swift-analysis.d.ts.map +1 -0
- package/build/utils/swift-analysis.js +339 -0
- package/build/utils/swift-analysis.js.map +1 -0
- package/build/utils/swift-analysis.test.d.ts +2 -0
- package/build/utils/swift-analysis.test.d.ts.map +1 -0
- package/build/utils/swift-analysis.test.js +473 -0
- package/build/utils/swift-analysis.test.js.map +1 -0
- package/package.json +85 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
// src/utils/search.ts
|
|
2
|
+
// Advanced search utilities with fuzzy matching and smart stemming
|
|
3
|
+
import MiniSearch from 'minisearch';
|
|
4
|
+
import natural from 'natural';
|
|
5
|
+
import { normalizeTokens } from './search-terms.js';
|
|
6
|
+
// Porter Stemmer for English
|
|
7
|
+
const stemmer = natural.PorterStemmer;
|
|
8
|
+
// Custom tokenizer with smart hyphen handling and stemming
|
|
9
|
+
function tokenize(text) {
|
|
10
|
+
return normalizeTokens(text, (token) => stemmer.stem(token));
|
|
11
|
+
}
|
|
12
|
+
// Process query with same tokenization for consistent matching
|
|
13
|
+
function processQuery(query) {
|
|
14
|
+
return tokenize(query);
|
|
15
|
+
}
|
|
16
|
+
export class SearchIndex {
|
|
17
|
+
miniSearch;
|
|
18
|
+
documents = new Map();
|
|
19
|
+
constructor(fields = ['title', 'content', 'topics']) {
|
|
20
|
+
this.miniSearch = new MiniSearch({
|
|
21
|
+
fields,
|
|
22
|
+
storeFields: ['id'],
|
|
23
|
+
tokenize, // Uses our new smart tokenizer
|
|
24
|
+
processTerm: (term) => term, // Already processed by tokenize
|
|
25
|
+
searchOptions: {
|
|
26
|
+
boost: { title: 2, topics: 1.5, content: 1 },
|
|
27
|
+
fuzzy: 0.2,
|
|
28
|
+
prefix: true,
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
addDocuments(docs) {
|
|
33
|
+
// Clear existing documents
|
|
34
|
+
this.miniSearch.removeAll();
|
|
35
|
+
this.documents.clear();
|
|
36
|
+
// Preprocess documents for indexing
|
|
37
|
+
const processedDocs = docs.map(doc => ({
|
|
38
|
+
...doc,
|
|
39
|
+
// Join topics array for indexing
|
|
40
|
+
topics: Array.isArray(doc.topics) ? doc.topics.join(' ') : doc.topics,
|
|
41
|
+
}));
|
|
42
|
+
// Add to MiniSearch
|
|
43
|
+
this.miniSearch.addAll(processedDocs);
|
|
44
|
+
// Store original documents for retrieval
|
|
45
|
+
docs.forEach(doc => this.documents.set(doc.id, doc));
|
|
46
|
+
}
|
|
47
|
+
search(query, options = {}) {
|
|
48
|
+
const { fuzzy = 0.2, boost = { title: 2, topics: 1.5, content: 1 }, minScore = 0, } = options;
|
|
49
|
+
// Get stemmed query terms for match highlighting
|
|
50
|
+
const queryTerms = processQuery(query);
|
|
51
|
+
// Search with MiniSearch
|
|
52
|
+
const results = this.miniSearch.search(query, {
|
|
53
|
+
fuzzy,
|
|
54
|
+
prefix: true,
|
|
55
|
+
boost,
|
|
56
|
+
combineWith: 'OR',
|
|
57
|
+
});
|
|
58
|
+
// Map results to original documents with scores
|
|
59
|
+
return results
|
|
60
|
+
.filter(result => result.score >= minScore)
|
|
61
|
+
.map(result => {
|
|
62
|
+
const doc = this.documents.get(result.id);
|
|
63
|
+
if (!doc)
|
|
64
|
+
return null;
|
|
65
|
+
// Find which terms matched
|
|
66
|
+
const matches = this.findMatches(doc, queryTerms);
|
|
67
|
+
return {
|
|
68
|
+
item: doc,
|
|
69
|
+
score: result.score,
|
|
70
|
+
matches,
|
|
71
|
+
};
|
|
72
|
+
})
|
|
73
|
+
.filter((r) => r !== null);
|
|
74
|
+
}
|
|
75
|
+
findMatches(doc, queryTerms) {
|
|
76
|
+
const matches = [];
|
|
77
|
+
const searchText = `${doc.title} ${doc.content} ${doc.topics.join(' ')}`.toLowerCase();
|
|
78
|
+
for (const term of queryTerms) {
|
|
79
|
+
// Check both stemmed and original
|
|
80
|
+
if (searchText.includes(term)) {
|
|
81
|
+
matches.push(term);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return [...new Set(matches)];
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// Standalone search function for simple use cases
|
|
88
|
+
export function fuzzySearch(documents, query, options = {}) {
|
|
89
|
+
const index = new SearchIndex();
|
|
90
|
+
index.addDocuments(documents);
|
|
91
|
+
return index.search(query, options);
|
|
92
|
+
}
|
|
93
|
+
// Get search relevance score (combines MiniSearch score with static relevance)
|
|
94
|
+
export function combineScores(searchScore, staticRelevance, searchWeight = 0.6) {
|
|
95
|
+
// Normalize search score (MiniSearch scores can vary widely)
|
|
96
|
+
const normalizedSearch = Math.min(searchScore / 10, 1) * 100;
|
|
97
|
+
// Weighted combination
|
|
98
|
+
return Math.round(normalizedSearch * searchWeight +
|
|
99
|
+
staticRelevance * (1 - searchWeight));
|
|
100
|
+
}
|
|
101
|
+
// Suggest similar terms (for "did you mean?" functionality)
|
|
102
|
+
export function suggestSimilar(query, knownTerms, maxSuggestions = 3) {
|
|
103
|
+
const queryLower = query.toLowerCase();
|
|
104
|
+
// Calculate Levenshtein distance
|
|
105
|
+
const suggestions = knownTerms
|
|
106
|
+
.map(term => ({
|
|
107
|
+
term,
|
|
108
|
+
distance: natural.LevenshteinDistance(queryLower, term.toLowerCase()),
|
|
109
|
+
}))
|
|
110
|
+
.filter(({ distance }) => distance <= 3 && distance > 0)
|
|
111
|
+
.sort((a, b) => a.distance - b.distance)
|
|
112
|
+
.slice(0, maxSuggestions)
|
|
113
|
+
.map(({ term }) => term);
|
|
114
|
+
return suggestions;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* CachedSearchIndex - Manages a SearchIndex with automatic invalidation based on document changes.
|
|
118
|
+
* Eliminates duplicated search caching logic across pattern sources.
|
|
119
|
+
*/
|
|
120
|
+
export class CachedSearchIndex {
|
|
121
|
+
searchIndex = null;
|
|
122
|
+
indexedPatternsHash = null;
|
|
123
|
+
fields;
|
|
124
|
+
constructor(fields = ['title', 'content', 'topics']) {
|
|
125
|
+
this.fields = fields;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Search patterns with automatic index caching.
|
|
129
|
+
* Index is rebuilt only when patterns change (detected via hash).
|
|
130
|
+
*/
|
|
131
|
+
search(patterns, query, options = {}) {
|
|
132
|
+
const { fuzzy = 0.2, boost = { title: 2.5, topics: 1.8, content: 1 }, } = options;
|
|
133
|
+
// Simple hash based on length and sorted IDs
|
|
134
|
+
const patternsHash = `${patterns.length}-${patterns.map(p => p.id).sort().join(',')}`;
|
|
135
|
+
// Rebuild index only if patterns changed
|
|
136
|
+
if (!this.searchIndex || this.indexedPatternsHash !== patternsHash) {
|
|
137
|
+
this.searchIndex = new SearchIndex(this.fields);
|
|
138
|
+
this.searchIndex.addDocuments(patterns);
|
|
139
|
+
this.indexedPatternsHash = patternsHash;
|
|
140
|
+
}
|
|
141
|
+
const results = this.searchIndex.search(query, { fuzzy, boost });
|
|
142
|
+
return results
|
|
143
|
+
.map(result => ({
|
|
144
|
+
...result.item,
|
|
145
|
+
relevanceScore: combineScores(result.score, result.item.relevanceScore),
|
|
146
|
+
}))
|
|
147
|
+
.sort((a, b) => b.relevanceScore - a.relevanceScore);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Invalidate the cached index (call after fetching new patterns)
|
|
151
|
+
*/
|
|
152
|
+
invalidate() {
|
|
153
|
+
this.searchIndex = null;
|
|
154
|
+
this.indexedPatternsHash = null;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
export default SearchIndex;
|
|
158
|
+
//# sourceMappingURL=search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/utils/search.ts"],"names":[],"mappings":"AAAA,sBAAsB;AACtB,mEAAmE;AAEnE,OAAO,UAAU,MAAM,YAAY,CAAC;AACpC,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD,6BAA6B;AAC7B,MAAM,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC;AAqBtC,2DAA2D;AAC3D,SAAS,QAAQ,CAAC,IAAY;IAC5B,OAAO,eAAe,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;AAC/D,CAAC;AAED,+DAA+D;AAC/D,SAAS,YAAY,CAAC,KAAa;IACjC,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC;AACzB,CAAC;AAED,MAAM,OAAO,WAAW;IACd,UAAU,CAAgB;IAC1B,SAAS,GAAmB,IAAI,GAAG,EAAE,CAAC;IAE9C,YAAY,SAAmB,CAAC,OAAO,EAAE,SAAS,EAAE,QAAQ,CAAC;QAC3D,IAAI,CAAC,UAAU,GAAG,IAAI,UAAU,CAAI;YAClC,MAAM;YACN,WAAW,EAAE,CAAC,IAAI,CAAC;YACnB,QAAQ,EAAE,+BAA+B;YACzC,WAAW,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,gCAAgC;YAC7D,aAAa,EAAE;gBACb,KAAK,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,EAAE;gBAC5C,KAAK,EAAE,GAAG;gBACV,MAAM,EAAE,IAAI;aACb;SACF,CAAC,CAAC;IACL,CAAC;IAED,YAAY,CAAC,IAAS;QACpB,2BAA2B;QAC3B,IAAI,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC;QAC5B,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;QAEvB,oCAAoC;QACpC,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACrC,GAAG,GAAG;YACN,iCAAiC;YACjC,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM;SACtE,CAAC,CAAC,CAAC;QAEJ,oBAAoB;QACpB,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;QAEtC,yCAAyC;QACzC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC;IACvD,CAAC;IAED,MAAM,CAAC,KAAa,EAAE,UAAyB,EAAE;QAC/C,MAAM,EACJ,KAAK,GAAG,GAAG,EACX,KAAK,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,EAAE,EAC7C,QAAQ,GAAG,CAAC,GACb,GAAG,OAAO,CAAC;QAEZ,iDAAiD;QACjD,MAAM,UAAU,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;QAEvC,yBAAyB;QACzB,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,EAAE;YAC5C,KAAK;YACL,MAAM,EAAE,IAAI;YACZ,KAAK;YACL,WAAW,EAAE,IAAI;SAClB,CAAC,CAAC;QAEH,gDAAgD;QAChD,OAAO,OAAO;aACX,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,KAAK,IAAI,QAAQ,CAAC;aAC1C,GAAG,CAAC,MAAM,CAAC,EAAE;YACZ,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YAC1C,IAAI,CAAC,GAAG;gBAAE,OAAO,IAAI,CAAC;YAEtB,2BAA2B;YAC3B,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;YAElD,OAAO;gBACL,IAAI,EAAE,GAAG;gBACT,KAAK,EAAE,MAAM,CAAC,KAAK;gBACnB,OAAO;aACR,CAAC;QACJ,CAAC,CAAC;aACD,MAAM,CAAC,CAAC,CAAC,EAAwB,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;IACrD,CAAC;IAEO,WAAW,CAAC,GAAM,EAAE,UAAoB;QAC9C,MAAM,OAAO,GAAa,EAAE,CAAC;QAC7B,MAAM,UAAU,GAAG,GAAG,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC;QAEvF,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;YAC9B,kCAAkC;YAClC,IAAI,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC9B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACrB,CAAC;QACH,CAAC;QAED,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;IAC/B,CAAC;CACF;AAED,kDAAkD;AAClD,MAAM,UAAU,WAAW,CACzB,SAAc,EACd,KAAa,EACb,UAAyB,EAAE;IAE3B,MAAM,KAAK,GAAG,IAAI,WAAW,EAAK,CAAC;IACnC,KAAK,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC;IAC9B,OAAO,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;AACtC,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,aAAa,CAC3B,WAAmB,EACnB,eAAuB,EACvB,eAAuB,GAAG;IAE1B,6DAA6D;IAC7D,MAAM,gBAAgB,GAAG,IAAI,CAAC,GAAG,CAAC,WAAW,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,GAAG,CAAC;IAE7D,uBAAuB;IACvB,OAAO,IAAI,CAAC,KAAK,CACf,gBAAgB,GAAG,YAAY;QAC/B,eAAe,GAAG,CAAC,CAAC,GAAG,YAAY,CAAC,CACrC,CAAC;AACJ,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,cAAc,CAC5B,KAAa,EACb,UAAoB,EACpB,iBAAyB,CAAC;IAE1B,MAAM,UAAU,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;IAEvC,iCAAiC;IACjC,MAAM,WAAW,GAAG,UAAU;SAC3B,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACZ,IAAI;QACJ,QAAQ,EAAE,OAAO,CAAC,mBAAmB,CAAC,UAAU,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC;KACtE,CAAC,CAAC;SACF,MAAM,CAAC,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,QAAQ,IAAI,CAAC,IAAI,QAAQ,GAAG,CAAC,CAAC;SACvD,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC;SACvC,KAAK,CAAC,CAAC,EAAE,cAAc,CAAC;SACxB,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;IAE3B,OAAO,WAAW,CAAC;AACrB,CAAC;AAED;;;GAGG;AACH,MAAM,OAAO,iBAAiB;IACpB,WAAW,GAA0B,IAAI,CAAC;IAC1C,mBAAmB,GAAkB,IAAI,CAAC;IAC1C,MAAM,CAAW;IAEzB,YAAY,SAAmB,CAAC,OAAO,EAAE,SAAS,EAAE,QAAQ,CAAC;QAC3D,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED;;;OAGG;IACH,MAAM,CACJ,QAAa,EACb,KAAa,EACb,UAAyB,EAAE;QAE3B,MAAM,EACJ,KAAK,GAAG,GAAG,EACX,KAAK,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,EAAE,GAChD,GAAG,OAAO,CAAC;QAEZ,6CAA6C;QAC7C,MAAM,YAAY,GAAG,GAAG,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QAEtF,yCAAyC;QACzC,IAAI,CAAC,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,mBAAmB,KAAK,YAAY,EAAE,CAAC;YACnE,IAAI,CAAC,WAAW,GAAG,IAAI,WAAW,CAAI,IAAI,CAAC,MAAM,CAAC,CAAC;YACnD,IAAI,CAAC,WAAW,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;YACxC,IAAI,CAAC,mBAAmB,GAAG,YAAY,CAAC;QAC1C,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;QAEjE,OAAO,OAAO;aACX,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YACd,GAAG,MAAM,CAAC,IAAI;YACd,cAAc,EAAE,aAAa,CAAC,MAAM,CAAC,KAAK,EAAG,MAAM,CAAC,IAAuC,CAAC,cAAc,CAAC;SAC5G,CAAC,CAAC;aACF,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,cAAc,GAAG,CAAC,CAAC,cAAc,CAAC,CAAC;IACzD,CAAC;IAED;;OAEG;IACH,UAAU;QACR,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QACxB,IAAI,CAAC,mBAAmB,GAAG,IAAI,CAAC;IAClC,CAAC;CACF;AAED,eAAe,WAAW,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.test.d.ts","sourceRoot":"","sources":["../../src/utils/search.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
// src/utils/search.test.ts
|
|
2
|
+
import { describe, it, expect } from 'vitest';
|
|
3
|
+
import SearchIndex, { fuzzySearch, combineScores, suggestSimilar } from './search.js';
|
|
4
|
+
// Test documents
|
|
5
|
+
const testDocs = [
|
|
6
|
+
{
|
|
7
|
+
id: 'doc1',
|
|
8
|
+
title: 'Building async SwiftUI apps',
|
|
9
|
+
content: 'Learn how to use async/await with SwiftUI views and state management',
|
|
10
|
+
topics: ['swiftui', 'concurrency'],
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
id: 'doc2',
|
|
14
|
+
title: 'Understanding Combine framework',
|
|
15
|
+
content: 'A deep dive into Combine publishers and subscribers',
|
|
16
|
+
topics: ['combine', 'reactive'],
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
id: 'doc3',
|
|
20
|
+
title: 'Swift Testing with XCTest',
|
|
21
|
+
content: 'Writing unit tests and UI tests for your iOS apps using XCTest framework',
|
|
22
|
+
topics: ['testing', 'xctest'],
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: 'doc4',
|
|
26
|
+
title: 'MVVM Architecture in Swift',
|
|
27
|
+
content: 'Implementing the Model-View-ViewModel pattern with protocols and bindings',
|
|
28
|
+
topics: ['architecture', 'mvvm'],
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: 'doc5',
|
|
32
|
+
title: 'Core Data persistence',
|
|
33
|
+
content: 'Saving and fetching data with CoreData and SwiftUI integration',
|
|
34
|
+
topics: ['coredata', 'persistence'],
|
|
35
|
+
},
|
|
36
|
+
];
|
|
37
|
+
describe('SearchIndex', () => {
|
|
38
|
+
describe('basic search', () => {
|
|
39
|
+
it('should find documents by title keywords', () => {
|
|
40
|
+
const index = new SearchIndex();
|
|
41
|
+
index.addDocuments(testDocs);
|
|
42
|
+
const results = index.search('SwiftUI');
|
|
43
|
+
expect(results.length).toBeGreaterThan(0);
|
|
44
|
+
expect(results.some(r => r.item.id === 'doc1')).toBe(true);
|
|
45
|
+
});
|
|
46
|
+
it('should find documents by content keywords', () => {
|
|
47
|
+
const index = new SearchIndex();
|
|
48
|
+
index.addDocuments(testDocs);
|
|
49
|
+
const results = index.search('publishers subscribers');
|
|
50
|
+
expect(results.length).toBeGreaterThan(0);
|
|
51
|
+
expect(results.some(r => r.item.id === 'doc2')).toBe(true);
|
|
52
|
+
});
|
|
53
|
+
it('should find documents by topic', () => {
|
|
54
|
+
const index = new SearchIndex();
|
|
55
|
+
index.addDocuments(testDocs);
|
|
56
|
+
const results = index.search('testing');
|
|
57
|
+
expect(results.length).toBeGreaterThan(0);
|
|
58
|
+
expect(results.some(r => r.item.id === 'doc3')).toBe(true);
|
|
59
|
+
});
|
|
60
|
+
it('should return empty array for non-matching query', () => {
|
|
61
|
+
const index = new SearchIndex();
|
|
62
|
+
index.addDocuments(testDocs);
|
|
63
|
+
const results = index.search('nonexistentterm12345');
|
|
64
|
+
expect(results.length).toBe(0);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
describe('fuzzy matching', () => {
|
|
68
|
+
it('should find documents with slight typos', () => {
|
|
69
|
+
const index = new SearchIndex();
|
|
70
|
+
index.addDocuments(testDocs);
|
|
71
|
+
// "swiftui" with typo "swiftiu"
|
|
72
|
+
const results = index.search('swiftiu', { fuzzy: 0.3 });
|
|
73
|
+
expect(results.length).toBeGreaterThan(0);
|
|
74
|
+
});
|
|
75
|
+
it('should respect fuzzy threshold', () => {
|
|
76
|
+
const index = new SearchIndex();
|
|
77
|
+
index.addDocuments(testDocs);
|
|
78
|
+
// Very strict fuzzy - should not match typos
|
|
79
|
+
const strictResults = index.search('swiftiu', { fuzzy: 0 });
|
|
80
|
+
const lenientResults = index.search('swiftiu', { fuzzy: 0.4 });
|
|
81
|
+
expect(lenientResults.length).toBeGreaterThanOrEqual(strictResults.length);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
describe('score filtering', () => {
|
|
85
|
+
it('should filter results by minimum score', () => {
|
|
86
|
+
const index = new SearchIndex();
|
|
87
|
+
index.addDocuments(testDocs);
|
|
88
|
+
const allResults = index.search('swift');
|
|
89
|
+
const filteredResults = index.search('swift', { minScore: 5 });
|
|
90
|
+
expect(allResults.length).toBeGreaterThanOrEqual(filteredResults.length);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
describe('document management', () => {
|
|
94
|
+
it('should clear old documents when adding new ones', () => {
|
|
95
|
+
const index = new SearchIndex();
|
|
96
|
+
index.addDocuments(testDocs);
|
|
97
|
+
const newDocs = [{
|
|
98
|
+
id: 'new1',
|
|
99
|
+
title: 'New document about Kotlin',
|
|
100
|
+
content: 'This is about Kotlin, not Swift',
|
|
101
|
+
topics: ['kotlin'],
|
|
102
|
+
}];
|
|
103
|
+
index.addDocuments(newDocs);
|
|
104
|
+
const swiftResults = index.search('SwiftUI');
|
|
105
|
+
const kotlinResults = index.search('Kotlin');
|
|
106
|
+
expect(swiftResults.length).toBe(0);
|
|
107
|
+
expect(kotlinResults.length).toBe(1);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
describe('fuzzySearch helper', () => {
|
|
112
|
+
it('should provide standalone fuzzy search', () => {
|
|
113
|
+
const results = fuzzySearch(testDocs, 'async await');
|
|
114
|
+
expect(results.length).toBeGreaterThan(0);
|
|
115
|
+
expect(results[0].item.id).toBe('doc1');
|
|
116
|
+
});
|
|
117
|
+
it('should return results with scores', () => {
|
|
118
|
+
const results = fuzzySearch(testDocs, 'SwiftUI');
|
|
119
|
+
expect(results.length).toBeGreaterThan(0);
|
|
120
|
+
expect(results[0]).toHaveProperty('score');
|
|
121
|
+
expect(results[0]).toHaveProperty('matches');
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
describe('combineScores', () => {
|
|
125
|
+
it('should combine search and static relevance scores', () => {
|
|
126
|
+
const combined = combineScores(10, 80);
|
|
127
|
+
expect(combined).toBeGreaterThan(0);
|
|
128
|
+
expect(combined).toBeLessThanOrEqual(100);
|
|
129
|
+
});
|
|
130
|
+
it('should respect search weight parameter', () => {
|
|
131
|
+
const highSearchWeight = combineScores(10, 50, 0.9);
|
|
132
|
+
const lowSearchWeight = combineScores(10, 50, 0.1);
|
|
133
|
+
// With high search weight, search score matters more
|
|
134
|
+
// With low search weight, static relevance matters more
|
|
135
|
+
expect(highSearchWeight).not.toBe(lowSearchWeight);
|
|
136
|
+
});
|
|
137
|
+
it('should cap scores at 100', () => {
|
|
138
|
+
const combined = combineScores(100, 100, 0.5);
|
|
139
|
+
expect(combined).toBeLessThanOrEqual(100);
|
|
140
|
+
});
|
|
141
|
+
it('should handle zero scores', () => {
|
|
142
|
+
const combined = combineScores(0, 0);
|
|
143
|
+
expect(combined).toBe(0);
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
describe('suggestSimilar', () => {
|
|
147
|
+
const knownTerms = ['swiftui', 'combine', 'async', 'await', 'protocol', 'struct'];
|
|
148
|
+
it('should suggest similar terms for typos', () => {
|
|
149
|
+
const suggestions = suggestSimilar('swiftiu', knownTerms);
|
|
150
|
+
expect(suggestions).toContain('swiftui');
|
|
151
|
+
});
|
|
152
|
+
it('should not suggest exact matches', () => {
|
|
153
|
+
const suggestions = suggestSimilar('swiftui', knownTerms);
|
|
154
|
+
expect(suggestions).not.toContain('swiftui');
|
|
155
|
+
});
|
|
156
|
+
it('should respect max suggestions limit', () => {
|
|
157
|
+
const suggestions = suggestSimilar('s', knownTerms, 2);
|
|
158
|
+
expect(suggestions.length).toBeLessThanOrEqual(2);
|
|
159
|
+
});
|
|
160
|
+
it('should return empty array for very different terms', () => {
|
|
161
|
+
const suggestions = suggestSimilar('xyzabc123', knownTerms);
|
|
162
|
+
expect(suggestions.length).toBe(0);
|
|
163
|
+
});
|
|
164
|
+
it('should sort by similarity (Levenshtein distance)', () => {
|
|
165
|
+
const suggestions = suggestSimilar('async', ['asyncc', 'asyncxx', 'asyn']);
|
|
166
|
+
// 'asyn' has distance 1, 'asyncc' has distance 1, 'asyncxx' has distance 2
|
|
167
|
+
expect(suggestions.length).toBeGreaterThan(0);
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
describe('tokenization', () => {
|
|
171
|
+
it('should preserve Swift-specific terms without stemming', () => {
|
|
172
|
+
const index = new SearchIndex();
|
|
173
|
+
const docs = [{
|
|
174
|
+
id: 'test1',
|
|
175
|
+
title: 'SwiftUI and Combine',
|
|
176
|
+
content: 'Using async await with SwiftUI',
|
|
177
|
+
topics: ['swiftui', 'combine'],
|
|
178
|
+
}];
|
|
179
|
+
index.addDocuments(docs);
|
|
180
|
+
// These terms should match exactly without stemming issues
|
|
181
|
+
const swiftuiResults = index.search('swiftui');
|
|
182
|
+
const asyncResults = index.search('async');
|
|
183
|
+
expect(swiftuiResults.length).toBe(1);
|
|
184
|
+
expect(asyncResults.length).toBe(1);
|
|
185
|
+
});
|
|
186
|
+
it('should handle hyphenated terms', () => {
|
|
187
|
+
const index = new SearchIndex();
|
|
188
|
+
const docs = [{
|
|
189
|
+
id: 'test1',
|
|
190
|
+
title: 'Async-await patterns',
|
|
191
|
+
content: 'Learn about async-await in Swift',
|
|
192
|
+
topics: ['concurrency'],
|
|
193
|
+
}];
|
|
194
|
+
index.addDocuments(docs);
|
|
195
|
+
const results = index.search('async await');
|
|
196
|
+
expect(results.length).toBe(1);
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
//# sourceMappingURL=search.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.test.js","sourceRoot":"","sources":["../../src/utils/search.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { BasePattern } from '../sources/free/rssPatternSource.js';
|
|
2
|
+
export interface SemanticRecallConfig {
|
|
3
|
+
enabled: boolean;
|
|
4
|
+
minLexicalScore: number;
|
|
5
|
+
minRelevanceScore: number;
|
|
6
|
+
}
|
|
7
|
+
export declare const DEFAULT_CONFIG: SemanticRecallConfig;
|
|
8
|
+
export declare class SemanticRecallIndex {
|
|
9
|
+
private indexedMap;
|
|
10
|
+
private cache;
|
|
11
|
+
private pipeline;
|
|
12
|
+
private config;
|
|
13
|
+
constructor(config?: SemanticRecallConfig);
|
|
14
|
+
/**
|
|
15
|
+
* Lazy-load the transformer pipeline
|
|
16
|
+
*/
|
|
17
|
+
private getEmbeddingPipeline;
|
|
18
|
+
/**
|
|
19
|
+
* Generate embedding for text
|
|
20
|
+
*/
|
|
21
|
+
private embed;
|
|
22
|
+
/**
|
|
23
|
+
* Index patterns for semantic search.
|
|
24
|
+
* Filters patterns by minRelevanceScore and caches embeddings.
|
|
25
|
+
* Uses incremental indexing - only processes new/changed patterns.
|
|
26
|
+
*/
|
|
27
|
+
index(patterns: BasePattern[]): Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* Search indexed patterns by semantic similarity.
|
|
30
|
+
* Returns top-K patterns sorted by cosine similarity.
|
|
31
|
+
*/
|
|
32
|
+
search(query: string, limit: number): Promise<BasePattern[]>;
|
|
33
|
+
/**
|
|
34
|
+
* Get the number of indexed patterns (for testing/debugging)
|
|
35
|
+
*/
|
|
36
|
+
get size(): number;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=semantic-recall.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"semantic-recall.d.ts","sourceRoot":"","sources":["../../src/utils/semantic-recall.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qCAAqC,CAAC;AAEvE,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,eAAO,MAAM,cAAc,EAAE,oBAI5B,CAAC;AA4BF,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,UAAU,CAA0C;IAC5D,OAAO,CAAC,KAAK,CAAY;IACzB,OAAO,CAAC,QAAQ,CAAa;IAC7B,OAAO,CAAC,MAAM,CAAuB;gBAEzB,MAAM,GAAE,oBAAqC;IAKzD;;OAEG;YACW,oBAAoB;IASlC;;OAEG;YACW,KAAK;IAMnB;;;;OAIG;IACG,KAAK,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAyDnD;;;OAGG;IACG,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAsBlE;;OAEG;IACH,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
// src/utils/semantic-recall.ts
|
|
2
|
+
// Semantic recall fallback for lexical search using embeddings
|
|
3
|
+
import { createHash } from 'crypto';
|
|
4
|
+
import { FileCache } from './cache.js';
|
|
5
|
+
export const DEFAULT_CONFIG = {
|
|
6
|
+
enabled: false,
|
|
7
|
+
minLexicalScore: 0.35,
|
|
8
|
+
minRelevanceScore: 70,
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Extract indexable content from pattern (title + excerpt only, not full content)
|
|
12
|
+
*/
|
|
13
|
+
function extractIndexableContent(pattern) {
|
|
14
|
+
const title = pattern.title || '';
|
|
15
|
+
const excerpt = pattern.excerpt || pattern.content?.substring(0, 500) || '';
|
|
16
|
+
// Concatenate title and excerpt for embedding
|
|
17
|
+
return `${title} ${excerpt}`.trim();
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Generate content hash for cache key
|
|
21
|
+
* Uses extractIndexableContent to ensure hash matches embedding input
|
|
22
|
+
*/
|
|
23
|
+
function getContentHash(pattern) {
|
|
24
|
+
const content = extractIndexableContent(pattern);
|
|
25
|
+
return createHash('sha256').update(content).digest('hex').substring(0, 16);
|
|
26
|
+
}
|
|
27
|
+
export class SemanticRecallIndex {
|
|
28
|
+
indexedMap = new Map();
|
|
29
|
+
cache;
|
|
30
|
+
pipeline = null; // Lazy-loaded transformer pipeline
|
|
31
|
+
config;
|
|
32
|
+
constructor(config = DEFAULT_CONFIG) {
|
|
33
|
+
this.config = config;
|
|
34
|
+
this.cache = new FileCache('semantic-embeddings');
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Lazy-load the transformer pipeline
|
|
38
|
+
*/
|
|
39
|
+
async getEmbeddingPipeline() {
|
|
40
|
+
if (!this.pipeline) {
|
|
41
|
+
const { pipeline, env } = await import('@xenova/transformers');
|
|
42
|
+
env.allowLocalModels = false; // Use remote models only
|
|
43
|
+
this.pipeline = await pipeline('feature-extraction', 'Xenova/all-MiniLM-L6-v2');
|
|
44
|
+
}
|
|
45
|
+
return this.pipeline;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Generate embedding for text
|
|
49
|
+
*/
|
|
50
|
+
async embed(text) {
|
|
51
|
+
const pipe = await this.getEmbeddingPipeline();
|
|
52
|
+
const output = await pipe(text, { pooling: 'mean', normalize: true });
|
|
53
|
+
return output.data;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Index patterns for semantic search.
|
|
57
|
+
* Filters patterns by minRelevanceScore and caches embeddings.
|
|
58
|
+
* Uses incremental indexing - only processes new/changed patterns.
|
|
59
|
+
*/
|
|
60
|
+
async index(patterns) {
|
|
61
|
+
// Filter patterns by relevance score
|
|
62
|
+
const highQualityPatterns = patterns.filter(p => p.relevanceScore >= this.config.minRelevanceScore);
|
|
63
|
+
// Build set of current cache keys to track what should remain
|
|
64
|
+
const currentKeys = new Set();
|
|
65
|
+
// Index each pattern (incremental - skip unchanged patterns)
|
|
66
|
+
for (const pattern of highQualityPatterns) {
|
|
67
|
+
const contentHash = getContentHash(pattern);
|
|
68
|
+
const cacheKey = `embedding::${pattern.id}::${contentHash}`;
|
|
69
|
+
currentKeys.add(cacheKey);
|
|
70
|
+
// Skip if already indexed with same content hash
|
|
71
|
+
if (this.indexedMap.has(cacheKey)) {
|
|
72
|
+
// Update pattern reference in case metadata changed (but content hash same)
|
|
73
|
+
const existing = this.indexedMap.get(cacheKey);
|
|
74
|
+
existing.pattern = pattern;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
// Try to load from file cache
|
|
78
|
+
const embedding = await this.cache.get(cacheKey);
|
|
79
|
+
if (embedding) {
|
|
80
|
+
// File cache hit - use cached embedding
|
|
81
|
+
this.indexedMap.set(cacheKey, {
|
|
82
|
+
pattern,
|
|
83
|
+
embedding: new Float32Array(embedding),
|
|
84
|
+
cacheKey,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
// Cache miss - compute embedding
|
|
89
|
+
const content = extractIndexableContent(pattern);
|
|
90
|
+
const embeddingArray = await this.embed(content);
|
|
91
|
+
// Store in file cache (convert Float32Array to regular array for JSON serialization)
|
|
92
|
+
await this.cache.set(cacheKey, Array.from(embeddingArray), 86400 * 7); // 7 days TTL
|
|
93
|
+
this.indexedMap.set(cacheKey, {
|
|
94
|
+
pattern,
|
|
95
|
+
embedding: embeddingArray,
|
|
96
|
+
cacheKey,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// Remove patterns no longer in the input set
|
|
101
|
+
for (const key of this.indexedMap.keys()) {
|
|
102
|
+
if (!currentKeys.has(key)) {
|
|
103
|
+
this.indexedMap.delete(key);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Search indexed patterns by semantic similarity.
|
|
109
|
+
* Returns top-K patterns sorted by cosine similarity.
|
|
110
|
+
*/
|
|
111
|
+
async search(query, limit) {
|
|
112
|
+
if (this.indexedMap.size === 0) {
|
|
113
|
+
return [];
|
|
114
|
+
}
|
|
115
|
+
// Generate query embedding
|
|
116
|
+
const queryEmbedding = await this.embed(query);
|
|
117
|
+
// Calculate cosine similarity for each indexed pattern
|
|
118
|
+
const { similarity } = await import('ml-distance');
|
|
119
|
+
const scored = Array.from(this.indexedMap.values()).map(({ pattern, embedding }) => ({
|
|
120
|
+
pattern,
|
|
121
|
+
similarity: similarity.cosine(queryEmbedding, embedding),
|
|
122
|
+
}));
|
|
123
|
+
// Sort by similarity descending and return top-K
|
|
124
|
+
scored.sort((a, b) => b.similarity - a.similarity);
|
|
125
|
+
return scored.slice(0, limit).map(s => s.pattern);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Get the number of indexed patterns (for testing/debugging)
|
|
129
|
+
*/
|
|
130
|
+
get size() {
|
|
131
|
+
return this.indexedMap.size;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=semantic-recall.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"semantic-recall.js","sourceRoot":"","sources":["../../src/utils/semantic-recall.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,+DAA+D;AAE/D,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AASvC,MAAM,CAAC,MAAM,cAAc,GAAyB;IAClD,OAAO,EAAE,KAAK;IACd,eAAe,EAAE,IAAI;IACrB,iBAAiB,EAAE,EAAE;CACtB,CAAC;AAEF;;GAEG;AACH,SAAS,uBAAuB,CAAC,OAAoB;IACnD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;IAClC,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC;IAE5E,8CAA8C;IAC9C,OAAO,GAAG,KAAK,IAAI,OAAO,EAAE,CAAC,IAAI,EAAE,CAAC;AACtC,CAAC;AAED;;;GAGG;AACH,SAAS,cAAc,CAAC,OAAoB;IAC1C,MAAM,OAAO,GAAG,uBAAuB,CAAC,OAAO,CAAC,CAAC;IACjD,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAC7E,CAAC;AAQD,MAAM,OAAO,mBAAmB;IACtB,UAAU,GAAgC,IAAI,GAAG,EAAE,CAAC;IACpD,KAAK,CAAY;IACjB,QAAQ,GAAQ,IAAI,CAAC,CAAC,mCAAmC;IACzD,MAAM,CAAuB;IAErC,YAAY,SAA+B,cAAc;QACvD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,KAAK,GAAG,IAAI,SAAS,CAAC,qBAAqB,CAAC,CAAC;IACpD,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,oBAAoB;QAChC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACnB,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,GAAG,MAAM,MAAM,CAAC,sBAAsB,CAAC,CAAC;YAC/D,GAAG,CAAC,gBAAgB,GAAG,KAAK,CAAC,CAAC,yBAAyB;YACvD,IAAI,CAAC,QAAQ,GAAG,MAAM,QAAQ,CAAC,oBAAoB,EAAE,yBAAyB,CAAC,CAAC;QAClF,CAAC;QACD,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,KAAK,CAAC,IAAY;QAC9B,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,oBAAoB,EAAE,CAAC;QAC/C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACtE,OAAO,MAAM,CAAC,IAAI,CAAC;IACrB,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,KAAK,CAAC,QAAuB;QACjC,qCAAqC;QACrC,MAAM,mBAAmB,GAAG,QAAQ,CAAC,MAAM,CACzC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,cAAc,IAAI,IAAI,CAAC,MAAM,CAAC,iBAAiB,CACvD,CAAC;QAEF,8DAA8D;QAC9D,MAAM,WAAW,GAAG,IAAI,GAAG,EAAU,CAAC;QAEtC,6DAA6D;QAC7D,KAAK,MAAM,OAAO,IAAI,mBAAmB,EAAE,CAAC;YAC1C,MAAM,WAAW,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;YAC5C,MAAM,QAAQ,GAAG,cAAc,OAAO,CAAC,EAAE,KAAK,WAAW,EAAE,CAAC;YAC5D,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YAE1B,iDAAiD;YACjD,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAClC,4EAA4E;gBAC5E,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAE,CAAC;gBAChD,QAAQ,CAAC,OAAO,GAAG,OAAO,CAAC;gBAC3B,SAAS;YACX,CAAC;YAED,8BAA8B;YAC9B,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAW,QAAQ,CAAC,CAAC;YAE3D,IAAI,SAAS,EAAE,CAAC;gBACd,wCAAwC;gBACxC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,QAAQ,EAAE;oBAC5B,OAAO;oBACP,SAAS,EAAE,IAAI,YAAY,CAAC,SAAS,CAAC;oBACtC,QAAQ;iBACT,CAAC,CAAC;YACL,CAAC;iBAAM,CAAC;gBACN,iCAAiC;gBACjC,MAAM,OAAO,GAAG,uBAAuB,CAAC,OAAO,CAAC,CAAC;gBACjD,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;gBAEjD,qFAAqF;gBACrF,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,aAAa;gBAEpF,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,QAAQ,EAAE;oBAC5B,OAAO;oBACP,SAAS,EAAE,cAAc;oBACzB,QAAQ;iBACT,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,6CAA6C;QAC7C,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,CAAC;YACzC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC1B,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC9B,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,MAAM,CAAC,KAAa,EAAE,KAAa;QACvC,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YAC/B,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,2BAA2B;QAC3B,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAE/C,uDAAuD;QACvD,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC;QAEnD,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,CAAC;YACnF,OAAO;YACP,UAAU,EAAE,UAAU,CAAC,MAAM,CAAC,cAAc,EAAE,SAAS,CAAC;SACzD,CAAC,CAAC,CAAC;QAEJ,iDAAiD;QACjD,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC;QAEnD,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IACpD,CAAC;IAED;;OAEG;IACH,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;IAC9B,CAAC;CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"semantic-recall.test.d.ts","sourceRoot":"","sources":["../../src/utils/semantic-recall.test.ts"],"names":[],"mappings":""}
|