mirror of
https://github.com/agentscope-ai/ReMe.git
synced 2026-08-28 05:25:04 +00:00
Some checks failed
Tests ReMe / Unit Tests - py3.12 (push) Has been cancelled
Tests ReMe / Unit Tests - py3.13 (push) Has been cancelled
Windows Smoke / CLI smoke - py3.11 (push) Has been cancelled
Pre-commit / run (ubuntu-latest) (push) Has been cancelled
Tests ReMe / Unit Tests - py3.11 (push) Has been cancelled
* refactor: rebuild auto-fin and daily-paper cookbooks on structured-output agents Rework the auto-fin and daily-paper cookbooks to run on structured-output LLM agents instead of Claude Code agent wrappers, replace the SSH proxy with data-source mirrors, and rewrite the affected unit tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * refactor(auto_fin): unify JSON output serialization and writing - Extracted _write_output static method to serialize and write Pydantic models as compact JSON - Replaced inline JSON dump and write calls with _write_output usage across auto_fin steps - Added _report_path and _current_report for managing intra-day reports in AutoFinMergeStep - Updated auto_fin merge step to write output via new _write_output method - Enhanced news reading with caching in AutoFinHistoryStep - Refined returns calculation to handle events before close on non-trading days correctly feat(daily_paper): improve note path resolution and metadata handling - Introduced iter_note_metadata generator for safe Markdown frontmatter iteration - Added resolve_unique_note_path to avoid note filename conflicts on disk and in used titles - Updated analyze, collect, digest, and select steps to use centralized constants and helpers - Used utc_now_iso for consistent timestamping in metadata - Replaced direct frontmatter loads with iter_note_metadata in collect and analyze steps - Replaced hardcoded paper selection count with PAPER_COUNT constant in all relevant places - Added _MAX_SELECT_ATTEMPTS constant in select step for attempt management - Improved error messages for filename validation in daily paper title normalization feat(auto_fin): add multi-run cron schedules for intraday refinement - Defined three auto_fin cron jobs at 09:30, 11:30, and 18:00 Shanghai time for gradual report updates - Each intraday run adds evidence cumulatively instead of replacing prior output wholly - Updated daily_cookbook.yaml to register new cron schedules and remove legacy 12:00 cron refactor(auto_fin_data): clean ETF code handling and page limits - Replaced hardcoded DEFAULT_ETF_CODES with required non-empty config value "etf_codes" - Added constants for major news and fund page limits to control pagination - Improved ETF name extraction logic to handle missing fields consistently fix(auto_fin_merge): fix report retrieval and merging logic - Added support for getting current intra-day report in addition to previous day's report - Modified merge template to include prior and current report sections for better context - Adjusted report path handling to consistently use Path objects test(auto_fin): add coverage for returns calculation and report retrieval - Added test for returns when event occurs before close on non-trading day, checking next session entry - Added test for previous and current report retrieval feeding merge context with disk files - Extended test asserts for auto_fin cron schedule changes in config style(daily_paper): reorder and cleanup imports - Reorganized imports in _common.py for clarity and added missing collections.abc.Iterator import - Cleaned up commented and unused imports across daily_paper steps * feat: add configurable upstream mirror proxy * style: format auto-fin data step * fix: align cookbook mirrors and contracts --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
93 lines
4.1 KiB
Python
93 lines
4.1 KiB
Python
"""Generate and save the final Auto Fin recommendation."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import re
|
|
from datetime import date
|
|
from pathlib import Path
|
|
from types import SimpleNamespace
|
|
|
|
from ....components import R
|
|
from ....schema import AutoFinReportOutput
|
|
from ...file_io import refresh_day_index
|
|
from ._base import AutoFinStep, _write
|
|
|
|
|
|
@R.register("auto_fin_merge_step")
|
|
class AutoFinMergeStep(AutoFinStep):
|
|
"""Call the final tool-free Agent with all evidence already prepared."""
|
|
|
|
def _report_path(self, run_date: date) -> Path:
|
|
return self.workspace_path / str(self.config_value("daily_dir")) / str(run_date) / "auto_fin.md"
|
|
|
|
def _previous_report(self, run_date: date) -> str:
|
|
"""Return the most recent report from a *prior* day (yesterday's, typically)."""
|
|
daily = self.workspace_path / str(self.config_value("daily_dir"))
|
|
candidates = []
|
|
for path in daily.glob("*/auto_fin.md"):
|
|
try:
|
|
day = date.fromisoformat(path.parent.name)
|
|
except ValueError:
|
|
continue
|
|
if day < run_date:
|
|
candidates.append((day, path))
|
|
return max(candidates)[1].read_text(encoding="utf-8") if candidates else "无历史推荐。"
|
|
|
|
def _current_report(self, run_date: date) -> str:
|
|
"""Return today's existing report so intra-day reruns refine it, not replace it."""
|
|
path = self._report_path(run_date)
|
|
if path.is_file():
|
|
return path.read_text(encoding="utf-8")
|
|
return "今日暂无更早时段的推荐,本次为当日首次生成。"
|
|
|
|
@staticmethod
|
|
def _normalize(output: AutoFinReportOutput) -> AutoFinReportOutput:
|
|
title = re.sub(r"^#+\s*", "", output.title.strip()) or "Auto Fin ETF 结论"
|
|
description = output.description.strip() or "基于当前事件与相似历史表现的 ETF 观察。"
|
|
body = output.body.strip() or "## 结论\n\n暂无可用结论。"
|
|
if body.startswith("# "):
|
|
body = body.partition("\n")[2].lstrip() or "## 结论\n\n暂无可用结论。"
|
|
return AutoFinReportOutput(title=title, description=description, body=body)
|
|
|
|
async def execute(self):
|
|
assert self.context is not None
|
|
if self.context.get("auto_fin_skipped"):
|
|
self.context.response.answer = str(self.context.get("auto_fin_skip_reason") or "Auto Fin 已跳过。")
|
|
return self.context.response
|
|
run_date = date.fromisoformat(str(self._required("auto_fin_date")))
|
|
output, output_path = await self._reply(
|
|
"merge_user",
|
|
"auto_fin_merge",
|
|
AutoFinReportOutput,
|
|
decision_at=str(self._required("auto_fin_decision_at")),
|
|
etfs=json.dumps(
|
|
[
|
|
{"etf_code": code, "etf_name": name}
|
|
for code, name in dict(self._required("auto_fin_etf_names")).items()
|
|
],
|
|
ensure_ascii=False,
|
|
),
|
|
analyses=json.dumps(self._required("auto_fin_analyses"), ensure_ascii=False),
|
|
previous_report=self._previous_report(run_date),
|
|
current_report=self._current_report(run_date),
|
|
)
|
|
output = self._normalize(output)
|
|
self._write_output(output_path, output)
|
|
markdown = (
|
|
f"# {output.title}\n\n> {output.description}\n\n{output.body}\n\n"
|
|
"> 仅为事件研究和持有时间参考,不构成投资建议,不会执行交易。\n"
|
|
)
|
|
report = self._report_path(run_date)
|
|
_write(report, markdown)
|
|
await refresh_day_index(
|
|
SimpleNamespace(workspace_path=self.workspace_path),
|
|
str(run_date),
|
|
str(self.config_value("daily_dir")),
|
|
)
|
|
relative = report.relative_to(self.workspace_path).as_posix()
|
|
self.context["markdown_path"] = relative
|
|
self.context["auto_fin_digest_path"] = relative
|
|
self.context.response.answer = output.body
|
|
self.context.response.metadata.update({"markdown_path": relative, "digest_path": relative})
|
|
return self.context.response
|