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

grounded-llm

v0.1.8

Published

TypeScript library to reduce hallucination in LLM-generated responses and decisions by grounding them in retrieved context.

Readme


English

TypeScript library to reduce hallucination in LLM-generated responses, by forcing literal fact extraction and an explicit sufficiency check before generating a final answer.

This version targets the OpenAI API. By default, each component uses the official openai client internally (injected by you or created from an apiKey). Optionally, you can instead pass an already-configured LangChain chat model via langchainModel — see "Using a LangChain model" below.

How it fights hallucination: chain-of-thought grounding

Every component in this library forces the model through the same explicit chain-of-thought sequence instead of asking for a final answer directly:

  1. Extract — the model pulls the literal, verbatim excerpts from the context that are relevant to the request. No paraphrasing is allowed at this step.
  2. Judge sufficiency — using only those excerpts, the model explicitly decides whether the context is enough to respond safely (contradictions or partial matches count as insufficient).
  3. Answer or fall back — only if the context was judged sufficient does the model write a final answer, and it may use only what was extracted in step 1. Otherwise, the call returns the developer-configured fallback instead of letting the model invent something plausible.
  4. Explain — the model must always return reasoning, an explanation that ties the extracted excerpts to the sufficiency decision and (when applicable) to the final answer.

This forced extract → judge → answer → explain pipeline, enforced through structured output (schema-validated, not just a prompting convention), is what makes hallucination structurally harder: the model cannot skip straight to a confident-sounding answer without first grounding it in literal text it can point to. Every result exposes this reasoning chain via result.extractedFacts and result.reasoning, so you can inspect why the model answered — or refused to — instead of trusting a black-box output.

Generators

The library offers three components, each for a different context-grounded LLM call scenario:

| Component | Use case | Method | | ----------------------------------------- | --------------------------------------------------------------------- | ------------------------------------- | | GroundedGenerator | Generate the final answer to the user from retrieved context | .generate({ context, question }) | | GroundedEnricher | Enrich an existing base text with retrieved context | .generate({ baseContent, context }) | | GroundedExtractor | Extract a structured object (fields you define) from the user message | .extract({ message }) |

All three share the same principles: optional fallback at construction (see below), structured output via schema, temperature zero by default, and operational errors (ModelUnavailableError, ContextTooLargeError, InvalidModelOutputError) always distinct from a fallback result.

fallbackValue is optional. When configured, it's the canned value returned in place of the model's output whenever the component judges its own result unsafe to return (insufficient context, nothing extractable). When omitted:

  • GroundedGenerator and GroundedEnricher always let the model produce a real answer — GroundedGenerator falls back to a best-effort answer (general knowledge, or a clarifying question) instead of an empty result; GroundedEnricher's behavior is unchanged either way, since it already returns baseContent unchanged on insufficient context rather than a configured fallback.
  • GroundedExtractor always returns the model's raw extraction (null for fields it couldn't find), ignoring strict, instead of substituting a fallback object.

All three also accept, at construction, three optional parameters to customize the model's behavior for that call:

  • identity — the model's role/objective for this call (e.g. "You are the support assistant for Acme Corp.").
  • rules — additional rules constraining the call (e.g. style, domain-specific constraints).
  • tone — the desired tone/personality for the response (e.g. "be empathetic, kind, and natural" — useful for chatbot scenarios).

All three are appended as extra sections in the same system prompt, in the order identityrulestone, always after the component's built-in grounding/anti-hallucination instructions — they complement persona and style, but never override the grounding rules.

Using a LangChain model (LangSmith tracing)

By default, every component talks to the OpenAI API directly (standalone mode) — no LangChain dependency required. If your application already runs on LangChain and you want these calls to show up in your LangSmith traces alongside the rest of your pipeline, pass an already-configured LangChain chat model via langchainModel instead of client/apiKey/model/temperature:

import { ChatOpenAI } from '@langchain/openai';
import { GroundedGenerator } from 'grounded-llm';

const langchainModel = new ChatOpenAI({ model: 'gpt-4o-mini', temperature: 0 });

