13 Commits

Author SHA1 Message Date
geoffsee
7ab1141540 sweet lander 2025-07-14 08:55:11 -04:00
geoffsee
5630a95f1a chat + maps + ai + tools 2025-07-08 13:53:36 -04:00
geoffsee
24351b0be7 Merge branch 'sweet-lander' into smart-lander 2025-07-08 11:50:31 -04:00
geoffsee
cd58a23942 Refactor chat-stream-provider to simplify tool structure. Optimize WeatherTool implementation with enriched function schema. 2025-07-08 11:47:46 -04:00
geoffsee
fbd696612a Enable tool-based message generation in chat-stream-provider and add BasicValueTool and WeatherTool.
Updated dependencies to latest versions in `bun.lock`. Modified development script in `package.json` to include watch mode.
2025-07-04 08:56:11 -04:00
geoffsee
b737ff09b3 mirror error handling behavior in cloudflare worker 2025-07-02 20:57:34 -04:00
geoffsee
c436ae1b62 add top level error handler to the router 2025-07-02 20:55:53 -04:00
geoffsee
5f6cb3d6c7 Optimize WASM handling and integrate service worker caching.
Removed unused pointer events in BevyScene, updated Vite config with Workbox for service worker caching, and adjusted file paths in generate-bevy-bundle.js. Added WASM size optimization to ensure smaller and efficient builds, skipping optimization for files below 30MB.
2025-07-02 20:25:58 -04:00
geoffsee
195d071c3c Add visible prop to toggle components and simplify conditional rendering 2025-07-01 15:43:17 -04:00
geoffsee
a996f115bc Add "Install App" button to the toolbar using react-use-pwa-install library 2025-07-01 15:21:54 -04:00
geoffsee
3bbd4243c5 **Integrate PWA asset generator and update favicon and manifest configuration** 2025-07-01 15:02:15 -04:00
geoffsee
944b956ffd - Refactor BevyScene to replace script injection with dynamic import.
- Update `NavItem` to provide fallback route for invalid `path`.
- Temporarily stub metric API endpoints with placeholders.
2025-07-01 12:28:44 -04:00
geoffsee
c3ea9ba599 * Introduced BevyScene React component in landing-component for rendering a 3D cockpit visualization.
* Included WebAssembly asset `yachtpit.js` for cockpit functionality.
* Added Bevy MIT license file.
* Implemented a service worker to cache assets locally instead of fetching them remotely.
* Added collapsible functionality to **Tweakbox** and included the `@chakra-ui/icons` dependency.
* Applied the `hidden` prop to the Tweakbox Heading for better accessibility.
* Refactored **Particles** component for improved performance, clarity, and maintainability.

  * Introduced helper functions for particle creation and count management.
  * Added responsive resizing with particle repositioning.
  * Optimized animation updates, including velocity adjustments for speed changes.
  * Ensured canvas size and particle state are cleanly managed on component unmount.
2025-07-01 11:54:40 -04:00
38 changed files with 558 additions and 1892 deletions

1
.gitignore vendored
View File

@@ -25,4 +25,3 @@ wrangler.dev.jsonc
/packages/client/public/pwa-192x192.png
/packages/client/public/pwa-512x512.png
packages/client/public/yachtpit_bg*
/project/

3
.gitmodules vendored Normal file
View File

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

157
bun.lock
View File

