pyannotators-entityfishing 1.6.154__tar.gz → 1.6.158__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.
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/PKG-INFO +1 -1
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/src/pyannotators_entityfishing/__init__.py +1 -1
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/src/pyannotators_entityfishing/candidates.py +36 -7
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/src/pyannotators_entityfishing/ef_client.py +29 -6
- pyannotators_entityfishing-1.6.158/tools/bench_plan.py +59 -0
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/tools/sherpa_plan_project.py +2 -2
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/.claude/skills/ef-mapping/SKILL.md +0 -0
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/.gitignore +0 -0
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/.gitmodules +0 -0
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/.python-version +0 -0
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/LICENSE +0 -0
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/README.md +0 -0
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/Taskfile.yml +0 -0
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/pyproject.toml +0 -0
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/src/pyannotators_entityfishing/entityfishing.py +0 -0
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/submodules/python-archetype/.claude-plugin/marketplace.json +0 -0
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/submodules/python-archetype/.claude-plugin/plugin.json +0 -0
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/submodules/python-archetype/HOWTO.md +0 -0
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/submodules/python-archetype/README.md +0 -0
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/submodules/python-archetype/resources/Stages.yml +0 -0
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/submodules/python-archetype/resources/Taskfile.yml +0 -0
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/submodules/python-archetype/resources/help/help_examples.py +0 -0
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/submodules/python-archetype/resources/help/plugin-help.py +0 -0
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/submodules/python-archetype/resources/help/template.en.md +0 -0
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/submodules/python-archetype/resources/help/template.fr.md +0 -0
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/submodules/python-archetype/skills/new-plugin/SKILL.md +0 -0
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/submodules/python-archetype/skills/new-plugin/references/annotator.md +0 -0
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/submodules/python-archetype/skills/new-plugin/references/converter.md +0 -0
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/submodules/python-archetype/skills/new-plugin/references/formatter.md +0 -0
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/submodules/python-archetype/skills/new-plugin/references/importer.md +0 -0
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/submodules/python-archetype/skills/new-plugin/references/processor.md +0 -0
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/submodules/python-archetype/skills/new-plugin/references/segmenter.md +0 -0
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/submodules/python-archetype/skills/new-plugin/references/types.md +0 -0
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/submodules/python-archetype/skills/plugin-help/SKILL.md +0 -0
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/submodules/python-archetype/tools/check-marketplace.py +0 -0
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/submodules/python-archetype/vars/pythonPipeline.groovy +0 -0
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/tools/build_poi.py +0 -0
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/tools/build_poi_lexicons.py +0 -0
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/tools/build_rerank_data.py +0 -0
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/tools/check_rerank_endpoint.py +0 -0
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/tools/el_jev.py +0 -0
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/tools/eval_linking.py +0 -0
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/tools/eval_mappings.py +0 -0
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/tools/eval_rerank.py +0 -0
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/tools/finetune_rerank.py +0 -0
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/tools/merge_rerank.py +0 -0
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/tools/poi_common_words.json +0 -0
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/tools/recalibrate_linking.py +0 -0
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/tools/review_linking.html +0 -0
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/tools/review_linking.py +0 -0
- {pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/tools/wdfilter.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: pyannotators-entityfishing
|
|
3
|
-
Version: 1.6.
|
|
3
|
+
Version: 1.6.158
|
|
4
4
|
Summary: Annotator based on entity-fishing
|
|
5
5
|
Project-URL: Homepage, https://github.com/oterrier/pyannotators_entityfishing/
|
|
6
6
|
Author-email: Olivier Terrier <olivier.terrier@kairntech.com>
|
|
@@ -23,6 +23,8 @@ import threading
|
|
|
23
23
|
import time
|
|
24
24
|
import unicodedata
|
|
25
25
|
from collections import OrderedDict, defaultdict
|
|
26
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
27
|
+
from functools import lru_cache
|
|
26
28
|
from typing import cast
|
|
27
29
|
|
|
28
30
|
import mongoquery
|
|
@@ -92,6 +94,14 @@ FILTERED = LRUCache(int(os.getenv("APP_EF_CANDIDATE_CACHE_SIZE", "50000")), CACH
|
|
|
92
94
|
def clear_caches() -> None:
|
|
93
95
|
CONCEPTS.clear()
|
|
94
96
|
FILTERED.clear()
|
|
97
|
+
get_client.cache_clear()
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@lru_cache(maxsize=8)
|
|
101
|
+
def get_client(ef_uri: str, pool_size: int) -> EntityFishingClient:
|
|
102
|
+
"""One client per service, kept from one batch to the next: its connections stay open, and its
|
|
103
|
+
pool_size threads cap the calls to entity-fishing of every document of the process together."""
|
|
104
|
+
return EntityFishingClient(base_url=ef_uri, pool_size=pool_size)
|
|
95
105
|
|
|
96
106
|
|
|
97
107
|
class EntityFishingCandidatesParameters(ProcessorParameters):
|
|
@@ -162,9 +172,15 @@ class EntityFishingCandidatesParameters(ProcessorParameters):
|
|
|
162
172
|
)
|
|
163
173
|
ef_pool_size: int = Field(
|
|
164
174
|
int(os.getenv("APP_EF_CLIENT_POOLSIZE", "8")),
|
|
165
|
-
description="Number of
|
|
175
|
+
description="Number of calls to entity-fishing at the same time, all the documents processed together",
|
|
166
176
|
json_schema_extra={"extra": "internal"},
|
|
167
177
|
)
|
|
178
|
+
concurrency: int = Field(
|
|
179
|
+
1,
|
|
180
|
+
description="""How many documents are processed at the same time. Their calls to entity-fishing
|
|
181
|
+
still go at most ef_pool_size at a time.""",
|
|
182
|
+
json_schema_extra={"extra": "advanced"},
|
|
183
|
+
)
|
|
168
184
|
|
|
169
185
|
|
|
170
186
|
class EntityFishingCandidatesProcessor(ProcessorBase):
|
|
@@ -185,7 +201,7 @@ class EntityFishingCandidatesProcessor(ProcessorBase):
|
|
|
185
201
|
raise AttributeError(f"Metadata language '{lang}' is required and must be in {SUPPORTED_LANGUAGES}")
|
|
186
202
|
langs.append(lang)
|
|
187
203
|
mapping = {k: json.loads(v) for k, v in params.mapped_labels.items()} if params.mapped_labels else {}
|
|
188
|
-
client =
|
|
204
|
+
client = get_client(params.ef_uri, params.ef_pool_size)
|
|
189
205
|
add_candidates(
|
|
190
206
|
documents,
|
|
191
207
|
langs,
|
|
@@ -200,6 +216,7 @@ class EntityFishingCandidatesProcessor(ProcessorBase):
|
|
|
200
216
|
comma_separated_to_list(params.wikidata_facts),
|
|
201
217
|
params.facts_values,
|
|
202
218
|
params.facts_max_values,
|
|
219
|
+
params.concurrency,
|
|
203
220
|
)
|
|
204
221
|
return documents
|
|
205
222
|
|
|
@@ -222,18 +239,21 @@ def add_candidates(
|
|
|
222
239
|
wiki_facts: list[str] | None = None,
|
|
223
240
|
facts_values: PropertyValues = PropertyValues.label,
|
|
224
241
|
facts_max_values: int = 3,
|
|
242
|
+
concurrency: int = 1,
|
|
225
243
|
) -> None:
|
|
226
244
|
"""Write the Wikidata candidates of every mention in `annotation.terms`, in place."""
|
|
227
245
|
wiki_facts = wiki_facts or []
|
|
228
246
|
queries = {label: mongoquery.Query(condition) for label, condition in mapping.items()}
|
|
229
247
|
hashes = {label: hashlib.sha1(json.dumps(condition, sort_keys=True).encode()).hexdigest() for label, condition in mapping.items()}
|
|
230
248
|
ef = client.base_url
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
249
|
+
|
|
250
|
+
def add_document(document: Document, lang: str) -> None:
|
|
251
|
+
# The calls this thread lost, so those of this document only.
|
|
252
|
+
failures: list[tuple[str, str, str]] = getattr(client, "failures", [])
|
|
253
|
+
failures.clear()
|
|
234
254
|
groups = group_mentions(document, labels, mapping)
|
|
235
255
|
if not groups:
|
|
236
|
-
|
|
256
|
+
return
|
|
237
257
|
# One term lookup per group, on its longest form; the surface when that finds nothing.
|
|
238
258
|
forms = {key: lookup_forms(members) for key, members in groups.items()}
|
|
239
259
|
|
|
@@ -312,7 +332,15 @@ def add_candidates(
|
|
|
312
332
|
# Replace the Wikidata candidates, keep the terms of any other lexicon (AFP...).
|
|
313
333
|
kept = [t for t in (a.terms or []) if t.lexicon != WIKIDATA]
|
|
314
334
|
a.terms = kept + [t.model_copy(deep=True) for t in mine]
|
|
315
|
-
report_failures(document, failures
|
|
335
|
+
report_failures(document, failures)
|
|
336
|
+
|
|
337
|
+
workers = min(max(concurrency, 1), len(documents))
|
|
338
|
+
if workers > 1:
|
|
339
|
+
with ThreadPoolExecutor(max_workers=workers) as pool:
|
|
340
|
+
list(pool.map(add_document, documents, langs))
|
|
341
|
+
else:
|
|
342
|
+
for document, lang in zip(documents, langs, strict=True):
|
|
343
|
+
add_document(document, lang)
|
|
316
344
|
|
|
317
345
|
|
|
318
346
|
def report_failures(document: Document, lost: list[tuple[str, str, str]]) -> None:
|
|
@@ -320,6 +348,7 @@ def report_failures(document: Document, lost: list[tuple[str, str, str]]) -> Non
|
|
|
320
348
|
they concern may have lost candidates, and the document is worth processing again. A document
|
|
321
349
|
processed again without failure drops the report of the earlier run."""
|
|
322
350
|
metadata = {k: v for k, v in (document.metadata or {}).items() if k != ERROR_METADATA}
|
|
351
|
+
lost = list(dict.fromkeys(lost)) # a page fetched again by a later step fails again
|
|
323
352
|
if lost:
|
|
324
353
|
calls = ", ".join(f"{kind} {key}" for kind, _, key in lost)
|
|
325
354
|
metadata[ERROR_METADATA] = f"{len(lost)} entity-fishing calls failed, candidates may be missing: {calls}"[:MAX_ERROR_CHARS]
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import hashlib
|
|
2
|
+
import json
|
|
2
3
|
import logging
|
|
3
4
|
import os
|
|
5
|
+
import re
|
|
6
|
+
import threading
|
|
4
7
|
import time
|
|
5
8
|
from collections.abc import Iterable
|
|
6
9
|
from concurrent.futures import as_completed
|
|
@@ -25,6 +28,18 @@ VERIFY_SSL = os.getenv("APP_EF_VERIFY_SSL", "true").lower() not in ("0", "false"
|
|
|
25
28
|
RETRY = Retry(total=4, backoff_factor=1, status_forcelist=(429, 500, 502, 503, 504), allowed_methods=None, raise_on_status=False)
|
|
26
29
|
# (connect, read) seconds for a term lookup or a concept: without one, a stuck entity-fishing holds the document forever
|
|
27
30
|
KB_TIMEOUT = (10, 60)
|
|
31
|
+
# EF does not escape the quotes of a property name ("Apple Maps "auid" identifier", P10046), which breaks the JSON of
|
|
32
|
+
# every concept holding that property: the White House, Georgia... Workaround until kermitt2/entity-fishing#177 is fixed
|
|
33
|
+
PROPERTY_NAME = re.compile(r'("propertyName" : ")(.*?)(", "valueType")')
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def concept_json(resp: requests.Response) -> dict:
|
|
37
|
+
"""The concept of a kb response, its unescaped property names escaped if it does not parse as is."""
|
|
38
|
+
try:
|
|
39
|
+
return resp.json()
|
|
40
|
+
except ValueError:
|
|
41
|
+
# ponytail: repairs propertyName only; drop concept_json once kermitt2/entity-fishing#177 is fixed
|
|
42
|
+
return json.loads(PROPERTY_NAME.sub(lambda m: m[1] + re.sub(r'(?<!\\)"', r"\"", m[2]) + m[3], resp.text))
|
|
28
43
|
|
|
29
44
|
|
|
30
45
|
class EntityFishingClient:
|
|
@@ -79,14 +94,22 @@ class EntityFishingClient:
|
|
|
79
94
|
self.ksession.verify = VERIFY_SSL
|
|
80
95
|
self.fksession = FuturesSession(session=self.ksession, max_workers=pool_size)
|
|
81
96
|
|
|
82
|
-
|
|
83
|
-
# candidates step reports them on the document, and does not cache what they left out
|
|
84
|
-
self.failures: list[tuple[str, str, str]] = []
|
|
97
|
+
self._local = threading.local()
|
|
85
98
|
|
|
86
99
|
self.disamb_url = "/service/disambiguate/"
|
|
87
100
|
self.kb_url = "/service/kb/concept/"
|
|
88
101
|
self.term_url = "/service/kb/term/"
|
|
89
102
|
|
|
103
|
+
@property
|
|
104
|
+
def failures(self) -> list[tuple[str, str, str]]:
|
|
105
|
+
"""The calls that failed even after the retries, ("term" or "concept", lang, term or page id): the
|
|
106
|
+
candidates step reports them on the document, and does not cache what they left out. One list per
|
|
107
|
+
thread, as the failures are recorded in the calling thread: documents processed at the same time
|
|
108
|
+
through one client each see only their own."""
|
|
109
|
+
if not hasattr(self._local, "failures"):
|
|
110
|
+
self._local.failures = []
|
|
111
|
+
return self._local.failures
|
|
112
|
+
|
|
90
113
|
def _build_disamb_query(self, text: str, sentences: list[dict], lang: str, minSelectorScore: float, maxTermFrequency: float | None, short_text: bool = False) -> dict:
|
|
91
114
|
text = text.replace("\r\n", " \n")
|
|
92
115
|
disamb_query = {
|
|
@@ -228,7 +251,7 @@ class EntityFishingClient:
|
|
|
228
251
|
try:
|
|
229
252
|
resp = self.ksession.get(self.base_url + self.kb_url + qid)
|
|
230
253
|
if resp.ok:
|
|
231
|
-
return resp
|
|
254
|
+
return concept_json(resp)
|
|
232
255
|
else:
|
|
233
256
|
resp.raise_for_status()
|
|
234
257
|
except Exception:
|
|
@@ -244,7 +267,7 @@ class EntityFishingClient:
|
|
|
244
267
|
try:
|
|
245
268
|
resp = future.result()
|
|
246
269
|
if resp.ok:
|
|
247
|
-
concept = resp
|
|
270
|
+
concept = concept_json(resp)
|
|
248
271
|
if "wikidataId" in concept:
|
|
249
272
|
concepts[concept["wikidataId"]] = concept
|
|
250
273
|
else:
|
|
@@ -288,7 +311,7 @@ class EntityFishingClient:
|
|
|
288
311
|
try:
|
|
289
312
|
resp = future.result()
|
|
290
313
|
if resp.ok:
|
|
291
|
-
concepts[futures[future]] = resp
|
|
314
|
+
concepts[futures[future]] = concept_json(resp)
|
|
292
315
|
elif resp.status_code != 404: # 404: a page the KB does not hold, not a failure
|
|
293
316
|
resp.raise_for_status()
|
|
294
317
|
except Exception:
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Throughput of one or more Sherpa plans on the same documents of a project.
|
|
3
|
+
|
|
4
|
+
Each plan runs twice on the same documents: the first run fills the caches (entity-fishing
|
|
5
|
+
concepts and candidates, vLLM prefixes), the second is the one measured. The documents are
|
|
6
|
+
sent without their annotations: the plan runs the NER itself.
|
|
7
|
+
|
|
8
|
+
SHERPA_TOKEN=... python3 tools/bench_plan.py --sherpa https://viper.kairntech.com --project afp_ner_xx \\
|
|
9
|
+
--plans pipeline_ner_old pipeline_ner_new --limit 200 --batch 10 --jobs 1
|
|
10
|
+
|
|
11
|
+
--batch is what each processor receives at once: a processor's `concurrency` only helps above 1.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import argparse
|
|
15
|
+
import os
|
|
16
|
+
import sys
|
|
17
|
+
import time
|
|
18
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
19
|
+
|
|
20
|
+
from sherpa_plan_project import annotate, documents, login
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def main() -> int:
|
|
24
|
+
parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
|
|
25
|
+
parser.add_argument("--sherpa", default="http://localhost:7070")
|
|
26
|
+
parser.add_argument("--project", required=True)
|
|
27
|
+
parser.add_argument("--plans", nargs="+", required=True)
|
|
28
|
+
parser.add_argument("--limit", type=int, default=200, help="first N documents")
|
|
29
|
+
parser.add_argument("--batch", type=int, default=10, help="documents per annotate call")
|
|
30
|
+
parser.add_argument("--jobs", type=int, default=1, help="annotate calls at the same time")
|
|
31
|
+
parser.add_argument("--parallelize", action="store_true", help="Sherpa sends each document of a batch to each step in its own call, all at once")
|
|
32
|
+
args = parser.parse_args()
|
|
33
|
+
token, email, password = os.getenv("SHERPA_TOKEN"), os.getenv("SHERPA_EMAIL"), os.getenv("SHERPA_PASSWORD")
|
|
34
|
+
if not token and not (email and password):
|
|
35
|
+
print("set SHERPA_TOKEN, or SHERPA_EMAIL and SHERPA_PASSWORD", file=sys.stderr)
|
|
36
|
+
return 1
|
|
37
|
+
auth = {"X-Kairntech-key": token} if token else {"Authorization": f"Bearer {login(args.sherpa, email, password)}"}
|
|
38
|
+
headers = {**auth, "Accept": "application/json"}
|
|
39
|
+
|
|
40
|
+
docs = documents(args.sherpa, args.project, headers)
|
|
41
|
+
print(f"{len(docs)} documents in {args.project}", flush=True)
|
|
42
|
+
docs = [{k: d[k] for k in ("identifier", "title", "text", "metadata") if d.get(k) is not None} for d in docs if d.get("text")][: args.limit]
|
|
43
|
+
if not docs:
|
|
44
|
+
print("no document with a text", file=sys.stderr)
|
|
45
|
+
return 1
|
|
46
|
+
batches = [docs[i : i + args.batch] for i in range(0, len(docs), args.batch)]
|
|
47
|
+
|
|
48
|
+
for plan in args.plans:
|
|
49
|
+
for run in ("warm-up", "measured"):
|
|
50
|
+
start = time.time()
|
|
51
|
+
with ThreadPoolExecutor(args.jobs) as pool:
|
|
52
|
+
list(pool.map(lambda batch, plan=plan: annotate(args.sherpa, args.project, plan, headers, batch, args.parallelize), batches))
|
|
53
|
+
elapsed = time.time() - start
|
|
54
|
+
print(f"{plan} [{run}] {len(docs)} documents in {elapsed:.1f} s -> {len(docs) / elapsed:.1f} documents/s", flush=True)
|
|
55
|
+
return 0
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
if __name__ == "__main__":
|
|
59
|
+
sys.exit(main())
|
|
@@ -47,10 +47,10 @@ def to_input(doc: dict) -> dict:
|
|
|
47
47
|
return out
|
|
48
48
|
|
|
49
49
|
|
|
50
|
-
def annotate(sherpa: str, project: str, plan: str, headers: dict, batch: list[dict]) -> list[dict]:
|
|
50
|
+
def annotate(sherpa: str, project: str, plan: str, headers: dict, batch: list[dict], parallelize: bool = False) -> list[dict]:
|
|
51
51
|
r = requests.post(
|
|
52
52
|
f"{sherpa}/api/annotate/projects/{project}/annotators/{plan}/_annotate_documents",
|
|
53
|
-
params={"inlineLabels": True, "inlineText": True},
|
|
53
|
+
params={"inlineLabels": True, "inlineText": True, "parallelize": str(parallelize).lower()},
|
|
54
54
|
json=batch,
|
|
55
55
|
headers=headers,
|
|
56
56
|
timeout=1800,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/tools/build_poi.py
RENAMED
|
File without changes
|
|
File without changes
|
{pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/tools/build_rerank_data.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/tools/eval_linking.py
RENAMED
|
File without changes
|
{pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/tools/eval_mappings.py
RENAMED
|
File without changes
|
{pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/tools/eval_rerank.py
RENAMED
|
File without changes
|
{pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/tools/finetune_rerank.py
RENAMED
|
File without changes
|
{pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/tools/merge_rerank.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/tools/review_linking.html
RENAMED
|
File without changes
|
{pyannotators_entityfishing-1.6.154 → pyannotators_entityfishing-1.6.158}/tools/review_linking.py
RENAMED
|
File without changes
|
|
File without changes
|