mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-10-27 13:40:37 +00:00
* fix(gateway): scope per-run message and event reads to the run's data identity
start_run stamps run-event rows with the raw trusted owner id, but
GET /runs/{rid}/messages and /runs/{rid}/events called the event store with
user_id=AUTO, which resolves to the internal caller's make_safe_user_id form.
On the database event store, owners whose id contains characters outside
[A-Za-z0-9_-] (IM channel ids such as "feishu:owner-777") therefore got an
empty list. The regenerate source-run scan had the same mismatch and fell
back or returned 409.
Pass _run_scope_user_id() explicitly, as the thread message routes already
do (#5448), and add user_id to RunEventStore.list_messages_by_run() so every
backend accepts it like list_messages()/list_events(). Browser and API
sessions keep their per-user filter.
* fix(gateway): scope workspace-changes and archive reads to the run's data identity
The same raw-owner mismatch reached two more run-scoped routes. On the SQL
stores, GET /runs/{rid}/workspace-changes reported no changes because its
event lookup used user_id=AUTO, and GET /runs/{rid}/artifacts/archive
answered 404 because both its run-row lookup and its run.delivery event
lookup did.
Pass _run_scope_user_id() to both, and let get_workspace_changes_response()
forward an explicit user_id to the event store.
* docs(changelog): reference #6282 in the run-scope read fix entries
---------
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
81 lines
2.0 KiB
Python
81 lines
2.0 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from deerflow.runtime.user_context import AUTO, _AutoSentinel
|
|
|
|
from .types import WORKSPACE_CHANGES_EVENT_TYPE, WORKSPACE_CHANGES_METADATA_KEY
|
|
|
|
EMPTY_SUMMARY = {
|
|
"created": 0,
|
|
"modified": 0,
|
|
"deleted": 0,
|
|
"symlink_created": 0,
|
|
"additions": 0,
|
|
"deletions": 0,
|
|
"truncated": False,
|
|
}
|
|
|
|
|
|
async def get_workspace_changes_response(
|
|
event_store: Any,
|
|
thread_id: str,
|
|
run_id: str,
|
|
*,
|
|
include_files: bool = True,
|
|
include_diff: bool = True,
|
|
user_id: str | None | _AutoSentinel = AUTO,
|
|
) -> dict[str, Any]:
|
|
events = await event_store.list_events(
|
|
thread_id,
|
|
run_id,
|
|
event_types=[WORKSPACE_CHANGES_EVENT_TYPE],
|
|
limit=10,
|
|
user_id=user_id,
|
|
)
|
|
if not events:
|
|
return _empty_response()
|
|
|
|
payload = _extract_workspace_changes_payload(events[-1])
|
|
if not isinstance(payload, dict):
|
|
return _empty_response()
|
|
|
|
response = dict(payload)
|
|
response["available"] = True
|
|
response.setdefault("summary", dict(EMPTY_SUMMARY))
|
|
if include_files:
|
|
response.setdefault("files", [])
|
|
if not include_diff:
|
|
response["files"] = [_without_diff(file) for file in response["files"]]
|
|
else:
|
|
response["files"] = []
|
|
return response
|
|
|
|
|
|
def _empty_response() -> dict[str, Any]:
|
|
return {
|
|
"available": False,
|
|
"version": 1,
|
|
"summary": dict(EMPTY_SUMMARY),
|
|
"files": [],
|
|
"limits": {},
|
|
}
|
|
|
|
|
|
def _extract_workspace_changes_payload(event: dict[str, Any]) -> Any:
|
|
metadata = event.get("metadata") or {}
|
|
if isinstance(metadata, dict) and WORKSPACE_CHANGES_METADATA_KEY in metadata:
|
|
return metadata[WORKSPACE_CHANGES_METADATA_KEY]
|
|
content = event.get("content")
|
|
if isinstance(content, dict):
|
|
return content
|
|
return None
|
|
|
|
|
|
def _without_diff(file: Any) -> Any:
|
|
if not isinstance(file, dict):
|
|
return file
|
|
sanitized = dict(file)
|
|
sanitized["diff"] = ""
|
|
return sanitized
|