feat: 在 generate 流程中接入 Delta 模式与推送闸门

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-09 11:37:54 +08:00
parent b2dd8721d0
commit 588def8eb2
2 changed files with 99 additions and 43 deletions

View File

@@ -22,15 +22,24 @@ from daily.config import (
LOG_DIR,
OUTPUT_DIR,
SNAPSHOT_FILE,
ensure_bot_on_path,
env,
env_int,
full_desc_limit,
news_summary_limit,
wecom_skill_desc_limit,
)
from daily.format_wecom import build_wecom_report, finalize_wecom_skill_groups, replace_wecom_skill_sections
from daily.format_wecom import (
build_wecom_report,
finalize_wecom_skill_groups,
replace_wecom_board_sections,
replace_wecom_skill_sections,
)
from daily.agent_workflow import is_agent_mode, run_agent_workflow
from daily.featured_pick import (
apply_featured_pick,
pick_command_from_featured,
pick_why_from_featured,
)
from daily.delta import compare_depth
from daily.cursor_editor import (
apply_descriptions,
@@ -50,18 +59,17 @@ from daily.news.fetch import (
prepare_wecom_cn_news_items,
prepare_wecom_news_items,
)
from daily.news.pushed_links import record_pushed_links
from daily.push_gate import evaluate_push_gate
from daily.report_data import (
build_full_payload,
build_llm_input,
data_json_path,
save_json,
)
from daily.skills_board import load_boards
from daily.skills_board import format_installs, load_boards, load_feed
from daily.skills_group import group_skills_by_source
ensure_bot_on_path()
from skills_service import _format_installs, load_feed # noqa: E402
THEME_RULES: list[tuple[str, str, list[str]]] = [
("🎬", "AI 多媒体 / 视频", ["runcomfy", "remotion", "video", "seedance", "inpaint", "lipsync"]),
("🔧", "工程协作 / Skill 元能力", ["grill", "tdd", "architecture", "find-skills", "to-issues"]),
@@ -232,7 +240,7 @@ def _prepare_skill_item(item: dict[str, Any], prev_ids: set[str], rank: int) ->
badge = "🆕"
elif rank == 1:
badge = "👑"
installs_fmt = item.get("installs_fmt") or _format_installs(item.get("installs", 0))
installs_fmt = item.get("installs_fmt") or format_installs(item.get("installs", 0))
title = item.get("source", "?") if item.get("cluster") else item.get("title", "?")
desc = item.get("wecom_desc") or item.get("description") or item.get("cluster_titles") or ""
limit = wecom_skill_desc_limit()
@@ -303,7 +311,7 @@ def _build_highlights(
)
if trending:
t0 = trending[0]
points.append(f"📈 Skills 榜首 **{t0.get('title')}**{_format_installs(t0.get('installs', 0))}")
points.append(f"📈 Skills 榜首 **{t0.get('title')}**{format_installs(t0.get('installs', 0))}")
if github_trending:
g0 = github_trending[0]
stars = g0.get("stars_today_fmt", "")
@@ -315,7 +323,7 @@ def _build_highlights(
points.append(f"🌱 新兴 [{e0['repo']}]({e0['url']})(⭐ {e0.get('total_stars_fmt', '?')}")
elif hot:
h0 = hot[0]
points.append(f"🔥 Skills Hot 榜首 **{h0.get('title')}**1H {_format_installs(h0.get('installs', 0))}")
points.append(f"🔥 Skills Hot 榜首 **{h0.get('title')}**1H {format_installs(h0.get('installs', 0))}")
while len(points) < 3 and len(trending) > len(points):
item = trending[len(points)]
points.append(f"✨ **{item.get('title')}** · `{item.get('source')}`")
@@ -397,7 +405,7 @@ def _format_skill_section(items: list[dict[str, Any]], *, hot: bool = False) ->
for i, item in enumerate(items, 1):
skill_id = item.get("id") or f"{item.get('source', '?')}/{item.get('title', '?')}"
link = item.get("link", "")
installs = _format_installs(item.get("installs", 0))
installs = format_installs(item.get("installs", 0))
meta = f"1H {installs}" if hot else f"总安装 {installs}"
if link:
lines.append(f"{i}. **[{skill_id}]({link})** · {meta}")
@@ -467,17 +475,22 @@ def generate_report() -> tuple[str, str, Path, Path]:
cn_ai_news=cn_ai_news,
wecom_limits=wecom_limits,
)
save_json(
data_json_path(date_str),
build_full_payload(
llm_input,
meta={
"generated_at": now.isoformat(),
"report_mode": "agent" if is_agent_mode() else "classic",
"cursor_editor": cursor_editor_enabled() and not is_agent_mode(),
},
),
featured = apply_featured_pick(llm_input, date_str=date_str)
movement = llm_input["movement"]
eff_mode = llm_input["effective_wecom_mode"]
wecom_ai = prepare_wecom_news_items(ai_news, date_str=date_str)
wecom_cn = prepare_wecom_cn_news_items(cn_ai_news, date_str=date_str)
push_gate = evaluate_push_gate(
movement=movement,
ai_news_items=wecom_ai,
cn_ai_news_items=wecom_cn,
featured_pick=featured,
)
llm_input["push_gate"] = {
"should_push": push_gate.should_push,
"silent": push_gate.silent,
"reasons": push_gate.reasons,
}
agent_wecom: str | None = None
if is_agent_mode():
@@ -586,11 +599,14 @@ def generate_report() -> tuple[str, str, Path, Path]:
pick_src = trending[0].get("source", "") if trending else ""
pick_name = trending[0].get("title", "") if trending else ""
pick_command = (
pick_command = pick_command_from_featured(featured) or (
f"npx skills add {pick_src}/{pick_name}"
if pick_src and pick_name
else "npx skills add vercel-labs/skills/find-skills"
)
pick_why = pick_why_from_featured(featured) or ""
pick_title = str((featured or {}).get("title") or pick_name or "").strip()
pick_url = str((featured or {}).get("url") or "").strip()
lines.extend(["---", "", "## 安装示例", "", "```bash"])
for item in trending[:4]:
@@ -600,10 +616,17 @@ def generate_report() -> tuple[str, str, Path, Path]:
lines.extend(["```", "", f"*企微短版见 `output/{date_str}.wecom.md`*"])
markdown = "\n".join(lines)
gt = group_skills_by_source(trending, limit=wecom_trending, pool_size=skill_pool)
gh = group_skills_by_source(hot, limit=wecom_hot, pool_size=skill_pool)
if agent_wecom:
gt = group_skills_by_source(trending, limit=wecom_trending, pool_size=skill_pool)
gh = group_skills_by_source(hot, limit=wecom_hot, pool_size=skill_pool)
wecom_md = replace_wecom_skill_sections(agent_wecom, trending=gt, hot=gh)
wecom_md = replace_wecom_skill_sections(
agent_wecom,
trending=gt,
hot=gh,
mode=eff_mode,
movement=movement,
topic_name=topic_name,
)
else:
wecom_md = build_wecom_report(
date_str=date_str,
@@ -612,32 +635,57 @@ def generate_report() -> tuple[str, str, Path, Path]:
highlights=editorial_highlights
or _build_highlights(trending, hot, github_trending, github_emerging, ai_news, cn_ai_news),
theme_line=editorial_theme or _detect_theme_line(feed),
ai_news=prepare_wecom_news_items(ai_news),
cn_ai_news=prepare_wecom_cn_news_items(cn_ai_news),
ai_news=wecom_ai,
cn_ai_news=wecom_cn,
trending=[
_prepare_skill_item(item, prev_ids, r)
for r, item in enumerate(
finalize_wecom_skill_groups(
group_skills_by_source(trending, limit=wecom_trending, pool_size=skill_pool)
),
1,
)
for r, item in enumerate(finalize_wecom_skill_groups(gt), 1)
],
hot=[
_prepare_skill_item(item, prev_ids, r)
for r, item in enumerate(
finalize_wecom_skill_groups(
group_skills_by_source(hot, limit=wecom_hot, pool_size=skill_pool)
),
1,
)
for r, item in enumerate(finalize_wecom_skill_groups(gh), 1)
],
repos=[_prepare_github_item(item) for item in github_trending[:wecom_github]],
emerging=[_prepare_github_item(item) for item in github_emerging[:wecom_emerging]],
topic_name=topic_name,
topic_repos=[_prepare_github_item(item) for item in github_topic[:wecom_topic]],
pick_command=pick_command,
pick_why=pick_why,
pick_title=pick_title,
pick_url=pick_url,
include_boards=(eff_mode == "full"),
)
wecom_md = replace_wecom_board_sections(
wecom_md,
mode=eff_mode,
movement=movement,
trending=gt,
hot=gh,
topic_name=topic_name,
)
if push_gate.should_push:
links = [x["link"] for x in wecom_ai + wecom_cn if x.get("link")]
record_pushed_links(date_str, links)
save_json(
data_json_path(date_str),
build_full_payload(
llm_input,
meta={
"generated_at": now.isoformat(),
"report_mode": "agent" if is_agent_mode() else "classic",
"cursor_editor": cursor_editor_enabled() and not is_agent_mode(),
"featured_pick": featured.get("title") if featured else None,
"effective_wecom_mode": eff_mode,
"push_gate": {
"should_push": push_gate.should_push,
"silent": push_gate.silent,
"reasons": push_gate.reasons,
},
},
),
)
_save_snapshot(feed, date_str)
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)

View File

@@ -6,8 +6,8 @@ import json
from pathlib import Path
from typing import Any
from daily.config import OUTPUT_DIR, env_int
from daily.delta import build_movement_baseline, build_movement_context, compare_depth
from daily.config import OUTPUT_DIR, env_int, wecom_mode
from daily.delta import build_movement_baseline, build_movement_context, compare_depth, effective_wecom_mode
from daily.news.fetch import prepare_wecom_cn_news_items, prepare_wecom_news_items
from daily.skills_group import group_skills_by_source
@@ -59,9 +59,10 @@ def _slim_news_items(
limit: int,
*,
prepare=prepare_wecom_news_items,
date_str: str | None = None,
) -> list[dict[str, Any]]:
items: list[dict[str, Any]] = []
for item in prepare(ai_news):
for item in prepare(ai_news, date_str=date_str):
items.append(
{
"link": item.get("link", ""),
@@ -147,10 +148,13 @@ def build_llm_input(
github_topic=[_slim_github(x) for x in topic_cmp],
depth=depth,
)
eff_mode = effective_wecom_mode(date_str=date_str)
return {
"date": date_str,
"data_updated": updated,
"wecom_mode": wecom_mode(),
"effective_wecom_mode": eff_mode,
"skills_trending": [_slim_skill(x) for x in trending_slice],
"skills_hot": [_slim_skill(x) for x in hot_slice],
"github_trending": [_slim_github(x) for x in github_slice],
@@ -159,9 +163,9 @@ def build_llm_input(
"topic": topic_name,
"repos": [_slim_github(x) for x in topic_slice],
},
"ai_news": _slim_news_items(ai_news, news_limit) if ai_news.get("enabled") else [],
"ai_news": _slim_news_items(ai_news, news_limit, date_str=date_str) if ai_news.get("enabled") else [],
"cn_ai_news": _slim_news_items(
cn_ai_news, cn_news_limit, prepare=prepare_wecom_cn_news_items
cn_ai_news, cn_news_limit, prepare=prepare_wecom_cn_news_items, date_str=date_str
)
if cn_ai_news.get("enabled")
else [],
@@ -186,6 +190,10 @@ def editorial_json_path(date_str: str) -> Path:
return OUTPUT_DIR / f"{date_str}.editorial.json"
def featured_json_path(date_str: str) -> Path:
return OUTPUT_DIR / f"{date_str}.featured.json"
def save_json(path: Path, data: dict[str, Any]) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8")