Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
add88ca
list & get agent api
Magicbook1108 Apr 16, 2026
5f5225f
update canvas
Magicbook1108 Apr 16, 2026
a256811
update testcase
Magicbook1108 Apr 17, 2026
2514218
Merge branch 'main' into main
Magicbook1108 Apr 17, 2026
9c85418
delete agent
Magicbook1108 Apr 17, 2026
7a4f2fd
Merge branch 'main' of https://github.com/Magicbook1108/ragflow
Magicbook1108 Apr 17, 2026
09c3aff
Merge branch 'main' into main
Magicbook1108 Apr 17, 2026
f4134ab
create agent && update agent && reset agent
Magicbook1108 Apr 17, 2026
bc23e54
get agent template
Magicbook1108 Apr 17, 2026
daa4755
Merge branch 'main' of https://github.com/Magicbook1108/ragflow
Magicbook1108 Apr 17, 2026
35679df
update agent patch -> put && fix completion issue
Magicbook1108 Apr 17, 2026
415d6d9
Merge branch 'main' of https://github.com/Magicbook1108/ragflow
Magicbook1108 Apr 20, 2026
b81584a
remove unused import
Magicbook1108 Apr 20, 2026
9b4ad62
getsse
Magicbook1108 Apr 20, 2026
14e40b0
update get-agent in sdk
Magicbook1108 Apr 20, 2026
c181dc7
agent chat completion api
Magicbook1108 Apr 21, 2026
9dc8786
fix unused import 1
Magicbook1108 Apr 21, 2026
2c8fe74
remove unused import && update variable name
Magicbook1108 Apr 21, 2026
1dd53a0
Fix: pipeline parser log not display
dcc123456 Apr 21, 2026
a2e129b
upload file
Magicbook1108 Apr 21, 2026
539d5b2
fix test case
Magicbook1108 Apr 21, 2026
31b3af6
input form
Magicbook1108 Apr 21, 2026
88b7291
Merge branch 'temp'
Magicbook1108 Apr 21, 2026
0d3bf28
component debug
Magicbook1108 Apr 21, 2026
5993433
Trace an agent
Magicbook1108 Apr 21, 2026
f9e532a
get agent version list
Magicbook1108 Apr 21, 2026
5b8802a
get agent version list
Magicbook1108 Apr 21, 2026
5ea5cdd
fix test case
Magicbook1108 Apr 22, 2026
cd1ce9a
fix test case 2
Magicbook1108 Apr 22, 2026
5844c52
test db connect & agent rerun
Magicbook1108 Apr 22, 2026
1fc3dca
fix ruff
Magicbook1108 Apr 22, 2026
37e2083
fix test case
Magicbook1108 Apr 22, 2026
b84d27d
get prompts
Magicbook1108 Apr 22, 2026
47f1bc3
fix testcase
Magicbook1108 Apr 22, 2026
d9946b9
fix test case
Magicbook1108 Apr 22, 2026
5617bda
fix test case 2
Magicbook1108 Apr 23, 2026
c93e86c
fix test case 3
Magicbook1108 Apr 23, 2026
b9cac2e
Merge branch 'main' of https://github.com/infiniflow/ragflow
Magicbook1108 Apr 23, 2026
b711765
session api
Magicbook1108 Apr 23, 2026
916c2d8
download api
Magicbook1108 Apr 23, 2026
055de1e
remove outdated testcases
Magicbook1108 Apr 23, 2026
179e581
fix test case
Magicbook1108 Apr 23, 2026
f1049c1
Merge branch 'main' into main
Magicbook1108 Apr 23, 2026
9241719
update list agent sessions
Magicbook1108 Apr 23, 2026
00175af
Merge branch 'main' of https://github.com/Magicbook1108/ragflow
Magicbook1108 Apr 23, 2026
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
79 changes: 0 additions & 79 deletions api/apps/canvas_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,9 @@
from api.db.services.canvas_service import CanvasTemplateService, UserCanvasService, API4ConversationService
from api.db.services.document_service import DocumentService
from api.db.services.file_service import FileService
from api.db.services.knowledgebase_service import KnowledgebaseService

Check failure on line 27 in api/apps/canvas_app.py

View workflow job for this annotation

GitHub Actions / ragflow_tests

ruff (F401)

api/apps/canvas_app.py:27:51: F401 `api.db.services.knowledgebase_service.KnowledgebaseService` imported but unused help: Remove unused import: `api.db.services.knowledgebase_service.KnowledgebaseService`

Check failure on line 27 in api/apps/canvas_app.py

View workflow job for this annotation

GitHub Actions / ragflow_tests

ruff (F401)

