Production Python микросервис, host loinc-harmonization-pipeline алгоритма. Async API + Redis queue + FastAPI. Отдельный репо, отдельный deploy.
В процессе вытеснения: production source of truth для маппинга на 2026-04, но команда переносит pipeline на TS (Mastra-based, в bloodgpt-for-business). Сервис заменяется + dictionary-first интерфейс поверх TS — см. loinc-unification-direction.
Использование в BloodGPT
- Consumers: .NET API (basic для трендов и FHIR Observation coding) + analysis-worker (BG-1023 feature-flag)
- Provides: mapping
{name, units, material} → LOINC code+ metadata (consumer_name, trending_group_id, panels) - Deploy: GCP (мигрировал с Azure Jan 2026), staging
loinc.bloodgpt.tech - Repo:
Realai-plus/loinc_harmonization_service→/home/i/JOBS/BloodGPT/loinc_harmonization_service/
Архитектура
POST /v1/normalizations → Redis queue → Worker → Pipeline → Results
GET /v1/normalizations/{job_id}/result ← poll
- API layer — FastAPI, async batch + poll
- Queue — Redis BZPOPMIN
- Worker — отдельный pod, hosts loinc-harmonization-pipeline
- Storage — PostgreSQL (audit, mappings, decisions, override-таблица) + Redis (decision cache, override, inflight lock; TTL убран)
API surface (production)
| Endpoint | Что делает |
|---|---|
POST /v1/normalizations | Submit batch, get job_id |
GET /v1/normalizations/{id}/result | Poll job status + результат |
GET /v1/loinc/{code} | Метаинфо LOINC (consumer_name, panels, trending_group_id) |
POST /v1/submit-loinc-refresh | Re-normalization: найти параметры с заданным originalName |
POST /v1/apply-loinc-refresh | Применить новый LOINC-код к найденным |
POST /admin/cache/rebuild | Перестроить Redis из PG |
GET /admin/cache/stats | Cache health |
GET /admin/cache/mappings | List mappings (только hashes — для UI см. ниже) |
POST /admin/cache/invalidate | Bulk invalidate |
GET /admin/overrides / POST / DELETE | Manual overrides CRUD |
GET /analytics/items | Items с полными данными (input_name, units, material, reasoning, candidates) — primary data source для UI |
GET /analytics/health | Pipeline health (queue depth, stuck items) |
auto_verify=true flag — валидировать сразу без cron-валидатора (cron-валидатор killed Mar 11 2026 после инцидента “пожрал деньги на мигрированных данных”).
benchmark=true flag — clean testing без cache + не сохранять. Используется для Python ↔ TS comparison при портаже.
UI / Admin прототип
loinc_harmonization_service/ui/ — standalone React+Vite SPA. Эксперимент для демонстрации возможности Dictionary-First admin-интерфейса. Создан Ильдаром в session fd4858f8 (Mar 2-3 2026, BG-864) — research-эксперимент по тому, как сервис должен быть устроен.
6 экранов: Dashboard / Dictionary / Review Queue / Pipeline / Analytics / Admin. Stack: Vite 6 + React 19 + TypeScript 5.7 + TailwindCSS 4 + shadcn/ui. Bundle 271KB gzip.
Текущий статус: прототип, не задеплоен, не git-tracked в репе сервиса. BG-864 закрыт как Done (прототип). Деплой откладывался — кандидат strategy — IAP (Identity-Aware Proxy, Google auth) + nginx static, предложение Жени.
Будущее: прототип станет интерфейсом для TS-portированной версии сервиса в монорепе bloodgpt-for-business. См. loinc-unification-direction и dictionary-first-paradigm.
Gotchas / ограничения
UI не git-tracked в репе сервиса
/ui/ лежит в working dir, но не закоммичен (это был быстрый experiment). При clone fresh репо UI отсутствует — нужен manual restore. Будет переехать в монорепу при TS-порте → проблема исчезнет.
Cache admin/cache/mappings — только hashes
Не содержит input_name/units/material — только dm:{hash}. Для UI нужно использовать analytics/items (полные данные + reasoning + candidates).
API field naming разошёлся со спецификациями
В session fd4858f8 выявлены расхождения staging API ↔ schemas в RFC: total_mappings → total, manual_required → manual, error_count → errors, endpoint analytics/health (не analytics/pipeline/health). Lesson: schemas в RFC ≠ actual API; типы надо генерировать из OpenAPI, не из документации.
Service был unhealthy в Mar 2 2026 — 296 stuck items
stuck_items.running_over_1h: 296 при пустой очереди — worker pod либо рестартанул (orphan items), либо завис на LLM call без error handling, либо стояло. Recovery механизм есть — recover-orphans (Admin tab → Reprocess → Heal Orphans или CLI loinc-service recover-orphans). Pattern может повторяться.
Job polling — общая Redis queue с normalization-service (Mar 19 2026 incident)
Артур развернул job-polling для normalization-service, назвал Redis-очередь так же как в LOINC — задачи терялись 3 дня. Workaround — переименование. Inngest substrate (см. inngest) решит это by-design в future state.
Открытые вопросы
- Обновить status каждой gotcha при verify-pass — какие resolved через эволюцию, какие ещё актуальны
- Migration timeline для прототипа UI в монорепу — зависит от прогресса loinc-unification-direction
Связанные решения
- loinc-unification-direction — TS-port + service unification — active
- dictionary-first-paradigm — Dictionary как продукт, не cache — proposed
- override-storage-design — отдельная таблица vs единая — contested
Связано
- loinc-harmonization-pipeline — алгоритм hosted этим сервисом
- normalization-service — старая нормализация (предшественник)
- loinc — стандарт
- inngest — substrate для замены ad-hoc queue в future
- team-dynamics — transparency / trust / “не прозрачный ящик” обсуждались в контексте этого сервиса (March 12 2026)
Источники
Источники: 1.
Сноски
-
Mar 20 1:1 (TTL removed, override-таблица, warmup endpoint), accessed 2026-05-17, https://github.com/Realai-plus/meeting-digests/blob/main/data/digest/2026/03/2026-03-20T13%3A09%3A00.000Z_Про_портирование_нормализации_01KM5NSEK9TYP3QZM5YYNDSF7Q.md. ↩