{"_id":"ai-sdk-rate-limiter","_rev":"15-29cfcd65803689eec9d6944ec3b18c91","name":"ai-sdk-rate-limiter","dist-tags":{"latest":"0.13.1"},"versions":{"0.1.0":{"name":"ai-sdk-rate-limiter","version":"0.1.0","keywords":["ai","rate-limit","openai","anthropic","vercel-ai-sdk","llm","queue","retry","cost-tracking","middleware"],"license":"MIT","_id":"ai-sdk-rate-limiter@0.1.0","maintainers":[{"name":"piyushgupta344","email":"piyushgupta.civil@gmail.com"}],"dist":{"shasum":"4281073525933ea954f1caa7b54fa5b7daab657a","tarball":"https://registry.npmjs.org/ai-sdk-rate-limiter/-/ai-sdk-rate-limiter-0.1.0.tgz","fileCount":8,"integrity":"sha512-dMpFC4eUlbDUzG5XuR9OCfX5cP+STAUVbjLm8MXgUDec3jFA8KEHlmOcgXF0xMBZWAmT/smO36UgHohYDpFIjg==","signatures":[{"sig":"MEUCICU3r2uDfCZyUxISLZoOjNRgM7jNlH3A39q8uFKOH30wAiEA6S2cRzaq5I0K93wplH81VgCSQx7IIKWr51yx3cf55NI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":320125},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"scripts":{"dev":"tsup --watch","lint":"tsc --noEmit","test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"piyushgupta344","email":"piyushgupta.civil@gmail.com"},"_npmVersion":"11.3.0","description":"Smart rate limiting, queuing, and cost tracking middleware for AI SDK calls. Works across providers.","directories":{},"_nodeVersion":"24.1.0","_hasShrinkwrap":false,"devDependencies":{"ai":"^6.0.0","tsup":"^8.5.0","vitest":"^3.0.0","typescript":"^5.8.0","@ai-sdk/provider":"^3.0.8"},"peerDependencies":{"@ai-sdk/provider":">=1.0.0"},"peerDependenciesMeta":{"@ai-sdk/provider":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/ai-sdk-rate-limiter_0.1.0_1773194876031_0.1348104463263966","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"ai-sdk-rate-limiter","version":"0.2.0","keywords":["ai","rate-limit","openai","anthropic","vercel-ai-sdk","llm","queue","retry","cost-tracking","middleware"],"license":"MIT","_id":"ai-sdk-rate-limiter@0.2.0","maintainers":[{"name":"piyushgupta344","email":"piyushgupta.civil@gmail.com"}],"dist":{"shasum":"2aecbcdf367b4d44eb063f405a7f8694853344e4","tarball":"https://registry.npmjs.org/ai-sdk-rate-limiter/-/ai-sdk-rate-limiter-0.2.0.tgz","fileCount":8,"integrity":"sha512-hh45t3egMilUvQ91o90NOOF3xYM/qx/uENSE5A+3cl0DaxTK7DdEPlZ/LlcJKfxPUAGp3DSwiHelINGCZuBlIw==","signatures":[{"sig":"MEQCIFWNLU9SvUPhWQ8JpMtHUSs1MEwaPgKc9A5ptmpJ0tLmAiArq/I10ZAhi5zUxaLwK65AAIP157YuuyMGoMVuFBB56w==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":380966},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"gitHead":"20ad3400ccb1202f4b0895ce2700ac8b9acca455","scripts":{"dev":"tsup --watch","lint":"tsc --noEmit","test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"piyushgupta344","email":"piyushgupta.civil@gmail.com"},"_npmVersion":"11.3.0","description":"Smart rate limiting, queuing, and cost tracking middleware for AI SDK calls. Works across providers.","directories":{},"_nodeVersion":"24.1.0","_hasShrinkwrap":false,"devDependencies":{"ai":"^6.0.0","tsup":"^8.5.0","vitest":"^3.0.0","typescript":"^5.8.0","@ai-sdk/provider":"^3.0.8"},"peerDependencies":{"@ai-sdk/provider":">=1.0.0"},"peerDependenciesMeta":{"@ai-sdk/provider":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/ai-sdk-rate-limiter_0.2.0_1773198513012_0.45959050297211834","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"ai-sdk-rate-limiter","version":"0.3.0","keywords":["ai","rate-limit","openai","anthropic","vercel-ai-sdk","llm","queue","retry","cost-tracking","middleware"],"license":"MIT","_id":"ai-sdk-rate-limiter@0.3.0","maintainers":[{"name":"piyushgupta344","email":"piyushgupta.civil@gmail.com"}],"dist":{"shasum":"1ea72a0887b1ffb0367adc6b85de65ba2b3e43e2","tarball":"https://registry.npmjs.org/ai-sdk-rate-limiter/-/ai-sdk-rate-limiter-0.3.0.tgz","fileCount":8,"integrity":"sha512-+rrKRAzbZK2qlBn+6uWRyxgo3JBw20S5fL9aUO9NFjvAe9N0dbumnwVC/e/wnHzgFLCQfTmMK4UZ/mzc5tR8wQ==","signatures":[{"sig":"MEQCIBaIS8RGOb40vroAh8K5KDEPhmnuoPNv6wupRFRXCp6tAiAU06Xm10vsryTsJMAHU+Kg3iIRt5F9o3DeSni4yehrXA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":397162},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"gitHead":"e9fcd6d42f8f8dea8dd51187d5985905c77af1f2","scripts":{"dev":"tsup --watch","lint":"tsc --noEmit","test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"piyushgupta344","email":"piyushgupta.civil@gmail.com"},"_npmVersion":"11.3.0","description":"Smart rate limiting, queuing, and cost tracking middleware for AI SDK calls. Works across providers.","directories":{},"_nodeVersion":"24.1.0","_hasShrinkwrap":false,"devDependencies":{"ai":"^6.0.0","tsup":"^8.5.0","vitest":"^3.0.0","typescript":"^5.8.0","@ai-sdk/provider":"^3.0.8"},"peerDependencies":{"@ai-sdk/provider":">=1.0.0"},"peerDependenciesMeta":{"@ai-sdk/provider":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/ai-sdk-rate-limiter_0.3.0_1773202712369_0.13984666381177657","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"ai-sdk-rate-limiter","version":"0.4.0","keywords":["ai","rate-limit","openai","anthropic","vercel-ai-sdk","llm","queue","retry","cost-tracking","middleware"],"license":"MIT","_id":"ai-sdk-rate-limiter@0.4.0","maintainers":[{"name":"piyushgupta344","email":"piyushgupta.civil@gmail.com"}],"dist":{"shasum":"be813f0541808d9bb2f0dca5a3690a3278e7f686","tarball":"https://registry.npmjs.org/ai-sdk-rate-limiter/-/ai-sdk-rate-limiter-0.4.0.tgz","fileCount":8,"integrity":"sha512-Mh7fn6MjLIt53nSUntMPhyD1JU4ONfLRpuePw6qgHnA5yw6ObcbDmg8WnTsoPOvXsbfZdfSEuVmCm77BHbDuSg==","signatures":[{"sig":"MEYCIQDS0sJeWEUmr5Zhsnv/SfeXTu4dzrvSe9BSjh1qxqaKBAIhAO/fC9sExLSD/xxyqaY9E4M7/cDBLTbC2K+ui4sqN21C","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":447118},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"gitHead":"92bd13d8dddf42238ab1089531f078b848f01d82","scripts":{"dev":"tsup --watch","lint":"tsc --noEmit","test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"piyushgupta344","email":"piyushgupta.civil@gmail.com"},"_npmVersion":"11.3.0","description":"Smart rate limiting, queuing, and cost tracking middleware for AI SDK calls. Works across providers.","directories":{},"_nodeVersion":"24.1.0","_hasShrinkwrap":false,"devDependencies":{"ai":"^6.0.0","tsup":"^8.5.0","vitest":"^3.0.0","typescript":"^5.8.0","@ai-sdk/provider":"^3.0.8"},"peerDependencies":{"@ai-sdk/provider":">=1.0.0"},"peerDependenciesMeta":{"@ai-sdk/provider":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/ai-sdk-rate-limiter_0.4.0_1773203340436_0.45321344778797634","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"name":"ai-sdk-rate-limiter","version":"0.5.0","keywords":["ai","rate-limit","openai","anthropic","vercel-ai-sdk","llm","queue","retry","cost-tracking","middleware"],"license":"MIT","_id":"ai-sdk-rate-limiter@0.5.0","maintainers":[{"name":"piyushgupta344","email":"piyushgupta.civil@gmail.com"}],"dist":{"shasum":"b1878bb6c53d78aeb45ea7216cf99af31258eb44","tarball":"https://registry.npmjs.org/ai-sdk-rate-limiter/-/ai-sdk-rate-limiter-0.5.0.tgz","fileCount":16,"integrity":"sha512-57uYXi2heoM57thRngZ3oyLWoPKf9dPw2DqSeODLlAREN84azlE/+g7MSjzzkSa0g21Hq4YCCIS31smnO3JF8A==","signatures":[{"sig":"MEYCIQCCNqQBHx+I0rl7fSLRwZI6/fBEDs3pAY+P401jdKXFnwIhAIdipX2ZVAN+Oy2ijpsBGeU66bvo0BAfEBKTTBQY7q8h","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":509976},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./redis":{"import":{"types":"./dist/redis.d.ts","default":"./dist/redis.js"},"require":{"types":"./dist/redis.d.cts","default":"./dist/redis.cjs"}}},"gitHead":"db45edb3d79643f00986120fdb944e6d5147b595","scripts":{"dev":"tsup --watch","lint":"tsc --noEmit","test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"piyushgupta344","email":"piyushgupta.civil@gmail.com"},"_npmVersion":"11.3.0","description":"Smart rate limiting, queuing, and cost tracking middleware for AI SDK calls. Works across providers.","directories":{},"_nodeVersion":"24.1.0","_hasShrinkwrap":false,"devDependencies":{"ai":"^6.0.0","tsup":"^8.5.0","vitest":"^3.0.0","typescript":"^5.8.0","@ai-sdk/provider":"^3.0.8"},"peerDependencies":{"ioredis":">=4.0.0","@ai-sdk/provider":">=1.0.0"},"peerDependenciesMeta":{"ioredis":{"optional":true},"@ai-sdk/provider":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/ai-sdk-rate-limiter_0.5.0_1773205869696_0.06497845792231671","host":"s3://npm-registry-packages-npm-production"}},"0.6.0":{"name":"ai-sdk-rate-limiter","version":"0.6.0","keywords":["ai","rate-limit","openai","anthropic","vercel-ai-sdk","llm","queue","retry","cost-tracking","middleware"],"license":"MIT","_id":"ai-sdk-rate-limiter@0.6.0","maintainers":[{"name":"piyushgupta344","email":"piyushgupta.civil@gmail.com"}],"bin":{"ai-sdk-rate-limiter":"dist/cli.js"},"dist":{"shasum":"616b9ae52d7a6eb57be8f89769eded505af61f5c","tarball":"https://registry.npmjs.org/ai-sdk-rate-limiter/-/ai-sdk-rate-limiter-0.6.0.tgz","fileCount":23,"integrity":"sha512-6YIyU53p6rOuM0Tt0QQvQ8RKAiTduuCvsLJYUIWwNlpTNfj9Wf95Jx56jysjBosuTmL/EmFQcP/AU21jPOvShQ==","signatures":[{"sig":"MEUCIFUezzf92tGxRdq3R3926TTsXKnaoyhw3G0AA1QE3dJ0AiEA9XMHRUIsN1WV1bkShxaYuVQ5ouvLmuxpV/wRCOFzGGk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":570874},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./otel":{"import":{"types":"./dist/otel.d.ts","default":"./dist/otel.js"},"require":{"types":"./dist/otel.d.cts","default":"./dist/otel.cjs"}},"./redis":{"import":{"types":"./dist/redis.d.ts","default":"./dist/redis.js"},"require":{"types":"./dist/redis.d.cts","default":"./dist/redis.cjs"}}},"gitHead":"644c9217f8e78e5c9ccf1db6242e0305ea6e80fa","scripts":{"dev":"tsup --watch","lint":"tsc --noEmit","test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"piyushgupta344","email":"piyushgupta.civil@gmail.com"},"_npmVersion":"11.3.0","description":"Smart rate limiting, queuing, and cost tracking middleware for AI SDK calls. Works across providers.","directories":{},"_nodeVersion":"24.1.0","_hasShrinkwrap":false,"devDependencies":{"ai":"^6.0.0","tsup":"^8.5.0","vitest":"^3.0.0","typescript":"^5.8.0","@ai-sdk/provider":"^3.0.8"},"peerDependencies":{"ioredis":">=4.0.0","@ai-sdk/provider":">=1.0.0","@opentelemetry/api":">=1.0.0"},"peerDependenciesMeta":{"ioredis":{"optional":true},"@ai-sdk/provider":{"optional":true},"@opentelemetry/api":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/ai-sdk-rate-limiter_0.6.0_1773214146381_0.483757143179919","host":"s3://npm-registry-packages-npm-production"}},"0.7.0":{"name":"ai-sdk-rate-limiter","version":"0.7.0","keywords":["ai","rate-limit","openai","anthropic","vercel-ai-sdk","llm","queue","retry","cost-tracking","middleware"],"license":"MIT","_id":"ai-sdk-rate-limiter@0.7.0","maintainers":[{"name":"piyushgupta344","email":"piyushgupta.civil@gmail.com"}],"bin":{"ai-sdk-rate-limiter":"dist/cli.js"},"dist":{"shasum":"975beb9b0c171ca10cd55c786b1fbc0c415b726c","tarball":"https://registry.npmjs.org/ai-sdk-rate-limiter/-/ai-sdk-rate-limiter-0.7.0.tgz","fileCount":29,"integrity":"sha512-mj+3cnxglFQnRsjFuCQ0UowsLLcUDu09EOGYoc6VFqS0GioR5EQFBKb+HZGT43ega3NOTL7DC84ukFQDtqIrGg==","signatures":[{"sig":"MEQCIGrrumUu9RPLKpTqXZJT+AMjj8LYhM4J76ApU2D0eAnWAiA92nnsPY0XsY0kYCRHpD66ppt/PpNm5ER7OWz20mpkVg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1056022},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./otel":{"import":{"types":"./dist/otel.d.ts","default":"./dist/otel.js"},"require":{"types":"./dist/otel.d.cts","default":"./dist/otel.cjs"}},"./redis":{"import":{"types":"./dist/redis.d.ts","default":"./dist/redis.js"},"require":{"types":"./dist/redis.d.cts","default":"./dist/redis.cjs"}},"./testing":{"import":{"types":"./dist/testing.d.ts","default":"./dist/testing.js"},"require":{"types":"./dist/testing.d.cts","default":"./dist/testing.cjs"}}},"gitHead":"06a03b82907ebb32ba8dc9d196336110bc410ecf","scripts":{"dev":"tsup --watch","lint":"tsc --noEmit","test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"piyushgupta344","email":"piyushgupta.civil@gmail.com"},"_npmVersion":"11.3.0","description":"Smart rate limiting, queuing, and cost tracking middleware for AI SDK calls. Works across providers.","directories":{},"_nodeVersion":"24.1.0","_hasShrinkwrap":false,"devDependencies":{"ai":"^6.0.0","tsup":"^8.5.0","vitest":"^3.0.0","typescript":"^5.8.0","@ai-sdk/provider":"^3.0.8"},"peerDependencies":{"ioredis":">=4.0.0","@ai-sdk/provider":">=1.0.0","@opentelemetry/api":">=1.0.0"},"peerDependenciesMeta":{"ioredis":{"optional":true},"@ai-sdk/provider":{"optional":true},"@opentelemetry/api":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/ai-sdk-rate-limiter_0.7.0_1773216577523_0.05985264660309997","host":"s3://npm-registry-packages-npm-production"}},"0.7.1":{"name":"ai-sdk-rate-limiter","version":"0.7.1","keywords":["ai","rate-limit","openai","anthropic","vercel-ai-sdk","llm","queue","retry","cost-tracking","middleware"],"license":"MIT","_id":"ai-sdk-rate-limiter@0.7.1","maintainers":[{"name":"piyushgupta344","email":"piyushgupta.civil@gmail.com"}],"bin":{"ai-sdk-rate-limiter":"dist/cli.js"},"dist":{"shasum":"a0a86079df31edcefcdfced0c4580811b43fd7b3","tarball":"https://registry.npmjs.org/ai-sdk-rate-limiter/-/ai-sdk-rate-limiter-0.7.1.tgz","fileCount":29,"integrity":"sha512-XCmExKVc4Ta9mHCPMmMRL52YLG+ayc8+vnlibyW3vkEUuJ60iCwg5yDcLBKqOBLcR2/FzARs2ANIvRZ0Gh7yAQ==","signatures":[{"sig":"MEUCIAxD3kdOnTQyj58floLyehVwn79Xc3VxEu4gOcYzUI1uAiEA/r6WAzoL4m339RhE9jjhTsQPmMuT+12AaDoq+A7JQEk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1064906},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./otel":{"import":{"types":"./dist/otel.d.ts","default":"./dist/otel.js"},"require":{"types":"./dist/otel.d.cts","default":"./dist/otel.cjs"}},"./redis":{"import":{"types":"./dist/redis.d.ts","default":"./dist/redis.js"},"require":{"types":"./dist/redis.d.cts","default":"./dist/redis.cjs"}},"./testing":{"import":{"types":"./dist/testing.d.ts","default":"./dist/testing.js"},"require":{"types":"./dist/testing.d.cts","default":"./dist/testing.cjs"}}},"gitHead":"06737e3a3465f4c2ca5329e82ac4219a5e076933","scripts":{"dev":"tsup --watch","lint":"tsc --noEmit","test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"piyushgupta344","email":"piyushgupta.civil@gmail.com"},"_npmVersion":"11.3.0","description":"Smart rate limiting, queuing, and cost tracking middleware for AI SDK calls. Works across providers.","directories":{},"_nodeVersion":"24.1.0","_hasShrinkwrap":false,"devDependencies":{"ai":"^6.0.0","tsup":"^8.5.0","vitest":"^3.0.0","typescript":"^5.8.0","@ai-sdk/provider":"^3.0.8"},"peerDependencies":{"ioredis":">=4.0.0","@ai-sdk/provider":">=1.0.0","@opentelemetry/api":">=1.0.0"},"peerDependenciesMeta":{"ioredis":{"optional":true},"@ai-sdk/provider":{"optional":true},"@opentelemetry/api":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/ai-sdk-rate-limiter_0.7.1_1773221943162_0.023214996802254362","host":"s3://npm-registry-packages-npm-production"}},"0.8.0":{"name":"ai-sdk-rate-limiter","version":"0.8.0","keywords":["ai","rate-limit","openai","anthropic","vercel-ai-sdk","llm","queue","retry","cost-tracking","middleware"],"license":"MIT","_id":"ai-sdk-rate-limiter@0.8.0","maintainers":[{"name":"piyushgupta344","email":"piyushgupta.civil@gmail.com"}],"bin":{"ai-sdk-rate-limiter":"dist/cli.js"},"dist":{"shasum":"5aa4b95fd612290413eb84630dd2b7a1ae8f5f59","tarball":"https://registry.npmjs.org/ai-sdk-rate-limiter/-/ai-sdk-rate-limiter-0.8.0.tgz","fileCount":29,"integrity":"sha512-V7LkAK8F2otKe2tgGxEPDvOaVL78gbiwUFW4KHF6LxNmJkJjhvai3PukneHBXdZrL4kT9oGUnnRvbW2FUpik7g==","signatures":[{"sig":"MEUCIHYV8Z1Nrah6FGH1G35FcFK/FwFb4zl18JVGdwZ6GxOCAiEApxnLV5JER4wMAjevXDCVZk+FfwCA83CATXHG25yGJIE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1162540},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./otel":{"import":{"types":"./dist/otel.d.ts","default":"./dist/otel.js"},"require":{"types":"./dist/otel.d.cts","default":"./dist/otel.cjs"}},"./redis":{"import":{"types":"./dist/redis.d.ts","default":"./dist/redis.js"},"require":{"types":"./dist/redis.d.cts","default":"./dist/redis.cjs"}},"./testing":{"import":{"types":"./dist/testing.d.ts","default":"./dist/testing.js"},"require":{"types":"./dist/testing.d.cts","default":"./dist/testing.cjs"}}},"gitHead":"900e48880778cb910fe19000ae418c306c6d23bf","scripts":{"dev":"tsup --watch","lint":"tsc --noEmit","test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"piyushgupta344","email":"piyushgupta.civil@gmail.com"},"_npmVersion":"11.3.0","description":"Smart rate limiting, queuing, and cost tracking middleware for AI SDK calls. Works across providers.","directories":{},"_nodeVersion":"24.1.0","_hasShrinkwrap":false,"devDependencies":{"ai":"^6.0.0","tsup":"^8.5.0","vitest":"^3.0.0","typescript":"^5.8.0","@ai-sdk/provider":"^3.0.8"},"peerDependencies":{"ioredis":">=4.0.0","@ai-sdk/provider":">=1.0.0","@opentelemetry/api":">=1.0.0"},"peerDependenciesMeta":{"ioredis":{"optional":true},"@ai-sdk/provider":{"optional":true},"@opentelemetry/api":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/ai-sdk-rate-limiter_0.8.0_1773223571065_0.2699475502362565","host":"s3://npm-registry-packages-npm-production"}},"0.9.0":{"name":"ai-sdk-rate-limiter","version":"0.9.0","keywords":["ai","rate-limit","openai","anthropic","vercel-ai-sdk","llm","queue","retry","cost-tracking","middleware"],"license":"MIT","_id":"ai-sdk-rate-limiter@0.9.0","maintainers":[{"name":"piyushgupta344","email":"piyushgupta.civil@gmail.com"}],"bin":{"ai-sdk-rate-limiter":"dist/cli.js"},"dist":{"shasum":"4849fba36f8061ea926332d9134305daa7717d67","tarball":"https://registry.npmjs.org/ai-sdk-rate-limiter/-/ai-sdk-rate-limiter-0.9.0.tgz","fileCount":41,"integrity":"sha512-/+JDLW0e3Zf7cO/2oxNlgccyFi1i4p7lqI3OW0dBu3HYUJ8n0DP1xotnQXXzf91r1tlxsrfXqfkV1Q42U6w0VQ==","signatures":[{"sig":"MEUCICfqGnNYBmYhq8zaNMOadlNaNdvHDKqoMnMXlv36JQMyAiEA74twlVf6xuE4s1w8eP3HPali1JXlZclp1Tj1vHYsl0U=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1412000},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./otel":{"import":{"types":"./dist/otel.d.ts","default":"./dist/otel.js"},"require":{"types":"./dist/otel.d.cts","default":"./dist/otel.cjs"}},"./redis":{"import":{"types":"./dist/redis.d.ts","default":"./dist/redis.js"},"require":{"types":"./dist/redis.d.cts","default":"./dist/redis.cjs"}},"./statsd":{"import":{"types":"./dist/statsd.d.ts","default":"./dist/statsd.js"},"require":{"types":"./dist/statsd.d.cts","default":"./dist/statsd.cjs"}},"./testing":{"import":{"types":"./dist/testing.d.ts","default":"./dist/testing.js"},"require":{"types":"./dist/testing.d.cts","default":"./dist/testing.cjs"}},"./prometheus":{"import":{"types":"./dist/prometheus.d.ts","default":"./dist/prometheus.js"},"require":{"types":"./dist/prometheus.d.cts","default":"./dist/prometheus.cjs"}}},"gitHead":"66fedeb837cef2b7c12ccd314cca0ee89921a745","scripts":{"dev":"tsup --watch","lint":"tsc --noEmit","test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"piyushgupta344","email":"piyushgupta.civil@gmail.com"},"_npmVersion":"11.3.0","description":"Smart rate limiting, queuing, and cost tracking middleware for AI SDK calls. Works across providers.","directories":{},"_nodeVersion":"24.1.0","_hasShrinkwrap":false,"devDependencies":{"ai":"^6.0.0","tsup":"^8.5.0","vitest":"^3.0.0","typescript":"^5.8.0","@ai-sdk/provider":"^3.0.8"},"peerDependencies":{"ioredis":">=4.0.0","@ai-sdk/provider":">=1.0.0","@opentelemetry/api":">=1.0.0"},"peerDependenciesMeta":{"ioredis":{"optional":true},"@ai-sdk/provider":{"optional":true},"@opentelemetry/api":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/ai-sdk-rate-limiter_0.9.0_1773284978959_0.060107641889380004","host":"s3://npm-registry-packages-npm-production"}},"0.10.0":{"name":"ai-sdk-rate-limiter","version":"0.10.0","keywords":["ai","rate-limit","openai","anthropic","vercel-ai-sdk","llm","queue","retry","cost-tracking","middleware"],"license":"MIT","_id":"ai-sdk-rate-limiter@0.10.0","maintainers":[{"name":"piyushgupta344","email":"piyushgupta.civil@gmail.com"}],"bin":{"ai-sdk-rate-limiter":"dist/cli.js"},"dist":{"shasum":"cd6319af607fbd51ce4b63f7723b5c6c17b1a482","tarball":"https://registry.npmjs.org/ai-sdk-rate-limiter/-/ai-sdk-rate-limiter-0.10.0.tgz","fileCount":49,"integrity":"sha512-giDiHnloQI67Hlpmh3ryxFfUYE6NS8q3wn1AdZEWvUzjtZzXUiRKP+ZFNSLOK2q0gkPj7eYDJgwTXbFdijd7+Q==","signatures":[{"sig":"MEQCIEuQDzXR9c0JTeraWGk2GlNhVkIEkPoILU9zTAl2UuULAiAJt48fF+wtrNHixCudymGR2oMXtM+eoks4QpvUa2VIwA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1489405},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./otel":{"import":{"types":"./dist/otel.d.ts","default":"./dist/otel.js"},"require":{"types":"./dist/otel.d.cts","default":"./dist/otel.cjs"}},"./redis":{"import":{"types":"./dist/redis.d.ts","default":"./dist/redis.js"},"require":{"types":"./dist/redis.d.cts","default":"./dist/redis.cjs"}},"./statsd":{"import":{"types":"./dist/statsd.d.ts","default":"./dist/statsd.js"},"require":{"types":"./dist/statsd.d.cts","default":"./dist/statsd.cjs"}},"./testing":{"import":{"types":"./dist/testing.d.ts","default":"./dist/testing.js"},"require":{"types":"./dist/testing.d.cts","default":"./dist/testing.cjs"}},"./middleware":{"import":{"types":"./dist/middleware.d.ts","default":"./dist/middleware.js"},"require":{"types":"./dist/middleware.d.cts","default":"./dist/middleware.cjs"}},"./prometheus":{"import":{"types":"./dist/prometheus.d.ts","default":"./dist/prometheus.js"},"require":{"types":"./dist/prometheus.d.cts","default":"./dist/prometheus.cjs"}}},"gitHead":"aeed577bbf77b93ebddadb34130124312744d8c5","scripts":{"dev":"tsup --watch","lint":"tsc --noEmit","test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"piyushgupta344","email":"piyushgupta.civil@gmail.com"},"_npmVersion":"11.3.0","description":"Smart rate limiting, queuing, and cost tracking middleware for AI SDK calls. Works across providers.","directories":{},"_nodeVersion":"24.1.0","_hasShrinkwrap":false,"devDependencies":{"ai":"^6.0.0","tsup":"^8.5.0","vitest":"^3.0.0","typescript":"^5.8.0","@types/node":"^22.19.15","@ai-sdk/provider":"^3.0.8"},"peerDependencies":{"ioredis":">=4.0.0","@ai-sdk/provider":">=1.0.0","@opentelemetry/api":">=1.0.0"},"peerDependenciesMeta":{"ioredis":{"optional":true},"@ai-sdk/provider":{"optional":true},"@opentelemetry/api":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/ai-sdk-rate-limiter_0.10.0_1773297320168_0.31460507136890303","host":"s3://npm-registry-packages-npm-production"}},"0.11.0":{"name":"ai-sdk-rate-limiter","version":"0.11.0","keywords":["ai","rate-limit","openai","anthropic","vercel-ai-sdk","llm","queue","retry","cost-tracking","middleware"],"license":"MIT","_id":"ai-sdk-rate-limiter@0.11.0","maintainers":[{"name":"piyushgupta344","email":"piyushgupta.civil@gmail.com"}],"bin":{"ai-sdk-rate-limiter":"dist/cli.js"},"dist":{"shasum":"b875260bf4886011a23b5714a27ae8af89a195fb","tarball":"https://registry.npmjs.org/ai-sdk-rate-limiter/-/ai-sdk-rate-limiter-0.11.0.tgz","fileCount":49,"integrity":"sha512-maOALtDS1VcawHd+okC+NXS/YauU8gmDS6tXFUHoqdQr8VNRY7QdkE+v35q7Vsn6PBbRr+FIvVUSYn5CQyqPHA==","signatures":[{"sig":"MEYCIQCSNGf/KI2bmm9nmZtKTSLRVgq4UbBtKQa7If2R0hKxRAIhAIpIc0YccGh49Pv/IsjfOn4HF7GlAad1x1CZgLuxgX1s","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1542568},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./otel":{"import":{"types":"./dist/otel.d.ts","default":"./dist/otel.js"},"require":{"types":"./dist/otel.d.cts","default":"./dist/otel.cjs"}},"./redis":{"import":{"types":"./dist/redis.d.ts","default":"./dist/redis.js"},"require":{"types":"./dist/redis.d.cts","default":"./dist/redis.cjs"}},"./statsd":{"import":{"types":"./dist/statsd.d.ts","default":"./dist/statsd.js"},"require":{"types":"./dist/statsd.d.cts","default":"./dist/statsd.cjs"}},"./testing":{"import":{"types":"./dist/testing.d.ts","default":"./dist/testing.js"},"require":{"types":"./dist/testing.d.cts","default":"./dist/testing.cjs"}},"./middleware":{"import":{"types":"./dist/middleware.d.ts","default":"./dist/middleware.js"},"require":{"types":"./dist/middleware.d.cts","default":"./dist/middleware.cjs"}},"./prometheus":{"import":{"types":"./dist/prometheus.d.ts","default":"./dist/prometheus.js"},"require":{"types":"./dist/prometheus.d.cts","default":"./dist/prometheus.cjs"}}},"gitHead":"a5cc6be904d12366e41cd79a8cdc472e29bcc019","scripts":{"dev":"tsup --watch","lint":"tsc --noEmit","test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"piyushgupta344","email":"piyushgupta.civil@gmail.com"},"_npmVersion":"11.3.0","description":"Smart rate limiting, queuing, and cost tracking middleware for AI SDK calls. Works across providers.","directories":{},"_nodeVersion":"24.1.0","_hasShrinkwrap":false,"devDependencies":{"ai":"^6.0.0","tsup":"^8.5.0","vitest":"^3.0.0","typescript":"^5.8.0","@types/node":"^22.19.15","@ai-sdk/provider":"^3.0.8"},"peerDependencies":{"ioredis":">=4.0.0","@ai-sdk/provider":">=1.0.0","@opentelemetry/api":">=1.0.0"},"peerDependenciesMeta":{"ioredis":{"optional":true},"@ai-sdk/provider":{"optional":true},"@opentelemetry/api":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/ai-sdk-rate-limiter_0.11.0_1773322263987_0.22201726121804266","host":"s3://npm-registry-packages-npm-production"}},"0.12.0":{"name":"ai-sdk-rate-limiter","version":"0.12.0","keywords":["ai","rate-limit","openai","anthropic","vercel-ai-sdk","llm","queue","retry","cost-tracking","middleware"],"license":"MIT","_id":"ai-sdk-rate-limiter@0.12.0","maintainers":[{"name":"piyushgupta344","email":"piyushgupta.civil@gmail.com"}],"bin":{"ai-sdk-rate-limiter":"dist/cli.js"},"dist":{"shasum":"13746bbd02c1a066a14771aa90d24912270a2ece","tarball":"https://registry.npmjs.org/ai-sdk-rate-limiter/-/ai-sdk-rate-limiter-0.12.0.tgz","fileCount":49,"integrity":"sha512-Sk//+oiE4/HZSD1rzoWIin/a3cbSahLni05V+xQS7Rvso3lSGZKyc8Q8yuQZw+Mk4RglogDnHRNXQMShecFYcA==","signatures":[{"sig":"MEYCIQDWgLGOkLuyeh91LJjk6Mwx3w3zxw2TObsJKS4pF8VvzQIhAMA2/WjeOqBEm8sMTn8CrzxduTealFlYZkaD1fmPVv+t","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1583245},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./otel":{"import":{"types":"./dist/otel.d.ts","default":"./dist/otel.js"},"require":{"types":"./dist/otel.d.cts","default":"./dist/otel.cjs"}},"./redis":{"import":{"types":"./dist/redis.d.ts","default":"./dist/redis.js"},"require":{"types":"./dist/redis.d.cts","default":"./dist/redis.cjs"}},"./statsd":{"import":{"types":"./dist/statsd.d.ts","default":"./dist/statsd.js"},"require":{"types":"./dist/statsd.d.cts","default":"./dist/statsd.cjs"}},"./testing":{"import":{"types":"./dist/testing.d.ts","default":"./dist/testing.js"},"require":{"types":"./dist/testing.d.cts","default":"./dist/testing.cjs"}},"./middleware":{"import":{"types":"./dist/middleware.d.ts","default":"./dist/middleware.js"},"require":{"types":"./dist/middleware.d.cts","default":"./dist/middleware.cjs"}},"./prometheus":{"import":{"types":"./dist/prometheus.d.ts","default":"./dist/prometheus.js"},"require":{"types":"./dist/prometheus.d.cts","default":"./dist/prometheus.cjs"}}},"gitHead":"55e42cd94c56f1af1db54c8a64f400710b395005","scripts":{"dev":"tsup --watch","lint":"tsc --noEmit","test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"piyushgupta344","email":"piyushgupta.civil@gmail.com"},"_npmVersion":"11.3.0","description":"Smart rate limiting, queuing, and cost tracking middleware for AI SDK calls. Works across providers.","directories":{},"_nodeVersion":"24.1.0","_hasShrinkwrap":false,"devDependencies":{"ai":"^6.0.0","tsup":"^8.5.0","vitest":"^3.0.0","typescript":"^5.8.0","@types/node":"^22.19.15","@ai-sdk/provider":"^3.0.8"},"peerDependencies":{"ioredis":">=4.0.0","@ai-sdk/provider":">=1.0.0","@opentelemetry/api":">=1.0.0"},"peerDependenciesMeta":{"ioredis":{"optional":true},"@ai-sdk/provider":{"optional":true},"@opentelemetry/api":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/ai-sdk-rate-limiter_0.12.0_1773322771845_0.39696204647183064","host":"s3://npm-registry-packages-npm-production"}},"0.13.0":{"name":"ai-sdk-rate-limiter","version":"0.13.0","keywords":["ai","rate-limit","openai","anthropic","vercel-ai-sdk","llm","queue","retry","cost-tracking","middleware"],"license":"MIT","_id":"ai-sdk-rate-limiter@0.13.0","maintainers":[{"name":"piyushgupta344","email":"piyushgupta.civil@gmail.com"}],"homepage":"https://piyushgupta344.github.io/ai-sdk-rate-limiter/","bugs":{"url":"https://github.com/piyushgupta344/ai-sdk-rate-limiter/issues"},"bin":{"ai-sdk-rate-limiter":"dist/cli.js"},"dist":{"shasum":"d8d37ac7779a0dbc8041bb854f970e982317f0a8","tarball":"https://registry.npmjs.org/ai-sdk-rate-limiter/-/ai-sdk-rate-limiter-0.13.0.tgz","fileCount":51,"integrity":"sha512-M+1R4jloRwJzs+yW9bBS4k7JUDHQA5jpNykdsvwqoyCEpu5DKyjj4Di5x4dijnJEPR4PnRMwOvxhmabQjYM8CA==","signatures":[{"sig":"MEUCIQDJi6zH6zvUhNSMgJXsV3yvWowGvviWS03zdgSP+YNztgIgEZqszYT0Up6u1mf4YlxLPrd/DqNMrhqmDLJ0tj+iHg8=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/ai-sdk-rate-limiter@0.13.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":1640403},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=20.0.0"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./otel":{"import":{"types":"./dist/otel.d.ts","default":"./dist/otel.js"},"require":{"types":"./dist/otel.d.cts","default":"./dist/otel.cjs"}},"./redis":{"import":{"types":"./dist/redis.d.ts","default":"./dist/redis.js"},"require":{"types":"./dist/redis.d.cts","default":"./dist/redis.cjs"}},"./statsd":{"import":{"types":"./dist/statsd.d.ts","default":"./dist/statsd.js"},"require":{"types":"./dist/statsd.d.cts","default":"./dist/statsd.cjs"}},"./testing":{"import":{"types":"./dist/testing.d.ts","default":"./dist/testing.js"},"require":{"types":"./dist/testing.d.cts","default":"./dist/testing.cjs"}},"./middleware":{"import":{"types":"./dist/middleware.d.ts","default":"./dist/middleware.js"},"require":{"types":"./dist/middleware.d.cts","default":"./dist/middleware.cjs"}},"./prometheus":{"import":{"types":"./dist/prometheus.d.ts","default":"./dist/prometheus.js"},"require":{"types":"./dist/prometheus.d.cts","default":"./dist/prometheus.cjs"}}},"gitHead":"6a952794a77d279ad2809c7c13185b76a5f06d05","scripts":{"dev":"tsup --watch","lint":"tsc --noEmit","test":"vitest run","build":"tsup","docs:dev":"vitepress dev docs","typecheck":"tsc --noEmit","docs:build":"vitepress build docs","test:watch":"vitest","docs:preview":"vitepress preview docs","test:coverage":"vitest run --coverage","prepublishOnly":"npm run typecheck && npm test && npm run build"},"_npmUser":{"name":"piyushgupta344","email":"piyushgupta.civil@gmail.com"},"overrides":{"esbuild":">=0.25.0"},"repository":{"url":"git+https://github.com/piyushgupta344/ai-sdk-rate-limiter.git","type":"git"},"_npmVersion":"10.9.4","description":"Smart rate limiting, queuing, and cost tracking middleware for AI SDK calls. Works across providers.","directories":{},"sideEffects":false,"_nodeVersion":"22.22.1","_hasShrinkwrap":false,"devDependencies":{"ai":"^6.0.0","tsup":"^8.5.0","vitest":"^4.1.0","vitepress":"^1.6.4","typescript":"^5.8.0","@types/node":"^25.5.0","@ai-sdk/provider":"^3.0.8","@vitest/coverage-v8":"^4.1.0"},"peerDependencies":{"ioredis":">=4.0.0","@ai-sdk/provider":">=1.0.0","@opentelemetry/api":">=1.0.0"},"peerDependenciesMeta":{"ioredis":{"optional":true},"@ai-sdk/provider":{"optional":true},"@opentelemetry/api":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/ai-sdk-rate-limiter_0.13.0_1773378899481_0.6152743931742015","host":"s3://npm-registry-packages-npm-production"}},"0.13.1":{"name":"ai-sdk-rate-limiter","version":"0.13.1","description":"Smart rate limiting, queuing, and cost tracking middleware for AI SDK calls. Works across providers.","repository":{"type":"git","url":"git+https://github.com/piyushgupta344/ai-sdk-rate-limiter.git"},"bugs":{"url":"https://github.com/piyushgupta344/ai-sdk-rate-limiter/issues"},"homepage":"https://piyushgupta344.github.io/ai-sdk-rate-limiter/","type":"module","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./redis":{"import":{"types":"./dist/redis.d.ts","default":"./dist/redis.js"},"require":{"types":"./dist/redis.d.cts","default":"./dist/redis.cjs"}},"./otel":{"import":{"types":"./dist/otel.d.ts","default":"./dist/otel.js"},"require":{"types":"./dist/otel.d.cts","default":"./dist/otel.cjs"}},"./testing":{"import":{"types":"./dist/testing.d.ts","default":"./dist/testing.js"},"require":{"types":"./dist/testing.d.cts","default":"./dist/testing.cjs"}},"./prometheus":{"import":{"types":"./dist/prometheus.d.ts","default":"./dist/prometheus.js"},"require":{"types":"./dist/prometheus.d.cts","default":"./dist/prometheus.cjs"}},"./statsd":{"import":{"types":"./dist/statsd.d.ts","default":"./dist/statsd.js"},"require":{"types":"./dist/statsd.d.cts","default":"./dist/statsd.cjs"}},"./middleware":{"import":{"types":"./dist/middleware.d.ts","default":"./dist/middleware.js"},"require":{"types":"./dist/middleware.d.cts","default":"./dist/middleware.cjs"}}},"bin":{"ai-sdk-rate-limiter":"dist/cli.js"},"sideEffects":false,"scripts":{"prepublishOnly":"npm run typecheck && npm test && npm run build","build":"tsup","dev":"tsup --watch","test":"vitest run","test:coverage":"vitest run --coverage","test:watch":"vitest","typecheck":"tsc --noEmit","lint":"tsc --noEmit","docs:dev":"vitepress dev docs","docs:build":"vitepress build docs","docs:preview":"vitepress preview docs"},"keywords":["ai","rate-limit","openai","anthropic","vercel-ai-sdk","llm","queue","retry","cost-tracking","middleware"],"license":"MIT","devDependencies":{"@ai-sdk/provider":"^3.0.8","@types/node":"^25.5.0","@vitest/coverage-v8":"^4.1.0","ai":"^6.0.0","tsup":"^8.5.0","typescript":"^5.8.0","vitepress":"^1.6.4","vitest":"^4.1.0"},"peerDependencies":{"@ai-sdk/provider":">=1.0.0","@opentelemetry/api":">=1.0.0","ioredis":">=4.0.0"},"peerDependenciesMeta":{"@ai-sdk/provider":{"optional":true},"ioredis":{"optional":true},"@opentelemetry/api":{"optional":true}},"engines":{"node":">=20.0.0"},"overrides":{"esbuild":">=0.25.0"},"_id":"ai-sdk-rate-limiter@0.13.1","gitHead":"c66a1482a605f1f060da4be316a9176e08154053","_nodeVersion":"22.22.1","_npmVersion":"10.9.4","dist":{"integrity":"sha512-WncaSIaiRFkwRTKEfpYagsTnsMyQDsXfhl1FBpyzHcoCbI21yKhR6Tm0ZQB6O9yvhF34p2sb/P9Dqmz9DMPnvQ==","shasum":"54d13100e647e1fd3f188b6380f51838bd032a0a","tarball":"https://registry.npmjs.org/ai-sdk-rate-limiter/-/ai-sdk-rate-limiter-0.13.1.tgz","fileCount":51,"unpackedSize":1640639,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/ai-sdk-rate-limiter@0.13.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIBoUEEdSt2QaGCUNtWhh/IYeSdf0KhusGoavDXcfW5ATAiBGErfg2qr2wwkaYGUJVnmdgb/HJ1tLl19v1eTMuMm2SQ=="}]},"_npmUser":{"name":"piyushgupta344","email":"piyushgupta.civil@gmail.com"},"directories":{},"maintainers":[{"name":"piyushgupta344","email":"piyushgupta.civil@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/ai-sdk-rate-limiter_0.13.1_1773630435016_0.16310118036348742"},"_hasShrinkwrap":false}},"time":{"created":"2026-03-11T02:07:56.030Z","modified":"2026-03-16T03:07:15.536Z","0.1.0":"2026-03-11T02:07:56.183Z","0.2.0":"2026-03-11T03:08:33.160Z","0.3.0":"2026-03-11T04:18:32.527Z","0.4.0":"2026-03-11T04:29:00.650Z","0.5.0":"2026-03-11T05:11:09.856Z","0.6.0":"2026-03-11T07:29:06.570Z","0.7.0":"2026-03-11T08:09:37.730Z","0.7.1":"2026-03-11T09:39:03.354Z","0.8.0":"2026-03-11T10:06:11.258Z","0.9.0":"2026-03-12T03:09:39.191Z","0.10.0":"2026-03-12T06:35:20.447Z","0.11.0":"2026-03-12T13:31:04.149Z","0.12.0":"2026-03-12T13:39:32.009Z","0.13.0":"2026-03-13T05:14:59.678Z","0.13.1":"2026-03-16T03:07:15.219Z"},"bugs":{"url":"https://github.com/piyushgupta344/ai-sdk-rate-limiter/issues"},"license":"MIT","homepage":"https://piyushgupta344.github.io/ai-sdk-rate-limiter/","keywords":["ai","rate-limit","openai","anthropic","vercel-ai-sdk","llm","queue","retry","cost-tracking","middleware"],"repository":{"type":"git","url":"git+https://github.com/piyushgupta344/ai-sdk-rate-limiter.git"},"description":"Smart rate limiting, queuing, and cost tracking middleware for AI SDK calls. Works across providers.","maintainers":[{"name":"piyushgupta344","email":"piyushgupta.civil@gmail.com"}],"readme":"# ai-sdk-rate-limiter\n\nSmart rate limiting, queuing, and cost tracking for AI API calls. Works across providers. Zero required dependencies.\n\n[![npm](https://img.shields.io/npm/v/ai-sdk-rate-limiter)](https://www.npmjs.com/package/ai-sdk-rate-limiter)\n[![CI](https://github.com/piyushgupta344/ai-sdk-rate-limiter/actions/workflows/ci.yml/badge.svg)](https://github.com/piyushgupta344/ai-sdk-rate-limiter/actions/workflows/ci.yml)\n[![codecov](https://codecov.io/gh/piyushgupta344/ai-sdk-rate-limiter/graph/badge.svg)](https://codecov.io/gh/piyushgupta344/ai-sdk-rate-limiter)\n[![npm downloads](https://img.shields.io/npm/dm/ai-sdk-rate-limiter)](https://www.npmjs.com/package/ai-sdk-rate-limiter)\n[![docs](https://img.shields.io/badge/docs-piyushgupta344.github.io-blue)](https://piyushgupta344.github.io/ai-sdk-rate-limiter/)\n\n```\nnpm install ai-sdk-rate-limiter\n```\n\n**[Documentation →](https://piyushgupta344.github.io/ai-sdk-rate-limiter/)**\n\n---\n\n## The problem\n\nEvery developer building with LLMs hits this eventually:\n\n- `429 Too Many Requests` crashes a production request mid-flight\n- You retry immediately and burn through your remaining quota\n- Rate limits differ per model, per tier, per provider — none documented uniformly\n- Your Node.js server runs 4 instances. They race against the same API quota\n- A bulk job spends $300 overnight and nobody notices until the bill arrives\n- You have no idea which model is responsible for the cost spike\n- In a multi-tenant app, one user's burst shouldn't block everyone else\n\nEvery existing tool solves one of these. This solves all of them.\n\n---\n\n## Quick start\n\n```typescript\nimport { createRateLimiter } from 'ai-sdk-rate-limiter'\nimport { openai } from '@ai-sdk/openai'\nimport { generateText, streamText } from 'ai'\n\nconst limiter = createRateLimiter()\n\n// Wrap any Vercel AI SDK model — that's it\nconst model = limiter.wrap(openai('gpt-4o'))\n\nconst { text } = await generateText({ model, prompt: 'Hello!' })\n```\n\nThe wrapped model is a drop-in replacement. Every Vercel AI SDK feature — streaming, tool calls, structured output — works exactly as before.\n\n---\n\n## What it does\n\n**Rate limiting** — Tracks requests and tokens in a 60-second sliding window per model. When a limit is reached, requests queue automatically rather than crashing.\n\n**Priority queuing** — Queued requests drain in priority order (`high` before `normal` before `low`), FIFO within the same priority. Your user-facing requests skip ahead of background jobs.\n\n**Concurrency limits** — Optional `maxConcurrent` cap per model enforced as a semaphore. Requests queue behind in-flight ones, then release slots as they complete.\n\n**Smart retry** — Retries on 429, 500, 502, 503, 504 with exponential backoff + jitter. Honors the `Retry-After` header exactly — if the API says wait 3 seconds, waits 3 seconds, not 30.\n\n**Cost tracking** — Records actual token usage from every response. Reports hourly, daily, and monthly spend per model. Optionally enforces budget caps.\n\n**Multi-tenant scoped limits** — Give each user or org its own isolated rate limit window without running separate limiter instances. Wildcard patterns match user tiers.\n\n**AbortSignal propagation** — Cancelling a request (e.g. user navigates away) immediately removes it from the queue. No wasted API calls for abandoned requests.\n\n**Built-in model registry** — Knows the RPM, ITPM, and per-token pricing for every major OpenAI, Anthropic, Google, Groq, Mistral, and Cohere model out of the box. Nothing to configure to get started.\n\n**Raw SDK support** — Works with the native OpenAI, Anthropic, Groq, Mistral, and Cohere SDKs directly via a transparent JavaScript Proxy. No Vercel AI SDK required.\n\n**Circuit breaker** — Automatically opens on repeated 5xx failures, blocking requests until the upstream recovers. Transitions to half-open state to probe recovery, then closes once healthy.\n\n**Graceful shutdown** — `limiter.shutdown()` drains in-flight requests before the process exits. New requests received during shutdown are rejected with `ShutdownError`.\n\n**Persistent cost tracking** — `RedisCostStore` (from `ai-sdk-rate-limiter/redis`) survives process restarts so budget caps remain accurate. `warmUp()` pre-loads historical spend from the store on startup.\n\n**Per-scope cost attribution** — `getCostReport()` includes a `byScope` breakdown so you can see exact spend per user, org, or tenant.\n\n**Auto-detected limits** — Parses `x-ratelimit-limit-*` headers from every response and tightens the local windows automatically. Your config always wins; detected values fill in where you haven't overridden.\n\n**Prometheus metrics** — `createPrometheusPlugin()` (from `ai-sdk-rate-limiter/prometheus`) exports counters, gauges, and histograms for every request, token, cost, retry, and queue event.\n\n**StatsD / DogStatsD** — `createStatsDPlugin()` (from `ai-sdk-rate-limiter/statsd`) bridges all events to any StatsD-compatible client.\n\n**Call timeout** — `callTimeout` option kills a hung AI call after N milliseconds via `Promise.race()` — independent of the Vercel AI SDK `abortSignal`.\n\n**Fallback chains** — `fallback` now accepts an array of models. On `BudgetExceededError`, the chain is walked in order until one succeeds.\n\n**Express / Hono middleware** — `createRateLimiterMiddleware()` (from `ai-sdk-rate-limiter/middleware`) attaches `req.rateLimiter` to every request and converts rate-limiter errors to proper HTTP responses at the middleware layer — no per-route boilerplate.\n\n**OpenTelemetry** — Drop-in OTel plugin that emits GenAI-spec spans for every request. Works with any OTel-compatible tracer.\n\n**Testing utilities** — `createTestLimiter()` records every completed call so you can assert on model usage, token counts, and costs in unit tests.\n\n**CLI audit** — `npx ai-sdk-rate-limiter audit` probes your API keys to detect your actual tier limits and generates a ready-to-paste config override.\n\n---\n\n## Contents\n\n- [Vercel AI SDK usage](#vercel-ai-sdk-usage)\n- [Raw SDK proxy](#raw-sdk-proxy)\n- [Configuration reference](#configuration-reference)\n- [Per-request options](#per-request-options)\n- [Multi-tenant scoped limits](#multi-tenant-scoped-limits)\n- [Concurrency limits](#concurrency-limits)\n- [AbortSignal support](#abortsignal-support)\n- [Call timeout](#call-timeout)\n- [Cost tracking](#cost-tracking)\n- [Budget fallback routing](#budget-fallback-routing)\n- [Persistent cost tracking](#persistent-cost-tracking)\n- [Multi-instance Redis store](#multi-instance-redis-store)\n- [Circuit breaker](#circuit-breaker)\n- [Graceful shutdown](#graceful-shutdown)\n- [Prometheus metrics](#prometheus-metrics)\n- [StatsD metrics](#statsD-metrics)\n- [Express / Hono middleware](#express--hono-middleware)\n- [Events](#events)\n- [Backpressure](#backpressure)\n- [Error handling](#error-handling)\n- [OpenTelemetry](#opentelemetry)\n- [Testing utilities](#testing-utilities)\n- [CLI audit](#cli-audit)\n- [Model registry](#model-registry)\n- [Advanced usage](#advanced-usage)\n- [How it works](#how-it-works)\n- [Comparison](#comparison)\n- [TypeScript](#typescript)\n- [Requirements](#requirements)\n\n---\n\n## Vercel AI SDK usage\n\n### Basic wrap\n\n```typescript\nimport { createRateLimiter } from 'ai-sdk-rate-limiter'\nimport { openai } from '@ai-sdk/openai'\nimport { generateText, streamText } from 'ai'\n\nconst limiter = createRateLimiter()\nconst model = limiter.wrap(openai('gpt-4o'))\n\n// generateText\nconst { text } = await generateText({ model, prompt: 'Summarize this...' })\n\n// streamText — streaming is first-class, rate limit slot consumed at request start\nconst result = streamText({ model, messages })\nfor await (const chunk of result.textStream) {\n  process.stdout.write(chunk)\n}\n```\n\n### Using the raw middleware\n\nIf you use `wrapLanguageModel` directly or need to compose middleware:\n\n```typescript\nimport { wrapLanguageModel } from 'ai'\n\n// Single middleware\nconst model = wrapLanguageModel({\n  model: openai('gpt-4o'),\n  middleware: limiter.middleware,\n})\n\n// Composed with other middleware\nconst model = wrapLanguageModel({\n  model: openai('gpt-4o'),\n  middleware: [loggingMiddleware, limiter.middleware, cachingMiddleware],\n})\n```\n\n---\n\n## Raw SDK proxy\n\nIf you're using the OpenAI, Anthropic, Groq, Mistral, or Cohere SDK directly — without the Vercel AI SDK — use `limiter.rawProxy()` to add rate limiting as a transparent drop-in:\n\n```typescript\nimport { createRateLimiter } from 'ai-sdk-rate-limiter'\nimport OpenAI from 'openai'\nimport Anthropic from '@anthropic-ai/sdk'\n\nconst limiter = createRateLimiter({\n  cost: { budget: { daily: 50 }, onExceeded: 'throw' },\n  on: { rateLimited: ({ model }) => console.warn(`${model} rate limited`) },\n})\n\n// Every API call goes through the same rate limiter and cost tracker\nconst openai    = limiter.rawProxy(new OpenAI())\nconst anthropic = limiter.rawProxy(new Anthropic())\n\n// Use exactly as before — no other changes needed\nconst completion = await openai.chat.completions.create({\n  model: 'gpt-4o',\n  messages: [{ role: 'user', content: 'Hello!' }],\n})\n\nconst message = await anthropic.messages.create({\n  model: 'claude-opus-4-6',\n  max_tokens: 1024,\n  messages: [{ role: 'user', content: 'Hello!' }],\n})\n\n// Cost from both clients tracked together\nconst report = limiter.getCostReport()\n```\n\n**Streaming** — the proxy wraps the returned `AsyncIterable` to capture the final usage chunk automatically:\n\n```typescript\nconst stream = await openai.chat.completions.create({\n  model: 'gpt-4o',\n  messages: [{ role: 'user', content: 'Stream this' }],\n  stream: true,\n  stream_options: { include_usage: true }, // OpenAI: include usage in final chunk\n})\n\nfor await (const chunk of stream) {\n  process.stdout.write(chunk.choices[0]?.delta?.content ?? '')\n}\n// After the loop, tokens are recorded in limiter.getCostReport()\n```\n\n**Standalone — no shared limiter needed:**\n\n```typescript\nimport { rateLimited } from 'ai-sdk-rate-limiter'\n\nconst openai = rateLimited(new OpenAI(), {\n  config: { cost: { budget: { daily: 20 } } },\n})\n```\n\n**Override auto-detected provider** — useful for OpenAI-compatible endpoints:\n\n```typescript\nconst client = limiter.rawProxy(new OpenAI({ baseURL: 'https://api.groq.com/openai/v1' }), {\n  provider: 'groq', // use Groq's limits and pricing instead of OpenAI's\n})\n```\n\nProvider is auto-detected from the client's constructor name (`OpenAI` → `openai`, `Anthropic` → `anthropic`, `Groq` → `groq`, etc.).\n\n---\n\n## Configuration reference\n\nEverything has a sensible default. Override only what you need.\n\n```typescript\nconst limiter = createRateLimiter({\n  // Override or extend built-in model limits for your API tier\n  limits: {\n    'gpt-4o':          { rpm: 500, itpm: 30_000, maxConcurrent: 20 },\n    'claude-opus-4-6': { rpm: 50,  itpm: 20_000 },\n    // rpd  — requests per day (enforced in a rolling 24-hour window)\n    // otpm — output tokens per minute (based on actuals from completed requests)\n    'gemini-1.5-flash': { rpm: 15, rpd: 1_500, otpm: 500_000 },\n  },\n\n  // Cost budgets and behavior when exceeded\n  cost: {\n    budget: {\n      hourly:  5,     // USD — hard cap per hour\n      daily:   50,    // USD — hard cap per day\n      monthly: 500,   // USD — hard cap per month\n    },\n    onExceeded: 'throw', // 'throw' | 'queue' | 'fallback'\n    store: new RedisCostStore(redis), // persist cost history across restarts (optional)\n  },\n\n  // Circuit breaker — open on repeated 5xx failures, probe recovery automatically\n  circuit: {\n    failureThreshold: 5,     // consecutive failures before opening\n    cooldownMs:       30_000, // how long to stay open before probing\n    tripOn:           [500, 502, 503, 504], // which status codes count as failures\n  },\n\n  // Queue behavior\n  queue: {\n    maxSize: 500,     // max requests waiting; throws QueueFullError when full\n    timeout: 30_000,  // ms before a queued request times out with QueueTimeoutError\n    onFull: 'throw',  // or 'drop-low' — evict lowest-priority requests first\n  },\n\n  // Retry behavior\n  retry: {\n    maxAttempts:     4,              // total attempts including the first\n    backoff:         'exponential',  // 'exponential' | 'linear' | 'fixed'\n    baseDelay:       1_000,          // ms\n    maxDelay:        60_000,         // ms cap\n    jitter:          true,           // ±30% randomness (prevents thundering herd)\n    parseRetryAfter: true,           // honor Retry-After header from 429 responses\n    retryOn:         [429, 500, 502, 503, 504],\n    callTimeout:     30_000,         // ms — kill hung AI calls via Promise.race()\n  },\n\n  // Per-scope rate limit overrides for multi-tenant use cases\n  scopes: {\n    'user:free:*':  { rpm: 5,   itpm: 10_000 },\n    'user:pro:*':   { rpm: 60,  itpm: 200_000 },\n    'org:*':        { rpm: 300, maxConcurrent: 20 },\n  },\n\n  // Observability — see Events section for all available events\n  on: {\n    rateLimited: ({ model, source, resetAt }) =>\n      console.warn(`${model} rate limited (${source}), resets ${new Date(resetAt).toISOString()}`),\n    retrying:    ({ model, attempt, delayMs }) =>\n      console.log(`${model} retry ${attempt} in ${delayMs}ms`),\n    budgetHit:   ({ model, currentCostUsd, limitUsd, period }) =>\n      alerts.send(`${model} hit $${limitUsd} ${period} budget`),\n    completed:   ({ model, inputTokens, outputTokens, costUsd, latencyMs }) =>\n      metrics.record({ model, inputTokens, outputTokens, costUsd, latencyMs }),\n  },\n})\n```\n\n---\n\n## Per-request options\n\nPass options to individual requests via `providerOptions.rateLimiter`:\n\n```typescript\nimport { generateText } from 'ai'\n\n// High-priority — skips ahead of normal traffic in the queue\nawait generateText({\n  model,\n  prompt: 'Urgent user request...',\n  providerOptions: {\n    rateLimiter: {\n      priority:    'high',   // 'high' | 'normal' | 'low'\n      timeout:     10_000,   // override the default queue timeout for this request\n      callTimeout: 15_000,   // kill the AI call itself if it hangs beyond 15s\n    },\n  },\n})\n\n// Background job — yields to user-facing traffic\nawait generateText({\n  model,\n  prompt: 'Nightly batch summary...',\n  providerOptions: {\n    rateLimiter: { priority: 'low' },\n  },\n})\n\n// Per-request scope (overrides any static scope set in limiter.wrap())\nawait generateText({\n  model,\n  prompt: 'User message...',\n  providerOptions: {\n    rateLimiter: { scope: `user:${userId}` },\n  },\n})\n```\n\nThis is the recommended way to colocate user requests and background jobs on the same model without background jobs starving users.\n\n---\n\n## Multi-tenant scoped limits\n\nScoped limits give each user, org, or tenant its own isolated rate limit window. A burst from one user doesn't consume quota for anyone else.\n\n### Static scope on the model\n\n```typescript\n// Each user gets their own model with an isolated window\nfunction getModelForUser(userId: string) {\n  return limiter.wrap(openai('gpt-4o'), { scope: `user:${userId}` })\n}\n\n// user:alice has its own RPM/ITPM window independent of user:bob\nconst aliceModel = getModelForUser('alice')\nconst bobModel   = getModelForUser('bob')\n```\n\n### Per-request scope\n\n```typescript\n// Same wrapped model, different scope per call\nawait generateText({\n  model,\n  prompt: req.body.message,\n  providerOptions: {\n    rateLimiter: { scope: `user:${req.user.id}` },\n  },\n})\n```\n\nPer-request scope takes precedence over any static scope set in `limiter.wrap()`.\n\n### Defining scope-level limits\n\nUse `config.scopes` to define separate rate limits for each scope tier. Keys support `*` wildcards:\n\n```typescript\nconst limiter = createRateLimiter({\n  scopes: {\n    'user:free:*':  { rpm: 5,   itpm: 10_000  },  // free tier: 5 rpm each\n    'user:pro:*':   { rpm: 60,  itpm: 200_000 },  // pro tier: 60 rpm each\n    'org:*':        { rpm: 300, maxConcurrent: 20 },\n  },\n})\n\n// Each scope gets its own isolated window under the matched limits\nawait generateText({\n  model,\n  providerOptions: {\n    rateLimiter: { scope: 'user:free:alice' }, // matches 'user:free:*' → 5 rpm\n  },\n})\n```\n\n**Model limit fields:**\n\n| Field | Description |\n|---|---|\n| `rpm` | Max requests per minute |\n| `itpm` | Max input tokens per minute |\n| `otpm` | Max output tokens per minute (based on completed request actuals) |\n| `rpd` | Max requests per day (rolling 24-hour window) |\n| `maxConcurrent` | Max concurrent in-flight requests |\n\n**Scope fields (`config.scopes`):**\n\n| Field | Description |\n|---|---|\n| `rpm` | Max requests per minute for this scope |\n| `itpm` | Max input tokens per minute for this scope |\n| `maxConcurrent` | Max concurrent in-flight requests for this scope |\n\nWhen a scope matches, its limits replace the model's global limits for that request. Each scope gets a fully independent sliding window — `user:alice` and `user:bob` don't share quota.\n\nIf no `scopes` config is defined, the model's global limits apply to all scoped requests.\n\n---\n\n## Concurrency limits\n\nLimit how many requests to a model can be in-flight simultaneously. Useful for:\n- Preventing connection pool exhaustion\n- Controlling cost burn rate during spikes\n- Enforcing per-scope parallelism\n\n```typescript\nconst limiter = createRateLimiter({\n  limits: {\n    'gpt-4o': {\n      rpm: 500,\n      maxConcurrent: 10, // at most 10 requests executing at once\n    },\n  },\n})\n```\n\nOnce `maxConcurrent` slots are occupied, new requests queue behind them. Each slot is released when its request completes (success or failure). Concurrency is checked after the rate limit slot is acquired — both limits apply independently.\n\nConcurrency limits also work in scoped contexts:\n\n```typescript\nconst limiter = createRateLimiter({\n  scopes: {\n    'org:*': { rpm: 300, maxConcurrent: 20 },\n  },\n})\n```\n\n---\n\n## AbortSignal support\n\nPass an `AbortSignal` to cancel a request that's waiting in the queue. If the signal fires before the request starts executing, it's removed from the queue immediately and the promise rejects with an `AbortError`.\n\n```typescript\nconst controller = new AbortController()\n\n// User closes the browser tab — cancel pending AI requests\nwindow.addEventListener('beforeunload', () => controller.abort())\n\nconst result = await generateText({\n  model,\n  prompt: 'Long running task...',\n  abortSignal: controller.signal,\n})\n```\n\n```typescript\n// With timeout\nconst signal = AbortSignal.timeout(5_000)\n\ntry {\n  const result = await generateText({ model, prompt, abortSignal: signal })\n} catch (err) {\n  if (err.name === 'AbortError') {\n    console.log('Request cancelled (timed out or aborted)')\n  }\n}\n```\n\nThe signal threads through both the rate-limit queue and the concurrency queue. A request that's already executing is not affected — only queued requests can be aborted this way.\n\n---\n\n## Call timeout\n\n`callTimeout` kills a hung AI API call after N milliseconds using `Promise.race()`. This is distinct from the queue `timeout` (which fires if a request waits too long to _start_) — `callTimeout` fires if the request is already executing but the API hasn't responded.\n\n```typescript\n// Global default for all requests\nconst limiter = createRateLimiter({\n  retry: { callTimeout: 30_000 }, // abort any call that takes longer than 30s\n})\n\n// Per-request override\nawait generateText({\n  model,\n  prompt: '...',\n  providerOptions: {\n    rateLimiter: { callTimeout: 10_000 }, // stricter timeout for this request\n  },\n})\n```\n\nWhen a `callTimeout` fires, the request throws a `TimeoutError` (native `DOMException` with `name: 'TimeoutError'`). The retry logic treats it as a retryable failure if the status code is in `retryOn`. Set `callTimeout` to `undefined` (the default) to disable it.\n\n---\n\n## Cost tracking\n\n```typescript\n// At any point — live snapshot\nconst report = limiter.getCostReport()\n\nconsole.log(report)\n// {\n//   hour:  { requests: 42,   inputTokens: 84_000,  outputTokens: 21_000,  costUsd: 0.29 },\n//   day:   { requests: 318,  inputTokens: 620_000, outputTokens: 155_000, costUsd: 2.11 },\n//   month: { requests: 4821, inputTokens: 9_100_000, outputTokens: 2_200_000, costUsd: 34.80 },\n//   byModel: {\n//     'gpt-4o':      { requests: 120, inputTokens: 240_000, outputTokens: 60_000, costUsd: 1.20 },\n//     'gpt-4o-mini': { requests: 198, inputTokens: 380_000, outputTokens: 95_000, costUsd: 0.91 },\n//   },\n//   byScope: {\n//     'user:alice': { requests: 15, inputTokens: 30_000, outputTokens: 7_500, costUsd: 0.15 },\n//     'user:bob':   { requests: 8,  inputTokens: 12_000, outputTokens: 3_000, costUsd: 0.06 },\n//   }\n// }\n```\n\nCosts are based on **actual token counts** from API responses — not estimates. The report uses rolling windows, so `hour` always means \"the last 60 minutes.\"\n\n`byScope` is populated automatically when requests carry a `scope` (either set on `limiter.wrap()` or via `providerOptions.rateLimiter.scope`). Unscoped requests don't appear in `byScope`.\n\n### Cost forecasting\n\n`getCostForecast()` projects your end-of-period spend based on the current hourly rate. Useful for alerting before a budget cap is hit:\n\n```typescript\nconst forecast = limiter.getCostForecast()\n\nconsole.log(forecast)\n// {\n//   hour:  { spentUsd: 1.20, projectedUsd: 1.20,  ratePerHourUsd: 1.20 },\n//   day:   { spentUsd: 3.50, projectedUsd: 28.80, ratePerHourUsd: 1.20 },\n//   month: { spentUsd: 8.10, projectedUsd: 864,   ratePerHourUsd: 1.20 },\n// }\n\nif (forecast.day.projectedUsd > 40) {\n  console.warn(`Heads up — on track to spend $${forecast.day.projectedUsd.toFixed(2)} today`)\n}\n```\n\n`projectedUsd` = current hourly rate × hours in the period. It is based on the **last 60 minutes** of spend, so it responds quickly to usage spikes.\n\n---\n\n## Budget fallback routing\n\nWhen a budget limit is hit, you can transparently reroute to a cheaper model instead of throwing an error:\n\n```typescript\nconst limiter = createRateLimiter({\n  cost: {\n    budget: { daily: 10 },\n    onExceeded: 'fallback', // reroute to fallback instead of throwing\n  },\n  on: {\n    budgetHit: ({ model, usingFallback }) =>\n      console.warn(`${model} budget hit — ${usingFallback ? 'using fallback' : 'throwing'}`),\n  },\n})\n\nconst model = limiter.wrap(\n  openai('gpt-4o'),                     // primary model\n  { fallback: openai('gpt-4o-mini') },  // used when budget is exceeded\n)\n\n// Under budget → uses gpt-4o normally\n// Over $10/day → silently switches to gpt-4o-mini, no code changes needed\nconst result = await generateText({ model, prompt })\n```\n\n**Fallback chains** — pass an array to walk multiple fallbacks in order:\n\n```typescript\nconst model = limiter.wrap(openai('gpt-4o'), {\n  fallback: [\n    openai('gpt-4o-mini'),    // try first\n    openai('gpt-3.5-turbo'),  // try second if gpt-4o-mini is also over budget\n  ],\n})\n```\n\nEach model in the chain is tried in order. If all are over budget, `BudgetExceededError` is thrown.\n\n**Behavior matrix:**\n\n| `onExceeded` | `fallback` configured | Outcome |\n|---|---|---|\n| `'throw'` | any | Throws `BudgetExceededError` |\n| `'fallback'` | yes | Transparently walks the fallback chain |\n| `'fallback'` | no | Throws `BudgetExceededError` |\n| `'queue'` | any | Holds the request until the rolling window clears enough spend; throws `QueueTimeoutError` if `queue.timeout` elapses |\n\nFallback usage is tracked under the fallback model's ID in `getCostReport()`.\n\n---\n\n## Persistent cost tracking\n\nBy default, cost history lives in memory and resets on restart. If your process restarts frequently (serverless, rolling deploys), budget caps could be bypassed because the new instance starts with $0 spend.\n\n`RedisCostStore` persists every cost entry to a Redis sorted set so budget caps survive restarts:\n\n```typescript\nimport { createRateLimiter } from 'ai-sdk-rate-limiter'\nimport { RedisCostStore } from 'ai-sdk-rate-limiter/redis'\nimport Redis from 'ioredis'\n\nconst redis = new Redis(process.env.REDIS_URL)\n\nconst limiter = createRateLimiter({\n  cost: {\n    budget: { daily: 50 },\n    onExceeded: 'throw',\n    store: new RedisCostStore(redis), // persist entries to Redis\n  },\n})\n\n// On startup — pre-load the last 30 days of spend so the in-memory\n// window is accurate immediately (before any new requests)\nawait limiter.warmUp()\n```\n\n**`warmUp()`** — loads the last 30 days of entries from the store into the in-memory cost tracker. Call it once after `createRateLimiter()`. Without it the limiter works correctly for new requests, but budget checks won't account for spend from previous process runs until the first request arrives.\n\n`RedisCostStore` options:\n\n```typescript\nnew RedisCostStore(redis, {\n  keyPrefix: 'cost:myapp:',  // namespace key (default: 'airl:cost:')\n  ttlMs:     30 * 86_400_000, // TTL for the sorted set (default: 30 days)\n})\n```\n\nErrors from the cost store (connection failures, etc.) are swallowed silently — cost persistence is best-effort and never blocks request execution.\n\n---\n\n## Multi-instance Redis store\n\nBy default, rate limit state is in-memory (per-process). For multi-instance deployments — multiple pods, serverless replicas, workers — each instance has its own counters. Install the Redis store to share state:\n\n```\nnpm install ioredis\n```\n\n```typescript\nimport { createRateLimiter } from 'ai-sdk-rate-limiter'\nimport { RedisStore } from 'ai-sdk-rate-limiter/redis'\nimport Redis from 'ioredis'\n\nconst limiter = createRateLimiter({\n  store: new RedisStore(new Redis(process.env.REDIS_URL)),\n  // ... rest of your config unchanged\n})\n```\n\nThat's the entire change. All APIs — `wrap()`, `rawProxy()`, events, cost reports — work identically. The Redis store enforces rate limits collectively so no two instances can jointly exceed the API limits.\n\n**Options:**\n\n```typescript\nnew RedisStore(redis, {\n  keyPrefix: 'rl:myapp:', // namespace if multiple apps share one Redis instance\n  windowMs:  60_000,      // window size in ms; match your provider's limit window\n})\n```\n\n**How it works internally:**\n\nEach request runs a Lua script atomically that: removes stale entries from a sorted set, counts requests and tokens in the current window, checks against RPM, ITPM, OTPM, and RPD limits, and either reserves the slot or returns when the next slot opens. The local queue (priority ordering, drain timer, timeout handling) stays in-memory per instance — only the window counters are shared via Redis.\n\n**Failover** — If Redis is unreachable (connection error, timeout), the store fails open: rate limit enforcement is suspended for that call and the request proceeds normally. Enforcement resumes as soon as the store recovers. This means AI calls never block on Redis availability — you trade enforcement precision for reliability during outages.\n\n**Compatible clients** — any client with `eval()`, `get()`, and `set()` works: `ioredis`, `node-redis`, Upstash Redis.\n\nUse the default `InMemoryStore` for single-instance deployments — it's more accurate (true sliding window, no network round-trips) and zero-config. Only switch to `RedisStore` when you actually need cross-instance coordination.\n\n---\n\n## Circuit breaker\n\nThe circuit breaker protects against cascading failures when an upstream AI API is degrading. After N consecutive 5xx failures, the circuit opens and subsequent requests fail immediately with `CircuitOpenError` rather than piling up and timing out.\n\n```typescript\nimport { createRateLimiter, CircuitOpenError } from 'ai-sdk-rate-limiter'\n\nconst limiter = createRateLimiter({\n  circuit: {\n    failureThreshold: 5,      // open after 5 consecutive failures\n    cooldownMs:       30_000, // stay open for 30s, then probe\n    tripOn:           [500, 502, 503, 504], // which HTTP status codes trip the circuit\n  },\n  on: {\n    circuitOpen:   ({ model, openUntilMs }) =>\n      console.error(`Circuit open for ${model} until ${new Date(openUntilMs).toISOString()}`),\n    circuitClosed: ({ model }) =>\n      console.log(`Circuit closed for ${model} — upstream recovered`),\n  },\n})\n\ntry {\n  const result = await generateText({ model, prompt })\n} catch (err) {\n  if (err instanceof CircuitOpenError) {\n    // Fail fast — upstream is degraded, don't pile on\n    return res.status(503).json({\n      error: 'AI service temporarily unavailable',\n      retryAfter: Math.ceil((err.openUntilMs - Date.now()) / 1000),\n    })\n  }\n}\n```\n\n**State machine:**\n\n- `CLOSED` (normal) — requests pass through; failures are counted\n- `OPEN` — requests fail immediately with `CircuitOpenError`; after `cooldownMs`, transitions to HALF_OPEN\n- `HALF_OPEN` — one probe request is allowed; success → CLOSED, failure → OPEN (resets cooldown)\n\nThe circuit is per-model. A failing `gpt-4o` doesn't affect `gpt-4o-mini`. 429 rate-limit errors do **not** trip the circuit — only 5xx errors (or whatever you configure in `tripOn`) count as failures.\n\n---\n\n## Graceful shutdown\n\n```typescript\n// On SIGTERM / process exit\nprocess.on('SIGTERM', async () => {\n  // Stop accepting new requests, wait up to 30s for in-flight ones to finish\n  await limiter.shutdown({ drainMs: 30_000 })\n  process.exit(0)\n})\n```\n\nAfter `shutdown()` is called:\n- New requests throw `ShutdownError` immediately\n- In-flight requests complete normally (up to `drainMs`)\n- The returned promise resolves when the queue drains or `drainMs` elapses\n\n```typescript\nimport { ShutdownError } from 'ai-sdk-rate-limiter'\n\ntry {\n  const result = await generateText({ model, prompt })\n} catch (err) {\n  if (err instanceof ShutdownError) {\n    // Process is shutting down — expected, not an error\n  }\n}\n```\n\n---\n\n## Prometheus metrics\n\n```\nnpm install ai-sdk-rate-limiter\n```\n\nThe `ai-sdk-rate-limiter/prometheus` entry point provides in-process Prometheus metrics with no external dependencies. Metrics are accumulated in memory and rendered to the text exposition format on demand.\n\n```typescript\nimport { createRateLimiter } from 'ai-sdk-rate-limiter'\nimport { createPrometheusPlugin } from 'ai-sdk-rate-limiter/prometheus'\n\nconst prometheus = createPrometheusPlugin()\n\nconst limiter = createRateLimiter({\n  on: prometheus,\n})\n\n// Expose /metrics endpoint (Express example)\napp.get('/metrics', (req, res) => {\n  res.set('Content-Type', 'text/plain; version=0.0.4')\n  res.send(prometheus.collect())\n})\n```\n\n**Metrics exported:**\n\n| Metric | Type | Description |\n|---|---|---|\n| `ai_requests_total` | counter | Total requests, labelled by `model`, `provider`, `status` |\n| `ai_tokens_input_total` | counter | Total input tokens, labelled by `model`, `provider` |\n| `ai_tokens_output_total` | counter | Total output tokens, labelled by `model`, `provider` |\n| `ai_cost_usd_total` | counter | Total cost in USD, labelled by `model`, `provider` |\n| `ai_request_duration_ms` | summary | Request latency (p50, p90, p99), labelled by `model` |\n| `ai_retries_total` | counter | Total retry attempts, labelled by `model` |\n| `ai_rate_limited_total` | counter | Rate-limit hits, labelled by `model`, `source` |\n| `ai_budget_exceeded_total` | counter | Budget exceeded events, labelled by `model`, `period` |\n| `ai_queue_depth` | gauge | Current queue depth, labelled by `model` |\n\n```typescript\n// Custom metric prefix\nconst prometheus = createPrometheusPlugin({ prefix: 'myapp_' })\n// → myapp_requests_total, myapp_tokens_input_total, ...\n\n// Reset counters (e.g. for tests)\nprometheus.reset()\n```\n\n---\n\n## StatsD metrics\n\n```typescript\nimport { createRateLimiter } from 'ai-sdk-rate-limiter'\nimport { createStatsDPlugin } from 'ai-sdk-rate-limiter/statsd'\nimport StatsD from 'hot-shots' // or node-statsd, dogstatsd-client, etc.\n\nconst statsd = new StatsD({ host: 'localhost', port: 8125 })\n\nconst limiter = createRateLimiter({\n  on: createStatsDPlugin(statsd, {\n    prefix:     'myapp.ai.',   // default: 'ai.'\n    globalTags: ['env:prod'],  // appended to every metric\n  }),\n})\n```\n\nAny client that implements the `StatsDClient` interface works — `hot-shots`, `node-statsd`, `datadog-metrics`, or a custom implementation:\n\n```typescript\nimport type { StatsDClient } from 'ai-sdk-rate-limiter/statsd'\n\nconst client: StatsDClient = {\n  increment(metric, value, tags) { /* ... */ },\n  gauge(metric, value, tags) { /* ... */ },\n  timing(metric, value, tags) { /* ... */ },\n}\n```\n\n**Metrics emitted** (same set as Prometheus, DogStatsD tag format `['key:value']`):\n\n| Metric | Type | Tags |\n|---|---|---|\n| `ai.requests` | increment | `model:*`, `provider:*`, `status:completed\\|dropped` |\n| `ai.tokens.input` | increment | `model:*`, `provider:*` |\n| `ai.tokens.output` | increment | `model:*`, `provider:*` |\n| `ai.cost_usd` | timing (gauge) | `model:*`, `provider:*` |\n| `ai.latency_ms` | timing | `model:*` |\n| `ai.retries` | increment | `model:*` |\n| `ai.rate_limited` | increment | `model:*`, `source:local\\|remote` |\n| `ai.budget_exceeded` | increment | `model:*`, `period:hourly\\|daily\\|monthly` |\n| `ai.queue_depth` | gauge | `model:*` |\n\n---\n\n## Express / Hono middleware\n\nThe `ai-sdk-rate-limiter/middleware` entry point eliminates per-route boilerplate. Scope extraction, priority assignment, and rate-limiter error handling all move to the middleware layer — route handlers just pass `req.rateLimiter` through.\n\n### Express\n\n```typescript\nimport { createRateLimiterMiddleware } from 'ai-sdk-rate-limiter/middleware'\n\nconst { middleware, errorHandler } = createRateLimiterMiddleware(limiter, {\n  // Extract scope from the request — stored in req.rateLimiter.scope\n  scope: (req) => {\n    const plan = req.headers['x-user-plan'] ?? 'free'\n    const id   = req.headers['x-user-id']\n    return id ? `user:${plan}:${id}` : undefined\n  },\n\n  // Derive queue priority per-request\n  priority: (req) => req.headers['x-user-plan'] === 'pro' ? 'normal' : 'low',\n\n  // Add X-RateLimit-* informational headers to every response\n  injectHeaders: 'gpt-4o-mini',\n})\n\napp.use(middleware)    // BEFORE routes\n\napp.post('/chat', async (req, res) => {\n  const { text } = await generateText({\n    model,\n    prompt: req.body.message,\n    // req.rateLimiter already has scope + priority — just pass it through\n    providerOptions: { rateLimiter: req.rateLimiter },\n  })\n  res.json({ text })\n})\n\napp.use(errorHandler)  // AFTER routes\n```\n\nThe `errorHandler` converts every `RateLimiterError` to a typed HTTP response automatically — no try/catch needed in route handlers:\n\n| Error | HTTP status | `code` |\n|---|---|---|\n| `QueueTimeoutError` | 503 | `QUEUE_TIMEOUT` |\n| `QueueFullError` | 503 | `QUEUE_FULL` |\n| `CircuitOpenError` | 503 | `CIRCUIT_OPEN` |\n| `ShutdownError` | 503 | `SHUTDOWN` |\n| `BudgetExceededError` | 402 | `BUDGET_EXCEEDED` |\n| `RateLimiterError` (generic) | 429 | `RATE_LIMITED` |\n\nNon-rate-limiter errors are passed to the next error handler unchanged.\n\n### Hono\n\n```typescript\nimport { createHonoMiddleware } from 'ai-sdk-rate-limiter/middleware'\n\napp.use(createHonoMiddleware(limiter, {\n  scope:    (c) => c.req.header('x-user-id'),\n  priority: (c) => c.req.header('x-plan') === 'pro' ? 'normal' : 'low',\n}))\n\napp.post('/chat', async (c) => {\n  const { text } = await generateText({\n    model,\n    prompt: await c.req.text(),\n    providerOptions: { rateLimiter: c.var.rateLimiter },\n  })\n  return c.json({ text })\n})\n```\n\n`createHonoMiddleware` wraps the `next()` call in a try/catch, so `RateLimiterErrors` thrown inside route handlers are caught and returned as JSON responses automatically.\n\n### Standalone error handler\n\nIf you only need error handling without scope injection:\n\n```typescript\nimport { createRateLimiterErrorHandler } from 'ai-sdk-rate-limiter/middleware'\n\napp.use(createRateLimiterErrorHandler({\n  includeDetails: false, // omit retryAfter, period, limitUsd from response body\n}))\n```\n\n### Custom framework (Fastify, etc.)\n\n`mapErrorToResponse` is exported for frameworks that don't use the `(req, res, next)` convention:\n\n```typescript\nimport { mapErrorToResponse } from 'ai-sdk-rate-limiter/middleware'\nimport { RateLimiterError } from 'ai-sdk-rate-limiter'\n\n// Fastify onError hook\nfastify.setErrorHandler((err, request, reply) => {\n  if (err instanceof RateLimiterError) {\n    const { status, body } = mapErrorToResponse(err)\n    return reply.status(status).send(body)\n  }\n  reply.send(err)\n})\n```\n\n### `req.rateLimiter` TypeScript type\n\nThe middleware augments `http.IncomingMessage` so `req.rateLimiter` is typed in Express and Fastify without any additional setup:\n\n```typescript\nimport type { RateLimiterRequestContext } from 'ai-sdk-rate-limiter/middleware'\n// req.rateLimiter is automatically typed as RateLimiterRequestContext | undefined\n```\n\n---\n\n## Events\n\nAll events are typed. Register handlers at creation time or dynamically:\n\n```typescript\n// At creation time\nconst limiter = createRateLimiter({\n  on: { rateLimited: handler },\n})\n\n// Dynamically\nlimiter.on('queued', ({ model, queueDepth, estimatedWaitMs }) => {\n  console.log(`${model} queued (depth: ${queueDepth}, ~${estimatedWaitMs}ms wait)`)\n})\n\nlimiter.off('queued', handler)\n```\n\n| Event | When | Key fields |\n|---|---|---|\n| `queued` | Request enters the queue | `model`, `provider`, `priority`, `queueDepth`, `estimatedWaitMs` |\n| `dequeued` | Request leaves the queue | `model`, `provider`, `waitedMs`, `priority` |\n| `retrying` | A failed request is about to retry | `model`, `provider`, `attempt`, `maxAttempts`, `delayMs`, `error` |\n| `rateLimited` | Limit hit (local or remote 429) | `model`, `provider`, `source`, `limitType`, `resetAt` |\n| `budgetHit` | Cost budget exceeded | `model`, `provider`, `currentCostUsd`, `limitUsd`, `period`, `usingFallback` |\n| `dropped` | Request rejected | `model`, `provider`, `reason`, `waitedMs?`, `queueDepth?`, `scope?`, `metadata?` |\n| `completed` | Request finished successfully | `model`, `provider`, `inputTokens`, `outputTokens`, `costUsd`, `latencyMs`, `streaming`, `scope?` |\n| `circuitOpen` | Circuit breaker opened | `model`, `provider`, `openUntilMs`, `failureCount` |\n| `circuitClosed` | Circuit breaker closed (upstream recovered) | `model`, `provider` |\n| `limitsDetected` | Limits auto-updated from response headers | `model`, `provider`, `detectedLimits` |\n\n**`dropped` reason values:**\n\n| Reason | Cause |\n|---|---|\n| `'queue-full'` | Queue at `maxSize` capacity |\n| `'queue-timeout'` | Request waited longer than `queue.timeout` |\n| `'circuit-open'` | Circuit breaker is open |\n| `'shutdown'` | Limiter is shutting down |\n\nThe `source` on `rateLimited` distinguishes between requests we blocked locally (`'local'`) vs. requests the API rejected with a 429 (`'remote'`). Local blocks are expected and free. Frequent remote blocks mean your configured limits are too high for your tier — run `npx ai-sdk-rate-limiter audit` to get accurate numbers.\n\n---\n\n## Backpressure\n\nCheck estimated wait time before committing to a request. Useful for showing loading states or shedding load gracefully:\n\n```typescript\nconst waitMs = await limiter.estimatedWait('gpt-4o')\n\nif (waitMs > 5_000) {\n  return res.status(503).json({\n    error: 'Model busy, try again shortly',\n    retryAfterMs: waitMs,\n  })\n}\n\nconst result = await generateText({ model, prompt })\n```\n\nReturns `0` if the model would proceed immediately.\n\n---\n\n## Error handling\n\nEvery error is typed and carries structured context:\n\n```typescript\nimport {\n  RateLimitExceededError,\n  QueueTimeoutError,\n  QueueFullError,\n  BudgetExceededError,\n  RetryExhaustedError,\n  CircuitOpenError,\n  ShutdownError,\n  RateLimiterError,\n} from 'ai-sdk-rate-limiter'\n\ntry {\n  const result = await generateText({ model, prompt })\n} catch (error) {\n  if (error instanceof QueueTimeoutError) {\n    // Request waited in queue longer than queue.timeout\n    console.error(`Timed out after ${error.waitedMs}ms (queue depth: ${error.queueDepth})`)\n  } else if (error instanceof BudgetExceededError) {\n    // Cost budget hit and onExceeded is 'throw' or no fallback configured\n    console.error(`Budget exceeded: $${error.currentCostUsd} of $${error.limitUsd} ${error.period}`)\n  } else if (error instanceof CircuitOpenError) {\n    // Circuit breaker is open — upstream is degraded\n    const retryAfterSec = Math.ceil((error.openUntilMs - Date.now()) / 1000)\n    res.status(503).json({ error: 'AI service temporarily unavailable', retryAfter: retryAfterSec })\n  } else if (error instanceof ShutdownError) {\n    // Limiter is shutting down — process is exiting\n    console.log('Limiter shutting down, request rejected')\n  } else if (error instanceof RetryExhaustedError) {\n    // All retry attempts failed\n    console.error(`All ${error.attempts} retries exhausted`, error.cause)\n  } else if (error instanceof QueueFullError) {\n    // Queue at capacity and onFull is 'throw'\n    console.error(`Queue full at ${error.maxSize} requests for ${error.model}`)\n  } else if (error instanceof RateLimitExceededError) {\n    // Rate limit hit and the request could not be queued\n    console.error(`${error.model} ${error.limitType} limit of ${error.limit} exceeded`)\n  } else if (error.name === 'AbortError') {\n    // Request was cancelled via AbortSignal before it started executing\n    console.log('Request cancelled')\n  }\n}\n```\n\nAll errors extend `RateLimiterError`, so a single `instanceof RateLimiterError` check separates rate-limiter failures from AI API errors.\n\n**Error fields:**\n\n| Error | Fields |\n|---|---|\n| `QueueTimeoutError` | `model`, `waitedMs`, `queueDepth` |\n| `BudgetExceededError` | `model`, `currentCostUsd`, `limitUsd`, `period` |\n| `CircuitOpenError` | `model`, `openUntilMs` |\n| `ShutdownError` | — |\n| `RetryExhaustedError` | `model`, `attempts`, `cause` |\n| `QueueFullError` | `model`, `maxSize` |\n| `RateLimitExceededError` | `model`, `limitType`, `limit`, `resetAt` |\n\n---\n\n## OpenTelemetry\n\nThe `ai-sdk-rate-limiter/otel` entry point provides a plugin that emits OpenTelemetry spans for every AI request. No hard dependency on `@opentelemetry/api` — the plugin accepts any OTel-compatible tracer via structural typing.\n\n```typescript\nimport { trace } from '@opentelemetry/api'\nimport { createRateLimiter } from 'ai-sdk-rate-limiter'\nimport { createOtelPlugin } from 'ai-sdk-rate-limiter/otel'\n\nconst limiter = createRateLimiter({\n  on: createOtelPlugin(trace.getTracer('my-service')),\n})\n```\n\n**Spans emitted:**\n\n| Span name | When | Status |\n|---|---|---|\n| `gen_ai.request` | Every completed request | OK |\n| `gen_ai.request` | Every dropped request | ERROR |\n| `ai_rate_limiter.retry` | Each retry attempt | OK |\n| `ai_rate_limiter.budget_hit` | Budget exceeded | ERROR |\n\n**Attributes on `gen_ai.request` (completed):**\n\n| Attribute | Value |\n|---|---|\n| `gen_ai.system` | Provider name (e.g. `openai`, `anthropic`) |\n| `gen_ai.request.model` | Model ID |\n| `gen_ai.usage.input_tokens` | Actual input tokens from API response |\n| `gen_ai.usage.output_tokens` | Actual output tokens from API response |\n| `ai_rate_limiter.cost_usd` | Cost in USD for this request |\n| `ai_rate_limiter.latency_ms` | Total latency including queue wait |\n| `ai_rate_limiter.streaming` | Whether the request used streaming |\n\nAttribute names follow the [OTel GenAI semantic conventions](https://opentelemetry.io/docs/specs/semconv/gen-ai/). The `gen_ai.request` span duration is reconstructed from `latencyMs` so it reflects the full wall-clock time of the request, including any queue wait.\n\n**Custom tracer interface** — if you don't want to install `@opentelemetry/api`, implement the `OtelTracer` interface directly:\n\n```typescript\nimport { createOtelPlugin, type OtelTracer } from 'ai-sdk-rate-limiter/otel'\n\nconst tracer: OtelTracer = {\n  startSpan(name, options) {\n    // return any object that implements OtelSpan\n  },\n}\n\nconst limiter = createRateLimiter({\n  on: createOtelPlugin(tracer),\n})\n```\n\n---\n\n## Testing utilities\n\nThe `ai-sdk-rate-limiter/testing` entry point provides a test-friendly limiter that records every completed call. Use it to assert on model usage, token counts, and costs in unit tests without mocking internals.\n\n```typescript\nimport { createTestLimiter } from 'ai-sdk-rate-limiter/testing'\nimport { openai } from '@ai-sdk/openai'\nimport { generateText } from 'ai'\n\nconst limiter = createTestLimiter()\nconst model = limiter.wrap(openai('gpt-4o'))\n\n// Run your code under test\nawait generateText({ model, prompt: 'Hello!' })\nawait generateText({ model, prompt: 'Another request' })\n\n// Assert on recorded calls\nconst calls = limiter.getCalls()\nexpect(calls).toHaveLength(2)\nexpect(calls[0].modelId).toBe('gpt-4o')\nexpect(calls[0].inputTokens).toBeGreaterThan(0)\nexpect(calls[0].costUsd).toBeGreaterThan(0)\n\n// Reset between tests\nlimiter.reset()\n```\n\n`createTestLimiter()` accepts the same config as `createRateLimiter()`, so you can test budget enforcement, retry behavior, and other scenarios with real config:\n\n```typescript\nconst limiter = createTestLimiter({\n  cost: { budget: { daily: 0.01 }, onExceeded: 'throw' },\n})\n\n// Test that your code handles budget errors gracefully\n```\n\n**`CallRecord` fields:**\n\n| Field | Type | Description |\n|---|---|---|\n| `modelId` | `string` | Model that was called |\n| `provider` | `string` | Provider name |\n| `inputTokens` | `number` | Input tokens from the API response |\n| `outputTokens` | `number` | Output tokens from the API response |\n| `costUsd` | `number` | Cost in USD for this call |\n| `latencyMs` | `number` | Total latency in ms |\n| `streaming` | `boolean` | Whether this was a streaming call |\n| `timestamp` | `number` | Unix timestamp (ms) when the call completed |\n\n**Methods:**\n\n| Method | Description |\n|---|---|\n| `getCalls()` | Returns all completed calls in chronological order |\n| `reset()` | Clears call history |\n\nAll other `RateLimiter` methods (`wrap`, `rawProxy`, `getCostReport`, `getStatus`, `on`, `off`) work identically.\n\n---\n\n## CLI audit\n\nProbe your API keys to discover your actual rate limit tier and generate a ready-to-paste config override:\n\n```\nnpx ai-sdk-rate-limiter audit\n```\n\n```\n────────────────────────────────────────────────────────────────────────────────\n  ai-sdk-rate-limiter audit\n────────────────────────────────────────────────────────────────────────────────\n\n  OPENAI  (OPENAI_API_KEY)\n  Model                            RPM        TPM            Registry\n  ──────────────────────────────────────────────────────────────────────────────\n  gpt-4o                           10000      2,000,000      (registry: 500 RPM / 30,000 TPM)\n  gpt-4o-mini                      10000      10,000,000 ≠   (registry: 500 RPM / 200,000 TPM)\n\n────────────────────────────────────────────────────────────────────────────────\n  1 model(s) differ from registry defaults.\n  Paste the config below into createRateLimiter():\n\n  const limiter = createRateLimiter({\n    limits: {\n      'gpt-4o-mini': { rpm: 10000, itpm: 10,000,000 },\n    },\n  })\n\n────────────────────────────────────────────────────────────────────────────────\n```\n\n**How it works** — Makes a minimal (5-token) request per model and reads the `x-ratelimit-limit-*` headers that every provider returns on each response. These headers reflect your account's actual tier, not the published defaults.\n\n**Options:**\n\n```\nnpx ai-sdk-rate-limiter audit [options]\n\n  --provider, -p <name>   Audit a single provider: openai, anthropic, groq, mistral, cohere\n  --json                  Machine-readable JSON output\n  --help, -h              Show help\n  --version, -v           Print version\n\nEnvironment variables required:\n  OPENAI_API_KEY\n  ANTHROPIC_API_KEY\n  GROQ_API_KEY\n  MISTRAL_API_KEY\n  COHERE_API_KEY\n```\n\n**Examples:**\n\n```bash\n# Audit all configured providers\nnpx ai-sdk-rate-limiter audit\n\n# Audit only OpenAI\nnpx ai-sdk-rate-limiter audit --provider openai\n\n# Machine-readable output for CI / scripts\nnpx ai-sdk-rate-limiter audit --json | jq '.providers[0].models'\n```\n\n---\n\n## Model registry\n\nLimits and pricing are built-in for every major model across 6 providers. Defaults are conservative (free/Tier 1) — override with your actual plan limits via the `limits` config option or by running `audit`.\n\n**OpenAI**\n\n| Model | RPM | ITPM | Input $/M | Output $/M |\n|---|---|---|---|---|\n| gpt-4o | 500 | 30,000 | $2.50 | $10.00 |\n| gpt-4o-mini | 500 | 200,000 | $0.15 | $0.60 |\n| o1 | 500 | 30,000 | $15.00 | $60.00 |\n| o3-mini / o4-mini | 500 | 200,000 | $1.10 | $4.40 |\n| gpt-3.5-turbo | 3,500 | 90,000 | $0.50 | $1.50 |\n\n**Anthropic**\n\n| Model | RPM | ITPM | Input $/M | Output $/M |\n|---|---|---|---|---|\n| claude-opus-4-6 | 50 | 30,000 | $15.00 | $75.00 |\n| claude-sonnet-4-6 | 50 | 30,000 | $3.00 | $15.00 |\n| claude-haiku-4-5 | 50 | 50,000 | $0.80 | $4.00 |\n\n**Google**\n\n| Model | RPM | ITPM | Input $/M | Output $/M |\n|---|---|---|---|---|\n| gemini-2.0-flash | 15 | 1,000,000 | $0.10 | $0.40 |\n| gemini-1.5-pro | 2 | 32,000 | $1.25 | $5.00 |\n| gemini-1.5-flash | 15 | 1,000,000 | $0.075 | $0.30 |\n\n**Groq** (free tier defaults — on-demand tier is 6,000 RPM / 200k TPM)\n\n| Model | RPM | ITPM | Input $/M | Output $/M |\n|---|---|---|---|---|\n| llama-3.3-70b-versatile | 30 | 6,000 | $0.59 | $0.79 |\n| llama-3.1-8b-instant | 30 | 20,000 | $0.05 | $0.08 |\n| mixtral-8x7b-32768 | 30 | 5,000 | $0.24 | $0.24 |\n| gemma2-9b-it | 30 | 15,000 | $0.20 | $0.20 |\n| deepseek-r1-distill-llama-70b | 30 | 6,000 | $0.75 | $0.99 |\n\n**Mistral**\n\n| Model | RPM | ITPM | Input $/M | Output $/M |\n|---|---|---|---|---|\n| mistral-large-latest | 500 | 100,000 | $2.00 | $6.00 |\n| mistral-small-latest | 500 | 100,000 | $0.10 | $0.30 |\n| codestral-latest | 500 | 100,000 | $0.30 | $0.90 |\n| open-mistral-nemo | 500 | 100,000 | $0.15 | $0.15 |\n| pixtral-large-latest | 500 | 100,000 | $2.00 | $6.00 |\n\n**Cohere** (trial tier defaults — production tier is 10,000+ RPM)\n\n| Model | RPM | ITPM | Input $/M | Output $/M |\n|---|---|---|---|---|\n| command-r-plus | 20 | 100,000 | $2.50 | $10.00 |\n| command-r | 20 | 100,000 | $0.15 | $0.60 |\n| command | 20 | 100,000 | $0.50 | $1.50 |\n| command-light | 20 | 100,000 | $0.15 | $0.60 |\n\nUnknown models fall back to 60 RPM / 100k ITPM with no cost tracking. You can inspect or extend the registry:\n\n```typescript\nimport {\n  OPENAI_MODELS,\n  ANTHROPIC_MODELS,\n  GOOGLE_MODELS,\n  GROQ_MODELS,\n  MISTRAL_MODELS,\n  COHERE_MODELS,\n  resolveModelLimits,\n  isKnownModel,\n} from 'ai-sdk-rate-limiter'\n\nconsole.log(GROQ_MODELS['llama-3.3-70b-versatile'])\n// { rpm: 30, itpm: 6000, rpd: 1000, inputPricePerMillion: 0.59, ... }\n\nconsole.log(isKnownModel('llama-3.3-70b-versatile', 'groq')) // true\nconsole.log(isKnownModel('my-fine-tune', 'openai'))           // false → fallback limits\n\n// Resolve the effective limits for a model (registry defaults merged with user overrides)\nconst limits = resolveModelLimits('gpt-4o', 'openai', { 'gpt-4o': { rpm: 1000 } })\n```\n\n---\n\n## Advanced usage\n\n### Multiple limiters per tier\n\n```typescript\nconst freeLimiter = createRateLimiter({\n  limits: { 'gpt-4o-mini': { rpm: 5 } },\n  cost:   { budget: { daily: 0.10 }, onExceeded: 'throw' },\n  queue:  { timeout: 5_000 },\n})\n\nconst paidLimiter = createRateLimiter({\n  limits: { 'gpt-4o': { rpm: 100 } },\n  cost:   { budget: { daily: 20 } },\n  queue:  { timeout: 30_000 },\n})\n\n// Route per request based on user plan\nconst model = req.user.plan === 'paid'\n  ? paidLimiter.wrap(openai('gpt-4o'))\n  : freeLimiter.wrap(openai('gpt-4o-mini'))\n```\n\n### Per-user limits with a single limiter\n\nFor large numbers of users, scoped limits are more efficient than one limiter per user:\n\n```typescript\nconst limiter = createRateLimiter({\n  scopes: {\n    'user:free:*': { rpm: 5,  itpm: 10_000 },\n    'user:pro:*':  { rpm: 60, itpm: 200_000 },\n  },\n})\n\nconst model = limiter.wrap(openai('gpt-4o'))\n\n// Each request is tracked in an isolated window per user\nawait generateText({\n  model,\n  prompt: req.body.message,\n  providerOptions: {\n    rateLimiter: { scope: `user:${req.user.plan}:${req.user.id}` },\n  },\n})\n```\n\n### Combine OTel tracing with event logging\n\n```typescript\nimport { createOtelPlugin } from 'ai-sdk-rate-limiter/otel'\n\nconst limiter = createRateLimiter({\n  on: {\n    // OTel spans for every request\n    ...createOtelPlugin(trace.getTracer('my-service')),\n    // Plus any additional handlers\n    budgetHit: ({ model, limitUsd, period }) =>\n      alerts.send(`Budget alert: ${model} hit $${limitUsd} ${period} cap`),\n  },\n})\n```\n\n### Custom rate limit store\n\nImplement `RateLimitStore` to use any backend (DynamoDB, Postgres, etc.):\n\n```typescript\nimport type { RateLimitStore } from 'ai-sdk-rate-limiter'\n\nclass MyStore implements RateLimitStore {\n  async checkAndRecord(key, estimatedInputTokens, limits) { /* ... */ }\n  async reconcile(key, actualInputTokens, actualOutputTokens) { /* ... */ }\n  async setBackoff(key, untilMs) { /* ... */ }\n  async getBackoff(key) { /* ... */ }\n  async nextSlotMs(key, limits, estimatedInputTokens) { /* ... */ }\n}\n\nconst limiter = createRateLimiter({ store: new MyStore() })\n```\n\n### Load balancing across API keys\n\n`createModelPool()` distributes requests round-robin across multiple model instances — useful when you have more than one API key:\n\n```typescript\nimport { createRateLimiter, createModelPool } from 'ai-sdk-rate-limiter'\nimport { createOpenAI } from '@ai-sdk/openai'\n\n// Two API keys, each with their own limiter tracking separate RPM limits\nconst limiter1 = createRateLimiter({ limits: { 'gpt-4o': { rpm: 500, itpm: 2_000_000 } } })\nconst limiter2 = createRateLimiter({ limits: { 'gpt-4o': { rpm: 500, itpm: 2_000_000 } } })\n\nconst openai1 = createOpenAI({ apiKey: process.env.OPENAI_KEY_1 })\nconst openai2 = createOpenAI({ apiKey: process.env.OPENAI_KEY_2 })\n\nconst pool = createModelPool([\n  limiter1.wrap(openai1('gpt-4o')),\n  limiter2.wrap(openai2('gpt-4o')),\n])\n\n// Use exactly like a regular model — calls alternate between the two keys\nconst { text } = await generateText({ model: pool, prompt: 'Hello!' })\n```\n\nPass `{ strategy: 'random' }` for random selection instead of round-robin.\n\n### Request deduplication\n\nWhen multiple concurrent requests carry the same `dedupKey`, only one API call is made and all callers receive the same result. Useful for FAQ-style workloads where many users ask the same question simultaneously:\n\n```typescript\nconst model = limiter.wrap(openai('gpt-4o'))\n\n// Server handler — two simultaneous identical requests share one API call\nasync function handleRequest(questionId: string) {\n  const { text } = await generateText({\n    model,\n    prompt: questions[questionId],\n    providerOptions: {\n      rateLimiter: { dedupKey: `faq:${questionId}` },\n    },\n  })\n  return text\n}\n\n// If 50 users hit the same FAQ item at the same time → 1 API call, not 50\nconst results = await Promise.all(users.map(() => handleRequest('faq-42')))\n```\n\nThe dedup entry is removed once the request completes (success or error), so subsequent requests always make a fresh call.\n\n---\n\n## How it works\n\n**Rate limiting** — Sliding window counter per model. Each model tracks a list of `{timestamp, tokens}` entries for the past 60 seconds. On every request, stale entries are evicted and the window is checked against RPM, ITPM, and OTPM limits simultaneously. RPD uses a separate 24-hour rolling window. OTPM is based on actual output token counts from completed requests.\n\n**Queue** — A sorted priority queue per model, ordered by `priority` then enqueue time (FIFO within same priority). A drain timer fires when the oldest window entry expires, processing as many waiters as possible before rescheduling.\n\n**Concurrency** — A semaphore (`activeCount` + `concurrencyWaiters`) per model key, checked after the rate limit slot is acquired. `release()` decrements the count and unblocks the next waiter.\n\n**Scoped limits** — Each scoped request uses a key in the form `scope:provider:modelId`. That key gets its own independent sliding window. Wildcard patterns in `config.scopes` are matched with `*` → `.*` regex expansion.\n\n**AbortSignal** — The signal is registered as an event listener on both the rate-limit queue and the concurrency queue. If it fires, the request is spliced out of whichever queue it's in and the promise rejects with an `AbortError`.\n\n**Retry-After propagation** — When a remote 429 arrives with a `Retry-After` header, the backoff is applied to the entire model key in the engine, not just the failing request. All requests queued behind it pause until the backoff clears. This prevents the common thundering-herd failure where you retry one request while 10 others immediately follow and all get 429s.\n\n**Token estimation** — Before a request fires, tokens are estimated from the prompt text (~4 chars/token) and reserved in the window. After the response, actual usage from the API replaces the estimate. For streaming, actual counts come from the `finish` chunk (Vercel AI SDK) or the final usage chunk (raw proxy). If a stream ends without a usage chunk (some error paths), the window is updated with zeros rather than leaving the estimate in place.\n\n**Zero dependencies** — The Vercel AI SDK middleware interface is implemented structurally — `@ai-sdk/provider` types are used for type checking only and not required at runtime. No `ioredis`, no `bottleneck`, no tokenizer libraries in the core.\n\n---\n\n## Debug mode\n\nSet `debug: true` to enable structured console logging for every rate-limit decision, queue entry/exit, slot acquisition, circuit breaker transition, and completed call cost:\n\n```typescript\nconst limiter = createRateLimiter({ debug: true })\n```\n\nSample output:\n\n```\n[ai-sdk-rate-limiter] gpt-4o: execute (provider=\"openai\" priority=\"normal\")\n[ai-sdk-rate-limiter] gpt-4o: queuing (queueDepth=3 estimatedWaitMs=1200 priority=\"normal\")\n[ai-sdk-rate-limiter] gpt-4o: dequeued (waitedMs=1187 priority=\"normal\")\n[ai-sdk-rate-limiter] gpt-4o: completed (tokens=342+87 costUsd=0.000021 latencyMs=1343 streaming=false)\n```\n\nDebug logging is completely zero-overhead when disabled — no string building, no `JSON.stringify`, no property access on the details object.\n\n---\n\n## Config validation\n\n`createRateLimiter()` validates your configuration at construction time. If it spots a likely misconfiguration it logs a `console.warn` (never throws). Catches you've got covered:\n\n| Issue | Warning |\n|---|---|\n| `cost.store` set but `warmUp()` never called | Reminds you to call `warmUp()` at startup |\n| `circuit.failureThreshold < 3` | Too sensitive — risks false trips on transient errors |\n| `retry.retryOn` excludes 429 | Rate-limit errors won't be retried |\n| `queue.timeout < 3000ms` | Requests will time out before they can be served |\n| `cost.budget` set without `onExceeded` | Silent default is `'throw'` — may want `'queue'` or `'fallback'` |\n| `cost.onExceeded: 'fallback'` | Reminds you to pass a `fallback` model to `limiter.wrap()` |\n\n---\n\n## Comparison\n\n| | ai-sdk-rate-limiter | bottleneck | p-limit | SDK built-in retry | LangChain |\n|---|:---:|:---:|:---:|:---:|:---:|\n| Vercel AI SDK `.wrap()` | yes | no | no | — | no |\n| Raw SDK proxy | yes | no | no | — | no |\n| Model-aware limits | yes | no | no | no | partial |\n| ITPM / token tracking | yes | no | no | no | no |\n| Priority queue | yes | yes | no | no | no |\n| Concurrency limits | yes | yes | yes | no | no |\n| Cost tracking + budgets | yes | no | no | no | no |\n| Persistent cost store | yes | no | no | no | no |\n| Per-scope cost attribution | yes | no | no | no | no |\n| Budget fallback chains | yes | no | no | no | no |\n| Circuit breaker | yes | no | no | no | no |\n| Graceful shutdown | yes | no | no | no | no |\n| Auto-detected limits | yes | no | no | no | no |\n| Multi-tenant scoped limits | yes | no | no | no | no |\n| AbortSignal propagation | yes | no | no | no | no |\n| Call timeout | yes | no | no | no | no |\n| Retry-After header | yes | no | no | partial | partial |\n| Backoff propagation | yes | no | no | no | no |\n| Prometheus metrics | yes | no | no | no | no |\n| StatsD metrics | yes | no | no | no | no |\n| Express/Hono middleware | yes | no | no | no | no |\n| OpenTelemetry | yes | no | no | no | partial |\n| Testing utilities | yes | no | no | no | no |\n| CLI audit | yes | no | no | no | no |\n| Zero runtime deps | yes | no | yes | — | no |\n| Provider-agnostic | yes | yes | yes | no | no |\n\n**bottleneck** — Excellent general-purpose rate limiting, but knows nothing about AI APIs. No model limits, no token counting, no cost tracking. You'd need to configure it per-model manually and rebuild the cost system yourself.\n\n**p-limit** — Controls concurrency, not rate. Limits to N concurrent requests, not N requests per minute. A different problem.\n\n**SDK built-in retry** — Retries on 429 with backoff. That's the floor, not the ceiling. No queuing, no priority, no cost tracking, no backoff propagation to other in-flight requests.\n\n---\n\n## TypeScript\n\nFully typed. All configuration options, events, errors, and report shapes have precise TypeScript definitions exported from the main entry point.\n\n```typescript\nimport type {\n  RateLimiterConfig,\n  ModelLimits,\n  ScopeConfig,\n  CostReport,\n  EventMap,\n  QueuedEvent,\n  Priority,\n  CircuitBreakerConfig,\n  CircuitOpenEvent,\n  CircuitClosedEvent,\n  LimitsDetectedEvent,\n  DroppedEvent,\n  CompletedEvent,\n} from 'ai-sdk-rate-limiter'\n\nimport type { CallRecord } from 'ai-sdk-rate-limiter/testing'\n\nimport type {\n  CostStore,\n  PersistedCostEntry,\n} from 'ai-sdk-rate-limiter/redis'\n\nimport type { StatsDClient } from 'ai-sdk-rate-limiter/statsd'\n\nimport type {\n  RateLimiterRequestContext,\n  RateLimiterMiddlewareOptions,\n  ErrorHandlerOptions,\n  HonoMiddlewareOptions,\n  HonoContext,\n} from 'ai-sdk-rate-limiter/middleware'\n```\n\n---\n\n## Examples\n\nFour runnable examples are included, each with its own README:\n\n| Example | What it shows |\n|---|---|\n| [`examples/nextjs/`](./examples/nextjs/) | Next.js 15 App Router streaming chat — rate limiting, live cost display, budget error handling |\n| [`examples/multi-tenant-express/`](./examples/multi-tenant-express/) | Express API with per-user isolated limits (free/pro tiers), per-user cost reports, circuit breaker |\n| [`examples/batch-processing/`](./examples/batch-processing/) | Classify 30+ items concurrently without 429s — priority queuing, graceful shutdown, live cost tracking |\n| [`examples/budget-alerts/`](./examples/budget-alerts/) | Slack/webhook alerts on budget thresholds — instant `budgetHit` events + periodic spend summaries |\n\n---\n\n## Bundle sizes\n\nEach entry point is independently tree-shakeable. Importing `ai-sdk-rate-limiter` never pulls in Redis, Prometheus, OTel, or StatsD.\n\n| Entry point | Size (minified) | Size (gzip) |\n|---|---|---|\n| `ai-sdk-rate-limiter` | ~80 KB | ~22 KB |\n| `ai-sdk-rate-limiter/redis` | ~12 KB | ~4 KB |\n| `ai-sdk-rate-limiter/middleware` | ~8 KB | ~2.5 KB |\n| `ai-sdk-rate-limiter/prometheus` | ~8 KB | ~2.5 KB |\n| `ai-sdk-rate-limiter/otel` | ~4 KB | ~1.5 KB |\n| `ai-sdk-rate-limiter/statsd` | ~4 KB | ~1.2 KB |\n\nThe core package is self-contained. Optional peer deps (`ioredis`, `@opentelemetry/api`) are only loaded when you import the corresponding entry point.\n\n---\n\n## Requirements\n\n- Node.js 18+ / Bun / Deno\n- `ai` v4+ (Vercel AI SDK) — optional peer dep, only needed for `.wrap()`\n- Zero required runtime dependencies\n\n---\n\n## License\n\nMIT\n","readmeFilename":"README.md"}