mirror of
https://github.com/agentscope-ai/ReMe.git
synced 2026-09-30 01:52:29 +00:00
* refactor(components): extract shared component state into mixin - Introduce ComponentMixin class with shared state for components and steps - Move identity, config, and vault path functionality to ComponentMixin - Update BaseComponent to inherit from ComponentMixin - Update BaseStep to inherit from ComponentMixin - Consolidate vault path helper methods in ComponentMixin - Remove duplicate vault path implementations from BaseComponent and BaseStep - Add ComponentMixin to components module exports * refactor(file_io): implement path locks cache eviction mechanism - Add _PATH_LOCKS_MAX constant set to 1024 for cache size limit - Implement cache eviction logic when locks exceed maximum capacity - Remove half of unlocked entries when cache limit is reached - Use list comprehension to identify unlocked locks for removal - Maintain existing path normalization and locking behavior fix(edit): correct method call from public to private fail method - Change self.fail to self._fail for internal error handling - Maintain consistent private method usage within class fix(mcp_client): change pop to get for optional command and args - Replace kwargs.pop with kwargs.get to avoid removing keys - Preserve original kwargs dictionary contents - Maintain default empty string and list values feat(reme): add client backend validation with error raising - Check if client_cls is None before instantiation - Raise ValueError with descriptive message for unknown backends - Provide clear error feedback for invalid backend configurations * fix(components): move directory creation to start method - Moved component_metadata_path.mkdir call from __init__ to _start in base_keyword_index - Moved component_metadata_path.mkdir call from __init__ to _start in local_file_graph - Moved component_metadata_path.mkdir call from __init__ to _start in local_file_store - Ensures directory creation happens after component initialization - Prevents potential issues with path creation during object construction * fix(steps): replace assertions with runtime errors for app_context validation - Replace assert statements with explicit RuntimeError exceptions when app_context is None - Add descriptive error messages for better debugging when resolving components - Replace assert in resolve_component method with proper exception handling - Replace assert in get_file_parser method with proper exception handling - Maintain same functionality while improving error reporting clarity * refactor(file_io): split file IO utilities into modular components - Move daily note helpers to separate _daily_index module - Extract path validation and resolution to new _path module - Remove unused code and imports from _file_io module - Update import statements across affected modules - Introduce WikilinkHandler utility for link parsing - Replace regex-based link extraction with WikilinkHandler - Add integration JSONL files to gitignore - Consolidate file locking mechanism in _file_io module * style(formatter): fix spacing issues in file IO and chunked file parser - Fixed whitespace around colon in slice notation in file_io.py - Corrected spacing around colon in slice notation in chunked_file_parser.py - Applied consistent formatting for array slicing operations - Improved code readability by standardizing space placement in ranges * refactor(steps): replace property-based component resolution with Ref descriptor - Introduce Ref descriptor class for lazy component dependency resolution - Replace _resolve method and individual properties with Ref descriptors - Add as_llm, as_llm_formatter, as_token_counter, file_store, and embedding Ref attributes - Remove legacy property methods and resolve logic from BaseStep - Add cache clearing mechanism for Ref values during step calls - Update UpdateCatalogStep to use Ref instead of property-based resolution
107 lines
4.5 KiB
Python
107 lines
4.5 KiB
Python
"""``daily_create`` — provision a session note under a daily folder.
|
|
|
|
When ``session_id`` is provided: ``daily/<date>/<session_id>.md``
|
|
When ``session_id`` is empty: ``daily/<date>.md`` (the day-level file)
|
|
|
|
Validates the session_id (when non-empty), mkdirs the day folder,
|
|
writes an empty-body note with frontmatter ``{name, description}``
|
|
if (and only if) the file does not already exist, refreshes the day
|
|
index (only when session_id is non-empty), and returns the
|
|
vault-relative path.
|
|
|
|
Idempotent: when the note already exists this is a no-op write.
|
|
The caller fills the body via ``file_write`` / ``file_edit`` /
|
|
``file_append`` (or a native editor); ``daily_create`` deliberately
|
|
does not accept a body.
|
|
|
|
Inputs:
|
|
session_id (optional) — the note's session identifier (also the file stem);
|
|
empty string → day-level file
|
|
date (optional, ``YYYY-MM-DD``; empty = today)
|
|
|
|
Outputs:
|
|
answer = one-line human-readable status
|
|
metadata = {date, session_id, path, created, index?}
|
|
"""
|
|
|
|
from datetime import date as _date
|
|
from pathlib import Path
|
|
|
|
import frontmatter
|
|
|
|
from ._daily_index import refresh_day_index, validate_session_id
|
|
from ._file_io import write_file_safe
|
|
from ..base_step import BaseStep
|
|
from ...components import R
|
|
|
|
|
|
@R.register("daily_create_step")
|
|
class DailyCreateStep(BaseStep):
|
|
"""Provision a daily note (idempotent); refresh day index when applicable."""
|
|
|
|
def _fail(self, message: str, **meta) -> None:
|
|
"""Mark response failed; copy ``meta`` into ``response.metadata``."""
|
|
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 _collect_params(self) -> tuple[str, str, str]:
|
|
"""Read ``session_id`` + ``date`` from context; default ``date`` today, ``daily_dir`` from app config."""
|
|
assert self.context is not None
|
|
session_id = self.context.get("session_id", "")
|
|
day = self.context.get("date", "") or _date.today().strftime("%Y-%m-%d")
|
|
daily_dir = self.app_context.app_config.daily_dir if self.app_context is not None else "daily"
|
|
return session_id, day, daily_dir
|
|
|
|
@staticmethod
|
|
def _empty_note_text(name: str) -> str:
|
|
"""Serialize an empty-body markdown note with frontmatter ``{name, description}``; trailing newline."""
|
|
text = frontmatter.dumps(frontmatter.Post("", name=name, description=""))
|
|
return text if text.endswith("\n") else text + "\n"
|
|
|
|
async def _create_if_missing(self, path_abs: Path, name: str) -> bool:
|
|
"""Write the empty note only when the file is absent. Returns ``True`` iff a new file was created."""
|
|
if path_abs.is_file():
|
|
return False
|
|
await write_file_safe(path_abs, self._empty_note_text(name), encoding="utf-8")
|
|
return True
|
|
|
|
def _set_success(self, payload: dict, created: bool) -> None:
|
|
"""Stamp the response with success + human-readable answer + metadata payload."""
|
|
assert self.context is not None
|
|
self.context.response.success = True
|
|
self.context.response.answer = f"{'Created' if created else 'Reused existing'} daily note {payload['path']}"
|
|
self.context.response.metadata.update(payload)
|
|
|
|
async def execute(self):
|
|
"""Provision the note file, optionally refresh the day index, stamp the response."""
|
|
assert self.context is not None
|
|
session_id, day, daily_dir = self._collect_params()
|
|
|
|
if session_id:
|
|
err = validate_session_id(session_id)
|
|
if err:
|
|
self._fail(err)
|
|
return None
|
|
path_rel = f"{daily_dir}/{day}/{session_id}.md"
|
|
name = session_id
|
|
else:
|
|
path_rel = f"{daily_dir}/{day}.md"
|
|
name = day
|
|
|
|
path_abs = (self.vault_path / path_rel).resolve()
|
|
try:
|
|
created = await self._create_if_missing(path_abs, name)
|
|
except Exception as e: # pylint: disable=broad-except
|
|
self._fail(f"create failed: {e}", date=day, session_id=session_id, path=path_rel)
|
|
return None
|
|
|
|
payload: dict = {"date": day, "session_id": session_id, "path": path_rel, "created": created}
|
|
if session_id:
|
|
payload["index"] = await refresh_day_index(self.file_store, day, daily_dir)
|
|
|
|
self._set_success(payload, created)
|
|
self.logger.info(f"[{self.name}] {'created' if created else 'reused'} path={path_rel}")
|
|
return self.context.response
|