cite
def cite(
p
)->str:A short citation for a passage: its source and its title.
The tutor writes better questions when it can see what you read and what you did. This module gives it that view.
ground asks every source and merges the passages.Learning.connect keeps the best passages for a concept as its links: where the concept shows up in your work.These are the imports of the module.
A passage is one short piece of text and where it came from. src names the source, ref points into it, and url opens it when there is a page for it.
One passage from a source.
The FTS5 sources need a safe query. fts_query keeps the words of three letters or more and joins them with OR. Marks such as ā and ṣ stay, because vedicreader stores IAST.
An FTS5 query that matches any of the first n words of q with three letters or more.
Words of three letters or more stay, with their marks. With only short words, the query is empty.
VaultSource searches every shelf of the vault except the learning shelf. It is the widest source: your documents, leela’s notes, and the session shelves that the indexers below make.
Passages from every vault shelf except the learning shelf.
VedicSource reads the verse databases of the vedicreader repo, data/db/shlokas/*.db, in read-only mode. It searches the English meaning of each section through the sections_fts table, and names the text from the titles table.
Passages from the vedicreader library: the meaning of each section of each text.
When the repo is on this machine, this live check searches it:
ground asks each source for passages and takes them in turn, so one source cannot fill the list.
Passages as prompt text, one line each, with citations.
Up to k passages for q, taken from the sources in turn. With must, a passage must name one of those terms.
Whether passage p names one of terms as a whole word, in its title or its text.
Two stand-in sources give numbered passages. ground takes them in turn.
A link must name the concept. names compares whole words without marks, so a passage on anumāna links to Anumana, and a passage on rank fusion does not link to rank.
connect searches the sources for one concept and keeps the best five passages in the seen_in meta of the concept. It does not keep the text, only the place of the text, so the links stay small.
Whether the links of concept c are missing or older than age seconds.
Find where concept slug shows up in your sources, and keep the links on the concept.
ramabana writes one JSON line for each turn to agent-history.jsonl, and the session titles to agent-sessions.json. index_ramabana makes one vault document for each session on the ramabana shelf. It skips a session whose text did not change, so a second run is fast. It only adds documents to its own shelf.
ramabana also keeps memory: one MEMORY.md for each project, and what dhrona learned (learned/AGENTS.md and its skills). ramabana_memory reads them. index_ramabana puts each one on the same shelf, with kind memory.
ramabana’s memory as (ref, title, text): each project’s MEMORY.md, and the learned AGENTS.md and skills.
ramabana_sessions reads the history. index_ramabana puts the sessions and the memory on the shelf. RAMA_CFG is the ramabana config folder: $RAMABANA_CFG, else ~/.config/ramabana.
Put each ramabana session on the vault shelf shelf, one document each. Returns counts.
Each ramabana session as (id, title, text, meta), from the history file.
This test makes a small history in a temporary folder, reads it and indexes it two times:
import tempfile
from abhyasa.store import temp_learning
d = Path(tempfile.mkdtemp())
(d/'memory/abc').mkdir(parents=True); (d/'memory/abc/MEMORY.md').write_text('- Use RRF for hybrid search.')
(d/'agent-sessions.json').write_text(json.dumps(dict(sessions={'s1': dict(title='Fix the RRF merge', roots=['/code'])})))
(d/'agent-history.jsonl').write_text('\n'.join(json.dumps(r) for r in [
dict(session='s1', at=1., prompt='<user-request>why does rrf use ranks</user-request>', reply='Because scores differ in scale.', model='m'),
dict(session='s1', at=2., prompt='and k?', reply='k damps the top ranks.', model='m'),
dict(session='s2', at=3., prompt='sandhi of a + i', reply='It gives e.', model='m')]) + '\nnot json\n')
ss = list(ramabana_sessions(d))
test_eq([s[1] for s in ss], ['Fix the RRF merge', 'sandhi of a + i'])
assert ss[0][2].startswith('## why does rrf use ranks')
lr = temp_learning()
test_eq(index_ramabana(lr.vault, d), dict(sessions=2, memory=1, changed=3))
test_eq(index_ramabana(lr.vault, d), dict(sessions=2, memory=1, changed=0)) # nothing changed: nothing written
test_eq(index_ramabana(lr.vault, d, dry_run=True), dict(sessions=2, memory=1, changed=0))With no history file yet, there are no sessions, and no error.
leela keeps agent sessions as notebooks in _leela_agent_sessions folders, and you keep your own notebooks next to your code. index_notebooks puts each notebook on the notebooks shelf: its markdown, its code and its text outputs. It skips .venv, node_modules and other build folders.
Put each notebook under roots that is new or changed on the vault shelf shelf. Returns counts.
The readable text of a notebook: markdown, code in fences, and text outputs.
Every .ipynb under roots, or only those in _leela_agent_sessions folders.
Two notebooks in a temporary folder: one in _leela_agent_sessions and one beside it. A third notebook in .venv is skipped. The first run indexes both.
from fastcore.nbio import new_nb, mk_cell, write_nb
root = Path(tempfile.mkdtemp())
(root/'proj/_leela_agent_sessions').mkdir(parents=True); (root/'proj/.venv').mkdir()
write_nb(new_nb([mk_cell('# RRF notes', 'markdown'), mk_cell('1 + 1')]), root/'proj/_leela_agent_sessions/a.ipynb')
write_nb(new_nb([mk_cell('x = 1')]), root/'proj/b.ipynb')
write_nb(new_nb([mk_cell('y = 2')]), root/'proj/.venv/c.ipynb')
test_eq(sorted(p.name for p in find_notebooks([root])), ['a.ipynb', 'b.ipynb'])
test_eq([p.name for p in find_notebooks([root], only_agent=True)], ['a.ipynb'])
assert nb_text(root/'proj/_leela_agent_sessions/a.ipynb').startswith('# RRF notes')
test_eq(index_notebooks(lr.vault, [root]), dict(notebooks=2, changed=2, failed=0, same=0))A second run reads no notebook again. A notebook that changes is read again.
With the two shelves in the vault, VaultSource finds sessions and notebooks too. connect then links a concept to them.
The ramabana beat runs every five minutes. It runs each pobblebonk schedule that is due, through the ramabana.ticks entry point with the same name. abhyasa gives the schedule abhyasa-index. Each fire indexes new and changed sessions, memory and leela agent notebooks. For a session that did not change, the tick only reads it. Thus a fire takes seconds.
The tick leaves a note for the next ramabana session only when something changed.
One fire of the abhyasa-index schedule: index sessions, memory and leela agent notebooks. A note when something changed.
A fire on the temporary history from above: the first one writes and leaves a note, the second finds nothing new.
schedule writes the schedule into pobblebonk once. Run it again to change how often it fires. It needs pobblebonk, which ramabana installs.
Add or change the abhyasa-index schedule in pobblebonk, so ramabana’s beat indexes your sessions.
A test on a temporary pobblebonk database. A second call changes the schedule, and does not add another one.
abhyasa-index ramabana and abhyasa-index notebooks fill the shelves of your vault. --dry-run counts and writes nothing.
abhyasa-index: put ramabana sessions or notebooks on vault shelves.