Commit History
-
feat: smarter fallback — heuristic sufficiency + result merging (v3.1)
Robin +1: Claude Sonnet 4.6Previously: fallback fired only on entities==0, and replaced primary results. Now: heuristic sufficiency check, with results MERGED (not replaced). Sufficiency criterion (NERService._expected_min) expected_min = max(length_floor, label_floor) length_floor: text<30→1, <100→2, <300→3, ≥300→4 label_floor : ⌈len(labels)/3⌉, default 1 fallback fires when len(primary_entities) < expected_min Override ExtractRequest.min_entities (int, ≥0): client overrides the heuristic min_entities=0 disables fallback entirely Merge semantics primary results are kept verbatim fallback results appended; deduplication keeps highest-score per (start,end) applies to both single-language fallback and mixed-language dual-run Tests: 38 → 48 (heuristic, override, additive merge, EN/ZH symmetric paths) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>2288fd7 -
feat: dual-model routing + fallback strategy (v3.0)
Robin +1: Claude Sonnet 4.6Models EN/AR → urchade/gliner_multi-v2.1 (GLiNER zero-shot, ~1 GB, ~500 ms) ZH → shibing624/bert4ner-base-chinese (BERT NER, ~400 MB, ~100 ms) mixed → both models run in parallel, results merged + deduplicated Language detection (app/ner.py) Layer-1 Unicode script ratio (CJK / Arabic / Latin) mixed = cjk>=8% AND latin>=10% (prevents dominant-CJK from masking bilingual text) Layer-2 langdetect n-gram fallback when Layer-1 returns 'en' Fallback / merge (NERService.extract) zh primary=BERT, fallback=GLiNER when entities==0 en/ar primary=GLiNER, fallback=BERT when entities==0 mixed run both, _deduplicate keeps highest-score per (start,end) span Other changes app/config.py EN_MODEL_NAME / ZH_MODEL_NAME env vars; legacy MODEL_NAME kept app/labels.py BERT_TYPE_TO_LABEL, LABEL_TO_BERT_TYPES, labels_to_bert_types() requirements.txt add transformers>=4.40.0, langdetect>=1.0.9 Dockerfile pre-download both models at build …d6faa4c -
feat: optional labels, bilingual auto-expansion, language hint (v2.0)
Robin +1: Claude Sonnet 4.6- app/labels.py (new): bilingual label pairs table, DEFAULT_LABELS, expand_bilingual() — auto-adds counterpart label for each known entry - app/models.py: labels optional (default []), add language field (auto/en/zh/ar/mixed), response now echoes labels_used - app/ner.py: integrate expand_bilingual + DEFAULT_LABELS; language-aware processing; span deduplication (_deduplicate) - app/main.py: pass language to NERService.extract(), return labels_used - tests/test_extract.py: expanded from 15 → 25 tests; cover Arabic, bilingual expansion, optional-labels, language field, labels_used echo - scripts/compare_models.py: bilingual labels in all cases, Markdown report - reports/comparison_report.md: model comparison results Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>372fe0c
-
test: add Chinese/English/mixed language unit tests with optimized labels
Robin +1: Claude Sonnet 4.6- English: person+org, location+date, threshold filtering - Chinese: person+org, entity boundary (尤氏 not 尤氏来请), location+product - Mixed: both scripts in one text, bilingual labels, no cross-language contamination - Labels use descriptive phrases (人名或姓名 / full name of a person) instead of single-word labels for better GLiNER zero-shot accuracy Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>4a2a819 -
test: add HF Space URL, support NER_BASE_URL env override
Robin +1: Claude Sonnet 4.6Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>8b89393
-
refactor: remove httpx proxy, call GLiNER model directly
Robin +1: Claude Sonnet 4.6- ner.py: restored GLiNER.from_pretrained with cache_dir - config.py: restored MODEL_NAME, MODEL_CACHE_DIR, HF_ENDPOINT - main.py: pass MODEL_NAME and MODEL_CACHE_DIR to NERService - requirements.txt: replaced httpx with gliner - conftest.py: restored gliner/torch stubs for unit tests Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>bfeb704 -
fix: add /api/v1 prefix and restore port 4000
Robin +1: Claude Sonnet 4.6- Routes changed to /api/v1/health and /api/v1/extract - Default PORT reverted to 4000 (8000 was in use) - conftest.py: remove gliner/torch stubs (ner.py now uses httpx) - Tests updated to match new route prefix Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>9a7f7d0 -
test: add concurrent two-request integration test
Robin +1: Claude Sonnet 4.6Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>6a5726c -
test: use ~200-char texts in integration test cases
Robin +1: Claude Sonnet 4.6Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>f988bf5 -
test: print input/output/elapsed time in integration tests
Robin +1: Claude Sonnet 4.6Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>b10a07b -
feat: GLiNER NER HTTP API
Robin +1: Claude Sonnet 4.6- POST /extract 接收文本和实体类型,返回抽取结果 - 模型启动时加载一次,多次请求复用 - 通过 HF_ENDPOINT 使用国内镜像,MODEL_CACHE_DIR 本地缓存 - 默认端口 4000,支持环境变量配置 - 包含单元测试(mock)和集成测试(真实 API) - start.bat 从 conda ai 环境启动 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>d470d45