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/normalizationsSubmit batch, get job_id
GET /v1/normalizations/{id}/resultPoll job status + результат
GET /v1/loinc/{code}Метаинфо LOINC (consumer_name, panels, trending_group_id)
POST /v1/submit-loinc-refreshRe-normalization: найти параметры с заданным originalName
POST /v1/apply-loinc-refreshПрименить новый LOINC-код к найденным
POST /admin/cache/rebuildПерестроить Redis из PG
GET /admin/cache/statsCache health
GET /admin/cache/mappingsList mappings (только hashes — для UI см. ниже)
POST /admin/cache/invalidateBulk invalidate
GET /admin/overrides / POST / DELETEManual overrides CRUD
GET /analytics/itemsItems с полными данными (input_name, units, material, reasoning, candidates) — primary data source для UI
GET /analytics/healthPipeline 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-harmonization-pipeline — алгоритм hosted этим сервисом
  • normalization-service — старая нормализация (предшественник)
  • loinc — стандарт
  • inngest — substrate для замены ad-hoc queue в future
  • team-dynamics — transparency / trust / “не прозрачный ящик” обсуждались в контексте этого сервиса (March 12 2026)

Источники

Источники: 1.

Сноски

  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.