fix(api): return agent timestamps as epoch seconds (#37057)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
yyh 2026-06-04 16:27:37 +08:00 committed by GitHub
parent f0fd7ddb60
commit f9320b2c91
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 71 additions and 41 deletions

View File

@ -37,7 +37,7 @@ class AgentConfigSnapshotSummaryResponse(ResponseModel):
summary: str | None = None
version_note: str | None = None
created_by: str | None = None
created_at: str | None = None
created_at: int | None = None
class AgentRosterResponse(ResponseModel):
@ -59,9 +59,9 @@ class AgentRosterResponse(ResponseModel):
created_by: str | None = None
updated_by: str | None = None
archived_by: str | None = None
archived_at: str | None = None
created_at: str | None = None
updated_at: str | None = None
archived_at: int | None = None
created_at: int | None = None
updated_at: int | None = None
class AgentInviteOptionResponse(AgentRosterResponse):
@ -95,7 +95,7 @@ class AgentConfigRevisionResponse(ResponseModel):
summary: str | None = None
version_note: str | None = None
created_by: str | None = None
created_at: str | None = None
created_at: int | None = None
class AgentConfigSnapshotDetailResponse(AgentConfigSnapshotSummaryResponse):

View File

@ -11058,7 +11058,7 @@ Audit operation recorded for Agent Soul version/revision changes.
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| created_at | string | | No |
| created_at | integer | | No |
| created_by | string | | No |
| current_snapshot_id | string | | Yes |
| id | string | | Yes |
@ -11074,7 +11074,7 @@ Audit operation recorded for Agent Soul version/revision changes.
| ---- | ---- | ----------- | -------- |
| agent_id | string | | No |
| config_snapshot | [AgentSoulConfig](#agentsoulconfig) | | Yes |
| created_at | string | | No |
| created_at | integer | | No |
| created_by | string | | No |
| id | string | | Yes |
| revisions | [ [AgentConfigRevisionResponse](#agentconfigrevisionresponse) ] | | No |
@ -11093,7 +11093,7 @@ Audit operation recorded for Agent Soul version/revision changes.
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| agent_id | string | | No |
| created_at | string | | No |
| created_at | integer | | No |
| created_by | string | | No |
| id | string | | Yes |
| summary | string | | No |
@ -11165,9 +11165,9 @@ Supported icon storage formats for Agent roster entries.
| active_config_snapshot_id | string | | No |
| agent_kind | [AgentKind](#agentkind) | | Yes |
| app_id | string | | No |
| archived_at | string | | No |
| archived_at | integer | | No |
| archived_by | string | | No |
| created_at | string | | No |
| created_at | integer | | No |
| created_by | string | | No |
| description | string | | Yes |
| existing_node_ids | [ string ] | | No |
@ -11181,7 +11181,7 @@ Supported icon storage formats for Agent roster entries.
| scope | [AgentScope](#agentscope) | | Yes |
| source | [AgentSource](#agentsource) | | Yes |
| status | [AgentStatus](#agentstatus) | | Yes |
| updated_at | string | | No |
| updated_at | integer | | No |
| updated_by | string | | No |
| workflow_id | string | | No |
| workflow_node_id | string | | No |
@ -11311,9 +11311,9 @@ the current roster/workflow APIs scoped to Dify Agent.
| active_config_snapshot_id | string | | No |
| agent_kind | [AgentKind](#agentkind) | | Yes |
| app_id | string | | No |
| archived_at | string | | No |
| archived_at | integer | | No |
| archived_by | string | | No |
| created_at | string | | No |
| created_at | integer | | No |
| created_by | string | | No |
| description | string | | Yes |
| icon | string | | No |
@ -11324,7 +11324,7 @@ the current roster/workflow APIs scoped to Dify Agent.
| scope | [AgentScope](#agentscope) | | Yes |
| source | [AgentSource](#agentsource) | | Yes |
| status | [AgentStatus](#agentstatus) | | Yes |
| updated_at | string | | No |
| updated_at | integer | | No |
| updated_by | string | | No |
| workflow_id | string | | No |
| workflow_node_id | string | | No |

View File

@ -4,6 +4,7 @@ from sqlalchemy import func, select
from sqlalchemy.exc import IntegrityError
from extensions.ext_database import db
from libs.helper import to_timestamp
from models.agent import (
Agent,
AgentConfigRevision,
@ -802,7 +803,7 @@ class AgentComposerService:
"version": version.version,
"version_note": version.version_note,
"created_by": version.created_by,
"created_at": version.created_at.isoformat() if version.created_at else None,
"created_at": to_timestamp(version.created_at),
}
@staticmethod

View File

@ -4,6 +4,7 @@ from sqlalchemy import func, select
from sqlalchemy.exc import IntegrityError
from libs.datetime_utils import naive_utc_now
from libs.helper import to_timestamp
from models.agent import (
Agent,
AgentConfigRevision,
@ -64,9 +65,9 @@ class AgentRosterService:
"created_by": agent.created_by,
"updated_by": agent.updated_by,
"archived_by": agent.archived_by,
"archived_at": agent.archived_at.isoformat() if agent.archived_at else None,
"created_at": agent.created_at.isoformat() if agent.created_at else None,
"updated_at": agent.updated_at.isoformat() if agent.updated_at else None,
"archived_at": to_timestamp(agent.archived_at),
"created_at": to_timestamp(agent.created_at),
"updated_at": to_timestamp(agent.updated_at),
}
@staticmethod
@ -80,7 +81,7 @@ class AgentRosterService:
"summary": version.summary,
"version_note": version.version_note,
"created_by": version.created_by,
"created_at": version.created_at.isoformat() if version.created_at else None,
"created_at": to_timestamp(version.created_at),
}
def list_roster_agents(
@ -418,7 +419,7 @@ class AgentRosterService:
"summary": revision.summary,
"version_note": revision.version_note,
"created_by": revision.created_by,
"created_at": revision.created_at.isoformat() if revision.created_at else None,
"created_at": to_timestamp(revision.created_at),
}
for revision in revisions
]

View File

@ -1,3 +1,4 @@
from datetime import UTC, datetime
from types import SimpleNamespace
import pytest
@ -495,6 +496,9 @@ def test_composer_current_version_and_error_paths(monkeypatch):
def test_roster_list_and_invite_options(monkeypatch):
created_at = datetime(2026, 1, 2, 3, 4, 5, tzinfo=UTC)
updated_at = datetime(2026, 1, 3, 3, 4, 5, tzinfo=UTC)
version_created_at = datetime(2026, 1, 4, 3, 4, 5, tzinfo=UTC)
agent = Agent(
id="agent-1",
tenant_id="tenant-1",
@ -505,7 +509,10 @@ def test_roster_list_and_invite_options(monkeypatch):
source=AgentSource.AGENT_APP,
status=AgentStatus.ACTIVE,
)
agent.created_at = created_at
agent.updated_at = updated_at
version = AgentConfigSnapshot(id="version-1", agent_id="agent-1", version=1)
version.created_at = version_created_at
agent.active_config_snapshot_id = "version-1"
fake_session = FakeSession(
scalar=[1, 1, SimpleNamespace(id="workflow-1")],
@ -518,13 +525,30 @@ def test_roster_list_and_invite_options(monkeypatch):
invited = service.list_invite_options(tenant_id="tenant-1", page=1, limit=20, app_id="app-1")
assert listed["data"][0]["active_config_snapshot"]["id"] == "version-1"
assert listed["data"][0]["created_at"] == int(created_at.timestamp())
assert listed["data"][0]["updated_at"] == int(updated_at.timestamp())
assert listed["data"][0]["active_config_snapshot"]["created_at"] == int(version_created_at.timestamp())
assert invited["data"][0]["is_in_current_workflow"] is True
assert invited["data"][0]["existing_node_ids"] == ["node-1"]
def test_roster_update_archive_versions_and_detail(monkeypatch):
listed_version = AgentConfigSnapshot(id="version-2", agent_id="agent-1", version=2)
fake_session = FakeSession(scalars=[[listed_version]])
listed_version_created_at = datetime(2026, 1, 5, 3, 4, 5, tzinfo=UTC)
listed_version.created_at = listed_version_created_at
revision_created_at = datetime(2026, 1, 6, 3, 4, 5, tzinfo=UTC)
revision = SimpleNamespace(
id="revision-1",
previous_snapshot_id=None,
current_snapshot_id="version-1",
revision=1,
operation=AgentConfigRevisionOperation.CREATE_VERSION,
summary=None,
version_note=None,
created_by="account-1",
created_at=revision_created_at,
)
fake_session = FakeSession(scalars=[[listed_version], [revision]])
agent = Agent(
id="agent-1",
tenant_id="tenant-1",
@ -536,6 +560,7 @@ def test_roster_update_archive_versions_and_detail(monkeypatch):
status=AgentStatus.ACTIVE,
)
version = AgentConfigSnapshot(id="version-1", agent_id="agent-1", version=1, config_snapshot='{"prompt":{}}')
version.created_at = datetime(2026, 1, 4, 3, 4, 5, tzinfo=UTC)
service = AgentRosterService(fake_session)
monkeypatch.setattr(service, "_get_agent", lambda **kwargs: agent)
@ -559,7 +584,10 @@ def test_roster_update_archive_versions_and_detail(monkeypatch):
assert updated["description"] == "new"
assert agent.status == AgentStatus.ARCHIVED
assert versions[0]["id"] == "version-2"
assert versions[0]["created_at"] == int(listed_version_created_at.timestamp())
assert detail["config_snapshot"] == {"prompt": {}}
assert detail["created_at"] == int(version.created_at.timestamp())
assert detail["revisions"][0]["created_at"] == int(revision_created_at.timestamp())
def test_roster_create_detail_and_lookup_helpers(monkeypatch):

View File

@ -27,9 +27,9 @@ export type AgentRosterResponse = {
active_config_snapshot_id?: string | null
agent_kind: AgentKind
app_id?: string | null
archived_at?: string | null
archived_at?: number | null
archived_by?: string | null
created_at?: string | null
created_at?: number | null
created_by?: string | null
description: string
icon?: string | null
@ -40,7 +40,7 @@ export type AgentRosterResponse = {
scope: AgentScope
source: AgentSource
status: AgentStatus
updated_at?: string | null
updated_at?: number | null
updated_by?: string | null
workflow_id?: string | null
workflow_node_id?: string | null
@ -69,7 +69,7 @@ export type AgentConfigSnapshotListResponse = {
export type AgentConfigSnapshotDetailResponse = {
agent_id?: string | null
config_snapshot: AgentSoulConfig
created_at?: string | null
created_at?: number | null
created_by?: string | null
id: string
revisions?: Array<AgentConfigRevisionResponse>
@ -98,7 +98,7 @@ export type AgentIconType = 'emoji' | 'image' | 'link'
export type AgentConfigSnapshotSummaryResponse = {
agent_id?: string | null
created_at?: string | null
created_at?: number | null
created_by?: string | null
id: string
summary?: string | null
@ -119,9 +119,9 @@ export type AgentInviteOptionResponse = {
active_config_snapshot_id?: string | null
agent_kind: AgentKind
app_id?: string | null
archived_at?: string | null
archived_at?: number | null
archived_by?: string | null
created_at?: string | null
created_at?: number | null
created_by?: string | null
description: string
existing_node_ids?: Array<string>
@ -135,14 +135,14 @@ export type AgentInviteOptionResponse = {
scope: AgentScope
source: AgentSource
status: AgentStatus
updated_at?: string | null
updated_at?: number | null
updated_by?: string | null
workflow_id?: string | null
workflow_node_id?: string | null
}
export type AgentConfigRevisionResponse = {
created_at?: string | null
created_at?: number | null
created_by?: string | null
current_snapshot_id: string
id: string

View File

@ -25,7 +25,7 @@ export const zRosterAgentUpdatePayload = z.object({
*/
export const zAgentConfigSnapshotSummaryResponse = z.object({
agent_id: z.string().nullish(),
created_at: z.string().nullish(),
created_at: z.int().nullish(),
created_by: z.string().nullish(),
id: z.string(),
summary: z.string().nullish(),
@ -79,9 +79,9 @@ export const zAgentRosterResponse = z.object({
active_config_snapshot_id: z.string().nullish(),
agent_kind: zAgentKind,
app_id: z.string().nullish(),
archived_at: z.string().nullish(),
archived_at: z.int().nullish(),
archived_by: z.string().nullish(),
created_at: z.string().nullish(),
created_at: z.int().nullish(),
created_by: z.string().nullish(),
description: z.string(),
icon: z.string().nullish(),
@ -92,7 +92,7 @@ export const zAgentRosterResponse = z.object({
scope: zAgentScope,
source: zAgentSource,
status: zAgentStatus,
updated_at: z.string().nullish(),
updated_at: z.int().nullish(),
updated_by: z.string().nullish(),
workflow_id: z.string().nullish(),
workflow_node_id: z.string().nullish(),
@ -117,9 +117,9 @@ export const zAgentInviteOptionResponse = z.object({
active_config_snapshot_id: z.string().nullish(),
agent_kind: zAgentKind,
app_id: z.string().nullish(),
archived_at: z.string().nullish(),
archived_at: z.int().nullish(),
archived_by: z.string().nullish(),
created_at: z.string().nullish(),
created_at: z.int().nullish(),
created_by: z.string().nullish(),
description: z.string(),
existing_node_ids: z.array(z.string()).optional(),
@ -133,7 +133,7 @@ export const zAgentInviteOptionResponse = z.object({
scope: zAgentScope,
source: zAgentSource,
status: zAgentStatus,
updated_at: z.string().nullish(),
updated_at: z.int().nullish(),
updated_by: z.string().nullish(),
workflow_id: z.string().nullish(),
workflow_node_id: z.string().nullish(),
@ -184,7 +184,7 @@ export const zAgentConfigRevisionOperation = z.enum([
* AgentConfigRevisionResponse
*/
export const zAgentConfigRevisionResponse = z.object({
created_at: z.string().nullish(),
created_at: z.int().nullish(),
created_by: z.string().nullish(),
current_snapshot_id: z.string(),
id: z.string(),
@ -591,7 +591,7 @@ export const zRosterAgentCreatePayload = z.object({
export const zAgentConfigSnapshotDetailResponse = z.object({
agent_id: z.string().nullish(),
config_snapshot: zAgentSoulConfig,
created_at: z.string().nullish(),
created_at: z.int().nullish(),
created_by: z.string().nullish(),
id: z.string(),
revisions: z.array(zAgentConfigRevisionResponse).optional(),

View File

@ -1034,7 +1034,7 @@ export type AdvancedChatWorkflowRunForListResponse = {
export type AgentConfigSnapshotSummaryResponse = {
agent_id?: string | null
created_at?: string | null
created_at?: number | null
created_by?: string | null
id: string
summary?: string | null

View File

@ -753,7 +753,7 @@ export const zSite = z.object({
*/
export const zAgentConfigSnapshotSummaryResponse = z.object({
agent_id: z.string().nullish(),
created_at: z.string().nullish(),
created_at: z.int().nullish(),
created_by: z.string().nullish(),
id: z.string(),
summary: z.string().nullish(),