API reference¶
Corpus¶
The corpus store: several CoNLL-U sources behind one search and edit API.
This is the API the MCP server exposes, and it can be used directly from Python (e.g. by llmaligner) without MCP. All public methods are thread-safe.
- mcparser.corpus.store.COLUMN_ALIASES = {'arc': 'head', 'features': 'feats', 'morphology': 'feats', 'pos': 'upos', 'relation': 'deprel', 'segmentation': 'form', 'tokenisation': 'form', 'tokenization': 'form'}¶
Friendly names accepted wherever a column is expected.
- class mcparser.corpus.store.CorpusStore(autosave=True)[source]¶
Bases:
objectA set of named CoNLL-U sources.
Read-only sources (
ro) hold trusted examples such as treebank training data. Writable sources (rw) hold predictions that agents edit; every change is saved immediately whenautosaveis on.- Parameters:
autosave (bool)
- add_text_corpus(name, paths)[source]¶
Register plain-text files as a corpus of unannotated evidence (indexed on first use).
- Parameters:
name (str)
paths (list[Path | str])
- Return type:
- text_corpus(name=None)[source]¶
The named corpus, or the only one when no name is given.
- Parameters:
name (str | None)
- Return type:
- corpus_find(form, corpus=None, loose=False, examples=3, width=6, neighbours=5, affixed=5)[source]¶
How
formis used in an unannotated corpus (seeTextCorpus.find()).- Parameters:
form (str)
corpus (str | None)
loose (bool)
examples (int)
width (int)
neighbours (int)
affixed (int)
- Return type:
- save(source=None)[source]¶
Save one writable source or alignment set, or all of them. Returns the names saved.
- Parameters:
source (str | None)
- Return type:
list[str]
- reload(source)[source]¶
Reload a source from disk, discarding unsaved changes.
- Parameters:
source (str)
- Return type:
- get_context(source, sent_id, before=2, after=2)[source]¶
The sentence and its neighbours in file order.
- Parameters:
source (str)
sent_id (str)
before (int)
after (int)
- Return type:
list[SentenceSummary]
- find_tokens(form=None, lemma=None, upos=None, xpos=None, feats=None, deprel=None, root=None, suffix=None, previous_form=None, next_form=None, previous_upos=None, next_upos=None, loose=False, sources=None, min_confidence=0.0, exclude_sentence=None, group_by=None, limit=10, window=3)[source]¶
Find words matching every given criterion.
featsmatches words having at least those features.rootmatches the MISC root attribute (Root=by default).suffixmatches word endings, for analogy with unseen forms;previous_formandnext_formrequire the neighbouring words (these three ignore case and diacritics),previous_uposandnext_upostheir UPOS. Hits are ordered by confidence (in LEMMA/UPOS/XPOS/FEATS), andgroupscounts all matches.- Parameters:
form (str | None)
lemma (str | None)
upos (str | None)
xpos (str | None)
feats (str | None)
deprel (str | None)
root (str | None)
suffix (str | None)
previous_form (str | None)
next_form (str | None)
previous_upos (str | None)
next_upos (str | None)
loose (bool)
sources (list[str] | None)
min_confidence (float)
exclude_sentence (str | None)
group_by (list[str] | None)
limit (int)
window (int)
- Return type:
- analyse_form(form, loose=False, previous_form=None, next_form=None, previous_upos=None, next_upos=None, sources=None, min_confidence=0.0, exclude_sentence=None, limit=5)[source]¶
How a word form is analysed (optionally next to given words or tags): counts of (lemma, upos, xpos, feats) plus examples.
- Parameters:
form (str)
loose (bool)
previous_form (str | None)
next_form (str | None)
previous_upos (str | None)
next_upos (str | None)
sources (list[str] | None)
min_confidence (float)
exclude_sentence (str | None)
limit (int)
- Return type:
- find_multiword(surface, loose=False, sources=None, limit=10)[source]¶
How a surface token is split into words, and how often it is left unsplit.
- Parameters:
surface (str)
loose (bool)
sources (list[str] | None)
limit (int)
- Return type:
- find_arcs(dependent_form=None, dependent_lemma=None, dependent_upos=None, head_form=None, head_lemma=None, head_upos=None, deprel=None, direction=None, loose=False, sources=None, min_confidence=0.0, exclude_sentence=None, limit=10, window=3)[source]¶
Find dependency arcs by properties of the dependent, the head and the relation.
deprelwithout a subtype (nmod) also matches subtypes (nmod:gen). Usehead_upos="ROOT"to find root words.directionis the side of the dependent on which the head lies.- Parameters:
dependent_form (str | None)
dependent_lemma (str | None)
dependent_upos (str | None)
head_form (str | None)
head_lemma (str | None)
head_upos (str | None)
deprel (str | None)
direction (Literal['left', 'right', 'root'] | None)
loose (bool)
sources (list[str] | None)
min_confidence (float)
exclude_sentence (str | None)
limit (int)
window (int)
- Return type:
- search_sentences(query, sources=None, limit=5, min_confidence=0.0, exclude_sentence=None)[source]¶
BM25 search over sentence text (accent- and case-insensitive).
- Parameters:
query (str)
sources (list[str] | None)
limit (int)
min_confidence (float)
exclude_sentence (str | None)
- Return type:
list[SentenceHit]
- allowed_values(column, upos=None, sources=None, limit=100)[source]¶
Values of UPOS, XPOS, DEPREL or FEATS with their frequencies.
By default only read-only (trusted) sources are counted. With
upos, XPOS/DEPREL/FEATS are restricted to words with that UPOS.- Parameters:
column (str)
upos (str | None)
sources (list[str] | None)
limit (int)
- Return type:
list[ValueCount]
- feature_inventory(upos=None, sources=None)[source]¶
Each morphological feature and its values (optionally for one UPOS).
- Parameters:
upos (str | None)
sources (list[str] | None)
- Return type:
dict[str, list[ValueCount]]
- feature_conventions(upos, min_count=20, min_share=0.1, sources=None)[source]¶
Where the trusted data writes a combined feature value (
Gender=Masc,Neut) forupos.One entry per (Case, Number, feature) with at least
min_countexamples of which at leastmin_sharecarry a combined value, most frequent first.- Parameters:
upos (str)
min_count (int)
min_share (float)
sources (list[str] | None)
- Return type:
list[FeatureConvention]
- relation_stats(dependent_lemma, dependent_upos, head_lemma, head_upos, direction, min_total=3, sources=None)[source]¶
DEPREL counts for arcs like this one, from the most specific level with
min_totalarcs.Levels: dependent lemma + head lemma; dependent lemma + head UPOS + direction; dependent UPOS + head UPOS + direction. Use
Noneheads for the root. Counts come from the read-only sources by default.- Parameters:
dependent_lemma (str)
dependent_upos (str)
head_lemma (str | None)
head_upos (str | None)
direction (Literal['left', 'right', 'root'])
min_total (int)
sources (list[str] | None)
- Return type:
- upos_for_function(lemma, deprel, sources=None)[source]¶
UPOS values used for
lemmain relationdeprel(subtype ignored); default: read-only sources.- Parameters:
lemma (str)
deprel (str)
sources (list[str] | None)
- Return type:
list[ValueCount]
- deprels_for_voice(deprel, passive, sources=None)[source]¶
DEPREL values (with subtype) used for
deprel(subtype ignored) on dependents of a passive or active head.- Parameters:
deprel (str)
passive (bool)
sources (list[str] | None)
- Return type:
list[ValueCount]
- fixed_expressions(source, sent_id, min_count=3, min_share=0.5, sources=None)[source]¶
Word sequences in a sentence that the trusted data annotates as fixed expressions.
A sequence is reported if it is fixed at least
min_counttimes and in at leastmin_shareof its occurrences.- Parameters:
source (str)
sent_id (str)
min_count (int)
min_share (float)
sources (list[str] | None)
- Return type:
list[FixedExpression]
- single_relations(min_heads=20, max_share=0.01, sources=None)[source]¶
Relations a word almost never has twice (e.g.
nsubj), from the trusted data.Returns
{deprel: (words with such a dependent, words with two or more)}for relations seen on at leastmin_headswords and repeated on at mostmax_shareof them.- Parameters:
min_heads (int)
max_share (float)
sources (list[str] | None)
- Return type:
dict[str, tuple[int, int]]
- unchained_relations(min_count=100, max_share=0.05, sources=None)[source]¶
Relations that (almost) never attach to a word with the same relation, from the trusted data.
Returns
{deprel: (words with the relation, of which chained)}, e.g.conjattaches to the first conjunct rather than to the previousconj.- Parameters:
min_count (int)
max_share (float)
sources (list[str] | None)
- Return type:
dict[str, tuple[int, int]]
- xpos_for_tags(upos, feats, sources=None)[source]¶
XPOS values used with exactly this UPOS and FEATS (default: read-only sources).
- Parameters:
upos (str)
feats (str)
sources (list[str] | None)
- Return type:
list[ValueCount]
- known_values()[source]¶
XPOS, DEPREL and feature values seen in read-only sources (empty if none).
- Return type:
dict[str, set[str]]
- validate(source, sent_id=None, limit=50)[source]¶
Validate one sentence or the whole source; errors are listed before warnings.
- Parameters:
source (str)
sent_id (str | None)
limit (int)
- Return type:
- low_confidence(source, threshold=0.5, columns=None, sent_id=None, limit=50)[source]¶
Word cells whose confidence is below
threshold, least confident first.- Parameters:
source (str)
threshold (float)
columns (list[str] | None)
sent_id (str | None)
limit (int)
- Return type:
- get_history(source, sent_id=None, token_id=None, limit=20)[source]¶
Edits to a source, newest first.
- Parameters:
source (str)
sent_id (str | None)
token_id (str | None)
limit (int)
- Return type:
list[Edit]
- update_cells(source, sent_id, updates, agent='agent', dry_run=False)[source]¶
Apply several cell updates to one sentence, all or nothing.
An update with a
valuereplaces the cell and sets its confidence toconfidence(or clears it, falling back to the source default). An update with onlyconfidencere-scores the cell. MISC edits never changeConf. Withdry_runthe updates are checked and the result (including validation issues) is returned, but nothing is changed.- Parameters:
source (str)
sent_id (str)
updates (list[CellUpdate | dict[str, Any]])
agent (str)
dry_run (bool)
- Return type:
- score_cells(source, sent_id, scores, reason='', agent='agent', dry_run=False)[source]¶
Set confidences compactly:
{"3:upos": 0.9, "4:*": 0.95}."ID:*"(or just"ID") scores every scored column of the word except required columns that are still empty (_), which stay unscored. Specific keys override a wildcard for the same word.- Parameters:
source (str)
sent_id (str)
scores (dict[str, float])
reason (str)
agent (str)
dry_run (bool)
- Return type:
- set_cell(source, sent_id, token_id, column, value, confidence=None, reason='', agent='agent')[source]¶
Set one cell. See
update_cells().- Parameters:
source (str)
sent_id (str)
token_id (str)
column (str)
value (str)
confidence (float | None)
reason (str)
agent (str)
- Return type:
- split_word(source, sent_id, token_id, forms, multiword=False, head_index=0, confidence=None, reason='', agent='agent', flag_head=True)[source]¶
Split a word. See
mcparser.corpus.segmentation.split_word().Like the other word-changing edits, this raises a sentence-level flag for the head agent unless
flag_headis false.- Parameters:
source (str)
sent_id (str)
token_id (str)
forms (list[str])
multiword (bool)
head_index (int)
confidence (float | None)
reason (str)
agent (str)
flag_head (bool)
- Return type:
- merge_words(source, sent_id, token_ids, form=None, confidence=None, reason='', agent='agent')[source]¶
Merge adjacent words. See
mcparser.corpus.segmentation.merge_words().- Parameters:
source (str)
sent_id (str)
token_ids (list[str])
form (str | None)
confidence (float | None)
reason (str)
agent (str)
- Return type:
- delete_word(source, sent_id, token_id, reason='', agent='agent')[source]¶
Delete a word; its dependents move to its head.
- Parameters:
source (str)
sent_id (str)
token_id (str)
reason (str)
agent (str)
- Return type:
- insert_word(source, sent_id, after, form, confidence=None, reason='', agent='agent')[source]¶
Insert an unannotated word after word
after("0"= at the start).- Parameters:
source (str)
sent_id (str)
after (str)
form (str)
confidence (float | None)
reason (str)
agent (str)
- Return type:
- add_multiword(source, sent_id, start, end, form, reason='', agent='agent')[source]¶
Group words
start..``end`` under a multiword surface token.- Parameters:
source (str)
sent_id (str)
start (int)
end (int)
form (str)
reason (str)
agent (str)
- Return type:
- remove_multiword(source, sent_id, range_id, reason='', agent='agent')[source]¶
Remove a multiword token line, keeping its words.
- Parameters:
source (str)
sent_id (str)
range_id (str)
reason (str)
agent (str)
- Return type:
- flag_issue(source, sent_id, column, reason, token_id=None, suggestion=None, agent='agent')[source]¶
Ask the agent responsible for
columnto review a word or sentence.- Parameters:
source (str)
sent_id (str)
column (str)
reason (str)
token_id (str | None)
suggestion (str | None)
agent (str)
- Return type:
- resolve_flag(source, flag_id, status='resolved', note='', agent='agent')[source]¶
Close a flag as
resolved(acted on) ordismissed(no change needed).- Parameters:
source (str)
flag_id (int)
status (Literal['resolved', 'dismissed'])
note (str)
agent (str)
- Return type:
- add_alignment(name, source_a, source_b, path, mode=SourceMode.READ_WRITE, default_confidence=None)[source]¶
Add word alignments between two loaded sources (sentences paired by
sent_id).- Parameters:
name (str)
source_a (str)
source_b (str)
path (Path | str)
mode (SourceMode | str)
default_confidence (float | None)
- Return type:
- get_parallel(alignment, sent_id)[source]¶
A sentence pair: both sentences as CoNLL-U, the links, and the words without links.
- Parameters:
alignment (str)
sent_id (str)
- Return type:
- set_links(alignment, sent_id, links, confidence=None, replace=True, reason='', agent='agent', dry_run=False)[source]¶
Set the links of a sentence pair.
linksis Ugarit text ("1-1S 2-3P") or a list of links;confidenceapplies to links that have none. Withreplacethe pair’s links are replaced, otherwise they are added (a link with the same word ids is updated). Word ids must exist in both sentences. Each new or changed link recordsagent; a link sent again unchanged (same S/P and confidence) keeps the agent that last changed it.- Parameters:
alignment (str)
sent_id (str)
links (str | list[Link | dict[str, Any]])
confidence (float | None)
replace (bool)
reason (str)
agent (str)
dry_run (bool)
- Return type:
- remove_links(alignment, sent_id, links, reason='', agent='agent')[source]¶
Remove links (Ugarit text; S/P suffixes are ignored) from a sentence pair.
- Parameters:
alignment (str)
sent_id (str)
links (str)
reason (str)
agent (str)
- Return type:
- translation_candidates(lemma, side='a', alignments=None, loose=False, min_confidence=0.0, exclude_sentence=None, limit=10)[source]¶
What
lemma(of source A, or B withside="b") is linked to, over the aligned sentence pairs.A word’s lemma falls back to its form when the lemma is empty. Only links with at least
min_confidencecount.- Parameters:
lemma (str)
side (Literal['a', 'b'])
alignments (list[str] | None)
loose (bool)
min_confidence (float)
exclude_sentence (str | None)
limit (int)
- Return type:
One CoNLL-U file loaded into memory, read-only or writable.
- mcparser.corpus.source.SCORED_COLUMNS = ('form', 'lemma', 'upos', 'xpos', 'feats', 'head', 'deprel')¶
Columns that count towards a sentence’s confidence (DEPS is optional in UD).
- mcparser.corpus.source.REQUIRED_COLUMNS = ('lemma', 'upos', 'head', 'deprel')¶
Columns where
_means “not annotated yet” (in XPOS, FEATS and DEPS it can be a real value).
- mcparser.corpus.source.atomic_write(path, text)[source]¶
Write
texttopathvia a temporary file and an atomic rename.- Parameters:
path (Path)
text (str)
- Return type:
None
- class mcparser.corpus.source.CorpusSource(name, path, mode=SourceMode.READ_ONLY, default_confidence=None, root_key='Root')[source]¶
Bases:
objectA CoNLL-U file plus, for writable sources, its review flags and history.
Writable sources keep two sidecar files next to the CoNLL-U file:
<file>.review.json(flags) and<file>.history.jsonl(edit log).- Parameters:
name (str)
path (Path | str)
mode (SourceMode | str)
default_confidence (float | None)
root_key (str)
A small, lossless CoNLL-U model.
Each cell is kept as the raw string from the file so that a read/write round trip never reorders features or rewrites values. Helpers parse FEATS and MISC into dictionaries when structure is needed.
- mcparser.corpus.conllu.COLUMNS = ('id', 'form', 'lemma', 'upos', 'xpos', 'feats', 'head', 'deprel', 'deps', 'misc')¶
The ten CoNLL-U columns in file order.
- mcparser.corpus.conllu.EDITABLE_COLUMNS = ('form', 'lemma', 'upos', 'xpos', 'feats', 'head', 'deprel', 'deps', 'misc')¶
Columns that may be changed with a cell edit (everything except
id).
- mcparser.corpus.conllu.ANNOTATION_COLUMNS = ('form', 'lemma', 'upos', 'xpos', 'feats', 'head', 'deprel', 'deps')¶
Columns that carry a confidence score.
formstands for segmentation.
- mcparser.corpus.conllu.UPOS_TAGS = ('ADJ', 'ADP', 'ADV', 'AUX', 'CCONJ', 'DET', 'INTJ', 'NOUN', 'NUM', 'PART', 'PRON', 'PROPN', 'PUNCT', 'SCONJ', 'SYM', 'VERB', 'X')¶
The universal part-of-speech tags.
- mcparser.corpus.conllu.parse_pairs(value, separator='|')[source]¶
Parse
Key=Value|Key2=Value2into an ordered dict.Items without
=map toNone._and the empty string give{}.- Parameters:
value (str)
separator (str)
- Return type:
dict[str, str | None]
- mcparser.corpus.conllu.format_pairs(pairs, separator='|')[source]¶
Inverse of
parse_pairs(). An empty dict gives_.- Parameters:
pairs (dict[str, str | None])
separator (str)
- Return type:
str
- mcparser.corpus.conllu.format_feats(pairs)[source]¶
Format FEATS with keys sorted case-insensitively, as UD requires.
- Parameters:
pairs (dict[str, str | None])
- Return type:
str
- mcparser.corpus.conllu.check_cell(column, value)[source]¶
Validate a cell value for
columnand return it (""becomes_).- Parameters:
column (str)
value (str)
- Return type:
str
- class mcparser.corpus.conllu.Token(*, id, form='_', lemma='_', upos='_', xpos='_', feats='_', head='_', deprel='_', deps='_', misc='_')[source]¶
Bases:
BaseModelOne CoNLL-U line: a word, a multiword range (
3-4) or an empty node (5.1).- Parameters:
id (str)
form (str)
lemma (str)
upos (str)
xpos (str)
feats (str)
head (str)
deprel (str)
deps (str)
misc (str)
- span()[source]¶
The
(start, end)word ids covered by a multiword token.- Return type:
tuple[int, int]
- set_misc_value(key, value)[source]¶
Set (or with
Noneremove) one MISC attribute, keeping the others.- Parameters:
key (str)
value (str | None)
- Return type:
None
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class mcparser.corpus.conllu.Sentence(*, comments=<factory>, tokens=<factory>)[source]¶
Bases:
BaseModelA sentence: its comment lines (without the leading
#) and its token lines.- Parameters:
comments (list[str])
tokens (list[Token])
- meta(key)[source]¶
The value of a
# key = valuecomment, if present.- Parameters:
key (str)
- Return type:
str | None
- property text: str¶
The
# textcomment, or the surface forms joined if it is missing.
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- mcparser.corpus.conllu.surface_text(tokens)[source]¶
Rebuild running text from surface tokens, honouring
SpaceAfter=No.- Parameters:
tokens (Iterable[Token])
- Return type:
str
- mcparser.corpus.conllu.parse_conllu(text)[source]¶
Parse CoNLL-U text into sentences.
- Parameters:
text (str)
- Return type:
list[Sentence]
- mcparser.corpus.conllu.format_conllu(sentences)[source]¶
Serialise sentences, each followed by a blank line.
- Parameters:
sentences (Iterable[Sentence])
- Return type:
str
Per-cell confidence scores stored in the MISC column.
Scores are kept in a single Conf attribute, one column:score pair per
annotated column, e.g. Conf=upos:0.95,lemma:0.80,head:0.40. form stands
for segmentation. A column without a score falls back to the source’s default
confidence (1.0 for read-only training data, 0.0 for predictions).
- mcparser.corpus.confidence.read_confidence(token)[source]¶
The explicit scores on
token. Malformed entries are ignored.- Parameters:
token (Token)
- Return type:
dict[str, float]
- mcparser.corpus.confidence.write_confidence(token, scores)[source]¶
Replace the scores on
token; an empty dict removesConf.- Parameters:
token (Token)
scores (dict[str, float])
- Return type:
None
- mcparser.corpus.confidence.set_confidence(token, column, value)[source]¶
Set one column’s score, or remove it with
None.- Parameters:
token (Token)
column (str)
value (float | None)
- Return type:
None
- mcparser.corpus.confidence.strip_confidence(misc, original)[source]¶
Return
miscwith itsConfreplaced by the one inoriginal.Used when MISC is edited directly so that confidence scores can only be changed through the confidence API.
- Parameters:
misc (str)
original (str)
- Return type:
str
Segmentation edits: splitting, merging, inserting and deleting words, and adding or removing multiword tokens.
Every edit works on a _Graph in which HEAD and DEPS point at token
objects rather than ids. After the edit the graph is renumbered, so heads,
enhanced dependencies, multiword ranges and empty nodes stay consistent.
- class mcparser.corpus.segmentation.SegmentationChange(*, id_map, new_ids)[source]¶
Bases:
BaseModelWhat a segmentation edit did to the sentence’s token ids.
- Parameters:
id_map (dict[str, str | None])
new_ids (list[str])
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- mcparser.corpus.segmentation.split_word(sentence, token_id, forms, multiword=False, head_index=0)[source]¶
Replace one word with several.
The piece at
head_indexkeeps the original word’s head, relation, lemma, tags and features, and receives its dependents; the other pieces attach to it with an empty relation. Withmultiword=Truethe original surface form is kept as a multiword range (e.g. Arabic clitics); otherwise the word is re-tokenised into separate surface tokens.- Parameters:
sentence (Sentence)
token_id (str | int)
forms (list[str])
multiword (bool)
head_index (int)
- Return type:
- mcparser.corpus.segmentation.merge_words(sentence, token_ids, form=None)[source]¶
Merge adjacent words into one.
The merged word takes its annotation from the group’s syntactic head (the first word whose head lies outside the group). If
formis omitted it is the surface form of a multiword token that exactly covers the group, or the forms concatenated.- Parameters:
sentence (Sentence)
token_ids (list[str | int])
form (str | None)
- Return type:
- mcparser.corpus.segmentation.delete_word(sentence, token_id)[source]¶
Delete a word. Its dependents are reattached to its head.
- Parameters:
sentence (Sentence)
token_id (str | int)
- Return type:
- mcparser.corpus.segmentation.insert_word(sentence, after, form)[source]¶
Insert a new, unannotated word after word
after(0= at the start).- Parameters:
sentence (Sentence)
after (str | int)
form (str)
- Return type:
- mcparser.corpus.segmentation.add_multiword(sentence, start, end, form)[source]¶
Group words
start..``end`` under a multiword token with surfaceform.- Parameters:
sentence (Sentence)
start (int)
end (int)
form (str)
- Return type:
- mcparser.corpus.segmentation.remove_multiword(sentence, range_id)[source]¶
Remove a multiword token line, keeping its words.
- Parameters:
sentence (Sentence)
range_id (str)
- Return type:
Structural and tagset checks for a sentence.
- class mcparser.corpus.validation.Issue(*, code, severity, message, token_id=None, column=None)[source]¶
Bases:
BaseModel- Parameters:
code (str)
severity (Literal['error', 'warning'])
message (str)
token_id (str | None)
column (str | None)
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- mcparser.corpus.validation.validate_sentence(sentence, known=None)[source]¶
Check ids, ranges, tags, features and the dependency tree.
knownmaps a column (xpos,deprel,feats) to the values seen in trusted data; values outside it give anunseen-valuewarning. Forfeatsthe set holds individualFeature=Valuepairs.
Word alignments between two sources (e.g. a translation and its original).
An alignment set pairs sentences with the same sent_id in two sources and
holds word links between them. Links are written in the Ugarit style
1-3S 2-5P (word id in source A, word id in source B, S = sure, P =
probable); many-to-many alignments are several links. Each link may carry a
confidence, as cells do.
Sets are stored as JSON Lines, one line per sentence pair:
{"sent_id": "B10K1V1", "links": [{"source": "1", "target": "1", "sure": true, "confidence": 0.95}]}
Like sources, a set is read-only (trusted, e.g. gold alignments) or writable,
edits are saved immediately, and every change is recorded in a
<file>.history.jsonl sidecar.
- class mcparser.corpus.alignment.Link(*, source, target, sure=True, confidence=None, agent=None)[source]¶
Bases:
BaseModel- Parameters:
source (str)
target (str)
sure (bool)
confidence (Annotated[float | None, Ge(ge=0.0), Le(le=1.0)])
agent (str | None)
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- mcparser.corpus.alignment.parse_ugarit(text, confidence=None)[source]¶
Parse
"1-3S 2-5P 4-4"(no suffix means sure) into links.- Parameters:
text (str)
confidence (float | None)
- Return type:
list[Link]
- class mcparser.corpus.alignment.AlignmentInfo(*, name, source_a, source_b, path, mode, default_confidence, sentences, links)[source]¶
Bases:
BaseModel- Parameters:
name (str)
source_a (str)
source_b (str)
path (str)
mode (str)
default_confidence (float)
sentences (int)
links (int)
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class mcparser.corpus.alignment.AlignmentSet(name, source_a, source_b, path, mode=SourceMode.READ_WRITE, default_confidence=None)[source]¶
Bases:
objectLinks between the sentences of two sources, persisted as JSON Lines.
- Parameters:
name (str)
source_a (CorpusSource)
source_b (CorpusSource)
path (Path | str)
mode (SourceMode | str)
default_confidence (float | None)
In-memory indexes over one source’s sentences.
Lookups are by exact string or by a loose key (Unicode NFD with combining marks removed, case-folded), which lets unvocalised or unaccented forms match annotated ones. Sentence search uses BM25 over loose word tokens.
- mcparser.corpus.index.loose(text)[source]¶
Case-fold and strip combining marks (accents, vowel points, breathings).
- Parameters:
text (str)
- Return type:
str
- mcparser.corpus.index.Location¶
(sentence position, token position) within a source.
alias of
tuple[int,int]
- mcparser.corpus.index.SUFFIX_LENGTHS = (3, 4, 5)¶
Lengths of the (loose) word endings that are indexed, for analogy with unseen forms.
- class mcparser.corpus.index.SourceIndex(sentences, root_key='Root', bm25_k1=1.5, bm25_b=0.75)[source]¶
Bases:
objectPostings and tag inventories for one list of sentences.
- Parameters:
sentences (list[Sentence])
root_key (str)
bm25_k1 (float)
bm25_b (float)
- by_case: defaultdict[tuple[str, str, str, str], Counter[str]]¶
Feature value counts for each (UPOS, Case, Number, feature), to see where combined values (
Masc,Neut) are used.
- xpos_by_tags: defaultdict[tuple[str, str], Counter[str]]¶
XPOS counts for each (UPOS, FEATS) pair.
- upos_by_function: defaultdict[tuple[str, str], Counter[str]]¶
UPOS counts for each (LEMMA, DEPREL without subtype) pair.
- fixed: Counter[tuple[str, ...]]¶
a head and its
fixeddependents.- Type:
Word sequences (loose forms) annotated as fixed expressions
- fixed_occurrences: Counter[tuple[str, ...]]¶
How often each of those sequences occurs at all (fixed or not).
- dependents: Counter[str]¶
Per DEPREL, the number of words with at least one dependent of that relation.
- by_voice: defaultdict[tuple[str, bool], Counter[str]]¶
pass).
- Type:
Per (DEPREL without subtype, whether the head is passive), the full DEPREL values (nsubj vs nsubj
- chained: Counter[str]¶
Per DEPREL, the number of words whose head has the same DEPREL (e.g. conj attached to a conj).
- mcparser.corpus.index.direction_of(token_id, head_id)[source]¶
Side of the dependent on which its head lies: left, right or root.
- Parameters:
token_id (str)
head_id (str)
- Return type:
str
- mcparser.corpus.index.relation_keys(dep_lemma, dep_upos, head_lemma, head_upos, direction)[source]¶
Keys for relation statistics, most specific first.
head_lemma/head_uposareNonefor the root. The levels are (dependent lemma, head lemma), (dependent lemma, head UPOS, direction) and (dependent UPOS, head UPOS, direction).- Parameters:
dep_lemma (str)
dep_upos (str)
head_lemma (str | None)
head_upos (str | None)
direction (str)
- Return type:
list[tuple[str, …]]
Unannotated text as evidence: how often a word occurs, next to which words, in which sentences.
Raw text has no analyses, so it cannot say what a word is, but it shows how the word is used: its frequency, its neighbours, whether longer words contain it (a stem with a clitic) and what it looks like in running text. That is useful for forms the treebank has never seen. A corpus can be tens of millions of words, so only counts and a few line numbers per word are kept in memory; the sentences are read from the files when an example is wanted.
- mcparser.corpus.textcorpus.WORD = re.compile('[\\ẁ-֑ͯ-ׇؐ-ًؚ-ٰٟۖ-ۭ]+')¶
Letters and digits plus the combining marks of Latin/Greek, Hebrew and Arabic (vowels, accents).
- mcparser.corpus.textcorpus.POSTINGS_MAX = 50¶
enough for examples and neighbour counts, small enough for 25 million words.
- Type:
Line numbers kept per word
- mcparser.corpus.textcorpus.AFFIX_MINIMUM = 3¶
Words shorter than this are not searched for inside longer words.
- mcparser.corpus.textcorpus.AFFIX_EXTRA = 4¶
Longer words are at most this many letters longer (clitics are short; longer ones are mostly run-together text).
- mcparser.corpus.textcorpus.FILE_BITS = 40¶
A line is addressed as
file_number << FILE_BITS | byte_offset.
- class mcparser.corpus.textcorpus.TextCorpus(name, paths)[source]¶
Bases:
objectOne or more plain-text files, one sentence or paragraph per line.
The index is built the first time it is needed (about 10 seconds for 25 million words) and is safe to share between threads. Words are runs of letters, digits and combining marks, so punctuation is ignored.
- Parameters:
name (str)
paths (list[Path | str])
- count(form, loose=False)[source]¶
Occurrences of
form(withloose, ignoring case and diacritics).- Parameters:
form (str)
loose (bool)
- Return type:
int
Pydantic models returned by the corpus store (and hence by the MCP tools).
- class mcparser.corpus.models.Flag(*, id, sent_id, token_id=None, column, reason, suggestion=None, raised_by, status='open', resolved_by=None, note=None, created, round=None)[source]¶
Bases:
BaseModelA request for the agent responsible for
columnto review a cell or sentence.- Parameters:
id (int)
sent_id (str)
token_id (str | None)
column (str)
reason (str)
suggestion (str | None)
raised_by (str)
status (Literal['open', 'resolved', 'dismissed'])
resolved_by (str | None)
note (str | None)
created (str)
round (int | None)
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class mcparser.corpus.models.Edit(*, seq, time, sent_id, token_id=None, action, column=None, old=None, new=None, confidence=None, reason='', agent, round=None)[source]¶
Bases:
BaseModelOne entry in a source’s edit history.
- Parameters:
seq (int)
time (str)
sent_id (str)
token_id (str | None)
action (str)
column (str | None)
old (str | None)
new (str | None)
confidence (float | None)
reason (str)
agent (str)
round (int | None)
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class mcparser.corpus.models.SourceInfo(*, name, path, mode, default_confidence, sentences, words, open_flags)[source]¶
Bases:
BaseModel- Parameters:
name (str)
path (str)
mode (Literal['ro', 'rw'])
default_confidence (float)
sentences (int)
words (int)
open_flags (int)
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class mcparser.corpus.models.SentenceSummary(*, source, sent_id, position, text, words, confidence, open_flags)[source]¶
Bases:
BaseModel- Parameters:
source (str)
sent_id (str)
position (int)
text (str)
words (int)
confidence (float)
open_flags (int)
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class mcparser.corpus.models.SentencePage(*, total, sentences)[source]¶
Bases:
BaseModel- Parameters:
total (int)
sentences (list[SentenceSummary])
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class mcparser.corpus.models.SentenceView(*, source, sent_id, position, text, conllu, tokens=None, confidence, open_flags, issues)[source]¶
Bases:
BaseModel- Parameters:
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class mcparser.corpus.models.TokenRef(*, id, form, lemma, upos, deprel)[source]¶
Bases:
BaseModel- Parameters:
id (str)
form (str)
lemma (str)
upos (str)
deprel (str)
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class mcparser.corpus.models.TokenView(*, source, sent_id, token, confidence, head=None, dependents, multiword=None)[source]¶
Bases:
BaseModel- Parameters:
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class mcparser.corpus.models.TokenHit(*, source, sent_id, token_id, form, lemma, upos, xpos, feats, head, deprel, misc, head_form=None, confidence, context)[source]¶
Bases:
BaseModel- Parameters:
source (str)
sent_id (str)
token_id (str)
form (str)
lemma (str)
upos (str)
xpos (str)
feats (str)
head (str)
deprel (str)
misc (str)
head_form (str | None)
confidence (float)
context (str)
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class mcparser.corpus.models.GroupCount(*, values, count)[source]¶
Bases:
BaseModel- Parameters:
values (dict[str, str])
count (int)
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class mcparser.corpus.models.TokenSearch(*, total, groups, hits)[source]¶
Bases:
BaseModel- Parameters:
total (int)
groups (list[GroupCount])
hits (list[TokenHit])
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class mcparser.corpus.models.SentenceHit(*, source, sent_id, text, score, confidence)[source]¶
Bases:
BaseModel- Parameters:
source (str)
sent_id (str)
text (str)
score (float)
confidence (float)
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class mcparser.corpus.models.MultiwordAnalysis(*, pieces, upos, count, examples)[source]¶
Bases:
BaseModel- Parameters:
pieces (list[str])
upos (list[str])
count (int)
examples (list[str])
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class mcparser.corpus.models.MultiwordSearch(*, surface, total, analyses, as_single_word)[source]¶
Bases:
BaseModel- Parameters:
surface (str)
total (int)
analyses (list[MultiwordAnalysis])
as_single_word (int)
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class mcparser.corpus.models.ArcHit(*, source, sent_id, dependent, head, deprel, direction, distance, confidence, context)[source]¶
Bases:
BaseModel- Parameters:
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class mcparser.corpus.models.ValueCount(*, value, count)[source]¶
Bases:
BaseModel- Parameters:
value (str)
count (int)
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class mcparser.corpus.models.FeatureConvention(*, upos, case, number, feature, values)[source]¶
Bases:
BaseModelHow a feature is written with a UPOS, Case and Number, where the treebank uses combined values (
Masc,Neut).- Parameters:
upos (str)
case (str)
number (str)
feature (str)
values (list[ValueCount])
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class mcparser.corpus.models.CorpusInfo(*, name, files, lines, words, types)[source]¶
Bases:
BaseModelAn unannotated text corpus (see
TextCorpus).- Parameters:
name (str)
files (list[str])
lines (int)
words (int)
types (int)
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class mcparser.corpus.models.CorpusExample(*, file, line, before, form, after)[source]¶
Bases:
BaseModelOne occurrence of a word in an unannotated corpus, with the words around it.
- Parameters:
file (str)
line (int)
before (str)
form (str)
after (str)
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class mcparser.corpus.models.CorpusSearch(*, corpus, form, total, forms, previous, next, sample, prefixed, suffixed, examples)[source]¶
Bases:
BaseModelHow a word is used in an unannotated corpus: counts, neighbours, longer words containing it, examples.
- Parameters:
corpus (str)
form (str)
total (int)
forms (list[ValueCount])
previous (list[ValueCount])
next (list[ValueCount])
sample (int)
prefixed (list[ValueCount])
suffixed (list[ValueCount])
examples (list[CorpusExample])
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class mcparser.corpus.models.FixedExpression(*, start, end, forms, fixed, occurrences)[source]¶
Bases:
BaseModelA word sequence of a sentence that the trusted data annotates as a fixed expression.
- Parameters:
start (str)
end (str)
forms (list[str])
fixed (int)
occurrences (int)
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class mcparser.corpus.models.RelationStats(*, basis, total, deprels)[source]¶
Bases:
BaseModelHow the trusted data labels arcs like a given one.
- Parameters:
basis (str)
total (int)
deprels (list[ValueCount])
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class mcparser.corpus.models.ArcSearch(*, total, deprels, directions, hits)[source]¶
Bases:
BaseModel- Parameters:
total (int)
deprels (list[ValueCount])
directions (list[ValueCount])
hits (list[ArcHit])
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class mcparser.corpus.models.CellRef(*, sent_id, token_id, form, column, value, confidence)[source]¶
Bases:
BaseModel- Parameters:
sent_id (str)
token_id (str)
form (str)
column (str)
value (str)
confidence (float)
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class mcparser.corpus.models.CellPage(*, total, cells)[source]¶
Bases:
BaseModel- Parameters:
total (int)
cells (list[CellRef])
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class mcparser.corpus.models.FlagPage(*, total, flags)[source]¶
Bases:
BaseModel- Parameters:
total (int)
flags (list[Flag])
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class mcparser.corpus.models.SentenceIssue(*, sent_id, issue)[source]¶
Bases:
BaseModel- Parameters:
sent_id (str)
issue (Issue)
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class mcparser.corpus.models.ValidationReport(*, total, errors, warnings, issues)[source]¶
Bases:
BaseModel- Parameters:
total (int)
errors (int)
warnings (int)
issues (list[SentenceIssue])
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class mcparser.corpus.models.ParallelView(*, alignment, sent_id, source_a, source_b, conllu_a, conllu_b, links, confidence, agents=<factory>, unaligned_a, unaligned_b)[source]¶
Bases:
BaseModelA sentence pair of an alignment set, with its links.
- Parameters:
alignment (str)
sent_id (str)
source_a (str)
source_b (str)
conllu_a (str)
conllu_b (str)
links (str)
confidence (dict[str, float])
agents (dict[str, str])
unaligned_a (list[str])
unaligned_b (list[str])
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class mcparser.corpus.models.TranslationSearch(*, lemma, side, occurrences, unaligned, candidates, examples)[source]¶
Bases:
BaseModelWhat a lemma is aligned to in the other source.
- Parameters:
lemma (str)
side (str)
occurrences (int)
unaligned (int)
candidates (list[ValueCount])
examples (list[str])
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class mcparser.corpus.models.LinkRef(*, alignment, sent_id, link, form_a, form_b, confidence)[source]¶
Bases:
BaseModel- Parameters:
alignment (str)
sent_id (str)
link (str)
form_a (str)
form_b (str)
confidence (float)
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class mcparser.corpus.models.LinkPage(*, total, links)[source]¶
Bases:
BaseModel- Parameters:
total (int)
links (list[LinkRef])
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class mcparser.corpus.models.CellUpdate(*, token_id, column, value=None, confidence=None, reason='')[source]¶
Bases:
BaseModelA change to one cell: a new value, a new confidence, or both.
- Parameters:
token_id (str)
column (str)
value (str | None)
confidence (Annotated[float | None, Ge(ge=0.0), Le(le=1.0)])
reason (str)
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class mcparser.corpus.models.EditResult(*, source, sent_id, edits, conllu, issues, id_map=None, new_ids=None)[source]¶
Bases:
BaseModel- Parameters:
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
Exceptions raised by the corpus layer.
Every exception derives from CorpusError so that callers (e.g. the MCP
server) can turn any expected failure into a message for the model.
- exception mcparser.corpus.errors.CorpusError[source]¶
Bases:
ExceptionBase class for all expected corpus failures.
- exception mcparser.corpus.errors.ConlluFormatError[source]¶
Bases:
CorpusErrorThe CoNLL-U text could not be parsed.
- exception mcparser.corpus.errors.NotFoundError[source]¶
Bases:
CorpusErrorA source, sentence, token or flag does not exist.
- exception mcparser.corpus.errors.ReadOnlyError[source]¶
Bases:
CorpusErrorAn edit was attempted on a read-only source.
- exception mcparser.corpus.errors.ConflictError[source]¶
Bases:
CorpusErrorThe file on disk changed since it was loaded.
- exception mcparser.corpus.errors.EditError[source]¶
Bases:
CorpusErrorAn edit was rejected because its arguments are invalid.
MCP server¶
MCP server exposing a CorpusStore.
The same server is used by the mcparser agents (in-process or over stdio), by Claude Desktop, and by other projects such as llmaligner.
- mcparser.mcp.server.build_server(store, name='conllu', allow_edits=True, log_level='WARNING')[source]¶
Create an MCP server over
store.With
allow_edits=Falseonly the read-only tools are registered, which is useful for exploring a corpus from Claude Desktop without risk. The MCP SDK configures the root logger atlog_level; the WARNING default keeps per-request logs out of the terminal.- Parameters:
store (CorpusStore)
name (str)
allow_edits (bool)
log_level (Literal['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'])
- Return type:
MCPServer
Source specifications and client configuration for the MCP server.
- mcparser.mcp.config.split_file_spec(text, spec)[source]¶
PATH[:MODE[:CONFIDENCE]]as (resolved path, mode or None, confidence or None).- Parameters:
text (str)
spec (str)
- Return type:
tuple[Path, SourceMode | None, float | None]
- class mcparser.mcp.config.SourceSpec(*, name, path, mode=SourceMode.READ_ONLY, default_confidence=None)[source]¶
Bases:
BaseModelA source given on the command line as
[NAME=]PATH[:MODE[:CONFIDENCE]].MODEisro(default) orrw.CONFIDENCEis the default confidence for cells without aConfscore.NAMEdefaults to the file stem. Examples:train.conllu,pred=out/pred.conllu:rw,silver=silver.conllu:ro:0.8.- Parameters:
name (str)
path (Path)
mode (SourceMode)
default_confidence (float | None)
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class mcparser.mcp.config.AlignmentSpec(*, name, source_a, source_b, path, mode=SourceMode.READ_WRITE, default_confidence=None)[source]¶
Bases:
BaseModelAn alignment set given as
NAME=SOURCE_A,SOURCE_B,PATH[:ro|rw[:CONFIDENCE]].SOURCE_AandSOURCE_Bare names of--sourcesources. The mode defaults torw; a writable file that does not exist yet is created. Example:verses=arabic,greek,alignments.jsonl.- Parameters:
name (str)
source_a (str)
source_b (str)
path (Path)
mode (SourceMode)
default_confidence (float | None)
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class mcparser.mcp.config.CorpusSpec(*, name, paths)[source]¶
Bases:
BaseModelAn unannotated text corpus given as
[NAME=]PATH[,PATH...].The files hold plain text, one sentence or paragraph per line.
NAMEdefaults to the stem of the first file. Example:arabic=train.txt,valid.txt.- Parameters:
name (str)
paths (list[Path])
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- mcparser.mcp.config.claude_desktop_config(specs, name='conllu', read_only=False, root_key='Root', alignments=None, corpora=None)[source]¶
The
mcpServersentry for Claude Desktop’sclaude_desktop_config.json.- Parameters:
specs (list[SourceSpec])
name (str)
read_only (bool)
root_key (str)
alignments (list[AlignmentSpec] | None)
corpora (list[CorpusSpec] | None)
- Return type:
dict[str, Any]
Agents and pipeline¶
The agent roles: one per annotated CoNLL-U column, plus the scorer.
- mcparser.agents.roles.READ_TOOLS = frozenset({'allowed_values', 'analyse_form', 'feature_inventory', 'find_arcs', 'find_multiword', 'find_tokens', 'get_context', 'get_sentence', 'list_flags', 'relation_stats', 'search_sentences', 'validate'})¶
MCP tools every agent may use.
Deliberately smaller than the server’s full set: the task prompt already gives the sentence, the cells to work on, open flags and the main evidence, so tools such as list_sources, get_token or low_confidence would only add tokens to every request.
- mcparser.agents.roles.CORPUS_TOOLS = frozenset({'corpus_find'})¶
Added to every role’s tools when there is a text corpus and the
corpus_toolsetting is on.
- class mcparser.agents.roles.Role(name: 'str', title: 'str', columns: 'tuple[str, ...]')[source]¶
Bases:
object- Parameters:
name (str)
title (str)
columns (tuple[str, ...])
- columns: tuple[str, ...]¶
Columns this role may change (and whose flags it handles).
- property column: str¶
The column whose low-confidence cells and flags this role handles.
- property edits_with_tools: bool¶
Segmentation edits through tools (it renumbers words); the other roles return compact answers.
- mcparser.agents.roles.REFINE_ORDER = ('segmentation', 'upos', 'lemma', 'syntax', 'head', 'deprel', 'feats', 'xpos', 'deps')¶
later columns build on earlier ones. Syntax comes before FEATS because case and definiteness depend on a word’s function, and XPOS comes last because in many treebanks it encodes UPOS and FEATS.
- Type:
Order in which roles work
- mcparser.agents.roles.DEFAULT_DISABLED = frozenset({'deps'})¶
Roles that only run when enabled in the settings (enhanced dependencies are optional in UD).
- mcparser.agents.roles.SEPARATE_SYNTAX_ROLES = frozenset({'deprel', 'head'})¶
By default one
syntaxagent sets HEAD and DEPREL together (settingjoint_syntax); withjoint_syntax: falsetheheadanddeprelagents do it one after the other.
The pydantic-ai agents, one per role, sharing one MCP connection.
- class mcparser.agents.team.TaskReport(*, summary)[source]¶
Bases:
BaseModelWhat an agent that edits through tools returns when it finishes a task.
- Parameters:
summary (str)
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class mcparser.agents.team.ModelUsage(*, tasks=0, requests=0, input_tokens=0, cache_read_tokens=0, cache_write_tokens=0, output_tokens=0, cost=None)[source]¶
Bases:
BaseModelToken use of one model over a run.
- Parameters:
tasks (int)
requests (int)
input_tokens (int)
cache_read_tokens (int)
cache_write_tokens (int)
output_tokens (int)
cost (float | None)
- merge(other)[source]¶
Add another usage (e.g. of a different model); cost is None if either has no price.
- Parameters:
other (ModelUsage)
- Return type:
None
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class mcparser.agents.team.AgentTeam(store, target, settings, models=None, toolset=None)[source]¶
Bases:
objectBuilds and runs the role agents.
By default the agents reach the corpus through an in-process MCP server over
store; passtoolsetto use another MCP connection (e.g. aMCPToolsetfor a remote server).modelsmaps a role name (or"*"for all roles) to a model name or instance, overriding the settings; tests use it to supply scripted models.- Parameters:
store (CorpusStore)
target (str)
settings (Settings)
models (Mapping[str, Model | str] | None)
toolset (AbstractToolset[Any] | None)
- usage: dict[str, ModelUsage]¶
Token use and estimated cost per model.
- role_usage: dict[str, ModelUsage]¶
Token use and estimated cost per role.
- relations()[source]¶
The trusted DEPREL inventory as one line, for the syntax roles’ instructions.
- Return type:
str
- async run(role, prompt, batch, usage=None, retries=None)[source]¶
Run
roleon a task; for compact answers, the edits are applied when the answer is accepted.The task’s token use is added to the per-model and per-role totals and, if given, to
usage; the retry messages sent to the model (why a request was repeated) are appended toretries.- Parameters:
role (str)
prompt (str)
batch (Batch)
usage (ModelUsage | None)
retries (list[str] | None)
- Return type:
TaskReport | Annotation | Scores
Restrict the MCP tools an agent sees and what it may write.
- class mcparser.agents.toolset.RoleToolset(wrapped, role, target, extra=frozenset({}))[source]¶
Bases:
WrapperToolset[Any]The MCP toolset as seen by one role.
Only the role’s tools are listed, and results come back as compact text (see
mcparser.agents.compact). Theagentargument is hidden from the model and filled in with the role name, so edits and flags are attributed correctly. Writes are limited to the target source and the role’s columns; the scorer may only send confidences. A refused call is returned to the model as a retry prompt explaining why.- Parameters:
wrapped (AbstractToolset[AgentDepsT])
role (Role)
target (str)
extra (frozenset[str])
- extra: frozenset[str] = frozenset({})¶
Tools every role may use besides its own (the corpus tool).
- async get_tools(ctx)[source]¶
The tools that are available in this toolset.
- Parameters:
ctx (RunContext[Any])
- Return type:
dict[str, ToolsetTool[Any]]
- check(name, args)[source]¶
Raise
ModelRetryif the role may not make this call.- Parameters:
name (str)
args (dict[str, Any])
- Return type:
None
- static normalise(name, args)[source]¶
Accept
updatessent as a JSON string, as models sometimes do.- Parameters:
name (str)
args (dict[str, Any])
- Return type:
dict[str, Any]
- async call_tool(name, tool_args, ctx, tool)[source]¶
Call a tool with the given arguments.
- Parameters:
name (str) – The name of the tool to call.
tool_args (dict[str, Any]) – The arguments to pass to the tool.
ctx (RunContext[Any]) – The run context.
tool (ToolsetTool[Any]) – The tool definition returned by [get_tools][pydantic_ai.toolsets.AbstractToolset.get_tools] that was called.
- Return type:
Any
Instructions and task prompts for the agents.
- class mcparser.agents.prompts.TaskItem(sent_id, cells, flags=<factory>, conllu='')[source]¶
Bases:
objectOne sentence of a task: its CoNLL-U, the cells to work on and open flags.
- mcparser.agents.prompts.sentence_block(conllu)[source]¶
CoNLL-U without the Conf attributes (the cells to work on list confidences) and trailing empty columns.
- Parameters:
conllu (str)
- Return type:
str
- mcparser.agents.prompts.task_prompt(role, target, items, evidence='')[source]¶
The user prompt for one role working on a batch of sentences.
The rules → score → refine loop over a prediction source.
Before the first round, code settles what it can (see mcparser.rules),
including columns the trusted data never uses. Then each round:
Score. The scorer rates cells that a model or a person changed since it last scored them. Cells set by code are not re-checked: their confidence is based on the evidence.
Refine. Each role in
REFINE_ORDERworks on cells of its column below the threshold and on flags raised for it. Sentences go from most to least confident, in batches of several sentences per task. After each role, the rules run again on the sentences it touched (e.g. XPOS from the new UPOS and FEATS, DEPREL for the new heads).
Rounds repeat until nothing is left to refine, a round changes nothing, or the round limit is reached. A final scoring pass then re-scores the last changes.
- mcparser.pipeline.EDIT_ACTIONS = frozenset({'add_multiword', 'delete_word', 'insert_word', 'merge_words', 'remove_multiword', 'set', 'split_word'})¶
History actions that change annotation (as opposed to confidences and flags).
- class mcparser.pipeline.TaskResult(*, round, sent_ids, role, summary=None, error=None, usage=None, retries=[])[source]¶
Bases:
BaseModel- Parameters:
round (int)
sent_ids (list[str])
role (str)
summary (str | None)
error (str | None)
usage (ModelUsage | None)
retries (list[str])
- retries: list[str]¶
Why the model had to answer again (invalid values, broken trees, doubled relations).
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class mcparser.pipeline.RoundReport(*, round, scored, tasks, edits, open_flags, confidence)[source]¶
Bases:
BaseModel- Parameters:
round (int)
scored (int)
tasks (int)
edits (int)
open_flags (int)
confidence (float)
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class mcparser.pipeline.RunReport(*, rounds, final_scored=0, confidence, rules, tasks, total, usage, roles={}, budget_reached=False)[source]¶
Bases:
BaseModel- Parameters:
rounds (list[RoundReport])
final_scored (int)
confidence (float)
rules (RulesReport)
tasks (list[TaskResult])
total (ModelUsage)
usage (dict[str, ModelUsage])
roles (dict[str, ModelUsage])
budget_reached (bool)
- roles: dict[str, ModelUsage]¶
Token use and estimated cost per role.
- budget_reached: bool¶
The run stopped early because
max_costwas reached; tasks that were not started are recorded as skipped.
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class mcparser.pipeline.Pipeline(store, target, team, settings, sent_ids=None, progress=None)[source]¶
Bases:
objectRuns the rules → score → refine rounds over one writable source.
sent_idsrestricts the work to some sentences;progressreceives a line of text after every agent task and round.- Parameters:
store (CorpusStore)
target (str)
team (AgentTeam)
settings (Settings)
sent_ids (list[str] | None)
progress (Callable[[str], None] | None)
- confidence()[source]¶
Mean confidence over the cells of the enabled roles’ columns.
- Return type:
float
- history_state()[source]¶
For each sentence, when (and by whom) each cell was last changed, and when it was last scored.
- Return type:
dict[str, _CellHistory]
- cells_to_score(sent_id, state=None)[source]¶
Cells that a model or person changed since the scorer last saw them.
Skipped: cells whose last change came from code (rules or the baseline), required cells that are still empty (
_LEMMA, UPOS, HEAD, DEPREL), and XPOS and FEATS of words without a UPOS, which cannot be judged yet.- Parameters:
sent_id (str)
state (dict[str, _CellHistory] | None)
- Return type:
list[CellRef]
- edits_since(seq)[source]¶
Edits by agents or people (not rules) after history entry
seq.- Parameters:
seq (int)
- Return type:
int
- batches(items)[source]¶
Group items into tasks of at most
batch_sentencessentences andbatch_cellswords with cells.
- async score()[source]¶
Score the cells that need it. Returns the number of sentences scored (none if the scorer is disabled).
- Return type:
int
- async refine()[source]¶
Run each role on low-confidence cells and open flags. Returns the number of tasks.
- Return type:
int
- settle_unused_columns()[source]¶
Settle XPOS/FEATS by rule when the trusted data never uses them.
Some treebanks leave XPOS (or FEATS) empty throughout. Then
_is the only right value, so it is set with confidence 1.0 and the role is not run, which saves a model task per sentence. Returns the settled columns.- Return type:
list[str]
- async run(rounds=None, score_only=False)[source]¶
Apply the rules and run up to
roundsrounds (default from the settings).A final scoring pass of the cells changed in the last round follows if
settings.final_scoringis on. It costs a scorer task per changed sentence but changes no annotation, so it is off by default. Withscore_onlyonly the rules and the scorer run, once.- Parameters:
rounds (int | None)
score_only (bool)
- Return type:
Run settings: which model each agent uses, thresholds and rounds.
Roles run on one of three model tiers: a strong model for the hard judgements (segmentation, lemmas, syntax), an efficient model for roles that mostly apply the evidence (UPOS, FEATS, scoring), and a light model for lookups (XPOS). Settings can be loaded from YAML, e.g.:
strong_model: anthropic:claude-opus-5-5
efficient_model: anthropic:claude-sonnet-5-5
light_model: anthropic:claude-haiku-5-5
efficient_roles: [upos, feats, scorer]
light_roles: [xpos]
strong_thinking: low # reasoning effort; omit for the model's default
batch_sentences: 8 # sentences per agent task
rules:
min_count: 5 # examples needed before code settles a cell
min_share: 0.95
threshold: 0.8
rounds: 3
roles:
head:
thinking: medium
deps:
enabled: true
lemma:
model: anthropic:claude-opus-5-5
instructions: Write Sumerograms as their Hittite reading when known.
- mcparser.settings.DEFAULT_EFFICIENT_ROLES = ('upos', 'feats', 'scorer')¶
Roles that mostly apply the evidence given in their prompt.
- mcparser.settings.DEFAULT_LIGHT_ROLES = ('xpos',)¶
Roles that are close to a lookup once UPOS and FEATS are known.
- mcparser.settings.Thinking¶
False(off),True(provider default) or a level.- Type:
Reasoning effort
alias of
bool|Literal[‘minimal’, ‘low’, ‘medium’, ‘high’, ‘xhigh’]
- mcparser.settings.parse_thinking(value)[source]¶
Parse a command-line thinking value: off, on, or a level.
- Parameters:
value (str)
- Return type:
bool | Literal[‘minimal’, ‘low’, ‘medium’, ‘high’, ‘xhigh’]
- class mcparser.settings.RoleSettings(*, model=None, thinking=None, enabled=None, instructions=None)[source]¶
Bases:
BaseModel- Parameters:
model (str | None)
thinking (bool | Literal['minimal', 'low', 'medium', 'high', 'xhigh'] | None)
enabled (bool | None)
instructions (str | None)
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class mcparser.settings.Settings(*, strong_model='anthropic:claude-opus-5-5', efficient_model='anthropic:claude-sonnet-5-5', light_model='anthropic:claude-haiku-5-5', efficient_roles=['upos', 'feats', 'scorer'], light_roles=['xpos'], strong_thinking=None, efficient_thinking=None, light_thinking=None, model=None, threshold=0.8, rounds=3, concurrency=1, request_limit=60, cache=True, batch_sentences=8, batch_cells=120, joint_syntax=True, final_scoring=False, max_cost=None, corpus_tool=True, conventions=True, rules=<factory>, roles=<factory>)[source]¶
Bases:
BaseModel- Parameters:
strong_model (str)
efficient_model (str)
light_model (str)
efficient_roles (list[str])
light_roles (list[str])
strong_thinking (bool | Literal['minimal', 'low', 'medium', 'high', 'xhigh'] | None)
efficient_thinking (bool | Literal['minimal', 'low', 'medium', 'high', 'xhigh'] | None)
light_thinking (bool | Literal['minimal', 'low', 'medium', 'high', 'xhigh'] | None)
model (str | None)
threshold (Annotated[float, Ge(ge=0), Le(le=1)])
rounds (Annotated[int, Ge(ge=1)])
concurrency (Annotated[int, Ge(ge=1)])
request_limit (Annotated[int, Ge(ge=1)])
cache (bool)
batch_sentences (Annotated[int, Ge(ge=1)])
batch_cells (Annotated[int, Ge(ge=1)])
joint_syntax (bool)
final_scoring (bool)
max_cost (Annotated[float | None, Gt(gt=0)])
corpus_tool (bool)
conventions (bool)
rules (RuleSettings)
roles (dict[str, RoleSettings])
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
A frequency baseline that needs no LLM.
For each surface token it applies the most frequent multiword split seen in the
trusted sources, and for each word it copies the most frequent (LEMMA, UPOS,
XPOS, FEATS) analysis of the same form. Only empty cells (_ with no
confidence recorded) are filled, so running it twice changes nothing. The
confidence reflects how much evidence there was, so the scorer and the role
agents know where to look first. HEAD and DEPREL are left to the agents.
- class mcparser.baseline.BaselineReport(*, sentences=0, splits=0, analysed=0, unknown=0)[source]¶
Bases:
BaseModel- Parameters:
sentences (int)
splits (int)
analysed (int)
unknown (int)
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- mcparser.baseline.BASELINE_MAX_CONFIDENCE = 0.7¶
a most-frequent analysis is only a candidate, so an agent always reviews it. Near-certain cells are settled by
mcparser.rulesinstead (at 0.97).- Type:
Below the default refinement threshold
- mcparser.baseline.evidence_confidence(count, total)[source]¶
Agreement among the examples, discounted when there are few (at most 0.7).
- Parameters:
count (int)
total (int)
- Return type:
float
Turn raw text into an unannotated CoNLL-U file.
Three input formats are accepted:
plain text, one sentence per line (sentence ids are the line numbers of the non-empty lines);
YAML mapping ids to entries with a
textfield, as inArabGr1-Ephesians-Parsed.yaml. Other scalar fields (e.g.verse) become comments; aconllufield is ignored. A YAML list of such entries (withsent_idorid) or of plain strings also works;CoNLL-U (e.g. a gold test set): the segmentation and comments are kept and all annotation is removed (see
blank_sentences()), which gives a fair held-out test with gold tokenisation.
- class mcparser.textinput.InputSentence(*, sent_id, text, meta=<factory>)[source]¶
Bases:
BaseModel- Parameters:
sent_id (str)
text (str)
meta (dict[str, str])
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- mcparser.textinput.tokenise(text)[source]¶
Split on whitespace and detach leading/trailing punctuation.
Returns
(form, space_after)pairs. Punctuation inside a word (don't,U.S) is kept, and combining marks are never split off.- Parameters:
text (str)
- Return type:
list[tuple[str, bool]]
- mcparser.textinput.read_input(path)[source]¶
Read plain text (one sentence per line) or YAML (by file extension).
- Parameters:
path (Path | str)
- Return type:
list[InputSentence]
- mcparser.textinput.build_sentences(entries)[source]¶
Unannotated sentences: ids, forms and
SpaceAfter=Noonly.- Parameters:
entries (list[InputSentence])
- Return type:
list[Sentence]
- mcparser.textinput.blank_sentences(sentences)[source]¶
Copies with all annotation removed: ids, forms and
SpaceAfter=Noonly.Multiword tokens are kept (their split is part of the segmentation); empty nodes are dropped (they belong to the enhanced annotation). Other MISC attributes are dropped too, since they may hold glosses or other answers.
Score predictions against gold CoNLL-U with the CoNLL 2018 shared task metrics.
The definitions and the word alignment are ported from the official
conll18_ud_eval.py and tested against it (tests/official/), so the numbers
agree with it exactly. Sentences are matched by sent_id instead of by
position, so a partly annotated file can be scored. Surface tokens are aligned by
their character span in the running text; words inside multiword tokens are then
matched by form, so segmentation differences are counted rather than breaking
the comparison. Each metric reports precision (over predicted words), recall
(over gold words) and F1; with identical segmentation all three equal accuracy.
run_official() runs the official script itself.
- class mcparser.evaluate.MetricScore(*, metric, correct, gold, predicted, precision, recall, f1)[source]¶
Bases:
BaseModel- Parameters:
metric (str)
correct (int)
gold (int)
predicted (int)
precision (float)
recall (float)
f1 (float)
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class mcparser.evaluate.Evaluation(*, sentences, missing, extra, invalid=[], scores)[source]¶
Bases:
BaseModel- Parameters:
sentences (int)
missing (list[str])
extra (list[str])
invalid (list[str])
scores (list[MetricScore])
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- mcparser.evaluate.UNIVERSAL_FEATURES = frozenset({'Abbr', 'Animacy', 'Aspect', 'Case', 'Definite', 'Degree', 'Evident', 'Foreign', 'Gender', 'Mood', 'NumType', 'Number', 'Person', 'Polarity', 'Polite', 'Poss', 'PronType', 'Reflex', 'Tense', 'VerbForm', 'Voice'})¶
The relations and features the CoNLL 2018 scorer distinguishes (copied from
conll18_ud_eval.py).
- mcparser.evaluate.STRICT = ('UFeatsAll', 'AllTagsAll', 'LASFull')¶
all features (not only the universal ones) and the full relation (with its subtype).
- Type:
Extra metrics with
strict=True
- mcparser.evaluate.official_problems(sentence)[source]¶
What the official scorer would reject in this sentence: a missing or impossible HEAD, a cycle, not exactly one root.
- Parameters:
sentence (Sentence)
- Return type:
list[str]
- mcparser.evaluate.is_annotated(sentence)[source]¶
Whether every word has a HEAD, i.e. the sentence has been parsed.
- Parameters:
sentence (Sentence)
- Return type:
bool
- mcparser.evaluate.evaluate(predicted, gold, predicted_only=False, annotated_only=False, strict=False)[source]¶
Score
predictedagainstgoldwith the metrics of the CoNLL 2018 shared task (OFFICIAL).The definitions follow
conll18_ud_eval.py: UFeats and AllTags look at the universal features only, relation subtypes are ignored (obl:argisobl), CLAS, MLAS and BLEX cover content words. Withstrict, three more metrics count language-specific features and relation subtypes (STRICT). Sentences are matched bysent_idrather than by position.With
predicted_only, gold sentences missing from the predictions are ignored (useful when only part of a test set was annotated). Withannotated_only, only predicted sentences that have been parsed (every word has a HEAD) are scored, so unparsed sentences do not hide the accuracy of the parsed ones.Evaluation.invalidlists scored sentences the official scorer would refuse (no HEAD, a cycle, several roots); their wrong heads count as errors.- Parameters:
- Return type:
- mcparser.evaluate.scorable(sentence)[source]¶
A copy of
sentencethe official scorer accepts, orNoneif it needs no change.For scoring only, never for output: a missing or impossible HEAD becomes the root, extra roots and cycles are attached to the first root (relation
dep), and a sentence without a root gets one at its first word. Every repaired word is a wrong head and relation in all likelihood, so the scores are not flattered.
- class mcparser.evaluate.OfficialResult(*, sentences, repaired, skipped=[], scores)[source]¶
Bases:
BaseModelThe output of the official
conll18_ud_eval.pyfor the sentences of a prediction file.- Parameters:
sentences (int)
repaired (list[str])
skipped (list[str])
scores (dict[str, MetricScore])
- skipped: list[str]¶
left out of both files.
- Type:
Gold sentences the official script cannot read (e.g. Arabic PADT
afp.20000715.0008:p3u1)
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- mcparser.evaluate.run_official(script, predicted, gold)[source]¶
Run the official scorer (
conll18_ud_eval.py, loaded fromscript) on the sentences inpredicted.The official scorer needs the same text in both files, so only gold sentences that were predicted are used, in gold order. Sentences it would refuse are repaired with
scorable()first and listed inrepaired.- Parameters:
- Return type:
- mcparser.evaluate.CALIBRATION_EDGES = (0.5, 0.7, 0.8, 0.9, 0.95)¶
Lower edges of the confidence buckets (the first bucket starts at 0).
- class mcparser.evaluate.CalibrationBucket(*, low, high, cells, correct)[source]¶
Bases:
BaseModel- Parameters:
low (float)
high (float)
cells (int)
correct (int)
- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- mcparser.evaluate.calibration(predicted, gold, columns=('upos', 'xpos', 'lemma', 'feats', 'head', 'deprel'))[source]¶
How accurate cells are, by the confidence recorded in MISC (
Conf=).Only cells of aligned words that carry a confidence count. A good scorer gives low scores to the cells that are wrong, so accuracy should rise with confidence; this shows where to set the refinement threshold.
- Parameters:
- Return type:
list[CalibrationBucket]