@ai2070/l0
Version:
L0: The Missing Reliability Substrate for AI
226 lines • 7.83 kB
JavaScript
import { l0 } from "./runtime/l0";
export async function pipe(steps, input, options = {}) {
const { name, stopOnError = true, timeout, signal, monitoring, onStart, onComplete, onError, onProgress, metadata = {}, } = options;
const startTime = Date.now();
const stepResults = [];
let currentInput = input;
let finalOutput = input;
let pipelineError;
let pipelineStatus = "success";
let timeoutId;
const timeoutPromise = timeout
? new Promise((_, reject) => {
timeoutId = setTimeout(() => reject(new Error(`Pipeline timeout after ${timeout}ms`)), timeout);
})
: null;
try {
if (onStart) {
await onStart(input);
}
for (let i = 0; i < steps.length; i++) {
const step = steps[i];
const stepStartTime = Date.now();
if (signal?.aborted) {
throw new Error("Pipeline aborted");
}
const context = {
stepIndex: i,
totalSteps: steps.length,
previousResults: stepResults,
metadata,
signal,
};
if (onProgress) {
await onProgress(i, steps.length);
}
if (step.condition) {
const shouldRun = await step.condition(currentInput, context);
if (!shouldRun) {
stepResults.push({
stepName: step.name,
stepIndex: i,
input: currentInput,
output: currentInput,
l0Result: undefined,
status: "skipped",
duration: Date.now() - stepStartTime,
startTime: stepStartTime,
endTime: Date.now(),
});
continue;
}
}
try {
const l0Options = await step.fn(currentInput, context);
const executeStep = async () => {
const result = await l0({
...l0Options,
signal,
monitoring,
});
let content = "";
for await (const event of result.stream) {
if (event.type === "token" && event.value) {
content += event.value;
}
}
return {
...result,
state: {
...result.state,
content: content || result.state.content,
},
};
};
const l0Result = timeoutPromise
? await Promise.race([executeStep(), timeoutPromise])
: await executeStep();
const stepOutput = step.transform
? await step.transform(l0Result, context)
: l0Result.state.content;
const stepResult = {
stepName: step.name,
stepIndex: i,
input: currentInput,
output: stepOutput,
l0Result,
status: "success",
duration: Date.now() - stepStartTime,
startTime: stepStartTime,
endTime: Date.now(),
};
stepResults.push(stepResult);
if (step.onComplete) {
await step.onComplete(stepResult, context);
}
currentInput = stepOutput;
finalOutput = stepOutput;
}
catch (error) {
const stepError = error instanceof Error ? error : new Error(String(error));
const stepResult = {
stepName: step.name,
stepIndex: i,
input: currentInput,
output: undefined,
l0Result: undefined,
status: "error",
error: stepError,
duration: Date.now() - stepStartTime,
startTime: stepStartTime,
endTime: Date.now(),
};
stepResults.push(stepResult);
if (step.onError) {
await step.onError(stepError, context);
}
if (onError) {
await onError(stepError, i);
}
if (stopOnError) {
pipelineError = stepError;
pipelineStatus = "error";
break;
}
else {
pipelineStatus = "partial";
}
}
}
}
catch (error) {
pipelineError = error instanceof Error ? error : new Error(String(error));
pipelineStatus = "error";
}
finally {
if (timeoutId) {
clearTimeout(timeoutId);
}
}
const result = {
name,
output: finalOutput,
steps: stepResults,
status: pipelineStatus,
error: pipelineError,
duration: Date.now() - startTime,
startTime,
endTime: Date.now(),
metadata,
};
if (onComplete) {
await onComplete(result);
}
return result;
}
export function createPipeline(steps, options = {}) {
const pipelineSteps = [...steps];
const pipelineOptions = { ...options };
const pipeline = {
name: options.name,
steps: pipelineSteps,
options: pipelineOptions,
async run(input) {
return pipe(pipelineSteps, input, pipelineOptions);
},
addStep(step) {
pipelineSteps.push(step);
return pipeline;
},
removeStep(name) {
const index = pipelineSteps.findIndex((s) => s.name === name);
if (index !== -1) {
pipelineSteps.splice(index, 1);
}
return pipeline;
},
getStep(name) {
return pipelineSteps.find((s) => s.name === name);
},
clone() {
return createPipeline(pipelineSteps.map((s) => ({ ...s })), { ...pipelineOptions });
},
};
return pipeline;
}
export function createStep(name, promptFn, streamFactory) {
return {
name,
fn: (input) => ({
stream: () => streamFactory(promptFn(input)),
}),
};
}
export function chainPipelines(...pipelines) {
const allSteps = [];
for (const p of pipelines) {
allSteps.push(...p.steps);
}
return createPipeline(allSteps, {
name: pipelines.map((p) => p.name).join(" -> "),
});
}
export async function parallelPipelines(pipelines, input, combiner) {
const results = await Promise.all(pipelines.map((p) => p.run(input)));
return combiner(results);
}
export function createBranchStep(name, condition, ifTrue, ifFalse) {
const branchByContext = new WeakMap();
return {
name,
fn: async (input, context) => {
const result = await condition(input, context);
const step = result ? ifTrue : ifFalse;
branchByContext.set(context, step);
return step.fn(input, context);
},
transform: async (result, context) => {
const step = branchByContext.get(context) ?? ifTrue;
if (step.transform) {
return step.transform(result, context);
}
return result.state.content;
},
};
}
//# sourceMappingURL=pipeline.js.map