abhyasa 0.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. abhyasa-0.0.1/.gitignore +158 -0
  2. abhyasa-0.0.1/LICENSE +21 -0
  3. abhyasa-0.0.1/PKG-INFO +158 -0
  4. abhyasa-0.0.1/README.md +129 -0
  5. abhyasa-0.0.1/abhyasa/__init__.py +1 -0
  6. abhyasa-0.0.1/abhyasa/_modidx.py +148 -0
  7. abhyasa-0.0.1/abhyasa/app.py +43 -0
  8. abhyasa-0.0.1/abhyasa/blocks/__init__.py +8 -0
  9. abhyasa-0.0.1/abhyasa/blocks/add/__init__.py +1 -0
  10. abhyasa-0.0.1/abhyasa/blocks/add/app.py +36 -0
  11. abhyasa-0.0.1/abhyasa/blocks/add/cfg.py +3 -0
  12. abhyasa-0.0.1/abhyasa/blocks/concepts/__init__.py +1 -0
  13. abhyasa-0.0.1/abhyasa/blocks/concepts/app.py +29 -0
  14. abhyasa-0.0.1/abhyasa/blocks/concepts/cfg.py +4 -0
  15. abhyasa-0.0.1/abhyasa/blocks/concepts/concepts.css +8 -0
  16. abhyasa-0.0.1/abhyasa/blocks/concepts/ui.py +37 -0
  17. abhyasa-0.0.1/abhyasa/blocks/graph/__init__.py +1 -0
  18. abhyasa-0.0.1/abhyasa/blocks/graph/app.py +28 -0
  19. abhyasa-0.0.1/abhyasa/blocks/graph/cfg.py +3 -0
  20. abhyasa-0.0.1/abhyasa/blocks/graph/graph.css +5 -0
  21. abhyasa-0.0.1/abhyasa/blocks/graph/graph.js +32 -0
  22. abhyasa-0.0.1/abhyasa/blocks/search/__init__.py +1 -0
  23. abhyasa-0.0.1/abhyasa/blocks/search/app.py +24 -0
  24. abhyasa-0.0.1/abhyasa/blocks/search/cfg.py +2 -0
  25. abhyasa-0.0.1/abhyasa/blocks/session/__init__.py +1 -0
  26. abhyasa-0.0.1/abhyasa/blocks/session/app.py +74 -0
  27. abhyasa-0.0.1/abhyasa/blocks/session/cfg.py +9 -0
  28. abhyasa-0.0.1/abhyasa/blocks/session/session.css +36 -0
  29. abhyasa-0.0.1/abhyasa/blocks/session/session.js +3 -0
  30. abhyasa-0.0.1/abhyasa/blocks/session/ui.py +58 -0
  31. abhyasa-0.0.1/abhyasa/blocks/sources/__init__.py +1 -0
  32. abhyasa-0.0.1/abhyasa/blocks/sources/app.py +43 -0
  33. abhyasa-0.0.1/abhyasa/blocks/sources/cfg.py +3 -0
  34. abhyasa-0.0.1/abhyasa/blocks/today/__init__.py +1 -0
  35. abhyasa-0.0.1/abhyasa/blocks/today/app.py +23 -0
  36. abhyasa-0.0.1/abhyasa/blocks/today/cfg.py +2 -0
  37. abhyasa-0.0.1/abhyasa/blocks/voice/__init__.py +1 -0
  38. abhyasa-0.0.1/abhyasa/blocks/voice/app.py +25 -0
  39. abhyasa-0.0.1/abhyasa/blocks/voice/cfg.py +2 -0
  40. abhyasa-0.0.1/abhyasa/blocks/voice/voice.js +32 -0
  41. abhyasa-0.0.1/abhyasa/core/__init__.py +4 -0
  42. abhyasa-0.0.1/abhyasa/core/base.css +85 -0
  43. abhyasa-0.0.1/abhyasa/core/block.py +47 -0
  44. abhyasa-0.0.1/abhyasa/core/cfg.py +12 -0
  45. abhyasa-0.0.1/abhyasa/core/core.js +18 -0
  46. abhyasa-0.0.1/abhyasa/core/fonts/LICENSES.txt +69 -0
  47. abhyasa-0.0.1/abhyasa/core/fonts/libre-baskerville-latin-400-normal.woff2 +0 -0
  48. abhyasa-0.0.1/abhyasa/core/fonts/libre-baskerville-latin-ext-400-normal.woff2 +0 -0
  49. abhyasa-0.0.1/abhyasa/core/fonts/literata-latin-ext-wght-italic.woff2 +0 -0
  50. abhyasa-0.0.1/abhyasa/core/fonts/literata-latin-ext-wght-normal.woff2 +0 -0
  51. abhyasa-0.0.1/abhyasa/core/fonts/literata-latin-wght-italic.woff2 +0 -0
  52. abhyasa-0.0.1/abhyasa/core/fonts/literata-latin-wght-normal.woff2 +0 -0
  53. abhyasa-0.0.1/abhyasa/core/fonts/tiro-devanagari-sanskrit-devanagari-400-normal.woff2 +0 -0
  54. abhyasa-0.0.1/abhyasa/core/hooks.py +19 -0
  55. abhyasa-0.0.1/abhyasa/core/ui.py +80 -0
  56. abhyasa-0.0.1/abhyasa/core/vendor/auto-render.min.js +1 -0
  57. abhyasa-0.0.1/abhyasa/core/vendor/d3.min.js +2 -0
  58. abhyasa-0.0.1/abhyasa/core/vendor/fasthtml.js +52 -0
  59. abhyasa-0.0.1/abhyasa/core/vendor/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  60. abhyasa-0.0.1/abhyasa/core/vendor/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  61. abhyasa-0.0.1/abhyasa/core/vendor/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  62. abhyasa-0.0.1/abhyasa/core/vendor/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  63. abhyasa-0.0.1/abhyasa/core/vendor/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  64. abhyasa-0.0.1/abhyasa/core/vendor/fonts/KaTeX_Main-Bold.woff2 +0 -0
  65. abhyasa-0.0.1/abhyasa/core/vendor/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  66. abhyasa-0.0.1/abhyasa/core/vendor/fonts/KaTeX_Main-Italic.woff2 +0 -0
  67. abhyasa-0.0.1/abhyasa/core/vendor/fonts/KaTeX_Main-Regular.woff2 +0 -0
  68. abhyasa-0.0.1/abhyasa/core/vendor/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  69. abhyasa-0.0.1/abhyasa/core/vendor/fonts/KaTeX_Math-Italic.woff2 +0 -0
  70. abhyasa-0.0.1/abhyasa/core/vendor/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  71. abhyasa-0.0.1/abhyasa/core/vendor/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  72. abhyasa-0.0.1/abhyasa/core/vendor/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  73. abhyasa-0.0.1/abhyasa/core/vendor/fonts/KaTeX_Script-Regular.woff2 +0 -0
  74. abhyasa-0.0.1/abhyasa/core/vendor/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  75. abhyasa-0.0.1/abhyasa/core/vendor/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  76. abhyasa-0.0.1/abhyasa/core/vendor/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  77. abhyasa-0.0.1/abhyasa/core/vendor/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  78. abhyasa-0.0.1/abhyasa/core/vendor/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  79. abhyasa-0.0.1/abhyasa/core/vendor/htmx.min.js +1 -0
  80. abhyasa-0.0.1/abhyasa/core/vendor/katex.min.css +1 -0
  81. abhyasa-0.0.1/abhyasa/core/vendor/katex.min.js +1 -0
  82. abhyasa-0.0.1/abhyasa/core/vendor/marked.min.js +6 -0
  83. abhyasa-0.0.1/abhyasa/ground.py +203 -0
  84. abhyasa-0.0.1/abhyasa/llm.py +60 -0
  85. abhyasa-0.0.1/abhyasa/model.py +102 -0
  86. abhyasa-0.0.1/abhyasa/nb.py +148 -0
  87. abhyasa-0.0.1/abhyasa/rama.py +98 -0
  88. abhyasa-0.0.1/abhyasa/sources.py +135 -0
  89. abhyasa-0.0.1/abhyasa/store.py +187 -0
  90. abhyasa-0.0.1/abhyasa/tutor.py +267 -0
  91. abhyasa-0.0.1/abhyasa/voice.py +52 -0
  92. abhyasa-0.0.1/pyproject.toml +60 -0
