mirror of
https://github.com/geoffsee/open-gsio.git
synced 2025-09-08 22:56:46 +00:00
Compare commits
6 Commits
map-buddy
...
vectors-in
Author | SHA1 | Date | |
---|---|---|---|
![]() |
9810f67af0 | ||
![]() |
6c433581d3 | ||
![]() |
ae6a6e4064 | ||
![]() |
67483d08db | ||
![]() |
53268b528d | ||
![]() |
f9d5fc8282 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -24,4 +24,5 @@ wrangler.dev.jsonc
|
||||
/packages/client/public/pwa-64x64.png
|
||||
/packages/client/public/pwa-192x192.png
|
||||
/packages/client/public/pwa-512x512.png
|
||||
packages/client/public/yachtpit_bg*
|
||||
packages/client/public/yachtpit_bg*
|
||||
/project/
|
||||
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -1,3 +0,0 @@
|
||||
[submodule "crates/yachtpit"]
|
||||
path = crates/yachtpit
|
||||
url = https://github.com/seemueller-io/yachtpit.git
|
157
bun.lock
157
bun.lock
@@ -26,6 +26,7 @@
|
||||
"@anthropic-ai/sdk": "^0.55.0",
|
||||
"@open-gsio/env": "workspace:*",
|
||||
"@open-gsio/schema": "workspace:*",
|
||||
"@zilliz/milvus2-sdk-node": "^2.6.0",
|
||||
"openai": "^5.0.1",
|
||||
"vite": "^6.3.5",
|
||||
"vitest": "^3.1.4",
|
||||
@@ -439,6 +440,8 @@
|
||||
|
||||
"@cloudflare/workers-types": ["@cloudflare/workers-types@4.20250404.0", "", {}, "sha512-XB8LNS9spWcxU56Tt/RPl0S06oyXfZCSDVKBbzjHYZtgN5kZP4UtTR01t16HUfo330EtJOo8cdo/PbiBAwRNIg=="],
|
||||
|
||||
"@colors/colors": ["@colors/colors@1.6.0", "", {}, "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA=="],
|
||||
|
||||
"@cspotcode/source-map-support": ["@cspotcode/source-map-support@0.8.1", "", { "dependencies": { "@jridgewell/trace-mapping": "0.3.9" } }, "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw=="],
|
||||
|
||||
"@csstools/color-helpers": ["@csstools/color-helpers@5.0.2", "", {}, "sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA=="],
|
||||
@@ -451,6 +454,8 @@
|
||||
|
||||
"@csstools/css-tokenizer": ["@csstools/css-tokenizer@3.0.3", "", {}, "sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw=="],
|
||||
|
||||
"@dabh/diagnostics": ["@dabh/diagnostics@2.0.3", "", { "dependencies": { "colorspace": "1.1.x", "enabled": "2.0.x", "kuler": "^2.0.0" } }, "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA=="],
|
||||
|
||||
"@emnapi/runtime": ["@emnapi/runtime@1.4.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-64WYIf4UYcdLnbKn/umDlNjQDSS8AgZrI/R9+x5ilkUVFxXcA1Ebl+gQLc/6mERA4407Xof0R7wEyEuj091CVw=="],
|
||||
|
||||
"@emotion/babel-plugin": ["@emotion/babel-plugin@11.13.5", "", { "dependencies": { "@babel/helper-module-imports": "^7.16.7", "@babel/runtime": "^7.18.3", "@emotion/hash": "^0.9.2", "@emotion/memoize": "^0.9.0", "@emotion/serialize": "^1.3.3", "babel-plugin-macros": "^3.1.0", "convert-source-map": "^1.5.0", "escape-string-regexp": "^4.0.0", "find-root": "^1.1.0", "source-map": "^0.5.7", "stylis": "4.2.0" } }, "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ=="],
|
||||
@@ -539,6 +544,10 @@
|
||||
|
||||
"@fastify/busboy": ["@fastify/busboy@2.1.1", "", {}, "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA=="],
|
||||
|
||||
"@grpc/grpc-js": ["@grpc/grpc-js@1.7.3", "", { "dependencies": { "@grpc/proto-loader": "^0.7.0", "@types/node": ">=12.12.47" } }, "sha512-H9l79u4kJ2PVSxUNA08HMYAnUBLj9v6KjYQ7SQ71hOZcEXhShE/y5iQCesP8+6/Ik/7i2O0a10bPquIcYfufog=="],
|
||||
|
||||
"@grpc/proto-loader": ["@grpc/proto-loader@0.7.15", "", { "dependencies": { "lodash.camelcase": "^4.3.0", "long": "^5.0.0", "protobufjs": "^7.2.5", "yargs": "^17.7.2" }, "bin": { "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" } }, "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ=="],
|
||||
|
||||
"@humanwhocodes/config-array": ["@humanwhocodes/config-array@0.13.0", "", { "dependencies": { "@humanwhocodes/object-schema": "^2.0.3", "debug": "^4.3.1", "minimatch": "^3.0.5" } }, "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw=="],
|
||||
|
||||
"@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="],
|
||||
@@ -645,6 +654,8 @@
|
||||
|
||||
"@open-gsio/worker": ["@open-gsio/worker@workspace:packages/cloudflare-workers/open-gsio"],
|
||||
|
||||
"@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="],
|
||||
|
||||
"@oven/bun-darwin-aarch64": ["@oven/bun-darwin-aarch64@1.2.17", "", { "os": "darwin", "cpu": "arm64" }, "sha512-66Xjz3NZXUUWKZJPvWKuwEkaqMZpir1Gm4SbhbB2iiRSSTW8jqwdkSb9RhgTCDt5OnSPd3+Cq0WsP/T5ExJbhA=="],
|
||||
|
||||
"@oven/bun-darwin-x64": ["@oven/bun-darwin-x64@1.2.17", "", { "os": "darwin", "cpu": "x64" }, "sha512-OMJMHpcpBlWcVnWfSQ6x+8fF7HpkQLqBfoIvzxgUjIZZvj2d8K46XX4N/h62RglDEinRC9VDGxt24vwvlk5tTw=="],
|
||||
@@ -667,6 +678,8 @@
|
||||
|
||||
"@oven/bun-windows-x64-baseline": ["@oven/bun-windows-x64-baseline@1.2.17", "", { "os": "win32", "cpu": "x64" }, "sha512-aVkq4l1yZ9VKfBOtZ2HEj0OCU5kUe3Fx6LbAG6oY6OglWVYj051i3RGaE2OdR4L4F2jDyxzfGYRTM/qs8nU5qA=="],
|
||||
|
||||
"@petamoriken/float16": ["@petamoriken/float16@3.9.2", "", {}, "sha512-VgffxawQde93xKxT3qap3OH+meZf7VaSB5Sqd4Rqc+FP5alWbpOyan/7tRbOAvynjpG3GpdtAuGU/NdhQpmrog=="],
|
||||
|
||||
"@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="],
|
||||
|
||||
"@pkgr/core": ["@pkgr/core@0.2.7", "", {}, "sha512-YLT9Zo3oNPJoBjBc4q8G2mjU4tqIbf5CEOORbUUr48dCD9q3umJ3IPlVqOqDakPfd2HuwccBaqlGhN4Gmr5OWg=="],
|
||||
@@ -675,6 +688,26 @@
|
||||
|
||||
"@popperjs/core": ["@popperjs/core@2.11.8", "", {}, "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A=="],
|
||||
|
||||
"@protobufjs/aspromise": ["@protobufjs/aspromise@1.1.2", "", {}, "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="],
|
||||
|
||||
"@protobufjs/base64": ["@protobufjs/base64@1.1.2", "", {}, "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="],
|
||||
|
||||
"@protobufjs/codegen": ["@protobufjs/codegen@2.0.4", "", {}, "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="],
|
||||
|
||||
"@protobufjs/eventemitter": ["@protobufjs/eventemitter@1.1.0", "", {}, "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q=="],
|
||||
|
||||
"@protobufjs/fetch": ["@protobufjs/fetch@1.1.0", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.1", "@protobufjs/inquire": "^1.1.0" } }, "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ=="],
|
||||
|
||||
"@protobufjs/float": ["@protobufjs/float@1.0.2", "", {}, "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="],
|
||||
|
||||
"@protobufjs/inquire": ["@protobufjs/inquire@1.1.0", "", {}, "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q=="],
|
||||
|
||||
"@protobufjs/path": ["@protobufjs/path@1.1.2", "", {}, "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="],
|
||||
|
||||
"@protobufjs/pool": ["@protobufjs/pool@1.1.0", "", {}, "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="],
|
||||
|
||||
"@protobufjs/utf8": ["@protobufjs/utf8@1.1.0", "", {}, "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="],
|
||||
|
||||
"@quansync/fs": ["@quansync/fs@0.1.3", "", { "dependencies": { "quansync": "^0.2.10" } }, "sha512-G0OnZbMWEs5LhDyqy2UL17vGhSVHkQIfVojMtEWVenvj0V5S84VBgy86kJIuNsGDp2p7sTKlpSIpBUWdC35OKg=="],
|
||||
|
||||
"@rollup/plugin-babel": ["@rollup/plugin-babel@5.3.1", "", { "dependencies": { "@babel/helper-module-imports": "^7.10.4", "@rollup/pluginutils": "^3.1.0" }, "peerDependencies": { "@babel/core": "^7.0.0", "@types/babel__core": "^7.1.9", "rollup": "^1.20.0||^2.0.0" }, "optionalPeers": ["@types/babel__core"] }, "sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q=="],
|
||||
@@ -799,6 +832,8 @@
|
||||
|
||||
"@types/supercluster": ["@types/supercluster@7.1.3", "", { "dependencies": { "@types/geojson": "*" } }, "sha512-Z0pOY34GDFl3Q6hUFYf3HkTwKEE02e7QgtJppBt+beEAxnyOpJua+voGFvxINBHa06GwLFFym7gRPY2SiKIfIA=="],
|
||||
|
||||
"@types/triple-beam": ["@types/triple-beam@1.3.5", "", {}, "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw=="],
|
||||
|
||||
"@types/trusted-types": ["@types/trusted-types@2.0.7", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="],
|
||||
|
||||
"@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
|
||||
@@ -859,6 +894,8 @@
|
||||
|
||||
"@zag-js/focus-visible": ["@zag-js/focus-visible@0.31.1", "", { "dependencies": { "@zag-js/dom-query": "0.31.1" } }, "sha512-dbLksz7FEwyFoANbpIlNnd3bVm0clQSUsnP8yUVQucStZPsuWjCrhL2jlAbGNrTrahX96ntUMXHb/sM68TibFg=="],
|
||||
|
||||
"@zilliz/milvus2-sdk-node": ["@zilliz/milvus2-sdk-node@2.6.0", "", { "dependencies": { "@grpc/grpc-js": "1.7.3", "@grpc/proto-loader": "^0.7.10", "@opentelemetry/api": "^1.9.0", "@petamoriken/float16": "^3.8.6", "dayjs": "^1.11.7", "generic-pool": "^3.9.0", "lru-cache": "^9.1.2", "protobufjs": "^7.2.6", "winston": "^3.9.0" } }, "sha512-0Pblc9WZXWWkEazDFTAHKEi93Bw20ErD1R62/59ZWo2TQcIQM4TP36Tu0x2Y/y+d1j6ot6CpgqfoC5Rub9DSxA=="],
|
||||
|
||||
"acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
|
||||
|
||||
"acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
|
||||
@@ -967,6 +1004,8 @@
|
||||
|
||||
"chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
|
||||
|
||||
"cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="],
|
||||
|
||||
"color": ["color@4.2.3", "", { "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" } }, "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A=="],
|
||||
|
||||
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
|
||||
@@ -979,6 +1018,8 @@
|
||||
|
||||
"colorette": ["colorette@2.0.20", "", {}, "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="],
|
||||
|
||||
"colorspace": ["colorspace@1.1.4", "", { "dependencies": { "color": "^3.1.3", "text-hex": "1.0.x" } }, "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w=="],
|
||||
|
||||
"combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="],
|
||||
|
||||
"comma-separated-tokens": ["comma-separated-tokens@2.0.3", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="],
|
||||
@@ -1023,6 +1064,8 @@
|
||||
|
||||
"data-view-byte-offset": ["data-view-byte-offset@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-data-view": "^1.0.1" } }, "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ=="],
|
||||
|
||||
"dayjs": ["dayjs@1.11.13", "", {}, "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg=="],
|
||||
|
||||
"debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
|
||||
|
||||
"decimal.js": ["decimal.js@10.5.0", "", {}, "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw=="],
|
||||
@@ -1073,10 +1116,12 @@
|
||||
|
||||
"electron-to-chromium": ["electron-to-chromium@1.5.132", "", {}, "sha512-QgX9EBvWGmvSRa74zqfnG7+Eno0Ak0vftBll0Pt2/z5b3bEGYL6OUXLgKPtvx73dn3dvwrlyVkjPKRRlhLYTEg=="],
|
||||
|
||||
"emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
|
||||
"emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||
|
||||
"emoji-regex-xs": ["emoji-regex-xs@1.0.0", "", {}, "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg=="],
|
||||
|
||||
"enabled": ["enabled@2.0.0", "", {}, "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ=="],
|
||||
|
||||
"entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
|
||||
|
||||
"error-ex": ["error-ex@1.3.2", "", { "dependencies": { "is-arrayish": "^0.2.1" } }, "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g=="],
|
||||
@@ -1157,6 +1202,8 @@
|
||||
|
||||
"fdir": ["fdir@6.4.5", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-4BG7puHpVsIYxZUbiUE3RqGloLaSSwzYie5jvasC4LWuBWzZawynvYouhjbQKw2JuIGYdm0DzIxl8iVidKlUEw=="],
|
||||
|
||||
"fecha": ["fecha@4.2.3", "", {}, "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw=="],
|
||||
|
||||
"fflate": ["fflate@0.8.2", "", {}, "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A=="],
|
||||
|
||||
"file-entry-cache": ["file-entry-cache@6.0.1", "", { "dependencies": { "flat-cache": "^3.0.4" } }, "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg=="],
|
||||
@@ -1173,6 +1220,8 @@
|
||||
|
||||
"flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="],
|
||||
|
||||
"fn.name": ["fn.name@1.1.0", "", {}, "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw=="],
|
||||
|
||||
"focus-lock": ["focus-lock@1.3.6", "", { "dependencies": { "tslib": "^2.0.3" } }, "sha512-Ik/6OCk9RQQ0T5Xw+hKNLWrjSMtv51dD4GRmJjbD5a58TIEpI5a5iXagKVl3Z5UuyslMCA8Xwnu76jQob62Yhg=="],
|
||||
|
||||
"for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="],
|
||||
@@ -1197,10 +1246,14 @@
|
||||
|
||||
"functions-have-names": ["functions-have-names@1.2.3", "", {}, "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ=="],
|
||||
|
||||
"generic-pool": ["generic-pool@3.9.0", "", {}, "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g=="],
|
||||
|
||||
"gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="],
|
||||
|
||||
"geojson-vt": ["geojson-vt@4.0.2", "", {}, "sha512-AV9ROqlNqoZEIJGfm1ncNjEXfkz2hdFlZf0qkVfmkwdKa8vj7H16YUOT81rJw1rdFhyEDlN2Tds91p/glzbl5A=="],
|
||||
|
||||
"get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="],
|
||||
|
||||
"get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
|
||||
|
||||
"get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="],
|
||||
@@ -1423,6 +1476,8 @@
|
||||
|
||||
"keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
|
||||
|
||||
"kuler": ["kuler@2.0.0", "", {}, "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A=="],
|
||||
|
||||
"leven": ["leven@3.1.0", "", {}, "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A=="],
|
||||
|
||||
"levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
|
||||
@@ -1455,6 +1510,8 @@
|
||||
|
||||
"lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
|
||||
|
||||
"lodash.camelcase": ["lodash.camelcase@4.3.0", "", {}, "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="],
|
||||
|
||||
"lodash.debounce": ["lodash.debounce@4.0.8", "", {}, "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="],
|
||||
|
||||
"lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="],
|
||||
@@ -1463,11 +1520,15 @@
|
||||
|
||||
"lodash.sortby": ["lodash.sortby@4.7.0", "", {}, "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA=="],
|
||||
|
||||
"logform": ["logform@2.7.0", "", { "dependencies": { "@colors/colors": "1.6.0", "@types/triple-beam": "^1.3.2", "fecha": "^4.2.0", "ms": "^2.1.1", "safe-stable-stringify": "^2.3.1", "triple-beam": "^1.3.0" } }, "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ=="],
|
||||
|
||||
"long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="],
|
||||
|
||||
"loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="],
|
||||
|
||||
"loupe": ["loupe@3.1.3", "", {}, "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug=="],
|
||||
|
||||
"lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
|
||||
"lru-cache": ["lru-cache@9.1.2", "", {}, "sha512-ERJq3FOzJTxBbFjZ7iDs+NiK4VI9Wz+RdrrAB8dio1oV+YvdPzUEE4QNiT2VD51DkIbCYRUUzCRkssXCHqSnKQ=="],
|
||||
|
||||
"lucide-react": ["lucide-react@0.436.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc" } }, "sha512-N292bIxoqm1aObAg0MzFtvhYwgQE6qnIOWx/GLj5ONgcTPH6N0fD9bVq/GfdeC9ZORBXozt/XeEKDpiB3x3vlQ=="],
|
||||
|
||||
@@ -1573,6 +1634,8 @@
|
||||
|
||||
"once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="],
|
||||
|
||||
"one-time": ["one-time@1.0.0", "", { "dependencies": { "fn.name": "1.x.x" } }, "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g=="],
|
||||
|
||||
"oniguruma-to-es": ["oniguruma-to-es@2.3.0", "", { "dependencies": { "emoji-regex-xs": "^1.0.0", "regex": "^5.1.1", "regex-recursion": "^5.1.1" } }, "sha512-bwALDxriqfKGfUufKGGepCzu9x7nJQuoRoAFp4AnwehhC2crqrDIAP/uN2qdlsAvSMpeRC3+Yzhqc7hLmle5+g=="],
|
||||
|
||||
"openai": ["openai@5.0.1", "", { "peerDependencies": { "ws": "^8.18.0", "zod": "^3.23.8" }, "optionalPeers": ["ws", "zod"], "bin": { "openai": "bin/cli" } }, "sha512-Do6vxhbDv7cXhji/4ct1lrpZYMAOmjYbhyA9LJTuG7OfpbWMpuS+EIXkRT7R+XxpRB1OZhU/op4FU3p3uxU6gw=="],
|
||||
@@ -1639,6 +1702,8 @@
|
||||
|
||||
"property-information": ["property-information@7.0.0", "", {}, "sha512-7D/qOz/+Y4X/rzSB6jKxKUsQnphO046ei8qxG59mtM3RG3DHgTK81HrxrmoDVINJb8NKT5ZsRbwHvQ6B68Iyhg=="],
|
||||
|
||||
"protobufjs": ["protobufjs@7.5.3", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-sildjKwVqOI2kmFDiXQ6aEB0fjYTafpEvIBs8tOR8qI4spuL9OPROLVu2qZqi/xgCfsHIwVqlaF8JBjWFHnKbw=="],
|
||||
|
||||
"protocol-buffers-schema": ["protocol-buffers-schema@3.6.0", "", {}, "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw=="],
|
||||
|
||||
"psl": ["psl@1.15.0", "", { "dependencies": { "punycode": "^2.3.1" } }, "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w=="],
|
||||
@@ -1685,6 +1750,8 @@
|
||||
|
||||
"react-textarea-autosize": ["react-textarea-autosize@8.5.9", "", { "dependencies": { "@babel/runtime": "^7.20.13", "use-composed-ref": "^1.3.0", "use-latest": "^1.2.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-U1DGlIQN5AwgjTyOEnI1oCcMuEr1pv1qOtklB2l4nyMGbHzWrI0eFsYK0zos2YWqAolJyG0IWJaqWmWj5ETh0A=="],
|
||||
|
||||
"readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
|
||||
|
||||
"readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
|
||||
|
||||
"redent": ["redent@3.0.0", "", { "dependencies": { "indent-string": "^4.0.0", "strip-indent": "^3.0.0" } }, "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg=="],
|
||||
@@ -1711,6 +1778,8 @@
|
||||
|
||||
"regjsparser": ["regjsparser@0.12.0", "", { "dependencies": { "jsesc": "~3.0.2" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ=="],
|
||||
|
||||
"require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="],
|
||||
|
||||
"require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="],
|
||||
|
||||
"requires-port": ["requires-port@1.0.0", "", {}, "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="],
|
||||
@@ -1743,6 +1812,8 @@
|
||||
|
||||
"safe-regex-test": ["safe-regex-test@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-regex": "^1.2.1" } }, "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw=="],
|
||||
|
||||
"safe-stable-stringify": ["safe-stable-stringify@2.5.0", "", {}, "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA=="],
|
||||
|
||||
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
|
||||
|
||||
"saxes": ["saxes@6.0.0", "", { "dependencies": { "xmlchars": "^2.2.0" } }, "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA=="],
|
||||
@@ -1811,6 +1882,8 @@
|
||||
|
||||
"split-string": ["split-string@3.1.0", "", { "dependencies": { "extend-shallow": "^3.0.0" } }, "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw=="],
|
||||
|
||||
"stack-trace": ["stack-trace@0.0.10", "", {}, "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg=="],
|
||||
|
||||
"stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="],
|
||||
|
||||
"stacktracey": ["stacktracey@2.1.8", "", { "dependencies": { "as-table": "^1.0.36", "get-source": "^2.0.12" } }, "sha512-Kpij9riA+UNg7TnphqjH7/CzctQ/owJGNbFkfEeve4Z4uxT5+JapVLFXcsurIfN34gnTWZNJ/f7NMG0E8JDzTw=="],
|
||||
@@ -1821,7 +1894,7 @@
|
||||
|
||||
"stoppable": ["stoppable@1.1.0", "", {}, "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw=="],
|
||||
|
||||
"string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="],
|
||||
"string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||
|
||||
"string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||
|
||||
@@ -1833,6 +1906,8 @@
|
||||
|
||||
"string.prototype.trimstart": ["string.prototype.trimstart@1.0.8", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg=="],
|
||||
|
||||
"string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
|
||||
|
||||
"stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="],
|
||||
|
||||
"stringify-object": ["stringify-object@3.3.0", "", { "dependencies": { "get-own-enumerable-property-symbols": "^3.0.0", "is-obj": "^1.0.1", "is-regexp": "^1.0.0" } }, "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw=="],
|
||||
@@ -1869,6 +1944,8 @@
|
||||
|
||||
"test-exclude": ["test-exclude@7.0.1", "", { "dependencies": { "@istanbuljs/schema": "^0.1.2", "glob": "^10.4.1", "minimatch": "^9.0.4" } }, "sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg=="],
|
||||
|
||||
"text-hex": ["text-hex@1.0.0", "", {}, "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg=="],
|
||||
|
||||
"text-table": ["text-table@0.2.0", "", {}, "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw=="],
|
||||
|
||||
"tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="],
|
||||
@@ -1903,6 +1980,8 @@
|
||||
|
||||
"trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="],
|
||||
|
||||
"triple-beam": ["triple-beam@1.4.1", "", {}, "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg=="],
|
||||
|
||||
"ts-api-utils": ["ts-api-utils@2.1.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ=="],
|
||||
|
||||
"tsconfig-paths": ["tsconfig-paths@3.15.0", "", { "dependencies": { "@types/json5": "^0.0.29", "json5": "^1.0.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg=="],
|
||||
@@ -1985,6 +2064,8 @@
|
||||
|
||||
"use-sync-external-store": ["use-sync-external-store@1.5.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A=="],
|
||||
|
||||
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
|
||||
|
||||
"vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="],
|
||||
|
||||
"vfile-message": ["vfile-message@4.0.2", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw=="],
|
||||
@@ -2023,6 +2104,10 @@
|
||||
|
||||
"why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="],
|
||||
|
||||
"winston": ["winston@3.17.0", "", { "dependencies": { "@colors/colors": "^1.6.0", "@dabh/diagnostics": "^2.0.2", "async": "^3.2.3", "is-stream": "^2.0.0", "logform": "^2.7.0", "one-time": "^1.0.0", "readable-stream": "^3.4.0", "safe-stable-stringify": "^2.3.1", "stack-trace": "0.0.x", "triple-beam": "^1.3.0", "winston-transport": "^4.9.0" } }, "sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw=="],
|
||||
|
||||
"winston-transport": ["winston-transport@4.9.0", "", { "dependencies": { "logform": "^2.7.0", "readable-stream": "^3.6.2", "triple-beam": "^1.3.0" } }, "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A=="],
|
||||
|
||||
"word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="],
|
||||
|
||||
"workbox-background-sync": ["workbox-background-sync@7.3.0", "", { "dependencies": { "idb": "^7.0.1", "workbox-core": "7.3.0" } }, "sha512-PCSk3eK7Mxeuyatb22pcSx9dlgWNv3+M8PqPaYDokks8Y5/FX4soaOqj3yhAZr5k6Q5JWTOMYgaJBpbw11G9Eg=="],
|
||||
@@ -2061,7 +2146,7 @@
|
||||
|
||||
"wrangler": ["wrangler@4.18.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.0", "@cloudflare/unenv-preset": "2.3.2", "blake3-wasm": "2.1.5", "esbuild": "0.25.4", "miniflare": "4.20250525.0", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.17", "workerd": "1.20250525.0" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20250525.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-/ng0KI9io97SNsBU1rheADBLLTE5Djybgsi4gXuvH1RBKJGpyj1xWvZ2fuWu8vAonit3EiZkwtERTm6kESHP3A=="],
|
||||
|
||||
"wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="],
|
||||
"wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
|
||||
|
||||
"wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
|
||||
|
||||
@@ -2073,10 +2158,16 @@
|
||||
|
||||
"xmlchars": ["xmlchars@2.2.0", "", {}, "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="],
|
||||
|
||||
"y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="],
|
||||
|
||||
"yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
|
||||
|
||||
"yaml": ["yaml@1.10.2", "", {}, "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="],
|
||||
|
||||
"yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="],
|
||||
|
||||
"yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="],
|
||||
|
||||
"yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
|
||||
|
||||
"youch": ["youch@3.3.4", "", { "dependencies": { "cookie": "^0.7.1", "mustache": "^4.2.0", "stacktracey": "^2.1.8" } }, "sha512-UeVBXie8cA35DS6+nBkls68xaBBXCye0CNznrhszZjTbRVnJKQuNsyLKBTTL4ln1o1rh2PKtv35twV7irj5SEg=="],
|
||||
@@ -2095,6 +2186,8 @@
|
||||
|
||||
"@babel/helper-annotate-as-pure/@babel/types": ["@babel/types@7.27.3", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-Y1GkI4ktrtvmawoSq+4FCVHNryea6uR+qUQy0AGxLSsjCX0nVmkYQMBLHDkXZuo5hGx7eYdnIaslsdBFm7zbUw=="],
|
||||
|
||||
"@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
|
||||
|
||||
"@babel/helper-create-class-features-plugin/@babel/traverse": ["@babel/traverse@7.27.3", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.27.3", "@babel/parser": "^7.27.3", "@babel/template": "^7.27.2", "@babel/types": "^7.27.3", "debug": "^4.3.1", "globals": "^11.1.0" } }, "sha512-lId/IfN/Ye1CIu8xG7oKBHXd2iNb2aW1ilPszzGcJug6M8RCKfVNcYhpI5+bMvFYjK7lXIM0R+a+6r8xhHp2FQ=="],
|
||||
|
||||
"@babel/helper-define-polyfill-provider/@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.27.2", "", { "dependencies": { "@babel/compat-data": "^7.27.2", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ=="],
|
||||
@@ -2295,15 +2388,21 @@
|
||||
|
||||
"@eslint/eslintrc/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
|
||||
|
||||
"@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="],
|
||||
|
||||
"@isaacs/cliui/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="],
|
||||
|
||||
"@open-gsio/client/@types/bun": ["@types/bun@1.2.18", "", { "dependencies": { "bun-types": "1.2.18" } }, "sha512-Xf6RaWVheyemaThV0kUfaAUvCNokFr+bH8Jxp+tTZfx7dAPA8z9ePnP9S9+Vspzuxxx9JRAXhnyccRj3GyCMdQ=="],
|
||||
"@isaacs/cliui/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="],
|
||||
|
||||
"@open-gsio/client/@types/bun": ["@types/bun@1.2.19", "", { "dependencies": { "bun-types": "1.2.19" } }, "sha512-d9ZCmrH3CJ2uYKXQIUuZ/pUnTqIvLDS0SK7pFmbx8ma+ziH/FRMoAq5bYpRG7y+w1gl+HgyNZbtqgMq4W4e2Lg=="],
|
||||
|
||||
"@open-gsio/client/vite": ["vite@7.0.0", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.6", "picomatch": "^4.0.2", "postcss": "^8.5.6", "rollup": "^4.40.0", "tinyglobby": "^0.2.14" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-ixXJB1YRgDIw2OszKQS9WxGHKwLdCsbQNkpJN171udl6szi/rIySHL6/Os3s2+oE4P/FLD4dxg4mD7Wust+u5g=="],
|
||||
|
||||
"@open-gsio/coordinators/@types/bun": ["@types/bun@1.2.18", "", { "dependencies": { "bun-types": "1.2.18" } }, "sha512-Xf6RaWVheyemaThV0kUfaAUvCNokFr+bH8Jxp+tTZfx7dAPA8z9ePnP9S9+Vspzuxxx9JRAXhnyccRj3GyCMdQ=="],
|
||||
"@open-gsio/coordinators/@types/bun": ["@types/bun@1.2.19", "", { "dependencies": { "bun-types": "1.2.19" } }, "sha512-d9ZCmrH3CJ2uYKXQIUuZ/pUnTqIvLDS0SK7pFmbx8ma+ziH/FRMoAq5bYpRG7y+w1gl+HgyNZbtqgMq4W4e2Lg=="],
|
||||
|
||||
"@open-gsio/scripts/@types/bun": ["@types/bun@1.2.18", "", { "dependencies": { "bun-types": "1.2.18" } }, "sha512-Xf6RaWVheyemaThV0kUfaAUvCNokFr+bH8Jxp+tTZfx7dAPA8z9ePnP9S9+Vspzuxxx9JRAXhnyccRj3GyCMdQ=="],
|
||||
"@open-gsio/scripts/@types/bun": ["@types/bun@1.2.19", "", { "dependencies": { "bun-types": "1.2.19" } }, "sha512-d9ZCmrH3CJ2uYKXQIUuZ/pUnTqIvLDS0SK7pFmbx8ma+ziH/FRMoAq5bYpRG7y+w1gl+HgyNZbtqgMq4W4e2Lg=="],
|
||||
|
||||
"@open-gsio/server/@types/bun": ["@types/bun@1.2.19", "", { "dependencies": { "bun-types": "1.2.19" } }, "sha512-d9ZCmrH3CJ2uYKXQIUuZ/pUnTqIvLDS0SK7pFmbx8ma+ziH/FRMoAq5bYpRG7y+w1gl+HgyNZbtqgMq4W4e2Lg=="],
|
||||
|
||||
"@open-gsio/server/vite": ["vite@7.0.0", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.6", "picomatch": "^4.0.2", "postcss": "^8.5.6", "rollup": "^4.40.0", "tinyglobby": "^0.2.14" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-ixXJB1YRgDIw2OszKQS9WxGHKwLdCsbQNkpJN171udl6szi/rIySHL6/Os3s2+oE4P/FLD4dxg4mD7Wust+u5g=="],
|
||||
|
||||
@@ -2333,6 +2432,8 @@
|
||||
|
||||
"babel-plugin-polyfill-corejs2/@babel/compat-data": ["@babel/compat-data@7.27.3", "", {}, "sha512-V42wFfx1ymFte+ecf6iXghnnP8kWTO+ZLXIyZq+1LAXHHvTZdVxicn4yiVYdYMGaCO3tmqub11AorKkv+iodqw=="],
|
||||
|
||||
"colorspace/color": ["color@3.2.1", "", { "dependencies": { "color-convert": "^1.9.3", "color-string": "^1.6.0" } }, "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA=="],
|
||||
|
||||
"cssstyle/rrweb-cssom": ["rrweb-cssom@0.8.0", "", {}, "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw=="],
|
||||
|
||||
"data-urls/whatwg-mimetype": ["whatwg-mimetype@4.0.0", "", {}, "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg=="],
|
||||
@@ -2401,10 +2502,6 @@
|
||||
|
||||
"split-string/extend-shallow": ["extend-shallow@3.0.2", "", { "dependencies": { "assign-symbols": "^1.0.0", "is-extendable": "^1.0.1" } }, "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q=="],
|
||||
|
||||
"string-width/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="],
|
||||
|
||||
"string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||
|
||||
"tempy/type-fest": ["type-fest@0.16.0", "", {}, "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg=="],
|
||||
|
||||
"terser/acorn": ["acorn@8.14.1", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg=="],
|
||||
@@ -2431,12 +2528,6 @@
|
||||
|
||||
"workbox-build/source-map": ["source-map@0.8.0-beta.0", "", { "dependencies": { "whatwg-url": "^7.0.0" } }, "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA=="],
|
||||
|
||||
"wrap-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="],
|
||||
|
||||
"wrap-ansi/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="],
|
||||
|
||||
"wrap-ansi-cjs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||
|
||||
"@apideck/better-ajv-errors/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
|
||||
|
||||
"@babel/core/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.25.9", "", {}, "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ=="],
|
||||
@@ -2457,6 +2548,8 @@
|
||||
|
||||
"@babel/helper-define-polyfill-provider/@babel/helper-compilation-targets/@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="],
|
||||
|
||||
"@babel/helper-define-polyfill-provider/@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
|
||||
|
||||
"@babel/helper-member-expression-to-functions/@babel/traverse/@babel/generator": ["@babel/generator@7.27.3", "", { "dependencies": { "@babel/parser": "^7.27.3", "@babel/types": "^7.27.3", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" } }, "sha512-xnlJYj5zepml8NXtjkG0WquFUv8RskFqyFcVgTBp5k+NaA/8uw/K+OSVf8AMGw5e9HKP2ETd5xpK5MLZQD6b4Q=="],
|
||||
|
||||
"@babel/helper-member-expression-to-functions/@babel/traverse/@babel/parser": ["@babel/parser@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" }, "bin": "./bin/babel-parser.js" }, "sha512-xyYxRj6+tLNDTWi0KCBcZ9V7yg3/lwL9DWh9Uwh/RIVlIfFidggcgxKX3GCXwCiswwcGRawBKbEg2LG/Y8eJhw=="],
|
||||
@@ -2547,6 +2640,8 @@
|
||||
|
||||
"@babel/plugin-transform-classes/@babel/helper-compilation-targets/@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="],
|
||||
|
||||
"@babel/plugin-transform-classes/@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
|
||||
|
||||
"@babel/plugin-transform-classes/@babel/traverse/@babel/generator": ["@babel/generator@7.27.3", "", { "dependencies": { "@babel/parser": "^7.27.3", "@babel/types": "^7.27.3", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" } }, "sha512-xnlJYj5zepml8NXtjkG0WquFUv8RskFqyFcVgTBp5k+NaA/8uw/K+OSVf8AMGw5e9HKP2ETd5xpK5MLZQD6b4Q=="],
|
||||
|
||||
"@babel/plugin-transform-classes/@babel/traverse/@babel/parser": ["@babel/parser@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" }, "bin": "./bin/babel-parser.js" }, "sha512-xyYxRj6+tLNDTWi0KCBcZ9V7yg3/lwL9DWh9Uwh/RIVlIfFidggcgxKX3GCXwCiswwcGRawBKbEg2LG/Y8eJhw=="],
|
||||
@@ -2563,6 +2658,8 @@
|
||||
|
||||
"@babel/plugin-transform-function-name/@babel/helper-compilation-targets/@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="],
|
||||
|
||||
"@babel/plugin-transform-function-name/@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
|
||||
|
||||
"@babel/plugin-transform-function-name/@babel/traverse/@babel/generator": ["@babel/generator@7.27.3", "", { "dependencies": { "@babel/parser": "^7.27.3", "@babel/types": "^7.27.3", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" } }, "sha512-xnlJYj5zepml8NXtjkG0WquFUv8RskFqyFcVgTBp5k+NaA/8uw/K+OSVf8AMGw5e9HKP2ETd5xpK5MLZQD6b4Q=="],
|
||||
|
||||
"@babel/plugin-transform-function-name/@babel/traverse/@babel/parser": ["@babel/parser@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" }, "bin": "./bin/babel-parser.js" }, "sha512-xyYxRj6+tLNDTWi0KCBcZ9V7yg3/lwL9DWh9Uwh/RIVlIfFidggcgxKX3GCXwCiswwcGRawBKbEg2LG/Y8eJhw=="],
|
||||
@@ -2601,13 +2698,21 @@
|
||||
|
||||
"@babel/plugin-transform-object-rest-spread/@babel/helper-compilation-targets/@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="],
|
||||
|
||||
"@babel/plugin-transform-object-rest-spread/@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
|
||||
|
||||
"@babel/preset-env/@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
|
||||
|
||||
"@babel/template/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.25.9", "", {}, "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ=="],
|
||||
|
||||
"@babel/traverse/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.25.9", "", {}, "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ=="],
|
||||
|
||||
"@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
|
||||
|
||||
"@isaacs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="],
|
||||
|
||||
"@open-gsio/client/@types/bun/bun-types": ["bun-types@1.2.18", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-04+Eha5NP7Z0A9YgDAzMk5PHR16ZuLVa83b26kH5+cp1qZW4F6FmAURngE7INf4tKOvCE69vYvDEwoNl1tGiWw=="],
|
||||
"@isaacs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="],
|
||||
|
||||
"@open-gsio/client/@types/bun/bun-types": ["bun-types@1.2.19", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-uAOTaZSPuYsWIXRpj7o56Let0g/wjihKCkeRqUBhlLVM/Bt+Fj9xTo+LhC1OV1XDaGkz4hNC80et5xgy+9KTHQ=="],
|
||||
|
||||
"@open-gsio/client/vite/fdir": ["fdir@6.4.6", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w=="],
|
||||
|
||||
@@ -2615,9 +2720,11 @@
|
||||
|
||||
"@open-gsio/client/vite/rollup": ["rollup@4.44.0", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.44.0", "@rollup/rollup-android-arm64": "4.44.0", "@rollup/rollup-darwin-arm64": "4.44.0", "@rollup/rollup-darwin-x64": "4.44.0", "@rollup/rollup-freebsd-arm64": "4.44.0", "@rollup/rollup-freebsd-x64": "4.44.0", "@rollup/rollup-linux-arm-gnueabihf": "4.44.0", "@rollup/rollup-linux-arm-musleabihf": "4.44.0", "@rollup/rollup-linux-arm64-gnu": "4.44.0", "@rollup/rollup-linux-arm64-musl": "4.44.0", "@rollup/rollup-linux-loongarch64-gnu": "4.44.0", "@rollup/rollup-linux-powerpc64le-gnu": "4.44.0", "@rollup/rollup-linux-riscv64-gnu": "4.44.0", "@rollup/rollup-linux-riscv64-musl": "4.44.0", "@rollup/rollup-linux-s390x-gnu": "4.44.0", "@rollup/rollup-linux-x64-gnu": "4.44.0", "@rollup/rollup-linux-x64-musl": "4.44.0", "@rollup/rollup-win32-arm64-msvc": "4.44.0", "@rollup/rollup-win32-ia32-msvc": "4.44.0", "@rollup/rollup-win32-x64-msvc": "4.44.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-qHcdEzLCiktQIfwBq420pn2dP+30uzqYxv9ETm91wdt2R9AFcWfjNAmje4NWlnCIQ5RMTzVf0ZyisOKqHR6RwA=="],
|
||||
|
||||
"@open-gsio/coordinators/@types/bun/bun-types": ["bun-types@1.2.18", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-04+Eha5NP7Z0A9YgDAzMk5PHR16ZuLVa83b26kH5+cp1qZW4F6FmAURngE7INf4tKOvCE69vYvDEwoNl1tGiWw=="],
|
||||
"@open-gsio/coordinators/@types/bun/bun-types": ["bun-types@1.2.19", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-uAOTaZSPuYsWIXRpj7o56Let0g/wjihKCkeRqUBhlLVM/Bt+Fj9xTo+LhC1OV1XDaGkz4hNC80et5xgy+9KTHQ=="],
|
||||
|
||||
"@open-gsio/scripts/@types/bun/bun-types": ["bun-types@1.2.18", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-04+Eha5NP7Z0A9YgDAzMk5PHR16ZuLVa83b26kH5+cp1qZW4F6FmAURngE7INf4tKOvCE69vYvDEwoNl1tGiWw=="],
|
||||
"@open-gsio/scripts/@types/bun/bun-types": ["bun-types@1.2.19", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-uAOTaZSPuYsWIXRpj7o56Let0g/wjihKCkeRqUBhlLVM/Bt+Fj9xTo+LhC1OV1XDaGkz4hNC80et5xgy+9KTHQ=="],
|
||||
|
||||
"@open-gsio/server/@types/bun/bun-types": ["bun-types@1.2.19", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-uAOTaZSPuYsWIXRpj7o56Let0g/wjihKCkeRqUBhlLVM/Bt+Fj9xTo+LhC1OV1XDaGkz4hNC80et5xgy+9KTHQ=="],
|
||||
|
||||
"@open-gsio/server/vite/fdir": ["fdir@6.4.6", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w=="],
|
||||
|
||||
@@ -2643,6 +2750,8 @@
|
||||
|
||||
"@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
|
||||
|
||||
"colorspace/color/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="],
|
||||
|
||||
"filelist/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
|
||||
|
||||
"glob/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
|
||||
@@ -2659,8 +2768,6 @@
|
||||
|
||||
"split-string/extend-shallow/is-extendable": ["is-extendable@1.0.1", "", { "dependencies": { "is-plain-object": "^2.0.4" } }, "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA=="],
|
||||
|
||||
"string-width/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="],
|
||||
|
||||
"test-exclude/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
|
||||
|
||||
"vike/vite/fdir": ["fdir@6.4.6", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w=="],
|
||||
@@ -2677,10 +2784,6 @@
|
||||
|
||||
"workbox-build/source-map/whatwg-url": ["whatwg-url@7.1.0", "", { "dependencies": { "lodash.sortby": "^4.7.0", "tr46": "^1.0.1", "webidl-conversions": "^4.0.2" } }, "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg=="],
|
||||
|
||||
"wrap-ansi-cjs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||
|
||||
"wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="],
|
||||
|
||||
"@babel/helper-create-class-features-plugin/@babel/traverse/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="],
|
||||
|
||||
"@babel/helper-remap-async-to-generator/@babel/traverse/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="],
|
||||
@@ -2875,6 +2978,8 @@
|
||||
|
||||
"@open-gsio/services/vite/rollup/@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
|
||||
|
||||
"colorspace/color/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="],
|
||||
|
||||
"vike/vite/rollup/@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.44.0", "", { "os": "android", "cpu": "arm" }, "sha512-xEiEE5oDW6tK4jXCAyliuntGR+amEMO7HLtdSshVuhFnKTYoeYMyXQK7pLouAJJj5KHdwdn87bfHAR2nSdNAUA=="],
|
||||
|
||||
"vike/vite/rollup/@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.44.0", "", { "os": "android", "cpu": "arm64" }, "sha512-uNSk/TgvMbskcHxXYHzqwiyBlJ/lGcv8DaUfcnNwict8ba9GTTNxfn3/FAoFZYgkaXXAdrAA+SLyKplyi349Jw=="],
|
||||
|
Submodule crates/yachtpit deleted from 348f20641c
@@ -10,7 +10,6 @@
|
||||
],
|
||||
"scripts": {
|
||||
"clean": "packages/scripts/cleanup.sh",
|
||||
"restore:submodules": "rm -rf crates/yachtpit && (git rm --cached crates/yachtpit) && git submodule add --force https://github.com/seemueller-io/yachtpit.git crates/yachtpit ",
|
||||
"test:all": "bun run --filter='*' tests",
|
||||
"client:dev": "(cd packages/client && bun run dev)",
|
||||
"server:dev": "bun build:client && (cd packages/server && bun run dev)",
|
||||
|
@@ -40,6 +40,7 @@
|
||||
"@open-gsio/env": "workspace:*",
|
||||
"@open-gsio/schema": "workspace:*",
|
||||
"@anthropic-ai/sdk": "^0.55.0",
|
||||
"@zilliz/milvus2-sdk-node": "^2.6.0",
|
||||
"openai": "^5.0.1",
|
||||
"wrangler": "^4.18.0",
|
||||
"vitest": "^3.1.4",
|
||||
|
@@ -24,68 +24,10 @@ export class ProviderRepository {
|
||||
};
|
||||
|
||||
static async getModelFamily(model: any, env: GenericEnv) {
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log(`[DEBUG_LOG] ProviderRepository.getModelFamily: Looking up model "${model}"`);
|
||||
|
||||
const allModels = await env.KV_STORAGE.get('supportedModels');
|
||||
const models = JSON.parse(allModels);
|
||||
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log(`[DEBUG_LOG] ProviderRepository.getModelFamily: Found ${models.length} total models in KV storage`);
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log('[DEBUG_LOG] ProviderRepository.getModelFamily: Available model IDs:', models.map((m: ModelMeta) => m.id));
|
||||
|
||||
// First try exact match
|
||||
let modelData = models.filter((m: ModelMeta) => m.id === model);
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log(`[DEBUG_LOG] ProviderRepository.getModelFamily: Exact match attempt for "${model}" found ${modelData.length} results`);
|
||||
|
||||
// If no exact match, try to find by partial match (handle provider prefixes)
|
||||
if (modelData.length === 0) {
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log(`[DEBUG_LOG] ProviderRepository.getModelFamily: Trying partial match for "${model}"`);
|
||||
modelData = models.filter((m: ModelMeta) => {
|
||||
// Check if the model ID ends with the requested model name
|
||||
// This handles cases like "accounts/fireworks/models/mixtral-8x22b-instruct" matching "mixtral-8x22b-instruct"
|
||||
const endsWithMatch = m.id.endsWith(model);
|
||||
const modelEndsWithStoredBase = model.endsWith(m.id.split('/').pop() || '');
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log(`[DEBUG_LOG] ProviderRepository.getModelFamily: Checking "${m.id}" - endsWith: ${endsWithMatch}, modelEndsWithBase: ${modelEndsWithStoredBase}`);
|
||||
return endsWithMatch || modelEndsWithStoredBase;
|
||||
});
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log(`[DEBUG_LOG] ProviderRepository.getModelFamily: Partial match found ${modelData.length} results`);
|
||||
}
|
||||
|
||||
// If still no match, try to find by the base model name (last part after /)
|
||||
if (modelData.length === 0) {
|
||||
const baseModelName = model.split('/').pop();
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log(`[DEBUG_LOG] ProviderRepository.getModelFamily: Trying base name match for "${baseModelName}"`);
|
||||
modelData = models.filter((m: ModelMeta) => {
|
||||
const baseStoredName = m.id.split('/').pop();
|
||||
const matches = baseStoredName === baseModelName;
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log(`[DEBUG_LOG] ProviderRepository.getModelFamily: Comparing base names "${baseStoredName}" === "${baseModelName}": ${matches}`);
|
||||
return matches;
|
||||
});
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log(`[DEBUG_LOG] ProviderRepository.getModelFamily: Base name match found ${modelData.length} results`);
|
||||
}
|
||||
|
||||
const selectedProvider = modelData[0]?.provider;
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log(`[DEBUG_LOG] ProviderRepository.getModelFamily: Final result for "${model}" -> provider: "${selectedProvider}"`);
|
||||
|
||||
if (modelData.length > 0) {
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log('[DEBUG_LOG] ProviderRepository.getModelFamily: Selected model data:', modelData[0]);
|
||||
} else {
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log(`[DEBUG_LOG] ProviderRepository.getModelFamily: No matching model found for "${model}"`);
|
||||
}
|
||||
|
||||
return selectedProvider;
|
||||
const modelData = models.filter((m: ModelMeta) => m.id === model);
|
||||
return modelData[0].provider;
|
||||
}
|
||||
|
||||
static async getModelMeta(meta: any, env: GenericEnv) {
|
||||
@@ -99,19 +41,12 @@ export class ProviderRepository {
|
||||
}
|
||||
|
||||
setProviders(env: GenericEnv) {
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log('[DEBUG_LOG] ProviderRepository.setProviders: Starting provider detection');
|
||||
|
||||
const indicies = {
|
||||
providerName: 0,
|
||||
providerValue: 1,
|
||||
};
|
||||
const valueDelimiter = '_';
|
||||
const envKeys = Object.keys(env);
|
||||
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log('[DEBUG_LOG] ProviderRepository.setProviders: Environment keys ending with KEY:', envKeys.filter(key => key.endsWith('KEY')));
|
||||
|
||||
for (let i = 0; i < envKeys.length; i++) {
|
||||
if (envKeys.at(i)?.endsWith('KEY')) {
|
||||
const detectedProvider = envKeys
|
||||
@@ -120,15 +55,9 @@ export class ProviderRepository {
|
||||
.at(indicies.providerName)
|
||||
?.toLowerCase();
|
||||
const detectedProviderValue = env[envKeys.at(i) as string];
|
||||
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log(`[DEBUG_LOG] ProviderRepository.setProviders: Processing ${envKeys[i]} -> detected provider: "${detectedProvider}", has value: ${!!detectedProviderValue}`);
|
||||
|
||||
if (detectedProviderValue) {
|
||||
switch (detectedProvider) {
|
||||
case 'anthropic':
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log('[DEBUG_LOG] ProviderRepository.setProviders: Adding Claude provider (anthropic)');
|
||||
this.#providers.push({
|
||||
name: 'claude',
|
||||
key: env.ANTHROPIC_API_KEY,
|
||||
@@ -136,8 +65,6 @@ export class ProviderRepository {
|
||||
});
|
||||
break;
|
||||
case 'gemini':
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log('[DEBUG_LOG] ProviderRepository.setProviders: Adding Google provider (gemini)');
|
||||
this.#providers.push({
|
||||
name: 'google',
|
||||
key: env.GEMINI_API_KEY,
|
||||
@@ -145,8 +72,6 @@ export class ProviderRepository {
|
||||
});
|
||||
break;
|
||||
case 'cloudflare':
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log('[DEBUG_LOG] ProviderRepository.setProviders: Adding Cloudflare provider');
|
||||
this.#providers.push({
|
||||
name: 'cloudflare',
|
||||
key: env.CLOUDFLARE_API_KEY,
|
||||
@@ -157,8 +82,6 @@ export class ProviderRepository {
|
||||
});
|
||||
break;
|
||||
default:
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log(`[DEBUG_LOG] ProviderRepository.setProviders: Adding default provider "${detectedProvider}"`);
|
||||
this.#providers.push({
|
||||
name: detectedProvider as SupportedProvider,
|
||||
key: env[envKeys[i] as string],
|
||||
@@ -166,14 +89,8 @@ export class ProviderRepository {
|
||||
ProviderRepository.OPENAI_COMPAT_ENDPOINTS[detectedProvider as SupportedProvider],
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log(`[DEBUG_LOG] ProviderRepository.setProviders: Skipping ${envKeys[i]} - no value provided`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log(`[DEBUG_LOG] ProviderRepository.setProviders: Final configured providers (${this.#providers.length}):`, this.#providers.map(p => ({ name: p.name, endpoint: p.endpoint, hasKey: !!p.key })));
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { OpenAI } from 'openai';
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
|
||||
import {
|
||||
BaseChatProvider,
|
||||
@@ -29,7 +29,7 @@ class TestChatProvider extends BaseChatProvider {
|
||||
}
|
||||
|
||||
// Mock dependencies
|
||||
vi.mock('../../lib/chat-sdk', () => ({
|
||||
vi.mock('../../chat-sdk/chat-sdk.ts', () => ({
|
||||
default: {
|
||||
buildAssistantPrompt: vi.fn().mockReturnValue('Assistant prompt'),
|
||||
buildMessageChain: vi.fn().mockReturnValue([
|
||||
@@ -39,6 +39,26 @@ vi.mock('../../lib/chat-sdk', () => ({
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('../../tools/agentic-rag.ts', () => ({
|
||||
agenticRAG: vi.fn(),
|
||||
AgenticRAGTools: {
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'agentic_rag',
|
||||
description: 'Test agentic RAG tool',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
action: { type: 'string', enum: ['search_knowledge'] },
|
||||
query: { type: 'string' },
|
||||
collection_name: { type: 'string' },
|
||||
},
|
||||
required: ['action', 'collection_name'],
|
||||
},
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
describe('ChatStreamProvider', () => {
|
||||
it('should define the required interface', () => {
|
||||
// Verify the interface has the required method
|
||||
@@ -50,26 +70,616 @@ describe('ChatStreamProvider', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('BaseChatProvider', () => {
|
||||
it('should implement the ChatStreamProvider interface', () => {
|
||||
// Create a concrete implementation
|
||||
const provider = new TestChatProvider();
|
||||
describe('BaseChatProvider - Model Tool Calling', () => {
|
||||
let provider: TestChatProvider;
|
||||
let mockOpenAI: any;
|
||||
let dataCallback: any;
|
||||
let commonParams: CommonProviderParams;
|
||||
|
||||
// Verify it implements the interface
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
provider = new TestChatProvider();
|
||||
dataCallback = vi.fn();
|
||||
|
||||
mockOpenAI = {
|
||||
chat: {
|
||||
completions: {
|
||||
create: vi.fn(),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
commonParams = {
|
||||
openai: mockOpenAI,
|
||||
systemPrompt: 'Test system prompt',
|
||||
preprocessedContext: {},
|
||||
maxTokens: 1000,
|
||||
messages: [{ role: 'user', content: 'Test message' }],
|
||||
model: 'gpt-4',
|
||||
env: {} as any,
|
||||
};
|
||||
});
|
||||
|
||||
it('should implement the ChatStreamProvider interface', () => {
|
||||
expect(provider.handleStream).toBeInstanceOf(Function);
|
||||
expect(provider.getOpenAIClient).toBeInstanceOf(Function);
|
||||
expect(provider.getStreamParams).toBeInstanceOf(Function);
|
||||
expect(provider.processChunk).toBeInstanceOf(Function);
|
||||
});
|
||||
|
||||
it('should have abstract methods that need to be implemented', () => {
|
||||
// This test verifies that the abstract methods exist
|
||||
// We can't instantiate BaseChatProvider directly, so we use the concrete implementation
|
||||
const provider = new TestChatProvider();
|
||||
it('should handle regular text streaming without tool calls', async () => {
|
||||
// Mock stream chunks for regular text response
|
||||
const chunks = [
|
||||
{
|
||||
choices: [
|
||||
{
|
||||
delta: { content: 'Hello ' },
|
||||
finish_reason: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
choices: [
|
||||
{
|
||||
delta: { content: 'world!' },
|
||||
finish_reason: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
choices: [
|
||||
{
|
||||
delta: {},
|
||||
finish_reason: 'stop',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
// Verify the abstract methods are implemented
|
||||
expect(provider.getOpenAIClient).toBeDefined();
|
||||
expect(provider.getStreamParams).toBeDefined();
|
||||
expect(provider.processChunk).toBeDefined();
|
||||
mockOpenAI.chat.completions.create.mockResolvedValue({
|
||||
async *[Symbol.asyncIterator]() {
|
||||
for (const chunk of chunks) {
|
||||
yield chunk;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
await provider.handleStream(commonParams, dataCallback);
|
||||
|
||||
expect(mockOpenAI.chat.completions.create).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
tools: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
type: 'function',
|
||||
function: expect.objectContaining({
|
||||
name: 'agentic_rag',
|
||||
}),
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle tool calls in streaming response', async () => {
|
||||
const { agenticRAG } = await import('../../tools/agentic-rag.ts');
|
||||
vi.mocked(agenticRAG).mockResolvedValue({
|
||||
success: true,
|
||||
data: {
|
||||
results: ['Test result'],
|
||||
analysis: { needsRetrieval: false },
|
||||
},
|
||||
});
|
||||
|
||||
// Mock stream chunks for tool call response
|
||||
const chunks = [
|
||||
{
|
||||
choices: [
|
||||
{
|
||||
delta: {
|
||||
tool_calls: [
|
||||
{
|
||||
index: 0,
|
||||
id: 'call_123',
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'agentic_rag',
|
||||
arguments:
|
||||
'{"action": "search_knowledge", "query": "test query", "collection_name": "test_collection"}',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
finish_reason: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
choices: [
|
||||
{
|
||||
delta: {},
|
||||
finish_reason: 'tool_calls',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
// Second stream for response after tool execution
|
||||
const secondStreamChunks = [
|
||||
{
|
||||
choices: [
|
||||
{
|
||||
delta: { content: 'Based on the search results: Test result' },
|
||||
finish_reason: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
choices: [
|
||||
{
|
||||
delta: {},
|
||||
finish_reason: 'stop',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
let callCount = 0;
|
||||
mockOpenAI.chat.completions.create.mockImplementation(() => {
|
||||
callCount++;
|
||||
if (callCount === 1) {
|
||||
return Promise.resolve({
|
||||
async *[Symbol.asyncIterator]() {
|
||||
for (const chunk of chunks) {
|
||||
yield chunk;
|
||||
}
|
||||
},
|
||||
});
|
||||
} else {
|
||||
return Promise.resolve({
|
||||
async *[Symbol.asyncIterator]() {
|
||||
for (const chunk of secondStreamChunks) {
|
||||
yield chunk;
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
await provider.handleStream(commonParams, dataCallback);
|
||||
|
||||
// Verify tool was called
|
||||
expect(agenticRAG).toHaveBeenCalledWith({
|
||||
action: 'search_knowledge',
|
||||
query: 'test query',
|
||||
collection_name: 'test_collection',
|
||||
});
|
||||
|
||||
// Verify feedback messages were sent
|
||||
expect(dataCallback).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
type: 'chat',
|
||||
data: expect.objectContaining({
|
||||
choices: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
delta: expect.objectContaining({
|
||||
content: expect.stringContaining('🔧 Invoking'),
|
||||
}),
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
||||
expect(dataCallback).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
type: 'chat',
|
||||
data: expect.objectContaining({
|
||||
choices: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
delta: expect.objectContaining({
|
||||
content: expect.stringContaining('📞 Calling agentic_rag'),
|
||||
}),
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle tool call streaming with incremental arguments', async () => {
|
||||
const { agenticRAG } = await import('../../tools/agentic-rag.ts');
|
||||
vi.mocked(agenticRAG).mockResolvedValue({
|
||||
success: true,
|
||||
data: { results: ['Test result'] },
|
||||
});
|
||||
|
||||
// Mock stream chunks with incremental tool call arguments
|
||||
const chunks = [
|
||||
{
|
||||
choices: [
|
||||
{
|
||||
delta: {
|
||||
tool_calls: [
|
||||
{
|
||||
index: 0,
|
||||
id: 'call_',
|
||||
type: 'function',
|
||||
function: { name: 'agentic_rag', arguments: '{"action": "search_' },
|
||||
},
|
||||
],
|
||||
},
|
||||
finish_reason: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
choices: [
|
||||
{
|
||||
delta: {
|
||||
tool_calls: [
|
||||
{
|
||||
index: 0,
|
||||
id: '123',
|
||||
function: { arguments: 'knowledge", "query": "test", ' },
|
||||
},
|
||||
],
|
||||
},
|
||||
finish_reason: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
choices: [
|
||||
{
|
||||
delta: {
|
||||
tool_calls: [
|
||||
{
|
||||
index: 0,
|
||||
function: { arguments: '"collection_name": "test_collection"}' },
|
||||
},
|
||||
],
|
||||
},
|
||||
finish_reason: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
choices: [
|
||||
{
|
||||
delta: {},
|
||||
finish_reason: 'tool_calls',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const secondStreamChunks = [
|
||||
{
|
||||
choices: [
|
||||
{
|
||||
delta: { content: 'Response after tool call' },
|
||||
finish_reason: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
choices: [
|
||||
{
|
||||
delta: {},
|
||||
finish_reason: 'stop',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
let callCount = 0;
|
||||
mockOpenAI.chat.completions.create.mockImplementation(() => {
|
||||
callCount++;
|
||||
if (callCount === 1) {
|
||||
return Promise.resolve({
|
||||
async *[Symbol.asyncIterator]() {
|
||||
for (const chunk of chunks) {
|
||||
yield chunk;
|
||||
}
|
||||
},
|
||||
});
|
||||
} else {
|
||||
return Promise.resolve({
|
||||
async *[Symbol.asyncIterator]() {
|
||||
for (const chunk of secondStreamChunks) {
|
||||
yield chunk;
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
await provider.handleStream(commonParams, dataCallback);
|
||||
|
||||
// Verify the complete tool call was assembled and executed
|
||||
expect(agenticRAG).toHaveBeenCalledWith({
|
||||
action: 'search_knowledge',
|
||||
query: 'test',
|
||||
collection_name: 'test_collection',
|
||||
});
|
||||
});
|
||||
|
||||
it('should prevent infinite tool call loops', async () => {
|
||||
const { agenticRAG } = await import('../../tools/agentic-rag.ts');
|
||||
vi.mocked(agenticRAG).mockResolvedValue({
|
||||
success: true,
|
||||
data: {
|
||||
results: [],
|
||||
analysis: { needsRetrieval: true },
|
||||
retrieved_documents: [],
|
||||
},
|
||||
});
|
||||
|
||||
// Mock stream that always returns tool calls
|
||||
const toolCallChunks = [
|
||||
{
|
||||
choices: [
|
||||
{
|
||||
delta: {
|
||||
tool_calls: [
|
||||
{
|
||||
index: 0,
|
||||
id: 'call_123',
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'agentic_rag',
|
||||
arguments:
|
||||
'{"action": "search_knowledge", "query": "test", "collection_name": "test_collection"}',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
finish_reason: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
choices: [
|
||||
{
|
||||
delta: {},
|
||||
finish_reason: 'tool_calls',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
mockOpenAI.chat.completions.create.mockResolvedValue({
|
||||
async *[Symbol.asyncIterator]() {
|
||||
for (const chunk of toolCallChunks) {
|
||||
yield chunk;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
await provider.handleStream(commonParams, dataCallback);
|
||||
|
||||
// Should detect duplicate tool calls and force completion (up to 5 iterations based on maxToolCallIterations)
|
||||
// In this case, it should stop after 2 calls due to duplicate detection, but could go up to 5
|
||||
expect(mockOpenAI.chat.completions.create).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('should handle tool call errors gracefully', async () => {
|
||||
const { agenticRAG } = await import('../../tools/agentic-rag.ts');
|
||||
vi.mocked(agenticRAG).mockRejectedValue(new Error('Tool execution failed'));
|
||||
|
||||
const chunks = [
|
||||
{
|
||||
choices: [
|
||||
{
|
||||
delta: {
|
||||
tool_calls: [
|
||||
{
|
||||
index: 0,
|
||||
id: 'call_123',
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'agentic_rag',
|
||||
arguments:
|
||||
'{"action": "search_knowledge", "query": "test", "collection_name": "test_collection"}',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
finish_reason: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
choices: [
|
||||
{
|
||||
delta: {},
|
||||
finish_reason: 'tool_calls',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const secondStreamChunks = [
|
||||
{
|
||||
choices: [
|
||||
{
|
||||
delta: { content: 'I apologize, but I encountered an error.' },
|
||||
finish_reason: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
choices: [
|
||||
{
|
||||
delta: {},
|
||||
finish_reason: 'stop',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
let callCount = 0;
|
||||
mockOpenAI.chat.completions.create.mockImplementation(() => {
|
||||
callCount++;
|
||||
if (callCount === 1) {
|
||||
return Promise.resolve({
|
||||
async *[Symbol.asyncIterator]() {
|
||||
for (const chunk of chunks) {
|
||||
yield chunk;
|
||||
}
|
||||
},
|
||||
});
|
||||
} else {
|
||||
return Promise.resolve({
|
||||
async *[Symbol.asyncIterator]() {
|
||||
for (const chunk of secondStreamChunks) {
|
||||
yield chunk;
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
await provider.handleStream(commonParams, dataCallback);
|
||||
|
||||
// Should still complete without throwing
|
||||
expect(mockOpenAI.chat.completions.create).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('should prevent duplicate tool calls', async () => {
|
||||
const { agenticRAG } = await import('../../tools/agentic-rag.ts');
|
||||
vi.mocked(agenticRAG).mockResolvedValue({
|
||||
success: true,
|
||||
data: { results: ['Test result'] },
|
||||
});
|
||||
|
||||
// Mock the same tool call twice
|
||||
const chunks = [
|
||||
{
|
||||
choices: [
|
||||
{
|
||||
delta: {
|
||||
tool_calls: [
|
||||
{
|
||||
index: 0,
|
||||
id: 'call_123',
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'agentic_rag',
|
||||
arguments:
|
||||
'{"action": "search_knowledge", "query": "test", "collection_name": "test_collection"}',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
finish_reason: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
choices: [
|
||||
{
|
||||
delta: {},
|
||||
finish_reason: 'tool_calls',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
// Second iteration with same tool call
|
||||
let callCount = 0;
|
||||
mockOpenAI.chat.completions.create.mockImplementation(() => {
|
||||
callCount++;
|
||||
return Promise.resolve({
|
||||
async *[Symbol.asyncIterator]() {
|
||||
for (const chunk of chunks) {
|
||||
yield chunk;
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
await provider.handleStream(commonParams, dataCallback);
|
||||
|
||||
// Should only execute the tool once, then force completion
|
||||
expect(agenticRAG).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should handle invalid JSON in tool call arguments', async () => {
|
||||
const chunks = [
|
||||
{
|
||||
choices: [
|
||||
{
|
||||
delta: {
|
||||
tool_calls: [
|
||||
{
|
||||
index: 0,
|
||||
id: 'call_123',
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'agentic_rag',
|
||||
arguments: '{"action": "search_knowledge", "invalid": json}', // Invalid JSON
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
finish_reason: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
choices: [
|
||||
{
|
||||
delta: {},
|
||||
finish_reason: 'tool_calls',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const secondStreamChunks = [
|
||||
{
|
||||
choices: [
|
||||
{
|
||||
delta: { content: 'I encountered an error parsing the tool arguments.' },
|
||||
finish_reason: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
choices: [
|
||||
{
|
||||
delta: {},
|
||||
finish_reason: 'stop',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
let callCount = 0;
|
||||
mockOpenAI.chat.completions.create.mockImplementation(() => {
|
||||
callCount++;
|
||||
if (callCount === 1) {
|
||||
return Promise.resolve({
|
||||
async *[Symbol.asyncIterator]() {
|
||||
for (const chunk of chunks) {
|
||||
yield chunk;
|
||||
}
|
||||
},
|
||||
});
|
||||
} else {
|
||||
return Promise.resolve({
|
||||
async *[Symbol.asyncIterator]() {
|
||||
for (const chunk of secondStreamChunks) {
|
||||
yield chunk;
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Should not throw, should handle gracefully
|
||||
await expect(provider.handleStream(commonParams, dataCallback)).resolves.not.toThrow();
|
||||
});
|
||||
});
|
||||
|
@@ -1,8 +1,7 @@
|
||||
import { OpenAI } from 'openai';
|
||||
|
||||
import ChatSdk from '../chat-sdk/chat-sdk.ts';
|
||||
import { mapControlAi, MapsTools } from '../tools/maps.ts';
|
||||
import { getWeather, WeatherTool } from '../tools/weather.ts';
|
||||
import { agenticRAG, AgenticRAGTools } from '../tools/agentic-rag.ts';
|
||||
import type { GenericEnv } from '../types';
|
||||
|
||||
export interface CommonProviderParams {
|
||||
@@ -38,14 +37,11 @@ export abstract class BaseChatProvider implements ChatStreamProvider {
|
||||
|
||||
const client = this.getOpenAIClient(param);
|
||||
|
||||
const tools = [WeatherTool, MapsTools];
|
||||
const tools = [AgenticRAGTools];
|
||||
|
||||
const callFunction = async (name, args) => {
|
||||
if (name === 'get_weather') {
|
||||
return getWeather(args.latitude, args.longitude);
|
||||
}
|
||||
if (name === 'maps_control') {
|
||||
return mapControlAi({ action: args.action, value: args.value });
|
||||
if (name === 'agentic_rag') {
|
||||
return agenticRAG(args);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -54,11 +50,13 @@ export abstract class BaseChatProvider implements ChatStreamProvider {
|
||||
let toolCallIterations = 0;
|
||||
const maxToolCallIterations = 5; // Prevent infinite loops
|
||||
let toolsExecuted = false; // Track if we've executed tools
|
||||
const attemptedToolCalls = new Set<string>(); // Track attempted tool calls to prevent duplicates
|
||||
|
||||
while (!conversationComplete && toolCallIterations < maxToolCallIterations) {
|
||||
const streamParams = this.getStreamParams(param, safeMessages);
|
||||
// Only provide tools on the first call, after that force text response
|
||||
const currentTools = toolsExecuted ? undefined : tools;
|
||||
|
||||
const stream = await client.chat.completions.create({ ...streamParams, tools: currentTools });
|
||||
|
||||
let assistantMessage = '';
|
||||
@@ -115,6 +113,21 @@ export abstract class BaseChatProvider implements ChatStreamProvider {
|
||||
// Execute tool calls and add results to conversation
|
||||
console.log('Executing tool calls:', toolCalls);
|
||||
|
||||
// Limit to one tool call per iteration to prevent concurrent execution issues
|
||||
// Also filter out duplicate tool calls
|
||||
const uniqueToolCalls = toolCalls.filter(toolCall => {
|
||||
const toolCallKey = `${toolCall.function.name}:${toolCall.function.arguments}`;
|
||||
return !attemptedToolCalls.has(toolCallKey);
|
||||
});
|
||||
const toolCallsToExecute = uniqueToolCalls.slice(0, 1);
|
||||
|
||||
if (toolCallsToExecute.length === 0) {
|
||||
console.log('All tool calls have been attempted already, forcing completion');
|
||||
toolsExecuted = true;
|
||||
conversationComplete = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Send feedback to user about tool invocation
|
||||
dataCallback({
|
||||
type: 'chat',
|
||||
@@ -122,7 +135,7 @@ export abstract class BaseChatProvider implements ChatStreamProvider {
|
||||
choices: [
|
||||
{
|
||||
delta: {
|
||||
content: `\n\n🔧 Invoking ${toolCalls.length} tool${toolCalls.length > 1 ? 's' : ''}...\n`,
|
||||
content: `\n\n🔧 Invoking ${toolCallsToExecute.length} tool${toolCallsToExecute.length > 1 ? 's' : ''}...\n`,
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -133,15 +146,20 @@ export abstract class BaseChatProvider implements ChatStreamProvider {
|
||||
safeMessages.push({
|
||||
role: 'assistant',
|
||||
content: assistantMessage || null,
|
||||
tool_calls: toolCalls,
|
||||
tool_calls: toolCallsToExecute,
|
||||
});
|
||||
|
||||
// Execute each tool call and add results
|
||||
for (const toolCall of toolCalls) {
|
||||
let needsMoreRetrieval = false;
|
||||
for (const toolCall of toolCallsToExecute) {
|
||||
if (toolCall.type === 'function') {
|
||||
const name = toolCall.function.name;
|
||||
console.log(`Calling function: ${name}`);
|
||||
|
||||
// Track this tool call attempt
|
||||
const toolCallKey = `${toolCall.function.name}:${toolCall.function.arguments}`;
|
||||
attemptedToolCalls.add(toolCallKey);
|
||||
|
||||
// Send feedback about specific tool being called
|
||||
dataCallback({
|
||||
type: 'chat',
|
||||
@@ -163,6 +181,36 @@ export abstract class BaseChatProvider implements ChatStreamProvider {
|
||||
const result = await callFunction(name, args);
|
||||
console.log(`Function result:`, result);
|
||||
|
||||
// Check if agentic-rag indicates more retrieval is needed
|
||||
if (
|
||||
name === 'agentic_rag' &&
|
||||
result?.data?.analysis?.needsRetrieval === true &&
|
||||
(!result?.data?.retrieved_documents ||
|
||||
result.data.retrieved_documents.length === 0)
|
||||
) {
|
||||
needsMoreRetrieval = true;
|
||||
console.log('Agentic RAG indicates more retrieval needed');
|
||||
|
||||
// Add context about previous attempts to help LLM make better decisions
|
||||
const attemptedActions = Array.from(attemptedToolCalls)
|
||||
.filter(key => key.startsWith('agentic_rag:'))
|
||||
.map(key => {
|
||||
try {
|
||||
const args = JSON.parse(key.split(':', 2)[1]);
|
||||
return `${args.action} with query: "${args.query}"`;
|
||||
} catch {
|
||||
return 'unknown action';
|
||||
}
|
||||
});
|
||||
|
||||
if (attemptedActions.length > 0) {
|
||||
safeMessages.push({
|
||||
role: 'system',
|
||||
content: `Previous retrieval attempts: ${attemptedActions.join(', ')}. Consider trying a different approach or more specific query.`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Send feedback about tool completion
|
||||
dataCallback({
|
||||
type: 'chat',
|
||||
@@ -170,7 +218,7 @@ export abstract class BaseChatProvider implements ChatStreamProvider {
|
||||
choices: [
|
||||
{
|
||||
delta: {
|
||||
content: ` ✅\n`,
|
||||
content: ` ✅\n ${JSON.stringify(result)}`,
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -181,7 +229,7 @@ export abstract class BaseChatProvider implements ChatStreamProvider {
|
||||
safeMessages.push({
|
||||
role: 'tool',
|
||||
tool_call_id: toolCall.id,
|
||||
content: result?.toString() || '',
|
||||
content: JSON.stringify(result),
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(`Error executing tool ${name}:`, error);
|
||||
@@ -209,8 +257,10 @@ export abstract class BaseChatProvider implements ChatStreamProvider {
|
||||
}
|
||||
}
|
||||
|
||||
// Mark that tools have been executed to prevent repeated calls
|
||||
toolsExecuted = true;
|
||||
// Only mark tools as executed if we don't need more retrieval
|
||||
if (!needsMoreRetrieval) {
|
||||
toolsExecuted = true;
|
||||
}
|
||||
|
||||
// Send feedback that tool execution is complete
|
||||
dataCallback({
|
||||
|
@@ -15,10 +15,21 @@ export class FireworksAiChatProvider extends BaseChatProvider {
|
||||
let modelPrefix = 'accounts/fireworks/models/';
|
||||
if (param.model.toLowerCase().includes('yi-')) {
|
||||
modelPrefix = 'accounts/yi-01-ai/models/';
|
||||
} else if (param.model.toLowerCase().includes('/perplexity/')) {
|
||||
modelPrefix = 'accounts/perplexity/models/';
|
||||
} else if (param.model.toLowerCase().includes('/sentientfoundation/')) {
|
||||
modelPrefix = 'accounts/sentientfoundation/models/';
|
||||
} else if (param.model.toLowerCase().includes('/sentientfoundation-serverless/')) {
|
||||
modelPrefix = 'accounts/sentientfoundation-serverless/models/';
|
||||
} else if (param.model.toLowerCase().includes('/instacart/')) {
|
||||
modelPrefix = 'accounts/instacart/models/';
|
||||
}
|
||||
|
||||
const finalModelIdentifier = param.model.includes(modelPrefix)
|
||||
? param.model
|
||||
: `${modelPrefix}${param.model}`;
|
||||
console.log('using fireworks model', finalModelIdentifier);
|
||||
return {
|
||||
model: `${param.model}`,
|
||||
model: finalModelIdentifier,
|
||||
messages: safeMessages,
|
||||
stream: true,
|
||||
};
|
||||
|
259
packages/ai/src/tools/__tests__/agentic-rag.test.ts
Normal file
259
packages/ai/src/tools/__tests__/agentic-rag.test.ts
Normal file
@@ -0,0 +1,259 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
|
||||
import { agenticRAG, AgenticRAGTools } from '../agentic-rag';
|
||||
|
||||
// Mock the dependencies
|
||||
vi.mock('@zilliz/milvus2-sdk-node', () => ({
|
||||
MilvusClient: vi.fn().mockImplementation(() => ({
|
||||
listCollections: vi.fn().mockResolvedValue({
|
||||
collection_names: ['family_domestic', 'business_corporate'],
|
||||
data: [{ name: 'family_domestic' }, { name: 'business_corporate' }],
|
||||
}),
|
||||
search: vi.fn().mockResolvedValue({
|
||||
results: [
|
||||
{
|
||||
content: 'Test document about AI and machine learning',
|
||||
score: 0.85,
|
||||
metadata: '{"category": "AI", "author": "Test Author"}',
|
||||
},
|
||||
{
|
||||
content: 'Another document about neural networks',
|
||||
score: 0.75,
|
||||
metadata: '{"category": "ML", "author": "Another Author"}',
|
||||
},
|
||||
],
|
||||
}),
|
||||
insert: vi.fn().mockResolvedValue({ success: true }),
|
||||
createCollection: vi.fn().mockResolvedValue({ success: true }),
|
||||
createIndex: vi.fn().mockResolvedValue({ success: true }),
|
||||
})),
|
||||
DataType: {
|
||||
VarChar: 'VarChar',
|
||||
FloatVector: 'FloatVector',
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('openai', () => ({
|
||||
OpenAI: vi.fn().mockImplementation(() => ({
|
||||
embeddings: {
|
||||
create: vi.fn().mockResolvedValue({
|
||||
data: [{ embedding: new Array(768).fill(0.1) }],
|
||||
}),
|
||||
},
|
||||
})),
|
||||
}));
|
||||
|
||||
// Mock environment variables
|
||||
vi.stubEnv('FIREWORKS_API_KEY', 'test-api-key');
|
||||
|
||||
describe('Agentic RAG System', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should analyze queries correctly', async () => {
|
||||
// Test factual query
|
||||
const factualResult = await agenticRAG({
|
||||
action: 'analyze_query',
|
||||
query: 'What is artificial intelligence?',
|
||||
collection_name: 'family_domestic',
|
||||
});
|
||||
|
||||
expect(factualResult.status).toBe('success');
|
||||
expect(factualResult.data.needsRetrieval).toBe(true);
|
||||
expect(factualResult.data.queryType).toBe('factual');
|
||||
|
||||
// Test conversational query with multiple conversational keywords
|
||||
const conversationalResult = await agenticRAG({
|
||||
action: 'analyze_query',
|
||||
query: 'Hello, how are you doing today?',
|
||||
collection_name: 'family_domestic',
|
||||
});
|
||||
|
||||
expect(conversationalResult.status).toBe('success');
|
||||
expect(conversationalResult.data.needsRetrieval).toBe(false);
|
||||
expect(conversationalResult.data.queryType).toBe('conversational');
|
||||
|
||||
// Test creative query with multiple creative keywords
|
||||
const creativeResult = await agenticRAG({
|
||||
action: 'analyze_query',
|
||||
query: 'Write a story and compose a poem',
|
||||
collection_name: 'family_domestic',
|
||||
});
|
||||
|
||||
expect(creativeResult.status).toBe('success');
|
||||
expect(creativeResult.data.needsRetrieval).toBe(false);
|
||||
expect(creativeResult.data.queryType).toBe('creative');
|
||||
});
|
||||
|
||||
it('should search knowledge base for factual queries', async () => {
|
||||
const result = await agenticRAG({
|
||||
action: 'search_knowledge',
|
||||
query: 'What is machine learning?',
|
||||
collection_name: 'family_domestic',
|
||||
top_k: 2,
|
||||
similarity_threshold: 0.1,
|
||||
});
|
||||
|
||||
expect(result.status).toBe('success');
|
||||
expect(result.context).toBeDefined();
|
||||
expect(Array.isArray(result.context)).toBe(true);
|
||||
expect(result.data.retrieved_documents).toBeDefined();
|
||||
expect(result.data.analysis.needsRetrieval).toBe(true);
|
||||
});
|
||||
|
||||
it('should not search for conversational queries', async () => {
|
||||
const result = await agenticRAG({
|
||||
action: 'search_knowledge',
|
||||
query: 'Hello there! How are you?',
|
||||
collection_name: 'family_domestic',
|
||||
});
|
||||
|
||||
expect(result.status).toBe('success');
|
||||
expect(result.data.analysis.needsRetrieval).toBe(false);
|
||||
expect(result.data.retrieved_documents).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should store documents successfully', async () => {
|
||||
const result = await agenticRAG({
|
||||
action: 'store_document',
|
||||
document: {
|
||||
id: 'test-doc-1',
|
||||
content: 'This is a test document about neural networks and deep learning.',
|
||||
metadata: { category: 'AI', author: 'Test Author' },
|
||||
},
|
||||
collection_name: 'family_domestic',
|
||||
});
|
||||
|
||||
expect(result.status).toBe('success');
|
||||
expect(result.data.document_id).toBe('test-doc-1');
|
||||
expect(result.data.content_length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('should get context for factual queries', async () => {
|
||||
const result = await agenticRAG({
|
||||
action: 'get_context',
|
||||
query: 'Tell me about vector databases',
|
||||
collection_name: 'family_domestic',
|
||||
top_k: 2,
|
||||
});
|
||||
|
||||
expect(result.status).toBe('success');
|
||||
expect(result.data.analysis.needsRetrieval).toBe(true);
|
||||
expect(result.context).toBeDefined();
|
||||
expect(result.data.context_summary).toBeDefined();
|
||||
});
|
||||
|
||||
it('should handle semantic search', async () => {
|
||||
const result = await agenticRAG({
|
||||
action: 'semantic_search',
|
||||
query: 'artificial intelligence concepts',
|
||||
collection_name: 'family_domestic',
|
||||
top_k: 3,
|
||||
});
|
||||
|
||||
expect(result.status).toBe('success');
|
||||
expect(result.data.results).toBeDefined();
|
||||
expect(Array.isArray(result.data.results)).toBe(true);
|
||||
});
|
||||
|
||||
it('should list collections', async () => {
|
||||
const result = await agenticRAG({
|
||||
action: 'list_collections',
|
||||
collection_name: 'family_domestic',
|
||||
});
|
||||
|
||||
expect(result.status).toBe('success');
|
||||
expect(result.message).toContain('family_domestic');
|
||||
});
|
||||
|
||||
it('should handle errors gracefully', async () => {
|
||||
const result = await agenticRAG({
|
||||
action: 'analyze_query',
|
||||
collection_name: 'family_domestic',
|
||||
// Missing query parameter
|
||||
});
|
||||
|
||||
expect(result.status).toBe('error');
|
||||
expect(result.message).toContain('Query is required');
|
||||
});
|
||||
|
||||
it('should handle invalid actions', async () => {
|
||||
const result = await agenticRAG({
|
||||
action: 'invalid_action',
|
||||
collection_name: 'family_domestic',
|
||||
});
|
||||
|
||||
expect(result.status).toBe('error');
|
||||
expect(result.message).toContain('Invalid action');
|
||||
});
|
||||
|
||||
it('should have correct tool definition structure', () => {
|
||||
expect(AgenticRAGTools.type).toBe('function');
|
||||
expect(AgenticRAGTools.function.name).toBe('agentic_rag');
|
||||
expect(AgenticRAGTools.function.description).toBeDefined();
|
||||
expect(AgenticRAGTools.function.parameters.type).toBe('object');
|
||||
expect(AgenticRAGTools.function.parameters.properties.action).toBeDefined();
|
||||
expect(AgenticRAGTools.function.parameters.required).toContain('action');
|
||||
expect(AgenticRAGTools.function.parameters.required).toContain('collection_name');
|
||||
});
|
||||
|
||||
it('should demonstrate intelligent retrieval decision making', async () => {
|
||||
// Test various query types to show intelligent decision making
|
||||
const queries = [
|
||||
{ query: 'What is AI?', expectedRetrieval: true },
|
||||
{ query: 'Hello world how are you', expectedRetrieval: false },
|
||||
{ query: 'Write a poem and create a story', expectedRetrieval: false },
|
||||
{ query: 'Explain machine learning', expectedRetrieval: true },
|
||||
{ query: 'How are you doing today?', expectedRetrieval: true },
|
||||
{ query: 'Tell me about neural networks', expectedRetrieval: true },
|
||||
];
|
||||
|
||||
for (const testCase of queries) {
|
||||
const result = await agenticRAG({
|
||||
action: 'search_knowledge',
|
||||
query: testCase.query,
|
||||
collection_name: 'family_domestic',
|
||||
});
|
||||
|
||||
expect(result.status).toBe('success');
|
||||
expect(result.data.analysis.needsRetrieval).toBe(testCase.expectedRetrieval);
|
||||
|
||||
console.log(
|
||||
`[DEBUG_LOG] Query: "${testCase.query}" - Retrieval needed: ${result.data.analysis.needsRetrieval}`,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it('should filter results by similarity threshold', async () => {
|
||||
const result = await agenticRAG({
|
||||
action: 'search_knowledge',
|
||||
query: 'What is machine learning?',
|
||||
collection_name: 'family_domestic',
|
||||
similarity_threshold: 0.8, // High threshold
|
||||
});
|
||||
|
||||
expect(result.status).toBe('success');
|
||||
if (result.data.analysis.needsRetrieval) {
|
||||
// Should only return results above threshold
|
||||
result.data.retrieved_documents.forEach((doc: any) => {
|
||||
expect(doc.score).toBeGreaterThanOrEqual(0.8);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it('should handle context window limits', async () => {
|
||||
const result = await agenticRAG({
|
||||
action: 'get_context',
|
||||
query: 'Tell me about artificial intelligence',
|
||||
collection_name: 'family_domestic',
|
||||
context_window: 1000,
|
||||
});
|
||||
|
||||
expect(result.status).toBe('success');
|
||||
if (result.data.analysis.needsRetrieval && result.data.context_summary) {
|
||||
// Context should respect the window limit (approximate check)
|
||||
expect(result.data.context_summary.length).toBeLessThanOrEqual(2000); // Allow some flexibility
|
||||
}
|
||||
});
|
||||
});
|
530
packages/ai/src/tools/agentic-rag.ts
Normal file
530
packages/ai/src/tools/agentic-rag.ts
Normal file
@@ -0,0 +1,530 @@
|
||||
import { MilvusClient, DataType } from '@zilliz/milvus2-sdk-node';
|
||||
import { OpenAI } from 'openai';
|
||||
|
||||
import { ProviderRepository } from '../providers/_ProviderRepository.ts';
|
||||
|
||||
/**
|
||||
* Configuration for the Agentic RAG system
|
||||
*/
|
||||
export interface AgenticRAGConfig {
|
||||
milvusAddress?: string;
|
||||
collectionName?: string;
|
||||
embeddingDimension?: number;
|
||||
topK?: number;
|
||||
similarityThreshold?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Result structure for Agentic RAG operations
|
||||
*/
|
||||
export interface AgenticRAGResult {
|
||||
message: string;
|
||||
status: 'success' | 'error';
|
||||
data?: any;
|
||||
context?: string[];
|
||||
relevanceScore?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Document structure for knowledge base
|
||||
*/
|
||||
export interface Document {
|
||||
id: string;
|
||||
content: string;
|
||||
metadata?: Record<string, any>;
|
||||
embedding?: number[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Agentic RAG Tools for intelligent retrieval-augmented generation
|
||||
* This system makes intelligent decisions about when and how to retrieve information
|
||||
*/
|
||||
export const AgenticRAGTools = {
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'agentic_rag',
|
||||
description:
|
||||
'Intelligent retrieval-augmented generation system that can store documents, search knowledge base, and provide contextual information based on user queries. The system intelligently decides when retrieval is needed.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
action: {
|
||||
type: 'string',
|
||||
enum: [
|
||||
'list_collections',
|
||||
'report_status',
|
||||
'semantic_search',
|
||||
'search_knowledge',
|
||||
'analyze_query',
|
||||
'get_context',
|
||||
],
|
||||
description: 'Action to perform with the agentic RAG system.',
|
||||
},
|
||||
query: {
|
||||
type: 'string',
|
||||
description: 'User query or search term for knowledge retrieval.',
|
||||
},
|
||||
// document: {
|
||||
// type: 'object',
|
||||
// properties: {
|
||||
// content: { type: 'string', description: 'Document content to store' },
|
||||
// metadata: { type: 'object', description: 'Additional metadata for the document' },
|
||||
// id: { type: 'string', description: 'Unique identifier for the document' },
|
||||
// },
|
||||
// description: 'Document to store in the knowledge base.',
|
||||
// },
|
||||
collection_name: {
|
||||
type: 'string',
|
||||
// todo: make this fancy w/ dynamic collection
|
||||
enum: [
|
||||
'business_corporate',
|
||||
'civil_procedure',
|
||||
'criminal_justice',
|
||||
'education_professions',
|
||||
'environmental_infrastructure',
|
||||
'family_domestic',
|
||||
'foundational_law',
|
||||
'government_administration',
|
||||
'health_social_services',
|
||||
'miscellaneous',
|
||||
'property_real_estate',
|
||||
'special_documents',
|
||||
'taxation_finance',
|
||||
'transportation_motor_vehicles',
|
||||
],
|
||||
description: 'Name of the collection to work with.',
|
||||
},
|
||||
top_k: {
|
||||
type: 'number',
|
||||
description: 'Number of similar documents to retrieve (default: 5).',
|
||||
},
|
||||
similarity_threshold: {
|
||||
type: 'number',
|
||||
description: 'Minimum similarity score for relevant results (0-1, default: 0.7).',
|
||||
},
|
||||
context_window: {
|
||||
type: 'number',
|
||||
description: 'Maximum number of context tokens to include (default: 2000).',
|
||||
},
|
||||
},
|
||||
required: ['action', 'collection_name'],
|
||||
additionalProperties: false,
|
||||
},
|
||||
strict: true,
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Default configuration for the Agentic RAG system
|
||||
*/
|
||||
const DEFAULT_CONFIG: AgenticRAGConfig = {
|
||||
milvusAddress: 'localhost:19530',
|
||||
collectionName: 'family_domestic',
|
||||
embeddingDimension: 768,
|
||||
topK: 5,
|
||||
similarityThreshold: 0.5,
|
||||
};
|
||||
|
||||
/**
|
||||
* Simple embedding function using a mock implementation
|
||||
* In production, this should use a real embedding service like OpenAI, Cohere, etc.
|
||||
*/
|
||||
async function generateEmbedding(text: string): Promise<number[] | undefined> {
|
||||
const embeddingsClient = new OpenAI({
|
||||
apiKey: process.env.FIREWORKS_API_KEY,
|
||||
baseURL: ProviderRepository.OPENAI_COMPAT_ENDPOINTS.fireworks,
|
||||
}).embeddings;
|
||||
|
||||
const embeddings = await embeddingsClient.create({
|
||||
input: [text],
|
||||
model: 'nomic-ai/nomic-embed-text-v1.5',
|
||||
dimensions: 768,
|
||||
});
|
||||
return embeddings.data.at(0)?.embedding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyze query to determine if retrieval is needed
|
||||
*/
|
||||
function analyzeQueryForRetrieval(query: string): {
|
||||
needsRetrieval: boolean;
|
||||
confidence: number;
|
||||
reasoning: string;
|
||||
queryType: 'factual' | 'conversational' | 'creative' | 'analytical';
|
||||
} {
|
||||
const lowerQuery = query.toLowerCase();
|
||||
|
||||
// Keywords that suggest factual information is needed
|
||||
const factualKeywords = [
|
||||
'what is',
|
||||
'who is',
|
||||
'when did',
|
||||
'where is',
|
||||
'how does',
|
||||
'explain',
|
||||
'define',
|
||||
'describe',
|
||||
'tell me about',
|
||||
'information about',
|
||||
'details on',
|
||||
'facts about',
|
||||
'history of',
|
||||
'background on',
|
||||
];
|
||||
|
||||
// Keywords that suggest conversational/creative responses
|
||||
const conversationalKeywords = [
|
||||
'hello',
|
||||
'hi',
|
||||
'how are you',
|
||||
'thank you',
|
||||
'please help',
|
||||
'i think',
|
||||
'in my opinion',
|
||||
'what do you think',
|
||||
'can you help',
|
||||
];
|
||||
|
||||
// Keywords that suggest creative tasks
|
||||
const creativeKeywords = [
|
||||
'write a',
|
||||
'create a',
|
||||
'generate',
|
||||
'compose',
|
||||
'draft',
|
||||
'story',
|
||||
'poem',
|
||||
'essay',
|
||||
'letter',
|
||||
'email',
|
||||
];
|
||||
|
||||
let factualScore = 0;
|
||||
let conversationalScore = 0;
|
||||
let creativeScore = 0;
|
||||
|
||||
factualKeywords.forEach(keyword => {
|
||||
if (lowerQuery.includes(keyword)) factualScore += 1;
|
||||
});
|
||||
|
||||
conversationalKeywords.forEach(keyword => {
|
||||
if (lowerQuery.includes(keyword)) conversationalScore += 1;
|
||||
});
|
||||
|
||||
creativeKeywords.forEach(keyword => {
|
||||
if (lowerQuery.includes(keyword)) creativeScore += 1;
|
||||
});
|
||||
|
||||
// Determine query type and retrieval need
|
||||
if (factualScore > conversationalScore && factualScore > creativeScore) {
|
||||
return {
|
||||
needsRetrieval: true,
|
||||
confidence: Math.min(factualScore * 0.3, 0.9),
|
||||
reasoning:
|
||||
'Query appears to be asking for factual information that may benefit from knowledge retrieval.',
|
||||
queryType: 'factual',
|
||||
};
|
||||
} else if (creativeScore > conversationalScore && creativeScore > 1) {
|
||||
// Only skip retrieval for clearly creative tasks with multiple creative keywords
|
||||
return {
|
||||
needsRetrieval: false,
|
||||
confidence: 0.8,
|
||||
reasoning: 'Query appears to be requesting creative content generation.',
|
||||
queryType: 'creative',
|
||||
};
|
||||
} else if (conversationalScore > 1 && conversationalScore > factualScore) {
|
||||
// Only skip retrieval for clearly conversational queries with multiple conversational keywords
|
||||
return {
|
||||
needsRetrieval: false,
|
||||
confidence: 0.7,
|
||||
reasoning: 'Query appears to be conversational in nature.',
|
||||
queryType: 'conversational',
|
||||
};
|
||||
} else {
|
||||
// Default to retrieval for most cases to ensure comprehensive responses
|
||||
return {
|
||||
needsRetrieval: true,
|
||||
confidence: 0.8,
|
||||
reasoning: 'Defaulting to retrieval to provide comprehensive and accurate information.',
|
||||
queryType: 'analytical',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Main Agentic RAG function that handles intelligent retrieval decisions
|
||||
*/
|
||||
export async function agenticRAG(args: {
|
||||
action: string;
|
||||
query?: string;
|
||||
document?: Document;
|
||||
collection_name?: string;
|
||||
top_k?: number;
|
||||
similarity_threshold?: number;
|
||||
context_window?: number;
|
||||
user_confirmed?: boolean;
|
||||
}): Promise<AgenticRAGResult> {
|
||||
const config = { ...DEFAULT_CONFIG };
|
||||
const collectionName = args.collection_name || config.collectionName!;
|
||||
const topK = args.top_k || config.topK!;
|
||||
const similarityThreshold = args.similarity_threshold || config.similarityThreshold!;
|
||||
|
||||
const milvusClient = new MilvusClient({ address: config.milvusAddress! });
|
||||
|
||||
try {
|
||||
switch (args.action) {
|
||||
case 'analyze_query':
|
||||
if (!args.query) {
|
||||
return { status: 'error', message: 'Query is required for analysis.' };
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
const analysis = analyzeQueryForRetrieval(args.query);
|
||||
return {
|
||||
status: 'success',
|
||||
message: `Query analysis complete. Retrieval ${analysis.needsRetrieval ? 'recommended' : 'not needed'}.`,
|
||||
data: analysis,
|
||||
};
|
||||
|
||||
case 'list_collections':
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
const { collection_names } = (await milvusClient.listCollections()) as any as {
|
||||
collection_names: string[];
|
||||
};
|
||||
return {
|
||||
status: 'success',
|
||||
message: JSON.stringify(collection_names),
|
||||
};
|
||||
case 'search_knowledge':
|
||||
if (!args.query) {
|
||||
return { status: 'error', message: 'Query is required for knowledge search.' };
|
||||
}
|
||||
|
||||
// First, analyze if retrieval is needed
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
const queryAnalysis = analyzeQueryForRetrieval(args.query);
|
||||
|
||||
if (!queryAnalysis.needsRetrieval) {
|
||||
return {
|
||||
status: 'success',
|
||||
message: 'Query analysis suggests retrieval is not needed for this type of query.',
|
||||
data: {
|
||||
analysis: queryAnalysis,
|
||||
retrieved_documents: [],
|
||||
context: [],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Generate embedding for the query
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
const queryEmbedding = await generateEmbedding(args.query);
|
||||
|
||||
// Search for similar documents
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
const searchResult = await milvusClient.search({
|
||||
collection_name: collectionName,
|
||||
vector: queryEmbedding,
|
||||
topk: topK,
|
||||
params: { nprobe: 8 },
|
||||
output_fields: ['content', 'metadata'],
|
||||
});
|
||||
|
||||
// Filter results by similarity threshold
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
const relevantResults = searchResult.results.filter(
|
||||
(result: any) => result.score >= similarityThreshold,
|
||||
);
|
||||
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
const contextDocuments = relevantResults.map((result: any) => ({
|
||||
content: result.content,
|
||||
score: result.score,
|
||||
metadata: result.metadata,
|
||||
}));
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
message: `Found ${relevantResults.length} relevant documents for query.`,
|
||||
data: {
|
||||
analysis: queryAnalysis,
|
||||
retrieved_documents: contextDocuments,
|
||||
context: contextDocuments.map((doc: any) => doc.content),
|
||||
},
|
||||
context: contextDocuments.map((doc: any) => doc.content),
|
||||
relevanceScore: relevantResults.length > 0 ? relevantResults.at(0)?.score : 0,
|
||||
};
|
||||
|
||||
case 'store_document':
|
||||
if (!args.document || !args.document.content) {
|
||||
return { status: 'error', message: 'Document with content is required for storage.' };
|
||||
}
|
||||
|
||||
// Generate embedding for the document
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
const docEmbedding = await generateEmbedding(args.document.content);
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
const docId =
|
||||
args.document.id || `doc_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
||||
|
||||
// Store document in Milvus
|
||||
await milvusClient.insert({
|
||||
collection_name: collectionName,
|
||||
fields_data: [
|
||||
{ name: 'id', values: [docId] },
|
||||
{ name: 'embedding', values: [docEmbedding] },
|
||||
{ name: 'content', values: [args.document.content] },
|
||||
{ name: 'metadata', values: [JSON.stringify(args.document.metadata || {})] },
|
||||
],
|
||||
});
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
message: `Document stored successfully with ID: ${docId}`,
|
||||
data: { document_id: docId, content_length: args.document.content.length },
|
||||
};
|
||||
|
||||
case 'manage_collection':
|
||||
try {
|
||||
// Check if collection exists
|
||||
const collections = await milvusClient.listCollections();
|
||||
const collectionExists =
|
||||
collections.data.filter(c => c.name.includes(collectionName)).length > 0;
|
||||
|
||||
if (!collectionExists) {
|
||||
// Create collection with proper schema for RAG
|
||||
const collectionSchema = {
|
||||
collection_name: collectionName,
|
||||
fields: [
|
||||
{
|
||||
name: 'id',
|
||||
type: DataType.VarChar,
|
||||
params: { max_length: 100 },
|
||||
is_primary_key: true,
|
||||
},
|
||||
{
|
||||
name: 'embedding',
|
||||
type: DataType.FloatVector,
|
||||
params: { dim: config.embeddingDimension },
|
||||
},
|
||||
{ name: 'content', type: DataType.VarChar, params: { max_length: 65535 } },
|
||||
{ name: 'metadata', type: DataType.VarChar, params: { max_length: 1000 } },
|
||||
],
|
||||
};
|
||||
|
||||
await milvusClient.createCollection(collectionSchema as any);
|
||||
|
||||
// Create index for efficient similarity search
|
||||
await milvusClient.createIndex({
|
||||
collection_name: collectionName,
|
||||
field_name: 'embedding',
|
||||
index_type: 'IVF_FLAT',
|
||||
params: { nlist: 1024 },
|
||||
metric_type: 'COSINE',
|
||||
});
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
message: `Collection '${collectionName}' created successfully with RAG schema.`,
|
||||
data: { collection_name: collectionName, action: 'created' },
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
status: 'success',
|
||||
message: `Collection '${collectionName}' already exists.`,
|
||||
data: { collection_name: collectionName, action: 'exists' },
|
||||
};
|
||||
}
|
||||
} catch (error: any) {
|
||||
return {
|
||||
status: 'error',
|
||||
message: `Error managing collection: ${error.message}`,
|
||||
};
|
||||
}
|
||||
|
||||
case 'semantic_search':
|
||||
if (!args.query) {
|
||||
return { status: 'error', message: 'Query is required for semantic search.' };
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
const semanticEmbedding = await generateEmbedding(args.query);
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
const semanticResults = await milvusClient.search({
|
||||
collection_name: collectionName,
|
||||
vector: semanticEmbedding,
|
||||
topk: topK,
|
||||
params: { nprobe: 8 },
|
||||
output_fields: ['content', 'metadata'],
|
||||
});
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
message: `Semantic search completed. Found ${semanticResults.results.length} results.`,
|
||||
data: {
|
||||
results: semanticResults.results.map((result: any) => ({
|
||||
content: result.content,
|
||||
score: result.score,
|
||||
metadata: JSON.parse(result.metadata || '{}'),
|
||||
})),
|
||||
},
|
||||
};
|
||||
|
||||
case 'get_context':
|
||||
if (!args.query) {
|
||||
return { status: 'error', message: 'Query is required to get context.' };
|
||||
}
|
||||
|
||||
// This is a comprehensive context retrieval that combines analysis and search
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
const contextAnalysis = analyzeQueryForRetrieval(args.query);
|
||||
if (contextAnalysis.needsRetrieval) {
|
||||
const contextEmbedding = await generateEmbedding(args.query);
|
||||
const contextSearch = await milvusClient.search({
|
||||
collection_name: collectionName,
|
||||
vector: contextEmbedding,
|
||||
topk: topK,
|
||||
params: { nprobe: 8 },
|
||||
output_fields: ['content', 'metadata'],
|
||||
});
|
||||
|
||||
const contextResults = contextSearch.results
|
||||
.filter((result: any) => result.score >= similarityThreshold)
|
||||
.map((result: any) => ({
|
||||
content: result.content,
|
||||
score: result.score,
|
||||
metadata: JSON.parse(result.metadata || '{}'),
|
||||
}));
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
message: `Context retrieved successfully. Found ${contextResults.length} relevant documents.`,
|
||||
data: {
|
||||
analysis: contextAnalysis,
|
||||
context_documents: contextResults,
|
||||
context_summary: contextResults.map((doc: any) => doc.content).join('\n\n'),
|
||||
},
|
||||
context: contextResults.map((doc: any) => doc.content),
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
status: 'success',
|
||||
message: 'No context retrieval needed for this query type.',
|
||||
data: {
|
||||
analysis: contextAnalysis,
|
||||
context_documents: [],
|
||||
context_summary: '',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
default:
|
||||
return { status: 'error', message: 'Invalid action specified.' };
|
||||
}
|
||||
} catch (error: any) {
|
||||
return {
|
||||
status: 'error',
|
||||
message: `Integration error: ${error.message}`,
|
||||
};
|
||||
}
|
||||
}
|
@@ -1,111 +0,0 @@
|
||||
export interface MapsControlResult {
|
||||
message: string;
|
||||
status: 'success' | 'error';
|
||||
data?: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* A mock interface for controlling a map.
|
||||
*/
|
||||
export const MapsTools = {
|
||||
type: 'function',
|
||||
|
||||
/**
|
||||
* Mock implementation of a maps control command.
|
||||
*/
|
||||
function: {
|
||||
name: 'maps_control',
|
||||
description:
|
||||
'Interface for controlling a web-rendered map to explore publicly available geospatial data',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
action: {
|
||||
type: 'string',
|
||||
enum: ['add_point', 'zoom_to', 'search_datasets', 'add_dataset', 'remove_dataset'],
|
||||
description: 'Action to perform on the geospatial map.',
|
||||
},
|
||||
value: {
|
||||
type: 'string',
|
||||
description: 'Numeric value for the action, indicating a code for reference',
|
||||
},
|
||||
},
|
||||
required: ['action'],
|
||||
additionalProperties: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export function mapControlAi(args: { action: string; value?: string }): Promise<MapsControlResult> {
|
||||
switch (args.action) {
|
||||
case 'add_point': {
|
||||
if (!args.value) {
|
||||
return Promise.resolve({
|
||||
status: 'error',
|
||||
message: 'Missing point coordinates or reference code.',
|
||||
});
|
||||
}
|
||||
return Promise.resolve({
|
||||
status: 'success',
|
||||
message: `Point added to map with reference: ${args.value}`,
|
||||
data: { pointId: args.value, action: 'add_point' },
|
||||
});
|
||||
}
|
||||
|
||||
case 'zoom_to': {
|
||||
if (!args.value) {
|
||||
return Promise.resolve({ status: 'error', message: 'Missing zoom target reference.' });
|
||||
}
|
||||
return Promise.resolve({
|
||||
status: 'success',
|
||||
message: `Map zoomed to: ${args.value}`,
|
||||
data: { target: args.value, action: 'zoom_to' },
|
||||
});
|
||||
}
|
||||
|
||||
case 'search_datasets': {
|
||||
const searchTerm = args.value || 'all';
|
||||
return Promise.resolve({
|
||||
status: 'success',
|
||||
message: `Searching datasets for: ${searchTerm}`,
|
||||
data: {
|
||||
searchTerm,
|
||||
action: 'search_datasets',
|
||||
results: [
|
||||
{ id: 'osm', name: 'OpenStreetMap', type: 'base_layer' },
|
||||
{ id: 'satellite', name: 'Satellite Imagery', type: 'base_layer' },
|
||||
{ id: 'maritime', name: 'Maritime Data', type: 'overlay' },
|
||||
],
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
case 'add_dataset': {
|
||||
if (!args.value) {
|
||||
return Promise.resolve({ status: 'error', message: 'Missing dataset reference.' });
|
||||
}
|
||||
return Promise.resolve({
|
||||
status: 'success',
|
||||
message: `Dataset added to map: ${args.value}`,
|
||||
data: { datasetId: args.value, action: 'add_dataset' },
|
||||
});
|
||||
}
|
||||
|
||||
case 'remove_dataset': {
|
||||
if (!args.value) {
|
||||
return Promise.resolve({ status: 'error', message: 'Missing dataset reference.' });
|
||||
}
|
||||
return Promise.resolve({
|
||||
status: 'success',
|
||||
message: `Dataset removed from map: ${args.value}`,
|
||||
data: { datasetId: args.value, action: 'remove_dataset' },
|
||||
});
|
||||
}
|
||||
|
||||
default:
|
||||
return Promise.resolve({
|
||||
status: 'error',
|
||||
message: `Invalid action: ${args.action}. Valid actions are: add_point, zoom_to, search_datasets, add_dataset, remove_dataset`,
|
||||
});
|
||||
}
|
||||
}
|
68
packages/ai/src/tools/yachtpit.ts
Normal file
68
packages/ai/src/tools/yachtpit.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
export interface ShipControlResult {
|
||||
message: string;
|
||||
status: 'success' | 'error';
|
||||
data?: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* A mock interface for controlling a ship.
|
||||
*/
|
||||
export const YachtpitTools = {
|
||||
type: 'function',
|
||||
description: 'Interface for controlling a ship: set speed, change heading, report status, etc.',
|
||||
|
||||
/**
|
||||
* Mock implementation of a ship control command.
|
||||
*/
|
||||
function: {
|
||||
name: 'ship_control',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
action: {
|
||||
type: 'string',
|
||||
enum: ['set_speed', 'change_heading', 'report_status', 'stop'],
|
||||
description: 'Action to perform on the ship.',
|
||||
},
|
||||
value: {
|
||||
type: 'number',
|
||||
description:
|
||||
'Numeric value for the action, such as speed (knots) or heading (degrees). Only required for set_speed and change_heading.',
|
||||
},
|
||||
},
|
||||
required: ['action'],
|
||||
additionalProperties: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export function yachtpitAi(args: { action: string; value?: number }): Promise<ShipControlResult> {
|
||||
switch (args.action) {
|
||||
case 'set_speed':
|
||||
if (typeof args.value !== 'number') {
|
||||
return { status: 'error', message: 'Missing speed value.' };
|
||||
}
|
||||
return { status: 'success', message: `Speed set to ${args.value} knots.` };
|
||||
case 'change_heading':
|
||||
if (typeof args.value !== 'number') {
|
||||
return { status: 'error', message: 'Missing heading value.' };
|
||||
}
|
||||
return { status: 'success', message: `Heading changed to ${args.value} degrees.` };
|
||||
case 'report_status':
|
||||
// Return a simulated ship status
|
||||
return {
|
||||
status: 'success',
|
||||
message: 'Ship status reported.',
|
||||
data: {
|
||||
speed: 12,
|
||||
heading: 87,
|
||||
engine: 'nominal',
|
||||
position: { lat: 42.35, lon: -70.88 },
|
||||
},
|
||||
};
|
||||
case 'stop':
|
||||
return { status: 'success', message: 'Ship stopped.' };
|
||||
default:
|
||||
return { status: 'error', message: 'Invalid action.' };
|
||||
}
|
||||
}
|
@@ -9,7 +9,6 @@
|
||||
"generate:sitemap": "bun ./scripts/generate_sitemap.js open-gsio.seemueller.workers.dev",
|
||||
"generate:robotstxt": "bun ./scripts/generate_robots_txt.js open-gsio.seemueller.workers.dev",
|
||||
"generate:fonts": "cp -r ../../node_modules/katex/dist/fonts public/static",
|
||||
"generate:bevy:bundle": "bun scripts/generate-bevy-bundle.js",
|
||||
"generate:pwa:assets": "test ! -f public/pwa-64x64.png && pwa-assets-generator --preset minimal-2023 public/logo.png || echo 'PWA assets already exist'"
|
||||
},
|
||||
"exports": {
|
||||
|
@@ -1,196 +0,0 @@
|
||||
import { execSync, execFileSync } from 'node:child_process';
|
||||
import {
|
||||
existsSync,
|
||||
readdirSync,
|
||||
readFileSync,
|
||||
writeFileSync,
|
||||
renameSync,
|
||||
rmSync,
|
||||
cpSync,
|
||||
statSync,
|
||||
} from 'node:fs';
|
||||
import { resolve, dirname, join, basename } from 'node:path';
|
||||
|
||||
import { Logger } from 'tslog';
|
||||
const logger = new Logger({
|
||||
stdio: 'inherit',
|
||||
prettyLogTimeZone: 'local',
|
||||
type: 'pretty',
|
||||
stylePrettyLogs: true,
|
||||
prefix: ['\n'],
|
||||
overwrite: true,
|
||||
});
|
||||
|
||||
function main() {
|
||||
bundleCrate();
|
||||
cleanup();
|
||||
logger.info('🎉 yachtpit built successfully');
|
||||
}
|
||||
|
||||
const getRepoRoot = execSync('git rev-parse --show-toplevel', { encoding: 'utf-8' }).trim();
|
||||
const repoRoot = resolve(getRepoRoot);
|
||||
const publicDir = resolve(repoRoot, 'packages/client/public');
|
||||
const indexHtml = resolve(publicDir, 'index.html');
|
||||
|
||||
// Build the yachtpit project
|
||||
const buildCwd = resolve(repoRoot, 'crates/yachtpit/crates/yachtpit');
|
||||
logger.info(`🔨 Building in directory: ${buildCwd}`);
|
||||
|
||||
function needsRebuild() {
|
||||
const optimizedWasm = join(buildCwd, 'dist', 'yachtpit_bg.wasm_optimized');
|
||||
if (!existsSync(optimizedWasm)) return true;
|
||||
}
|
||||
|
||||
const NEEDS_REBUILD = needsRebuild();
|
||||
|
||||
function bundleCrate() {
|
||||
// ───────────── Build yachtpit project ───────────────────────────────────
|
||||
logger.info('🔨 Building yachtpit...');
|
||||
|
||||
logger.info(`📁 Repository root: ${repoRoot}`);
|
||||
|
||||
// Check if submodules need to be initialized
|
||||
const yachtpitPath = resolve(repoRoot, 'crates/yachtpit');
|
||||
logger.info(`📁 Yachtpit path: ${yachtpitPath}`);
|
||||
|
||||
if (!existsSync(yachtpitPath)) {
|
||||
logger.info('📦 Initializing submodules...');
|
||||
execSync('git submodule update --init --remote', { stdio: 'inherit' });
|
||||
} else {
|
||||
logger.info(`✅ Submodules already initialized at: ${yachtpitPath}`);
|
||||
}
|
||||
|
||||
try {
|
||||
if (NEEDS_REBUILD) {
|
||||
logger.info('🛠️ Changes detected — rebuilding yachtpit...');
|
||||
execSync('trunk build --release', { cwd: buildCwd, stdio: 'inherit' });
|
||||
logger.info('✅ Yachtpit built');
|
||||
} else {
|
||||
logger.info('⏩ No changes since last build — skipping yachtpit rebuild');
|
||||
process.exit(0);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ Failed to build yachtpit:', error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// ───────────── Copy assets to public directory ──────────────────────────
|
||||
const yachtpitDistDir = join(buildCwd, 'dist');
|
||||
|
||||
logger.info(`📋 Copying assets to public directory...`);
|
||||
|
||||
// Remove existing yachtpit assets from public directory
|
||||
const skipRemoveOldAssets = false;
|
||||
|
||||
if (!skipRemoveOldAssets) {
|
||||
const existingAssets = readdirSync(publicDir).filter(
|
||||
file => file.startsWith('yachtpit') && (file.endsWith('.js') || file.endsWith('.wasm')),
|
||||
);
|
||||
|
||||
existingAssets.forEach(asset => {
|
||||
const assetPath = join(publicDir, asset);
|
||||
rmSync(assetPath, { force: true });
|
||||
logger.info(`🗑️ Removed old asset: ${assetPath}`);
|
||||
});
|
||||
} else {
|
||||
logger.warn('SKIPPING REMOVING OLD ASSETS');
|
||||
}
|
||||
|
||||
// Copy new assets from yachtpit/dist to public directory
|
||||
if (existsSync(yachtpitDistDir)) {
|
||||
logger.info(`📍Located yachtpit build: ${yachtpitDistDir}`);
|
||||
try {
|
||||
cpSync(yachtpitDistDir, publicDir, {
|
||||
recursive: true,
|
||||
force: true,
|
||||
});
|
||||
logger.info(`✅ Assets copied from ${yachtpitDistDir} to ${publicDir}`);
|
||||
} catch (error) {
|
||||
console.error('❌ Failed to copy assets:', error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
} else {
|
||||
console.error(`❌ Yachtpit dist directory not found at: ${yachtpitDistDir}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// ───────────── locate targets ───────────────────────────────────────────
|
||||
const dstPath = join(publicDir, 'yachtpit.html');
|
||||
|
||||
// Regexes for the hashed filenames produced by most bundlers
|
||||
const JS_RE = /^yachtpit-[\da-f]{16}\.js$/i;
|
||||
const WASM_RE = /^yachtpit-[\da-f]{16}_bg\.wasm$/i;
|
||||
|
||||
// Always perform renaming of bundle files
|
||||
const files = readdirSync(publicDir);
|
||||
|
||||
// helper that doesn't explode if the target file is already present
|
||||
const safeRename = (from, to) => {
|
||||
if (!existsSync(from)) return;
|
||||
if (existsSync(to)) {
|
||||
logger.info(`ℹ️ ${to} already exists – removing and replacing.`);
|
||||
rmSync(to, { force: true });
|
||||
}
|
||||
renameSync(from, to);
|
||||
logger.info(`📝 Renamed: ${basename(from)} → ${basename(to)}`);
|
||||
};
|
||||
|
||||
files.forEach(f => {
|
||||
const fullPath = join(publicDir, f);
|
||||
if (JS_RE.test(f)) safeRename(fullPath, join(publicDir, 'yachtpit.js'));
|
||||
if (WASM_RE.test(f)) safeRename(fullPath, join(publicDir, 'yachtpit_bg.wasm'));
|
||||
});
|
||||
|
||||
// ───────────── patch markup inside HTML ─────────────────────────────────
|
||||
if (existsSync(indexHtml)) {
|
||||
logger.info(`📝 Patching HTML file: ${indexHtml}`);
|
||||
let html = readFileSync(indexHtml, 'utf8');
|
||||
|
||||
html = html
|
||||
.replace(/yachtpit-[\da-f]{16}\.js/gi, 'yachtpit.js')
|
||||
.replace(/yachtpit-[\da-f]{16}_bg\.wasm/gi, 'yachtpit_bg.wasm');
|
||||
|
||||
writeFileSync(indexHtml, html, 'utf8');
|
||||
|
||||
// ───────────── rename HTML entrypoint ─────────────────────────────────
|
||||
if (basename(indexHtml) !== 'yachtpit.html') {
|
||||
logger.info(`📝 Renaming HTML file: ${indexHtml} → ${dstPath}`);
|
||||
// Remove existing yachtpit.html if it exists
|
||||
if (existsSync(dstPath)) {
|
||||
rmSync(dstPath, { force: true });
|
||||
}
|
||||
renameSync(indexHtml, dstPath);
|
||||
}
|
||||
} else {
|
||||
logger.info(`⚠️ ${indexHtml} not found – skipping HTML processing.`);
|
||||
}
|
||||
optimizeWasmSize();
|
||||
}
|
||||
|
||||
function optimizeWasmSize() {
|
||||
logger.info('🔨 Checking WASM size...');
|
||||
const wasmPath = resolve(publicDir, 'yachtpit_bg.wasm');
|
||||
const fileSize = statSync(wasmPath).size;
|
||||
const sizeInMb = fileSize / (1024 * 1024);
|
||||
|
||||
if (sizeInMb > 30) {
|
||||
logger.info(`WASM size is ${sizeInMb.toFixed(2)}MB, optimizing...`);
|
||||
execFileSync('wasm-opt', ['-Oz', '-o', wasmPath, wasmPath], {
|
||||
encoding: 'utf-8',
|
||||
});
|
||||
logger.info(`✅ WASM size optimized`);
|
||||
} else {
|
||||
logger.info(
|
||||
`⏩ Skipping WASM optimization, size (${sizeInMb.toFixed(2)}MB) is under 30MB threshold`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
logger.info('Running cleanup...');
|
||||
rmSync(indexHtml, { force: true });
|
||||
const creditsDir = resolve(`${repoRoot}/packages/client/public`, 'credits');
|
||||
rmSync(creditsDir, { force: true, recursive: true });
|
||||
}
|
||||
|
||||
main();
|
@@ -8,7 +8,6 @@ import {
|
||||
MenuButton,
|
||||
MenuItem,
|
||||
MenuList,
|
||||
Spinner,
|
||||
Text,
|
||||
useDisclosure,
|
||||
useOutsideClick,
|
||||
@@ -42,38 +41,19 @@ const InputMenu: React.FC<{ isDisabled?: boolean }> = observer(({ isDisabled })
|
||||
|
||||
const [controlledOpen, setControlledOpen] = useState<boolean>(false);
|
||||
const [supportedModels, setSupportedModels] = useState<any[]>([]);
|
||||
const [isLoadingModels, setIsLoadingModels] = useState<boolean>(true);
|
||||
|
||||
useEffect(() => {
|
||||
setControlledOpen(isOpen);
|
||||
}, [isOpen]);
|
||||
|
||||
useEffect(() => {
|
||||
setIsLoadingModels(true);
|
||||
fetch('/api/models')
|
||||
.then(response => response.json())
|
||||
.then(models => {
|
||||
setSupportedModels(models);
|
||||
|
||||
// Update the ModelStore with supported models
|
||||
const modelIds = models.map((model: any) => model.id);
|
||||
clientChatStore.setSupportedModels(modelIds);
|
||||
|
||||
// If no model is currently selected or the current model is not in the list,
|
||||
// select a random model from the available ones
|
||||
if (!clientChatStore.model || !modelIds.includes(clientChatStore.model)) {
|
||||
if (models.length > 0) {
|
||||
const randomIndex = Math.floor(Math.random() * models.length);
|
||||
const randomModel = models[randomIndex];
|
||||
clientChatStore.setModel(randomModel.id);
|
||||
}
|
||||
}
|
||||
|
||||
setIsLoadingModels(false);
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('Could not fetch models: ', err);
|
||||
setIsLoadingModels(false);
|
||||
});
|
||||
}, []);
|
||||
|
||||
@@ -128,8 +108,8 @@ const InputMenu: React.FC<{ isDisabled?: boolean }> = observer(({ isDisabled })
|
||||
<MenuButton
|
||||
as={IconButton}
|
||||
bg="text.accent"
|
||||
icon={isLoadingModels ? <Spinner size="sm" /> : <Settings size={20} />}
|
||||
isDisabled={isDisabled || isLoadingModels}
|
||||
icon={<Settings size={20} />}
|
||||
isDisabled={isDisabled}
|
||||
aria-label="Settings"
|
||||
_hover={{ bg: 'rgba(255, 255, 255, 0.2)' }}
|
||||
_focus={{ boxShadow: 'none' }}
|
||||
@@ -138,8 +118,8 @@ const InputMenu: React.FC<{ isDisabled?: boolean }> = observer(({ isDisabled })
|
||||
) : (
|
||||
<MenuButton
|
||||
as={Button}
|
||||
rightIcon={isLoadingModels ? <Spinner size="sm" /> : <ChevronDown size={16} />}
|
||||
isDisabled={isDisabled || isLoadingModels}
|
||||
rightIcon={<ChevronDown size={16} />}
|
||||
isDisabled={isDisabled}
|
||||
variant="ghost"
|
||||
display="flex"
|
||||
justifyContent="space-between"
|
||||
@@ -148,7 +128,7 @@ const InputMenu: React.FC<{ isDisabled?: boolean }> = observer(({ isDisabled })
|
||||
{...MsM_commonButtonStyles}
|
||||
>
|
||||
<Text noOfLines={1} maxW="100px" fontSize="sm">
|
||||
{isLoadingModels ? 'Loading...' : clientChatStore.model}
|
||||
{clientChatStore.model}
|
||||
</Text>
|
||||
</MenuButton>
|
||||
)}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import React, { useEffect, useLayoutEffect, useState } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
import { useComponent } from '../contexts/ComponentContext.tsx';
|
||||
|
||||
@@ -8,25 +8,8 @@ import Tweakbox from './Tweakbox.tsx';
|
||||
|
||||
export const LandingComponent: React.FC = () => {
|
||||
const [intensity, setIntensity] = useState(0.99);
|
||||
const [mapActive, setMapActive] = useState(true);
|
||||
const [aiActive, setAiActive] = useState(false);
|
||||
|
||||
const appCtlState = `app-ctl-state`;
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const value = localStorage.getItem(appCtlState);
|
||||
if (value) {
|
||||
const parsed = JSON.parse(value);
|
||||
setIntensity(parsed.intensity);
|
||||
setMapActive(parsed.mapActive);
|
||||
setAiActive(parsed.aiActive);
|
||||
}
|
||||
}, []);
|
||||
|
||||
// create a hook for saving the state as a json object when it changes
|
||||
useEffect(() => {
|
||||
localStorage.setItem(appCtlState, JSON.stringify({ intensity, mapActive, aiActive }));
|
||||
});
|
||||
const [mapActive, setMapActive] = useState(false);
|
||||
const [aiActive, setAiActive] = useState(true);
|
||||
|
||||
const component = useComponent();
|
||||
const { setEnabledComponent } = component;
|
||||
@@ -38,14 +21,12 @@ export const LandingComponent: React.FC = () => {
|
||||
if (aiActive) {
|
||||
setEnabledComponent('ai');
|
||||
}
|
||||
}, [mapActive, aiActive, setEnabledComponent]);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Box as="section" bg="background.primary" overflow="hidden">
|
||||
<Box position="fixed" right={0} maxWidth="300px" minWidth="200px" zIndex={1000}>
|
||||
<Tweakbox
|
||||
id="app-tweaker"
|
||||
persist={true}
|
||||
sliders={{
|
||||
intensity: {
|
||||
value: intensity,
|
||||
@@ -58,19 +39,19 @@ export const LandingComponent: React.FC = () => {
|
||||
},
|
||||
}}
|
||||
switches={{
|
||||
GpsMap: {
|
||||
value: mapActive,
|
||||
onChange(enabled) {
|
||||
if (enabled) {
|
||||
setEnabledComponent('gpsmap');
|
||||
setAiActive(false);
|
||||
} else {
|
||||
setEnabledComponent('');
|
||||
}
|
||||
setMapActive(enabled);
|
||||
},
|
||||
label: 'GPS',
|
||||
},
|
||||
// GpsMap: {
|
||||
// value: mapActive,
|
||||
// onChange(enabled) {
|
||||
// if (enabled) {
|
||||
// setEnabledComponent('gpsmap');
|
||||
// setAiActive(false);
|
||||
// } else {
|
||||
// setEnabledComponent('');
|
||||
// }
|
||||
// setMapActive(enabled);
|
||||
// },
|
||||
// label: 'GPS',
|
||||
// },
|
||||
AI: {
|
||||
value: aiActive,
|
||||
onChange(enabled) {
|
||||
|
@@ -1,8 +1,7 @@
|
||||
import ReactMap from 'react-map-gl/mapbox'; // ↔ v5+ uses this import path
|
||||
import 'mapbox-gl/dist/mapbox-gl.css';
|
||||
import { Box, Button, HStack, Input } from '@chakra-ui/react';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
|
||||
import clientChatStore from '../../stores/ClientChatStore.ts';
|
||||
import { Box, HStack, Button, Input, Center } from '@chakra-ui/react';
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
|
||||
import MapNext from './MapNext.tsx';
|
||||
|
||||
@@ -31,175 +30,17 @@ interface AuthParams {
|
||||
token: string | null;
|
||||
}
|
||||
|
||||
export type Layer = { name: string; value: string };
|
||||
export type Layers = Layer[];
|
||||
|
||||
// public key
|
||||
const key =
|
||||
'cGsuZXlKMUlqb2laMlZ2Wm1aelpXVWlMQ0poSWpvaVkycDFOalo0YkdWNk1EUTRjRE41YjJnNFp6VjNNelp6YXlKOS56LUtzS1l0X3VGUGdCSDYwQUFBNFNn';
|
||||
|
||||
const layers = [
|
||||
{ name: 'Bathymetry', value: 'mapbox://styles/geoffsee/cmd1qz39x01ga01qv5acea02y' },
|
||||
{ name: 'Satellite', value: 'mapbox://styles/mapbox/satellite-v9' },
|
||||
];
|
||||
|
||||
function LayerSelector(props: { onClick: (e) => Promise<void> }) {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<Box position="relative">
|
||||
<Button colorScheme="blue" size="sm" variant="solid" onClick={() => setIsOpen(!isOpen)}>
|
||||
Layer
|
||||
</Button>
|
||||
|
||||
{isOpen && (
|
||||
<Box
|
||||
position="absolute"
|
||||
top="100%"
|
||||
left={0}
|
||||
w="200px"
|
||||
bg="background.secondary"
|
||||
boxShadow="md"
|
||||
zIndex={2}
|
||||
>
|
||||
{layers.map(layer => (
|
||||
<Box
|
||||
id={layer.value}
|
||||
p={2}
|
||||
cursor="pointer"
|
||||
_hover={{ bg: 'whiteAlpha.200' }}
|
||||
onClick={async e => {
|
||||
setIsOpen(false);
|
||||
await props.onClick(e);
|
||||
}}
|
||||
>
|
||||
{layer.name}
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function Map(props: { visible: boolean }) {
|
||||
const [isSearchOpen, setIsSearchOpen] = useState(false);
|
||||
const [selectedLayer, setSelectedLayer] = useState(layers[0]);
|
||||
const [searchInput, setSearchInput] = useState('');
|
||||
const [searchResults, setSearchResults] = useState<any[]>([]);
|
||||
|
||||
// const handleSearchClick = useCallback(async () => {
|
||||
// console
|
||||
// }, []);
|
||||
//
|
||||
|
||||
async function selectSearchResult({ lat, lon }) {
|
||||
// clientChatStore.mapState.latitude = searchResult.lat;
|
||||
// clientChatStore.mapState.longitude = searchResult.lon;
|
||||
await clientChatStore.setMapView(lon, lat, 15);
|
||||
}
|
||||
|
||||
async function handleSc(e) {
|
||||
if (isSearchOpen && searchInput.length > 1) {
|
||||
try {
|
||||
console.log(`trying to geocode ${searchInput}`);
|
||||
const geocode = await fetch('https://geocode.geoffsee.com', {
|
||||
method: 'POST',
|
||||
mode: 'cors',
|
||||
body: JSON.stringify({
|
||||
location: searchInput,
|
||||
}),
|
||||
});
|
||||
const coordinates = await geocode.json();
|
||||
const { lat, lon } = coordinates;
|
||||
console.log(`got geocode coordinates: ${coordinates}`);
|
||||
setSearchResults([{ lat, lon }]);
|
||||
} catch (e) {
|
||||
// continue without
|
||||
}
|
||||
} else {
|
||||
setIsSearchOpen(!isSearchOpen);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
console.log(selectedLayer);
|
||||
}, [selectedLayer]);
|
||||
|
||||
function handleLayerChange(e) {
|
||||
setSelectedLayer(layers.find(layer => layer.value === e.target.id));
|
||||
}
|
||||
|
||||
return (
|
||||
/* Full-screen wrapper — fills the viewport and becomes the positioning context */
|
||||
<Box position={'absolute'} top={0} w="100%" h={'100vh'} overflow="hidden">
|
||||
<Box position={'absolute'} top={0} w="100vw" h={'100vh'} overflow="hidden">
|
||||
{/* Button bar — absolutely positioned inside the wrapper */}
|
||||
|
||||
<HStack position="relative" zIndex={1}>
|
||||
<Box display="flex" alignItems="center">
|
||||
<Button size="sm" variant="solid" onClick={handleSc} mr={2}>
|
||||
Search
|
||||
</Button>
|
||||
{isSearchOpen && (
|
||||
<Box
|
||||
w="200px"
|
||||
transition="all 0.3s"
|
||||
transform={`translateX(${isSearchOpen ? '0' : '100%'})`}
|
||||
background="background.secondary"
|
||||
opacity={isSearchOpen ? 1 : 0}
|
||||
color="white"
|
||||
>
|
||||
<Input
|
||||
placeholder="Search..."
|
||||
size="sm"
|
||||
value={searchInput}
|
||||
onChange={e => setSearchInput(e.target.value)}
|
||||
color="white"
|
||||
bg="background.secondary"
|
||||
border="none"
|
||||
borderRadius="0"
|
||||
_focus={{
|
||||
outline: 'none',
|
||||
}}
|
||||
_placeholder={{
|
||||
color: '#d1cfcf',
|
||||
}}
|
||||
/>
|
||||
{searchResults.length > 0 && (
|
||||
<Box
|
||||
position="absolute"
|
||||
top="100%"
|
||||
left={0}
|
||||
w="200px"
|
||||
bg="background.secondary"
|
||||
boxShadow="md"
|
||||
zIndex={2}
|
||||
>
|
||||
{searchResults.map((result, index) => (
|
||||
<Box
|
||||
key={index}
|
||||
p={2}
|
||||
cursor="pointer"
|
||||
_hover={{ bg: 'whiteAlpha.200' }}
|
||||
onClick={async () => {
|
||||
// setSearchInput(result);
|
||||
console.log(`selecting result ${result.lat}, ${result.lon}`);
|
||||
await selectSearchResult(result);
|
||||
setSearchResults([]);
|
||||
setIsSearchOpen(false);
|
||||
}}
|
||||
>
|
||||
{`${result.lat}, ${result.lon}`}
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
<LayerSelector onClick={handleLayerChange} />
|
||||
</HStack>
|
||||
<MapNext mapboxPublicKey={atob(key)} visible={props.visible} layer={selectedLayer} />
|
||||
<MapNext mapboxPublicKey={atob(key)} />
|
||||
{/*<Map*/}
|
||||
{/* mapboxAccessToken={atob(key)}*/}
|
||||
{/* initialViewState={mapView}*/}
|
||||
|
@@ -1,6 +1,5 @@
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import Map, {
|
||||
FullscreenControl,
|
||||
GeolocateControl,
|
||||
@@ -8,42 +7,27 @@ import Map, {
|
||||
NavigationControl,
|
||||
Popup,
|
||||
ScaleControl,
|
||||
Source,
|
||||
} from 'react-map-gl/mapbox';
|
||||
|
||||
import clientChatStore from '../../stores/ClientChatStore';
|
||||
|
||||
import type { Layer } from './Map.tsx';
|
||||
import PORTS from './nautical-base-data.json';
|
||||
import Pin from './pin';
|
||||
|
||||
function MapNextComponent(
|
||||
props: any = { mapboxPublicKey: '', visible: true, layer: {} as Layer } as any,
|
||||
) {
|
||||
export default function MapNext(props: any = { mapboxPublicKey: '' } as any) {
|
||||
const [popupInfo, setPopupInfo] = useState(null);
|
||||
const [isSearchOpen, setIsSearchOpen] = useState(false);
|
||||
const [isTokenLoading, setIsTokenLoading] = useState(false);
|
||||
const [authenticated, setAuthenticated] = useState(false);
|
||||
const mapRef = useRef<any>(null);
|
||||
|
||||
useEffect(() => {
|
||||
setAuthenticated(true);
|
||||
setIsTokenLoading(false);
|
||||
}, []);
|
||||
|
||||
// Handle map resize when component becomes visible
|
||||
useEffect(() => {
|
||||
if (props.visible && mapRef.current) {
|
||||
// Small delay to ensure the container is fully visible
|
||||
const timer = setTimeout(() => {
|
||||
if (mapRef.current) {
|
||||
mapRef.current.resize();
|
||||
}
|
||||
}, 100);
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
}, [props.visible]);
|
||||
const [mapView, setMapView] = useState({
|
||||
longitude: -122.4,
|
||||
latitude: 37.8,
|
||||
zoom: 14,
|
||||
});
|
||||
|
||||
const handleNavigationClick = useCallback(async () => {
|
||||
console.log('handling navigation in map');
|
||||
@@ -55,10 +39,7 @@ function MapNextComponent(
|
||||
|
||||
const handleMapViewChange = useCallback(async (evt: any) => {
|
||||
const { longitude, latitude, zoom } = evt.viewState;
|
||||
clientChatStore.setMapView(longitude, latitude, zoom);
|
||||
// setMapView({ longitude, latitude, zoom });
|
||||
|
||||
// Update the store with the new view state
|
||||
setMapView({ longitude, latitude, zoom });
|
||||
}, []);
|
||||
|
||||
const pins = useMemo(
|
||||
@@ -117,19 +98,14 @@ Type '{ city: string; population: string; image: string; state: string; latitude
|
||||
{/* </Button>*/}
|
||||
{/*</HStack>*/}
|
||||
<Map
|
||||
ref={mapRef}
|
||||
initialViewState={{
|
||||
latitude: clientChatStore.mapState.latitude,
|
||||
longitude: clientChatStore.mapState.longitude,
|
||||
zoom: clientChatStore.mapState.zoom,
|
||||
bearing: clientChatStore.mapState.bearing,
|
||||
pitch: clientChatStore.mapState.pitch,
|
||||
latitude: 40,
|
||||
longitude: -100,
|
||||
zoom: 3.5,
|
||||
bearing: 0,
|
||||
pitch: 0,
|
||||
}}
|
||||
viewState={clientChatStore.mapState}
|
||||
onMove={handleMapViewChange}
|
||||
terrain={{ source: 'mapbox-dem', exaggeration: 1.5 }}
|
||||
maxPitch={85}
|
||||
mapStyle={props.layer.value}
|
||||
mapStyle="mapbox://styles/geoffsee/cmd1qz39x01ga01qv5acea02y"
|
||||
attributionControl={false}
|
||||
mapboxAccessToken={props.mapboxPublicKey}
|
||||
style={{
|
||||
@@ -142,13 +118,6 @@ Type '{ city: string; population: string; image: string; state: string; latitude
|
||||
right: 0,
|
||||
}}
|
||||
>
|
||||
<Source
|
||||
id="mapbox-dem"
|
||||
type="raster-dem"
|
||||
url="mapbox://mapbox.mapbox-terrain-dem-v1"
|
||||
tileSize={512}
|
||||
maxzoom={14}
|
||||
/>
|
||||
<GeolocateControl position="top-left" style={{ marginTop: '6rem' }} />
|
||||
<FullscreenControl position="top-left" />
|
||||
<NavigationControl position="top-left" />
|
||||
@@ -201,6 +170,3 @@ Type '{ city: string; population: string; image: string; state: string; latitude
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
const MapNext = observer(MapNextComponent);
|
||||
export default MapNext;
|
||||
|
@@ -14,7 +14,7 @@ import {
|
||||
} from '@chakra-ui/react';
|
||||
import { ChevronDownIcon, ChevronUpIcon } from 'lucide-react';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
interface SliderControl {
|
||||
value: number;
|
||||
@@ -34,8 +34,6 @@ interface SwitchControl {
|
||||
}
|
||||
|
||||
interface TweakboxProps {
|
||||
id: string;
|
||||
persist: boolean;
|
||||
sliders: {
|
||||
speed: SliderControl;
|
||||
intensity: SliderControl;
|
||||
@@ -46,7 +44,7 @@ interface TweakboxProps {
|
||||
} & Record<string, SwitchControl>;
|
||||
}
|
||||
|
||||
const Tweakbox = observer(({ id, persist, sliders, switches }: TweakboxProps) => {
|
||||
const Tweakbox = observer(({ sliders, switches }: TweakboxProps) => {
|
||||
const [isCollapsed, setIsCollapsed] = useState(false);
|
||||
|
||||
return (
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { Box, useMediaQuery } from '@chakra-ui/react';
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import React, { useEffect } from 'react';
|
||||
|
||||
import Chat from '../../components/chat/Chat.tsx';
|
||||
@@ -21,11 +21,10 @@ export default function IndexPage() {
|
||||
|
||||
const component = useComponent();
|
||||
|
||||
const mediaQuery = useMediaQuery();
|
||||
|
||||
return (
|
||||
<Box height="100%" width="100%">
|
||||
<LandingComponent />
|
||||
|
||||
<Box
|
||||
display={component.enabledComponent === 'ai' ? undefined : 'none'}
|
||||
width="100%"
|
||||
@@ -37,8 +36,8 @@ export default function IndexPage() {
|
||||
</Box>
|
||||
<Box
|
||||
display={component.enabledComponent === 'gpsmap' ? undefined : 'none'}
|
||||
width={{ base: '100%', md: '100%' }}
|
||||
height={{ base: '100%', md: '100%' }}
|
||||
width="100%"
|
||||
height="100%"
|
||||
padding={'unset'}
|
||||
>
|
||||
<ReactMap visible={component.enabledComponent === 'gpsmap'} />
|
||||
|
@@ -1,5 +1,5 @@
|
||||
export default {
|
||||
'/': { sidebarLabel: 'Home', heroLabel: 'gsio' },
|
||||
'/': { sidebarLabel: 'Home', heroLabel: 'va-chat' },
|
||||
'/connect': { sidebarLabel: 'Connect', heroLabel: 'connect' },
|
||||
'/privacy-policy': {
|
||||
sidebarLabel: '',
|
||||
|
@@ -3,14 +3,13 @@
|
||||
// ---------------------------
|
||||
import { types, type Instance } from 'mobx-state-tree';
|
||||
|
||||
import { MapStore } from './MapStore';
|
||||
import { MessagesStore } from './MessagesStore';
|
||||
import { ModelStore } from './ModelStore';
|
||||
import { StreamStore } from './StreamStore';
|
||||
import { UIStore } from './UIStore';
|
||||
|
||||
export const ClientChatStore = types
|
||||
.compose(MessagesStore, UIStore, ModelStore, StreamStore, MapStore)
|
||||
.compose(MessagesStore, UIStore, ModelStore, StreamStore)
|
||||
.named('ClientChatStore');
|
||||
|
||||
const clientChatStore = ClientChatStore.create();
|
||||
|
@@ -1,122 +0,0 @@
|
||||
import { types, type Instance } from 'mobx-state-tree';
|
||||
|
||||
export interface MapControlCommand {
|
||||
action: string;
|
||||
value?: string;
|
||||
data?: any;
|
||||
}
|
||||
|
||||
export const MapStore = types
|
||||
.model('MapStore', {
|
||||
// Current map view state
|
||||
// 37°47'21"N 122°23'52"W
|
||||
longitude: types.optional(types.number, -87.6319),
|
||||
latitude: types.optional(types.number, 41.883415),
|
||||
zoom: types.optional(types.number, 14.5),
|
||||
bearing: types.optional(types.number, 15.165878375019094),
|
||||
pitch: types.optional(types.number, 45),
|
||||
// Map control state
|
||||
isControlActive: types.optional(types.boolean, false),
|
||||
})
|
||||
.volatile(self => ({
|
||||
// Store pending map commands from AI
|
||||
pendingCommands: [] as MapControlCommand[],
|
||||
// 41.88341413374059-87.630091075785714.57273962016686450
|
||||
mapState: {
|
||||
latitude: self.latitude,
|
||||
longitude: self.longitude,
|
||||
zoom: self.zoom,
|
||||
bearing: self.bearing,
|
||||
pitch: self.pitch,
|
||||
} as any,
|
||||
}))
|
||||
.actions(self => ({
|
||||
// Update map view state
|
||||
setMapView(longitude: number, latitude: number, zoom: number) {
|
||||
console.log(latitude, longitude, zoom, self.mapState.pitch, self.mapState.bearing);
|
||||
self.longitude = longitude;
|
||||
self.latitude = latitude;
|
||||
self.zoom = zoom;
|
||||
|
||||
// Also update the mapState object to keep it in sync
|
||||
self.mapState = {
|
||||
...self.mapState,
|
||||
longitude,
|
||||
latitude,
|
||||
zoom,
|
||||
};
|
||||
},
|
||||
|
||||
// Handle map control commands from AI
|
||||
executeMapCommand(command: MapControlCommand) {
|
||||
console.log('[DEBUG_LOG] Executing map command:', command);
|
||||
|
||||
switch (command.action) {
|
||||
case 'zoom_to': {
|
||||
if (command.data?.target) {
|
||||
// For now, we'll implement a simple zoom behavior
|
||||
// In a real implementation, this could parse coordinates or location names
|
||||
const zoomLevel = 10; // Default zoom level for zoom_to commands
|
||||
self.zoom = zoomLevel;
|
||||
console.log('[DEBUG_LOG] Zoomed to level:', zoomLevel);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'add_point': {
|
||||
if (command.data?.pointId) {
|
||||
console.log('[DEBUG_LOG] Adding point:', command.data.pointId);
|
||||
// Point addition logic would go here
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'add_dataset':
|
||||
case 'remove_dataset': {
|
||||
if (command.data?.datasetId) {
|
||||
console.log('[DEBUG_LOG] Dataset operation:', command.action, command.data.datasetId);
|
||||
// Dataset management logic would go here
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'search_datasets': {
|
||||
console.log('[DEBUG_LOG] Searching datasets:', command.data?.searchTerm);
|
||||
// Dataset search logic would go here
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
console.warn('[DEBUG_LOG] Unknown map command:', command.action);
|
||||
}
|
||||
|
||||
self.isControlActive = true;
|
||||
|
||||
// Clear the command after a short delay
|
||||
setTimeout(() => {
|
||||
self.isControlActive = false;
|
||||
}, 1000);
|
||||
},
|
||||
|
||||
// Add a command to the pending queue
|
||||
addPendingCommand(command: MapControlCommand) {
|
||||
self.pendingCommands.push(command);
|
||||
},
|
||||
|
||||
// Process all pending commands
|
||||
processPendingCommands() {
|
||||
while (self.pendingCommands.length > 0) {
|
||||
const command = self.pendingCommands.shift();
|
||||
if (command) {
|
||||
this.executeMapCommand(command);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Clear all pending commands
|
||||
clearPendingCommands() {
|
||||
self.pendingCommands.splice(0);
|
||||
},
|
||||
}));
|
||||
|
||||
export type IMapStore = Instance<typeof MapStore>;
|
@@ -2,8 +2,6 @@ import { flow, getParent, type Instance, types } from 'mobx-state-tree';
|
||||
|
||||
import Message, { batchContentUpdate } from '../models/Message';
|
||||
|
||||
import clientChatStore from './ClientChatStore.ts';
|
||||
import type { MapControlCommand } from './MapStore';
|
||||
import type { RootDeps } from './RootDeps.ts';
|
||||
import UserOptionsStore from './UserOptionsStore';
|
||||
|
||||
@@ -94,30 +92,6 @@ export const StreamStore = types
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle tool call responses
|
||||
if (parsed.type === 'tool_result') {
|
||||
console.log('[DEBUG_LOG] Received tool result:', parsed);
|
||||
|
||||
// Check if this is a map control tool call
|
||||
if (parsed.tool_name === 'maps_control' && parsed.result?.data) {
|
||||
const mapCommand: MapControlCommand = {
|
||||
action: parsed.result.data.action,
|
||||
value: parsed.args?.value,
|
||||
data: parsed.result.data,
|
||||
};
|
||||
|
||||
console.log('[DEBUG_LOG] Processing map command:', mapCommand);
|
||||
|
||||
// Execute the map command through the store
|
||||
if ('executeMapCommand' in root) {
|
||||
(root as any).executeMapCommand(mapCommand);
|
||||
} else {
|
||||
console.warn('[DEBUG_LOG] MapStore not available in root');
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the last message
|
||||
const lastMessage = root.items[root.items.length - 1];
|
||||
|
||||
@@ -178,4 +152,4 @@ export const StreamStore = types
|
||||
return { sendMessage, stopIncomingMessage, cleanup, setEventSource, setStreamId };
|
||||
});
|
||||
|
||||
export type IStreamStore = Instance<typeof StreamStore>;
|
||||
export interface IStreamStore extends Instance<typeof StreamStore> {}
|
||||
|
53
packages/server/src/server/asset-handler.ts
Normal file
53
packages/server/src/server/asset-handler.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { readdir } from 'node:fs/promises';
|
||||
|
||||
export const assetHandler = {
|
||||
ASSETS: {
|
||||
/**
|
||||
* Fetches the requested static asset from local dist
|
||||
*
|
||||
* @param {Request} request - The incoming Fetch API Request object.
|
||||
* @returns {Promise<Response>} A Promise that resolves with the Response for the requested asset,
|
||||
* or a 404 Response if the asset is not found or an error occurs.
|
||||
*/
|
||||
async fetch(request: Request): Promise<Response> {
|
||||
// Serialize incoming request URL
|
||||
const originalUrl = new URL(request.url);
|
||||
const url = new URL(request.url);
|
||||
|
||||
// Fixed path: go up to packages level, then to client/public
|
||||
const PUBLIC_DIR = new URL('../../../client/public/', import.meta.url).pathname;
|
||||
|
||||
let publicFiles: string[] = [];
|
||||
try {
|
||||
publicFiles = await readdir(PUBLIC_DIR, { recursive: true });
|
||||
} catch (error) {
|
||||
console.warn(`Could not read public directory ${PUBLIC_DIR}:`, error);
|
||||
// Continue without public files list
|
||||
}
|
||||
|
||||
// Get the filename from pathname and remove any path traversal attempts
|
||||
const filename = url.pathname.split('/').pop()?.replace(/\.\./g, '') || '';
|
||||
|
||||
const isStatic = publicFiles.some(file => file === filename);
|
||||
|
||||
if (url.pathname === '/') {
|
||||
url.pathname = '/index.html';
|
||||
} else if (isStatic && !url.pathname.startsWith('/static')) {
|
||||
// leave it alone
|
||||
} else if (isStatic) {
|
||||
url.pathname = `/static${url.pathname}`;
|
||||
}
|
||||
|
||||
// Fixed path: go up to packages level, then to client/dist/client
|
||||
const dist = new URL('../../../client/dist/client', import.meta.url).pathname;
|
||||
|
||||
try {
|
||||
return new Response(Bun.file(`${dist}${url.pathname}`));
|
||||
} catch (error) {
|
||||
// Log the error with the original requested path
|
||||
console.error(`Error reading asset from path ${originalUrl.pathname}:`, error);
|
||||
return new Response(null, { status: 404 });
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
61
packages/server/src/server/create-server.ts
Normal file
61
packages/server/src/server/create-server.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import ServerCoordinator from '@open-gsio/coordinators/src/ServerCoordinatorBun.ts';
|
||||
import Router from '@open-gsio/router';
|
||||
import { config } from 'dotenv';
|
||||
import type { RequestLike } from 'itty-router';
|
||||
import { error } from 'itty-router';
|
||||
|
||||
import { BunSqliteKVNamespace } from '../storage/BunSqliteKVNamespace.ts';
|
||||
|
||||
import { assetHandler } from './asset-handler.ts';
|
||||
|
||||
export function createServer() {
|
||||
const router = Router.Router();
|
||||
config({
|
||||
path: '.env',
|
||||
debug: true,
|
||||
// defaults: {
|
||||
// EVENTSOURCE_HOST: "https://eventsource.seemueller.io",
|
||||
// }
|
||||
});
|
||||
|
||||
// bootstrap the root path of the existing router to the asset handler defined here
|
||||
router.get('/', async (request: RequestLike, env: any) => {
|
||||
return await assetHandler.ASSETS.fetch(request as Request);
|
||||
});
|
||||
|
||||
const server = {
|
||||
port: 3003,
|
||||
fetch: async (request: RequestLike, env: { [key: string]: any }, ctx: any) => {
|
||||
// console.log("[trace] request: ", request.method, request.url, "headers: ", request.headers.get("referer"), "body: ", request.body, "env: ", env, "ctx: ", ctx, "")
|
||||
|
||||
env['SERVER_COORDINATOR'] = ServerCoordinator;
|
||||
env['ASSETS'] = assetHandler.ASSETS;
|
||||
env['EVENTSOURCE_HOST'] = process.env.EVENTSOURCE_HOST;
|
||||
env['GROQ_API_KEY'] = process.env.GROQ_API_KEY;
|
||||
env['ANTHROPIC_API_KEY'] = process.env.ANTHROPIC_API_KEY;
|
||||
env['FIREWORKS_API_KEY'] = process.env.FIREWORKS_API_KEY;
|
||||
env['XAI_API_KEY'] = process.env.XAI_API_KEY;
|
||||
env['CEREBRAS_API_KEY'] = process.env.CEREBRAS_API_KEY;
|
||||
env['CLOUDFLARE_API_KEY'] = process.env.CLOUDFLARE_API_KEY;
|
||||
env['CLOUDFLARE_ACCOUNT_ID'] = process.env.CLOUDFLARE_ACCOUNT_ID;
|
||||
env['MLX_API_KEY'] = process.env.MLX_API_KEY;
|
||||
env['OLLAMA_API_KEY'] = process.env.OLLAMA_API_KEY;
|
||||
env['KV_STORAGE'] = new BunSqliteKVNamespace({ namespace: 'open-gsio' });
|
||||
|
||||
try {
|
||||
const controller = new AbortController();
|
||||
const timeout = new Promise((_, reject) =>
|
||||
setTimeout(() => {
|
||||
controller.abort();
|
||||
reject(new Error('Request timeout after 5s'));
|
||||
}, 5000),
|
||||
);
|
||||
return await Promise.race([router.fetch(request, env, ctx).catch(error), timeout]);
|
||||
} catch (e) {
|
||||
console.error('Error handling request:', e);
|
||||
return new Response('Server Error', { status: 500 });
|
||||
}
|
||||
},
|
||||
};
|
||||
return { server, router, assetHandler };
|
||||
}
|
@@ -1,111 +1,6 @@
|
||||
import { readdir } from 'node:fs/promises';
|
||||
import { createServer } from './create-server.ts';
|
||||
|
||||
import ServerCoordinator from '@open-gsio/coordinators/src/ServerCoordinatorBun.ts';
|
||||
import Router from '@open-gsio/router';
|
||||
import { config } from 'dotenv';
|
||||
import type { RequestLike } from 'itty-router';
|
||||
import { error } from 'itty-router';
|
||||
// creates a bun server with the itty router
|
||||
const { server } = createServer();
|
||||
|
||||
import { BunSqliteKVNamespace } from '../storage/BunSqliteKVNamespace.ts';
|
||||
|
||||
const router = Router.Router();
|
||||
|
||||
config({
|
||||
path: '.env',
|
||||
debug: true,
|
||||
// defaults: {
|
||||
// EVENTSOURCE_HOST: "https://eventsource.seemueller.io",
|
||||
// }
|
||||
});
|
||||
|
||||
// bootstrap the root path of the existing router to the asset handler defined here
|
||||
router.get('/', async (request: RequestLike, env: any) => {
|
||||
return await assetHandler.ASSETS.fetch(request as Request);
|
||||
});
|
||||
|
||||
export default {
|
||||
port: 3003,
|
||||
fetch: async (request: RequestLike, env: { [key: string]: any }, ctx: any) => {
|
||||
// console.log("[trace] request: ", request.method, request.url, "headers: ", request.headers.get("referer"), "body: ", request.body, "env: ", env, "ctx: ", ctx, "")
|
||||
|
||||
env['SERVER_COORDINATOR'] = ServerCoordinator;
|
||||
env['ASSETS'] = assetHandler.ASSETS;
|
||||
env['EVENTSOURCE_HOST'] = process.env.EVENTSOURCE_HOST;
|
||||
env['GROQ_API_KEY'] = process.env.GROQ_API_KEY;
|
||||
env['ANTHROPIC_API_KEY'] = process.env.ANTHROPIC_API_KEY;
|
||||
env['FIREWORKS_API_KEY'] = process.env.FIREWORKS_API_KEY;
|
||||
env['XAI_API_KEY'] = process.env.XAI_API_KEY;
|
||||
env['CEREBRAS_API_KEY'] = process.env.CEREBRAS_API_KEY;
|
||||
env['CLOUDFLARE_API_KEY'] = process.env.CLOUDFLARE_API_KEY;
|
||||
env['CLOUDFLARE_ACCOUNT_ID'] = process.env.CLOUDFLARE_ACCOUNT_ID;
|
||||
env['MLX_API_KEY'] = process.env.MLX_API_KEY;
|
||||
env['OLLAMA_API_KEY'] = process.env.OLLAMA_API_KEY;
|
||||
env['KV_STORAGE'] = new BunSqliteKVNamespace({ namespace: 'open-gsio' });
|
||||
|
||||
try {
|
||||
const controller = new AbortController();
|
||||
const timeout = new Promise((_, reject) =>
|
||||
setTimeout(() => {
|
||||
controller.abort();
|
||||
reject(new Error('Request timeout after 5s'));
|
||||
}, 5000),
|
||||
);
|
||||
return await Promise.race([router.fetch(request, env, ctx).catch(error), timeout]);
|
||||
} catch (e) {
|
||||
console.error('Error handling request:', e);
|
||||
return new Response('Server Error', { status: 500 });
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export const assetHandler = {
|
||||
ASSETS: {
|
||||
/**
|
||||
* Fetches the requested static asset from local dist
|
||||
*
|
||||
* @param {Request} request - The incoming Fetch API Request object.
|
||||
* @returns {Promise<Response>} A Promise that resolves with the Response for the requested asset,
|
||||
* or a 404 Response if the asset is not found or an error occurs.
|
||||
*/
|
||||
async fetch(request: Request): Promise<Response> {
|
||||
// Serialize incoming request URL
|
||||
const originalUrl = new URL(request.url);
|
||||
const url = new URL(request.url);
|
||||
|
||||
// Fixed path: go up to packages level, then to client/public
|
||||
const PUBLIC_DIR = new URL('../../../client/public/', import.meta.url).pathname;
|
||||
|
||||
let publicFiles: string[] = [];
|
||||
try {
|
||||
publicFiles = await readdir(PUBLIC_DIR, { recursive: true });
|
||||
} catch (error) {
|
||||
console.warn(`Could not read public directory ${PUBLIC_DIR}:`, error);
|
||||
// Continue without public files list
|
||||
}
|
||||
|
||||
// Get the filename from pathname and remove any path traversal attempts
|
||||
const filename = url.pathname.split('/').pop()?.replace(/\.\./g, '') || '';
|
||||
|
||||
const isStatic = publicFiles.some(file => file === filename);
|
||||
|
||||
if (url.pathname === '/') {
|
||||
url.pathname = '/index.html';
|
||||
} else if (isStatic && !url.pathname.startsWith('/static')) {
|
||||
// leave it alone
|
||||
} else if (isStatic) {
|
||||
url.pathname = `/static${url.pathname}`;
|
||||
}
|
||||
|
||||
// Fixed path: go up to packages level, then to client/dist/client
|
||||
const dist = new URL('../../../client/dist/client', import.meta.url).pathname;
|
||||
|
||||
try {
|
||||
return new Response(Bun.file(`${dist}${url.pathname}`));
|
||||
} catch (error) {
|
||||
// Log the error with the original requested path
|
||||
console.error(`Error reading asset from path ${originalUrl.pathname}:`, error);
|
||||
return new Response(null, { status: 404 });
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
export default server;
|
||||
|
@@ -161,19 +161,29 @@ const ChatService = types
|
||||
|
||||
const openai = new OpenAI({ apiKey: provider.key, baseURL: provider.endpoint });
|
||||
|
||||
// 2‑a. List models
|
||||
const basicFilters = (model: any) => {
|
||||
return (
|
||||
!model.id.includes('whisper') &&
|
||||
!model.id.includes('flux') &&
|
||||
!model.id.includes('ocr') &&
|
||||
!model.id.includes('tts') &&
|
||||
!model.id.includes('guard')
|
||||
);
|
||||
}; // 2‑a. List models
|
||||
try {
|
||||
const listResp: any = yield openai.models.list(); // <‑‑ async
|
||||
const models = 'data' in listResp ? listResp.data : listResp;
|
||||
|
||||
providerModels.set(
|
||||
provider.name,
|
||||
models.filter(
|
||||
(mdl: any) =>
|
||||
!mdl.id.includes('whisper') &&
|
||||
!mdl.id.includes('tts') &&
|
||||
!mdl.id.includes('guard'),
|
||||
),
|
||||
models.filter((mdl: any) => {
|
||||
if ('supports_chat' in mdl && mdl.supports_chat) {
|
||||
return basicFilters(mdl);
|
||||
} else if ('supports_chat' in mdl && !mdl.supports_chat) {
|
||||
return false;
|
||||
}
|
||||
return basicFilters(mdl);
|
||||
}),
|
||||
);
|
||||
|
||||
// 2‑b. Retrieve metadata
|
||||
@@ -183,7 +193,7 @@ const ChatService = types
|
||||
modelMeta.set(mdl.id, { ...mdl, ...meta });
|
||||
} catch (err) {
|
||||
// logger.error(`Metadata fetch failed for ${mdl.id}`, err);
|
||||
modelMeta.set(mdl.id, { provider: provider.name, ...mdl });
|
||||
modelMeta.set(mdl.id, { provider: provider.name, mdl });
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
@@ -277,23 +287,8 @@ const ChatService = types
|
||||
}) {
|
||||
const { streamConfig, streamParams, controller, encoder, streamId } = params;
|
||||
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log(
|
||||
`[DEBUG_LOG] ChatService.runModelHandler: Processing model "${streamConfig.model}" for stream ${streamId}`,
|
||||
);
|
||||
|
||||
const modelFamily = await ProviderRepository.getModelFamily(streamConfig.model, self.env);
|
||||
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log(
|
||||
`[DEBUG_LOG] ChatService.runModelHandler: Detected model family "${modelFamily}" for model "${streamConfig.model}"`,
|
||||
);
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log(
|
||||
'[DEBUG_LOG] ChatService.runModelHandler: Available model handlers:',
|
||||
Object.keys(modelHandlers),
|
||||
);
|
||||
|
||||
const useModelHandler = () => {
|
||||
// @ts-expect-error - language server does not have enough information to validate modelFamily as an indexer for modelHandlers
|
||||
return modelHandlers[modelFamily];
|
||||
@@ -302,28 +297,9 @@ const ChatService = types
|
||||
const handler = useModelHandler();
|
||||
|
||||
if (handler) {
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log(
|
||||
`[DEBUG_LOG] ChatService.runModelHandler: Found handler for model family "${modelFamily}"`,
|
||||
);
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log(
|
||||
`[DEBUG_LOG] ChatService.runModelHandler: Calling handler for model "${streamConfig.model}" with maxTokens: ${streamParams.maxTokens}`,
|
||||
);
|
||||
|
||||
try {
|
||||
await handler(streamParams, Common.Utils.handleStreamData(controller, encoder));
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log(
|
||||
`[DEBUG_LOG] ChatService.runModelHandler: Successfully completed handler for model "${streamConfig.model}"`,
|
||||
);
|
||||
} catch (error: any) {
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log(
|
||||
`[DEBUG_LOG] ChatService.runModelHandler: Handler error for model "${streamConfig.model}":`,
|
||||
error.message,
|
||||
);
|
||||
|
||||
const message = error.message.toLowerCase();
|
||||
|
||||
if (
|
||||
@@ -352,80 +328,11 @@ const ChatService = types
|
||||
);
|
||||
}
|
||||
if (message.includes('404')) {
|
||||
// Try to find a fallback model from the same provider
|
||||
try {
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log(
|
||||
`[DEBUG_LOG] ChatService.runModelHandler: Model "${streamConfig.model}" not found, attempting fallback`,
|
||||
);
|
||||
|
||||
const allModels = await self.env.KV_STORAGE.get('supportedModels');
|
||||
const models = JSON.parse(allModels);
|
||||
|
||||
// Find all models from the same provider
|
||||
const sameProviderModels = models.filter(
|
||||
(m: any) => m.provider === modelFamily && m.id !== streamConfig.model,
|
||||
);
|
||||
|
||||
if (sameProviderModels.length > 0) {
|
||||
// Try the first available model from the same provider
|
||||
const fallbackModel = sameProviderModels[0];
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log(
|
||||
`[DEBUG_LOG] ChatService.runModelHandler: Trying fallback model "${fallbackModel.id}" from provider "${modelFamily}"`,
|
||||
);
|
||||
|
||||
// Update streamParams with the fallback model
|
||||
const fallbackStreamParams = { ...streamParams, model: fallbackModel.id };
|
||||
|
||||
// Try the fallback model
|
||||
await handler(
|
||||
fallbackStreamParams,
|
||||
Common.Utils.handleStreamData(controller, encoder),
|
||||
);
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log(
|
||||
`[DEBUG_LOG] ChatService.runModelHandler: Successfully completed handler with fallback model "${fallbackModel.id}"`,
|
||||
);
|
||||
return; // Success with fallback
|
||||
} else {
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log(
|
||||
`[DEBUG_LOG] ChatService.runModelHandler: No fallback models available for provider "${modelFamily}"`,
|
||||
);
|
||||
}
|
||||
} catch (fallbackError: any) {
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log(
|
||||
`[DEBUG_LOG] ChatService.runModelHandler: Fallback attempt failed:`,
|
||||
fallbackError.message,
|
||||
);
|
||||
}
|
||||
|
||||
throw new ClientError(
|
||||
`Model not found or unavailable. Please try a different model.`,
|
||||
404,
|
||||
{
|
||||
model: streamConfig.model,
|
||||
},
|
||||
);
|
||||
console.log(message);
|
||||
throw new ClientError(`Something went wrong, try again.`, 404, {});
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
} else {
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log(
|
||||
`[DEBUG_LOG] ChatService.runModelHandler: No handler found for model family "${modelFamily}" (model: "${streamConfig.model}")`,
|
||||
);
|
||||
throw new ClientError(
|
||||
`No handler available for model family "${modelFamily}". Model: "${streamConfig.model}"`,
|
||||
500,
|
||||
{
|
||||
model: streamConfig.model,
|
||||
modelFamily: modelFamily,
|
||||
availableHandlers: Object.keys(modelHandlers),
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -437,27 +344,11 @@ const ChatService = types
|
||||
}) {
|
||||
const { streamId, streamConfig, savedStreamConfig, durableObject } = params;
|
||||
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log(
|
||||
`[DEBUG_LOG] ChatService.createSseReadableStream: Creating stream ${streamId} for model "${streamConfig.model}"`,
|
||||
);
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log(`[DEBUG_LOG] ChatService.createSseReadableStream: Stream config:`, {
|
||||
model: streamConfig.model,
|
||||
systemPrompt: streamConfig.systemPrompt?.substring(0, 100) + '...',
|
||||
messageCount: streamConfig.messages?.length,
|
||||
});
|
||||
|
||||
return new ReadableStream({
|
||||
async start(controller) {
|
||||
const encoder = new TextEncoder();
|
||||
|
||||
try {
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log(
|
||||
`[DEBUG_LOG] ChatService.createSseReadableStream: Starting stream processing for ${streamId}`,
|
||||
);
|
||||
|
||||
const dynamicContext = Schema.Message.create(streamConfig.preprocessedContext);
|
||||
|
||||
// Process the stream data using the appropriate handler
|
||||
@@ -467,16 +358,6 @@ const ChatService = types
|
||||
durableObject,
|
||||
);
|
||||
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log(
|
||||
`[DEBUG_LOG] ChatService.createSseReadableStream: Created stream params for ${streamId}:`,
|
||||
{
|
||||
model: streamParams.model,
|
||||
maxTokens: streamParams.maxTokens,
|
||||
messageCount: streamParams.messages?.length,
|
||||
},
|
||||
);
|
||||
|
||||
await self.runModelHandler({
|
||||
streamConfig,
|
||||
streamParams,
|
||||
@@ -485,11 +366,6 @@ const ChatService = types
|
||||
streamId,
|
||||
});
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log(
|
||||
`[DEBUG_LOG] ChatService.createSseReadableStream: Error in stream ${streamId}:`,
|
||||
error,
|
||||
);
|
||||
console.error(`chatService::handleSseStream::${streamId}::Error`, error);
|
||||
|
||||
if (error instanceof ClientError) {
|
||||
@@ -511,10 +387,6 @@ const ChatService = types
|
||||
controller.close();
|
||||
} finally {
|
||||
try {
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log(
|
||||
`[DEBUG_LOG] ChatService.createSseReadableStream: Closing stream ${streamId}`,
|
||||
);
|
||||
controller.close();
|
||||
} catch (_) {
|
||||
// Ignore errors when closing the controller, as it might already be closed
|
||||
@@ -527,53 +399,21 @@ const ChatService = types
|
||||
handleSseStream: flow(function* (
|
||||
streamId: string,
|
||||
): Generator<Promise<string>, Response, unknown> {
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log(
|
||||
`[DEBUG_LOG] ChatService.handleSseStream: Handling SSE stream request for ${streamId}`,
|
||||
);
|
||||
|
||||
// Check if a stream is already active for this ID
|
||||
if (self.activeStreams.has(streamId)) {
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log(
|
||||
`[DEBUG_LOG] ChatService.handleSseStream: Stream ${streamId} already active, returning 409`,
|
||||
);
|
||||
return new Response('Stream already active', { status: 409 });
|
||||
}
|
||||
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log(
|
||||
`[DEBUG_LOG] ChatService.handleSseStream: Retrieving stream configuration for ${streamId}`,
|
||||
);
|
||||
|
||||
// Retrieve the stream configuration from the durable object
|
||||
const objectId = self.env.SERVER_COORDINATOR.idFromName('stream-index');
|
||||
const durableObject = self.env.SERVER_COORDINATOR.get(objectId);
|
||||
const savedStreamConfig: any = yield durableObject.getStreamData(streamId);
|
||||
|
||||
if (!savedStreamConfig) {
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log(
|
||||
`[DEBUG_LOG] ChatService.handleSseStream: No stream configuration found for ${streamId}, returning 404`,
|
||||
);
|
||||
return new Response('Stream not found', { status: 404 });
|
||||
}
|
||||
|
||||
const streamConfig = JSON.parse(savedStreamConfig);
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log(
|
||||
`[DEBUG_LOG] ChatService.handleSseStream: Retrieved stream config for ${streamId}:`,
|
||||
{
|
||||
model: streamConfig.model,
|
||||
messageCount: streamConfig.messages?.length,
|
||||
systemPrompt: streamConfig.systemPrompt?.substring(0, 100) + '...',
|
||||
},
|
||||
);
|
||||
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log(
|
||||
`[DEBUG_LOG] ChatService.handleSseStream: Creating SSE readable stream for ${streamId}`,
|
||||
);
|
||||
|
||||
const stream = self.createSseReadableStream({
|
||||
streamId,
|
||||
@@ -585,37 +425,18 @@ const ChatService = types
|
||||
// Use `tee()` to create two streams: one for processing and one for the response
|
||||
const [processingStream, responseStream] = stream.tee();
|
||||
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log(
|
||||
`[DEBUG_LOG] ChatService.handleSseStream: Setting active stream for ${streamId}`,
|
||||
);
|
||||
|
||||
self.setActiveStream(streamId, {
|
||||
...streamConfig,
|
||||
});
|
||||
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log(
|
||||
`[DEBUG_LOG] ChatService.handleSseStream: Setting up processing stream pipeline for ${streamId}`,
|
||||
);
|
||||
|
||||
processingStream.pipeTo(
|
||||
new WritableStream({
|
||||
close() {
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log(
|
||||
`[DEBUG_LOG] ChatService.handleSseStream: Processing stream closed for ${streamId}, removing active stream`,
|
||||
);
|
||||
self.removeActiveStream(streamId);
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
console.log(
|
||||
`[DEBUG_LOG] ChatService.handleSseStream: Returning response stream for ${streamId}`,
|
||||
);
|
||||
|
||||
// Return the second stream as the response
|
||||
return new Response(responseStream, {
|
||||
headers: {
|
||||
|
Reference in New Issue
Block a user