api/apps/canvas_app.py:27:51: F401 `api.db.services.knowledgebase_service.KnowledgebaseService` imported but unused help: Remove unused import: `api.db.services.knowledgebase_service.KnowledgebaseService`
from api.db.services.pipeline_operation_log_service import PipelineOperationLogService
from api.db.services.task_service import queue_dataflow, CANVAS_DEBUG_DOC_ID, TaskService
from api.db.services.user_service import TenantService
from api.db.services.user_canvas_version import UserCanvasVersionService
from common.constants import RetCode
from common.misc_utils import get_uuid, thread_pool_exec
Expand All @@ -39,7 +38,7 @@
get_request_json,
)
from agent.canvas import Canvas
from agent.dsl_migration import normalize_chunker_dsl

Check failure on line 41 in api/apps/canvas_app.py

View workflow job for this annotation

GitHub Actions / ragflow_tests

ruff (F401)

api/apps/canvas_app.py:41:33: F401 `agent.dsl_migration.normalize_chunker_dsl` imported but unused help: Remove unused import: `agent.dsl_migration.normalize_chunker_dsl`

Check failure on line 41 in api/apps/canvas_app.py

View workflow job for this annotation

GitHub Actions / ragflow_tests

ruff (F401)

api/apps/canvas_app.py:41:33: F401 `agent.dsl_migration.normalize_chunker_dsl` imported but unused help: Remove unused import: `agent.dsl_migration.normalize_chunker_dsl`
from peewee import MySQLDatabase, PostgresqlDatabase
from api.db.db_models import APIToken, Task

Expand Down Expand Up @@ -115,56 +114,6 @@
return get_data_error_result(message="canvas saved, but replica sync failed.")
return get_json_result(data=req)


@manager.route('/get/<canvas_id>', methods=['GET']) # noqa: F821
@login_required
def get(canvas_id):
if not UserCanvasService.accessible(canvas_id, current_user.id):
return get_data_error_result(message="canvas not found.")
e, c = UserCanvasService.get_by_canvas_id(canvas_id)
if not e:
return get_data_error_result(message="canvas not found.")
try:
# DELETE
CanvasReplicaService.bootstrap(
canvas_id=canvas_id,
tenant_id=str(current_user.id),
runtime_user_id=str(current_user.id),
dsl=c.get("dsl"),
canvas_category=c.get("canvas_category", CanvasCategory.Agent),
title=c.get("title", ""),
)
except ValueError as e:
return get_data_error_result(message=str(e))

# Get the last publication time (latest released version's update_time)
last_publish_time = None
versions = UserCanvasVersionService.list_by_canvas_id(canvas_id)
if versions:
released_versions = [v for v in versions if v.release]
if released_versions:
# Sort by update_time descending and get the latest
released_versions.sort(key=lambda x: x.update_time, reverse=True)
last_publish_time = released_versions[0].update_time

# Add last_publish_time to response data
if isinstance(c, dict):
c["dsl"] = normalize_chunker_dsl(c.get("dsl", {}))
c["last_publish_time"] = last_publish_time
else:
# If c is a model object, convert to dict first
c = c.to_dict()
c["dsl"] = normalize_chunker_dsl(c.get("dsl", {}))
c["last_publish_time"] = last_publish_time

# For pipeline type, get associated datasets
if c.get("canvas_category") == CanvasCategory.DataFlow:
datasets = list(KnowledgebaseService.query(pipeline_id=canvas_id))
c["datasets"] = [{"id": d.id, "name": d.name, "avatar": d.avatar} for d in datasets]

return get_json_result(data=c)


@manager.route('/getsse/<canvas_id>', methods=['GET']) # type: ignore # noqa: F821
def getsse(canvas_id):
token = request.headers.get('Authorization').split()
Expand Down Expand Up @@ -575,34 +524,6 @@
return get_json_result(data=f"Error getting history file: {e}")


@manager.route('/list', methods=['GET']) # noqa: F821
@login_required
def list_canvas():
keywords = request.args.get("keywords", "")
page_number = int(request.args.get("page", 0))
items_per_page = int(request.args.get("page_size", 0))
orderby = request.args.get("orderby", "create_time")
canvas_category = request.args.get("canvas_category")
if request.args.get("desc", "true").lower() == "false":
desc = False
else:
desc = True
owner_ids = [id for id in request.args.get("owner_ids", "").strip().split(",") if id]
if not owner_ids:
tenants = TenantService.get_joined_tenants_by_user_id(current_user.id)
tenants = [m["tenant_id"] for m in tenants]
tenants.append(current_user.id)
canvas, total = UserCanvasService.get_by_tenant_ids(
tenants, current_user.id, page_number,
items_per_page, orderby, desc, keywords, canvas_category)
else:
tenants = owner_ids
canvas, total = UserCanvasService.get_by_tenant_ids(
tenants, current_user.id, 0,
0, orderby, desc, keywords, canvas_category)
return get_json_result(data={"canvas": canvas, "total": total})


