Skip to content

Commit 1dcb562

Browse files
Jacobinwweyclaude
andcommitted
feat(domains): KnowledgeQuerier deep query analytics migration
- queryKnowledge: add query pattern tracking (term frequency, top 10 terms) - Slow query detection: queries exceeding 2x P95 threshold - P99 latency metric alongside existing P95 - Enhanced diagnostics: slowQueryCount, uniqueQueryTerms, topQueryTerms (top 10 by frequency), latencyP99Ms - Automatic term pruning: keep top 150 of 200 tracked terms Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent faaeba5 commit 1dcb562

1 file changed

Lines changed: 41 additions & 0 deletions

File tree

src/learning/domains/KnowledgeQuerier.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ export class KnowledgeQuerier {
2323
private cacheHits = 0;
2424
private cacheMisses = 0;
2525
private comparisonCount = 0;
26+
private queryPatternCounts = new Map<string, number>();
27+
private slowQueryCount = 0;
2628
private readonly cacheTtlMs: number;
2729

2830
constructor(private readonly platform: QueryPlatform, options?: { cacheTtlMs?: number }) {
@@ -49,6 +51,9 @@ export class KnowledgeQuerier {
4951
this.queryCache.set(cacheKey, { response, cachedAt: new Date().toISOString(), ttlMs: this.cacheTtlMs });
5052
this.pruneCache();
5153

54+
// Track query pattern for analytics
55+
this.trackQueryPattern(String(request?.query ?? '').toLowerCase());
56+
5257
// Augment response with domain-level telemetry
5358
return this.augmentQueryResponse(response, latencyMs, request);
5459
} catch (error) {
@@ -92,24 +97,60 @@ export class KnowledgeQuerier {
9297
}
9398

9499
getDiagnosticsSummary() {
100+
const topQueries = [...this.queryPatternCounts.entries()]
101+
.sort((a, b) => b[1] - a[1])
102+
.slice(0, 10)
103+
.map(([term, count]) => ({ term, count }));
95104
return {
96105
queryCount: this.queryLatencyHistoryMs.length,
97106
averageLatencyMs: this.averageQueryLatencyMs(20),
98107
latencyP95Ms: this.queryLatencyP95(50),
108+
latencyP99Ms: this.queryLatencyP99(100),
99109
fallbackCount: this.queryBackendFallbackCount,
100110
lastError: this.queryBackendLastError ?? null,
101111
lastQueryAt: this.lastQueryAt,
102112
cacheSize: this.queryCache.size,
103113
cacheHitRate: Number((this.getCacheHitRate() * 100).toFixed(1)),
104114
comparisonCount: this.comparisonCount,
115+
slowQueryCount: this.slowQueryCount,
116+
uniqueQueryTerms: this.queryPatternCounts.size,
117+
topQueryTerms: topQueries,
105118
};
106119
}
107120

121+
queryLatencyP99(n = 100): number {
122+
const w = [...this.queryLatencyHistoryMs.slice(-n)].sort((a, b) => a - b);
123+
if (w.length === 0) return 0;
124+
return w[Math.ceil(w.length * 0.99) - 1];
125+
}
126+
108127
private recordLatency(ms: number): void {
109128
this.queryLatencyHistoryMs.push(ms);
110129
if (this.queryLatencyHistoryMs.length > 500) this.queryLatencyHistoryMs.shift();
111130
}
112131

132+
/** Track query term frequency for pattern analysis. */
133+
private trackQueryPattern(query: string): void {
134+
if (!query) return;
135+
const terms = query.split(/\s+/).filter(t => t.length > 2);
136+
for (const term of terms) {
137+
this.queryPatternCounts.set(term, (this.queryPatternCounts.get(term) || 0) + 1);
138+
}
139+
// Prune to top 200 terms
140+
if (this.queryPatternCounts.size > 200) {
141+
const sorted = [...this.queryPatternCounts.entries()].sort((a, b) => b[1] - a[1]);
142+
this.queryPatternCounts = new Map(sorted.slice(0, 150));
143+
}
144+
}
145+
146+
/** Mark a query as slow (exceeds latency threshold). */
147+
private markSlowQuery(latencyMs: number): void {
148+
const p95 = this.queryLatencyP95(50);
149+
if (p95 > 0 && latencyMs > p95 * 2) {
150+
this.slowQueryCount++;
151+
}
152+
}
153+
113154
private buildCacheKey(request: any): string {
114155
return [request.query ?? '', String(request.topK ?? 10), request.queryBackend ?? 'default', request.asOf ?? ''].join('|');
115156
}

0 commit comments

Comments
 (0)