const generator = new GroundedGenerator({
  langchainModel,
  fallbackValue: "Sorry, I don't have enough information to answer that.",
});
  • langchainModel is mutually exclusive with client, apiKey, model, and temperature — the chat model already carries its own credentials, model id, and temperature, so combining it with any of those throws a configuration error at construction.
  • maxContextTokens still applies; when omitted in this mode, a conservative default of 128 000 tokens is used (there's no OpenAI model id to derive a known limit from).
  • identity/rules/tone/fallbackValue, the result shape, and the operational error types (ModelUnavailableError/ContextTooLargeError/InvalidModelOutputError) all behave identically whether you use client/apiKey or langchainModel.
  • @langchain/core is an optional peer dependency — install it (and whichever LangChain chat model integration you use, e.g. @langchain/openai) only if you use langchainModel. Standalone consumers never need it.

GroundedGenerator

Generates a final answer strictly grounded in retrieved context, or falls back to a developer-configured value when the context is insufficient — instead of inventing an answer.

import { GroundedGenerator } from 'grounded-llm';

const generator = new GroundedGenerator({
  fallbackValue: "Sorry, I don't have enough information to answer that.",
  // Optional: fallbackValue (see "Generators" above for what happens when it's
  // omitted), model (default "gpt-4o-mini"), apiKey (default OPENAI_API_KEY),
  // temperature (default 0), maxContextTokens, or an already-configured `client`
  // instance from the `openai` package (or `langchainModel` instead — see above).
  // Also accepts identity/rules/tone.
});

const result = await generator.generate({
  context: 'Paris is the capital of France.',
  question: 'What is the capital of France?',
});

console.log(result.usedFallback); // false
console.log(result.finalAnswer); // "Paris is the capital of France."
console.log(result.extractedFacts); // ["Paris is the capital of France."]
console.log(result.reasoning); // explanation connecting facts to the answer

GroundedGenerator is standalone by default — it depends only on the official openai client, so it can be plugged into any pipeline (LangGraph, a manual chain, or a direct call) without requiring any third-party types. See "Using a LangChain model" above if you'd rather route calls through an existing LangChain chat model.

Error handling

generate() throws one of three distinct operational errors (none of which are retried automatically — retry policy is the caller's responsibility):

  • ModelUnavailableError — technical failure calling the model (network, timeout).
  • ContextTooLargeError — the context exceeds the model's processable limit.
  • InvalidModelOutputError — the model's response failed schema validation or was refused.

These are distinct from a normal result with usedFallback: true, which is a valid outcome (insufficient context), not an error.

GroundedEnricher

Enriches an existing base text with retrieved context (e.g., via RAG) — useful when you already have a template/draft response and want to add dynamic information to it, instead of generating a response from scratch.

import { GroundedEnricher } from 'grounded-llm';

const enricher = new GroundedEnricher({
  fallbackValue: 'N/A', // required for API consistency; never actually returned in normal use (see note below)
  // Also accepts identity/rules/tone and langchainModel, plus the same config options as GroundedGenerator.
});

const result = await enricher.generate({
  baseContent: 'Thanks for your order!',
  context: 'Orders ship within 3 business days.',
});

console.log(result.usedFallback); // false
console.log(result.finalAnswer); // "Thanks for your order! Orders ship within 3 business days."
console.log(result.extractedFacts); // ["Orders ship within 3 business days."]
console.log(result.reasoning); // explanation connecting facts to the enrichment

Fallback semantics differ from GroundedGenerator: when the context is insufficient to enrich safely, GroundedEnricher returns baseContent unchanged (with usedFallback: true) — never fallbackValue. fallbackValue is required at construction only for consistency with the other components in the family; it's never returned by any success path. An empty/blank baseContent is treated as invalid usage and throws immediately, without calling the model.

GroundedExtractor

Extracts a structured object with fields you define from a user message — useful for the "JSON mode" scenarios common in chatbots (name, email, intent, etc.), without requiring a closed set of actions or logprob-based confidence (that's the future GroundedDecider's job).

import { GroundedExtractor } from 'grounded-llm';
import { z } from 'zod';

const extractor = new GroundedExtractor({
  fields: { name: z.string(), email: z.string() },
  fallbackValue: { name: null, email: null }, // whole object, same shape as `fields`
  // Optional: strict (default false) — see below. Also accepts identity/rules/tone and langchainModel.
});

const result = await extractor.extract({
  message: "Hi, I'm Ada Lovelace, [email protected]",
});

console.log(result.usedFallback); // false
console.log(result.data); // { name: "Ada Lovelace", email: "[email protected]" }
console.log(result.reasoning);

Partial extraction and strict mode: if the message only supports part of the fields, the default behavior (strict: false) returns the extracted fields with null for the rest, without triggering fallbackValue. With strict: true, any missing field triggers fallbackValue (whole object) instead of a partial result. If no field can be safely extracted (or the message is empty), fallbackValue is returned regardless of strict.

Releasing

CI (.github/workflows/ci.yml) runs type-check, tests, and build on every push/PR to main. Publishing to npm (.github/workflows/release.yml) is triggered by pushing a v*.*.* tag:

npm version patch   # or minor / major — bumps package.json and creates a git tag
git push --follow-tags

The release workflow verifies the tag matches package.json's version, then runs the same build/test steps before publishing with npm provenance. Requires an NPM_TOKEN secret (an npm Automation token) configured in the repository settings.


Português

Biblioteca TypeScript para reduzir alucinação em respostas geradas por LLM, forçando extração literal de fatos e uma checagem explícita de suficiência de contexto antes de gerar a resposta final.

Nesta versão, o alvo é a API da OpenAI. Por padrão, cada componente usa o client oficial openai internamente (injetado por você ou criado a partir de uma apiKey). Opcionalmente, você pode em vez disso passar um chat model LangChain já configurado via langchainModel — veja "Usando um modelo LangChain" abaixo.

Como o combate à alucinação funciona: chain-of-thought ancorado em contexto

Todos os componentes da biblioteca forçam o modelo a passar pela mesma sequência explícita de chain-of-thought (cadeia de raciocínio), em vez de pedir a resposta final diretamente:

  1. Extrair — o modelo retira do contexto os trechos literais e relevantes para a solicitação, verbatim. Paráfrase não é permitida nesta etapa.
  2. Julgar suficiência — usando apenas esses trechos extraídos, o modelo decide explicitamente se o contexto é suficiente para responder com segurança (contradições ou correspondências parciais contam como insuficientes).
  3. Responder ou usar fallback — só se o contexto for julgado suficiente o modelo escreve uma resposta final, e ela só pode usar o que foi extraído no passo 1. Caso contrário, a chamada retorna o fallback configurado pelo desenvolvedor, em vez de deixar o modelo inventar algo plausível.
  4. Explicar — o modelo sempre deve retornar reasoning, uma explicação que conecta os trechos extraídos à decisão de suficiência e (quando aplicável) à resposta final.

Esse pipeline forçado de extrair → julgar → responder → explicar, garantido via saída estruturada (validada por schema, não apenas uma convenção de prompt), é o que torna a alucinação estruturalmente mais difícil: o modelo não consegue pular direto para uma resposta com aparência confiante sem antes ancorá-la em texto literal que ele pode apontar. Todo resultado expõe essa cadeia de raciocínio via result.extractedFacts e result.reasoning, permitindo inspecionar por que o modelo respondeu — ou se recusou a responder — em vez de confiar em uma saída caixa-preta.

Generators

A lib oferece três componentes, cada um para um cenário diferente de chamada LLM ancorada em contexto:

| Componente | Uso | Método | | ------------------------------------------- | -------------------------------------------------------------------------------- | ------------------------------------- | | GroundedGenerator | Gerar a resposta final ao usuário a partir de contexto recuperado | .generate({ context, question }) | | GroundedEnricher | Enriquecer um texto-base existente com contexto recuperado | .generate({ baseContent, context }) | | GroundedExtractor | Extrair um objeto estruturado (campos definidos por você) da mensagem do usuário | .extract({ message }) |

Os três compartilham os mesmos princípios: fallback opcional na construção (veja abaixo), saída estruturada via schema, temperature zero por padrão, e erros operacionais (ModelUnavailableError, ContextTooLargeError, InvalidModelOutputError) sempre distintos de um resultado com fallback.

fallbackValue é opcional. Quando configurado, é o valor fixo retornado no lugar da saída do modelo sempre que o componente julga seu próprio resultado inseguro para retornar (contexto insuficiente, nada extraível). Quando omitido:

  • GroundedGenerator e GroundedEnricher sempre deixam o modelo produzir uma resposta real — o GroundedGenerator recorre a uma resposta best-effort (conhecimento geral, ou uma pergunta de esclarecimento) em vez de um resultado vazio; o comportamento do GroundedEnricher não muda de qualquer forma, já que ele já retorna o baseContent inalterado quando o contexto é insuficiente, em vez de um fallback configurado.
  • GroundedExtractor sempre retorna a extração bruta do modelo (null nos campos não encontrados), ignorando strict, em vez de substituir por um objeto de fallback.

Os três também aceitam, na construção, três parâmetros opcionais para customizar o comportamento do modelo naquela chamada:

  • identity — o papel/objetivo do modelo naquela chamada (ex: "Você é o assistente de suporte da Acme Corp.").
  • rules — regras adicionais para restringir a chamada (ex: estilo, restrições específicas do domínio).
  • tone — o tom/personalidade desejado para a resposta (ex: "seja empático, gentil e natural" — útil em cenários de chatbot).

Os três são anexados como seções extras no mesmo system prompt, na ordem identityrulestone, sempre depois das instruções internas de ancoragem/anti-alucinação — eles complementam persona e estilo, mas nunca sobrescrevem as regras de grounding.

Usando um modelo LangChain (tracing do LangSmith)

Por padrão, todos os componentes falam diretamente com a API da OpenAI (modo standalone) — sem exigir nenhuma dependência do LangChain. Se sua aplicação já roda sobre LangChain e você quer que essas chamadas apareçam nos seus traces do LangSmith junto com o restante do seu pipeline, passe um chat model LangChain já configurado via langchainModel, em vez de client/apiKey/model/temperature:

import { ChatOpenAI } from '@langchain/openai';
import { GroundedGenerator } from 'grounded-llm';

const langchainModel = new ChatOpenAI({ model: 'gpt-4o-mini', temperature: 0 });

const generator = new GroundedGenerator({
  langchainModel,
  fallbackValue: 'Desculpe, não tenho informação suficiente para responder isso.',
});
  • langchainModel é mutuamente exclusivo com client, apiKey, model e temperature — o chat model já traz suas próprias credenciais, model id e temperatura, então combiná-lo com qualquer um desses campos lança um erro de configuração na construção.
  • maxContextTokens continua valendo; quando omitido nesse modo, um limite conservador padrão de 128.000 tokens é usado (não há um model id da OpenAI do qual derivar um limite conhecido).
  • identity/rules/tone/fallbackValue, o formato do resultado, e os tipos de erro operacionais (ModelUnavailableError/ContextTooLargeError/ InvalidModelOutputError) se comportam de forma idêntica, seja usando client/apiKey ou langchainModel.
  • @langchain/core é uma peerDependency opcional — instale-a (e a integração de chat model LangChain que você usar, ex: @langchain/openai) apenas se for usar langchainModel. Consumidores do modo standalone nunca precisam dela.

GroundedGenerator

Gera uma resposta final estritamente ancorada no contexto recuperado, ou recorre a um valor de fallback configurado pelo desenvolvedor quando o contexto é insuficiente — em vez de inventar uma resposta.

import { GroundedGenerator } from 'grounded-llm';

const generator = new GroundedGenerator({
  fallbackValue: 'Desculpe, não tenho informação suficiente para responder isso.',
  // Opcional: fallbackValue (ver "Generators" acima para o que acontece quando
  // omitido), model (default "gpt-4o-mini"), apiKey (default OPENAI_API_KEY),
  // temperature (default 0), maxContextTokens, ou uma instância `client` já
  // configurada do pacote `openai` (ou `langchainModel` em vez disso — veja acima).
  // Também aceita identity/rules/tone.
});

const result = await generator.generate({
  context: 'Paris é a capital da França.',
  question: 'Qual é a capital da França?',
});

console.log(result.usedFallback); // false
console.log(result.finalAnswer); // "Paris é a capital da França."
console.log(result.extractedFacts); // ["Paris é a capital da França."]
console.log(result.reasoning); // explicação conectando os fatos à resposta

O GroundedGenerator é standalone — depende apenas do client oficial openai, então pode ser plugado em qualquer pipeline (LangGraph, uma chain manual, ou uma chamada direta) sem exigir nenhum tipo de terceiros.

Tratamento de erros

generate() lança um de três erros operacionais distintos (nenhum deles é reexecutado automaticamente — a política de retry é responsabilidade de quem consome a lib):

  • ModelUnavailableError — falha técnica na chamada ao modelo (rede, timeout).
  • ContextTooLargeError — o contexto excede o limite processável do modelo.
  • InvalidModelOutputError — a resposta do modelo falhou na validação do schema ou foi recusada.

Esses erros são distintos de um resultado normal com usedFallback: true, que é um desfecho válido (contexto insuficiente), não um erro.

GroundedEnricher

Enriquece um texto-base existente com contexto recuperado (por exemplo, via RAG) — útil quando você já tem uma resposta-template e quer adicionar informação dinâmica a ela, em vez de gerar uma resposta do zero.

import { GroundedEnricher } from 'grounded-llm';

const enricher = new GroundedEnricher({
  fallbackValue: 'N/A', // exigido por consistência de API; nunca é retornado em uso normal (ver nota abaixo)
  // Também aceita identity/rules/tone e langchainModel, além das mesmas opções de configuração do GroundedGenerator.
});

const result = await enricher.generate({
  baseContent: 'Obrigado pelo seu pedido!',
  context: 'Pedidos são entregues em até 3 dias úteis.',
});

console.log(result.usedFallback); // false
console.log(result.finalAnswer); // "Obrigado pelo seu pedido! Pedidos são entregues em até 3 dias úteis."
console.log(result.extractedFacts); // ["Pedidos são entregues em até 3 dias úteis."]
console.log(result.reasoning); // explicação conectando os fatos ao enriquecimento

Semântica de fallback diferente do GroundedGenerator: quando o contexto é insuficiente para enriquecer com segurança, o GroundedEnricher retorna o baseContent inalterado (com usedFallback: true) — nunca o fallbackValue. O fallbackValue é exigido na construção apenas por consistência com os demais componentes da família; ele nunca é retornado em nenhum fluxo de sucesso. Um baseContent vazio/em branco é tratado como uso inválido e lança uma exceção imediatamente, sem chamar o modelo.

GroundedExtractor

Extrai um objeto estruturado com campos definidos por você a partir de uma mensagem do usuário — útil para os cenários de "JSON mode" de chatbots (nome, e-mail, intenção, etc.), sem exigir um conjunto fechado de ações nem cálculo de confiança via logprob (isso é responsabilidade do futuro GroundedDecider).

import { GroundedExtractor } from 'grounded-llm';
import { z } from 'zod';

const extractor = new GroundedExtractor({
  fields: { name: z.string(), email: z.string() },
  fallbackValue: { name: null, email: null }, // objeto completo, mesmo formato de `fields`
  // Opcional: strict (default false) — veja abaixo. Também aceita identity/rules/tone e langchainModel.
});

const result = await extractor.extract({
  message: 'Oi, sou a Ada Lovelace, [email protected]',
});

console.log(result.usedFallback); // false
console.log(result.data); // { name: "Ada Lovelace", email: "[email protected]" }
console.log(result.reasoning);

Extração parcial e modo strict: se a mensagem preencher só parte dos campos, o comportamento padrão (strict: false) retorna os campos extraídos e null nos demais, sem acionar o fallbackValue. Com strict: true, qualquer campo ausente aciona o fallbackValue (objeto completo) em vez de um resultado parcial. Se nenhum campo puder ser extraído com segurança (ou a mensagem estiver vazia), o fallbackValue é retornado independentemente do modo strict.

Releases

O CI (.github/workflows/ci.yml) roda type-check, testes e build em todo push/PR para main. A publicação no npm (.github/workflows/release.yml) é disparada ao subir uma tag v*.*.*:

npm version patch   # ou minor / major — atualiza o package.json e cria a tag git
git push --follow-tags

O workflow de release confere se a tag bate com a versão do package.json, roda o mesmo build/test novamente, e então publica com npm provenance. Requer um secret NPM_TOKEN (token de automação do npm) configurado nas configurações do repositório.