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,
|
LOG_DIR,
|
||||||
OUTPUT_DIR,
|
OUTPUT_DIR,
|
||||||
SNAPSHOT_FILE,
|
SNAPSHOT_FILE,
|
||||||
ensure_bot_on_path,
|
|
||||||
env,
|
env,
|
||||||
env_int,
|
env_int,
|
||||||
full_desc_limit,
|
full_desc_limit,
|
||||||
news_summary_limit,
|
news_summary_limit,
|
||||||
wecom_skill_desc_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.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.delta import compare_depth
|
||||||
from daily.cursor_editor import (
|
from daily.cursor_editor import (
|
||||||
apply_descriptions,
|
apply_descriptions,
|
||||||
@@ -50,18 +59,17 @@ from daily.news.fetch import (
|
|||||||
prepare_wecom_cn_news_items,
|
prepare_wecom_cn_news_items,
|
||||||
prepare_wecom_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 (
|
from daily.report_data import (
|
||||||
build_full_payload,
|
build_full_payload,
|
||||||
build_llm_input,
|
build_llm_input,
|
||||||
data_json_path,
|
data_json_path,
|
||||||
save_json,
|
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
|
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]]] = [
|
THEME_RULES: list[tuple[str, str, list[str]]] = [
|
||||||
("🎬", "AI 多媒体 / 视频", ["runcomfy", "remotion", "video", "seedance", "inpaint", "lipsync"]),
|
("🎬", "AI 多媒体 / 视频", ["runcomfy", "remotion", "video", "seedance", "inpaint", "lipsync"]),
|
||||||
("🔧", "工程协作 / Skill 元能力", ["grill", "tdd", "architecture", "find-skills", "to-issues"]),
|
("🔧", "工程协作 / 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 = "🆕"
|
badge = "🆕"
|
||||||
elif rank == 1:
|
elif rank == 1:
|
||||||
badge = "👑"
|
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", "?")
|
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 ""
|
desc = item.get("wecom_desc") or item.get("description") or item.get("cluster_titles") or ""
|
||||||
limit = wecom_skill_desc_limit()
|
limit = wecom_skill_desc_limit()
|
||||||
@@ -303,7 +311,7 @@ def _build_highlights(
|
|||||||
)
|
)
|
||||||
if trending:
|
if trending:
|
||||||
t0 = trending[0]
|
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:
|
if github_trending:
|
||||||
g0 = github_trending[0]
|
g0 = github_trending[0]
|
||||||
stars = g0.get("stars_today_fmt", "")
|
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', '?')})")
|
points.append(f"🌱 新兴 [{e0['repo']}]({e0['url']})(⭐ {e0.get('total_stars_fmt', '?')})")
|
||||||
elif hot:
|
elif hot:
|
||||||
h0 = hot[0]
|
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):
|
while len(points) < 3 and len(trending) > len(points):
|
||||||
item = trending[len(points)]
|
item = trending[len(points)]
|
||||||
points.append(f"✨ **{item.get('title')}** · `{item.get('source')}`")
|
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):
|
for i, item in enumerate(items, 1):
|
||||||
skill_id = item.get("id") or f"{item.get('source', '?')}/{item.get('title', '?')}"
|
skill_id = item.get("id") or f"{item.get('source', '?')}/{item.get('title', '?')}"
|
||||||
link = item.get("link", "")
|
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}"
|
meta = f"1H {installs}" if hot else f"总安装 {installs}"
|
||||||
if link:
|
if link:
|
||||||
lines.append(f"{i}. **[{skill_id}]({link})** · {meta}")
|
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,
|
cn_ai_news=cn_ai_news,
|
||||||
wecom_limits=wecom_limits,
|
wecom_limits=wecom_limits,
|
||||||
)
|
)
|
||||||
save_json(
|
featured = apply_featured_pick(llm_input, date_str=date_str)
|
||||||
data_json_path(date_str),
|
movement = llm_input["movement"]
|
||||||
build_full_payload(
|
eff_mode = llm_input["effective_wecom_mode"]
|
||||||
llm_input,
|
wecom_ai = prepare_wecom_news_items(ai_news, date_str=date_str)
|
||||||
meta={
|
wecom_cn = prepare_wecom_cn_news_items(cn_ai_news, date_str=date_str)
|
||||||
"generated_at": now.isoformat(),
|
push_gate = evaluate_push_gate(
|
||||||
"report_mode": "agent" if is_agent_mode() else "classic",
|
movement=movement,
|
||||||
"cursor_editor": cursor_editor_enabled() and not is_agent_mode(),
|
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
|
agent_wecom: str | None = None
|
||||||
if is_agent_mode():
|
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_src = trending[0].get("source", "") if trending else ""
|
||||||
pick_name = trending[0].get("title", "") 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}"
|
f"npx skills add {pick_src}/{pick_name}"
|
||||||
if pick_src and pick_name
|
if pick_src and pick_name
|
||||||
else "npx skills add vercel-labs/skills/find-skills"
|
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"])
|
lines.extend(["---", "", "## 安装示例", "", "```bash"])
|
||||||
for item in trending[:4]:
|
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`*"])
|
lines.extend(["```", "", f"*企微短版见 `output/{date_str}.wecom.md`*"])
|
||||||
|
|
||||||
markdown = "\n".join(lines)
|
markdown = "\n".join(lines)
|
||||||
if agent_wecom:
|
|
||||||
gt = group_skills_by_source(trending, limit=wecom_trending, pool_size=skill_pool)
|
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)
|
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)
|
if agent_wecom:
|
||||||
|
wecom_md = replace_wecom_skill_sections(
|
||||||
|
agent_wecom,
|
||||||
|
trending=gt,
|
||||||
|
hot=gh,
|
||||||
|
mode=eff_mode,
|
||||||
|
movement=movement,
|
||||||
|
topic_name=topic_name,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
wecom_md = build_wecom_report(
|
wecom_md = build_wecom_report(
|
||||||
date_str=date_str,
|
date_str=date_str,
|
||||||
@@ -612,31 +635,56 @@ def generate_report() -> tuple[str, str, Path, Path]:
|
|||||||
highlights=editorial_highlights
|
highlights=editorial_highlights
|
||||||
or _build_highlights(trending, hot, github_trending, github_emerging, ai_news, cn_ai_news),
|
or _build_highlights(trending, hot, github_trending, github_emerging, ai_news, cn_ai_news),
|
||||||
theme_line=editorial_theme or _detect_theme_line(feed),
|
theme_line=editorial_theme or _detect_theme_line(feed),
|
||||||
ai_news=prepare_wecom_news_items(ai_news),
|
ai_news=wecom_ai,
|
||||||
cn_ai_news=prepare_wecom_cn_news_items(cn_ai_news),
|
cn_ai_news=wecom_cn,
|
||||||
trending=[
|
trending=[
|
||||||
_prepare_skill_item(item, prev_ids, r)
|
_prepare_skill_item(item, prev_ids, r)
|
||||||
for r, item in enumerate(
|
for r, item in enumerate(finalize_wecom_skill_groups(gt), 1)
|
||||||
finalize_wecom_skill_groups(
|
|
||||||
group_skills_by_source(trending, limit=wecom_trending, pool_size=skill_pool)
|
|
||||||
),
|
|
||||||
1,
|
|
||||||
)
|
|
||||||
],
|
],
|
||||||
hot=[
|
hot=[
|
||||||
_prepare_skill_item(item, prev_ids, r)
|
_prepare_skill_item(item, prev_ids, r)
|
||||||
for r, item in enumerate(
|
for r, item in enumerate(finalize_wecom_skill_groups(gh), 1)
|
||||||
finalize_wecom_skill_groups(
|
|
||||||
group_skills_by_source(hot, limit=wecom_hot, pool_size=skill_pool)
|
|
||||||
),
|
|
||||||
1,
|
|
||||||
)
|
|
||||||
],
|
],
|
||||||
repos=[_prepare_github_item(item) for item in github_trending[:wecom_github]],
|
repos=[_prepare_github_item(item) for item in github_trending[:wecom_github]],
|
||||||
emerging=[_prepare_github_item(item) for item in github_emerging[:wecom_emerging]],
|
emerging=[_prepare_github_item(item) for item in github_emerging[:wecom_emerging]],
|
||||||
topic_name=topic_name,
|
topic_name=topic_name,
|
||||||
topic_repos=[_prepare_github_item(item) for item in github_topic[:wecom_topic]],
|
topic_repos=[_prepare_github_item(item) for item in github_topic[:wecom_topic]],
|
||||||
pick_command=pick_command,
|
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)
|
_save_snapshot(feed, date_str)
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import json
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from daily.config import OUTPUT_DIR, env_int
|
from daily.config import OUTPUT_DIR, env_int, wecom_mode
|
||||||
from daily.delta import build_movement_baseline, build_movement_context, compare_depth
|
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.news.fetch import prepare_wecom_cn_news_items, prepare_wecom_news_items
|
||||||
from daily.skills_group import group_skills_by_source
|
from daily.skills_group import group_skills_by_source
|
||||||
|
|
||||||
@@ -59,9 +59,10 @@ def _slim_news_items(
|
|||||||
limit: int,
|
limit: int,
|
||||||
*,
|
*,
|
||||||
prepare=prepare_wecom_news_items,
|
prepare=prepare_wecom_news_items,
|
||||||
|
date_str: str | None = None,
|
||||||
) -> list[dict[str, Any]]:
|
) -> list[dict[str, Any]]:
|
||||||
items: 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(
|
items.append(
|
||||||
{
|
{
|
||||||
"link": item.get("link", ""),
|
"link": item.get("link", ""),
|
||||||
@@ -147,10 +148,13 @@ def build_llm_input(
|
|||||||
github_topic=[_slim_github(x) for x in topic_cmp],
|
github_topic=[_slim_github(x) for x in topic_cmp],
|
||||||
depth=depth,
|
depth=depth,
|
||||||
)
|
)
|
||||||
|
eff_mode = effective_wecom_mode(date_str=date_str)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"date": date_str,
|
"date": date_str,
|
||||||
"data_updated": updated,
|
"data_updated": updated,
|
||||||
|
"wecom_mode": wecom_mode(),
|
||||||
|
"effective_wecom_mode": eff_mode,
|
||||||
"skills_trending": [_slim_skill(x) for x in trending_slice],
|
"skills_trending": [_slim_skill(x) for x in trending_slice],
|
||||||
"skills_hot": [_slim_skill(x) for x in hot_slice],
|
"skills_hot": [_slim_skill(x) for x in hot_slice],
|
||||||
"github_trending": [_slim_github(x) for x in github_slice],
|
"github_trending": [_slim_github(x) for x in github_slice],
|
||||||
@@ -159,9 +163,9 @@ def build_llm_input(
|
|||||||
"topic": topic_name,
|
"topic": topic_name,
|
||||||
"repos": [_slim_github(x) for x in topic_slice],
|
"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": _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")
|
if cn_ai_news.get("enabled")
|
||||||
else [],
|
else [],
|
||||||
@@ -186,6 +190,10 @@ def editorial_json_path(date_str: str) -> Path:
|
|||||||
return OUTPUT_DIR / f"{date_str}.editorial.json"
|
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:
|
def save_json(path: Path, data: dict[str, Any]) -> None:
|
||||||
path.parent.mkdir(parents=True, exist_ok=True)
|
path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
path.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8")
|
path.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8")
|
||||||
|
|||||||
Reference in New Issue
Block a user