Overview
The selectGeneAndCapsule() function implements the core selection logic for matching signals to genes and capsules. It uses pattern matching, memory graph advice, and drift parameters to select the best gene for the current context.
Location: src/gep/selector.js:185
Function Signature
function selectGeneAndCapsule({ genes, capsules, signals, memoryAdvice, driftEnabled, failedCapsules })
Parameters
Array of available Gene objects
Array of available Capsule objects
Current signals extracted from logs
Memory graph advice with preferred and banned gene IDs
Enable random drift mode for exploration
Recent failed capsules to avoid repeating
Return Value
The selected Gene object (or null if no match)
Array of matching capsules
Selection decision metadata with reasoning
Computed drift intensity (0-1 scale)
Pattern Matching
Location: src/gep/selector.js:1
Basic Pattern Matching
function matchPatternToSignals(pattern, signals) {
const p = String(pattern);
const sig = signals.map(s => String(s));
// Regex pattern: /body/flags
if (p.startsWith('/') && p.lastIndexOf('/') > 0) {
const lastSlash = p.lastIndexOf('/');
const body = p.slice(1, lastSlash);
const flags = p.slice(lastSlash + 1);
const re = new RegExp(body, flags || 'i');
return sig.some(s => re.test(s));
}
// Multi-language alias: "en_term|zh_term|ja_term"
if (p.includes('|') && !p.startsWith('/')) {
const branches = p.split('|').map(b => b.trim().toLowerCase());
return branches.some(needle => sig.some(s => s.toLowerCase().includes(needle)));
}
// Substring match
const needle = p.toLowerCase();
return sig.some(s => s.toLowerCase().includes(needle));
}
Gene Scoring
Location: src/gep/selector.js:30
function scoreGene(gene, signals) {
if (!gene || gene.type !== 'Gene') return 0;
const patterns = Array.isArray(gene.signals_match) ? gene.signals_match : [];
if (patterns.length === 0) return 0;
let score = 0;
for (const pat of patterns) {
if (matchPatternToSignals(pat, signals)) score += 1;
}
return score;
}
Drift Mechanics
Location: src/gep/selector.js:41
Drift Intensity Calculation
function computeDriftIntensity(opts) {
const driftEnabled = !!(opts && opts.driftEnabled);
const ne = opts.effectivePopulationSize || opts.genePoolSize || null;
if (driftEnabled) {
// Explicit drift: moderate-to-high intensity
return ne && ne > 1 ? Math.min(1, 1 / Math.sqrt(ne) + 0.3) : 0.7;
}
if (ne != null && ne > 0) {
// Population-dependent drift: small population = more drift
// Ne=1: intensity=1.0 (pure drift)
// Ne=25: intensity=0.2
// Ne=100: intensity=0.1
return Math.min(1, 1 / Math.sqrt(ne));
}
return 0; // No drift info available, pure selection
}
Stochastic Selection
Location: src/gep/selector.js:124
// Stochastic selection under drift
var selectedIdx = 0;
if (driftIntensity > 0 && filtered.length > 1 && Math.random() < driftIntensity) {
// Weighted random selection from top candidates
var topN = Math.min(filtered.length, Math.max(2, Math.ceil(filtered.length * driftIntensity)));
selectedIdx = Math.floor(Math.random() * topN);
}
return {
selected: filtered[selectedIdx].gene,
alternatives: filtered.filter((_, i) => i !== selectedIdx).slice(0, 4),
driftIntensity,
};
Failed Capsule Filtering
Location: src/gep/selector.js:161
const FAILED_CAPSULE_BAN_THRESHOLD = 2;
const FAILED_CAPSULE_OVERLAP_MIN = 0.6;
function banGenesFromFailedCapsules(failedCapsules, signals, existingBans) {
var bans = existingBans instanceof Set ? new Set(existingBans) : new Set();
var geneFailCounts = {};
for (var fc of failedCapsules) {
var overlap = computeSignalOverlap(signals, fc.trigger || []);
if (overlap < FAILED_CAPSULE_OVERLAP_MIN) continue;
var gid = String(fc.gene);
geneFailCounts[gid] = (geneFailCounts[gid] || 0) + 1;
}
for (var [gid, count] of Object.entries(geneFailCounts)) {
if (count >= FAILED_CAPSULE_BAN_THRESHOLD) {
bans.add(gid);
}
}
return bans;
}
Memory Advice Integration
Location: src/gep/selector.js:105
if (preferredGeneId) {
const preferred = scored.find(x => x.gene && x.gene.id === preferredGeneId);
if (preferred && (useDrift || !bannedGeneIds.has(preferredGeneId))) {
const rest = scored.filter(x => x.gene && x.gene.id !== preferredGeneId);
const filteredRest = useDrift ? rest : rest.filter(x => !bannedGeneIds.has(x.gene.id));
return {
selected: preferred.gene,
alternatives: filteredRest.slice(0, 4).map(x => x.gene),
driftIntensity,
};
}
}
Capsule Selection
Location: src/gep/selector.js:138
function selectCapsule(capsules, signals) {
const scored = (capsules || [])
.map(c => {
const triggers = Array.isArray(c.trigger) ? c.trigger : [];
const score = triggers.reduce((acc, t) =>
(matchPatternToSignals(t, signals) ? acc + 1 : acc), 0
);
return { capsule: c, score };
})
.filter(x => x.score > 0)
.sort((a, b) => b.score - a.score);
return scored.length ? scored[0].capsule : null;
}
Location: src/gep/selector.js:219
function buildSelectorDecision({ gene, capsule, signals, alternatives, memoryAdvice, driftEnabled, driftIntensity }) {
const reason = [];
if (gene) reason.push('signals match gene.signals_match');
if (capsule) reason.push('capsule trigger matches signals');
if (!gene) reason.push('no matching gene found; new gene may be required');
if (signals && signals.length) reason.push(`signals: ${signals.join(', ')}`);
if (memoryAdvice && Array.isArray(memoryAdvice.explanation)) {
reason.push(`memory_graph: ${memoryAdvice.explanation.join(' | ')}`);
}
if (driftEnabled) {
reason.push('random_drift_override: true');
}
if (Number.isFinite(driftIntensity) && driftIntensity > 0) {
reason.push(`drift_intensity: ${driftIntensity.toFixed(3)}`);
}
return {
selected: gene ? gene.id : null,
reason,
alternatives: Array.isArray(alternatives) ? alternatives.map(g => g.id) : [],
};
}
Example Usage
const genes = loadGenes();
const capsules = loadCapsules();
const signals = ['log_error', 'errsig:TypeError: undefined'];
const memoryAdvice = getMemoryAdvice({ signals, genes, driftEnabled: false });
const { selectedGene, capsuleCandidates, selector, driftIntensity } = selectGeneAndCapsule({
genes,
capsules,
signals,
memoryAdvice,
driftEnabled: false,
failedCapsules: [],
});
console.log('Selected gene:', selectedGene.id);
console.log('Reason:', selector.reason);
console.log('Alternatives:', selector.alternatives);
console.log('Drift intensity:', driftIntensity);