mirror of
https://github.com/agentscope-ai/ReMe.git
synced 2026-10-03 02:24:31 +00:00
* refactor: derive dialog paths from session directory * fix: normalize configured session paths * fix: align dialog watch paths with writers * fix: reject absolute session directories
120 lines
4.7 KiB
Python
120 lines
4.7 KiB
Python
"""Write a daily markdown note by dispatching the generic ``write`` step."""
|
|
|
|
from ._daily_index import parse_daily_date, refresh_day_index, validate_session_id
|
|
from ._path import validate_filename_component
|
|
from ..base_step import BaseStep
|
|
from ..index import normalize_posix_path
|
|
from ...components import R
|
|
from ...steps.evolve import now
|
|
|
|
_RESERVED_METADATA_KEYS = {"name", "description", "session_id", "source_conversation"}
|
|
|
|
|
|
@R.register("daily_write_step")
|
|
class DailyWriteStep(BaseStep):
|
|
"""Write ``daily/<date>/<name>.md`` with conversation frontmatter."""
|
|
|
|
def _fail(self, message: str, **meta) -> None:
|
|
assert self.context is not None
|
|
self.context.response.success = False
|
|
self.context.response.answer = f"Error: {message}"
|
|
if meta:
|
|
self.context.response.metadata.update(meta)
|
|
|
|
def _session_dir(self) -> str:
|
|
return normalize_posix_path(str(self.config_value("session_dir")))
|
|
|
|
def _session_link(self, session_id: str) -> str:
|
|
path = normalize_posix_path(f"{self._session_dir()}/dialog/{session_id}.jsonl")
|
|
return f"[[{path}]]"
|
|
|
|
def _collect_required(self) -> tuple[str, str, str, str] | None:
|
|
assert self.context is not None
|
|
missing = [key for key in ("name", "description", "session_id", "content") if self.context.get(key) is None]
|
|
if missing:
|
|
self._fail(f"missing required parameter(s): {', '.join(missing)}")
|
|
return None
|
|
|
|
name = str(self.context.get("name")).strip()
|
|
description = str(self.context.get("description")).strip()
|
|
session_id = str(self.context.get("session_id")).strip()
|
|
content = str(self.context.get("content"))
|
|
|
|
if err := validate_filename_component(name, kind="name"):
|
|
self._fail(err)
|
|
return None
|
|
if err := validate_session_id(session_id):
|
|
self._fail(err)
|
|
return None
|
|
return name, description, session_id, content
|
|
|
|
def _metadata(self, session_id: str) -> dict:
|
|
assert self.context is not None
|
|
metadata_raw = self.context.get("metadata")
|
|
metadata = {}
|
|
if isinstance(metadata_raw, dict):
|
|
metadata = {
|
|
str(k): v for k, v in metadata_raw.items() if k not in _RESERVED_METADATA_KEYS and v is not None
|
|
}
|
|
metadata.update(
|
|
{
|
|
"session_id": session_id,
|
|
"source_conversation": self._session_link(session_id),
|
|
},
|
|
)
|
|
# ``write`` takes name/description as explicit parameters, not metadata.
|
|
return metadata
|
|
|
|
async def execute(self):
|
|
assert self.context is not None
|
|
collected = self._collect_required()
|
|
if collected is None:
|
|
return None
|
|
|
|
name, description, session_id, content = collected
|
|
tz = self.app_context.app_config.timezone if self.app_context is not None else None
|
|
raw_date = self.context.get("date", "")
|
|
day = parse_daily_date(raw_date) if raw_date else now(tz).strftime("%Y-%m-%d")
|
|
if raw_date and day is None:
|
|
self._fail("date must be YYYY-MM-DD", date=raw_date)
|
|
return self.context.response
|
|
daily_dir = self.config_value("daily_dir")
|
|
path = f"{daily_dir}/{day}/{name}.md"
|
|
source_conversation = self._session_link(session_id)
|
|
|
|
write_responses = await self.dispatch_steps(
|
|
[{"backend": "write_step"}],
|
|
path=path,
|
|
name=name,
|
|
description=description,
|
|
content=content,
|
|
metadata=self._metadata(session_id),
|
|
)
|
|
write_response = write_responses[0]
|
|
write_success = write_response.success
|
|
write_answer = write_response.answer
|
|
if not write_success:
|
|
self.context.response.success = False
|
|
self.context.response.answer = f"write failed: {write_answer}"
|
|
self.context.response.metadata.update(
|
|
{"date": day, "path": path, "session_id": session_id, "source_conversation": source_conversation},
|
|
)
|
|
return self.context.response
|
|
|
|
index_payload = await refresh_day_index(self.file_store, day, daily_dir)
|
|
|
|
self.context.response.success = True
|
|
self.context.response.answer = write_answer
|
|
self.context.response.metadata.update(
|
|
{
|
|
"date": day,
|
|
"path": path,
|
|
"name": name,
|
|
"description": description,
|
|
"session_id": session_id,
|
|
"source_conversation": source_conversation,
|
|
"index": index_payload,
|
|
},
|
|
)
|
|
self.logger.info(f"[{self.name}] wrote daily note path={path} session_id={session_id!r}")
|
|
return self.context.response
|