lots of changes on making new experiments work
This commit is contained in:
176
@types/nextjs-routes.d.ts
vendored
Normal file
176
@types/nextjs-routes.d.ts
vendored
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||||
|
// This file will be automatically regenerated when your Next.js server is running.
|
||||||
|
// nextjs-routes version: 2.0.1
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
|
// prettier-ignore
|
||||||
|
declare module "nextjs-routes" {
|
||||||
|
import type {
|
||||||
|
GetServerSidePropsContext as NextGetServerSidePropsContext,
|
||||||
|
GetServerSidePropsResult as NextGetServerSidePropsResult
|
||||||
|
} from "next";
|
||||||
|
|
||||||
|
export type Route =
|
||||||
|
| DynamicRoute<"/api/auth/[...nextauth]", { "nextauth": string[] }>
|
||||||
|
| DynamicRoute<"/api/trpc/[trpc]", { "trpc": string }>
|
||||||
|
| DynamicRoute<"/experiments/[id]", { "id": string }>;
|
||||||
|
|
||||||
|
interface StaticRoute<Pathname> {
|
||||||
|
pathname: Pathname;
|
||||||
|
query?: Query | undefined;
|
||||||
|
hash?: string | null | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DynamicRoute<Pathname, Parameters> {
|
||||||
|
pathname: Pathname;
|
||||||
|
query: Parameters & Query;
|
||||||
|
hash?: string | null | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Query {
|
||||||
|
[key: string]: string | string[] | undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type RoutedQuery<P extends Route["pathname"]> = Extract<
|
||||||
|
Route,
|
||||||
|
{ pathname: P }
|
||||||
|
>["query"];
|
||||||
|
|
||||||
|
export type Locale =
|
||||||
|
| "en";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A typesafe utility function for generating paths in your application.
|
||||||
|
*
|
||||||
|
* route({ pathname: "/foos/[foo]", query: { foo: "bar" }}) will produce "/foos/bar".
|
||||||
|
*/
|
||||||
|
export declare function route(r: Route): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Nearly identical to GetServerSidePropsContext from next, but further narrows
|
||||||
|
* types based on nextjs-route's route data.
|
||||||
|
*/
|
||||||
|
export type GetServerSidePropsContext<
|
||||||
|
Pathname extends Route["pathname"] = Route["pathname"],
|
||||||
|
Preview extends NextGetServerSidePropsContext["previewData"] = NextGetServerSidePropsContext["previewData"]
|
||||||
|
> = Omit<NextGetServerSidePropsContext, 'params' | 'query' | 'defaultLocale' | 'locale' | 'locales'> & {
|
||||||
|
params: Extract<Route, { pathname: Pathname }>["query"];
|
||||||
|
query: Query;
|
||||||
|
defaultLocale: "en";
|
||||||
|
locale: Locale;
|
||||||
|
locales: [
|
||||||
|
"en"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Nearly identical to GetServerSideProps from next, but further narrows
|
||||||
|
* types based on nextjs-route's route data.
|
||||||
|
*/
|
||||||
|
export type GetServerSideProps<
|
||||||
|
Props extends { [key: string]: any } = { [key: string]: any },
|
||||||
|
Pathname extends Route["pathname"] = Route["pathname"],
|
||||||
|
Preview extends NextGetServerSideProps["previewData"] = NextGetServerSideProps["previewData"]
|
||||||
|
> = (
|
||||||
|
context: GetServerSidePropsContext<Pathname, Preview>
|
||||||
|
) => Promise<NextGetServerSidePropsResult<Props>>
|
||||||
|
}
|
||||||
|
|
||||||
|
// prettier-ignore
|
||||||
|
declare module "next/link" {
|
||||||
|
import type { Route } from "nextjs-routes";
|
||||||
|
import type { LinkProps as NextLinkProps } from "next/dist/client/link";
|
||||||
|
import type {
|
||||||
|
AnchorHTMLAttributes,
|
||||||
|
DetailedReactHTMLElement,
|
||||||
|
MouseEventHandler,
|
||||||
|
PropsWithChildren,
|
||||||
|
} from "react";
|
||||||
|
export * from "next/dist/client/link";
|
||||||
|
|
||||||
|
type StaticRoute = Exclude<Route, { query: any }>["pathname"];
|
||||||
|
|
||||||
|
export interface LinkProps
|
||||||
|
extends Omit<NextLinkProps, "href" | "locale">,
|
||||||
|
AnchorHTMLAttributes<HTMLAnchorElement> {
|
||||||
|
href: Route | StaticRoute | Omit<Route, "pathname">
|
||||||
|
locale?: Locale | false;
|
||||||
|
}
|
||||||
|
|
||||||
|
type LinkReactElement = DetailedReactHTMLElement<
|
||||||
|
{
|
||||||
|
onMouseEnter?: MouseEventHandler<Element> | undefined;
|
||||||
|
onClick: MouseEventHandler;
|
||||||
|
href?: string | undefined;
|
||||||
|
ref?: any;
|
||||||
|
},
|
||||||
|
HTMLElement
|
||||||
|
>;
|
||||||
|
|
||||||
|
declare function Link(props: PropsWithChildren<LinkProps>): LinkReactElement;
|
||||||
|
|
||||||
|
export default Link;
|
||||||
|
}
|
||||||
|
|
||||||
|
// prettier-ignore
|
||||||
|
declare module "next/router" {
|
||||||
|
import type { Locale, Route, RoutedQuery } from "nextjs-routes";
|
||||||
|
import type { NextRouter as Router } from "next/dist/client/router";
|
||||||
|
export * from "next/dist/client/router";
|
||||||
|
export { default } from "next/dist/client/router";
|
||||||
|
|
||||||
|
type NextTransitionOptions = NonNullable<Parameters<Router["push"]>[2]>;
|
||||||
|
type StaticRoute = Exclude<Route, { query: any }>["pathname"];
|
||||||
|
|
||||||
|
interface TransitionOptions extends Omit<NextTransitionOptions, "locale"> {
|
||||||
|
locale?: Locale | false;
|
||||||
|
}
|
||||||
|
|
||||||
|
type PathnameAndQuery<Pathname> = Required<
|
||||||
|
Pick<Extract<Route, { pathname: Pathname }>, "pathname" | "query">
|
||||||
|
>;
|
||||||
|
|
||||||
|
type AutomaticStaticOptimizedQuery<PaQ> = Omit<PaQ, "query"> & {
|
||||||
|
query: Partial<PaQ["query"]>;
|
||||||
|
};
|
||||||
|
|
||||||
|
type BaseRouter<PaQ> =
|
||||||
|
| ({ isReady: false } & AutomaticStaticOptimizedQuery<PaQ>)
|
||||||
|
| ({ isReady: true } & PaQ);
|
||||||
|
|
||||||
|
export type NextRouter<P extends Route["pathname"] = Route["pathname"]> =
|
||||||
|
BaseRouter<PathnameAndQuery<P>> &
|
||||||
|
Omit<
|
||||||
|
Router,
|
||||||
|
| "defaultLocale"
|
||||||
|
| "domainLocales"
|
||||||
|
| "isReady"
|
||||||
|
| "locale"
|
||||||
|
| "locales"
|
||||||
|
| "pathname"
|
||||||
|
| "push"
|
||||||
|
| "query"
|
||||||
|
| "replace"
|
||||||
|
| "route"
|
||||||
|
> & {
|
||||||
|
defaultLocale: "en";
|
||||||
|
domainLocales?: undefined;
|
||||||
|
locale: Locale;
|
||||||
|
locales: [
|
||||||
|
"en"
|
||||||
|
];
|
||||||
|
push(
|
||||||
|
url: Route | StaticRoute | Omit<Route, "pathname">,
|
||||||
|
as?: string,
|
||||||
|
options?: TransitionOptions
|
||||||
|
): Promise<boolean>;
|
||||||
|
replace(
|
||||||
|
url: Route | StaticRoute | Omit<Route, "pathname">,
|
||||||
|
as?: string,
|
||||||
|
options?: TransitionOptions
|
||||||
|
): Promise<boolean>;
|
||||||
|
route: P;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function useRouter<P extends Route["pathname"]>(): NextRouter<P>;
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import nextRoutes from "nextjs-routes/config";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially useful
|
* Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially useful
|
||||||
* for Docker builds.
|
* for Docker builds.
|
||||||
@@ -20,4 +22,4 @@ const config = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default config;
|
export default nextRoutes()(config);
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"next": "^13.4.2",
|
"next": "^13.4.2",
|
||||||
"next-auth": "^4.22.1",
|
"next-auth": "^4.22.1",
|
||||||
|
"nextjs-routes": "^2.0.1",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
"react-icons": "^4.10.1",
|
"react-icons": "^4.10.1",
|
||||||
|
|||||||
68
pnpm-lock.yaml
generated
68
pnpm-lock.yaml
generated
@@ -68,6 +68,9 @@ dependencies:
|
|||||||
next-auth:
|
next-auth:
|
||||||
specifier: ^4.22.1
|
specifier: ^4.22.1
|
||||||
version: 4.22.1(next@13.4.2)(react-dom@18.2.0)(react@18.2.0)
|
version: 4.22.1(next@13.4.2)(react-dom@18.2.0)(react@18.2.0)
|
||||||
|
nextjs-routes:
|
||||||
|
specifier: ^2.0.1
|
||||||
|
version: 2.0.1(next@13.4.2)
|
||||||
react:
|
react:
|
||||||
specifier: 18.2.0
|
specifier: 18.2.0
|
||||||
version: 18.2.0
|
version: 18.2.0
|
||||||
@@ -2256,6 +2259,14 @@ packages:
|
|||||||
color-convert: 2.0.1
|
color-convert: 2.0.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/anymatch@3.1.3:
|
||||||
|
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
|
||||||
|
engines: {node: '>= 8'}
|
||||||
|
dependencies:
|
||||||
|
normalize-path: 3.0.0
|
||||||
|
picomatch: 2.3.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
/argparse@2.0.1:
|
/argparse@2.0.1:
|
||||||
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
|
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
|
||||||
dev: true
|
dev: true
|
||||||
@@ -2364,6 +2375,11 @@ packages:
|
|||||||
engines: {node: '>=0.6'}
|
engines: {node: '>=0.6'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/binary-extensions@2.2.0:
|
||||||
|
resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/bplist-parser@0.2.0:
|
/bplist-parser@0.2.0:
|
||||||
resolution: {integrity: sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==}
|
resolution: {integrity: sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==}
|
||||||
engines: {node: '>= 5.10.0'}
|
engines: {node: '>= 5.10.0'}
|
||||||
@@ -2383,7 +2399,6 @@ packages:
|
|||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
dependencies:
|
dependencies:
|
||||||
fill-range: 7.0.1
|
fill-range: 7.0.1
|
||||||
dev: true
|
|
||||||
|
|
||||||
/buffer-from@0.1.2:
|
/buffer-from@0.1.2:
|
||||||
resolution: {integrity: sha512-RiWIenusJsmI2KcvqQABB83tLxCByE3upSP8QU3rJDMVFGPWLvPQJt/O1Su9moRWeH7d+Q2HYb68f6+v+tw2vg==}
|
resolution: {integrity: sha512-RiWIenusJsmI2KcvqQABB83tLxCByE3upSP8QU3rJDMVFGPWLvPQJt/O1Su9moRWeH7d+Q2HYb68f6+v+tw2vg==}
|
||||||
@@ -2438,6 +2453,21 @@ packages:
|
|||||||
supports-color: 7.2.0
|
supports-color: 7.2.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/chokidar@3.5.3:
|
||||||
|
resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
|
||||||
|
engines: {node: '>= 8.10.0'}
|
||||||
|
dependencies:
|
||||||
|
anymatch: 3.1.3
|
||||||
|
braces: 3.0.2
|
||||||
|
glob-parent: 5.1.2
|
||||||
|
is-binary-path: 2.1.0
|
||||||
|
is-glob: 4.0.3
|
||||||
|
normalize-path: 3.0.0
|
||||||
|
readdirp: 3.6.0
|
||||||
|
optionalDependencies:
|
||||||
|
fsevents: 2.3.2
|
||||||
|
dev: false
|
||||||
|
|
||||||
/client-only@0.0.1:
|
/client-only@0.0.1:
|
||||||
resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==}
|
resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==}
|
||||||
dev: false
|
dev: false
|
||||||
@@ -3117,7 +3147,6 @@ packages:
|
|||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
dependencies:
|
dependencies:
|
||||||
to-regex-range: 5.0.1
|
to-regex-range: 5.0.1
|
||||||
dev: true
|
|
||||||
|
|
||||||
/find-root@1.1.0:
|
/find-root@1.1.0:
|
||||||
resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==}
|
resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==}
|
||||||
@@ -3246,7 +3275,6 @@ packages:
|
|||||||
engines: {node: '>= 6'}
|
engines: {node: '>= 6'}
|
||||||
dependencies:
|
dependencies:
|
||||||
is-glob: 4.0.3
|
is-glob: 4.0.3
|
||||||
dev: true
|
|
||||||
|
|
||||||
/glob-parent@6.0.2:
|
/glob-parent@6.0.2:
|
||||||
resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
|
resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
|
||||||
@@ -3466,6 +3494,13 @@ packages:
|
|||||||
has-bigints: 1.0.2
|
has-bigints: 1.0.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/is-binary-path@2.1.0:
|
||||||
|
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dependencies:
|
||||||
|
binary-extensions: 2.2.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/is-boolean-object@1.1.2:
|
/is-boolean-object@1.1.2:
|
||||||
resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==}
|
resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -3506,14 +3541,12 @@ packages:
|
|||||||
/is-extglob@2.1.1:
|
/is-extglob@2.1.1:
|
||||||
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
|
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
dev: true
|
|
||||||
|
|
||||||
/is-glob@4.0.3:
|
/is-glob@4.0.3:
|
||||||
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
|
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
dependencies:
|
dependencies:
|
||||||
is-extglob: 2.1.1
|
is-extglob: 2.1.1
|
||||||
dev: true
|
|
||||||
|
|
||||||
/is-inside-container@1.0.0:
|
/is-inside-container@1.0.0:
|
||||||
resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==}
|
resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==}
|
||||||
@@ -3538,7 +3571,6 @@ packages:
|
|||||||
/is-number@7.0.0:
|
/is-number@7.0.0:
|
||||||
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
|
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
|
||||||
engines: {node: '>=0.12.0'}
|
engines: {node: '>=0.12.0'}
|
||||||
dev: true
|
|
||||||
|
|
||||||
/is-path-inside@3.0.3:
|
/is-path-inside@3.0.3:
|
||||||
resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==}
|
resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==}
|
||||||
@@ -3867,6 +3899,21 @@ packages:
|
|||||||
- babel-plugin-macros
|
- babel-plugin-macros
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/nextjs-routes@2.0.1(next@13.4.2):
|
||||||
|
resolution: {integrity: sha512-pBGRm6uR44zwUjWWYn6+gwz08BhBbqUYlIzsbNHAh1TWohHYKWFaa2YVsj8BxEo726MZYg87OJPnHpaaY1ia0w==}
|
||||||
|
hasBin: true
|
||||||
|
peerDependencies:
|
||||||
|
next: '*'
|
||||||
|
dependencies:
|
||||||
|
chokidar: 3.5.3
|
||||||
|
next: 13.4.2(react-dom@18.2.0)(react@18.2.0)
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/normalize-path@3.0.0:
|
||||||
|
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/npm-run-path@4.0.1:
|
/npm-run-path@4.0.1:
|
||||||
resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==}
|
resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@@ -4083,7 +4130,6 @@ packages:
|
|||||||
/picomatch@2.3.1:
|
/picomatch@2.3.1:
|
||||||
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
|
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
|
||||||
engines: {node: '>=8.6'}
|
engines: {node: '>=8.6'}
|
||||||
dev: true
|
|
||||||
|
|
||||||
/postcss@8.4.14:
|
/postcss@8.4.14:
|
||||||
resolution: {integrity: sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==}
|
resolution: {integrity: sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==}
|
||||||
@@ -4305,6 +4351,13 @@ packages:
|
|||||||
util-deprecate: 1.0.2
|
util-deprecate: 1.0.2
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/readdirp@3.6.0:
|
||||||
|
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
|
||||||
|
engines: {node: '>=8.10.0'}
|
||||||
|
dependencies:
|
||||||
|
picomatch: 2.3.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
/regenerator-runtime@0.13.11:
|
/regenerator-runtime@0.13.11:
|
||||||
resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==}
|
resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==}
|
||||||
|
|
||||||
@@ -4636,7 +4689,6 @@ packages:
|
|||||||
engines: {node: '>=8.0'}
|
engines: {node: '>=8.0'}
|
||||||
dependencies:
|
dependencies:
|
||||||
is-number: 7.0.0
|
is-number: 7.0.0
|
||||||
dev: true
|
|
||||||
|
|
||||||
/toggle-selection@1.0.6:
|
/toggle-selection@1.0.6:
|
||||||
resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==}
|
resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==}
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ model Experiment {
|
|||||||
id String @id @default(uuid()) @db.Uuid
|
id String @id @default(uuid()) @db.Uuid
|
||||||
label String
|
label String
|
||||||
|
|
||||||
|
sortIndex Int @default(0)
|
||||||
|
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
TemplateVariable TemplateVariable[]
|
TemplateVariable TemplateVariable[]
|
||||||
@@ -33,7 +35,7 @@ model PromptVariant {
|
|||||||
sortIndex Int @default(0)
|
sortIndex Int @default(0)
|
||||||
|
|
||||||
experimentId String @db.Uuid
|
experimentId String @db.Uuid
|
||||||
experiment Experiment @relation(fields: [experimentId], references: [id])
|
experiment Experiment @relation(fields: [experimentId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
@@ -52,7 +54,7 @@ model TestScenario {
|
|||||||
sortIndex Int @default(0)
|
sortIndex Int @default(0)
|
||||||
|
|
||||||
experimentId String @db.Uuid
|
experimentId String @db.Uuid
|
||||||
experiment Experiment @relation(fields: [experimentId], references: [id])
|
experiment Experiment @relation(fields: [experimentId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
@@ -65,7 +67,7 @@ model TemplateVariable {
|
|||||||
label String
|
label String
|
||||||
|
|
||||||
experimentId String @db.Uuid
|
experimentId String @db.Uuid
|
||||||
experiment Experiment @relation(fields: [experimentId], references: [id])
|
experiment Experiment @relation(fields: [experimentId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
@@ -78,10 +80,10 @@ model ModelOutput {
|
|||||||
output Json
|
output Json
|
||||||
|
|
||||||
promptVariantId String @db.Uuid
|
promptVariantId String @db.Uuid
|
||||||
promptVariant PromptVariant @relation(fields: [promptVariantId], references: [id])
|
promptVariant PromptVariant @relation(fields: [promptVariantId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
testScenarioId String @db.Uuid
|
testScenarioId String @db.Uuid
|
||||||
testScenario TestScenario @relation(fields: [testScenarioId], references: [id])
|
testScenario TestScenario @relation(fields: [testScenarioId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
|
|||||||
@@ -1,14 +1,18 @@
|
|||||||
import { prisma } from "~/server/db";
|
import { prisma } from "~/server/db";
|
||||||
|
|
||||||
const experimentId = "11111111-1111-1111-1111-111111111111";
|
const experimentId = "11111111-1111-1111-1111-111111111111";
|
||||||
const experiment = await prisma.experiment.upsert({
|
|
||||||
|
// Delete the existing experiment
|
||||||
|
await prisma.experiment.delete({
|
||||||
where: {
|
where: {
|
||||||
id: experimentId,
|
id: experimentId,
|
||||||
},
|
},
|
||||||
update: {},
|
});
|
||||||
create: {
|
|
||||||
|
const experiment = await prisma.experiment.create({
|
||||||
|
data: {
|
||||||
id: experimentId,
|
id: experimentId,
|
||||||
label: "Quick Start",
|
label: "Country Capitals Example",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -34,7 +38,7 @@ const resp = await prisma.promptVariant.createMany({
|
|||||||
sortIndex: 0,
|
sortIndex: 0,
|
||||||
config: {
|
config: {
|
||||||
model: "gpt-3.5-turbo",
|
model: "gpt-3.5-turbo",
|
||||||
messages: [{ role: "user", content: "What is the capitol of {{state}}?" }],
|
messages: [{ role: "user", content: "What is the capital of {{country}}?" }],
|
||||||
temperature: 0,
|
temperature: 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -44,7 +48,12 @@ const resp = await prisma.promptVariant.createMany({
|
|||||||
sortIndex: 1,
|
sortIndex: 1,
|
||||||
config: {
|
config: {
|
||||||
model: "gpt-3.5-turbo",
|
model: "gpt-3.5-turbo",
|
||||||
messages: [{ role: "user", content: "What is the capitol of the US state {{state}}?" }],
|
messages: [
|
||||||
|
{
|
||||||
|
role: "user",
|
||||||
|
content: "What is the capital of {{country}}? Only return the city name.",
|
||||||
|
},
|
||||||
|
],
|
||||||
temperature: 0,
|
temperature: 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -61,7 +70,7 @@ await prisma.templateVariable.createMany({
|
|||||||
data: [
|
data: [
|
||||||
{
|
{
|
||||||
experimentId,
|
experimentId,
|
||||||
label: "state",
|
label: "country",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
@@ -78,21 +87,21 @@ await prisma.testScenario.createMany({
|
|||||||
experimentId,
|
experimentId,
|
||||||
sortIndex: 0,
|
sortIndex: 0,
|
||||||
variableValues: {
|
variableValues: {
|
||||||
state: "Washington",
|
country: "USA",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
experimentId,
|
experimentId,
|
||||||
sortIndex: 1,
|
sortIndex: 1,
|
||||||
variableValues: {
|
variableValues: {
|
||||||
state: "California",
|
country: "Spain",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
experimentId,
|
experimentId,
|
||||||
sortIndex: 2,
|
sortIndex: 2,
|
||||||
variableValues: {
|
variableValues: {
|
||||||
state: "Utah",
|
country: "Chile",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { api } from "~/utils/api";
|
|||||||
import { PromptVariant, Scenario } from "./types";
|
import { PromptVariant, Scenario } from "./types";
|
||||||
import { Center, Spinner, Text } from "@chakra-ui/react";
|
import { Center, Spinner, Text } from "@chakra-ui/react";
|
||||||
import { useExperiment } from "~/utils/hooks";
|
import { useExperiment } from "~/utils/hooks";
|
||||||
import { JSONSerializable } from "~/server/types";
|
|
||||||
import { cellPadding } from "../constants";
|
import { cellPadding } from "../constants";
|
||||||
|
|
||||||
const CellShell = ({ children }: { children: React.ReactNode }) => (
|
const CellShell = ({ children }: { children: React.ReactNode }) => (
|
||||||
@@ -19,12 +18,11 @@ export default function OutputCell({
|
|||||||
variant: PromptVariant;
|
variant: PromptVariant;
|
||||||
}) {
|
}) {
|
||||||
const experiment = useExperiment();
|
const experiment = useExperiment();
|
||||||
|
const vars = api.templateVars.list.useQuery({ experimentId: experiment.data?.id ?? "" }).data;
|
||||||
|
|
||||||
const experimentVariables = experiment.data?.TemplateVariable.map((v) => v.label) ?? [];
|
|
||||||
const scenarioVariables = scenario.variableValues as Record<string, string>;
|
const scenarioVariables = scenario.variableValues as Record<string, string>;
|
||||||
const templateHasVariables =
|
const templateHasVariables =
|
||||||
experimentVariables.length === 0 ||
|
vars?.length === 0 || vars?.some((v) => scenarioVariables[v.label] !== undefined);
|
||||||
experimentVariables.some((v) => scenarioVariables[v] !== undefined);
|
|
||||||
|
|
||||||
let disabledReason: string | null = null;
|
let disabledReason: string | null = null;
|
||||||
|
|
||||||
@@ -41,6 +39,8 @@ export default function OutputCell({
|
|||||||
{ enabled: disabledReason === null }
|
{ enabled: disabledReason === null }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (!vars) return;
|
||||||
|
|
||||||
if (disabledReason)
|
if (disabledReason)
|
||||||
return (
|
return (
|
||||||
<CellShell>
|
<CellShell>
|
||||||
|
|||||||
@@ -25,8 +25,9 @@ export default function ScenarioEditor({
|
|||||||
const [values, setValues] = useState<Record<string, string>>(savedValues);
|
const [values, setValues] = useState<Record<string, string>>(savedValues);
|
||||||
|
|
||||||
const experiment = useExperiment();
|
const experiment = useExperiment();
|
||||||
|
const vars = api.templateVars.list.useQuery({ experimentId: experiment.data?.id ?? "" }).data;
|
||||||
|
|
||||||
const variableLabels = experiment.data?.TemplateVariable.map((v) => v.label) ?? [];
|
const variableLabels = vars?.map((v) => v.label) ?? [];
|
||||||
|
|
||||||
const hasChanged = !isEqual(savedValues, values);
|
const hasChanged = !isEqual(savedValues, values);
|
||||||
|
|
||||||
@@ -113,58 +114,69 @@ export default function ScenarioEditor({
|
|||||||
_hover={{ color: "gray.800", cursor: "pointer" }}
|
_hover={{ color: "gray.800", cursor: "pointer" }}
|
||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Stack>
|
{variableLabels.length === 0 ? (
|
||||||
{variableLabels.map((key) => {
|
<Box color="gray.500">No scenario variables configured</Box>
|
||||||
const value = values[key] ?? "";
|
) : (
|
||||||
const layoutDirection = value.length > 20 ? "column" : "row";
|
<Stack>
|
||||||
return (
|
{variableLabels.map((key) => {
|
||||||
<Flex
|
const value = values[key] ?? "";
|
||||||
key={key}
|
const layoutDirection = value.length > 20 ? "column" : "row";
|
||||||
direction={layoutDirection}
|
return (
|
||||||
alignItems={layoutDirection === "column" ? "flex-start" : "center"}
|
<Flex
|
||||||
flexWrap="wrap"
|
key={key}
|
||||||
>
|
direction={layoutDirection}
|
||||||
<Box bgColor="blue.100" color="blue.600" px={2} fontSize="xs" fontWeight="bold">
|
alignItems={layoutDirection === "column" ? "flex-start" : "center"}
|
||||||
{key}
|
flexWrap="wrap"
|
||||||
</Box>
|
>
|
||||||
<Textarea
|
<Box bgColor="blue.100" color="blue.600" px={2} fontSize="xs" fontWeight="bold">
|
||||||
px={2}
|
{key}
|
||||||
py={1}
|
</Box>
|
||||||
placeholder="empty"
|
<Textarea
|
||||||
value={value}
|
px={2}
|
||||||
onChange={(e) => {
|
py={1}
|
||||||
setValues((prev) => ({ ...prev, [key]: e.target.value }));
|
placeholder="empty"
|
||||||
|
borderRadius="sm"
|
||||||
|
value={value}
|
||||||
|
onChange={(e) => {
|
||||||
|
setValues((prev) => ({ ...prev, [key]: e.target.value }));
|
||||||
|
}}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
|
||||||
|
e.preventDefault();
|
||||||
|
onSave();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
resize="none"
|
||||||
|
overflow="hidden"
|
||||||
|
minRows={1}
|
||||||
|
minH="unset"
|
||||||
|
as={ResizeTextarea}
|
||||||
|
flex={layoutDirection === "row" ? 1 : undefined}
|
||||||
|
borderColor={hasChanged ? "blue.300" : "transparent"}
|
||||||
|
_hover={{ borderColor: "gray.300" }}
|
||||||
|
_focus={{ borderColor: "blue.500", outline: "none", bg: "white" }}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{hasChanged && (
|
||||||
|
<HStack justify="right">
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
onMouseDown={() => {
|
||||||
|
setValues(savedValues);
|
||||||
}}
|
}}
|
||||||
resize="none"
|
colorScheme="gray"
|
||||||
overflow="hidden"
|
>
|
||||||
minRows={1}
|
Reset
|
||||||
minH="unset"
|
</Button>
|
||||||
as={ResizeTextarea}
|
<Button size="sm" onMouseDown={onSave} colorScheme="blue">
|
||||||
flex={layoutDirection === "row" ? 1 : undefined}
|
Save
|
||||||
borderColor={hasChanged ? "blue.300" : "transparent"}
|
</Button>
|
||||||
_hover={{ borderColor: "gray.300" }}
|
</HStack>
|
||||||
_focus={{ borderColor: "blue.500", outline: "none", bg: "white" }}
|
)}
|
||||||
/>
|
</Stack>
|
||||||
</Flex>
|
)}
|
||||||
);
|
|
||||||
})}
|
|
||||||
{hasChanged && (
|
|
||||||
<HStack justify="right">
|
|
||||||
<Button
|
|
||||||
size="sm"
|
|
||||||
onMouseDown={() => {
|
|
||||||
setValues(savedValues);
|
|
||||||
}}
|
|
||||||
colorScheme="gray"
|
|
||||||
>
|
|
||||||
Reset
|
|
||||||
</Button>
|
|
||||||
<Button size="sm" onMouseDown={onSave} colorScheme="blue">
|
|
||||||
Save
|
|
||||||
</Button>
|
|
||||||
</HStack>
|
|
||||||
)}
|
|
||||||
</Stack>
|
|
||||||
</HStack>
|
</HStack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Box, Grid, GridItem, Heading, type SystemStyleObject } from "@chakra-ui/react";
|
import { Grid, GridItem, type SystemStyleObject } from "@chakra-ui/react";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { api } from "~/utils/api";
|
import { api } from "~/utils/api";
|
||||||
import NewScenarioButton from "./NewScenarioButton";
|
import NewScenarioButton from "./NewScenarioButton";
|
||||||
@@ -8,7 +8,6 @@ import ScenarioEditor from "./ScenarioEditor";
|
|||||||
import VariantConfigEditor from "./VariantConfigEditor";
|
import VariantConfigEditor from "./VariantConfigEditor";
|
||||||
import VariantHeader from "./VariantHeader";
|
import VariantHeader from "./VariantHeader";
|
||||||
import type { Scenario, PromptVariant } from "./types";
|
import type { Scenario, PromptVariant } from "./types";
|
||||||
import { cellPadding } from "../constants";
|
|
||||||
import ScenarioHeader from "~/server/ScenarioHeader";
|
import ScenarioHeader from "~/server/ScenarioHeader";
|
||||||
|
|
||||||
const stickyHeaderStyle: SystemStyleObject = {
|
const stickyHeaderStyle: SystemStyleObject = {
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
import { Box, Flex } from "@chakra-ui/react";
|
|
||||||
import Head from "next/head";
|
|
||||||
|
|
||||||
export default function AppNav(props: { children: React.ReactNode; title?: string }) {
|
|
||||||
return (
|
|
||||||
<Flex minH="100vh">
|
|
||||||
<Head>
|
|
||||||
<title>{props.title ? `${props.title} | Prompt Lab` : "Prompt Lab"}</title>
|
|
||||||
</Head>
|
|
||||||
{/* Placeholder for now */}
|
|
||||||
<Box bgColor="gray.100" flexShrink={0} width="200px">
|
|
||||||
Nav Sidebar
|
|
||||||
</Box>
|
|
||||||
<Box flex={1} overflowX="auto" overflowY="auto" h="100vh">
|
|
||||||
{props.children}
|
|
||||||
</Box>
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
67
src/components/nav/AppShell.tsx
Normal file
67
src/components/nav/AppShell.tsx
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import { Box, Flex, Stack, Button, Heading, VStack, Icon, HStack } from "@chakra-ui/react";
|
||||||
|
import Head from "next/head";
|
||||||
|
import { api } from "~/utils/api";
|
||||||
|
import { BsPlusSquare } from "react-icons/bs";
|
||||||
|
import { RiFlaskLine } from "react-icons/ri";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { useHandledAsyncCallback } from "~/utils/hooks";
|
||||||
|
|
||||||
|
const NavButton = ({ label, onClick }) => {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
justifyContent="start"
|
||||||
|
onClick={onClick}
|
||||||
|
w="full"
|
||||||
|
leftIcon={<BsPlusSquare />}
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function AppShell(props: { children: React.ReactNode; title?: string }) {
|
||||||
|
const experiments = api.experiments.list.useQuery();
|
||||||
|
const router = useRouter();
|
||||||
|
const utils = api.useContext();
|
||||||
|
|
||||||
|
const createMutation = api.experiments.create.useMutation();
|
||||||
|
const [createExperiment] = useHandledAsyncCallback(async () => {
|
||||||
|
const newExperiment = await createMutation.mutateAsync({ label: "New Experiment" });
|
||||||
|
await utils.experiments.list.invalidate();
|
||||||
|
await router.push({ pathname: "/experiments/[id]", query: { id: newExperiment.id } });
|
||||||
|
}, [createMutation, router]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex minH="100vh">
|
||||||
|
<Head>
|
||||||
|
<title>{props.title ? `${props.title} | Prompt Lab` : "Prompt Lab"}</title>
|
||||||
|
</Head>
|
||||||
|
<Box bgColor="gray.100" flexShrink={0} width="220px" p={8}>
|
||||||
|
<VStack align="start" spacing={4}>
|
||||||
|
<HStack align="center" spacing={2}>
|
||||||
|
<Icon as={RiFlaskLine} boxSize={6} />
|
||||||
|
<Heading size="sm" textAlign="center">
|
||||||
|
Experiments
|
||||||
|
</Heading>
|
||||||
|
</HStack>
|
||||||
|
{experiments?.data?.map((exp) => (
|
||||||
|
<Box
|
||||||
|
key={exp.id}
|
||||||
|
as={Link}
|
||||||
|
href={{ pathname: "/experiments/[id]", query: { id: exp.id } }}
|
||||||
|
justifyContent="start"
|
||||||
|
>
|
||||||
|
{exp.label}
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
|
<NavButton label="New Experiment" onClick={createExperiment} />
|
||||||
|
</VStack>
|
||||||
|
</Box>
|
||||||
|
<Box flex={1} overflowX="auto" overflowY="auto" h="100vh">
|
||||||
|
{props.children}
|
||||||
|
</Box>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Box, Center } from "@chakra-ui/react";
|
import { Box, Center } from "@chakra-ui/react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import OutputsTable from "~/components/OutputsTable";
|
import OutputsTable from "~/components/OutputsTable";
|
||||||
import AppNav from "~/components/nav/AppNav";
|
import AppShell from "~/components/nav/AppShell";
|
||||||
import { api } from "~/utils/api";
|
import { api } from "~/utils/api";
|
||||||
|
|
||||||
export default function Experiment() {
|
export default function Experiment() {
|
||||||
@@ -14,19 +14,19 @@ export default function Experiment() {
|
|||||||
|
|
||||||
if (!experiment.isLoading && !experiment.data) {
|
if (!experiment.isLoading && !experiment.data) {
|
||||||
return (
|
return (
|
||||||
<AppNav title="Experiment not found">
|
<AppShell title="Experiment not found">
|
||||||
<Center h="100vh">
|
<Center h="100vh">
|
||||||
<div>Experiment not found 😕</div>
|
<div>Experiment not found 😕</div>
|
||||||
</Center>
|
</Center>
|
||||||
</AppNav>
|
</AppShell>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppNav title={experiment.data?.label}>
|
<AppShell title={experiment.data?.label}>
|
||||||
<Box minH="100vh" mb={50}>
|
<Box minH="100vh" mb={50}>
|
||||||
<OutputsTable experimentId={router.query.id as string | undefined} />
|
<OutputsTable experimentId={router.query.id as string | undefined} />
|
||||||
</Box>
|
</Box>
|
||||||
</AppNav>
|
</AppShell>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,63 +1,62 @@
|
|||||||
import { Text, Box, Button, HStack, Heading, Icon, Input, Stack, Code } from "@chakra-ui/react";
|
import { Text, Box, Button, HStack, Heading, Icon, Input, Stack, Code } from "@chakra-ui/react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { BsCheck, BsChevronDown, BsX } from "react-icons/bs";
|
import { BsCheck, BsChevronDown, BsChevronUp, BsX } from "react-icons/bs";
|
||||||
import { cellPadding } from "~/components/constants";
|
import { cellPadding } from "~/components/constants";
|
||||||
import { api } from "~/utils/api";
|
import { api } from "~/utils/api";
|
||||||
import { useExperiment, useHandledAsyncCallback } from "~/utils/hooks";
|
import { useExperiment, useHandledAsyncCallback } from "~/utils/hooks";
|
||||||
|
|
||||||
export default function ScenarioHeader() {
|
export default function ScenarioHeader() {
|
||||||
const experiment = useExperiment();
|
const experiment = useExperiment();
|
||||||
|
const vars =
|
||||||
|
api.templateVars.list.useQuery({ experimentId: experiment.data?.id ?? "" }).data ?? [];
|
||||||
|
|
||||||
const initialVariables = experiment.data?.TemplateVariable ?? [];
|
|
||||||
|
|
||||||
const [variables, setVariables] = useState<string[]>(initialVariables.map((v) => v.label));
|
|
||||||
const [newVariable, setNewVariable] = useState<string>("");
|
const [newVariable, setNewVariable] = useState<string>("");
|
||||||
|
const newVarIsValid = newVariable.length > 0 && !vars.map((v) => v.label).includes(newVariable);
|
||||||
|
|
||||||
const [editing, setEditing] = useState(false);
|
const [editing, setEditing] = useState(false);
|
||||||
|
|
||||||
const utils = api.useContext();
|
const utils = api.useContext();
|
||||||
const setVarsMutation = api.experiments.setTemplateVariables.useMutation();
|
const addVarMutation = api.templateVars.create.useMutation();
|
||||||
const [onSave] = useHandledAsyncCallback(async () => {
|
const [onAddVar] = useHandledAsyncCallback(async () => {
|
||||||
if (!experiment.data?.id) return;
|
if (!experiment.data?.id) return;
|
||||||
setEditing(false);
|
if (!newVarIsValid) return;
|
||||||
await setVarsMutation.mutateAsync({
|
await addVarMutation.mutateAsync({
|
||||||
id: experiment.data.id,
|
experimentId: experiment.data.id,
|
||||||
labels: variables,
|
label: newVariable,
|
||||||
});
|
});
|
||||||
await utils.experiments.get.invalidate();
|
await utils.templateVars.list.invalidate();
|
||||||
}, [setVarsMutation, experiment.data?.id, variables]);
|
setNewVariable("");
|
||||||
|
}, [addVarMutation, experiment.data?.id, newVarIsValid, newVariable]);
|
||||||
|
|
||||||
|
const deleteMutation = api.templateVars.delete.useMutation();
|
||||||
|
const [onDeleteVar] = useHandledAsyncCallback(async (id: string) => {
|
||||||
|
await deleteMutation.mutateAsync({ id });
|
||||||
|
await utils.templateVars.list.invalidate();
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack flex={1} px={cellPadding.x} py={cellPadding.y}>
|
<Stack flex={1} px={cellPadding.x} py={cellPadding.y}>
|
||||||
<HStack>
|
<HStack>
|
||||||
<Heading size="sm" fontWeight="bold" flex={1}>
|
<Heading size="sm" fontWeight="bold" flex={1}>
|
||||||
Scenario
|
Scenario Vars
|
||||||
</Heading>
|
</Heading>
|
||||||
{editing ? (
|
{
|
||||||
<HStack>
|
|
||||||
<Button size="xs" colorScheme="gray" onClick={() => setEditing(false)}>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
<Button size="xs" colorScheme="blue" onClick={onSave}>
|
|
||||||
Save
|
|
||||||
</Button>
|
|
||||||
</HStack>
|
|
||||||
) : (
|
|
||||||
<Button
|
<Button
|
||||||
size="xs"
|
size="xs"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
colorScheme="blue"
|
colorScheme="blue"
|
||||||
rightIcon={<Icon as={BsChevronDown} />}
|
rightIcon={<Icon as={editing ? BsCheck : BsChevronDown} />}
|
||||||
onClick={() => setEditing(true)}
|
onClick={() => setEditing((editing) => !editing)}
|
||||||
>
|
>
|
||||||
Configure
|
{editing ? "Finish" : "Configure"}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
}
|
||||||
</HStack>
|
</HStack>
|
||||||
{editing && (
|
{editing && (
|
||||||
<Stack spacing={2} pt={2}>
|
<Stack spacing={2} pt={2}>
|
||||||
<Text fontSize="sm">
|
<Text fontSize="sm">
|
||||||
You can use variables in your prompt text inside <Code>{`{{curly_braces}}`}</Code>.
|
Define scenario variables. Reference them from your prompt variants using{" "}
|
||||||
|
<Code>{`{{curly_braces}}`}</Code>.
|
||||||
</Text>
|
</Text>
|
||||||
<HStack spacing={0}>
|
<HStack spacing={0}>
|
||||||
<Input
|
<Input
|
||||||
@@ -67,25 +66,28 @@ export default function ScenarioHeader() {
|
|||||||
borderRightRadius={0}
|
borderRightRadius={0}
|
||||||
value={newVariable}
|
value={newVariable}
|
||||||
onChange={(e) => setNewVariable(e.target.value)}
|
onChange={(e) => setNewVariable(e.target.value)}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
e.preventDefault();
|
||||||
|
onAddVar();
|
||||||
|
}
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
size="xs"
|
size="xs"
|
||||||
height="100%"
|
height="100%"
|
||||||
borderLeftRadius={0}
|
borderLeftRadius={0}
|
||||||
isDisabled={newVariable.length === 0 || variables.includes(newVariable)}
|
isDisabled={!newVarIsValid}
|
||||||
onClick={() => {
|
onClick={onAddVar}
|
||||||
setVariables([...variables, newVariable]);
|
|
||||||
setNewVariable("");
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Icon as={BsCheck} boxSize={8} />
|
<Icon as={BsCheck} boxSize={8} />
|
||||||
</Button>
|
</Button>
|
||||||
</HStack>
|
</HStack>
|
||||||
|
|
||||||
<HStack spacing={2} py={4} wrap="wrap">
|
<HStack spacing={2} py={4} wrap="wrap">
|
||||||
{variables.map((label, i) => (
|
{vars.map((variable, i) => (
|
||||||
<HStack
|
<HStack
|
||||||
key={label}
|
key={variable.id}
|
||||||
spacing={0}
|
spacing={0}
|
||||||
bgColor="blue.100"
|
bgColor="blue.100"
|
||||||
color="blue.600"
|
color="blue.600"
|
||||||
@@ -94,7 +96,7 @@ export default function ScenarioHeader() {
|
|||||||
fontWeight="bold"
|
fontWeight="bold"
|
||||||
>
|
>
|
||||||
<Text fontSize="sm" flex={1}>
|
<Text fontSize="sm" flex={1}>
|
||||||
{label}
|
{variable.label}
|
||||||
</Text>
|
</Text>
|
||||||
<Button
|
<Button
|
||||||
size="xs"
|
size="xs"
|
||||||
@@ -103,11 +105,7 @@ export default function ScenarioHeader() {
|
|||||||
p="unset"
|
p="unset"
|
||||||
minW="unset"
|
minW="unset"
|
||||||
px="unset"
|
px="unset"
|
||||||
onClick={() => {
|
onClick={() => onDeleteVar(variable.id)}
|
||||||
const newVariables = [...variables];
|
|
||||||
newVariables.splice(i, 1);
|
|
||||||
setVariables(newVariables);
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Icon as={BsX} boxSize={6} color="blue.800" />
|
<Icon as={BsX} boxSize={6} color="blue.800" />
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { createTRPCRouter } from "~/server/api/trpc";
|
|||||||
import { experimentsRouter } from "./routers/experiments.router";
|
import { experimentsRouter } from "./routers/experiments.router";
|
||||||
import { scenariosRouter } from "./routers/scenarios.router";
|
import { scenariosRouter } from "./routers/scenarios.router";
|
||||||
import { modelOutputsRouter } from "./routers/modelOutputs.router";
|
import { modelOutputsRouter } from "./routers/modelOutputs.router";
|
||||||
|
import { templateVarsRouter } from "./routers/templateVariables.router";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the primary router for your server.
|
* This is the primary router for your server.
|
||||||
@@ -14,6 +15,7 @@ export const appRouter = createTRPCRouter({
|
|||||||
experiments: experimentsRouter,
|
experiments: experimentsRouter,
|
||||||
scenarios: scenariosRouter,
|
scenarios: scenariosRouter,
|
||||||
outputs: modelOutputsRouter,
|
outputs: modelOutputsRouter,
|
||||||
|
templateVars: templateVarsRouter,
|
||||||
});
|
});
|
||||||
|
|
||||||
// export type definition of API
|
// export type definition of API
|
||||||
|
|||||||
@@ -3,50 +3,64 @@ import { createTRPCRouter, publicProcedure, protectedProcedure } from "~/server/
|
|||||||
import { prisma } from "~/server/db";
|
import { prisma } from "~/server/db";
|
||||||
|
|
||||||
export const experimentsRouter = createTRPCRouter({
|
export const experimentsRouter = createTRPCRouter({
|
||||||
|
list: publicProcedure.query(async () => {
|
||||||
|
return await prisma.experiment.findMany({
|
||||||
|
orderBy: {
|
||||||
|
sortIndex: "asc",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
|
||||||
get: publicProcedure.input(z.object({ id: z.string() })).query(async ({ input }) => {
|
get: publicProcedure.input(z.object({ id: z.string() })).query(async ({ input }) => {
|
||||||
return await prisma.experiment.findFirst({
|
return await prisma.experiment.findFirst({
|
||||||
where: {
|
where: {
|
||||||
id: input.id,
|
id: input.id,
|
||||||
},
|
},
|
||||||
include: {
|
|
||||||
TemplateVariable: {
|
|
||||||
orderBy: {
|
|
||||||
createdAt: "asc",
|
|
||||||
},
|
|
||||||
select: {
|
|
||||||
id: true,
|
|
||||||
label: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
|
|
||||||
setTemplateVariables: publicProcedure
|
create: publicProcedure.input(z.object({})).mutation(async () => {
|
||||||
.input(
|
const maxSortIndex =
|
||||||
z.object({
|
(
|
||||||
id: z.string(),
|
await prisma.experiment.aggregate({
|
||||||
labels: z.array(z.string()),
|
_max: {
|
||||||
})
|
sortIndex: true,
|
||||||
)
|
},
|
||||||
.mutation(async ({ input }) => {
|
})
|
||||||
const existing = await prisma.templateVariable.findMany({
|
)._max?.sortIndex ?? 0;
|
||||||
where: { experimentId: input.id },
|
|
||||||
});
|
|
||||||
const toDelete = existing.filter((e) => !input.labels.includes(e.label));
|
|
||||||
|
|
||||||
const toCreate = new Set(
|
const exp = await prisma.experiment.create({
|
||||||
input.labels.filter((l) => !existing.map((e) => e.label).includes(l))
|
data: {
|
||||||
).values();
|
sortIndex: maxSortIndex + 1,
|
||||||
|
label: `Experiment ${maxSortIndex + 1}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
await prisma.$transaction([
|
await prisma.$transaction([
|
||||||
prisma.templateVariable.deleteMany({
|
prisma.promptVariant.create({
|
||||||
where: { id: { in: toDelete.map((e) => e.id) } },
|
data: {
|
||||||
}),
|
experimentId: exp.id,
|
||||||
prisma.templateVariable.createMany({
|
label: "Prompt Variant 1",
|
||||||
data: [...toCreate].map((l) => ({ label: l, experimentId: input.id })),
|
sortIndex: 0,
|
||||||
}),
|
config: {
|
||||||
]);
|
model: "gpt-3.5-turbo",
|
||||||
return null;
|
messages: [
|
||||||
}),
|
{
|
||||||
|
role: "system",
|
||||||
|
content: "count to three in Spanish...",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
prisma.testScenario.create({
|
||||||
|
data: {
|
||||||
|
experimentId: exp.id,
|
||||||
|
variableValues: {},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return exp;
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
35
src/server/api/routers/templateVariables.router.ts
Normal file
35
src/server/api/routers/templateVariables.router.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
import { createTRPCRouter, publicProcedure, protectedProcedure } from "~/server/api/trpc";
|
||||||
|
import { prisma } from "~/server/db";
|
||||||
|
|
||||||
|
export const templateVarsRouter = createTRPCRouter({
|
||||||
|
create: publicProcedure
|
||||||
|
.input(z.object({ experimentId: z.string(), label: z.string() }))
|
||||||
|
.mutation(async ({ input }) => {
|
||||||
|
await prisma.templateVariable.create({
|
||||||
|
data: {
|
||||||
|
experimentId: input.experimentId,
|
||||||
|
label: input.label,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
|
||||||
|
delete: publicProcedure.input(z.object({ id: z.string() })).mutation(async ({ input }) => {
|
||||||
|
await prisma.templateVariable.delete({ where: { id: input.id } });
|
||||||
|
}),
|
||||||
|
|
||||||
|
list: publicProcedure.input(z.object({ experimentId: z.string() })).query(async ({ input }) => {
|
||||||
|
return await prisma.templateVariable.findMany({
|
||||||
|
where: {
|
||||||
|
experimentId: input.experimentId,
|
||||||
|
},
|
||||||
|
orderBy: {
|
||||||
|
createdAt: "asc",
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
label: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
});
|
||||||
@@ -16,11 +16,17 @@ const theme = extendTheme({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Input: {
|
Input: {
|
||||||
sizes: {
|
baseStyle: {
|
||||||
md: {
|
field: {
|
||||||
field: {
|
borderRadius: "sm",
|
||||||
borderRadius: "sm",
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
Textarea: {
|
||||||
|
baseStyle: {
|
||||||
|
borderRadius: "sm",
|
||||||
|
field: {
|
||||||
|
borderRadius: "sm",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user