Status
active. Реализовано в Realai-plus/bifrost-plugins (локально), не задеплоено на стейдж на 2026-05-07.
Контекст
BloodGPT pipeline зовёт LLM с response_format: json_schema (structured output) на каждом шаге recognition / normalization / FHIR-генерации. На стейдже это:
- Дорого по cost, особенно при rapid iteration
- Медленно (Gemini Pro ~500ms, GPT-5.2 ~400ms на запрос; pipeline делает их десятками)
- Недетерминированно (тестам сложно assert’ить против stochastic output’а)
При этом структуру output’а pipeline контролирует — он сам подаёт schema. Идея mock’а: получать обратно валидный JSON по schema, без LLM-вызова. Дальше pipeline может прогнать end-to-end (recognize → normalize → FHIR build → DiagnosticReport), без зависимости от network/API key/quota.
Старая попытка — TS apps/mock-llm-server в bloodgpt-for-business (BG-1068). Был standalone HTTP-сервер с json-schema-faker + medical biomarker dictionary + fixFhirValues для FHIR oneOf value[x]. Источник потерялся в working copy, hover в dangling commit ae3728e8. После перехода на bifrost как gateway вместо litellm — нужно либо restore TS-сервер и подключать как provider, либо переписывать как bifrost-плагин.
bifrost уже research-направление зафиксировал: «нашёл Go-плагин для Bifrost Proxy, который генерирует ответы по schema без реального LLM-вызова». Upstream maximhq/bifrost/plugins/mocker/ существует, но возвращает только canned strings — response_format: json_schema не понимает.
Рассматривали
Restore TS mock-llm-server, подключить как bifrost provider. Минимум кода. Минус — отдельный сервис в k8s (deployment + service + secrets), отдельный point of failure. Plus dependency Bun runtime в стейдже.
Один большой плагин который делает всё: schema generation + medical biomarker substitution + FHIR fix + latency simulation + error injection. Полностью замещает upstream mocker плюс наши фичи. Минус — domain-specific код (FHIR, medical) сцеплен с generic schema-faker’ом, тяжело поддерживать совместно с upstream.
Schema-only плагин без BloodGPT-специфики. Generic, ship’абельный upstream. Но тогда Parameter_Info в prompt’е никем не парсится, FHIR valueQuantity остаётся random — pipeline получает garbage values, тесты падают по non-trivial причинам.
Два плагина в цепочке — один generic schema-mocker, второй BloodGPT-specific fhir-postprocessor. Первый short-circuit’ит запрос синтетическим JSON. Второй extract’ит из prompt’а ParameterValue / Unit и патчит fhir_value в response уже сгенерированный первым.
Выбрали
Два плагина в цепочке.
schema-mocker (Realai-plus/bifrost-plugins/schema-mocker/):
- Domain-agnostic: walks JSON schema, генерирует value матчинга shape
- Pluggable preset registry:
presets/medical/— отдельный subpackage с biomarker / unit / interpretation generators, регистрируется черезinit() - PreLLMHook short-circuit: если запрос имеет
response_format.json_schema.schemaи model в allowlist’е — вернуть synthetic response
fhir-postprocessor (Realai-plus/bifrost-plugins/fhir-postprocessor/):
- BloodGPT-specific
- PreLLMHook: regex-extract
Parameter_InfoJSON из user-message → stash вBifrostContext - PostLLMHook: парсит assistant content, патчит
fhir_value.valueQuantityextracted значениями - Безопасный no-op если в response нет
fhir_value— можно держать enabled всегда
Plugin chain: fhir-postprocessor order=1 → schema-mocker order=2. Pre: fhir-Pre stashes → schema-Pre short-circuit’ит. Post (reverse): schema-Post no-op → fhir-Post patches.
Почему
- Single responsibility. schema-mocker domain-agnostic — потенциально шиппабельный upstream maximhq. fhir-postprocessor BloodGPT-only, остаётся в нашем форке
- Composable с upstream
mocker. Latency simulation, error injection,MessageTemplate— можно делегировать upstream’у. Не дублируем то что уже есть - Нет отдельного сервиса. Оба плагина живут в том же процессе что и bifrost — никакого нового deployment / service / SA / secret. Деплой = build + push + image bump
- Toggle layers. bifrost-mock-plugins подробно — header / model gating / plugin enable. Каждый layer отвечает своей аудитории (app dev / DevOps / operator)
Следствия
- Plugin ordering критичен. Если operator случайно поменяет order в config.json — fhir-postprocessor не успеет stash’нуть до short-circuit’а. Тесты молча начнут получать random
valueQuantityvalue. Гард не реализован — опираемся на знание operator’а - Streaming не покрывается. PreLLMHook гейтится на
ChatCompletionRequest.ChatCompletionStreamRequestпроходит through. BloodGPT pipeline на 2026-05-07 не стримит structured output, но если апп начнёт — silent break dataAbsentReasonне патчится. TS-parity. Если RNG выпадает на этой веткеoneOf— наблюдение пойдёт в pipeline как absent. У TS-варианта то же поведение, у нас same. Если на проде это начнёт ронять тесты — нуженforce_value_quantityflag- Pinned bifrost-core версия. v1.5.8. Bump core требует одновременного rebuild наших плагинов + UI build. Drift detection — manual
- Re-enable через runtime API сломан — описано в bifrost-custom-plugin-loading
Открытые вопросы
- Стратегия model-gating’а в production: mock-suffix (
gpt-5.2-mock) безопасный default vs all-models (mock everything) для max savings. Не зафиксировано - Submit
schema-mockerupstream maximhq/bifrost. Плюс — community maintenance. Минус — coupling on upstream PR review pace, возможно дополнительный config knob под их governance force_value_quantityflag в fhir-postprocessor — добавить еслиdataAbsentReasonначнёт мешать на нагрузке
Связано
- bifrost-mock-plugins — entity, deployment shape, configuration примеры
- bifrost-custom-plugin-loading — как технически плагины подключаются (SyncLoadedPlugin)
- bifrost — vendor сам по себе
Источники
Источники: 1.
Сноски
-
Upstream mocker plugin (для сравнения), accessed 2026-05-17, https://github.com/maximhq/bifrost/tree/main/plugins/mocker. ↩