mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-07-15 20:27:20 +08:00
## Summary Closes #14774. Adds free-form tags on agents (UserCanvas) with full UI + API: - Stored as comma-separated `tags` column on `UserCanvas` with online migration. - New endpoints: `GET /v1/agents/tags` (aggregate counts) and `PUT /v1/agent/<id>/tags` (write). `GET /v1/agents` accepts a `tags=` query. - "Edit tags" item in agent dropdown opens a chip-style editor dialog; tags render as badges on each agent card. - New "Tags" facet in the agents filter bar, with counts. ## Implementation notes - **Tag matching is exact-token**: the SQL filter wraps stored tags as `,…,` and matches `,ml,` so `ml` doesn't match `ml-ops`. - **Server-side normalization** in `UserCanvasService.update_tags`: dedup (case-insensitive), per-tag cap of 64 chars, total length capped at 512 chars to fit the column, commas inside tag values are replaced with spaces. - **Tenant authorization**: `PUT /v1/agent/<id>/tags` gates on `UserCanvasService.accessible(canvas_id, tenant_id)`. - **Tag listing scope**: `UserCanvasService.list_tags` follows the same own + team-shared rule as `get_by_tenant_ids`. - **i18n**: keys added to `en.ts` and `zh.ts` only (per project convention; other locales fall back). - **`HomeCard`** gets a non-breaking `extra?: ReactNode` slot for the chip row; no `src/components/ui/` files modified. ## Test plan - [ ] Backend boot runs `migrate_db` → confirm `user_canvas.tags` column exists (`DESCRIBE user_canvas`). - [ ] Agents page renders cards normally (no console error from missing field). - [ ] `⋯ → Edit tags` opens a dialog that stays open (regression: dialog was unmounting with the dropdown). - [ ] Typing a tag without pressing Enter and clicking Save persists it (regression: last typed tag was being dropped). - [ ] Chip input supports Enter/comma to commit, Backspace on empty to remove, `×` to remove individual chip. - [ ] Tag containing a comma sent via API is stored with the comma replaced by a space. - [ ] 20 long tags sent via API does not error (length cap silently truncates). - [ ] "Tags" filter in the filter bar shows counts and narrows the list. - [ ] Filtering by `ml` does **not** return agents tagged `ml-ops`. - [ ] UI in Chinese shows 编辑标签 / 添加标签以整理和筛选你的智能体 etc. - [ ] `PUT /v1/agent/<other-tenant-id>/tags` returns `Agent not found or no permission.`
195 lines
4.4 KiB
TypeScript
195 lines
4.4 KiB
TypeScript
import {
|
|
IAgentLogsRequest,
|
|
IPipeLineListRequest,
|
|
} from '@/interfaces/database/agent';
|
|
import { IAgentWebhookTraceRequest } from '@/interfaces/request/agent';
|
|
import api from '@/utils/api';
|
|
import { registerNextServer } from '@/utils/register-server';
|
|
import request from '@/utils/request';
|
|
|
|
const {
|
|
createAgent,
|
|
updateAgent: updateAgentApi,
|
|
listAgents,
|
|
deleteAgent,
|
|
agentChatCompletion,
|
|
resetAgent,
|
|
listAgentTemplate,
|
|
testDbConnect,
|
|
getInputElements,
|
|
trace,
|
|
fetchVersionList,
|
|
fetchVersion,
|
|
getAgent,
|
|
fetchAgentSessions,
|
|
fetchExternalAgentInputs,
|
|
prompt,
|
|
cancelDataflow,
|
|
cancelCanvas,
|
|
} = api;
|
|
|
|
const methods = {
|
|
getAgent: {
|
|
url: getAgent,
|
|
method: 'get',
|
|
},
|
|
createAgent: {
|
|
url: createAgent,
|
|
method: 'post',
|
|
},
|
|
fetchVersionList: {
|
|
url: fetchVersionList,
|
|
method: 'get',
|
|
},
|
|
fetchVersion: {
|
|
url: (config: { agentId: string; versionId: string }) =>
|
|
fetchVersion(config.agentId, config.versionId),
|
|
method: 'get',
|
|
},
|
|
listAgents: {
|
|
url: listAgents,
|
|
method: 'get',
|
|
},
|
|
listAgentTags: {
|
|
url: api.listAgentTags,
|
|
method: 'get',
|
|
},
|
|
resetAgent: {
|
|
url: resetAgent,
|
|
method: 'post',
|
|
},
|
|
deleteAgent: {
|
|
url: deleteAgent,
|
|
method: 'delete',
|
|
},
|
|
agentChatCompletion: {
|
|
url: agentChatCompletion,
|
|
method: 'post',
|
|
},
|
|
listAgentTemplate: {
|
|
url: listAgentTemplate,
|
|
method: 'get',
|
|
},
|
|
testDbConnect: {
|
|
url: testDbConnect,
|
|
method: 'post',
|
|
},
|
|
getInputElements: {
|
|
url: getInputElements,
|
|
method: 'get',
|
|
},
|
|
debugSingle: {
|
|
url: (config: { agentId: string; componentId: string }) =>
|
|
api.debug(config.agentId, config.componentId),
|
|
method: 'post',
|
|
},
|
|
uploadAgentFile: {
|
|
url: (config: { agentId: string }) => api.uploadAgentFile(config.agentId),
|
|
method: 'post',
|
|
},
|
|
trace: {
|
|
url: (config: { agentId: string; messageId: string }) =>
|
|
trace(config.agentId, config.messageId),
|
|
method: 'get',
|
|
},
|
|
inputForm: {
|
|
url: (config: { agentId: string; componentId: string }) =>
|
|
api.inputForm(config.agentId, config.componentId),
|
|
method: 'get',
|
|
},
|
|
fetchAgentLogs: {
|
|
url: fetchAgentSessions,
|
|
method: 'get',
|
|
},
|
|
fetchExternalAgentInputs: {
|
|
url: fetchExternalAgentInputs,
|
|
method: 'get',
|
|
},
|
|
fetchPrompt: {
|
|
url: prompt,
|
|
method: 'get',
|
|
},
|
|
cancelDataflow: {
|
|
url: cancelDataflow,
|
|
method: 'post',
|
|
},
|
|
cancelCanvas: {
|
|
url: cancelCanvas,
|
|
method: 'post',
|
|
},
|
|
createAgentSession: {
|
|
url: api.createAgentSession,
|
|
method: 'post',
|
|
},
|
|
} as const;
|
|
|
|
const agentService = registerNextServer<keyof typeof methods>(methods);
|
|
|
|
export const updateAgent = (
|
|
agentId: string,
|
|
params: {
|
|
title?: string;
|
|
dsl?: Record<string, any>;
|
|
avatar?: string;
|
|
description?: string | null;
|
|
permission?: string;
|
|
release?: string;
|
|
},
|
|
) => {
|
|
return request(updateAgentApi(agentId), { method: 'put', data: params });
|
|
};
|
|
|
|
export const updateAgentTags = (agentId: string, tags: string[]) => {
|
|
return request(api.updateAgentTags(agentId), {
|
|
method: 'put',
|
|
data: { tags: tags.join(',') },
|
|
});
|
|
};
|
|
|
|
export const fetchTrace = (data: { canvas_id: string; message_id: string }) => {
|
|
return request.get(
|
|
methods.trace.url({
|
|
agentId: data.canvas_id,
|
|
messageId: data.message_id,
|
|
}),
|
|
);
|
|
};
|
|
export const fetchAgentLogsByCanvasId = (
|
|
canvasId: string,
|
|
params: IAgentLogsRequest,
|
|
) => {
|
|
return request.get(methods.fetchAgentLogs.url(canvasId), { params: params });
|
|
};
|
|
|
|
export const fetchAgentLogsById = (canvasId: string, sessionId: string) => {
|
|
return request.get(api.fetchAgentSessionById(canvasId, sessionId));
|
|
};
|
|
|
|
export const fetchPipeLineList = (params: IPipeLineListRequest) => {
|
|
return request.get(api.listAgents, { params: params });
|
|
};
|
|
|
|
export const fetchWebhookTrace = (
|
|
id: string,
|
|
params: IAgentWebhookTraceRequest,
|
|
) => {
|
|
return request.get(api.fetchWebhookTrace(id), { params: params });
|
|
};
|
|
|
|
export function createAgentSession({ id, name }: { id: string; name: string }) {
|
|
return request.post(api.createAgentSession(id), { data: { name } });
|
|
}
|
|
|
|
export const deleteAgentSession = (canvasId: string, sessionId: string) => {
|
|
return request.delete(api.fetchAgentSessionById(canvasId, sessionId));
|
|
};
|
|
|
|
export const uploadAgentFile = (agentId: string, data: FormData) => {
|
|
return request(api.uploadAgentFile(agentId), {
|
|
method: 'post',
|
|
data,
|
|
});
|
|
};
|
|
|
|
export default agentService;
|