Feat: Agent api#14157
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughRemoved canvas-specific handlers and replaced them with agent-focused REST endpoints; updated Python SDK client, backend SDK module, frontend API mappings, hooks, types, docs, and tests to call and handle the new Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant API as AgentAPI (manager)
participant Tenant as TenantService
participant UserCanvas as UserCanvasService
participant Replica as CanvasReplicaService
participant KB as KnowledgebaseService
Client->>API: GET /agents?params
API->>Tenant: (if owner_ids empty) get_joined_tenants_by_user_id(user_id)
Tenant-->>API: tenant_ids
API->>UserCanvas: get_by_tenant_ids(tenant_ids, params)
UserCanvas-->>API: {canvas, total}
API-->>Client: {canvas, total}
Client->>API: GET /agents/{agent_id}
API->>UserCanvas: accessible(agent_id, tenant_id)
UserCanvas-->>API: canvas_meta
API->>Replica: bootstrap(dsl, metadata)
Replica-->>API: runtime_replica / error
API->>UserCanvas: list_canvas_versions(agent_id)
UserCanvas-->>API: versions
API->>KB: (if DataFlow) query datasets
KB-->>API: datasets
API-->>Client: assembled agent payload
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ❌ 3❌ Failed checks (2 warnings, 1 inconclusive)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
web/src/hooks/use-agent-request.ts (1)
192-205:⚠️ Potential issue | 🟠 MajorAdd FetchAllAgentList to cache invalidation after agent mutations.
useFetchAllAgentList()uses query keyAgentApiAction.FetchAllAgentList(line 194), but none of the agent mutations invalidate it. The create, update, and delete mutations invalidate onlyAgentApiAction.FetchAgentListByPage(lines 228, 252, 348), leaving any dropdown or selector backed by the full list with stale data after mutations.Add
queryClient.invalidateQueries({ queryKey: [AgentApiAction.FetchAllAgentList] })to all three mutation blocks.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/hooks/use-agent-request.ts` around lines 192 - 205, The full-agent list query key AgentApiAction.FetchAllAgentList (used in useFetchAllAgentList) is not being invalidated after agent create/update/delete mutations; update the three mutation handlers that currently call queryClient.invalidateQueries({ queryKey: [AgentApiAction.FetchAgentListByPage] }) to also call queryClient.invalidateQueries({ queryKey: [AgentApiAction.FetchAllAgentList] }) so the cache for useFetchAllAgentList is refreshed after create, update, and delete operations; locate these changes in the mutation blocks for createAgent, updateAgent, and deleteAgent in this file and add the extra invalidateQueries call for AgentApiAction.FetchAllAgentList.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@api/apps/restful_apis/agent_api.py`:
- Around line 33-69: The new list_agents flow lacks server-side logging; add
structured logs at key points in list_agents: log incoming request scope
(including current_user.id, tenant_id, keywords, owner_ids, page/page_size,
orderby, desc) at function start, log access denial or empty-owners branching
when TenantService.get_joined_tenants_by_user_id returns empty or when
current_user is unauthorized, log bootstrap/Service call failures and exceptions
around TenantService.get_joined_tenants_by_user_id and
UserCanvasService.get_by_tenant_ids (including caught exception details), and
log the final response summary before returning via get_json_result; use the
existing app logger (or processLogger) and include identifiers like list_agents,
TenantService.get_joined_tenants_by_user_id,
UserCanvasService.get_by_tenant_ids, and get_json_result to aid tracing.
- Around line 57-67: The branch currently passes raw owner_ids to
UserCanvasService.get_by_tenant_ids() and forces pagination to zeros; instead,
first filter/validate owner_ids against the caller's allowed tenants (e.g., call
an auth helper like AuthService.filter_authorized_tenants(owner_ids,
current_user) or TenantService.get_allowed_tenant_ids(current_user)) and use
that returned, authorized list when calling
UserCanvasService.get_by_tenant_ids(), and restore the original pagination
arguments (page and page_size) rather than passing 0,0 so pagination is
preserved; also handle the case where the authorized list is empty (return empty
result or appropriate error) before calling get_by_tenant_ids().
In `@sdk/python/ragflow_sdk/ragflow.py`:
- Around line 240-257: The current list_agents implementation filters by title
locally per page, causing false negatives for agents on other pages; update the
GET request params (the dict passed to self.get in the method that constructs
Agent objects) to include "title": title when title is provided so the server
can do an exact-title filter, or alternatively implement server-side-equivalent
behavior by looping over paginated results (calling self.get repeatedly with
page++ until found or no more data) and then constructing Agent(self, data) —
update the code paths that call self.get and the result_list building
(referencing the self.get call and Agent class usage) to use one of these fixes.
---
Outside diff comments:
In `@web/src/hooks/use-agent-request.ts`:
- Around line 192-205: The full-agent list query key
AgentApiAction.FetchAllAgentList (used in useFetchAllAgentList) is not being
invalidated after agent create/update/delete mutations; update the three
mutation handlers that currently call queryClient.invalidateQueries({ queryKey:
[AgentApiAction.FetchAgentListByPage] }) to also call
queryClient.invalidateQueries({ queryKey: [AgentApiAction.FetchAllAgentList] })
so the cache for useFetchAllAgentList is refreshed after create, update, and
delete operations; locate these changes in the mutation blocks for createAgent,
updateAgent, and deleteAgent in this file and add the extra invalidateQueries
call for AgentApiAction.FetchAllAgentList.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 3d00b208-3ffa-4525-aaee-9049432912a1
📒 Files selected for processing (9)
api/apps/canvas_app.pyapi/apps/restful_apis/agent_api.pyapi/apps/sdk/agents.pydocs/references/python_api_reference.mdsdk/python/ragflow_sdk/ragflow.pyweb/src/hooks/use-agent-request.tsweb/src/interfaces/database/agent.tsweb/src/services/agent-service.tsweb/src/utils/api.ts
💤 Files with no reviewable changes (2)
- api/apps/sdk/agents.py
- api/apps/canvas_app.py
| def list_agents(tenant_id): | ||
| keywords = request.args.get("keywords", "") | ||
| canvas_category = request.args.get("canvas_category") | ||
| owner_ids = [item for item in request.args.get("owner_ids", "").strip().split(",") if item] | ||
|
|
||
| page_number = int(request.args.get("page", 0)) | ||
| items_per_page = int(request.args.get("page_size", 0)) | ||
| order_by = request.args.get("orderby", "create_time") | ||
| desc = str(request.args.get("desc", "true")).lower() != "false" | ||
|
|
||
| if not owner_ids: | ||
| tenants = TenantService.get_joined_tenants_by_user_id(current_user.id) | ||
| tenants = [member["tenant_id"] for member in tenants] | ||
| tenants.append(current_user.id) | ||
| canvas, total = UserCanvasService.get_by_tenant_ids( | ||
| tenants, | ||
| current_user.id, | ||
| page_number, | ||
| items_per_page, | ||
| order_by, | ||
| desc, | ||
| keywords, | ||
| canvas_category, | ||
| ) | ||
| else: | ||
| canvas, total = UserCanvasService.get_by_tenant_ids( | ||
| owner_ids, | ||
| current_user.id, | ||
| 0, | ||
| 0, | ||
| order_by, | ||
| desc, | ||
| keywords, | ||
| canvas_category, | ||
| ) | ||
|
|
||
| return get_json_result(data={"canvas": canvas, "total": total}) |
There was a problem hiding this comment.
Add server-side logging for these new agent flows.
The new list/get endpoints have no logs for request scope, access denials, or bootstrap failures, which will make production debugging and audit trails much harder.
As per coding guidelines, "Add logging for new flows".
Also applies to: 75-110
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@api/apps/restful_apis/agent_api.py` around lines 33 - 69, The new list_agents
flow lacks server-side logging; add structured logs at key points in
list_agents: log incoming request scope (including current_user.id, tenant_id,
keywords, owner_ids, page/page_size, orderby, desc) at function start, log
access denial or empty-owners branching when
TenantService.get_joined_tenants_by_user_id returns empty or when current_user
is unauthorized, log bootstrap/Service call failures and exceptions around
TenantService.get_joined_tenants_by_user_id and
UserCanvasService.get_by_tenant_ids (including caught exception details), and
log the final response summary before returning via get_json_result; use the
existing app logger (or processLogger) and include identifiers like list_agents,
TenantService.get_joined_tenants_by_user_id,
UserCanvasService.get_by_tenant_ids, and get_json_result to aid tracing.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #14157 +/- ##
==========================================
- Coverage 95.97% 95.30% -0.67%
==========================================
Files 10 10
Lines 695 703 +8
Branches 111 112 +1
==========================================
+ Hits 667 670 +3
- Misses 11 16 +5
Partials 17 17 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
test/testcases/test_sdk_api/test_agent_management/test_agent_crud_unit.py (1)
41-63:⚠️ Potential issue | 🟡 MinorPlease add coverage for the new
get_agent()/list_agents(id=...)path.This update only exercises the paginated
/agentsflow. The new/agents/{agent_id}behavior and thetitle-mismatch[]branch are still untested, so regressions in the actual feature added by this PR will slip through.Based on learnings, "Applies to tests/**/*.py : Add/adjust tests for behavior changes".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/testcases/test_sdk_api/test_agent_management/test_agent_crud_unit.py` around lines 41 - 63, Add test coverage for the new single-agent path by exercising RAGFlow.get_agent() and list_agents(id=...) and the title-mismatch branch: monkeypatch client.get to return a success payload for "/agents/{agent_id}" and assert the returned object is an Agent with the expected id/title (use get_agent and list_agents(id="agent-1") calls), then monkeypatch client.get to return an empty data.canvas (or a payload where the title does not match) and assert list_agents(title="Nonexistent") yields an empty list; also keep the existing error-case assertion by returning {"code":1,"message":"list boom"} for the paginated flow to ensure exceptions still bubble up.
♻️ Duplicate comments (1)
sdk/python/ragflow_sdk/ragflow.py (1)
240-257:⚠️ Potential issue | 🟠 MajorPass
titlethrough to/agentsinstead of filtering one page locally.When
titleis set, this code still fetches a single paginated page and filters that page in Python, so a matching agent on page 2+ is returned as[]. The request should includetitleor the SDK should keep paginating until exhausted.Suggested fix
res = self.get( "/agents", { "page": page, "page_size": page_size, "orderby": orderby, "desc": desc, + "title": title, }, )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/python/ragflow_sdk/ragflow.py` around lines 240 - 257, The current agents list call paginates a single page then filters locally, which misses matches on later pages; update the GET request in the method that calls self.get("/agents", ...) to include the title parameter when title is provided (i.e., add "title": title into the request params) so the server can filter, or alternatively implement server-pagination by looping pages (incrementing page and aggregating results_list until no more data) instead of only filtering the first page; modify the parameters passed into self.get and remove the local single-page title filter (the block that checks data.get("title") and continues) if you opt to pass the title to the API.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@sdk/python/ragflow_sdk/ragflow.py`:
- Around line 233-266: The new remote-call flows in list_agents and get_agent
must log request context and failure details before raising; modify list_agents
(when calling self.get("/agents", {...}) and on non-zero res.get("code") path)
to log the request params (page, page_size, orderby, desc, title, id if used)
and the full response body/status, and modify get_agent (when calling
self.get(f"/agents/{agent_id}") and on non-zero res.get("code") path) to log the
agent_id and the full response body/status; use the module/class logger (e.g.,
self.logger.error or a module logging.getLogger(__name__)) to emit an
error-level message that includes the context and res contents before raising
the Exception.
- Around line 252-257: The SDK's list_agents implementation assumes res["data"]
is a dict with a "canvas" key and will crash if the backend returns a list;
update the logic in list_agents to handle both shapes: detect whether data (the
value from res.get("data")) is a dict and use data.get("canvas", []) otherwise
if it's a list treat it as the agent list directly, then iterate that list and
construct Agent(self, item) (avoid calling .get() on a list). Ensure you
reference the variables/data_list, data, and the Agent(self, ...) construction
when making the change.
---
Outside diff comments:
In `@test/testcases/test_sdk_api/test_agent_management/test_agent_crud_unit.py`:
- Around line 41-63: Add test coverage for the new single-agent path by
exercising RAGFlow.get_agent() and list_agents(id=...) and the title-mismatch
branch: monkeypatch client.get to return a success payload for
"/agents/{agent_id}" and assert the returned object is an Agent with the
expected id/title (use get_agent and list_agents(id="agent-1") calls), then
monkeypatch client.get to return an empty data.canvas (or a payload where the
title does not match) and assert list_agents(title="Nonexistent") yields an
empty list; also keep the existing error-case assertion by returning
{"code":1,"message":"list boom"} for the paginated flow to ensure exceptions
still bubble up.
---
Duplicate comments:
In `@sdk/python/ragflow_sdk/ragflow.py`:
- Around line 240-257: The current agents list call paginates a single page then
filters locally, which misses matches on later pages; update the GET request in
the method that calls self.get("/agents", ...) to include the title parameter
when title is provided (i.e., add "title": title into the request params) so the
server can filter, or alternatively implement server-pagination by looping pages
(incrementing page and aggregating results_list until no more data) instead of
only filtering the first page; modify the parameters passed into self.get and
remove the local single-page title filter (the block that checks
data.get("title") and continues) if you opt to pass the title to the API.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 3ad09380-407d-4745-9a7f-7848ef021419
📒 Files selected for processing (2)
sdk/python/ragflow_sdk/ragflow.pytest/testcases/test_sdk_api/test_agent_management/test_agent_crud_unit.py
| def list_agents(self, page: int = 1, page_size: int = 30, orderby: str = "update_time", desc: bool = True, id: str | None = None, title: str | None = None) -> list[Agent]: | ||
| if id is not None: | ||
| agent = self.get_agent(id) | ||
| if title is not None and agent.title != title: | ||
| return [] | ||
| return [agent] | ||
|
|
||
| res = self.get( | ||
| "/agents", | ||
| { | ||
| "page": page, | ||
| "page_size": page_size, | ||
| "orderby": orderby, | ||
| "desc": desc, | ||
| "id": id, | ||
| "title": title, | ||
| }, | ||
| ) | ||
| res = res.json() | ||
| result_list = [] | ||
| if res.get("code") == 0: | ||
| for data in res["data"]: | ||
| data = res.get("data") or {} | ||
| data_list = data.get("canvas", []) | ||
| for data in data_list: | ||
| if title is not None and data.get("title") != title: | ||
| continue | ||
| result_list.append(Agent(self, data)) | ||
| return result_list | ||
| raise Exception(res["message"]) | ||
|
|
||
| def get_agent(self, agent_id: str) -> Agent: | ||
| res = self.get(f"/agents/{agent_id}") | ||
| res = res.json() | ||
| if res.get("code") == 0: | ||
| return Agent(self, res["data"]) | ||
| raise Exception(res["message"]) |
There was a problem hiding this comment.
Add logging in the new agent read flows.
list_agents() and get_agent() add new remote-call paths, but failures go straight to Exception(...) with no traceability. Please log the request context and the non-zero-code failure path before raising.
As per coding guidelines, "Add logging for new flows".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@sdk/python/ragflow_sdk/ragflow.py` around lines 233 - 266, The new
remote-call flows in list_agents and get_agent must log request context and
failure details before raising; modify list_agents (when calling
self.get("/agents", {...}) and on non-zero res.get("code") path) to log the
request params (page, page_size, orderby, desc, title, id if used) and the full
response body/status, and modify get_agent (when calling
self.get(f"/agents/{agent_id}") and on non-zero res.get("code") path) to log the
agent_id and the full response body/status; use the module/class logger (e.g.,
self.logger.error or a module logging.getLogger(__name__)) to emit an
error-level message that includes the context and res contents before raising
the Exception.
| data = res.get("data") or {} | ||
| data_list = data.get("canvas", []) | ||
| for data in data_list: | ||
| if title is not None and data.get("title") != title: | ||
| continue | ||
| result_list.append(Agent(self, data)) |
There was a problem hiding this comment.
The new /agents parser no longer matches the SDK route contract.
The current backend snippet in api/apps/sdk/agents.py:51-68 returns data as the agent list itself, not { "canvas": ... }. In that case, Line 253 will call .get() on a list and break list_agents() at runtime.
Suggested fix
- data = res.get("data") or {}
- data_list = data.get("canvas", [])
- for data in data_list:
- if title is not None and data.get("title") != title:
+ payload = res.get("data") or []
+ data_list = payload.get("canvas", []) if isinstance(payload, dict) else payload
+ for data in data_list:
+ if title is not None and data.get("title") != title:
continue
result_list.append(Agent(self, data))🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@sdk/python/ragflow_sdk/ragflow.py` around lines 252 - 257, The SDK's
list_agents implementation assumes res["data"] is a dict with a "canvas" key and
will crash if the backend returns a list; update the logic in list_agents to
handle both shapes: detect whether data (the value from res.get("data")) is a
dict and use data.get("canvas", []) otherwise if it's a list treat it as the
agent list directly, then iterate that list and construct Agent(self, item)
(avoid calling .get() on a list). Ensure you reference the variables/data_list,
data, and the Agent(self, ...) construction when making the change.
同步 infiniflow/ragflow main(v0.25.0 tag + 后续 commits)。 上游主要变更: - REST API 大重构:user/tenant/stats/plugin/chunk/document/file2document/ connector/langfuse/mcp/system/chat/search 全部迁到 api/apps/restful_apis/ - feat: Agent API (infiniflow#14157) / deepseek v4 (infiniflow#14346) / docling native chunking (infiniflow#14218) / Astraflow provider (infiniflow#14270) / title chunk 优化 (infiniflow#14325) - refactor: doc metadata update / agent webhook → REST / 移除 legacy MCP web API - Go 侧:retrieval_test in GO / stream think chat / zhipu thinking / minimax provider / balance command - 若干 fix:think tags normalization / upload truncation / audio-video pipeline - 7 个 ingestion_pipeline templates 重命名(Book→book 等) 冲突解决(2 处): - CLAUDE.md:保留 fork 专属整块(Fork 信息/端口/macOS 本地/活跃文档/首次运行 记录/Task Executor),追加上游新增的 Working Style 8 条作为独立段 - web/src/pages/dataset/testing/testing-result.tsx:保留 HEAD 版本 (P1.7-C4 设计对齐版已内含 upstream 要加的 RAGFlowPagination + isRuned 空态 文案,upstream 版被 HEAD 超集) 验证: - `pytest test/agent_v2/` → 301 passed / 8 skipped(RAGFLOW_TEST_DB gate) - 其它冲突文件 auto-merge 成功:dialog_service.py / docker/.env / pyproject.toml / uv.lock / locales/en.ts / dataset-table.tsx / testing-form.tsx Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Resolve session.py conflict with the post-infiniflow#14157 agent API layout. Port extra_body.reference_metadata handling to api/apps/restful_apis/agent_api.py (OpenAI-compatible and session completion paths) with the prior validation and null-safety fixes. Made-with: Cursor
What problem does this PR solve?
List agents
Prev API:
/v1/canvas/list GET/api/v1/agents GETCurrent API:
/api/v2/agents GETGet canvas template
Prev API:
/v1/canvas/templates GETCurrent API:
/api/v2/agents/templates GETDelete an agent
Prev API:
/v1/canvas/rm POST/api/v1/agents/<agent_id> DELETECurrent API:
/api/v2/agents/<agent_id> DELETEUpdate an agent
Prev API:
/api/v1/agents/<agent_id> PUT/v1/canvas/setting POSTCurrent API:
/api/v2/agents/<agent_id> PATCHCreate an agent
Prev API:
/v1/canvas/set POST/api/v1/agents POSTCurrent API:
/api/v2/agents POSTGet an agent
Prev API:
/v1/canvas/get/<canvas_id> GETCurrent API:
/api/v2/agents/<agent_id> GETReset an agent
Prev API:
/v1/canvas/reset POSTCurrent API:
/api/v2/agents/<agent_id>/reset POSTUpload a file to an agent
Prev API:
/v1/canvas/upload/<canvas_id> POSTCurrent API:
/api/v2/agents/<agent_id>/upload POSTInput form
Prev API:
/v1/canvas/input_form GETCurrent API:
/api/v2/agents/<agent_id>/components/<component_id>/input-form GETDebug an agent
Prev API:
/v1/canvas/debug POSTCurrent API:
/api/v2/agents/<agent_id>/components/<component_id>/debug POSTTrace an agent
Prev API:
/v1/canvas/trace GETCurrent API:
/api/v2/agents/<agent_id>/logs/<message_id> GETGet an agent version list
Prev API:
/v1/canvas/getlistversion/<canvas_id>Current API:
/api/v2/agents/<agent_id>/versions GETGet a version of agent
Prev API:
/v1/canvas/getversion/<version_id>Current API:
/api/v2/agents/<agent_id>/versions/<version_id> GETTest db connection
Prev API:
/v1/canvas/test_db_connect POSTCurrent API:
/api/v2/agents/test_db_connectionRerun the agent
Prev API:
/v1/canvas/rerun POSTCurrent API:
/api/v2/agents/rerun POSTGet prompts
Prev API:
/v1/canvas/prompts GETCurrent API:
/api/v2/agents/prompts GETType of change