Skip to content

Commit f1e99cf

Browse files
[Meru][Assistant Builder] Improved description suggestion (#4559)
* update description suggestion app * clean dust app + don't use cache * improve description suggestions behaviour * review * clean
1 parent a412772 commit f1e99cf

File tree

2 files changed

+116
-72
lines changed

2 files changed

+116
-72
lines changed

front/components/assistant_builder/NamingScreen.tsx

+114-64
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
Page,
77
PencilSquareIcon,
88
SparklesIcon,
9+
Spinner2,
910
} from "@dust-tt/sparkle";
1011
import type {
1112
APIError,
@@ -14,14 +15,21 @@ import type {
1415
WorkspaceType,
1516
} from "@dust-tt/types";
1617
import { Err, Ok } from "@dust-tt/types";
17-
import React, { useCallback, useEffect, useRef, useState } from "react";
18+
import React, {
19+
useCallback,
20+
useContext,
21+
useEffect,
22+
useRef,
23+
useState,
24+
} from "react";
1825

1926
import { AvatarPicker } from "@app/components/assistant_builder/AssistantBuilderAvatarPicker";
2027
import {
2128
DROID_AVATAR_URLS,
2229
SPIRIT_AVATAR_URLS,
2330
} from "@app/components/assistant_builder/shared";
2431
import type { AssistantBuilderState } from "@app/components/assistant_builder/types";
32+
import { SendNotificationsContext } from "@app/components/sparkle/Notification";
2533
import { isDevelopmentOrDustWorkspace } from "@app/lib/development";
2634
import { debounce } from "@app/lib/utils/debounce";
2735

@@ -134,6 +142,7 @@ export default function NamingScreen({
134142
setEdited: (edited: boolean) => void;
135143
assistantHandleError: string | null;
136144
}) {
145+
const sendNotification = useContext(SendNotificationsContext);
137146
const [isAvatarModalOpen, setIsAvatarModalOpen] = useState(false);
138147

139148
// Name suggestions handling
@@ -168,35 +177,74 @@ export default function NamingScreen({
168177
]);
169178

170179
// Description suggestions handling
171-
const [descriptionSuggestions, setDescriptionSuggestions] =
172-
useState<BuilderSuggestionsType>({
173-
status: "unavailable",
174-
reason: "irrelevant",
175-
});
176180

177-
const [descriptionSuggestionsIndex, setDescriptionSuggestionIndex] =
178-
useState(0);
181+
const [generatingDescription, setGeneratingDescription] = useState(false);
182+
const [descriptionIsGenerated, setDescriptionIsGenerated] = useState(false);
179183

180-
const updateDescriptionSuggestions = useCallback(async () => {
181-
const descriptionSuggestions = await getDescriptionSuggestions({
184+
const suggestDescription = useCallback(
185+
async (fromUserClick?: boolean) => {
186+
setGeneratingDescription(true);
187+
const notifyError = fromUserClick ? sendNotification : console.log;
188+
const descriptionSuggestions = await getDescriptionSuggestions({
189+
owner,
190+
instructions: builderState.instructions || "",
191+
name: builderState.handle || "",
192+
});
193+
if (descriptionSuggestions.isOk()) {
194+
const suggestion =
195+
descriptionSuggestions.value.status === "ok" &&
196+
descriptionSuggestions.value.suggestions.length > 0
197+
? descriptionSuggestions.value.suggestions[0]
198+
: null;
199+
if (suggestion) {
200+
setBuilderState((state) => ({
201+
...state,
202+
description: suggestion,
203+
}));
204+
setDescriptionIsGenerated(true);
205+
} else {
206+
const errorMessage =
207+
descriptionSuggestions.value.status === "unavailable"
208+
? descriptionSuggestions.value.reason ||
209+
"No suggestions available"
210+
: "No suggestions available";
211+
notifyError({
212+
type: "error",
213+
title: "Error generating description suggestion.",
214+
description: errorMessage,
215+
});
216+
}
217+
} else {
218+
notifyError({
219+
type: "error",
220+
title: "Error generating description suggestion.",
221+
description: descriptionSuggestions.error.message,
222+
});
223+
}
224+
setGeneratingDescription(false);
225+
},
226+
[
182227
owner,
183-
instructions: builderState.instructions || "",
184-
name: builderState.handle || "",
185-
});
186-
if (descriptionSuggestions.isOk()) {
187-
setDescriptionSuggestions(descriptionSuggestions.value);
188-
}
189-
}, [owner, builderState.instructions, builderState.handle]);
228+
builderState.instructions,
229+
builderState.handle,
230+
setBuilderState,
231+
sendNotification,
232+
]
233+
);
190234

191235
useEffect(() => {
192236
if (isDevelopmentOrDustWorkspace(owner)) {
193-
void updateDescriptionSuggestions();
237+
if (
238+
!builderState.description?.trim() &&
239+
builderState.instructions?.trim() &&
240+
!generatingDescription
241+
) {
242+
void suggestDescription();
243+
}
194244
}
195-
}, [owner, updateDescriptionSuggestions]);
196-
197-
const suggestionsAvailable =
198-
descriptionSuggestions.status === "ok" &&
199-
descriptionSuggestions.suggestions.length > 0;
245+
// Here we only want to run this effect once
246+
// eslint-disable-next-line react-hooks/exhaustive-deps
247+
}, []);
200248

