import json
from rupaka.spec import *
from rupaka.spec import _gates
from rupaka.render import show, save, page
from rupaka.check import check, can_check, browser, in_threadgallery
The gallery demos live in rupaka/demos/ as JSON. They show what a good demo looks like, and the agent gives two of them to the model as examples. The data of the Sanskrit and jyotiṣa demos comes from the vedicreader and ganapati sources, not from memory:
| demo | data | source |
|---|---|---|
| vowel sandhi | the six rule names and their Aṣṭādhyāyī numbers (6.1.101, 6.1.87, 6.1.88, 6.1.77, 6.1.78, 6.1.109) | ganapati vyakarana.py, JOINS |
| nakṣatra and pāda | the 27 nakṣatra names and lords, the 12 rāśi names, 13°20′ for a nakṣatra and 3°20′ for a pāda | vedicreader vr/muhurtha/names.py, vr/jyotisha/nakshatra/primer.py |
The demos
Each demo has a gate. Here are the gate questions and the blocks of each demo.
g = gallery()
for k, s in g.items(): print(f"{k:24} {[b['type'] for b in s['blocks']]}\n{'':24} {next(_gates(s['blocks']))['prompt']}")Each demo is valid
validate finds no errors in any gallery demo.
test_eq({k: list(validate(s)) for k, s in g.items()}, {k: [] for k in g})The builder form
A notebook author can write the sandhi demo with the builders. The result is the same dict as the JSON file.
V = ['a', 'ā', 'i', 'ī', 'u', 'ū', 'ṛ', 'e', 'ai', 'o', 'au']
sd = g['vowel-sandhi']
d = demo('vowel-sandhi', 'Vowel sandhi', ask=sd['ask'], state=dict(a='a', b='i'), derive=[rules('sandhi', ['a', 'b'], 'join')],
blocks=[row(wheel('a', V, 'End of word 1'), wheel('b', V, 'Start of word 2')),
gate('join', 'What do the two vowels become?', from_('join.alts'), 'join.out', context='For {a} + {b}:',
why='Say why, in one line, before you lock in.', again=True),
readout('{a} + {b} → {join.out}', label='The join', after='join'),
caption(sd['blocks'][3]['text'], after='join')],
tables=sd['tables'])
test_eq(d, sd)The data
At 47°, the point is in Rohiṇī, pāda 3, in Vṛṣabha. At 30°, the start of Vṛṣabha, it is in Kṛttikā, pāda 2.
nk = g['nakshatra-pada']
test_eq(run(nk)['where'], 'Rohiṇī, pāda 3')
test_eq(run(nk)['rasi']['name'], 'Vṛṣabha')
test_eq(run(nk, dict(lon=30, show_pada=False))['where'], 'Kṛttikā, pāda 2')For a + i, the join is e (guṇa). The wrong options come from the other rows of the table.
j = run(sd)['join']
test_eq((j['out'], j['name']), ('e', 'guṇa'))
test_eq(sorted(j['alts']), ['e', 'ā', 'ī', 'ū'])With k = 60 the fused winner is B, which is in both lists. With k = 1 it is A, the top of one list.
rr = g['reciprocal-rank-fusion']
test_eq(run(rr)['fused'][0]['id'], 'B')
test_eq(run(rr, {**rr['state'], 'k': 1})['fused'][0]['id'], 'A')Each demo draws and plays
In a browser, each demo passes the whole check in light and dark.
def check_all(specs, shots=None):
with browser() as br: return {k: check(s, shots=shots, b=br).errors for k, s in specs.items()}
if can_check(): test_eq(in_thread(check_all, g), {k: [] for k in g})One page
save writes the whole gallery to one page that works from file://.
import tempfile
from pathlib import Pathp = save(list(g.values()), Path(tempfile.mkdtemp())/'gallery.html', title='rupaka gallery', lead='Predict first. Then the demo shows the rest.')
assert p.exists() and (p.parent/'fonts').exists()In a notebook, show draws a demo:
show(g['reciprocal-rank-fusion'])