okami.mom/albedo
git clone https://git.nkp.pet/okami.mom/albedo
f479db5
main
1"""Project-scoped Markdown memory with literal and full-text recall. 2 3The files live on the daemon's machine, wherever this kernel runs, and every 4call goes through the memory host route. Reads of a linked workspace's memory 5come back with it; writes always land in this workspace's own. 6""" 7 8from __future__import annotations 9 10from collections .abc import Callable 11from datetime import date 12import re 13from typing import cast 14 15from albedo_api import PythonApi ,Text 16from albedo_plugins .files import Rows 17 18 19class Memory : 20def __init__ (self ,host :Callable [[str ,dict [str ,object ]],object ])-> None : 21self ._host = host 22 23def read (self ,max_chars :int = 16000 )-> Text : 24if not 0 < max_chars <= 200_000 : 25raise ValueError ("0 < max_chars <= 200000" ) 26content = cast (str ,self ._host ("memory.read" , {})) 27suffix = ( 28"\n[truncated; raise max_chars to read more]" 29if len (content )> max_chars 30else "" 31 ) 32return Text (content [:max_chars ]+ suffix ) 33 34def save (self ,text :str )-> Text : 35"""Atomically replace curated memory.""" 36return Text (cast (str ,self ._host ("memory.save" , {"text" :text }))) 37 38def append (self ,text :str )-> Text : 39return Text (cast (str ,self ._host ("memory.append" , {"text" :text }))) 40 41def journal (self ,text :str )-> Text : 42day = date .today ().isoformat () 43return Text ( 44cast (str ,self ._host ("memory.journal" , {"date" :day ,"text" :text })) 45 ) 46 47def _documents (self )-> tuple [list [tuple [str ,str ]],bool ]: 48"""(name, text) for every memory and journal file this workspace 49reads; a linked workspace's names lead with it.""" 50answer = cast (dict ,self ._host ("memory.documents" , {})) 51named = [ 52 ( 53document ["path" ] 54if document ["workspace" ]== answer ["own" ] 55else f"[ { document [ 'workspace' ] } ] { document [ 'path' ] } " , 56document ["text" ], 57 ) 58for document in answer ["documents" ] 59 ] 60return named ,bool (answer ["truncated" ]) 61 62def grep (self ,term :str ,limit :int = 20 )-> Rows : 63if not term or not 1 <= limit <= 100 : 64raise ValueError ("term must be nonempty and 1 <= limit <= 100" ) 65documents ,cut = self ._documents () 66matches = [] 67for name ,content in documents : 68for number ,line in enumerate (content .splitlines (),1 ): 69if term .casefold ()in line .casefold (): 70matches .append (f" { name } : { number } : { line [: 500 ] } " ) 71if len (matches )== limit : 72return Rows (matches ,truncated = True ) 73return Rows (matches ,truncated = cut ) 74 75def search (self ,query :str ,limit :int = 20 )-> Rows : 76"""FTS5 ranks paragraphs, including multiline entries, without a stale index.""" 77if not query .strip ()or not 1 <= limit <= 100 : 78raise ValueError ("query must be nonempty and 1 <= limit <= 100" ) 79import sqlite3 # only search needs it; not worth every kernel's boot 80 81documents ,cut = self ._documents () 82with sqlite3 .connect (":memory:" )as db : 83db .execute ( 84"CREATE VIRTUAL TABLE notes USING fts5(path UNINDEXED, line UNINDEXED, body)" 85 ) 86for name ,content in documents : 87for match in re .finditer ( 88r"(?s)\S.*?(?=\n[ \t]*\n|\s*\Z)" ,content ,re .S 89 ): 90line = content .count ("\n" ,0 ,match .start ())+ 1 91db .execute ( 92"INSERT INTO notes VALUES (?, ?, ?)" , 93 (name ,line ,match .group ()[:100_000 ]), 94 ) 95try : 96rows = db .execute ( 97"SELECT path, line, snippet(notes, 2, '[', ']', '…', 16) " 98"FROM notes WHERE notes MATCH ? ORDER BY rank LIMIT ?" , 99 (query ,limit ), 100 ).fetchall () 101except sqlite3 .OperationalError as exc : 102raise ValueError (f"invalid full-text query: { exc } " )from exc 103return Rows ( 104 ( 105f" { path } : { line } : { snippet . replace ( chr ( 10 ), ' ' )[: 500 ] } " 106for path ,line ,snippet in rows 107 ), 108truncated = cut , 109 ) 110 111 112def setup (api :PythonApi )-> dict [str ,object ]: 113if api .host_now is None : 114raise RuntimeError ("this host cannot answer synchronous memory calls" ) 115return {"memory" :Memory (api .host_now )}