Compare commits

..

24 Commits

Author SHA1 Message Date
Kyle Corbitt
8d1609dd52 Add admin role
Allow privileged users to administer the system.
2023-08-03 09:35:13 -07:00
David Corbitt
f3380f302d Simplify world champs screen 2023-08-02 23:57:44 -07:00
David Corbitt
3dba9c7ee1 Update posthog version 2023-08-02 23:30:15 -07:00
David Corbitt
e0e4f7a9d6 Fix mobile table padding 2023-08-02 23:08:49 -07:00
arcticfly
48293dc579 Add link to demo experiment (#114) 2023-08-02 22:50:09 -07:00
arcticfly
38ac6243a0 Add server posthog events (#113) 2023-08-02 14:21:07 -07:00
arcticfly
bd2f58e2a5 Improve posthog (#112)
* Add SessionIdentifier

* Identify by id

* Rewrite posthog events

* Add NEXT_PUBLIC_HOST to dockerfile

* Fix default url

* Move SessionIdentifier into analytics file
2023-08-02 13:30:25 -07:00
Kyle Corbitt
808e47c6b9 Merge pull request #111 from OpenPipe/gh-btn
Update TopNavbar component to include a GitHub button
2023-08-02 10:15:26 -07:00
Kyle Corbitt
5945f0ed6b Update TopNavbar component to include a GitHub button 2023-08-02 10:11:41 -07:00
arcticfly
6bc7d76d15 Update README.md 2023-08-02 00:59:05 -07:00
arcticfly
e9ed173e34 Update README.md 2023-08-02 00:57:24 -07:00
arcticfly
75d58d7021 Update README.md 2023-08-02 00:56:19 -07:00
arcticfly
896c8c5c57 Update README.md 2023-08-02 00:51:57 -07:00
arcticfly
ec5547d0b0 Update README.md with new features and gifs (#110) 2023-08-02 00:46:48 -07:00
Kyle Corbitt
77e4e3b8c3 mobile styles 2023-08-01 23:08:35 -07:00
Kyle Corbitt
a1b03ddad1 Merge pull request #109 from OpenPipe/debug-prompts
Add debug modal for output cells
2023-08-01 22:51:39 -07:00
Kyle Corbitt
6be32bea4c Add debug modal for output cells
See the actual input that a model got for a specific cell. The formatting isn't great right now; should probably iterate on that.
2023-08-01 22:49:38 -07:00
arcticfly
72c70e2a55 Improve conversion to/from Claude (#108)
* Increase min width of prompt variant

* Increase width of custom instructions input

* Start recording API docs

* Provide better instructions for converting to/from Claude

* Fix prettier
2023-08-01 21:03:23 -07:00
arcticfly
026532f2c2 Model selection styling changes (#107)
* Model selection styling changes

* Fix prettier
2023-08-01 18:45:15 -07:00
Kyle Corbitt
f88538336f fix types 2023-08-01 18:31:34 -07:00
Kyle Corbitt
3c7178115e Merge pull request #105 from OpenPipe/bump-models
Bump Replicate models
2023-08-01 18:26:16 -07:00
Kyle Corbitt
292aaf090a Merge pull request #106 from OpenPipe/dark-mode
Update global background color in ChakraThemeProvider
2023-08-01 18:25:57 -07:00
Kyle Corbitt
d9915dc41b Update global background color in ChakraThemeProvider 2023-08-01 18:25:29 -07:00
David Corbitt
3560bcff14 Correct time stamps on waiting message 2023-08-01 18:09:23 -07:00
37 changed files with 648 additions and 377 deletions

View File

@@ -26,6 +26,8 @@ NEXT_PUBLIC_SOCKET_URL="http://localhost:3318"
NEXTAUTH_SECRET="your_secret" NEXTAUTH_SECRET="your_secret"
NEXTAUTH_URL="http://localhost:3000" NEXTAUTH_URL="http://localhost:3000"
NEXT_PUBLIC_HOST="http://localhost:3000"
# Next Auth Github Provider # Next Auth Github Provider
GITHUB_CLIENT_ID="your_client_id" GITHUB_CLIENT_ID="your_client_id"
GITHUB_CLIENT_SECRET="your_secret" GITHUB_CLIENT_SECRET="your_secret"

View File

@@ -20,6 +20,7 @@ FROM base as builder
# Include all NEXT_PUBLIC_* env vars here # Include all NEXT_PUBLIC_* env vars here
ARG NEXT_PUBLIC_POSTHOG_KEY ARG NEXT_PUBLIC_POSTHOG_KEY
ARG NEXT_PUBLIC_SOCKET_URL ARG NEXT_PUBLIC_SOCKET_URL
ARG NEXT_PUBLIC_HOST
WORKDIR /app WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules COPY --from=deps /app/node_modules ./node_modules

View File

@@ -6,40 +6,47 @@ OpenPipe is a flexible playground for comparing and optimizing LLM prompts. It l
## Sample Experiments ## Sample Experiments
These are simple experiments users have created that show how OpenPipe works. These are simple experiments users have created that show how OpenPipe works. Feel free to fork them and start experimenting yourself.
- [Country Capitals](https://app.openpipe.ai/experiments/11111111-1111-1111-1111-111111111111) - [Twitter Sentiment Analysis](https://app.openpipe.ai/experiments/62c20a73-2012-4a64-973c-4b665ad46a57)
- [Reddit User Needs](https://app.openpipe.ai/experiments/22222222-2222-2222-2222-222222222222) - [Reddit User Needs](https://app.openpipe.ai/experiments/22222222-2222-2222-2222-222222222222)
- [OpenAI Function Calls](https://app.openpipe.ai/experiments/2ebbdcb3-ed51-456e-87dc-91f72eaf3e2b) - [OpenAI Function Calls](https://app.openpipe.ai/experiments/2ebbdcb3-ed51-456e-87dc-91f72eaf3e2b)
- [Activity Classification](https://app.openpipe.ai/experiments/3950940f-ab6b-4b74-841d-7e9dbc4e4ff8) - [Activity Classification](https://app.openpipe.ai/experiments/3950940f-ab6b-4b74-841d-7e9dbc4e4ff8)
<img src="https://github.com/openpipe/openpipe/assets/176426/fc7624c6-5b65-4d4d-82b7-4a816f3e5678" alt="demo" height="400px"> <img src="https://github.com/openpipe/openpipe/assets/41524992/219a844e-3f4e-4f6b-8066-41348b42977b" alt="demo">
You can use our hosted version of OpenPipe at [https://openpipe.ai]. You can also clone this repository and [run it locally](#running-locally). You can use our hosted version of OpenPipe at https://openpipe.ai. You can also clone this repository and [run it locally](#running-locally).
## High-Level Features ## High-Level Features
**Configure Multiple Prompts**
Set up multiple prompt configurations and compare their output side-by-side. Each configuration can be configured independently.
**Visualize Responses** **Visualize Responses**
Inspect prompt completions side-by-side. Inspect prompt completions side-by-side.
**Test Many Inputs** <br>
OpenPipe lets you _template_ a prompt. Use the templating feature to run the prompts you're testing against many potential inputs for broader coverage of your problem space than you'd get with manual testing.
**Test Many Inputs**
OpenPipe lets you _template_ a prompt. Use the templating feature to run the prompts you're testing against many potential inputs for broad coverage of your problem space.
<br>
**Translate between Model APIs**
Write your prompt in one format and automatically convert it to work with any other model.
<img width="480" alt="Screenshot 2023-08-01 at 11 55 38 PM" src="https://github.com/OpenPipe/OpenPipe/assets/41524992/1e19ccf2-96b6-4e93-a3a5-1449710d1b5b" alt="translate between models">
<br><br>
**Refine your prompts automatically**
Use a growing database of best-practice refinements to improve your prompts automatically.
<img width="480" alt="Screenshot 2023-08-01 at 11 55 38 PM" src="https://github.com/OpenPipe/OpenPipe/assets/41524992/87a27fe7-daef-445c-a5e2-1c82b23f9f99" alt="add function call">
<br><br>
**🪄 Auto-generate Test Scenarios** **🪄 Auto-generate Test Scenarios**
OpenPipe includes a tool to generate new test scenarios based on your existing prompts and scenarios. Just click "Autogenerate Scenario" to try it out! OpenPipe includes a tool to generate new test scenarios based on your existing prompts and scenarios. Just click "Autogenerate Scenario" to try it out!
**Prompt Validation and Typeahead** <img width="600" src="https://github.com/openpipe/openpipe/assets/41524992/219a844e-3f4e-4f6b-8066-41348b42977b" alt="auto-generate">
We use OpenAI's OpenAPI spec to automatically provide typeahead and validate prompts.
<img alt="typeahead" src="https://github.com/openpipe/openpipe/assets/176426/acc638f8-d851-4742-8d01-fe6f98890840" height="300px"> <br><br>
**Function Call Support**
Natively supports [OpenAI function calls](https://openai.com/blog/function-calling-and-other-api-updates) on supported models.
<img height="300px" alt="function calls" src="https://github.com/openpipe/openpipe/assets/176426/48ad13fe-af2f-4294-bf32-62015597fd9b">
## Supported Models ## Supported Models

View File

@@ -21,6 +21,13 @@ const config = {
defaultLocale: "en", defaultLocale: "en",
}, },
rewrites: async () => [
{
source: "/ingest/:path*",
destination: "https://app.posthog.com/:path*",
},
],
webpack: (config) => { webpack: (config) => {
config.module.rules.push({ config.module.rules.push({
test: /\.txt$/, test: /\.txt$/,

View File

@@ -25,8 +25,10 @@
"@apidevtools/json-schema-ref-parser": "^10.1.0", "@apidevtools/json-schema-ref-parser": "^10.1.0",
"@babel/preset-typescript": "^7.22.5", "@babel/preset-typescript": "^7.22.5",
"@babel/standalone": "^7.22.9", "@babel/standalone": "^7.22.9",
"@chakra-ui/anatomy": "^2.2.0",
"@chakra-ui/next-js": "^2.1.4", "@chakra-ui/next-js": "^2.1.4",
"@chakra-ui/react": "^2.7.1", "@chakra-ui/react": "^2.7.1",
"@chakra-ui/styled-system": "^2.9.1",
"@emotion/react": "^11.11.1", "@emotion/react": "^11.11.1",
"@emotion/server": "^11.11.0", "@emotion/server": "^11.11.0",
"@emotion/styled": "^11.11.0", "@emotion/styled": "^11.11.0",
@@ -63,15 +65,18 @@
"next-auth": "^4.22.1", "next-auth": "^4.22.1",
"next-query-params": "^4.2.3", "next-query-params": "^4.2.3",
"nextjs-routes": "^2.0.1", "nextjs-routes": "^2.0.1",
"openai": "4.0.0-beta.2", "openai": "4.0.0-beta.7",
"pluralize": "^8.0.0", "pluralize": "^8.0.0",
"posthog-js": "^1.68.4", "posthog-js": "^1.75.3",
"posthog-node": "^3.1.1",
"prettier": "^3.0.0", "prettier": "^3.0.0",
"prismjs": "^1.29.0", "prismjs": "^1.29.0",
"react": "18.2.0", "react": "18.2.0",
"react-diff-viewer": "^3.1.1", "react-diff-viewer": "^3.1.1",
"react-dom": "18.2.0", "react-dom": "18.2.0",
"react-github-btn": "^1.4.0",
"react-icons": "^4.10.1", "react-icons": "^4.10.1",
"react-json-tree": "^0.18.0",
"react-select": "^5.7.4", "react-select": "^5.7.4",
"react-syntax-highlighter": "^15.5.0", "react-syntax-highlighter": "^15.5.0",
"react-textarea-autosize": "^8.5.0", "react-textarea-autosize": "^8.5.0",

216
pnpm-lock.yaml generated
View File

@@ -1,4 +1,4 @@
lockfileVersion: '6.1' lockfileVersion: '6.0'
settings: settings:
autoInstallPeers: true autoInstallPeers: true
@@ -17,12 +17,18 @@ dependencies:
'@babel/standalone': '@babel/standalone':
specifier: ^7.22.9 specifier: ^7.22.9
version: 7.22.9 version: 7.22.9
'@chakra-ui/anatomy':
specifier: ^2.2.0
version: 2.2.0
'@chakra-ui/next-js': '@chakra-ui/next-js':
specifier: ^2.1.4 specifier: ^2.1.4
version: 2.1.4(@chakra-ui/react@2.7.1)(@emotion/react@11.11.1)(next@13.4.2)(react@18.2.0) version: 2.1.4(@chakra-ui/react@2.7.1)(@emotion/react@11.11.1)(next@13.4.2)(react@18.2.0)
'@chakra-ui/react': '@chakra-ui/react':
specifier: ^2.7.1 specifier: ^2.7.1
version: 2.7.1(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.6)(framer-motion@10.12.17)(react-dom@18.2.0)(react@18.2.0) version: 2.7.1(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.6)(framer-motion@10.12.17)(react-dom@18.2.0)(react@18.2.0)
'@chakra-ui/styled-system':
specifier: ^2.9.1
version: 2.9.1
'@emotion/react': '@emotion/react':
specifier: ^11.11.1 specifier: ^11.11.1
version: 11.11.1(@types/react@18.2.6)(react@18.2.0) version: 11.11.1(@types/react@18.2.6)(react@18.2.0)
@@ -132,14 +138,17 @@ dependencies:
specifier: ^2.0.1 specifier: ^2.0.1
version: 2.0.1(next@13.4.2) version: 2.0.1(next@13.4.2)
openai: openai:
specifier: 4.0.0-beta.2 specifier: 4.0.0-beta.7
version: 4.0.0-beta.2 version: 4.0.0-beta.7
pluralize: pluralize:
specifier: ^8.0.0 specifier: ^8.0.0
version: 8.0.0 version: 8.0.0
posthog-js: posthog-js:
specifier: ^1.68.4 specifier: ^1.75.3
version: 1.68.4 version: 1.75.3
posthog-node:
specifier: ^3.1.1
version: 3.1.1
prettier: prettier:
specifier: ^3.0.0 specifier: ^3.0.0
version: 3.0.0 version: 3.0.0
@@ -155,9 +164,15 @@ dependencies:
react-dom: react-dom:
specifier: 18.2.0 specifier: 18.2.0
version: 18.2.0(react@18.2.0) version: 18.2.0(react@18.2.0)
react-github-btn:
specifier: ^1.4.0
version: 1.4.0(react@18.2.0)
react-icons: react-icons:
specifier: ^4.10.1 specifier: ^4.10.1
version: 4.10.1(react@18.2.0) version: 4.10.1(react@18.2.0)
react-json-tree:
specifier: ^0.18.0
version: 0.18.0(@types/react@18.2.6)(react@18.2.0)
react-select: react-select:
specifier: ^5.7.4 specifier: ^5.7.4
version: 5.7.4(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0) version: 5.7.4(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0)
@@ -686,6 +701,10 @@ packages:
resolution: {integrity: sha512-pKfOS/mztc4sUXHNc8ypJ1gPWSolWT770jrgVRfolVbYlki8y5Y+As996zMF6k5lewTu6j9DQequ7Cc9a69IVQ==} resolution: {integrity: sha512-pKfOS/mztc4sUXHNc8ypJ1gPWSolWT770jrgVRfolVbYlki8y5Y+As996zMF6k5lewTu6j9DQequ7Cc9a69IVQ==}
dev: false dev: false
/@chakra-ui/anatomy@2.2.0:
resolution: {integrity: sha512-cD8Ms5C8+dFda0LrORMdxiFhAZwOIY1BSlCadz6/mHUIgNdQy13AHPrXiq6qWdMslqVHq10k5zH7xMPLt6kjFg==}
dev: false
/@chakra-ui/avatar@2.2.11(@chakra-ui/system@2.5.8)(react@18.2.0): /@chakra-ui/avatar@2.2.11(@chakra-ui/system@2.5.8)(react@18.2.0):
resolution: {integrity: sha512-CJFkoWvlCTDJTUBrKA/aVyG5Zz6TBEIVmmsJtqC6VcQuVDTxkWod8ruXnjb0LT2DUveL7xR5qZM9a5IXcsH3zg==} resolution: {integrity: sha512-CJFkoWvlCTDJTUBrKA/aVyG5Zz6TBEIVmmsJtqC6VcQuVDTxkWod8ruXnjb0LT2DUveL7xR5qZM9a5IXcsH3zg==}
peerDependencies: peerDependencies:
@@ -2831,6 +2850,10 @@ packages:
'@babel/types': 7.22.5 '@babel/types': 7.22.5
dev: true dev: true
/@types/base16@1.0.2:
resolution: {integrity: sha512-oYO/U4VD1DavwrKuCSQWdLG+5K22SLPem2OQaHmFcQuwHoVeGC+JGVRji2MUqZUAIQZHEonOeVfAX09hYiLsdg==}
dev: false
/@types/body-parser@1.19.2: /@types/body-parser@1.19.2:
resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==} resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==}
dependencies: dependencies:
@@ -2876,7 +2899,7 @@ packages:
/@types/eslint-scope@3.7.4: /@types/eslint-scope@3.7.4:
resolution: {integrity: sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==} resolution: {integrity: sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==}
dependencies: dependencies:
'@types/eslint': 8.37.0 '@types/eslint': 8.44.1
'@types/estree': 1.0.1 '@types/estree': 1.0.1
dev: true dev: true
@@ -2887,6 +2910,13 @@ packages:
'@types/json-schema': 7.0.12 '@types/json-schema': 7.0.12
dev: true dev: true
/@types/eslint@8.44.1:
resolution: {integrity: sha512-XpNDc4Z5Tb4x+SW1MriMVeIsMoONHCkWFMkR/aPJbzEsxqHy+4Glu/BqTdPrApfDeMaXbtNh6bseNgl5KaWrSg==}
dependencies:
'@types/estree': 1.0.1
'@types/json-schema': 7.0.12
dev: true
/@types/estree@1.0.1: /@types/estree@1.0.1:
resolution: {integrity: sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==} resolution: {integrity: sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==}
dev: true dev: true
@@ -2980,6 +3010,10 @@ packages:
/@types/node@18.16.0: /@types/node@18.16.0:
resolution: {integrity: sha512-BsAaKhB+7X+H4GnSjGhJG9Qi8Tw+inU9nJDwmD5CgOmBLEI6ArdhikpLX7DjbjDRDTbqZzU2LSQNZg8WGPiSZQ==} resolution: {integrity: sha512-BsAaKhB+7X+H4GnSjGhJG9Qi8Tw+inU9nJDwmD5CgOmBLEI6ArdhikpLX7DjbjDRDTbqZzU2LSQNZg8WGPiSZQ==}
/@types/node@18.17.1:
resolution: {integrity: sha512-xlR1jahfizdplZYRU59JlUx9uzF1ARa8jbhM11ccpCJya8kvos5jwdm2ZAgxSCwOl0fq21svP18EVwPBXMQudw==}
dev: true
/@types/node@20.4.2: /@types/node@20.4.2:
resolution: {integrity: sha512-Dd0BYtWgnWJKwO1jkmTrzofjK2QXXcai0dmtzvIBhcA+RsG5h8R3xlyta0kGOZRNfL9GuRtb1knmPEhQrePCEw==} resolution: {integrity: sha512-Dd0BYtWgnWJKwO1jkmTrzofjK2QXXcai0dmtzvIBhcA+RsG5h8R3xlyta0kGOZRNfL9GuRtb1knmPEhQrePCEw==}
dev: true dev: true
@@ -3013,6 +3047,7 @@ packages:
/@types/qs@6.9.7: /@types/qs@6.9.7:
resolution: {integrity: sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==} resolution: {integrity: sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==}
dev: true
/@types/range-parser@1.2.4: /@types/range-parser@1.2.4:
resolution: {integrity: sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==} resolution: {integrity: sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==}
@@ -3609,6 +3644,15 @@ packages:
engines: {node: '>=4'} engines: {node: '>=4'}
dev: true dev: true
/axios@0.27.2:
resolution: {integrity: sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==}
dependencies:
follow-redirects: 1.15.2
form-data: 4.0.0
transitivePeerDependencies:
- debug
dev: false
/axobject-query@3.2.1: /axobject-query@3.2.1:
resolution: {integrity: sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==} resolution: {integrity: sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==}
dependencies: dependencies:
@@ -3658,6 +3702,10 @@ packages:
resolution: {integrity: sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA==} resolution: {integrity: sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA==}
dev: false dev: false
/base16@1.0.0:
resolution: {integrity: sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ==}
dev: false
/base64-js@0.0.8: /base64-js@0.0.8:
resolution: {integrity: sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw==} resolution: {integrity: sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@@ -3725,6 +3773,17 @@ packages:
dependencies: dependencies:
fill-range: 7.0.1 fill-range: 7.0.1
/browserslist@4.21.10:
resolution: {integrity: sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==}
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
dependencies:
caniuse-lite: 1.0.30001519
electron-to-chromium: 1.4.482
node-releases: 2.0.13
update-browserslist-db: 1.0.11(browserslist@4.21.10)
dev: true
/browserslist@4.21.9: /browserslist@4.21.9:
resolution: {integrity: sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==} resolution: {integrity: sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==}
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
@@ -3791,6 +3850,10 @@ packages:
/caniuse-lite@1.0.30001517: /caniuse-lite@1.0.30001517:
resolution: {integrity: sha512-Vdhm5S11DaFVLlyiKu4hiUTkpZu+y1KA/rZZqVQfOD5YdDT/eQKlkt7NaE0WGOFgX32diqt9MiP9CAiFeRklaA==} resolution: {integrity: sha512-Vdhm5S11DaFVLlyiKu4hiUTkpZu+y1KA/rZZqVQfOD5YdDT/eQKlkt7NaE0WGOFgX32diqt9MiP9CAiFeRklaA==}
/caniuse-lite@1.0.30001519:
resolution: {integrity: sha512-0QHgqR+Jv4bxHMp8kZ1Kn8CH55OikjKJ6JmKkZYP1F3D7w+lnFXF70nG5eNfsZS89jadi5Ywy5UCSKLAglIRkg==}
dev: true
/chai@4.3.7: /chai@4.3.7:
resolution: {integrity: sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==} resolution: {integrity: sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==}
engines: {node: '>=4'} engines: {node: '>=4'}
@@ -3916,10 +3979,24 @@ packages:
/color-name@1.1.4: /color-name@1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
/color-string@1.9.1:
resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==}
dependencies:
color-name: 1.1.4
simple-swizzle: 0.2.2
dev: false
/color2k@2.0.2: /color2k@2.0.2:
resolution: {integrity: sha512-kJhwH5nAwb34tmyuqq/lgjEKzlFXn1U99NlnB6Ws4qVaERcRUYeYP1cBw6BJ4vxaWStAUEef4WMr7WjOCnBt8w==} resolution: {integrity: sha512-kJhwH5nAwb34tmyuqq/lgjEKzlFXn1U99NlnB6Ws4qVaERcRUYeYP1cBw6BJ4vxaWStAUEef4WMr7WjOCnBt8w==}
dev: false dev: false
/color@3.2.1:
resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==}
dependencies:
color-convert: 1.9.3
color-string: 1.9.1
dev: false
/combined-stream@1.0.8: /combined-stream@1.0.8:
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
engines: {node: '>= 0.8'} engines: {node: '>= 0.8'}
@@ -4287,6 +4364,10 @@ packages:
/electron-to-chromium@1.4.465: /electron-to-chromium@1.4.465:
resolution: {integrity: sha512-XQcuHvEJRMU97UJ75e170mgcITZoz0lIyiaVjk6R+NMTJ8KBIvUHYd1779swgOppUlzxR+JsLpq59PumaXS1jQ==} resolution: {integrity: sha512-XQcuHvEJRMU97UJ75e170mgcITZoz0lIyiaVjk6R+NMTJ8KBIvUHYd1779swgOppUlzxR+JsLpq59PumaXS1jQ==}
/electron-to-chromium@1.4.482:
resolution: {integrity: sha512-h+UqpfmEr1Qkk0zp7ej/jid7CXoq4m4QzW6wNTb0ELJ/BZCpA4wgUylBIMGCe621tnr4l5VmoHjdoSx2lbnNJA==}
dev: true
/emoji-regex@10.2.1: /emoji-regex@10.2.1:
resolution: {integrity: sha512-97g6QgOk8zlDRdgq1WxwgTMgEWGVAQvB5Fdpgc1MkNy56la5SKP9GsMXKDOdqwn90/41a8yPwIGk1Y6WVbeMQA==} resolution: {integrity: sha512-97g6QgOk8zlDRdgq1WxwgTMgEWGVAQvB5Fdpgc1MkNy56la5SKP9GsMXKDOdqwn90/41a8yPwIGk1Y6WVbeMQA==}
dev: false dev: false
@@ -5069,6 +5150,16 @@ packages:
tslib: 2.6.0 tslib: 2.6.0
dev: false dev: false
/follow-redirects@1.15.2:
resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==}
engines: {node: '>=4.0'}
peerDependencies:
debug: '*'
peerDependenciesMeta:
debug:
optional: true
dev: false
/for-each@0.3.3: /for-each@0.3.3:
resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
dependencies: dependencies:
@@ -5087,6 +5178,15 @@ packages:
mime-types: 2.1.35 mime-types: 2.1.35
dev: false dev: false
/form-data@4.0.0:
resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==}
engines: {node: '>= 6'}
dependencies:
asynckit: 0.4.0
combined-stream: 1.0.8
mime-types: 2.1.35
dev: false
/format@0.2.2: /format@0.2.2:
resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==}
engines: {node: '>=0.4.x'} engines: {node: '>=0.4.x'}
@@ -5210,6 +5310,10 @@ packages:
dependencies: dependencies:
resolve-pkg-maps: 1.0.0 resolve-pkg-maps: 1.0.0
/github-buttons@2.27.0:
resolution: {integrity: sha512-PmfRMI2Rttg/2jDfKBeSl621sEznrsKF019SuoLdoNlO7qRUZaOyEI5Li4uW+79pVqnDtKfIEVuHTIJ5lgy64w==}
dev: false
/glob-parent@5.1.2: /glob-parent@5.1.2:
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
engines: {node: '>= 6'} engines: {node: '>= 6'}
@@ -5536,6 +5640,10 @@ packages:
resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==}
dev: false dev: false
/is-arrayish@0.3.2:
resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==}
dev: false
/is-bigint@1.0.4: /is-bigint@1.0.4:
resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==}
dependencies: dependencies:
@@ -5748,7 +5856,7 @@ packages:
resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==}
engines: {node: '>= 10.13.0'} engines: {node: '>= 10.13.0'}
dependencies: dependencies:
'@types/node': 18.16.0 '@types/node': 18.17.1
merge-stream: 2.0.0 merge-stream: 2.0.0
supports-color: 8.1.1 supports-color: 8.1.1
dev: true dev: true
@@ -5910,6 +6018,10 @@ packages:
resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==}
dev: false dev: false
/lodash.curry@4.1.1:
resolution: {integrity: sha512-/u14pXGviLaweY5JI0IUzgzF2J6Ne8INyzAZjImcryjgkZ+ebruBxy2/JaOOkTqScddcYtakjhSaeemV8lR0tA==}
dev: false
/lodash.merge@4.6.2: /lodash.merge@4.6.2:
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
dev: true dev: true
@@ -6387,19 +6499,17 @@ packages:
is-wsl: 2.2.0 is-wsl: 2.2.0
dev: true dev: true
/openai@4.0.0-beta.2: /openai@4.0.0-beta.7:
resolution: {integrity: sha512-zTuAxBFe5nSO7LngbV+/O0udtgHWfXb2lFei8/sDY4GB5cOdnrRoSOtiyUfV65ANdvlI4F75oYZX7w067cxj3w==} resolution: {integrity: sha512-jHjwvpMuGkNxiQ3erwLZsOvPEhcVrMtwtfNeYmGCjhbdB+oStVw/7pIhIPkualu8rlhLwgMR7awknIaN3IQcOA==}
dependencies: dependencies:
'@types/node': 18.16.0 '@types/node': 18.16.0
'@types/node-fetch': 2.6.4 '@types/node-fetch': 2.6.4
'@types/qs': 6.9.7
abort-controller: 3.0.0 abort-controller: 3.0.0
agentkeepalive: 4.3.0 agentkeepalive: 4.3.0
digest-fetch: 1.3.0 digest-fetch: 1.3.0
form-data-encoder: 1.7.2 form-data-encoder: 1.7.2
formdata-node: 4.4.1 formdata-node: 4.4.1
node-fetch: 2.6.12 node-fetch: 2.6.12
qs: 6.11.2
transitivePeerDependencies: transitivePeerDependencies:
- encoding - encoding
- supports-color - supports-color
@@ -6729,12 +6839,22 @@ packages:
resolution: {integrity: sha512-VdlZoocy5lCP0c/t66xAfclglEapXPCIVhqqJRncYpvbCgImF0w67aPKfbqUMr72tO2k5q0TdTZwCLjPTI6C9g==} resolution: {integrity: sha512-VdlZoocy5lCP0c/t66xAfclglEapXPCIVhqqJRncYpvbCgImF0w67aPKfbqUMr72tO2k5q0TdTZwCLjPTI6C9g==}
dev: false dev: false
/posthog-js@1.68.4: /posthog-js@1.75.3:
resolution: {integrity: sha512-rHk4uk99nvWiDTU7P2mFdEfzFR6km0hvOpCR3tm/+F7kCJKs7QDkMblOZZHZultxM4wSNyB4neeohmnHjKYUhQ==} resolution: {integrity: sha512-q5xP4R/Tx8E6H0goZQjY+URMLATFiYXc2raHA+31aNvpBs118fPTmExa4RK6MgRZDFhBiMUBZNT6aj7dM3SyUQ==}
dependencies: dependencies:
fflate: 0.4.8 fflate: 0.4.8
dev: false dev: false
/posthog-node@3.1.1:
resolution: {integrity: sha512-OUSYcnLHbzvY/dxNsbUGoYuTZz5XNx48BkfiCkOIJZMFvot5VPQ0KWEjX+kzYxEwHeXbjW9plqsOVcYCYfidgg==}
engines: {node: '>=15.0.0'}
dependencies:
axios: 0.27.2
rusha: 0.8.14
transitivePeerDependencies:
- debug
dev: false
/preact-render-to-string@5.2.6(preact@10.16.0): /preact-render-to-string@5.2.6(preact@10.16.0):
resolution: {integrity: sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==} resolution: {integrity: sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==}
peerDependencies: peerDependencies:
@@ -6832,13 +6952,6 @@ packages:
side-channel: 1.0.4 side-channel: 1.0.4
dev: false 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: /queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
dev: true dev: true
@@ -6875,6 +6988,18 @@ packages:
webpack: 5.88.2 webpack: 5.88.2
dev: true dev: true
/react-base16-styling@0.9.1:
resolution: {integrity: sha512-1s0CY1zRBOQ5M3T61wetEpvQmsYSNtWEcdYzyZNxKa8t7oDvaOn9d21xrGezGAHFWLM7SHcktPuPTrvoqxSfKw==}
dependencies:
'@babel/runtime': 7.22.6
'@types/base16': 1.0.2
'@types/lodash': 4.14.195
base16: 1.0.0
color: 3.2.1
csstype: 3.1.2
lodash.curry: 4.1.1
dev: false
/react-clientside-effect@1.2.6(react@18.2.0): /react-clientside-effect@1.2.6(react@18.2.0):
resolution: {integrity: sha512-XGGGRQAKY+q25Lz9a/4EPqom7WRjz3z9R2k4jhVKA/puQFH/5Nt27vFZYql4m4NVNdUvX8PS3O7r/Zzm7cjUlg==} resolution: {integrity: sha512-XGGGRQAKY+q25Lz9a/4EPqom7WRjz3z9R2k4jhVKA/puQFH/5Nt27vFZYql4m4NVNdUvX8PS3O7r/Zzm7cjUlg==}
peerDependencies: peerDependencies:
@@ -6934,6 +7059,15 @@ packages:
use-sidecar: 1.1.2(@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 dev: false
/react-github-btn@1.4.0(react@18.2.0):
resolution: {integrity: sha512-lV4FYClAfjWnBfv0iNlJUGhamDgIq6TayD0kPZED6VzHWdpcHmPfsYOZ/CFwLfPv4Zp+F4m8QKTj0oy2HjiGXg==}
peerDependencies:
react: '>=16.3.0'
dependencies:
github-buttons: 2.27.0
react: 18.2.0
dev: false
/react-icons@4.10.1(react@18.2.0): /react-icons@4.10.1(react@18.2.0):
resolution: {integrity: sha512-/ngzDP/77tlCfqthiiGNZeYFACw85fUjZtLbedmJ5DTlNDIwETxhwBzdOJ21zj4iJdvc0J3y7yOsX3PpxAJzrw==} resolution: {integrity: sha512-/ngzDP/77tlCfqthiiGNZeYFACw85fUjZtLbedmJ5DTlNDIwETxhwBzdOJ21zj4iJdvc0J3y7yOsX3PpxAJzrw==}
peerDependencies: peerDependencies:
@@ -6949,6 +7083,19 @@ packages:
resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==}
dev: true dev: true
/react-json-tree@0.18.0(@types/react@18.2.6)(react@18.2.0):
resolution: {integrity: sha512-Qe6HKSXrr++n9Y31nkRJ3XvQMATISpqigH1vEKhLwB56+nk5thTP0ITThpjxY6ZG/ubpVq/aEHIcyLP/OPHxeA==}
peerDependencies:
'@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0
react: ^16.8.0 || ^17.0.0 || ^18.0.0
dependencies:
'@babel/runtime': 7.22.6
'@types/lodash': 4.14.195
'@types/react': 18.2.6
react: 18.2.0
react-base16-styling: 0.9.1
dev: false
/react-remove-scroll-bar@2.3.4(@types/react@18.2.6)(react@18.2.0): /react-remove-scroll-bar@2.3.4(@types/react@18.2.6)(react@18.2.0):
resolution: {integrity: sha512-63C4YQBUt0m6ALadE9XV56hV8BgJWDmmTPY758iIJjfQKt2nYwoUrPk0LXRXcB/yIj82T1/Ixfdpdk68LwIB0A==} resolution: {integrity: sha512-63C4YQBUt0m6ALadE9XV56hV8BgJWDmmTPY758iIJjfQKt2nYwoUrPk0LXRXcB/yIj82T1/Ixfdpdk68LwIB0A==}
engines: {node: '>=10'} engines: {node: '>=10'}
@@ -7204,6 +7351,10 @@ packages:
queue-microtask: 1.2.3 queue-microtask: 1.2.3
dev: true dev: true
/rusha@0.8.14:
resolution: {integrity: sha512-cLgakCUf6PedEu15t8kbsjnwIFFR2D4RfL+W3iWFJ4iac7z4B0ZI8fxy4R3J956kAI68HclCFGL8MPoUVC3qVA==}
dev: false
/rxjs@7.8.1: /rxjs@7.8.1:
resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==}
dependencies: dependencies:
@@ -7360,6 +7511,12 @@ packages:
resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
dev: true dev: true
/simple-swizzle@0.2.2:
resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==}
dependencies:
is-arrayish: 0.3.2
dev: false
/slash@3.0.0: /slash@3.0.0:
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
engines: {node: '>=8'} engines: {node: '>=8'}
@@ -7655,12 +7812,12 @@ packages:
jest-worker: 27.5.1 jest-worker: 27.5.1
schema-utils: 3.3.0 schema-utils: 3.3.0
serialize-javascript: 6.0.1 serialize-javascript: 6.0.1
terser: 5.19.1 terser: 5.19.2
webpack: 5.88.2 webpack: 5.88.2
dev: true dev: true
/terser@5.19.1: /terser@5.19.2:
resolution: {integrity: sha512-27hxBUVdV6GoNg1pKQ7Z5cbR6V9txPVyBA+FQw3BaZ1Wuzvztce5p156DaP0NVZNrMZZ+6iG9Syf7WgMNKDg2Q==} resolution: {integrity: sha512-qC5+dmecKJA4cpYxRa5aVkKehYsQKc+AHeKl0Oe62aYjBL8ZA33tTljktDHJSaxxMnbI5ZYw+o/S2DxxLu8OfA==}
engines: {node: '>=10'} engines: {node: '>=10'}
hasBin: true hasBin: true
dependencies: dependencies:
@@ -7939,6 +8096,17 @@ packages:
engines: {node: '>=8'} engines: {node: '>=8'}
dev: true dev: true
/update-browserslist-db@1.0.11(browserslist@4.21.10):
resolution: {integrity: sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==}
hasBin: true
peerDependencies:
browserslist: '>= 4.21.0'
dependencies:
browserslist: 4.21.10
escalade: 3.1.1
picocolors: 1.0.0
dev: true
/update-browserslist-db@1.0.11(browserslist@4.21.9): /update-browserslist-db@1.0.11(browserslist@4.21.9):
resolution: {integrity: sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==} resolution: {integrity: sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==}
hasBin: true hasBin: true
@@ -8259,7 +8427,7 @@ packages:
'@webassemblyjs/wasm-parser': 1.11.6 '@webassemblyjs/wasm-parser': 1.11.6
acorn: 8.10.0 acorn: 8.10.0
acorn-import-assertions: 1.9.0(acorn@8.10.0) acorn-import-assertions: 1.9.0(acorn@8.10.0)
browserslist: 4.21.9 browserslist: 4.21.10
chrome-trace-event: 1.0.3 chrome-trace-event: 1.0.3
enhanced-resolve: 5.15.0 enhanced-resolve: 5.15.0
es-module-lexer: 1.3.0 es-module-lexer: 1.3.0

View File

@@ -0,0 +1,5 @@
-- CreateEnum
CREATE TYPE "UserRole" AS ENUM ('ADMIN', 'USER');
-- AlterTable
ALTER TABLE "User" ADD COLUMN "role" "UserRole" NOT NULL DEFAULT 'USER';

View File

@@ -249,12 +249,20 @@ model Session {
user User @relation(fields: [userId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade)
} }
enum UserRole {
ADMIN
USER
}
model User { model User {
id String @id @default(uuid()) @db.Uuid id String @id @default(uuid()) @db.Uuid
name String? name String?
email String? @unique email String? @unique
emailVerified DateTime? emailVerified DateTime?
image String? image String?
role UserRole @default(USER)
accounts Account[] accounts Account[]
sessions Session[] sessions Session[]
organizationUsers OrganizationUser[] organizationUsers OrganizationUser[]

View File

@@ -22,8 +22,8 @@ export const ModelSearch = (props: {
const [containerRef, containerDimensions] = useElementDimensions(); const [containerRef, containerDimensions] = useElementDimensions();
return ( return (
<VStack ref={containerRef as LegacyRef<HTMLDivElement>} w="full"> <VStack ref={containerRef as LegacyRef<HTMLDivElement>} w="full" fontFamily="inconsolata">
<Text>Browse Models</Text> <Text fontWeight="bold">Browse Models</Text>
<Select<ProviderModel> <Select<ProviderModel>
styles={{ control: (provided) => ({ ...provided, width: containerDimensions?.width }) }} styles={{ control: (provided) => ({ ...provided, width: containerDimensions?.width }) }}
getOptionLabel={(data) => modelLabel(data.provider, data.model)} getOptionLabel={(data) => modelLabel(data.provider, data.model)}

View File

@@ -23,16 +23,24 @@ export const ModelStatsCard = ({
{label} {label}
</Text> </Text>
<VStack w="full" spacing={6} bgColor="gray.100" p={4} borderRadius={4}> <VStack
w="full"
spacing={6}
borderWidth={1}
borderColor="gray.300"
p={4}
borderRadius={8}
fontFamily="inconsolata"
>
<HStack w="full" align="flex-start"> <HStack w="full" align="flex-start">
<Text flex={1} fontSize="lg"> <VStack flex={1} fontSize="lg" alignItems="flex-start">
<Text as="span" color="gray.600">
{model.provider} /{" "}
</Text>
<Text as="span" fontWeight="bold" color="gray.900"> <Text as="span" fontWeight="bold" color="gray.900">
{model.name} {model.name}
</Text> </Text>
</Text> <Text as="span" color="gray.600" fontSize="sm">
Provider: {model.provider}
</Text>
</VStack>
<Link <Link
href={model.learnMoreUrl} href={model.learnMoreUrl}
isExternal isExternal

View File

@@ -1,19 +0,0 @@
import { type StackProps, VStack } from "@chakra-ui/react";
import { CellOptions } from "./CellOptions";
export const CellContent = ({
hardRefetch,
hardRefetching,
children,
...props
}: {
hardRefetch: () => void;
hardRefetching: boolean;
} & StackProps) => (
<VStack w="full" alignItems="flex-start" {...props}>
<CellOptions refetchingOutput={hardRefetching} refetchOutput={hardRefetch} />
<VStack w="full" alignItems="flex-start" maxH={500} overflowY="auto">
{children}
</VStack>
</VStack>
);

View File

@@ -1,37 +0,0 @@
import { Button, HStack, Icon, Spinner, Tooltip } from "@chakra-ui/react";
import { BsArrowClockwise } from "react-icons/bs";
import { useExperimentAccess } from "~/utils/hooks";
export const CellOptions = ({
refetchingOutput,
refetchOutput,
}: {
refetchingOutput: boolean;
refetchOutput: () => void;
}) => {
const { canModify } = useExperimentAccess();
return (
<HStack justifyContent="flex-end" w="full">
{canModify && (
<Tooltip label="Refetch output" aria-label="refetch output">
<Button
size="xs"
w={4}
h={4}
py={4}
px={4}
minW={0}
borderRadius={8}
color="gray.500"
variant="ghost"
cursor="pointer"
onClick={refetchOutput}
aria-label="refetch output"
>
<Icon as={refetchingOutput ? Spinner : BsArrowClockwise} boxSize={4} />
</Button>
</Tooltip>
)}
</HStack>
);
};

View File

@@ -1,17 +1,17 @@
import { api } from "~/utils/api"; import { api } from "~/utils/api";
import { type PromptVariant, type Scenario } from "../types"; import { type PromptVariant, type Scenario } from "../types";
import { Text, VStack } from "@chakra-ui/react"; import { type StackProps, Text, VStack } from "@chakra-ui/react";
import { useExperiment, useHandledAsyncCallback } from "~/utils/hooks"; import { useExperiment, useHandledAsyncCallback } from "~/utils/hooks";
import SyntaxHighlighter from "react-syntax-highlighter"; import SyntaxHighlighter from "react-syntax-highlighter";
import { docco } from "react-syntax-highlighter/dist/cjs/styles/hljs"; import { docco } from "react-syntax-highlighter/dist/cjs/styles/hljs";
import stringify from "json-stringify-pretty-compact"; import stringify from "json-stringify-pretty-compact";
import { type ReactElement, useState, useEffect, Fragment } from "react"; import { type ReactElement, useState, useEffect, Fragment, useCallback } from "react";
import useSocket from "~/utils/useSocket"; import useSocket from "~/utils/useSocket";
import { OutputStats } from "./OutputStats"; import { OutputStats } from "./OutputStats";
import { RetryCountdown } from "./RetryCountdown"; import { RetryCountdown } from "./RetryCountdown";
import frontendModelProviders from "~/modelProviders/frontendModelProviders"; import frontendModelProviders from "~/modelProviders/frontendModelProviders";
import { ResponseLog } from "./ResponseLog"; import { ResponseLog } from "./ResponseLog";
import { CellContent } from "./CellContent"; import { CellOptions } from "./TopActions";
const WAITING_MESSAGE_INTERVAL = 20000; const WAITING_MESSAGE_INTERVAL = 20000;
@@ -72,37 +72,49 @@ export default function OutputCell({
// TODO: disconnect from socket if we're not streaming anymore // TODO: disconnect from socket if we're not streaming anymore
const streamedMessage = useSocket<OutputSchema>(cell?.id); const streamedMessage = useSocket<OutputSchema>(cell?.id);
const mostRecentResponse = cell?.modelResponses[cell.modelResponses.length - 1];
const CellWrapper = useCallback(
({ children, ...props }: StackProps) => (
<VStack w="full" alignItems="flex-start" {...props} px={2} py={2} h="100%">
{cell && (
<CellOptions refetchingOutput={hardRefetching} refetchOutput={hardRefetch} cell={cell} />
)}
<VStack w="full" alignItems="flex-start" maxH={500} overflowY="auto" flex={1}>
{children}
</VStack>
{mostRecentResponse && (
<OutputStats modelResponse={mostRecentResponse} scenario={scenario} />
)}
</VStack>
),
[hardRefetching, hardRefetch, mostRecentResponse, scenario, cell],
);
if (!vars) return null; if (!vars) return null;
if (!cell && !fetchingOutput) if (!cell && !fetchingOutput)
return ( return (
<CellContent hardRefetching={hardRefetching} hardRefetch={hardRefetch}> <CellWrapper>
<Text color="gray.500">Error retrieving output</Text> <Text color="gray.500">Error retrieving output</Text>
</CellContent> </CellWrapper>
); );
if (cell && cell.errorMessage) { if (cell && cell.errorMessage) {
return ( return (
<CellContent hardRefetching={hardRefetching} hardRefetch={hardRefetch}> <CellWrapper>
<Text color="red.500">{cell.errorMessage}</Text> <Text color="red.500">{cell.errorMessage}</Text>
</CellContent> </CellWrapper>
); );
} }
if (disabledReason) return <Text color="gray.500">{disabledReason}</Text>; if (disabledReason) return <Text color="gray.500">{disabledReason}</Text>;
const mostRecentResponse = cell?.modelResponses[cell.modelResponses.length - 1];
const showLogs = !streamedMessage && !mostRecentResponse?.output; const showLogs = !streamedMessage && !mostRecentResponse?.output;
if (showLogs) if (showLogs)
return ( return (
<CellContent <CellWrapper alignItems="flex-start" fontFamily="inconsolata, monospace" spacing={0}>
hardRefetching={hardRefetching}
hardRefetch={hardRefetch}
alignItems="flex-start"
fontFamily="inconsolata, monospace"
spacing={0}
>
{cell?.jobQueuedAt && <ResponseLog time={cell.jobQueuedAt} title="Job queued" />} {cell?.jobQueuedAt && <ResponseLog time={cell.jobQueuedAt} title="Job queued" />}
{cell?.jobStartedAt && <ResponseLog time={cell.jobStartedAt} title="Job started" />} {cell?.jobStartedAt && <ResponseLog time={cell.jobStartedAt} title="Job started" />}
{cell?.modelResponses?.map((response) => { {cell?.modelResponses?.map((response) => {
@@ -124,9 +136,13 @@ export default function OutputCell({
Array.from({ length: numWaitingMessages }, (_, i) => ( Array.from({ length: numWaitingMessages }, (_, i) => (
<ResponseLog <ResponseLog
key={`waiting-${i}`} key={`waiting-${i}`}
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion time={
time={new Date(response.requestedAt!.getTime() + i * WAITING_MESSAGE_INTERVAL)} new Date(
title="Waiting for response" (response.requestedAt?.getTime?.() ?? 0) +
(i + 1) * WAITING_MESSAGE_INTERVAL,
)
}
title="Waiting for response..."
/> />
))} ))}
{response.receivedAt && ( {response.receivedAt && (
@@ -144,7 +160,7 @@ export default function OutputCell({
{mostRecentResponse?.retryTime && ( {mostRecentResponse?.retryTime && (
<RetryCountdown retryTime={mostRecentResponse.retryTime} /> <RetryCountdown retryTime={mostRecentResponse.retryTime} />
)} )}
</CellContent> </CellWrapper>
); );
const normalizedOutput = mostRecentResponse?.output const normalizedOutput = mostRecentResponse?.output
@@ -155,50 +171,27 @@ export default function OutputCell({
if (mostRecentResponse?.output && normalizedOutput?.type === "json") { if (mostRecentResponse?.output && normalizedOutput?.type === "json") {
return ( return (
<VStack <CellWrapper>
w="100%" <SyntaxHighlighter
h="100%" customStyle={{ overflowX: "unset", width: "100%", flex: 1 }}
fontSize="xs" language="json"
flexWrap="wrap" style={docco}
overflowX="hidden" lineProps={{
justifyContent="space-between" style: { wordBreak: "break-all", whiteSpace: "pre-wrap" },
> }}
<CellContent wrapLines
hardRefetching={hardRefetching}
hardRefetch={hardRefetch}
w="full"
flex={1}
spacing={0}
> >
<SyntaxHighlighter {stringify(normalizedOutput.value, { maxLength: 40 })}
customStyle={{ overflowX: "unset", width: "100%", flex: 1 }} </SyntaxHighlighter>
language="json" </CellWrapper>
style={docco}
lineProps={{
style: { wordBreak: "break-all", whiteSpace: "pre-wrap" },
}}
wrapLines
>
{stringify(normalizedOutput.value, { maxLength: 40 })}
</SyntaxHighlighter>
</CellContent>
<OutputStats modelResponse={mostRecentResponse} scenario={scenario} />
</VStack>
); );
} }
const contentToDisplay = (normalizedOutput?.type === "text" && normalizedOutput.value) || ""; const contentToDisplay = (normalizedOutput?.type === "text" && normalizedOutput.value) || "";
return ( return (
<VStack w="100%" h="100%" justifyContent="space-between" whiteSpace="pre-wrap"> <CellWrapper>
<VStack w="full" alignItems="flex-start" spacing={0}> <Text>{contentToDisplay}</Text>
<CellContent hardRefetching={hardRefetching} hardRefetch={hardRefetch}> </CellWrapper>
<Text>{contentToDisplay}</Text>
</CellContent>
</VStack>
{mostRecentResponse?.output && (
<OutputStats modelResponse={mostRecentResponse} scenario={scenario} />
)}
</VStack>
); );
} }

View File

@@ -0,0 +1,36 @@
import {
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalHeader,
ModalOverlay,
type UseDisclosureReturn,
} from "@chakra-ui/react";
import { type RouterOutputs } from "~/utils/api";
import { JSONTree } from "react-json-tree";
export default function ExpandedModal(props: {
cell: NonNullable<RouterOutputs["scenarioVariantCells"]["get"]>;
disclosure: UseDisclosureReturn;
}) {
return (
<Modal isOpen={props.disclosure.isOpen} onClose={props.disclosure.onClose} size="2xl">
<ModalOverlay />
<ModalContent>
<ModalHeader>Prompt</ModalHeader>
<ModalCloseButton />
<ModalBody>
<JSONTree
data={props.cell.prompt}
invertTheme={true}
theme="chalk"
shouldExpandNodeInitially={() => true}
getItemString={() => ""}
hideRoot
/>
</ModalBody>
</ModalContent>
</Modal>
);
}

View File

@@ -0,0 +1,53 @@
import { HStack, Icon, IconButton, Spinner, Tooltip, useDisclosure } from "@chakra-ui/react";
import { BsArrowClockwise, BsInfoCircle } from "react-icons/bs";
import { useExperimentAccess } from "~/utils/hooks";
import ExpandedModal from "./PromptModal";
import { type RouterOutputs } from "~/utils/api";
export const CellOptions = ({
cell,
refetchingOutput,
refetchOutput,
}: {
cell: RouterOutputs["scenarioVariantCells"]["get"];
refetchingOutput: boolean;
refetchOutput: () => void;
}) => {
const { canModify } = useExperimentAccess();
const modalDisclosure = useDisclosure();
return (
<HStack justifyContent="flex-end" w="full">
{cell && (
<>
<Tooltip label="See Prompt">
<IconButton
aria-label="See Prompt"
icon={<Icon as={BsInfoCircle} boxSize={4} />}
onClick={modalDisclosure.onOpen}
size="xs"
colorScheme="gray"
color="gray.500"
variant="ghost"
/>
</Tooltip>
<ExpandedModal cell={cell} disclosure={modalDisclosure} />
</>
)}
{canModify && (
<Tooltip label="Refetch output">
<IconButton
size="xs"
color="gray.500"
variant="ghost"
cursor="pointer"
onClick={refetchOutput}
aria-label="refetch output"
icon={<Icon as={refetchingOutput ? Spinner : BsArrowClockwise} boxSize={4} />}
/>
</Tooltip>
)}
</HStack>
);
};

View File

@@ -1,9 +1,8 @@
import { useEffect, type DragEvent } from "react";
import { api } from "~/utils/api";
import { isEqual } from "lodash-es"; import { isEqual } from "lodash-es";
import { type Scenario } from "./types"; import { useEffect, useState, type DragEvent } from "react";
import { api } from "~/utils/api";
import { useExperiment, useExperimentAccess, useHandledAsyncCallback } from "~/utils/hooks"; import { useExperiment, useExperimentAccess, useHandledAsyncCallback } from "~/utils/hooks";
import { useState } from "react"; import { type Scenario } from "./types";
import { import {
Box, Box,
@@ -12,14 +11,12 @@ import {
Icon, Icon,
IconButton, IconButton,
Spinner, Spinner,
Stack, Text,
Tooltip, Tooltip,
VStack, VStack,
Text,
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import { cellPadding } from "../constants";
import { BsArrowsAngleExpand, BsX } from "react-icons/bs"; import { BsArrowsAngleExpand, BsX } from "react-icons/bs";
import { RiDraggable } from "react-icons/ri"; import { cellPadding } from "../constants";
import { FloatingLabelInput } from "./FloatingLabelInput"; import { FloatingLabelInput } from "./FloatingLabelInput";
import { ScenarioEditorModal } from "./ScenarioEditorModal"; import { ScenarioEditorModal } from "./ScenarioEditorModal";
@@ -115,60 +112,44 @@ export default function ScenarioEditor({
onDrop={onReorder} onDrop={onReorder}
backgroundColor={isDragTarget ? "gray.100" : "transparent"} backgroundColor={isDragTarget ? "gray.100" : "transparent"}
> >
{canModify && props.canHide && (
<Stack
alignSelf="flex-start"
opacity={props.hovered ? 1 : 0}
spacing={0}
ml={-cellPadding.x}
>
<Tooltip label="Hide scenario" hasArrow>
{/* for some reason the tooltip can't position itself properly relative to the icon without the wrapping box */}
<Button
variant="unstyled"
color="gray.400"
height="unset"
width="unset"
minW="unset"
onClick={onHide}
_hover={{
color: "gray.800",
cursor: "pointer",
}}
>
<Icon as={hidingInProgress ? Spinner : BsX} boxSize={hidingInProgress ? 4 : 6} />
</Button>
</Tooltip>
<Icon
as={RiDraggable}
boxSize={6}
color="gray.400"
_hover={{ color: "gray.800", cursor: "pointer" }}
/>
</Stack>
)}
{variableLabels.length === 0 ? ( {variableLabels.length === 0 ? (
<Box color="gray.500"> <Box color="gray.500">
{vars.data ? "No scenario variables configured" : "Loading..."} {vars.data ? "No scenario variables configured" : "Loading..."}
</Box> </Box>
) : ( ) : (
<VStack spacing={4} flex={1} py={2}> <VStack spacing={4} flex={1} py={2}>
<HStack justifyContent="space-between" w="100%"> <HStack justifyContent="space-between" w="100%" align="center" spacing={0}>
<Text color="gray.500">Scenario</Text> <Text flex={1}>Scenario</Text>
<IconButton <Tooltip label="Expand" hasArrow>
className="fullscreen-toggle" <IconButton
aria-label="Maximize" aria-label="Expand"
icon={<BsArrowsAngleExpand />} icon={<Icon as={BsArrowsAngleExpand} boxSize={3} />}
onClick={() => setScenarioEditorModalOpen(true)} onClick={() => setScenarioEditorModalOpen(true)}
boxSize={6} size="xs"
borderRadius={4} colorScheme="gray"
p={1.5} color="gray.500"
minW={0} variant="ghost"
colorScheme="gray" />
color="gray.500" </Tooltip>
variant="ghost" {canModify && props.canHide && (
/> <Tooltip label="Delete" hasArrow>
<IconButton
aria-label="Delete"
icon={
<Icon
as={hidingInProgress ? Spinner : BsX}
boxSize={hidingInProgress ? 4 : 6}
/>
}
onClick={onHide}
size="xs"
display="flex"
colorScheme="gray"
color="gray.500"
variant="ghost"
/>
</Tooltip>
)}
</HStack> </HStack>
{variableLabels.map((key) => { {variableLabels.map((key) => {
const value = values[key] ?? ""; const value = values[key] ?? "";

View File

@@ -1,7 +1,6 @@
import { import {
Button, Button,
HStack, HStack,
Icon,
Modal, Modal,
ModalBody, ModalBody,
ModalCloseButton, ModalCloseButton,
@@ -14,7 +13,6 @@ import {
VStack, VStack,
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { BsFileTextFill } from "react-icons/bs";
import { isEqual } from "lodash-es"; import { isEqual } from "lodash-es";
import { api } from "~/utils/api"; import { api } from "~/utils/api";
@@ -60,8 +58,6 @@ export const ScenarioEditorModal = ({
await utils.scenarios.list.invalidate(); await utils.scenarios.list.invalidate();
}, [mutation, values]); }, [mutation, values]);
console.log("scenario", scenario);
const vars = api.templateVars.list.useQuery({ experimentId: experiment.data?.id ?? "" }); const vars = api.templateVars.list.useQuery({ experimentId: experiment.data?.id ?? "" });
const variableLabels = vars.data?.map((v) => v.label) ?? []; const variableLabels = vars.data?.map((v) => v.label) ?? [];
@@ -73,12 +69,7 @@ export const ScenarioEditorModal = ({
> >
<ModalOverlay /> <ModalOverlay />
<ModalContent w={1200}> <ModalContent w={1200}>
<ModalHeader> <ModalHeader />
<HStack>
<Icon as={BsFileTextFill} />
<Text>Scenario</Text>
</HStack>
</ModalHeader>
<ModalCloseButton /> <ModalCloseButton />
<ModalBody maxW="unset"> <ModalBody maxW="unset">
<VStack spacing={8}> <VStack spacing={8}>

View File

@@ -1,6 +1,5 @@
import { Box, GridItem } from "@chakra-ui/react"; import { GridItem } from "@chakra-ui/react";
import React, { useState } from "react"; import React, { useState } from "react";
import { cellPadding } from "../constants";
import OutputCell from "./OutputCell/OutputCell"; import OutputCell from "./OutputCell/OutputCell";
import ScenarioEditor from "./ScenarioEditor"; import ScenarioEditor from "./ScenarioEditor";
import type { PromptVariant, Scenario } from "./types"; import type { PromptVariant, Scenario } from "./types";
@@ -39,9 +38,7 @@ const ScenarioRow = (props: {
colStart={i + 2} colStart={i + 2}
{...borders} {...borders}
> >
<Box h="100%" w="100%" px={cellPadding.x} py={cellPadding.y}> <OutputCell key={variant.id} scenario={props.scenario} variant={variant} />
<OutputCell key={variant.id} scenario={props.scenario} variant={variant} />
</Box>
</GridItem> </GridItem>
))} ))}
</> </>

View File

@@ -35,7 +35,7 @@ export default function OutputsTable({ experimentId }: { experimentId: string |
pb={24} pb={24}
pl={8} pl={8}
display="grid" display="grid"
gridTemplateColumns={`250px repeat(${variants.data.length}, minmax(300px, 1fr)) auto`} gridTemplateColumns={`250px repeat(${variants.data.length}, minmax(360px, 1fr)) auto`}
sx={{ sx={{
"> *": { "> *": {
borderColor: "gray.300", borderColor: "gray.300",

View File

@@ -97,7 +97,7 @@ export const RefinePromptModal = ({
<ModalCloseButton /> <ModalCloseButton />
<ModalBody maxW="unset"> <ModalBody maxW="unset">
<VStack spacing={8}> <VStack spacing={8}>
<VStack spacing={4}> <VStack spacing={4} w="full">
{Object.keys(refinementActions).length && ( {Object.keys(refinementActions).length && (
<> <>
<SimpleGrid columns={{ base: 1, md: 2 }} spacing={8}> <SimpleGrid columns={{ base: 1, md: 2 }} spacing={8}>

View File

@@ -29,6 +29,7 @@ export const env = createEnv({
client: { client: {
NEXT_PUBLIC_POSTHOG_KEY: z.string().optional(), NEXT_PUBLIC_POSTHOG_KEY: z.string().optional(),
NEXT_PUBLIC_SOCKET_URL: z.string().url().default("http://localhost:3318"), NEXT_PUBLIC_SOCKET_URL: z.string().url().default("http://localhost:3318"),
NEXT_PUBLIC_HOST: z.string().url().default("http://localhost:3000"),
}, },
/** /**
@@ -42,6 +43,7 @@ export const env = createEnv({
RESTRICT_PRISMA_LOGS: process.env.RESTRICT_PRISMA_LOGS, RESTRICT_PRISMA_LOGS: process.env.RESTRICT_PRISMA_LOGS,
NEXT_PUBLIC_POSTHOG_KEY: process.env.NEXT_PUBLIC_POSTHOG_KEY, NEXT_PUBLIC_POSTHOG_KEY: process.env.NEXT_PUBLIC_POSTHOG_KEY,
NEXT_PUBLIC_SOCKET_URL: process.env.NEXT_PUBLIC_SOCKET_URL, NEXT_PUBLIC_SOCKET_URL: process.env.NEXT_PUBLIC_SOCKET_URL,
NEXT_PUBLIC_HOST: process.env.NEXT_PUBLIC_HOST,
GITHUB_CLIENT_ID: process.env.GITHUB_CLIENT_ID, GITHUB_CLIENT_ID: process.env.GITHUB_CLIENT_ID,
GITHUB_CLIENT_SECRET: process.env.GITHUB_CLIENT_SECRET, GITHUB_CLIENT_SECRET: process.env.GITHUB_CLIENT_SECRET,
REPLICATE_API_TOKEN: process.env.REPLICATE_API_TOKEN, REPLICATE_API_TOKEN: process.env.REPLICATE_API_TOKEN,

View File

@@ -2,7 +2,7 @@
"type": "object", "type": "object",
"properties": { "properties": {
"model": { "model": {
"description": "The model that will complete your prompt.\nAs we improve Claude, we develop new versions of it that you can query.\nThis parameter controls which version of Claude answers your request.\nRight now we are offering two model families: Claude, and Claude Instant.\nYou can use them by setting model to \"claude-2\" or \"claude-instant-1\", respectively.\nSee models for additional details.\n", "description": "The model that will complete your prompt.",
"x-oaiTypeLabel": "string", "x-oaiTypeLabel": "string",
"type": "string", "type": "string",
"enum": [ "enum": [
@@ -13,116 +13,50 @@
] ]
}, },
"prompt": { "prompt": {
"description": "The prompt that you want Claude to complete.\n\nFor proper response generation you will need to format your prompt as follows:\n\\n\\nHuman: ${userQuestion}\\n\\nAssistant:\nSee our comments on prompts for more context.\n", "description": "The prompt that you want Claude to complete.\n\nFor proper response generation you will need to format your prompt as follows:\n\"\\n\\nHuman: all instructions for the assistant\\n\\nAssistant:\". The prompt string should begin with the characters \"Human:\" and end with \"Assistant:\".",
"default": "<|endoftext|>", "default": "<|endoftext|>",
"nullable": true, "example": "\\n\\nHuman: What is the correct translation of ${scenario.input}? I would like a long analysis followed by a short answer.\\n\\nAssistant:",
"oneOf": [ "type": "string"
{
"type": "string",
"default": "",
"example": "This is a test."
},
{
"type": "array",
"items": {
"type": "string",
"default": "",
"example": "This is a test."
}
},
{
"type": "array",
"minItems": 1,
"items": {
"type": "integer"
},
"example": "[1212, 318, 257, 1332, 13]"
},
{
"type": "array",
"minItems": 1,
"items": {
"type": "array",
"minItems": 1,
"items": {
"type": "integer"
}
},
"example": "[[1212, 318, 257, 1332, 13]]"
}
]
}, },
"max_tokens_to_sample": { "max_tokens_to_sample": {
"type": "integer", "type": "integer",
"minimum": 1, "minimum": 1,
"default": 256, "default": 256,
"example": 256,
"nullable": true, "nullable": true,
"description": "The maximum number of tokens to generate before stopping.\n\nNote that our models may stop before reaching this maximum. This parameter only specifies the absolute maximum number of tokens to generate.\n" "description": "The maximum number of tokens to generate before stopping."
}, },
"temperature": { "temperature": {
"type": "number", "type": "number",
"minimum": 0, "minimum": 0,
"maximum": 1, "maximum": 1,
"default": 1,
"example": 1,
"nullable": true, "nullable": true,
"description": "Amount of randomness injected into the response.\n\nDefaults to 1. Ranges from 0 to 1. Use temp closer to 0 for analytical / multiple choice, and closer to 1 for creative and generative tasks.\n" "description": "Amount of randomness injected into the response.\n\nDefaults to 1."
}, },
"top_p": { "top_p": {
"type": "number", "type": "number",
"minimum": 0, "minimum": 0,
"maximum": 1, "maximum": 1,
"default": 1,
"example": 1,
"nullable": true, "nullable": true,
"description": "Use nucleus sampling.\n\nIn nucleus sampling, we compute the cumulative distribution over all the options \nfor each subsequent token in decreasing probability order and cut it off once \nit reaches a particular probability specified by top_p. You should either alter temperature or top_p, but not both.\n" "description": "Use nucleus sampling.\n\nYou should either alter temperature or top_p, but not both.\n"
}, },
"top_k": { "top_k": {
"type": "number", "type": "number",
"minimum": 0, "minimum": 0,
"default": 5, "default": 5,
"example": 5,
"nullable": true, "nullable": true,
"description": "Only sample from the top K options for each subsequent token.\n\nUsed to remove \"long tail\" low probability responses. Learn more technical details here.\n" "description": "Only sample from the top K options for each subsequent token."
}, },
"stream": { "stream": {
"description": "Whether to incrementally stream the response using server-sent events.\nSee this guide to SSE events for details.type: boolean\n", "description": "Whether to incrementally stream the response using server-sent events.",
"type": "boolean",
"nullable": true, "nullable": true,
"default": false "default": false
}, },
"stop_sequences": { "stop_sequences": {
"description": "Sequences that will cause the model to stop generating completion text.\nOur models stop on \"\\n\\nHuman:\", and may include additional built-in stop sequences in the future. By providing the stop_sequences parameter, you may include additional strings that will cause the model to stop generating.\n", "description": "Sequences that will cause the model to stop generating completion text.\nBy default, our models stop on \"\\n\\nHuman:\".",
"default": null, "default": null,
"nullable": true, "nullable": true,
"oneOf": [ "type": "array"
{
"type": "string",
"default": "<|endoftext|>",
"example": "\n",
"nullable": true
},
{
"type": "array",
"minItems": 1,
"maxItems": 4,
"items": {
"type": "string",
"example": "[\"\\n\"]"
}
}
]
},
"metadata": {
"type": "object",
"properties": {
"user_id": {
"type": "string",
"example": "13803d75-b4b5-4c3e-b2a2-6f21399b021b",
"description": "An external identifier for the user who is associated with the request.\n\nThis should be a uuid, hash value, or other opaque identifier. Anthropic may use this id to help detect abuse. \nDo not include any identifying information such as name, email address, or phone number.\n"
}
},
"description": "An object describing metadata about the request.\n"
} }
}, },
"required": ["model", "prompt", "max_tokens_to_sample"] "required": ["model", "prompt", "max_tokens_to_sample"]

View File

@@ -15,6 +15,7 @@ const frontendModelProvider: FrontendModelProvider<SupportedModel, Completion> =
speed: "medium", speed: "medium",
provider: "anthropic", provider: "anthropic",
learnMoreUrl: "https://www.anthropic.com/product", learnMoreUrl: "https://www.anthropic.com/product",
apiDocsUrl: "https://docs.anthropic.com/claude/reference/complete_post",
}, },
"claude-instant-1.1": { "claude-instant-1.1": {
name: "Claude Instant 1.1", name: "Claude Instant 1.1",
@@ -24,6 +25,7 @@ const frontendModelProvider: FrontendModelProvider<SupportedModel, Completion> =
speed: "fast", speed: "fast",
provider: "anthropic", provider: "anthropic",
learnMoreUrl: "https://www.anthropic.com/product", learnMoreUrl: "https://www.anthropic.com/product",
apiDocsUrl: "https://docs.anthropic.com/claude/reference/complete_post",
}, },
}, },

View File

@@ -6,7 +6,7 @@ import {
} from "openai/resources/chat"; } from "openai/resources/chat";
import { countOpenAIChatTokens } from "~/utils/countTokens"; import { countOpenAIChatTokens } from "~/utils/countTokens";
import { type CompletionResponse } from "../types"; import { type CompletionResponse } from "../types";
import { omit } from "lodash-es"; import { isArray, isString, omit } from "lodash-es";
import { openai } from "~/server/utils/openai"; import { openai } from "~/server/utils/openai";
import { truthyFilter } from "~/utils/utils"; import { truthyFilter } from "~/utils/utils";
import { APIError } from "openai"; import { APIError } from "openai";
@@ -40,6 +40,8 @@ const mergeStreamedChunks = (
((choice.delta.function_call.arguments as string) ?? ""); ((choice.delta.function_call.arguments as string) ?? "");
} }
} else { } else {
// @ts-expect-error the types are correctly telling us that finish_reason
// could be null, but don't want to fix it right now.
choices.push({ ...omit(choice, "delta"), message: { role: "assistant", ...choice.delta } }); choices.push({ ...omit(choice, "delta"), message: { role: "assistant", ...choice.delta } });
} }
} }
@@ -64,6 +66,7 @@ export async function getCompletion(
try { try {
if (onStream) { if (onStream) {
console.log("got started");
const resp = await openai.chat.completions.create( const resp = await openai.chat.completions.create(
{ ...input, stream: true }, { ...input, stream: true },
{ {
@@ -71,9 +74,11 @@ export async function getCompletion(
}, },
); );
for await (const part of resp) { for await (const part of resp) {
console.log("got part", part);
finalCompletion = mergeStreamedChunks(finalCompletion, part); finalCompletion = mergeStreamedChunks(finalCompletion, part);
onStream(finalCompletion); onStream(finalCompletion);
} }
console.log("got final", finalCompletion);
if (!finalCompletion) { if (!finalCompletion) {
return { return {
type: "error", type: "error",
@@ -121,9 +126,17 @@ export async function getCompletion(
}; };
} catch (error: unknown) { } catch (error: unknown) {
if (error instanceof APIError) { if (error instanceof APIError) {
// The types from the sdk are wrong
const rawMessage = error.message as string | string[];
// If the message is not a string, stringify it
const message = isString(rawMessage)
? rawMessage
: isArray(rawMessage)
? rawMessage.map((m) => m.toString()).join("\n")
: (rawMessage as any).toString();
return { return {
type: "error", type: "error",
message: error.message, message,
autoRetry: error.status === 429 || error.status === 503, autoRetry: error.status === 429 || error.status === 503,
statusCode: error.status, statusCode: error.status,
}; };

View File

@@ -54,7 +54,7 @@ const modelProvider: ReplicateLlama2Provider = {
temperature: { temperature: {
type: "number", type: "number",
description: description:
"Adjusts randomness of outputs, greater than 1 is random and 0 is deterministic, 0.75 is a good starting value. (minimum: 0.01; maximum: 5)", "Adjusts randomness of outputs, 0.1 is a good starting value. (minimum: 0.01; maximum: 5)",
}, },
top_p: { top_p: {
type: "number", type: "number",

View File

@@ -21,6 +21,7 @@ export type Model = {
provider: SupportedProvider; provider: SupportedProvider;
description?: string; description?: string;
learnMoreUrl?: string; learnMoreUrl?: string;
apiDocsUrl?: string;
}; };
export type ProviderModel = { provider: z.infer<typeof ZodSupportedProvider>; model: string }; export type ProviderModel = { provider: z.infer<typeof ZodSupportedProvider>; model: string };

View File

@@ -3,12 +3,12 @@ import { SessionProvider } from "next-auth/react";
import { type AppType } from "next/app"; import { type AppType } from "next/app";
import { api } from "~/utils/api"; import { api } from "~/utils/api";
import Favicon from "~/components/Favicon"; import Favicon from "~/components/Favicon";
import "~/utils/analytics";
import Head from "next/head"; import Head from "next/head";
import { ChakraThemeProvider } from "~/theme/ChakraThemeProvider"; import { ChakraThemeProvider } from "~/theme/ChakraThemeProvider";
import { SyncAppStore } from "~/state/sync"; import { SyncAppStore } from "~/state/sync";
import NextAdapterApp from "next-query-params/app"; import NextAdapterApp from "next-query-params/app";
import { QueryParamProvider } from "use-query-params"; import { QueryParamProvider } from "use-query-params";
import { SessionIdentifier } from "~/utils/analytics/clientAnalytics";
const MyApp: AppType<{ session: Session | null }> = ({ const MyApp: AppType<{ session: Session | null }> = ({
Component, Component,
@@ -36,6 +36,7 @@ const MyApp: AppType<{ session: Session | null }> = ({
<SessionProvider session={session}> <SessionProvider session={session}>
<SyncAppStore /> <SyncAppStore />
<Favicon /> <Favicon />
<SessionIdentifier />
<ChakraThemeProvider> <ChakraThemeProvider>
<QueryParamProvider adapter={NextAdapterApp}> <QueryParamProvider adapter={NextAdapterApp}>
<Component {...pageProps} /> <Component {...pageProps} />

View File

@@ -9,7 +9,7 @@ const inconsolataRegularFontP = fetch(
new URL("../../../../public/fonts/Inconsolata_SemiExpanded-Medium.ttf", import.meta.url), new URL("../../../../public/fonts/Inconsolata_SemiExpanded-Medium.ttf", import.meta.url),
).then((res) => res.arrayBuffer()); ).then((res) => res.arrayBuffer());
const OgImage = async (req: NextApiRequest, res: NextApiResponse) => { const OgImage = async (req: NextApiRequest, _res: NextApiResponse) => {
// @ts-expect-error - nextUrl is not defined on NextApiRequest for some reason // @ts-expect-error - nextUrl is not defined on NextApiRequest for some reason
const searchParams = req.nextUrl?.searchParams as URLSearchParams; const searchParams = req.nextUrl?.searchParams as URLSearchParams;
const experimentLabel = searchParams.get("experimentLabel"); const experimentLabel = searchParams.get("experimentLabel");

View File

@@ -18,6 +18,7 @@ import {
VStack, VStack,
useInterval, useInterval,
Image, Image,
Flex,
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import { signIn, useSession } from "next-auth/react"; import { signIn, useSession } from "next-auth/react";
import Head from "next/head"; import Head from "next/head";
@@ -27,24 +28,45 @@ import UserMenu from "~/components/nav/UserMenu";
import { api } from "~/utils/api"; import { api } from "~/utils/api";
import dayjs from "~/utils/dayjs"; import dayjs from "~/utils/dayjs";
import { useHandledAsyncCallback } from "~/utils/hooks"; import { useHandledAsyncCallback } from "~/utils/hooks";
import GitHubButton from "react-github-btn";
const TopNavbar = () => ( const TopNavbar = () => (
<DarkMode> <HStack px={4} py={2} align="center" justify="center">
<GlobalStyle /> <HStack
<HStack px={4} py={2}> as={Link}
<HStack as={Link} href="/" _hover={{ textDecoration: "none" }} spacing={0} py={2} pr={16}> href="/"
<Image src="/logo.svg" alt="" boxSize={6} mr={4} /> _hover={{ textDecoration: "none" }}
<Heading size="md" fontFamily="inconsolata, monospace"> spacing={0}
OpenPipe py={2}
</Heading> pr={16}
</HStack> flex={1}
sx={{
".widget": {
display: "block",
},
}}
>
<Image src="/logo.svg" alt="" boxSize={6} mr={4} />
<Heading size="md" fontFamily="inconsolata, monospace">
OpenPipe
</Heading>
</HStack> </HStack>
</DarkMode> <Box pt="6px">
<GitHubButton
href="https://github.com/openpipe/openpipe"
data-color-scheme="no-preference: dark; light: dark; dark: dark;"
data-size="large"
aria-label="Follow @openpipe on GitHub"
>
Github
</GitHubButton>
</Box>
</HStack>
); );
// Shows how long until the competition starts. Refreshes every second // Shows how long until the competition starts. Refreshes every second
function CountdownTimer(props: { date: Date } & TextProps) { function CountdownTimer(props: { date: Date } & TextProps) {
const [now, setNow] = useState(dayjs(0)); const [now, setNow] = useState(dayjs());
useInterval(() => { useInterval(() => {
setNow(dayjs()); setNow(dayjs());
@@ -52,7 +74,7 @@ function CountdownTimer(props: { date: Date } & TextProps) {
const { date, ...rest } = props; const { date, ...rest } = props;
const kickoff = dayjs(props.date); const kickoff = dayjs(date);
const diff = kickoff.diff(now, "second"); const diff = kickoff.diff(now, "second");
const days = Math.floor(diff / 86400); const days = Math.floor(diff / 86400);
const hours = Math.floor((diff % 86400) / 3600); const hours = Math.floor((diff % 86400) / 3600);
@@ -60,7 +82,7 @@ function CountdownTimer(props: { date: Date } & TextProps) {
const seconds = Math.floor(diff % 60); const seconds = Math.floor(diff % 60);
return ( return (
<Text {...rest}> <Text {...rest} suppressHydrationWarning>
<Text as="span" fontWeight="bold"> <Text as="span" fontWeight="bold">
Kickoff in Kickoff in
</Text>{" "} </Text>{" "}
@@ -103,8 +125,16 @@ function ApplicationStatus(props: BoxProps) {
} else if (user) { } else if (user) {
return ( return (
<Wrapper> <Wrapper>
<HStack spacing={8}> <Flex flexDirection={{ base: "column", md: "row" }} alignItems="center">
<UserMenu user={user} borderRadius={2} borderColor={"gray.700"} borderWidth={1} pr={6} /> <UserMenu
user={user}
borderRadius={2}
borderColor={"gray.700"}
borderWidth={1}
pr={6}
mr={{ base: 0, md: 8 }}
mb={{ base: 8, md: 0 }}
/>
<Box flex={1}> <Box flex={1}>
{entrant?.approved ? ( {entrant?.approved ? (
<Text fontSize="sm"> <Text fontSize="sm">
@@ -112,7 +142,11 @@ function ApplicationStatus(props: BoxProps) {
</Text> </Text>
) : entrant ? ( ) : entrant ? (
<Text fontSize="sm"> <Text fontSize="sm">
Application submitted successfully! We'll notify you by email before August 14th. Application submitted successfully. We'll notify you by email before August 14th.{" "}
<Link href="https://github.com/openpipe/openpipe" isExternal textDecor="underline">
Star our Github
</Link>{" "}
for updates while you wait!
</Text> </Text>
) : ( ) : (
<Button onClick={onApply} colorScheme="orange"> <Button onClick={onApply} colorScheme="orange">
@@ -120,7 +154,7 @@ function ApplicationStatus(props: BoxProps) {
</Button> </Button>
)} )}
</Box> </Box>
</HStack> </Flex>
</Wrapper> </Wrapper>
); );
} }
@@ -143,10 +177,12 @@ export default function Signup() {
/> />
</Head> </Head>
<Box bgColor="gray.900" color="gray.200" minH="100vh" w="full"> <Box color="gray.200" minH="100vh" w="full">
<TopNavbar /> <TopNavbar />
<VStack mx="auto" py={24} maxW="2xl" align="start" fontSize="lg"> <VStack mx="auto" py={24} maxW="2xl" px={4} align="center" fontSize="lg">
<Heading size="lg">🏆 Prompt Engineering World Championships</Heading> <Heading size="lg" textAlign="center">
🏆 Prompt Engineering World Championships
</Heading>
<CountdownTimer <CountdownTimer
date={new Date("2023-08-14T00:00:00Z")} date={new Date("2023-08-14T00:00:00Z")}
fontSize="2xl" fontSize="2xl"
@@ -156,7 +192,7 @@ export default function Signup() {
<ApplicationStatus py={8} alignSelf="center" /> <ApplicationStatus py={8} alignSelf="center" />
<Text fontSize="lg"> <Text fontSize="lg" textAlign="left">
Think you have what it takes to be the best? Compete with the world's top prompt Think you have what it takes to be the best? Compete with the world's top prompt
engineers and see where you rank! engineers and see where you rank!
</Text> </Text>
@@ -165,7 +201,14 @@ export default function Signup() {
Event Details Event Details
</Heading> </Heading>
<Table variant="simple"> <Table variant="simple">
<Tbody> <Tbody
sx={{
th: {
base: { px: 0 },
md: { px: 6 },
},
}}
>
<Tr> <Tr>
<Th>Kickoff</Th> <Th>Kickoff</Th>
<Td>August 14</Td> <Td>August 14</Td>

View File

@@ -3,7 +3,7 @@ import { prisma } from "~/server/db";
import { requireNothing } from "~/utils/accessControl"; import { requireNothing } from "~/utils/accessControl";
export const worldChampsRouter = createTRPCRouter({ export const worldChampsRouter = createTRPCRouter({
userStatus: publicProcedure.query(async ({ input, ctx }) => { userStatus: publicProcedure.query(async ({ ctx }) => {
const userId = ctx.session?.user.id; const userId = ctx.session?.user.id;
if (!userId) { if (!userId) {

View File

@@ -14,6 +14,7 @@ import superjson from "superjson";
import { ZodError } from "zod"; import { ZodError } from "zod";
import { getServerAuthSession } from "~/server/auth"; import { getServerAuthSession } from "~/server/auth";
import { prisma } from "~/server/db"; import { prisma } from "~/server/db";
import { capturePath } from "~/utils/analytics/serverAnalytics";
/** /**
* 1. CONTEXT * 1. CONTEXT
@@ -112,7 +113,7 @@ export const createTRPCRouter = t.router;
export const publicProcedure = t.procedure; export const publicProcedure = t.procedure;
/** Reusable middleware that enforces users are logged in before running the procedure. */ /** Reusable middleware that enforces users are logged in before running the procedure. */
const enforceUserIsAuthed = t.middleware(async ({ ctx, next }) => { const enforceUserIsAuthed = t.middleware(async ({ ctx, next, path }) => {
if (!ctx.session || !ctx.session.user) { if (!ctx.session || !ctx.session.user) {
throw new TRPCError({ code: "UNAUTHORIZED" }); throw new TRPCError({ code: "UNAUTHORIZED" });
} }
@@ -134,6 +135,8 @@ const enforceUserIsAuthed = t.middleware(async ({ ctx, next }) => {
"Protected routes must perform access control checks then explicitly invoke the `ctx.markAccessControlRun()` function to ensure we don't forget access control on a route.", "Protected routes must perform access control checks then explicitly invoke the `ctx.markAccessControlRun()` function to ensure we don't forget access control on a route.",
}); });
capturePath(ctx.session, path);
return resp; return resp;
}); });

View File

@@ -3,7 +3,7 @@ import ivm from "isolated-vm";
import dedent from "dedent"; import dedent from "dedent";
import { openai } from "./openai"; import { openai } from "./openai";
import { isObject } from "lodash-es"; import { isObject } from "lodash-es";
import { type CompletionCreateParams } from "openai/resources/chat/completions"; import type { CreateChatCompletionRequestMessage } from "openai/resources/chat/completions";
import formatPromptConstructor from "~/utils/formatPromptConstructor"; import formatPromptConstructor from "~/utils/formatPromptConstructor";
import { type SupportedProvider, type Model } from "~/modelProviders/types"; import { type SupportedProvider, type Model } from "~/modelProviders/types";
import modelProviders from "~/modelProviders/modelProviders"; import modelProviders from "~/modelProviders/modelProviders";
@@ -44,7 +44,7 @@ const requestUpdatedPromptFunction = async (
let newContructionFn = ""; let newContructionFn = "";
for (let i = 0; i < NUM_RETRIES; i++) { for (let i = 0; i < NUM_RETRIES; i++) {
try { try {
const messages: CompletionCreateParams.CreateChatCompletionRequestNonStreaming.Message[] = [ const messages: CreateChatCompletionRequestMessage[] = [
{ {
role: "system", role: "system",
content: `Your job is to update prompt constructor functions. Here is the api shape for the current model:\n---\n${JSON.stringify( content: `Your job is to update prompt constructor functions. Here is the api shape for the current model:\n---\n${JSON.stringify(
@@ -66,9 +66,11 @@ const requestUpdatedPromptFunction = async (
if (newModel.provider !== originalModel.provider) { if (newModel.provider !== originalModel.provider) {
messages.push({ messages.push({
role: "user", role: "user",
content: `The old provider was ${originalModel.provider}. The new provider is ${ content: `As seen in the first argument to definePrompt, the old provider endpoint was "${
originalModel.provider
}". The new provider endpoint is "${
newModel.provider newModel.provider
}. Here is the schema for the new model:\n---\n${JSON.stringify( }". Here is the schema for the new model:\n---\n${JSON.stringify(
modelProviders[newModel.provider].inputSchema, modelProviders[newModel.provider].inputSchema,
null, null,
2, 2,

View File

@@ -1,11 +1,31 @@
import { extendTheme } from "@chakra-ui/react"; import { extendTheme } from "@chakra-ui/react";
import "@fontsource/inconsolata"; import "@fontsource/inconsolata";
import { ChakraProvider } from "@chakra-ui/react"; import { ChakraProvider } from "@chakra-ui/react";
import { modalAnatomy } from "@chakra-ui/anatomy";
import { createMultiStyleConfigHelpers } from "@chakra-ui/styled-system";
const systemFont = const systemFont =
'ui-sans-serif, -apple-system, "system-ui", "Segoe UI", Helvetica, "Apple Color Emoji", Arial, sans-serif, "Segoe UI Emoji", "Segoe UI Symbol"'; 'ui-sans-serif, -apple-system, "system-ui", "Segoe UI", Helvetica, "Apple Color Emoji", Arial, sans-serif, "Segoe UI Emoji", "Segoe UI Symbol"';
/* eslint-disable @typescript-eslint/unbound-method */
const { definePartsStyle, defineMultiStyleConfig } = createMultiStyleConfigHelpers(
modalAnatomy.keys,
);
const modalTheme = defineMultiStyleConfig({
baseStyle: definePartsStyle({
dialog: { borderRadius: "sm" },
}),
});
const theme = extendTheme({ const theme = extendTheme({
styles: {
global: (props: { colorMode: "dark" | "light" }) => ({
"html, body": {
backgroundColor: props.colorMode === "dark" ? "gray.900" : "white",
},
}),
},
fonts: { fonts: {
heading: systemFont, heading: systemFont,
body: systemFont, body: systemFont,
@@ -32,6 +52,7 @@ const theme = extendTheme({
}, },
}, },
}, },
Modal: modalTheme,
}, },
}); });

View File

@@ -3,6 +3,14 @@ import { TRPCError } from "@trpc/server";
import { type TRPCContext } from "~/server/api/trpc"; import { type TRPCContext } from "~/server/api/trpc";
import { prisma } from "~/server/db"; import { prisma } from "~/server/db";
const isAdmin = async (userId: string) => {
const user = await prisma.user.findFirst({
where: { id: userId, role: "ADMIN" },
});
return !!user;
};
// No-op method for protected routes that really should be accessible to anyone. // No-op method for protected routes that really should be accessible to anyone.
export const requireNothing = (ctx: TRPCContext) => { export const requireNothing = (ctx: TRPCContext) => {
ctx.markAccessControlRun(); ctx.markAccessControlRun();
@@ -18,21 +26,24 @@ export const requireCanViewExperiment = async (experimentId: string, ctx: TRPCCo
}; };
export const canModifyExperiment = async (experimentId: string, userId: string) => { export const canModifyExperiment = async (experimentId: string, userId: string) => {
const experiment = await prisma.experiment.findFirst({ const [adminUser, experiment] = await Promise.all([
where: { isAdmin(userId),
id: experimentId, prisma.experiment.findFirst({
organization: { where: {
organizationUsers: { id: experimentId,
some: { organization: {
role: { in: [OrganizationUserRole.ADMIN, OrganizationUserRole.MEMBER] }, organizationUsers: {
userId, some: {
role: { in: [OrganizationUserRole.ADMIN, OrganizationUserRole.MEMBER] },
userId,
},
}, },
}, },
}, },
}, }),
}); ]);
return !!experiment; return adminUser || !!experiment;
}; };
export const requireCanModifyExperiment = async (experimentId: string, ctx: TRPCContext) => { export const requireCanModifyExperiment = async (experimentId: string, ctx: TRPCContext) => {

View File

@@ -1,13 +0,0 @@
// Make sure we're in the browser
import posthog from "posthog-js";
import { env } from "~/env.mjs";
const enableAnalytics = typeof window !== "undefined";
if (enableAnalytics) {
if (env.NEXT_PUBLIC_POSTHOG_KEY) {
posthog.init(env.NEXT_PUBLIC_POSTHOG_KEY, {
api_host: "https://app.posthog.com",
});
}
}

View File

@@ -0,0 +1,31 @@
import { type Session } from "next-auth";
import { useSession } from "next-auth/react";
import { useEffect } from "react";
import posthog from "posthog-js";
import { env } from "~/env.mjs";
// Make sure we're in the browser
const enableBrowserAnalytics = typeof window !== "undefined";
if (env.NEXT_PUBLIC_POSTHOG_KEY && enableBrowserAnalytics) {
posthog.init(env.NEXT_PUBLIC_POSTHOG_KEY, {
api_host: `${env.NEXT_PUBLIC_HOST}/ingest`,
});
}
export const identifySession = (session: Session) => {
if (!session.user) return;
posthog.identify(session.user.id, {
name: session.user.name,
email: session.user.email,
});
};
export const SessionIdentifier = () => {
const session = useSession().data;
useEffect(() => {
if (session && enableBrowserAnalytics) identifySession(session);
}, [session]);
return null;
};

View File

@@ -0,0 +1,14 @@
import { type Session } from "next-auth";
import { PostHog } from "posthog-node";
import { env } from "~/env.mjs";
export const posthogServerClient = env.NEXT_PUBLIC_POSTHOG_KEY
? new PostHog(env.NEXT_PUBLIC_POSTHOG_KEY, {
host: "https://app.posthog.com",
})
: null;
export const capturePath = (session: Session, path: string) => {
if (!session.user || !posthogServerClient) return;
posthogServerClient?.capture({ distinctId: session.user.id, event: path });
};