Files
deerflow-code/frontend-web/src/core/agents/api.ts
T
吉鑫升andClaude Opus 4.7 646cb8caeb feat: 标签管理功能 + 问答前缀对用户隐藏
标签管理:
- 新增 tags/tag_assignments 持久化层、迁移、/api/tags 路由
- 标签分智能体/技能/定时任务三类, 仅同类型可互打, 仅 admin 可增删改与打标签
- 三类资源列表支持按名称模糊检索与按标签筛选(走后端查询参数)
- 前端新增标签管理页、TagPickerDialog(点击即时绑定)、彩色标签 chip

问答前缀:
- 前缀不再拼入消息 content, 改存 additional_kwargs 由 PromptPrefixMiddleware
  在调模型前临时注入, 用户界面看不到前缀但模型仍收到
- 修正 thread_existed 闸门导致首轮前缀被跳过的问题

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 16:19:08 +08:00

108 lines
3.4 KiB
TypeScript

import { getBackendBaseURL } from "@/core/config";
import { apiFetch } from "@/core/api";
import type { Agent, CreateAgentRequest, UpdateAgentRequest } from "./types";
const BACKEND_UNAVAILABLE_STATUSES = new Set([502, 503, 504]);
export class AgentNameCheckError extends Error {
constructor(
message: string,
public readonly reason: "backend_unreachable" | "request_failed",
) {
super(message);
this.name = "AgentNameCheckError";
}
}
export interface ListAgentsParams {
search?: string;
tagId?: string | null;
}
export async function listAgents(params: ListAgentsParams = {}): Promise<Agent[]> {
const qs = new URLSearchParams();
if (params.search) qs.set("search", params.search);
if (params.tagId) qs.set("tag_id", params.tagId);
const query = qs.toString();
const res = await apiFetch(
`${getBackendBaseURL()}/api/agents${query ? `?${query}` : ""}`,
);
if (!res.ok) throw new Error(`Failed to load agents: ${res.statusText}`);
const data = (await res.json()) as { agents: Agent[] };
return data.agents;
}
export async function getAgent(id: string): Promise<Agent> {
const res = await apiFetch(`${getBackendBaseURL()}/api/agents/${id}`);
if (!res.ok) throw new Error(`Agent '${id}' not found`);
return res.json() as Promise<Agent>;
}
export async function createAgent(request: CreateAgentRequest): Promise<Agent> {
const res = await apiFetch(`${getBackendBaseURL()}/api/agents`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(request),
});
if (!res.ok) {
const err = (await res.json().catch(() => ({}))) as { detail?: string };
throw new Error(err.detail ?? `Failed to create agent: ${res.statusText}`);
}
return res.json() as Promise<Agent>;
}
export async function updateAgent(
id: string,
request: UpdateAgentRequest,
): Promise<Agent> {
const res = await apiFetch(`${getBackendBaseURL()}/api/agents/${id}`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(request),
});
if (!res.ok) {
const err = (await res.json().catch(() => ({}))) as { detail?: string };
throw new Error(err.detail ?? `Failed to update agent: ${res.statusText}`);
}
return res.json() as Promise<Agent>;
}
export async function deleteAgent(id: string): Promise<void> {
const res = await apiFetch(`${getBackendBaseURL()}/api/agents/${id}`, {
method: "DELETE",
});
if (!res.ok) throw new Error(`Failed to delete agent: ${res.statusText}`);
}
export async function checkAgentName(
name: string,
): Promise<{ available: boolean; name: string }> {
let res: Response;
try {
res = await apiFetch(
`${getBackendBaseURL()}/api/agents/check?name=${encodeURIComponent(name)}`,
);
} catch {
throw new AgentNameCheckError(
"Could not reach the CM Assistant backend.",
"backend_unreachable",
);
}
if (!res.ok) {
const err = (await res.json().catch(() => ({}))) as { detail?: string };
if (BACKEND_UNAVAILABLE_STATUSES.has(res.status)) {
throw new AgentNameCheckError(
"Could not reach the CM Assistant backend.",
"backend_unreachable",
);
}
throw new AgentNameCheckError(
err.detail ?? `Failed to check agent name: ${res.statusText}`,
"request_failed",
);
}
return res.json() as Promise<{ available: boolean; name: string }>;
}