chore(release): prepare ReMe 0.4.1.9

This commit is contained in:
jinli.yl 2026-08-27 21:17:46 +08:00
parent 2dd2255760
commit 34cc579b97
19 changed files with 72 additions and 80 deletions

View file

@ -73,8 +73,8 @@ jobs:
if len(requirements) != 1:
raise SystemExit(f"Expected one reme-ai dependency, found {requirements!r}")
reme_requirement = Requirement(requirements[0])
if reme_requirement.name != "reme-ai" or set(reme_requirement.extras) != {"core"}:
raise SystemExit(f"Expected a reme-ai[core] dependency, found {requirements[0]!r}")
if reme_requirement.name != "reme-ai" or reme_requirement.extras:
raise SystemExit(f"Expected a base reme-ai dependency, found {requirements[0]!r}")
with Path(os.environ["GITHUB_OUTPUT"]).open("a", encoding="utf-8") as output:
print(f"reme_requirement={reme_requirement}", file=output)
print(f"Publishing {project['name']} {actual}")
@ -88,7 +88,7 @@ jobs:
REME_REQUIREMENT: ${{ steps.package.outputs.reme_requirement }}
run: |
python -m pip download --no-deps \
--dest "${RUNNER_TEMP}/reme-auto-fin-core" \
--dest "${RUNNER_TEMP}/reme-auto-fin-base" \
"${REME_REQUIREMENT}"
- name: Build and check distributions
@ -122,9 +122,7 @@ jobs:
}
assert set(manifest.application_defaults["jobs"]) == {
"auto_fin",
"auto_fin_0930_cron",
"auto_fin_1130_cron",
"auto_fin_1800_cron",
"auto_fin_cron",
}
PY

View file

@ -70,8 +70,8 @@ jobs:
raise SystemExit(f"Package version is {actual}, but workflow input is {expected}")
requirements = [Requirement(value) for value in project["dependencies"]]
reme_requirements = [requirement for requirement in requirements if requirement.name == "reme-ai"]
if len(reme_requirements) != 1 or set(reme_requirements[0].extras) != {"core"}:
raise SystemExit(f"Expected one reme-ai[core] dependency, found {reme_requirements!r}")
if len(reme_requirements) != 1 or reme_requirements[0].extras:
raise SystemExit(f"Expected one base reme-ai dependency, found {reme_requirements!r}")
if sum(requirement.name == "pypdf" for requirement in requirements) != 1:
raise SystemExit("Expected exactly one pypdf dependency")
with Path(os.environ["GITHUB_OUTPUT"]).open("a", encoding="utf-8") as output:
@ -87,7 +87,7 @@ jobs:
REME_REQUIREMENT: ${{ steps.package.outputs.reme_requirement }}
run: |
python -m pip download --no-deps \
--dest "${RUNNER_TEMP}/reme-daily-paper-core" \
--dest "${RUNNER_TEMP}/reme-daily-paper-base" \
"${REME_REQUIREMENT}"
- name: Build and check distributions

View file

@ -17,7 +17,7 @@ through `plugins=["auto-fin"]`.
### 1. Install ReMe and Auto Fin
```bash
python -m pip install "reme-ai[core]>=0.4.1.8"
python -m pip install "reme-ai[core]>=0.4.1.9"
reme plugins install reme-auto-fin
```
@ -74,7 +74,7 @@ normalize and deduplicate in RuntimeContext
topic Agent selects real news IDs in bounded batches
research Agent uses memory_search + read on historical memory
research Agent uses search + read on historical memory
validate historical wikilinks in code
@ -89,8 +89,8 @@ records outside the window are discarded.
IDs and deduplicates repeated IDs, then preserves the source-news order. If nothing is relevant, the job succeeds as a
skip without writing or sending a report.
`auto_fin_merge_step` receives only selected current news. It exposes `memory_search` and `read`, instructs the Agent to
search no later than yesterday, and keeps current CLS IDs, times, and titles as plain evidence. The prompt limits
`auto_fin_merge_step` receives only selected current news. It exposes `search` and `read`, and keeps current CLS IDs,
times, and titles as plain evidence. The prompt limits
wikilinks to historical Markdown actually used by the Agent; the code-level boundary independently keeps only existing,
workspace-relative Markdown targets. Missing, absolute, escaping, backslash, and self-referential targets are degraded
to their readable aliases.

View file