@@ -26,7 +26,6 @@
"@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",
@@ -440,8 +439,6 @@
"@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=="],
@@ -454,8 +451,6 @@
"@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=="],
@@ -544,10 +539,6 @@
"@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=="],
@@ -654,8 +645,6 @@
"@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=="],
@@ -678,8 +667,6 @@
"@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=="],
@@ -688,26 +675,6 @@
"@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=="],
@@ -832,8 +799,6 @@
"@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=="],
@@ -894,8 +859,6 @@
"@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=="],
@@ -1004,8 +967,6 @@
"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=="],
@@ -1018,8 +979,6 @@
"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=="],
@@ -1064,8 +1023,6 @@
"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=="],
@@ -1116,12 +1073,10 @@
"electron-to-chromium": ["electron-to-chromium@1.5.132", "", {}, "sha512-QgX9EBvWGmvSRa74zqfnG7+Eno0Ak0vftBll0Pt2/z5b3bEGYL6OUXLgKPtvx73dn3dvwrlyVkjPKRRlhLYTEg=="],
"emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
"emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
"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=="],
@@ -1202,8 +1157,6 @@
"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=="],
@@ -1220,8 +1173,6 @@
"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=="],
@@ -1246,14 +1197,10 @@
"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=="],
@@ -1476,8 +1423,6 @@
"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=="],
@@ -1510,8 +1455,6 @@
"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=="],
@@ -1520,15 +1463,11 @@
"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@9.1.2", "", {}, "sha512-ERJq3FOzJTxBbFjZ7iDs+NiK4VI9Wz+RdrrAB8dio1oV+YvdPzUEE4QNiT2VD51DkIbCYRUUzCRkssXCHqSnKQ=="],
"lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
"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=="],
@@ -1634,8 +1573,6 @@
"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=="],
@@ -1702,8 +1639,6 @@
"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=="],
@@ -1750,8 +1685,6 @@
"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=="],
@@ -1778,8 +1711,6 @@
"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=="],
@@ -1812,8 +1743,6 @@
"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=="],
@@ -1882,8 +1811,6 @@
"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=="],
@@ -1894,7 +1821,7 @@
"stoppable": ["stoppable@1.1.0", "", {}, "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw=="],
"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": ["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-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=="],
@@ -1906,8 +1833,6 @@
"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=="],
@@ -1944,8 +1869,6 @@
"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=="],
@@ -1980,8 +1903,6 @@
"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=="],
@@ -2064,8 +1985,6 @@
"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=="],
@@ -2104,10 +2023,6 @@
"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=="],
@@ -2146,7 +2061,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@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": ["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-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=="],
@@ -2158,16 +2073,10 @@
"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=="],
@@ -2186,8 +2095,6 @@
"@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=="],
@@ -2388,21 +2295,15 @@
"@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=="],
"@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/@types/bun": ["@types/bun@1.2.18", "", { "dependencies": { "bun-types": "1.2.18" } }, "sha512-Xf6RaWVheyemaThV0kUfaAUvCNokFr+bH8Jxp+tTZfx7dAPA8z9ePnP9S9+Vspzuxxx9JRAXhnyccRj3GyCMdQ=="],
"@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.19", "", { "dependencies": { "bun-types": "1.2.19" } }, "sha512-d9ZCmrH3CJ2uYKXQIUuZ/pUnTqIvLDS0SK7pFmbx8ma+ziH/FRMoAq5bYpRG7y+w1gl+HgyNZbtqgMq4W4e2Lg=="],
"@open-gsio/coordinators/@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/scripts/@types/bun": ["@types/bun@1.2.18", "", { "dependencies": { "bun-types": "1.2.18" } }, "sha512-Xf6RaWVheyemaThV0kUfaAUvCNokFr+bH8Jxp+tTZfx7dAPA8z9ePnP9S9+Vspzuxxx9JRAXhnyccRj3GyCMdQ=="],
"@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=="],
@@ -2432,8 +2333,6 @@
"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=="],
@@ -2502,6 +2401,10 @@
"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=="],
@@ -2528,6 +2431,12 @@
"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=="],
@@ -2548,8 +2457,6 @@
"@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=="],
@@ -2640,8 +2547,6 @@
"@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=="],
@@ -2658,8 +2563,6 @@
"@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=="],
@@ -2698,21 +2601,13 @@
"@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=="],
"@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/@types/bun/bun-types": ["bun-types@1.2.18", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-04+Eha5NP7Z0A9YgDAzMk5PHR16ZuLVa83b26kH5+cp1qZW4F6FmAURngE7INf4tKOvCE69vYvDEwoNl1tGiWw=="],
"@open-gsio/client/vite/fdir": ["fdir@6.4.6", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w=="],
@@ -2720,11 +2615,9 @@
"@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.19", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-uAOTaZSPuYsWIXRpj7o56Let0g/wjihKCkeRqUBhlLVM/Bt+Fj9xTo+LhC1OV1XDaGkz4hNC80et5xgy+9KTHQ=="],
"@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/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/scripts/@types/bun/bun-types": ["bun-types@1.2.18", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-04+Eha5NP7Z0A9YgDAzMk5PHR16ZuLVa83b26kH5+cp1qZW4F6FmAURngE7INf4tKOvCE69vYvDEwoNl1tGiWw=="],
"@open-gsio/server/vite/fdir": ["fdir@6.4.6", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w=="],
@@ -2750,8 +2643,6 @@
"@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=="],
@@ -2768,6 +2659,8 @@
"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=="],
@@ -2784,6 +2677,10 @@
"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=="],
@@ -2978,8 +2875,6 @@
"@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=="],

