Skip to main content

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

genes
Gene[]
required
Array of available Gene objects
capsules
Capsule[]
required
Array of available Capsule objects
signals
string[]
required
Current signals extracted from logs
memoryAdvice
object
Memory graph advice with preferred and banned gene IDs
driftEnabled
boolean
default:false
Enable random drift mode for exploration
failedCapsules
object[]
Recent failed capsules to avoid repeating

Return Value

selectedGene
Gene
The selected Gene object (or null if no match)
capsuleCandidates
Capsule[]
Array of matching capsules
selector
object
Selection decision metadata with reasoning
driftIntensity
number
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;
}

Selection Decision Metadata

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);

Build docs developers (and LLMs) love