201249
return (
202250
<>
@@ -299,62 +347,54 @@ export default function NamingScreen({
299347
</div>
300348
<div className="flex flex-col gap-4">
301349
<div>
302-
<Page.SectionHeader title="Description" />
350+
<div className="flex gap-1">
351+
<Page.SectionHeader title="Description" />
352+
{generatingDescription && <Spinner2 size="sm" />}
353+
</div>
303354
<div className="text-sm font-normal text-element-700">
304-
Describe for others the assistant’s purpose.
355+
Describe for others the assistant’s purpose.{" "}
305356
</div>
306357
</div>
307358

308359
<div className="flex items-center gap-2">
309360
<div className="flex-grow">
310361
<Input
311362
placeholder={
312-
suggestionsAvailable
313-
? "Click on sparkles to generate a description"
314-
: "Answer questions about sales, translate from English to French…"
363+
generatingDescription
364+
? "Generating description..."
365+
: "Click on sparkles to generate a description"
315366
}
316-
value={builderState.description}
367+
value={generatingDescription ? "" : builderState.description}
317368
onChange={(value) => {
318369
setEdited(true);
370+
setDescriptionIsGenerated(false);
319371
setBuilderState((state) => ({
320372
...state,
321373
description: value,
322374
}));
323375
}}
324-
error={null} // TODO ?
325376
name="assistantDescription"
326377
className="text-sm"
378+
disabled={generatingDescription}
327379
/>
328380
</div>
329381
{isDevelopmentOrDustWorkspace(owner) && (
330382
<IconButton
331383
icon={SparklesIcon}
332384
size="md"
385+
disabled={generatingDescription}
333386
onClick={async () => {
334-
if (!suggestionsAvailable) return;
335-
setEdited(true);
336-
setBuilderState((state) => ({
337-
...state,
338-
description:
339-
descriptionSuggestions.suggestions[
340-
descriptionSuggestionsIndex
341-
],
342-
}));
343-
if (descriptionSuggestionsIndex === 1) {
344-
await updateDescriptionSuggestions();
345-
setDescriptionSuggestionIndex(0);
346-
} else {
347-
setDescriptionSuggestionIndex(
348-
descriptionSuggestionsIndex + 1
349-
);
387+
if (
388+
!builderState.description?.trim() ||
389+
descriptionIsGenerated ||
390+
confirm(
391+
"Heads up! This will overwrite your current description. Are you sure you want to proceed?"
392+
)
393+
) {
394+
await suggestDescription();
350395
}
351396
}}
352-
disabled={!suggestionsAvailable}
353-
tooltip={
354-
suggestionsAvailable
355-
? "Click to generate a description"
356-
: "Description generation not yet available"
357-
}
397+
tooltip="Click to generate a description"
358398
/>
359399
)}
360400
</div>
@@ -373,7 +413,7 @@ async function getNamingSuggestions({
373413
instructions: string;
374414
description: string;
375415
}): Promise<Result<BuilderSuggestionsType, APIError>> {
376-
const res = await fetch(`/api/w/${owner.sId}/assistant/builder/suggestions`, {
416+
return fetchWithErr(`/api/w/${owner.sId}/assistant/builder/suggestions`, {
377417
method: "POST",
378418
headers: {
379419
"Content-Type": "application/json",
@@ -383,13 +423,6 @@ async function getNamingSuggestions({
383423
inputs: { instructions, description },
384424
}),
385425
});
386-
if (!res.ok) {
387-
return new Err({
388-
type: "internal_server_error",
389-
message: "Failed to get suggestions",
390-
});
391-
}
392-
return new Ok(await res.json());
393426
}
394427

395428
async function getDescriptionSuggestions({
@@ -401,7 +434,7 @@ async function getDescriptionSuggestions({
401434
instructions: string;
402435
name: string;
403436
}): Promise<Result<BuilderSuggestionsType, APIError>> {
404-
const res = await fetch(`/api/w/${owner.sId}/assistant/builder/suggestions`, {
437+
return fetchWithErr(`/api/w/${owner.sId}/assistant/builder/suggestions`, {
405438
method: "POST",
406439
headers: {
407440
"Content-Type": "application/json",
@@ -411,11 +444,28 @@ async function getDescriptionSuggestions({
411444
inputs: { instructions, name },
412445
}),
413446
});
414-
if (!res.ok) {
447+
}
448+
449+
async function fetchWithErr<T>(
450+
input: RequestInfo | URL,
451+
init?: RequestInit
452+
): Promise<Result<T, APIError>> {
453+
try {
454+
const res = await fetch(input, init);
455+
if (!res.ok) {
456+
return new Err({
457+
type: "internal_server_error",
458+
message: `Failed to fetch: ${
459+
res.statusText
460+
}\nResponse: ${await res.text()}`,
461+
});
462+
}
463+
464+
return new Ok((await res.json()) as T);
465+
} catch (e) {
415466
return new Err({
416467
type: "internal_server_error",
417-
message: "Failed to get suggestions",
468+
message: `Failed to fetch.\nError: ${e}`,
418469
});
419470
}
420-
return new Ok(await res.json());
421471
}

types/src/front/lib/actions/registry.ts

+2-8
Original file line numberDiff line numberDiff line change
@@ -193,20 +193,14 @@ export const DustProdActionRegistry = createActionRegistry({
193193
workspaceId: PRODUCTION_DUST_APPS_WORKSPACE_ID,
194194
appId: "aba0057f4c",
195195
appHash:
196-
"14d9afdf184226e1fc3f0fd7c231300581bc81a37eeb8c06016cc7b2fb9fecc3",
196+
"51922001984d1c9a3b84ce1b0275b4925d13fc574a73f1b23fe865d2d1b56eb1",
197197
},
198198
config: {
199199
CREATE_SUGGESTIONS: {
200200
provider_id: "openai",
201201
model_id: "gpt-3.5-turbo",
202202
function_call: "send_suggestions",
203-
use_cache: true,
204-
},
205-
IS_FINISHED: {
206-
provider_id: "openai",
207-
model_id: "gpt-3.5-turbo",
208-
function_call: "send_instructions_finished",
209-
use_cache: true,
203+
use_cache: false,
210204
},
211205
},
212206
},

0 commit comments

Comments
 (0)