From 7ecb73ac6e5c2b8f96b44fac6ce236a7bcdd7882 Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 12 Jun 2025 16:50:44 +0300 Subject: [PATCH 01/16] add constants --- .../kfu-m-24-1/sber_mobile/get-constants.js | 68 ++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/server/routers/kfu-m-24-1/sber_mobile/get-constants.js b/server/routers/kfu-m-24-1/sber_mobile/get-constants.js index fba7807..7c9e18d 100644 --- a/server/routers/kfu-m-24-1/sber_mobile/get-constants.js +++ b/server/routers/kfu-m-24-1/sber_mobile/get-constants.js @@ -18,8 +18,74 @@ const getSupabaseServiceKey = async () => { return data.features['sber_mobile'].SUPABASE_SERVICE_KEY.value; }; +const getGigaAuth = async () => { + const response = await fetch('https://admin.bro-js.ru/api/config/v1/dev'); + const data = await response.json(); + return data.features['sber_mobile'].GIGA_AUTH.value; +}; + +const getLangsmithApiKey = async () => { + const response = await fetch('https://admin.bro-js.ru/api/config/v1/dev'); + const data = await response.json(); + return data.features['sber_mobile'].LANGSMITH_API_KEY.value; +}; + +const getLangsmithEndpoint = async () => { + const response = await fetch('https://admin.bro-js.ru/api/config/v1/dev'); + const data = await response.json(); + return data.features['sber_mobile'].LANGSMITH_ENDPOINT.value; +}; + +const getLangsmithTracing = async () => { + const response = await fetch('https://admin.bro-js.ru/api/config/v1/dev'); + const data = await response.json(); + return data.features['sber_mobile'].LANGSMITH_TRACING.value; +}; + +const getLangsmithProject = async () => { + const response = await fetch('https://admin.bro-js.ru/api/config/v1/dev'); + const data = await response.json(); + return data.features['sber_mobile'].LANGSMITH_PROJECT.value; +}; + +const getTavilyApiKey = async () => { + const response = await fetch('https://admin.bro-js.ru/api/config/v1/dev'); + const data = await response.json(); + return data.features['sber_mobile'].TAVILY_API_KEY.value; +}; + +const getRagSupabaseServiceRoleKey = async () => { + const response = await fetch('https://admin.bro-js.ru/api/config/v1/dev'); + const data = await response.json(); + return data.features['sber_mobile'].RAG_SUPABASE_SERVICE_ROLE_KEY.value; +}; + +const getRagSupabaseUrl = async () => { + const response = await fetch('https://admin.bro-js.ru/api/config/v1/dev'); + const data = await response.json(); + return data.features['sber_mobile'].RAG_SUPABASE_URL.value; +}; + module.exports = { getSupabaseUrl, getSupabaseKey, getSupabaseServiceKey -}; \ No newline at end of file +}; + +// IIFE для установки переменных окружения +(async () => { + try { + process.env.GIGA_AUTH = await getGigaAuth(); + process.env.LANGSMITH_API_KEY = await getLangsmithApiKey(); + process.env.LANGSMITH_ENDPOINT = await getLangsmithEndpoint(); + process.env.LANGSMITH_TRACING = await getLangsmithTracing(); + process.env.LANGSMITH_PROJECT = await getLangsmithProject(); + process.env.TAVILY_API_KEY = await getTavilyApiKey(); + process.env.RAG_SUPABASE_SERVICE_ROLE_KEY = await getRagSupabaseServiceRoleKey(); + process.env.RAG_SUPABASE_URL = await getRagSupabaseUrl(); + + console.log('Environment variables loaded successfully'); + } catch (error) { + console.error('Error loading environment variables:', error); + } +})(); \ No newline at end of file From ec6b30e2209ce97167429b79e99f65ba3ed6e897 Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 12 Jun 2025 20:58:54 +0300 Subject: [PATCH 02/16] add support ai-agent --- package-lock.json | 2357 ++++++++++++++++- package.json | 7 +- .../kfu-m-24-1/sber_mobile/get-constants.js | 2 - .../sber_mobile/support-ai-agent/README.md | 63 + .../sber_mobile/support-ai-agent/gigachat.ts | 18 + .../support-ai-agent/support-agent.ts | 131 + .../kfu-m-24-1/sber_mobile/supportApi.js | 149 +- 7 files changed, 2611 insertions(+), 116 deletions(-) create mode 100644 server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/README.md create mode 100644 server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/gigachat.ts create mode 100644 server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/support-agent.ts diff --git a/package-lock.json b/package-lock.json index 57a84e7..0205535 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,9 +7,11 @@ "": { "name": "multi-stub", "version": "2.0.0", - "hasInstallScript": true, "license": "MIT", "dependencies": { + "@langchain/community": "^0.3.41", + "@langchain/core": "^0.3.46", + "@langchain/langgraph": "^0.2.65", "@supabase/supabase-js": "^2.49.4", "ai": "^4.1.13", "axios": "^1.7.7", @@ -23,8 +25,10 @@ "express": "5.0.1", "express-jwt": "^8.5.1", "express-session": "^1.18.1", + "gigachat": "^0.0.14", "jsdom": "^25.0.1", "jsonwebtoken": "^9.0.2", + "langchain-gigachat": "^0.0.11", "mongodb": "^6.12.0", "mongoose": "^8.9.2", "mongoose-sequence": "^6.0.1", @@ -33,7 +37,7 @@ "pbkdf2-password": "^1.2.1", "rotating-file-stream": "^3.2.5", "socket.io": "^4.8.1", - "uuid": "^11.0.3" + "zod": "^3.24.3" }, "devDependencies": { "@eslint/js": "^9.17.0", @@ -146,6 +150,60 @@ "node": ">=6.0.0" } }, + "node_modules/@anthropic-ai/sdk": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.27.3.tgz", + "integrity": "sha512-IjLt0gd3L4jlOfilxVXTifn42FnVffMgDC04RJK1KDZpmkBWLv0XC92MVVmkxrFZNS/7l3xWgP/I3nqtX1sQHw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "^18.11.18", + "@types/node-fetch": "^2.6.4", + "abort-controller": "^3.0.0", + "agentkeepalive": "^4.2.1", + "form-data-encoder": "1.7.2", + "formdata-node": "^4.3.2", + "node-fetch": "^2.6.7" + } + }, + "node_modules/@anthropic-ai/sdk/node_modules/@types/node": { + "version": "18.19.111", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.111.tgz", + "integrity": "sha512-90sGdgA+QLJr1F9X79tQuEut0gEYIfkX9pydI4XGRgvFo9g2JWswefI+WUSUHPYVBHYSEfTEqBxA5hQvAZB3Mw==", + "license": "MIT", + "peer": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@anthropic-ai/sdk/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "peer": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/@anthropic-ai/sdk/node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "license": "MIT", + "peer": true + }, "node_modules/@asamuzakjp/css-color": { "version": "2.8.3", "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-2.8.3.tgz", @@ -856,6 +914,86 @@ "dev": true, "license": "MIT" }, + "node_modules/@browserbasehq/sdk": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@browserbasehq/sdk/-/sdk-2.6.0.tgz", + "integrity": "sha512-83iXP5D7xMm8Wyn66TUaUrgoByCmAJuoMoZQI3sGg3JAiMlTfnCIMqyVBoNSaItaPIkaCnrsj6LiusmXV2X9YA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@types/node": "^18.11.18", + "@types/node-fetch": "^2.6.4", + "abort-controller": "^3.0.0", + "agentkeepalive": "^4.2.1", + "form-data-encoder": "1.7.2", + "formdata-node": "^4.3.2", + "node-fetch": "^2.6.7" + } + }, + "node_modules/@browserbasehq/sdk/node_modules/@types/node": { + "version": "18.19.111", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.111.tgz", + "integrity": "sha512-90sGdgA+QLJr1F9X79tQuEut0gEYIfkX9pydI4XGRgvFo9g2JWswefI+WUSUHPYVBHYSEfTEqBxA5hQvAZB3Mw==", + "license": "MIT", + "peer": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@browserbasehq/sdk/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "peer": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/@browserbasehq/sdk/node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "license": "MIT", + "peer": true + }, + "node_modules/@browserbasehq/stagehand": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/@browserbasehq/stagehand/-/stagehand-1.14.0.tgz", + "integrity": "sha512-Hi/EzgMFWz+FKyepxHTrqfTPjpsuBS4zRy3e9sbMpBgLPv+9c0R+YZEvS7Bw4mTS66QtvvURRT6zgDGFotthVQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@anthropic-ai/sdk": "^0.27.3", + "@browserbasehq/sdk": "^2.0.0", + "ws": "^8.18.0", + "zod-to-json-schema": "^3.23.5" + }, + "peerDependencies": { + "@playwright/test": "^1.42.1", + "deepmerge": "^4.3.1", + "dotenv": "^16.4.5", + "openai": "^4.62.1", + "zod": "^3.23.8" + } + }, + "node_modules/@cfworker/json-schema": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@cfworker/json-schema/-/json-schema-4.1.1.tgz", + "integrity": "sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==", + "license": "MIT" + }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -1178,6 +1316,46 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@graphql-typed-document-node/core": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz", + "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==", + "license": "MIT", + "peerDependencies": { + "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/@grpc/grpc-js": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.13.4.tgz", + "integrity": "sha512-GsFaMXCkMqkKIvwCQjCrwH+GHbPKBjhwo/8ZuUkWHqbI73Kky9I+pQltrlT0+MWpedCoosda53lgjYfyEPgxBg==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/proto-loader": "^0.7.13", + "@js-sdsl/ordered-map": "^4.4.2" + }, + "engines": { + "node": ">=12.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.15", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.15.tgz", + "integrity": "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==", + "license": "Apache-2.0", + "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" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -1244,6 +1422,38 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@ibm-cloud/watsonx-ai": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/@ibm-cloud/watsonx-ai/-/watsonx-ai-1.6.7.tgz", + "integrity": "sha512-lyHG5pjIINc+3fVbodD+ui0kvs7xk6TRAPJasK+8d8+j/FXS6TNsNGjvP79nfQJPTTYYy9IXxFc/3Z4jAqfD7w==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@types/node": "^18.0.0", + "extend": "3.0.2", + "ibm-cloud-sdk-core": "^5.3.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@ibm-cloud/watsonx-ai/node_modules/@types/node": { + "version": "18.19.111", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.111.tgz", + "integrity": "sha512-90sGdgA+QLJr1F9X79tQuEut0gEYIfkX9pydI4XGRgvFo9g2JWswefI+WUSUHPYVBHYSEfTEqBxA5hQvAZB3Mw==", + "license": "MIT", + "peer": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@ibm-cloud/watsonx-ai/node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "license": "MIT", + "peer": true + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -1706,6 +1916,786 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/@langchain/community": { + "version": "0.3.46", + "resolved": "https://registry.npmjs.org/@langchain/community/-/community-0.3.46.tgz", + "integrity": "sha512-loix9LkoNcn1gQlVCopmrJW9TmgZb+YpZw7nkFzXT6ozR8ZDh1XlFq1ymR5gTFtdNzF0neK2oJtE9iEl1lm7Dw==", + "license": "MIT", + "dependencies": { + "@langchain/openai": ">=0.2.0 <0.6.0", + "@langchain/weaviate": "^0.2.0", + "binary-extensions": "^2.2.0", + "expr-eval": "^2.0.2", + "flat": "^5.0.2", + "js-yaml": "^4.1.0", + "langchain": ">=0.2.3 <0.3.0 || >=0.3.4 <0.4.0", + "langsmith": "^0.3.29", + "uuid": "^10.0.0", + "zod": "^3.25.32" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@arcjet/redact": "^v1.0.0-alpha.23", + "@aws-crypto/sha256-js": "^5.0.0", + "@aws-sdk/client-bedrock-agent-runtime": "^3.749.0", + "@aws-sdk/client-bedrock-runtime": "^3.749.0", + "@aws-sdk/client-dynamodb": "^3.749.0", + "@aws-sdk/client-kendra": "^3.749.0", + "@aws-sdk/client-lambda": "^3.749.0", + "@aws-sdk/client-s3": "^3.749.0", + "@aws-sdk/client-sagemaker-runtime": "^3.749.0", + "@aws-sdk/client-sfn": "^3.749.0", + "@aws-sdk/credential-provider-node": "^3.388.0", + "@azure/search-documents": "^12.0.0", + "@azure/storage-blob": "^12.15.0", + "@browserbasehq/sdk": "*", + "@browserbasehq/stagehand": "^1.0.0", + "@clickhouse/client": "^0.2.5", + "@cloudflare/ai": "*", + "@datastax/astra-db-ts": "^1.0.0", + "@elastic/elasticsearch": "^8.4.0", + "@getmetal/metal-sdk": "*", + "@getzep/zep-cloud": "^1.0.6", + "@getzep/zep-js": "^0.9.0", + "@gomomento/sdk": "^1.51.1", + "@gomomento/sdk-core": "^1.51.1", + "@google-ai/generativelanguage": "*", + "@google-cloud/storage": "^6.10.1 || ^7.7.0", + "@gradientai/nodejs-sdk": "^1.2.0", + "@huggingface/inference": "^2.6.4", + "@huggingface/transformers": "^3.2.3", + "@ibm-cloud/watsonx-ai": "*", + "@lancedb/lancedb": "^0.12.0", + "@langchain/core": ">=0.3.58 <0.4.0", + "@layerup/layerup-security": "^1.5.12", + "@libsql/client": "^0.14.0", + "@mendable/firecrawl-js": "^1.4.3", + "@mlc-ai/web-llm": "*", + "@mozilla/readability": "*", + "@neondatabase/serverless": "*", + "@notionhq/client": "^2.2.10", + "@opensearch-project/opensearch": "*", + "@pinecone-database/pinecone": "*", + "@planetscale/database": "^1.8.0", + "@premai/prem-sdk": "^0.3.25", + "@qdrant/js-client-rest": "^1.8.2", + "@raycast/api": "^1.55.2", + "@rockset/client": "^0.9.1", + "@smithy/eventstream-codec": "^2.0.5", + "@smithy/protocol-http": "^3.0.6", + "@smithy/signature-v4": "^2.0.10", + "@smithy/util-utf8": "^2.0.0", + "@spider-cloud/spider-client": "^0.0.21", + "@supabase/supabase-js": "^2.45.0", + "@tensorflow-models/universal-sentence-encoder": "*", + "@tensorflow/tfjs-converter": "*", + "@tensorflow/tfjs-core": "*", + "@upstash/ratelimit": "^1.1.3 || ^2.0.3", + "@upstash/redis": "^1.20.6", + "@upstash/vector": "^1.1.1", + "@vercel/kv": "*", + "@vercel/postgres": "*", + "@writerai/writer-sdk": "^0.40.2", + "@xata.io/client": "^0.28.0", + "@zilliz/milvus2-sdk-node": ">=2.3.5", + "apify-client": "^2.7.1", + "assemblyai": "^4.6.0", + "azion": "^1.11.1", + "better-sqlite3": ">=9.4.0 <12.0.0", + "cassandra-driver": "^4.7.2", + "cborg": "^4.1.1", + "cheerio": "^1.0.0-rc.12", + "chromadb": "*", + "closevector-common": "0.1.3", + "closevector-node": "0.1.6", + "closevector-web": "0.1.6", + "cohere-ai": "*", + "convex": "^1.3.1", + "crypto-js": "^4.2.0", + "d3-dsv": "^2.0.0", + "discord.js": "^14.14.1", + "dria": "^0.0.3", + "duck-duck-scrape": "^2.2.5", + "epub2": "^3.0.1", + "fast-xml-parser": "*", + "firebase-admin": "^11.9.0 || ^12.0.0", + "google-auth-library": "*", + "googleapis": "*", + "hnswlib-node": "^3.0.0", + "html-to-text": "^9.0.5", + "ibm-cloud-sdk-core": "*", + "ignore": "^5.2.0", + "interface-datastore": "^8.2.11", + "ioredis": "^5.3.2", + "it-all": "^3.0.4", + "jsdom": "*", + "jsonwebtoken": "^9.0.2", + "llmonitor": "^0.5.9", + "lodash": "^4.17.21", + "lunary": "^0.7.10", + "mammoth": "^1.6.0", + "mariadb": "^3.4.0", + "mem0ai": "^2.1.8", + "mongodb": ">=5.2.0", + "mysql2": "^3.9.8", + "neo4j-driver": "*", + "notion-to-md": "^3.1.0", + "officeparser": "^4.0.4", + "openai": "*", + "pdf-parse": "1.1.1", + "pg": "^8.11.0", + "pg-copy-streams": "^6.0.5", + "pickleparser": "^0.2.1", + "playwright": "^1.32.1", + "portkey-ai": "^0.1.11", + "puppeteer": "*", + "pyodide": ">=0.24.1 <0.27.0", + "redis": "*", + "replicate": "*", + "sonix-speech-recognition": "^2.1.1", + "srt-parser-2": "^1.2.3", + "typeorm": "^0.3.20", + "typesense": "^1.5.3", + "usearch": "^1.1.1", + "voy-search": "0.6.2", + "weaviate-client": "^3.5.2", + "web-auth-library": "^1.0.3", + "word-extractor": "*", + "ws": "^8.14.2", + "youtubei.js": "*" + }, + "peerDependenciesMeta": { + "@arcjet/redact": { + "optional": true + }, + "@aws-crypto/sha256-js": { + "optional": true + }, + "@aws-sdk/client-bedrock-agent-runtime": { + "optional": true + }, + "@aws-sdk/client-bedrock-runtime": { + "optional": true + }, + "@aws-sdk/client-dynamodb": { + "optional": true + }, + "@aws-sdk/client-kendra": { + "optional": true + }, + "@aws-sdk/client-lambda": { + "optional": true + }, + "@aws-sdk/client-s3": { + "optional": true + }, + "@aws-sdk/client-sagemaker-runtime": { + "optional": true + }, + "@aws-sdk/client-sfn": { + "optional": true + }, + "@aws-sdk/credential-provider-node": { + "optional": true + }, + "@aws-sdk/dsql-signer": { + "optional": true + }, + "@azure/search-documents": { + "optional": true + }, + "@azure/storage-blob": { + "optional": true + }, + "@browserbasehq/sdk": { + "optional": true + }, + "@clickhouse/client": { + "optional": true + }, + "@cloudflare/ai": { + "optional": true + }, + "@datastax/astra-db-ts": { + "optional": true + }, + "@elastic/elasticsearch": { + "optional": true + }, + "@getmetal/metal-sdk": { + "optional": true + }, + "@getzep/zep-cloud": { + "optional": true + }, + "@getzep/zep-js": { + "optional": true + }, + "@gomomento/sdk": { + "optional": true + }, + "@gomomento/sdk-core": { + "optional": true + }, + "@google-ai/generativelanguage": { + "optional": true + }, + "@google-cloud/storage": { + "optional": true + }, + "@gradientai/nodejs-sdk": { + "optional": true + }, + "@huggingface/inference": { + "optional": true + }, + "@huggingface/transformers": { + "optional": true + }, + "@lancedb/lancedb": { + "optional": true + }, + "@layerup/layerup-security": { + "optional": true + }, + "@libsql/client": { + "optional": true + }, + "@mendable/firecrawl-js": { + "optional": true + }, + "@mlc-ai/web-llm": { + "optional": true + }, + "@mozilla/readability": { + "optional": true + }, + "@neondatabase/serverless": { + "optional": true + }, + "@notionhq/client": { + "optional": true + }, + "@opensearch-project/opensearch": { + "optional": true + }, + "@pinecone-database/pinecone": { + "optional": true + }, + "@planetscale/database": { + "optional": true + }, + "@premai/prem-sdk": { + "optional": true + }, + "@qdrant/js-client-rest": { + "optional": true + }, + "@raycast/api": { + "optional": true + }, + "@rockset/client": { + "optional": true + }, + "@smithy/eventstream-codec": { + "optional": true + }, + "@smithy/protocol-http": { + "optional": true + }, + "@smithy/signature-v4": { + "optional": true + }, + "@smithy/util-utf8": { + "optional": true + }, + "@spider-cloud/spider-client": { + "optional": true + }, + "@supabase/supabase-js": { + "optional": true + }, + "@tensorflow-models/universal-sentence-encoder": { + "optional": true + }, + "@tensorflow/tfjs-converter": { + "optional": true + }, + "@tensorflow/tfjs-core": { + "optional": true + }, + "@upstash/ratelimit": { + "optional": true + }, + "@upstash/redis": { + "optional": true + }, + "@upstash/vector": { + "optional": true + }, + "@vercel/kv": { + "optional": true + }, + "@vercel/postgres": { + "optional": true + }, + "@writerai/writer-sdk": { + "optional": true + }, + "@xata.io/client": { + "optional": true + }, + "@zilliz/milvus2-sdk-node": { + "optional": true + }, + "apify-client": { + "optional": true + }, + "assemblyai": { + "optional": true + }, + "azion": { + "optional": true + }, + "better-sqlite3": { + "optional": true + }, + "cassandra-driver": { + "optional": true + }, + "cborg": { + "optional": true + }, + "cheerio": { + "optional": true + }, + "chromadb": { + "optional": true + }, + "closevector-common": { + "optional": true + }, + "closevector-node": { + "optional": true + }, + "closevector-web": { + "optional": true + }, + "cohere-ai": { + "optional": true + }, + "convex": { + "optional": true + }, + "crypto-js": { + "optional": true + }, + "d3-dsv": { + "optional": true + }, + "discord.js": { + "optional": true + }, + "dria": { + "optional": true + }, + "duck-duck-scrape": { + "optional": true + }, + "epub2": { + "optional": true + }, + "fast-xml-parser": { + "optional": true + }, + "firebase-admin": { + "optional": true + }, + "google-auth-library": { + "optional": true + }, + "googleapis": { + "optional": true + }, + "hnswlib-node": { + "optional": true + }, + "html-to-text": { + "optional": true + }, + "ignore": { + "optional": true + }, + "interface-datastore": { + "optional": true + }, + "ioredis": { + "optional": true + }, + "it-all": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "jsonwebtoken": { + "optional": true + }, + "llmonitor": { + "optional": true + }, + "lodash": { + "optional": true + }, + "lunary": { + "optional": true + }, + "mammoth": { + "optional": true + }, + "mariadb": { + "optional": true + }, + "mem0ai": { + "optional": true + }, + "mongodb": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "neo4j-driver": { + "optional": true + }, + "notion-to-md": { + "optional": true + }, + "officeparser": { + "optional": true + }, + "pdf-parse": { + "optional": true + }, + "pg": { + "optional": true + }, + "pg-copy-streams": { + "optional": true + }, + "pickleparser": { + "optional": true + }, + "playwright": { + "optional": true + }, + "portkey-ai": { + "optional": true + }, + "puppeteer": { + "optional": true + }, + "pyodide": { + "optional": true + }, + "redis": { + "optional": true + }, + "replicate": { + "optional": true + }, + "sonix-speech-recognition": { + "optional": true + }, + "srt-parser-2": { + "optional": true + }, + "typeorm": { + "optional": true + }, + "typesense": { + "optional": true + }, + "usearch": { + "optional": true + }, + "voy-search": { + "optional": true + }, + "weaviate-client": { + "optional": true + }, + "web-auth-library": { + "optional": true + }, + "word-extractor": { + "optional": true + }, + "ws": { + "optional": true + }, + "youtubei.js": { + "optional": true + } + } + }, + "node_modules/@langchain/community/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@langchain/core": { + "version": "0.3.58", + "resolved": "https://registry.npmjs.org/@langchain/core/-/core-0.3.58.tgz", + "integrity": "sha512-HLkOtVofgBHefaUae/+2fLNkpMLzEjHSavTmUF0YC7bDa5NPIZGlP80CGrSFXAeJ+WCPd8rIK8K/p6AW94inUQ==", + "license": "MIT", + "dependencies": { + "@cfworker/json-schema": "^4.0.2", + "ansi-styles": "^5.0.0", + "camelcase": "6", + "decamelize": "1.2.0", + "js-tiktoken": "^1.0.12", + "langsmith": "^0.3.29", + "mustache": "^4.2.0", + "p-queue": "^6.6.2", + "p-retry": "4", + "uuid": "^10.0.0", + "zod": "^3.25.32", + "zod-to-json-schema": "^3.22.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@langchain/core/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@langchain/core/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@langchain/core/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@langchain/langgraph": { + "version": "0.2.74", + "resolved": "https://registry.npmjs.org/@langchain/langgraph/-/langgraph-0.2.74.tgz", + "integrity": "sha512-oHpEi5sTZTPaeZX1UnzfM2OAJ21QGQrwReTV6+QnX7h8nDCBzhtipAw1cK616S+X8zpcVOjgOtJuaJhXa4mN8w==", + "license": "MIT", + "dependencies": { + "@langchain/langgraph-checkpoint": "~0.0.17", + "@langchain/langgraph-sdk": "~0.0.32", + "uuid": "^10.0.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@langchain/core": ">=0.2.36 <0.3.0 || >=0.3.40 < 0.4.0", + "zod-to-json-schema": "^3.x" + }, + "peerDependenciesMeta": { + "zod-to-json-schema": { + "optional": true + } + } + }, + "node_modules/@langchain/langgraph-checkpoint": { + "version": "0.0.18", + "resolved": "https://registry.npmjs.org/@langchain/langgraph-checkpoint/-/langgraph-checkpoint-0.0.18.tgz", + "integrity": "sha512-IS7zJj36VgY+4pf8ZjsVuUWef7oTwt1y9ylvwu0aLuOn1d0fg05Om9DLm3v2GZ2Df6bhLV1kfWAM0IAl9O5rQQ==", + "license": "MIT", + "dependencies": { + "uuid": "^10.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@langchain/core": ">=0.2.31 <0.4.0" + } + }, + "node_modules/@langchain/langgraph-checkpoint/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@langchain/langgraph-sdk": { + "version": "0.0.84", + "resolved": "https://registry.npmjs.org/@langchain/langgraph-sdk/-/langgraph-sdk-0.0.84.tgz", + "integrity": "sha512-l0PFQyJ+6m6aclORNPPWlcRwgKcXVXsPaJCbCUYFABR3yf4cOpsjhUNR0cJ7+2cS400oieHjGRdGGyO/hbSjhg==", + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.15", + "p-queue": "^6.6.2", + "p-retry": "4", + "uuid": "^9.0.0" + }, + "peerDependencies": { + "@langchain/core": ">=0.2.31 <0.4.0", + "react": "^18 || ^19" + }, + "peerDependenciesMeta": { + "@langchain/core": { + "optional": true + }, + "react": { + "optional": true + } + } + }, + "node_modules/@langchain/langgraph-sdk/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@langchain/langgraph/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@langchain/openai": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/@langchain/openai/-/openai-0.5.13.tgz", + "integrity": "sha512-t5UsO7XYE+DBQlXQ21QK74Y+LH4It20wnENrmueNvxIWTn0nHDIGVmO6wo4rJxbmOOPRQ4l/oAxGRnYU8B8v6w==", + "license": "MIT", + "dependencies": { + "js-tiktoken": "^1.0.12", + "openai": "^4.96.0", + "zod": "3.25.32" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@langchain/core": ">=0.3.58 <0.4.0" + } + }, + "node_modules/@langchain/openai/node_modules/zod": { + "version": "3.25.32", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.32.tgz", + "integrity": "sha512-OSm2xTIRfW8CV5/QKgngwmQW/8aPfGdaQFlrGoErlgg/Epm7cjb6K6VEyExfe65a3VybUOnu381edLb0dfJl0g==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@langchain/textsplitters": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@langchain/textsplitters/-/textsplitters-0.1.0.tgz", + "integrity": "sha512-djI4uw9rlkAb5iMhtLED+xJebDdAG935AdP4eRTB02R7OB/act55Bj9wsskhZsvuyQRpO4O1wQOp85s6T6GWmw==", + "license": "MIT", + "dependencies": { + "js-tiktoken": "^1.0.12" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@langchain/core": ">=0.2.21 <0.4.0" + } + }, + "node_modules/@langchain/weaviate": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@langchain/weaviate/-/weaviate-0.2.0.tgz", + "integrity": "sha512-gAtTCxSllR8Z92qAuRn2ir0cop241VmftQHQN+UYtTeoLge8hvZT5k0j55PDVaXTVpjx0ecx6DKv5I/wLRQI+A==", + "license": "MIT", + "dependencies": { + "uuid": "^10.0.0", + "weaviate-client": "^3.5.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@langchain/core": ">=0.2.21 <0.4.0" + } + }, + "node_modules/@langchain/weaviate/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@mapbox/node-pre-gyp": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", @@ -1725,6 +2715,26 @@ "node-pre-gyp": "bin/node-pre-gyp" } }, + "node_modules/@mapbox/node-pre-gyp/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/@mongodb-js/saslprep": { "version": "1.1.9", "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.9.tgz", @@ -1743,6 +2753,86 @@ "node": ">=8.0.0" } }, + "node_modules/@playwright/test": { + "version": "1.53.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.53.0.tgz", + "integrity": "sha512-15hjKreZDcp7t6TL/7jkAo6Df5STZN09jGiv5dbP9A6vMVncXRqE7/B2SncsyOwrkZRBH2i6/TPOL8BVmm3c7w==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "playwright": "1.53.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "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/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -1826,27 +2916,6 @@ "ws": "^8.18.0" } }, - "node_modules/@supabase/realtime-js/node_modules/ws": { - "version": "8.18.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz", - "integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, "node_modules/@supabase/storage-js": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.7.1.tgz", @@ -1870,6 +2939,13 @@ "@supabase/storage-js": "2.7.1" } }, + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", + "license": "MIT", + "peer": true + }, "node_modules/@tsconfig/node10": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", @@ -1952,6 +3028,16 @@ "@types/node": "*" } }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/ms": "*" + } + }, "node_modules/@types/diff-match-patch": { "version": "1.0.36", "resolved": "https://registry.npmjs.org/@types/diff-match-patch/-/diff-match-patch-1.0.36.tgz", @@ -2006,7 +3092,6 @@ "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, "license": "MIT" }, "node_modules/@types/jsonwebtoken": { @@ -2017,6 +3102,13 @@ "@types/node": "*" } }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT", + "peer": true + }, "node_modules/@types/node": { "version": "22.10.2", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.2.tgz", @@ -2026,12 +3118,28 @@ "undici-types": "~6.20.0" } }, + "node_modules/@types/node-fetch": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.0" + } + }, "node_modules/@types/phoenix": { "version": "1.6.6", "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.6.tgz", "integrity": "sha512-PIzZZlEppgrpoT2QgbnDU+MMzuR6BbCjllj0bM70lWoejMeNJAxCchxnv7J3XFkI8MpygtRpzXrIlmWUBclP5A==", "license": "MIT" }, + "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/stack-utils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", @@ -2053,6 +3161,19 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "license": "MIT", + "peer": true + }, + "node_modules/@types/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", + "license": "MIT" + }, "node_modules/@types/webidl-conversions": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", @@ -2099,6 +3220,24 @@ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/abort-controller-x": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/abort-controller-x/-/abort-controller-x-0.4.3.tgz", + "integrity": "sha512-VtUwTNU8fpMwvWGn4xE93ywbogTYsuT+AUxAXOeelbXuQVIwNmC5YLeho9sH4vZ4ITW8414TTAOG1nW6uIVHCA==", + "license": "MIT" + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -2179,6 +3318,18 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/agentkeepalive": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", + "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", + "license": "MIT", + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, "node_modules/ai": { "version": "4.1.13", "resolved": "https://registry.npmjs.org/ai/-/ai-4.1.13.tgz", @@ -2253,7 +3404,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -2325,7 +3475,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, "license": "Python-2.0" }, "node_modules/array-flatten": { @@ -2353,9 +3502,9 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/axios": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", - "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz", + "integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", @@ -2494,6 +3643,26 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "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/base64id": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", @@ -2538,7 +3707,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, "engines": { "node": ">=8" } @@ -2641,6 +3809,31 @@ "node": ">=16.20.1" } }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "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", + "peer": true, + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, "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", @@ -2745,7 +3938,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", @@ -2842,7 +4034,6 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, "license": "ISC", "dependencies": { "string-width": "^4.2.0", @@ -2875,7 +4066,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -2888,7 +4078,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, "license": "MIT" }, "node_modules/color-support": { @@ -2945,6 +4134,15 @@ "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" }, + "node_modules/console-table-printer": { + "version": "2.14.3", + "resolved": "https://registry.npmjs.org/console-table-printer/-/console-table-printer-2.14.3.tgz", + "integrity": "sha512-X5OCFnjYlXzRuC8ac5hPA2QflRjJvNKJocMhlnqK/Ap7q3DHXr0NJ0TGzwmEKOiOdJrjsSwEd0m+a32JAYPrKQ==", + "license": "MIT", + "dependencies": { + "simple-wcswidth": "^1.0.1" + } + }, "node_modules/content-disposition": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", @@ -3070,6 +4268,35 @@ "yarn": ">=1" } }, + "node_modules/cross-fetch": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.2.0.tgz", + "integrity": "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==", + "license": "MIT", + "dependencies": { + "node-fetch": "^2.7.0" + } + }, + "node_modules/cross-fetch/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -3164,6 +4391,15 @@ "ms": "2.0.0" } }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/decimal.js": { "version": "10.4.3", "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", @@ -3195,7 +4431,6 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -3430,6 +4665,27 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, + "node_modules/engine.io/node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/entities": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", @@ -3485,7 +4741,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -3712,6 +4967,31 @@ "node": ">= 0.6" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "license": "MIT" + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.8.x" + } + }, "node_modules/eventsource-parser": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.0.tgz", @@ -3770,6 +5050,12 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/expr-eval": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expr-eval/-/expr-eval-2.0.2.tgz", + "integrity": "sha512-4EMSHGOPSwAfBiibw3ndnP0AvjDWLsMvGOvWEZ2F96IGk0bIVdjQisOHxReSkE13mHcfbuCiXw+G4y0zv6N8Eg==", + "license": "MIT" + }, "node_modules/express": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/express/-/express-5.0.1.tgz", @@ -4067,6 +5353,13 @@ "node": ">= 0.8" } }, + "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", + "peer": true + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -4129,6 +5422,24 @@ "node": ">=16.0.0" } }, + "node_modules/file-type": { + "version": "16.5.4", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-16.5.4.tgz", + "integrity": "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==", + "license": "MIT", + "peer": true, + "dependencies": { + "readable-web-to-node-stream": "^3.0.0", + "strtok3": "^6.2.4", + "token-types": "^4.1.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -4186,6 +5497,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } + }, "node_modules/flat-cache": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", @@ -4240,6 +5560,25 @@ "node": ">= 6" } }, + "node_modules/form-data-encoder": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", + "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==", + "license": "MIT" + }, + "node_modules/formdata-node": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", + "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", + "license": "MIT", + "dependencies": { + "node-domexception": "1.0.0", + "web-streams-polyfill": "4.0.0-beta.3" + }, + "engines": { + "node": ">= 12.20" + } + }, "node_modules/formidable": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.1.tgz", @@ -4299,6 +5638,20 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -4341,7 +5694,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" @@ -4406,6 +5758,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/gigachat": { + "version": "0.0.14", + "resolved": "https://registry.npmjs.org/gigachat/-/gigachat-0.0.14.tgz", + "integrity": "sha512-BwXDecDxF6aKJT+juuoATrBnFLDBg5Vho1dxYRsgM18zgZ55q5SwNiOgC05/J7rhGY66Pj6Wsnvk3FC6K4IMQw==", + "license": "ISC", + "dependencies": { + "axios": "^1.8.2", + "uuid": "^11.0.3" + } + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -4470,11 +5832,32 @@ "dev": true, "license": "ISC" }, + "node_modules/graphql": { + "version": "16.11.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.11.0.tgz", + "integrity": "sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw==", + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + } + }, + "node_modules/graphql-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/graphql-request/-/graphql-request-6.1.0.tgz", + "integrity": "sha512-p+XPfS4q7aIpKVcgmnZKhMNqhltk20hfXtkaIkTfjjmiKMJ5xrt5c743cL03y/K7y1rg3WrIC49xGiEQ4mxdNw==", + "license": "MIT", + "dependencies": { + "@graphql-typed-document-node/core": "^3.2.0", + "cross-fetch": "^3.1.5" + }, + "peerDependencies": { + "graphql": "14 - 16" + } + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -4640,6 +6023,113 @@ "node": ">=10.17.0" } }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/ibm-cloud-sdk-core": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/ibm-cloud-sdk-core/-/ibm-cloud-sdk-core-5.4.0.tgz", + "integrity": "sha512-c4cwOuUDbMiFROYM/Ti1aC+Umi1v3TdvC2DO5zR7w44FYY/3xrs79+3DVPXt/nRhJeaMHN2L9XwlXsPSoVDHJA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@types/debug": "^4.1.12", + "@types/node": "^18.19.80", + "@types/tough-cookie": "^4.0.0", + "axios": "^1.8.2", + "camelcase": "^6.3.0", + "debug": "^4.3.4", + "dotenv": "^16.4.5", + "extend": "3.0.2", + "file-type": "16.5.4", + "form-data": "4.0.0", + "isstream": "0.1.2", + "jsonwebtoken": "^9.0.2", + "mime-types": "2.1.35", + "retry-axios": "^2.6.0", + "tough-cookie": "^4.1.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/ibm-cloud-sdk-core/node_modules/@types/node": { + "version": "18.19.111", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.111.tgz", + "integrity": "sha512-90sGdgA+QLJr1F9X79tQuEut0gEYIfkX9pydI4XGRgvFo9g2JWswefI+WUSUHPYVBHYSEfTEqBxA5hQvAZB3Mw==", + "license": "MIT", + "peer": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/ibm-cloud-sdk-core/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ibm-cloud-sdk-core/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/ibm-cloud-sdk-core/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT", + "peer": true + }, + "node_modules/ibm-cloud-sdk-core/node_modules/tough-cookie": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/ibm-cloud-sdk-core/node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "license": "MIT", + "peer": true + }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -4651,11 +6141,32 @@ "node": ">=0.10.0" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "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": "BSD-3-Clause", + "peer": true + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">= 4" @@ -4854,6 +6365,13 @@ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", + "license": "MIT", + "peer": true + }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", @@ -5549,6 +7067,15 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/js-tiktoken": { + "version": "1.0.20", + "resolved": "https://registry.npmjs.org/js-tiktoken/-/js-tiktoken-1.0.20.tgz", + "integrity": "sha512-Xlaqhhs8VfCd6Sh7a1cFkZHQbYTLCwVJJWiHVxBYzLPxW0XsoxBy1hitmjkdIjD3Aon5BXLHFwU5O8WUx6HH+A==", + "license": "MIT", + "dependencies": { + "base64-js": "^1.5.1" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -5560,7 +7087,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -5688,27 +7214,6 @@ "node": ">=18" } }, - "node_modules/jsdom/node_modules/ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, "node_modules/jsesc": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", @@ -5798,6 +7303,15 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/jsonpointer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/jsonwebtoken": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", @@ -5873,6 +7387,168 @@ "node": ">=6" } }, + "node_modules/langchain": { + "version": "0.3.28", + "resolved": "https://registry.npmjs.org/langchain/-/langchain-0.3.28.tgz", + "integrity": "sha512-h4GGlBJNGU/Sj2PipW9kL+ewj7To3c+SnnNKH3HZaVHEqGPMHVB96T1lLjtCLcZCyUfabMr/zFIkLNI4War+Xg==", + "license": "MIT", + "dependencies": { + "@langchain/openai": ">=0.1.0 <0.6.0", + "@langchain/textsplitters": ">=0.0.0 <0.2.0", + "js-tiktoken": "^1.0.12", + "js-yaml": "^4.1.0", + "jsonpointer": "^5.0.1", + "langsmith": "^0.3.29", + "openapi-types": "^12.1.3", + "p-retry": "4", + "uuid": "^10.0.0", + "yaml": "^2.2.1", + "zod": "^3.25.32" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@langchain/anthropic": "*", + "@langchain/aws": "*", + "@langchain/cerebras": "*", + "@langchain/cohere": "*", + "@langchain/core": ">=0.3.58 <0.4.0", + "@langchain/deepseek": "*", + "@langchain/google-genai": "*", + "@langchain/google-vertexai": "*", + "@langchain/google-vertexai-web": "*", + "@langchain/groq": "*", + "@langchain/mistralai": "*", + "@langchain/ollama": "*", + "@langchain/xai": "*", + "axios": "*", + "cheerio": "*", + "handlebars": "^4.7.8", + "peggy": "^3.0.2", + "typeorm": "*" + }, + "peerDependenciesMeta": { + "@langchain/anthropic": { + "optional": true + }, + "@langchain/aws": { + "optional": true + }, + "@langchain/cerebras": { + "optional": true + }, + "@langchain/cohere": { + "optional": true + }, + "@langchain/deepseek": { + "optional": true + }, + "@langchain/google-genai": { + "optional": true + }, + "@langchain/google-vertexai": { + "optional": true + }, + "@langchain/google-vertexai-web": { + "optional": true + }, + "@langchain/groq": { + "optional": true + }, + "@langchain/mistralai": { + "optional": true + }, + "@langchain/ollama": { + "optional": true + }, + "@langchain/xai": { + "optional": true + }, + "axios": { + "optional": true + }, + "cheerio": { + "optional": true + }, + "handlebars": { + "optional": true + }, + "peggy": { + "optional": true + }, + "typeorm": { + "optional": true + } + } + }, + "node_modules/langchain-gigachat": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/langchain-gigachat/-/langchain-gigachat-0.0.11.tgz", + "integrity": "sha512-2hYES1Dt0U/p/h+F+/1lDfmaYTWQyuHG5KAAIQGYygursAUGDDoyKQlGywbJ4JgmENy4u5fv7keVC9+k0X8tbQ==", + "license": "MIT", + "dependencies": { + "gigachat": "^0.0.14", + "uuid": "^11.0.5", + "zod": "^3.23.8", + "zod-to-json-schema": "^3.23.5" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@langchain/core": ">=0.2.21 <0.4.0" + } + }, + "node_modules/langchain/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/langsmith": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/langsmith/-/langsmith-0.3.31.tgz", + "integrity": "sha512-9lwuLZuN3tXFYQ6eMg0rmbBw7oxQo4bu1NYeylbjz27bOdG1XB9XNoxaiIArkK4ciLdOIOhPMBXP4bkvZOgHRw==", + "license": "MIT", + "dependencies": { + "@types/uuid": "^10.0.0", + "chalk": "^4.1.2", + "console-table-printer": "^2.12.1", + "p-queue": "^6.6.2", + "p-retry": "4", + "semver": "^7.6.3", + "uuid": "^10.0.0" + }, + "peerDependencies": { + "openai": "*" + }, + "peerDependenciesMeta": { + "openai": { + "optional": true + } + } + }, + "node_modules/langsmith/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -5926,6 +7602,12 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "license": "MIT" }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "license": "MIT" + }, "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -5968,16 +7650,11 @@ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } + "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/make-dir": { "version": "3.1.0", @@ -6467,6 +8144,15 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/mustache": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", + "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", + "license": "MIT", + "bin": { + "mustache": "bin/mustache" + } + }, "node_modules/nanoid": { "version": "3.3.8", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", @@ -6500,28 +8186,59 @@ "node": ">= 0.6" } }, + "node_modules/nice-grpc": { + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/nice-grpc/-/nice-grpc-2.1.12.tgz", + "integrity": "sha512-J1n4Wg+D3IhRhGQb+iqh2OpiM0GzTve/kf2lnlW4S+xczmIEd0aHUDV1OsJ5a3q8GSTqJf+s4Rgg1M8uJltarw==", + "license": "MIT", + "dependencies": { + "@grpc/grpc-js": "^1.13.1", + "abort-controller-x": "^0.4.0", + "nice-grpc-common": "^2.0.2" + } + }, + "node_modules/nice-grpc-client-middleware-retry": { + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/nice-grpc-client-middleware-retry/-/nice-grpc-client-middleware-retry-3.1.11.tgz", + "integrity": "sha512-xW/imz/kNG2g0DwTfH2eYEGrg1chSLrXtvGp9fg2qkhTgGFfAS/Pq3+t+9G8KThcC4hK/xlEyKvZWKk++33S6A==", + "license": "MIT", + "dependencies": { + "abort-controller-x": "^0.4.0", + "nice-grpc-common": "^2.0.2" + } + }, + "node_modules/nice-grpc-common": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/nice-grpc-common/-/nice-grpc-common-2.0.2.tgz", + "integrity": "sha512-7RNWbls5kAL1QVUOXvBsv1uO0wPQK3lHv+cY1gwkTzirnG1Nop4cBJZubpgziNbaVc/bl9QJcyvsf/NQxa3rjQ==", + "license": "MIT", + "dependencies": { + "ts-error": "^1.0.6" + } + }, "node_modules/node-addon-api": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" }, - "node_modules/node-fetch": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz", - "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true + "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-int64": { @@ -6727,6 +8444,77 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/openai": { + "version": "4.104.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-4.104.0.tgz", + "integrity": "sha512-p99EFNsA/yX6UhVO93f5kJsDRLAg+CTA2RBqdHK4RtK8u5IJw32Hyb2dTGKbnnFmnuoBv5r7Z2CURI9sGZpSuA==", + "license": "Apache-2.0", + "dependencies": { + "@types/node": "^18.11.18", + "@types/node-fetch": "^2.6.4", + "abort-controller": "^3.0.0", + "agentkeepalive": "^4.2.1", + "form-data-encoder": "1.7.2", + "formdata-node": "^4.3.2", + "node-fetch": "^2.6.7" + }, + "bin": { + "openai": "bin/cli" + }, + "peerDependencies": { + "ws": "^8.18.0", + "zod": "^3.23.8" + }, + "peerDependenciesMeta": { + "ws": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/openai/node_modules/@types/node": { + "version": "18.19.111", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.111.tgz", + "integrity": "sha512-90sGdgA+QLJr1F9X79tQuEut0gEYIfkX9pydI4XGRgvFo9g2JWswefI+WUSUHPYVBHYSEfTEqBxA5hQvAZB3Mw==", + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/openai/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/openai/node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "license": "MIT" + }, + "node_modules/openapi-types": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", + "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==", + "license": "MIT" + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -6745,6 +8533,15 @@ "node": ">= 0.8.0" } }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -6777,6 +8574,47 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-queue": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", + "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", + "license": "MIT", + "dependencies": { + "eventemitter3": "^4.0.4", + "p-timeout": "^3.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "license": "MIT", + "dependencies": { + "p-finally": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -6888,6 +8726,20 @@ "fastfall": "^1.2.3" } }, + "node_modules/peek-readable": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-4.1.0.tgz", + "integrity": "sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/picocolors": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", @@ -6986,6 +8838,38 @@ "node": ">=8" } }, + "node_modules/playwright": { + "version": "1.53.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.53.0.tgz", + "integrity": "sha512-ghGNnIEYZC4E+YtclRn4/p6oYbdPiASELBIYkBXfaTVKreQUYbMUYQDwS12a8F0/HtIjr/CkGjtwABeFPGcS4Q==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "playwright-core": "1.53.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.53.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.53.0.tgz", + "integrity": "sha512-mGLg8m0pm4+mmtB7M89Xw/GSqoNC+twivl8ITteqvAndachozYe2ZA7srU6uleV1vEdAHYqjq+SV8SNxRRFYBw==", + "license": "Apache-2.0", + "peer": true, + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -7024,6 +8908,16 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -7044,6 +8938,30 @@ "node": ">= 6" } }, + "node_modules/protobufjs": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.3.tgz", + "integrity": "sha512-sildjKwVqOI2kmFDiXQ6aEB0fjYTafpEvIBs8tOR8qI4spuL9OPROLVu2qZqi/xgCfsHIwVqlaF8JBjWFHnKbw==", + "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/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -7062,6 +8980,19 @@ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "license": "MIT" }, + "node_modules/psl": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", + "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", + "license": "MIT", + "peer": true, + "dependencies": { + "punycode": "^2.3.1" + }, + "funding": { + "url": "https://github.com/sponsors/lupomontero" + } + }, "node_modules/pstree.remy": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", @@ -7109,6 +9040,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "license": "MIT", + "peer": true + }, "node_modules/random-bytes": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", @@ -7178,6 +9116,50 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "license": "MIT" }, + "node_modules/readable-web-to-node-stream": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.4.tgz", + "integrity": "sha512-9nX56alTf5bwXQ3ZDipHJhusu9NTQJ/CVPtb/XHAJCXihZeitfJvIRS4GqQ/mfIoOE3IelHMrpayVrosdHBuLw==", + "license": "MIT", + "peer": true, + "dependencies": { + "readable-stream": "^4.7.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/readable-web-to-node-stream/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "peer": true, + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/readable-web-to-node-stream/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "peer": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -7194,12 +9176,18 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "license": "MIT", + "peer": true + }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", @@ -7261,6 +9249,28 @@ "node": ">=10" } }, + "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/retry-axios": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/retry-axios/-/retry-axios-2.6.0.tgz", + "integrity": "sha512-pOLi+Gdll3JekwuFjXO3fTq+L9lzMQGcSq7M5gIjExcl3Gu1hd4XXuf5o3+LuSBsaULQH7DiNbsqPd1chVpQGQ==", + "license": "Apache-2.0", + "peer": true, + "engines": { + "node": ">=10.7.0" + }, + "peerDependencies": { + "axios": "*" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -7362,12 +9372,10 @@ "license": "BSD-3-Clause" }, "node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dependencies": { - "lru-cache": "^6.0.0" - }, + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -7569,6 +9577,12 @@ "node": ">=10" } }, + "node_modules/simple-wcswidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-wcswidth/-/simple-wcswidth-1.0.1.tgz", + "integrity": "sha512-xMO/8eNREtaROt7tJvWJqHBDTMFN4eiQ5I4JRMuilwfnFcV5W9u7RUkueNkdw0jPqGMX36iCywelS5yilTuOxg==", + "license": "MIT" + }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -7637,6 +9651,27 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, + "node_modules/socket.io-adapter/node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/socket.io-parser": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", @@ -7849,6 +9884,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strtok3": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-6.3.0.tgz", + "integrity": "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@tokenizer/token": "^0.3.0", + "peek-readable": "^4.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/superagent": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/superagent/-/superagent-9.0.2.tgz", @@ -7926,7 +9979,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -8066,6 +10118,24 @@ "node": ">=0.6" } }, + "node_modules/token-types": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-4.2.1.tgz", + "integrity": "sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/touch": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", @@ -8120,6 +10190,12 @@ "tree-kill": "cli.js" } }, + "node_modules/ts-error": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/ts-error/-/ts-error-1.0.6.tgz", + "integrity": "sha512-tLJxacIQUM82IR7JO1UUkKlYuUTmoY9HBJAmNWFzheSlDS5SPMcNIepejHJa4BpPQLAcbRhRf3GDJzyj6rbKvA==", + "license": "MIT" + }, "node_modules/ts-node": { "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", @@ -8337,6 +10413,16 @@ "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", "license": "MIT" }, + "node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -8386,6 +10472,17 @@ "punycode": "^2.1.0" } }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, "node_modules/use-sync-external-store": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz", @@ -8473,6 +10570,47 @@ "makeerror": "1.0.12" } }, + "node_modules/weaviate-client": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/weaviate-client/-/weaviate-client-3.6.1.tgz", + "integrity": "sha512-C/sVYLqLuRQn2FeP3YQFNSOV61gNNtswNDRnb117ZfweSU/DIGAYeZZn63DpNI11qDSWTS3Cbz/aQjUzZXbA6Q==", + "license": "BSD 3-Clause", + "dependencies": { + "abort-controller-x": "^0.4.3", + "graphql": "^16.10.0", + "graphql-request": "^6.1.0", + "long": "^5.2.4", + "nice-grpc": "^2.1.11", + "nice-grpc-client-middleware-retry": "^3.1.10", + "nice-grpc-common": "^2.0.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/weaviate-client/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/web-streams-polyfill": { + "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", + "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", @@ -8556,7 +10694,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -8590,9 +10727,9 @@ } }, "node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "version": "8.18.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz", + "integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==", "license": "MIT", "engines": { "node": ">=10.0.0" @@ -8637,7 +10774,6 @@ "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, "license": "ISC", "engines": { "node": ">=10" @@ -8648,11 +10784,22 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, + "node_modules/yaml": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz", + "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + } + }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, "license": "MIT", "dependencies": { "cliui": "^8.0.1", @@ -8671,7 +10818,6 @@ "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, "license": "ISC", "engines": { "node": ">=12" @@ -8701,11 +10847,10 @@ } }, "node_modules/zod": { - "version": "3.24.1", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz", - "integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==", + "version": "3.25.63", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.63.tgz", + "integrity": "sha512-3ttCkqhtpncYXfP0f6dsyabbYV/nEUW+Xlu89jiXbTBifUfjaSqXOG6JnQPLtqt87n7KAmnMqcjay6c0Wq0Vbw==", "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/package.json b/package.json index 17ea786..10a157b 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,9 @@ "homepage": "https://bitbucket.org/online-mentor/multi-stub#readme", "dependencies": { "@supabase/supabase-js": "^2.49.4", + "@langchain/community": "^0.3.41", + "@langchain/core": "^0.3.46", + "@langchain/langgraph": "^0.2.65", "ai": "^4.1.13", "axios": "^1.7.7", "bcrypt": "^5.1.0", @@ -34,8 +37,10 @@ "express": "5.0.1", "express-jwt": "^8.5.1", "express-session": "^1.18.1", + "gigachat": "^0.0.14", "jsdom": "^25.0.1", "jsonwebtoken": "^9.0.2", + "langchain-gigachat": "^0.0.11", "mongodb": "^6.12.0", "mongoose": "^8.9.2", "mongoose-sequence": "^6.0.1", @@ -44,7 +49,7 @@ "pbkdf2-password": "^1.2.1", "rotating-file-stream": "^3.2.5", "socket.io": "^4.8.1", - "uuid": "^11.0.3" + "zod": "^3.24.3" }, "devDependencies": { "@eslint/js": "^9.17.0", diff --git a/server/routers/kfu-m-24-1/sber_mobile/get-constants.js b/server/routers/kfu-m-24-1/sber_mobile/get-constants.js index 7c9e18d..0d7c355 100644 --- a/server/routers/kfu-m-24-1/sber_mobile/get-constants.js +++ b/server/routers/kfu-m-24-1/sber_mobile/get-constants.js @@ -1,5 +1,3 @@ -const fetch = require('node-fetch'); - const getSupabaseUrl = async () => { const response = await fetch('https://admin.bro-js.ru/api/config/v1/dev'); const data = await response.json(); diff --git a/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/README.md b/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/README.md new file mode 100644 index 0000000..ccad3cd --- /dev/null +++ b/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/README.md @@ -0,0 +1,63 @@ +# AI Support Agent + +AI-агент поддержки интегрирован в существующий `supportApi.js`. + +## Структура + +``` +support-ai-agent/ +├── gigachat.ts # Конфигурация GigaChat +├── support-agent.ts # Основной класс агента +└── README.md # Документация +``` + +## API + +### POST `/support` +Отправить сообщение в службу поддержки (теперь с AI-агентом). + +**Запрос:** +```json +{ + "user_id": "string", // Обязательно + "message": "string", // Обязательно + "system_prompt": "string" // Опционально - настройка поведения агента +} +``` + +**Ответ:** +```json +{ + "reply": "Ответ AI-агента", + "success": true +} +``` + +### POST `/support/configure` +Настроить системный промпт для конкретного пользователя. + +### DELETE `/support/history/:userId` +Очистить историю диалога пользователя. + +## Возможности + +- 🤖 Интеллектуальные ответы на основе GigaChat +- 💾 Сохранение всех сообщений в базу данных Supabase +- 🧠 Память контекста диалога для каждого пользователя +- ⚙️ Настраиваемые системные промпты +- 📊 Поддержка множественных пользователей + +## Примеры системных промптов + +### Техническая поддержка +``` +Ты - специалист технической поддержки мобильного приложения "Умный дом". +Помогай пользователям решать проблемы, объясняй функции простым языком, +проводи диагностику пошагово. Всегда будь дружелюбным и терпеливым. +``` + +### Общая поддержка клиентов +``` +Ты - профессиональный агент службы поддержки. Помогай решать вопросы +пользователей, отвечай вежливо и по существу, проявляй эмпатию. +``` \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/gigachat.ts b/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/gigachat.ts new file mode 100644 index 0000000..e46c067 --- /dev/null +++ b/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/gigachat.ts @@ -0,0 +1,18 @@ +import { Agent } from 'node:https'; +import { GigaChat } from 'langchain-gigachat'; + +const httpsAgent = new Agent({ + rejectUnauthorized: false, +}); + +// Получаем GIGA_AUTH из переменной окружения (устанавливается в get-constants.js) +export const gigachat = new GigaChat({ + model: 'GigaChat-2', + temperature: 0.7, + scope: 'GIGACHAT_API_PERS', + streaming: false, + credentials: process.env.GIGA_AUTH, + httpsAgent +}); + +export default gigachat; \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/support-agent.ts b/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/support-agent.ts new file mode 100644 index 0000000..514a3c8 --- /dev/null +++ b/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/support-agent.ts @@ -0,0 +1,131 @@ +import { HumanMessage, AIMessage, SystemMessage, BaseMessage } from '@langchain/core/messages'; +import { ChatPromptTemplate, MessagesPlaceholder } from '@langchain/core/prompts'; +import { createReactAgent } from '@langchain/langgraph/prebuilt'; +import { MemorySaver } from '@langchain/langgraph'; +import gigachat from './gigachat'; + +export interface SupportAgentConfig { + systemPrompt?: string; + temperature?: number; + threadId?: string; +} + +export interface SupportResponse { + content: string; + success: boolean; + error?: string; +} + +export class SupportAgent { + private llm: any; + private memorySaver: MemorySaver; + private agent: any; + private systemPrompt: string; + private threadId: string; + + constructor(config: SupportAgentConfig = {}) { + this.systemPrompt = config.systemPrompt || this.getDefaultSystemPrompt(); + this.threadId = config.threadId || 'default'; + this.memorySaver = new MemorySaver(); + + // Настраиваем модель с заданной температурой + this.llm = gigachat; + if (config.temperature !== undefined) { + this.llm.temperature = config.temperature; + } + + // Создаем агента без инструментов для простого чата + this.agent = createReactAgent({ + llm: this.llm, + tools: [], + checkpointSaver: this.memorySaver + }); + } + + /** + * Получить системный промпт по умолчанию для агента поддержки + */ + private getDefaultSystemPrompt(): string { + return `Ты - профессиональный агент службы поддержки. + +Твои основные задачи: +- Помогать пользователям решать их вопросы и проблемы +- Отвечать вежливо, профессионально и по существу +- Предоставлять четкие и понятные инструкции +- Проявлять эмпатию к проблемам пользователей +- Если не знаешь ответ, честно сообщить об этом и предложить альтернативные способы получения помощи + +Всегда отвечай на русском языке и старайся быть максимально полезным.`; + } + + /** + * Обновить системный промпт + */ + public updateSystemPrompt(newPrompt: string): void { + this.systemPrompt = newPrompt; + } + + /** + * Получить текущий системный промпт + */ + public getSystemPrompt(): string { + return this.systemPrompt; + } + + /** + * Обработать сообщение пользователя и получить ответ + */ + public async processMessage(userMessage: string): Promise { + try { + // Создаем сообщения с системным промптом + const messages = [ + new SystemMessage(this.systemPrompt), + new HumanMessage(userMessage) + ]; + + // Получаем ответ от агента + const response = await this.agent.invoke({ + messages: messages + }, { + configurable: { + thread_id: this.threadId + } + }); + + // Извлекаем последнее сообщение от ассистента + const lastMessage = response.messages[response.messages.length - 1]; + + return { + content: lastMessage.content || 'Извините, не удалось сформировать ответ.', + success: true + }; + + } catch (error) { + console.error('Ошибка при обработке сообщения:', error); + return { + content: 'Извините, произошла ошибка при обработке вашего запроса. Попробуйте позже.', + success: false, + error: error instanceof Error ? error.message : 'Неизвестная ошибка' + }; + } + } + + /** + * Очистить историю диалога + */ + public async clearHistory(): Promise { + this.memorySaver = new MemorySaver(); + this.agent = createReactAgent({ + llm: this.llm, + tools: [], + checkpointSaver: this.memorySaver + }); + } + + /** + * Изменить ID потока (для работы с разными пользователями) + */ + public setThreadId(threadId: string): void { + this.threadId = threadId; + } +} \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/supportApi.js b/server/routers/kfu-m-24-1/sber_mobile/supportApi.js index 71eef57..de908a8 100644 --- a/server/routers/kfu-m-24-1/sber_mobile/supportApi.js +++ b/server/routers/kfu-m-24-1/sber_mobile/supportApi.js @@ -1,16 +1,151 @@ const router = require('express').Router(); const { getSupabaseClient } = require('./supabaseClient'); +const { SupportAgent } = require('./support-ai-agent/support-agent'); + +// Хранилище агентов для разных пользователей +const userAgents = new Map(); + +/** + * Получить или создать агента для пользователя + */ +function getUserAgent(userId, systemPrompt) { + if (!userAgents.has(userId)) { + const config = { + threadId: userId, + temperature: 0.7 + }; + if (systemPrompt) { + config.systemPrompt = systemPrompt; + } + userAgents.set(userId, new SupportAgent(config)); + } + return userAgents.get(userId); +} // POST /api/support router.post('/support', async (req, res) => { const supabase = getSupabaseClient(); - const { user_id, message } = req.body; - if (!user_id || !message) return res.status(400).json({ error: 'user_id и message обязательны' }); - const { error } = await supabase - .from('support') - .insert({ user_id, message, is_from_user: true }); - if (error) return res.status(400).json({ error: error.message }); - res.json({ reply: 'Спасибо за ваше сообщение! Служба поддержки свяжется с вами в ближайшее время.' }); + const { user_id, message, system_prompt } = req.body; + + if (!user_id || !message) { + return res.status(400).json({ error: 'user_id и message обязательны' }); + } + + try { + // Сохраняем сообщение пользователя в базу данных + const { error: insertError } = await supabase + .from('support') + .insert({ user_id, message, is_from_user: true }); + + if (insertError) { + return res.status(400).json({ error: insertError.message }); + } + + // Получаем агента для пользователя + const agent = getUserAgent(user_id, system_prompt); + + // Обновляем системный промпт если передан + if (system_prompt) { + agent.updateSystemPrompt(system_prompt); + } + + // Получаем ответ от AI-агента + const aiResponse = await agent.processMessage(message); + + if (!aiResponse.success) { + console.error('Ошибка AI-агента:', aiResponse.error); + return res.status(500).json({ + error: 'Ошибка при генерации ответа', + reply: 'Извините, произошла ошибка. Попробуйте позже.' + }); + } + + // Сохраняем ответ агента в базу данных + const { error: responseError } = await supabase + .from('support') + .insert({ + user_id, + message: aiResponse.content, + is_from_user: false + }); + + if (responseError) { + console.error('Ошибка сохранения ответа:', responseError); + // Не возвращаем ошибку пользователю, так как ответ уже сгенерирован + } + + // Возвращаем ответ пользователю + res.json({ + reply: aiResponse.content, + success: true + }); + + } catch (error) { + console.error('Ошибка в supportApi:', error); + res.status(500).json({ + error: 'Внутренняя ошибка сервера', + reply: 'Извините, произошла ошибка. Попробуйте позже.' + }); + } +}); + +// POST /api/support/configure - Настройка системного промпта +router.post('/support/configure', async (req, res) => { + const { user_id, system_prompt } = req.body; + + if (!user_id) { + return res.status(400).json({ error: 'user_id обязателен' }); + } + + try { + const agent = getUserAgent(user_id, system_prompt); + + if (system_prompt) { + agent.updateSystemPrompt(system_prompt); + } + + res.json({ + message: 'Конфигурация агента обновлена', + current_system_prompt: agent.getSystemPrompt(), + success: true + }); + + } catch (error) { + console.error('Ошибка в /support/configure:', error); + res.status(500).json({ + error: 'Внутренняя ошибка сервера', + success: false + }); + } +}); + +// DELETE /api/support/history/:userId - Очистка истории диалога +router.delete('/support/history/:userId', async (req, res) => { + const { userId } = req.params; + + try { + if (userAgents.has(userId)) { + const agent = userAgents.get(userId); + await agent.clearHistory(); + + res.json({ + message: 'История диалога очищена', + success: true + }); + } else { + res.json({ + message: 'Агент для данного пользователя не найден', + success: true + }); + } + + } catch (error) { + console.error('Ошибка в /support/history:', error); + res.status(500).json({ + error: 'Внутренняя ошибка сервера', + success: false + }); + } }); module.exports = router; \ No newline at end of file From 24ff71230641ee4167d4fa422b49529f23876b8b Mon Sep 17 00:00:00 2001 From: DenAntonov Date: Thu, 12 Jun 2025 21:04:12 +0300 Subject: [PATCH 03/16] add sockets and change subscription --- server/index.ts | 13 +- server/io.ts | 9 +- .../routers/kfu-m-24-1/sber_mobile/chats.js | 22 +-- .../routers/kfu-m-24-1/sber_mobile/index.js | 1 + .../kfu-m-24-1/sber_mobile/messages.js | 90 ++++++----- .../kfu-m-24-1/sber_mobile/socket-chat.js | 147 ++++++++++++++++-- 6 files changed, 199 insertions(+), 83 deletions(-) diff --git a/server/index.ts b/server/index.ts index d39f65f..3d4f58e 100644 --- a/server/index.ts +++ b/server/index.ts @@ -20,7 +20,9 @@ import gamehubRouter from './routers/gamehub' import escRouter from './routers/esc' import connectmeRouter from './routers/connectme' import questioneerRouter from './routers/questioneer' -import { setIo } from './io' +import { setIo, getIo } from './io' +// Импорт обработчика чата +const { initializeChatSocket } = require('./routers/kfu-m-24-1/sber_mobile/socket-chat') export const app = express() @@ -65,6 +67,15 @@ const initServer = async () => { console.log('warming up 🔥') const server = setIo(app) + + // Инициализация Socket.IO для чата + const io = getIo() + if (io) { + const chatHandler = initializeChatSocket(io) + // Сохраняем ссылку на chat handler для доступа из эндпоинтов + io.chatHandler = chatHandler + console.log('✅ Socket.IO для чата инициализирован') + } const sess = { secret: "super-secret-key", diff --git a/server/io.ts b/server/io.ts index 71833d6..4121b06 100644 --- a/server/io.ts +++ b/server/io.ts @@ -5,7 +5,14 @@ let io = null export const setIo = (app) => { const server = createServer(app) - io = new Server(server, {}) + io = new Server(server, { + cors: { + origin: "*", + methods: ["GET", "POST"], + credentials: false + }, + transports: ['websocket', 'polling'] + }) return server } diff --git a/server/routers/kfu-m-24-1/sber_mobile/chats.js b/server/routers/kfu-m-24-1/sber_mobile/chats.js index a20d9df..047ac37 100644 --- a/server/routers/kfu-m-24-1/sber_mobile/chats.js +++ b/server/routers/kfu-m-24-1/sber_mobile/chats.js @@ -3,31 +3,22 @@ const { getSupabaseClient } = require('./supabaseClient'); // Получить все чаты по дому router.get('/chats', async (req, res) => { - console.log('🏠 [Server] GET /chats запрос получен'); - console.log('🏠 [Server] Query параметры:', req.query); - const supabase = getSupabaseClient(); const { building_id } = req.query; if (!building_id) { - console.log('❌ [Server] Ошибка: building_id обязателен'); return res.status(400).json({ error: 'building_id required' }); } try { - console.log('🔍 [Server] Выполняем запрос к Supabase для здания:', building_id); - const { data, error } = await supabase.from('chats').select('*').eq('building_id', building_id); if (error) { - console.log('❌ [Server] Ошибка Supabase:', error); return res.status(400).json({ error: error.message }); } - console.log('✅ [Server] Чаты получены:', data?.length || 0, 'шт.'); res.json(data || []); } catch (err) { - console.log('❌ [Server] Неожиданная ошибка:', err); res.status(500).json({ error: 'Internal server error' }); } }); @@ -184,15 +175,10 @@ router.get('/chats/:chat_id/stats', async (req, res) => { // Получить последнее сообщение в чате router.get('/chats/:chat_id/last-message', async (req, res) => { - console.log('💬 [Server] GET /chats/:chat_id/last-message запрос получен'); - console.log('💬 [Server] Chat ID:', req.params.chat_id); - const supabase = getSupabaseClient(); const { chat_id } = req.params; try { - console.log('🔍 [Server] Выполняем запрос последнего сообщения для чата:', chat_id); - // Получаем последнее сообщение const { data: lastMessage, error } = await supabase .from('messages') @@ -205,10 +191,8 @@ router.get('/chats/:chat_id/last-message', async (req, res) => { let data = null; if (error && error.code === 'PGRST116') { - console.log('ℹ️ [Server] Сообщений в чате нет (PGRST116)'); data = null; } else if (error) { - console.log('❌ [Server] Ошибка Supabase при получении последнего сообщения:', error); return res.status(400).json({ error: error.message }); } else if (lastMessage) { // Получаем профиль пользователя для сообщения @@ -223,12 +207,10 @@ router.get('/chats/:chat_id/last-message', async (req, res) => { ...lastMessage, user_profiles: userProfile || null }; - console.log('✅ [Server] Последнее сообщение получено для чата:', chat_id); - } + } - res.json(data); + res.json(data); } catch (err) { - console.log('❌ [Server] Неожиданная ошибка при получении последнего сообщения:', err); res.status(500).json({ error: 'Internal server error' }); } }); diff --git a/server/routers/kfu-m-24-1/sber_mobile/index.js b/server/routers/kfu-m-24-1/sber_mobile/index.js index e419be7..4dfad13 100644 --- a/server/routers/kfu-m-24-1/sber_mobile/index.js +++ b/server/routers/kfu-m-24-1/sber_mobile/index.js @@ -15,6 +15,7 @@ const buildingsRouter = require('./buildings'); const userApartmentsRouter = require('./user_apartments'); const avatarRouter = require('./media'); const supportRouter = require('./supportApi'); +const { getIo } = require('../../../io'); module.exports = router; diff --git a/server/routers/kfu-m-24-1/sber_mobile/messages.js b/server/routers/kfu-m-24-1/sber_mobile/messages.js index 6c0bfa3..f729679 100644 --- a/server/routers/kfu-m-24-1/sber_mobile/messages.js +++ b/server/routers/kfu-m-24-1/sber_mobile/messages.js @@ -1,64 +1,59 @@ const router = require('express').Router(); const { getSupabaseClient } = require('./supabaseClient'); +const { getIo } = require('../../../io'); // Импортируем Socket.IO // Получить все сообщения в чате с информацией о пользователе router.get('/messages', async (req, res) => { - console.log('📬 [Server] GET /messages запрос получен'); - console.log('📬 [Server] Query параметры:', req.query); - - const supabase = getSupabaseClient(); - const { chat_id, limit = 50, offset = 0 } = req.query; - - if (!chat_id) { - console.log('❌ [Server] Ошибка: chat_id обязателен'); - return res.status(400).json({ error: 'chat_id required' }); - } - try { - console.log('🔍 [Server] Выполняем запрос к Supabase для чата:', chat_id); - - // Получаем сообщения - const { data: messages, error } = await supabase - .from('messages') - .select('*') - .eq('chat_id', chat_id) - .order('created_at', { ascending: false }) - .limit(limit) - .range(offset, offset + limit - 1); - - if (error) { - console.log('❌ [Server] Ошибка получения сообщений:', error); - return res.status(400).json({ error: error.message }); + const { chat_id, limit = 50, offset = 0 } = req.query; + + if (!chat_id) { + return res.status(400).json({ error: 'chat_id is required' }); } + + const supabase = getSupabaseClient(); - // Получаем профили пользователей для всех уникальных user_id - let data = messages || []; - if (data.length > 0) { - const userIds = [...new Set(data.map(msg => msg.user_id))]; - console.log('👥 [Server] Получаем профили для пользователей:', userIds); - + const { data, error } = await supabase + .from('messages') + .select(` + *, + user_profiles ( + id, + full_name, + avatar_url + ) + `) + .eq('chat_id', chat_id) + .order('created_at', { ascending: true }) + .range(offset, offset + limit - 1); + + if (error) { + return res.status(500).json({ error: 'Failed to fetch messages' }); + } + + // Получаем уникальные ID пользователей из сообщений, у которых нет профиля + const messagesWithoutProfiles = data.filter(msg => !msg.user_profiles); + const userIds = [...new Set(messagesWithoutProfiles.map(msg => msg.user_id))]; + + if (userIds.length > 0) { const { data: profiles, error: profilesError } = await supabase .from('user_profiles') .select('id, full_name, avatar_url') .in('id', userIds); - + if (!profilesError && profiles) { - // Объединяем сообщения с профилями - data = data.map(msg => ({ - ...msg, - user_profiles: profiles.find(profile => profile.id === msg.user_id) || null - })); - console.log('✅ [Server] Профили пользователей добавлены к сообщениям'); - } else { - console.log('⚠️ [Server] Ошибка получения профилей пользователей:', profilesError); + // Добавляем профили к сообщениям + data.forEach(message => { + if (!message.user_profiles) { + message.user_profiles = profiles.find(profile => profile.id === message.user_id) || null; + } + }); } - } - - console.log('✅ [Server] Сообщения получены:', data?.length || 0, 'шт.'); - res.json(data?.reverse() || []); // Возвращаем в хронологическом порядке + } + + res.json(data); } catch (err) { - console.log('❌ [Server] Неожиданная ошибка:', err); - res.status(500).json({ error: 'Internal server error' }); + res.status(500).json({ error: 'Unexpected error occurred' }); } }); @@ -94,6 +89,9 @@ router.post('/messages', async (req, res) => { ...newMessage, user_profiles: userProfile || null }; + + // Отправка через Socket.IO теперь происходит автоматически через Supabase Real-time подписку + // Это предотвращает дублирование сообщений res.json(data); }); diff --git a/server/routers/kfu-m-24-1/sber_mobile/socket-chat.js b/server/routers/kfu-m-24-1/sber_mobile/socket-chat.js index d35c2a2..62102c5 100644 --- a/server/routers/kfu-m-24-1/sber_mobile/socket-chat.js +++ b/server/routers/kfu-m-24-1/sber_mobile/socket-chat.js @@ -5,13 +5,29 @@ class ChatSocketHandler { this.io = io; this.onlineUsers = new Map(); // Хранение онлайн пользователей: socket.id -> user info this.chatRooms = new Map(); // Хранение участников комнат: chat_id -> Set(socket.id) + this.realtimeSubscription = null; // Ссылка на подписку для управления + this.setupSocketHandlers(); + + try { + this.setupRealtimeSubscription(); // Добавляем Real-time подписки + } catch (error) { + // Ignore error + } + + // Запускаем тестирование через 2 секунды после инициализации + setTimeout(() => { + this.testRealtimeConnection(); + }, 2000); + + // Проверяем статус подписки через 5 секунд + setTimeout(() => { + this.checkSubscriptionStatus(); + }, 5000); } setupSocketHandlers() { this.io.on('connection', (socket) => { - console.log(`User connected: ${socket.id}`); - // Аутентификация пользователя socket.on('authenticate', async (data) => { await this.handleAuthentication(socket, data); @@ -84,10 +100,7 @@ class ChatSocketHandler { message: 'Successfully authenticated', user: userProfile }); - - console.log(`User ${user_id} authenticated with socket ${socket.id}`); } catch (error) { - console.error('Authentication error:', error); socket.emit('auth_error', { message: 'Authentication failed' }); } } @@ -105,7 +118,6 @@ class ChatSocketHandler { socket.emit('error', { message: 'chat_id is required' }); return; } - // Проверяем, что чат существует и пользователь имеет доступ к нему const supabase = getSupabaseClient(); const { data: chat, error } = await supabase @@ -140,7 +152,6 @@ class ChatSocketHandler { socket.emit('error', { message: 'Access denied to this chat' }); return; } - // Добавляем сокет в комнату socket.join(chat_id); @@ -148,8 +159,11 @@ class ChatSocketHandler { if (!this.chatRooms.has(chat_id)) { this.chatRooms.set(chat_id, new Set()); } + + const participantsBefore = this.chatRooms.get(chat_id).size; this.chatRooms.get(chat_id).add(socket.id); - + const participantsAfter = this.chatRooms.get(chat_id).size; + socket.emit('joined_chat', { chat_id, chat: chat, @@ -158,15 +172,13 @@ class ChatSocketHandler { // Уведомляем других участников о подключении const userInfo = this.onlineUsers.get(socket.id); + socket.to(chat_id).emit('user_joined', { chat_id, user: userInfo?.profile, timestamp: new Date() }); - - console.log(`User ${socket.user_id} joined chat ${chat_id}`); } catch (error) { - console.error('Join chat error:', error); socket.emit('error', { message: 'Failed to join chat' }); } } @@ -196,7 +208,7 @@ class ChatSocketHandler { timestamp: new Date() }); - console.log(`User ${socket.user_id} left chat ${chat_id}`); + } async handleSendMessage(socket, data) { @@ -243,9 +255,7 @@ class ChatSocketHandler { timestamp: new Date() }); - console.log(`Message sent to chat ${chat_id} by user ${socket.user_id}`); } catch (error) { - console.error('Send message error:', error); socket.emit('error', { message: 'Failed to send message' }); } } @@ -277,7 +287,6 @@ class ChatSocketHandler { } handleDisconnect(socket) { - console.log(`User disconnected: ${socket.id}`); // Удаляем пользователя из всех комнат this.chatRooms.forEach((participants, chat_id) => { @@ -326,11 +335,119 @@ class ChatSocketHandler { timestamp: new Date() }); } + + // Тестирование Real-time подписки + async testRealtimeConnection() { + try { + const supabase = getSupabaseClient(); + if (!supabase) { + return false; + } + + // Создаем тестовый канал для проверки подключения + const testChannel = supabase + .channel('test_connection') + .subscribe((status, error) => { + if (status === 'SUBSCRIBED') { + // Отписываемся от тестового канала + setTimeout(() => { + testChannel.unsubscribe(); + }, 2000); + } + }); + + return true; + } catch (error) { + return false; + } + } + + // Проверка статуса подписки + checkSubscriptionStatus() { + if (this.realtimeSubscription) { + return true; + } else { + return false; + } + } + + setupRealtimeSubscription() { + // Добавляем небольшую задержку, чтобы убедиться, что Supabase клиент инициализирован + setTimeout(() => { + this._doSetupRealtimeSubscription(); + }, 1000); + } + + _doSetupRealtimeSubscription() { + try { + const supabase = getSupabaseClient(); + + if (!supabase) { + return; + } + + // Подписываемся на изменения в таблице messages + const subscription = supabase + .channel('messages_changes') + .on( + 'postgres_changes', + { + event: 'INSERT', + schema: 'public', + table: 'messages' + }, + async (payload) => { + try { + const newMessage = payload.new; + if (!newMessage) { + return; + } + + if (!newMessage.chat_id) { + return; + } + + // Получаем профиль пользователя + const { data: userProfile, error: profileError } = await supabase + .from('user_profiles') + .select('id, full_name, avatar_url') + .eq('id', newMessage.user_id) + .single(); + + // Объединяем сообщение с профилем + const messageWithProfile = { + ...newMessage, + user_profiles: userProfile || null + }; + + // Проверяем, есть ли участники в чате + const chatRoomParticipants = this.chatRooms.get(newMessage.chat_id); + + // Отправляем сообщение через Socket.IO всем участникам чата + this.io.to(newMessage.chat_id).emit('new_message', { + message: messageWithProfile, + timestamp: new Date() + }); + } catch (callbackError) { + // Ignore error + } + } + ) + .subscribe(); + + // Сохраняем ссылку на подписку для возможности отписки + this.realtimeSubscription = subscription; + + } catch (error) { + // Ignore error + } + } } // Функция инициализации Socket.IO для чатов function initializeChatSocket(io) { const chatHandler = new ChatSocketHandler(io); + return chatHandler; } From 3af82f74787f28da146c9acd9d35f6b811d56801 Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 13 Jun 2025 19:44:45 +0300 Subject: [PATCH 04/16] fix system prompt --- .../sber_mobile/support-ai-agent/README.md | 63 ------------------- .../support-ai-agent/support-agent.ts | 42 +++++-------- .../kfu-m-24-1/sber_mobile/supportApi.js | 42 +------------ 3 files changed, 20 insertions(+), 127 deletions(-) delete mode 100644 server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/README.md diff --git a/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/README.md b/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/README.md deleted file mode 100644 index ccad3cd..0000000 --- a/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/README.md +++ /dev/null @@ -1,63 +0,0 @@ -# AI Support Agent - -AI-агент поддержки интегрирован в существующий `supportApi.js`. - -## Структура - -``` -support-ai-agent/ -├── gigachat.ts # Конфигурация GigaChat -├── support-agent.ts # Основной класс агента -└── README.md # Документация -``` - -## API - -### POST `/support` -Отправить сообщение в службу поддержки (теперь с AI-агентом). - -**Запрос:** -```json -{ - "user_id": "string", // Обязательно - "message": "string", // Обязательно - "system_prompt": "string" // Опционально - настройка поведения агента -} -``` - -**Ответ:** -```json -{ - "reply": "Ответ AI-агента", - "success": true -} -``` - -### POST `/support/configure` -Настроить системный промпт для конкретного пользователя. - -### DELETE `/support/history/:userId` -Очистить историю диалога пользователя. - -## Возможности - -- 🤖 Интеллектуальные ответы на основе GigaChat -- 💾 Сохранение всех сообщений в базу данных Supabase -- 🧠 Память контекста диалога для каждого пользователя -- ⚙️ Настраиваемые системные промпты -- 📊 Поддержка множественных пользователей - -## Примеры системных промптов - -### Техническая поддержка -``` -Ты - специалист технической поддержки мобильного приложения "Умный дом". -Помогай пользователям решать проблемы, объясняй функции простым языком, -проводи диагностику пошагово. Всегда будь дружелюбным и терпеливым. -``` - -### Общая поддержка клиентов -``` -Ты - профессиональный агент службы поддержки. Помогай решать вопросы -пользователей, отвечай вежливо и по существу, проявляй эмпатию. -``` \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/support-agent.ts b/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/support-agent.ts index 514a3c8..3ea39b7 100644 --- a/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/support-agent.ts +++ b/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/support-agent.ts @@ -5,7 +5,6 @@ import { MemorySaver } from '@langchain/langgraph'; import gigachat from './gigachat'; export interface SupportAgentConfig { - systemPrompt?: string; temperature?: number; threadId?: string; } @@ -22,11 +21,13 @@ export class SupportAgent { private agent: any; private systemPrompt: string; private threadId: string; + private isFirstMessage: boolean; constructor(config: SupportAgentConfig = {}) { - this.systemPrompt = config.systemPrompt || this.getDefaultSystemPrompt(); + this.systemPrompt = this.getDefaultSystemPrompt(); this.threadId = config.threadId || 'default'; this.memorySaver = new MemorySaver(); + this.isFirstMessage = true; // Настраиваем модель с заданной температурой this.llm = gigachat; @@ -58,30 +59,24 @@ export class SupportAgent { Всегда отвечай на русском языке и старайся быть максимально полезным.`; } - /** - * Обновить системный промпт - */ - public updateSystemPrompt(newPrompt: string): void { - this.systemPrompt = newPrompt; - } - /** - * Получить текущий системный промпт - */ - public getSystemPrompt(): string { - return this.systemPrompt; - } /** * Обработать сообщение пользователя и получить ответ */ public async processMessage(userMessage: string): Promise { try { - // Создаем сообщения с системным промптом - const messages = [ - new SystemMessage(this.systemPrompt), - new HumanMessage(userMessage) - ]; + // Создаем массив сообщений + const messages: BaseMessage[] = []; + + // Добавляем системный промпт только в первом сообщении + if (this.isFirstMessage) { + messages.push(new SystemMessage(this.systemPrompt)); + this.isFirstMessage = false; + } + + // Добавляем сообщение пользователя + messages.push(new HumanMessage(userMessage)); // Получаем ответ от агента const response = await this.agent.invoke({ @@ -120,12 +115,9 @@ export class SupportAgent { tools: [], checkpointSaver: this.memorySaver }); + // Сбрасываем флаг первого сообщения + this.isFirstMessage = true; } - /** - * Изменить ID потока (для работы с разными пользователями) - */ - public setThreadId(threadId: string): void { - this.threadId = threadId; - } + } \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/supportApi.js b/server/routers/kfu-m-24-1/sber_mobile/supportApi.js index de908a8..88f488c 100644 --- a/server/routers/kfu-m-24-1/sber_mobile/supportApi.js +++ b/server/routers/kfu-m-24-1/sber_mobile/supportApi.js @@ -8,15 +8,12 @@ const userAgents = new Map(); /** * Получить или создать агента для пользователя */ -function getUserAgent(userId, systemPrompt) { +function getUserAgent(userId) { if (!userAgents.has(userId)) { const config = { threadId: userId, temperature: 0.7 }; - if (systemPrompt) { - config.systemPrompt = systemPrompt; - } userAgents.set(userId, new SupportAgent(config)); } return userAgents.get(userId); @@ -25,7 +22,7 @@ function getUserAgent(userId, systemPrompt) { // POST /api/support router.post('/support', async (req, res) => { const supabase = getSupabaseClient(); - const { user_id, message, system_prompt } = req.body; + const { user_id, message } = req.body; if (!user_id || !message) { return res.status(400).json({ error: 'user_id и message обязательны' }); @@ -42,12 +39,7 @@ router.post('/support', async (req, res) => { } // Получаем агента для пользователя - const agent = getUserAgent(user_id, system_prompt); - - // Обновляем системный промпт если передан - if (system_prompt) { - agent.updateSystemPrompt(system_prompt); - } + const agent = getUserAgent(user_id); // Получаем ответ от AI-агента const aiResponse = await agent.processMessage(message); @@ -89,35 +81,7 @@ router.post('/support', async (req, res) => { } }); -// POST /api/support/configure - Настройка системного промпта -router.post('/support/configure', async (req, res) => { - const { user_id, system_prompt } = req.body; - - if (!user_id) { - return res.status(400).json({ error: 'user_id обязателен' }); - } - try { - const agent = getUserAgent(user_id, system_prompt); - - if (system_prompt) { - agent.updateSystemPrompt(system_prompt); - } - - res.json({ - message: 'Конфигурация агента обновлена', - current_system_prompt: agent.getSystemPrompt(), - success: true - }); - - } catch (error) { - console.error('Ошибка в /support/configure:', error); - res.status(500).json({ - error: 'Внутренняя ошибка сервера', - success: false - }); - } -}); // DELETE /api/support/history/:userId - Очистка истории диалога router.delete('/support/history/:userId', async (req, res) => { From 8dd8ec89300ca2aabc9712759e58d6fd655af143 Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 13 Jun 2025 21:07:13 +0300 Subject: [PATCH 05/16] add getting support chat history --- .../kfu-m-24-1/sber_mobile/supportApi.js | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/server/routers/kfu-m-24-1/sber_mobile/supportApi.js b/server/routers/kfu-m-24-1/sber_mobile/supportApi.js index 88f488c..39db5a7 100644 --- a/server/routers/kfu-m-24-1/sber_mobile/supportApi.js +++ b/server/routers/kfu-m-24-1/sber_mobile/supportApi.js @@ -19,6 +19,41 @@ function getUserAgent(userId) { return userAgents.get(userId); } +// GET /api/support - Получить историю сообщений пользователя +router.get('/support', async (req, res) => { + const supabase = getSupabaseClient(); + const { user_id } = req.query; + + if (!user_id) { + return res.status(400).json({ error: 'user_id обязателен' }); + } + + try { + // Получаем все сообщения пользователя из базы данных + const { data: messages, error } = await supabase + .from('support') + .select('*') + .eq('user_id', user_id) + .order('created_at', { ascending: true }); + + if (error) { + return res.status(400).json({ error: error.message }); + } + + res.json({ + messages: messages || [], + success: true + }); + + } catch (error) { + console.error('Ошибка в GET /support:', error); + res.status(500).json({ + error: 'Внутренняя ошибка сервера', + success: false + }); + } +}); + // POST /api/support router.post('/support', async (req, res) => { const supabase = getSupabaseClient(); From 5886270e298e119d14a34ff582c1ea02cc934459 Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 13 Jun 2025 22:31:32 +0300 Subject: [PATCH 06/16] add history tool --- .../support-ai-agent/support-agent.ts | 44 +++++++-------- .../support-ai-agent/support-context-tool.ts | 56 +++++++++++++++++++ 2 files changed, 77 insertions(+), 23 deletions(-) create mode 100644 server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/support-context-tool.ts diff --git a/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/support-agent.ts b/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/support-agent.ts index 3ea39b7..0850b95 100644 --- a/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/support-agent.ts +++ b/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/support-agent.ts @@ -3,6 +3,7 @@ import { ChatPromptTemplate, MessagesPlaceholder } from '@langchain/core/prompts import { createReactAgent } from '@langchain/langgraph/prebuilt'; import { MemorySaver } from '@langchain/langgraph'; import gigachat from './gigachat'; +import { SupportContextTool } from './support-context-tool'; export interface SupportAgentConfig { temperature?: number; @@ -22,30 +23,31 @@ export class SupportAgent { private systemPrompt: string; private threadId: string; private isFirstMessage: boolean; + private userId: string; constructor(config: SupportAgentConfig = {}) { this.systemPrompt = this.getDefaultSystemPrompt(); this.threadId = config.threadId || 'default'; + this.userId = this.threadId; this.memorySaver = new MemorySaver(); this.isFirstMessage = true; - // Настраиваем модель с заданной температурой this.llm = gigachat; if (config.temperature !== undefined) { this.llm.temperature = config.temperature; } - // Создаем агента без инструментов для простого чата + const tools = [ + new SupportContextTool(this.userId) + ]; + this.agent = createReactAgent({ llm: this.llm, - tools: [], + tools: tools, checkpointSaver: this.memorySaver }); } - /** - * Получить системный промпт по умолчанию для агента поддержки - */ private getDefaultSystemPrompt(): string { return `Ты - профессиональный агент службы поддержки. @@ -56,29 +58,26 @@ export class SupportAgent { - Проявлять эмпатию к проблемам пользователей - Если не знаешь ответ, честно сообщить об этом и предложить альтернативные способы получения помощи +ВАЖНО: У тебя есть доступ к инструменту get_support_context, который позволяет получить историю предыдущих сообщений пользователя. +ВСЕГДА используй этот инструмент ПЕРВЫМ ДЕЛОМ при получении каждого нового сообщения, чтобы понять контекст и предыдущие обращения пользователя. +Только после получения контекста отвечай на вопрос пользователя. + +Если в истории есть предыдущие обращения, обязательно ссылайся на них в своем ответе, показывая что помнишь предыдущее общение. + Всегда отвечай на русском языке и старайся быть максимально полезным.`; } - - - /** - * Обработать сообщение пользователя и получить ответ - */ public async processMessage(userMessage: string): Promise { try { - // Создаем массив сообщений const messages: BaseMessage[] = []; - // Добавляем системный промпт только в первом сообщении if (this.isFirstMessage) { messages.push(new SystemMessage(this.systemPrompt)); this.isFirstMessage = false; } - // Добавляем сообщение пользователя messages.push(new HumanMessage(userMessage)); - // Получаем ответ от агента const response = await this.agent.invoke({ messages: messages }, { @@ -87,7 +86,6 @@ export class SupportAgent { } }); - // Извлекаем последнее сообщение от ассистента const lastMessage = response.messages[response.messages.length - 1]; return { @@ -105,19 +103,19 @@ export class SupportAgent { } } - /** - * Очистить историю диалога - */ public async clearHistory(): Promise { this.memorySaver = new MemorySaver(); + + const tools = [ + new SupportContextTool(this.userId) + ]; + this.agent = createReactAgent({ llm: this.llm, - tools: [], + tools: tools, checkpointSaver: this.memorySaver }); - // Сбрасываем флаг первого сообщения + this.isFirstMessage = true; } - - } \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/support-context-tool.ts b/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/support-context-tool.ts new file mode 100644 index 0000000..94a573c --- /dev/null +++ b/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/support-context-tool.ts @@ -0,0 +1,56 @@ +import { StructuredTool, ToolRunnableConfig } from '@langchain/core/tools'; +import { z } from 'zod'; +import { CallbackManagerForToolRun } from '@langchain/core/callbacks/manager'; +import { getSupabaseClient } from '../supabaseClient'; + +export class SupportContextTool extends StructuredTool { + name = 'get_support_context'; + description = 'Получает последние 10 сообщений из истории поддержки для понимания контекста разговора. Используй этот инструмент в начале разговора.'; + + schema = z.object({}); + + private userId: string; + + constructor(userId: string) { + super(); + this.userId = userId; + } + + protected async _call( + arg: z.infer, + runManager?: CallbackManagerForToolRun, + parentConfig?: ToolRunnableConfig> + ): Promise { + try { + const supabase = getSupabaseClient(); + + const { data: messages, error } = await supabase + .from('support') + .select('message, is_from_user, created_at') + .eq('user_id', this.userId) + .order('created_at', { ascending: false }) + .limit(10); + + if (error) { + return 'Не удалось получить историю сообщений.'; + } + + if (!messages || messages.length === 0) { + return 'История сообщений поддержки пуста. Это первое обращение пользователя.'; + } + + const chronologicalMessages = messages.reverse(); + + const contextMessages = chronologicalMessages.map((msg, index) => { + const role = msg.is_from_user ? 'Пользователь' : 'Агент поддержки'; + const time = new Date(msg.created_at).toLocaleString('ru-RU'); + return `${index + 1}. [${time}] ${role}: ${msg.message}`; + }).join('\n'); + + return `Последние сообщения из истории поддержки (${messages.length} сообщений):\n\n${contextMessages}\n\nИспользуй этот контекст для понимания предыдущих обращений пользователя и предоставления более точных ответов.`; + + } catch (error) { + return 'Произошла ошибка при получении истории сообщений.'; + } + } +} \ No newline at end of file From 1aeb62d490ff7fe586b040d3116a94909a75705f Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 13 Jun 2025 23:15:13 +0300 Subject: [PATCH 07/16] add rag tool --- package.json | 1 + .../support-ai-agent/knowledge-base-tool.ts | 41 +++++++++++++++++++ .../support-ai-agent/support-agent.ts | 26 +++++++++--- .../support-ai-agent/vector-store.ts | 33 +++++++++++++++ 4 files changed, 95 insertions(+), 6 deletions(-) create mode 100644 server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/knowledge-base-tool.ts create mode 100644 server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/vector-store.ts diff --git a/package.json b/package.json index 10a157b..a43b5aa 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "gigachat": "^0.0.14", "jsdom": "^25.0.1", "jsonwebtoken": "^9.0.2", + "langchain": "^0.3.7", "langchain-gigachat": "^0.0.11", "mongodb": "^6.12.0", "mongoose": "^8.9.2", diff --git a/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/knowledge-base-tool.ts b/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/knowledge-base-tool.ts new file mode 100644 index 0000000..84a681d --- /dev/null +++ b/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/knowledge-base-tool.ts @@ -0,0 +1,41 @@ +import { StructuredTool, ToolRunnableConfig } from '@langchain/core/tools'; +import { z } from 'zod'; +import { CallbackManagerForToolRun } from '@langchain/core/callbacks/manager'; +import { getVectorStore } from './vector-store'; + +export class KnowledgeBaseTool extends StructuredTool { + name = 'search_knowledge_base'; + description = 'Ищет информацию в базе знаний компании о процессах, оплатах, подаче заявок, правилах и документах УК. Используй этот инструмент для вопросов, требующих специфических знаний о компании.'; + + schema = z.object({ + query: z.string().describe('Поисковый запрос для поиска в базе знаний'), + }); + + protected async _call( + arg: z.infer, + runManager?: CallbackManagerForToolRun, + parentConfig?: ToolRunnableConfig> + ): Promise { + try { + const vectorStore = getVectorStore(); + const retriever = vectorStore.asRetriever({ + k: 5 + }); + + const relevantDocs = await retriever.getRelevantDocuments(arg.query); + + if (!relevantDocs || relevantDocs.length === 0) { + return 'В базе знаний не найдено информации по данному запросу. Возможно, стоит переформулировать вопрос или обратиться к специалисту.'; + } + + const formattedDocs = relevantDocs.map((doc, index) => { + return `Документ ${index + 1}:\n${doc.pageContent}\n`; + }).join('\n---\n'); + + return `Найдена следующая информация в базе знаний компании:\n\n${formattedDocs}\n\nИспользуй эту информацию для ответа на вопрос пользователя.`; + + } catch (error) { + return 'Произошла ошибка при поиске в базе знаний. Попробуйте переформулировать запрос.'; + } + } +} \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/support-agent.ts b/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/support-agent.ts index 0850b95..56578ec 100644 --- a/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/support-agent.ts +++ b/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/support-agent.ts @@ -4,6 +4,7 @@ import { createReactAgent } from '@langchain/langgraph/prebuilt'; import { MemorySaver } from '@langchain/langgraph'; import gigachat from './gigachat'; import { SupportContextTool } from './support-context-tool'; +import { KnowledgeBaseTool } from './knowledge-base-tool'; export interface SupportAgentConfig { temperature?: number; @@ -38,7 +39,8 @@ export class SupportAgent { } const tools = [ - new SupportContextTool(this.userId) + new SupportContextTool(this.userId), + new KnowledgeBaseTool() ]; this.agent = createReactAgent({ @@ -58,11 +60,22 @@ export class SupportAgent { - Проявлять эмпатию к проблемам пользователей - Если не знаешь ответ, честно сообщить об этом и предложить альтернативные способы получения помощи -ВАЖНО: У тебя есть доступ к инструменту get_support_context, который позволяет получить историю предыдущих сообщений пользователя. -ВСЕГДА используй этот инструмент ПЕРВЫМ ДЕЛОМ при получении каждого нового сообщения, чтобы понять контекст и предыдущие обращения пользователя. -Только после получения контекста отвечай на вопрос пользователя. +У тебя есть доступ к двум инструментам: -Если в истории есть предыдущие обращения, обязательно ссылайся на них в своем ответе, показывая что помнишь предыдущее общение. +1. get_support_context - получает историю предыдущих сообщений пользователя + ВСЕГДА используй этот инструмент ПЕРВЫМ ДЕЛОМ при получении каждого нового сообщения + +2. search_knowledge_base - ищет информацию в базе знаний компании + Используй этот инструмент для вопросов о: + - Процессах оплаты и тарифах + - Подаче заявок и документооборота + - Правилах и регламентах УК + - Технических вопросах приложения + - Любых специфических вопросах о компании + +ВАЖНО: Сначала получи контекст, затем при необходимости найди информацию в базе знаний, и только после этого отвечай пользователю. + +Если в истории есть предыдущие обращения, обязательно ссылайся на них в своем ответе. Всегда отвечай на русском языке и старайся быть максимально полезным.`; } @@ -107,7 +120,8 @@ export class SupportAgent { this.memorySaver = new MemorySaver(); const tools = [ - new SupportContextTool(this.userId) + new SupportContextTool(this.userId), + new KnowledgeBaseTool() ]; this.agent = createReactAgent({ diff --git a/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/vector-store.ts b/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/vector-store.ts new file mode 100644 index 0000000..3841672 --- /dev/null +++ b/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/vector-store.ts @@ -0,0 +1,33 @@ +import { createClient } from '@supabase/supabase-js'; +import { SupabaseVectorStore } from '@langchain/community/vectorstores/supabase'; +import { GigaChatEmbeddings } from 'langchain-gigachat'; +import { Agent } from 'node:https'; + +const httpsAgent = new Agent({ + rejectUnauthorized: false, +}); + +let vectorStoreInstance: SupabaseVectorStore | null = null; + +export function getVectorStore(): SupabaseVectorStore { + if (!vectorStoreInstance) { + const client = createClient( + process.env.RAG_SUPABASE_URL!, + process.env.RAG_SUPABASE_SERVICE_ROLE_KEY!, + ); + + vectorStoreInstance = new SupabaseVectorStore( + new GigaChatEmbeddings({ + credentials: process.env.GIGA_AUTH, + httpsAgent, + }), + { + client, + tableName: 'slon', + queryName: 'match_slon' + } + ); + } + + return vectorStoreInstance; +} \ No newline at end of file From 7bd82fedced6f62bd7bd555de797dd90659b9f68 Mon Sep 17 00:00:00 2001 From: DenAntonov Date: Fri, 13 Jun 2025 23:52:02 +0300 Subject: [PATCH 08/16] change socket settings --- server/index.ts | 13 +------------ server/routers/kfu-m-24-1/sber_mobile/index.js | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/server/index.ts b/server/index.ts index 3d4f58e..d39f65f 100644 --- a/server/index.ts +++ b/server/index.ts @@ -20,9 +20,7 @@ import gamehubRouter from './routers/gamehub' import escRouter from './routers/esc' import connectmeRouter from './routers/connectme' import questioneerRouter from './routers/questioneer' -import { setIo, getIo } from './io' -// Импорт обработчика чата -const { initializeChatSocket } = require('./routers/kfu-m-24-1/sber_mobile/socket-chat') +import { setIo } from './io' export const app = express() @@ -67,15 +65,6 @@ const initServer = async () => { console.log('warming up 🔥') const server = setIo(app) - - // Инициализация Socket.IO для чата - const io = getIo() - if (io) { - const chatHandler = initializeChatSocket(io) - // Сохраняем ссылку на chat handler для доступа из эндпоинтов - io.chatHandler = chatHandler - console.log('✅ Socket.IO для чата инициализирован') - } const sess = { secret: "super-secret-key", diff --git a/server/routers/kfu-m-24-1/sber_mobile/index.js b/server/routers/kfu-m-24-1/sber_mobile/index.js index 4dfad13..854576e 100644 --- a/server/routers/kfu-m-24-1/sber_mobile/index.js +++ b/server/routers/kfu-m-24-1/sber_mobile/index.js @@ -15,7 +15,10 @@ const buildingsRouter = require('./buildings'); const userApartmentsRouter = require('./user_apartments'); const avatarRouter = require('./media'); const supportRouter = require('./supportApi'); -const { getIo } = require('../../../io'); +const {setIo, getIo } = require('../../../io'); +// Импорт обработчика чата +const { initializeChatSocket } = require('./socket-chat') + module.exports = router; @@ -34,4 +37,14 @@ router.use('', apartmentsRouter); router.use('', buildingsRouter); router.use('', userApartmentsRouter); router.use('', avatarRouter); -router.use('', supportRouter); \ No newline at end of file +router.use('', supportRouter); + + + // Инициализация Socket.IO для чата + const io = getIo() + if (io) { + const chatHandler = initializeChatSocket(io) + // Сохраняем ссылку на chat handler для доступа из эндпоинтов + io.chatHandler = chatHandler + console.log('✅ Socket.IO для чата инициализирован') + } \ No newline at end of file From ca81e19d14ab5064dcfab15d241dc9de6ba2d927 Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 14 Jun 2025 00:16:02 +0300 Subject: [PATCH 09/16] add tickets creation --- .../kfu-m-24-1/sber_mobile/DB_Scheme.txt | 3 +- .../support-ai-agent/create-ticket-tool.ts | 66 +++++++++++++++++ .../support-ai-agent/support-agent.ts | 73 +++++++++++++------ .../kfu-m-24-1/sber_mobile/supportApi.js | 8 +- 4 files changed, 123 insertions(+), 27 deletions(-) create mode 100644 server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/create-ticket-tool.ts diff --git a/server/routers/kfu-m-24-1/sber_mobile/DB_Scheme.txt b/server/routers/kfu-m-24-1/sber_mobile/DB_Scheme.txt index 0a7b25a..367f42a 100644 --- a/server/routers/kfu-m-24-1/sber_mobile/DB_Scheme.txt +++ b/server/routers/kfu-m-24-1/sber_mobile/DB_Scheme.txt @@ -170,7 +170,7 @@ CREATE TABLE payment_service_details ( CREATE TABLE tickets ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL REFERENCES auth.users(id), - building_id UUID NOT NULL REFERENCES buildings(id), + apartment_id UUID NOT NULL REFERENCES apartments(id), title TEXT NOT NULL, description TEXT NOT NULL, status TEXT NOT NULL CHECK (status IN ('open', 'in_progress', 'resolved')), @@ -197,6 +197,7 @@ CREATE INDEX idx_votes_initiative ON votes(initiative_id); CREATE INDEX idx_messages_chat ON messages(chat_id); CREATE INDEX idx_cameras_building ON cameras(building_id); CREATE INDEX idx_tickets_user ON tickets(user_id); +CREATE INDEX idx_tickets_apartment ON tickets(apartment_id); CREATE INDEX idx_apartments_building ON apartments(building_id); CREATE INDEX idx_apartment_residents_apartment ON apartment_residents(apartment_id); CREATE INDEX idx_apartment_residents_user ON apartment_residents(user_id); diff --git a/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/create-ticket-tool.ts b/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/create-ticket-tool.ts new file mode 100644 index 0000000..ff88787 --- /dev/null +++ b/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/create-ticket-tool.ts @@ -0,0 +1,66 @@ +import { StructuredTool, ToolRunnableConfig } from '@langchain/core/tools'; +import { z } from 'zod'; +import { CallbackManagerForToolRun } from '@langchain/core/callbacks/manager'; +import { getSupabaseClient } from '../supabaseClient'; + +export class CreateTicketTool extends StructuredTool { + name = 'create_ticket'; + description = 'Создает заявку в системе. ВАЖНО: используй этот инструмент ТОЛЬКО после получения явного согласия пользователя на создание заявки с конкретным текстом.'; + + schema = z.object({ + title: z.string().describe('Заголовок заявки'), + description: z.string().describe('Подробное описание проблемы'), + category: z.string().describe('Категория заявки (например: ремонт, уборка, техническая_поддержка, жалоба)'), + }); + + private userId: string; + private apartmentId: string; + + constructor(userId: string, apartmentId: string) { + super(); + this.userId = userId; + this.apartmentId = apartmentId; + } + + protected async _call( + arg: z.infer, + runManager?: CallbackManagerForToolRun, + parentConfig?: ToolRunnableConfig> + ): Promise { + try { + if (!this.apartmentId) { + return 'Не удалось определить вашу квартиру. Обратитесь к администратору для создания заявки.'; + } + + const supabase = getSupabaseClient(); + + const { data: ticket, error } = await supabase + .from('tickets') + .insert({ + user_id: this.userId, + apartment_id: this.apartmentId, + title: arg.title, + description: arg.description, + category: arg.category, + status: 'open' + }) + .select() + .single(); + + if (error) { + return 'Произошла ошибка при создании заявки. Попробуйте позже или обратитесь к администратору.'; + } + + return `Заявка успешно создана! +Номер заявки: ${ticket.id} +Заголовок: ${ticket.title} +Статус: Открыта +Дата создания: ${new Date(ticket.created_at).toLocaleString('ru-RU')} + +Ваша заявка принята в работу. Мы свяжемся с вами в ближайшее время.`; + + } catch (error) { + return 'Произошла техническая ошибка при создании заявки. Пожалуйста, попробуйте позже.'; + } + } +} \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/support-agent.ts b/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/support-agent.ts index 56578ec..93816e6 100644 --- a/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/support-agent.ts +++ b/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/support-agent.ts @@ -5,6 +5,7 @@ import { MemorySaver } from '@langchain/langgraph'; import gigachat from './gigachat'; import { SupportContextTool } from './support-context-tool'; import { KnowledgeBaseTool } from './knowledge-base-tool'; +import { CreateTicketTool } from './create-ticket-tool'; export interface SupportAgentConfig { temperature?: number; @@ -51,36 +52,52 @@ export class SupportAgent { } private getDefaultSystemPrompt(): string { - return `Ты - профессиональный агент службы поддержки. + return `Ты - профессиональный агент службы поддержки управляющей компании. -Твои основные задачи: -- Помогать пользователям решать их вопросы и проблемы -- Отвечать вежливо, профессионально и по существу -- Предоставлять четкие и понятные инструкции -- Проявлять эмпатию к проблемам пользователей -- Если не знаешь ответ, честно сообщить об этом и предложить альтернативные способы получения помощи +ОСНОВНЫЕ ПРИНЦИПЫ: +- Помогай только с реальными проблемами и вопросами, связанными с ЖКХ, управляющей компанией и приложением +- Будь вежливым, профессиональным и по существу +- Если вопрос неуместен, не связан с твоими обязанностями или является развлекательным - вежливо откажись и перенаправь к основным темам -У тебя есть доступ к двум инструментам: +ДОСТУПНЫЕ ИНСТРУМЕНТЫ: -1. get_support_context - получает историю предыдущих сообщений пользователя - ВСЕГДА используй этот инструмент ПЕРВЫМ ДЕЛОМ при получении каждого нового сообщения +1. get_support_context - получает историю сообщений пользователя + ВСЕГДА используй ПЕРВЫМ при каждом новом сообщении -2. search_knowledge_base - ищет информацию в базе знаний компании - Используй этот инструмент для вопросов о: - - Процессах оплаты и тарифах - - Подаче заявок и документооборота +2. search_knowledge_base - поиск в базе знаний компании + Используй ТОЛЬКО для серьезных вопросов о: + - Процессах оплаты ЖКХ и тарифах + - Подаче заявок и документообороте - Правилах и регламентах УК - Технических вопросах приложения - - Любых специфических вопросах о компании + - Процедурах и инструкциях компании -ВАЖНО: Сначала получи контекст, затем при необходимости найди информацию в базе знаний, и только после этого отвечай пользователю. +3. create_ticket - создание заявки в системе + Используй ТОЛЬКО когда: + - Пользователь сообщает о реальной проблеме (поломка, неисправность, жалоба) + - Проблема требует вмешательства УК или технических служб + - ОБЯЗАТЕЛЬНО сначала покажи пользователю полный текст заявки + - Получи ЯВНОЕ согласие пользователя перед созданием + - НЕ создавай заявки для консультационных вопросов -Если в истории есть предыдущие обращения, обязательно ссылайся на них в своем ответе. +ПРАВИЛА ИСПОЛЬЗОВАНИЯ ИНСТРУМЕНТОВ: +- НЕ используй search_knowledge_base и create_ticket для: + * Общих вопросов и болтовни + * Развлекательных запросов + * Вопросов не по теме ЖКХ/УК + * Простых консультаций, которые можно решить обычным ответом -Всегда отвечай на русском языке и старайся быть максимально полезным.`; +АЛГОРИТМ РАБОТЫ: +1. Получи контекст истории сообщений +2. Определи, является ли вопрос уместным и серьезным +3. Если нужна специфическая информация - найди в базе знаний +4. Если нужно создать заявку - покажи текст и получи согласие +5. Дай полный и полезный ответ + +Всегда отвечай на русском языке и фокусируйся на помощи с реальными проблемами ЖКХ.`; } - public async processMessage(userMessage: string): Promise { + public async processMessage(userMessage: string, apartmentId?: string): Promise { try { const messages: BaseMessage[] = []; @@ -91,7 +108,21 @@ export class SupportAgent { messages.push(new HumanMessage(userMessage)); - const response = await this.agent.invoke({ + // Создаем инструменты с актуальным apartmentId + const tools = [ + new SupportContextTool(this.userId), + new KnowledgeBaseTool(), + new CreateTicketTool(this.userId, apartmentId || '') + ]; + + // Пересоздаем агента с обновленными инструментами + const tempAgent = createReactAgent({ + llm: this.llm, + tools: tools, + checkpointSaver: this.memorySaver + }); + + const response = await tempAgent.invoke({ messages: messages }, { configurable: { @@ -102,7 +133,7 @@ export class SupportAgent { const lastMessage = response.messages[response.messages.length - 1]; return { - content: lastMessage.content || 'Извините, не удалось сформировать ответ.', + content: typeof lastMessage.content === 'string' ? lastMessage.content : 'Извините, не удалось сформировать ответ.', success: true }; diff --git a/server/routers/kfu-m-24-1/sber_mobile/supportApi.js b/server/routers/kfu-m-24-1/sber_mobile/supportApi.js index 39db5a7..1babe43 100644 --- a/server/routers/kfu-m-24-1/sber_mobile/supportApi.js +++ b/server/routers/kfu-m-24-1/sber_mobile/supportApi.js @@ -57,7 +57,7 @@ router.get('/support', async (req, res) => { // POST /api/support router.post('/support', async (req, res) => { const supabase = getSupabaseClient(); - const { user_id, message } = req.body; + const { user_id, message, apartment_id } = req.body; if (!user_id || !message) { return res.status(400).json({ error: 'user_id и message обязательны' }); @@ -76,8 +76,8 @@ router.post('/support', async (req, res) => { // Получаем агента для пользователя const agent = getUserAgent(user_id); - // Получаем ответ от AI-агента - const aiResponse = await agent.processMessage(message); + // Получаем ответ от AI-агента, передавая apartment_id + const aiResponse = await agent.processMessage(message, apartment_id); if (!aiResponse.success) { console.error('Ошибка AI-агента:', aiResponse.error); @@ -116,8 +116,6 @@ router.post('/support', async (req, res) => { } }); - - // DELETE /api/support/history/:userId - Очистка истории диалога router.delete('/support/history/:userId', async (req, res) => { const { userId } = req.params; From bde67dc7c310a1e542d2d378267ae9ef78b82a72 Mon Sep 17 00:00:00 2001 From: DenAntonov Date: Sat, 14 Jun 2025 10:30:12 +0300 Subject: [PATCH 10/16] fix socket server --- server/io.ts | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/server/io.ts b/server/io.ts index 4121b06..71833d6 100644 --- a/server/io.ts +++ b/server/io.ts @@ -5,14 +5,7 @@ let io = null export const setIo = (app) => { const server = createServer(app) - io = new Server(server, { - cors: { - origin: "*", - methods: ["GET", "POST"], - credentials: false - }, - transports: ['websocket', 'polling'] - }) + io = new Server(server, {}) return server } From 580651094f42183c44d45b919cd5260c5c1d591f Mon Sep 17 00:00:00 2001 From: DenAntonov Date: Sat, 14 Jun 2025 13:36:06 +0300 Subject: [PATCH 11/16] remove websocket add polling --- server/index.ts | 18 +- .../routers/kfu-m-24-1/sber_mobile/index.js | 14 +- .../kfu-m-24-1/sber_mobile/polling-chat.js | 822 ++++++++++++++++++ .../kfu-m-24-1/sber_mobile/socket-chat.js | 457 ---------- .../kfu-m-24-1/sber_mobile/supabaseClient.js | 65 +- 5 files changed, 894 insertions(+), 482 deletions(-) create mode 100644 server/routers/kfu-m-24-1/sber_mobile/polling-chat.js delete mode 100644 server/routers/kfu-m-24-1/sber_mobile/socket-chat.js diff --git a/server/index.ts b/server/index.ts index d39f65f..7f1d913 100644 --- a/server/index.ts +++ b/server/index.ts @@ -21,6 +21,7 @@ import escRouter from './routers/esc' import connectmeRouter from './routers/connectme' import questioneerRouter from './routers/questioneer' import { setIo } from './io' +const { createChatPollingRouter } = require('./routers/kfu-m-24-1/sber_mobile/polling-chat') export const app = express() @@ -64,8 +65,6 @@ const initServer = async () => { console.log('warming up 🔥') - const server = setIo(app) - const sess = { secret: "super-secret-key", resave: true, @@ -90,10 +89,18 @@ const initServer = async () => { ) app.use(root) + // Инициализация Polling для чата (после настройки middleware) + const { router: chatPollingRouter, chatHandler } = createChatPollingRouter(express) + + /** * Добавляйте сюда свои routers. */ app.use("/kfu-m-24-1", kfuM241Router) + + // Добавляем Polling роутер для чата + app.use("/kfu-m-24-1/sber_mobile", chatPollingRouter) + app.use("/epja-2024-1", epja20241Router) app.use("/v1/todo", todoRouter) app.use("/dogsitters-finder", dogsittersFinderRouter) @@ -109,9 +116,10 @@ const initServer = async () => { app.use(errorHandler) - server.listen(process.env.PORT ?? 8044, () => + // Создаем обычный HTTP сервер + const server = app.listen(process.env.PORT ?? 8044, () => { console.log(`🚀 Сервер запущен на http://localhost:${process.env.PORT ?? 8044}`) - ) + }) // Обработка сигналов завершения процесса process.on('SIGTERM', () => { @@ -145,6 +153,8 @@ const initServer = async () => { process.exit(1) }) }) + + return server } initServer().catch(console.error) diff --git a/server/routers/kfu-m-24-1/sber_mobile/index.js b/server/routers/kfu-m-24-1/sber_mobile/index.js index 854576e..2fdc6bd 100644 --- a/server/routers/kfu-m-24-1/sber_mobile/index.js +++ b/server/routers/kfu-m-24-1/sber_mobile/index.js @@ -15,9 +15,6 @@ const buildingsRouter = require('./buildings'); const userApartmentsRouter = require('./user_apartments'); const avatarRouter = require('./media'); const supportRouter = require('./supportApi'); -const {setIo, getIo } = require('../../../io'); -// Импорт обработчика чата -const { initializeChatSocket } = require('./socket-chat') module.exports = router; @@ -39,12 +36,5 @@ router.use('', userApartmentsRouter); router.use('', avatarRouter); router.use('', supportRouter); - - // Инициализация Socket.IO для чата - const io = getIo() - if (io) { - const chatHandler = initializeChatSocket(io) - // Сохраняем ссылку на chat handler для доступа из эндпоинтов - io.chatHandler = chatHandler - console.log('✅ Socket.IO для чата инициализирован') - } \ No newline at end of file + + diff --git a/server/routers/kfu-m-24-1/sber_mobile/polling-chat.js b/server/routers/kfu-m-24-1/sber_mobile/polling-chat.js new file mode 100644 index 0000000..db528ca --- /dev/null +++ b/server/routers/kfu-m-24-1/sber_mobile/polling-chat.js @@ -0,0 +1,822 @@ +const { getSupabaseClient, initializationPromise } = require('./supabaseClient'); + +class ChatPollingHandler { + constructor() { + this.connectedClients = new Map(); // user_id -> { user_info, chats: Set(), lastActivity: Date } + this.chatParticipants = new Map(); // chat_id -> Set(user_id) + this.userEventQueues = new Map(); // user_id -> [{id, event, data, timestamp}] + this.eventIdCounter = 0; + this.realtimeSubscription = null; + + // Инициализируем Supabase подписку с задержкой и проверками + this.initializeWithRetry(); + + // Очистка старых событий каждые 5 минут + setInterval(() => { + this.cleanupOldEvents(); + }, 5 * 60 * 1000); + } + + // Инициализация с повторными попытками + async initializeWithRetry() { + try { + // Сначала ждем завершения основной инициализации + await initializationPromise; + + this.setupRealtimeSubscription(); + this.testRealtimeConnection(); + return; + + } catch (error) { + console.log('❌ [Supabase] Основная инициализация неудачна, пробуем альтернативный подход'); + } + + // Если основная инициализация не удалась, используем повторные попытки + let attempts = 0; + const maxAttempts = 10; + const baseDelay = 2000; // 2 секунды + + while (attempts < maxAttempts) { + try { + attempts++; + + // Ждем перед попыткой + await new Promise(resolve => setTimeout(resolve, baseDelay * attempts)); + + // Проверяем готовность Supabase клиента + const supabase = getSupabaseClient(); + if (supabase) { + this.setupRealtimeSubscription(); + this.testRealtimeConnection(); + return; // Успех, выходим + } + } catch (error) { + console.log(`❌ [Supabase] Попытка #${attempts} неудачна:`, error.message); + + if (attempts === maxAttempts) { + console.error('❌ [Supabase] Все попытки инициализации исчерпаны'); + console.error('❌ [Supabase] Realtime подписка будет недоступна'); + return; + } + } + } + } + + // Аутентификация пользователя + async handleAuthentication(req, res) { + const { user_id, token } = req.body; + + if (!user_id) { + res.status(400).json({ error: 'user_id is required' }); + return; + } + + try { + // Проверяем пользователя в базе данных + const supabase = getSupabaseClient(); + const { data: userProfile, error } = await supabase + .from('user_profiles') + .select('*') + .eq('id', user_id) + .single(); + + if (error) { + console.log('❌ [Polling Server] Пользователь не найден:', error); + res.status(401).json({ error: 'User not found' }); + return; + } + + // Регистрируем пользователя + this.connectedClients.set(user_id, { + user_info: { + user_id, + profile: userProfile, + last_seen: new Date() + }, + chats: new Set(), + lastActivity: new Date() + }); + + // Создаем очередь событий для пользователя + if (!this.userEventQueues.has(user_id)) { + this.userEventQueues.set(user_id, []); + } + + // Добавляем событие аутентификации в очередь + this.addEventToQueue(user_id, 'authenticated', { + message: 'Successfully authenticated', + user: userProfile + }); + + res.json({ + success: true, + message: 'Successfully authenticated', + user: userProfile + }); + + } catch (error) { + console.error('❌ [Polling Server] Ошибка аутентификации:', error); + res.status(500).json({ error: 'Authentication failed' }); + } + } + + // Эндпоинт для получения событий (polling) + async handleGetEvents(req, res) { + try { + const { user_id, last_event_id } = req.query; + + if (!user_id) { + res.status(400).json({ error: 'user_id is required' }); + return; + } + + const client = this.connectedClients.get(user_id); + if (!client) { + res.status(401).json({ error: 'Not authenticated' }); + return; + } + + // Обновляем время последней активности + client.lastActivity = new Date(); + + // Получаем очередь событий пользователя + const eventQueue = this.userEventQueues.get(user_id) || []; + + // Фильтруем события после last_event_id + const lastEventId = parseInt(last_event_id) || 0; + const newEvents = eventQueue.filter(event => event.id > lastEventId); + + res.json({ + success: true, + events: newEvents, + last_event_id: eventQueue.length > 0 ? Math.max(...eventQueue.map(e => e.id)) : lastEventId + }); + + } catch (error) { + console.error('❌ [Polling Server] Ошибка получения событий:', error); + res.status(500).json({ error: 'Failed to get events' }); + } + } + + // HTTP эндпоинт для присоединения к чату + async handleJoinChat(req, res) { + try { + const { user_id, chat_id } = req.body; + + if (!user_id || !chat_id) { + res.status(400).json({ error: 'user_id and chat_id are required' }); + return; + } + + const client = this.connectedClients.get(user_id); + if (!client) { + res.status(401).json({ error: 'Not authenticated' }); + return; + } + + // Проверяем, что чат существует и пользователь имеет доступ к нему + const supabase = getSupabaseClient(); + const { data: chat, error } = await supabase + .from('chats') + .select(` + *, + buildings ( + management_company_id, + apartments ( + apartment_residents ( + user_id + ) + ) + ) + `) + .eq('id', chat_id) + .single(); + + if (error || !chat) { + res.status(404).json({ error: 'Chat not found' }); + return; + } + + // Проверяем доступ пользователя к чату через квартиры в доме + const hasAccess = chat.buildings.apartments.some(apartment => + apartment.apartment_residents.some(resident => + resident.user_id === user_id + ) + ); + + if (!hasAccess) { + res.status(403).json({ error: 'Access denied to this chat' }); + return; + } + + // Добавляем пользователя в чат + client.chats.add(chat_id); + + if (!this.chatParticipants.has(chat_id)) { + this.chatParticipants.set(chat_id, new Set()); + } + this.chatParticipants.get(chat_id).add(user_id); + + // Добавляем событие присоединения в очередь пользователя + this.addEventToQueue(user_id, 'joined_chat', { + chat_id, + chat: chat, + message: 'Successfully joined chat' + }); + + // Уведомляем других участников о подключении + this.broadcastToChatExcludeUser(chat_id, user_id, 'user_joined', { + chat_id, + user: client.user_info.profile, + timestamp: new Date() + }); + + res.json({ success: true, message: 'Joined chat successfully' }); + + } catch (error) { + res.status(500).json({ error: 'Failed to join chat' }); + } + } + + // HTTP эндпоинт для покидания чата + async handleLeaveChat(req, res) { + try { + const { user_id, chat_id } = req.body; + + if (!user_id || !chat_id) { + res.status(400).json({ error: 'user_id and chat_id are required' }); + return; + } + + const client = this.connectedClients.get(user_id); + if (!client) { + res.status(401).json({ error: 'Not authenticated' }); + return; + } + + // Удаляем пользователя из чата + client.chats.delete(chat_id); + + if (this.chatParticipants.has(chat_id)) { + this.chatParticipants.get(chat_id).delete(user_id); + + // Если чат пуст, удаляем его + if (this.chatParticipants.get(chat_id).size === 0) { + this.chatParticipants.delete(chat_id); + } + } + + // Уведомляем других участников об отключении + this.broadcastToChatExcludeUser(chat_id, user_id, 'user_left', { + chat_id, + user: client.user_info.profile, + timestamp: new Date() + }); + + res.json({ success: true, message: 'Left chat successfully' }); + + } catch (error) { + res.status(500).json({ error: 'Failed to leave chat' }); + } + } + + // HTTP эндпоинт для отправки сообщения + async handleSendMessage(req, res) { + try { + const { user_id, chat_id, text } = req.body; + + if (!user_id || !chat_id || !text) { + res.status(400).json({ error: 'user_id, chat_id and text are required' }); + return; + } + + const client = this.connectedClients.get(user_id); + if (!client) { + res.status(401).json({ error: 'Not authenticated' }); + return; + } + + if (!client.chats.has(chat_id)) { + res.status(403).json({ error: 'Not joined to this chat' }); + return; + } + + // Сохраняем сообщение в базу данных + const supabase = getSupabaseClient(); + const { data: message, error } = await supabase + .from('messages') + .insert({ + chat_id, + user_id, + text + }) + .select(` + *, + user_profiles ( + id, + full_name, + avatar_url + ) + `) + .single(); + + if (error) { + res.status(500).json({ error: 'Failed to save message' }); + return; + } + + // Отправляем сообщение всем участникам чата + this.broadcastToChat(chat_id, 'new_message', { + message, + timestamp: new Date() + }); + + res.json({ success: true, message: 'Message sent successfully' }); + + } catch (error) { + res.status(500).json({ error: 'Failed to send message' }); + } + } + + // HTTP эндпоинт для индикации печатания + async handleTypingStart(req, res) { + try { + const { user_id, chat_id } = req.body; + + if (!user_id || !chat_id) { + res.status(400).json({ error: 'user_id and chat_id are required' }); + return; + } + + const client = this.connectedClients.get(user_id); + if (!client) { + res.status(401).json({ error: 'Not authenticated' }); + return; + } + + if (!client.chats.has(chat_id)) { + res.status(403).json({ error: 'Not joined to this chat' }); + return; + } + + this.broadcastToChatExcludeUser(chat_id, user_id, 'user_typing_start', { + chat_id, + user: client.user_info.profile, + timestamp: new Date() + }); + + res.json({ success: true }); + + } catch (error) { + res.status(500).json({ error: 'Failed to send typing indicator' }); + } + } + + // HTTP эндпоинт для остановки индикации печатания + async handleTypingStop(req, res) { + try { + const { user_id, chat_id } = req.body; + + if (!user_id || !chat_id) { + res.status(400).json({ error: 'user_id and chat_id are required' }); + return; + } + + const client = this.connectedClients.get(user_id); + if (!client) { + res.status(401).json({ error: 'Not authenticated' }); + return; + } + + if (!client.chats.has(chat_id)) { + res.status(403).json({ error: 'Not joined to this chat' }); + return; + } + + this.broadcastToChatExcludeUser(chat_id, user_id, 'user_typing_stop', { + chat_id, + user: client.user_info.profile, + timestamp: new Date() + }); + + res.json({ success: true }); + + } catch (error) { + res.status(500).json({ error: 'Failed to send typing indicator' }); + } + } + + // Обработка отключения клиента + handleClientDisconnect(user_id) { + const client = this.connectedClients.get(user_id); + if (!client) return; + + // Удаляем пользователя из всех чатов + client.chats.forEach(chat_id => { + if (this.chatParticipants.has(chat_id)) { + this.chatParticipants.get(chat_id).delete(user_id); + + // Уведомляем других участников об отключении + this.broadcastToChatExcludeUser(chat_id, user_id, 'user_left', { + chat_id, + user: client.user_info.profile, + timestamp: new Date() + }); + + // Если чат пуст, удаляем его + if (this.chatParticipants.get(chat_id).size === 0) { + this.chatParticipants.delete(chat_id); + } + } + }); + + // Удаляем клиента + this.connectedClients.delete(user_id); + } + + // Добавление события в очередь пользователя + addEventToQueue(user_id, event, data) { + if (!this.userEventQueues.has(user_id)) { + this.userEventQueues.set(user_id, []); + } + + const eventQueue = this.userEventQueues.get(user_id); + const eventId = ++this.eventIdCounter; + + eventQueue.push({ + id: eventId, + event, + data, + timestamp: new Date() + }); + + // Ограничиваем размер очереди (последние 100 событий) + if (eventQueue.length > 100) { + eventQueue.splice(0, eventQueue.length - 100); + } + } + + // Рассылка события всем участникам чата + broadcastToChat(chat_id, event, data) { + const participants = this.chatParticipants.get(chat_id); + if (!participants) return; + + participants.forEach(user_id => { + this.addEventToQueue(user_id, event, data); + }); + } + + // Рассылка события всем участникам чата кроме отправителя + broadcastToChatExcludeUser(chat_id, exclude_user_id, event, data) { + const participants = this.chatParticipants.get(chat_id); + if (!participants) return; + + participants.forEach(user_id => { + if (user_id !== exclude_user_id) { + this.addEventToQueue(user_id, event, data); + } + }); + } + + // Получение списка онлайн пользователей в чате + getOnlineUsersInChat(chat_id) { + const participants = this.chatParticipants.get(chat_id) || new Set(); + const onlineUsers = []; + const now = new Date(); + const ONLINE_THRESHOLD = 2 * 60 * 1000; // 2 минуты + + participants.forEach(user_id => { + const client = this.connectedClients.get(user_id); + if (client && (now - client.lastActivity) < ONLINE_THRESHOLD) { + onlineUsers.push(client.user_info.profile); + } + }); + + return onlineUsers; + } + + // Отправка системного сообщения в чат + async sendSystemMessage(chat_id, text) { + this.broadcastToChat(chat_id, 'system_message', { + chat_id, + text, + timestamp: new Date() + }); + } + + // Очистка старых событий + cleanupOldEvents() { + const now = new Date(); + const MAX_EVENT_AGE = 24 * 60 * 60 * 1000; // 24 часа + const INACTIVE_USER_THRESHOLD = 60 * 60 * 1000; // 1 час + + // Очищаем старые события + this.userEventQueues.forEach((eventQueue, user_id) => { + const filteredEvents = eventQueue.filter(event => + (now - event.timestamp) < MAX_EVENT_AGE + ); + + if (filteredEvents.length !== eventQueue.length) { + this.userEventQueues.set(user_id, filteredEvents); + } + }); + + // Удаляем неактивных пользователей + this.connectedClients.forEach((client, user_id) => { + if ((now - client.lastActivity) > INACTIVE_USER_THRESHOLD) { + this.handleClientDisconnect(user_id); + this.userEventQueues.delete(user_id); + } + }); + } + + // Тестирование Real-time подписки + async testRealtimeConnection() { + try { + const supabase = getSupabaseClient(); + if (!supabase) { + return false; + } + + // Создаем тестовый канал для проверки подключения + const testChannel = supabase + .channel('test_connection') + .subscribe((status, error) => { + if (error) { + console.error('❌ [Supabase] Тестовый канал - ошибка:', error); + } + + if (status === 'SUBSCRIBED') { + // Отписываемся от тестового канала + setTimeout(() => { + testChannel.unsubscribe(); + }, 2000); + } + }); + + return true; + } catch (error) { + console.error('❌ [Supabase] Ошибка тестирования Realtime:', error); + return false; + } + } + + // Проверка статуса подписки + checkSubscriptionStatus() { + if (this.realtimeSubscription) { + return true; + } else { + return false; + } + } + + setupRealtimeSubscription() { + // Убираем setTimeout, вызываем сразу + this._doSetupRealtimeSubscription(); + } + + _doSetupRealtimeSubscription() { + try { + const supabase = getSupabaseClient(); + + if (!supabase) { + console.log('❌ [Supabase] Supabase клиент не найден'); + throw new Error('Supabase client not available'); + } + + // Подписываемся на изменения в таблице messages + const subscription = supabase + .channel('messages_changes') + .on( + 'postgres_changes', + { + event: 'INSERT', + schema: 'public', + table: 'messages' + }, + async (payload) => { + try { + const newMessage = payload.new; + if (!newMessage) { + return; + } + + if (!newMessage.chat_id) { + return; + } + + // Получаем профиль пользователя + const { data: userProfile, error: profileError } = await supabase + .from('user_profiles') + .select('id, full_name, avatar_url') + .eq('id', newMessage.user_id) + .single(); + + if (profileError) { + console.error('❌ [Supabase] Ошибка получения профиля пользователя:', profileError); + } + + // Объединяем сообщение с профилем + const messageWithProfile = { + ...newMessage, + user_profiles: userProfile || null + }; + + // Отправляем сообщение всем участникам чата + this.broadcastToChat(newMessage.chat_id, 'new_message', { + message: messageWithProfile, + timestamp: new Date() + }); + + } catch (callbackError) { + console.error('❌ [Supabase] Ошибка в обработчике сообщения:', callbackError); + } + } + ) + .subscribe((status, error) => { + if (error) { + console.error('❌ [Supabase] Ошибка подписки:', error); + } + + if (status === 'CHANNEL_ERROR') { + console.error('❌ [Supabase] Ошибка канала'); + } else if (status === 'TIMED_OUT') { + console.error('❌ [Supabase] Таймаут подписки'); + } + }); + + // Сохраняем ссылку на подписку для возможности отписки + this.realtimeSubscription = subscription; + + } catch (error) { + console.error('❌ [Supabase] Критическая ошибка при настройке подписки:', error); + throw error; // Пробрасываем ошибку для обработки в initializeWithRetry + } + } + + // Получение статистики подключений + getConnectionStats() { + return { + connectedClients: this.connectedClients.size, + activeChats: this.chatParticipants.size, + totalChatParticipants: Array.from(this.chatParticipants.values()) + .reduce((total, participants) => total + participants.size, 0), + totalEventQueues: this.userEventQueues.size, + totalEvents: Array.from(this.userEventQueues.values()) + .reduce((total, queue) => total + queue.length, 0) + }; + } +} + +// Функция для создания роутера с polling эндпоинтами +function createChatPollingRouter(express) { + const router = express.Router(); + const chatHandler = new ChatPollingHandler(); + + // CORS middleware для всех запросов + router.use((req, res, next) => { + res.header('Access-Control-Allow-Origin', '*'); + res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); + res.header('Access-Control-Allow-Headers', 'Content-Type, Cache-Control, Authorization'); + res.header('Access-Control-Allow-Credentials', 'true'); + + // Обрабатываем OPTIONS запросы + if (req.method === 'OPTIONS') { + res.status(200).end(); + return; + } + + next(); + }); + + // Эндпоинт для аутентификации + router.post('/auth', (req, res) => { + chatHandler.handleAuthentication(req, res); + }); + + // Эндпоинт для получения событий (polling) + router.get('/events', (req, res) => { + chatHandler.handleGetEvents(req, res); + }); + + // HTTP эндпоинты для действий + router.post('/join-chat', (req, res) => { + chatHandler.handleJoinChat(req, res); + }); + + router.post('/leave-chat', (req, res) => { + chatHandler.handleLeaveChat(req, res); + }); + + router.post('/send-message', (req, res) => { + chatHandler.handleSendMessage(req, res); + }); + + router.post('/typing-start', (req, res) => { + chatHandler.handleTypingStart(req, res); + }); + + router.post('/typing-stop', (req, res) => { + chatHandler.handleTypingStop(req, res); + }); + + // Эндпоинт для получения онлайн пользователей в чате + router.get('/online-users/:chat_id', (req, res) => { + const { chat_id } = req.params; + const onlineUsers = chatHandler.getOnlineUsersInChat(chat_id); + res.json({ onlineUsers }); + }); + + // Эндпоинт для получения статистики + router.get('/stats', (req, res) => { + const stats = chatHandler.getConnectionStats(); + res.json(stats); + }); + + // Эндпоинт для проверки статуса Supabase подписки + router.get('/supabase-status', (req, res) => { + const isConnected = chatHandler.checkSubscriptionStatus(); + res.json({ + supabaseSubscriptionActive: isConnected, + subscriptionExists: !!chatHandler.realtimeSubscription, + subscriptionInfo: chatHandler.realtimeSubscription ? { + channel: chatHandler.realtimeSubscription.topic, + state: chatHandler.realtimeSubscription.state + } : null + }); + }); + + // Эндпоинт для принудительного переподключения к Supabase + router.post('/reconnect-supabase', (req, res) => { + try { + // Отписываемся от текущей подписки + if (chatHandler.realtimeSubscription) { + chatHandler.realtimeSubscription.unsubscribe(); + chatHandler.realtimeSubscription = null; + } + + // Создаем новую подписку + chatHandler.setupRealtimeSubscription(); + + res.json({ + success: true, + message: 'Reconnection initiated' + }); + } catch (error) { + console.error('❌ [Polling Server] Ошибка переподключения:', error); + res.status(500).json({ + success: false, + error: 'Reconnection failed', + details: error.message + }); + } + }); + + // Тестовый эндпоинт для создания сообщения в обход API + router.post('/test-message', async (req, res) => { + const { chat_id, user_id, text } = req.body; + + if (!chat_id || !user_id || !text) { + res.status(400).json({ error: 'chat_id, user_id и text обязательны' }); + return; + } + + try { + // Создаем тестовое событие напрямую + chatHandler.broadcastToChat(chat_id, 'new_message', { + message: { + id: `test_${Date.now()}`, + chat_id, + user_id, + text, + created_at: new Date().toISOString(), + user_profiles: { + id: user_id, + full_name: 'Test User', + avatar_url: null + } + }, + timestamp: new Date() + }); + + res.json({ + success: true, + message: 'Test message sent to polling clients' + }); + } catch (error) { + console.error('❌ [Polling Server] Ошибка отправки тестового сообщения:', error); + res.status(500).json({ + success: false, + error: 'Failed to send test message', + details: error.message + }); + } + }); + + return { router, chatHandler }; +} + +module.exports = { + ChatPollingHandler, + createChatPollingRouter +}; \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/socket-chat.js b/server/routers/kfu-m-24-1/sber_mobile/socket-chat.js deleted file mode 100644 index 62102c5..0000000 --- a/server/routers/kfu-m-24-1/sber_mobile/socket-chat.js +++ /dev/null @@ -1,457 +0,0 @@ -const { getSupabaseClient } = require('./supabaseClient'); - -class ChatSocketHandler { - constructor(io) { - this.io = io; - this.onlineUsers = new Map(); // Хранение онлайн пользователей: socket.id -> user info - this.chatRooms = new Map(); // Хранение участников комнат: chat_id -> Set(socket.id) - this.realtimeSubscription = null; // Ссылка на подписку для управления - - this.setupSocketHandlers(); - - try { - this.setupRealtimeSubscription(); // Добавляем Real-time подписки - } catch (error) { - // Ignore error - } - - // Запускаем тестирование через 2 секунды после инициализации - setTimeout(() => { - this.testRealtimeConnection(); - }, 2000); - - // Проверяем статус подписки через 5 секунд - setTimeout(() => { - this.checkSubscriptionStatus(); - }, 5000); - } - - setupSocketHandlers() { - this.io.on('connection', (socket) => { - // Аутентификация пользователя - socket.on('authenticate', async (data) => { - await this.handleAuthentication(socket, data); - }); - - // Присоединение к чату - socket.on('join_chat', async (data) => { - await this.handleJoinChat(socket, data); - }); - - // Покидание чата - socket.on('leave_chat', (data) => { - this.handleLeaveChat(socket, data); - }); - - // Отправка сообщения - socket.on('send_message', async (data) => { - await this.handleSendMessage(socket, data); - }); - - // Пользователь начал печатать - socket.on('typing_start', (data) => { - this.handleTypingStart(socket, data); - }); - - // Пользователь закончил печатать - socket.on('typing_stop', (data) => { - this.handleTypingStop(socket, data); - }); - - // Отключение пользователя - socket.on('disconnect', () => { - this.handleDisconnect(socket); - }); - }); - } - - async handleAuthentication(socket, data) { - try { - const { user_id, token } = data; - - if (!user_id) { - socket.emit('auth_error', { message: 'user_id is required' }); - return; - } - - // Получаем информацию о пользователе из базы данных - const supabase = getSupabaseClient(); - const { data: userProfile, error } = await supabase - .from('user_profiles') - .select('*') - .eq('id', user_id) - .single(); - - if (error) { - socket.emit('auth_error', { message: 'User not found' }); - return; - } - - // Сохраняем информацию о пользователе - this.onlineUsers.set(socket.id, { - user_id, - socket_id: socket.id, - profile: userProfile, - last_seen: new Date() - }); - - socket.user_id = user_id; - socket.emit('authenticated', { - message: 'Successfully authenticated', - user: userProfile - }); - } catch (error) { - socket.emit('auth_error', { message: 'Authentication failed' }); - } - } - - async handleJoinChat(socket, data) { - try { - const { chat_id } = data; - - if (!socket.user_id) { - socket.emit('error', { message: 'Not authenticated' }); - return; - } - - if (!chat_id) { - socket.emit('error', { message: 'chat_id is required' }); - return; - } - // Проверяем, что чат существует и пользователь имеет доступ к нему - const supabase = getSupabaseClient(); - const { data: chat, error } = await supabase - .from('chats') - .select(` - *, - buildings ( - management_company_id, - apartments ( - apartment_residents ( - user_id - ) - ) - ) - `) - .eq('id', chat_id) - .single(); - - if (error || !chat) { - socket.emit('error', { message: 'Chat not found' }); - return; - } - - // Проверяем доступ пользователя к чату через квартиры в доме - const hasAccess = chat.buildings.apartments.some(apartment => - apartment.apartment_residents.some(resident => - resident.user_id === socket.user_id - ) - ); - - if (!hasAccess) { - socket.emit('error', { message: 'Access denied to this chat' }); - return; - } - // Добавляем сокет в комнату - socket.join(chat_id); - - // Обновляем список участников комнаты - if (!this.chatRooms.has(chat_id)) { - this.chatRooms.set(chat_id, new Set()); - } - - const participantsBefore = this.chatRooms.get(chat_id).size; - this.chatRooms.get(chat_id).add(socket.id); - const participantsAfter = this.chatRooms.get(chat_id).size; - - socket.emit('joined_chat', { - chat_id, - chat: chat, - message: 'Successfully joined chat' - }); - - // Уведомляем других участников о подключении - const userInfo = this.onlineUsers.get(socket.id); - - socket.to(chat_id).emit('user_joined', { - chat_id, - user: userInfo?.profile, - timestamp: new Date() - }); - } catch (error) { - socket.emit('error', { message: 'Failed to join chat' }); - } - } - - handleLeaveChat(socket, data) { - const { chat_id } = data; - - if (!chat_id) return; - - socket.leave(chat_id); - - // Удаляем из списка участников - if (this.chatRooms.has(chat_id)) { - this.chatRooms.get(chat_id).delete(socket.id); - - // Если комната пуста, удаляем её - if (this.chatRooms.get(chat_id).size === 0) { - this.chatRooms.delete(chat_id); - } - } - - // Уведомляем других участников об отключении - const userInfo = this.onlineUsers.get(socket.id); - socket.to(chat_id).emit('user_left', { - chat_id, - user: userInfo?.profile, - timestamp: new Date() - }); - - - } - - async handleSendMessage(socket, data) { - try { - const { chat_id, text } = data; - - if (!socket.user_id) { - socket.emit('error', { message: 'Not authenticated' }); - return; - } - - if (!chat_id || !text) { - socket.emit('error', { message: 'chat_id and text are required' }); - return; - } - - // Сохраняем сообщение в базу данных - const supabase = getSupabaseClient(); - const { data: message, error } = await supabase - .from('messages') - .insert({ - chat_id, - user_id: socket.user_id, - text - }) - .select(` - *, - user_profiles ( - id, - full_name, - avatar_url - ) - `) - .single(); - - if (error) { - socket.emit('error', { message: 'Failed to save message' }); - return; - } - - // Отправляем сообщение всем участникам чата - this.io.to(chat_id).emit('new_message', { - message, - timestamp: new Date() - }); - - } catch (error) { - socket.emit('error', { message: 'Failed to send message' }); - } - } - - handleTypingStart(socket, data) { - const { chat_id } = data; - - if (!socket.user_id || !chat_id) return; - - const userInfo = this.onlineUsers.get(socket.id); - socket.to(chat_id).emit('user_typing_start', { - chat_id, - user: userInfo?.profile, - timestamp: new Date() - }); - } - - handleTypingStop(socket, data) { - const { chat_id } = data; - - if (!socket.user_id || !chat_id) return; - - const userInfo = this.onlineUsers.get(socket.id); - socket.to(chat_id).emit('user_typing_stop', { - chat_id, - user: userInfo?.profile, - timestamp: new Date() - }); - } - - handleDisconnect(socket) { - - // Удаляем пользователя из всех комнат - this.chatRooms.forEach((participants, chat_id) => { - if (participants.has(socket.id)) { - participants.delete(socket.id); - - // Уведомляем других участников об отключении - const userInfo = this.onlineUsers.get(socket.id); - socket.to(chat_id).emit('user_left', { - chat_id, - user: userInfo?.profile, - timestamp: new Date() - }); - - // Если комната пуста, удаляем её - if (participants.size === 0) { - this.chatRooms.delete(chat_id); - } - } - }); - - // Удаляем пользователя из списка онлайн - this.onlineUsers.delete(socket.id); - } - - // Получение списка онлайн пользователей в чате - getOnlineUsersInChat(chat_id) { - const participants = this.chatRooms.get(chat_id) || new Set(); - const onlineUsers = []; - - participants.forEach(socketId => { - const userInfo = this.onlineUsers.get(socketId); - if (userInfo) { - onlineUsers.push(userInfo.profile); - } - }); - - return onlineUsers; - } - - // Отправка системного сообщения в чат - async sendSystemMessage(chat_id, text) { - this.io.to(chat_id).emit('system_message', { - chat_id, - text, - timestamp: new Date() - }); - } - - // Тестирование Real-time подписки - async testRealtimeConnection() { - try { - const supabase = getSupabaseClient(); - if (!supabase) { - return false; - } - - // Создаем тестовый канал для проверки подключения - const testChannel = supabase - .channel('test_connection') - .subscribe((status, error) => { - if (status === 'SUBSCRIBED') { - // Отписываемся от тестового канала - setTimeout(() => { - testChannel.unsubscribe(); - }, 2000); - } - }); - - return true; - } catch (error) { - return false; - } - } - - // Проверка статуса подписки - checkSubscriptionStatus() { - if (this.realtimeSubscription) { - return true; - } else { - return false; - } - } - - setupRealtimeSubscription() { - // Добавляем небольшую задержку, чтобы убедиться, что Supabase клиент инициализирован - setTimeout(() => { - this._doSetupRealtimeSubscription(); - }, 1000); - } - - _doSetupRealtimeSubscription() { - try { - const supabase = getSupabaseClient(); - - if (!supabase) { - return; - } - - // Подписываемся на изменения в таблице messages - const subscription = supabase - .channel('messages_changes') - .on( - 'postgres_changes', - { - event: 'INSERT', - schema: 'public', - table: 'messages' - }, - async (payload) => { - try { - const newMessage = payload.new; - if (!newMessage) { - return; - } - - if (!newMessage.chat_id) { - return; - } - - // Получаем профиль пользователя - const { data: userProfile, error: profileError } = await supabase - .from('user_profiles') - .select('id, full_name, avatar_url') - .eq('id', newMessage.user_id) - .single(); - - // Объединяем сообщение с профилем - const messageWithProfile = { - ...newMessage, - user_profiles: userProfile || null - }; - - // Проверяем, есть ли участники в чате - const chatRoomParticipants = this.chatRooms.get(newMessage.chat_id); - - // Отправляем сообщение через Socket.IO всем участникам чата - this.io.to(newMessage.chat_id).emit('new_message', { - message: messageWithProfile, - timestamp: new Date() - }); - } catch (callbackError) { - // Ignore error - } - } - ) - .subscribe(); - - // Сохраняем ссылку на подписку для возможности отписки - this.realtimeSubscription = subscription; - - } catch (error) { - // Ignore error - } - } -} - -// Функция инициализации Socket.IO для чатов -function initializeChatSocket(io) { - const chatHandler = new ChatSocketHandler(io); - - return chatHandler; -} - -module.exports = { - ChatSocketHandler, - initializeChatSocket -}; \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/supabaseClient.js b/server/routers/kfu-m-24-1/sber_mobile/supabaseClient.js index 938cc18..0568afa 100644 --- a/server/routers/kfu-m-24-1/sber_mobile/supabaseClient.js +++ b/server/routers/kfu-m-24-1/sber_mobile/supabaseClient.js @@ -3,12 +3,30 @@ const { createClient } = require('@supabase/supabase-js'); const { getSupabaseUrl, getSupabaseKey, getSupabaseServiceKey } = require('./get-constants'); let supabase = null; +let initializationPromise = null; async function initSupabaseClient() { - const supabaseUrl = await getSupabaseUrl(); - const supabaseAnonKey = await getSupabaseKey(); - const supabaseServiceRoleKey = await getSupabaseServiceKey(); - supabase = createClient(supabaseUrl, supabaseServiceRoleKey); + console.log('🔄 [Supabase Client] Начинаем инициализацию...'); + + try { + console.log('🔄 [Supabase Client] Получаем конфигурацию...'); + const supabaseUrl = await getSupabaseUrl(); + const supabaseAnonKey = await getSupabaseKey(); + const supabaseServiceRoleKey = await getSupabaseServiceKey(); + + + if (!supabaseUrl || !supabaseServiceRoleKey) { + throw new Error('Missing required Supabase configuration'); + } + + supabase = createClient(supabaseUrl, supabaseServiceRoleKey); + + return supabase; + + } catch (error) { + console.error('❌ [Supabase Client] Ошибка инициализации:', error); + throw error; + } } function getSupabaseClient() { @@ -20,20 +38,49 @@ function getSupabaseClient() { // POST /refresh-supabase-client router.post('/refresh-supabase-client', async (req, res) => { -try { + try { await initSupabaseClient(); res.json({ success: true, message: 'Supabase client refreshed' }); -} catch (error) { + } catch (error) { + console.error('❌ [Supabase Client] Ошибка обновления:', error); res.status(500).json({ error: error.message }); -} + } +}); + +// GET /supabase-client-status +router.get('/supabase-client-status', (req, res) => { + console.log('🔍 [Supabase Client] Проверяем статус клиента...'); + + const isInitialized = !!supabase; + + res.json({ + initialized: isInitialized, + clientExists: !!supabase, + timestamp: new Date().toISOString() + }); }); // Инициализация клиента при старте -(async () => { +initializationPromise = (async () => { + try { await initSupabaseClient(); + } catch (error) { + console.error('❌ [Supabase Client] Ошибка инициализации при старте:', error); + // Планируем повторную попытку через 5 секунд + setTimeout(async () => { + try { + await initSupabaseClient(); + } catch (retryError) { + console.error('❌ [Supabase Client] Повторная инициализация неудачна:', retryError); + } + }, 5000); + } })(); module.exports = { getSupabaseClient, - supabaseRouter: router + initSupabaseClient, + supabaseRouter: router, + // Экспортируем промис инициализации для возможности ожидания + initializationPromise }; \ No newline at end of file From 45cafbee91a97187946da83aac15b58fa3542738 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B5=D0=B2=20=D0=9C?= =?UTF-8?q?=D0=B0=D0=BA=D1=81=D0=B8=D0=BC=20=D0=A1=D0=B5=D1=80=D0=B3=D0=B5?= =?UTF-8?q?=D0=B5=D0=B2=D0=B8=D1=87?= Date: Sat, 14 Jun 2025 13:37:26 +0300 Subject: [PATCH 12/16] add requirements --- package-lock.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package-lock.json b/package-lock.json index 0205535..7313728 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,6 +28,7 @@ "gigachat": "^0.0.14", "jsdom": "^25.0.1", "jsonwebtoken": "^9.0.2", + "langchain": "^0.3.7", "langchain-gigachat": "^0.0.11", "mongodb": "^6.12.0", "mongoose": "^8.9.2", From 07d35c45168e9614573a9d53e53952d164ef1da8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B5=D0=B2=20=D0=9C?= =?UTF-8?q?=D0=B0=D0=BA=D1=81=D0=B8=D0=BC=20=D0=A1=D0=B5=D1=80=D0=B3=D0=B5?= =?UTF-8?q?=D0=B5=D0=B2=D0=B8=D1=87?= Date: Sat, 14 Jun 2025 14:45:31 +0300 Subject: [PATCH 13/16] add test endpoint --- server/routers/kfu-m-24-1/sber_mobile/cameras.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/server/routers/kfu-m-24-1/sber_mobile/cameras.js b/server/routers/kfu-m-24-1/sber_mobile/cameras.js index 1425b9f..a1a1e8e 100644 --- a/server/routers/kfu-m-24-1/sber_mobile/cameras.js +++ b/server/routers/kfu-m-24-1/sber_mobile/cameras.js @@ -11,6 +11,10 @@ router.get('/cameras', async (req, res) => { res.json(data); }); +router.get('/creds', async (req, res) => { + res.json({data: process.env.GIGA_AUTH}); +}); + // Получить все камеры по квартире (через building_id) router.get('/cameras/by-apartment', async (req, res) => { const supabase = getSupabaseClient(); From f37f34d8035fb4fd243de2f12c8c70e7eb6c736e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B5=D0=B2=20=D0=9C?= =?UTF-8?q?=D0=B0=D0=BA=D1=81=D0=B8=D0=BC=20=D0=A1=D0=B5=D1=80=D0=B3=D0=B5?= =?UTF-8?q?=D0=B5=D0=B2=D0=B8=D1=87?= Date: Sat, 14 Jun 2025 18:26:13 +0300 Subject: [PATCH 14/16] fix getting giga token --- .../kfu-m-24-1/sber_mobile/get-constants.js | 3 ++- .../sber_mobile/support-ai-agent/gigachat.ts | 18 ++++++++++-------- .../support-ai-agent/support-agent.ts | 3 ++- .../kfu-m-24-1/sber_mobile/supportApi.js | 9 ++++++--- 4 files changed, 20 insertions(+), 13 deletions(-) diff --git a/server/routers/kfu-m-24-1/sber_mobile/get-constants.js b/server/routers/kfu-m-24-1/sber_mobile/get-constants.js index 0d7c355..47f34e8 100644 --- a/server/routers/kfu-m-24-1/sber_mobile/get-constants.js +++ b/server/routers/kfu-m-24-1/sber_mobile/get-constants.js @@ -67,7 +67,8 @@ const getRagSupabaseUrl = async () => { module.exports = { getSupabaseUrl, getSupabaseKey, - getSupabaseServiceKey + getSupabaseServiceKey, + getGigaAuth }; // IIFE для установки переменных окружения diff --git a/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/gigachat.ts b/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/gigachat.ts index e46c067..ab98ebb 100644 --- a/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/gigachat.ts +++ b/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/gigachat.ts @@ -1,18 +1,20 @@ import { Agent } from 'node:https'; import { GigaChat } from 'langchain-gigachat'; +import { getGigaAuth } from '../get-constants'; const httpsAgent = new Agent({ rejectUnauthorized: false, }); // Получаем GIGA_AUTH из переменной окружения (устанавливается в get-constants.js) -export const gigachat = new GigaChat({ - model: 'GigaChat-2', - temperature: 0.7, - scope: 'GIGACHAT_API_PERS', - streaming: false, - credentials: process.env.GIGA_AUTH, - httpsAgent -}); +export const gigachat = (GIGA_AUTH) => + new GigaChat({ + model: 'GigaChat-2', + temperature: 0.7, + scope: 'GIGACHAT_API_PERS', + streaming: false, + credentials: GIGA_AUTH, + httpsAgent + }); export default gigachat; \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/support-agent.ts b/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/support-agent.ts index 93816e6..e8c5c68 100644 --- a/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/support-agent.ts +++ b/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/support-agent.ts @@ -10,6 +10,7 @@ import { CreateTicketTool } from './create-ticket-tool'; export interface SupportAgentConfig { temperature?: number; threadId?: string; + GIGA_AUTH?: string; } export interface SupportResponse { @@ -34,7 +35,7 @@ export class SupportAgent { this.memorySaver = new MemorySaver(); this.isFirstMessage = true; - this.llm = gigachat; + this.llm = gigachat(config.GIGA_AUTH); if (config.temperature !== undefined) { this.llm.temperature = config.temperature; } diff --git a/server/routers/kfu-m-24-1/sber_mobile/supportApi.js b/server/routers/kfu-m-24-1/sber_mobile/supportApi.js index 1babe43..afb4ea8 100644 --- a/server/routers/kfu-m-24-1/sber_mobile/supportApi.js +++ b/server/routers/kfu-m-24-1/sber_mobile/supportApi.js @@ -1,5 +1,6 @@ const router = require('express').Router(); const { getSupabaseClient } = require('./supabaseClient'); +const { getGigaAuth } = require('./get-constants'); const { SupportAgent } = require('./support-ai-agent/support-agent'); // Хранилище агентов для разных пользователей @@ -8,11 +9,13 @@ const userAgents = new Map(); /** * Получить или создать агента для пользователя */ -function getUserAgent(userId) { +async function getUserAgent(userId) { if (!userAgents.has(userId)) { + const GIGA_AUTH = await getGigaAuth(); const config = { threadId: userId, - temperature: 0.7 + temperature: 0.7, + GIGA_AUTH }; userAgents.set(userId, new SupportAgent(config)); } @@ -74,7 +77,7 @@ router.post('/support', async (req, res) => { } // Получаем агента для пользователя - const agent = getUserAgent(user_id); + const agent = await getUserAgent(user_id); // Получаем ответ от AI-агента, передавая apartment_id const aiResponse = await agent.processMessage(message, apartment_id); From 825d7f1dd21337f8c7f2f89ccb9a0b954f5aca72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B5=D0=B2=20=D0=9C?= =?UTF-8?q?=D0=B0=D0=BA=D1=81=D0=B8=D0=BC=20=D0=A1=D0=B5=D1=80=D0=B3=D0=B5?= =?UTF-8?q?=D0=B5=D0=B2=D0=B8=D1=87?= Date: Sat, 14 Jun 2025 18:29:20 +0300 Subject: [PATCH 15/16] remove test api --- server/routers/kfu-m-24-1/sber_mobile/cameras.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/server/routers/kfu-m-24-1/sber_mobile/cameras.js b/server/routers/kfu-m-24-1/sber_mobile/cameras.js index a1a1e8e..1425b9f 100644 --- a/server/routers/kfu-m-24-1/sber_mobile/cameras.js +++ b/server/routers/kfu-m-24-1/sber_mobile/cameras.js @@ -11,10 +11,6 @@ router.get('/cameras', async (req, res) => { res.json(data); }); -router.get('/creds', async (req, res) => { - res.json({data: process.env.GIGA_AUTH}); -}); - // Получить все камеры по квартире (через building_id) router.get('/cameras/by-apartment', async (req, res) => { const supabase = getSupabaseClient(); From 6e59e801b0246eaba21474802661528c226e9865 Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 14 Jun 2025 19:29:48 +0300 Subject: [PATCH 16/16] add tickets data --- .../routers/kfu-m-24-1/sber_mobile/tickets.js | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/server/routers/kfu-m-24-1/sber_mobile/tickets.js b/server/routers/kfu-m-24-1/sber_mobile/tickets.js index 5ff082d..bfeceb8 100644 --- a/server/routers/kfu-m-24-1/sber_mobile/tickets.js +++ b/server/routers/kfu-m-24-1/sber_mobile/tickets.js @@ -1,14 +1,31 @@ const router = require('express').Router(); const { getSupabaseClient } = require('./supabaseClient'); -// Получить все тикеты по дому +// Получить заявки пользователя по квартире router.get('/tickets', async (req, res) => { const supabase = getSupabaseClient(); - const { building_id } = req.query; - if (!building_id) return res.status(400).json({ error: 'building_id required' }); - const { data, error } = await supabase.from('tickets').select('*').eq('building_id', building_id); - if (error) return res.status(400).json({ error: error.message }); - res.json(data); + const { user_id, apartment_id } = req.query; + + if (!user_id || !apartment_id) { + return res.status(400).json({ error: 'Требуется user_id и apartment_id' }); + } + + try { + const { data, error } = await supabase + .from('tickets') + .select('*') + .eq('user_id', user_id) + .eq('apartment_id', apartment_id) + .order('created_at', { ascending: false }); + + if (error) { + return res.status(400).json({ error: error.message }); + } + + res.json(data || []); + } catch (err) { + res.status(500).json({ error: 'Внутренняя ошибка сервера' }); + } }); module.exports = router; \ No newline at end of file