refactor(annual): move the annual package and report facts into annual_service

Staging's backend gate (run 6338) failed three budgets: handler method
count 218 > 217, JyotishAPIHandler.__new__ sites 6 > 5, and the scripts/
root .py count. The package and facts code now live in
scripts/annual_service/ (package.py, report_facts.py, api.py with
compute_annual_package / compute_annual_report_facts and
AnnualServiceInputError). jyotish_api_server.py registers /api/annual
mode=package, /api/annual/package and /api/annual/report_facts thinly
through a module-level _annual_service helper, like /api/chart_points,
with no handler methods: back to 217. Tests call the module functions
and keep a thin-registration test; no cap or baseline changed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DbBXoyeZrYn6ryNcSe5Y2Y
This commit is contained in:
2026-11-01 20:33:17 +08:00
co-authored by Claude Opus 5.5
parent ab0de9e842
commit 32fa756bd4
13 changed files with 218 additions and 124 deletions
+1 -1
View File
@@ -8,7 +8,7 @@
- `"detail": "slim"`(#154 遮罩用):只回报告范围、每张年盘的有效期、生日分界和块状态,几 KB;不回运限行、行运和生产器数据。默认 `"full"`。
- 年运报告的事实与结论卡:`POST /api/annual/report_facts`(同样的请求体,加必填的 `birth_time_confirmed`),见 `annual-report-v1.md`。
- 不带 `mode` 的 `/api/annual` 仍返回原来的基础太阳返照报告(`report`),形状不变;只多传了 `node_mode`(默认 mean,结果不变)。
- 代码:`scripts/annual_package.py`(`normalize_request`、`build_annual_package`),API 处理在 `scripts/jyotish_api_server.py::_compute_annual_package`。前端合同:`frontend/src/lib/annual-package-contract.ts`(zod + 映射,无界面)。
- 代码:`scripts/annual_service/package.py`(`normalize_request`、`build_annual_package`);请求解析与入口在 `scripts/annual_service/api.py`(`compute_annual_package`、`compute_annual_report_facts`,输入错误 `AnnualServiceInputError` 映射为 400),`scripts/jyotish_api_server.py` 只做薄注册(`_annual_service`,与 `/api/chart_points` 同一做法)。前端合同:`frontend/src/lib/annual-package-contract.ts`(zod + 映射,无界面)。
### 请求字段
+2 -2
View File
@@ -7,7 +7,7 @@
1. **入口**:「我的报告」页多一张「年运」卡片,直接进 `/reports/annual`(放在 `/reports` 下面,侧栏仍亮「我的报告」)。没有单独的入口页。
2. **遮罩**:页面先请求 `GET /api/annual/overview?subject=&kind=`(Next 路由 → Python `/api/annual/package`,`detail: "slim"`,几 KB)。返回人物、范围、三条注意事项、出生时间是否确认、价格,以及这一年是不是已经写过。写过的(或正在写的)直接打开,不出遮罩,也不再扣点。
3. **开始**:`POST /api/reports {reportType: "annual_full", subject, rangeKind, situation}`。服务端预留 `annual.full` 的点数,建 `personal_reports` 一行(`report_type = annual_full`,范围键放在 `requested_themes`,例如 `annual:birthday_year:2026`),把范围和现状存成 `personal_report_sections` 的 `annual-report-input`,入报告队列。
4. **算**:worker 调 Python `POST /api/annual/report_facts`(`scripts/annual_report_facts.py`)。这是写手唯一能用的材料:本命底子、年内各段及走势、转折点、候选窗口、各领域信号、分歧、技法执行表。
4. **算**:worker 调 Python `POST /api/annual/report_facts`(`scripts/annual_service/report_facts.py`)。这是写手唯一能用的材料:本命底子、年内各段及走势、转折点、候选窗口、各领域信号、分歧、技法执行表。
5. **写**:按顺序一节一节写(`src/lib/annual-report-generate.ts`,写手 `src/mastra/annual-report.ts`,没有工具、记忆和技能)。每写完一节就把快照存进 `annual-report-snapshot`,页面每 2.5 秒读一次,写好的那一节出现,下一节显示「正在写这一节……」。
6. **检查**:每一节过程序检查(`gateSection`):格式、确定性用语、死亡灾祸疾病诊断、正文里出现具体日期、回看写成「你已经经历了」、重复标题、段 / 窗口 / 行的 id 对不上。不过的那一节带着原因重写一次,再不过整份停下。
7. **结束**:写完结算 `annual.full`(`complete_usage`,事件键 `annual.full`),状态改为 `ready`、`schema_version = annual_report.v1`。中途失败按报告队列原来的规则重试;最终失败释放预留,不扣点(遮罩上「写到一半断掉不扣」)。
@@ -46,7 +46,7 @@
## 默认口径(待占星师确认)
以下都集中在一处,改一行即可:Python `scripts/annual_report_facts.py` 的 `CONFIG` / `HOUSE_DOMAINS`,前端 `src/lib/annual-report-sections.ts`(分段与写法)。
以下都集中在一处,改一行即可:Python `scripts/annual_service/report_facts.py` 的 `CONFIG` / `HOUSE_DOMAINS`,前端 `src/lib/annual-report-sections.ts`(分段与写法)。
1. **Narayana 骨架**:用产品时间轴现行的旧算法(`narayana_skeleton_profile = "legacy"`);Sanjay Rath 版只并列参考,不算第二份支持(`narayana_rath_counts_as_support = False`)。
2. **Tithi Pravesha**:保持生产器自己的地点规则(出生地、出生固定偏移),只作独立年度视角,不并入 Tajika 年,不进候选窗口(`tithi_pravesha_in_windows = False`)。