{"_id":"@aeye/ai","_rev":"12-661fea3746656a44ccc8a126e6ddb38e","name":"@aeye/ai","dist-tags":{"latest":"0.3.9"},"versions":{"0.1.0":{"name":"@aeye/ai","version":"0.1.0","keywords":["ai","typescript","core"],"author":{"name":"ClickerMonkey"},"license":"MIT","_id":"@aeye/ai@0.1.0","maintainers":[{"name":"philip.diffenderfer","email":"pdiffenderfer@gmail.com"}],"homepage":"https://github.com/ClickerMonkey/aeye#readme","bugs":{"url":"https://github.com/ClickerMonkey/aeye/issues"},"dist":{"shasum":"901ad770f4527c3b9dbd4edc02a3dab23a4a403b","tarball":"https://registry.npmjs.org/@aeye/ai/-/ai-0.1.0.tgz","fileCount":54,"integrity":"sha512-bxFvAccSTjj+8f+ebjwEhoQ9rRUFEwRf+3dcEfwpVX3qaH1t1p/Rd0ccffJZ2RpV2tzS9i7ClpSP8imAufEEJQ==","signatures":[{"sig":"MEUCIQCTC8yZVrZL/Dz1MzxsbiVNH3a73DujbKwncq+TrnTOdQIgT5ag3n1HxXQRn7ZjSQDAbBk+VeMff4iIQ9K1nkXHJCQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":337705},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=16.0.0"},"gitHead":"7e1a90cc8ba4f871aaa577977959317604a10b08","scripts":{"test":"jest","build":"tsc","clean":"rimraf dist tsconfig.tsbuildinfo","typecheck":"tsc --noEmit","test:watch":"jest --watch","test:coverage":"jest --coverage"},"_npmUser":{"name":"philip.diffenderfer","email":"pdiffenderfer@gmail.com"},"repository":{"url":"git+https://github.com/ClickerMonkey/aeye.git","type":"git","directory":"packages/ai"},"_npmVersion":"10.8.2","description":"@aeye AI - AI TypeScript library main functionality","directories":{},"_nodeVersion":"20.18.1","dependencies":{"@aeye/core":"^0.1.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^30.2.0","ts-jest":"^29.4.5","typescript":"^5.9.3","@types/jest":"^30.0.0","@types/node":"^24.9.1"},"_npmOperationalInternal":{"tmp":"tmp/ai_0.1.0_1763393628163_0.7102529572459544","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@aeye/ai","version":"0.1.1","keywords":["ai","typescript","core"],"author":{"name":"ClickerMonkey"},"license":"GPL-3.0","_id":"@aeye/ai@0.1.1","maintainers":[{"name":"philip.diffenderfer","email":"pdiffenderfer@gmail.com"}],"homepage":"https://github.com/ClickerMonkey/aeye#readme","bugs":{"url":"https://github.com/ClickerMonkey/aeye/issues"},"dist":{"shasum":"57ffbb6b7df7b8511e49196e443ddaa8ecefb1fa","tarball":"https://registry.npmjs.org/@aeye/ai/-/ai-0.1.1.tgz","fileCount":54,"integrity":"sha512-+CmXk5HwEyCepOe165Knws7U0UarEIEp/3P2HoARCPkK4VIyBl+Be3rGO3wTP7FPKBP9IexRUecpot9Ia96iAg==","signatures":[{"sig":"MEYCIQDmbu9FgfGVfMdlOrgjXjYZFkRqKuGhAqQUhZu9J7r/0wIhAMAG1Lp2+9C7xYAgrSsxxqzpQjZ2oyO4Dr5R2W0OHyER","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":338102},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=16.0.0"},"gitHead":"2515fffb0b757b7460c813024d78f3481ba58d10","scripts":{"test":"jest","build":"tsc","clean":"rimraf dist tsconfig.tsbuildinfo","typecheck":"tsc --noEmit","test:watch":"jest --watch","test:coverage":"jest --coverage"},"_npmUser":{"name":"philip.diffenderfer","email":"pdiffenderfer@gmail.com"},"repository":{"url":"git+https://github.com/ClickerMonkey/aeye.git","type":"git","directory":"packages/ai"},"_npmVersion":"10.8.2","description":"@aeye AI - AI TypeScript library main functionality","directories":{},"_nodeVersion":"20.18.1","dependencies":{"@aeye/core":"^0.1.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^30.2.0","ts-jest":"^29.4.5","typescript":"^5.9.3","@types/jest":"^30.0.0","@types/node":"^24.9.1"},"_npmOperationalInternal":{"tmp":"tmp/ai_0.1.1_1763671240210_0.631469648009422","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@aeye/ai","version":"0.2.0","keywords":["ai","typescript","core"],"author":{"name":"ClickerMonkey"},"license":"GPL-3.0","_id":"@aeye/ai@0.2.0","maintainers":[{"name":"philip.diffenderfer","email":"pdiffenderfer@gmail.com"}],"homepage":"https://github.com/ClickerMonkey/aeye#readme","bugs":{"url":"https://github.com/ClickerMonkey/aeye/issues"},"dist":{"shasum":"94e539ea15226cc25e99aa1337a642210c810ccb","tarball":"https://registry.npmjs.org/@aeye/ai/-/ai-0.2.0.tgz","fileCount":54,"integrity":"sha512-0JE/fPnFWU+NeMP0W3Z+QVOTXLCt+bwExvqbpxyQSXH8Jz/FG9WlNHH+lmR5cDnen6k5txVyJhIGnbkiR55wRA==","signatures":[{"sig":"MEUCIBlA4rmjsZVAoDT4h/HcllXr/S8boiKr/1tR3M8WqckmAiEA9lnhOi2asoCP+ZdbGnoP/p41Xb81RQlAJgzTD3X8rHA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":338426},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=16.0.0"},"gitHead":"25679e42025ba167faec33e98d702939cc22f2c0","scripts":{"test":"jest","build":"tsc","clean":"rimraf dist tsconfig.tsbuildinfo","typecheck":"tsc --noEmit","test:watch":"jest --watch","test:coverage":"jest --coverage"},"_npmUser":{"name":"philip.diffenderfer","email":"pdiffenderfer@gmail.com"},"repository":{"url":"git+https://github.com/ClickerMonkey/aeye.git","type":"git","directory":"packages/ai"},"_npmVersion":"10.8.2","description":"@aeye AI - AI TypeScript library main functionality","directories":{},"_nodeVersion":"20.18.1","dependencies":{"@aeye/core":"^0.1.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^30.2.0","ts-jest":"^29.4.5","typescript":"^5.9.3","@types/jest":"^30.0.0","@types/node":"^24.9.1"},"_npmOperationalInternal":{"tmp":"tmp/ai_0.2.0_1764260294960_0.9835911263388335","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@aeye/ai","version":"0.3.0","keywords":["ai","typescript","core"],"author":{"name":"ClickerMonkey"},"license":"GPL-3.0","_id":"@aeye/ai@0.3.0","maintainers":[{"name":"philip.diffenderfer","email":"pdiffenderfer@gmail.com"}],"homepage":"https://github.com/ClickerMonkey/aeye#readme","bugs":{"url":"https://github.com/ClickerMonkey/aeye/issues"},"dist":{"shasum":"e22dfee35b774877fdd8281a627b8a0fc6212e1d","tarball":"https://registry.npmjs.org/@aeye/ai/-/ai-0.3.0.tgz","fileCount":54,"integrity":"sha512-duCERwB0Ua9njogoqQMe57YGOQd54Opyezxpqy9Nwf4jyNxU3qE37NYmOp4GHX9N5UnkcMc9lLS1TXBMc9ml6Q==","signatures":[{"sig":"MEUCIQDe2bDVSEayHX4y1t0UeKZdiiGxy0ccpK/CwXr8aDCzHwIgFMzY8i8h8r1hvhsK1LPrDL1uo/qhEM4zaRAOaiAZRHM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":339014},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=16.0.0"},"gitHead":"fe6afb5adb096a8de1dfc9a115bba1d48193612d","scripts":{"test":"jest","build":"tsc","clean":"rimraf dist tsconfig.tsbuildinfo","typecheck":"tsc --noEmit","test:watch":"jest --watch","test:coverage":"jest --coverage"},"_npmUser":{"name":"philip.diffenderfer","email":"pdiffenderfer@gmail.com"},"repository":{"url":"git+https://github.com/ClickerMonkey/aeye.git","type":"git","directory":"packages/ai"},"_npmVersion":"10.8.2","description":"@aeye AI - AI TypeScript library main functionality","directories":{},"_nodeVersion":"20.18.1","dependencies":{"@aeye/core":"^0.3.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^30.2.0","ts-jest":"^29.4.5","typescript":"^5.9.3","@types/jest":"^30.0.0","@types/node":"^24.9.1"},"_npmOperationalInternal":{"tmp":"tmp/ai_0.3.0_1764993449132_0.4499318647170576","host":"s3://npm-registry-packages-npm-production"}},"0.3.1":{"name":"@aeye/ai","version":"0.3.1","keywords":["ai","typescript","core"],"author":{"name":"ClickerMonkey"},"license":"GPL-3.0","_id":"@aeye/ai@0.3.1","maintainers":[{"name":"philip.diffenderfer","email":"pdiffenderfer@gmail.com"}],"homepage":"https://github.com/ClickerMonkey/aeye#readme","bugs":{"url":"https://github.com/ClickerMonkey/aeye/issues"},"dist":{"shasum":"2e657cf1d3a3d07d32d1eb050ba9d7cd07d37e84","tarball":"https://registry.npmjs.org/@aeye/ai/-/ai-0.3.1.tgz","fileCount":54,"integrity":"sha512-9oy2vRGRo3huxbYd3Wo9n6cOmU2YWgjoaVm2WKi5+zn7hc3kFWanqJfl0tge8sOgffXdMlTOK+QYKfx8jv3nYA==","signatures":[{"sig":"MEYCIQCDyBmne6W7BxEk0790ew9+gWwFWRxr9QSYxjmi+uloOAIhAMlrY1XPo6ybK3vFJ+BbveJUy+ICUqSPPJVaYG1QGGd9","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":356525},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=16.0.0"},"gitHead":"cabd7c1d28f8048b51cb89b40efa8d07cead332d","scripts":{"test":"jest","build":"tsc","clean":"rimraf dist tsconfig.tsbuildinfo","typecheck":"tsc --noEmit","test:watch":"jest --watch","test:coverage":"jest --coverage"},"_npmUser":{"name":"philip.diffenderfer","email":"pdiffenderfer@gmail.com"},"repository":{"url":"git+https://github.com/ClickerMonkey/aeye.git","type":"git","directory":"packages/ai"},"_npmVersion":"10.8.2","description":"@aeye AI - AI TypeScript library main functionality","directories":{},"_nodeVersion":"20.18.1","dependencies":{"@aeye/core":"^0.3.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^30.2.0","ts-jest":"^29.4.5","typescript":"^5.9.3","@types/jest":"^30.0.0","@types/node":"^24.9.1"},"_npmOperationalInternal":{"tmp":"tmp/ai_0.3.1_1765363622840_0.6594252690590283","host":"s3://npm-registry-packages-npm-production"}},"0.3.2":{"name":"@aeye/ai","version":"0.3.2","keywords":["ai","typescript","core"],"author":{"name":"ClickerMonkey"},"license":"GPL-3.0","_id":"@aeye/ai@0.3.2","maintainers":[{"name":"philip.diffenderfer","email":"pdiffenderfer@gmail.com"}],"homepage":"https://github.com/ClickerMonkey/aeye#readme","bugs":{"url":"https://github.com/ClickerMonkey/aeye/issues"},"dist":{"shasum":"666d262770b7016f3797bda41e0edc7c0718ea98","tarball":"https://registry.npmjs.org/@aeye/ai/-/ai-0.3.2.tgz","fileCount":54,"integrity":"sha512-15AcYT74ntjgj+WMRLPbZqnHroHGllKwcjsjyc+hZ8KQn25zGvE7V7f9OrQC+XzHyuJgilWesmb1kwrtp4lBtg==","signatures":[{"sig":"MEUCIQCdrWzTG02mq7pXFXbu/g3Ku/5WSasJ2p8ddBm8yLERkQIgYpJV7FpM+X+lINA2t/TWST4OJItvoPMDomXEjX2hYic=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":356525},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=16.0.0"},"gitHead":"ae51c2a8249a968b210a64efc7c8bedb35fad70b","scripts":{"test":"jest","build":"tsc","clean":"rimraf dist tsconfig.tsbuildinfo","typecheck":"tsc --noEmit","test:watch":"jest --watch","test:coverage":"jest --coverage"},"_npmUser":{"name":"philip.diffenderfer","email":"pdiffenderfer@gmail.com"},"repository":{"url":"git+https://github.com/ClickerMonkey/aeye.git","type":"git","directory":"packages/ai"},"_npmVersion":"10.8.2","description":"@aeye AI - AI TypeScript library main functionality","directories":{},"_nodeVersion":"20.18.1","dependencies":{"@aeye/core":"^0.3.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^30.2.0","ts-jest":"^29.4.5","typescript":"^5.9.3","@types/jest":"^30.0.0","@types/node":"^24.9.1"},"_npmOperationalInternal":{"tmp":"tmp/ai_0.3.2_1766336257890_0.8151325117240404","host":"s3://npm-registry-packages-npm-production"}},"0.3.4":{"name":"@aeye/ai","version":"0.3.4","keywords":["ai","typescript","core"],"author":{"name":"ClickerMonkey"},"license":"GPL-3.0","_id":"@aeye/ai@0.3.4","maintainers":[{"name":"philip.diffenderfer","email":"pdiffenderfer@gmail.com"}],"homepage":"https://github.com/ClickerMonkey/aeye#readme","bugs":{"url":"https://github.com/ClickerMonkey/aeye/issues"},"dist":{"shasum":"14255f73c07f57107038535d559bf350bed5096f","tarball":"https://registry.npmjs.org/@aeye/ai/-/ai-0.3.4.tgz","fileCount":54,"integrity":"sha512-5tz69Msh0bumaVb6tjHh5PZOjhbQ94hY4BoHtMhwlzODBl3bqgJvYFTjmKvh/cs3JmrxS6PN3aGfvuJMXP7u3g==","signatures":[{"sig":"MEUCIQDiZ2iZ8LUteSQPa1+4FIu25JC7GctsqR95KYGZy3OTbwIgFqGXSQaIWLexeNZK1Rc9pDri+64A1T1KkXeNVsrMC/U=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":356525},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=16.0.0"},"gitHead":"3f4fe5f9da6cad0851958297b5dd34b2c795d6d2","scripts":{"test":"jest","build":"tsc","clean":"rimraf dist tsconfig.tsbuildinfo","typecheck":"tsc --noEmit","test:watch":"jest --watch","test:coverage":"jest --coverage"},"_npmUser":{"name":"philip.diffenderfer","email":"pdiffenderfer@gmail.com"},"repository":{"url":"git+https://github.com/ClickerMonkey/aeye.git","type":"git","directory":"packages/ai"},"_npmVersion":"10.8.2","description":"@aeye AI - AI TypeScript library main functionality","directories":{},"_nodeVersion":"20.18.1","dependencies":{"@aeye/core":"^0.3.4"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^30.2.0","ts-jest":"^29.4.5","typescript":"^5.9.3","@types/jest":"^30.0.0","@types/node":"^24.9.1"},"_npmOperationalInternal":{"tmp":"tmp/ai_0.3.4_1770754889752_0.7530350858629227","host":"s3://npm-registry-packages-npm-production"}},"0.3.5":{"name":"@aeye/ai","version":"0.3.5","keywords":["ai","typescript","core"],"author":{"name":"ClickerMonkey"},"license":"GPL-3.0","_id":"@aeye/ai@0.3.5","maintainers":[{"name":"philip.diffenderfer","email":"pdiffenderfer@gmail.com"}],"homepage":"https://github.com/ClickerMonkey/aeye#readme","bugs":{"url":"https://github.com/ClickerMonkey/aeye/issues"},"dist":{"shasum":"bfbaa0a94fc3bb7e88c317d32b63a075fb1d7fbf","tarball":"https://registry.npmjs.org/@aeye/ai/-/ai-0.3.5.tgz","fileCount":54,"integrity":"sha512-nnxHy6OV3B0EzyHk3OjFkwkrGzvKnWYa4h46ue5PCy+x6Oc2+DdX/s4c3Gqwf85eNpYWVdk++CKYYYfBAmb+Lw==","signatures":[{"sig":"MEUCIQD2aJglELXjySWaNC0oPMkVTEr7B34gRrNehomYr3yqGwIgUMaynrrgS+CCoCy5zXiI0g5qlGkLNik8EYMhCUwNHh0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":356525},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=16.0.0"},"gitHead":"1ca1832163262906c1ae12624a107c0652daeb68","scripts":{"test":"jest","build":"tsc","clean":"rimraf dist tsconfig.tsbuildinfo","typecheck":"tsc --noEmit","test:watch":"jest --watch","test:coverage":"jest --coverage"},"_npmUser":{"name":"philip.diffenderfer","email":"pdiffenderfer@gmail.com"},"repository":{"url":"git+https://github.com/ClickerMonkey/aeye.git","type":"git","directory":"packages/ai"},"_npmVersion":"10.8.2","description":"@aeye AI - AI TypeScript library main functionality","directories":{},"_nodeVersion":"20.18.1","dependencies":{"@aeye/core":"^0.3.5"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^30.2.0","ts-jest":"^29.4.5","typescript":"^5.9.3","@types/jest":"^30.0.0","@types/node":"^24.9.1"},"_npmOperationalInternal":{"tmp":"tmp/ai_0.3.5_1770757032251_0.04056129402140485","host":"s3://npm-registry-packages-npm-production"}},"0.3.6":{"name":"@aeye/ai","version":"0.3.6","keywords":["ai","typescript","core"],"author":{"name":"ClickerMonkey"},"license":"GPL-3.0","_id":"@aeye/ai@0.3.6","maintainers":[{"name":"philip.diffenderfer","email":"pdiffenderfer@gmail.com"}],"homepage":"https://github.com/ClickerMonkey/aeye#readme","bugs":{"url":"https://github.com/ClickerMonkey/aeye/issues"},"dist":{"shasum":"2017bcbd56578ce21b4d793001613b762d7683fc","tarball":"https://registry.npmjs.org/@aeye/ai/-/ai-0.3.6.tgz","fileCount":54,"integrity":"sha512-r2sO4i8EO+n4BT74oWLNiDxdqBX0oKCo1z4YLPejmZk3cdElSPGTzIQkSA98uOQV9Byw/J/BlWGfYzL8dNRw5w==","signatures":[{"sig":"MEUCIGyDHxw8i73kKC2wmIDaKWwL8z9JLYtg6Mn1wPfOIH6BAiEAqFrVEOkNuGp6OI4xxIuRZ3i9etUKTzg8tOxUn/0PhuY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":356802},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=16.0.0"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"default":"./dist/index.js","require":{"types":"./dist/index.d.ts","default":"./dist/index.js"}}},"gitHead":"1ca1832163262906c1ae12624a107c0652daeb68","scripts":{"test":"jest","build":"tsc","clean":"rimraf dist tsconfig.tsbuildinfo","typecheck":"tsc --noEmit","test:watch":"jest --watch","test:coverage":"jest --coverage"},"_npmUser":{"name":"philip.diffenderfer","email":"pdiffenderfer@gmail.com"},"repository":{"url":"git+https://github.com/ClickerMonkey/aeye.git","type":"git","directory":"packages/ai"},"_npmVersion":"10.8.2","description":"@aeye AI - AI TypeScript library main functionality","directories":{},"_nodeVersion":"20.18.1","dependencies":{"@aeye/core":"^0.3.6"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^30.2.0","ts-jest":"^29.4.5","typescript":"^5.9.3","@types/jest":"^30.0.0","@types/node":"^24.9.1"},"_npmOperationalInternal":{"tmp":"tmp/ai_0.3.6_1772640916976_0.1334431840447805","host":"s3://npm-registry-packages-npm-production"}},"0.3.7":{"name":"@aeye/ai","version":"0.3.7","keywords":["ai","typescript","core"],"author":{"name":"ClickerMonkey"},"license":"GPL-3.0","_id":"@aeye/ai@0.3.7","maintainers":[{"name":"philip.diffenderfer","email":"pdiffenderfer@gmail.com"}],"homepage":"https://github.com/ClickerMonkey/aeye#readme","bugs":{"url":"https://github.com/ClickerMonkey/aeye/issues"},"dist":{"shasum":"090639fb3a81fdceedbeb903530b3394e73cb7ee","tarball":"https://registry.npmjs.org/@aeye/ai/-/ai-0.3.7.tgz","fileCount":54,"integrity":"sha512-GbUOT9/uElXOrPPohbo+7IYeLuTx78bPZl9k2LjQ3d3uD651il1YA2Bf3/OmesOsZV3WzvBu+a4xDEwOsGJiOA==","signatures":[{"sig":"MEUCIHxuwWffxzI1DdBa4pNS/wXBw3VVaM3wf02kqojKG8i+AiEA0NTKfPrhA8gaQPipakU0PLQ5F6mrFHttSxqcwYlLKok=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":356802},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=16.0.0"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"default":"./dist/index.js","require":{"types":"./dist/index.d.ts","default":"./dist/index.js"}}},"gitHead":"a3490a88105713496870fbcf0a8d5ff9d85a3e6c","scripts":{"test":"jest","build":"tsc","clean":"rimraf dist tsconfig.tsbuildinfo","typecheck":"tsc --noEmit","test:watch":"jest --watch","test:coverage":"jest --coverage"},"_npmUser":{"name":"philip.diffenderfer","email":"pdiffenderfer@gmail.com"},"repository":{"url":"git+https://github.com/ClickerMonkey/aeye.git","type":"git","directory":"packages/ai"},"_npmVersion":"10.8.2","description":"@aeye AI - AI TypeScript library main functionality","directories":{},"_nodeVersion":"20.18.1","dependencies":{"@aeye/core":"^0.3.7"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^30.2.0","ts-jest":"^29.4.5","typescript":"^5.9.3","@types/jest":"^30.0.0","@types/node":"^24.9.1"},"_npmOperationalInternal":{"tmp":"tmp/ai_0.3.7_1772642468767_0.5192152596282766","host":"s3://npm-registry-packages-npm-production"}},"0.3.8":{"name":"@aeye/ai","version":"0.3.8","keywords":["ai","typescript","core"],"author":{"name":"ClickerMonkey"},"license":"GPL-3.0","_id":"@aeye/ai@0.3.8","maintainers":[{"name":"philip.diffenderfer","email":"pdiffenderfer@gmail.com"}],"homepage":"https://github.com/ClickerMonkey/aeye#readme","bugs":{"url":"https://github.com/ClickerMonkey/aeye/issues"},"dist":{"shasum":"917c2695244351de8b57a4e9f9b3477edbd521af","tarball":"https://registry.npmjs.org/@aeye/ai/-/ai-0.3.8.tgz","fileCount":54,"integrity":"sha512-vyleSMzKJjGQ932TirV8OoKWX+1XTKdZd748C2Tn2IoFhziHeR8/b2Co/yIQX6CLPXEDIJXsolFChZLC82wvMQ==","signatures":[{"sig":"MEYCIQCzCsfC0TGqcawi4P1I5weokudHIAcGbVvCPVXG4Ikp6AIhAKXvjoczDUq6iiWK2+5yc9gyKcm6T4LtaJOx169QI6O9","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":352369},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=16.0.0"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"default":"./dist/index.js","require":{"types":"./dist/index.d.ts","default":"./dist/index.js"}}},"gitHead":"04319fa907b4c219f0843292baa1bcd313a97c74","scripts":{"test":"jest","build":"tsc","clean":"rimraf dist tsconfig.tsbuildinfo","typecheck":"tsc --noEmit","test:watch":"jest --watch","test:coverage":"jest --coverage"},"_npmUser":{"name":"philip.diffenderfer","email":"pdiffenderfer@gmail.com"},"repository":{"url":"git+https://github.com/ClickerMonkey/aeye.git","type":"git","directory":"packages/ai"},"_npmVersion":"10.8.2","description":"@aeye AI - AI TypeScript library main functionality","directories":{},"_nodeVersion":"20.18.1","dependencies":{"@aeye/core":"^0.3.8"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^30.2.0","ts-jest":"^29.4.5","typescript":"^5.9.3","@types/jest":"^30.0.0","@types/node":"^24.9.1"},"_npmOperationalInternal":{"tmp":"tmp/ai_0.3.8_1775335850576_0.4209590669595622","host":"s3://npm-registry-packages-npm-production"}},"0.3.9":{"name":"@aeye/ai","version":"0.3.9","description":"@aeye AI - AI TypeScript library main functionality","type":"module","main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"default":"./dist/index.js"}},"scripts":{"build":"tsc","test":"jest","test:coverage":"jest --coverage","test:watch":"jest --watch","clean":"rimraf dist tsconfig.tsbuildinfo","typecheck":"tsc --noEmit"},"keywords":["ai","typescript","core"],"author":{"name":"ClickerMonkey"},"license":"GPL-3.0","repository":{"type":"git","url":"git+https://github.com/ClickerMonkey/aeye.git","directory":"packages/ai"},"dependencies":{"@aeye/core":"^0.3.9"},"devDependencies":{"@types/jest":"^30.0.0","@types/node":"^24.9.1","jest":"^30.2.0","ts-jest":"^29.4.5","typescript":"^5.9.3"},"publishConfig":{"access":"public"},"engines":{"node":">=16.0.0"},"homepage":"https://github.com/ClickerMonkey/aeye#readme","bugs":{"url":"https://github.com/ClickerMonkey/aeye/issues"},"_id":"@aeye/ai@0.3.9","gitHead":"7df3bae899c5a30cf17f4925fb7057e9a61e7332","_nodeVersion":"22.22.3","_npmVersion":"10.9.8","dist":{"integrity":"sha512-NfvYtE3x52kjtF7ddt0eHdVa+PgSSfXBL5YpU6fx7EoYYOUruotWIIJWaaEDKeH87OvhNcRQ+32kqcayTWy+AQ==","shasum":"56c7750a309e86e242e4229a9ea5e7a57415729b","tarball":"https://registry.npmjs.org/@aeye/ai/-/ai-0.3.9.tgz","fileCount":59,"unpackedSize":397098,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIFL3ohXQ+1AlZLqsmddYQ0+GpjFK2hSvLZPPMtLhIEgSAiAqkuH7mV/qKqke1Xoeu1VrumYZ5LT3tPYlkQzALaOaXg=="}]},"_npmUser":{"name":"philip.diffenderfer","email":"pdiffenderfer@gmail.com"},"directories":{},"maintainers":[{"name":"philip.diffenderfer","email":"pdiffenderfer@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/ai_0.3.9_1783858082283_0.9439335793730008"},"_hasShrinkwrap":false}},"time":{"created":"2025-11-17T15:33:48.065Z","modified":"2026-07-12T12:08:02.527Z","0.1.0":"2025-11-17T15:33:48.350Z","0.1.1":"2025-11-20T20:40:40.403Z","0.2.0":"2025-11-27T16:18:15.209Z","0.3.0":"2025-12-06T03:57:29.321Z","0.3.1":"2025-12-10T10:47:03.003Z","0.3.2":"2025-12-21T16:57:38.039Z","0.3.4":"2026-02-10T20:21:29.903Z","0.3.5":"2026-02-10T20:57:12.409Z","0.3.6":"2026-03-04T16:15:17.166Z","0.3.7":"2026-03-04T16:41:08.938Z","0.3.8":"2026-04-04T20:50:50.743Z","0.3.9":"2026-07-12T12:08:02.426Z"},"bugs":{"url":"https://github.com/ClickerMonkey/aeye/issues"},"author":{"name":"ClickerMonkey"},"license":"GPL-3.0","homepage":"https://github.com/ClickerMonkey/aeye#readme","keywords":["ai","typescript","core"],"repository":{"type":"git","url":"git+https://github.com/ClickerMonkey/aeye.git","directory":"packages/ai"},"description":"@aeye AI - AI TypeScript library main functionality","maintainers":[{"name":"philip.diffenderfer","email":"pdiffenderfer@gmail.com"}],"readme":"# @aeye/ai\r\n\r\n> **Multi-provider AI library with intelligent model selection, type-safe context management, and comprehensive hooks system.**\r\n\r\nThe `@aeye/ai` package is the main AI library built on `@aeye/core`, providing a unified interface for working with multiple AI providers with automatic model selection, cost tracking, and extensible architecture.\r\n\r\n```ts\r\nimport { AI } from '@aeye/ai';\r\nimport { OpenAIProvider } from '@aeye/openai';\r\nimport z from 'zod';\r\n\r\nconst openai = new OpenAIProvider({ apiKey: process.env.OPENAI_API_KEY! });\r\n\r\nconst ai = AI.with<MyContext>()\r\n  .providers({ openai })\r\n  .create({ /* defaultContext, providedContext, defaultWeights, hooks, ... */ });\r\n\r\n// Low-level APIs\r\nai.chat.get(request, ctx?)           // or .stream(request, ctx?)\r\nai.image.generate.get(request, ctx?) // or .stream(request, ctx?)\r\nai.image.edit.get(request, ctx?)     // or .stream(request, ctx?)\r\nai.image.analyze.get(request, ctx?)  // or .stream(request, ctx?)\r\nai.transcribe.get(request, ctx?)     // or .stream(request, ctx?)\r\nai.speech.get(request, ctx?)         // or .stream(request, ctx?)\r\nai.embed.get(request, ctx?)\r\nai.models.list()  // .get(id), .search(criteria), .select(criteria), .refresh()\r\n\r\n// Components bound to this AI instance\r\nconst myTool   = ai.tool({ name, description, schema, call, ... });\r\nconst myPrompt = ai.prompt({ name, description, content, tools, schema, ... });\r\nconst myAgent  = ai.agent({ name, description, refs, call });\r\n\r\nmyTool.run(input, ctx?)\r\nmyPrompt.run(input, ctx?)                    // streaming generator\r\nmyPrompt.get('result', input, ctx?)          // awaitable result\r\nmyPrompt.get('stream', input, ctx?)          // stream all events\r\nmyPrompt.get('streamContent', input, ctx?)   // stream text only\r\nmyAgent.run(input, ctx?)\r\n```\r\n\r\n## Features\r\n\r\n- **Multi-Provider Support**: Single interface for OpenAI, OpenRouter, Replicate, AWS Bedrock, and custom providers\r\n- **Intelligent Model Selection**: Automatic model selection based on capabilities, cost, speed, and quality\r\n- **Type-Safe Context**: Strongly-typed context and metadata with compiler validation\r\n- **Comprehensive APIs**: Chat, Image Generation/Analysis/Editing, Speech Synthesis, Transcription, Embeddings\r\n- **Lifecycle Hooks**: Intercept and modify operations at every stage\r\n- **Cost Tracking**: Automatic token usage and cost calculation\r\n- **Streaming Support**: Full streaming support across all compatible capabilities\r\n- **Model Registry**: Centralized model management with external sources (OpenRouter, etc.)\r\n- **Extensible**: Custom providers, model handlers, and transformers\r\n\r\n## Table of Contents\r\n\r\n- [Installation](#installation)\r\n- [Quick Start](#quick-start)\r\n- [Architecture](#architecture)\r\n- [Core Concepts](#core-concepts)\r\n- [API Reference](#api-reference)\r\n  - [Chat API](#chat-api)\r\n  - [Image API](#image-api)\r\n  - [Speech API](#speech-api)\r\n  - [Transcribe API](#transcribe-api)\r\n  - [Embed API](#embed-api)\r\n  - [Models API](#models-api)\r\n- [Tools, Prompts & Agents](#tools-prompts--agents)\r\n- [Advanced Features](#advanced-features)\r\n  - [Custom Context & Metadata](#custom-context--metadata)\r\n  - [Lifecycle Hooks](#lifecycle-hooks)\r\n  - [Model Selection](#model-selection)\r\n  - [Model Sources](#model-sources)\r\n  - [Custom Providers](#custom-providers)\r\n  - [Extending AI Instances](#extending-ai-instances)\r\n- [Examples](#examples)\r\n\r\n## Installation\r\n\r\n```bash\r\nnpm install @aeye/ai @aeye/core\r\n```\r\n\r\nYou'll also need provider packages:\r\n\r\n```bash\r\nnpm install @aeye/openai openai       # OpenAI\r\nnpm install @aeye/openrouter          # OpenRouter\r\nnpm install @aeye/replicate replicate # Replicate\r\nnpm install @aeye/aws                 # AWS Bedrock\r\n```\r\n\r\n## Quick Start\r\n\r\n```typescript\r\nimport { AI } from '@aeye/ai';\r\nimport { OpenAIProvider } from '@aeye/openai';\r\nimport { OpenRouterProvider } from '@aeye/openrouter';\r\n\r\nconst openai = new OpenAIProvider({ apiKey: process.env.OPENAI_API_KEY! });\r\nconst openrouter = new OpenRouterProvider({ apiKey: process.env.OPENROUTER_API_KEY! });\r\n\r\nconst ai = AI.with()\r\n  .providers({ openai, openrouter })\r\n  .create();\r\n\r\n// Simple chat completion\r\nconst response = await ai.chat.get({\r\n  messages: [{ role: 'user', content: 'What is TypeScript?' }]\r\n});\r\nconsole.log(response.content);\r\n\r\n// Streaming\r\nfor await (const chunk of ai.chat.stream({\r\n  messages: [{ role: 'user', content: 'Write a poem' }]\r\n})) {\r\n  if (chunk.content) {\r\n    process.stdout.write(chunk.content);\r\n  }\r\n}\r\n```\r\n\r\n## Architecture\r\n\r\n```mermaid\r\ngraph TD\r\n    AI[\"<b>AI Class</b><br/>Context Management<br/>Model Registry<br/>Lifecycle Hooks\"]\r\n    APIs[\"<b>APIs</b><br/>Chat · Image<br/>Speech · Embed\"]\r\n    Registry[\"<b>Registry</b><br/>Models · Search · Select\"]\r\n    Providers[\"<b>Providers</b><br/>OpenAI · OpenRouter<br/>Replicate · AWS · Custom\"]\r\n\r\n    AI --> APIs\r\n    AI --> Registry\r\n    Registry --> Providers\r\n    APIs --> Providers\r\n```\r\n\r\n## Core Concepts\r\n\r\n### Context\r\n\r\nContext is data passed through your entire AI operation:\r\n\r\n- **Default Context**: Static values provided at AI creation via `defaultContext`\r\n- **Provided Context**: Async-loaded values (e.g., from a database) via `providedContext`\r\n- **Required Context**: Values that must be supplied per-call\r\n\r\n```typescript\r\ninterface AppContext {\r\n  userId: string;\r\n  user?: User;       // loaded by providedContext\r\n  db?: Database;     // loaded by providedContext\r\n}\r\n\r\nconst ai = AI.with<AppContext>()\r\n  .providers({ openai })\r\n  .create({\r\n    providedContext: async (ctx) => ({\r\n      user: await db.users.findById(ctx.userId),\r\n      db,\r\n    }),\r\n  });\r\n\r\n// At call time only userId is required — the rest is loaded automatically\r\nconst response = await ai.chat.get(\r\n  { messages: [{ role: 'user', content: 'Hi!' }] },\r\n  { userId: 'user123' }\r\n);\r\n```\r\n\r\n### Metadata\r\n\r\nMetadata controls model selection and per-request configuration:\r\n\r\n```typescript\r\ninterface AppMetadata {\r\n  priority: 'cost' | 'speed' | 'quality';\r\n}\r\n\r\nconst ai = AI.with<{}, AppMetadata>()\r\n  .providers({ openai, openrouter })\r\n  .create({\r\n    defaultMetadata: { priority: 'cost' },\r\n  });\r\n\r\n// Override per request via context\r\nconst response = await ai.chat.get(\r\n  { messages: [{ role: 'user', content: 'Hello' }] },\r\n  {\r\n    metadata: {\r\n      model: 'openai/gpt-4o',        // pin a specific model\r\n      weights: { cost: 0.2, speed: 0.3, accuracy: 0.5 },\r\n      priority: 'quality',\r\n    },\r\n  }\r\n);\r\n```\r\n\r\n## API Reference\r\n\r\n### Chat API\r\n\r\n**`chat.get(request, ctx?)`** — Non-streaming chat completion.\r\n\r\n```typescript\r\nconst response = await ai.chat.get({\r\n  messages: [\r\n    { role: 'system', content: 'You are a helpful assistant.' },\r\n    { role: 'user', content: 'Hello!' },\r\n  ],\r\n  temperature: 0.7,\r\n  maxTokens: 1024,\r\n});\r\n\r\nconsole.log(response.content);\r\nconsole.log(response.usage?.text?.input, 'input tokens');\r\nconsole.log(response.usage?.text?.output, 'output tokens');\r\nconsole.log(response.usage?.cost, 'cost in dollars');\r\nconsole.log(response.finishReason); // 'stop' | 'length' | 'tool_calls' | ...\r\n```\r\n\r\n**`chat.stream(request, ctx?)`** — Streaming chat completion.\r\n\r\n```typescript\r\nfor await (const chunk of ai.chat.stream({\r\n  messages: [{ role: 'user', content: 'Count to 10' }]\r\n})) {\r\n  if (chunk.content) {\r\n    process.stdout.write(chunk.content);\r\n  }\r\n  if (chunk.finishReason) {\r\n    console.log('\\nFinished:', chunk.finishReason);\r\n  }\r\n}\r\n```\r\n\r\n**Vision:**\r\n\r\n```typescript\r\nconst response = await ai.chat.get({\r\n  messages: [{\r\n    role: 'user',\r\n    content: [\r\n      { type: 'text', content: 'What is in this image?' },\r\n      { type: 'image', content: 'https://example.com/image.jpg' },\r\n    ],\r\n  }],\r\n});\r\n// vision capability is detected automatically from the message content\r\n```\r\n\r\n### Image API\r\n\r\n**`image.generate.get(request, ctx?)`** — Generate images from a text prompt.\r\n\r\n```typescript\r\nconst response = await ai.image.generate.get({\r\n  prompt: 'A futuristic city at sunset',\r\n  n: 2,\r\n  size: '1024x1024',\r\n  quality: 'high',\r\n});\r\n\r\nfor (const image of response.images) {\r\n  console.log(image.url ?? image.b64_json);\r\n}\r\n```\r\n\r\n**`image.edit.get(request, ctx?)`** — Edit an image with inpainting.\r\n\r\n```typescript\r\nimport fs from 'fs';\r\n\r\nconst response = await ai.image.edit.get({\r\n  prompt: 'Add a sunset in the background',\r\n  image: fs.readFileSync('photo.png'),\r\n  mask: fs.readFileSync('mask.png'),\r\n  size: '1024x1024',\r\n});\r\n```\r\n\r\n**`image.analyze.get(request, ctx?)`** — Analyze image content (uses a vision chat model).\r\n\r\n```typescript\r\nconst response = await ai.image.analyze.get({\r\n  prompt: 'Describe this image in detail',\r\n  images: ['https://example.com/photo.jpg'],\r\n});\r\nconsole.log(response.content);\r\n```\r\n\r\n### Speech API\r\n\r\n**`speech.get(request, ctx?)`** — Text-to-speech synthesis.\r\n\r\n```typescript\r\nimport fs from 'fs';\r\nimport { Readable } from 'stream';\r\n\r\nconst response = await ai.speech.get({\r\n  text: 'Hello, this is a text-to-speech example.',\r\n  voice: 'alloy',\r\n  speed: 1.0,\r\n});\r\n\r\n// Pipe the audio stream to a file\r\nconst fileStream = fs.createWriteStream('output.mp3');\r\nReadable.fromWeb(response.audio).pipe(fileStream);\r\n```\r\n\r\n### Transcribe API\r\n\r\n**`transcribe.get(request, ctx?)`** — Speech-to-text transcription.\r\n\r\n```typescript\r\nimport fs from 'fs';\r\n\r\nconst response = await ai.transcribe.get({\r\n  audio: fs.readFileSync('recording.mp3'),\r\n  language: 'en',\r\n});\r\n\r\nconsole.log(response.text);\r\n```\r\n\r\n### Embed API\r\n\r\n**`embed.get(request, ctx?)`** — Generate text embeddings.\r\n\r\n```typescript\r\nconst response = await ai.embed.get({\r\n  texts: [\r\n    'TypeScript is a typed superset of JavaScript',\r\n    'Python is a high-level programming language',\r\n  ],\r\n});\r\n\r\nresponse.embeddings.forEach(({ embedding, index }) => {\r\n  console.log(`Text ${index}: ${embedding.length} dimensions`);\r\n});\r\n```\r\n\r\n### Models API\r\n\r\n```typescript\r\n// List all registered models\r\nconst models = ai.models.list();\r\n\r\n// Get a specific model\r\nconst model = ai.models.get('gpt-4o');\r\nif (model) {\r\n  console.log('Context window:', model.contextWindow);\r\n  console.log('Capabilities:', Array.from(model.capabilities).join(', '));\r\n  console.log('Input cost:', model.pricing.text?.input, 'per 1M tokens');\r\n}\r\n\r\n// Search with selection criteria\r\nconst results = ai.models.search({\r\n  capabilities: new Set(['chat', 'structured']),\r\n  weights: { cost: 0.6, speed: 0.4 },\r\n  providers: { allow: ['openai', 'openrouter'] },\r\n  contextWindow: { min: 100000 },\r\n});\r\nconsole.log('Best match:', results[0]?.model.id);\r\n\r\n// Refresh model list from all sources\r\nawait ai.models.refresh();\r\n```\r\n\r\n## Tools, Prompts & Agents\r\n\r\nUse `ai.tool()`, `ai.prompt()`, and `ai.agent()` to create components that are automatically wired to the AI instance (executor, streamer, and context are injected automatically).\r\n\r\n### Tool\r\n\r\n```typescript\r\nimport z from 'zod';\r\n\r\nconst searchKnowledge = ai.tool({\r\n  name: 'searchKnowledge',\r\n  description: 'Search the knowledge base for relevant information',\r\n  instructions: 'Use this to find information relevant to the query: \"{{query}}\"',\r\n  schema: z.object({\r\n    query: z.string().describe('Search query'),\r\n    limit: z.number().int().min(1).max(20).optional().describe('Max results (default: 5)'),\r\n  }),\r\n  call: async ({ query, limit = 5 }, _refs, ctx) => {\r\n    // ctx is the full AI context including your custom fields\r\n    return { results: await vectorSearch(query, limit) };\r\n  },\r\n});\r\n```\r\n\r\n### Prompt\r\n\r\n```typescript\r\nconst knowledgeAssistant = ai.prompt({\r\n  name: 'knowledgeAssistant',\r\n  description: 'Answers questions using the knowledge base',\r\n  content: `You are a helpful assistant. Use searchKnowledge to find information, then answer the question.\r\n\r\nQuestion: {{question}}`,\r\n  input: (input: { question: string }) => ({ question: input.question }),\r\n  tools: [searchKnowledge],\r\n  schema: z.object({\r\n    answer: z.string(),\r\n    sources: z.array(z.string()),\r\n  }),\r\n});\r\n\r\n// Get structured result\r\nconst result = await knowledgeAssistant.get('result', { question: 'How does X work?' });\r\nconsole.log(result?.answer);\r\n\r\n// Stream text\r\nfor await (const text of knowledgeAssistant.get('streamContent', { question: 'How does X work?' })) {\r\n  process.stdout.write(text);\r\n}\r\n```\r\n\r\n### Agent\r\n\r\n```typescript\r\nconst chatAgent = ai.agent({\r\n  name: 'chatAgent',\r\n  description: 'Chat with the user and analyze the response quality',\r\n  refs: [searchKnowledge, knowledgeAssistant] as const,\r\n  call: async (\r\n    input: { userId: string; question: string },\r\n    [search, assistant],\r\n    ctx\r\n  ) => {\r\n    // Step 1: Search for relevant context\r\n    const { results } = await search.run({ query: input.question }, ctx);\r\n\r\n    // Step 2: Generate answer with knowledge context\r\n    const answer = await assistant.get(\r\n      'result',\r\n      { question: input.question },\r\n      { ...ctx, messages: [] }\r\n    );\r\n\r\n    return {\r\n      userId: input.userId,\r\n      question: input.question,\r\n      answer: answer?.answer,\r\n      sources: answer?.sources ?? [],\r\n      contextUsed: results.length,\r\n    };\r\n  },\r\n});\r\n\r\nconst result = await chatAgent.run({ userId: 'user1', question: 'What is aeye?' });\r\nconsole.log(result.answer);\r\n```\r\n\r\n## Advanced Features\r\n\r\n### Custom Context & Metadata\r\n\r\n```typescript\r\ninterface AppContext {\r\n  userId: string;\r\n  user?: User;\r\n  db?: Database;\r\n}\r\n\r\ninterface AppMetadata {\r\n  feature: 'chat' | 'analysis';\r\n  priority: 'low' | 'normal' | 'high';\r\n}\r\n\r\nconst ai = AI.with<AppContext, AppMetadata>()\r\n  .providers({ openai, openrouter })\r\n  .create({\r\n    providedContext: async (ctx) => {\r\n      const user = await db.users.findById(ctx.userId);\r\n      return { user, db };\r\n    },\r\n    defaultMetadata: {\r\n      priority: 'normal',\r\n    },\r\n  });\r\n\r\n// At call time only userId is required\r\nconst response = await ai.chat.get(\r\n  { messages: [{ role: 'user', content: 'Hello' }] },\r\n  { userId: '123', metadata: { feature: 'chat' } }\r\n);\r\n```\r\n\r\n### Lifecycle Hooks\r\n\r\n```typescript\r\nconst ai = AI.with<AppContext>()\r\n  .providers({ openai })\r\n  .create({ /* ... */ })\r\n  .withHooks({\r\n    beforeModelSelection: async (ctx, request, metadata) => {\r\n      // Adjust weights based on context (return modified metadata)\r\n      if (ctx.user?.tier === 'free') {\r\n        return { ...metadata, weights: { cost: 1.0 } };\r\n      }\r\n      return metadata;\r\n    },\r\n\r\n    onModelSelected: async (ctx, request, selected) => {\r\n      console.log(`Selected: ${selected.model.id} (score: ${selected.score.toFixed(3)})`);\r\n      // Return a modified selected model to override, or undefined to accept\r\n    },\r\n\r\n    beforeRequest: async (ctx, request, selected, estimatedUsage, estimatedCost) => {\r\n      // Throw to cancel the request\r\n      if (ctx.user && estimatedCost > ctx.user.budgetRemaining) {\r\n        throw new Error('Insufficient budget');\r\n      }\r\n      console.log(\r\n        `[${ctx.userId}] ${selected.model.id} — ` +\r\n        `~${estimatedUsage.text?.input ?? 0} input tokens, ~$${estimatedCost.toFixed(5)}`\r\n      );\r\n    },\r\n\r\n    afterRequest: async (ctx, request, response, responseComplete, selected, usage, cost) => {\r\n      // Record actual usage\r\n      if (ctx.user) {\r\n        ctx.user.budgetRemaining -= cost;\r\n        ctx.user.totalSpent += cost;\r\n        await ctx.user.save();\r\n      }\r\n      console.log(\r\n        `[${ctx.userId}] ${usage.text?.input ?? 0} in / ${usage.text?.output ?? 0} out, ` +\r\n        `cost: $${cost.toFixed(5)}`\r\n      );\r\n    },\r\n\r\n    onError: (errorType, message, error, ctx) => {\r\n      console.error(`[AI ${errorType}] ${message}`, { error, userId: ctx?.userId });\r\n    },\r\n  });\r\n```\r\n\r\n### Model Selection\r\n\r\n```typescript\r\n// Cost-optimized\r\nawait ai.chat.get(\r\n  { messages },\r\n  { metadata: { weights: { cost: 0.9, speed: 0.05, accuracy: 0.05 } } }\r\n);\r\n\r\n// Quality-optimized with large context\r\nawait ai.chat.get(\r\n  { messages },\r\n  {\r\n    metadata: {\r\n      weights: { cost: 0.1, speed: 0.4, accuracy: 0.5 },\r\n      contextWindow: { min: 128000 },\r\n    }\r\n  }\r\n);\r\n\r\n// Provider-specific\r\nawait ai.chat.get(\r\n  { messages },\r\n  { metadata: { providers: { allow: ['openai'], deny: ['replicate'] } } }\r\n);\r\n\r\n// Explicit model override\r\nawait ai.chat.get(\r\n  { messages },\r\n  { metadata: { model: 'openai/gpt-4o' } }\r\n);\r\n```\r\n\r\n### Named Weight Profiles\r\n\r\n```typescript\r\nconst ai = AI.with()\r\n  .providers({ openai, openrouter })\r\n  .create({\r\n    weightProfiles: {\r\n      costPriority:  { cost: 0.9, speed: 0.1 },\r\n      balanced:      { cost: 0.4, speed: 0.3, accuracy: 0.3 },\r\n      performance:   { cost: 0.1, speed: 0.4, accuracy: 0.5 },\r\n    },\r\n  });\r\n\r\nawait ai.chat.get(\r\n  { messages },\r\n  { metadata: { weightProfile: 'balanced' } }\r\n);\r\n```\r\n\r\n### Model Sources\r\n\r\n```typescript\r\nimport { OpenRouterModelSource } from '@aeye/openrouter';\r\n\r\nconst source = new OpenRouterModelSource({ apiKey: process.env.OPENROUTER_API_KEY });\r\n\r\nconst ai = AI.with()\r\n  .providers({ openai, openrouter })\r\n  .create({\r\n    modelSources: [source],\r\n  });\r\n\r\n// Fetch and register all models from sources\r\nawait ai.models.refresh();\r\n```\r\n\r\n### Model Overrides\r\n\r\n```typescript\r\nconst ai = AI.with()\r\n  .providers({ openai })\r\n  .create({\r\n    modelOverrides: [\r\n      {\r\n        modelPattern: /gpt-4/,\r\n        overrides: {\r\n          pricing: { text: { input: 30, output: 60 } },\r\n        },\r\n      },\r\n    ],\r\n  });\r\n```\r\n\r\n### Strict Mode\r\n\r\nTool and Prompt schemas can ride the LLM's grammar-constrained strict mode for guaranteed type-safe output. Mechanics — descriptors, dialects, schema rewriting — live in [`@aeye/core`](../core/README.md#strict-mode). What `@aeye/ai` adds: model selection respects the `strict` flag (selection filters to strict-capable models when `strict: true`, scores them higher when `strict: <number>`).\r\n\r\nSplat the curated `strictSupport` overrides from `@aeye/models` so model selection knows which models actually support strict — without it, every model defaults to lenient even when the underlying API supports strict:\r\n\r\n```typescript\r\nimport { models, strictSupport } from '@aeye/models';\r\n\r\nconst ai = AI.with()\r\n  .providers({ openai })\r\n  .create({\r\n    models,\r\n    modelOverrides: [...strictSupport],\r\n  });\r\n```\r\n\r\n`strictSupport` is a `ModelOverride[]` you can combine with your own overrides:\r\n\r\n```typescript\r\nmodelOverrides: [\r\n  ...strictSupport,\r\n  { modelPattern: /gpt-4/, overrides: { tier: 'flagship' } },\r\n],\r\n```\r\n\r\n### Custom Providers\r\n\r\nExtend an existing provider or implement the `Provider<TConfig>` interface from `@aeye/core`:\r\n\r\n```typescript\r\nimport { OpenAIProvider, OpenAIConfig } from '@aeye/openai';\r\nimport OpenAI from 'openai';\r\n\r\nclass MyProvider extends OpenAIProvider {\r\n  readonly name = 'my-provider';\r\n\r\n  protected createClient(config: OpenAIConfig) {\r\n    return new OpenAI({\r\n      apiKey: config.apiKey,\r\n      baseURL: 'https://my-api.example.com/v1',\r\n    });\r\n  }\r\n}\r\n```\r\n\r\n### Extending AI Instances\r\n\r\nShare providers and configuration between multiple AI instances:\r\n\r\n```typescript\r\nconst baseAI = AI.with<BaseContext>()\r\n  .providers({ openai, openrouter })\r\n  .create({ /* base config */ });\r\n\r\n// Extend with additional context for a specific feature\r\ninterface ChatFeatureContext extends BaseContext {\r\n  chatId: string;\r\n  chatHistory: Message[];\r\n}\r\n\r\nconst chatAI = baseAI.extend<ChatFeatureContext>({\r\n  defaultContext: { chatHistory: [] },\r\n});\r\n\r\nconst response = await chatAI.chat.get(\r\n  { messages: [{ role: 'user', content: 'Hello' }] },\r\n  { chatId: 'chat-123', chatHistory: [] }\r\n);\r\n```\r\n\r\n## Examples\r\n\r\n### Complete Application\r\n\r\n```typescript\r\nimport { AI } from '@aeye/ai';\r\nimport { OpenAIProvider } from '@aeye/openai';\r\nimport { OpenRouterProvider } from '@aeye/openrouter';\r\nimport z from 'zod';\r\n\r\ninterface User {\r\n  id: string;\r\n  budgetRemaining: number;\r\n  totalSpent: number;\r\n  save: () => Promise<void>;\r\n}\r\n\r\ninterface AppContext {\r\n  userId: string;\r\n  user?: User;\r\n}\r\n\r\nconst openai = new OpenAIProvider({ apiKey: process.env.OPENAI_API_KEY! });\r\nconst openrouter = new OpenRouterProvider({ apiKey: process.env.OPENROUTER_API_KEY! });\r\n\r\nexport const ai = AI.with<AppContext>()\r\n  .providers({ openai, openrouter })\r\n  .create({\r\n    providedContext: async (ctx) => ({\r\n      user: await getUser(ctx.userId),\r\n    }),\r\n    defaultWeights: { cost: 0.5, speed: 0.3, accuracy: 0.2 },\r\n    hooks: {\r\n      beforeRequest: async (ctx, _request, selected, estimatedUsage, estimatedCost) => {\r\n        if (ctx.user && estimatedCost > ctx.user.budgetRemaining) {\r\n          throw new Error(\r\n            `Estimated cost $${estimatedCost.toFixed(4)} exceeds budget ` +\r\n            `$${ctx.user.budgetRemaining.toFixed(4)}`\r\n          );\r\n        }\r\n      },\r\n      afterRequest: async (ctx, _request, _response, _complete, _selected, usage, cost) => {\r\n        if (ctx.user) {\r\n          ctx.user.budgetRemaining -= cost;\r\n          ctx.user.totalSpent += cost;\r\n          await ctx.user.save();\r\n        }\r\n      },\r\n      onError: (errorType, message, error, ctx) => {\r\n        console.error(`[AI ${errorType}] ${message}`, { userId: ctx?.userId, error });\r\n      },\r\n    },\r\n  });\r\n\r\n// Define a tool\r\nconst webSearch = ai.tool({\r\n  name: 'webSearch',\r\n  description: 'Search the web for recent information',\r\n  schema: z.object({ query: z.string() }),\r\n  call: async ({ query }) => {\r\n    // Integrate with a search API\r\n    return { results: [`Result for: ${query}`] };\r\n  },\r\n});\r\n\r\n// Define a research prompt\r\nconst researcher = ai.prompt({\r\n  name: 'researcher',\r\n  description: 'Researches a topic and provides a structured summary',\r\n  content: `You are a research assistant. Research the following topic and provide a clear summary.\r\n\r\nTopic: {{topic}}`,\r\n  input: (input: { topic: string }) => ({ topic: input.topic }),\r\n  tools: [webSearch],\r\n  schema: z.object({\r\n    summary: z.string(),\r\n    keyFindings: z.array(z.string()),\r\n    sources: z.array(z.string()),\r\n  }),\r\n});\r\n\r\n// Use the prompt\r\nasync function researchTopic(userId: string, topic: string) {\r\n  const result = await researcher.get('result', { topic }, { userId });\r\n  return result;\r\n}\r\n\r\nconst findings = await researchTopic('user123', 'TypeScript 5.0 features');\r\nconsole.log(findings?.summary);\r\nconsole.log(findings?.keyFindings);\r\n```\r\n\r\n## License\r\n\r\nGPL-3.0 © [ClickerMonkey](https://github.com/ClickerMonkey)\r\n\r\n## Links\r\n\r\n- [Root README](../../README.md)\r\n- [@aeye/core README](../core/README.md)\r\n- [GitHub Repository](https://github.com/ClickerMonkey/aeye)\r\n- [Issue Tracker](https://github.com/ClickerMonkey/aeye/issues)\r\n","readmeFilename":"README.md"}