{"_id":"@amanv685/ai-guard","_rev":"3-f614b17bc509ad1328130caef0ae2230","name":"@amanv685/ai-guard","dist-tags":{"latest":"0.1.2"},"versions":{"0.1.0":{"name":"@amanv685/ai-guard","version":"0.1.0","keywords":["ai","llm","metering","credits","tokens","rate-limit","ai-coins"],"license":"MIT","_id":"@amanv685/ai-guard@0.1.0","maintainers":[{"name":"amanv685","email":"av36232@gmail.com"}],"homepage":"https://github.com/your-org/ai-guard#readme","bugs":{"url":"https://github.com/your-org/ai-guard/issues"},"dist":{"shasum":"d05d9f9f4cdfa4c5603595639bd48556343d20dc","tarball":"https://registry.npmjs.org/@amanv685/ai-guard/-/ai-guard-0.1.0.tgz","fileCount":18,"integrity":"sha512-OyfLOuaYAm/Cn+7Btg0hp/yUiDIeN6y7YVGJNouVXDh38iBSz0T9pnTV2RBIhMrx2Sxx4ljV6UGGgxAAM4lS0A==","signatures":[{"sig":"MEUCIQD4ByUedJQB8guXLQkbYJ41LhcxdpZCR3xRhOjsKpdf8QIgDrsPer/oJgIT728PY3P23W/ULAdis/Psud1hrG4/KdI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":271823},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./redis":{"types":"./dist/redis.d.ts","import":"./dist/redis.js","require":"./dist/redis.cjs"}},"scripts":{"test":"vitest run","build":"tsup","example":"npx tsx examples/basic-usage.ts","typecheck":"tsc --noEmit","test:watch":"vitest","prepublishOnly":"npm run typecheck && npm test && npm run build"},"_npmUser":{"name":"amanv685","email":"av36232@gmail.com"},"repository":{"url":"git+https://github.com/your-org/ai-guard.git","type":"git"},"_npmVersion":"11.6.1","description":"Provider-agnostic AI usage guard with coin budgets, token/image limits, and task-scoped responses","directories":{},"_nodeVersion":"24.11.0","dependencies":{"zod":"^3.24.2"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.3","tsup":"^8.4.0","vitest":"^3.0.9","typescript":"^5.8.2","@types/node":"^22.14.0"},"peerDependencies":{"ioredis":">=5.0.0"},"peerDependenciesMeta":{"ioredis":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/ai-guard_0.1.0_1781723787108_0.5865686152900065","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@amanv685/ai-guard","version":"0.1.1","keywords":["ai","llm","metering","credits","tokens","rate-limit","ai-coins"],"license":"MIT","_id":"@amanv685/ai-guard@0.1.1","maintainers":[{"name":"amanv685","email":"av36232@gmail.com"}],"homepage":"https://github.com/your-org/ai-guard#readme","bugs":{"url":"https://github.com/your-org/ai-guard/issues"},"dist":{"shasum":"9a5c0d885f788410daf17361da47692c4989c807","tarball":"https://registry.npmjs.org/@amanv685/ai-guard/-/ai-guard-0.1.1.tgz","fileCount":18,"integrity":"sha512-bH4HHswXW3GrnA8Opg66g6NukSiNVg4sZ5UyObjLRr9/YuOY93+FWct4QueiDKPjIhaZw6TpWNGyQTEXZexcYQ==","signatures":[{"sig":"MEUCIFiWiCIt5mce0RokNJhazk5JP2Xgac3+zUC/UxENAIB3AiEA0kBwXOnPlVqWjEJ40yoMQxm+Oq6jekwfX4CVZia7k8M=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":271823},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./redis":{"types":"./dist/redis.d.ts","import":"./dist/redis.js","require":"./dist/redis.cjs"}},"scripts":{"test":"vitest run","build":"tsup","example":"npx tsx examples/basic-usage.ts","typecheck":"tsc --noEmit","test:watch":"vitest","prepublishOnly":"npm run typecheck && npm test && npm run build"},"_npmUser":{"name":"amanv685","email":"av36232@gmail.com"},"repository":{"url":"git+https://github.com/your-org/ai-guard.git","type":"git"},"_npmVersion":"11.6.1","description":"Provider-agnostic AI usage guard with coin budgets, token/image limits, and task-scoped responses","directories":{},"_nodeVersion":"24.11.0","dependencies":{"zod":"^3.24.2"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.3","tsup":"^8.4.0","vitest":"^3.0.9","typescript":"^5.8.2","@types/node":"^22.14.0"},"peerDependencies":{"ioredis":">=5.0.0"},"peerDependenciesMeta":{"ioredis":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/ai-guard_0.1.1_1781723939091_0.24884273423183156","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@amanv685/ai-guard","version":"0.1.2","description":"Provider-agnostic AI usage guard with coin budgets, token/image limits, and task-scoped responses","type":"module","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./redis":{"types":"./dist/redis.d.ts","import":"./dist/redis.js","require":"./dist/redis.cjs"}},"scripts":{"build":"tsup","test":"vitest run","test:watch":"vitest","typecheck":"tsc --noEmit","prepublishOnly":"npm run typecheck && npm test && npm run build","example":"npx tsx examples/basic-usage.ts"},"keywords":["ai","llm","metering","credits","tokens","rate-limit","ai-coins"],"license":"MIT","homepage":"https://github.com/amanverma685/ai-guard#readme","bugs":{"url":"https://github.com/amanverma685/ai-guard/issues"},"repository":{"type":"git","url":"git+https://github.com/amanverma685/ai-guard.git"},"dependencies":{"zod":"^3.24.2"},"devDependencies":{"@types/node":"^22.14.0","tsup":"^8.4.0","tsx":"^4.19.3","typescript":"^5.8.2","vitest":"^3.0.9"},"peerDependencies":{"ioredis":">=5.0.0"},"peerDependenciesMeta":{"ioredis":{"optional":true}},"gitHead":"ebcf5d0214ba8079f946691796aebb0c3da45f73","_id":"@amanv685/ai-guard@0.1.2","_nodeVersion":"24.11.0","_npmVersion":"11.6.1","dist":{"integrity":"sha512-dZTvoJE9OTKyqHtRcvpcGnnYTORcDlCOVguxnBNMPZcqsQ0h7r0SuMPGLXPk03cNveHt38W7A2CgVGVxQDLKTw==","shasum":"8332784ed3423ef2ba91540b255a7b87eddbe269","tarball":"https://registry.npmjs.org/@amanv685/ai-guard/-/ai-guard-0.1.2.tgz","fileCount":18,"unpackedSize":271982,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIBXVQWCg1+kgon1nq0lvU7CKMOZNSJhE3K0qkdyxKeCmAiEA5DlpyO6Y93tctq6sHo8jz4bowbA5iHd3EfOWQ0ZuTxQ="}]},"_npmUser":{"name":"amanv685","email":"av36232@gmail.com"},"directories":{},"maintainers":[{"name":"amanv685","email":"av36232@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/ai-guard_0.1.2_1781724596522_0.11647732947209355"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-17T19:16:26.898Z","modified":"2026-06-17T19:29:56.841Z","0.1.0":"2026-06-17T19:16:27.255Z","0.1.1":"2026-06-17T19:18:59.283Z","0.1.2":"2026-06-17T19:29:56.703Z"},"bugs":{"url":"https://github.com/amanverma685/ai-guard/issues"},"license":"MIT","homepage":"https://github.com/amanverma685/ai-guard#readme","keywords":["ai","llm","metering","credits","tokens","rate-limit","ai-coins"],"repository":{"type":"git","url":"git+https://github.com/amanverma685/ai-guard.git"},"description":"Provider-agnostic AI usage guard with coin budgets, token/image limits, and task-scoped responses","maintainers":[{"name":"amanv685","email":"av36232@gmail.com"}],"readme":"# @amanv685/ai-guard\r\n\r\nProvider-agnostic npm package for enforcing **AI coin budgets**, **token/image limits**, and **task-scoped LLM responses**.\r\n\r\nAI coins and tokens are separate units:\r\n\r\n- **Tokens** — technical caps on input/output size (enforced before the request).\r\n- **AI coins** — user-facing plan allowance (deducted after the response completes).\r\n\r\nA request that starts with a positive coin balance always **completes fully**; coins are deducted afterward and may bring the balance to zero. The next request is blocked when balance is `0`.\r\n\r\n## Install\r\n\r\n```bash\r\nnpm install @amanv685/ai-guard\r\n```\r\n\r\n## Quick start\r\n\r\n```typescript\r\nimport {\r\n  createAIGuard,\r\n  createMemoryStore,\r\n  PLANS,\r\n} from \"@amanv685/ai-guard\";\r\n\r\nconst store = createMemoryStore();\r\nconst guard = createAIGuard({\r\n  userId: \"user_123\",\r\n  plan: PLANS.pro,\r\n  store,\r\n  chatId: \"chat_abc\",\r\n});\r\n\r\nconst result = await guard.execute({\r\n  taskId: \"generateTitle\",\r\n  input: \"Article about building npm packages for AI metering\",\r\n  invoke: async (constraints) => {\r\n    // Call your provider (OpenAI, Anthropic, etc.)\r\n    // Respect constraints.maxOutputTokens and constraints.systemPrompt\r\n    const text = await callYourProvider({\r\n      system: constraints.systemPrompt,\r\n      user: input,\r\n      maxTokens: constraints.maxOutputTokens,\r\n    });\r\n\r\n    return {\r\n      text,\r\n      usage: {\r\n        inputTokens: 180,\r\n        outputTokens: 6,\r\n        imageCount: 0,\r\n      },\r\n    };\r\n  },\r\n});\r\n\r\nconsole.log(result.output);           // \"Building npm Packages\"\r\nconsole.log(result.coinsConsumed);    // e.g. 1\r\nconsole.log(result.balanceRemaining); // e.g. 499\r\n```\r\n\r\n## Low-level API\r\n\r\nUse `beforeRequest` / `afterRequest` when you manage the provider call yourself:\r\n\r\n```typescript\r\nconst pre = await guard.beforeRequest({\r\n  taskId: \"generateTitle\",\r\n  estimatedInputTokens: 400,\r\n  imageCount: 0,\r\n});\r\n\r\n// pre.constraints.maxOutputTokens\r\n// pre.constraints.systemPrompt\r\n// pre.requestId\r\n\r\nconst modelText = await yourProvider(pre.constraints);\r\n\r\nconst post = await guard.afterRequest({\r\n  requestId: pre.requestId,\r\n  taskId: \"generateTitle\",\r\n  input: userInput,\r\n  output: modelText,\r\n  usage: { inputTokens: 380, outputTokens: 8, imageCount: 0 },\r\n});\r\n```\r\n\r\n## AI coin formula\r\n\r\n```\r\nweightedTokens = (inputTokens × inputWeight)\r\n               + (outputTokens × outputWeight)\r\n               + (visionTokens × visionWeight)\r\n\r\ntokenCoins = ceil(weightedTokens / tokensPerCoin)\r\nimageCoins = imageCount × imageCoinCost\r\n\r\ncoinsConsumed = max(tokenCoins + imageCoins, minimumCoinsPerRequest)\r\n```\r\n\r\nDefault weights by model tier:\r\n\r\n| Tier     | inputWeight | outputWeight | tokensPerCoin | imageCoinCost |\r\n|----------|-------------|--------------|---------------|---------------|\r\n| fast     | 1.0         | 1.5          | 1000          | 5             |\r\n| standard | 1.5         | 2.0          | 1000          | 10            |\r\n| premium  | 2.5         | 3.0          | 1000          | 20            |\r\n\r\nOverride per plan via `plan.coinWeights`.\r\n\r\n## Plans\r\n\r\nBuilt-in presets: `PLANS.free`, `PLANS.pro`, `PLANS.enterprise`.\r\n\r\nEach plan defines:\r\n\r\n- `monthlyCoins` — starting balance (reset via `guard.resetMonthlyUsage()`)\r\n- `limits` — token, image, and rate limits\r\n- `features.allowedTaskProfiles` — task whitelist or `\"*\"`\r\n\r\n## Task profiles\r\n\r\nBuilt-in tasks:\r\n\r\n| Task ID         | Behavior                                      |\r\n|-----------------|-----------------------------------------------|\r\n| `generateTitle` | Returns only a title from context (max 80 chars) |\r\n| `summarize`     | Concise summary without meta-commentary       |\r\n| `generalChat`   | Answers from context only                     |\r\n\r\nRegister custom tasks:\r\n\r\n```typescript\r\nimport { registerTaskProfile } from \"@amanv685/ai-guard\";\r\n\r\nregisterTaskProfile({\r\n  id: \"extractKeywords\",\r\n  systemPrompt: \"Return comma-separated keywords only.\",\r\n  maxOutputTokens: 64,\r\n  outputFormat: \"text\",\r\n  allowRetry: true,\r\n  validator: (output) => ({ valid: output.length > 0, sanitizedOutput: output }),\r\n});\r\n```\r\n\r\n## Graceful coin exhaustion\r\n\r\n1. **Pre-check** — `balance > 0` required to start a new request.\r\n2. **During generation** — never abort mid-response because of coin balance.\r\n3. **Post-check** — deduct coins; balance clamps to `0` (unless `allowNegativeBalance` on enterprise).\r\n4. **Next request** — blocked with `INSUFFICIENT_COINS`.\r\n\r\n## Error codes\r\n\r\n| Code                    | When                                      |\r\n|-------------------------|-------------------------------------------|\r\n| `INSUFFICIENT_COINS`    | No coins left for a new request           |\r\n| `INPUT_TOKEN_LIMIT`     | Estimated input exceeds plan cap          |\r\n| `OUTPUT_TOKEN_LIMIT`    | Actual output tokens exceed plan cap      |\r\n| `CHAT_TOKEN_LIMIT`      | Chat session token budget exceeded        |\r\n| `IMAGE_LIMIT`           | Per-request or per-chat image cap hit     |\r\n| `RATE_LIMIT`            | Too many requests per minute              |\r\n| `TASK_VALIDATION_FAILED`| Output failed task validator              |\r\n| `UNKNOWN_TASK`          | Task profile not found                    |\r\n| `TASK_NOT_ALLOWED`      | Task not on plan whitelist                |\r\n| `DOMAIN_VIOLATION`      | Input or output outside the allowed domain |\r\n\r\n## Domain restrictions\r\n\r\nRestrict the agent to **only answer within a specific domain** (e.g. e-commerce, healthcare admin, legal docs).\r\n\r\n### Built-in domains\r\n\r\n- `ecommerce` — orders, shipping, returns, products\r\n- `healthcare` — appointments, billing, insurance (not medical advice)\r\n\r\n### Guard-level domain (applies to all requests)\r\n\r\n```typescript\r\nimport { createAIGuard, createMemoryStore, PLANS } from \"@amanv685/ai-guard\";\r\n\r\nconst guard = createAIGuard({\r\n  userId: \"user_123\",\r\n  plan: PLANS.pro,\r\n  store: createMemoryStore(),\r\n  domain: \"ecommerce\", // or pass a full DomainProfile object\r\n});\r\n\r\nconst result = await guard.execute({\r\n  taskId: \"generalChat\",\r\n  input: \"Where is my order #4821?\",\r\n  invoke: async (constraints) => {\r\n    // constraints.systemPrompt already includes domain rules\r\n    return callYourProvider(constraints);\r\n  },\r\n});\r\n\r\n// Off-domain input — no LLM call, no coins spent:\r\nconst refused = await guard.execute({\r\n  taskId: \"generalChat\",\r\n  input: \"What's the weather today?\",\r\n  invoke: async () => ({ text: \"...\", usage: { inputTokens: 0, outputTokens: 0, imageCount: 0 } }),\r\n});\r\n// refused.refused === true\r\n// refused.output === \"I can only help with orders, products, shipping...\"\r\n```\r\n\r\n### Custom domain\r\n\r\n```typescript\r\nimport { createDomainProfile, registerDomainProfile } from \"@amanv685/ai-guard\";\r\n\r\nregisterDomainProfile(\r\n  createDomainProfile({\r\n    id: \"myProduct\",\r\n    name: \"My SaaS Product\",\r\n    description: \"Features, pricing, and account management only\",\r\n    systemPrompt:\r\n      \"You answer ONLY about My SaaS features, pricing, billing, and account settings using the provided context.\",\r\n    forbiddenKeywords: [\"competitor\", \"weather\", \"politics\"],\r\n    allowedKeywords: [\"account\", \"pricing\", \"feature\", \"billing\", \"plan\"],\r\n    strictKeywordCheck: false, // true = input must contain an allowed keyword\r\n    refuseMessage: \"I can only help with questions about My SaaS.\",\r\n    inputValidator: (input) => ({\r\n      valid: !input.includes(\"competitor\"),\r\n      reason: \"Competitor comparisons are not allowed\",\r\n    }),\r\n    outputValidator: (output, input) => ({\r\n      valid: output.length > 0,\r\n      sanitizedOutput: output,\r\n    }),\r\n  }),\r\n);\r\n```\r\n\r\n### How domain enforcement works\r\n\r\n1. **Input check** (before LLM) — blocks forbidden keywords; optional allowlist; custom `inputValidator`. Saves coins.\r\n2. **System prompt** — domain rules prepended to the task prompt.\r\n3. **Output check** (after LLM) — rejects responses that reference off-domain topics.\r\n\r\nFor semantic classification (e.g. detecting subtle off-topic questions), plug in a custom `inputValidator` using embeddings or a lightweight classifier.\r\n\r\n## Storage\r\n\r\n### In-memory (dev / single server)\r\n\r\n```typescript\r\nimport { createMemoryStore } from \"@amanv685/ai-guard\";\r\n\r\nconst store = createMemoryStore();\r\nawait store.initializeUser(\"user_123\", 500);\r\n```\r\n\r\n### Redis (production / multi-instance)\r\n\r\n```typescript\r\nimport { createAIGuard, PLANS } from \"@amanv685/ai-guard\";\r\nimport { createRedisStore } from \"@amanv685/ai-guard/redis\";\r\nimport Redis from \"ioredis\";\r\n\r\nconst redis = new Redis(process.env.REDIS_URL);\r\nconst store = createRedisStore({\r\n  client: {\r\n    get: (key) => redis.get(key),\r\n    set: (key, value) => redis.set(key, value),\r\n  },\r\n  keyPrefix: \"myapp:ai-guard:\",\r\n});\r\n\r\nconst guard = createAIGuard({ userId: \"user_123\", plan: PLANS.pro, store });\r\n```\r\n\r\nImplement the `UsageStore` interface for Postgres or other backends.\r\n\r\n## Plan validation (Zod)\r\n\r\nPlans are validated on `createAIGuard()` by default:\r\n\r\n```typescript\r\nimport { parsePlan, safeParsePlan, planSchema } from \"@amanv685/ai-guard\";\r\n\r\nconst customPlan = parsePlan({\r\n  id: \"starter\",\r\n  monthlyCoins: 200,\r\n  limits: { /* ... */ },\r\n  coinWeights: { /* ... */ },\r\n  features: { /* ... */ },\r\n});\r\n\r\nconst guard = createAIGuard({\r\n  userId: \"user_123\",\r\n  plan: customPlan,\r\n  store,\r\n  validatePlan: true, // default; set false to skip\r\n});\r\n```\r\n\r\n## Example app\r\n\r\n```bash\r\nnpm run example\r\n```\r\n\r\nSee [`examples/basic-usage.ts`](examples/basic-usage.ts) for a full integration demo.\r\n\r\n## Publish\r\n\r\n```bash\r\nnpm run build\r\nnpm publish --access public\r\n```\r\n\r\n`prepublishOnly` runs typecheck, tests, and build automatically.\r\n\r\n## Development\r\n\r\n```bash\r\nnpm install\r\nnpm test\r\nnpm run build\r\nnpm run example\r\n```\r\n\r\n## License\r\n\r\nMIT\r\n","readmeFilename":"README.md"}