返回 last30days-skill
registers.py
根目录 / skills / last30days / scripts / lib / registers.py
1 """Named audience registers for standard research brief synthesis."""
2
3 from __future__ import annotations
4
5 from dataclasses import dataclass
6 from types import MappingProxyType
7 from typing import Mapping
8
9
10 SectionName = str
11
12
13 @dataclass(frozen=True)
14 class AudienceRegister:
15 """A bounded renderer/synthesis preset for one intended audience."""
16
17 name: str
18 section_order: tuple[SectionName, ...]
19 item_budgets: Mapping[SectionName, int]
20 emphasis_weights: Mapping[str, float]
21
22 def budget_for(self, section: SectionName, fallback: int) -> int:
23 return self.item_budgets.get(section, fallback)
24
25 def emphasis_for(self, source: str) -> float:
26 return self.emphasis_weights.get(source, 1.0)
27
28
29 _DEFAULT_ORDER = (
30 "hiring_signals",
31 "clusters",
32 "stats",
33 "best_takes",
34 "top_comments",
35 "source_outcomes",
36 "source_coverage",
37 )
38
39
40 def _preset(
41 name: str,
42 *,
43 section_order: tuple[SectionName, ...] = _DEFAULT_ORDER,
44 item_budgets: Mapping[SectionName, int] | None = None,
45 emphasis_weights: Mapping[str, float] | None = None,
46 ) -> AudienceRegister:
47 return AudienceRegister(
48 name=name,
49 section_order=section_order,
50 item_budgets=MappingProxyType(dict(item_budgets or {})),
51 emphasis_weights=MappingProxyType(dict(emphasis_weights or {})),
52 )
53
54
55 _REGISTERS = {
56 "default": _preset("default"),
57 "exec": _preset(
58 "exec",
59 section_order=(
60 "stats",
61 "clusters",
62 "hiring_signals",
63 "source_outcomes",
64 "source_coverage",
65 "best_takes",
66 "top_comments",
67 ),
68 item_budgets={"clusters": 5, "best_takes": 2, "top_comments": 3},
69 emphasis_weights={
70 "polymarket": 1.50,
71 "jobs": 1.30,
72 "github": 1.20,
73 "grounding": 1.10,
74 },
75 ),
76 "dev": _preset(
77 "dev",
78 section_order=(
79 "clusters",
80 "source_outcomes",
81 "source_coverage",
82 "hiring_signals",
83 "stats",
84 "top_comments",
85 "best_takes",
86 ),
87 item_budgets={"clusters": 10, "best_takes": 3, "top_comments": 4},
88 emphasis_weights={
89 "github": 1.60,
90 "hackernews": 1.35,
91 "arxiv": 1.30,
92 "grounding": 1.10,
93 },
94 ),
95 "creator": _preset(
96 "creator",
97 section_order=(
98 "best_takes",
99 "top_comments",
100 "stats",
101 "clusters",
102 "hiring_signals",
103 "source_outcomes",
104 "source_coverage",
105 ),
106 item_budgets={"clusters": 6, "best_takes": 5, "top_comments": 8},
107 emphasis_weights={
108 "tiktok": 1.60,
109 "instagram": 1.50,
110 "youtube": 1.40,
111 "x": 1.20,
112 "reddit": 1.10,
113 },
114 ),
115 # ELI5 historically changed only the agent's prose. Keep the renderer
116 # descriptor identical to default and express its voice in SKILL.md.
117 "eli5": _preset("eli5"),
118 }
119
120 REGISTER_NAMES = tuple(_REGISTERS)
121
122
123 def get_register(name: str | None = None) -> AudienceRegister:
124 """Return a named register, rejecting unsupported/free-form templates."""
125
126 normalized = (name or "default").strip().lower()
127 try:
128 return _REGISTERS[normalized]
129 except KeyError as exc:
130 choices = ", ".join(REGISTER_NAMES)
131 raise ValueError(
132 f"unknown audience register {name!r}; choose one of: {choices}"
133 ) from exc
134
134 lines PYTHON