deerflow2/backend/app/gateway
Xinmin Zeng ca3332f8bf
fix(gateway): return ISO 8601 timestamps from threads endpoints (#2599)
* fix(gateway): return ISO 8601 timestamps from threads endpoints (#2594)

ThreadResponse documents created_at / updated_at as ISO timestamps,
matching the LangGraph Platform schema (langgraph_sdk.schema.Thread
exposes them as datetime, JSON-encoded as ISO 8601). The gateway
threads router was instead emitting str(time.time()) — unix-second
floats — breaking frontend new Date() parsing and producing a mixed
ISO/unix wire format that also corrupted the search sort order.

Centralize timestamp generation in deerflow.utils.time:
- now_iso()       — datetime.now(UTC).isoformat()
- coerce_iso(x)   — heals legacy unix-timestamp strings on read so the
                    store converges to ISO without a one-shot migration

threads.py: replace 6 time.time() call sites with now_iso(); wrap all
read paths and Phase-2 checkpoint metadata with coerce_iso(); _store_upsert
opportunistically heals legacy created_at on update; drop unused time import.

thread_runs.py: reuse now_iso() instead of a private duplicate _now_iso(),
preventing future drift between the two timestamp call sites.

Tests: 9 unit tests for the helper; 5 integration tests pinning the ISO
contract for create/get/patch/search and the legacy-healing path on the
internal store upsert. Full suite: 2144 passed, 15 skipped, 0 failed.

Closes #2594

* fix(gateway): coerce checkpoint metadata timestamps to ISO on read

After the merge with main, three additional read paths in ``threads.py``
were still emitting raw ``str(metadata.get("created_at", ""))`` —
``get_thread_state``, ``update_thread_state``, and ``get_thread_history``.

Same root cause as #2594: when the checkpoint metadata's ``created_at``
is a unix-second float (legacy data, or a checkpoint written by an older
Gateway version), ``str(float)`` produces ``"1777252410.411327"`` and the
frontend's ``new Date(...)`` returns ``Invalid Date``. The fix on the
``/threads/{id}`` GET path was already in place; these three sibling
endpoints needed the same treatment.

All four call sites now flow through ``coerce_iso``, so:
- legacy float metadata heals to ISO on the way out,
- ISO metadata passes through unchanged,
- ``datetime`` instances (which the new ``coerce_iso`` branch handles
  explicitly) emit with the ``T`` separator instead of falling through
  to the space-separated ``str(datetime)`` form.

Coverage added for the two endpoints not already pinned by the merge:
- ``test_get_thread_state_returns_iso_for_legacy_checkpoint_metadata``
- ``test_get_thread_history_returns_iso_for_legacy_checkpoint_metadata``

Both pre-seed a checkpoint whose metadata carries the literal float
from the issue body and assert the wire format is ISO.
2026-05-02 15:16:16 +08:00
..
auth fix(security): harden auth system and fix run journal logic bug (#2593) 2026-04-28 11:34:07 +08:00
routers fix(gateway): return ISO 8601 timestamps from threads endpoints (#2599) 2026-05-02 15:16:16 +08:00
__init__.py refactor: split backend into harness (deerflow.*) and app (app.*) (#1131) 2026-03-14 22:55:52 +08:00
app.py fix(config): unify log_level from config.yaml across Gateway and debug entry points (#2601) 2026-04-30 22:27:14 +08:00
auth_middleware.py feat: implement process-local internal authentication for Gateway and enhance CSRF handling 2026-04-26 22:20:57 +08:00
authz.py fix(security): harden auth system and fix run journal logic bug (#2593) 2026-04-28 11:34:07 +08:00
config.py fix(security): allow disabling API docs in production via GATEWAY_ENABLE_DOCS (#2651) 2026-04-30 10:58:32 +08:00
csrf_middleware.py feat: replace auto-admin creation with secure interactive first-boot setup (#2063) 2026-04-26 11:08:41 +08:00
deps.py refactor: thread release config through lead path (#2612) 2026-04-28 14:53:18 +08:00
internal_auth.py feat: implement process-local internal authentication for Gateway and enhance CSRF handling 2026-04-26 22:20:57 +08:00
langgraph_auth.py fix(security): harden auth system and fix run journal logic bug (#2593) 2026-04-28 11:34:07 +08:00
path_utils.py feat(persistence): per-user filesystem isolation, run-scoped APIs, and state/history simplification (#2153) 2026-04-26 11:13:01 +08:00
services.py fix(agents): propagate agent_name into ToolRuntime.context for setup_agent (#2679) 2026-05-01 16:00:11 +08:00
utils.py feat(persistence): add unified persistence layer with event store, token tracking, and feedback (#1930) 2026-04-26 11:05:47 +08:00