Simon Clematide
Add morphological features toggle, hover tooltip, and UD documentation links
ae56e3f Download core/udpipe_client.py from simon-clmtd/universal-dependencies-visualizer: direct link, hf CLI and curl.
- Browser
- Download file 3.69 kB
-
https://huggingface.co/spaces/simon-clmtd/universal-dependencies-visualizer/resolve/main/core/udpipe_client.py
- Command line
-
hf download hf://spaces/simon-clmtd/universal-dependencies-visualizer/core/udpipe_client.py
-
curl -L -o udpipe_client.py https://huggingface.co/spaces/simon-clmtd/universal-dependencies-visualizer/resolve/main/core/udpipe_client.py
3.69 kB
| """ | |
| Client wrapper for LINDAT UDPipe 2 REST API. | |
| API Reference: https://lindat.mff.cuni.cz/services/udpipe/api/ | |
| """ | |
| import requests | |
| from typing import Dict, Any, Optional | |
| UDPIPE_API_URL = "https://lindat.mff.cuni.cz/services/udpipe/api/process" | |
| class UDPipeClient: | |
| def __init__(self, timeout: int = 15): | |
| self.timeout = timeout | |
| self.session = requests.Session() | |
| def parse_sentence( | |
| self, | |
| text: str, | |
| model: str, | |
| strip_morphology: bool = True, | |
| cache: Optional[Any] = None | |
| ) -> Dict[str, Any]: | |
| """ | |
| Parses a single sentence with UDPipe 2. | |
| Args: | |
| text: Sentence text (single sentence). | |
| model: Model identifier (e.g. 'english-ewt-ud-2.17-251125'). | |
| strip_morphology: If True, sets FEATS column to '_' so the visualizer | |
| and parser focus strictly on POS tags and dependencies. | |
| cache: Optional BoundedParseCache or mapping with .get(model, text) and | |
| .set(model, text, conllu). | |
| Returns: | |
| Dictionary with { "conllu": str, "model": str, "error": Optional[str] } | |
| """ | |
| clean_text = text.strip() | |
| # Enforce single sentence processing (first sentence line) | |
| if "\n" in clean_text: | |
| clean_text = clean_text.split("\n")[0].strip() | |
| if not clean_text: | |
| return {"conllu": "", "model": model, "error": "Empty input text."} | |
| if cache is not None: | |
| cached_conllu = cache.get(model, clean_text, strip_morphology=strip_morphology) | |
| if cached_conllu is not None: | |
| return { | |
| "conllu": cached_conllu, | |
| "model": model, | |
| "error": None | |
| } | |
| data = { | |
| "data": clean_text, | |
| "model": model, | |
| "tokenizer": "", | |
| "tagger": "", | |
| "parser": "", | |
| } | |
| try: | |
| resp = self.session.post(UDPIPE_API_URL, data=data, timeout=self.timeout) | |
| resp.raise_for_status() | |
| res_json = resp.json() | |
| conllu_raw = res_json.get("result", "") | |
| if strip_morphology: | |
| conllu_processed = self._strip_feats_column(conllu_raw) | |
| else: | |
| conllu_processed = conllu_raw | |
| if cache is not None and conllu_processed: | |
| cache.set(model, clean_text, conllu_processed, strip_morphology=strip_morphology) | |
| return { | |
| "conllu": conllu_processed, | |
| "model": model, | |
| "error": None | |
| } | |
| except requests.exceptions.RequestException as e: | |
| return { | |
| "conllu": "", | |
| "model": model, | |
| "error": f"UDPipe API Request Error: {e}" | |
| } | |
| def _strip_feats_column(conllu_text: str) -> str: | |
| """ | |
| Replaces column 6 (FEATS) with '_' to keep focus on POS tags and dependency relations. | |
| CoNLL-U columns: 1:ID, 2:FORM, 3:LEMMA, 4:UPOS, 5:XPOS, 6:FEATS, 7:HEAD, 8:DEPREL, 9:DEPS, 10:MISC | |
| """ | |
| out_lines = [] | |
| for line in conllu_text.splitlines(): | |
| line_str = line.strip() | |
| if not line_str or line_str.startswith("#"): | |
| out_lines.append(line) | |
| continue | |
| cols = line.split("\t") | |
| if len(cols) == 10: | |
| cols[5] = "_" # Blank morphological features | |
| out_lines.append("\t".join(cols)) | |
| else: | |
| out_lines.append(line) | |
| return "\n".join(out_lines) + "\n" | |