Skip to content

Commit

Permalink
feat: add top level obot page to admin ui (#1993)
Browse files Browse the repository at this point in the history
* fix: ensure project inherits parent from copied project

- additionally ensure project IDs are converted properly when forwarding to the UI

Signed-off-by: Ryan Hopper-Lowe <[email protected]>

* feat: add top level obots page to admin ui

* chore: fix tests

* fix: invalid json tag on project type

* feat: add filtering to threads page by obotId in admin ui

* feat: add filters for obot children and parents in admin ui

* feat: enable filtering obots by userID

* feat: add ability to filter out spawned obots from obots page

* feat: add column filters to top level obot page

* chore: update tests in admin UI

* chore: generate new types

* chore: update obot page icon

---------

Signed-off-by: Ryan Hopper-Lowe <[email protected]>
  • Loading branch information
ryanhopperlowe authored Mar 8, 2025
1 parent 59db081 commit 35fdfc2
Show file tree
Hide file tree
Showing 25 changed files with 609 additions and 33 deletions.
2 changes: 2 additions & 0 deletions apiclient/types/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ type Project struct {
ProjectManifest
AssistantID string `json:"assistantID,omitempty"`
Editor bool `json:"editor"`
ParentID string `json:"parentID,omitempty"`
UserID string `json:"userID,omitempty"`
}

type ProjectManifest struct {
Expand Down
2 changes: 2 additions & 0 deletions pkg/api/handlers/projects.go
Original file line number Diff line number Diff line change
Expand Up @@ -494,8 +494,10 @@ func convertProject(thread *v1.Thread) types.Project {
ProjectManifest: types.ProjectManifest{
ThreadManifest: thread.Spec.Manifest,
},
ParentID: strings.Replace(thread.Spec.ParentThreadName, system.ThreadPrefix, system.ProjectPrefix, 1),
AssistantID: thread.Spec.AgentName,
Editor: thread.IsEditor(),
UserID: thread.Spec.UserID,
}
p.Type = "project"
p.ID = strings.Replace(p.ID, system.ThreadPrefix, system.ProjectPrefix, 1)
Expand Down
3 changes: 2 additions & 1 deletion pkg/api/handlers/threads.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/obot-platform/obot/pkg/api"
"github.com/obot-platform/obot/pkg/events"
v1 "github.com/obot-platform/obot/pkg/storage/apis/obot.obot.ai/v1"
"github.com/obot-platform/obot/pkg/system"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

Expand Down Expand Up @@ -49,7 +50,7 @@ func convertThread(thread v1.Thread) types.Thread {
LastRunID: thread.Status.LastRunName,
CurrentRunID: thread.Status.CurrentRunName,
State: state,
ProjectID: thread.Spec.ParentThreadName,
ProjectID: strings.Replace(thread.Spec.ParentThreadName, system.ThreadPrefix, system.ProjectPrefix, 1),
UserID: thread.Spec.UserID,
Abort: thread.Spec.Abort,
SystemTask: thread.Spec.SystemTask,
Expand Down
12 changes: 12 additions & 0 deletions pkg/storage/openapi/generated/openapi_generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 23 additions & 1 deletion ui/admin/app/components/composed/Filters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useNavigate, useSearchParams } from "react-router";
import { $path, Routes } from "safe-routes";

import { Agent } from "~/lib/model/agents";
import { Project } from "~/lib/model/project";
import { Task } from "~/lib/model/tasks";
import { User } from "~/lib/model/users";
import { RouteService } from "~/lib/service/routeService";
Expand All @@ -14,6 +15,8 @@ type QueryParams = {
agentId?: string;
userId?: string;
taskId?: string;
obotId?: string;
parentObotId?: string;
createdStart?: string;
createdEnd?: string;
};
Expand All @@ -22,11 +25,13 @@ export function Filters({
agentMap,
userMap,
taskMap,
projectMap,
url,
}: {
agentMap?: Map<string, Agent>;
userMap?: Map<string, User>;
taskMap?: Map<string, Task>;
projectMap?: Map<string, Project>;
url: keyof Routes;
}) {
const [searchParams] = useSearchParams();
Expand Down Expand Up @@ -85,8 +90,25 @@ export function Filters({
value: `${new Date(filters.createdStart).toLocaleDateString()} ${filters.createdEnd ? `- ${new Date(filters.createdEnd).toLocaleDateString()}` : ""}`,
onRemove: () => deleteFilters("createdStart", "createdEnd"),
},
"obotId" in filters &&
filters.obotId &&
projectMap && {
key: "obotId",
label: "Obot",
value: projectMap.get(filters.obotId)?.name ?? filters.obotId,
onRemove: () => deleteFilters("obotId"),
},
"parentObotId" in filters &&
filters.parentObotId &&
projectMap && {
key: "parentObotId",
label: "Spawned from",
value:
projectMap.get(filters.parentObotId)?.name ?? filters.parentObotId,
onRemove: () => deleteFilters("parentObotId"),
},
].filter((x) => !!x);
}, [navigate, searchParams, agentMap, userMap, taskMap, url]);
}, [url, searchParams, agentMap, userMap, taskMap, projectMap, navigate]);

