Let crew managers edit a bot's workspace instructions where they work with it
A bot's instructions and memory for one workspace were only reachable
from the workspace's settings. The editor is now shared and also opens
from the conversation menu in that workspace ("Instructions here",
loaded on open through api/admin/workspaces/workspace-bot) and from the
workspace rows on the bot's settings page.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -7,16 +7,22 @@ import SettingsRowAction from "../../../(partials)/settings-row-action";
|
||||
type Props = {
|
||||
workspace: BotWorkspace;
|
||||
busy?: boolean;
|
||||
/**
|
||||
* Opens what the bot has for this workspace alone.
|
||||
*/
|
||||
onEdit?: () => void;
|
||||
onRemove?: () => void;
|
||||
};
|
||||
|
||||
/**
|
||||
* One workspace the bot works in. Its title opens the workspace's
|
||||
* settings; a worker can be taken out from here.
|
||||
* settings; its instructions and memory there are edited from here, and a
|
||||
* worker can be taken out.
|
||||
*/
|
||||
export default function BotWorkspaceRow({
|
||||
workspace,
|
||||
busy,
|
||||
onEdit,
|
||||
onRemove,
|
||||
}: Props) {
|
||||
const name = workspace.name || `Workspace ${workspace.id}`;
|
||||
@@ -41,6 +47,14 @@ export default function BotWorkspaceRow({
|
||||
: workspace.description || "Uses the workspace's context"
|
||||
}
|
||||
>
|
||||
{onEdit ? (
|
||||
<SettingsRowAction
|
||||
title={`Instructions and memory in ${name}`}
|
||||
label="Instructions"
|
||||
disabled={busy}
|
||||
onClick={onEdit}
|
||||
/>
|
||||
) : null}
|
||||
{onRemove ? (
|
||||
<SettingsRowAction
|
||||
title={`Take ${name} out of this workspace`}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState } from "react";
|
||||
import type { BotWorkspace, BotWithLastMessage, WorkspaceWithActivity } from "@/src/types";
|
||||
import type { BotWorkspace, BotWithLastMessage, WorkspaceBot, WorkspaceWithActivity } from "@/src/types";
|
||||
import InlineAddSelect from "@/src/components/general/inline-add-select";
|
||||
import WorkspaceBotModal from "@/src/components/general/workspace-bot-modal";
|
||||
import postAdminApi from "@/src/layouts/bots/(functions)/post-admin-api";
|
||||
import SettingsGroup from "../../../(partials)/settings-group";
|
||||
import SettingsList from "../../../(partials)/settings-list";
|
||||
@@ -33,6 +34,7 @@ export default function BotWorkspacesGroup({
|
||||
const [bot_workspaces, setBotWorkspaces] = useState(initial);
|
||||
const [busy_id, setBusyId] = useState<number | "add">();
|
||||
const [status, setStatus] = useState<Status>();
|
||||
const [editing, setEditing] = useState<BotWorkspace>();
|
||||
const is_coordinator = bot.kind == "coordinator";
|
||||
|
||||
const addable = workspaces.filter(
|
||||
@@ -86,6 +88,27 @@ export default function BotWorkspacesGroup({
|
||||
setStatus({ success: true, msg: res.msg || "Saved." });
|
||||
};
|
||||
|
||||
/**
|
||||
* The save answers with the workspace's crew: this bot's row there
|
||||
* carries what it has for the workspace now.
|
||||
*/
|
||||
const saved = ({ workspace_id, bots }: { workspace_id: number; bots: WorkspaceBot[] }) => {
|
||||
const member = bots.find((item) => item.id == bot.id);
|
||||
|
||||
setBotWorkspaces((prev) =>
|
||||
prev.map((workspace) =>
|
||||
Number(workspace.id) == workspace_id
|
||||
? {
|
||||
...workspace,
|
||||
workspace_system_prompt: member?.workspace_system_prompt || null,
|
||||
workspace_memory: member?.workspace_memory || null,
|
||||
}
|
||||
: workspace,
|
||||
),
|
||||
);
|
||||
setStatus({ success: true, msg: "Saved." });
|
||||
};
|
||||
|
||||
return (
|
||||
<SettingsGroup
|
||||
id="workspaces"
|
||||
@@ -109,6 +132,7 @@ export default function BotWorkspacesGroup({
|
||||
key={workspace.id}
|
||||
workspace={workspace}
|
||||
busy={busy_id == workspace.id}
|
||||
onEdit={() => setEditing(workspace)}
|
||||
onRemove={
|
||||
is_coordinator
|
||||
? undefined
|
||||
@@ -141,6 +165,18 @@ export default function BotWorkspacesGroup({
|
||||
/>
|
||||
)}
|
||||
<SettingsStatus status={status} />
|
||||
{editing ? (
|
||||
<WorkspaceBotModal
|
||||
workspace_id={Number(editing.id)}
|
||||
workspace_name={editing.name}
|
||||
bot_id={Number(bot.id)}
|
||||
bot_name={bot.name}
|
||||
system_prompt={editing.workspace_system_prompt}
|
||||
memory={editing.workspace_memory}
|
||||
onClose={() => setEditing(undefined)}
|
||||
onSaved={(bots) => saved({ workspace_id: Number(editing.id), bots })}
|
||||
/>
|
||||
) : null}
|
||||
</SettingsGroup>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
import { useState } from "react";
|
||||
import Form from "@moduletrace/twui/form/Form";
|
||||
import CodeEditor from "@/src/components/general/code-editor";
|
||||
import type { WorkspaceBot } from "@/src/types";
|
||||
import postAdminApi from "@/src/layouts/bots/(functions)/post-admin-api";
|
||||
import AddFormActions from "@/src/layouts/bots/(partials)/add-form-actions";
|
||||
import AddModalShell from "@/src/layouts/bots/(partials)/add-modal-shell";
|
||||
|
||||
type Props = {
|
||||
workspace_id: number;
|
||||
workspace_name: string;
|
||||
bot: WorkspaceBot;
|
||||
onClose: () => void;
|
||||
onSaved: (bots: WorkspaceBot[]) => void;
|
||||
};
|
||||
|
||||
/**
|
||||
* What one bot has for this workspace alone: instructions on top of its
|
||||
* own, and its memory here. The bot rewrites both as it learns; the
|
||||
* coordinator can set the instructions too.
|
||||
*/
|
||||
export default function WorkspaceBotModal({ workspace_id, workspace_name, bot, onClose, onSaved }: Props) {
|
||||
const [system_prompt, setSystemPrompt] = useState(bot.workspace_system_prompt || "");
|
||||
const [memory, setMemory] = useState(bot.workspace_memory || "");
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string>();
|
||||
const name = bot.name || "This bot";
|
||||
|
||||
const submit = async () => {
|
||||
if (loading) return;
|
||||
if (!window.confirm(`Save ${bot.name || "this bot"}'s instructions and memory here?`)) return;
|
||||
|
||||
setLoading(true);
|
||||
setError(undefined);
|
||||
|
||||
const res = await postAdminApi<WorkspaceBot>({
|
||||
path: "workspaces/update-bot",
|
||||
body: { workspace_id, bot_id: bot.id, system_prompt, memory },
|
||||
});
|
||||
|
||||
setLoading(false);
|
||||
|
||||
if (!res.success) {
|
||||
setError(res.msg || "Couldn't save.");
|
||||
return;
|
||||
}
|
||||
|
||||
onSaved(res.payload || []);
|
||||
onClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<AddModalShell
|
||||
open
|
||||
setOpen={(open) => !open && onClose()}
|
||||
title={`${name} in ${workspace_name}`}
|
||||
description={`${name} keeps its own instructions and memory everywhere. These apply only here, and it updates them itself as it learns how this workspace works.`}
|
||||
>
|
||||
<Form className="w-full gap-4! items-stretch pt-4" onSubmit={submit}>
|
||||
<CodeEditor
|
||||
mode="markdown"
|
||||
className="h-44"
|
||||
title="Instructions here"
|
||||
content={system_prompt}
|
||||
placeholder="In this workspace, write for the marketing team: short, plain, no jargon."
|
||||
onChange={(value) => setSystemPrompt(value)}
|
||||
/>
|
||||
<CodeEditor
|
||||
mode="markdown"
|
||||
className="h-32"
|
||||
title="Memory here"
|
||||
content={memory}
|
||||
info="What it must remember in this workspace."
|
||||
onChange={(value) => setMemory(value)}
|
||||
/>
|
||||
<AddFormActions submit_label="Save" loading={loading} error={error} onCancel={onClose} />
|
||||
</Form>
|
||||
</AddModalShell>
|
||||
);
|
||||
}
|
||||
+5
-2
@@ -9,7 +9,7 @@ import postAdminApi from "@/src/layouts/bots/(functions)/post-admin-api";
|
||||
import SettingsGroup from "@/src/pages/admin/settings/(partials)/settings-group";
|
||||
import SettingsStatus from "@/src/pages/admin/settings/(partials)/settings-status";
|
||||
import InlineAddSelect from "@/src/components/general/inline-add-select";
|
||||
import WorkspaceBotModal from "./workspace-bot-modal";
|
||||
import WorkspaceBotModal from "@/src/components/general/workspace-bot-modal";
|
||||
import WorkspaceBotRow from "./workspace-bot-row";
|
||||
|
||||
type Props = {
|
||||
@@ -148,7 +148,10 @@ export default function WorkspaceBotsGroup({ workspace_id, workspace_name, bots:
|
||||
<WorkspaceBotModal
|
||||
workspace_id={workspace_id}
|
||||
workspace_name={workspace_name}
|
||||
bot={editing}
|
||||
bot_id={Number(editing.id)}
|
||||
bot_name={editing.name}
|
||||
system_prompt={editing.workspace_system_prompt}
|
||||
memory={editing.workspace_memory}
|
||||
onClose={() => setEditing(undefined)}
|
||||
onSaved={setBots}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user