Documentation•Reference

HazelJS CAG Package

npm downloads

@hazeljs/​cag provides Cache-Augmented Generation — prepare, fingerprint, cache, and reuse stable context without embedding + search on every request.

Prepare once. Reuse on every request.

CAG is not a wrapper around RAG. Optional @hazeljs/​rag loaders can supply file/URL sources; vector search stays in @hazeljs/​rag. Store concepts reuse @hazeljs/​cache via CagStore.

Use CAG for policies, catalogs, schemas, and tenant config that barely change. Use RAG when relevance depends on the query. Combine both (plus KAG / Memory) with @hazeljs/context.

Quick Reference

  • Purpose: Preload and reuse relatively static context under a token budget with multi-tenant cache keys.
  • When to use: Multi-merchant support bots, always-on compliance text, OpenAPI / tool schemas stuck in every turn, weekly catalogs keyed by version.
  • Key APIs: CagModule.forRoot / forRootAsync, CagService, register / registerClass, getContext, invalidate, getMetrics, @ContextSource, MemoryCagStore, Redis-shaped store via redis.client, PromptCacheProvider.
  • Dependencies: Peer @hazeljs/​core, @hazeljs/​cache. Optional: @hazeljs/​rag loaders, @hazeljs/​ai promptCache field.
  • Common mistakes: Using CAG instead of RAG for dynamic handbook lookup; omitting tenantId when multiTenant is on; compressing compliance text (priority >= 90 / compressible: false); forgetting version bumps when policy content changes.

CAG vs RAG

CAGRAG
QuestionWhat context can I preload/reuse?What information should I retrieve?
Typical dataPolicies, catalogs, schemas, tenant configDocuments matched to a query
Hot pathCache hit → assemble → LLMEmbed → search → passages → LLM
Failure modeStale cache if invalidation is wrongWrong or missing passage

Full comparison (including KAG): RAG vs CAG vs KAG.

Installation

npm install @hazeljs/​cag @hazeljs/​core @hazeljs/​cache

Quick Start

import { CagModule, CagService } from '@hazeljs/​cag';

CagModule.forRoot({
  store: 'memory',
  ttl: '1h',
  maxTokens: 50_000,
  compression: true,
  invalidation: { strategy: 'version' },
  multiTenant: true,
});

/​/​ somewhere injectable:
cag.register(
  {
    id: 'return-policy',
    priority: 100,
    compressible: false,
    version: '2026-10',
    source: { type: 'text', content: 'Returns within 30 days…' },
  },
  'support'
);

const { context, hit } = await cag.getContext({
  namespace: 'support',
  tenantId: 'merchant-123',
});
/​/​ context.text → assembled policy blob
/​/​ hit → true on warm cache

Per-tenant function sources

Registration is shared by namespace; vary content by tenantId in a function source:

cag.register(
  {
    id: 'return-policy',
    priority: 100,
    compressible: false,
    version: '2026-10',
    source: {
      type: 'function',
      load: async ({ tenantId }) => loadMerchantPolicy(tenantId),
    },
  },
  'support'
);

Context Sources

TypeUse
text / jsonInline stable strings / objects
file / urlPrefer RAG loaders when installed; fs/fetch fallback
function / custom / memoryDynamic load with ContextLoadInput (includes tenantId)
ragOptional retrieve port for hybrid prep

Declarative:

@ContextSource({ namespace: 'merchant-policy', ttl: '6h', priority: 100 })
export class MerchantPolicyContext {
  async load(input: ContextLoadInput) {
    return loadMerchantPolicies(input.tenantId);
  }
}

Caching & Invalidation

  • Memory store by default; Redis via injected redis.client (ioredis-compatible)
  • Keys include tenantId, namespace, version, and fingerprint
  • Invalidate with TTL, bumping version on sources, or cag.invalidate({ tenantId, namespace })

Token Budgets & Compression

tokenBudget: { max: 50_000, strategy: 'priority' }
/​/​ truncate | priority | compress | summarize | reject

Sources with compressible: false or priority >= 90 are never rewritten by compression.

Multi-Tenancy

multiTenant defaults to true. Cache keys always include tenantId. Tenant A cannot read Tenant B context.

LLM Provider Caching

PromptCacheProvider.prepare() returns opaque hints for OpenAI / Anthropic / Gemini. Pass them on @hazeljs/​ai AICompletionRequest.promptCache. CAG does not call vendor SDKs.

Observability

  • Events: cag.context.hit | miss | build | invalidate | compress
  • Metrics: cag.getMetrics() (hits, misses, hitRatio, tokensSaved, …)

Agent Integration

Inject CagService as AgentRuntimeConfig.cagService. Optional DNA:

{
  "context": {
    "cag": { "enabled": true, "namespace": "merchant" }
  }
}

Prefer @hazeljs/context when the agent needs CAG + RAG + KAG + Memory in one plan.

Production Guide

  • Prefer Redis with a real client in production
  • Keep multiTenant: true; add isolation tests
  • Mark compliance sources compressible: false and high priority
  • Set strict: true to fail closed on store errors
  • Monitor hit ratio via getMetrics()

Try it offline

No API keys — Meridian Ops labs:

# from hazeljs-meridian-ops/​
npm run example:install
npm run example:cag

cag-support-agent demonstrates cold miss → warm hit, tenant isolation, and version invalidation.

License

Apache-2.0