@manager.route('/setting', methods=['POST']) # noqa: F821
@validate_request("id", "title", "permission")
@login_required
Expand Down
110 changes: 110 additions & 0 deletions api/apps/restful_apis/agent_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
#
# Copyright 2026 The InfiniFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

from quart import request

from agent.dsl_migration import normalize_chunker_dsl
from api.apps import current_user, login_required
from api.apps.services.canvas_replica_service import CanvasReplicaService
from api.db import CanvasCategory
from api.db.services.canvas_service import UserCanvasService
from api.db.services.knowledgebase_service import KnowledgebaseService
from api.db.services.user_service import TenantService
from api.db.services.user_canvas_version import UserCanvasVersionService
from api.utils.api_utils import add_tenant_id_to_kwargs, get_data_error_result, get_json_result


@manager.route("/agents", methods=["GET"]) # noqa: F821
@login_required
@add_tenant_id_to_kwargs
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,
)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

return get_json_result(data={"canvas": canvas, "total": total})
Comment on lines +294 to +331
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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.



@manager.route("/agents/<agent_id>", methods=["GET"]) # noqa: F821
@login_required
@add_tenant_id_to_kwargs
def get_agent(agent_id, tenant_id):
if not UserCanvasService.accessible(agent_id, current_user.id):
return get_data_error_result(message="canvas not found.")

exists, canvas = UserCanvasService.get_by_canvas_id(agent_id)
if not exists:
return get_data_error_result(message="canvas not found.")

try:
CanvasReplicaService.bootstrap(
canvas_id=agent_id,
tenant_id=str(tenant_id),
runtime_user_id=str(current_user.id),
dsl=canvas.get("dsl"),
canvas_category=canvas.get("canvas_category", CanvasCategory.Agent),
title=canvas.get("title", ""),
)
except ValueError as exc:
return get_data_error_result(message=str(exc))

last_publish_time = None
versions = UserCanvasVersionService.list_by_canvas_id(agent_id)
if versions:
released_versions = [version for version in versions if version.release]
if released_versions:
released_versions.sort(key=lambda version: version.update_time, reverse=True)
last_publish_time = released_versions[0].update_time

canvas["dsl"] = normalize_chunker_dsl(canvas.get("dsl", {}))
canvas["last_publish_time"] = last_publish_time

if canvas.get("canvas_category") == CanvasCategory.DataFlow:
datasets = list(KnowledgebaseService.query(pipeline_id=agent_id))
canvas["datasets"] = [{"id": item.id, "name": item.name, "avatar": item.avatar} for item in datasets]

return get_json_result(data=canvas)
21 changes: 0 additions & 21 deletions api/apps/sdk/agents.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@
from api.db.services.user_canvas_version import UserCanvasVersionService
from common.constants import RetCode
from common.misc_utils import get_uuid
from api.utils.api_utils import get_data_error_result, get_error_data_result, get_json_result, get_request_json, token_required

Check failure on line 38 in api/apps/sdk/agents.py

View workflow job for this annotation

GitHub Actions / ragflow_tests

ruff (F401)

api/apps/sdk/agents.py:38:56: F401 `api.utils.api_utils.get_error_data_result` imported but unused help: Remove unused import: `api.utils.api_utils.get_error_data_result`

Check failure on line 38 in api/apps/sdk/agents.py

View workflow job for this annotation

GitHub Actions / ragflow_tests

ruff (F401)

api/apps/sdk/agents.py:38:56: F401 `api.utils.api_utils.get_error_data_result` imported but unused help: Remove unused import: `api.utils.api_utils.get_error_data_result`
from api.utils.api_utils import get_result

Check failure on line 39 in api/apps/sdk/agents.py

View workflow job for this annotation

GitHub Actions / ragflow_tests

ruff (F401)

api/apps/sdk/agents.py:39:33: F401 `api.utils.api_utils.get_result` imported but unused help: Remove unused import: `api.utils.api_utils.get_result`

Check failure on line 39 in api/apps/sdk/agents.py

View workflow job for this annotation

GitHub Actions / ragflow_tests

ruff (F401)

api/apps/sdk/agents.py:39:33: F401 `api.utils.api_utils.get_result` imported but unused help: Remove unused import: `api.utils.api_utils.get_result`
from quart import request, Response
from rag.utils.redis_conn import REDIS_CONN

Expand All @@ -47,27 +47,6 @@
return user_id
return str(getattr(user, "nickname", "") or user_id)


