diff --git a/package-lock.json b/package-lock.json index 3f43397..fde260d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,6 @@ "name": "expria-backend", "version": "1.0.0", "dependencies": { - "@google/genai": "^1.50.1", "@hono/node-server": "^1.13.7", "@hono/node-ws": "^1.3.0", "@supabase/supabase-js": "^2.49.4", @@ -542,29 +541,6 @@ "node": ">=18" } }, - "node_modules/@google/genai": { - "version": "1.50.1", - "resolved": "https://registry.npmjs.org/@google/genai/-/genai-1.50.1.tgz", - "integrity": "sha512-YbkX7H9+1Pt8wOt7DDREy8XSoiL6fRDzZQRyaVBarFf8MR3zHGqVdvM4cLbDXqPhxqvegZShgfxb8kw9C7YhAQ==", - "license": "Apache-2.0", - "dependencies": { - "google-auth-library": "^10.3.0", - "p-retry": "^4.6.2", - "protobufjs": "^7.5.4", - "ws": "^8.18.0" - }, - "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "@modelcontextprotocol/sdk": "^1.25.2" - }, - "peerDependenciesMeta": { - "@modelcontextprotocol/sdk": { - "optional": true - } - } - }, "node_modules/@hono/node-server": { "version": "1.19.14", "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.14.tgz", @@ -671,70 +647,6 @@ "node": ">=14" } }, - "node_modules/@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/fetch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", - "license": "BSD-3-Clause", - "dependencies": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" - } - }, - "node_modules/@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/inquire": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/pool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", - "license": "BSD-3-Clause" - }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.60.1", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.1.tgz", @@ -1205,12 +1117,6 @@ "undici-types": "~6.21.0" } }, - "node_modules/@types/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", - "license": "MIT" - }, "node_modules/@types/ws": { "version": "8.18.1", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", @@ -1369,15 +1275,6 @@ "url": "https://opencollective.com/vitest" } }, - "node_modules/agent-base": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, "node_modules/ansi-regex": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", @@ -1436,35 +1333,6 @@ "node": "18 || 20 || >=22" } }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/bignumber.js": { - "version": "9.3.1", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", - "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", - "license": "MIT", - "engines": { - "node": "*" - } - }, "node_modules/brace-expansion": { "version": "5.0.5", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", @@ -1478,12 +1346,6 @@ "node": "18 || 20 || >=22" } }, - "node_modules/buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", - "license": "BSD-3-Clause" - }, "node_modules/cac": { "version": "6.7.14", "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", @@ -1585,19 +1447,11 @@ "node": ">= 8" } }, - "node_modules/data-uri-to-buffer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", - "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -1654,15 +1508,6 @@ "dev": true, "license": "MIT" }, - "node_modules/ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "license": "Apache-2.0", - "dependencies": { - "safe-buffer": "^5.0.1" - } - }, "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", @@ -1769,12 +1614,6 @@ "node": ">=12.0.0" } }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "license": "MIT" - }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -1793,29 +1632,6 @@ } } }, - "node_modules/fetch-blob": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", - "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "paypal", - "url": "https://paypal.me/jimmywarting" - } - ], - "license": "MIT", - "dependencies": { - "node-domexception": "^1.0.0", - "web-streams-polyfill": "^3.0.3" - }, - "engines": { - "node": "^12.20 || >= 14.13" - } - }, "node_modules/foreground-child": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", @@ -1833,18 +1649,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/formdata-polyfill": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", - "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", - "license": "MIT", - "dependencies": { - "fetch-blob": "^3.1.2" - }, - "engines": { - "node": ">=12.20.0" - } - }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -1869,34 +1673,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/gaxios": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.4.tgz", - "integrity": "sha512-bTIgTsM2bWn3XklZISBTQX7ZSddGW+IO3bMdGaemHZ3tbqExMENHLx6kKZ/KlejgrMtj8q7wBItt51yegqalrA==", - "license": "Apache-2.0", - "dependencies": { - "extend": "^3.0.2", - "https-proxy-agent": "^7.0.1", - "node-fetch": "^3.3.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/gcp-metadata": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.2.tgz", - "integrity": "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==", - "license": "Apache-2.0", - "dependencies": { - "gaxios": "^7.0.0", - "google-logging-utils": "^1.0.0", - "json-bigint": "^1.0.0" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -2002,32 +1778,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/google-auth-library": { - "version": "10.6.2", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.6.2.tgz", - "integrity": "sha512-e27Z6EThmVNNvtYASwQxose/G57rkRuaRbQyxM2bvYLLX/GqWZ5chWq2EBoUchJbCc57eC9ArzO5wMsEmWftCw==", - "license": "Apache-2.0", - "dependencies": { - "base64-js": "^1.3.0", - "ecdsa-sig-formatter": "^1.0.11", - "gaxios": "^7.1.4", - "gcp-metadata": "8.1.2", - "google-logging-utils": "1.1.3", - "jws": "^4.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/google-logging-utils": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.1.3.tgz", - "integrity": "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==", - "license": "Apache-2.0", - "engines": { - "node": ">=14" - } - }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -2090,19 +1840,6 @@ "dev": true, "license": "MIT" }, - "node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/iceberg-js": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/iceberg-js/-/iceberg-js-0.8.1.tgz", @@ -2206,42 +1943,6 @@ "dev": true, "license": "MIT" }, - "node_modules/json-bigint": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", - "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", - "license": "MIT", - "dependencies": { - "bignumber.js": "^9.0.0" - } - }, - "node_modules/jwa": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", - "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", - "license": "MIT", - "dependencies": { - "buffer-equal-constant-time": "^1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/jws": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", - "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", - "license": "MIT", - "dependencies": { - "jwa": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/long": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", - "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", - "license": "Apache-2.0" - }, "node_modules/loupe": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", @@ -2333,6 +2034,7 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, "license": "MIT" }, "node_modules/nanoid": { @@ -2354,44 +2056,6 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/node-domexception": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", - "deprecated": "Use your platform's native DOMException instead", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "github", - "url": "https://paypal.me/jimmywarting" - } - ], - "license": "MIT", - "engines": { - "node": ">=10.5.0" - } - }, - "node_modules/node-fetch": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", - "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", - "license": "MIT", - "dependencies": { - "data-uri-to-buffer": "^4.0.0", - "fetch-blob": "^3.1.4", - "formdata-polyfill": "^4.0.10" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/node-fetch" - } - }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", @@ -2404,19 +2068,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/p-retry": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", - "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", - "license": "MIT", - "dependencies": { - "@types/retry": "0.12.0", - "retry": "^0.13.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/package-json-from-dist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", @@ -2517,30 +2168,6 @@ "node": "^10 || ^12 || >=14" } }, - "node_modules/protobufjs": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.5.tgz", - "integrity": "sha512-3wY1AxV+VBNW8Yypfd1yQY9pXnqTAN+KwQxL8iYm3/BjKYMNg4i0owhEe26PWDOMaIrzeeF98Lqd5NGz4omiIg==", - "hasInstallScript": true, - "license": "BSD-3-Clause", - "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" - }, - "engines": { - "node": ">=12.0.0" - } - }, "node_modules/qs": { "version": "6.15.1", "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.1.tgz", @@ -2566,15 +2193,6 @@ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, - "node_modules/retry": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, "node_modules/rollup": { "version": "4.60.1", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.1.tgz", @@ -2620,26 +2238,6 @@ "fsevents": "~2.3.2" } }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, "node_modules/semver": { "version": "7.7.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", @@ -3235,15 +2833,6 @@ } } }, - "node_modules/web-streams-polyfill": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", - "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index b2fad31..0e64c8b 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,6 @@ "test:coverage": "vitest run --coverage" }, "dependencies": { - "@google/genai": "^1.50.1", "@hono/node-server": "^1.13.7", "@hono/node-ws": "^1.3.0", "@supabase/supabase-js": "^2.49.4", diff --git a/src/lib/__tests__/geminiLive.test.ts b/src/lib/__tests__/geminiLive.test.ts index d8285c1..2247eb6 100644 --- a/src/lib/__tests__/geminiLive.test.ts +++ b/src/lib/__tests__/geminiLive.test.ts @@ -81,8 +81,13 @@ describe("openGeminiLiveSession (raw WS)", () => { ); expect(setup.setup.inputAudioTranscription).toEqual({}); expect(setup.setup.outputAudioTranscription).toEqual({}); - // ⚠ DEBUG : realtimeInputConfig (VAD) encore absent — prochain push. - expect(setup.setup.realtimeInputConfig).toBeUndefined(); + // VAD réintégré (Sprint 6d Bug 2) — cf. IMPLEMENTATION_T2_LIVE.md §3 step 7. + expect(setup.setup.realtimeInputConfig.automaticActivityDetection).toEqual({ + disabled: false, + startOfSpeechSensitivity: "START_SENSITIVITY_LOW", + endOfSpeechSensitivity: "END_SENSITIVITY_LOW", + silenceDurationMs: 2000, + }); }); it("forwarde un chunk audio client {type:'audio'} en realtimeInput vers Gemini", () => { diff --git a/src/lib/geminiLive.ts b/src/lib/geminiLive.ts index 5aef9d6..88fdc12 100644 --- a/src/lib/geminiLive.ts +++ b/src/lib/geminiLive.ts @@ -1,11 +1,11 @@ /** * geminiLive.ts — Sprint 6d (revert WS brut). * - * Le SDK `@google/genai` fermait la session sans setupComplete ni raison - * exploitable. On revient au WebSocket brut (package `ws`) qui était utilisé - * par `test-gemini-live.js` et permet de loguer précisément ce que Gemini - * répond. Config setup réduite au strict minimum tant que `setupComplete` - * n'est pas confirmé en prod ; on réintègre champs un par un ensuite. + * Historiquement, le proxy s'appuyait sur un SDK Gemini de haut niveau, mais + * celui-ci fermait la session sans setupComplete ni raison exploitable. On + * utilise désormais le WebSocket brut (package `ws`), qui permet de loguer + * précisément ce que Gemini répond et de maîtriser le contenu exact du setup + * frame (model, systemInstruction, transcriptions, VAD). * * Interface publique (consommée par `routes/t2live.ts`) — INCHANGÉE : * - openGeminiLiveSession(clientWs, opts) @@ -22,7 +22,7 @@ export const GEMINI_LIVE_URL = /** * Modèle Live cible. `gemini-2.0-flash-live-001` est le modèle Live confirmé * par la doc Google pour les clés API Developer + Express. Format `models/...` - * dans le setup frame natif (cf. `test-gemini-live.js`). + * requis dans le setup frame natif. */ export const GEMINI_LIVE_MODEL = "gemini-3.1-flash-live-preview"; @@ -41,22 +41,23 @@ export function buildT2SystemPrompt(input: { contexte: string; }): string { const { role, contexte } = input; - return `Tu es un examinateur du TCF Canada pour l'épreuve d'Expression Orale, Tâche 2 (dialogue interactif). -RÔLE : Tu incarnes ${role}. + return `RÔLE : Tu incarnes ${role}. CONTEXTE : ${contexte} + RÈGLES ABSOLUES : 1. Tu parles TOUJOURS en français naturel et courant, niveau B2-C1. -2. Tu NE corriges JAMAIS les erreurs du candidat. +2. Tu NE corriges JAMAIS les erreurs du candidat. Tu continues naturellement. 3. Tu attends que le candidat finisse sa question avant de répondre. -4. Tes réponses sont courtes (15 à 25 mots maximum). Pas de monologue. +4. Tes réponses sont courtes (15 à 25 mots maximum) pour laisser la place au dialogue. 5. Ne donne pas toutes les informations d'un coup. Force le candidat à poser des questions précises. -6. Si le candidat est vague, réponds brièvement sans chercher à compléter. -7. Ne pose JAMAIS de question après tes réponses. Tu réponds et tu te tais. La seule exception : si le candidat marque un long silence et semble avoir terminé, tu peux dire une seule fois « Si vous n'avez plus de questions, je vous souhaite une bonne journée » ou équivalent pour clore naturellement. -8. Ne prends jamais d'initiative pour orienter la conversation. -9. Tu peux être légèrement pressé ou hésitant pour rendre l'échange réaliste. -10. JAMAIS de listes ni de structure numérotée dans tes réponses. -11. Ne mentionne jamais que tu es une IA ou un modèle. -12. Tu ne prends PAS la parole en premier. Tu attends que le candidat s'adresse à toi.`; +6. Si le candidat est vague, réponds brièvement sans chercher à compléter — c'est à lui de reformuler. +7. STRICTE INTERDICTION DE POSER DES QUESTIONS. Tu n'as pas le droit d'utiliser de point d'interrogation. Tes phrases se terminent par un point. +8. SILENCE TOTAL APRÈS LA RÉPONSE. Réponds de manière factuelle, puis arrête-toi immédiatement. Ne suggère rien, ne relance pas, ne dis pas "et vous ?". +9. RÔLE PASSIF : tu es une source d'information inerte. Tu n'aides pas le candidat à tenir la conversation. S'il ne parle plus, le silence s'installe. +10. AUCUNE FORMULE DE POLITESSE DE FIN : bannis "n'hésitez pas", "j'espère que ça vous aide", "qu'en pensez-vous ?". +11. JAMAIS de listes ni de structure numérotée — parle naturellement. +12. Ne mentionne jamais que tu es une IA ou un modèle. +13. Tu ne prends PAS la parole en premier. Tu attends que le candidat s'adresse à toi.`; } /** @@ -212,11 +213,10 @@ function tryParseGeminiJson(data: unknown): GeminiServerMessage | null { } /** - * Construit le setup frame minimal Gemini Live (équivalent du mode - * `minimal` de `test-gemini-live.js`). Les champs `systemInstruction`, - * `inputAudioTranscription`, `outputAudioTranscription`, - * `realtimeInputConfig.automaticActivityDetection` sont volontairement - * retirés tant que `setupComplete` n'est pas confirmé en prod. + * Construit le setup frame Gemini Live : model + responseModalities AUDIO, + * systemInstruction (prompt T2), input/outputAudioTranscription, et + * realtimeInputConfig.automaticActivityDetection (VAD : START/END_SENSITIVITY_LOW, + * 2 s de silence avant que l'IA réponde — cf. IMPLEMENTATION_T2_LIVE.md §3). */ function buildSetupFrame(systemPrompt: string): string { return JSON.stringify({ @@ -230,6 +230,14 @@ function buildSetupFrame(systemPrompt: string): string { }, inputAudioTranscription: {}, outputAudioTranscription: {}, + realtimeInputConfig: { + automaticActivityDetection: { + disabled: false, + startOfSpeechSensitivity: "START_SENSITIVITY_LOW", + endOfSpeechSensitivity: "END_SENSITIVITY_LOW", + silenceDurationMs: 2000, + }, + }, }, }); } diff --git a/test-gemini-live.js b/test-gemini-live.js deleted file mode 100644 index 93771f0..0000000 --- a/test-gemini-live.js +++ /dev/null @@ -1,150 +0,0 @@ -// test-gemini-live.js — Sprint 6d : debug du setup frame Gemini Live via SDK. -// -// Usage : -// node --env-file=.env test-gemini-live.js minimal -// node --env-file=.env test-gemini-live.js +system -// node --env-file=.env test-gemini-live.js +transcription -// node --env-file=.env test-gemini-live.js +vad -// -// Chaque mode part du `minimal` qui doit fonctionner avec une clé Express -// Mode et ajoute UN champ. Si le mode reçoit `setupComplete` → le champ est -// accepté. Si l'ouverture échoue → c'est ce champ qui pose problème. -// -// Migration Sprint 6d : passage du WebSocket brut au SDK officiel -// `@google/genai` qui gère l'auth Express Mode automatiquement. - -import { - GoogleGenAI, - Modality, - StartSensitivity, - EndSensitivity, -} from "@google/genai"; - -const MODES = ["minimal", "+system", "+transcription", "+vad"]; -const mode = process.argv[2] ?? "minimal"; -if (!MODES.includes(mode)) { - console.error( - `❌ Mode inconnu : "${mode}". Modes valides : ${MODES.join(", ")}`, - ); - process.exit(1); -} - -const KEY = process.env.GEMINI_API_KEY; -if (!KEY) { - console.error("❌ GEMINI_API_KEY manquante dans l'env"); - process.exit(1); -} - -// Modèle par défaut Sprint 6d. Fallback documenté : `gemini-2.0-flash-live-001`. -const MODEL = "gemini-3.1-flash-live-preview"; - -const SAMPLE_PROMPT = - "Tu joues le rôle d'un bailleur. Tu réponds uniquement en français. " + - "Tu attends que ton interlocuteur s'adresse à toi avant de parler."; - -function buildConfig(mode) { - // Base minimal — équivalent au mode `minimal` qui doit fonctionner. - const config = { - responseModalities: [Modality.AUDIO], - }; - - if (mode === "+system") { - config.systemInstruction = SAMPLE_PROMPT; - } - - if (mode === "+transcription") { - config.inputAudioTranscription = {}; - config.outputAudioTranscription = {}; - } - - if (mode === "+vad") { - config.realtimeInputConfig = { - automaticActivityDetection: { - disabled: false, - startOfSpeechSensitivity: StartSensitivity.START_SENSITIVITY_LOW, - endOfSpeechSensitivity: EndSensitivity.END_SENSITIVITY_LOW, - silenceDurationMs: 2000, - }, - }; - } - - return config; -} - -const ai = new GoogleGenAI({ vertexai: true, apiKey: KEY }); - -console.log(`→ Mode : ${mode}`); -console.log(`→ Modèle : ${MODEL}`); -console.log("→ Connexion à Gemini Live (via SDK)…"); - -let setupCompleteReceived = false; -let resolved = false; - -const config = buildConfig(mode); -console.log("→ Config envoyée :"); -console.log(JSON.stringify(config, null, 2)); - -const timeoutId = setTimeout(() => { - if (!resolved) { - console.log("⏱ Timeout 15 s — pas de setupComplete reçu."); - process.exit(setupCompleteReceived ? 0 : 1); - } -}, 15000); - -try { - const session = await ai.live.connect({ - model: MODEL, - config, - callbacks: { - onopen: () => { - console.log("✅ Connexion ouverte"); - }, - onmessage: (msg) => { - // Compat : selon la version du SDK, setupComplete arrive soit comme - // propriété directe, soit dans serverContent. On loggue tout. - console.log("📨 Message reçu :", JSON.stringify(msg).slice(0, 600)); - if (msg.setupComplete || msg?.serverContent?.setupComplete) { - setupCompleteReceived = true; - resolved = true; - console.log( - `\n🎉 [${mode}] ACCEPTÉ — setupComplete reçu (modèle ${MODEL}).`, - ); - clearTimeout(timeoutId); - try { - session.close(); - } catch { - /* ignore */ - } - process.exit(0); - } - }, - onerror: (err) => { - console.log("❌ Erreur :", err?.message ?? err); - }, - onclose: (evt) => { - console.log( - `🔒 Fermeture${evt?.code ? ` — code ${evt.code}` : ""}${evt?.reason ? ` reason: ${evt.reason}` : ""}`, - ); - if (!setupCompleteReceived) { - console.log(`\n⚠ [${mode}] REJETÉ — fermeture avant setupComplete.`); - console.log( - "→ Le ou les champs ajoutés par ce mode ne sont pas acceptés.", - ); - } - resolved = true; - clearTimeout(timeoutId); - process.exit(setupCompleteReceived ? 0 : 1); - }, - }, - }); - // Conserver la session vivante jusqu'au timeout/setupComplete. - void session; -} catch (err) { - resolved = true; - clearTimeout(timeoutId); - console.log( - "❌ live.connect a échoué :", - err instanceof Error ? err.message : String(err), - ); - process.exit(1); -}