feat: 在 generate 流程中接入 Delta 模式与推送闸门
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user