Files
NexusOS/synapse/playbooks/store.py
T
jonandClaude Opus 4.8 f509034fdd feat: playbook tools, RAG, vision, voice, chat controls, faster boot
- Tool-using playbooks: read-only tool registry (search_memory,
  search_history, search_documents, list_models, get_time), per-playbook
  allowlist, agentic loop, and a "running tool" status indicator.
- Document ingest / RAG: documents table + chunker + embed/cosine retrieval
  reusing the existing stack; Documents page (upload/paste, viewer, delete);
  top-k chunks injected into the system prompt.
- Vision chat: attach images, base64 into /api/chat.
- Voice I/O: Web Speech dictation + read-aloud (browser-native, no backend).
- Chat controls: stop, regenerate, edit-and-resend; num_ctx knob in Settings.
- Per-playbook model override.
- History polish: per-conversation + bulk ShareGPT export.
- Faster ncp start: UI up first, Ollama warms in the background, with a
  per-phase timing readout.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 13:31:48 -05:00

102 lines
3.0 KiB
Python

from pathlib import Path
from typing import List, Optional
import yaml
from pydantic import BaseModel
class _BlockDumper(yaml.Dumper):
pass
_BlockDumper.add_representer(
str,
lambda dumper, data: dumper.represent_scalar(
"tag:yaml.org,2002:str", data, style="|" if "\n" in data else None
),
)
class PlaybookItem(BaseModel):
id: str
title: str
goal: str
instructions: str
tags: List[str] = []
tools: List[str] = []
model: str = ""
order: int = 0
class PlaybookFileStore:
def __init__(self, directory: Path):
self.directory = directory
directory.mkdir(parents=True, exist_ok=True)
def _path(self, id: str) -> Path:
return self.directory / f"{id}.yaml"
def _read(self, path: Path) -> Optional[PlaybookItem]:
try:
with open(path, encoding="utf-8") as f:
data = yaml.safe_load(f)
return PlaybookItem(
id=data["id"],
title=data["title"],
goal=data.get("goal", ""),
instructions=data.get("instructions", ""),
tags=data.get("tags", []),
tools=data.get("tools", []),
model=data.get("model", ""),
order=data.get("order", 0),
)
except Exception:
return None
@staticmethod
def _clean(s: str) -> str:
return "\n".join(line.rstrip() for line in s.split("\n")).strip()
def _write(self, item: PlaybookItem):
data = {
"id": item.id,
"title": item.title,
"goal": item.goal,
"tags": item.tags,
"tools": item.tools,
"model": item.model,
"order": item.order,
"instructions": self._clean(item.instructions),
}
with open(self._path(item.id), "w", encoding="utf-8") as f:
yaml.dump(data, f, Dumper=_BlockDumper, allow_unicode=True,
default_flow_style=False, sort_keys=False, width=4096)
def all_playbooks(self) -> List[PlaybookItem]:
items = [self._read(p) for p in self.directory.glob("*.yaml")]
return sorted((i for i in items if i), key=lambda x: x.order)
def get_playbook(self, id: str) -> Optional[PlaybookItem]:
return self._read(self._path(id))
def add_playbook(self, item: PlaybookItem):
self._write(item)
def delete_playbook(self, id: str):
p = self._path(id)
if p.exists():
p.unlink()
def reorder_playbooks(self, ordered_ids: List[str]):
all_ids = {p.stem for p in self.directory.glob("*.yaml")}
valid = [pid for pid in ordered_ids if pid in all_ids]
missing = sorted(all_ids - set(valid))
for index, pid in enumerate(valid + missing):
item = self.get_playbook(pid)
if item:
item.order = index
self._write(item)
from ..nexus_config import PLAYBOOK_DIR
playbook_store = PlaybookFileStore(PLAYBOOK_DIR)