6 Commits

Author SHA1 Message Date
geoffsee
9810f67af0 Refine tool call execution logic in chat-stream-provider to prevent duplicates, enhance retries for agentic-rag, and improve incremental processing, including test updates. 2025-07-31 18:26:42 -04:00
geoffsee
6c433581d3 Add Agentic RAG Tool integration with test cases
- Implemented intelligent retrieval-augmented generation system (`agentic_rag`) for dynamic decision-making on knowledge retrieval.
- Uses Milvus with a large dataset
- Added comprehensive test cases for query analysis, storage, retrieval, and error handling.
- Integrated `AgenticRAGTools` into `chat-stream-provider` enabling tool-based responses.
- Updated dependencies with `@zilliz/milvus2-sdk-node` for Milvus integration.
- Updated lander hero title.
2025-07-31 16:15:23 -04:00
geoffsee
ae6a6e4064 Refactor model filtering logic into reusable basicFilters function. 2025-07-31 10:10:35 -04:00
geoffsee
67483d08db Update model path handling logic for FireworksAI and refine supported model filtering. 2025-07-27 12:30:47 -04:00
geoffsee
53268b528d Update hero label for home route in renderer routes. 2025-07-27 09:32:46 -04:00
geoffsee
f9d5fc8282 Remove unused submodules and related scripts 2025-07-27 09:00:25 -04:00
30 changed files with 1884 additions and 1199 deletions

3
.gitignore vendored
View File

@@ -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
View File

@@ -1,3 +0,0 @@
[submodule "crates/yachtpit"]
path = crates/yachtpit
url = https://github.com/seemueller-io/yachtpit.git

157
bun.lock
View File

@@ -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

View File

@@ -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)",

View File

@@ -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",

View File

@@ -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 })));
}
}

View File

@@ -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();
});
});

View File

@@ -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({

View File

@@ -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,
};

View 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
}
});
});

View 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}`,
};
}
}

View File

@@ -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`,
});
}
}

View 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.' };
}
}

View File

@@ -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": {

View File

@@ -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();

View File

@@ -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>
)}

View File

@@ -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) {

View File

@@ -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}*/}

View File

@@ -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;

View File

@@ -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 (

View File

@@ -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'} />

View File

@@ -1,5 +1,5 @@
export default {
'/': { sidebarLabel: 'Home', heroLabel: 'gsio' },
'/': { sidebarLabel: 'Home', heroLabel: 'va-chat' },
'/connect': { sidebarLabel: 'Connect', heroLabel: 'connect' },
'/privacy-policy': {
sidebarLabel: '',

View File

@@ -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();

View File

@@ -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>;

View File

@@ -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> {}

View 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 });
}
},
},
};

View 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 };
}

View File

@@ -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;

View File

@@ -161,19 +161,29 @@ const ChatService = types
const openai = new OpenAI({ apiKey: provider.key, baseURL: provider.endpoint });
// 2a. 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')
);
}; // 2a. 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);
}),
);
// 2b. 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: {