@ -14,7 +14,7 @@ distribution单个 `reme.plugins` entry point 暴露 `plugin.yaml`,其中
### 1. 安装 ReMe 和 Auto Fin
```bash
python -m pip install "reme-ai[core]>=0.4.1.8"
python -m pip install "reme-ai[core]>=0.4.1.9"
reme plugins install reme-auto-fin
```
@ -69,7 +69,7 @@ reme start config=daily_cookbook plugins='["auto-fin"]'
Topic Agent 分批选择真实 news_id
Research Agent 使用 memory_search + read 检索历史记忆
Research Agent 使用 search + read 检索历史记忆
代码校验历史 wikilink
@ -82,8 +82,8 @@ daily/YYYY-MM-DD/auto_fin.md
`auto_fin_topic_step` 分批接收当前新闻,只返回相关的 `news_id`。代码会忽略未知 ID、去除重复 ID并保持源新闻顺序。如果没有相关新闻Job
会成功跳过,不写报告也不发送通知。
`auto_fin_merge_step` 只接收筛选后的当前新闻,并向 Agent 开放 `memory_search` 和 `read`历史检索截止到昨天;当前新闻以
CLS ID、时间和标题作为普通证据。Prompt 要求 Agent 只链接实际使用过的历史 Markdown代码边界则独立保证只保留真实存在、相对
`auto_fin_merge_step` 只接收筛选后的当前新闻,并向 Agent 开放 `search` 和 `read`。当前新闻以 CLS ID、时间和标题作为普通证据。
Prompt 要求 Agent 只链接实际使用过的历史 Markdown代码边界则独立保证只保留真实存在、相对
workspace 的 Markdown 目标。不存在、绝对路径、越界、带反斜杠和自引用的目标都会降级为可读 alias。
同日重跑会参考当天已有报告并覆盖为修订结果。最终写入使用原子替换并刷新当天索引;流程不会写入 JSONL、中间 Markdown 或 Agent

View file

@ -1,13 +1,13 @@
[project]
name = "reme-auto-fin"
version = "0.1.1"
version = "0.1.2"
description = "Auto Fin example plugin for ReMe."
readme = "README.md"
license = "Apache-2.0"
license-files = ["LICENSE"]
requires-python = ">=3.11"
dependencies = [
"reme-ai[core]>=0.4.1.8",
"reme-ai",
]
[project.entry-points."reme.plugins"]

View file

@ -4,7 +4,7 @@ from __future__ import annotations
import json
import re
from datetime import date, timedelta
from datetime import date
from pathlib import Path
from types import SimpleNamespace
@ -102,6 +102,7 @@ class AutoFinMergeStep(AutoFinStep):
)
async def execute(self):
"""Research the selected news and persist the validated report."""
assert self.context is not None
if self.context.get("auto_fin_skipped"):
return self.context.response
@ -112,7 +113,6 @@ class AutoFinMergeStep(AutoFinStep):
job_tools=list(self.kwargs.get("job_tools") or []),
decision_at=str(self._required("auto_fin_decision_at")),
window_start=str(self._required("auto_fin_window_start")),
historical_end=(run_date - timedelta(days=1)).isoformat(),
topics=json.dumps(self._required("auto_fin_topics"), ensure_ascii=False),
news=json.dumps(self._required("auto_fin_selected_news"), ensure_ascii=False),
current_report=self._current_report(run_date),

View file

@ -1,5 +1,5 @@
merge_user: |
你是主题新闻研究 Agent。当前新闻已经按 topics 做过语义筛选。你可以使用 `memory_search` 搜索历史记忆,
你是主题新闻研究 Agent。当前新闻已经按 topics 做过语义筛选。你可以使用 `search` 搜索历史记忆,
并使用 `read` 阅读可能相关的完整 Markdown。不得使用外部搜索不得虚构行情、收益、价格或未提供的数据。
研究窗口:{window_start} 至 {decision_at}
@ -8,7 +8,7 @@ merge_user: |
今天早些时段的报告(如有,请保留仍成立的判断,只修订变化部分):
{current_report}
先围绕 topics 和当前重要事件多次调用 `memory_search`,并将 end_date 设为 {historical_end},避免召回今天的旧报告
先围绕 topics 和当前重要事件多次调用 `search` 检索历史记忆
只对明显相关的结果调用 `read`。说明历史事件与当前事件的相同点、关键差异,以及旧判断是否仍适用。
给出值得回顾的新闻、应继续观察的信息,以及哪些条件会强化或推翻判断,但不要给出投资建议。

View file

