From 989e59b6a640469cd113e924f443440893b93d22 Mon Sep 17 00:00:00 2001 From: Manuel Ruck Date: Sat, 10 Aug 2024 23:55:47 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=9A=80=20add=20non-named-votes-ai?= =?UTF-8?q?=20service?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Manuel Ruck --- .../src/models/NonNamedVotesAI/index.ts | 5 + .../src/models/NonNamedVotesAI/schema.ts | 22 ++ common/bundestagio/src/models/index.ts | 1 + pnpm-lock.yaml | 368 +++++++++++++----- services/non-named-votes-ai/.env.example | 10 + services/non-named-votes-ai/.gitignore | 1 + services/non-named-votes-ai/FLOW.md | 66 ++++ services/non-named-votes-ai/package.json | 30 ++ .../non-named-votes-ai/src/authMiddleware.ts | 31 ++ .../src/beschlusstextController.ts | 78 ++++ services/non-named-votes-ai/src/index.ts | 30 ++ services/non-named-votes-ai/src/logger.ts | 13 + .../non-named-votes-ai/src/rateLimiter.ts | 6 + services/non-named-votes-ai/src/test.ts | 24 ++ services/non-named-votes-ai/src/utils.ts | 122 ++++++ services/non-named-votes-ai/tsconfig.json | 3 + services/non-named-votes-ai/tsup.config.ts | 6 + 17 files changed, 725 insertions(+), 91 deletions(-) create mode 100644 common/bundestagio/src/models/NonNamedVotesAI/index.ts create mode 100644 common/bundestagio/src/models/NonNamedVotesAI/schema.ts create mode 100644 services/non-named-votes-ai/.env.example create mode 100644 services/non-named-votes-ai/.gitignore create mode 100644 services/non-named-votes-ai/FLOW.md create mode 100644 services/non-named-votes-ai/package.json create mode 100644 services/non-named-votes-ai/src/authMiddleware.ts create mode 100644 services/non-named-votes-ai/src/beschlusstextController.ts create mode 100644 services/non-named-votes-ai/src/index.ts create mode 100644 services/non-named-votes-ai/src/logger.ts create mode 100644 services/non-named-votes-ai/src/rateLimiter.ts create mode 100644 services/non-named-votes-ai/src/test.ts create mode 100644 services/non-named-votes-ai/src/utils.ts create mode 100644 services/non-named-votes-ai/tsconfig.json create mode 100644 services/non-named-votes-ai/tsup.config.ts diff --git a/common/bundestagio/src/models/NonNamedVotesAI/index.ts b/common/bundestagio/src/models/NonNamedVotesAI/index.ts new file mode 100644 index 000000000..311b8c7ec --- /dev/null +++ b/common/bundestagio/src/models/NonNamedVotesAI/index.ts @@ -0,0 +1,5 @@ +import mongoose from 'mongoose'; +import NonNamedVotesAiSchema, { INonNamedVotesAi } from './schema'; + +export const NonNamedVotesAiModel = mongoose.model('NonNamedVotesAi', NonNamedVotesAiSchema); +export { NonNamedVotesAiSchema, INonNamedVotesAi }; diff --git a/common/bundestagio/src/models/NonNamedVotesAI/schema.ts b/common/bundestagio/src/models/NonNamedVotesAI/schema.ts new file mode 100644 index 000000000..795295f6a --- /dev/null +++ b/common/bundestagio/src/models/NonNamedVotesAI/schema.ts @@ -0,0 +1,22 @@ +import { Schema, SchemaTimestampsConfig, Document } from 'mongoose'; + +export interface INonNamedVotesAi extends Document, SchemaTimestampsConfig { + pdfUrl: string; + assistantId: string; + vectorStoreId: string; + threadId: string; + fileId?: string; +} + +const NonNamedVotesAiSchema = new Schema( + { + pdfUrl: { type: String, unique: true, index: true, required: true }, + assistantId: { type: String, required: true }, + vectorStoreId: { type: String, required: true }, + threadId: { type: String, required: true }, + fileId: { type: String, required: true }, + }, + { timestamps: true }, +); + +export default NonNamedVotesAiSchema; diff --git a/common/bundestagio/src/models/index.ts b/common/bundestagio/src/models/index.ts index 530903fb8..0e817d5e9 100644 --- a/common/bundestagio/src/models/index.ts +++ b/common/bundestagio/src/models/index.ts @@ -6,3 +6,4 @@ export * from './NamedPoll'; export * from './Procedure'; export * from './User'; export * from './PlenaryMinute'; +export * from './NonNamedVotesAI'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8922b6bbe..c9342cc04 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -192,7 +192,7 @@ importers: devDependencies: '@graphql-codegen/cli': specifier: 5.0.0 - version: 5.0.0(@parcel/watcher@2.4.1)(@types/node@20.12.13)(graphql@16.8.1)(typescript@5.2.2) + version: 5.0.0(@parcel/watcher@2.4.1)(@types/node@22.1.0)(graphql@16.8.1)(typescript@5.2.2) '@graphql-codegen/typescript': specifier: 4.0.1 version: 4.0.1(graphql@16.8.1) @@ -219,7 +219,7 @@ importers: version: 5.0.1(@types/eslint@8.56.10)(eslint-config-prettier@9.0.0(eslint@8.52.0))(eslint@8.52.0)(prettier@3.0.3) jest: specifier: 29.7.0 - version: 29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.2.2)) + version: 29.7.0(@types/node@22.1.0)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@22.1.0)(typescript@5.2.2)) prettier: specifier: 3.0.3 version: 3.0.3 @@ -1092,6 +1092,46 @@ importers: specifier: ^5.4.5 version: 5.4.5 + services/non-named-votes-ai: + dependencies: + '@democracy-deutschland/bundestagio-common': + specifier: workspace:* + version: link:../../common/bundestagio + express: + specifier: ^4.19.2 + version: 4.19.2 + express-rate-limit: + specifier: ^7.4.0 + version: 7.4.0(express@4.19.2) + helmet: + specifier: ^7.1.0 + version: 7.1.0 + openai: + specifier: ^4.55.1 + version: 4.55.4 + pino: + specifier: ^9.3.2 + version: 9.3.2 + devDependencies: + '@types/express': + specifier: ^4.17.21 + version: 4.17.21 + '@types/node': + specifier: ^22.1.0 + version: 22.1.0 + tsconfig: + specifier: workspace:* + version: link:../../packages/tsconfig + tsup: + specifier: 'catalog:' + version: 8.0.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(postcss@8.4.38)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@22.1.0)(typescript@5.4.5))(typescript@5.4.5) + tsup-config: + specifier: workspace:* + version: link:../../packages/tsup-config + tsx: + specifier: ^4.11.0 + version: 4.11.0 + services/qr-code-handler: dependencies: express: @@ -3717,6 +3757,9 @@ packages: '@types/node@15.14.9': resolution: {integrity: sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A==} + '@types/node@18.19.44': + resolution: {integrity: sha512-ZsbGerYg72WMXUIE9fYxtvfzLEuq6q8mKERdWFnqTmOvudMxnz+CBNRoOwJ2kNpFOncrKjT1hZwxjlFgQ9qvQA==} + '@types/node@20.12.12': resolution: {integrity: sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==} @@ -4144,6 +4187,10 @@ packages: resolution: {integrity: sha512-yqXL+k5rr8+ZRpOAntkaaRgWgE5o8ESAj5DyRmVTCSoZxXmqemb9Dd7T4i5UzwuERdLAJUy6XzR9zFVuf0kzkw==} engines: {node: '>= 4.0.0'} + agentkeepalive@4.5.0: + resolution: {integrity: sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==} + engines: {node: '>= 8.0.0'} + aggregate-error@3.1.0: resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} engines: {node: '>=8'} @@ -5938,6 +5985,12 @@ packages: resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + express-rate-limit@7.4.0: + resolution: {integrity: sha512-v1204w3cXu5gCDmAvgvzI6qjzZzoMWKnyVDk3ACgfswTQLYiGen+r8w0VnXnGMmzEN/g8fwIQ4JrFFd4ZP6ssg==} + engines: {node: '>= 16'} + peerDependencies: + express: 4 || 5 || ^5.0.0-beta.1 + express@4.19.2: resolution: {integrity: sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==} engines: {node: '>= 0.10.0'} @@ -6150,6 +6203,9 @@ packages: forever-agent@0.6.1: resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==} + form-data-encoder@1.7.2: + resolution: {integrity: sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==} + form-data-encoder@4.0.2: resolution: {integrity: sha512-KQVhvhK8ZkWzxKxOr56CPulAhH3dobtuQ4+hNQ+HekH/Wp5gSOafqRAeTphQUJAIk0GBvHZgJ2ZGRWd5kphMuw==} engines: {node: '>= 18'} @@ -6166,6 +6222,10 @@ packages: resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} engines: {node: '>= 6'} + formdata-node@4.4.1: + resolution: {integrity: sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==} + engines: {node: '>= 12.20'} + forwarded@0.2.0: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} @@ -6520,6 +6580,10 @@ packages: resolution: {integrity: sha512-3KdYYOPcFaQ8539nxxsuR8Pr1PYw/rx2ju40rVTKVR0PBKVnVPaz1acBzsLfofYlWNZgf/2rJcd0trj9Ss5kuw==} engines: {node: '>=16.0.0'} + helmet@7.1.0: + resolution: {integrity: sha512-g+HZqgfbpXdCkme/Cd/mZkV0aV3BZZZSugecH03kl38m/Kmdx8jKjBikpDj2cr+Iynv4KpYEviojNdTJActJAg==} + engines: {node: '>=16.0.0'} + help-me@5.0.0: resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==} @@ -7262,7 +7326,6 @@ packages: jsondiffpatch@0.1.43: resolution: {integrity: sha512-lvOkGuk7gl9Rr4M/SfN530TslMb9QZG9PM5uznjb6oVwkkHNt7rgPNOBO59mwegJ/0Msx/yjwYzdiuoET6a87Q==} hasBin: true - bundledDependencies: [] jsonfile@4.0.0: resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} @@ -7940,6 +8003,10 @@ packages: resolution: {integrity: sha512-mNcltoe1R8o7STTegSOHdnJNN7s5EUvhoS7ShnTHDyOSd+8H+UdWODq6qSv67PjC8Zc5JRT8+oLAMCr0SIXw7g==} engines: {node: ^16 || ^18 || >= 20} + node-domexception@1.0.0: + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} + node-fetch@2.7.0: resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} engines: {node: 4.x || >=6.0.0} @@ -8131,6 +8198,15 @@ packages: resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} engines: {node: '>=12'} + openai@4.55.4: + resolution: {integrity: sha512-TEC75Y6U/OKIJp9fHao3zkTYfKLYGqXdD2TI+xN2Zd5W8KNKvv6E4/OBTOW7jg7fySfrBrhy5fYzBbyBcdHEtQ==} + hasBin: true + peerDependencies: + zod: ^3.23.8 + peerDependenciesMeta: + zod: + optional: true + opener@1.5.2: resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==} hasBin: true @@ -10533,6 +10609,10 @@ packages: resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} engines: {node: '>= 8'} + web-streams-polyfill@4.0.0-beta.3: + resolution: {integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==} + engines: {node: '>= 14'} + webcrypto-core@1.8.0: resolution: {integrity: sha512-kR1UQNH8MD42CYuLzvibfakG5Ew5seG85dMMoAM/1LqvckxaF6pUiidLuraIu4V+YCIFabYecUZAW0TuxAoaqw==} @@ -12084,7 +12164,7 @@ snapshots: graphql: 16.8.1 tslib: 2.6.2 - '@graphql-codegen/cli@5.0.0(@parcel/watcher@2.4.1)(@types/node@20.12.13)(graphql@16.8.1)(typescript@5.2.2)': + '@graphql-codegen/cli@5.0.0(@parcel/watcher@2.4.1)(@types/node@22.1.0)(graphql@16.8.1)(typescript@5.2.2)': dependencies: '@babel/generator': 7.24.6 '@babel/template': 7.24.6 @@ -12094,12 +12174,12 @@ snapshots: '@graphql-tools/apollo-engine-loader': 8.0.1(graphql@16.8.1) '@graphql-tools/code-file-loader': 8.1.2(graphql@16.8.1) '@graphql-tools/git-loader': 8.0.6(graphql@16.8.1) - '@graphql-tools/github-loader': 8.0.1(@types/node@20.12.13)(graphql@16.8.1) + '@graphql-tools/github-loader': 8.0.1(@types/node@22.1.0)(graphql@16.8.1) '@graphql-tools/graphql-file-loader': 8.0.1(graphql@16.8.1) '@graphql-tools/json-file-loader': 8.0.1(graphql@16.8.1) '@graphql-tools/load': 8.0.2(graphql@16.8.1) - '@graphql-tools/prisma-loader': 8.0.4(@types/node@20.12.13)(graphql@16.8.1) - '@graphql-tools/url-loader': 8.0.2(@types/node@20.12.13)(graphql@16.8.1) + '@graphql-tools/prisma-loader': 8.0.4(@types/node@22.1.0)(graphql@16.8.1) + '@graphql-tools/url-loader': 8.0.2(@types/node@22.1.0)(graphql@16.8.1) '@graphql-tools/utils': 10.2.1(graphql@16.8.1) '@whatwg-node/fetch': 0.8.8 chalk: 4.1.2 @@ -12107,7 +12187,7 @@ snapshots: debounce: 1.2.1 detect-indent: 6.1.0 graphql: 16.8.1 - graphql-config: 5.0.3(@types/node@20.12.13)(graphql@16.8.1)(typescript@5.2.2) + graphql-config: 5.0.3(@types/node@22.1.0)(graphql@16.8.1)(typescript@5.2.2) inquirer: 8.2.6 is-glob: 4.0.3 jiti: 1.21.0 @@ -12423,6 +12503,19 @@ snapshots: transitivePeerDependencies: - '@types/node' + '@graphql-tools/executor-http@1.0.9(@types/node@22.1.0)(graphql@16.8.1)': + dependencies: + '@graphql-tools/utils': 10.2.1(graphql@16.8.1) + '@repeaterjs/repeater': 3.0.6 + '@whatwg-node/fetch': 0.9.17 + extract-files: 11.0.0 + graphql: 16.8.1 + meros: 1.3.0(@types/node@22.1.0) + tslib: 2.6.2 + value-or-promise: 1.0.12 + transitivePeerDependencies: + - '@types/node' + '@graphql-tools/executor-legacy-ws@1.0.6(graphql@16.8.1)': dependencies: '@graphql-tools/utils': 10.2.1(graphql@16.8.1) @@ -12471,6 +12564,21 @@ snapshots: - encoding - supports-color + '@graphql-tools/github-loader@8.0.1(@types/node@22.1.0)(graphql@16.8.1)': + dependencies: + '@ardatan/sync-fetch': 0.0.1 + '@graphql-tools/executor-http': 1.0.9(@types/node@22.1.0)(graphql@16.8.1) + '@graphql-tools/graphql-tag-pluck': 8.3.1(graphql@16.8.1) + '@graphql-tools/utils': 10.2.1(graphql@16.8.1) + '@whatwg-node/fetch': 0.9.17 + graphql: 16.8.1 + tslib: 2.6.2 + value-or-promise: 1.0.12 + transitivePeerDependencies: + - '@types/node' + - encoding + - supports-color + '@graphql-tools/graphql-file-loader@8.0.1(graphql@16.8.1)': dependencies: '@graphql-tools/import': 7.0.1(graphql@16.8.1) @@ -12559,6 +12667,32 @@ snapshots: - supports-color - utf-8-validate + '@graphql-tools/prisma-loader@8.0.4(@types/node@22.1.0)(graphql@16.8.1)': + dependencies: + '@graphql-tools/url-loader': 8.0.2(@types/node@22.1.0)(graphql@16.8.1) + '@graphql-tools/utils': 10.2.1(graphql@16.8.1) + '@types/js-yaml': 4.0.9 + '@whatwg-node/fetch': 0.9.17 + chalk: 4.1.2 + debug: 4.3.4 + dotenv: 16.4.5 + graphql: 16.8.1 + graphql-request: 6.1.0(graphql@16.8.1) + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.4 + jose: 5.3.0 + js-yaml: 4.1.0 + lodash: 4.17.21 + scuid: 1.1.0 + tslib: 2.6.2 + yaml-ast-parser: 0.0.43 + transitivePeerDependencies: + - '@types/node' + - bufferutil + - encoding + - supports-color + - utf-8-validate + '@graphql-tools/relay-operation-optimizer@7.0.1(graphql@16.8.1)': dependencies: '@ardatan/relay-compiler': 12.0.0(graphql@16.8.1) @@ -12607,6 +12741,28 @@ snapshots: - encoding - utf-8-validate + '@graphql-tools/url-loader@8.0.2(@types/node@22.1.0)(graphql@16.8.1)': + dependencies: + '@ardatan/sync-fetch': 0.0.1 + '@graphql-tools/delegate': 10.0.11(graphql@16.8.1) + '@graphql-tools/executor-graphql-ws': 1.1.2(graphql@16.8.1) + '@graphql-tools/executor-http': 1.0.9(@types/node@22.1.0)(graphql@16.8.1) + '@graphql-tools/executor-legacy-ws': 1.0.6(graphql@16.8.1) + '@graphql-tools/utils': 10.2.1(graphql@16.8.1) + '@graphql-tools/wrap': 10.0.5(graphql@16.8.1) + '@types/ws': 8.5.10 + '@whatwg-node/fetch': 0.9.17 + graphql: 16.8.1 + isomorphic-ws: 5.0.0(ws@8.17.0) + tslib: 2.6.2 + value-or-promise: 1.0.12 + ws: 8.17.0 + transitivePeerDependencies: + - '@types/node' + - bufferutil + - encoding + - utf-8-validate + '@graphql-tools/utils@10.2.1(graphql@16.8.1)': dependencies: '@graphql-typed-document-node/core': 3.2.0(graphql@16.8.1) @@ -12772,27 +12928,27 @@ snapshots: '@jest/console@29.7.0': dependencies: '@jest/types': 29.6.3 - '@types/node': 20.12.13 + '@types/node': 22.1.0 chalk: 4.1.2 jest-message-util: 29.7.0 jest-util: 29.7.0 slash: 3.0.0 - '@jest/core@29.7.0(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.2.2))': + '@jest/core@29.7.0(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5))': dependencies: '@jest/console': 29.7.0 '@jest/reporters': 29.7.0 '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.12.13 + '@types/node': 22.1.0 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.9.0 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.2.2)) + jest-config: 29.7.0(@types/node@22.1.0)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5)) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -12813,21 +12969,21 @@ snapshots: - supports-color - ts-node - '@jest/core@29.7.0(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5))': + '@jest/core@29.7.0(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@22.1.0)(typescript@5.2.2))': dependencies: '@jest/console': 29.7.0 '@jest/reporters': 29.7.0 '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.12.13 + '@types/node': 22.1.0 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.9.0 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5)) + jest-config: 29.7.0(@types/node@22.1.0)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@22.1.0)(typescript@5.2.2)) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -12855,14 +13011,14 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.12.13 + '@types/node': 22.1.0 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.9.0 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@22.1.0)(typescript@5.4.5)) + jest-config: 29.7.0(@types/node@22.1.0)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@22.1.0)(typescript@5.4.5)) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -12928,7 +13084,7 @@ snapshots: '@jest/transform': 29.7.0 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.25 - '@types/node': 20.12.13 + '@types/node': 22.1.0 chalk: 4.1.2 collect-v8-coverage: 1.0.2 exit: 0.1.2 @@ -12998,7 +13154,7 @@ snapshots: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 20.12.13 + '@types/node': 22.1.0 '@types/yargs': 17.0.32 chalk: 4.1.2 @@ -13904,7 +14060,7 @@ snapshots: '@types/jsdom@21.1.6': dependencies: - '@types/node': 20.12.13 + '@types/node': 22.1.0 '@types/tough-cookie': 4.0.5 parse5: 7.1.2 @@ -13957,13 +14113,17 @@ snapshots: '@types/node-fetch@2.6.11': dependencies: - '@types/node': 20.12.13 + '@types/node': 22.1.0 form-data: 4.0.0 '@types/node-gcm@1.0.5': {} '@types/node@15.14.9': {} + '@types/node@18.19.44': + dependencies: + undici-types: 5.26.5 + '@types/node@20.12.12': dependencies: undici-types: 5.26.5 @@ -14004,7 +14164,7 @@ snapshots: '@types/sax@1.2.7': dependencies: - '@types/node': 20.12.13 + '@types/node': 22.1.0 '@types/semver@7.5.8': {} @@ -14043,22 +14203,22 @@ snapshots: '@types/whatwg-url@8.2.2': dependencies: - '@types/node': 20.12.13 + '@types/node': 22.1.0 '@types/webidl-conversions': 7.0.3 '@types/ws@8.5.10': dependencies: - '@types/node': 20.12.13 + '@types/node': 22.1.0 '@types/xml2js@0.4.14': dependencies: - '@types/node': 20.12.13 + '@types/node': 22.1.0 '@types/xmldom@0.1.34': {} '@types/xsd-schema-validator@0.5.7': dependencies: - '@types/node': 20.12.13 + '@types/node': 22.1.0 '@types/yargs-parser@21.0.3': {} @@ -14506,7 +14666,7 @@ snapshots: '@wry/context@0.4.4': dependencies: - '@types/node': 20.12.13 + '@types/node': 22.1.0 tslib: 1.14.1 '@wry/context@0.7.4': @@ -14590,6 +14750,10 @@ snapshots: dependencies: humanize-ms: 1.2.1 + agentkeepalive@4.5.0: + dependencies: + humanize-ms: 1.2.1 + aggregate-error@3.1.0: dependencies: clean-stack: 2.2.0 @@ -15889,13 +16053,13 @@ snapshots: - supports-color - utf-8-validate - create-jest@29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.2.2)): + create-jest@29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5)): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.2.2)) + jest-config: 29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5)) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -15904,13 +16068,13 @@ snapshots: - supports-color - ts-node - create-jest@29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5)): + create-jest@29.7.0(@types/node@22.1.0)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@22.1.0)(typescript@5.2.2)): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5)) + jest-config: 29.7.0(@types/node@22.1.0)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@22.1.0)(typescript@5.2.2)) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -16758,8 +16922,8 @@ snapshots: '@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.4.5) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0))(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) eslint-plugin-jsx-a11y: 6.8.0(eslint@8.57.0) eslint-plugin-react: 7.34.2(eslint@8.57.0) eslint-plugin-react-hooks: 4.6.2(eslint@8.57.0) @@ -16802,13 +16966,13 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0): + eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0))(eslint@8.57.0): dependencies: debug: 4.3.4 enhanced-resolve: 5.16.1 eslint: 8.57.0 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) fast-glob: 3.3.2 get-tsconfig: 4.7.5 is-core-module: 2.13.1 @@ -16830,24 +16994,14 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0): - dependencies: - debug: 3.2.7 - optionalDependencies: - '@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.4.5) - eslint: 8.57.0 - eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0) - transitivePeerDependencies: - - supports-color - - eslint-module-utils@2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0): + eslint-module-utils@2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.4.5) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0))(eslint@8.57.0) transitivePeerDependencies: - supports-color @@ -16878,7 +17032,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0): + eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): dependencies: array-includes: 3.1.8 array.prototype.findlastindex: 1.2.5 @@ -16888,7 +17042,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) hasown: 2.0.2 is-core-module: 2.13.1 is-glob: 4.0.3 @@ -17117,7 +17271,7 @@ snapshots: eval@0.1.8: dependencies: - '@types/node': 20.12.13 + '@types/node': 22.1.0 require-like: 0.1.2 event-stream@3.3.4: @@ -17170,6 +17324,10 @@ snapshots: jest-message-util: 29.7.0 jest-util: 29.7.0 + express-rate-limit@7.4.0(express@4.19.2): + dependencies: + express: 4.19.2 + express@4.19.2: dependencies: accepts: 1.3.8 @@ -17431,6 +17589,8 @@ snapshots: forever-agent@0.6.1: optional: true + form-data-encoder@1.7.2: {} + form-data-encoder@4.0.2: {} form-data@2.3.3: @@ -17452,6 +17612,11 @@ snapshots: combined-stream: 1.0.8 mime-types: 2.1.35 + formdata-node@4.4.1: + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 4.0.0-beta.3 + forwarded@0.2.0: {} fraction.js@4.3.7: {} @@ -17773,7 +17938,7 @@ snapshots: graphemer@1.4.0: {} - graphql-config@5.0.3(@types/node@20.12.13)(graphql@16.8.1)(typescript@5.2.2): + graphql-config@5.0.3(@types/node@20.12.13)(graphql@16.8.1)(typescript@5.4.5): dependencies: '@graphql-tools/graphql-file-loader': 8.0.1(graphql@16.8.1) '@graphql-tools/json-file-loader': 8.0.1(graphql@16.8.1) @@ -17781,7 +17946,7 @@ snapshots: '@graphql-tools/merge': 9.0.4(graphql@16.8.1) '@graphql-tools/url-loader': 8.0.2(@types/node@20.12.13)(graphql@16.8.1) '@graphql-tools/utils': 10.2.1(graphql@16.8.1) - cosmiconfig: 8.3.6(typescript@5.2.2) + cosmiconfig: 8.3.6(typescript@5.4.5) graphql: 16.8.1 jiti: 1.21.0 minimatch: 4.2.3 @@ -17794,15 +17959,15 @@ snapshots: - typescript - utf-8-validate - graphql-config@5.0.3(@types/node@20.12.13)(graphql@16.8.1)(typescript@5.4.5): + graphql-config@5.0.3(@types/node@22.1.0)(graphql@16.8.1)(typescript@5.2.2): dependencies: '@graphql-tools/graphql-file-loader': 8.0.1(graphql@16.8.1) '@graphql-tools/json-file-loader': 8.0.1(graphql@16.8.1) '@graphql-tools/load': 8.0.2(graphql@16.8.1) '@graphql-tools/merge': 9.0.4(graphql@16.8.1) - '@graphql-tools/url-loader': 8.0.2(@types/node@20.12.13)(graphql@16.8.1) + '@graphql-tools/url-loader': 8.0.2(@types/node@22.1.0)(graphql@16.8.1) '@graphql-tools/utils': 10.2.1(graphql@16.8.1) - cosmiconfig: 8.3.6(typescript@5.4.5) + cosmiconfig: 8.3.6(typescript@5.2.2) graphql: 16.8.1 jiti: 1.21.0 minimatch: 4.2.3 @@ -17927,6 +18092,8 @@ snapshots: ow: 0.28.2 tslib: 2.6.2 + helmet@7.1.0: {} + help-me@5.0.0: {} hoist-non-react-statics@3.3.2: @@ -18471,16 +18638,16 @@ snapshots: - babel-plugin-macros - supports-color - jest-cli@29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.2.2)): + jest-cli@29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5)): dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.2.2)) + '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5)) '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.2.2)) + create-jest: 29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5)) exit: 0.1.2 import-local: 3.1.0 - jest-config: 29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.2.2)) + jest-config: 29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5)) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -18490,16 +18657,16 @@ snapshots: - supports-color - ts-node - jest-cli@29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5)): + jest-cli@29.7.0(@types/node@22.1.0)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@22.1.0)(typescript@5.2.2)): dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5)) + '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@22.1.0)(typescript@5.2.2)) '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5)) + create-jest: 29.7.0(@types/node@22.1.0)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@22.1.0)(typescript@5.2.2)) exit: 0.1.2 import-local: 3.1.0 - jest-config: 29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5)) + jest-config: 29.7.0(@types/node@22.1.0)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@22.1.0)(typescript@5.2.2)) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -18529,7 +18696,7 @@ snapshots: - ts-node optional: true - jest-config@29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.2.2)): + jest-config@29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5)): dependencies: '@babel/core': 7.24.6 '@jest/test-sequencer': 29.7.0 @@ -18555,12 +18722,12 @@ snapshots: strip-json-comments: 3.1.1 optionalDependencies: '@types/node': 20.12.13 - ts-node: 10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.2.2) + ts-node: 10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5) transitivePeerDependencies: - babel-plugin-macros - supports-color - jest-config@29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5)): + jest-config@29.7.0(@types/node@22.1.0)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5)): dependencies: '@babel/core': 7.24.6 '@jest/test-sequencer': 29.7.0 @@ -18585,13 +18752,13 @@ snapshots: slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - '@types/node': 20.12.13 + '@types/node': 22.1.0 ts-node: 10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5) transitivePeerDependencies: - babel-plugin-macros - supports-color - jest-config@29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@22.1.0)(typescript@5.4.5)): + jest-config@29.7.0(@types/node@22.1.0)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@22.1.0)(typescript@5.2.2)): dependencies: '@babel/core': 7.24.6 '@jest/test-sequencer': 29.7.0 @@ -18616,12 +18783,11 @@ snapshots: slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - '@types/node': 20.12.13 - ts-node: 10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@22.1.0)(typescript@5.4.5) + '@types/node': 22.1.0 + ts-node: 10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@22.1.0)(typescript@5.2.2) transitivePeerDependencies: - babel-plugin-macros - supports-color - optional: true jest-config@29.7.0(@types/node@22.1.0)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@22.1.0)(typescript@5.4.5)): dependencies: @@ -18689,7 +18855,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.9 - '@types/node': 20.12.13 + '@types/node': 22.1.0 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -18763,7 +18929,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.12.13 + '@types/node': 22.1.0 chalk: 4.1.2 emittery: 0.13.1 graceful-fs: 4.2.11 @@ -18791,7 +18957,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.12.13 + '@types/node': 22.1.0 chalk: 4.1.2 cjs-module-lexer: 1.3.1 collect-v8-coverage: 1.0.2 @@ -18837,7 +19003,7 @@ snapshots: jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 20.12.13 + '@types/node': 22.1.0 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -18856,7 +19022,7 @@ snapshots: dependencies: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.12.13 + '@types/node': 22.1.0 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -18865,7 +19031,7 @@ snapshots: jest-worker@27.5.1: dependencies: - '@types/node': 20.12.13 + '@types/node': 22.1.0 merge-stream: 2.0.0 supports-color: 8.1.1 @@ -18876,24 +19042,24 @@ snapshots: merge-stream: 2.0.0 supports-color: 8.1.1 - jest@29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.2.2)): + jest@29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5)): dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.2.2)) + '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5)) '@jest/types': 29.6.3 import-local: 3.1.0 - jest-cli: 29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.2.2)) + jest-cli: 29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5)) transitivePeerDependencies: - '@types/node' - babel-plugin-macros - supports-color - ts-node - jest@29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5)): + jest@29.7.0(@types/node@22.1.0)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@22.1.0)(typescript@5.2.2)): dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5)) + '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@22.1.0)(typescript@5.2.2)) '@jest/types': 29.6.3 import-local: 3.1.0 - jest-cli: 29.7.0(@types/node@20.12.13)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5)) + jest-cli: 29.7.0(@types/node@22.1.0)(ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@22.1.0)(typescript@5.2.2)) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -19513,6 +19679,10 @@ snapshots: optionalDependencies: '@types/node': 20.12.13 + meros@1.3.0(@types/node@22.1.0): + optionalDependencies: + '@types/node': 22.1.0 + methods@1.1.2: {} micromatch@4.0.7: @@ -19801,6 +19971,8 @@ snapshots: node-addon-api@7.1.0: optional: true + node-domexception@1.0.0: {} + node-fetch@2.7.0: dependencies: whatwg-url: 5.0.0 @@ -20004,6 +20176,18 @@ snapshots: dependencies: mimic-fn: 4.0.0 + openai@4.55.4: + dependencies: + '@types/node': 18.19.44 + '@types/node-fetch': 2.6.11 + abort-controller: 3.0.0 + agentkeepalive: 4.5.0 + form-data-encoder: 1.7.2 + formdata-node: 4.4.1 + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + opener@1.5.2: {} openid-client@5.6.5: @@ -20639,7 +20823,7 @@ snapshots: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 - '@types/node': 20.12.13 + '@types/node': 22.1.0 long: 5.2.3 optional: true @@ -22330,7 +22514,7 @@ snapshots: '@swc/core': 1.5.24(@swc/helpers@0.5.11) optional: true - ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.2.2): + ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 @@ -22344,32 +22528,32 @@ snapshots: create-require: 1.1.1 diff: 4.0.2 make-error: 1.3.6 - typescript: 5.2.2 + typescript: 5.4.5 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 optionalDependencies: '@swc/core': 1.5.24(@swc/helpers@0.5.11) - optional: true - ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.13)(typescript@5.4.5): + ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@22.1.0)(typescript@5.2.2): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 20.12.13 + '@types/node': 22.1.0 acorn: 8.11.3 acorn-walk: 8.3.2 arg: 4.1.3 create-require: 1.1.1 diff: 4.0.2 make-error: 1.3.6 - typescript: 5.4.5 + typescript: 5.2.2 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 optionalDependencies: '@swc/core': 1.5.24(@swc/helpers@0.5.11) + optional: true ts-node@10.9.2(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@22.1.0)(typescript@5.4.5): dependencies: @@ -22869,6 +23053,8 @@ snapshots: web-streams-polyfill@3.3.3: {} + web-streams-polyfill@4.0.0-beta.3: {} + webcrypto-core@1.8.0: dependencies: '@peculiar/asn1-schema': 2.3.8 diff --git a/services/non-named-votes-ai/.env.example b/services/non-named-votes-ai/.env.example new file mode 100644 index 000000000..9fcb91951 --- /dev/null +++ b/services/non-named-votes-ai/.env.example @@ -0,0 +1,10 @@ +# https://platform.openai.com/organization/api-keys +OPENAI_API_KEY= +# Secret key to authenticate the requests +SECRET=CHANGE_ME +# Port to run the server on +PORT=3005 +# MongoDB connection string +DB_URL=mongodb://localhost:27017/bundestagio +# Log level for pino +PINO_LOG_LEVEL=info \ No newline at end of file diff --git a/services/non-named-votes-ai/.gitignore b/services/non-named-votes-ai/.gitignore new file mode 100644 index 000000000..2eea525d8 --- /dev/null +++ b/services/non-named-votes-ai/.gitignore @@ -0,0 +1 @@ +.env \ No newline at end of file diff --git a/services/non-named-votes-ai/FLOW.md b/services/non-named-votes-ai/FLOW.md new file mode 100644 index 000000000..53bab7e10 --- /dev/null +++ b/services/non-named-votes-ai/FLOW.md @@ -0,0 +1,66 @@ +# Flow Charts + +Dieser Service liefert den Textabschnitt, in dem über eine **nicht namentliche Abstimmung** abgestimmt wird. Dafür benötigt er eine **URL** zu einem PDF mit einem Plenarsaalprotokoll, den **Titel** der Gesetzgebung oder des Antrags und optional die **Dokumentennummer**. + +## High Level + +```mermaid +flowchart TD + Start([Input: '´plenaarsaalprotokoll-url, titel, dokumentennummer]) + Error([Es konnte kein text gefunden werden, da das Plenarsaalprotokoll nicht gefunden wurde]) + Output([Output: Abstimmungstext der nicht namentlichen abstimmung]) + + Start --> |Ok|Output + Start --> |Fehler|Error +``` + +## In Deep + +```mermaid +flowchart TD + Start([Start mit '´plenaarsaalprotokoll-url, titel, dokumentennummer]) + RequestDb([Datenbankabfrage: Plenarsaalprotokoll 'url']) + AssistantExists{Assistent existiert?} + CreateAssistant([Assistent erstellen]) + UploadFile([Datei bei openAI hochladen]) + CreateVectorStore([VectorStore erstellen]) + CheckOpenAI{VectorStore bei openAI verfügbar?} + UseExistingVectorStore([Bestehenden VectorStore verwenden]) + CreateThread([Neuen Thread erstellen]) + StartThread([Thread mit Assistant starten]) + SaveEntry([Assistent und VectorStore in Datenbank speichern]) + GetMessages([Antwort abrufen]) + FilterMessage([Antwort zwischen ### herausfiltern]) + DeleteThread([Thread löschen]) + ReturnMessage([Antwort zurückgeben]) + Error([Fehler: Kein Plenarsaalprotokoll vorhanden]) + ReturnError([Fehlerantwort]) + + Start --> RequestDb + + RequestDb -->|Eintrag vorhanden| AssistantExists + RequestDb -->|Kein Eintrag| Error + + Error --> ReturnError + + AssistantExists --> |Ja| CheckOpenAI + AssistantExists --> |Nein| CreateAssistant + + CreateAssistant --> CreateThread + + CheckOpenAI -->|Ja| UseExistingVectorStore + CheckOpenAI -->|Nein| UploadFile + + UseExistingVectorStore --> CreateThread + UploadFile --> CreateVectorStore + + CreateVectorStore --> CreateThread + + + CreateThread --> StartThread + StartThread --> SaveEntry + SaveEntry --> GetMessages + GetMessages --> FilterMessage + FilterMessage --> DeleteThread + DeleteThread --> ReturnMessage +``` diff --git a/services/non-named-votes-ai/package.json b/services/non-named-votes-ai/package.json new file mode 100644 index 000000000..146789eda --- /dev/null +++ b/services/non-named-votes-ai/package.json @@ -0,0 +1,30 @@ +{ + "name": "non-named-votes-ai", + "version": "1.0.0", + "description": "", + "main": "build/index.js", + "scripts": { + "dev": "tsx --env-file .env --watch src/index.ts", + "build": "tsup-node", + "start": "node build/index.js" + }, + "keywords": [], + "author": "", + "license": "Apache-2.0", + "dependencies": { + "@democracy-deutschland/bundestagio-common": "workspace:*", + "express": "^4.19.2", + "express-rate-limit": "^7.4.0", + "helmet": "^7.1.0", + "openai": "^4.55.1", + "pino": "^9.3.2" + }, + "devDependencies": { + "@types/express": "^4.17.21", + "@types/node": "^22.1.0", + "tsconfig": "workspace:*", + "tsup": "catalog:", + "tsup-config": "workspace:*", + "tsx": "^4.11.0" + } +} diff --git a/services/non-named-votes-ai/src/authMiddleware.ts b/services/non-named-votes-ai/src/authMiddleware.ts new file mode 100644 index 000000000..3d8833ed3 --- /dev/null +++ b/services/non-named-votes-ai/src/authMiddleware.ts @@ -0,0 +1,31 @@ +import { Request, Response, NextFunction } from 'express'; +import crypto from 'crypto'; + +export const authMiddleware = (req: Request, res: Response, next: NextFunction) => { + const authHeader = req.headers.authorization; + + if (!authHeader) { + return res.status(401).send('Authorization header missing'); + } + + const token = authHeader.split(' ')[1]; + + if (!token) { + return res.status(401).send('Token missing'); + } + + const secret = process.env.SECRET; + + if (!secret) { + return res.status(500).send('Server configuration error'); + } + + const tokenBuffer = Buffer.from(token); + const secretBuffer = Buffer.from(secret); + + if (tokenBuffer.length !== secretBuffer.length || !crypto.timingSafeEqual(tokenBuffer, secretBuffer)) { + return res.status(401).send('Unauthorized'); + } + + next(); +}; diff --git a/services/non-named-votes-ai/src/beschlusstextController.ts b/services/non-named-votes-ai/src/beschlusstextController.ts new file mode 100644 index 000000000..aa5ba4813 --- /dev/null +++ b/services/non-named-votes-ai/src/beschlusstextController.ts @@ -0,0 +1,78 @@ +import { Request, Response } from 'express'; +import { NonNamedVotesAiModel } from '@democracy-deutschland/bundestagio-common'; +import { + createThread, + ensureAssistantId, + ensureFile, + ensureVectorStore, + filterMessage, + getMessageList, + runThread, +} from './utils'; +import { log } from './logger'; + +export const getBeschlusstext = async (req: Request, res: Response) => { + const { pdfUrl, title, drucksachen } = req.query as { pdfUrl: string; title: string; drucksachen?: string[] }; + log.info('getBeschlusstext start'); + log.debug(`getBeschlusstext: ${pdfUrl}, ${title}, ${drucksachen}`); + + if (!pdfUrl || typeof pdfUrl !== 'string') { + return res.status(400).json({ message: 'pdfUrl muss angegeben werden' }); + } + + if (!title || typeof title !== 'string') { + return res.status(400).json({ message: 'title muss angegeben werden' }); + } + + const databaseEntry = await NonNamedVotesAiModel.findOne({ pdfUrl: pdfUrl }); + log.debug({ databaseEntry }); + + // Ensure OpenAi Objects exist + const file = await ensureFile({ pdfUrl: pdfUrl as string, file_id: databaseEntry?.fileId }); + log.debug({ file }); + const vectorStore = await ensureVectorStore({ file_id: file.id, vector_store_id: databaseEntry?.vectorStoreId }); + log.debug({ vectorStore }); + const assistant = await ensureAssistantId({ + vector_store_id: vectorStore.id, + assistant_id: databaseEntry?.assistantId, + }); + log.debug({ assistant }); + const thread = await createThread({ title, drucksachen }); + log.debug({ thread }); + + // Save to Database + if (!databaseEntry) { + await new NonNamedVotesAiModel({ + pdfUrl, + fileId: file.id, + vectorStoreId: vectorStore.id, + assistantId: assistant.id, + threadId: thread.id, + }).save(); + log.debug('saved'); + } else { + databaseEntry.fileId = file.id; + databaseEntry.vectorStoreId = vectorStore.id; + databaseEntry.assistantId = assistant.id; + await databaseEntry.save(); + log.debug('updated'); + } + + // Run + const run = await runThread({ assistant_id: assistant.id, threadId: thread.id }); + log.debug({ run }); + const messages = await getMessageList(thread.id, run.id); + log.debug({ messages }); + const message = await filterMessage(messages); + log.debug({ message }); + + const beschlusstext = { + pdfUrl, + text: message, + }; + log.debug({ beschlusstext }); + + log.info('getBeschlusstext end'); + log.debug(`getBeschlusstext: ${pdfUrl}, ${title}, ${drucksachen} -> ${beschlusstext.text}`); + return res.json(beschlusstext); +}; diff --git a/services/non-named-votes-ai/src/index.ts b/services/non-named-votes-ai/src/index.ts new file mode 100644 index 000000000..c5e679eb6 --- /dev/null +++ b/services/non-named-votes-ai/src/index.ts @@ -0,0 +1,30 @@ +import express from 'express'; +import { authMiddleware } from './authMiddleware'; +import { getBeschlusstext } from './beschlusstextController'; +import { mongoConnect } from '@democracy-deutschland/bundestagio-common'; +import { log } from './logger'; +import helmet from 'helmet'; +import { limiter } from './rateLimiter'; + +(async () => { + await mongoConnect(process.env.DB_URL!); + + const app = express(); + + // Disable x-powered-by header to make it harder for attackers + app.disable('x-powered-by'); + + // Helmet verwenden, um HTTP-Header zu sichern + app.use(helmet()); + + // apply rate limiter to all requests + app.use(limiter); + + app.get('/beschlusstext', authMiddleware, getBeschlusstext); + + const PORT = process.env.PORT || 3005; + app.listen(PORT, () => { + log.info(`Server is running`); + log.debug(`Server is running on port ${PORT}`); + }); +})(); diff --git a/services/non-named-votes-ai/src/logger.ts b/services/non-named-votes-ai/src/logger.ts new file mode 100644 index 000000000..6eda4095d --- /dev/null +++ b/services/non-named-votes-ai/src/logger.ts @@ -0,0 +1,13 @@ +import pino from 'pino'; + +const log = pino({ + level: process.env.PINO_LOG_LEVEL || 'info', + transport: { + target: 'pino-pretty', + options: { + colorize: true, + }, + }, +}); + +export { log }; diff --git a/services/non-named-votes-ai/src/rateLimiter.ts b/services/non-named-votes-ai/src/rateLimiter.ts new file mode 100644 index 000000000..f286ee424 --- /dev/null +++ b/services/non-named-votes-ai/src/rateLimiter.ts @@ -0,0 +1,6 @@ +// set up rate limiter: maximum of five requests per minute +import RateLimit from 'express-rate-limit'; +export const limiter = RateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100, // max 100 requests per windowMs +}); diff --git a/services/non-named-votes-ai/src/test.ts b/services/non-named-votes-ai/src/test.ts new file mode 100644 index 000000000..49917d021 --- /dev/null +++ b/services/non-named-votes-ai/src/test.ts @@ -0,0 +1,24 @@ +import { log } from './logger'; +import { createThread, ensureAssistantId, ensureFile, filterMessage, getMessageList, runThread } from './utils'; + +let assistantId = 'asst_lK6r9ApD1ZYNvqwEOkleXmfd'; +let fileId = 'file-pEo6HmgAFFGlMvmMKLYeSiEF'; +const vector_store_ids = ['vs_i8KmJXDEtEdUyomai5oCavfz']; + +export const getBeschlusstext = async (pdfUrl: string, title: string, drucksachen: string[]) => { + const assistant = await ensureAssistantId({ vector_store_id: vector_store_ids[0], assistant_id: assistantId }); + assistantId = assistant.id; + + fileId = await ensureFile({ pdfUrl, file_id: fileId }).then((file) => file.id); + + const thread = await createThread({ title, drucksachen }); + log.debug('thread id', thread.id); + + log.debug(thread.tool_resources?.file_search); + + const run = await runThread({ threadId: thread.id, assistant_id: assistantId }); + + const messages = await getMessageList(thread.id, run.id); + + log.debug(filterMessage(messages)); +}; diff --git a/services/non-named-votes-ai/src/utils.ts b/services/non-named-votes-ai/src/utils.ts new file mode 100644 index 000000000..6948635dd --- /dev/null +++ b/services/non-named-votes-ai/src/utils.ts @@ -0,0 +1,122 @@ +import OpenAI from 'openai'; +import { Assistant } from 'openai/resources/beta/assistants'; +import { MessagesPage } from 'openai/resources/beta/threads/messages'; +import { VectorStore } from 'openai/resources/beta/vector-stores/vector-stores'; + +const openai = new OpenAI(); +const name = process.env.NAME ?? 'Non-Named Votes AI'; + +export const retriveAssistant = async (assistantId: string) => await openai.beta.assistants.retrieve(assistantId); +export const createAssistant = async ({ vector_store_id }: { vector_store_id: string }) => + await openai.beta.assistants.create({ + name, + instructions: ` + Du erhältst vom User einen Titel und eine Dokumentennummer zu einer Abstimmung aus dem Plenarsaalprotokoll des Deutschen Bundestages. Deine Aufgabe ist es, den exakten Wortlaut des Textabschnitts zur Abstimmung, inklusive der namentlichen Nennung der Parteien und ihres Abstimmungsverhaltens, wie er im Protokoll steht, wiederzugeben. Füge keine zusätzlichen Informationen oder Erklärungen hinzu. Gib den Textabschnitt wortwörtlich und vollständig so zurück, wie er im Dokument erscheint. + Setze am anfang des Textes ein "#####" und am Ende ein "#####" um den Textabschnitt zu kennzeichnen. + `, + model: 'gpt-4o', + tools: [{ type: 'file_search' }], + tool_resources: { file_search: { vector_store_ids: [vector_store_id] } }, + temperature: 0, + }); +export const ensureAssistantId = async ({ + vector_store_id, + assistant_id, +}: { + vector_store_id: string; + assistant_id?: string; +}) => { + let assistant: Assistant | undefined = undefined; + if (assistant_id) { + assistant = await retriveAssistant(assistant_id); + } + + if (!assistant) { + assistant = await createAssistant({ vector_store_id }); + } + return assistant; +}; + +export const retriveFile = async (fileId: string) => await openai.files.retrieve(fileId); +export const createFile = async ({ pdfUrl }: { pdfUrl: string }) => + await openai.files.create({ + file: await fetch(pdfUrl), + purpose: 'assistants', + }); +export const ensureFile = async ({ pdfUrl, file_id }: { pdfUrl: string; file_id?: string }) => { + let file = undefined; + if (file_id) { + file = await retriveFile(file_id); + } + + if (!file) { + file = await createFile({ pdfUrl }); + } + + return file; +}; + +export const retriveVectorStore = async (vectorStoreId: string) => + await openai.beta.vectorStores.retrieve(vectorStoreId); +export const createVectorStore = async ({ file_ids }: { file_ids: string[] }) => + await openai.beta.vectorStores.create({ + file_ids, + }); +export const ensureVectorStore = async ({ + file_id, + vector_store_id, +}: { + file_id: string; + vector_store_id?: string; +}) => { + let vectorStore: VectorStore | undefined = undefined; + if (vector_store_id) { + vectorStore = await retriveVectorStore(vector_store_id); + } + + if (!vectorStore) { + vectorStore = await createVectorStore({ file_ids: [file_id] }); + } + + return vectorStore; +}; + +export const createThread = async ({ title, drucksachen }: { title: string; drucksachen?: string[] }) => + await openai.beta.threads.create({ + messages: [ + { + role: 'user', + content: `Titel: "${title}" + Drucksachen: ${drucksachen?.join(', ')}`, + // Attach the new file to the message. + // attachments: [{ file_id: fileId, tools: [{ type: 'file_search' }] }], + }, + ], + }); +export const runThread = async ({ threadId, assistant_id }: { threadId: string; assistant_id: string }) => + await openai.beta.threads.runs.createAndPoll(threadId, { + assistant_id, + }); + +export const getMessageList = async (threadId: string, runId: string) => { + return await openai.beta.threads.messages.list(threadId, { + run_id: runId, + }); +}; + +export const extractBetweenHashesFromBeschluss = (text: string): string | null => { + const regex = /#####([\s\S]*?)#####/; + const match = text.match(regex); + return match ? match[1].trim() : null; +}; + +export const filterMessage = (messages: MessagesPage) => { + const message = messages.data.pop()!; + if (message.content[0].type === 'text') { + const { text } = message.content[0]; + + return extractBetweenHashesFromBeschluss(text.value); + return text.value; + } + throw new Error('No text message found'); +}; diff --git a/services/non-named-votes-ai/tsconfig.json b/services/non-named-votes-ai/tsconfig.json new file mode 100644 index 000000000..6b7962de0 --- /dev/null +++ b/services/non-named-votes-ai/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "tsconfig/base.json" +} diff --git a/services/non-named-votes-ai/tsup.config.ts b/services/non-named-votes-ai/tsup.config.ts new file mode 100644 index 000000000..af5ec3392 --- /dev/null +++ b/services/non-named-votes-ai/tsup.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from 'tsup'; +import { tsupConfig } from 'tsup-config'; + +export default defineConfig({ + ...tsupConfig, +});