@manager.route('/agents', methods=['GET']) # noqa: F821
@token_required
def list_agents(tenant_id):
id = request.args.get("id")
title = request.args.get("title")
if id or title:
canvas = UserCanvasService.query(id=id, title=title, user_id=tenant_id)
if not canvas:
return get_error_data_result("The agent doesn't exist.")
page_number = int(request.args.get("page", 1))
items_per_page = int(request.args.get("page_size", 30))
order_by = request.args.get("orderby", "update_time")
if str(request.args.get("desc","false")).lower() == "false":
desc = False
else:
desc = True
canvas = UserCanvasService.get_list(tenant_id, page_number, items_per_page, order_by, desc, id, title)
return get_result(data=canvas)


@manager.route("/agents", methods=["POST"]) # noqa: F821
@token_required
async def create_agent(tenant_id: str):
Expand Down
47 changes: 39 additions & 8 deletions docs/references/python_api_reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -1702,7 +1702,7 @@ from ragflow_sdk import RAGFlow, Agent

rag_object = RAGFlow(api_key="<YOUR_API_KEY>", base_url="http://<YOUR_BASE_URL>:9380")
agent_id = "AGENT_ID"
agent = rag_object.list_agents(id = agent_id)[0]
agent = rag_object.get_agent(agent_id)
session = agent.create_session()
# Or create in release mode:
# session = agent.create_session(release=True)
Expand Down Expand Up @@ -1784,7 +1784,7 @@ from ragflow_sdk import RAGFlow, Agent

rag_object = RAGFlow(api_key="<YOUR_API_KEY>", base_url="http://<YOUR_BASE_URL>:9380")
AGENT_id = "AGENT_ID"
agent = rag_object.list_agents(id = AGENT_id)[0]
agent = rag_object.get_agent(AGENT_id)
session = agent.create_session()

print("\n===== Miss R ====\n")
Expand Down Expand Up @@ -1853,7 +1853,7 @@ from ragflow_sdk import RAGFlow

rag_object = RAGFlow(api_key="<YOUR_API_KEY>", base_url="http://<YOUR_BASE_URL>:9380")
AGENT_id = "AGENT_ID"
agent = rag_object.list_agents(id = AGENT_id)[0]
agent = rag_object.get_agent(AGENT_id)
sessons = agent.list_sessions()
for session in sessions:
print(session)
Expand Down Expand Up @@ -1892,7 +1892,7 @@ from ragflow_sdk import RAGFlow

rag_object = RAGFlow(api_key="<YOUR_API_KEY>", base_url="http://<YOUR_BASE_URL>:9380")
AGENT_id = "AGENT_ID"
agent = rag_object.list_agents(id = AGENT_id)[0]
agent = rag_object.get_agent(AGENT_id)
agent.delete_sessions(ids=["id_1","id_2"])
agent.delete_sessions(delete_all=True)
```
Expand All @@ -1909,7 +1909,7 @@ agent.delete_sessions(delete_all=True)
RAGFlow.list_agents(
page: int = 1,
page_size: int = 30,
orderby: str = "create_time",
orderby: str = "update_time",
desc: bool = True,
id: str = None,
title: str = None
Expand All @@ -1932,8 +1932,8 @@ The number of agents on each page. Defaults to `30`.

The attribute by which the results are sorted. Available options:

- `"create_time"` (default)
- `"update_time"`
- `"create_time"`
- `"update_time"` (default)

##### desc: `bool`

Expand All @@ -1943,7 +1943,7 @@ Indicates whether the retrieved agents should be sorted in descending order. Def

The ID of the agent to retrieve. Defaults to `None`.

##### name: `string`
##### title: `string`

The name of the agent to retrieve. Defaults to `None`.

Expand All @@ -1963,6 +1963,37 @@ for agent in rag_object.list_agents():

---

### Get agent

```python
RAGFlow.get_agent(agent_id: str) -> Agent
```

Gets an agent by ID.

#### Parameters

##### agent_id: `string`

The ID of the agent to retrieve.

#### Returns

- Success: An `Agent` object.
- Failure: `Exception`.

#### Examples

```python
from ragflow_sdk import RAGFlow

rag_object = RAGFlow(api_key="<YOUR_API_KEY>", base_url="http://<YOUR_BASE_URL>:9380")
agent = rag_object.get_agent("AGENT_ID")
print(agent)
```

---

### Create agent

```python
Expand Down
20 changes: 17 additions & 3 deletions sdk/python/ragflow_sdk/ragflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,25 +231,39 @@ def retrieve(
raise Exception(res.get("message"))

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_list = res.get("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))
Comment on lines +246 to 249
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

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.

return result_list
Comment thread
coderabbitai[bot] marked this conversation as resolved.
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"])

def create_agent(self, title: str, dsl: dict, description: str | None = None) -> None:
req = {"title": title, "dsl": dsl}

Expand Down
Loading
Loading