@ -42,19 +42,9 @@ application_defaults:
- backend: auto_fin_data_step
- backend: auto_fin_topic_step
- backend: auto_fin_merge_step
job_tools: [memory_search, read]
job_tools: [search, read]
auto_fin_0930_cron:
backend: cron
cron: "30 9 * * *"
steps: *auto_fin_steps
auto_fin_1130_cron:
backend: cron
cron: "30 11 * * *"
steps: *auto_fin_steps
auto_fin_1800_cron:
auto_fin_cron:
backend: cron
cron: "0 18 * * *"
steps: *auto_fin_steps

View file

@ -195,14 +195,14 @@ async def test_merge_writes_only_final_report_and_validates_historical_links(tmp
response = await AutoFinMergeStep(
app_context=app_context,
agent_wrapper=agent,
job_tools=["memory_search", "read"],
job_tools=["search", "read"],
)(context)
prompt, kwargs = agent.calls[0]
assert "end_date 设为 2026-08-09" in prompt
assert "调用 `memory_search`" in prompt
assert "end_date" not in prompt
assert "调用 `search`" in prompt
assert "调用 `read`" in prompt
assert kwargs == {"output_schema": AutoFinReportOutput, "job_tools": ["memory_search", "read"]}
assert kwargs == {"output_schema": AutoFinReportOutput, "job_tools": ["search", "read"]}
report = (tmp_path / "daily" / "2026-08-10" / "auto_fin.md").read_text(encoding="utf-8")
assert "[[daily/2026-08-01/auto_fin.md|历史黄金观察]]" in report
assert "](daily/2026-08-01/auto_fin.md)" not in report
@ -254,14 +254,17 @@ def test_plugin_config_has_default_topics_and_no_intermediate_index_step():
"auto_fin_topic_step",
"auto_fin_merge_step",
]
assert job["steps"][2]["job_tools"] == ["memory_search", "read"]
for name, schedule in {
"auto_fin_0930_cron": "30 9 * * *",
"auto_fin_1130_cron": "30 11 * * *",
"auto_fin_1800_cron": "0 18 * * *",
}.items():
assert jobs[name]["cron"] == schedule
assert jobs[name]["steps"] == job["steps"]
assert job["steps"][2]["job_tools"] == ["search", "read"]
assert jobs["auto_fin_cron"]["cron"] == "0 18 * * *"
assert jobs["auto_fin_cron"]["steps"] == job["steps"]
assert (
not {
"auto_fin_0930_cron",
"auto_fin_1130_cron",
"auto_fin_1800_cron",
}
& jobs.keys()
)
def test_agent_schemas_are_small_and_required():

View file

