mirror of
https://github.com/HKUDS/OpenSpace.git
synced 2026-08-28 05:15:00 +00:00
180 lines
6.4 KiB
Python
180 lines
6.4 KiB
Python
"""Backend implementation of the ``/memory`` slash command.
|
|
|
|
OpenSpace's command is a local React dialog. OpenSpace's core command surface is
|
|
headless, so this module exposes the same state transitions as text commands:
|
|
clear caches, preload/list memory files, create the selected file, and launch
|
|
an external editor when one is configured.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
from openspace.tools.memory_tools import (
|
|
format_memory_targets,
|
|
get_relative_memory_path,
|
|
list_memory_targets,
|
|
open_memory_target,
|
|
resolve_memory_target,
|
|
)
|
|
|
|
|
|
@dataclass(slots=True)
|
|
class MemoryCommandResult:
|
|
message: str
|
|
display: str = "system"
|
|
|
|
|
|
def build_memory_selector_payload(
|
|
*,
|
|
openspace: Any | None = None,
|
|
cwd: str | Path | None = None,
|
|
) -> dict[str, Any]:
|
|
"""Build the TUI selector payload for ``/memory``.
|
|
|
|
This mirrors OpenSpace's ``MemoryFileSelector`` data model, but the actual target
|
|
discovery stays in Python so the TUI does not duplicate OpenSpace memory
|
|
path and auto-memory rules.
|
|
"""
|
|
|
|
project_path = Path(cwd or _resolve_project_path(openspace)).expanduser().resolve()
|
|
targets = list_memory_targets(cwd=project_path)
|
|
return {
|
|
"cwd": str(project_path),
|
|
"targets": [
|
|
{
|
|
"label": target.label,
|
|
"path": str(target.path),
|
|
"kind": target.kind,
|
|
"description": target.description,
|
|
"exists": target.exists,
|
|
"is_folder": target.is_folder,
|
|
"display_path": get_relative_memory_path(target.path, cwd=project_path),
|
|
}
|
|
for target in targets
|
|
],
|
|
}
|
|
|
|
|
|
async def execute_memory_command(
|
|
args: list[str],
|
|
*,
|
|
openspace: Any | None = None,
|
|
cwd: str | Path | None = None,
|
|
launch_editor: bool = True,
|
|
) -> MemoryCommandResult:
|
|
"""Execute ``/memory``.
|
|
|
|
Supported forms:
|
|
- ``/memory`` or ``/memory list``: list editable memory files/folders.
|
|
- ``/memory edit [user|project|local|auto|folder|<path>]``: create/open.
|
|
- ``/memory read [filename]``: print an auto-memory topic or ``MEMORY.md``.
|
|
- ``/memory logs [all]``: print daily-log entries.
|
|
"""
|
|
|
|
project_path = Path(cwd or _resolve_project_path(openspace)).expanduser().resolve()
|
|
if not args or args[0].lower() in {"list", "ls"}:
|
|
targets = list_memory_targets(cwd=project_path)
|
|
return MemoryCommandResult(format_memory_targets(targets, cwd=project_path))
|
|
|
|
action = args[0].lower()
|
|
if action in {"edit", "open"}:
|
|
selector = " ".join(args[1:]).strip() or "user"
|
|
try:
|
|
target = resolve_memory_target(selector, cwd=project_path)
|
|
message, opened = open_memory_target(
|
|
target,
|
|
cwd=project_path,
|
|
launch_editor=launch_editor,
|
|
)
|
|
except Exception as exc: # noqa: BLE001 - mirrors OpenSpace's catch-all UI branch
|
|
return MemoryCommandResult(f"Error opening memory file: {exc}", display="system")
|
|
_clear_memory_caches()
|
|
if target.is_folder:
|
|
return MemoryCommandResult(message, display="system")
|
|
if not opened:
|
|
message += "\n\nEditor was not launched; the file is ready to edit."
|
|
return MemoryCommandResult(message, display="system")
|
|
|
|
if action == "read":
|
|
filename = " ".join(args[1:]).strip() or "MEMORY.md"
|
|
from openspace.tools.memory_tools import MemoryReadTool
|
|
from openspace.tool_runtime.direct_context import build_direct_tool_use_context
|
|
from openspace.tool_runtime.pipeline.execution import (
|
|
run_tool_use,
|
|
tool_call_result_to_tool_result,
|
|
)
|
|
|
|
tool = MemoryReadTool()
|
|
tool_call = {
|
|
"id": "memory-command-read",
|
|
"type": "function",
|
|
"function": {"name": tool.schema.name, "arguments": {"filename": filename}},
|
|
}
|
|
tool_context = build_direct_tool_use_context(
|
|
tools=[tool],
|
|
all_tools=[tool],
|
|
model="memory-command",
|
|
cwd=str(project_path),
|
|
agent_id="memory-command",
|
|
read_file_state={},
|
|
tui_available=False,
|
|
)
|
|
result = tool_call_result_to_tool_result(
|
|
await run_tool_use(tool_call, {tool.schema.name: tool}, tool_context)
|
|
)
|
|
if result.is_error:
|
|
return MemoryCommandResult(str(result.content or result.error), display="system")
|
|
return MemoryCommandResult(str(result.content), display="system")
|
|
|
|
if action == "logs":
|
|
from openspace.services.memory import format_daily_log_entries, get_auto_mem_path
|
|
|
|
include_consolidated = any(arg.lower() in {"all", "--all"} for arg in args[1:])
|
|
memory_dir = get_auto_mem_path(cwd=project_path)
|
|
return MemoryCommandResult(
|
|
format_daily_log_entries(
|
|
memory_dir,
|
|
include_consolidated=include_consolidated,
|
|
),
|
|
display="system",
|
|
)
|
|
|
|
if action in {"path", "show"}:
|
|
selector = " ".join(args[1:]).strip() or "user"
|
|
try:
|
|
target = resolve_memory_target(selector, cwd=project_path)
|
|
except Exception as exc: # noqa: BLE001 - command boundary
|
|
return MemoryCommandResult(f"Error resolving memory file: {exc}", display="system")
|
|
return MemoryCommandResult(
|
|
get_relative_memory_path(target.path, cwd=project_path),
|
|
display="system",
|
|
)
|
|
|
|
return MemoryCommandResult(
|
|
"Usage: /memory [list] | /memory edit [user|project|local|auto|folder|<listed-path>] | /memory read [filename] | /memory logs [all]",
|
|
display="system",
|
|
)
|
|
|
|
|
|
def _resolve_project_path(openspace: Any | None) -> str:
|
|
metadata = getattr(openspace, "current_session_metadata", None)
|
|
if isinstance(metadata, dict):
|
|
for key in ("project_path", "workspace_dir", "cwd"):
|
|
value = metadata.get(key)
|
|
if isinstance(value, str) and value.strip():
|
|
return value
|
|
return str(Path.cwd())
|
|
|
|
|
|
def _clear_memory_caches() -> None:
|
|
try:
|
|
from openspace.services.memory import clear_memory_file_caches
|
|
from openspace.prompts.grounding_agent_prompts import clear_system_prompt_sections
|
|
|
|
clear_memory_file_caches()
|
|
clear_system_prompt_sections()
|
|
except Exception:
|
|
return
|