feat: add agent debug conversation refresh API (#37784)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
zyssyz123
2026-06-23 04:34:13 +00:00
committed by GitHub
co-authored by autofix-ci[bot]
parent 7852c273e4
commit 26639e0923
8 changed files with 277 additions and 16 deletions
+30
View File
@@ -228,6 +228,10 @@ class AgentAppDetailWithSite(GenericAppDetailWithSite):
active_config_is_published: bool = False
class AgentDebugConversationRefreshResponse(BaseModel):
debug_conversation_id: str
class AgentAppPagination(GenericAppPagination):
data: list[AgentAppPartial] = Field( # type: ignore[assignment] # pyrefly: ignore[bad-override-mutable-attribute]
validation_alias=AliasChoices("items", "data")
@@ -254,6 +258,7 @@ register_response_schema_models(
AgentAppPublishedReferenceResponse,
AgentAppDetailWithSite,
AgentAppPartial,
AgentDebugConversationRefreshResponse,
AgentConfigSnapshotDetailResponse,
AgentConfigSnapshotListResponse,
AgentConfigSnapshotRestoreResponse,
@@ -535,6 +540,31 @@ class AgentAppApi(Resource):
return "", 204
@console_ns.route("/agent/<uuid:agent_id>/debug-conversation/refresh")
class AgentDebugConversationRefreshApi(Resource):
@console_ns.response(
200,
"Agent debug conversation refreshed",
console_ns.models[AgentDebugConversationRefreshResponse.__name__],
)
@console_ns.response(403, "Insufficient permissions")
@setup_required
@login_required
@account_initialization_required
@edit_permission_required
@with_current_user
@with_current_tenant_id
def post(self, tenant_id: str, current_user: Account, agent_id: UUID):
debug_conversation_id = _agent_roster_service().refresh_agent_app_debug_conversation_id(
tenant_id=tenant_id,
agent_id=str(agent_id),
account_id=current_user.id,
)
return AgentDebugConversationRefreshResponse(debug_conversation_id=debug_conversation_id).model_dump(
mode="json"
)
@console_ns.route("/agent/<uuid:agent_id>/copy")
class AgentAppCopyApi(Resource):
@console_ns.expect(console_ns.models[CopyAppPayload.__name__])
+20
View File
@@ -602,6 +602,20 @@ Stop a running Agent App chat message generation
| 400 | Invalid request parameters | |
| 403 | Insufficient permissions | |
### [POST] /agent/{agent_id}/debug-conversation/refresh
#### Parameters
| Name | Located in | Description | Required | Schema |
| ---- | ---------- | ----------- | -------- | ------ |
| agent_id | path | | Yes | string (uuid) |
#### Responses
| Code | Description | Schema |
| ---- | ----------- | ------ |
| 200 | Agent debug conversation refreshed | **application/json**: [AgentDebugConversationRefreshResponse](#agentdebugconversationrefreshresponse)<br> |
| 403 | Insufficient permissions | |
### [GET] /agent/{agent_id}/drive/files
List agent drive entries for an Agent App
@@ -12562,6 +12576,12 @@ Audit operation recorded for Agent Soul version/revision changes.
| date | string | | Yes |
| message_count | integer | | Yes |
#### AgentDebugConversationRefreshResponse
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| debug_conversation_id | string | | Yes |
#### AgentDriveDeleteFileByAgentQuery
| Name | Type | Description | Required |
+46
View File
@@ -492,6 +492,52 @@ class AgentRosterService:
self._session.commit()
return conversation_id
def refresh_agent_app_debug_conversation_id(
self, *, tenant_id: str, agent_id: str, account_id: str, commit: bool = True
) -> str:
"""Start a new console debug conversation for the current Agent App editor."""
agent = self._session.scalar(
select(Agent).where(
Agent.tenant_id == tenant_id,
Agent.id == agent_id,
Agent.scope == AgentScope.ROSTER,
Agent.source == AgentSource.AGENT_APP,
Agent.status == AgentStatus.ACTIVE,
)
)
if agent is None or not agent.app_id:
raise AgentNotFoundError()
conversation_id = self._create_agent_app_debug_conversation(
app_id=agent.app_id,
account_id=account_id,
)
mapping = self._session.scalar(
select(AgentDebugConversation).where(
AgentDebugConversation.tenant_id == tenant_id,
AgentDebugConversation.agent_id == agent_id,
AgentDebugConversation.account_id == account_id,
)
)
if mapping is None:
self._session.add(
AgentDebugConversation(
tenant_id=tenant_id,
agent_id=agent_id,
app_id=agent.app_id,
account_id=account_id,
conversation_id=conversation_id,
)
)
else:
mapping.app_id = agent.app_id
mapping.conversation_id = conversation_id
self._session.flush()
if commit:
self._session.commit()
return conversation_id
def load_or_create_agent_app_debug_conversation_ids_by_agent_id(
self, *, tenant_id: str, agents: list[Agent], account_id: str
) -> dict[str, str]:
@@ -27,6 +27,7 @@ from controllers.console.agent.roster import (
AgentAppApi,
AgentAppCopyApi,
AgentAppListApi,
AgentDebugConversationRefreshApi,
AgentInviteOptionsApi,
AgentLogMessagesApi,
AgentLogsApi,
@@ -158,6 +159,7 @@ def test_agent_v2_console_routes_are_agent_id_first() -> None:
"/agent/<uuid:agent_id>/api-enable",
"/agent/<uuid:agent_id>/api-keys",
"/agent/<uuid:agent_id>/api-keys/<uuid:api_key_id>",
"/agent/<uuid:agent_id>/debug-conversation/refresh",
"/agent/<uuid:agent_id>/chat-messages",
"/agent/<uuid:agent_id>/chat-messages/<string:task_id>/stop",
"/agent/<uuid:agent_id>/feedbacks",
@@ -483,6 +485,38 @@ def test_agent_app_copy_uses_agent_id_and_returns_agent_detail(
}
def test_agent_debug_conversation_refresh_uses_current_user(
app: Flask, monkeypatch: pytest.MonkeyPatch, account_id: str
) -> None:
agent_id = "00000000-0000-0000-0000-000000000001"
captured: dict[str, object] = {}
class FakeRosterService:
def refresh_agent_app_debug_conversation_id(self, **kwargs: object) -> str:
captured.update(kwargs)
return "new-debug-conversation-id"
monkeypatch.setattr(roster_controller, "_agent_roster_service", lambda: FakeRosterService())
with app.test_request_context(
"/console/api/agent/00000000-0000-0000-0000-000000000001/debug-conversation/refresh",
method="POST",
):
response = unwrap(AgentDebugConversationRefreshApi.post)(
AgentDebugConversationRefreshApi(),
"tenant-1",
SimpleNamespace(id=account_id),
agent_id,
)
assert response == {"debug_conversation_id": "new-debug-conversation-id"}
assert captured == {
"tenant_id": "tenant-1",
"agent_id": agent_id,
"account_id": account_id,
}
def test_agent_api_access_uses_agent_id_and_returns_service_api_metadata(
monkeypatch: pytest.MonkeyPatch,
) -> None:
@@ -1649,6 +1649,74 @@ class TestAgentAppBackingAgent:
with pytest.raises(roster_service.AgentNotFoundError):
service.get_agent_app_model(tenant_id="tenant-1", agent_id="agent-x")
def test_refresh_agent_app_debug_conversation_creates_mapping(self):
agent = Agent(
id="agent-1",
tenant_id="tenant-1",
name="Iris",
description="",
agent_kind=AgentKind.DIFY_AGENT,
scope=AgentScope.ROSTER,
source=AgentSource.AGENT_APP,
status=AgentStatus.ACTIVE,
app_id="app-1",
)
session = FakeSession(scalar=[agent, None])
service = AgentRosterService(session)
conversation_id = service.refresh_agent_app_debug_conversation_id(
tenant_id="tenant-1",
agent_id="agent-1",
account_id="account-1",
)
conversations = [a for a in session.added if isinstance(a, Conversation)]
assert len(conversations) == 1
assert conversations[0].id == conversation_id
assert conversations[0].app_id == "app-1"
assert conversations[0].from_source == ConversationFromSource.CONSOLE
assert conversations[0].from_account_id == "account-1"
mappings = [a for a in session.added if isinstance(a, AgentDebugConversation)]
assert len(mappings) == 1
assert mappings[0].tenant_id == "tenant-1"
assert mappings[0].agent_id == "agent-1"
assert mappings[0].app_id == "app-1"
assert mappings[0].account_id == "account-1"
assert mappings[0].conversation_id == conversation_id
assert session.deleted == []
assert session.commits == 1
def test_refresh_agent_app_debug_conversation_replaces_existing_mapping(self):
agent = Agent(
id="agent-1",
tenant_id="tenant-1",
name="Iris",
description="",
agent_kind=AgentKind.DIFY_AGENT,
scope=AgentScope.ROSTER,
source=AgentSource.AGENT_APP,
status=AgentStatus.ACTIVE,
app_id="app-1",
)
mapping = SimpleNamespace(app_id="old-app", conversation_id="old-conversation")
session = FakeSession(scalar=[agent, mapping])
service = AgentRosterService(session)
conversation_id = service.refresh_agent_app_debug_conversation_id(
tenant_id="tenant-1",
agent_id="agent-1",
account_id="account-1",
)
assert mapping.app_id == "app-1"
assert mapping.conversation_id == conversation_id
assert [a for a in session.added if isinstance(a, AgentDebugConversation)] == []
conversations = [a for a in session.added if isinstance(a, Conversation)]
assert len(conversations) == 1
assert conversations[0].id == conversation_id
assert session.deleted == []
assert session.commits == 1
def test_duplicate_agent_app_copies_app_config_and_active_soul(self, monkeypatch: pytest.MonkeyPatch):
source_config = SimpleNamespace(
opening_statement="hello",