refactor: implement snippet truncation for task descriptions and comments

This commit is contained in:
Abhimanyu Saharan
2026-02-09 02:54:52 +05:30
parent 86cc0f087d
commit 3660aa44d6

View File

@@ -64,6 +64,8 @@ TASK_EVENT_TYPES = {
"task.comment", "task.comment",
} }
SSE_SEEN_MAX = 2000 SSE_SEEN_MAX = 2000
TASK_SNIPPET_MAX_LEN = 500
TASK_SNIPPET_TRUNCATED_LEN = 497
def _comment_validation_error() -> HTTPException: def _comment_validation_error() -> HTTPException:
@@ -83,6 +85,13 @@ def _blocked_task_error(blocked_by_task_ids: Sequence[UUID]) -> HTTPException:
) )
def _truncate_snippet(value: str) -> str:
text = value.strip()
if len(text) <= TASK_SNIPPET_MAX_LEN:
return text
return f"{text[:TASK_SNIPPET_TRUNCATED_LEN]}..."
async def has_valid_recent_comment( async def has_valid_recent_comment(
session: AsyncSession, session: AsyncSession,
task: Task, task: Task,
@@ -282,9 +291,7 @@ async def _notify_agent_on_task_assign(
config = await _gateway_config(session, board) config = await _gateway_config(session, board)
if config is None: if config is None:
return return
description = (task.description or "").strip() description = _truncate_snippet(task.description or "")
if len(description) > 500:
description = f"{description[:497]}..."
details = [ details = [
f"Board: {board.name}", f"Board: {board.name}",
f"Task: {task.title}", f"Task: {task.title}",
@@ -340,9 +347,7 @@ async def _notify_lead_on_task_create(
config = await _gateway_config(session, board) config = await _gateway_config(session, board)
if config is None: if config is None:
return return
description = (task.description or "").strip() description = _truncate_snippet(task.description or "")
if len(description) > 500:
description = f"{description[:497]}..."
details = [ details = [
f"Board: {board.name}", f"Board: {board.name}",
f"Task: {task.title}", f"Task: {task.title}",
@@ -397,9 +402,7 @@ async def _notify_lead_on_task_unassigned(
config = await _gateway_config(session, board) config = await _gateway_config(session, board)
if config is None: if config is None:
return return
description = (task.description or "").strip() description = _truncate_snippet(task.description or "")
if len(description) > 500:
description = f"{description[:497]}..."
details = [ details = [
f"Board: {board.name}", f"Board: {board.name}",
f"Task: {task.title}", f"Task: {task.title}",
@@ -486,33 +489,31 @@ async def stream_tasks(
if len(seen_queue) > SSE_SEEN_MAX: if len(seen_queue) > SSE_SEEN_MAX:
oldest = seen_queue.popleft() oldest = seen_queue.popleft()
seen_ids.discard(oldest) seen_ids.discard(oldest)
if event.created_at > last_seen: last_seen = max(event.created_at, last_seen)
last_seen = event.created_at
payload: dict[str, object] = {"type": event.event_type} payload: dict[str, object] = {"type": event.event_type}
if event.event_type == "task.comment": if event.event_type == "task.comment":
payload["comment"] = _serialize_comment(event) payload["comment"] = _serialize_comment(event)
elif task is None:
payload["task"] = None
else: else:
if task is None: dep_list = deps_map.get(task.id, [])
payload["task"] = None blocked_by = blocked_by_dependency_ids(
else: dependency_ids=dep_list,
dep_list = deps_map.get(task.id, []) status_by_id=dep_status,
blocked_by = blocked_by_dependency_ids( )
dependency_ids=dep_list, if task.status == "done":
status_by_id=dep_status, blocked_by = []
) payload["task"] = (
if task.status == "done": TaskRead.model_validate(task, from_attributes=True)
blocked_by = [] .model_copy(
payload["task"] = ( update={
TaskRead.model_validate(task, from_attributes=True) "depends_on_task_ids": dep_list,
.model_copy( "blocked_by_task_ids": blocked_by,
update={ "is_blocked": bool(blocked_by),
"depends_on_task_ids": dep_list, }
"blocked_by_task_ids": blocked_by,
"is_blocked": bool(blocked_by),
}
)
.model_dump(mode="json")
) )
.model_dump(mode="json")
)
yield {"event": "task", "data": json.dumps(payload)} yield {"event": "task", "data": json.dumps(payload)}
await asyncio.sleep(2) await asyncio.sleep(2)
@@ -891,17 +892,14 @@ async def update_task(
task.updated_at = utcnow() task.updated_at = utcnow()
if "status" in updates and updates["status"] == "review": if "status" in updates and updates["status"] == "review":
if comment is not None and comment.strip(): comment_text = (comment or "").strip()
if not comment.strip(): if not comment_text and not await has_valid_recent_comment(
raise _comment_validation_error() session,
else: task,
if not await has_valid_recent_comment( task.assigned_agent_id,
session, task.in_progress_at,
task, ):
task.assigned_agent_id, raise _comment_validation_error()
task.in_progress_at,
):
raise _comment_validation_error()
session.add(task) session.add(task)
await session.commit() await session.commit()
@@ -1083,9 +1081,7 @@ async def create_task_comment(
board = await Board.objects.by_id(task.board_id).first(session) if task.board_id else None board = await Board.objects.by_id(task.board_id).first(session) if task.board_id else None
config = await _gateway_config(session, board) if board else None config = await _gateway_config(session, board) if board else None
if board and config: if board and config:
snippet = payload.message.strip() snippet = _truncate_snippet(payload.message)
if len(snippet) > 500:
snippet = f"{snippet[:497]}..."
actor_name = actor.agent.name if actor.actor_type == "agent" and actor.agent else "User" actor_name = actor.agent.name if actor.actor_type == "agent" and actor.agent else "User"
for agent in targets.values(): for agent in targets.values():
if not agent.openclaw_session_id: if not agent.openclaw_session_id: