Quick start
Collect trustworthy examples, train a candidate deliberately, and promote it only after review.
SwapAI lets an application learn from a classifier it already trusts. Collection stays in the application. Training can run locally, on Runpod, or through another provider. A candidate never replaces the trusted classifier until you explicitly promote it.
Install
SwapAI requires Node.js 22.13 or newer.
npm install @swapai/coreThe package and source are public at npm and GitHub.
Create a classifier
import { createClassifier } from "@swapai/core";
const relevance = createClassifier({
name: "accountant-relevance",
result: { type: "number", min: 0, max: 1 },
reference: async (input) => (await callGrok(input)).score,
decisionBoundaries: [0.5],
facets: ["documentFamily"],
acceptableError: "10%",
dataDirectory: "/var/lib/my-service/swapai",
});Classify and collect
const score = await relevance.classify(input, {
documentFamily: "invoice",
});Until an accepted model exists, classify() calls reference, returns its answer, and records the input, result, result bin and declared facets. Once a model is promoted, the same call uses it.
Pass the complete classifier input
Include the instruction and context that define the task, not only the document text. The candidate must receive the same task the reference received.
Inspect before spending money
const inspection = relevance.inspect();
if (!inspection.readyForTraining) {
console.table(inspection.deficits);
}Readiness requires useful coverage across training, validation, representative-test and coverage-test data. A large total count cannot hide an empty result bin or missing protected set. Declared facet coverage remains visible for operator review.
Train a candidate
Training is an explicit command or operator action. Configure a provider, then request one bounded run:
const result = await relevance.requestTraining();
if (result.status !== "candidate") {
return;
}requestTraining() returns deficits when the dataset is not ready. Otherwise it produces and evaluates a candidate. It does not promote it.
Promote deliberately
Review the protected and fresh shadow evidence, then promote that exact run:
await relevance.classify(freshInput, {
documentFamily: "invoice",
});
await relevance.flush();
await relevance.promoteCandidate(result.trainingRunId);Promotion requires passing protected evaluation and passing fresh shadow evidence. Keep the reference authoritative until this call succeeds. See Training and Candidates and promotion for the complete release path.
Shut down cleanly
process.on("SIGTERM", async () => {
await relevance.close();
process.exit(0);
});close() finishes queued storage work and releases the database. It does not wait indefinitely for an external trainer.