med-pdf-nmo
v0.1.2
Published
Browser-first non-LLM predictor for answering NMO medical PDF questions.
Readme
med-pdf-nmo
med-pdf-nmo - browser-first JavaScript/Node.js пакет, который выбирает наиболее вероятный ответ или набор ответов на НМО-вопрос по PDF-файлу с медицинскими или клиническими рекомендациями.
Runtime работает локально и не использует LLM. В inference нет ChatGPT, OpenAI API, Anthropic, Gemini, HuggingFace inference, transformer-моделей или внешних интеллектуальных сервисов. Алгоритм основан на извлечении текста PDF, нормализации, поиске, структурных эвристиках, скоринге и evidence-фрагментах из PDF.
Что делает пакет
- Принимает PDF медицинских рекомендаций, вопрос и варианты ответа.
- Извлекает текст из PDF через
pdfjs-dist. - Нормализует русский медицинский текст, PDF-артефакты, греческие буквы, числовые ссылки, дозировки и частые OCR-искажения.
- Считает score для каждого варианта ответа.
- Поддерживает
singleиmultiвопросы. - Возвращает выбранные ответы, confidence, score по вариантам, raw score, evidence из PDF и метаданные.
- Работает в Node.js, browser bundle и Chrome-extension окружениях.
Текущие метрики
Цифры получены на локальном корпусе PDF-групп с answer key. Это не гарантия качества на любом новом PDF, а текущий ориентир после финального прогона.
| Набор | Exact accuracy | Single-answer | Multi-answer exact set |
| --- | ---: | ---: | ---: |
| Все keyed cases | 73.53% (2069/2814) | 81.17% (1573/1938) | 56.62% (496/876) |
| Holdout split | 83.79% (486/580) | 87.39% | 72.92% |
| Dev split | 77.14% (388/503) | 83.09% | 63.64% |
Для single правильным считается только точный выбор одного ответа. Для multi правильным считается только полное совпадение множества ответов, поэтому multi-метрика строже и обычно ниже.
Установка
Из npm, когда пакет опубликован:
npm install med-pdf-nmoНапрямую из Git HTTPS URL:
npm install git+https://github.com/lKolabrodl/med-pdf-nmo.git#mainИли в package.json:
{
"dependencies": {
"med-pdf-nmo": "git+https://github.com/lKolabrodl/med-pdf-nmo.git#main"
}
}При установке из Git npm выполнит prepare, поэтому пакет сам соберет dist.
Browser / React / Chrome Extension
Для браузерного окружения используй browser entrypoint:
import { answerQuestion } from "med-pdf-nmo/browser";
const result = await answerQuestion(new Uint8Array(pdfData.slice(0)), {
question,
variants,
type: isSingle ? "single" : "multi",
});Browser entrypoint уже содержит и регистрирует PDF.js внутри пакета. В обычном React, Vite, Webpack или Chrome-extension коде не нужно отдельно импортировать pdfjs-dist, настраивать GlobalWorkerOptions.workerSrc или передавать pdfjsLib в каждый вызов.
Подключение через script tag
Для прямого подключения в браузере:
<script src="./dist/med-pdf-nmo.browser.js"></script>Глобальный объект:
<input id="pdf" type="file" accept="application/pdf" />
<script>
document.querySelector("#pdf").addEventListener("change", async (event) => {
const file = event.target.files[0];
const result = await MedPdfNmo.answerQuestion(file, {
question: "Текст вопроса",
variants: ["Ответ A", "Ответ B", "Ответ C"],
type: "single"
});
console.log(result.selectedIds, result.selected, result.confidence);
});
</script>Для публичного GitHub-репозитория можно использовать CDN:
<script src="https://cdn.jsdelivr.net/gh/lKolabrodl/med-pdf-nmo@main/dist/med-pdf-nmo.browser.js"></script>Node.js
import fs from "node:fs/promises";
import { answerQuestion } from "med-pdf-nmo";
const pdfBuffer = await fs.readFile("./doc.pdf");
const result = await answerQuestion(pdfBuffer, {
question: "Какой препарат показан пациенту?",
variants: ["Ответ A", "Ответ B", "Ответ C", "Ответ D"],
type: "single"
});
console.log(result.selectedIds);
console.log(result.selected);
console.log(result.confidence);
console.log(result.evidence);В Node.js PDF можно передавать как Buffer, Uint8Array, ArrayBuffer или URL-строку.
API
answerQuestion(pdf, options)
const result = await answerQuestion(pdf, {
question: "Текст вопроса",
variants: ["Ответ A", "Ответ B", "Ответ C"],
type: "single"
});pdf может быть:
FileBlobBufferArrayBufferUint8Array- URL-строка
- объект с методом
arrayBuffer()
options:
question: текст вопроса.variants: варианты ответа.answers: алиас дляvariants.type:"single"или"multi".mode: алиас дляtype.cacheKey: необязательный ключ кеша для текста PDF.pdfjsLib: необязательная явная передача PDF.js модуля.pdfVerbosity: необязательный уровень логирования PDF.js. По умолчанию показываются только ошибки PDF.js, поэтому нефатальные font warnings вродеTT: undefined functionподавляются.
Варианты можно передавать строками:
variants: ["Ответ A", "Ответ B", "Ответ C"]Или объектами со стабильными ID:
variants: [
{ id: "A", text: "Ответ A" },
{ id: "B", text: "Ответ B" },
{ id: "C", text: "Ответ C" }
]Результат
{
selected: ["Ответ B"],
selectedIds: ["B"],
mode: "single",
confidence: 0.73,
scores: [
{ id: "A", variant: "Ответ A", score: 0.12, raw: 0.41 },
{ id: "B", variant: "Ответ B", score: 0.73, raw: 1.92 }
],
evidence: [],
meta: {},
raw: {}
}Главные поля:
selected: выбранные тексты ответов.selectedIds: ID выбранных ответов.confidence: относительная уверенность.scores: score по всем вариантам.evidence: найденные фрагменты PDF.raw: низкоуровневый результат predictor.
Multi-answer вопросы
const result = await answerQuestion(pdfBuffer, {
question: "Какие утверждения верны?",
variants: [
{ id: "A", text: "Утверждение A" },
{ id: "B", text: "Утверждение B" },
{ id: "C", text: "Утверждение C" },
{ id: "D", text: "Утверждение D" }
],
type: "multi"
});В selectedIds будет массив выбранных ID.
Низкоуровневые exports
import {
predict,
answerQuestion,
setPdfJsLib,
clearPredictorCache
} from "med-pdf-nmo";answerQuestion: удобный высокоуровневый API.predict: низкоуровневый predictor API.setPdfJsLib: ручная настройка PDF.js.clearPredictorCache: очистка runtime-кеша predictor.
CLI
После установки пакет добавляет команду:
med-pdf-nmo --helpПример:
med-pdf-nmo --pdf doc.pdf --question "Текст вопроса" --mode single --answer A="Ответ A" --answer B="Ответ B"Локально в репозитории:
npm run predict -- --pdf doc.pdf --question "Текст вопроса" --mode single --answer A="Ответ A" --answer B="Ответ B"Сборка
npm install
npm run buildСборка создает:
dist/index.js: основной ESM entrypoint.dist/index.d.ts: TypeScript-типы.dist/med-pdf-nmo.browser.js: браузерный global bundleMedPdfNmo.dist/med-pdf-nmo.browser.mjs: браузерный ESM bundle с PDF.js внутри.dist/browser-shims/*: browser alias targets для Node built-ins.dist/cli.js: CLI entrypoint.
Проверки разработки
npm test
npm run typecheck
npm run build
npm pack --dry-run
npm run eval
npm run eval:holdoutnpm run eval и npm run eval:holdout - developer tooling. Они читают локальные тестовые PDF и answer key, чтобы посчитать accuracy.
Runtime API пакета во время inference не читает eval-файлы, split-файлы, правильные ответы или тестовые fixtures.
Ограничения
- Пакет не является медицинским советником и не заменяет эксперта.
- Качество зависит от того, насколько хорошо PDF.js извлек текст из конкретного PDF.
- Сканированные PDF без текстового слоя могут потребовать OCR до передачи в пакет.
- Алгоритм выбирает вероятные ответы по PDF evidence, но не гарантирует абсолютную правильность.
- Runtime inference не использует LLM и не обращается к внешним интеллектуальным сервисам.
Лицензия
MIT. Подробнее см. LICENSE.
