Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions backend/apps/agent_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -837,6 +837,9 @@ async def list_agent_page_api(
search_tag_predicates: Optional[str] = Query(None, description="Text-search tag predicates as JSON"),
page: int = Query(1, ge=1, description="Page number starting from 1"),
page_size: int = Query(20, ge=1, le=100, description="Items per page"),
include_repository_info: bool = Query(
False, description="Include repository listings for agents on this page"
),
authorization: Optional[str] = Header(None),
request: Request = None,
):
Expand All @@ -852,11 +855,13 @@ async def list_agent_page_api(
kwargs = {
"tenant_id": resolved_tenant_id,
"user_id": user_id,
"caller_tenant_id": auth_tenant_id,
"permission": permission,
"tag": tag,
"search": search,
"page": page,
"page_size": page_size,
"include_repository_info": include_repository_info,
}
if created_by:
kwargs["created_by"] = created_by
Expand Down
9 changes: 7 additions & 2 deletions backend/database/agent_repository_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -354,14 +354,15 @@ def list_agent_repository_by_agent_ids(
*,
statuses: Collection[str],
publisher_tenant_id: str,
publisher_user_id: Optional[str] = None,
) -> List[dict]:
"""List repository rows for the given agents, scoped to publisher tenant and statuses."""
if not agent_ids:
return []

status_list = list(statuses)
with get_db_session() as session:
rows = (
query = (
session.query(
AgentRepository.agent_repository_id,
AgentRepository.agent_id,
Expand All @@ -377,7 +378,11 @@ def list_agent_repository_by_agent_ids(
AgentRepository.agent_id.in_(agent_ids),
AgentRepository.status.in_(status_list),
)
.order_by(
)
if publisher_user_id is not None:
query = query.filter(AgentRepository.publisher_user_id == publisher_user_id)
rows = (
query.order_by(
AgentRepository.agent_id,
AgentRepository.create_time.desc(),
)
Expand Down
70 changes: 70 additions & 0 deletions backend/management/services/agent/management.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from agents.create_agent_info import create_tool_config_list
from utils.agent_transfer_utils import portable_tool_params, validate_import_tool_params
from services.agent_version_service import publish_version_impl
from consts.agent_repository import STATUS_PENDING_REVIEW, STATUS_REJECTED, STATUS_SHARED
from consts.const import TOOL_TYPE_MAPPING, \
MODEL_CONFIG_MAPPING, CAN_EDIT_ALL_USER_ROLES, PERMISSION_PRIVATE
from consts.exceptions import (
Expand Down Expand Up @@ -67,6 +68,7 @@
)
from database import skill_db
from management.services.skill.service import SkillService
from database.agent_repository_db import list_agent_repository_by_agent_ids
from database.agent_version_db import batch_search_version_names, query_version_list
from database.group_db import query_group_ids_by_user
from database.user_tenant_db import get_user_tenant_by_user_id
Expand Down Expand Up @@ -920,18 +922,20 @@


async def list_agent_page_impl(
tenant_id: str,
user_id: str,
caller_tenant_id: Optional[str] = None,
permission: Optional[str] = None,
tag: Optional[str] = None,
search: Optional[str] = None,
page: int = 1,
page_size: int = 20,
additional_tenant_id: Optional[str] = None,
created_by: Optional[str] = None,
created_by_not: Optional[str] = None,
tag_predicates: Optional[list] = None,
search_tag_predicates: Optional[list] = None,
include_repository_info: bool = False,

Check warning on line 938 in backend/management/services/agent/management.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Function "list_agent_page_impl" has 14 parameters, which is greater than the 13 authorized.

See more on https://sonarcloud.io/project/issues?id=ModelEngine-Group_nexent&issues=AaDsshImu4HR_mn1602y&open=AaDsshImu4HR_mn1602y&pullRequest=4039
) -> Dict[str, Any]:
"""List visible agents with server-side filters and pagination."""
if created_by and created_by_not:
Expand Down Expand Up @@ -1127,6 +1131,72 @@
)
agent["version_label"] = version.get("version_name")
agent["version_create_time"] = version.get("create_time")
if include_repository_info:
for scope_tenant_id in tenant_ids:
scoped_agents = [
agent for scope, agent in paged_scoped_agents
if scope == scope_tenant_id
]
if not scoped_agents:
continue
scoped_agent_ids = [int(agent["agent_id"]) for agent in scoped_agents]
shared_records = list_agent_repository_by_agent_ids(
scoped_agent_ids,
statuses=(STATUS_SHARED,),
publisher_tenant_id=scope_tenant_id,
)
publisher_records = []
if scope_tenant_id == caller_tenant_id and user_role == "ADMIN":
publisher_records = list_agent_repository_by_agent_ids(
scoped_agent_ids,
statuses=(STATUS_PENDING_REVIEW, STATUS_REJECTED, STATUS_SHARED),
publisher_tenant_id=scope_tenant_id,
)
elif scope_tenant_id == caller_tenant_id and user_role == "DEV":
publisher_records = list_agent_repository_by_agent_ids(
scoped_agent_ids,
statuses=(STATUS_PENDING_REVIEW, STATUS_REJECTED, STATUS_SHARED),
publisher_tenant_id=scope_tenant_id,
publisher_user_id=user_id,
)
records_by_id = {
int(record["agent_repository_id"]): (record, False)
for record in shared_records
if record["status"] == STATUS_SHARED
}
records_by_id.update({
int(record["agent_repository_id"]): (record, True)
for record in publisher_records
})
repository_by_agent_id: dict[int, list[dict]] = {}
for record, is_publisher in records_by_id.values():
created_at = record.get("create_time")
repository_by_agent_id.setdefault(int(record["agent_id"]), []).append(
{
"agent_repository_id": record["agent_repository_id"],
"status": record["status"],
"version_no": record["version_no"],
"version_label": record.get("version_name"),
"create_time": (
created_at.isoformat()
if hasattr(created_at, "isoformat")
else created_at
),
"content": record.get("content") if is_publisher else None,
}
)
for records in repository_by_agent_id.values():
records.sort(
key=lambda item: (
str(item["create_time"] or ""),
int(item["agent_repository_id"]),
),
reverse=True,
)
for agent in scoped_agents:
agent["repository_info"] = repository_by_agent_id.get(
int(agent["agent_id"]), []
)
paged_agents = [agent for _, agent in paged_scoped_agents]
return {
"items": paged_agents,
Expand Down
63 changes: 25 additions & 38 deletions frontend/app/[locale]/agent-space/components/MyAgentCard.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
"use client";

import { Button, Dropdown, Spin, Tooltip } from "antd";
import { Button, Dropdown, Tooltip } from "antd";
import type { MenuProps } from "antd";
import { useState } from "react";
import { useAgentRepositoryListings } from "@/hooks/agentRepository/useAgentRepositoryListings";
import {
ClipboardCheck,
Clock,
Expand All @@ -21,7 +19,7 @@
import {
formatMineDate,
getMineCardMenuActions,
toMineRepositoryInfo,
getMineCardRepositoryStatusBadge,
type MineCardMenuAction,
} from "@/lib/agentRepositoryMine";
import type { MyEditableAgentItem } from "@/types/agentRepository";
Expand Down Expand Up @@ -69,16 +67,6 @@
isDeleting = false,
}: MyAgentCardProps) {
const { t } = useTranslation("common");
const [menuOpen, setMenuOpen] = useState(false);
const {
data: listingData,
isLoading: isListingLoading,
isError: isListingError,
refetch,
} = useAgentRepositoryListings(
{ agent_id: agent.agent_id, page: 1, page_size: 100 },
menuOpen || guideMenuOpen === true
);

const title = agent.name?.trim() || t("agentRepository.card.untitled");
const description =
Expand All @@ -88,8 +76,6 @@
agent.unavailable_reasons ?? [],
t
);
const repositoryInfo = toMineRepositoryInfo(listingData?.items ?? []);
const agentWithRepository = { ...agent, repository_info: repositoryInfo };
const { canOpen: published } = getAgentUsageGuideAccess({
currentVersionNo: agent.current_version_no,
permission: agent.permission,
Expand All @@ -99,9 +85,10 @@
const canEdit = agent.permission !== "READ_ONLY";
const canView = (agent.current_version_no ?? 0) > 0;
const canEvaluate = canView;
const menuActions = listingData
? getMineCardMenuActions(agentWithRepository)
: [];
const menuActions = getMineCardMenuActions(agent);
const repositoryBadge = getMineCardRepositoryStatusBadge(
agent.repository_info
);

const menuItems: MenuProps["items"] = menuActions.map((action) => {
const icon =
Expand All @@ -122,29 +109,13 @@
return;
}
onViewReview(
agentWithRepository,
agent,
action === "reviewUpdate" ? "reviewUpdate" : "review"
);
},
};
});

if (isListingLoading) {
menuItems.unshift({
key: "loading",
label: <Spin size="small" />,
disabled: true,
});
} else if (isListingError) {
menuItems.unshift({
key: "retry",
label: t("repository.common.retry"),
onClick: () => {
void refetch();
},
});
}

if (canEvaluate) {
menuItems.push({
key: "evaluate",
Expand Down Expand Up @@ -223,7 +194,7 @@
<Dropdown
menu={{ items: menuItems }}
open={guideMenuOpen}
onOpenChange={onGuideMenuOpenChange ?? setMenuOpen}
onOpenChange={onGuideMenuOpenChange}
trigger={["click"]}
>
<Button
Expand All @@ -236,7 +207,23 @@
/>
</Dropdown>
) : null}
<div className="flex items-center gap-1.5">
<div className="flex flex-wrap items-center justify-end gap-1.5">
{repositoryBadge ? (
<span
className={`rounded-md px-1.5 py-0.5 text-[11px] font-medium ${
repositoryBadge.variant === "pending"
? "bg-amber-50 text-amber-700 dark:bg-amber-500/10 dark:text-amber-300"
: repositoryBadge.variant === "rejected"
? "bg-rose-50 text-rose-700 dark:bg-rose-500/10 dark:text-rose-300"
: "bg-blue-50 text-blue-700 dark:bg-blue-500/10 dark:text-blue-300"

Check warning on line 218 in frontend/app/[locale]/agent-space/components/MyAgentCard.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested ternary operation into an independent statement.

See more on https://sonarcloud.io/project/issues?id=ModelEngine-Group_nexent&issues=AaDsf_FGl7nS8i7vgiqF&open=AaDsf_FGl7nS8i7vgiqF&pullRequest=4039
}`}
>
{t(repositoryBadge.labelKey)}
{repositoryBadge.versionLabel
? ` · ${repositoryBadge.versionLabel}`
: null}
</span>
) : null}
{agent.is_available === false ? (
<Tooltip
title={
Expand Down
13 changes: 3 additions & 10 deletions frontend/app/[locale]/agent-space/my-agent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ export function MyAgent({
(): AgentListFilters => ({
tenantId: user?.tenantId ?? null,
enabled: active,
includeRepositoryInfo: true,
page,
pageSize,
search: searchQuery.trim() || undefined,
Expand Down Expand Up @@ -697,15 +698,7 @@ export function MyAgent({
}`}
>
{t(ownershipLabelKey[filter])}
<span
className={`rounded px-1.5 text-xs ${
ownership === filter
? "bg-white/20"
: "bg-white/70 text-slate-500 dark:bg-slate-900/50 dark:text-slate-400"
}`}
>
{counts[filter]}
</span>
<span className="text-xs opacity-80">{counts[filter]}</span>
</button>
))}
</div>
Expand Down Expand Up @@ -888,7 +881,7 @@ function toMyAgentItem(agent: Agent): MyEditableAgentItem {
version_create_time: agent.version_create_time ?? null,
permission: agent.permission,
tags: agent.tags,
repository_info: [],
repository_info: agent.repository_info ?? [],
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,7 @@ export default function McpToolsSearchFilterBar({
}`}
>
<span>{item.label}</span>
<span
className={`ml-1.5 rounded-full px-1.5 text-xs ${
selected ? "bg-white/20 text-white" : "bg-white text-slate-500"
}`}
>
{item.count}
</span>
<span className="ml-1 text-xs opacity-80">{item.count}</span>
</button>
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,11 @@ export default function MineMcpServiceCard({
<Button
type="text"
size="small"
className="!text-slate-600 hover:!bg-transparent hover:!text-blue-500"
className={
isEnabled
? "!bg-emerald-50 !text-emerald-700 hover:!bg-emerald-100 hover:!text-emerald-800 dark:!bg-emerald-900/30 dark:!text-emerald-300 dark:hover:!bg-emerald-900/50"
: "!text-slate-600 hover:!bg-transparent hover:!text-blue-500"
}
loading={toggling}
icon={<Power className="size-3.5" />}
onClick={() => onToggle(localService)}
Expand Down
Loading
Loading