return (
<div className="flex gap-2 pb-2">
Expand Down
1 change: 1 addition & 0 deletions ui/admin/app/components/composed/typography.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export function Truncate({
"line-clamp-2": clamp && clampLength === 2,
truncate: !clamp,
},
"break-all",
classNames?.content
)}
>
Expand Down
6 changes: 6 additions & 0 deletions ui/admin/app/components/sidebar/Sidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
BotIcon,
BotMessageSquareIcon,
BoxesIcon,
CpuIcon,
InfoIcon,
Expand Down Expand Up @@ -46,6 +47,11 @@ const items = [
url: $path("/agents"),
icon: BotIcon,
},
{
title: "Obots",
url: $path("/obots"),
icon: BotMessageSquareIcon,
},
{
title: "Chat Threads",
url: $path("/chat-threads"),
Expand Down
4 changes: 2 additions & 2 deletions ui/admin/app/components/thread/ThreadMeta.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -160,10 +160,10 @@ export function ThreadMeta({ entity, thread, className }: ThreadMetaProps) {
<td className="text-right">{thread.currentRunId}</td>
</tr>
)}
{thread.parentThreadId && (
{thread.projectID && (
<tr className="border-foreground/25">
<td className="py-2 pr-4 font-medium">Parent Thread ID</td>
<td className="text-right">{thread.parentThreadId}</td>
<td className="text-right">{thread.projectID}</td>
</tr>
)}
{thread.lastRunID && (
Expand Down
6 changes: 3 additions & 3 deletions ui/admin/app/components/tools/ToolCatalogGroup.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState } from "react";
import { Fragment, useState } from "react";

import {
ToolReference,
Expand Down Expand Up @@ -67,7 +67,7 @@ export function ToolCatalogGroup({
const configured = configuredTools.has(tool.id);

return (
<>
<Fragment key={tool.id}>
<ToolItem
key={tool.id}
tool={tool}
Expand Down Expand Up @@ -100,7 +100,7 @@ export function ToolCatalogGroup({
}
/>
))}
</>
</Fragment>
);
})}
</CommandGroup>
Expand Down
14 changes: 3 additions & 11 deletions ui/admin/app/components/user/UserMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { LogOutIcon, User } from "lucide-react";
import React from "react";

import { AuthDisabledUsername, CommonAuthProviderIds } from "~/lib/model/auth";
import { User as UserModel, roleLabel } from "~/lib/model/users";
import { AuthDisabledUsername } from "~/lib/model/auth";
import { getUserDisplayName, roleLabel } from "~/lib/model/users";
import { BootstrapApiService } from "~/lib/service/api/bootstrapApiService";
import { cn } from "~/lib/utils";

Expand Down Expand Up @@ -32,7 +32,7 @@ export const UserMenu: React.FC<UserMenuProps> = ({
return null;
}

const displayName = getDisplayName(me);
const displayName = getUserDisplayName(me);

return (
<DropdownMenu>
Expand Down Expand Up @@ -80,12 +80,4 @@ export const UserMenu: React.FC<UserMenuProps> = ({
</DropdownMenuContent>
</DropdownMenu>
);

function getDisplayName(user?: UserModel) {
if (user?.currentAuthProvider === CommonAuthProviderIds.GITHUB) {
return user.username;
}

return user?.email;
}
};
46 changes: 43 additions & 3 deletions ui/admin/app/hooks/useRouteInfo.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { useMemo } from "react";
import { Location, useLocation, useParams } from "react-router";
import { useCallback, useMemo } from "react";
import {
Location,
useLocation,
useParams,
useSearchParams,
} from "react-router";
import { Routes } from "safe-routes";

import { RouteService } from "~/lib/service/routeService";
import { QueryInfo, RouteService } from "~/lib/service/routeService";

const urlFromLocation = (location: Location) => {
const { pathname, search, hash } = location;
Expand All @@ -23,3 +29,37 @@ export function useUnknownPathParams() {
[url, params]
);
}

export function useQueryInfo<T extends keyof Routes>(route: T) {
const [searchParams, setSearchParams] = useSearchParams();

const params = useMemo(
() => RouteService.getQueryParams(route, searchParams.toString()),
[route, searchParams]
);

const update = useCallback(
<TKey extends keyof QueryInfo<T>>(
param: TKey,
value: QueryInfo<T>[TKey]
) => {
setSearchParams((prev) => {
prev.set(param as string, String(value));
return prev;
});
},
[setSearchParams]
);

const remove = useCallback(
(param: keyof QueryInfo<T>) => {
setSearchParams((prev) => {
prev.delete(param as string);
return prev;
});
},
[setSearchParams]
);

return { params, update, remove };
}
11 changes: 11 additions & 0 deletions ui/admin/app/lib/model/project.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { EntityMeta } from "~/lib/model/primitives";
import { ThreadManifest } from "~/lib/model/threads";

type ProjectManifest = ThreadManifest & {
parentID?: Nullish<string>;
assistantID: string;
editor: boolean;
userID: string;
};

export type Project = EntityMeta & ProjectManifest;
16 changes: 12 additions & 4 deletions ui/admin/app/lib/model/threads.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
import { RunState } from "@gptscript-ai/gptscript";

import { AgentIcons } from "~/lib/model/agents";
import { EntityMeta } from "~/lib/model/primitives";

export type ThreadBase = {
export type ThreadManifest = {
name: string;
description?: string;
tools?: string[];
icons?: Nullish<AgentIcons>;
revision?: string;
prompt: string;
knowledgeDescription: string;
introductionMessage: string;
starterMessages: Nullish<string[]>;
};

export type Thread = EntityMeta &
ThreadBase & {
ThreadManifest & {
state?: RunState;
currentRunId?: string;
parentThreadId?: string;
projectID?: string;
lastRunID?: string;
userID?: string;
project?: boolean;
Expand All @@ -20,4 +28,4 @@ export type Thread = EntityMeta &
| { agentID?: never; workflowID: string }
);

export type UpdateThread = ThreadBase;
export type UpdateThread = ThreadManifest;
10 changes: 9 additions & 1 deletion ui/admin/app/lib/model/users.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CommonAuthProviderId } from "~/lib/model/auth";
import { CommonAuthProviderId, CommonAuthProviderIds } from "~/lib/model/auth";
import { EntityMeta } from "~/lib/model/primitives";

export type User = EntityMeta & {
Expand Down Expand Up @@ -31,3 +31,11 @@ export const roleFromString = (role: string) => {

export const ExplicitAdminDescription =
"This user is explicitly set as an admin at the system level and their role cannot be changed.";

export function getUserDisplayName(user?: User) {
if (user?.currentAuthProvider === CommonAuthProviderIds.GITHUB) {
return user.username;
}

return user?.email;
}
5 changes: 5 additions & 0 deletions ui/admin/app/lib/routers/apiRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,11 @@ export const ApiRoutes = {
base: () => buildUrl("/prompt"),
promptResponse: () => buildUrl("/prompt"),
},
projects: {
getAll: () => buildUrl("/projects", { all: true }),
deleteProject: (agentId: string, projectId: string) =>
buildUrl(`/assistants/${agentId}/projects/${projectId}`),
},
runs: {
base: () => buildUrl("/runs"),
getRunById: (runId: string) => buildUrl(`/runs/${runId}`),
Expand Down
2 changes: 1 addition & 1 deletion ui/admin/app/lib/routers/baseRouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@ export const ApiUrl = () => {
return window.location.origin + "/api";
};

export const ConsumptionUrl = (route: string) => {
export const ConsumptionUrl = (route: string = "") => {
return window.location.protocol + "//" + window.location.host + route;
};
23 changes: 23 additions & 0 deletions ui/admin/app/lib/routers/userRoutes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import queryString from "query-string";

import { ConsumptionUrl } from "~/lib/routers/baseRouter";

const buildUrl = (path: string, params?: Record<string, string>) => {
const url = new URL(ConsumptionUrl(path));

if (params) {
url.search = queryString.stringify(params, { skipNull: true });
}

return {
url: url.toString(),
path: url.pathname,
};
};

export const UserRoutes = {
root: () => buildUrl("/"),
obot: (projectId: string) => buildUrl(`/o/${projectId}`),
thread: (projectId: string, threadId: string) =>
buildUrl(`/o/${projectId}/t/${threadId}`),
};
Loading

0 comments on commit 35fdfc2

Please sign in to comment.