Skip to content

Commit

Permalink
Tracker Builder: create / edit without dsv
Browse files Browse the repository at this point in the history
  • Loading branch information
PopDaph committed Dec 9, 2024
1 parent 30d241f commit 76c9f74
Show file tree
Hide file tree
Showing 8 changed files with 660 additions and 12 deletions.
2 changes: 1 addition & 1 deletion front/components/assistant_builder/InstructionScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,7 @@ function ModelList({ modelConfigs, onClick }: ModelListProps) {
);
}

function AdvancedSettings({
export function AdvancedSettings({
owner,
plan,
generationSettings,
Expand Down
380 changes: 380 additions & 0 deletions front/components/trackers/TrackerBuilder.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,380 @@
import {
Button,
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
Input,
Label,
Page,
TextArea,
useSendNotification,
} from "@dust-tt/sparkle";
import type {
DataSourceViewType,
SpaceType,
SubscriptionType,
SupportedModel,
TrackerConfigurationStateType,
TrackerConfigurationType,
WorkspaceType,
} from "@dust-tt/types";
import {
CLAUDE_3_5_SONNET_DEFAULT_MODEL_CONFIG,
TRACKER_FREQUENCY_TYPES,
} from "@dust-tt/types";
import { useRouter } from "next/router";
import { useState } from "react";

import { AdvancedSettings } from "@app/components/assistant_builder/InstructionScreen";
import AppLayout from "@app/components/sparkle/AppLayout";
import { AppLayoutSimpleSaveCancelTitle } from "@app/components/sparkle/AppLayoutTitle";
import { isEmailValid } from "@app/lib/utils";

export const TrackerBuilder = ({
owner,
subscription,
globalSpace,
dataSourceViews,
trackerToEdit,
}: {
owner: WorkspaceType;
subscription: SubscriptionType;
globalSpace: SpaceType;
dataSourceViews: DataSourceViewType[];
trackerToEdit: TrackerConfigurationType | null;
}) => {
const router = useRouter();
const sendNotification = useSendNotification();
const [isSubmitting, setIsSubmitting] = useState(false);

const [tracker, setTracker] = useState<TrackerConfigurationStateType>({
name: trackerToEdit?.name ?? null,
nameError: null,
description: trackerToEdit?.description ?? null,
descriptionError: null,
prompt: trackerToEdit?.prompt ?? null,
promptError: null,
frequency: trackerToEdit?.frequency ?? "daily",
frequencyError: null,
recipients: trackerToEdit?.recipients?.join(", ") ?? null,
recipientsError: null,
modelId:
trackerToEdit?.modelId ?? CLAUDE_3_5_SONNET_DEFAULT_MODEL_CONFIG.modelId,
providerId:
trackerToEdit?.providerId ??
CLAUDE_3_5_SONNET_DEFAULT_MODEL_CONFIG.providerId,
temperature: trackerToEdit?.temperature ?? 0.5,
});

void dataSourceViews; // todo: use this

const extractEmails = (text: string) =>
text
.split(/[\n,]+/)
.map((e) => e.trim())
.filter((e, i, self) => self.indexOf(e) === i);

const onSubmit = async () => {
// Validate the form
setIsSubmitting(true);
let hasValidationError = false;
if (!tracker.name) {
setTracker((t) => ({
...t,
nameError: "Name is required.",
}));
hasValidationError = true;
}
if (!tracker.recipients?.length) {
setTracker((t) => ({
...t,
recipientsError: "At least one recipient is required.",
}));
hasValidationError = true;
} else {
const recipients = extractEmails(tracker.recipients);
if (recipients.map(isEmailValid).includes(false)) {
setTracker((t) => ({
...t,
recipientsError:
"Invalid email addresses: " +
recipients.filter((e) => !isEmailValid(e)).join(", "),
}));
hasValidationError = true;
}
}
if (!tracker.prompt) {
setTracker((t) => ({
...t,
promptError: "Prompt is required.",
}));
hasValidationError = true;
}
if (hasValidationError) {
setIsSubmitting(false);
return;
}

let route = `/api/w/${owner.sId}/spaces/${globalSpace.sId}/trackers`;
let method = "POST";

if (trackerToEdit) {
route += `/${trackerToEdit.sId}`;
method = "PATCH";
}

const res = await fetch(route, {
method,
body: JSON.stringify({
name: tracker.name,
description: tracker.description,
prompt: tracker.prompt,
modelId: tracker.modelId,
providerId: tracker.providerId,
temperature: tracker.temperature,
frequency: tracker.frequency,
recipients: tracker.recipients ? extractEmails(tracker.recipients) : [],
}),
headers: {
"Content-Type": "application/json",
},
});

// Handle errors.
if (!res.ok) {
const resJson = await res.json();
sendNotification({
title: trackerToEdit
? "Failed to update tracker"
: "Failed to create tracker",
description: resJson.error.message,
type: "error",
});
setIsSubmitting(false);
return;
}

sendNotification({
title: trackerToEdit ? "Tracker updated" : "Tracker Created",
description: trackerToEdit
? "Tracker updated successfully"
: "Tracker created successfully.",
type: "success",
});
setIsSubmitting(false);
await router.push(`/w/${owner.sId}/assistant/labs/trackers`);
};

return (
<AppLayout
owner={owner}
subscription={subscription}
hideSidebar
isWideMode
pageTitle="Dust - New Tracker"
titleChildren={
<AppLayoutSimpleSaveCancelTitle
title="New Tracker"
onCancel={async () => {
await router.push(`/w/${owner.sId}/assistant/labs/trackers`);
}}
onSave={onSubmit}
isSaving={isSubmitting}
/>
}
>
<div className="mx-auto flex w-full max-w-4xl flex-col gap-16 pb-12">
<div className="flex">
<div className="flex flex-grow" />
<div className="flex flex-shrink-0 flex-col justify-end">
<AdvancedSettings
owner={owner}
plan={subscription.plan}
generationSettings={{
modelSettings: {
modelId: tracker.modelId,
providerId: tracker.providerId,
},
temperature: tracker.temperature,
}}
setGenerationSettings={(g: {
modelSettings: SupportedModel;
temperature: number;
}) => {
setTracker((t) => ({
...t,
modelId: g.modelSettings.modelId,
providerId: g.modelSettings.providerId,
temperature: g.temperature,
}));
}}
/>
</div>
</div>

<div className="flex flex-col gap-6">
<div>
<Page.SectionHeader title="Naming" />
<div className="text-sm font-normal text-element-700">
Give your tracker a clear, memorable name and description that
will help you and your team identify its purpose.
</div>
</div>
<div className="grid grid-cols-1 gap-4 md:grid-cols-3">
<div className="md:col-span-1">
<Input
label="Name"
value={tracker.name || ""}
onChange={(e) =>
setTracker((t) => ({
...t,
name: e.target.value,
nameError: null,
}))
}
placeholder="Descriptive name."
message={tracker.nameError}
messageStatus={tracker.nameError ? "error" : undefined}
/>
</div>
<div className="md:col-span-2">
<Input
label="Description"
value={tracker.description || ""}
onChange={(e) =>
setTracker((t) => ({
...t,
description: e.target.value,
descriptionError: null,
}))
}
placeholder="Brief description of what you're tracking and why."
message={tracker.descriptionError}
messageStatus={tracker.descriptionError ? "error" : undefined}
/>
</div>
</div>
</div>

<div className="flex flex-col gap-6">
<div>
<Page.SectionHeader title="Notification Settings" />
<div className="text-sm font-normal text-element-700">
Choose when and who receives update notifications. We'll bundle
all tracked changes into organized email summaries delivered on
your preferred schedule.
</div>
</div>
<div className="grid grid-cols-1 gap-4 md:grid-cols-3">
<div className="flex md:col-span-1">
<div className="flex flex-col space-y-2">
<Label className="mb-1">Notification frequency</Label>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button label={tracker.frequency} variant="primary" />
</DropdownMenuTrigger>
<DropdownMenuContent>
{TRACKER_FREQUENCY_TYPES.map((f) => (
<DropdownMenuItem
key={f}
label={f}
onClick={() => {
setTracker((t) => ({
...t,
frequency: f,
}));
}}
/>
))}
</DropdownMenuContent>
</DropdownMenu>
</div>
</div>
<div className="md:col-span-2">
<Input
label="Recipients"
placeholder="Enter email addresses (separate multiple addresses with commas)."
value={tracker.recipients}
onChange={(e) =>
setTracker((t) => ({
...t,
recipients: e.target.value,
recipientsError: null,
}))
}
message={tracker.recipientsError}
messageStatus={tracker.recipientsError ? "error" : undefined}
/>
</div>
</div>
</div>
<div className="flex flex-col gap-6">
<div>
<Page.SectionHeader title="Tracker Settings" />
<div className="text-sm font-normal text-element-700">
Set up what you want to track and monitor. Tell us what to look
for, specify which documents to maintain current versions of, and
select which documents to watch for changes.
</div>
</div>
<div className="flex flex-col space-y-2">
<Label className="mb-1">Instructions</Label>
<TextArea
placeholder="Describe what changes or updates you want to track (be as specific as possible)."
value={tracker.prompt || ""}
onChange={(e) =>
setTracker((t) => ({
...t,
prompt: e.target.value,
promptError: null,
}))
}
error={tracker.promptError}
showErrorLabel={!!tracker.promptError}
/>
</div>
<div className="grid grid-cols-1 gap-4 md:grid-cols-3">
<div className="md:col-span-1">
<div className="flex flex-col space-y-2">
<Label className="mb-1">Documents to maintain</Label>
<Button
label="Select Documents"
onClick={() => {
alert("Select Documents");
}}
className="w-fit"
/>
</div>
</div>
<div className="flex items-end md:col-span-2">
<span className="text-sm text-slate-900">
Click 'Select Documents' to choose files
</span>
</div>
</div>

<div className="grid grid-cols-1 gap-4 md:grid-cols-3">
<div className="md:col-span-1">
<div className="flex flex-col space-y-2">
<Label className="mb-1">Documents to watch</Label>
<Button
label="Select Documents"
onClick={() => {
alert("Select Documents");
}}
className="w-fit"
/>
</div>
</div>
<div className="flex items-end md:col-span-2">
<span className="text-sm text-slate-900">
Click 'Select Documents' to choose files
</span>
</div>
</div>
</div>
</div>
</AppLayout>
);
};
Loading

0 comments on commit 76c9f74

Please sign in to comment.