1
crates/yachtpit Submodule

Submodule crates/yachtpit added at 348f20641c

View File

@@ -10,6 +10,7 @@
],
"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,7 +40,6 @@
"@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

@@ -46,6 +46,7 @@ describe('AssistantSdk', () => {
expect(prompt).toContain('# Assistant Knowledge');
expect(prompt).toContain('### Date: ');
expect(prompt).toContain('### Web Host: ');
expect(prompt).toContain('### User Location: ');
expect(prompt).toContain('### Timezone: ');
});

View File

@@ -23,7 +23,7 @@ export class AssistantSdk {
return `# Assistant Knowledge
## Assistant Name
### open-gsio
### yachtpit-ai
## Current Context
### Date: ${currentDate} ${currentTime}
${maxTokens ? `### Max Response Length: ${maxTokens} tokens (maximum)` : ''}

View File

@@ -1,5 +1,5 @@
import { OpenAI } from 'openai';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { describe, it, expect, vi } from 'vitest';
import {
BaseChatProvider,
@@ -29,7 +29,7 @@ class TestChatProvider extends BaseChatProvider {
}
// Mock dependencies
vi.mock('../../chat-sdk/chat-sdk.ts', () => ({
vi.mock('../../lib/chat-sdk', () => ({
default: {
buildAssistantPrompt: vi.fn().mockReturnValue('Assistant prompt'),
buildMessageChain: vi.fn().mockReturnValue([
@@ -39,26 +39,6 @@ vi.mock('../../chat-sdk/chat-sdk.ts', () => ({
},
}));
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
@@ -70,616 +50,26 @@ describe('ChatStreamProvider', () => {
});
});
describe('BaseChatProvider - Model Tool Calling', () => {
let provider: TestChatProvider;
let mockOpenAI: any;
let dataCallback: any;
let commonParams: CommonProviderParams;
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,
};
});
describe('BaseChatProvider', () => {
it('should implement the ChatStreamProvider interface', () => {
// Create a concrete implementation
const provider = new TestChatProvider();
// Verify it implements the interface
expect(provider.handleStream).toBeInstanceOf(Function);
expect(provider.getOpenAIClient).toBeInstanceOf(Function);
expect(provider.getStreamParams).toBeInstanceOf(Function);
expect(provider.processChunk).toBeInstanceOf(Function);
});
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',
},
],
},
];
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();
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();
// Verify the abstract methods are implemented
expect(provider.getOpenAIClient).toBeDefined();
expect(provider.getStreamParams).toBeDefined();
expect(provider.processChunk).toBeDefined();
});
});

View File

@@ -1,7 +1,8 @@
import { OpenAI } from 'openai';
import ChatSdk from '../chat-sdk/chat-sdk.ts';
import { agenticRAG, AgenticRAGTools } from '../tools/agentic-rag.ts';
import { getWeather, WeatherTool } from '../tools/weather.ts';
import { yachtpitAi, YachtpitTools } from '../tools/yachtpit.ts';
import type { GenericEnv } from '../types';
export interface CommonProviderParams {
@@ -37,11 +38,14 @@ export abstract class BaseChatProvider implements ChatStreamProvider {
const client = this.getOpenAIClient(param);
const tools = [AgenticRAGTools];
const tools = [WeatherTool, YachtpitTools];
const callFunction = async (name, args) => {
if (name === 'agentic_rag') {
return agenticRAG(args);
if (name === 'get_weather') {
return getWeather(args.latitude, args.longitude);
}
if (name === 'ship_control') {
return yachtpitAi({ action: args.action, value: args.value });
}
};
@@ -50,13 +54,11 @@ 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 = '';
@@ -113,21 +115,6 @@ 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',
@@ -135,7 +122,7 @@ export abstract class BaseChatProvider implements ChatStreamProvider {
choices: [
{
delta: {
content: `\n\n🔧 Invoking ${toolCallsToExecute.length} tool${toolCallsToExecute.length > 1 ? 's' : ''}...\n`,
content: `\n\n🔧 Invoking ${toolCalls.length} tool${toolCalls.length > 1 ? 's' : ''}...\n`,
},
},
],
@@ -146,20 +133,15 @@ export abstract class BaseChatProvider implements ChatStreamProvider {
safeMessages.push({
role: 'assistant',
content: assistantMessage || null,
tool_calls: toolCallsToExecute,
tool_calls: toolCalls,
});
// Execute each tool call and add results
let needsMoreRetrieval = false;
for (const toolCall of toolCallsToExecute) {
for (const toolCall of toolCalls) {
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',
@@ -181,36 +163,6 @@ 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',
@@ -218,7 +170,7 @@ export abstract class BaseChatProvider implements ChatStreamProvider {
choices: [
{
delta: {
content: `\n ${JSON.stringify(result)}`,
content: `\n`,
},
},
],
@@ -229,7 +181,7 @@ export abstract class BaseChatProvider implements ChatStreamProvider {
safeMessages.push({
role: 'tool',
tool_call_id: toolCall.id,
content: JSON.stringify(result),
content: result?.toString() || '',
});
} catch (error) {
console.error(`Error executing tool ${name}:`, error);
@@ -257,10 +209,8 @@ export abstract class BaseChatProvider implements ChatStreamProvider {
}
}
// Only mark tools as executed if we don't need more retrieval
if (!needsMoreRetrieval) {
// Mark that tools have been executed to prevent repeated calls
toolsExecuted = true;
}
// Send feedback that tool execution is complete
dataCallback({
@@ -286,7 +236,7 @@ export abstract class BaseChatProvider implements ChatStreamProvider {
// Process chunk normally for non-tool-call responses
if (!chunk.choices[0]?.delta?.tool_calls) {
// console.log('after-tool-call-chunk', chunk);
console.log('after-tool-call-chunk', chunk);
const shouldBreak = await this.processChunk(chunk, dataCallback);
if (shouldBreak) {
conversationComplete = true;

View File

@@ -15,21 +15,10 @@ 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: finalModelIdentifier,
model: `${modelPrefix}${param.model}`,
messages: safeMessages,
stream: true,
};

View File

@@ -1,259 +0,0 @@
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

@@ -1,530 +0,0 @@
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

@@ -9,6 +9,7 @@
"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

@@ -0,0 +1,196 @@
import { execSync } 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...`);
execSync(`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

@@ -0,0 +1,35 @@
import { IconButton } from '@chakra-ui/react';
import { HardDriveDownload } from 'lucide-react';
import React from 'react';
import { toolbarButtonZIndex } from './toolbar/Toolbar.tsx';
function InstallButton() {
// const install = usePWAInstall();
const install = () => {
console.warn('this does not work in all browsers');
};
return (
<IconButton
aria-label="Install App"
title="Install App"
icon={<HardDriveDownload />}
size="md"
bg="transparent"
stroke="text.accent"
color="text.accent"
onClick={() => install}
_hover={{
bg: 'transparent',
svg: {
stroke: 'accent.secondary',
transition: 'stroke 0.3s ease-in-out',
},
}}
zIndex={toolbarButtonZIndex}
/>
);
}
export default InstallButton;

View File

@@ -171,7 +171,7 @@ const InputMenu: React.FC<{ isDisabled?: boolean }> = observer(({ isDisabled })
bg="background.tertiary"
color="text.primary"
onClick={() => {
clientChatStore.reset();
clientChatStore.setActiveConversation('conversation:new');
onClose();
}}
_hover={{ bg: 'rgba(0, 0, 0, 0.05)' }}

View File

@@ -49,7 +49,7 @@ const InputTextArea: React.FC<InputTextAreaProps> = observer(
color="text.primary"
borderRadius="20px"
border="none"
placeholder="Free my mind..."
placeholder="To Gilligan's island!"
_placeholder={{
color: 'gray.400',
textWrap: 'nowrap',

View File

@@ -9,7 +9,7 @@ export function formatConversationMarkdown(messages: Instance<typeof IMessage>[]
if (message.role === 'user') {
return `**You**: ${message.content}`;
} else if (message.role === 'assistant') {
return `**open-gsio**: ${message.content}`;
return `**yachtpit-ai**: ${message.content}`;
}
return '';
})

View File

@@ -51,7 +51,7 @@ const MessageBubble = observer(({ msg, scrollRef }) => {
const [isEditing, setIsEditing] = useState(false);
const [isHovered, setIsHovered] = useState(false);
const isUser = msg.role === 'user';
const senderName = isUser ? 'You' : 'open-gsio';
const senderName = isUser ? 'You' : 'yachtpit-ai';
const isLoading = !msg.content || !(msg.content.trim().length > 0);
const messageRef = useRef();

View File

@@ -104,7 +104,7 @@ describe('MessageBubble', () => {
it('should render assistant message correctly', () => {
render(<MessageBubble msg={mockAssistantMessage} scrollRef={mockScrollRef} />);
expect(screen.getByText('open-gsio')).toBeInTheDocument();
expect(screen.getByText('yachtpit-ai')).toBeInTheDocument();
expect(screen.getByTestId('message-content')).toHaveTextContent('Assistant response');
});

View File

@@ -1,7 +0,0 @@
import React, { useEffect, useState } from 'react';
function InstallButton() {
return <button onClick={handleInstall}>Install App</button>;
}
export default InstallButton;

View File

@@ -1,61 +0,0 @@
import { IconButton } from '@chakra-ui/react';
import { HardDriveDownload } from 'lucide-react';
import React, { useEffect, useState } from 'react';
import { toolbarButtonZIndex } from '../toolbar/Toolbar.tsx';
function InstallButton() {
const [deferredPrompt, setDeferredPrompt] = useState(null);
const [isInstalled, setIsInstalled] = useState(false);
useEffect(() => {
const handleBeforeInstallPrompt = e => {
// Prevent the default prompt
e.preventDefault();
setDeferredPrompt(e);
};
window.addEventListener('beforeinstallprompt', handleBeforeInstallPrompt);
return () => {
window.removeEventListener('beforeinstallprompt', handleBeforeInstallPrompt);
};
}, []);
const handleInstall = () => {
if (deferredPrompt) {
deferredPrompt.prompt();
deferredPrompt.userChoice.then(choiceResult => {
if (choiceResult.outcome === 'accepted') {
console.log('User accepted the installation prompt');
} else {
console.log('User dismissed the installation prompt');
}
});
setDeferredPrompt(null);
}
};
return (
<IconButton
aria-label="Install App"
title="Install App"
icon={<HardDriveDownload />}
size="md"
bg="transparent"
stroke="text.accent"
color="text.accent"
onClick={handleInstall}
_hover={{
bg: 'transparent',
svg: {
stroke: 'accent.secondary',
transition: 'stroke 0.3s ease-in-out',
},
}}
zIndex={toolbarButtonZIndex}
/>
);
}
export default InstallButton;

View File

@@ -0,0 +1,58 @@
import { Box, useBreakpointValue } from '@chakra-ui/react';
import React, { memo, useEffect, useMemo } from 'react';
export interface BevySceneProps {
speed?: number;
intensity?: number; // 0-1 when visible
glow?: boolean;
visible?: boolean; // NEW — defaults to true
}
const BevySceneInner: React.FC<BevySceneProps> = ({
speed = 1,
intensity = 1,
glow = false,
visible,
}) => {
const maxWidth = useBreakpointValue({ base: 640, md: 720 }, { ssr: true });
/* initialise once */
useEffect(() => {
let dispose: (() => void) | void;
(async () => {
const { default: init } = await import(/* webpackIgnore: true */ '/public/yachtpit.js');
dispose = await init(); // zero-arg, uses #yachtpit-canvas
})();
return () => {
if (typeof dispose === 'function') dispose();
};
}, []);
/* memoised styles */
const wrapperStyles = useMemo(
() => ({
position: 'absolute' as const,
inset: 0,
zIndex: 1,
maxWidth: maxWidth,
opacity: visible ? Math.min(Math.max(intensity, 0), 1) : 0,
filter: glow ? 'blur(1px)' : 'none',
transition: `opacity ${speed}s ease-in-out`,
display: visible ? 'block' : 'none', // optional: reclaim hit-testing entirely
}),
[visible, intensity, glow, speed],
);
return (
<Box as="div" sx={wrapperStyles}>
<canvas
id="yachtpit-canvas"
width={useBreakpointValue({ base: 640, md: 1280 }, { ssr: true })}
height={useBreakpointValue({ base: 360, md: 720 }, { ssr: true })}
aria-hidden
/>
</Box>
);
};
export const BevyScene = memo(BevySceneInner);

View File

@@ -3,13 +3,16 @@ import React, { useEffect, useState } from 'react';
import { useComponent } from '../contexts/ComponentContext.tsx';
// import { BevyScene } from './BevyScene.tsx';
import { BevyScene } from './BevyScene.tsx';
import Tweakbox from './Tweakbox.tsx';
export const LandingComponent: React.FC = () => {
const [speed, setSpeed] = useState(0.2);
const [intensity, setIntensity] = useState(0.99);
const [mapActive, setMapActive] = useState(false);
const [aiActive, setAiActive] = useState(true);
const [glow, setGlow] = useState(false);
const [bevyScene, setBevyScene] = useState(true);
const [mapActive, setMapActive] = useState(true);
const [aiActive, setAiActive] = useState(false);
const component = useComponent();
const { setEnabledComponent } = component;
@@ -24,8 +27,22 @@ export const LandingComponent: React.FC = () => {
}, []);
return (
<Box as="section" bg="background.primary" overflow="hidden">
<Box position="fixed" right={0} maxWidth="300px" minWidth="200px" zIndex={1000}>
<Box
as="section"
bg="background.primary"
w="100%"
h="100vh"
overflow="hidden"
position="relative"
>
<Box
position="fixed"
bottom="100x"
right="12px"
maxWidth="300px"
minWidth="200px"
zIndex={1000}
>
<Tweakbox
sliders={{
intensity: {
@@ -39,19 +56,26 @@ export const LandingComponent: React.FC = () => {
},
}}
switches={{
// GpsMap: {
// value: mapActive,
// onChange(enabled) {
// if (enabled) {
// setEnabledComponent('gpsmap');
// setAiActive(false);
// } else {
// setEnabledComponent('');
// }
// setMapActive(enabled);
// },
// label: 'GPS',
// },
bevyScene: {
value: bevyScene,
onChange(enabled) {
setBevyScene(enabled);
},
label: 'Instruments',
},
GpsMap: {
value: mapActive,
onChange(enabled) {
if (enabled) {
setEnabledComponent('gpsmap');
setAiActive(false);
} else {
setEnabledComponent('');
}
setMapActive(enabled);
},
label: 'Map',
},
AI: {
value: aiActive,
onChange(enabled) {
@@ -68,6 +92,7 @@ export const LandingComponent: React.FC = () => {
}}
/>
</Box>
<BevyScene speed={speed} intensity={intensity} glow={glow} visible={bevyScene} />
</Box>
);
};

View File

@@ -37,7 +37,7 @@ const key =
function Map(props: { visible: boolean }) {
return (
/* Full-screen wrapper — fills the viewport and becomes the positioning context */
<Box position={'absolute'} top={0} w="100vw" h={'100vh'} overflow="hidden">
<Box w="100%" h="100vh" position="relative" overflow="hidden">
{/* Button bar — absolutely positioned inside the wrapper */}
<MapNext mapboxPublicKey={atob(key)} />

View File

@@ -1,4 +1,4 @@
import { Box } from '@chakra-ui/react';
import { Box, Button, HStack, Input } from '@chakra-ui/react';
import { useCallback, useEffect, useMemo, useState } from 'react';
import Map, {
FullscreenControl,
@@ -118,10 +118,11 @@ Type '{ city: string; population: string; image: string; state: string; latitude
right: 0,
}}
>
<GeolocateControl position="top-left" style={{ marginTop: '6rem' }} />
<GeolocateControl position="top-left" />
<FullscreenControl position="top-left" />
<NavigationControl position="top-left" />
<ScaleControl position="top-left" />
{pins}
{popupInfo && (

View File

@@ -2,7 +2,7 @@ import { Flex } from '@chakra-ui/react';
import React from 'react';
import BuiltWithButton from '../BuiltWithButton';
import InstallButton from '../install/InstallButton.tsx';
import InstallButton from '../InstallButton.tsx';
import GithubButton from './GithubButton';
import SupportThisSiteButton from './SupportThisSiteButton';

View File

@@ -6,7 +6,7 @@ import { useIsMobile } from '../components/contexts/MobileContext';
function Content({ children }) {
const isMobile = useIsMobile();
return (
<Flex flexDirection="column" w="100%" h="100vh">
<Flex flexDirection="column" w="100%" h="100vh" p={!isMobile ? 4 : 1}>
{children}
</Flex>
);

View File

@@ -1,6 +1,4 @@
// runs before anything else
import { registerSW } from 'virtual:pwa-register';
import UserOptionsStore from '../stores/UserOptionsStore';
UserOptionsStore.initialize();
@@ -8,11 +6,7 @@ UserOptionsStore.initialize();
try {
const isLocal = window.location.hostname.includes('localhost');
if (!isLocal) {
if ('serviceWorker' in navigator) {
// && !/localhost/.test(window.location)) {
registerSW();
}
// navigator.serviceWorker.register('/service-worker.js');
navigator.serviceWorker.register('/service-worker.js');
} else {
(async () => {
await navigator.serviceWorker.getRegistrations().then(registrations => {

View File

@@ -1,4 +1,4 @@
import { Box } from '@chakra-ui/react';
import { Box, Grid, GridItem, Stack } from '@chakra-ui/react';
import React, { useEffect } from 'react';
import Chat from '../../components/chat/Chat.tsx';
@@ -22,15 +22,16 @@ export default function IndexPage() {
const component = useComponent();
return (
<Box height="100%" width="100%">
<Grid templateColumns="repeat(2, 1fr)" height="100%" width="100%" gap={0}>
<GridItem>
<LandingComponent />
</GridItem>
<GridItem p={2}>
<Box
display={component.enabledComponent === 'ai' ? undefined : 'none'}
width="100%"
height="100%"
overflowY="scroll"
padding={'unset'}
>
<Chat />
</Box>
@@ -38,10 +39,10 @@ export default function IndexPage() {
display={component.enabledComponent === 'gpsmap' ? undefined : 'none'}
width="100%"
height="100%"
padding={'unset'}
>
<ReactMap visible={component.enabledComponent === 'gpsmap'} />
</Box>
</Box>
</GridItem>
</Grid>
);
}

View File

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

View File

@@ -1,5 +1,5 @@
export const welcome_home_text = `
# open-gsio
# yachtpit-ai
---
<br/>

View File

@@ -22,6 +22,10 @@ const prebuildPlugin = () => ({
console.log('Generated robots.txt -> public/robots.txt');
child_process.execSync('bun run generate:fonts');
console.log('Copied fonts -> public/static/fonts');
child_process.execSync('bun run generate:bevy:bundle', {
stdio: 'inherit',
});
console.log('Bundled bevy app -> public/yachtpit.html');
}
},
});

View File

@@ -1,53 +0,0 @@
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

@@ -1,61 +0,0 @@
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,6 +1,111 @@
import { createServer } from './create-server.ts';
import { readdir } from 'node:fs/promises';
// creates a bun server with the itty router
const { server } = createServer();
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';
export default server;
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 });
}
},
},
};

View File

@@ -161,29 +161,19 @@ const ChatService = types
const openai = new OpenAI({ apiKey: provider.key, baseURL: provider.endpoint });
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
// 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) => {
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);
}),
models.filter(
(mdl: any) =>
!mdl.id.includes('whisper') &&
!mdl.id.includes('tts') &&
!mdl.id.includes('guard'),
),
);
// 2b. Retrieve metadata
@@ -328,8 +318,7 @@ const ChatService = types
);
}
if (message.includes('404')) {
console.log(message);
throw new ClientError(`Something went wrong, try again.`, 404, {});
throw new ClientError(`Something went wrong, try again.`, 413, {});
}
throw error;
}