@@ -0,0 +1,158 @@
1
+ _docs/
2
+ _proc/
3
+
4
+ *.bak
5
+ .gitattributes
6
+ .last_checked
7
+ .gitconfig
8
+ *.bak
9
+ *.log
10
+ *~
11
+ ~*
12
+ _tmp*
13
+ tmp*
14
+ tags
15
+ *.pkg
16
+
17
+ # Byte-compiled / optimized / DLL files
18
+ __pycache__/
19
+ *.py[cod]
20
+ *$py.class
21
+
22
+ # C extensions
23
+ *.so
24
+
25
+ # Distribution / packaging
26
+ .Python
27
+ env/
28
+ build/
29
+ conda/
30
+ develop-eggs/
31
+ dist/
32
+ downloads/
33
+ eggs/
34
+ .eggs/
35
+ lib/
36
+ lib64/
37
+ parts/
38
+ sdist/
39
+ var/
40
+ wheels/
41
+ *.egg-info/
42
+ .installed.cfg
43
+ *.egg
44
+
45
+ # PyInstaller
46
+ # Usually these files are written by a python script from a template
47
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
48
+ *.manifest
49
+ *.spec
50
+
51
+ # Installer logs
52
+ pip-log.txt
53
+ pip-delete-this-directory.txt
54
+
55
+ # Unit test / coverage reports
56
+ htmlcov/
57
+ .tox/
58
+ .coverage
59
+ .coverage.*
60
+ .cache
61
+ nosetests.xml
62
+ coverage.xml
63
+ *.cover
64
+ .hypothesis/
65
+
66
+ # Translations
67
+ *.mo
68
+ *.pot
69
+
70
+ # Django stuff:
71
+ *.log
72
+ local_settings.py
73
+
74
+ # Flask stuff:
75
+ instance/
76
+ .webassets-cache
77
+
78
+ # Scrapy stuff:
79
+ .scrapy
80
+
81
+ # Sphinx documentation
82
+ docs/_build/
83
+
84
+ # PyBuilder
85
+ target/
86
+
87
+ # Jupyter Notebook
88
+ .ipynb_checkpoints
89
+
90
+ # pyenv
91
+ .python-version
92
+
93
+ # celery beat schedule file
94
+ celerybeat-schedule
95
+
96
+ # SageMath parsed files
97
+ *.sage.py
98
+
99
+ # dotenv
100
+ .env
101
+
102
+ # virtualenv
103
+ .venv
104
+ venv/
105
+ ENV/
106
+
107
+ # Spyder project settings
108
+ .spyderproject
109
+ .spyproject
110
+
111
+ # Rope project settings
112
+ .ropeproject
113
+
114
+ # mkdocs documentation
115
+ /site
116
+
117
+ # mypy
118
+ .mypy_cache/
119
+
120
+ .vscode
121
+ *.swp
122
+
123
+ # osx generated files
124
+ .DS_Store
125
+ .DS_Store?
126
+ .Trashes
127
+ ehthumbs.db
128
+ Thumbs.db
129
+ .idea
130
+
131
+ # pytest
132
+ .pytest_cache
133
+
134
+ # tools/trust-doc-nbs
135
+ docs_src/.last_checked
136
+
137
+ # symlinks to fastai
138
+ docs_src/fastai
139
+ tools/fastai
140
+
141
+ # link checker
142
+ checklink/cookies.txt
143
+
144
+ # .gitconfig is now autogenerated
145
+ .gitconfig
146
+
147
+ # Quarto installer
148
+ .deb
149
+ .pkg
150
+
151
+ # Quarto
152
+ .quarto
153
+
154
+ .venv/
155
+ .kosha/
156
+ _docs/
157
+ _proc/
158
+ nbs/_fixtures/
abhyasa-0.0.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Karthik
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
abhyasa-0.0.1/PKG-INFO ADDED
@@ -0,0 +1,158 @@
1
+ Metadata-Version: 2.5
2
+ Name: abhyasa
3
+ Version: 0.0.1
4
+ Summary: A learning harness that makes you think: active recall, a learner model and a concept graph in your vault
5
+ Project-URL: Repository, https://github.com/vedicreader/abhyasa
6
+ Project-URL: Documentation, https://vedicreader.github.io/abhyasa/
7
+ Author-email: Karthik <karthik.rajgopal@hotmail.com>
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: active-recall,learning,nbdev,spaced-repetition,vishalakshi
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Education
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3 :: Only
15
+ Requires-Python: >=3.12
16
+ Requires-Dist: fastcore>=1.8
17
+ Requires-Dist: ipywidgets>=8.1
18
+ Requires-Dist: python-fasthtml>=0.14
19
+ Requires-Dist: rishi>=0.1.56
20
+ Requires-Dist: vishalakshi>=0.1.35
21
+ Provides-Extra: dev
22
+ Requires-Dist: ipykernel>=7.3.0; extra == 'dev'
23
+ Requires-Dist: nbdev>=3.3.25; extra == 'dev'
24
+ Requires-Dist: notebook>=7.6.3; extra == 'dev'
25
+ Requires-Dist: pytest>=8; extra == 'dev'
26
+ Provides-Extra: whisper
27
+ Requires-Dist: mlx-audio>=0.5; extra == 'whisper'
28
+ Description-Content-Type: text/markdown
29
+
30
+ # abhyasa
31
+
32
+
33
+ <!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->
34
+
35
+ abhyasa (अभ्यास) means steady practice. It is a tutor that asks you questions and makes you do the thinking.
36
+ It never gives the answer first. You answer in text or by voice, and you can think aloud.
37
+ The tutor grades your reasoning, not only the result.
38
+ A learner model keeps the depth of each concept that you know, and the date of its next review.
39
+
40
+ ## How it works
41
+
42
+ Each question goes through one loop:
43
+
44
+ 1. Pick. Code chooses the next concept and the depth: a due review first, then the frontier. The app shows the reason.
45
+ 2. Ask. A model writes one question at that depth, with a hidden model answer and a rubric.
46
+ 3. Struggle. You answer. A wrong try gets the first error and a nudge, but no fix. You can ask for hints, one rung at a time.
47
+ 4. Grade. The model grades the answer and the think-aloud. Code grades choice questions and code exercises.
48
+ 5. Update. Correctness and confidence move the floor, the ceiling and the next review date of the concept.
49
+ 6. Teach. After a miss, the tutor teaches the smallest step that fixes the first error. Then it asks a check question.
50
+
51
+ | term | meaning |
52
+ |---------|-------------------------------------------------------------------|
53
+ | concept | one idea that you can be asked about |
54
+ | depth | 0 unseen, 1 recognise, 2 recall, 3 explain, 4 apply, 5 transfer |
55
+ | floor | the highest depth that you pass reliably |
56
+ | ceiling | the lowest depth that you failed |
57
+ | attempt | one answer, kept as a vault document |
58
+ | session | one sitting of questions, with a summary |
59
+ | hint | one rung of a ladder of six, from the least to the most revealing |
60
+ | lesson | the smallest step that the tutor teaches after a miss |
61
+
62
+ ## Install and run
63
+
64
+ Install the package and its dependencies:
65
+
66
+ ``` sh
67
+ uv sync
68
+ ```
69
+
70
+ Start the web app on <http://127.0.0.1:5077>:
71
+
72
+ ``` sh
73
+ uv run abhyasa
74
+ ```
75
+
76
+ The app listens on the loopback address only. These variables change the defaults:
77
+
78
+ | variable | what it sets | default |
79
+ |----|----|----|
80
+ | `ABHYASA_VAULT` | the vault file | the vault of vishalakshi |
81
+ | `ABHYASA_MODEL` | the rishi model for questions, grades, hints and lessons | `claude/sonnet` |
82
+ | `ABHYASA_STT` | the speech-to-text engine: `gemma` or `whisper` | `gemma` |
83
+
84
+ ## Surfaces
85
+
86
+ All surfaces use the same tutor and the same data.
87
+
88
+ ### Web app
89
+
90
+ The web app is made from blocks under `abhyasa/blocks`:
91
+
92
+ | block | what it does |
93
+ |----|----|
94
+ | `today` | the home page: what is due and what to do now |
95
+ | `session` | one sitting with the tutor: one question at a time, with hints, confidence and “I don’t know” |
96
+ | `voice` | a mic button for each text box; it turns speech into text on this machine |
97
+ | `concepts` | the list of concepts, and one page for each concept with its state |
98
+ | `graph` | the concept graph: a color for each floor, a ring when a review is due, arrows for prereqs |
99
+ | `search` | one search over the `learning` shelf and the other shelves of your vault |
100
+ | `add` | add concepts from a text, a vault document or vedicreader |
101
+
102
+ ### ramabana
103
+
104
+ The `/learn` extension gives the tutor in the terminal, beside your code.
105
+ To turn it on, make the file `~/.config/ramabana/extensions/abhyasa.py` with this line:
106
+
107
+ ``` python
108
+ from abhyasa.rama import setup
109
+ ```
110
+
111
+ ### Notebooks
112
+
113
+ In leela or Jupyter, [`learn`](https://vedicreader.github.io/abhyasa/nb.html#learn) shows a question card. [`exercise`](https://vedicreader.github.io/abhyasa/nb.html#exercise) gives a code exercise that its tests check in the kernel.
114
+
115
+ ``` python
116
+ from abhyasa.nb import learn, exercise
117
+ learn('reciprocal-rank-fusion')
118
+ ex = exercise('reciprocal-rank-fusion')
119
+ ```
120
+
121
+ ## Where the data is
122
+
123
+ All data is on the `learning` shelf of the vishalakshi vault.
124
+ Each concept, attempt and session is one vault document, so other tools can search it.
125
+ Tests use a vault in a temporary folder and never open your real vault.
126
+
127
+ ## Example
128
+
129
+ This example runs offline. [`temp_learning`](https://vedicreader.github.io/abhyasa/store.html#temp_learning) makes a shelf in a temporary vault, and [`Fake`](https://vedicreader.github.io/abhyasa/llm.html#fake) replaces the model.
130
+
131
+ ``` python
132
+ from abhyasa.store import temp_learning
133
+ from abhyasa.llm import Fake
134
+ from abhyasa.tutor import Tutor
135
+
136
+ lr = temp_learning()
137
+ lr.put('Ranking', 'Order items by a score.', 'ml')
138
+ fake = Fake({'You grade one answer': dict(correct=True, grade=8, first_error='', misconception='', feedback='Yes. Only the order counts.')},
139
+ dflt=dict(question='Why can you compare ranks from two systems, but not their scores?', answer='Ranks share one scale.', rubric=['ranks share one scale']))
140
+ t = Tutor(lr, fake, context=False)
141
+ ```
142
+
143
+ Ask the first question. `why` gives the reason for the choice. A new concept starts at depth 3 (explain).
144
+
145
+ ``` python
146
+ q = t.next()
147
+ assert q.why == 'next on the frontier: its prereqs are in place'
148
+ assert q.depth == 3
149
+ print(q.question)
150
+ ```
151
+
152
+ Answer with high confidence. A `solid` pass at depth 3 raises the floor to 3.
153
+
154
+ ``` python
155
+ r = t.answer(q.id, 'A rank is a position, and positions use the same scale in each list.', confidence=80)
156
+ assert r.outcome == 'solid'
157
+ assert lr.concept('ranking').floor == 3
158
+ ```
@@ -0,0 +1,129 @@
1
+ # abhyasa
2
+
3
+
4
+ <!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->
5
+
6
+ abhyasa (अभ्यास) means steady practice. It is a tutor that asks you questions and makes you do the thinking.
7
+ It never gives the answer first. You answer in text or by voice, and you can think aloud.
8
+ The tutor grades your reasoning, not only the result.
9
+ A learner model keeps the depth of each concept that you know, and the date of its next review.
10
+
11
+ ## How it works
12
+
13
+ Each question goes through one loop:
14
+
15
+ 1. Pick. Code chooses the next concept and the depth: a due review first, then the frontier. The app shows the reason.
16
+ 2. Ask. A model writes one question at that depth, with a hidden model answer and a rubric.
17
+ 3. Struggle. You answer. A wrong try gets the first error and a nudge, but no fix. You can ask for hints, one rung at a time.
18
+ 4. Grade. The model grades the answer and the think-aloud. Code grades choice questions and code exercises.
19
+ 5. Update. Correctness and confidence move the floor, the ceiling and the next review date of the concept.
20
+ 6. Teach. After a miss, the tutor teaches the smallest step that fixes the first error. Then it asks a check question.
21
+
22
+ | term | meaning |
23
+ |---------|-------------------------------------------------------------------|
24
+ | concept | one idea that you can be asked about |
25
+ | depth | 0 unseen, 1 recognise, 2 recall, 3 explain, 4 apply, 5 transfer |
26
+ | floor | the highest depth that you pass reliably |
27
+ | ceiling | the lowest depth that you failed |
28
+ | attempt | one answer, kept as a vault document |
29
+ | session | one sitting of questions, with a summary |
30
+ | hint | one rung of a ladder of six, from the least to the most revealing |
31
+ | lesson | the smallest step that the tutor teaches after a miss |
32
+
33
+ ## Install and run
34
+
35
+ Install the package and its dependencies:
36
+
37
+ ``` sh
38
+ uv sync
39
+ ```
40
+
41
+ Start the web app on <http://127.0.0.1:5077>:
42
+
43
+ ``` sh
44
+ uv run abhyasa
45
+ ```
46
+
47
+ The app listens on the loopback address only. These variables change the defaults:
48
+
49
+ | variable | what it sets | default |
50
+ |----|----|----|
51
+ | `ABHYASA_VAULT` | the vault file | the vault of vishalakshi |
52
+ | `ABHYASA_MODEL` | the rishi model for questions, grades, hints and lessons | `claude/sonnet` |
53
+ | `ABHYASA_STT` | the speech-to-text engine: `gemma` or `whisper` | `gemma` |
54
+
55
+ ## Surfaces
56
+
57
+ All surfaces use the same tutor and the same data.
58
+
59
+ ### Web app
60
+
61
+ The web app is made from blocks under `abhyasa/blocks`:
62
+
63
+ | block | what it does |
64
+ |----|----|
65
+ | `today` | the home page: what is due and what to do now |
66
+ | `session` | one sitting with the tutor: one question at a time, with hints, confidence and “I don’t know” |
67
+ | `voice` | a mic button for each text box; it turns speech into text on this machine |
68
+ | `concepts` | the list of concepts, and one page for each concept with its state |
69
+ | `graph` | the concept graph: a color for each floor, a ring when a review is due, arrows for prereqs |
70
+ | `search` | one search over the `learning` shelf and the other shelves of your vault |
71
+ | `add` | add concepts from a text, a vault document or vedicreader |
72
+
73
+ ### ramabana
74
+
75
+ The `/learn` extension gives the tutor in the terminal, beside your code.
76
+ To turn it on, make the file `~/.config/ramabana/extensions/abhyasa.py` with this line:
77
+
78
+ ``` python
79
+ from abhyasa.rama import setup
80
+ ```
81
+
82
+ ### Notebooks
83
+
84
+ In leela or Jupyter, [`learn`](https://vedicreader.github.io/abhyasa/nb.html#learn) shows a question card. [`exercise`](https://vedicreader.github.io/abhyasa/nb.html#exercise) gives a code exercise that its tests check in the kernel.
85
+
86
+ ``` python
87
+ from abhyasa.nb import learn, exercise
88
+ learn('reciprocal-rank-fusion')
89
+ ex = exercise('reciprocal-rank-fusion')
90
+ ```
91
+
92
+ ## Where the data is
93
+
94
+ All data is on the `learning` shelf of the vishalakshi vault.
95
+ Each concept, attempt and session is one vault document, so other tools can search it.
96
+ Tests use a vault in a temporary folder and never open your real vault.
97
+
98
+ ## Example
99
+
100
+ This example runs offline. [`temp_learning`](https://vedicreader.github.io/abhyasa/store.html#temp_learning) makes a shelf in a temporary vault, and [`Fake`](https://vedicreader.github.io/abhyasa/llm.html#fake) replaces the model.
101
+
102
+ ``` python
103
+ from abhyasa.store import temp_learning
104
+ from abhyasa.llm import Fake
105
+ from abhyasa.tutor import Tutor
106
+
107
+ lr = temp_learning()
108
+ lr.put('Ranking', 'Order items by a score.', 'ml')
109
+ fake = Fake({'You grade one answer': dict(correct=True, grade=8, first_error='', misconception='', feedback='Yes. Only the order counts.')},
110
+ dflt=dict(question='Why can you compare ranks from two systems, but not their scores?', answer='Ranks share one scale.', rubric=['ranks share one scale']))
111
+ t = Tutor(lr, fake, context=False)
112
+ ```
113
+
114
+ Ask the first question. `why` gives the reason for the choice. A new concept starts at depth 3 (explain).
115
+
116
+ ``` python
117
+ q = t.next()
118
+ assert q.why == 'next on the frontier: its prereqs are in place'
119
+ assert q.depth == 3
120
+ print(q.question)
121
+ ```
122
+
123
+ Answer with high confidence. A `solid` pass at depth 3 raises the floor to 3.
124
+
125
+ ``` python
126
+ r = t.answer(q.id, 'A rank is a position, and positions use the same scale in each list.', confidence=80)
127
+ assert r.outcome == 'solid'
128
+ assert lr.concept('ranking').floor == 3
129
+ ```
@@ -0,0 +1 @@
1
+ __version__ = "0.0.1"
@@ -0,0 +1,148 @@
1
+ # Autogenerated by nbdev
2
+
3
+ d = { 'settings': { 'branch': 'main',
4
+ 'doc_baseurl': '/abhyasa',
5
+ 'doc_host': 'https://vedicreader.github.io',
6
+ 'git_url': 'https://github.com/vedicreader/abhyasa',
7
+ 'lib_path': 'abhyasa'},
8
+ 'syms': { 'abhyasa.ground': { 'abhyasa.ground.Learning.connect': ('ground.html#learning.connect', 'abhyasa/ground.py'),
9
+ 'abhyasa.ground.Learning.stale_links': ('ground.html#learning.stale_links', 'abhyasa/ground.py'),
10
+ 'abhyasa.ground.VaultSource': ('ground.html#vaultsource', 'abhyasa/ground.py'),
11
+ 'abhyasa.ground.VaultSource.__init__': ('ground.html#vaultsource.__init__', 'abhyasa/ground.py'),
12
+ 'abhyasa.ground.VaultSource._doc': ('ground.html#vaultsource._doc', 'abhyasa/ground.py'),
13
+ 'abhyasa.ground.VaultSource.search': ('ground.html#vaultsource.search', 'abhyasa/ground.py'),
14
+ 'abhyasa.ground.VedicSource': ('ground.html#vedicsource', 'abhyasa/ground.py'),
15
+ 'abhyasa.ground.VedicSource.__init__': ('ground.html#vedicsource.__init__', 'abhyasa/ground.py'),
16
+ 'abhyasa.ground.VedicSource.dbs': ('ground.html#vedicsource.dbs', 'abhyasa/ground.py'),
17
+ 'abhyasa.ground.VedicSource.search': ('ground.html#vedicsource.search', 'abhyasa/ground.py'),
18
+ 'abhyasa.ground._clean': ('ground.html#_clean', 'abhyasa/ground.py'),
19
+ 'abhyasa.ground.cite': ('ground.html#cite', 'abhyasa/ground.py'),
20
+ 'abhyasa.ground.find_notebooks': ('ground.html#find_notebooks', 'abhyasa/ground.py'),
21
+ 'abhyasa.ground.fold': ('ground.html#fold', 'abhyasa/ground.py'),
22
+ 'abhyasa.ground.fts_query': ('ground.html#fts_query', 'abhyasa/ground.py'),
23
+ 'abhyasa.ground.ground': ('ground.html#ground', 'abhyasa/ground.py'),
24
+ 'abhyasa.ground.ground_text': ('ground.html#ground_text', 'abhyasa/ground.py'),
25
+ 'abhyasa.ground.index_main': ('ground.html#index_main', 'abhyasa/ground.py'),
26
+ 'abhyasa.ground.index_notebooks': ('ground.html#index_notebooks', 'abhyasa/ground.py'),
27
+ 'abhyasa.ground.index_ramabana': ('ground.html#index_ramabana', 'abhyasa/ground.py'),
28
+ 'abhyasa.ground.names': ('ground.html#names', 'abhyasa/ground.py'),
29
+ 'abhyasa.ground.nb_text': ('ground.html#nb_text', 'abhyasa/ground.py'),
30
+ 'abhyasa.ground.passage': ('ground.html#passage', 'abhyasa/ground.py'),
31
+ 'abhyasa.ground.ramabana_sessions': ('ground.html#ramabana_sessions', 'abhyasa/ground.py')},
32
+ 'abhyasa.llm': { 'abhyasa.llm.Fake': ('llm.html#fake', 'abhyasa/llm.py'),
33
+ 'abhyasa.llm.Fake.__call__': ('llm.html#fake.__call__', 'abhyasa/llm.py'),
34
+ 'abhyasa.llm.Fake.__init__': ('llm.html#fake.__init__', 'abhyasa/llm.py'),
35
+ 'abhyasa.llm.ask_json': ('llm.html#ask_json', 'abhyasa/llm.py'),
36
+ 'abhyasa.llm.parse_json': ('llm.html#parse_json', 'abhyasa/llm.py'),
37
+ 'abhyasa.llm.rishi_ask': ('llm.html#rishi_ask', 'abhyasa/llm.py')},
38
+ 'abhyasa.model': { 'abhyasa.model._back': ('model.html#_back', 'abhyasa/model.py'),
39
+ 'abhyasa.model.grow': ('model.html#grow', 'abhyasa/model.py'),
40
+ 'abhyasa.model.learned': ('model.html#learned', 'abhyasa/model.py'),
41
+ 'abhyasa.model.next_depth': ('model.html#next_depth', 'abhyasa/model.py'),
42
+ 'abhyasa.model.outcome': ('model.html#outcome', 'abhyasa/model.py'),
43
+ 'abhyasa.model.pick': ('model.html#pick', 'abhyasa/model.py'),
44
+ 'abhyasa.model.ready': ('model.html#ready', 'abhyasa/model.py'),
45
+ 'abhyasa.model.recall': ('model.html#recall', 'abhyasa/model.py'),
46
+ 'abhyasa.model.update': ('model.html#update', 'abhyasa/model.py')},
47
+ 'abhyasa.nb': { 'abhyasa.nb.Card': ('nb.html#card', 'abhyasa/nb.py'),
48
+ 'abhyasa.nb.Card.__init__': ('nb.html#card.__init__', 'abhyasa/nb.py'),
49
+ 'abhyasa.nb.Card._do': ('nb.html#card._do', 'abhyasa/nb.py'),
50
+ 'abhyasa.nb.Card._hint': ('nb.html#card._hint', 'abhyasa/nb.py'),
51
+ 'abhyasa.nb.Card._idk': ('nb.html#card._idk', 'abhyasa/nb.py'),
52
+ 'abhyasa.nb.Card._ipython_display_': ('nb.html#card._ipython_display_', 'abhyasa/nb.py'),
53
+ 'abhyasa.nb.Card._next': ('nb.html#card._next', 'abhyasa/nb.py'),
54
+ 'abhyasa.nb.Card._send': ('nb.html#card._send', 'abhyasa/nb.py'),
55
+ 'abhyasa.nb.Card._show': ('nb.html#card._show', 'abhyasa/nb.py'),
56
+ 'abhyasa.nb.Exercise': ('nb.html#exercise', 'abhyasa/nb.py'),
57
+ 'abhyasa.nb.Exercise.__init__': ('nb.html#exercise.__init__', 'abhyasa/nb.py'),
58
+ 'abhyasa.nb.Exercise._ipython_display_': ('nb.html#exercise._ipython_display_', 'abhyasa/nb.py'),
59
+ 'abhyasa.nb.Exercise.check': ('nb.html#exercise.check', 'abhyasa/nb.py'),
60
+ 'abhyasa.nb.Exercise.hint': ('nb.html#exercise.hint', 'abhyasa/nb.py'),
61
+ 'abhyasa.nb.Exercise.run': ('nb.html#exercise.run', 'abhyasa/nb.py'),
62
+ 'abhyasa.nb._esc': ('nb.html#_esc', 'abhyasa/nb.py'),
63
+ 'abhyasa.nb.code_prompt': ('nb.html#code_prompt', 'abhyasa/nb.py'),
64
+ 'abhyasa.nb.exercise': ('nb.html#exercise', 'abhyasa/nb.py'),
65
+ 'abhyasa.nb.fb_html': ('nb.html#fb_html', 'abhyasa/nb.py'),
66
+ 'abhyasa.nb.fb_text': ('nb.html#fb_text', 'abhyasa/nb.py'),
67
+ 'abhyasa.nb.learn': ('nb.html#learn', 'abhyasa/nb.py'),
68
+ 'abhyasa.nb.tutor': ('nb.html#tutor', 'abhyasa/nb.py'),
69
+ 'abhyasa.nb.write_exercise': ('nb.html#write_exercise', 'abhyasa/nb.py')},
70
+ 'abhyasa.rama': { 'abhyasa.rama._learning': ('rama.html#_learning', 'abhyasa/rama.py'),
71
+ 'abhyasa.rama._need': ('rama.html#_need', 'abhyasa/rama.py'),
72
+ 'abhyasa.rama._safe': ('rama.html#_safe', 'abhyasa/rama.py'),
73
+ 'abhyasa.rama._tutor': ('rama.html#_tutor', 'abhyasa/rama.py'),
74
+ 'abhyasa.rama.answer': ('rama.html#answer', 'abhyasa/rama.py'),
75
+ 'abhyasa.rama.due': ('rama.html#due', 'abhyasa/rama.py'),
76
+ 'abhyasa.rama.find': ('rama.html#find', 'abhyasa/rama.py'),
77
+ 'abhyasa.rama.hint': ('rama.html#hint', 'abhyasa/rama.py'),
78
+ 'abhyasa.rama.idk': ('rama.html#idk', 'abhyasa/rama.py'),
79
+ 'abhyasa.rama.learn': ('rama.html#learn', 'abhyasa/rama.py'),
80
+ 'abhyasa.rama.learning_due': ('rama.html#learning_due', 'abhyasa/rama.py'),
81
+ 'abhyasa.rama.learning_note': ('rama.html#learning_note', 'abhyasa/rama.py'),
82
+ 'abhyasa.rama.setup': ('rama.html#setup', 'abhyasa/rama.py'),
83
+ 'abhyasa.rama.show_q': ('rama.html#show_q', 'abhyasa/rama.py'),
84
+ 'abhyasa.rama.show_r': ('rama.html#show_r', 'abhyasa/rama.py'),
85
+ 'abhyasa.rama.teach': ('rama.html#teach', 'abhyasa/rama.py')},
86
+ 'abhyasa.sources': { 'abhyasa.sources._explain_concepts': ('sources.html#_explain_concepts', 'abhyasa/sources.py'),
87
+ 'abhyasa.sources._explain_name': ('sources.html#_explain_name', 'abhyasa/sources.py'),
88
+ 'abhyasa.sources._index': ('sources.html#_index', 'abhyasa/sources.py'),
89
+ 'abhyasa.sources._lesson_concepts': ('sources.html#_lesson_concepts', 'abhyasa/sources.py'),
90
+ 'abhyasa.sources._term_concepts': ('sources.html#_term_concepts', 'abhyasa/sources.py'),
91
+ 'abhyasa.sources._windows': ('sources.html#_windows', 'abhyasa/sources.py'),
92
+ 'abhyasa.sources.extract': ('sources.html#extract', 'abhyasa/sources.py'),
93
+ 'abhyasa.sources.extract_prompt': ('sources.html#extract_prompt', 'abhyasa/sources.py'),
94
+ 'abhyasa.sources.from_vault': ('sources.html#from_vault', 'abhyasa/sources.py'),
95
+ 'abhyasa.sources.from_vedicreader': ('sources.html#from_vedicreader', 'abhyasa/sources.py'),
96
+ 'abhyasa.sources.plain': ('sources.html#plain', 'abhyasa/sources.py')},
97
+ 'abhyasa.store': { 'abhyasa.store.Learning': ('store.html#learning', 'abhyasa/store.py'),
98
+ 'abhyasa.store.Learning.__init__': ('store.html#learning.__init__', 'abhyasa/store.py'),
99
+ 'abhyasa.store.Learning.__repr__': ('store.html#learning.__repr__', 'abhyasa/store.py'),
100
+ 'abhyasa.store.Learning._docs': ('store.html#learning._docs', 'abhyasa/store.py'),
101
+ 'abhyasa.store.Learning._row': ('store.html#learning._row', 'abhyasa/store.py'),
102
+ 'abhyasa.store.Learning.attempts': ('store.html#learning.attempts', 'abhyasa/store.py'),
103
+ 'abhyasa.store.Learning.concept': ('store.html#learning.concept', 'abhyasa/store.py'),
104
+ 'abhyasa.store.Learning.concepts': ('store.html#learning.concepts', 'abhyasa/store.py'),
105
+ 'abhyasa.store.Learning.drop': ('store.html#learning.drop', 'abhyasa/store.py'),
106
+ 'abhyasa.store.Learning.graph': ('store.html#learning.graph', 'abhyasa/store.py'),
107
+ 'abhyasa.store.Learning.link': ('store.html#learning.link', 'abhyasa/store.py'),
108
+ 'abhyasa.store.Learning.log_session': ('store.html#learning.log_session', 'abhyasa/store.py'),
109
+ 'abhyasa.store.Learning.put': ('store.html#learning.put', 'abhyasa/store.py'),
110
+ 'abhyasa.store.Learning.record': ('store.html#learning.record', 'abhyasa/store.py'),
111
+ 'abhyasa.store.Learning.search': ('store.html#learning.search', 'abhyasa/store.py'),
112
+ 'abhyasa.store.Learning.subjects': ('store.html#learning.subjects', 'abhyasa/store.py'),
113
+ 'abhyasa.store.Learning.update': ('store.html#learning.update', 'abhyasa/store.py'),
114
+ 'abhyasa.store._src': ('store.html#_src', 'abhyasa/store.py'),
115
+ 'abhyasa.store._union': ('store.html#_union', 'abhyasa/store.py'),
116
+ 'abhyasa.store.attempt_text': ('store.html#attempt_text', 'abhyasa/store.py'),
117
+ 'abhyasa.store.concept_text': ('store.html#concept_text', 'abhyasa/store.py'),
118
+ 'abhyasa.store.slugify': ('store.html#slugify', 'abhyasa/store.py'),
119
+ 'abhyasa.store.temp_learning': ('store.html#temp_learning', 'abhyasa/store.py')},
120
+ 'abhyasa.tutor': { 'abhyasa.tutor.Session': ('tutor.html#session', 'abhyasa/tutor.py'),
121
+ 'abhyasa.tutor.Session.__init__': ('tutor.html#session.__init__', 'abhyasa/tutor.py'),
122
+ 'abhyasa.tutor.Session._skip': ('tutor.html#session._skip', 'abhyasa/tutor.py'),
123
+ 'abhyasa.tutor.Session.answer': ('tutor.html#session.answer', 'abhyasa/tutor.py'),
124
+ 'abhyasa.tutor.Session.give_up': ('tutor.html#session.give_up', 'abhyasa/tutor.py'),
125
+ 'abhyasa.tutor.Session.hint': ('tutor.html#session.hint', 'abhyasa/tutor.py'),
126
+ 'abhyasa.tutor.Session.next': ('tutor.html#session.next', 'abhyasa/tutor.py'),
127
+ 'abhyasa.tutor.Session.summary': ('tutor.html#session.summary', 'abhyasa/tutor.py'),
128
+ 'abhyasa.tutor.Tutor': ('tutor.html#tutor', 'abhyasa/tutor.py'),
129
+ 'abhyasa.tutor.Tutor.__init__': ('tutor.html#tutor.__init__', 'abhyasa/tutor.py'),
130
+ 'abhyasa.tutor.Tutor._close': ('tutor.html#tutor._close', 'abhyasa/tutor.py'),
131
+ 'abhyasa.tutor.Tutor._grade': ('tutor.html#tutor._grade', 'abhyasa/tutor.py'),
132
+ 'abhyasa.tutor.Tutor._ground': ('tutor.html#tutor._ground', 'abhyasa/tutor.py'),
133
+ 'abhyasa.tutor.Tutor.answer': ('tutor.html#tutor.answer', 'abhyasa/tutor.py'),
134
+ 'abhyasa.tutor.Tutor.ask_on': ('tutor.html#tutor.ask_on', 'abhyasa/tutor.py'),
135
+ 'abhyasa.tutor.Tutor.give_up': ('tutor.html#tutor.give_up', 'abhyasa/tutor.py'),
136
+ 'abhyasa.tutor.Tutor.hint': ('tutor.html#tutor.hint', 'abhyasa/tutor.py'),
137
+ 'abhyasa.tutor.Tutor.next': ('tutor.html#tutor.next', 'abhyasa/tutor.py'),
138
+ 'abhyasa.tutor._concept_block': ('tutor.html#_concept_block', 'abhyasa/tutor.py'),
139
+ 'abhyasa.tutor._ready_q': ('tutor.html#_ready_q', 'abhyasa/tutor.py'),
140
+ 'abhyasa.tutor.grade_prompt': ('tutor.html#grade_prompt', 'abhyasa/tutor.py'),
141
+ 'abhyasa.tutor.hint_prompt': ('tutor.html#hint_prompt', 'abhyasa/tutor.py'),
142
+ 'abhyasa.tutor.public': ('tutor.html#public', 'abhyasa/tutor.py'),
143
+ 'abhyasa.tutor.question_prompt': ('tutor.html#question_prompt', 'abhyasa/tutor.py'),
144
+ 'abhyasa.tutor.teach_prompt': ('tutor.html#teach_prompt', 'abhyasa/tutor.py')},
145
+ 'abhyasa.voice': { 'abhyasa.voice._gemma': ('voice.html#_gemma', 'abhyasa/voice.py'),
146
+ 'abhyasa.voice._mlx_whisper': ('voice.html#_mlx_whisper', 'abhyasa/voice.py'),
147
+ 'abhyasa.voice.transcribe': ('voice.html#transcribe', 'abhyasa/voice.py'),
148
+ 'abhyasa.voice.wav_seconds': ('voice.html#wav_seconds', 'abhyasa/voice.py')}}}
@@ -0,0 +1,43 @@
1
+ "The web app: `fast_app`, the services, and `connect_all` over the blocks in `NAMES` order. Loopback only."
2
+ import argparse
3
+ from concurrent.futures import ThreadPoolExecutor
4
+ from fasthtml.common import *
5
+ from abhyasa.core import cfg, RouteOverrides, connect_all, serve_assets, HDRS
6
+ from abhyasa.core import hooks
7
+ from abhyasa.blocks import blocks
8
+ from abhyasa.store import Learning
9
+ from abhyasa.tutor import Tutor
10
+ from abhyasa.llm import rishi_ask
11
+ from abhyasa.ground import VaultSource, VedicSource
12
+
13
+ __all__ = ['mk_app', 'main']
14
+
15
+ FAVICON = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><circle cx="8" cy="8" r="6" fill="#7a4a2e"/></svg>'
16
+
17
+ def mk_app(learning:Learning, ask=None, transcribe=None, names=None, sources=None):
18
+ "The app over `learning`; `ask` is the model, `transcribe` the speech to text, `sources` ground the tutor, `names` the blocks to connect."
19
+ if transcribe is None: from abhyasa.voice import transcribe
20
+ ask = ask or rishi_ask()
21
+ hooks.clear(); RouteOverrides.nav.clear()
22
+ app, rt = fast_app(hdrs=HDRS(), pico=False, default_hdrs=False) # htmx and fasthtml.js come from core/vendor
23
+ if sources is None: sources = [VaultSource(learning), *([vs] if (vs := VedicSource()).dbs() else [])]
24
+ svc = dict(learning=learning, tutor=Tutor(learning, ask, sources=sources), ask=ask, transcribe=transcribe,
25
+ pool=ThreadPoolExecutor(2), sessions={}, sources=sources)
26
+ app.state.blocks = connect_all(app, blocks(*([names] if names else [])), **svc)
27
+ app.state.svc = svc
28
+ serve_assets(app)
29
+ app.get('/health')(lambda: JSONResponse(dict(status='ok')))
30
+ app.get('/favicon.ico')(lambda: Response(FAVICON, media_type='image/svg+xml'))
31
+ return app
32
+
33
+ def main():
34
+ "Serve the app on loopback."
35
+ import uvicorn
36
+ p = argparse.ArgumentParser(description='abhyasa: a learning harness that makes you think')
37
+ p.add_argument('--port', type=int, default=cfg.port)
38
+ p.add_argument('--vault', default=cfg.vault, help='the vault file; default $ABHYASA_VAULT, then vishalakshi\'s')
39
+ p.add_argument('--model', default=None, help='a rishi model name; default $ABHYASA_MODEL or claude/sonnet')
40
+ a = p.parse_args()
41
+ lr = Learning(a.vault)
42
+ print(f'abhyasa on http://{cfg.host}:{a.port} ({lr})')
43
+ uvicorn.run(mk_app(lr, rishi_ask(a.model)), host=cfg.host, port=a.port, log_level='warning')