Skip to content

Commit

Permalink
v basic creation flow
Browse files Browse the repository at this point in the history
  • Loading branch information
Henry Fontanier committed Mar 26, 2024
1 parent 5381510 commit b3db839
Show file tree
Hide file tree
Showing 7 changed files with 251 additions and 32 deletions.
9 changes: 7 additions & 2 deletions front/lib/resources/template_resource.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import type { Result } from "@dust-tt/types";
import { Err, Ok } from "@dust-tt/types";
import type { Attributes, ModelStatic, Transaction } from "sequelize";
import type {
Attributes,
CreationAttributes,
ModelStatic,
Transaction,
} from "sequelize";

import { BaseResource } from "@app/lib/resources/base_resource";
import { TemplateModel } from "@app/lib/resources/storage/models/templates";
Expand All @@ -21,7 +26,7 @@ export class TemplateResource extends BaseResource<TemplateModel> {
super(TemplateModel, blob);
}

static async makeNew(blob: Attributes<TemplateModel>) {
static async makeNew(blob: CreationAttributes<TemplateModel>) {
const template = await TemplateModel.create({
...blob,
});
Expand Down
1 change: 1 addition & 0 deletions front/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

111 changes: 111 additions & 0 deletions front/pages/api/poke/templates.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import type { WithAPIErrorReponse } from "@dust-tt/types";
import {
CreateTemplateFormSchema,
isAssistantTemplateTagNameTypeArray,
} from "@dust-tt/types";
import { isLeft } from "fp-ts/lib/Either";
import * as reporter from "io-ts-reporters";
import type { NextApiRequest, NextApiResponse } from "next";
import { v4 as uuidv4 } from "uuid";

import { USED_MODEL_CONFIGS } from "@app/components/assistant_builder/InstructionScreen";
import { Authenticator, getSession } from "@app/lib/auth";
import { TemplateResource } from "@app/lib/resources/template_resource";
import { apiError, withLogging } from "@app/logger/withlogging";

export type CreateTemplateResponseBody = {
success: boolean;
};

async function handler(
req: NextApiRequest,
res: NextApiResponse<WithAPIErrorReponse<CreateTemplateResponseBody>>
): Promise<void> {
const session = await getSession(req, res);
const auth = await Authenticator.fromSuperUserSession(session, null);

if (!auth.isDustSuperUser()) {
return apiError(req, res, {
status_code: 404,
api_error: {
type: "user_not_found",
message: "Could not find the user.",
},
});
}

switch (req.method) {
case "POST":
const bodyValidation = CreateTemplateFormSchema.decode(req.body);
if (isLeft(bodyValidation)) {
const pathError = reporter.formatValidationErrors(bodyValidation.left);
return apiError(req, res, {
status_code: 400,
api_error: {
type: "invalid_request_error",
message: `The request body is invalid: ${pathError}`,
},
});
}
const body = bodyValidation.right;

if (!isAssistantTemplateTagNameTypeArray(body.tags)) {
return apiError(req, res, {
status_code: 400,
api_error: {
type: "invalid_request_error",
message:
"The request body is invalid: tags must be an array of template tag names.",
},
});
}

const model = USED_MODEL_CONFIGS.find(
(config) => config.modelId === body.presetModel
);

if (!model) {
return apiError(req, res, {
status_code: 400,
api_error: {
type: "invalid_request_error",
message: "The request body is invalid: model not found.",
},
});
}

await TemplateResource.makeNew({
sId: uuidv4(),
name: body.name,
description: body.description ?? null,
presetHandle: body.presetHandle ?? null,
presetInstructions: body.presetInstructions ?? null,
presetTemperature: body.presetTemperature ?? null,
presetAction: body.presetAction,
helpInstructions: body.helpInstructions ?? null,
helpActions: body.helpActions ?? null,
tags: body.tags,
presetProviderId: model.providerId,
presetModelId: model.modelId,
// TODO
visibility: "draft",
presetDescription: null,
});

res.status(200).json({
success: true,
});
break;

default:
return apiError(req, res, {
status_code: 405,
api_error: {
type: "method_not_supported_error",
message: "The method passed is not supported, POST is expected.",
},
});
}
}

export default withLogging(handler);
104 changes: 74 additions & 30 deletions front/pages/poke/templates.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import type { CreateTemplateFormType } from "@dust-tt/types";
import {
ActionPresetSchema,
assistantTemplateTagNames,
CreateTemplateFormSchema,
GPT_4_TURBO_MODEL_CONFIG,
TemperaturePresetSchema,
removeNulls,
} from "@dust-tt/types";
import { ioTsResolver } from "@hookform/resolvers/io-ts";
import * as t from "io-ts";
import { nonEmptyArray } from "io-ts-types/lib/nonEmptyArray";
import { NonEmptyString } from "io-ts-types/lib/NonEmptyString";
import type { InferGetServerSidePropsType } from "next";
import { useRouter } from "next/router";
import React from "react";
import type { Control } from "react-hook-form";
import { useForm } from "react-hook-form";
Expand All @@ -34,6 +33,7 @@ import {
PokeSelectValue,
} from "@app/components/poke/shadcn/ui/select";
import { PokeTextarea } from "@app/components/poke/shadcn/ui/textarea";
import { SendNotificationsContext } from "@app/components/sparkle/Notification";
import { withSuperUserAuthRequirements } from "@app/lib/iam/session";

export const getServerSideProps = withSuperUserAuthRequirements<object>(
Expand All @@ -50,29 +50,14 @@ export const getServerSideProps = withSuperUserAuthRequirements<object>(
}
);

const formSchema = t.type({
name: NonEmptyString,
description: t.union([t.string, t.undefined]),
presetHandle: t.union([t.string, t.undefined]),
presetInstructions: t.union([t.string, t.undefined]),
presetModel: t.string,
presetTemperature: TemperaturePresetSchema,
presetAction: ActionPresetSchema,
helpInstructions: t.union([t.string, t.undefined]),
helpActions: t.union([t.string, t.undefined]),
tags: nonEmptyArray(t.string),
});

type FormSchema = t.TypeOf<typeof formSchema>;

function InputField({
control,
name,
title,
placeholder,
}: {
control: Control<FormSchema>;
name: keyof FormSchema;
control: Control<CreateTemplateFormType>;
name: keyof CreateTemplateFormType;
title?: string;
placeholder?: string;
}) {
Expand All @@ -99,8 +84,8 @@ function TextareaField({
title,
placeholder,
}: {
control: Control<FormSchema>;
name: keyof FormSchema;
control: Control<CreateTemplateFormType>;
name: keyof CreateTemplateFormType;
title?: string;
placeholder?: string;
}) {
Expand Down Expand Up @@ -132,8 +117,8 @@ function SelectField({
title,
options,
}: {
control: Control<FormSchema>;
name: keyof FormSchema;
control: Control<CreateTemplateFormType>;
name: keyof CreateTemplateFormType;
title?: string;
options: string[];
}) {
Expand Down Expand Up @@ -177,12 +162,63 @@ function TemplatesPage(
) {
void _props;

function onSubmit(values: FormSchema) {
console.log(values);
const [isSubmitting, setIsSubmitting] = React.useState(false);
const router = useRouter();
const sendNotification = React.useContext(SendNotificationsContext);

function onSubmit(values: CreateTemplateFormType) {
const cleanedValues = Object.fromEntries(
removeNulls(
Object.entries(values).map(([key, value]) => {
if (typeof value !== "string") {
return [key, value];
}
const cleanedValue = value.trim();
if (!cleanedValue) {
return null;
}
return [key, cleanedValue];
})
)
);

void submit();

async function submit() {
setIsSubmitting(true);
try {
const r = await fetch("/api/poke/templates", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(cleanedValues),
});
if (!r.ok) {
throw new Error(
`Something went wrong: ${r.status} ${await r.text()}`
);
}
sendNotification({
title: "Template created",
type: "success",
description: "Template created successfully.",
});
await new Promise((resolve) => setTimeout(resolve, 1000));
router.reload();
} catch (e) {
setIsSubmitting(false);
sendNotification({
title: "Error creating template",
type: "error",
description: `${e}`,
});
}
}
}

const form = useForm<FormSchema>({
resolver: ioTsResolver(formSchema),
const form = useForm<CreateTemplateFormType>({
resolver: ioTsResolver(CreateTemplateFormSchema),
defaultValues: {
name: "",
description: "",
Expand All @@ -197,6 +233,14 @@ function TemplatesPage(
},
});

if (isSubmitting) {
return (
<div className="flex min-h-screen items-center justify-center bg-structure-50">
<div className="text-structure-900">Creating template...</div>
</div>
);
}

return (
<div className="min-h-screen bg-structure-50 pb-48">
<PokeNavbar />
Expand Down
31 changes: 31 additions & 0 deletions types/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions types/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"eventsource-parser": "^1.1.1",
"hot-shots": "^10.0.0",
"io-ts": "^2.2.20",
"io-ts-types": "^0.5.19",
"redis": "^4.6.8",
"uuid": "^9.0.1"
},
Expand Down
Loading

0 comments on commit b3db839

Please sign in to comment.