Skip to content

Commit

Permalink
[editor] Support Deleting Prompt (#665)
Browse files Browse the repository at this point in the history
[editor] Support Deleting Prompt

# [editor] Support Deleting Prompt

Add the UI side for deleting a prompt, with placeholder to call the
server endpoint once it's implemented



https://github.com/lastmile-ai/aiconfig/assets/5060851/b3e682dd-a3ed-45c1-b153-b4b6e2094616

---
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with
[ReviewStack](https://reviewstack.dev/lastmile-ai/aiconfig/pull/665).
* #667
* #666
* __->__ #665
* #662
  • Loading branch information
rholinshead authored Dec 29, 2023
2 parents ca59d2c + 9b7bee7 commit 8c35024
Show file tree
Hide file tree
Showing 10 changed files with 346 additions and 40 deletions.
20 changes: 19 additions & 1 deletion python/src/aiconfig/editor/client/src/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import EditorContainer, {
AIConfigCallbacks,
} from "./components/EditorContainer";
import { Flex, Loader, MantineProvider } from "@mantine/core";
import { AIConfig, Prompt } from "aiconfig";
import { AIConfig, ModelMetadata, Prompt } from "aiconfig";
import { useCallback, useEffect, useMemo, useState } from "react";
import { ufetch } from "ufetch";
import { ROUTE_TABLE } from "./utils/api";
Expand Down Expand Up @@ -50,6 +50,12 @@ export default function Editor() {
[]
);

const deletePrompt = useCallback(async (promptName: string) => {
return await ufetch.post(ROUTE_TABLE.DELETE_PROMPT, {
prompt_name: promptName,
});
}, []);

const runPrompt = useCallback(async (promptName: string) => {
return await ufetch.post(ROUTE_TABLE.RUN_PROMPT, {
prompt_name: promptName,
Expand All @@ -66,12 +72,24 @@ export default function Editor() {
[]
);

const updateModel = useCallback(
async (_promptName?: string, _modelData?: string | ModelMetadata) => {
// return await ufetch.post(ROUTE_TABLE.UPDATE_MODEL,
// prompt_name: promptName,
// model_data: modelData,
// });
},
[]
);

const callbacks: AIConfigCallbacks = useMemo(
() => ({
addPrompt,
deletePrompt,
getModels,
runPrompt,
save,
updateModel,
updatePrompt,
}),
[save, getModels, addPrompt, runPrompt]
Expand Down
120 changes: 104 additions & 16 deletions python/src/aiconfig/editor/client/src/components/EditorContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import PromptContainer from "./prompt/PromptContainer";
import { Container, Group, Button, createStyles, Stack } from "@mantine/core";
import {
Container,
Group,
Button,
createStyles,
Stack,
Flex,
} from "@mantine/core";
import { showNotification } from "@mantine/notifications";
import { AIConfig, Prompt, PromptInput } from "aiconfig";
import { AIConfig, ModelMetadata, Prompt, PromptInput } from "aiconfig";
import { useCallback, useMemo, useReducer, useRef, useState } from "react";
import aiconfigReducer, { AIConfigReducerAction } from "./aiconfigReducer";
import {
Expand All @@ -11,8 +18,12 @@ import {
clientPromptToAIConfigPrompt,
} from "../shared/types";
import AddPromptButton from "./prompt/AddPromptButton";
import { getDefaultNewPromptName } from "../utils/aiconfigStateUtils";
import {
getDefaultNewPromptName,
getPrompt,
} from "../utils/aiconfigStateUtils";
import { debounce, uniqueId } from "lodash";
import PromptMenuButton from "./prompt/PromptMenuButton";

type Props = {
aiconfig: AIConfig;
Expand All @@ -25,9 +36,14 @@ export type AIConfigCallbacks = {
prompt: Prompt,
index: number
) => Promise<{ aiconfig: AIConfig }>;
deletePrompt: (promptName: string) => Promise<void>;
getModels: (search: string) => Promise<string[]>;
runPrompt: (promptName: string) => Promise<void>;
save: (aiconfig: AIConfig) => Promise<void>;
updateModel: (
promptName?: string,
modelData?: string | ModelMetadata
) => Promise<void /*{ aiconfig: AIConfig }*/>;
updatePrompt: (
promptName: string,
promptData: Prompt
Expand Down Expand Up @@ -150,18 +166,63 @@ export default function EditorContainer({
[dispatch]
);

const debouncedUpdateModel = useMemo(
() =>
debounce(
(promptName?: string, modelMetadata?: string | ModelMetadata) =>
callbacks.updateModel(promptName, modelMetadata),
250
),
[callbacks.updateModel]
);

const onUpdatePromptModelSettings = useCallback(
async (promptIndex: number, newModelSettings: any) => {
dispatch({
type: "UPDATE_PROMPT_MODEL_SETTINGS",
index: promptIndex,
modelSettings: newModelSettings,
});
// TODO: Call server-side endpoint to update model settings
// TODO: Call server-side endpoint to update model
},
[dispatch]
);

const onUpdatePromptModel = useCallback(
async (promptIndex: number, newModel?: string) => {
dispatch({
type: "UPDATE_PROMPT_MODEL",
index: promptIndex,
modelName: newModel,
});

try {
const prompt = clientPromptToAIConfigPrompt(
aiconfigState.prompts[promptIndex]
);
const currentModel = prompt.metadata?.model;
let modelData: string | ModelMetadata | undefined = newModel;
if (newModel && currentModel && typeof currentModel !== "string") {
modelData = {
...currentModel,
name: newModel,
};
}

await debouncedUpdateModel(prompt.name, modelData);

// TODO: Consolidate
} catch (err: any) {
showNotification({
title: "Error updating prompt model",
message: err.message,
color: "red",
});
}
},
[dispatch, debouncedUpdateModel]
);

const onUpdatePromptParameters = useCallback(
async (promptIndex: number, newParameters: any) => {
dispatch({
Expand Down Expand Up @@ -223,6 +284,27 @@ export default function EditorContainer({
[callbacks.addPrompt, dispatch]
);

const onDeletePrompt = useCallback(
async (promptId: string) => {
dispatch({
type: "DELETE_PROMPT",
id: promptId,
});

try {
const prompt = getPrompt(stateRef.current, promptId)!;
await callbacks.deletePrompt(prompt.name);
} catch (err: any) {
showNotification({
title: "Error deleting prompt",
message: err.message,
color: "red",
});
}
},
[callbacks.deletePrompt, dispatch]
);

const onRunPrompt = useCallback(
async (promptIndex: number) => {
const promptName = aiconfigState.prompts[promptIndex].name;
Expand All @@ -241,8 +323,6 @@ export default function EditorContainer({

const { classes } = useStyles();

// TODO: Implement editor context for callbacks, readonly state, etc.

return (
<>
<Container maw="80rem">
Expand All @@ -259,16 +339,24 @@ export default function EditorContainer({
{aiconfigState.prompts.map((prompt: ClientPrompt, i: number) => {
return (
<Stack key={prompt._ui.id}>
<PromptContainer
index={i}
prompt={prompt}
onChangePromptInput={onChangePromptInput}
onChangePromptName={onChangePromptName}
onRunPrompt={onRunPrompt}
onUpdateModelSettings={onUpdatePromptModelSettings}
onUpdateParameters={onUpdatePromptParameters}
defaultConfigModelName={aiconfigState.metadata.default_model}
/>
<Flex mt="md">
<PromptMenuButton
promptId={prompt._ui.id}
onDeletePrompt={() => onDeletePrompt(prompt._ui.id)}
/>
<PromptContainer
index={i}
prompt={prompt}
getModels={callbacks.getModels}
onChangePromptInput={onChangePromptInput}
onChangePromptName={onChangePromptName}
onRunPrompt={onRunPrompt}
onUpdateModel={onUpdatePromptModel}
onUpdateModelSettings={onUpdatePromptModelSettings}
onUpdateParameters={onUpdatePromptParameters}
defaultConfigModelName={aiconfigState.metadata.default_model}
/>
</Flex>
<div className={classes.addPromptRow}>
<AddPromptButton
getModels={callbacks.getModels}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ export type AIConfigReducerAction =

export type MutateAIConfigAction =
| AddPromptAction
| DeletePromptAction
| UpdatePromptInputAction
| UpdatePromptNameAction
| UpdatePromptModelAction
| UpdatePromptModelSettingsAction
| UpdatePromptParametersAction;

Expand All @@ -25,6 +27,12 @@ export type AddPromptAction = {
prompt: ClientPrompt;
};

export type DeletePromptAction = {
type: "DELETE_PROMPT";
id: string;
};

// TODO: Update index to prompt id for all existing-prompt actions
export type UpdatePromptInputAction = {
type: "UPDATE_PROMPT_INPUT";
index: number;
Expand All @@ -37,6 +45,12 @@ export type UpdatePromptNameAction = {
name: string;
};

export type UpdatePromptModelAction = {
type: "UPDATE_PROMPT_MODEL";
index: number;
modelName?: string;
};

export type UpdatePromptModelSettingsAction = {
type: "UPDATE_PROMPT_MODEL_SETTINGS";
index: number;
Expand Down Expand Up @@ -125,6 +139,12 @@ export default function aiconfigReducer(
case "ADD_PROMPT_AT_INDEX": {
return reduceInsertPromptAtIndex(state, action.index, action.prompt);
}
case "DELETE_PROMPT": {
return {
...state,
prompts: state.prompts.filter((prompt) => prompt._ui.id !== action.id),
};
}
case "UPDATE_PROMPT_INPUT": {
return reduceReplaceInput(state, action.index, () => action.input);
}
Expand All @@ -134,6 +154,20 @@ export default function aiconfigReducer(
name: action.name,
}));
}
case "UPDATE_PROMPT_MODEL": {
return reduceReplacePrompt(state, action.index, (prompt) => ({
...prompt,
metadata: {
...prompt.metadata,
model: action.modelName
? {
name: action.modelName,
// TODO: Consolidate settings based on schema union
}
: undefined,
},
}));
}
case "UPDATE_PROMPT_MODEL_SETTINGS": {
return reduceReplacePrompt(state, action.index, (prompt) => ({
...prompt,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ActionIcon, Menu, TextInput } from "@mantine/core";
import { IconPlus, IconSearch, IconTextCaption } from "@tabler/icons-react";
import { memo, useCallback, useEffect, useState } from "react";
import { showNotification } from "@mantine/notifications";
import { memo, useCallback, useState } from "react";
import useLoadModels from "../../hooks/useLoadModels";

type Props = {
addPrompt: (prompt: string) => void;
Expand Down Expand Up @@ -41,30 +41,14 @@ function ModelMenuItems({

export default memo(function AddPromptButton({ addPrompt, getModels }: Props) {
const [modelSearch, setModelSearch] = useState("");
const [models, setModels] = useState<string[]>([]);
const [isOpen, setIsOpen] = useState(false);

const loadModels = useCallback(async (modelSearch: string) => {
try {
const models = await getModels(modelSearch);
setModels(models);
} catch (err: any) {
showNotification({
title: "Error loading models",
message: err?.message,
color: "red",
});
}
}, []);

const onAddPrompt = useCallback((model: string) => {
addPrompt(model);
setIsOpen(false);
}, []);

useEffect(() => {
loadModels(modelSearch);
}, [loadModels, modelSearch]);
const models = useLoadModels(modelSearch, getModels);

return (
<Menu
Expand Down
Loading

0 comments on commit 8c35024

Please sign in to comment.