diff --git a/front/components/assistant/AssistantDropdownMenu.tsx b/front/components/assistant/AssistantDropdownMenu.tsx
deleted file mode 100644
index 89b0a510dd5c..000000000000
--- a/front/components/assistant/AssistantDropdownMenu.tsx
+++ /dev/null
@@ -1,210 +0,0 @@
-import {
- Button,
- ChatBubbleBottomCenterTextIcon,
- ClipboardIcon,
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuLabel,
- DropdownMenuSeparator,
- DropdownMenuTrigger,
- EyeIcon,
- Icon,
- MoreIcon,
- PencilSquareIcon,
- StarIcon,
- StarStrokeIcon,
- TrashIcon,
-} from "@dust-tt/sparkle";
-import type {
- LightAgentConfigurationType,
- UserType,
- WorkspaceType,
-} from "@dust-tt/types";
-import { assertNever, isBuilder } from "@dust-tt/types";
-import { useRouter } from "next/router";
-import { useState } from "react";
-
-import { DeleteAssistantDialog } from "@app/components/assistant/DeleteAssistantDialog";
-import { useUpdateUserFavorite } from "@app/lib/swr/assistants";
-import { useUser } from "@app/lib/swr/user";
-import { setQueryParam } from "@app/lib/utils/router";
-
-interface AssistantDetailsMenuProps {
- agentConfiguration: LightAgentConfigurationType;
- owner: WorkspaceType;
- user: UserType;
- variant?: "button" | "plain";
- canDelete?: boolean;
- isMoreInfoVisible?: boolean;
- showAddRemoveToFavorite?: boolean;
-}
-
-export function AssistantDropdownMenu({
- agentConfiguration,
- owner,
- variant = "plain",
- canDelete,
- isMoreInfoVisible,
- showAddRemoveToFavorite = false,
-}: AssistantDetailsMenuProps) {
- const [showDeletionModal, setShowDeletionModal] = useState(false);
-
- const router = useRouter();
-
- const { user } = useUser();
- const { updateUserFavorite, isUpdatingFavorite } = useUpdateUserFavorite({
- owner,
- agentConfigurationId: agentConfiguration.sId,
- });
-
- if (
- !agentConfiguration ||
- agentConfiguration.status === "archived" ||
- !user
- ) {
- return <>>;
- }
-
- const isPrivate = agentConfiguration.scope === "private";
- if (isPrivate && agentConfiguration.versionAuthorId !== user.id) {
- return <>>;
- }
-
- const isAgentWorkspace = agentConfiguration.scope === "workspace";
- const isGlobalAgent = agentConfiguration.scope === "global";
-
- const isFavorite = agentConfiguration.userFavorite;
- const allowDeletion = canDelete && (isBuilder(owner) || !isAgentWorkspace);
-
- const updateFavorite = async (favorite: boolean) => {
- await updateUserFavorite(favorite);
- };
-
- const dropdownButton = (() => {
- switch (variant) {
- case "button":
- return (
-
- );
-
- case "plain":
- return (
-
-
-
- );
-
- default:
- assertNever(variant);
- }
- })();
-
- const onDeletionModalClose = () => {
- setShowDeletionModal(false);
- };
-
- return (
- <>
-
-
-
- {dropdownButton}
-
- {agentConfiguration.name}
-
- router.push(
- `/w/${owner.sId}/assistant/new?assistant=${agentConfiguration.sId}`
- )
- }
- />
- {isMoreInfoVisible ? (
- {
- e.stopPropagation();
- setQueryParam(
- router,
- "assistantDetails",
- agentConfiguration.sId
- );
- }}
- />
- ) : (
- {
- e.stopPropagation();
- await navigator.clipboard.writeText(agentConfiguration.sId);
- }}
- />
- )}
- {showAddRemoveToFavorite && (
- {
- e.stopPropagation();
- await updateFavorite(!isFavorite);
- }}
- />
- )}
- {!isGlobalAgent && (
- <>
-
- Edition
- {(isBuilder(owner) || !isAgentWorkspace) && (
- {
- e.stopPropagation();
- await router.push(
- `/w/${owner.sId}/builder/assistants/${agentConfiguration.sId}?flow=${isAgentWorkspace ? "workspace_assistants" : "personal_assistants"}`
- );
- }}
- />
- )}
- {
- e.stopPropagation();
- await router.push(
- `/w/${owner.sId}/builder/assistants/new?flow=personal_assistants&duplicate=${agentConfiguration.sId}`
- );
- }}
- />
- {allowDeletion && (
- setShowDeletionModal(true)}
- />
- )}
- >
- )}
-
-
- >
- );
-}
diff --git a/front/components/assistant/AssistantsTable.tsx b/front/components/assistant/AssistantsTable.tsx
new file mode 100644
index 000000000000..8800d3c0cbc5
--- /dev/null
+++ b/front/components/assistant/AssistantsTable.tsx
@@ -0,0 +1,418 @@
+import {
+ Avatar,
+ Button,
+ ClipboardIcon,
+ Cog6ToothIcon,
+ DataTable,
+ HandThumbDownIcon,
+ HandThumbUpIcon,
+ MagnifyingGlassIcon,
+ PencilSquareIcon,
+ Popup,
+ SliderToggle,
+ Tooltip,
+ TrashIcon,
+ UserIcon,
+} from "@dust-tt/sparkle";
+import type {
+ AgentConfigurationScope,
+ AgentUsageType,
+ LightAgentConfigurationType,
+ WorkspaceType,
+} from "@dust-tt/types";
+import { isBuilder, pluralize } from "@dust-tt/types";
+import type { CellContext, Row } from "@tanstack/react-table";
+import { useRouter } from "next/router";
+import { useMemo, useState } from "react";
+
+import { DeleteAssistantDialog } from "@app/components/assistant/DeleteAssistantDialog";
+import { assistantUsageMessage } from "@app/components/assistant/Usage";
+import { SCOPE_INFO } from "@app/components/assistant_builder/Sharing";
+import { classNames, formatTimestampToFriendlyDate } from "@app/lib/utils";
+
+export const ASSISTANT_MANAGER_TABS = [
+ // default shown tab = earliest in this list with non-empty agents
+ {
+ label: "Edited by me",
+ icon: UserIcon,
+ id: "current_user",
+ description: "Edited or created by you.",
+ },
+ {
+ label: "Company",
+ icon: SCOPE_INFO["workspace"].icon,
+ id: "workspace",
+ description: SCOPE_INFO["workspace"].text,
+ },
+ {
+ label: "Shared",
+ icon: SCOPE_INFO["published"].icon,
+ id: "published",
+ description: SCOPE_INFO["published"].text,
+ },
+ {
+ id: "global",
+ label: "Default Assistant",
+ icon: SCOPE_INFO["global"].icon,
+ description: SCOPE_INFO["global"].text,
+ },
+ {
+ label: "Searching across all assistants",
+ icon: MagnifyingGlassIcon,
+ id: "search",
+ description: "Searching across all assistants",
+ },
+] as const;
+
+export type AssistantManagerTabsType =
+ (typeof ASSISTANT_MANAGER_TABS)[number]["id"];
+
+type RowData = {
+ name: string;
+ description: string;
+ pictureUrl: string;
+ usage: AgentUsageType | undefined;
+ feedbacks: { up: number; down: number } | undefined;
+ lastUpdate: string | null;
+ scope: AgentConfigurationScope;
+ onClick?: () => void;
+};
+
+const calculateFeedback = (row: Row) => {
+ const feedbacks = row.original.feedbacks;
+ const totalFeedbacks = feedbacks ? feedbacks.up + feedbacks.down : 0;
+ return feedbacks && totalFeedbacks > 0 ? feedbacks.up / totalFeedbacks : 0;
+};
+
+const getTableColumns = () => {
+ return [
+ {
+ header: "Name",
+ accessorKey: "name",
+ cell: (info: CellContext) => (
+
+
+
+
+
+ {`@${info.getValue()}`}
+
+
+ {info.row.original.description}
+
+
+
+
+ ),
+ meta: { className: "h-16" },
+ },
+ {
+ header: "Messages",
+ accessorKey: "usage.messageCount",
+ cell: (info: CellContext) => (
+
+
+ {info.row.original.usage?.messageCount ?? 0}
+
+ }
+ />
+
+ ),
+ meta: {
+ width: "6rem",
+ },
+ },
+ {
+ header: "Feedbacks",
+ accessorKey: "feedbacks",
+ cell: (info: CellContext) => {
+ if (info.row.original.scope === "global") {
+ return "-";
+ }
+ const f = info.getValue();
+ if (f) {
+ const feedbacksCount = `${f.up + f.down} feedback${pluralize(f.up + f.down)} over the last 30 days`;
+ return (
+
+
+
+
+
+ }
+ />
+
+ );
+ }
+ },
+ sortingFn: (rowA: Row, rowB: Row) =>
+ calculateFeedback(rowA) - calculateFeedback(rowB),
+ meta: {
+ width: "6rem",
+ },
+ },
+ {
+ header: "Last Update",
+ accessorKey: "lastUpdate",
+ cell: (info: CellContext) => (
+
+ {info.getValue()
+ ? formatTimestampToFriendlyDate(info.getValue(), "short")
+ : "-"}
+
+ ),
+ meta: {
+ width: "10rem",
+ },
+ },
+ {
+ header: "",
+ accessorKey: "action",
+ cell: (info: CellContext) => (
+ {info.getValue()}
+ ),
+ meta: {
+ width: "0",
+ },
+ },
+ ];
+};
+
+type AgentsTableProps = {
+ owner: WorkspaceType;
+ agents: LightAgentConfigurationType[];
+ setShowDetails: (agent: LightAgentConfigurationType) => void;
+ handleToggleAgentStatus: (
+ agent: LightAgentConfigurationType
+ ) => Promise;
+ showDisabledFreeWorkspacePopup: string | null;
+ setShowDisabledFreeWorkspacePopup: (s: string | null) => void;
+};
+
+export function AssistantsTable({
+ owner,
+ agents,
+ setShowDetails,
+ handleToggleAgentStatus,
+ showDisabledFreeWorkspacePopup,
+ setShowDisabledFreeWorkspacePopup,
+}: AgentsTableProps) {
+ const [showDeleteDialog, setShowDeleteDialog] = useState<{
+ open: boolean;
+ agentConfiguration: LightAgentConfigurationType | undefined;
+ }>({
+ open: false,
+ agentConfiguration: undefined,
+ });
+
+ const router = useRouter();
+ const rows: RowData[] = useMemo(
+ () =>
+ agents.map((agentConfiguration) => {
+ return {
+ name: agentConfiguration.name,
+ usage: agentConfiguration.usage ?? {
+ messageCount: 0,
+ timePeriodSec: 30 * 24 * 60 * 60,
+ },
+ description: agentConfiguration.description,
+ pictureUrl: agentConfiguration.pictureUrl,
+ lastUpdate: agentConfiguration.versionCreatedAt,
+ feedbacks: agentConfiguration.feedbacks,
+ scope: agentConfiguration.scope,
+ action:
+ agentConfiguration.scope === "global" ? (
+
+ ) : undefined,
+ onClick: () => {
+ setShowDetails(agentConfiguration);
+ },
+ moreMenuItems:
+ agentConfiguration.scope !== "global"
+ ? [
+ {
+ label: "Edit",
+ icon: PencilSquareIcon,
+ onClick: (e: React.MouseEvent) => {
+ e.stopPropagation();
+ void router.push(
+ `/w/${owner.sId}/builder/assistants/${
+ agentConfiguration.sId
+ }?flow=${
+ agentConfiguration.scope
+ ? "workspace_assistants"
+ : "personal_assistants"
+ }`
+ );
+ },
+ },
+ {
+ label: "Copy assistant ID",
+ icon: ClipboardIcon,
+ onClick: (e: React.MouseEvent) => {
+ e.stopPropagation();
+ void navigator.clipboard.writeText(
+ agentConfiguration.sId
+ );
+ },
+ },
+ {
+ label: "Duplicate (New)",
+ icon: ClipboardIcon,
+ onClick: (e: React.MouseEvent) => {
+ e.stopPropagation();
+ void router.push(
+ `/w/${owner.sId}/builder/assistants/new?flow=personal_assistants&duplicate=${agentConfiguration.sId}`
+ );
+ },
+ },
+ {
+ label: "Delete",
+ icon: TrashIcon,
+ variant: "warning",
+ onClick: (e: React.MouseEvent) => {
+ e.stopPropagation();
+ setShowDeleteDialog({ open: true, agentConfiguration });
+ },
+ },
+ ]
+ : [],
+ };
+ }),
+ [
+ agents,
+ handleToggleAgentStatus,
+ owner,
+ router,
+ setShowDetails,
+ setShowDisabledFreeWorkspacePopup,
+ showDisabledFreeWorkspacePopup,
+ ]
+ );
+
+ return (
+ <>
+ {
+ setShowDeleteDialog(({ agentConfiguration }) => ({
+ open: false,
+ agentConfiguration,
+ }));
+ }}
+ isPrivateAssistant={
+ showDeleteDialog.agentConfiguration?.scope === "private"
+ }
+ />
+
+ {rows.length > 0 && (
+
+ )}
+
+ >
+ );
+}
+
+function GlobalAgentAction({
+ agent,
+ owner,
+ handleToggleAgentStatus,
+ showDisabledFreeWorkspacePopup,
+ setShowDisabledFreeWorkspacePopup,
+}: {
+ agent: LightAgentConfigurationType;
+ owner: WorkspaceType;
+ handleToggleAgentStatus: (
+ agent: LightAgentConfigurationType
+ ) => Promise;
+ showDisabledFreeWorkspacePopup: string | null;
+ setShowDisabledFreeWorkspacePopup: (s: string | null) => void;
+}) {
+ const router = useRouter();
+ if (agent.sId === "helper") {
+ return null;
+ }
+
+ if (agent.sId === "dust") {
+ return (
+
+
+ );
+ }
+
+ return (
+
+
{
+ e.stopPropagation();
+ await handleToggleAgentStatus(agent);
+ }}
+ selected={agent.status === "active"}
+ disabled={agent.status === "disabled_missing_datasource"}
+ />
+ e.stopPropagation()}>
+
{
+ void router.push(`/w/${owner.sId}/subscription`);
+ }}
+ onClose={() => {
+ setShowDisabledFreeWorkspacePopup(null);
+ }}
+ />
+
+
+ );
+}
diff --git a/front/components/assistant/DeleteAssistantDialog.tsx b/front/components/assistant/DeleteAssistantDialog.tsx
index b211e9a4839f..211f60686683 100644
--- a/front/components/assistant/DeleteAssistantDialog.tsx
+++ b/front/components/assistant/DeleteAssistantDialog.tsx
@@ -19,7 +19,7 @@ import {
} from "@app/lib/swr/assistants";
interface DeleteAssistantDialogProps {
- agentConfiguration: LightAgentConfigurationType;
+ agentConfiguration?: LightAgentConfigurationType;
isOpen: boolean;
isPrivateAssistant?: boolean;
onClose: () => void;
@@ -34,7 +34,7 @@ export function DeleteAssistantDialog({
owner,
}: DeleteAssistantDialogProps) {
const agentUsage = useAgentUsage({
- agentConfigurationId: agentConfiguration.sId,
+ agentConfigurationId: agentConfiguration?.sId ?? null,
disabled: !isOpen,
workspaceId: owner.sId,
});
diff --git a/front/components/assistant/SearchOrderDropdown.tsx b/front/components/assistant/SearchOrderDropdown.tsx
deleted file mode 100644
index 03c1d16c0a11..000000000000
--- a/front/components/assistant/SearchOrderDropdown.tsx
+++ /dev/null
@@ -1,51 +0,0 @@
-import {
- Button,
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuTrigger,
-} from "@dust-tt/sparkle";
-
-export const SearchOrder = ["name", "usage", "edited_at"] as const;
-export type SearchOrderType = (typeof SearchOrder)[number];
-
-const prettyfiedSearchOrder: { [key in SearchOrderType]: string } = {
- name: "Name",
- usage: "Usage",
- edited_at: "Last edited at",
-};
-
-interface SearchOrderDropdownProps {
- orderBy: SearchOrderType;
- setOrderBy: (orderBy: SearchOrderType) => void;
- disabled?: boolean;
-}
-
-export function SearchOrderDropdown({
- orderBy,
- setOrderBy,
- disabled,
-}: SearchOrderDropdownProps) {
- return (
-
-
-
-
-
- {SearchOrder.map((order) => (
- setOrderBy(order)}
- />
- ))}
-
-
- );
-}
diff --git a/front/lib/api/assistant/configuration.ts b/front/lib/api/assistant/configuration.ts
index e1d752d1e513..3050a4b6e787 100644
--- a/front/lib/api/assistant/configuration.ts
+++ b/front/lib/api/assistant/configuration.ts
@@ -177,6 +177,7 @@ function determineGlobalAgentIdsToFetch(
case "workspace":
case "published":
case "archived":
+ case "current_user":
return []; // fetch no global agents
case "global":
case "list":
@@ -272,6 +273,24 @@ async function fetchWorkspaceAgentConfigurationsWithoutActions(
...baseAgentsSequelizeQuery,
where: baseWhereConditions,
});
+ case "current_user":
+ const authorId = auth.getNonNullableUser().id;
+ const r = await AgentConfiguration.findAll({
+ attributes: ["sId"],
+ group: "sId",
+ where: {
+ workspaceId: owner.id,
+ authorId,
+ },
+ });
+
+ return AgentConfiguration.findAll({
+ ...baseAgentsSequelizeQuery,
+ where: {
+ ...baseWhereConditions,
+ sId: { [Op.in]: [...new Set(r.map((r) => r.sId))] },
+ },
+ });
case "archived":
// Get the latest version of all archived agents.
// For each sId, we want to fetch the one with the highest version, only if it's status is "archived".
diff --git a/front/lib/resources/agent_message_feedback_resource.ts b/front/lib/resources/agent_message_feedback_resource.ts
index b53448983ae0..7e1e5dbf0197 100644
--- a/front/lib/resources/agent_message_feedback_resource.ts
+++ b/front/lib/resources/agent_message_feedback_resource.ts
@@ -9,12 +9,17 @@ import type {
UserType,
WorkspaceType,
} from "@dust-tt/types";
-import { GLOBAL_AGENTS_SID } from "@dust-tt/types";
-import { Err, Ok } from "@dust-tt/types";
-import type { Attributes, ModelStatic, WhereOptions } from "sequelize";
-import type { CreationAttributes, Transaction } from "sequelize";
+import { Err, GLOBAL_AGENTS_SID, Ok } from "@dust-tt/types";
+import type {
+ Attributes,
+ CreationAttributes,
+ ModelStatic,
+ Transaction,
+ WhereOptions,
+} from "sequelize";
import { Op } from "sequelize";
+import type { AgentMessageFeedbackDirection } from "@app/lib/api/assistant/conversation/feedbacks";
import type { PaginationParams } from "@app/lib/api/pagination";
import type { Authenticator } from "@app/lib/auth";
import { AgentConfiguration } from "@app/lib/models/assistant/agent";
@@ -235,6 +240,33 @@ export class AgentMessageFeedbackResource extends BaseResource {
+ if (!agentConfiguration) {
+ return;
+ }
const res = await fetch(
`/api/w/${owner.sId}/assistant/agent_configurations/${agentConfiguration.sId}`,
{
diff --git a/front/pages/api/w/[wId]/assistant/agent_configurations/index.ts b/front/pages/api/w/[wId]/assistant/agent_configurations/index.ts
index 9cfc4f93868b..3cef3d3761a3 100644
--- a/front/pages/api/w/[wId]/assistant/agent_configurations/index.ts
+++ b/front/pages/api/w/[wId]/assistant/agent_configurations/index.ts
@@ -33,6 +33,7 @@ import { getAgentsRecentAuthors } from "@app/lib/api/assistant/recent_authors";
import { withSessionAuthenticationForWorkspace } from "@app/lib/api/auth_wrappers";
import { runOnRedis } from "@app/lib/api/redis";
import type { Authenticator } from "@app/lib/auth";
+import { AgentMessageFeedbackResource } from "@app/lib/resources/agent_message_feedback_resource";
import { AppResource } from "@app/lib/resources/app_resource";
import { KillSwitchResource } from "@app/lib/resources/kill_switch_resource";
import { ServerSideTracking } from "@app/lib/tracking/server";
@@ -79,7 +80,7 @@ async function handler(
});
}
- const { view, limit, withUsage, withAuthors, sort } =
+ const { view, limit, withUsage, withAuthors, withFeedbacks, sort } =
queryValidation.right;
let viewParam = view ? view : "all";
// @ts-expect-error: added for backwards compatibility
@@ -132,20 +133,42 @@ async function handler(
auth,
agents: agentConfigurations,
});
- agentConfigurations = await Promise.all(
- agentConfigurations.map(
- async (
- agentConfiguration,
- index
- ): Promise => {
- return {
- ...agentConfiguration,
- lastAuthors: recentAuthors[index],
- };
- }
- )
+ agentConfigurations = agentConfigurations.map(
+ (agentConfiguration, index) => {
+ return {
+ ...agentConfiguration,
+ lastAuthors: recentAuthors[index],
+ };
+ }
);
}
+ if (withFeedbacks === "true") {
+ const feedbacks =
+ await AgentMessageFeedbackResource.getFeedbackCountForAssistants(
+ auth,
+ agentConfigurations
+ .filter((agent) => agent.scope !== "global")
+ .map((agent) => agent.sId),
+ 30
+ );
+ agentConfigurations = agentConfigurations.map((agentConfiguration) => ({
+ ...agentConfiguration,
+ feedbacks: {
+ up:
+ feedbacks.find(
+ (f) =>
+ f.agentConfigurationId === agentConfiguration.sId &&
+ f.thumbDirection === "up"
+ )?.count ?? 0,
+ down:
+ feedbacks.find(
+ (f) =>
+ f.agentConfigurationId === agentConfiguration.sId &&
+ f.thumbDirection === "down"
+ )?.count ?? 0,
+ },
+ }));
+ }
return res.status(200).json({
agentConfigurations,
diff --git a/front/pages/w/[wId]/builder/assistants/index.tsx b/front/pages/w/[wId]/builder/assistants/index.tsx
index c7ec63f47178..b31ec22f2bb8 100644
--- a/front/pages/w/[wId]/builder/assistants/index.tsx
+++ b/front/pages/w/[wId]/builder/assistants/index.tsx
@@ -1,40 +1,32 @@
import {
- Avatar,
Button,
- Cog6ToothIcon,
- ContextItem,
Page,
PlusIcon,
- Popup,
RobotIcon,
SearchInput,
- SliderToggle,
Tabs,
TabsList,
TabsTrigger,
useHashParam,
} from "@dust-tt/sparkle";
import type {
- AgentConfigurationScope,
AgentsGetViewType,
LightAgentConfigurationType,
SubscriptionType,
WorkspaceType,
} from "@dust-tt/types";
-import { AGENT_CONFIGURATION_SCOPES } from "@dust-tt/types";
-import { assertNever, isBuilder } from "@dust-tt/types";
import type { InferGetServerSidePropsType } from "next";
import Link from "next/link";
-import { useRouter } from "next/router";
import { useEffect, useMemo, useRef, useState } from "react";
import { AssistantDetails } from "@app/components/assistant/AssistantDetails";
+import type { AssistantManagerTabsType } from "@app/components/assistant/AssistantsTable";
+import {
+ ASSISTANT_MANAGER_TABS,
+ AssistantsTable,
+} from "@app/components/assistant/AssistantsTable";
import { ConversationsNavigationProvider } from "@app/components/assistant/conversation/ConversationsNavigationProvider";
import { AssistantSidebarMenu } from "@app/components/assistant/conversation/SidebarMenu";
-import type { SearchOrderType } from "@app/components/assistant/SearchOrderDropdown";
-import { SearchOrderDropdown } from "@app/components/assistant/SearchOrderDropdown";
-import { assistantUsageMessage } from "@app/components/assistant/Usage";
-import { SCOPE_INFO } from "@app/components/assistant_builder/Sharing";
import { EmptyCallToAction } from "@app/components/EmptyCallToAction";
import AppLayout from "@app/components/sparkle/AppLayout";
import { withDefaultUserAuthRequirements } from "@app/lib/iam/session";
@@ -44,7 +36,7 @@ import { subFilter } from "@app/lib/utils";
export const getServerSideProps = withDefaultUserAuthRequirements<{
owner: WorkspaceType;
subscription: SubscriptionType;
- tabScope: AgentConfigurationScope;
+ tabScope: AssistantManagerTabsType;
}>(async (context, auth) => {
const owner = auth.workspace();
const subscription = auth.subscription();
@@ -54,10 +46,11 @@ export const getServerSideProps = withDefaultUserAuthRequirements<{
notFound: true,
};
}
- const tabScope = Object.keys(SCOPE_INFO).includes(
- context.query.tabScope as AgentConfigurationScope
+
+ const tabScope = ASSISTANT_MANAGER_TABS.map((tab) => tab.id).includes(
+ context.query.tabScope as AssistantManagerTabsType
)
- ? (context.query.tabScope as AgentConfigurationScope)
+ ? (context.query.tabScope as AssistantManagerTabsType)
: "workspace";
return {
props: {
@@ -68,8 +61,10 @@ export const getServerSideProps = withDefaultUserAuthRequirements<{
};
});
-function isValidTab(tab: string): tab is AgentConfigurationScope {
- return AGENT_CONFIGURATION_SCOPES.includes(tab as AgentConfigurationScope);
+function isValidTab(tab?: string): tab is AssistantManagerTabsType {
+ return ASSISTANT_MANAGER_TABS.map((tab) => tab.id).includes(
+ tab as AssistantManagerTabsType
+ );
}
export default function WorkspaceAssistants({
@@ -78,23 +73,17 @@ export default function WorkspaceAssistants({
subscription,
}: InferGetServerSidePropsType) {
const [assistantSearch, setAssistantSearch] = useState("");
- const [orderBy, setOrderBy] = useState("name");
const [showDisabledFreeWorkspacePopup, setShowDisabledFreeWorkspacePopup] =
useState(null);
- const [selectedTab, setSelectedTab] = useHashParam("tabScope", "workspace");
+ const [selectedTab, setSelectedTab] = useHashParam("tabScope", tabScope);
- const includes: ("authors" | "usage")[] = (() => {
- switch (tabScope) {
- case "published":
- return ["authors", "usage"];
- case "private":
- case "global":
- case "workspace":
- return ["usage"];
- default:
- assertNever(tabScope);
+ const activeTab = useMemo(() => {
+ if (assistantSearch.trim() !== "") {
+ return "search";
}
- })();
+
+ return selectedTab && isValidTab(selectedTab) ? selectedTab : "workspace";
+ }, [assistantSearch, selectedTab]);
// only fetch the agents that are relevant to the current scope, except when
// user searches: search across all agents
@@ -105,26 +94,23 @@ export default function WorkspaceAssistants({
} = useAgentConfigurations({
workspaceId: owner.sId,
agentsGetView:
- selectedTab === "private" ? "list" : (selectedTab as AgentsGetViewType),
- includes,
+ activeTab === "search" ? "list" : (activeTab as AgentsGetViewType),
+ includes: ["usage", "feedbacks"],
});
- const { agentConfigurations: searchableAgentConfigurations } =
- useAgentConfigurations({
- workspaceId: owner.sId,
- agentsGetView: assistantSearch ? "list" : null,
- });
-
- const filteredAgents = (
- assistantSearch ? searchableAgentConfigurations : agentConfigurations
- ).filter((a) => {
- return (
- // filter by tab only if no search
- (assistantSearch || a.scope === selectedTab) &&
- subFilter(assistantSearch.toLowerCase(), a.name.toLowerCase())
- );
+ const filteredAgents = agentConfigurations.filter((a) => {
+ if (assistantSearch && assistantSearch.trim() !== "") {
+ return subFilter(
+ assistantSearch.trim().toLowerCase(),
+ a.name.toLowerCase()
+ );
+ } else if (activeTab === "current_user") {
+ return true;
+ } else {
+ return a.scope === activeTab;
+ }
});
-
+ console.log(activeTab, filteredAgents.length);
const [showDetails, setShowDetails] =
useState(null);
@@ -159,13 +145,18 @@ export default function WorkspaceAssistants({
await mutateAgentConfigurations();
};
- const tabs = (
- ["workspace", "published", "private", "global"] as AgentConfigurationScope[]
- ).map((scope) => ({
- label: SCOPE_INFO[scope].shortLabel,
- icon: SCOPE_INFO[scope].icon,
- scope,
- }));
+
+ // if search is active, only show the search tab, otherwise show all tabs with agents except the search tab
+ const visibleTabs = useMemo(() => {
+ const searchTab = ASSISTANT_MANAGER_TABS.find((tab) => tab.id === "search");
+ if (!searchTab) {
+ throw new Error("Unexpected: Search tab not found");
+ }
+
+ return assistantSearch.trim() !== ""
+ ? [searchTab]
+ : ASSISTANT_MANAGER_TABS.filter((tab) => tab.id !== "search");
+ }, [assistantSearch]);
const disabledTablineClass =
"!border-element-500 !text-element-500 !cursor-default";
@@ -188,44 +179,6 @@ export default function WorkspaceAssistants({
};
}, []);
- switch (orderBy) {
- case "name": {
- filteredAgents.sort((a, b) => {
- return a.name.localeCompare(b.name);
- });
- break;
- }
- case "usage": {
- filteredAgents.sort((a, b) => {
- return (b.usage?.messageCount || 0) - (a.usage?.messageCount || 0);
- });
- break;
- }
- case "edited_at":
- filteredAgents.sort((a, b) => {
- const dateA = a.versionCreatedAt
- ? new Date(a.versionCreatedAt).getTime()
- : -Infinity;
- const dateB = b.versionCreatedAt
- ? new Date(b.versionCreatedAt).getTime()
- : -Infinity;
- return dateB - dateA;
- });
- break;
- default:
- assertNever(orderBy);
- }
-
- useEffect(() => {
- if (tabScope === "global") {
- setOrderBy("name");
- }
- }, [tabScope]);
-
- const activeTab = useMemo(() => {
- return selectedTab && isValidTab(selectedTab) ? selectedTab : "workspace";
- }, [selectedTab]);
-
return (
- {tabs.map((tab) => (
+ {visibleTabs.map((tab) => (
setSelectedTab(tab.scope)}
+ onClick={() => !assistantSearch && setSelectedTab(tab.id)}
/>
))}
-
-
-
- {assistantSearch
- ? "Searching across all assistants"
- : SCOPE_INFO[activeTab].text}
+ {
+ ASSISTANT_MANAGER_TABS.find((tab) => tab.id === activeTab)
+ ?.description
+ }
{filteredAgents.length > 0 || isAgentConfigurationsLoading ? (
-
);
}
-
-function AgentViewForScope({
- owner,
- agents,
- scopeView,
- setShowDetails,
- handleToggleAgentStatus,
- showDisabledFreeWorkspacePopup,
- setShowDisabledFreeWorkspacePopup,
-}: {
- owner: WorkspaceType;
- agents: LightAgentConfigurationType[];
- scopeView: AgentConfigurationScope | "search-view";
- setShowDetails: (agent: LightAgentConfigurationType) => void;
- handleToggleAgentStatus: (
- agent: LightAgentConfigurationType
- ) => Promise;
- showDisabledFreeWorkspacePopup: string | null;
- setShowDisabledFreeWorkspacePopup: (s: string | null) => void;
-}) {
- const router = useRouter();
-
- return (
-
- {agents.map((agent) => (
- }
- onClick={() => setShowDetails(agent)}
- action={
- agent.scope === "global" ? (
-
- ) : null
- }
- >
-
-
- {agent.description}
-
-
-
- ))}
-
- );
-
- function GlobalAgentAction({
- agent,
- }: {
- agent: LightAgentConfigurationType;
- }) {
- if (agent.sId === "helper") {
- return null;
- }
-
- if (agent.sId === "dust") {
- return (
-