diff --git a/package.json b/package.json
index 34dec99..a8c1553 100644
--- a/package.json
+++ b/package.json
@@ -11,6 +11,7 @@
"build": "next build",
"dev:next": "next dev",
"dev:wss": "pnpm tsx --watch src/wss-server.ts",
+ "dev:worker": "NODE_ENV='development' pnpm tsx --watch src/server/tasks/worker.ts",
"dev": "concurrently --kill-others 'pnpm dev:next' 'pnpm dev:wss'",
"postinstall": "prisma generate",
"lint": "next lint",
@@ -44,6 +45,7 @@
"express": "^4.18.2",
"framer-motion": "^10.12.17",
"gpt-tokens": "^1.0.10",
+ "graphile-worker": "^0.13.0",
"immer": "^10.0.2",
"isolated-vm": "^4.5.0",
"json-stringify-pretty-compact": "^4.0.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 05fa609..2896a20 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1,4 +1,4 @@
-lockfileVersion: '6.1'
+lockfileVersion: '6.0'
settings:
autoInstallPeers: true
@@ -83,6 +83,9 @@ dependencies:
gpt-tokens:
specifier: ^1.0.10
version: 1.0.10
+ graphile-worker:
+ specifier: ^0.13.0
+ version: 0.13.0
immer:
specifier: ^10.0.2
version: 10.0.2
@@ -229,12 +232,10 @@ devDependencies:
packages:
- /@ampproject/remapping@2.2.1:
- resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==}
- engines: {node: '>=6.0.0'}
- dependencies:
- '@jridgewell/gen-mapping': 0.3.3
- '@jridgewell/trace-mapping': 0.3.18
+ /@aashutoshrathi/word-wrap@1.2.6:
+ resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==}
+ engines: {node: '>=0.10.0'}
+ dev: true
/@babel/code-frame@7.22.5:
resolution: {integrity: sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==}
@@ -497,8 +498,8 @@ packages:
'@babel/plugin-transform-typescript': 7.22.9(@babel/core@7.22.9)
dev: false
- /@babel/runtime@7.22.5:
- resolution: {integrity: sha512-ecjvYlnAaZ/KVneE/OdKYBYfgXV3Ptu6zQWmgEF7vwKhQnvVS6bjMD2XYgj+SNvQ1GfK/pjgokfPkC/2CO8CuA==}
+ /@babel/runtime@7.22.6:
+ resolution: {integrity: sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ==}
engines: {node: '>=6.9.0'}
dependencies:
regenerator-runtime: 0.13.11
@@ -762,7 +763,7 @@ packages:
dependencies:
'@chakra-ui/dom-utils': 2.1.0
react: 18.2.0
- react-focus-lock: 2.9.4(@types/react@18.2.6)(react@18.2.0)
+ react-focus-lock: 2.9.5(@types/react@18.2.6)(react@18.2.0)
transitivePeerDependencies:
- '@types/react'
dev: false
@@ -1647,7 +1648,7 @@ packages:
resolution: {integrity: sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==}
dependencies:
'@babel/helper-module-imports': 7.22.5
- '@babel/runtime': 7.22.5
+ '@babel/runtime': 7.22.6
'@emotion/hash': 0.9.1
'@emotion/memoize': 0.8.1
'@emotion/serialize': 1.1.2
@@ -1705,7 +1706,7 @@ packages:
'@types/react':
optional: true
dependencies:
- '@babel/runtime': 7.22.5
+ '@babel/runtime': 7.22.6
'@emotion/babel-plugin': 11.11.0
'@emotion/cache': 11.11.0
'@emotion/serialize': 1.1.2
@@ -1755,7 +1756,7 @@ packages:
'@types/react':
optional: true
dependencies:
- '@babel/runtime': 7.22.5
+ '@babel/runtime': 7.22.6
'@emotion/babel-plugin': 11.11.0
'@emotion/is-prop-valid': 1.2.1
'@emotion/react': 11.11.1(@types/react@18.2.6)(react@18.2.0)
@@ -1790,7 +1791,7 @@ packages:
resolution: {integrity: sha512-BDXFbYOJzT/NBEtp71cvsrGPwGAMGRB/349rwKuoxNSiKjPraNNnlK6MIIabViCjqZugu6j+xeMDlEkWdHHJSg==}
dependencies:
'@esbuild-kit/core-utils': 3.1.0
- get-tsconfig: 4.6.0
+ get-tsconfig: 4.6.2
dev: false
/@esbuild-kit/core-utils@3.1.0:
@@ -1804,7 +1805,7 @@ packages:
resolution: {integrity: sha512-Qwfvj/qoPbClxCRNuac1Du01r9gvNOT+pMYtJDapfB1eoGN1YlJ1BixLyL9WVENRx5RXgNLdfYdx/CuswlGhMw==}
dependencies:
'@esbuild-kit/core-utils': 3.1.0
- get-tsconfig: 4.6.0
+ get-tsconfig: 4.6.2
dev: false
/@esbuild/android-arm64@0.17.19:
@@ -2218,13 +2219,13 @@ packages:
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
dev: true
- /@eslint/eslintrc@2.0.3:
- resolution: {integrity: sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==}
+ /@eslint/eslintrc@2.1.0:
+ resolution: {integrity: sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dependencies:
ajv: 6.12.6
debug: 4.3.4
- espree: 9.5.2
+ espree: 9.6.1
globals: 13.20.0
ignore: 5.2.4
import-fresh: 3.3.0
@@ -2240,6 +2241,10 @@ packages:
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: true
+ /@graphile/logger@0.2.0:
+ resolution: {integrity: sha512-jjcWBokl9eb1gVJ85QmoaQ73CQ52xAaOCF29ukRbYNl6lY+ts0ErTaDYOBlejcbUs2OpaiqYLO5uDhyLFzWw4w==}
+ dev: false
+
/@humanwhocodes/config-array@0.11.10:
resolution: {integrity: sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==}
engines: {node: '>=10.10.0'}
@@ -2437,7 +2442,7 @@ packages:
resolution: {integrity: sha512-E6s9hfQx125CfGXW5896s0ZtUEecTS69KvqkNDPxKomeZ/Y2rNsG90yO8K47uchXqKw5RhD/rCNcOJ2VODfQiw==}
dependencies:
'@types/json-schema': 7.0.12
- '@types/node': 20.3.1
+ '@types/node': 20.4.2
fast-deep-equal: 3.1.3
openapi-typescript: 5.4.1
dev: true
@@ -2446,8 +2451,8 @@ packages:
resolution: {integrity: sha512-dhPeilub1NuIG0X5Kvhh9lH4iW3ZsHlnzwgwbOlgwQ2wG1IqFzsgHqmKPk3WzsdWAeaxKJxgM0+W433RmN45GA==}
dev: false
- /@pkgr/utils@2.4.1:
- resolution: {integrity: sha512-JOqwkgFEyi+OROIyq7l4Jy28h/WwhDnG/cPkXG2Z1iFbubB6jsHW1NDvmyOzTBxHr3yg68YGirmh1JUgMqa+9w==}
+ /@pkgr/utils@2.4.2:
+ resolution: {integrity: sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==}
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
dependencies:
cross-spawn: 7.0.3
@@ -2455,7 +2460,7 @@ packages:
is-glob: 4.0.3
open: 9.1.0
picocolors: 1.0.0
- tslib: 2.5.3
+ tslib: 2.6.0
dev: true
/@popperjs/core@2.11.8:
@@ -2499,7 +2504,7 @@ packages:
/@swc/helpers@0.5.1:
resolution: {integrity: sha512-sJ902EfIzn1Fa+qYmjdQqh8tPsoxyBz+8yBKC2HKUxyezKJFwPGOn7pv4WY6QuQW//ySQi5lJjA/ZT9sNWWNTg==}
dependencies:
- tslib: 2.5.3
+ tslib: 2.6.0
dev: false
/@t3-oss/env-core@0.3.1(typescript@5.0.4)(zod@3.21.4):
@@ -2681,6 +2686,12 @@ packages:
dependencies:
'@types/node': 18.16.0
+ /@types/debug@4.1.8:
+ resolution: {integrity: sha512-/vPO1EPOs306Cvhwv7KfVfYvOJqA/S/AXjaHQiJboCZzcNDb+TIJFN9/2C9DZ//ijSKWioNyUxD792QmDJ+HKQ==}
+ dependencies:
+ '@types/ms': 0.7.31
+ dev: false
+
/@types/eslint-scope@3.7.4:
resolution: {integrity: sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==}
dependencies:
@@ -2717,10 +2728,10 @@ packages:
'@types/serve-static': 1.15.2
dev: true
- /@types/hast@2.3.4:
- resolution: {integrity: sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==}
+ /@types/hast@2.3.5:
+ resolution: {integrity: sha512-SvQi0L/lNpThgPoleH53cdjB3y9zpLlVjRbqB3rH8hx1jiRSBGAhyjV3H+URFjNVRqt2EdYNrbZE5IsGlNfpRg==}
dependencies:
- '@types/unist': 2.0.6
+ '@types/unist': 2.0.7
dev: false
/@types/http-errors@2.0.1:
@@ -2752,6 +2763,10 @@ packages:
resolution: {integrity: sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==}
dev: true
+ /@types/ms@0.7.31:
+ resolution: {integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==}
+ dev: false
+
/@types/node-fetch@2.6.4:
resolution: {integrity: sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==}
dependencies:
@@ -2762,14 +2777,22 @@ packages:
/@types/node@18.16.0:
resolution: {integrity: sha512-BsAaKhB+7X+H4GnSjGhJG9Qi8Tw+inU9nJDwmD5CgOmBLEI6ArdhikpLX7DjbjDRDTbqZzU2LSQNZg8WGPiSZQ==}
- /@types/node@20.3.1:
- resolution: {integrity: sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg==}
+ /@types/node@20.4.2:
+ resolution: {integrity: sha512-Dd0BYtWgnWJKwO1jkmTrzofjK2QXXcai0dmtzvIBhcA+RsG5h8R3xlyta0kGOZRNfL9GuRtb1knmPEhQrePCEw==}
dev: true
/@types/parse-json@4.0.0:
resolution: {integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==}
dev: false
+ /@types/pg@8.10.2:
+ resolution: {integrity: sha512-MKFs9P6nJ+LAeHLU3V0cODEOgyThJ3OAnmOlsZsxux6sfQs3HRXR5bBn7xG5DjckEFhTAxsXi7k7cd0pCMxpJw==}
+ dependencies:
+ '@types/node': 18.16.0
+ pg-protocol: 1.6.0
+ pg-types: 4.0.1
+ dev: false
+
/@types/pluralize@0.0.30:
resolution: {integrity: sha512-kVww6xZrW/db5BR9OqiT71J9huRdQ+z/r+LbDuT7/EK50mCmj5FoaIARnVv0rvjUS/YpDox0cDU9lpQT011VBA==}
dev: true
@@ -2825,8 +2848,8 @@ packages:
'@types/node': 18.16.0
dev: true
- /@types/unist@2.0.6:
- resolution: {integrity: sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==}
+ /@types/unist@2.0.7:
+ resolution: {integrity: sha512-cputDpIbFgLUaGQn6Vqg3/YsJwxUwHLO13v3i5ouxT4lat0khip9AEWxtERujXV9wxIB1EyF97BSJFt6vpdI8g==}
dev: false
/@typescript-eslint/eslint-plugin@5.59.6(@typescript-eslint/parser@5.59.6)(eslint@8.40.0)(typescript@5.0.4):
@@ -2850,7 +2873,7 @@ packages:
grapheme-splitter: 1.0.4
ignore: 5.2.4
natural-compare-lite: 1.4.0
- semver: 7.5.2
+ semver: 7.5.4
tsutils: 3.21.0(typescript@5.0.4)
typescript: 5.0.4
transitivePeerDependencies:
@@ -2924,7 +2947,7 @@ packages:
debug: 4.3.4
globby: 11.1.0
is-glob: 4.0.3
- semver: 7.5.2
+ semver: 7.5.4
tsutils: 3.21.0(typescript@5.0.4)
typescript: 5.0.4
transitivePeerDependencies:
@@ -2945,7 +2968,7 @@ packages:
'@typescript-eslint/typescript-estree': 5.59.6(typescript@5.0.4)
eslint: 8.40.0
eslint-scope: 5.1.1
- semver: 7.5.2
+ semver: 7.5.4
transitivePeerDependencies:
- supports-color
- typescript
@@ -3134,20 +3157,20 @@ packages:
negotiator: 0.6.3
dev: false
- /acorn-import-assertions@1.9.0(acorn@8.9.0):
+ /acorn-import-assertions@1.9.0(acorn@8.10.0):
resolution: {integrity: sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==}
peerDependencies:
acorn: ^8
dependencies:
- acorn: 8.9.0
+ acorn: 8.10.0
dev: true
- /acorn-jsx@5.3.2(acorn@8.9.0):
+ /acorn-jsx@5.3.2(acorn@8.10.0):
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
peerDependencies:
acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
dependencies:
- acorn: 8.9.0
+ acorn: 8.10.0
dev: true
/acorn-walk@8.2.0:
@@ -3155,8 +3178,8 @@ packages:
engines: {node: '>=0.4.0'}
dev: true
- /acorn@8.9.0:
- resolution: {integrity: sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==}
+ /acorn@8.10.0:
+ resolution: {integrity: sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==}
engines: {node: '>=0.4.0'}
hasBin: true
dev: true
@@ -3231,11 +3254,11 @@ packages:
resolution: {integrity: sha512-xcLxITLe2HYa1cnYnwCjkOO1PqUHQpozB8x9AR0OgWN2woOBi5kSDVxKfd0b7sb1hw5qFeJhXm9H1nu3xSfLeQ==}
engines: {node: '>=10'}
dependencies:
- tslib: 2.5.3
+ tslib: 2.6.0
dev: false
- /aria-query@5.2.1:
- resolution: {integrity: sha512-7uFg4b+lETFgdaJyETnILsXgnnzVnkHcgRbwbPwevm5x/LmUlt3MjczMRe1zg824iBgXZNRPTBftNYyRSKLp2g==}
+ /aria-query@5.3.0:
+ resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==}
dependencies:
dequal: 2.0.3
dev: true
@@ -3257,7 +3280,7 @@ packages:
dependencies:
call-bind: 1.0.2
define-properties: 1.2.0
- es-abstract: 1.21.2
+ es-abstract: 1.21.3
get-intrinsic: 1.2.1
is-string: 1.0.7
dev: true
@@ -3273,7 +3296,7 @@ packages:
dependencies:
call-bind: 1.0.2
define-properties: 1.2.0
- es-abstract: 1.21.2
+ es-abstract: 1.21.3
es-shim-unscopables: 1.0.0
dev: true
@@ -3283,7 +3306,7 @@ packages:
dependencies:
call-bind: 1.0.2
define-properties: 1.2.0
- es-abstract: 1.21.2
+ es-abstract: 1.21.3
es-shim-unscopables: 1.0.0
dev: true
@@ -3292,7 +3315,7 @@ packages:
dependencies:
call-bind: 1.0.2
define-properties: 1.2.0
- es-abstract: 1.21.2
+ es-abstract: 1.21.3
es-shim-unscopables: 1.0.0
get-intrinsic: 1.2.1
dev: true
@@ -3329,7 +3352,7 @@ packages:
resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==}
engines: {node: '>=10', npm: '>=6'}
dependencies:
- '@babel/runtime': 7.22.5
+ '@babel/runtime': 7.22.6
cosmiconfig: 7.1.0
resolve: 1.22.2
dev: false
@@ -3410,8 +3433,8 @@ packages:
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
dependencies:
- caniuse-lite: 1.0.30001506
- electron-to-chromium: 1.4.459
+ caniuse-lite: 1.0.30001515
+ electron-to-chromium: 1.4.461
node-releases: 2.0.13
update-browserslist-db: 1.0.11(browserslist@4.21.9)
@@ -3422,6 +3445,11 @@ packages:
/buffer-from@1.1.2:
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
+ /buffer-writer@2.0.0:
+ resolution: {integrity: sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==}
+ engines: {node: '>=4'}
+ dev: false
+
/bundle-name@3.0.0:
resolution: {integrity: sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==}
engines: {node: '>=12'}
@@ -3455,8 +3483,8 @@ packages:
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
engines: {node: '>=6'}
- /caniuse-lite@1.0.30001506:
- resolution: {integrity: sha512-6XNEcpygZMCKaufIcgpQNZNf00GEqc7VQON+9Rd0K1bMYo8xhMZRAo5zpbnbMNizi4YNgIDAFrdykWsvY3H4Hw==}
+ /caniuse-lite@1.0.30001515:
+ resolution: {integrity: sha512-eEFDwUOZbE24sb+Ecsx3+OvNETqjWIdabMy52oOkIgcUtAsQifjUG9q4U9dgTHJM2mfk4uEPxc0+xuFdJ629QA==}
/chai@4.3.7:
resolution: {integrity: sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==}
@@ -3534,6 +3562,14 @@ packages:
resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==}
dev: false
+ /cliui@7.0.4:
+ resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==}
+ dependencies:
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+ wrap-ansi: 7.0.0
+ dev: false
+
/cliui@8.0.1:
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
engines: {node: '>=12'}
@@ -3698,7 +3734,7 @@ packages:
resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==}
engines: {node: '>=0.11'}
dependencies:
- '@babel/runtime': 7.22.5
+ '@babel/runtime': 7.22.6
dev: false
/dayjs@1.11.8:
@@ -3862,8 +3898,9 @@ packages:
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
dev: false
- /electron-to-chromium@1.4.459:
- resolution: {integrity: sha512-XXRS5NFv8nCrBL74Rm3qhJjA2VCsRFx0OjHKBMPI0otij56aun8UWiKTDABmd5/7GTR021pA4wivs+Ri6XCElg==}
+ /electron-to-chromium@1.4.461:
+ resolution: {integrity: sha512-1JkvV2sgEGTDXjdsaQCeSwYYuhLRphRpc+g6EHTFELJXEiznLt3/0pZ9JuAOQ5p2rI3YxKTbivtvajirIfhrEQ==}
+ dev: true
/emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
@@ -3936,8 +3973,8 @@ packages:
is-arrayish: 0.2.1
dev: false
- /es-abstract@1.21.2:
- resolution: {integrity: sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==}
+ /es-abstract@1.21.3:
+ resolution: {integrity: sha512-ZU4miiY1j3sGPFLJ34VJXEqhpmL+HGByCinGHv4HC+Fxl2fI2Z4yR6tl0mORnDr6PA8eihWo4LmSWDbvhALckg==}
engines: {node: '>= 0.4'}
dependencies:
array-buffer-byte-length: 1.0.0
@@ -3971,9 +4008,10 @@ packages:
string.prototype.trim: 1.2.7
string.prototype.trimend: 1.0.6
string.prototype.trimstart: 1.0.6
+ typed-array-byte-offset: 1.0.0
typed-array-length: 1.0.4
unbox-primitive: 1.0.2
- which-typed-array: 1.1.9
+ which-typed-array: 1.1.10
dev: true
/es-module-lexer@1.3.0:
@@ -4127,8 +4165,8 @@ packages:
eslint: 8.40.0
eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.59.6)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.40.0)
eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.59.6)(eslint-import-resolver-typescript@3.5.5)(eslint@8.40.0)
- get-tsconfig: 4.6.0
- globby: 13.2.0
+ get-tsconfig: 4.6.2
+ globby: 13.2.2
is-core-module: 2.12.1
is-glob: 4.0.3
synckit: 0.8.5
@@ -4208,8 +4246,8 @@ packages:
peerDependencies:
eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8
dependencies:
- '@babel/runtime': 7.22.5
- aria-query: 5.2.1
+ '@babel/runtime': 7.22.6
+ aria-query: 5.3.0
array-includes: 3.1.6
array.prototype.flatmap: 1.3.1
ast-types-flow: 0.0.7
@@ -4219,7 +4257,7 @@ packages:
emoji-regex: 9.2.2
eslint: 8.40.0
has: 1.0.3
- jsx-ast-utils: 3.3.3
+ jsx-ast-utils: 3.3.4
language-tags: 1.0.5
minimatch: 3.1.2
object.entries: 1.1.6
@@ -4248,7 +4286,7 @@ packages:
doctrine: 2.1.0
eslint: 8.40.0
estraverse: 5.3.0
- jsx-ast-utils: 3.3.3
+ jsx-ast-utils: 3.3.4
minimatch: 3.1.2
object.entries: 1.1.6
object.fromentries: 2.0.6
@@ -4288,8 +4326,8 @@ packages:
estraverse: 4.3.0
dev: true
- /eslint-scope@7.2.0:
- resolution: {integrity: sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==}
+ /eslint-scope@7.2.1:
+ resolution: {integrity: sha512-CvefSOsDdaYYvxChovdrPo/ZGt8d5lrJWleAc1diXRKhHGiTYEI26cvo8Kle/wGnsizoCJjK73FMg1/IkIwiNA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dependencies:
esrecurse: 4.3.0
@@ -4308,7 +4346,7 @@ packages:
dependencies:
'@eslint-community/eslint-utils': 4.4.0(eslint@8.40.0)
'@eslint-community/regexpp': 4.5.1
- '@eslint/eslintrc': 2.0.3
+ '@eslint/eslintrc': 2.1.0
'@eslint/js': 8.40.0
'@humanwhocodes/config-array': 0.11.10
'@humanwhocodes/module-importer': 1.0.1
@@ -4319,9 +4357,9 @@ packages:
debug: 4.3.4
doctrine: 3.0.0
escape-string-regexp: 4.0.0
- eslint-scope: 7.2.0
+ eslint-scope: 7.2.1
eslint-visitor-keys: 3.4.1
- espree: 9.5.2
+ espree: 9.6.1
esquery: 1.5.0
esutils: 2.0.3
fast-deep-equal: 3.1.3
@@ -4342,7 +4380,7 @@ packages:
lodash.merge: 4.6.2
minimatch: 3.1.2
natural-compare: 1.4.0
- optionator: 0.9.1
+ optionator: 0.9.3
strip-ansi: 6.0.1
strip-json-comments: 3.1.1
text-table: 0.2.0
@@ -4350,12 +4388,12 @@ packages:
- supports-color
dev: true
- /espree@9.5.2:
- resolution: {integrity: sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==}
+ /espree@9.6.1:
+ resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dependencies:
- acorn: 8.9.0
- acorn-jsx: 5.3.2(acorn@8.9.0)
+ acorn: 8.10.0
+ acorn-jsx: 5.3.2(acorn@8.10.0)
eslint-visitor-keys: 3.4.1
dev: true
@@ -4567,7 +4605,7 @@ packages:
resolution: {integrity: sha512-KSuV3ur4gf2KqMNoZx3nXNVhqCkn42GuTYCX4tXPEwf0MjpFQmNMiN6m7dXaUXgIoivL6/65agoUMg4RLS0Vbg==}
engines: {node: '>=10'}
dependencies:
- tslib: 2.5.3
+ tslib: 2.6.0
dev: false
/for-each@0.3.3:
@@ -4620,7 +4658,7 @@ packages:
dependencies:
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
- tslib: 2.5.3
+ tslib: 2.6.0
optionalDependencies:
'@emotion/is-prop-valid': 0.8.8
dev: false
@@ -4656,7 +4694,7 @@ packages:
dependencies:
call-bind: 1.0.2
define-properties: 1.2.0
- es-abstract: 1.21.2
+ es-abstract: 1.21.3
functions-have-names: 1.2.3
dev: true
@@ -4703,8 +4741,8 @@ packages:
get-intrinsic: 1.2.1
dev: true
- /get-tsconfig@4.6.0:
- resolution: {integrity: sha512-lgbo68hHTQnFddybKbbs/RDRJnJT5YyGy2kQzVwbq+g67X73i+5MVTval34QxGkOe9X5Ujf1UYpCaphLyltjEg==}
+ /get-tsconfig@4.6.2:
+ resolution: {integrity: sha512-E5XrT4CbbXcXWy+1jChlZmrmCwd5KGx502kDCXJJ7y898TtWW9FwoG5HfOLVRKmlmDGkWN2HM9Ho+/Y8F0sJDg==}
dependencies:
resolve-pkg-maps: 1.0.0
@@ -4781,8 +4819,8 @@ packages:
slash: 3.0.0
dev: true
- /globby@13.2.0:
- resolution: {integrity: sha512-jWsQfayf13NvqKUIL3Ta+CIqMnvlaIDFveWE/dpOZ9+3AMEJozsxDvKA02zync9UuvOM8rOXzsD5GqKP4OnWPQ==}
+ /globby@13.2.2:
+ resolution: {integrity: sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
dependencies:
dir-glob: 3.0.1
@@ -4817,6 +4855,24 @@ packages:
resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==}
dev: true
+ /graphile-worker@0.13.0:
+ resolution: {integrity: sha512-8Hl5XV6hkabZRhYzvbUfvjJfPFR5EPxYRVWlzQC2rqYHrjULTLBgBYZna5R9ukbnsbWSvn4vVrzOBIOgIC1jjw==}
+ engines: {node: '>=10.0.0'}
+ hasBin: true
+ dependencies:
+ '@graphile/logger': 0.2.0
+ '@types/debug': 4.1.8
+ '@types/pg': 8.10.2
+ chokidar: 3.5.3
+ cosmiconfig: 7.1.0
+ json5: 2.2.3
+ pg: 8.11.1
+ tslib: 2.6.0
+ yargs: 16.2.0
+ transitivePeerDependencies:
+ - pg-native
+ dev: false
+
/has-bigints@1.0.2:
resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==}
dev: true
@@ -4863,7 +4919,7 @@ packages:
/hastscript@6.0.0:
resolution: {integrity: sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==}
dependencies:
- '@types/hast': 2.3.4
+ '@types/hast': 2.3.5
comma-separated-tokens: 1.0.8
hast-util-parse-selector: 2.2.5
property-information: 5.6.0
@@ -5259,12 +5315,14 @@ packages:
resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==}
dev: true
- /jsx-ast-utils@3.3.3:
- resolution: {integrity: sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==}
+ /jsx-ast-utils@3.3.4:
+ resolution: {integrity: sha512-fX2TVdCViod6HwKEtSWGHs57oFhVfCMwieb9PuRDgjDPh5XeqJiHFFFJCHxU5cnTc3Bu/GRL+kPiFmw8XWOfKw==}
engines: {node: '>=4.0'}
dependencies:
array-includes: 3.1.6
+ array.prototype.flat: 1.3.1
object.assign: 4.1.4
+ object.values: 1.1.6
dev: true
/language-subtag-registry@0.3.22:
@@ -5447,7 +5505,7 @@ packages:
/mlly@1.4.0:
resolution: {integrity: sha512-ua8PAThnTwpprIaU47EPeZ/bPUVp2QYBbWMphUQpVdBI3Lgqzm5KZQ45Agm3YJedHXaIHl6pBGabaLSUPPSptg==}
dependencies:
- acorn: 8.9.0
+ acorn: 8.10.0
pathe: 1.1.1
pkg-types: 1.0.3
ufo: 1.1.2
@@ -5506,15 +5564,15 @@ packages:
nodemailer:
optional: true
dependencies:
- '@babel/runtime': 7.22.5
+ '@babel/runtime': 7.22.6
'@panva/hkdf': 1.1.1
cookie: 0.5.0
jose: 4.14.4
next: 13.4.2(@babel/core@7.22.9)(react-dom@18.2.0)(react@18.2.0)
oauth: 0.9.15
- openid-client: 5.4.2
- preact: 10.15.1
- preact-render-to-string: 5.2.6(preact@10.15.1)
+ openid-client: 5.4.3
+ preact: 10.16.0
+ preact-render-to-string: 5.2.6(preact@10.16.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
uuid: 8.3.2
@@ -5544,7 +5602,7 @@ packages:
'@next/env': 13.4.2
'@swc/helpers': 0.5.1
busboy: 1.6.0
- caniuse-lite: 1.0.30001506
+ caniuse-lite: 1.0.30001515
postcss: 8.4.14
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
@@ -5655,7 +5713,7 @@ packages:
dependencies:
call-bind: 1.0.2
define-properties: 1.2.0
- es-abstract: 1.21.2
+ es-abstract: 1.21.3
dev: true
/object.fromentries@2.0.6:
@@ -5664,14 +5722,14 @@ packages:
dependencies:
call-bind: 1.0.2
define-properties: 1.2.0
- es-abstract: 1.21.2
+ es-abstract: 1.21.3
dev: true
/object.hasown@1.1.2:
resolution: {integrity: sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==}
dependencies:
define-properties: 1.2.0
- es-abstract: 1.21.2
+ es-abstract: 1.21.3
dev: true
/object.values@1.1.6:
@@ -5680,9 +5738,13 @@ packages:
dependencies:
call-bind: 1.0.2
define-properties: 1.2.0
- es-abstract: 1.21.2
+ es-abstract: 1.21.3
dev: true
+ /obuf@1.1.2:
+ resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==}
+ dev: false
+
/oidc-token-hash@5.0.3:
resolution: {integrity: sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw==}
engines: {node: ^10.13.0 || >=12.0.0}
@@ -5737,7 +5799,7 @@ packages:
form-data-encoder: 1.7.2
formdata-node: 4.4.1
node-fetch: 2.6.12
- qs: 6.11.0
+ qs: 6.11.2
transitivePeerDependencies:
- encoding
- supports-color
@@ -5768,8 +5830,8 @@ packages:
yargs-parser: 21.1.1
dev: true
- /openid-client@5.4.2:
- resolution: {integrity: sha512-lIhsdPvJ2RneBm3nGBBhQchpe3Uka//xf7WPHTIglery8gnckvW7Bd9IaQzekzXJvWthCMyi/xVEyGW0RFPytw==}
+ /openid-client@5.4.3:
+ resolution: {integrity: sha512-sVQOvjsT/sbSfYsQI/9liWQGVZH/Pp3rrtlGEwgk/bbHfrUDZ24DN57lAagIwFtuEu+FM9Ev7r85s8S/yPjimQ==}
dependencies:
jose: 4.14.4
lru-cache: 6.0.0
@@ -5777,16 +5839,16 @@ packages:
oidc-token-hash: 5.0.3
dev: false
- /optionator@0.9.1:
- resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==}
+ /optionator@0.9.3:
+ resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==}
engines: {node: '>= 0.8.0'}
dependencies:
+ '@aashutoshrathi/word-wrap': 1.2.6
deep-is: 0.1.4
fast-levenshtein: 2.0.6
levn: 0.4.1
prelude-ls: 1.2.1
type-check: 0.4.0
- word-wrap: 1.2.3
dev: true
/p-limit@3.1.0:
@@ -5810,6 +5872,10 @@ packages:
p-limit: 3.1.0
dev: true
+ /packet-reader@1.0.0:
+ resolution: {integrity: sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==}
+ dev: false
+
/parent-module@1.0.1:
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
engines: {node: '>=6'}
@@ -5881,6 +5947,88 @@ packages:
resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==}
dev: true
+ /pg-cloudflare@1.1.1:
+ resolution: {integrity: sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==}
+ requiresBuild: true
+ dev: false
+ optional: true
+
+ /pg-connection-string@2.6.1:
+ resolution: {integrity: sha512-w6ZzNu6oMmIzEAYVw+RLK0+nqHPt8K3ZnknKi+g48Ak2pr3dtljJW3o+D/n2zzCG07Zoe9VOX3aiKpj+BN0pjg==}
+ dev: false
+
+ /pg-int8@1.0.1:
+ resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==}
+ engines: {node: '>=4.0.0'}
+ dev: false
+
+ /pg-numeric@1.0.2:
+ resolution: {integrity: sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==}
+ engines: {node: '>=4'}
+ dev: false
+
+ /pg-pool@3.6.1(pg@8.11.1):
+ resolution: {integrity: sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==}
+ peerDependencies:
+ pg: '>=8.0'
+ dependencies:
+ pg: 8.11.1
+ dev: false
+
+ /pg-protocol@1.6.0:
+ resolution: {integrity: sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==}
+ dev: false
+
+ /pg-types@2.2.0:
+ resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==}
+ engines: {node: '>=4'}
+ dependencies:
+ pg-int8: 1.0.1
+ postgres-array: 2.0.0
+ postgres-bytea: 1.0.0
+ postgres-date: 1.0.7
+ postgres-interval: 1.2.0
+ dev: false
+
+ /pg-types@4.0.1:
+ resolution: {integrity: sha512-hRCSDuLII9/LE3smys1hRHcu5QGcLs9ggT7I/TCs0IE+2Eesxi9+9RWAAwZ0yaGjxoWICF/YHLOEjydGujoJ+g==}
+ engines: {node: '>=10'}
+ dependencies:
+ pg-int8: 1.0.1
+ pg-numeric: 1.0.2
+ postgres-array: 3.0.2
+ postgres-bytea: 3.0.0
+ postgres-date: 2.0.1
+ postgres-interval: 3.0.0
+ postgres-range: 1.1.3
+ dev: false
+
+ /pg@8.11.1:
+ resolution: {integrity: sha512-utdq2obft07MxaDg0zBJI+l/M3mBRfIpEN3iSemsz0G5F2/VXx+XzqF4oxrbIZXQxt2AZzIUzyVg/YM6xOP/WQ==}
+ engines: {node: '>= 8.0.0'}
+ peerDependencies:
+ pg-native: '>=3.0.1'
+ peerDependenciesMeta:
+ pg-native:
+ optional: true
+ dependencies:
+ buffer-writer: 2.0.0
+ packet-reader: 1.0.0
+ pg-connection-string: 2.6.1
+ pg-pool: 3.6.1(pg@8.11.1)
+ pg-protocol: 1.6.0
+ pg-types: 2.2.0
+ pgpass: 1.0.5
+ optionalDependencies:
+ pg-cloudflare: 1.1.1
+ dev: false
+
+ /pgpass@1.0.5:
+ resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==}
+ dependencies:
+ split2: 4.2.0
+ dev: false
+
/picocolors@1.0.0:
resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
@@ -5919,23 +6067,71 @@ packages:
source-map-js: 1.0.2
dev: true
+ /postgres-array@2.0.0:
+ resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==}
+ engines: {node: '>=4'}
+ dev: false
+
+ /postgres-array@3.0.2:
+ resolution: {integrity: sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog==}
+ engines: {node: '>=12'}
+ dev: false
+
+ /postgres-bytea@1.0.0:
+ resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==}
+ engines: {node: '>=0.10.0'}
+ dev: false
+
+ /postgres-bytea@3.0.0:
+ resolution: {integrity: sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==}
+ engines: {node: '>= 6'}
+ dependencies:
+ obuf: 1.1.2
+ dev: false
+
+ /postgres-date@1.0.7:
+ resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==}
+ engines: {node: '>=0.10.0'}
+ dev: false
+
+ /postgres-date@2.0.1:
+ resolution: {integrity: sha512-YtMKdsDt5Ojv1wQRvUhnyDJNSr2dGIC96mQVKz7xufp07nfuFONzdaowrMHjlAzY6GDLd4f+LUHHAAM1h4MdUw==}
+ engines: {node: '>=12'}
+ dev: false
+
+ /postgres-interval@1.2.0:
+ resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==}
+ engines: {node: '>=0.10.0'}
+ dependencies:
+ xtend: 4.0.2
+ dev: false
+
+ /postgres-interval@3.0.0:
+ resolution: {integrity: sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==}
+ engines: {node: '>=12'}
+ dev: false
+
+ /postgres-range@1.1.3:
+ resolution: {integrity: sha512-VdlZoocy5lCP0c/t66xAfclglEapXPCIVhqqJRncYpvbCgImF0w67aPKfbqUMr72tO2k5q0TdTZwCLjPTI6C9g==}
+ dev: false
+
/posthog-js@1.68.4:
resolution: {integrity: sha512-rHk4uk99nvWiDTU7P2mFdEfzFR6km0hvOpCR3tm/+F7kCJKs7QDkMblOZZHZultxM4wSNyB4neeohmnHjKYUhQ==}
dependencies:
fflate: 0.4.8
dev: false
- /preact-render-to-string@5.2.6(preact@10.15.1):
+ /preact-render-to-string@5.2.6(preact@10.16.0):
resolution: {integrity: sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==}
peerDependencies:
preact: '>=10'
dependencies:
- preact: 10.15.1
+ preact: 10.16.0
pretty-format: 3.8.0
dev: false
- /preact@10.15.1:
- resolution: {integrity: sha512-qs2ansoQEwzNiV5eAcRT1p1EC/dmEzaATVDJNiB3g2sRDWdA7b7MurXdJjB2+/WQktGWZwxvDrnuRFbWuIr64g==}
+ /preact@10.16.0:
+ resolution: {integrity: sha512-XTSj3dJ4roKIC93pald6rWuB2qQJO9gO2iLLyTe87MrjQN+HklueLsmskbywEWqCHlclgz3/M4YLL2iBr9UmMA==}
dev: false
/prelude-ls@1.2.1:
@@ -6023,6 +6219,13 @@ packages:
side-channel: 1.0.4
dev: false
+ /qs@6.11.2:
+ resolution: {integrity: sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==}
+ engines: {node: '>=0.6'}
+ dependencies:
+ side-channel: 1.0.4
+ dev: false
+
/queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
dev: true
@@ -6064,7 +6267,7 @@ packages:
peerDependencies:
react: ^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0
dependencies:
- '@babel/runtime': 7.22.5
+ '@babel/runtime': 7.22.6
react: 18.2.0
dev: false
@@ -6082,8 +6285,8 @@ packages:
resolution: {integrity: sha512-xTYf9zFim2pEif/Fw16dBiXpe0hoy5PxcD8+OwBnTtNLfIm3g6WxhKNurY+6OmdH1u6Ta/W/Vl6vjbYP1MFnDg==}
dev: false
- /react-focus-lock@2.9.4(@types/react@18.2.6)(react@18.2.0):
- resolution: {integrity: sha512-7pEdXyMseqm3kVjhdVH18sovparAzLg5h6WvIx7/Ck3ekjhrrDMEegHSa3swwC8wgfdd7DIdUVRGeiHT9/7Sgg==}
+ /react-focus-lock@2.9.5(@types/react@18.2.6)(react@18.2.0):
+ resolution: {integrity: sha512-h6vrdgUbsH2HeD5I7I3Cx1PPrmwGuKYICS+kB9m+32X/9xHRrAbxgvaBpG7BFBN9h3tO+C3qX1QAVESmi4CiIA==}
peerDependencies:
'@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0
react: ^16.8.0 || ^17.0.0 || ^18.0.0
@@ -6091,7 +6294,7 @@ packages:
'@types/react':
optional: true
dependencies:
- '@babel/runtime': 7.22.5
+ '@babel/runtime': 7.22.6
'@types/react': 18.2.6
focus-lock: 0.11.6
prop-types: 15.8.1
@@ -6129,7 +6332,7 @@ packages:
'@types/react': 18.2.6
react: 18.2.0
react-style-singleton: 2.2.1(@types/react@18.2.6)(react@18.2.0)
- tslib: 2.5.3
+ tslib: 2.6.0
dev: false
/react-remove-scroll@2.5.6(@types/react@18.2.6)(react@18.2.0):
@@ -6146,7 +6349,7 @@ packages:
react: 18.2.0
react-remove-scroll-bar: 2.3.4(@types/react@18.2.6)(react@18.2.0)
react-style-singleton: 2.2.1(@types/react@18.2.6)(react@18.2.0)
- tslib: 2.5.3
+ tslib: 2.6.0
use-callback-ref: 1.3.0(@types/react@18.2.6)(react@18.2.0)
use-sidecar: 1.1.2(@types/react@18.2.6)(react@18.2.0)
dev: false
@@ -6173,7 +6376,7 @@ packages:
get-nonce: 1.0.1
invariant: 2.2.4
react: 18.2.0
- tslib: 2.5.3
+ tslib: 2.6.0
dev: false
/react-syntax-highlighter@15.5.0(react@18.2.0):
@@ -6181,7 +6384,7 @@ packages:
peerDependencies:
react: '>= 0.14.0'
dependencies:
- '@babel/runtime': 7.22.5
+ '@babel/runtime': 7.22.6
highlight.js: 10.7.3
lowlight: 1.20.0
prismjs: 1.29.0
@@ -6195,7 +6398,7 @@ packages:
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
dependencies:
- '@babel/runtime': 7.22.5
+ '@babel/runtime': 7.22.6
react: 18.2.0
use-composed-ref: 1.3.0(react@18.2.0)
use-latest: 1.2.1(@types/react@18.2.6)(react@18.2.0)
@@ -6323,7 +6526,7 @@ packages:
/rxjs@7.8.1:
resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==}
dependencies:
- tslib: 2.5.3
+ tslib: 2.6.0
dev: false
/safe-buffer@5.1.2:
@@ -6364,8 +6567,8 @@ packages:
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
hasBin: true
- /semver@7.5.2:
- resolution: {integrity: sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ==}
+ /semver@7.5.4:
+ resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==}
engines: {node: '>=10'}
hasBin: true
dependencies:
@@ -6533,6 +6736,11 @@ packages:
resolution: {integrity: sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==}
dev: false
+ /split2@4.2.0:
+ resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==}
+ engines: {node: '>= 10.x'}
+ dev: false
+
/stackback@0.0.2:
resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
dev: true
@@ -6568,7 +6776,7 @@ packages:
dependencies:
call-bind: 1.0.2
define-properties: 1.2.0
- es-abstract: 1.21.2
+ es-abstract: 1.21.3
get-intrinsic: 1.2.1
has-symbols: 1.0.3
internal-slot: 1.0.5
@@ -6582,7 +6790,7 @@ packages:
dependencies:
call-bind: 1.0.2
define-properties: 1.2.0
- es-abstract: 1.21.2
+ es-abstract: 1.21.3
dev: true
/string.prototype.trimend@1.0.6:
@@ -6590,7 +6798,7 @@ packages:
dependencies:
call-bind: 1.0.2
define-properties: 1.2.0
- es-abstract: 1.21.2
+ es-abstract: 1.21.3
dev: true
/string.prototype.trimstart@1.0.6:
@@ -6598,7 +6806,7 @@ packages:
dependencies:
call-bind: 1.0.2
define-properties: 1.2.0
- es-abstract: 1.21.2
+ es-abstract: 1.21.3
dev: true
/string_decoder@0.10.31:
@@ -6640,7 +6848,7 @@ packages:
/strip-literal@1.0.1:
resolution: {integrity: sha512-QZTsipNpa2Ppr6v1AmJHESqJ3Uz247MUS0OjrnnZjFAvEoWqxuyFuXn2xLgMtRnijJShAa1HL0gtJyUs7u7n3Q==}
dependencies:
- acorn: 8.9.0
+ acorn: 8.10.0
dev: true
/styled-jsx@5.1.1(@babel/core@7.22.9)(react@18.2.0):
@@ -6703,8 +6911,8 @@ packages:
resolution: {integrity: sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==}
engines: {node: ^14.18.0 || >=16.0.0}
dependencies:
- '@pkgr/utils': 2.4.1
- tslib: 2.5.3
+ '@pkgr/utils': 2.4.2
+ tslib: 2.6.0
dev: true
/tapable@2.2.1:
@@ -6742,7 +6950,7 @@ packages:
hasBin: true
dependencies:
'@jridgewell/source-map': 0.3.5
- acorn: 8.9.0
+ acorn: 8.10.0
commander: 2.20.3
source-map-support: 0.5.21
dev: true
@@ -6837,8 +7045,8 @@ packages:
resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==}
dev: false
- /tslib@2.5.3:
- resolution: {integrity: sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==}
+ /tslib@2.6.0:
+ resolution: {integrity: sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==}
/tsutils@3.21.0(typescript@5.0.4):
resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==}
@@ -6886,6 +7094,17 @@ packages:
mime-types: 2.1.35
dev: false
+ /typed-array-byte-offset@1.0.0:
+ resolution: {integrity: sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ available-typed-arrays: 1.0.5
+ call-bind: 1.0.2
+ for-each: 0.3.3
+ has-proto: 1.0.1
+ is-typed-array: 1.1.10
+ dev: true
+
/typed-array-length@1.0.4:
resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==}
dependencies:
@@ -6957,7 +7176,7 @@ packages:
dependencies:
'@types/react': 18.2.6
react: 18.2.0
- tslib: 2.5.3
+ tslib: 2.6.0
dev: false
/use-composed-ref@1.3.0(react@18.2.0):
@@ -7008,7 +7227,7 @@ packages:
'@types/react': 18.2.6
detect-node-es: 1.1.0
react: 18.2.0
- tslib: 2.5.3
+ tslib: 2.6.0
dev: false
/use-sync-external-store@1.2.0(react@18.2.0):
@@ -7048,7 +7267,7 @@ packages:
mlly: 1.4.0
pathe: 1.1.1
picocolors: 1.0.0
- vite: 4.4.3(@types/node@18.16.0)
+ vite: 4.4.4(@types/node@18.16.0)
transitivePeerDependencies:
- '@types/node'
- less
@@ -7060,8 +7279,8 @@ packages:
- terser
dev: true
- /vite@4.4.3(@types/node@18.16.0):
- resolution: {integrity: sha512-IMnXQXXWgLi5brBQx/4WzDxdzW0X3pjO4nqFJAuNvwKtxzAmPzFE1wszW3VDpAGQJm3RZkm/brzRdyGsnwgJIA==}
+ /vite@4.4.4(@types/node@18.16.0):
+ resolution: {integrity: sha512-4mvsTxjkveWrKDJI70QmelfVqTm+ihFAb6+xf4sjEU2TmUCTlVX87tmg/QooPEMQb/lM9qGHT99ebqPziEd3wg==}
engines: {node: ^14.18.0 || >=16.0.0}
hasBin: true
peerDependencies:
@@ -7135,7 +7354,7 @@ packages:
'@vitest/snapshot': 0.33.0
'@vitest/spy': 0.33.0
'@vitest/utils': 0.33.0
- acorn: 8.9.0
+ acorn: 8.10.0
acorn-walk: 8.2.0
cac: 6.7.14
chai: 4.3.7
@@ -7148,7 +7367,7 @@ packages:
strip-literal: 1.0.1
tinybench: 2.5.0
tinypool: 0.6.0
- vite: 4.4.3(@types/node@18.16.0)
+ vite: 4.4.4(@types/node@18.16.0)
vite-node: 0.33.0(@types/node@18.16.0)
why-is-node-running: 2.2.2
transitivePeerDependencies:
@@ -7198,8 +7417,8 @@ packages:
'@webassemblyjs/ast': 1.11.6
'@webassemblyjs/wasm-edit': 1.11.6
'@webassemblyjs/wasm-parser': 1.11.6
- acorn: 8.9.0
- acorn-import-assertions: 1.9.0(acorn@8.9.0)
+ acorn: 8.10.0
+ acorn-import-assertions: 1.9.0(acorn@8.10.0)
browserslist: 4.21.9
chrome-trace-event: 1.0.3
enhanced-resolve: 5.15.0
@@ -7240,8 +7459,8 @@ packages:
is-symbol: 1.0.4
dev: true
- /which-typed-array@1.1.9:
- resolution: {integrity: sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==}
+ /which-typed-array@1.1.10:
+ resolution: {integrity: sha512-uxoA5vLUfRPdjCuJ1h5LlYdmTLbYfums398v3WLkM+i/Wltl2/XyZpQWKbN++ck5L64SR/grOHqtXCUKmlZPNA==}
engines: {node: '>= 0.4'}
dependencies:
available-typed-arrays: 1.0.5
@@ -7269,11 +7488,6 @@ packages:
stackback: 0.0.2
dev: true
- /word-wrap@1.2.3:
- resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==}
- engines: {node: '>=0.10.0'}
- dev: true
-
/wrap-ansi@7.0.0:
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
engines: {node: '>=10'}
@@ -7338,10 +7552,28 @@ packages:
engines: {node: '>= 14'}
dev: true
+ /yargs-parser@20.2.9:
+ resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==}
+ engines: {node: '>=10'}
+ dev: false
+
/yargs-parser@21.1.1:
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
engines: {node: '>=12'}
+ /yargs@16.2.0:
+ resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==}
+ engines: {node: '>=10'}
+ dependencies:
+ cliui: 7.0.4
+ escalade: 3.1.1
+ get-caller-file: 2.0.5
+ require-directory: 2.1.1
+ string-width: 4.2.3
+ y18n: 5.0.8
+ yargs-parser: 20.2.9
+ dev: false
+
/yargs@17.7.2:
resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
engines: {node: '>=12'}
diff --git a/prisma/migrations/20230714113205_create_scenariotvariant/migration.sql b/prisma/migrations/20230714113205_create_scenariotvariant/migration.sql
new file mode 100644
index 0000000..dbe1cba
--- /dev/null
+++ b/prisma/migrations/20230714113205_create_scenariotvariant/migration.sql
@@ -0,0 +1,49 @@
+-- Drop the foreign key constraints on the original ModelOutput
+ALTER TABLE "ModelOutput" DROP CONSTRAINT "ModelOutput_promptVariantId_fkey";
+ALTER TABLE "ModelOutput" DROP CONSTRAINT "ModelOutput_testScenarioId_fkey";
+
+-- Rename the old table
+ALTER TABLE "ModelOutput" RENAME TO "ScenarioVariantCell";
+ALTER TABLE "ScenarioVariantCell" RENAME CONSTRAINT "ModelOutput_pkey" TO "ScenarioVariantCell_pkey";
+ALTER INDEX "ModelOutput_inputHash_idx" RENAME TO "ScenarioVariantCell_inputHash_idx";
+ALTER INDEX "ModelOutput_promptVariantId_testScenarioId_key" RENAME TO "ScenarioVariantCell_promptVariantId_testScenarioId_key";
+
+-- Add the new fields to the renamed table
+ALTER TABLE "ScenarioVariantCell" ADD COLUMN "retryTime" TIMESTAMP(3);
+ALTER TABLE "ScenarioVariantCell" ADD COLUMN "streamingChannel" TEXT;
+ALTER TABLE "ScenarioVariantCell" ALTER COLUMN "inputHash" DROP NOT NULL;
+ALTER TABLE "ScenarioVariantCell" ALTER COLUMN "output" DROP NOT NULL,
+ALTER COLUMN "statusCode" DROP NOT NULL,
+ALTER COLUMN "timeToComplete" DROP NOT NULL;
+
+-- Create the new table
+CREATE TABLE "ModelOutput" (
+ "id" UUID NOT NULL,
+ "inputHash" TEXT NOT NULL,
+ "output" JSONB NOT NULL,
+ "timeToComplete" INTEGER NOT NULL DEFAULT 0,
+ "promptTokens" INTEGER,
+ "completionTokens" INTEGER,
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "updatedAt" TIMESTAMP(3) NOT NULL,
+ "scenarioVariantCellId" UUID
+);
+
+-- Move inputHash index
+DROP INDEX "ScenarioVariantCell_inputHash_idx";
+CREATE INDEX "ModelOutput_inputHash_idx" ON "ModelOutput"("inputHash");
+
+CREATE UNIQUE INDEX "ModelOutput_scenarioVariantCellId_key" ON "ModelOutput"("scenarioVariantCellId");
+ALTER TABLE "ModelOutput" ADD CONSTRAINT "ModelOutput_scenarioVariantCellId_fkey" FOREIGN KEY ("scenarioVariantCellId") REFERENCES "ScenarioVariantCell"("id") ON DELETE CASCADE ON UPDATE CASCADE;
+
+ALTER TABLE "ModelOutput" ALTER COLUMN "scenarioVariantCellId" SET NOT NULL,
+ADD CONSTRAINT "ModelOutput_pkey" PRIMARY KEY ("id");
+
+ALTER TABLE "ScenarioVariantCell" ADD CONSTRAINT "ScenarioVariantCell_promptVariantId_fkey" FOREIGN KEY ("promptVariantId") REFERENCES "PromptVariant"("id") ON DELETE CASCADE ON UPDATE CASCADE;
+ALTER TABLE "ScenarioVariantCell" ADD CONSTRAINT "ScenarioVariantCell_testScenarioId_fkey" FOREIGN KEY ("testScenarioId") REFERENCES "TestScenario"("id") ON DELETE CASCADE ON UPDATE CASCADE;
+
+-- CreateEnum
+CREATE TYPE "CellRetrievalStatus" AS ENUM ('PENDING', 'IN_PROGRESS', 'COMPLETE', 'ERROR');
+
+-- AlterTable
+ALTER TABLE "ScenarioVariantCell" ADD COLUMN "retrievalStatus" "CellRetrievalStatus" NOT NULL DEFAULT 'COMPLETE';
diff --git a/prisma/migrations/20230714182259_add_model_to_variant/migration.sql b/prisma/migrations/20230714182259_add_model_to_variant/migration.sql
new file mode 100644
index 0000000..b445e09
--- /dev/null
+++ b/prisma/migrations/20230714182259_add_model_to_variant/migration.sql
@@ -0,0 +1,2 @@
+-- AlterTable
+ALTER TABLE "PromptVariant" ADD COLUMN "model" TEXT NOT NULL DEFAULT 'gpt-3.5-turbo';
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index baa9f4f..6e0d22c 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -41,7 +41,7 @@ model PromptVariant {
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
- ModelOutput ModelOutput[]
+ scenarioVariantCells ScenarioVariantCell[]
EvaluationResult EvaluationResult[]
@@index([uiId])
@@ -61,7 +61,7 @@ model TestScenario {
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
- ModelOutput ModelOutput[]
+ scenarioVariantCells ScenarioVariantCell[]
}
model TemplateVariable {
@@ -76,17 +76,28 @@ model TemplateVariable {
updatedAt DateTime @updatedAt
}
-model ModelOutput {
+enum CellRetrievalStatus {
+ PENDING
+ IN_PROGRESS
+ COMPLETE
+ ERROR
+}
+
+model ScenarioVariantCell {
id String @id @default(uuid()) @db.Uuid
- inputHash String
- output Json
- statusCode Int
- errorMessage String?
- timeToComplete Int @default(0)
+ inputHash String? // TODO: Remove once migration is complete
+ output Json? // TODO: Remove once migration is complete
+ statusCode Int?
+ errorMessage String?
+ timeToComplete Int? @default(0) // TODO: Remove once migration is complete
+ retryTime DateTime?
+ streamingChannel String?
+ retrievalStatus CellRetrievalStatus @default(COMPLETE)
- promptTokens Int? // Added promptTokens field
- completionTokens Int? // Added completionTokens field
+ promptTokens Int? // TODO: Remove once migration is complete
+ completionTokens Int? // TODO: Remove once migration is complete
+ modelOutput ModelOutput?
promptVariantId String @db.Uuid
promptVariant PromptVariant @relation(fields: [promptVariantId], references: [id], onDelete: Cascade)
@@ -98,6 +109,24 @@ model ModelOutput {
updatedAt DateTime @updatedAt
@@unique([promptVariantId, testScenarioId])
+}
+
+model ModelOutput {
+ id String @id @default(uuid()) @db.Uuid
+
+ inputHash String
+ output Json
+ timeToComplete Int @default(0)
+ promptTokens Int?
+ completionTokens Int?
+
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+
+ scenarioVariantCellId String @db.Uuid
+ scenarioVariantCell ScenarioVariantCell @relation(fields: [scenarioVariantCellId], references: [id], onDelete: Cascade)
+
+ @@unique([scenarioVariantCellId])
@@index([inputHash])
}
diff --git a/prisma/seed.ts b/prisma/seed.ts
index c7676e9..d3d4986 100644
--- a/prisma/seed.ts
+++ b/prisma/seed.ts
@@ -16,7 +16,7 @@ const experiment = await prisma.experiment.create({
},
});
-await prisma.modelOutput.deleteMany({
+await prisma.scenarioVariantCell.deleteMany({
where: {
promptVariant: {
experimentId,
diff --git a/src/components/OutputsTable/OutputCell/CellOptions.tsx b/src/components/OutputsTable/OutputCell/CellOptions.tsx
new file mode 100644
index 0000000..4572964
--- /dev/null
+++ b/src/components/OutputsTable/OutputCell/CellOptions.tsx
@@ -0,0 +1,33 @@
+import { Button, HStack, Icon } from "@chakra-ui/react";
+import { BsArrowClockwise } from "react-icons/bs";
+
+export const CellOptions = ({
+ refetchingOutput,
+ refetchOutput,
+}: {
+ refetchingOutput: boolean;
+ refetchOutput: () => void;
+}) => {
+ return (
+
+ {!refetchingOutput && (
+
+ )}
+
+ );
+};
diff --git a/src/components/OutputsTable/OutputCell/ErrorHandler.tsx b/src/components/OutputsTable/OutputCell/ErrorHandler.tsx
index 419a3e8..b626bcc 100644
--- a/src/components/OutputsTable/OutputCell/ErrorHandler.tsx
+++ b/src/components/OutputsTable/OutputCell/ErrorHandler.tsx
@@ -1,29 +1,21 @@
-import { type ModelOutput } from "@prisma/client";
-import { HStack, VStack, Text, Button, Icon } from "@chakra-ui/react";
+import { type ScenarioVariantCell } from "@prisma/client";
+import { VStack, Text } from "@chakra-ui/react";
import { useEffect, useState } from "react";
-import { BsArrowClockwise } from "react-icons/bs";
-import { rateLimitErrorMessage } from "~/sharedStrings";
import pluralize from "pluralize";
-const MAX_AUTO_RETRIES = 3;
-
export const ErrorHandler = ({
- output,
+ cell,
refetchOutput,
- numPreviousTries,
}: {
- output: ModelOutput;
+ cell: ScenarioVariantCell;
refetchOutput: () => void;
- numPreviousTries: number;
}) => {
const [msToWait, setMsToWait] = useState(0);
- const shouldAutoRetry =
- output.errorMessage === rateLimitErrorMessage && numPreviousTries < MAX_AUTO_RETRIES;
useEffect(() => {
- if (!shouldAutoRetry) return;
+ if (!cell.retryTime) return;
- const initialWaitTime = calculateDelay(numPreviousTries);
+ const initialWaitTime = cell.retryTime.getTime() - Date.now();
const msModuloOneSecond = initialWaitTime % 1000;
let remainingTime = initialWaitTime - msModuloOneSecond;
setMsToWait(remainingTime);
@@ -35,7 +27,6 @@ export const ErrorHandler = ({
setMsToWait(remainingTime);
if (remainingTime <= 0) {
- refetchOutput();
clearInterval(interval);
}
}, 1000);
@@ -45,32 +36,12 @@ export const ErrorHandler = ({
clearInterval(interval);
clearTimeout(timeout);
};
- }, [shouldAutoRetry, setMsToWait, refetchOutput, numPreviousTries]);
+ }, [cell.retryTime, cell.statusCode, setMsToWait, refetchOutput]);
return (
-
-
- Error
-
-
-
- {output.errorMessage}
+ {cell.errorMessage}
{msToWait > 0 && (
@@ -80,12 +51,3 @@ export const ErrorHandler = ({
);
};
-
-const MIN_DELAY = 500; // milliseconds
-const MAX_DELAY = 5000; // milliseconds
-
-function calculateDelay(numPreviousTries: number): number {
- const baseDelay = Math.min(MAX_DELAY, MIN_DELAY * Math.pow(2, numPreviousTries));
- const jitter = Math.random() * baseDelay;
- return baseDelay + jitter;
-}
diff --git a/src/components/OutputsTable/OutputCell/OutputCell.tsx b/src/components/OutputsTable/OutputCell/OutputCell.tsx
index 8307852..2e1585a 100644
--- a/src/components/OutputsTable/OutputCell/OutputCell.tsx
+++ b/src/components/OutputsTable/OutputCell/OutputCell.tsx
@@ -1,17 +1,16 @@
-import { type RouterOutputs, api } from "~/utils/api";
+import { api } from "~/utils/api";
import { type PromptVariant, type Scenario } from "../types";
-import { Spinner, Text, Box, Center, Flex } from "@chakra-ui/react";
+import { Spinner, Text, Box, Center, Flex, VStack } from "@chakra-ui/react";
import { useExperiment, useHandledAsyncCallback } from "~/utils/hooks";
import SyntaxHighlighter from "react-syntax-highlighter";
import { docco } from "react-syntax-highlighter/dist/cjs/styles/hljs";
import stringify from "json-stringify-pretty-compact";
-import { type ReactElement, useState, useEffect, useRef, useCallback } from "react";
+import { type ReactElement, useState, useEffect } from "react";
import { type ChatCompletion } from "openai/resources/chat";
-import { generateChannel } from "~/utils/generateChannel";
-import { isObject } from "lodash";
import useSocket from "~/utils/useSocket";
import { OutputStats } from "./OutputStats";
import { ErrorHandler } from "./ErrorHandler";
+import { CellOptions } from "./CellOptions";
export default function OutputCell({
scenario,
@@ -37,116 +36,103 @@ export default function OutputCell({
// if (variant.config === null || Object.keys(variant.config).length === 0)
// disabledReason = "Save your prompt variant to see output";
- const outputMutation = api.outputs.get.useMutation();
-
- const [output, setOutput] = useState(null);
- const [channel, setChannel] = useState(undefined);
- const [numPreviousTries, setNumPreviousTries] = useState(0);
-
- const fetchMutex = useRef(false);
- const [fetchOutput, fetchingOutput] = useHandledAsyncCallback(
- async (forceRefetch?: boolean) => {
- if (fetchMutex.current) return;
- setNumPreviousTries((prev) => prev + 1);
-
- fetchMutex.current = true;
- setOutput(null);
-
- const shouldStream =
- isObject(variant) &&
- "config" in variant &&
- isObject(variant.config) &&
- "stream" in variant.config &&
- variant.config.stream === true;
-
- const channel = shouldStream ? generateChannel() : undefined;
- setChannel(channel);
-
- const output = await outputMutation.mutateAsync({
- scenarioId: scenario.id,
- variantId: variant.id,
- channel,
- forceRefetch,
- });
- setOutput(output);
- await utils.promptVariants.stats.invalidate();
- fetchMutex.current = false;
- },
- [outputMutation, scenario.id, variant.id],
+ const [refetchInterval, setRefetchInterval] = useState(0);
+ const { data: cell, isLoading: queryLoading } = api.scenarioVariantCells.get.useQuery(
+ { scenarioId: scenario.id, variantId: variant.id },
+ { refetchInterval },
);
- const hardRefetch = useCallback(() => fetchOutput(true), [fetchOutput]);
- useEffect(fetchOutput, [scenario.id, variant.id]);
+ const { mutateAsync: hardRefetchMutate, isLoading: refetchingOutput } =
+ api.scenarioVariantCells.forceRefetch.useMutation();
+ const [hardRefetch] = useHandledAsyncCallback(async () => {
+ await hardRefetchMutate({ scenarioId: scenario.id, variantId: variant.id });
+ await utils.scenarioVariantCells.get.invalidate({
+ scenarioId: scenario.id,
+ variantId: variant.id,
+ });
+ }, [hardRefetchMutate, scenario.id, variant.id]);
+
+ const fetchingOutput = queryLoading || refetchingOutput;
+
+ const awaitingOutput =
+ !cell || cell.retrievalStatus === "PENDING" || cell.retrievalStatus === "IN_PROGRESS";
+ useEffect(() => setRefetchInterval(awaitingOutput ? 1000 : 0), [awaitingOutput]);
+
+ const modelOutput = cell?.modelOutput;
// Disconnect from socket if we're not streaming anymore
- const streamedMessage = useSocket(fetchingOutput ? channel : undefined);
+ const streamedMessage = useSocket(cell?.streamingChannel);
const streamedContent = streamedMessage?.choices?.[0]?.message?.content;
if (!vars) return null;
if (disabledReason) return {disabledReason};
- if (fetchingOutput && !streamedMessage)
+ if (awaitingOutput && !streamedMessage)
return (
);
- if (!output && !fetchingOutput) return Error retrieving output;
+ if (!cell && !fetchingOutput) return Error retrieving output;
- if (output && output.errorMessage) {
- return (
-
- );
+ if (cell && cell.errorMessage) {
+ return ;
}
- const response = output?.output as unknown as ChatCompletion;
+ const response = modelOutput?.output as unknown as ChatCompletion;
const message = response?.choices?.[0]?.message;
- if (output && message?.function_call) {
+ if (modelOutput && message?.function_call) {
const rawArgs = message.function_call.arguments ?? "null";
let parsedArgs: string;
try {
parsedArgs = JSON.parse(rawArgs);
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (e: any) {
parsedArgs = `Failed to parse arguments as JSON: '${rawArgs}' ERROR: ${e.message as string}`;
}
return (
-
- {stringify(
- {
- function: message.function_call.name,
- args: parsedArgs,
- },
- { maxLength: 40 },
- )}
-
-
+
+
+
+ {stringify(
+ {
+ function: message.function_call.name,
+ args: parsedArgs,
+ },
+ { maxLength: 40 },
+ )}
+
+
+
);
}
- const contentToDisplay = message?.content ?? streamedContent ?? JSON.stringify(output?.output);
+ const contentToDisplay =
+ message?.content ?? streamedContent ?? JSON.stringify(modelOutput?.output);
return (
- {contentToDisplay}
- {output && }
+
+
+ {contentToDisplay}
+
+ {modelOutput && (
+
+ )}
);
}
diff --git a/src/components/OutputsTable/OutputCell/OutputStats.tsx b/src/components/OutputsTable/OutputCell/OutputStats.tsx
index 02e5780..580f4f9 100644
--- a/src/components/OutputsTable/OutputCell/OutputStats.tsx
+++ b/src/components/OutputsTable/OutputCell/OutputStats.tsx
@@ -9,8 +9,8 @@ import { HStack, Icon, Text } from "@chakra-ui/react";
import { BsCheck, BsClock, BsCurrencyDollar, BsX } from "react-icons/bs";
import { CostTooltip } from "~/components/tooltip/CostTooltip";
-const SHOW_COST = false;
-const SHOW_TIME = false;
+const SHOW_COST = true;
+const SHOW_TIME = true;
export const OutputStats = ({
model,
@@ -35,8 +35,6 @@ export const OutputStats = ({
const cost = promptCost + completionCost;
- if (!evals.length) return null;
-
return (
diff --git a/src/pages/index.tsx b/src/pages/index.tsx
index a20c3a6..ac4897c 100644
--- a/src/pages/index.tsx
+++ b/src/pages/index.tsx
@@ -1,7 +1,7 @@
import { type GetServerSideProps } from "next";
// eslint-disable-next-line @typescript-eslint/require-await
-export const getServerSideProps: GetServerSideProps = async (context) => {
+export const getServerSideProps: GetServerSideProps = async () => {
return {
redirect: {
destination: "/experiments",
diff --git a/src/server/api/autogen.ts b/src/server/api/autogen.ts
index 516599a..1d103b4 100644
--- a/src/server/api/autogen.ts
+++ b/src/server/api/autogen.ts
@@ -3,10 +3,6 @@ import { prisma } from "../db";
import { openai } from "../utils/openai";
import { pick } from "lodash";
-function promptHasVariable(prompt: string, variableName: string) {
- return prompt.includes(`{{${variableName}}}`);
-}
-
type AxiosError = {
response?: {
data?: {
diff --git a/src/server/api/root.router.ts b/src/server/api/root.router.ts
index cb1ff9a..271879e 100644
--- a/src/server/api/root.router.ts
+++ b/src/server/api/root.router.ts
@@ -2,7 +2,7 @@ import { promptVariantsRouter } from "~/server/api/routers/promptVariants.router
import { createTRPCRouter } from "~/server/api/trpc";
import { experimentsRouter } from "./routers/experiments.router";
import { scenariosRouter } from "./routers/scenarios.router";
-import { modelOutputsRouter } from "./routers/modelOutputs.router";
+import { scenarioVariantCellsRouter } from "./routers/scenarioVariantCells.router";
import { templateVarsRouter } from "./routers/templateVariables.router";
import { evaluationsRouter } from "./routers/evaluations.router";
@@ -15,7 +15,7 @@ export const appRouter = createTRPCRouter({
promptVariants: promptVariantsRouter,
experiments: experimentsRouter,
scenarios: scenariosRouter,
- outputs: modelOutputsRouter,
+ scenarioVariantCells: scenarioVariantCellsRouter,
templateVars: templateVarsRouter,
evaluations: evaluationsRouter,
});
diff --git a/src/server/api/routers/experiments.router.ts b/src/server/api/routers/experiments.router.ts
index 1c47d7a..7f5aab9 100644
--- a/src/server/api/routers/experiments.router.ts
+++ b/src/server/api/routers/experiments.router.ts
@@ -2,6 +2,7 @@ import { z } from "zod";
import { createTRPCRouter, publicProcedure } from "~/server/api/trpc";
import { prisma } from "~/server/db";
import dedent from "dedent";
+import { generateNewCell } from "~/server/utils/generateNewCell";
export const experimentsRouter = createTRPCRouter({
list: publicProcedure.query(async () => {
@@ -64,7 +65,7 @@ export const experimentsRouter = createTRPCRouter({
},
});
- await prisma.$transaction([
+ const [variant, scenario] = await prisma.$transaction([
prisma.promptVariant.create({
data: {
experimentId: exp.id,
@@ -86,6 +87,8 @@ export const experimentsRouter = createTRPCRouter({
}),
]);
+ await generateNewCell(variant.id, scenario.id);
+
return exp;
}),
diff --git a/src/server/api/routers/modelOutputs.router.ts b/src/server/api/routers/modelOutputs.router.ts
deleted file mode 100644
index 7b11850..0000000
--- a/src/server/api/routers/modelOutputs.router.ts
+++ /dev/null
@@ -1,101 +0,0 @@
-import { z } from "zod";
-import { createTRPCRouter, publicProcedure } from "~/server/api/trpc";
-import { prisma } from "~/server/db";
-import crypto from "crypto";
-import type { Prisma } from "@prisma/client";
-import { reevaluateVariant } from "~/server/utils/evaluations";
-import { getCompletion } from "~/server/utils/getCompletion";
-import { constructPrompt } from "~/server/utils/constructPrompt";
-import { type CompletionCreateParams } from "openai/resources/chat";
-
-export const modelOutputsRouter = createTRPCRouter({
- get: publicProcedure
- .input(
- z.object({
- scenarioId: z.string(),
- variantId: z.string(),
- channel: z.string().optional(),
- forceRefetch: z.boolean().optional(),
- }),
- )
- .mutation(async ({ input }) => {
- const existing = await prisma.modelOutput.findUnique({
- where: {
- promptVariantId_testScenarioId: {
- promptVariantId: input.variantId,
- testScenarioId: input.scenarioId,
- },
- },
- });
-
- if (existing && !input.forceRefetch) return existing;
-
- const variant = await prisma.promptVariant.findUnique({
- where: {
- id: input.variantId,
- },
- });
-
- const scenario = await prisma.testScenario.findUnique({
- where: {
- id: input.scenarioId,
- },
- });
-
- if (!variant || !scenario) return null;
-
- const prompt = await constructPrompt(variant, scenario.variableValues);
-
- const inputHash = crypto.createHash("sha256").update(JSON.stringify(prompt)).digest("hex");
-
- // TODO: we should probably only use this if temperature=0
- const existingResponse = await prisma.modelOutput.findFirst({
- where: { inputHash, errorMessage: null },
- });
-
- let modelResponse: Awaited>;
-
- if (existingResponse) {
- modelResponse = {
- output: existingResponse.output as Prisma.InputJsonValue,
- statusCode: existingResponse.statusCode,
- errorMessage: existingResponse.errorMessage,
- timeToComplete: existingResponse.timeToComplete,
- promptTokens: existingResponse.promptTokens ?? undefined,
- completionTokens: existingResponse.completionTokens ?? undefined,
- };
- } else {
- try {
- modelResponse = await getCompletion(
- prompt as unknown as CompletionCreateParams,
- input.channel,
- );
- } catch (e) {
- console.error(e);
- throw e;
- }
- }
-
- const modelOutput = await prisma.modelOutput.upsert({
- where: {
- promptVariantId_testScenarioId: {
- promptVariantId: input.variantId,
- testScenarioId: input.scenarioId,
- },
- },
- create: {
- promptVariantId: input.variantId,
- testScenarioId: input.scenarioId,
- inputHash,
- ...modelResponse,
- },
- update: {
- ...modelResponse,
- },
- });
-
- await reevaluateVariant(input.variantId);
-
- return modelOutput;
- }),
-});
diff --git a/src/server/api/routers/promptVariants.router.ts b/src/server/api/routers/promptVariants.router.ts
index 943c4c1..8a57a65 100644
--- a/src/server/api/routers/promptVariants.router.ts
+++ b/src/server/api/routers/promptVariants.router.ts
@@ -2,6 +2,7 @@ import { isObject } from "lodash";
import { z } from "zod";
import { createTRPCRouter, publicProcedure } from "~/server/api/trpc";
import { prisma } from "~/server/db";
+import { generateNewCell } from "~/server/utils/generateNewCell";
import { OpenAIChatModel } from "~/server/types";
import { constructPrompt } from "~/server/utils/constructPrompt";
import userError from "~/server/utils/error";
@@ -43,17 +44,24 @@ export const promptVariantsRouter = createTRPCRouter({
visible: true,
},
});
- const outputCount = await prisma.modelOutput.count({
+ const outputCount = await prisma.scenarioVariantCell.count({
where: {
promptVariantId: input.variantId,
testScenario: { visible: true },
+ modelOutput: {
+ isNot: null,
+ },
},
});
const overallTokens = await prisma.modelOutput.aggregate({
where: {
- promptVariantId: input.variantId,
- testScenario: { visible: true },
+ scenarioVariantCell: {
+ promptVariantId: input.variantId,
+ testScenario: {
+ visible: true,
+ },
+ },
},
_sum: {
promptTokens: true,
@@ -115,6 +123,17 @@ export const promptVariantsRouter = createTRPCRouter({
recordExperimentUpdated(input.experimentId),
]);
+ const scenarios = await prisma.testScenario.findMany({
+ where: {
+ experimentId: input.experimentId,
+ visible: true,
+ },
+ });
+
+ for (const scenario of scenarios) {
+ await generateNewCell(newVariant.id, scenario.id);
+ }
+
return newVariant;
}),
@@ -234,6 +253,17 @@ export const promptVariantsRouter = createTRPCRouter({
await prisma.$transaction([hideOldVariants, recordExperimentUpdated(existing.experimentId)]);
+ const scenarios = await prisma.testScenario.findMany({
+ where: {
+ experimentId: newVariant.experimentId,
+ visible: true,
+ },
+ });
+
+ for (const scenario of scenarios) {
+ await generateNewCell(newVariant.id, scenario.id);
+ }
+
return { status: "ok" } as const;
}),
diff --git a/src/server/api/routers/scenarioVariantCells.router.ts b/src/server/api/routers/scenarioVariantCells.router.ts
new file mode 100644
index 0000000..09e1172
--- /dev/null
+++ b/src/server/api/routers/scenarioVariantCells.router.ts
@@ -0,0 +1,68 @@
+import { z } from "zod";
+import { createTRPCRouter, publicProcedure } from "~/server/api/trpc";
+import { prisma } from "~/server/db";
+import { generateNewCell } from "~/server/utils/generateNewCell";
+import { queueLLMRetrievalTask } from "~/server/utils/queueLLMRetrievalTask";
+
+export const scenarioVariantCellsRouter = createTRPCRouter({
+ get: publicProcedure
+ .input(
+ z.object({
+ scenarioId: z.string(),
+ variantId: z.string(),
+ }),
+ )
+ .query(async ({ input }) => {
+ return await prisma.scenarioVariantCell.findUnique({
+ where: {
+ promptVariantId_testScenarioId: {
+ promptVariantId: input.variantId,
+ testScenarioId: input.scenarioId,
+ },
+ },
+ include: {
+ modelOutput: true,
+ },
+ });
+ }),
+ forceRefetch: publicProcedure
+ .input(
+ z.object({
+ scenarioId: z.string(),
+ variantId: z.string(),
+ }),
+ )
+ .mutation(async ({ input }) => {
+ const cell = await prisma.scenarioVariantCell.findUnique({
+ where: {
+ promptVariantId_testScenarioId: {
+ promptVariantId: input.variantId,
+ testScenarioId: input.scenarioId,
+ },
+ },
+ include: {
+ modelOutput: true,
+ },
+ });
+
+ if (!cell) {
+ await generateNewCell(input.variantId, input.scenarioId);
+ return true;
+ }
+
+ if (cell.modelOutput) {
+ // TODO: Maybe keep these around to show previous generations?
+ await prisma.modelOutput.delete({
+ where: { id: cell.modelOutput.id },
+ });
+ }
+
+ await prisma.scenarioVariantCell.update({
+ where: { id: cell.id },
+ data: { retrievalStatus: "PENDING" },
+ });
+
+ await queueLLMRetrievalTask(cell.id);
+ return true;
+ }),
+});
diff --git a/src/server/api/routers/scenarios.router.ts b/src/server/api/routers/scenarios.router.ts
index dbc2096..5075ae7 100644
--- a/src/server/api/routers/scenarios.router.ts
+++ b/src/server/api/routers/scenarios.router.ts
@@ -4,6 +4,7 @@ import { prisma } from "~/server/db";
import { autogenerateScenarioValues } from "../autogen";
import { recordExperimentUpdated } from "~/server/utils/recordExperimentUpdated";
import { reevaluateAll } from "~/server/utils/evaluations";
+import { generateNewCell } from "~/server/utils/generateNewCell";
export const scenariosRouter = createTRPCRouter({
list: publicProcedure.input(z.object({ experimentId: z.string() })).query(async ({ input }) => {
@@ -48,10 +49,21 @@ export const scenariosRouter = createTRPCRouter({
},
});
- await prisma.$transaction([
+ const [scenario] = await prisma.$transaction([
createNewScenarioAction,
recordExperimentUpdated(input.experimentId),
]);
+
+ const promptVariants = await prisma.promptVariant.findMany({
+ where: {
+ experimentId: input.experimentId,
+ visible: true,
+ },
+ });
+
+ for (const variant of promptVariants) {
+ await generateNewCell(variant.id, scenario.id);
+ }
}),
hide: publicProcedure.input(z.object({ id: z.string() })).mutation(async ({ input }) => {
@@ -175,6 +187,17 @@ export const scenariosRouter = createTRPCRouter({
},
});
+ const promptVariants = await prisma.promptVariant.findMany({
+ where: {
+ experimentId: newScenario.experimentId,
+ visible: true,
+ },
+ });
+
+ for (const variant of promptVariants) {
+ await generateNewCell(variant.id, newScenario.id);
+ }
+
return newScenario;
}),
});
diff --git a/src/server/scripts/migrateScenarioVariantOutputData.ts b/src/server/scripts/migrateScenarioVariantOutputData.ts
new file mode 100644
index 0000000..99984fa
--- /dev/null
+++ b/src/server/scripts/migrateScenarioVariantOutputData.ts
@@ -0,0 +1,47 @@
+import { type Prisma } from "@prisma/client";
+import { prisma } from "../db";
+
+async function migrateScenarioVariantOutputData() {
+ // Get all ScenarioVariantCells
+ const cells = await prisma.scenarioVariantCell.findMany({ include: { modelOutput: true } });
+ console.log(`Found ${cells.length} records`);
+
+ let updatedCount = 0;
+
+ // Loop through all scenarioVariants
+ for (const cell of cells) {
+ // Create a new ModelOutput for each ScenarioVariant with an existing output
+ if (cell.output && !cell.modelOutput) {
+ updatedCount++;
+ await prisma.modelOutput.create({
+ data: {
+ scenarioVariantCellId: cell.id,
+ inputHash: cell.inputHash || "",
+ output: cell.output as Prisma.InputJsonValue,
+ timeToComplete: cell.timeToComplete ?? undefined,
+ promptTokens: cell.promptTokens,
+ completionTokens: cell.completionTokens,
+ createdAt: cell.createdAt,
+ updatedAt: cell.updatedAt,
+ },
+ });
+ } else if (cell.errorMessage && cell.retrievalStatus === "COMPLETE") {
+ updatedCount++;
+ await prisma.scenarioVariantCell.update({
+ where: { id: cell.id },
+ data: {
+ retrievalStatus: "ERROR",
+ },
+ });
+ }
+ }
+
+ console.log("Data migration completed");
+ console.log(`Updated ${updatedCount} records`);
+}
+
+// Execute the function
+migrateScenarioVariantOutputData().catch((error) => {
+ console.error("An error occurred while migrating data: ", error);
+ process.exit(1);
+});
diff --git a/src/server/tasks/defineTask.ts b/src/server/tasks/defineTask.ts
new file mode 100644
index 0000000..64bd834
--- /dev/null
+++ b/src/server/tasks/defineTask.ts
@@ -0,0 +1,31 @@
+// Import necessary dependencies
+import { quickAddJob, type Helpers, type Task } from "graphile-worker";
+import { env } from "~/env.mjs";
+
+// Define the defineTask function
+function defineTask(
+ taskIdentifier: string,
+ taskHandler: (payload: TPayload, helpers: Helpers) => Promise,
+) {
+ const enqueue = async (payload: TPayload) => {
+ console.log("Enqueuing task", taskIdentifier, payload);
+ await quickAddJob({ connectionString: env.DATABASE_URL }, taskIdentifier, payload);
+ };
+
+ const handler = (payload: TPayload, helpers: Helpers) => {
+ helpers.logger.info(`Running task ${taskIdentifier} with payload: ${JSON.stringify(payload)}`);
+ return taskHandler(payload, helpers);
+ };
+
+ const task = {
+ identifier: taskIdentifier,
+ handler: handler as Task,
+ };
+
+ return {
+ enqueue,
+ task,
+ };
+}
+
+export default defineTask;
diff --git a/src/server/tasks/queryLLM.task.ts b/src/server/tasks/queryLLM.task.ts
new file mode 100644
index 0000000..d2178d0
--- /dev/null
+++ b/src/server/tasks/queryLLM.task.ts
@@ -0,0 +1,144 @@
+import crypto from "crypto";
+import { prisma } from "~/server/db";
+import defineTask from "./defineTask";
+import { type CompletionResponse, getCompletion } from "../utils/getCompletion";
+import { type JSONSerializable } from "../types";
+import { sleep } from "../utils/sleep";
+import { shouldStream } from "../utils/shouldStream";
+import { generateChannel } from "~/utils/generateChannel";
+import { reevaluateVariant } from "../utils/evaluations";
+import { constructPrompt } from "../utils/constructPrompt";
+import { type CompletionCreateParams } from "openai/resources/chat";
+
+const MAX_AUTO_RETRIES = 10;
+const MIN_DELAY = 500; // milliseconds
+const MAX_DELAY = 15000; // milliseconds
+
+function calculateDelay(numPreviousTries: number): number {
+ const baseDelay = Math.min(MAX_DELAY, MIN_DELAY * Math.pow(2, numPreviousTries));
+ const jitter = Math.random() * baseDelay;
+ return baseDelay + jitter;
+}
+
+const getCompletionWithRetries = async (
+ cellId: string,
+ payload: JSONSerializable,
+ channel?: string,
+): Promise => {
+ for (let i = 0; i < MAX_AUTO_RETRIES; i++) {
+ const modelResponse = await getCompletion(
+ payload as unknown as CompletionCreateParams,
+ channel,
+ );
+ if (modelResponse.statusCode !== 429 || i === MAX_AUTO_RETRIES - 1) {
+ return modelResponse;
+ }
+ const delay = calculateDelay(i);
+ await prisma.scenarioVariantCell.update({
+ where: { id: cellId },
+ data: {
+ errorMessage: "Rate limit exceeded",
+ statusCode: 429,
+ retryTime: new Date(Date.now() + delay),
+ },
+ });
+ // TODO: Maybe requeue the job so other jobs can run in the future?
+ await sleep(delay);
+ }
+ throw new Error("Max retries limit reached");
+};
+
+export type queryLLMJob = {
+ scenarioVariantCellId: string;
+};
+
+export const queryLLM = defineTask("queryLLM", async (task) => {
+ const { scenarioVariantCellId } = task;
+ const cell = await prisma.scenarioVariantCell.findUnique({
+ where: { id: scenarioVariantCellId },
+ include: { modelOutput: true },
+ });
+ if (!cell) {
+ return;
+ }
+
+ // If cell is not pending, then some other job is already processing it
+ if (cell.retrievalStatus !== "PENDING") {
+ return;
+ }
+ await prisma.scenarioVariantCell.update({
+ where: { id: scenarioVariantCellId },
+ data: {
+ retrievalStatus: "IN_PROGRESS",
+ },
+ });
+
+ const variant = await prisma.promptVariant.findUnique({
+ where: { id: cell.promptVariantId },
+ });
+ if (!variant) {
+ return;
+ }
+
+ const scenario = await prisma.testScenario.findUnique({
+ where: { id: cell.testScenarioId },
+ });
+ if (!scenario) {
+ return;
+ }
+
+ const prompt = await constructPrompt(variant, scenario.variableValues);
+
+ const streamingEnabled = shouldStream(prompt);
+ let streamingChannel;
+
+ if (streamingEnabled) {
+ streamingChannel = generateChannel();
+ // Save streaming channel so that UI can connect to it
+ await prisma.scenarioVariantCell.update({
+ where: { id: scenarioVariantCellId },
+ data: {
+ streamingChannel,
+ },
+ });
+ }
+
+ const modelResponse = await getCompletionWithRetries(
+ scenarioVariantCellId,
+ prompt,
+ streamingChannel,
+ );
+
+ let modelOutput = null;
+ if (modelResponse.statusCode === 200) {
+ const inputHash = crypto.createHash("sha256").update(JSON.stringify(prompt)).digest("hex");
+
+ modelOutput = await prisma.modelOutput.create({
+ data: {
+ scenarioVariantCellId,
+ inputHash,
+ output: modelResponse.output,
+ timeToComplete: modelResponse.timeToComplete,
+ promptTokens: modelResponse.promptTokens,
+ completionTokens: modelResponse.completionTokens,
+ },
+ });
+ }
+
+ await prisma.scenarioVariantCell.update({
+ where: { id: scenarioVariantCellId },
+ data: {
+ statusCode: modelResponse.statusCode,
+ errorMessage: modelResponse.errorMessage,
+ streamingChannel: null,
+ retrievalStatus: modelOutput ? "COMPLETE" : "ERROR",
+ modelOutput: {
+ connect: {
+ id: modelOutput?.id,
+ },
+ },
+ },
+ });
+
+ await reevaluateVariant(cell.promptVariantId);
+});
diff --git a/src/server/tasks/worker.ts b/src/server/tasks/worker.ts
new file mode 100644
index 0000000..e2fc916
--- /dev/null
+++ b/src/server/tasks/worker.ts
@@ -0,0 +1,40 @@
+import { type TaskList, run } from "graphile-worker";
+import "dotenv/config";
+
+import { env } from "~/env.mjs";
+import { queryLLM } from "./queryLLM.task";
+
+const registeredTasks = [queryLLM];
+
+const taskList = registeredTasks.reduce((acc, task) => {
+ acc[task.task.identifier] = task.task.handler;
+ return acc;
+}, {} as TaskList);
+
+async function main() {
+ // Run a worker to execute jobs:
+ const runner = await run({
+ connectionString: env.DATABASE_URL,
+ concurrency: 20,
+ // Install signal handlers for graceful shutdown on SIGINT, SIGTERM, etc
+ noHandleSignals: false,
+ pollInterval: 1000,
+ // you can set the taskList or taskDirectory but not both
+ taskList,
+ // or:
+ // taskDirectory: `${__dirname}/tasks`,
+ });
+
+ // Immediately await (or otherwise handled) the resulting promise, to avoid
+ // "unhandled rejection" errors causing a process crash in the event of
+ // something going wrong.
+ await runner.promise;
+
+ // If the worker exits (whether through fatal error or otherwise), the above
+ // promise will resolve/reject.
+}
+
+main().catch((err) => {
+ console.error("Unhandled error occurred running worker: ", err);
+ process.exit(1);
+});
diff --git a/src/server/utils/evaluations.ts b/src/server/utils/evaluations.ts
index 690cad8..3b24334 100644
--- a/src/server/utils/evaluations.ts
+++ b/src/server/utils/evaluations.ts
@@ -1,4 +1,4 @@
-import { type Evaluation } from "@prisma/client";
+import { type ModelOutput, type Evaluation } from "@prisma/client";
import { prisma } from "../db";
import { evaluateOutput } from "./evaluateOutput";
@@ -12,21 +12,22 @@ export const reevaluateVariant = async (variantId: string) => {
where: { experimentId: variant.experimentId },
});
- const modelOutputs = await prisma.modelOutput.findMany({
+ const cells = await prisma.scenarioVariantCell.findMany({
where: {
promptVariantId: variantId,
- statusCode: { notIn: [429] },
+ retrievalStatus: "COMPLETE",
testScenario: { visible: true },
+ modelOutput: { isNot: null },
},
- include: { testScenario: true },
+ include: { testScenario: true, modelOutput: true },
});
await Promise.all(
evaluations.map(async (evaluation) => {
- const passCount = modelOutputs.filter((output) =>
- evaluateOutput(output, output.testScenario, evaluation),
+ const passCount = cells.filter((cell) =>
+ evaluateOutput(cell.modelOutput as ModelOutput, cell.testScenario, evaluation),
).length;
- const failCount = modelOutputs.length - passCount;
+ const failCount = cells.length - passCount;
await prisma.evaluationResult.upsert({
where: {
@@ -55,22 +56,23 @@ export const reevaluateEvaluation = async (evaluation: Evaluation) => {
where: { experimentId: evaluation.experimentId, visible: true },
});
- const modelOutputs = await prisma.modelOutput.findMany({
+ const cells = await prisma.scenarioVariantCell.findMany({
where: {
promptVariantId: { in: variants.map((v) => v.id) },
testScenario: { visible: true },
statusCode: { notIn: [429] },
+ modelOutput: { isNot: null },
},
- include: { testScenario: true },
+ include: { testScenario: true, modelOutput: true },
});
await Promise.all(
variants.map(async (variant) => {
- const outputs = modelOutputs.filter((output) => output.promptVariantId === variant.id);
- const passCount = outputs.filter((output) =>
- evaluateOutput(output, output.testScenario, evaluation),
+ const variantCells = cells.filter((cell) => cell.promptVariantId === variant.id);
+ const passCount = variantCells.filter((cell) =>
+ evaluateOutput(cell.modelOutput as ModelOutput, cell.testScenario, evaluation),
).length;
- const failCount = outputs.length - passCount;
+ const failCount = variantCells.length - passCount;
await prisma.evaluationResult.upsert({
where: {
diff --git a/src/server/utils/generateNewCell.ts b/src/server/utils/generateNewCell.ts
new file mode 100644
index 0000000..f55adfe
--- /dev/null
+++ b/src/server/utils/generateNewCell.ts
@@ -0,0 +1,76 @@
+import crypto from "crypto";
+import { type Prisma } from "@prisma/client";
+import { prisma } from "../db";
+import { queueLLMRetrievalTask } from "./queueLLMRetrievalTask";
+import { constructPrompt } from "./constructPrompt";
+
+export const generateNewCell = async (variantId: string, scenarioId: string) => {
+ const variant = await prisma.promptVariant.findUnique({
+ where: {
+ id: variantId,
+ },
+ });
+
+ const scenario = await prisma.testScenario.findUnique({
+ where: {
+ id: scenarioId,
+ },
+ });
+
+ if (!variant || !scenario) return null;
+
+ const prompt = await constructPrompt(variant, scenario.variableValues);
+
+ const inputHash = crypto.createHash("sha256").update(JSON.stringify(prompt)).digest("hex");
+
+ let cell = await prisma.scenarioVariantCell.findUnique({
+ where: {
+ promptVariantId_testScenarioId: {
+ promptVariantId: variantId,
+ testScenarioId: scenarioId,
+ },
+ },
+ include: {
+ modelOutput: true,
+ },
+ });
+
+ if (cell) return cell;
+
+ cell = await prisma.scenarioVariantCell.create({
+ data: {
+ promptVariantId: variantId,
+ testScenarioId: scenarioId,
+ },
+ include: {
+ modelOutput: true,
+ },
+ });
+
+ const matchingModelOutput = await prisma.modelOutput.findFirst({
+ where: {
+ inputHash,
+ },
+ });
+
+ let newModelOutput;
+
+ if (matchingModelOutput) {
+ newModelOutput = await prisma.modelOutput.create({
+ data: {
+ scenarioVariantCellId: cell.id,
+ inputHash,
+ output: matchingModelOutput.output as Prisma.InputJsonValue,
+ timeToComplete: matchingModelOutput.timeToComplete,
+ promptTokens: matchingModelOutput.promptTokens,
+ completionTokens: matchingModelOutput.completionTokens,
+ createdAt: matchingModelOutput.createdAt,
+ updatedAt: matchingModelOutput.updatedAt,
+ },
+ });
+ } else {
+ cell = await queueLLMRetrievalTask(cell.id);
+ }
+
+ return { ...cell, modelOutput: newModelOutput };
+};
diff --git a/src/server/utils/getCompletion.ts b/src/server/utils/getCompletion.ts
index 61452e4..32d7b61 100644
--- a/src/server/utils/getCompletion.ts
+++ b/src/server/utils/getCompletion.ts
@@ -9,7 +9,7 @@ import { env } from "~/env.mjs";
import { countOpenAIChatTokens } from "~/utils/countTokens";
import { rateLimitErrorMessage } from "~/sharedStrings";
-type CompletionResponse = {
+export type CompletionResponse = {
output: Prisma.InputJsonValue | typeof Prisma.JsonNull;
statusCode: number;
errorMessage: string | null;
diff --git a/src/server/utils/queueLLMRetrievalTask.ts b/src/server/utils/queueLLMRetrievalTask.ts
new file mode 100644
index 0000000..762b708
--- /dev/null
+++ b/src/server/utils/queueLLMRetrievalTask.ts
@@ -0,0 +1,22 @@
+import { prisma } from "../db";
+import { queryLLM } from "../tasks/queryLLM.task";
+
+export const queueLLMRetrievalTask = async (cellId: string) => {
+ const updatedCell = await prisma.scenarioVariantCell.update({
+ where: {
+ id: cellId,
+ },
+ data: {
+ retrievalStatus: "PENDING",
+ errorMessage: null,
+ },
+ include: {
+ modelOutput: true,
+ },
+ });
+
+ // @ts-expect-error we aren't passing the helpers but that's ok
+ void queryLLM.task.handler({ scenarioVariantCellId: cellId }, { logger: console });
+
+ return updatedCell;
+};
diff --git a/src/server/utils/shouldStream.ts b/src/server/utils/shouldStream.ts
new file mode 100644
index 0000000..8dd0a38
--- /dev/null
+++ b/src/server/utils/shouldStream.ts
@@ -0,0 +1,7 @@
+import { isObject } from "lodash";
+import { type JSONSerializable } from "../types";
+
+export const shouldStream = (config: JSONSerializable): boolean => {
+ const shouldStream = isObject(config) && "stream" in config && config.stream === true;
+ return shouldStream;
+};
diff --git a/src/server/utils/sleep.ts b/src/server/utils/sleep.ts
new file mode 100644
index 0000000..fe57bb6
--- /dev/null
+++ b/src/server/utils/sleep.ts
@@ -0,0 +1 @@
+export const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
diff --git a/src/utils/useSocket.ts b/src/utils/useSocket.ts
index ee0738c..175036c 100644
--- a/src/utils/useSocket.ts
+++ b/src/utils/useSocket.ts
@@ -5,7 +5,7 @@ import { env } from "~/env.mjs";
const url = env.NEXT_PUBLIC_SOCKET_URL;
-export default function useSocket(channel?: string) {
+export default function useSocket(channel?: string | null) {
const socketRef = useRef();
const [message, setMessage] = useState(null);