@ -13,7 +13,7 @@ their Job configuration under `application_defaults`. Enable the installed plugi
### 1. Install ReMe and Daily Paper
```bash
python -m pip install "reme-ai[core]>=0.4.1.8"
python -m pip install "reme-ai[core]>=0.4.1.9"
reme plugins install reme-daily-paper
```
@ -62,7 +62,7 @@ rank with RRF and let an Agent select three papers
download and parse arXiv PDFs, then write three Chinese analyses
use memory_search + read to connect prior memory and generate a brief
use search + read to connect prior memory and generate a brief
refresh the daily index and optionally send the brief to DingTalk
```
@ -80,7 +80,7 @@ the configured page, character, and file-size limits. It writes the three Chines
PDFs and files without a text layer fail explicitly.
`daily_paper_digest_step` treats those three analyses as the factual source and receives only the read-only
`memory_search` and `read` tools for linking earlier memory. Code validates historical wikilinks, appends links to all
`search` and `read` tools for linking earlier memory. Code validates historical wikilinks, appends links to all
three source notes, and rebuilds the daily index. The optional `dingtalk_markdown_send_step` sends the final brief when
conversation IDs are configured and otherwise skips without side effects.

View file

@ -11,7 +11,7 @@ Step backend并在 `application_defaults` 下提供 Job 配置;通过 `plug
### 1. 安装 ReMe 和每日论文插件
```bash
python -m pip install "reme-ai[core]>=0.4.1.8"
python -m pip install "reme-ai[core]>=0.4.1.9"
reme plugins install reme-daily-paper
```
@ -58,7 +58,7 @@ RRF 排序后由 Agent 精选三篇
下载并解析 arXiv PDF生成三篇中文解读
使用 memory_search + read 关联历史记忆并生成简报
使用 search + read 关联历史记忆并生成简报
写入当日索引,并按需发送到钉钉
```
@ -72,7 +72,7 @@ RRF 排序后由 Agent 精选三篇
`daily_paper_analyze_step` 下载 PDF 到 `resource/papers/`,复用已有的有效文件,并在页数、字符数和文件大小限制内提取
文本。三篇中文解读按精选顺序写入当天目录;扫描版或没有文本层的 PDF 会明确失败。
`daily_paper_digest_step` 以本次生成的三篇解读为事实来源,只开放只读的 `memory_search` 和 `read` 来关联较早记忆。
`daily_paper_digest_step` 以本次生成的三篇解读为事实来源,只开放只读的 `search` 和 `read` 来关联较早记忆。
代码会校验历史 wikilink、追加三篇源笔记链接并重建当日索引。可选的 `dingtalk_markdown_send_step` 在配置群会话后
发送最终简报;未配置时无副作用跳过。

View file

@ -1,6 +1,6 @@
[project]
name = "reme-daily-paper"
version = "0.1.1"
version = "0.1.2"
description = "Daily Paper research and reading-note plugin for ReMe."
readme = "README.md"
license = "Apache-2.0"
@ -8,7 +8,7 @@ license-files = ["LICENSE"]
requires-python = ">=3.11"
dependencies = [
"pypdf>=5.0.0",
"reme-ai[core]>=0.4.1.8",
"reme-ai",
]
[project.entry-points."reme.plugins"]

View file

@ -64,6 +64,7 @@ class DailyPaperDigestStep(DailyPaperStep):
return _WIKILINK_RE.sub(replace, body)
async def execute(self):
"""Generate and persist the final brief from analyzed papers."""
assert self.context is not None
if self._skip():
self.logger.info(f"[{self.name}] skip existing digest")
@ -79,13 +80,11 @@ class DailyPaperDigestStep(DailyPaperStep):
documents = [{"title": item.title, "desc": item.desc, "body": item.body} for item in analyses]
wikilinks = [f"[[{item.note_path}]]" for item in analyses]
previous_day = (dt.date.fromisoformat(self._run_day()) - dt.timedelta(days=1)).isoformat()
self.logger.info(f"[{self.name}] agent start notes={len(analyses)}")
result = await self.agent_wrapper.reply(
self.prompt_format(
"digest_user",
documents=json.dumps(documents, ensure_ascii=False, indent=2),
previous_day=previous_day,
),
output_schema=DailyPaperMarkdownOutput,
job_tools=list(self.kwargs.get("job_tools") or []),

View file

@ -4,8 +4,8 @@ digest_user: |
内容只能依据输入文档,不得补充文档中没有提供的事实。
保留技术准确性,同时解释三篇论文为什么值得关注,以及它们之间有什么联系。
在写作前,先调用 `memory_search` 检索以前的文章:围绕三篇论文的核心问题、方法、关键词和同义表达组织查询,
使用 end_date={previous_day}、limit=20。主题跨度较大时可以多次检索。只把 `daily/` 下日期早于今天、
在写作前,先调用 `search` 检索以前的文章:围绕三篇论文的核心问题、方法、关键词和同义表达组织查询,
使用 limit=20。主题跨度较大时可以多次检索。只把 `daily/` 下日期早于今天、
且与本期内容确实相似或互补的 Markdown 文章作为候选;必要时调用 `read` 核验全文,不要仅凭标题判断。
将确认相关的旧文章以 Wikilink 自然织入正文,并用句子说明关联(延续、对比、补充或方法相似);
链接必须采用带 `.md` 的完整 workspace-relative 路径,例如

View file

@ -53,7 +53,7 @@ application_defaults:
- backend: daily_paper_select_step
- backend: daily_paper_analyze_step
- backend: daily_paper_digest_step
job_tools: [memory_search, read]
job_tools: [search, read]
- backend: dingtalk_markdown_send_step
input_mapping:
daily_paper_digest_path: markdown_path

View file

@ -846,7 +846,7 @@ async def test_pipeline_filters_strict_yesterday_and_writes_outputs(
await DailyPaperDigestStep(
app_context=app_context,
agent_wrapper=cc_wrapper,
job_tools=["memory_search", "read"],
job_tools=["search", "read"],
)(context)
assert _FakeHfClient.requested_daily == ["2026-07-20"]
@ -886,7 +886,7 @@ async def test_pipeline_filters_strict_yesterday_and_writes_outputs(
assert all(call["kwargs"] == {"output_schema": DailyPaperMarkdownOutput} for call in cc_wrapper.calls[1:-1])
assert cc_wrapper.calls[-1]["kwargs"] == {
"output_schema": DailyPaperMarkdownOutput,
"job_tools": ["memory_search", "read"],
"job_tools": ["search", "read"],
}
assert [call["kwargs"]["output_schema"] for call in cc_wrapper.calls] == [
PaperPickList,
@ -906,8 +906,8 @@ async def test_pipeline_filters_strict_yesterday_and_writes_outputs(
assert "调用 Read" not in digest_prompt
assert "daily/2026-07-21" not in digest_prompt
assert "长期记忆" not in digest_prompt
assert "先调用 `memory_search` 检索以前的文章" in digest_prompt
assert "end_date=2026-07-20" in digest_prompt
assert "先调用 `search` 检索以前的文章" in digest_prompt
assert "end_date" not in digest_prompt
assert "Wikilink" in digest_prompt
rerun = RuntimeContext(date="2026-07-21")

View file

@ -42,7 +42,7 @@ dependencies = [
[project.optional-dependencies]
as = [
"agentscope[model-ollama]==2.0.6",
"agentscope[model-ollama]==2.0.7",
]
web = [
"reme_studio",
@ -63,9 +63,9 @@ core = [
"reme_studio",
]
qwenpaw = [
"reme-ai[core]",
"reme-auto-fin>=0.1.1",
"reme-daily-paper>=0.1.1",
"reme-ai",
"reme-auto-fin>=0.1.2",
"reme-daily-paper>=0.1.2",
]
dev = [
"packaging>=24.2",

View file

@ -1,6 +1,6 @@
"""ReMe CLI package."""
__version__ = "0.4.1.8"
__version__ = "0.4.1.9"
from . import config
from . import constants

View file

@ -41,17 +41,17 @@ def test_studio_packages_have_independent_identity() -> None:
assert studio_config["project"]["name"] == "reme_studio"
assert npm_config["name"] == "@agentscope-ai/reme_studio"
assert studio_config["project"]["version"] == npm_config["version"]
assert main_config["project"]["optional-dependencies"]["as"] == ["agentscope[model-ollama]==2.0.6"]
assert main_config["project"]["optional-dependencies"]["as"] == ["agentscope[model-ollama]==2.0.7"]
assert main_config["project"]["optional-dependencies"]["web"] == ["reme_studio"]
assert main_config["project"]["optional-dependencies"]["core"].count("reme-ai[as]") == 1
assert main_config["project"]["optional-dependencies"]["core"].count("reme_studio") == 1
assert main_config["project"]["optional-dependencies"]["qwenpaw"] == [
"reme-ai[core]",
"reme-auto-fin>=0.1.1",
"reme-daily-paper>=0.1.1",
"reme-ai",
"reme-auto-fin>=0.1.2",
"reme-daily-paper>=0.1.2",
]
assert auto_fin_config["project"]["version"] == "0.1.1"
assert daily_paper_config["project"]["version"] == "0.1.1"
assert auto_fin_config["project"]["version"] == "0.1.2"
assert daily_paper_config["project"]["version"] == "0.1.2"
assert main_config["tool"]["setuptools"]["packages"]["find"]["include"] == ["reme", "reme.*"]
assert "reme_studio*" in main_config["tool"]["setuptools"]["packages"]["find"]["exclude"]
@ -153,14 +153,15 @@ def test_auto_fin_license_matches_repository() -> None:
).read_text(encoding="utf-8")
def test_auto_fin_requires_reme_core() -> None:
"""Install the optional runtime packages needed while loading Auto Fin's entry points."""
def test_auto_fin_requires_reme_base() -> None:
"""Keep the plugin dependency limited to ReMe's public base package."""
config = tomllib.loads((REPOSITORY / "plugins" / "auto-fin" / "pyproject.toml").read_text(encoding="utf-8"))
requirements = [Requirement(value) for value in config["project"]["dependencies"]]
reme_requirements = [requirement for requirement in requirements if requirement.name == "reme-ai"]
assert len(reme_requirements) == 1
assert set(reme_requirements[0].extras) == {"core"}
assert not reme_requirements[0].extras
assert not reme_requirements[0].specifier
def test_daily_paper_license_matches_repository() -> None:
@ -171,12 +172,13 @@ def test_daily_paper_license_matches_repository() -> None:
def test_daily_paper_declares_runtime_dependencies() -> None:
"""Keep Daily Paper's ReMe feature set and PDF parser explicit in its own distribution."""
"""Keep Daily Paper's minimal ReMe and PDF dependencies explicit."""
config = tomllib.loads((REPOSITORY / "plugins" / "daily_paper" / "pyproject.toml").read_text(encoding="utf-8"))
requirements = [Requirement(value) for value in config["project"]["dependencies"]]
by_name = {requirement.name: requirement for requirement in requirements}
assert set(by_name["reme-ai"].extras) == {"core"}
assert not by_name["reme-ai"].extras
assert not by_name["reme-ai"].specifier
assert "pypdf" in by_name