Files
wehub-resource-sync 4b6817381b
Benchmark image — build + push to ECR (any adapter) / build + push (push) Waiting to run
CI / quality (ubuntu-latest) (push) Waiting to run
CI / test (tools-runtime) (push) Waiting to run
CI / test (e2e-general) (push) Waiting to run
CI / test (cli-runtime) (push) Waiting to run
CI / test (e2e-provider-and-openclaw) (push) Waiting to run
CI / test (integrations-and-misc) (push) Waiting to run
CI / coverage-report (push) Blocked by required conditions
CI / test-kubernetes (push) Waiting to run
CI / should-run-thorough (push) Waiting to run
CI / test-thorough (cloudwatch-demo) (push) Blocked by required conditions
CI / test-thorough (flink-ecs) (push) Blocked by required conditions
CI / test-thorough (upstream-lambda) (push) Blocked by required conditions
CI / test-thorough (prefect-ecs-fargate) (push) Blocked by required conditions
CodeQL / Analyze (python) (push) Waiting to run
Release / build-binaries (zip, opensre.exe, onefile, windows-latest, windows-x64) (push) Blocked by required conditions
Release / publish-release (push) Blocked by required conditions
Release / publish-main-release (push) Blocked by required conditions
Release / prepare (push) Waiting to run
Release / verify (push) Blocked by required conditions
Release / build-python-dist (push) Blocked by required conditions
Release / build-binaries (tar.gz, opensre, onedir, macos-15-intel, darwin-x64) (push) Blocked by required conditions
Release / build-binaries (tar.gz, opensre, onedir, macos-latest, darwin-arm64) (push) Blocked by required conditions
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04, linux-x64) (push) Blocked by required conditions
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04-arm, linux-arm64) (push) Blocked by required conditions
Synthetic Deterministic Tests / Synthetic offline (deterministic) (push) Waiting to run
Interactive Shell Live (PR + post-merge) / turn-checks (no-LLM) (push) Waiting to run
Interactive Shell Live (PR + post-merge) / turn-live shard ${{ matrix.shard_index }} (push) Waiting to run
CI (OpenClaw E2E) / openclaw test (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:10:45 +08:00

103 lines
3.5 KiB
Python

"""Community follow-up summarization for GitHub workflow tools."""
from __future__ import annotations
import re
from typing import Any
from integrations.github.tools.workflow.models import CommunityFollowup
_ISSUE_NUMBER_RE = re.compile(r"/issues/(?P<number>\d+)(?:$|[#?])")
def issue_number_from_url(url: str) -> int | None:
match = _ISSUE_NUMBER_RE.search(url)
if match is None:
return None
return int(match.group("number"))
def normalize_community_comment(raw: dict[str, Any]) -> CommunityFollowup:
"""Normalize GitHub repository issue-comment payloads."""
issue_number = raw.get("issue_number")
if not isinstance(issue_number, int):
issue_url = str(raw.get("issue_url") or raw.get("html_url") or "")
issue_number = issue_number_from_url(issue_url)
return CommunityFollowup(
issue_number=issue_number,
issue_title=str(raw.get("issue_title", "")),
author=str(raw.get("author") or (raw.get("user") or {}).get("login", "")),
body=str(raw.get("body", "")),
created_at=str(raw.get("created_at", "")),
url=str(raw.get("url") or raw.get("html_url") or ""),
)
def _is_question(text: str) -> bool:
lowered = text.lower()
return "?" in text or lowered.startswith(
("when ", "what ", "who ", "how ", "where ", "can ", "could ")
)
def _is_agenda_item(text: str) -> bool:
lowered = text.lower()
return any(term in lowered for term in ("agenda", "standup"))
def _question_answered_later(
question: CommunityFollowup,
comments: list[CommunityFollowup],
maintainers: set[str],
) -> bool:
for comment in comments:
if comment.issue_number != question.issue_number:
continue
if comment.created_at <= question.created_at:
continue
if comment.author.lower() in maintainers:
return True
return False
def _suggest_reply(question: CommunityFollowup) -> dict[str, Any]:
return {
"issue_number": question.issue_number,
"issue_title": question.issue_title,
"context": question.body,
"suggested_reply": (
"Thanks for the question — we should confirm the current owner/status "
"and reply in this thread with the next concrete step."
),
"url": question.url,
}
def summarize_community_followups_from_comments(
*,
comments: list[dict[str, Any]],
maintainer_logins: list[str] | None = None,
) -> dict[str, Any]:
normalized_comments = [normalize_community_comment(comment) for comment in comments]
maintainers = {login.lower() for login in (maintainer_logins or [])}
questions = [comment for comment in normalized_comments if _is_question(comment.body)]
unanswered = [
question
for question in questions
if question.author.lower() not in maintainers
and not _question_answered_later(question, normalized_comments, maintainers)
]
agenda_items = [comment for comment in normalized_comments if _is_agenda_item(comment.body)]
return {
"unanswered_questions": [question.to_dict() for question in unanswered],
"agenda_items": [item.to_dict() for item in agenda_items],
"suggested_replies": [_suggest_reply(question) for question in unanswered],
"counts": {
"comments": len(normalized_comments),
"unanswered_questions": len(unanswered),
"agenda_items": len(agenda_items),
},
"side_effects": [],
}