diyclassics Claude Opus 4.8 (1M context) commited on
Commit
67d78de
·
1 Parent(s): b33dd8b

feat: re-land vocab demo on main via PyPI latincy-vocab

Browse files

Restores pages/14_vocab_demo.py (removed from main in 1d0c8f9 pending the
PyPI publish) and depends on latincy-vocab>=0.1.0 from PyPI instead of the
stale file://../latincy-vocab local ref that lived on the vocab-demo branch.
Cherry-picked the demo file only; the old vocab-demo branch is a stale
ancestor of main and is not merged. Verified the PyPI wheel ships the
vocabbuilder package the demo imports; app.py already links the page.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Files changed (2) hide show
  1. pages/14_vocab_demo.py +167 -0
  2. requirements.txt +1 -0
pages/14_vocab_demo.py ADDED
@@ -0,0 +1,167 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+
3
+ from dcc_helpers import is_dcc_core
4
+ from lexicon_helpers import load_lexicon_pipeline
5
+ from vocabbuilder.core.config import PipelineConfig
6
+ from vocabbuilder.core.models import VocabList
7
+ from vocabbuilder.data.gloss_provider import GlossProvider
8
+ from vocabbuilder.processors.vocab_core import build_vocab_list
9
+ from vocabbuilder.utils.normalization import to_u_form
10
+
11
+ st.set_page_config(page_title="Vocab Builder Demo", layout="wide")
12
+ st.sidebar.header("Vocab Builder Demo")
13
+
14
+ st.title("Latin Vocabulary Builder")
15
+ st.markdown(
16
+ """
17
+ Generate a textbook-style vocabulary list from any Latin passage using
18
+ [latincy-vocab](https://github.com/latincy/latincy-vocab) —
19
+ lemmatized, glossed, and sortable.
20
+ """
21
+ )
22
+
23
+ DEFAULT_TEXT = (
24
+ "Haec narrantur a poetis de Perseo. Perseus filius erat Iovis, maximi "
25
+ "deorum; avus eius Acrisius appellabatur. Acrisius volebat Perseum nepotem "
26
+ "suum necare; nam propter oraculum puerum timebat."
27
+ )
28
+
29
+
30
+ @st.cache_resource(show_spinner="Loading vocabulary pipeline…")
31
+ def load_resources():
32
+ # Use the shared lexicon pipeline so token._.lexicon is populated,
33
+ # giving us citation forms (principal parts, gen+gender, -a,-um).
34
+ nlp = load_lexicon_pipeline("la_core_web_lg")
35
+ config = PipelineConfig(spacy_model="la_core_web_lg", spacy_disable=[])
36
+ config.resolve_data_paths()
37
+ gloss_provider = GlossProvider(config.glosses_path) if config.glosses_path else None
38
+ return nlp, config, gloss_provider
39
+
40
+
41
+ nlp, _config, _gloss_provider = load_resources()
42
+
43
+
44
+ def process(text: str) -> VocabList:
45
+ doc = nlp(text)
46
+ vocab = build_vocab_list(doc, _config)
47
+ if _gloss_provider:
48
+ for entry in vocab:
49
+ result = _gloss_provider.lookup(entry.lemma, entry.pos, _config.max_glosses)
50
+ if result:
51
+ entry.glosses = result.glosses
52
+ entry.display_lemma = result.display_lemma
53
+ else:
54
+ entry.display_lemma = _gloss_provider.get_display_lemma(entry.lemma)
55
+ return vocab
56
+
57
+
58
+ tab1, tab2 = st.tabs(["Vocab List", "About"])
59
+
60
+ with tab1:
61
+ col_input, col_vocab = st.columns([2, 3])
62
+
63
+ with col_input:
64
+ text = st.text_area(
65
+ "Latin text:",
66
+ value=DEFAULT_TEXT,
67
+ height=280,
68
+ key="vocab_text",
69
+ )
70
+ run = st.button("Build Vocabulary List", key="vocab_run")
71
+
72
+ if run:
73
+ with st.spinner("Processing…"):
74
+ vocab: VocabList = process(text)
75
+ st.session_state["vocab_list"] = vocab
76
+ st.session_state["vocab_sort"] = "alpha"
77
+
78
+ with col_vocab:
79
+ if "vocab_list" in st.session_state:
80
+ vocab: VocabList = st.session_state["vocab_list"]
81
+
82
+ s1, s2, s3 = st.columns(3)
83
+ with s1:
84
+ if st.button("Alphabetical", key="sort_alpha", use_container_width=True):
85
+ st.session_state["vocab_sort"] = "alpha"
86
+ with s2:
87
+ if st.button("First Occurrence", key="sort_occ", use_container_width=True):
88
+ st.session_state["vocab_sort"] = "occurrence"
89
+ with s3:
90
+ if st.button("Frequency", key="sort_freq", use_container_width=True):
91
+ st.session_state["vocab_sort"] = "freq"
92
+
93
+ hide_dcc = st.checkbox(
94
+ "Hide DCC Core words",
95
+ value=False,
96
+ key="hide_dcc",
97
+ help="Remove high-frequency words from the DCC Core Latin Vocabulary",
98
+ )
99
+
100
+ sort_key = st.session_state.get("vocab_sort", "alpha")
101
+ if sort_key == "alpha":
102
+ sorted_vocab = vocab.by_alpha()
103
+ elif sort_key == "occurrence":
104
+ sorted_vocab = vocab.by_first_occurrence()
105
+ else:
106
+ sorted_vocab = vocab.by_frequency()
107
+
108
+ visible = [e for e in sorted_vocab if e.headword and e.headword[0].isalpha()]
109
+ dcc_count = sum(1 for e in visible if is_dcc_core(to_u_form(e.lemma)))
110
+ new_count = len(visible) - dcc_count
111
+
112
+ if hide_dcc:
113
+ visible = [e for e in visible if not is_dcc_core(to_u_form(e.lemma))]
114
+
115
+ st.caption(
116
+ f"{len(visible)} entries shown · {new_count} new, {dcc_count} DCC core · "
117
+ "assembled from probabilistic LatinCy pipeline annotations — verify before use"
118
+ )
119
+
120
+ lines = []
121
+ for entry in visible:
122
+ in_dcc = is_dcc_core(to_u_form(entry.lemma))
123
+ hw_text = entry.headword.lower()
124
+ hw = f"<strong>{hw_text}</strong>"
125
+ pm = f" <em>{entry.pos_marker}</em>" if entry.pos_marker else ""
126
+ gloss = f", {entry.short_gloss}" if entry.short_gloss else ""
127
+ freq = f" <em>×{entry.frequency}</em>" if entry.frequency > 1 else ""
128
+ dcc_tag = (
129
+ " <span style='color:#09a3d5;font-size:0.75em'>dcc</span>"
130
+ if in_dcc else ""
131
+ )
132
+ lines.append(
133
+ f"<p style='margin:0 0 2px 0;line-height:1.4'>"
134
+ f"{hw}{pm}{gloss}{freq}{dcc_tag}</p>"
135
+ )
136
+
137
+ st.markdown("".join(lines), unsafe_allow_html=True)
138
+
139
+ with tab2:
140
+ st.markdown(
141
+ """
142
+ ### About
143
+
144
+ This demo uses **latincy-vocab** (`vocabbuilder.VocabPipeline`) to process a Latin
145
+ passage end-to-end:
146
+
147
+ 1. **spaCy** tokenizes and annotates (lemma, POS, morphology)
148
+ 2. **latincy-lexicon** supplies Whitaker's Words glosses, display lemmas, and
149
+ citation forms (principal parts for verbs; nominative, genitive, and gender
150
+ for nouns; nominative with -a, -um endings for adjectives)
151
+ 3. `VocabList` deduplicates by lemma+POS and exposes three orderings
152
+
153
+ The **Hide DCC Core words** checkbox filters out the ~1,000 high-frequency lemmas
154
+ from the [DCC Core Latin Vocabulary](https://dcc.dickinson.edu/vocab/core-vocabulary),
155
+ leaving only words a student would need to look up.
156
+
157
+ > **Note:** vocabulary entries are assembled from probabilistic LatinCy pipeline
158
+ > annotations and should be verified before use in publication or teaching.
159
+
160
+ The default text is §1 of Ritchie's *Fabulae Faciles* (1902).
161
+
162
+ **Sort modes**
163
+ - *Alphabetical* — traditional glossary order
164
+ - *First Occurrence* — passage reading order (as in textbook footnotes)
165
+ - *Frequency* — most common lemmas first
166
+ """
167
+ )
requirements.txt CHANGED
@@ -10,5 +10,6 @@ spacy-streamlit==1.0.6
10
  latincy-diacritics @ https://huggingface.co/latincy/latincy-diacritics/resolve/main/latincy_diacritics-0.1.0-py3-none-any.whl
11
  latincy-preprocess>=0.2.0
12
  latincy-lexicon>=0.5.0
 
13
  streamlit==1.45.1
14
  watchdog==6.0.0
 
10
  latincy-diacritics @ https://huggingface.co/latincy/latincy-diacritics/resolve/main/latincy_diacritics-0.1.0-py3-none-any.whl
11
  latincy-preprocess>=0.2.0
12
  latincy-lexicon>=0.5.0
13
+ latincy-vocab>=0.1.0
14
  streamlit==1.45.1
15
  watchdog==6.0.0