Feat: Agent api (#14157)

### What problem does this PR solve?

1. **List agents**  
   **Prev API**:  
   - `/v1/canvas/list GET`  
   - `/api/v1/agents GET`  
   **Current API**: `/api/v2/agents GET`

2. **Get canvas template**  
   **Prev API**: `/v1/canvas/templates GET`  
   **Current API**: `/api/v2/agents/templates GET`

3. **Delete an agent**  
   **Prev API**: 
    - `/v1/canvas/rm POST`  
    - `/api/v1/agents/<agent_id> DELETE`
   **Current API**: `/api/v2/agents/<agent_id> DELETE`

4. **Update an agent**  
   **Prev API**: 
    - `/api/v1/agents/<agent_id> PUT`   
    - `/v1/canvas/setting POST `
   **Current API**: `/api/v2/agents/<agent_id> PATCH`


5. **Create an agent**  
   **Prev API**: 
    - `/v1/canvas/set POST`  
    - `/api/v1/agents POST`
   **Current API**: `/api/v2/agents POST`


6. **Get an agent**  
   **Prev API**: 
    - `/v1/canvas/get/<canvas_id> GET `  
   **Current API**: `/api/v2/agents/<agent_id> GET`


7. **Reset an agent**  
   **Prev API**: 
    - `/v1/canvas/reset POST`  
   **Current API**: `/api/v2/agents/<agent_id>/reset POST`


8. **Upload a file to an agent**  
   **Prev API**: 
    - `/v1/canvas/upload/<canvas_id> POST`  
   **Current API**: `/api/v2/agents/<agent_id>/upload POST`


9. **Input form**  
   **Prev API**: 
    - `/v1/canvas/input_form GET`  
**Current API**:
`/api/v2/agents/<agent_id>/components/<component_id>/input-form GET`


10. **Debug an agent**  
   **Prev API**: 
    - `/v1/canvas/debug POST`  
**Current API**:
`/api/v2/agents/<agent_id>/components/<component_id>/debug POST`


11. **Trace an agent**  
   **Prev API**: 
    - `/v1/canvas/trace GET`  
   **Current API**: `/api/v2/agents/<agent_id>/logs/<message_id> GET`


12. **Get an agent version list**  
   **Prev API**: 
    - `/v1/canvas/getlistversion/<canvas_id>`  
   **Current API**: `/api/v2/agents/<agent_id>/versions GET`


13. **Get a version of agent**  
   **Prev API**: 
    - `/v1/canvas/getversion/<version_id>`  
**Current API**: `/api/v2/agents/<agent_id>/versions/<version_id> GET`


14. **Test db connection**  
   **Prev API**: 
    - `/v1/canvas/test_db_connect POST`  
   **Current API**: `/api/v2/agents/test_db_connection`


15. **Rerun the agent**  
   **Prev API**: 
    - `/v1/canvas/rerun POST`  
   **Current API**: `/api/v2/agents/rerun POST`


16. **Get prompts**  
   **Prev API**: 
    - `/v1/canvas/prompts GET`  
   **Current API**: `/api/v2/agents/prompts GET`

### Type of change
- [x] New Feature (non-breaking change which adds functionality)

---------

Co-authored-by: chanx <1243304602@qq.com>
This commit is contained in:
Magicbook1108
2026-04-24 10:02:22 +08:00
committed by GitHub
parent d84438fd53
commit c74aece63c
34 changed files with 1819 additions and 4671 deletions

View File

@@ -406,8 +406,11 @@ def delete_all_agent_sessions(auth, agent_id, *, page_size=1000):
def agent_completions(auth, agent_id, payload=None):
url = f"{HOST_ADDRESS}{AGENT_API_URL}/{agent_id}/completions"
res = requests.post(url=url, headers=HEADERS, auth=auth, json=payload)
url = f"{HOST_ADDRESS}{AGENT_API_URL}/chat/completion"
body = {"agent_id": agent_id}
if payload:
body.update(payload)
res = requests.post(url=url, headers=HEADERS, auth=auth, json=body)
return res.json()

View File

@@ -49,11 +49,18 @@ MINIMAL_DSL = {
"variables": {},
}
def _agent_items(res):
data = res.get("data", [])
if isinstance(data, dict):
return data.get("canvas", [])
return data
@pytest.fixture(scope="function")
def agent_id(HttpApiAuth, request):
res = list_agents(HttpApiAuth, {"page_size": 1000})
assert res["code"] == 0, res
for agent in res.get("data", []):
for agent in _agent_items(res):
if agent.get("title") == AGENT_TITLE:
delete_agent(HttpApiAuth, agent["id"])
@@ -61,8 +68,9 @@ def agent_id(HttpApiAuth, request):
assert res["code"] == 0, res
res = list_agents(HttpApiAuth, {"title": AGENT_TITLE})
assert res["code"] == 0, res
assert res.get("data"), res
agent_id = res["data"][0]["id"]
agents = _agent_items(res)
assert agents, res
agent_id = agents[0]["id"]
def cleanup():
delete_all_agent_sessions(HttpApiAuth, agent_id)
@@ -82,7 +90,7 @@ class TestAgentCompletions:
res = agent_completions(
HttpApiAuth,
agent_id,
{"question": "hello", "stream": False, "session_id": session_id},
{"query": "hello", "stream": False, "session_id": session_id},
)
assert res["code"] == 0, res
if isinstance(res["data"], dict):

View File

@@ -17,11 +17,8 @@ import pytest
import requests
from common import (
create_agent,
create_agent_session,
delete_agent,
delete_all_agent_sessions,
delete_agent_sessions,
list_agent_sessions,
list_agents,
)
from configs import HOST_ADDRESS, VERSION
@@ -52,11 +49,18 @@ MINIMAL_DSL = {
"variables": {},
}
def _agent_items(res):
data = res.get("data", [])
if isinstance(data, dict):
return data.get("canvas", [])
return data
@pytest.fixture(scope="function")
def agent_id(HttpApiAuth, request):
res = list_agents(HttpApiAuth, {"page_size": 1000})
assert res["code"] == 0, res
for agent in res.get("data", []):
for agent in _agent_items(res):
if agent.get("title") == AGENT_TITLE:
delete_agent(HttpApiAuth, agent["id"])
@@ -64,8 +68,9 @@ def agent_id(HttpApiAuth, request):
assert res["code"] == 0, res
res = list_agents(HttpApiAuth, {"title": AGENT_TITLE})
assert res["code"] == 0, res
assert res.get("data"), res
agent_id = res["data"][0]["id"]
agents = _agent_items(res)
assert agents, res
agent_id = agents[0]["id"]
def cleanup():
delete_all_agent_sessions(HttpApiAuth, agent_id)
@@ -76,39 +81,14 @@ def agent_id(HttpApiAuth, request):
class TestAgentSessions:
@pytest.mark.p2
def test_delete_agent_sessions_empty_ids_noop(self, HttpApiAuth, agent_id):
res = create_agent_session(HttpApiAuth, agent_id, payload={})
assert res["code"] == 0, res
session_id = res["data"]["id"]
res = delete_agent_sessions(HttpApiAuth, agent_id, {"ids": []})
assert res["code"] == 0, res
res = list_agent_sessions(HttpApiAuth, agent_id, params={"id": session_id})
assert res["code"] == 0, res
assert len(res["data"]) == 1, res
@pytest.mark.p2
def test_create_list_delete_agent_sessions(self, HttpApiAuth, agent_id):
res = create_agent_session(HttpApiAuth, agent_id, payload={})
assert res["code"] == 0, res
session_id = res["data"]["id"]
assert res["data"]["agent_id"] == agent_id, res
res = list_agent_sessions(HttpApiAuth, agent_id, params={"id": session_id})
assert res["code"] == 0, res
assert len(res["data"]) == 1, res
assert res["data"][0]["id"] == session_id, res
res = delete_agent_sessions(HttpApiAuth, agent_id, {"ids": [session_id]})
assert res["code"] == 0, res
@pytest.mark.p2
def test_agent_crud_validation_contract(self, HttpApiAuth, agent_id):
res = list_agents(HttpApiAuth, {"id": "missing-agent-id", "title": "missing-agent-title"})
assert res["code"] == 102, res
assert "doesn't exist" in res["message"], res
assert res["code"] == 0, res
assert isinstance(res.get("data"), dict), res
assert "canvas" in res["data"], res
assert "total" in res["data"], res
res = list_agents(HttpApiAuth, {"title": AGENT_TITLE, "desc": "true", "page_size": 1})
assert res["code"] == 0, res

View File

@@ -498,6 +498,14 @@ def _load_session_module(monkeypatch):
monkeypatch.setitem(sys.modules, "agent.canvas", agent_canvas_mod)
monkeypatch.setitem(sys.modules, "agent.dsl_migration", agent_dsl_migration_mod)
quart_mod = ModuleType("quart")
quart_mod.request = SimpleNamespace(args=_Args(), headers={}, files=_AwaitableValue({}), method="POST")
quart_mod.Response = _StubResponse
quart_mod.jsonify = lambda payload: payload
quart_mod.current_app = SimpleNamespace()
quart_mod.has_app_context = lambda: False
monkeypatch.setitem(sys.modules, "quart", quart_mod)
module_path = repo_root / "api" / "apps" / "sdk" / "session.py"
spec = importlib.util.spec_from_file_location("test_session_sdk_routes_unit_module", module_path)
module = importlib.util.module_from_spec(spec)
@@ -530,6 +538,134 @@ def _load_session_module(monkeypatch):
return module
def _load_agent_api_module(monkeypatch):
_load_session_module(monkeypatch)
repo_root = Path(__file__).resolve().parents[4]
agent_component_mod = ModuleType("agent.component")
class _StubAgentLLM:
pass
agent_component_mod.LLM = _StubAgentLLM
monkeypatch.setitem(sys.modules, "agent.component", agent_component_mod)
api_apps_mod = ModuleType("api.apps")
api_apps_mod.__path__ = [str(repo_root / "api" / "apps")]
api_apps_mod.login_required = lambda func: func
monkeypatch.setitem(sys.modules, "api.apps", api_apps_mod)
api_apps_services_mod = ModuleType("api.apps.services")
api_apps_services_mod.__path__ = [str(repo_root / "api" / "apps" / "services")]
monkeypatch.setitem(sys.modules, "api.apps.services", api_apps_services_mod)
canvas_replica_mod = ModuleType("api.apps.services.canvas_replica_service")
class _StubCanvasReplicaService:
@staticmethod
def normalize_dsl(dsl):
return dsl
@staticmethod
def replace_for_set(**_kwargs):
return True
@staticmethod
def bootstrap(**_kwargs):
return True
@staticmethod
def load_for_run(**_kwargs):
return {"dsl": {}, "title": "agent", "canvas_category": "agent"}
@staticmethod
def commit_after_run(**_kwargs):
return True
canvas_replica_mod.CanvasReplicaService = _StubCanvasReplicaService
monkeypatch.setitem(sys.modules, "api.apps.services.canvas_replica_service", canvas_replica_mod)
file_service_mod = ModuleType("api.db.services.file_service")
file_service_mod.FileService = SimpleNamespace(upload_info=lambda *_args, **_kwargs: {})
monkeypatch.setitem(sys.modules, "api.db.services.file_service", file_service_mod)
api_service_mod = ModuleType("api.db.services.api_service")
api_service_mod.API4ConversationService = SimpleNamespace(
get_names=lambda *_args, **_kwargs: [],
get_list=lambda *_args, **_kwargs: (0, []),
save=lambda **_kwargs: True,
get_by_id=lambda _session_id: (True, SimpleNamespace(to_dict=lambda: {"id": _session_id})),
delete_by_id=lambda *_args, **_kwargs: True,
)
monkeypatch.setitem(sys.modules, "api.db.services.api_service", api_service_mod)
document_service_mod = ModuleType("api.db.services.document_service")
document_service_mod.DocumentService = SimpleNamespace(
clear_chunk_num_when_rerun=lambda *_args, **_kwargs: True,
update_by_id=lambda *_args, **_kwargs: True,
)
monkeypatch.setitem(sys.modules, "api.db.services.document_service", document_service_mod)
knowledgebase_service_mod = ModuleType("api.db.services.knowledgebase_service")
knowledgebase_service_mod.KnowledgebaseService = SimpleNamespace(query=lambda **_kwargs: [])
monkeypatch.setitem(sys.modules, "api.db.services.knowledgebase_service", knowledgebase_service_mod)
task_service_mod = ModuleType("api.db.services.task_service")
task_service_mod.CANVAS_DEBUG_DOC_ID = "debug-doc"
task_service_mod.GRAPH_RAPTOR_FAKE_DOC_ID = "graph-raptor-fake-doc"
task_service_mod.TaskService = SimpleNamespace(filter_delete=lambda *_args, **_kwargs: True)
task_service_mod.queue_dataflow = lambda *_args, **_kwargs: (True, "")
monkeypatch.setitem(sys.modules, "api.db.services.task_service", task_service_mod)
pipeline_operation_log_service_mod = ModuleType("api.db.services.pipeline_operation_log_service")
pipeline_operation_log_service_mod.PipelineOperationLogService = SimpleNamespace(
get_documents_info=lambda *_args, **_kwargs: [],
update_by_id=lambda *_args, **_kwargs: True,
)
monkeypatch.setitem(
sys.modules,
"api.db.services.pipeline_operation_log_service",
pipeline_operation_log_service_mod,
)
user_service_mod = ModuleType("api.db.services.user_service")
user_service_mod.TenantService = SimpleNamespace(get_joined_tenants_by_user_id=lambda *_args, **_kwargs: [])
user_service_mod.UserService = SimpleNamespace(get_by_id=lambda *_args, **_kwargs: (False, None))
user_service_mod.UserTenantService = SimpleNamespace(query=lambda **_kwargs: [])
monkeypatch.setitem(sys.modules, "api.db.services.user_service", user_service_mod)
user_canvas_version_mod = ModuleType("api.db.services.user_canvas_version")
user_canvas_version_mod.UserCanvasVersionService = SimpleNamespace(
list_by_canvas_id=lambda *_args, **_kwargs: [],
get_by_id=lambda *_args, **_kwargs: (False, None),
get_latest_version_title=lambda *_args, **_kwargs: "",
save_or_replace_latest=lambda **_kwargs: True,
build_version_title=lambda *_args, **_kwargs: "v1",
)
monkeypatch.setitem(sys.modules, "api.db.services.user_canvas_version", user_canvas_version_mod)
rag_flow_pipeline_mod = ModuleType("rag.flow.pipeline")
class _StubPipeline:
def __init__(self, *_args, **_kwargs):
pass
rag_flow_pipeline_mod.Pipeline = _StubPipeline
monkeypatch.setitem(sys.modules, "rag.flow.pipeline", rag_flow_pipeline_mod)
rag_redis_mod = ModuleType("rag.utils.redis_conn")
rag_redis_mod.REDIS_CONN = SimpleNamespace(get=lambda *_args, **_kwargs: None)
monkeypatch.setitem(sys.modules, "rag.utils.redis_conn", rag_redis_mod)
module_path = repo_root / "api" / "apps" / "restful_apis" / "agent_api.py"
spec = importlib.util.spec_from_file_location("test_agent_api_unit_module", module_path)
module = importlib.util.module_from_spec(spec)
module.manager = _DummyManager()
monkeypatch.setitem(sys.modules, "test_agent_api_unit_module", module)
spec.loader.exec_module(module)
return module
@pytest.mark.p2
def test_create_and_update_guard_matrix(monkeypatch):
module = _load_session_module(monkeypatch)
@@ -734,34 +870,22 @@ def test_openai_nonstream_branch_unit(monkeypatch):
@pytest.mark.p2
def test_agents_openai_compatibility_unit(monkeypatch):
module = _load_session_module(monkeypatch)
module = _load_agent_api_module(monkeypatch)
monkeypatch.setattr(module, "Response", _StubResponse)
monkeypatch.setattr(module, "jsonify", lambda payload: payload)
monkeypatch.setattr(module, "num_tokens_from_string", lambda text: len(text or ""))
monkeypatch.setattr(module, "get_request_json", lambda: _AwaitableValue({"openai-compatible": True}))
res = _run(inspect.unwrap(module.agent_chat_completion)("tenant-1"))
assert "`agent_id` is required." in res["message"]
monkeypatch.setattr(module, "get_request_json", lambda: _AwaitableValue({"model": "model", "messages": []}))
res = _run(inspect.unwrap(module.agents_completion_openai_compatibility)("tenant-1", "agent-1"))
monkeypatch.setattr(
module,
"get_request_json",
lambda: _AwaitableValue({"agent_id": "agent-1", "openai-compatible": True, "model": "model", "messages": []}),
)
res = _run(inspect.unwrap(module.agent_chat_completion)("tenant-1"))
assert "at least one message" in res["message"]
monkeypatch.setattr(
module,
"get_request_json",
lambda: _AwaitableValue({"model": "model", "messages": [{"role": "user", "content": "hello"}]}),
)
monkeypatch.setattr(module.UserCanvasService, "query", lambda **_kwargs: [])
res = _run(inspect.unwrap(module.agents_completion_openai_compatibility)("tenant-1", "agent-1"))
assert "don't own the agent" in res["message"]
monkeypatch.setattr(module.UserCanvasService, "query", lambda **_kwargs: [SimpleNamespace(id="agent-1")])
monkeypatch.setattr(
module,
"get_request_json",
lambda: _AwaitableValue({"model": "model", "messages": [{"role": "system", "content": "system only"}]}),
)
res = _run(inspect.unwrap(module.agents_completion_openai_compatibility)("tenant-1", "agent-1"))
assert "No valid messages found" in json.dumps(res)
captured_calls = []
async def _completion_openai_stream(*args, **kwargs):
@@ -774,6 +898,8 @@ def test_agents_openai_compatibility_unit(monkeypatch):
"get_request_json",
lambda: _AwaitableValue(
{
"agent_id": "agent-1",
"openai-compatible": True,
"model": "model",
"messages": [
{"role": "assistant", "content": "preface"},
@@ -784,7 +910,7 @@ def test_agents_openai_compatibility_unit(monkeypatch):
}
),
)
resp = _run(inspect.unwrap(module.agents_completion_openai_compatibility)("tenant-1", "agent-1"))
resp = _run(inspect.unwrap(module.agent_chat_completion)("tenant-1"))
assert isinstance(resp, _StubResponse)
assert resp.headers.get("Content-Type") == "text/event-stream; charset=utf-8"
_run(_collect_stream(resp.body))
@@ -795,11 +921,15 @@ def test_agents_openai_compatibility_unit(monkeypatch):
yield {"id": "non-stream"}
monkeypatch.setattr(module, "completion_openai", _completion_openai_nonstream)
monkeypatch.setattr(module.API4ConversationService, "get_by_id", lambda _session_id: (True, SimpleNamespace(dialog_id="agent-1")))
monkeypatch.setattr(module.UserCanvasService, "accessible", lambda *_args, **_kwargs: True)
monkeypatch.setattr(
module,
"get_request_json",
lambda: _AwaitableValue(
{
"agent_id": "agent-1",
"openai-compatible": True,
"model": "model",
"messages": [
{"role": "user", "content": "first"},
@@ -812,7 +942,7 @@ def test_agents_openai_compatibility_unit(monkeypatch):
}
),
)
res = _run(inspect.unwrap(module.agents_completion_openai_compatibility)("tenant-1", "agent-1"))
res = _run(inspect.unwrap(module.agent_chat_completion)("tenant-1"))
assert res["id"] == "non-stream"
assert captured_calls[-1][0][2] == "final user"
assert captured_calls[-1][1]["stream"] is False
@@ -821,9 +951,11 @@ def test_agents_openai_compatibility_unit(monkeypatch):
@pytest.mark.p2
def test_agent_completions_stream_and_nonstream_unit(monkeypatch):
module = _load_session_module(monkeypatch)
module = _load_agent_api_module(monkeypatch)
monkeypatch.setattr(module, "Response", _StubResponse)
monkeypatch.setattr(module.API4ConversationService, "get_by_id", lambda _session_id: (True, SimpleNamespace(dialog_id="agent-1")))
monkeypatch.setattr(module.UserCanvasService, "accessible", lambda *_args, **_kwargs: True)
async def _agent_stream(*_args, **_kwargs):
yield "data:not-json"
@@ -843,9 +975,20 @@ def test_agent_completions_stream_and_nonstream_unit(monkeypatch):
yield "data:" + json.dumps({"event": "message", "data": {"content": "hello"}})
monkeypatch.setattr(module, "agent_completion", _agent_stream)
monkeypatch.setattr(module, "get_request_json", lambda: _AwaitableValue({"stream": True, "return_trace": True}))
monkeypatch.setattr(
module,
"get_request_json",
lambda: _AwaitableValue(
{
"agent_id": "agent-1",
"session_id": "session-1",
"stream": True,
"return_trace": True,
}
),
)
resp = _run(inspect.unwrap(module.agent_completions)("tenant-1", "agent-1"))
resp = _run(inspect.unwrap(module.agent_chat_completion)("tenant-1"))
chunks = _run(_collect_stream(resp.body))
assert resp.headers.get("Content-Type") == "text/event-stream; charset=utf-8"
assert any('"trace"' in chunk for chunk in chunks)
@@ -874,8 +1017,19 @@ def test_agent_completions_stream_and_nonstream_unit(monkeypatch):
)
monkeypatch.setattr(module, "agent_completion", _agent_nonstream)
monkeypatch.setattr(module, "get_request_json", lambda: _AwaitableValue({"stream": False, "return_trace": True}))
res = _run(inspect.unwrap(module.agent_completions)("tenant-1", "agent-1"))
monkeypatch.setattr(
module,
"get_request_json",
lambda: _AwaitableValue(
{
"agent_id": "agent-1",
"session_id": "session-1",
"stream": False,
"return_trace": True,
}
),
)
res = _run(inspect.unwrap(module.agent_chat_completion)("tenant-1"))
assert res["data"]["data"]["content"] == "A"
assert res["data"]["data"]["reference"] == {"doc": "r"}
assert res["data"]["data"]["structured"] == {
@@ -884,64 +1038,7 @@ def test_agent_completions_stream_and_nonstream_unit(monkeypatch):
"c4": {},
}
assert [item["component_id"] for item in res["data"]["data"]["trace"]] == ["c2", "c3", "c4"]
async def _agent_nonstream_broken(*_args, **_kwargs):
yield "data:{"
monkeypatch.setattr(module, "agent_completion", _agent_nonstream_broken)
monkeypatch.setattr(module, "get_request_json", lambda: _AwaitableValue({"stream": False, "return_trace": False}))
res = _run(inspect.unwrap(module.agent_completions)("tenant-1", "agent-1"))
assert res["data"].startswith("**ERROR**")
@pytest.mark.p2
def test_list_agent_session_projection_unit(monkeypatch):
module = _load_session_module(monkeypatch)
monkeypatch.setattr(module, "request", SimpleNamespace(args=_Args({})))
monkeypatch.setattr(module.UserCanvasService, "query", lambda **_kwargs: [SimpleNamespace(id="agent-1")])
conv_non_list_reference = {
"id": "session-1",
"dialog_id": "agent-1",
"message": [{"role": "assistant", "content": "hello", "prompt": "internal"}],
"reference": {"unexpected": "shape"},
}
monkeypatch.setattr(module.API4ConversationService, "get_list", lambda *_args, **_kwargs: (1, [conv_non_list_reference]))
res = _run(inspect.unwrap(module.list_agent_session)("tenant-1", "agent-1"))
assert res["data"][0]["agent_id"] == "agent-1"
assert "prompt" not in res["data"][0]["messages"][0]
conv_with_chunks = {
"id": "session-2",
"dialog_id": "agent-1",
"message": [
{"role": "user", "content": "question"},
{"role": "assistant", "content": "answer", "prompt": "internal"},
],
"reference": [
{
"chunks": [
"not-a-dict",
{
"chunk_id": "chunk-2",
"content_with_weight": "weighted",
"doc_id": "doc-2",
"docnm_kwd": "doc-name-2",
"kb_id": "kb-2",
"image_id": "img-2",
"positions": [9],
},
]
}
],
}
monkeypatch.setattr(module.API4ConversationService, "get_list", lambda *_args, **_kwargs: (1, [conv_with_chunks]))
res = _run(inspect.unwrap(module.list_agent_session)("tenant-1", "agent-1"))
projected_chunk = res["data"][0]["messages"][1]["reference"][0]
assert projected_chunk["image_id"] == "img-2"
assert projected_chunk["positions"] == [9]
@pytest.mark.p2
def test_delete_routes_partial_duplicate_unit(monkeypatch):

View File

@@ -47,12 +47,12 @@ def test_list_agents_success_and_error(monkeypatch):
captured["path"] = path
captured["params"] = params
captured["json"] = json
return _DummyResponse({"code": 0, "data": [{"id": "agent-1", "title": "Agent One"}]})
return _DummyResponse({"code": 0, "data": {"canvas": [{"id": "agent-1", "title": "Agent One"}], "total": 1}})
monkeypatch.setattr(client, "get", _ok_get)
agents = client.list_agents(title="Agent One")
agents = client.list_agents()
assert captured["path"] == "/agents"
assert captured["params"]["title"] == "Agent One"
assert captured["params"] == {"page": 1, "page_size": 30, "orderby": "update_time", "desc": True}
assert isinstance(agents[0], Agent), str(agents)
assert agents[0].id == "agent-1", str(agents[0])
assert agents[0].title == "Agent One", str(agents[0])

View File

@@ -160,8 +160,10 @@ def test_session_module_streaming_and_helper_paths_unit(monkeypatch):
assert calls[0][2]["session_id"] == "session-chat"
assert calls[0][2]["temperature"] == 0.2
assert calls[0][3] is True
assert calls[1][1] == "/agents/agent-1/completions"
assert calls[1][2]["question"] == "hello agent"
assert calls[1][1] == "/agents/chat/completion"
assert calls[1][2]["agent_id"] == "agent-1"
assert calls[1][2]["query"] == "hello agent"
assert calls[1][2]["session_id"] == "session-agent"
assert calls[1][2]["openai-compatible"] is False
assert calls[1][2]["top_p"] == 0.8
assert calls[1][3] is True