feat(heartbeat): custom evaluator prompt
This commit is contained in:
@@ -143,6 +143,14 @@ BUILTIN_COMMAND_SPECS: tuple[BuiltinCommandSpec, ...] = (
|
||||
"[init]",
|
||||
accepts_args=True,
|
||||
),
|
||||
BuiltinCommandSpec(
|
||||
"/evaluator-prompt",
|
||||
"Heartbeat evaluator",
|
||||
"Customize the heartbeat notification gate prompt for this workspace.",
|
||||
"file-text",
|
||||
"[init]",
|
||||
accepts_args=True,
|
||||
),
|
||||
BuiltinCommandSpec(
|
||||
"/skill",
|
||||
"List skills",
|
||||
@@ -515,6 +523,64 @@ async def cmd_dream_prompt(ctx: CommandContext) -> OutboundMessage:
|
||||
)
|
||||
|
||||
|
||||
async def cmd_evaluator_prompt(ctx: CommandContext) -> OutboundMessage:
|
||||
"""Show or set up the workspace heartbeat evaluator prompt."""
|
||||
from nanobot.utils.evaluator import (
|
||||
default_evaluator_prompt,
|
||||
evaluator_prompt_file,
|
||||
has_evaluator_prompt_override,
|
||||
)
|
||||
|
||||
workspace = ctx.loop.context.memory.workspace
|
||||
path = evaluator_prompt_file(workspace)
|
||||
display_path = path.relative_to(workspace).as_posix()
|
||||
args = ctx.args.strip().lower()
|
||||
|
||||
if args == "init":
|
||||
try:
|
||||
prompt_exists_with_content = path.exists() and (
|
||||
not path.is_file() or bool(path.read_text(encoding="utf-8").strip())
|
||||
)
|
||||
except OSError:
|
||||
prompt_exists_with_content = True
|
||||
if prompt_exists_with_content:
|
||||
content = (
|
||||
f"Heartbeat evaluator prompt already exists at `{display_path}`.\n\n"
|
||||
"Edit that file, or delete/empty it to return to nanobot's default."
|
||||
)
|
||||
else:
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_text(default_evaluator_prompt() + "\n", encoding="utf-8")
|
||||
content = (
|
||||
f"Created heartbeat evaluator prompt at `{display_path}`.\n\n"
|
||||
"Edit that file to control when the heartbeat notification gate speaks. "
|
||||
"It must still instruct the model to call the `evaluate_notification` tool, "
|
||||
"otherwise the gate fails closed and stays silent. "
|
||||
"Delete or empty it to return to nanobot's default."
|
||||
)
|
||||
elif args:
|
||||
content = "Usage: /evaluator-prompt [init]"
|
||||
elif has_evaluator_prompt_override(workspace):
|
||||
content = (
|
||||
"Heartbeat evaluator prompt: custom for this workspace\n\n"
|
||||
f"- Path: `{display_path}`\n"
|
||||
"- Delete or empty this file to return to nanobot's default."
|
||||
)
|
||||
else:
|
||||
content = (
|
||||
"Heartbeat evaluator prompt: nanobot default\n\n"
|
||||
f"- Editable file: `{display_path}`\n"
|
||||
"- Run `/evaluator-prompt init` to create an editable copy."
|
||||
)
|
||||
|
||||
return OutboundMessage(
|
||||
channel=ctx.msg.channel,
|
||||
chat_id=ctx.msg.chat_id,
|
||||
content=content,
|
||||
metadata={**dict(ctx.msg.metadata or {}), "render_as": "text"},
|
||||
)
|
||||
|
||||
|
||||
def _format_dream_no_input_message() -> str:
|
||||
return "\n".join([
|
||||
"Dream has no conversation history to process yet.",
|
||||
@@ -954,6 +1020,8 @@ def register_builtin_commands(router: CommandRouter) -> None:
|
||||
router.prefix("/dream-restore ", cmd_dream_restore)
|
||||
router.exact("/dream-prompt", cmd_dream_prompt)
|
||||
router.prefix("/dream-prompt ", cmd_dream_prompt)
|
||||
router.exact("/evaluator-prompt", cmd_evaluator_prompt)
|
||||
router.prefix("/evaluator-prompt ", cmd_evaluator_prompt)
|
||||
router.exact("/skill", cmd_skill)
|
||||
router.exact("/help", cmd_help)
|
||||
router.exact("/pairing", cmd_pairing)
|
||||
|
||||
Reference in New Issue
Block a user