| name | openevidence-performance-tuning |
| description | Optimize OpenEvidence clinical query performance and response times.
Use when improving latency, optimizing query efficiency,
or tuning caching for clinical AI applications.
Trigger with phrases like "openevidence performance", "openevidence slow",
"optimize openevidence", "openevidence latency", "speed up clinical queries".
|
| allowed-tools | Read, Write, Edit |
| version | 1.0.0 |
| license | MIT |
| author | Jeremy Longshore <jeremy@intentsolutions.io> |
OpenEvidence Performance Tuning
Overview
Optimize OpenEvidence clinical query performance for point-of-care response times.
Prerequisites
- OpenEvidence integration running
- Monitoring configured (see
openevidence-observability)
- Understanding of caching strategies
- Access to performance metrics
Performance Targets
| Metric | Target | Critical |
|---|
| Clinical Query P50 | < 3s | > 10s |
| Clinical Query P95 | < 8s | > 15s |
| Clinical Query P99 | < 15s | > 30s |
| DeepConsult Start | < 5s | > 15s |
| Cache Hit Rate | > 70% | < 50% |
Instructions
Step 1: Query Optimization
function optimizeQuestion(question: string): string {
let optimized = question
.replace(/\b(please|can you|could you|I want to know)\b/gi, '')
.replace(/\s+/g, ' ')
.trim();
if (!optimized.endsWith('?')) {
optimized += '?';
}
return optimized;
}
function optimizeContext(context: ClinicalContext): ClinicalContext {
return {
specialty: context.specialty,
urgency: context.urgency,
...(context.patientAge && { patientAge: context.patientAge }),
...(context.patientSex && { patientSex: context.patientSex }),
...(context.relevantConditions && {
relevantConditions: context.relevantConditions.(, ),
}),
...(context. && {
: context..(, ),
}),
};
}
= {
: ,
: ,
: ,
: ,
};
Step 2: Intelligent Caching Layer
import { createHash } from 'crypto';
import Redis from 'ioredis';
interface CacheConfig {
redis: Redis;
defaultTTL: number;
maxEntries: number;
}
interface CachedResponse {
data: any;
cachedAt: number;
ttl: number;
hitCount: number;
}
export class ClinicalQueryCache {
private redis: Redis;
private defaultTTL: number;
private prefix = 'oe:query:';
constructor(config: CacheConfig) {
this.redis = config.redis;
this.defaultTTL = config.defaultTTL;
}
private generateKey(question: string, context: ): {
normalized = .({
: question.().(),
: context.,
: context.,
});
. + ().(normalized).().(, );
}
(: , : ): {
lowerQuestion = question.();
(context. === || context. === ) {
;
}
(lowerQuestion.() ||
lowerQuestion.() ||
lowerQuestion.()) {
;
}
(lowerQuestion.() ||
lowerQuestion.() ||
lowerQuestion.()) {
;
}
.;
}
(: , : ): < | > {
key = .(question, context);
cached = ..(key);
(!cached) ;
: = .(cached);
entry.++;
..(key, entry., .(entry));
entry.;
}
(
: ,
: ,
:
): <> {
key = .(question, context);
ttl = .(question, context);
: = {
data,
: .(),
ttl,
: ,
};
..(key, ttl, .(entry));
}
(?: ): <> {
keys = ..(. + (pattern || ));
(keys. === ) ;
..(...keys);
}
(): <{
: ;
: ;
: ;
}> {
keys = ..(. + );
info = ..();
totalHits = ;
totalEntries = ;
( key keys.(, )) {
cached = ..(key);
(cached) {
: = .(cached);
totalHits += entry.;
totalEntries++;
}
}
{
: keys.,
: info.()?.[] || ,
: totalEntries > ? totalHits / totalEntries : ,
};
}
}
Step 3: Connection Pooling & Keep-Alive
import { OpenEvidenceClient } from '@openevidence/sdk';
import { Agent } from 'https';
const httpsAgent = new Agent({
keepAlive: true,
keepAliveMsecs: 30000,
maxSockets: 10,
maxFreeSockets: 5,
});
export const optimizedClient = new OpenEvidenceClient({
apiKey: process.env.OPENEVIDENCE_API_KEY!,
orgId: process.env.OPENEVIDENCE_ORG_ID!,
httpAgent: httpsAgent,
timeout: 30000,
});
export async function warmupConnections(): Promise<void> {
console.log('[Performance] Warming up OpenEvidence connections...');
try {
await optimizedClient.health.check();
.();
} (error) {
.(, error);
}
}
Step 4: Request Batching
import DataLoader from 'dataloader';
const queryBatcher = new DataLoader<
{ question: string; context: ClinicalContext },
ClinicalQueryResponse
>(
async (queries) => {
const results = await Promise.all(
queries.map(q => optimizedClient.query({
question: q.question,
context: q.context,
}))
);
return results;
},
{
maxBatchSize: 5,
batchScheduleFn: (callback) => setTimeout(callback, 50),
cacheKeyFn: (query) => `${query.question}:${query.context.specialty}`,
}
);
export async function (): <> {
queryBatcher.({ question, context });
}
Step 5: Response Streaming (When Available)
export async function streamingClinicalQuery(
question: string,
context: ClinicalContext,
onPartialResponse: (partial: string) => void
): Promise<ClinicalQueryResponse> {
if (optimizedClient.supportsStreaming?.()) {
const stream = await optimizedClient.query.stream({
question,
context,
});
let fullAnswer = '';
for await (const chunk of stream) {
fullAnswer += chunk.text;
onPartialResponse(fullAnswer);
}
return stream.finalResponse;
}
const response = await optimizedClient.query({ question, context });
onPartialResponse(response.answer);
return response;
}
Step 6: Performance Monitoring
import { Histogram, Counter, Gauge } from 'prom-client';
const queryLatency = new Histogram({
name: 'openevidence_query_duration_seconds',
help: 'Clinical query latency',
labelNames: ['specialty', 'urgency', 'cached'],
buckets: [0.5, 1, 2, 5, 10, 15, 30],
});
const cacheHits = new Counter({
name: 'openevidence_cache_hits_total',
help: 'Cache hit count',
});
const cacheMisses = new Counter({
name: 'openevidence_cache_misses_total',
help: 'Cache miss count',
});
const queueSize = new Gauge({
name: 'openevidence_queue_size',
help: 'Current request queue size',
labelNames: ['priority'],
});
(): <> {
timer = queryLatency.({
: context.,
: context.,
});
cached = cache.(question, context);
(cached) {
cacheHits.();
({ : });
cached;
}
cacheMisses.();
response = optimizedClient.({ question, context });
cache.(question, context, response);
({ : });
response;
}
Performance Checklist
Output
- Optimized query construction
- Intelligent caching with TTL management
- Connection pooling for efficiency
- Performance metrics and monitoring
Error Handling
| Performance Issue | Detection | Resolution |
|---|
| High P95 latency | Metrics alert | Check cache hit rate, optimize queries |
| Low cache hit rate | < 50% hits | Review TTL strategy, increase cache size |
| Connection timeouts | Timeout errors | Check keep-alive, increase pool size |
| Memory pressure | Redis alerts | Implement LRU eviction |
Resources
Next Steps
For cost optimization, see openevidence-cost-tuning.