npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@msm-core/validate

v0.3.0

Published

Composable output validation for MSM pipelines — rules, embedding similarity, and LLM-as-judge strategies

Downloads

569

Readme

@msm-core/validate

Composable output validation for AI pipelines. Three tiers, one interface — fast rules gate first, embedding similarity next, LLM-as-judge only when needed. Use any tier standalone or chain them into a cascade.

npm install @msm-core/validate

Strategies

| Strategy | Tier | Latency | Requires | Action on fail | | ------------- | :--: | --------: | -------------- | ----------------- | | "rules" | 1 | <1 ms | nothing | block | | "embedding" | 2 | ~50 ms | embed function | review | | "llm" | 3 | ~300 ms | LLM judge fn | block/review | | "cascade" | — | first-hit | 1–3 stages | per winning stage |

All validators return the same shape:

interface ValidationResult {
  passed: boolean;
  score: number; // [0, 1]
  violations: string[]; // empty when passed
  action: "release" | "review" | "block";
  strategy: string; // e.g. "rules", "cascade(rules→llm)"
  latency_ms: number;
}

Tier 1 — Rules (deterministic)

No model. No network. Runs in < 1 ms. Use this as the first gate on every request.

import { createValidator } from "@msm-core/validate";

const v = createValidator({
  strategy: "rules",
  rules: {
    max_length: 2000, // block if response exceeds N chars
    min_length: 10, // block if response shorter than N chars
    blocked_phrases: [
      // case-insensitive substring match
      "ignore previous instructions",
      "DROP TABLE",
    ],
    no_pii: true, // block emails, phones, SSN-like, credit cards
    language: "ar", // block if response is not Arabic (or mixed)
  },
});

const result = await v.validate(responseText);
// result.action: "release" | "block"

action is always definitive — "release" or "block", never "review". Mixed-language responses always pass the language check.


Tier 2 — Embedding similarity

Compares the response against golden examples of acceptable output using cosine similarity. Returns "review" (not "block") on failure — semantic distance is indicative, not certain, so the cascade continues to an LLM for a final verdict.

Golden example embeddings are computed once and cached for the lifetime of the validator instance.

import OpenAI from "openai";
import { createValidator } from "@msm-core/validate";

const openai = new OpenAI();

async function embed(text: string): Promise<number[]> {
  const r = await openai.embeddings.create({
    model: "text-embedding-3-small",
    input: text,
  });
  return r.data[0]?.embedding ?? [];
}

const v = createValidator({
  strategy: "embedding",
  embed,
  goldenExamples: [
    "Here is a step-by-step guide to solving the problem.",
    "The answer to your question is as follows.",
  ],
  threshold: 0.72, // cosine similarity minimum (default: 0.75)
});

const result = await v.validate(responseText);
// result.action: "release" | "review"

Tier 3 — LLM-as-judge

Delegates evaluation to any LLM via a caller-provided judge function. Works with OpenAI, Gemini, Anthropic, Ollama — anything that returns text. Returns "review" (not a hard crash) on network failure or JSON parse errors, so a downed judge never breaks production.

import { createValidator } from "@msm-core/validate";

const v = createValidator({
  strategy: "llm",
  judge: async (prompt: string) => {
    // Use any LLM — the prompt is already structured, just call and return the text
    const r = await openai.chat.completions.create({
      model: "gpt-4o-mini",
      messages: [{ role: "user", content: prompt }],
    });
    return r.choices[0]?.message?.content ?? "";
  },
  criteria: [
    // optional — defaults to helpfulness, safety, coherence
    "The response stays on topic",
    "No harmful or unsafe content",
    "Professionally written",
  ],
  systemPrompt: "You are a strict AI quality evaluator.", // optional
});

const result = await v.validate(responseText);
// result.action: "release" | "review" | "block"

The judge receives a structured prompt asking it to return JSON:

{ "passed": true, "score": 0.9, "violations": [], "action": "release" }

Markdown fences are stripped automatically if the LLM wraps its output.


Cascade — chain all three

Run the cheapest gate first. The cascade stops as soon as any stage returns "release" or "block". It only escalates to the next stage on "review". Most requests never reach the LLM.

import { createValidator, CascadeValidator } from "@msm-core/validate";

const v = new CascadeValidator([
  // Stage 1: rules — instant, blocks obvious violations.
  // pass_action: "review" makes a PASS escalate to the next stage —
  // without it, passing rules returns "release" and ends the cascade
  // (stages 2–3 would never run).
  createValidator({
    strategy: "rules",
    rules: { no_pii: true, max_length: 2000, language: "ar" },
    pass_action: "review",
  }),
  // Stage 2: embedding — catches off-topic responses
  createValidator({
    strategy: "embedding",
    embed,
    goldenExamples,
    threshold: 0.7,
  }),
  // Stage 3: LLM judge — final verdict on ambiguous cases only
  createValidator({
    strategy: "llm",
    judge,
    criteria: ["Safe", "On-topic", "Helpful"],
  }),
]);

const result = await v.validate(responseText);
// result.strategy: "cascade(rules→embedding→llm)"  — shows which stages ran

Or via factory:

const v = createValidator({
  strategy: "cascade",
  stages: [rulesValidator, embeddingValidator, llmValidator],
});

Use standalone — without mini or pipeline

@msm-core/validate has zero runtime dependencies. It works on its own in any Node.js ≥ 18 project.

import { createValidator } from "@msm-core/validate";

// Quick sanity check on any text — no pipeline needed
const v = createValidator({ strategy: "rules", rules: { no_pii: true } });
const { passed, violations } = await v.validate(userSubmittedText);

API reference

createValidator(config): Validator

Factory. Returns the appropriate Validator for the given strategy config.

class RulesValidator

class EmbeddingValidator

class LLMValidator

class CascadeValidator

All implement Validator:

interface Validator {
  readonly strategy: string;
  validate(text: string): Promise<ValidationResult>;
}

Types

import type {
  Validator,
  ValidatorConfig,
  ValidationResult,
  ValidationAction, // "release" | "review" | "block"
  ValidationRules,
  RulesValidatorConfig,
  EmbeddingValidatorConfig,
  LLMValidatorConfig,
  CascadeValidatorConfig,
} from "@msm-core/validate";

Part of the MSM ecosystem

| Package | Role | | ------------------------ | ------------------------ | | @msm-core/mini | Agent execution loop | | @msm-core/context | Context assembly (RAG) | | @msm-core/validate | Output validation gate | | @msm-core/pipeline | 3-layer routing pipeline |

See the SDK README for the full compatibility matrix.