@ai2070/l0
Version:
L0: The Missing Reliability Substrate for AI
297 lines (296 loc) • 9.05 kB
JavaScript
import { l0 } from "./runtime/l0";
import { structured } from "./structured";
import {
calculateSimilarityMatrix,
findAgreements,
findDisagreements,
calculateFieldConsensus,
resolveMajority,
resolveBest,
resolveMerge,
meetsMinimumAgreement
} from "./utils/consensusUtils";
async function consensus(options) {
const {
streams,
schema,
strategy = "majority",
threshold = 0.8,
resolveConflicts = "vote",
weights,
minimumAgreement = 0.6,
timeout,
signal,
detectZeroTokens = true,
monitoring,
onComplete,
onConsensus,
metadata
} = options;
if (streams.length < 2) {
throw new Error("Consensus requires at least 2 streams");
}
const startTime = Date.now();
const outputs = [];
const defaultWeights = weights || streams.map(() => 1);
const promises = streams.map(async (streamFactory, index) => {
const outputStartTime = Date.now();
try {
if (schema) {
const result2 = await structured({
schema,
stream: streamFactory,
monitoring,
detectZeroTokens
});
const text = result2.raw || JSON.stringify(result2.data);
return {
index,
text,
data: result2.data,
l0Result: void 0,
structuredResult: result2,
status: "success",
duration: Date.now() - outputStartTime,
weight: defaultWeights[index] ?? 1
};
} else {
const result2 = await l0({
stream: streamFactory,
monitoring,
signal,
detectZeroTokens
});
let text = "";
for await (const event of result2.stream) {
if (event.type === "token" && event.value) {
text += event.value;
}
}
return {
index,
text: text || result2.state.content,
data: void 0,
l0Result: result2,
status: "success",
duration: Date.now() - outputStartTime,
weight: defaultWeights[index] ?? 1
};
}
} catch (error) {
return {
index,
text: "",
data: void 0,
l0Result: void 0,
status: "error",
error: error instanceof Error ? error : new Error(String(error)),
duration: Date.now() - outputStartTime,
weight: defaultWeights[index] ?? 1
};
}
});
const timeoutPromise = timeout ? new Promise(
(_, reject) => setTimeout(() => reject(new Error("Consensus timeout")), timeout)
) : null;
const results = timeoutPromise ? await Promise.race([Promise.all(promises), timeoutPromise]) : await Promise.all(promises);
outputs.push(...results);
if (onComplete) {
await onComplete(outputs);
}
const successfulOutputs = outputs.filter((o) => o.status === "success");
if (successfulOutputs.length === 0) {
throw new Error("All consensus streams failed");
}
const similarityMatrix = calculateSimilarityMatrix(successfulOutputs);
let totalSimilarity = 0;
let comparisons = 0;
let minSimilarity = 1;
let maxSimilarity = 0;
for (let i = 0; i < similarityMatrix.length; i++) {
for (let j = i + 1; j < similarityMatrix.length; j++) {
const sim = similarityMatrix[i]?.[j] ?? 0;
totalSimilarity += sim;
comparisons++;
minSimilarity = Math.min(minSimilarity, sim);
maxSimilarity = Math.max(maxSimilarity, sim);
}
}
const averageSimilarity = comparisons > 0 ? totalSimilarity / comparisons : 1;
const agreements = findAgreements(successfulOutputs, threshold);
const disagreements = findDisagreements(successfulOutputs, threshold);
if (!meetsMinimumAgreement(
agreements,
successfulOutputs.length,
minimumAgreement
)) {
if (resolveConflicts === "fail") {
throw new Error(
`Consensus failed: agreement ratio ${agreements[0]?.ratio || 0} below minimum ${minimumAgreement}`
);
}
}
let consensusOutput;
switch (strategy) {
case "majority":
consensusOutput = resolveMajority(successfulOutputs, defaultWeights);
break;
case "unanimous":
if (averageSimilarity < 0.95) {
if (resolveConflicts === "fail") {
throw new Error("Unanimous consensus failed: outputs differ");
}
consensusOutput = resolveMajority(successfulOutputs, defaultWeights);
} else {
consensusOutput = successfulOutputs[0];
}
break;
case "weighted":
if (!weights) {
throw new Error("Weighted strategy requires weights to be provided");
}
consensusOutput = resolveMajority(successfulOutputs, weights);
break;
case "best":
consensusOutput = resolveBest(successfulOutputs, defaultWeights);
break;
default:
consensusOutput = resolveMajority(successfulOutputs, defaultWeights);
}
if (disagreements.length > 0 && resolveConflicts !== "vote") {
switch (resolveConflicts) {
case "merge":
consensusOutput = resolveMerge(successfulOutputs);
break;
case "best":
consensusOutput = resolveBest(successfulOutputs, defaultWeights);
break;
case "fail":
throw new Error(
`Consensus failed: ${disagreements.length} disagreements found`
);
}
}
disagreements.forEach((d) => {
d.resolution = resolveConflicts;
d.resolutionConfidence = averageSimilarity;
});
const fieldConsensus = schema ? calculateFieldConsensus(successfulOutputs) : void 0;
const confidence = calculateConfidence(
successfulOutputs,
agreements,
disagreements,
averageSimilarity,
strategy
);
const identicalOutputs = countIdenticalOutputs(successfulOutputs);
const analysis = {
totalOutputs: outputs.length,
successfulOutputs: successfulOutputs.length,
failedOutputs: outputs.length - successfulOutputs.length,
identicalOutputs,
similarityMatrix,
averageSimilarity,
minSimilarity,
maxSimilarity,
totalAgreements: agreements.length,
totalDisagreements: disagreements.length,
strategy,
conflictResolution: resolveConflicts,
duration: Date.now() - startTime
};
const status = successfulOutputs.length === outputs.length ? "success" : successfulOutputs.length > 0 ? "partial" : "failed";
const result = {
consensus: schema ? consensusOutput.data : consensusOutput.text,
confidence,
outputs,
agreements,
disagreements,
analysis,
type: schema ? "structured" : "text",
fieldConsensus,
status,
metadata
};
if (onConsensus) {
await onConsensus(result);
}
return result;
}
function calculateConfidence(outputs, agreements, disagreements, averageSimilarity, strategy) {
if (outputs.length === 1) return 1;
let confidence = averageSimilarity;
if (agreements.length > 0) {
const maxAgreementRatio = Math.max(...agreements.map((a) => a.ratio));
confidence = (confidence + maxAgreementRatio) / 2;
}
if (disagreements.length > 0) {
const majorDisagreements = disagreements.filter(
(d) => d.severity === "major" || d.severity === "critical"
).length;
const penalty = majorDisagreements * 0.1;
confidence = Math.max(0, confidence - penalty);
}
if (strategy === "unanimous" && averageSimilarity > 0.95) {
confidence = Math.min(1, confidence + 0.1);
}
return Math.max(0, Math.min(1, confidence));
}
function countIdenticalOutputs(outputs) {
if (outputs.length === 0) return 0;
const first = outputs[0].text;
return outputs.filter((o) => o.text === first).length;
}
function quickConsensus(outputs, threshold = 0.8) {
if (outputs.length < 2) return true;
const counts = /* @__PURE__ */ new Map();
outputs.forEach((output) => {
counts.set(output, (counts.get(output) || 0) + 1);
});
const maxCount = Math.max(...Array.from(counts.values()));
const ratio = maxCount / outputs.length;
return ratio >= threshold;
}
function getConsensusValue(outputs) {
if (outputs.length === 0) {
throw new Error("No outputs to get consensus from");
}
const counts = /* @__PURE__ */ new Map();
outputs.forEach((output) => {
const key = JSON.stringify(output);
const existing = counts.get(key);
if (existing) {
existing.count++;
} else {
counts.set(key, { value: output, count: 1 });
}
});
let maxCount = 0;
let consensusValue = outputs[0];
for (const { value, count } of counts.values()) {
if (count > maxCount) {
maxCount = count;
consensusValue = value;
}
}
return consensusValue;
}
function validateConsensus(result, minConfidence = 0.8, maxDisagreements = 0) {
if (result.confidence < minConfidence) {
return false;
}
const criticalDisagreements = result.disagreements.filter(
(d) => d.severity === "major" || d.severity === "critical"
).length;
if (criticalDisagreements > maxDisagreements) {
return false;
}
return true;
}
export {
consensus,
getConsensusValue,
quickConsensus,
validateConsensus
};
//# sourceMappingURL=consensus.js.map