semidex-lite
v0.1.6
Published
Cloud RAG pipeline for Node.js with structural document chunking, Qdrant Cloud embeddings and hybrid search, and Gemini-grounded Ask APIs.
Maintainers
Readme
semidex-lite
Semidex Lite — це хмарне RAG-ядро та JS/TS-клієнт для Node.js-застосунків, яким потрібно перетворити власні документи на базу знань із пошуком і відповідями на основі джерел. Воно є хмарною версією Semidex і закриває шлях від індексації документа до інтеграції пошуку або асистента у ваш backend.
Пакет уже надає:
- ingestion для Markdown, PDF, plain text і підтримуваних Pandoc-форматів;
- структурний skeleton-first чанкінг Markdown, детермінований контекст та інвентар документа;
- dense і sparse ембединги через Qdrant Cloud Inference;
- гібридний пошук і версійний
POST /api/v1/search; - Ask API для одноходових і багатоходових відповідей із джерелами;
semidex-lite/clientдля Search, Ask v1 та Ask v2 у JavaScript/TypeScript;- CLI для індексації та операторську адмін-панель.
[!IMPORTANT] Статус: ранній pre-1.0 продукт. Semidex Lite активно розвивається, тому API, конфігурація та поведінка ще можуть змінюватися між релізами. Проєкт не пройшов незалежного security audit і постачається без гарантій; не використовуйте його як єдину межу безпеки або в критичних системах без власного аналізу ризиків, тестування й належного захисту deployment. Вихідний код доступний за ліцензією MIT: його можна безкоштовно використовувати, змінювати й поширювати, зокрема в комерційних продуктах, за умови дотримання тексту ліцензії. Ліцензія не надає гарантій.
Semidex Lite не є готовим SaaS, сховищем чатів або повною платформою керування користувачами. Ваш застосунок володіє інтерфейсом, користувачами, історією розмов і бізнес-правилами; Semidex Lite надає ingestion, retrieval та grounded answering як окреме backend-ядро. Qdrant Cloud використовується для зберігання й обчислення ембедингів, а Gemini — для генерації відповідей.
Повний цикл від документа до відповіді
semidex-lite — це не лише Ask endpoint або тонка обгортка над Qdrant. Пакет
виконує повний хмарний RAG-цикл від вхідних документів до обґрунтованої
відповіді:
документи
-> парсинг і структурне розбиття на чанки
-> детермінований контекст і структурний інвентар
-> обчислення ембедингів через Qdrant Cloud
-> dense + sparse індексація в Qdrant
-> гібридний пошук
-> відповідь Gemini з посиланнями на джерелаMarkdown обробляється skeleton-first чанкером: ієрархія заголовків і структурні сутності, зокрема таблиці, блоки коду та чеклісти, залишаються доступними для перевірки, а не перетворюються на один плоский текст. Навігаційні вузли отримують детермінований інвентар навіть без необов'язкових LLM summary. PDF, plain-text і підтримувані Pandoc-формати використовують окремий шлях ingestion та поки не мають такої самої структурної точності, як Markdown.
Для чого використовувати semidex-lite
Пакет можна використовувати як хмарне RAG-ядро для застосунків, яким потрібно шукати у власних документах і формувати відповіді на їх основі. Наприклад:
- асистент із документації або підтримки на сайті;
- бот для Telegram чи іншого каналу спілкування;
- пошук і Ask-інтерфейс для внутрішньої бази знань команди або організації;
- навчальний чи дослідницький помічник для роботи з власними матеріалами;
- retrieval-компонент більшої агентної системи або спеціалізованого продукту.
Для інтеграції з backend використовуйте версійні Search/Ask endpoints напряму
або клієнт semidex-lite/client. Адмін-панель залишається операторським
інструментом для локального налаштування, індексації та діагностики, а не
публічним API вашого продукту.
semidex-lite відповідає за індексацію, пошук релевантного evidence і цикл
Ask — включно з обчисленням обмеженого rolling summary для багатоходових
розмов (/api/v2/ask, див.
нижче), коли про це просять.
Застосунок поверх нього володіє всім, що стосується самої розмови: власним
інтерфейсом, автентифікацією, повною історією повідомлень, збереженням
підсумку, який повертає Semidex, і застосуванням підтвердженої межі
compaction, пам'яттю поза межами однієї розмови, додатковими інструментами
та бізнес-правилами. Для інтеграції використовуйте HTTP Ask API через
власний backend; поточний адмін-сервер не призначений для прямого відкриття в
публічний інтернет.
Водночас системний prompt самого Ask у цьому MVP є внутрішнім і незмінним через публічний API або налаштування. Зовнішня надбудова може керувати контекстом до та після виклику Ask, але для зміни внутрішніх правил Gemini зараз потрібно змінити або форкнути вихідний код пакета. Конфігурований системний prompt може з'явитися в майбутньому, однак поки не є частиною публічного контракту.
Напрям розвитку
Найближчий фокус — стабільний публічний integration surface, відтворюваний end-to-end приклад, оцінювання retrieval/groundedness і подальше посилення безпеки. Наступні напрями включають якісніший ingestion для PDF, OCR та зображень, додаткових generation-провайдерів, agentic research/MCP facade, Codebase Memory і підключні механізми довготривалої пам'яті. Це напрями розвитку, а не обіцянка конкретних дат або складу наступного релізу.
Актуальний стан, пріоритети, non-goals і дослідницький backlog описані в canonical roadmap.
Чим semidex-lite відрізняється від повної версії semidex
| | semidex | semidex-lite |
|---|---|---|
| Сховище | Qdrant (локальний або Cloud) | Лише Qdrant Cloud |
| Ембединги | Ollama, локальний ONNX (BGE-M3) або Qdrant Cloud Inference | Лише Qdrant Cloud Inference |
| Генерація відповідей | Ollama або Gemini | Лише Gemini |
| Контекст чанків (файли, відмінні від Markdown) | Генерується LLM (Ollama) | Детермінований (текст заголовка/секції, без викликів LLM) |
| Генерація тегів, комбінований LLM-прохід | Підтримується (Ollama або локальний ONNX) | Недоступно |
| Апаратні перевірки CUDA/DirectML | Підтримуються | Недоступні |
| Розмір пакета / місце після встановлення | Містить onnxruntime-node і @huggingface/transformers | Не містить жодного з них — встановлення значно менше та швидше |
Все інше — гібридний dense+sparse retrieval, детермінований reranking, skeleton-first розбиття Markdown, підтримка PDF/Pandoc, Ask API, перегляд колекцій і пошук в адмін-панелі — працює так само.
[!NOTE] Повна версія
semidexнаразі доступна як вихідний код у GitHub-репозиторії, але ще не публікується як окремий готовий npm-пакет. Його планується підготувати після завершення тестування, стабілізації інсталяції та реалізації критично важливого функціоналу. До того часу для встановлення через npm використовуйтеsemidex-lite, а повну версію запускайте безпосередньо з репозиторію.
Встановлення
У проєкт (рекомендовано)
npm install semidex-liteПакет буде додано до залежностей поточного проєкту, а його версію зафіксують
package.json і lockfile. Це рекомендований варіант для застосунків,
контейнерів, серверів та відтворюваного розгортання: кожен проєкт використовує
власну визначену версію semidex-lite.
Локально встановлену CLI запускайте через npx, наприклад
npx semidex-lite doctor. Усі доступні команди та їх призначення наведено в
розділі CLI.
Глобально (необов'язково)
npm install -g semidex-liteГлобальне встановлення додає команду semidex-lite до системного PATH, тому
її можна запускати з будь-якої директорії без npx. Цей варіант зручний для
особистого використання на одному комп'ютері, навчальних або дослідницьких
експериментів і ручної роботи з власними колекціями. Водночас версія пакета не
фіксується окремо для кожного проєкту, тому для інтеграції та розгортання краще
використовувати локальне встановлення.
[!NOTE] Поточний пакет
semidex-liteне містить MCP-сервера. Для інтеграції із сайтами, ботами та іншими застосунками він надає HTTP Ask API. MCP-сервер наразі входить лише до повної версії semidex.
semidex-lite ніколи не записує дані у власну директорію встановлення, тому
працює навіть із доступним лише для читання node_modules/. Усі дані стану
(конфігурація, налаштування, кеш токенайзера) зберігаються в окремій для кожної
ОС директорії даних застосунку — див. розділ SEMIDEX_HOME.
Налаштування
Створіть файл .env у директорії, з якої запускатимете semidex-lite, і
заповніть:
QDRANT_URL,QDRANT_KEY— дані вашого кластера Qdrant Cloud;GEMINI_API_KEY— ключ для Ask/генерації. Під час запуску він необов'язковий:serve,doctorтаindexпрацюють без нього, але запити Ask завершуватимуться помилкою, доки ключ не буде задано.
QDRANT_URL=https://your-cluster-id.your-region.cloud.qdrant.io
QDRANT_KEY=your-qdrant-cloud-api-key
GEMINI_API_KEY=your-gemini-api-keyПакет постачається з повністю прокоментованим .env.example, що охоплює всі
необов'язкові налаштування, включно з QDRANT_CLOUD_DENSE_MODEL,
ASK_MODEL, ADMIN_HOST/ADMIN_PORT, налаштуваннями compaction Ask v2 та
SEMIDEX_HOME. Якщо ви встановили semidex-lite як залежність проєкту
(npm install semidex-lite), цей файл знаходиться за шляхом
node_modules/semidex-lite/.env.example — копії у корені вашого власного
проєкту немає, — тож або відкрийте його там, щоб побачити всі параметри,
або почніть із мінімального блоку вище й додавайте параметри з цього README
за потреби.
Облікові дані поки що налаштовуються поза адмін-панеллю. Додайте їх у локальний
файл .env або у змінні оточення операційної системи перед запуском
semidex-lite. Системні змінні оточення мають вищий пріоритет за значення з
.env. Адмін-панель показує, чи налаштовані облікові дані, але в поточній
версії не може додавати, показувати або змінювати QDRANT_URL, QDRANT_KEY чи
GEMINI_API_KEY.
Приклад для поточної сесії PowerShell:
$env:QDRANT_URL='https://your-cluster.cloud.qdrant.io'
$env:QDRANT_KEY='your-qdrant-api-key'
$env:GEMINI_API_KEY='your-gemini-api-key'
npx semidex-lite serveЗначення, задані таким способом, діють лише у цьому процесі PowerShell і в
запущених із нього програмах. Для локального налаштування проєкту краще
використовувати файл .env, виключений із системи контролю версій. Ніколи не
додавайте API-ключі до Git.
Під час запуску semidex-lite безумовно фіксує свою хмарну конфігурацію
(DENSE_PROVIDER, SPARSE_PROVIDER, SEMIDEX_GENERATION_BACKEND,
CONTEXT_MODE та перемикачі локальних runtime). Випадкова змінна локального
провайдера, яка залишилася у .env від повної версії semidex, не може знову
увімкнути локальний код. Settings API та API завдань індексації окремо
відхиляють будь-які спроби змінити ці параметри під час роботи.
Моделі ембедінгу Qdrant Cloud
semidex-lite виконує retrieval через два окремі векторні канали, обидва обчислюються через Qdrant Cloud Inference:
- dense-модель відповідає за семантичний (смисловий) пошук;
- sparse-модель додає lexical/keyword retrieval — точні збіги термінів, ідентифікаторів та іншого, що чисто семантичний ембединг може пропустити.
Пошук у semidex-lite (аналог qdrant_search) завжди гібридний: він
звертається до обох каналів і поєднує результати, а не обирає лише один із
них.
Нижче наведено повний перелік моделей поточного каталогу. У цій версії
реально можна вибрати лише моделі зі статусом supported — записи
dedicated/planned є справжніми, live-перевіреними ідентифікаторами
моделей Qdrant Cloud, але semidex-lite поки не визначає рівень (tier)
конкретного кластера, тому ці моделі ніколи не доступні для вибору й ніколи
не використовуються, незалежно від того, який план Qdrant Cloud у вас є.
| Модель | Тип | Розмірність | Контекстне вікно | Доступність | Примітки |
|---|---|---|---|---|---|
| intfloat/multilingual-e5-small | dense | 384 | 512 токенів | supported (free tier) | Багатомовна (100+ мов, включно з українською) |
| sentence-transformers/all-minilm-l6-v2 | dense | 384 | 256 токенів | supported (free tier) | Налаштована на англійську; працює, але не оптимізована для інших мов |
| qdrant/bm25 | sparse | n/a | n/a | supported (free tier) | Єдина sparse-модель, доступна для вибору в цій версії |
| mixedbread-ai/mxbai-embed-large-v1 | dense | 1024 | невідомо | planned (потрібен dedicated-кластер) | Недоступна для вибору в цій версії |
| prithivida/Splade_PP_en_v1 | sparse | n/a | 128 токенів | planned (потрібен dedicated-кластер) | Лише англійська; недоступна для вибору в цій версії |
Налаштування dense-моделі
Dense-модель для нових колекцій задається так:
QDRANT_CLOUD_DENSE_MODEL=intfloat/multilingual-e5-smallПро те, як завантажуються та пріоритезуються .env і системні змінні
оточення, див. розділ Налаштування вище.
Sparse-модель
Наразі є лише одна sparse-модель зі статусом supported — qdrant/bm25, і
немає користувацького вибору між кількома sparse-моделями. У коді існує
розширена змінна оточення QDRANT_SPARSE_MODEL для сумісності наперед. Якщо
її не задано, Semidex Lite використовує qdrant/bm25 за замовчуванням. Якщо
задати модель без статусу supported, створення профілю нової колекції
завершиться помилкою замість непомітного fallback до BM25. Адмін-панель
показує активну sparse-модель лише для читання, оскільки другої sparse-моделі
зі статусом supported поки немає.
Вибір dense-моделі через адмін-панель
Відкрийте адмін-панель (npx semidex-lite serve, потім
http://127.0.0.1:8642) і перейдіть у Settings → Embeddings. Поле "Dense
model (Qdrant Cloud)" перелічує всі dense-моделі зі статусом supported з
таблиці вище, з позначенням розмірності та контекстного вікна. Налаштування
застосовується до новостворених колекцій — зміна цього поля не впливає на
колекції, які вже існують (див.
Життєвий цикл колекції нижче).
Облікові дані Qdrant/Gemini (QDRANT_URL, QDRANT_KEY, GEMINI_API_KEY)
поки не можна додати, показати чи змінити з адмін-панелі — див. розділ
Налаштування вище. Жодне налаштування в цьому розділі
адмін-панелі не потребує перезапуску semidex-lite serve, щоб набути
чинності.
Життєвий цикл колекції та зміна моделі
- Dense/sparse-модель, активна на момент першого створення колекції, записується в metadata embedding-профілю саме цієї колекції в Qdrant.
- Пошук у колекції автоматично використовує саме її власний записаний профіль — ніколи поточний глобальний default і ніколи іншу модель або розмірність векторів, ніж ту, з якою колекцію було створено.
- Зміна
QDRANT_CLOUD_DENSE_MODEL(або відповідного налаштування в адмін-панелі) впливає лише на колекції, створені після цієї зміни. Вона ніколи не перезаписує профіль чи збережені вектори вже наявної колекції. - Щоб перевести наявну колекцію на іншу модель, створіть нову колекцію під новою моделлю і переіндексуйте вихідні документи в неї (контрольована повна переіндексація) — вбудованої міграції моделі "на місці" немає.
- Не редагуйте вручну розмір вектора чи metadata embedding-профілю колекції. Вони повністю визначаються моделлю, якою колекцію було створено, а пошук залежить від їх відповідності реальній векторній схемі колекції.
Перевірка, чи справді працює Cloud Inference
npx semidex-lite doctor --probe-inferenceКоманда виконує справжній, мінімальний цикл ембедингу через Qdrant Cloud
Inference: створює тимчасову колекцію, обчислює нею ембединг і одразу видаляє
цю колекцію. Повний опис команди — у розділі doctor нижче.
Який варіант обрати
- Українські або інші багатомовні документи:
intfloat/multilingual-e5-small(E5 Small). - Переважно англійські документи, особливо з короткими chunks: підходить
будь-яка з моделей;
sentence-transformers/all-minilm-l6-v2(MiniLM) налаштована на англійську, але має менше контекстне вікно (256 токенів).
Жодна з моделей не визнана однозначно «кращою» — benchmark, що порівнював би їх для цього сценарію використання, поки не проводився. Обирайте, виходячи з мови ваших документів, і перевіряйте результат на власних даних.
Стан безпеки
[!IMPORTANT] Integration Ask API має автентифікацію, але Admin API — ні. Прочитайте цей розділ, перш ніж робити будь-яку частину
semidex-lite serveдоступною за межами власного комп'ютера.
Що захищено в цій версії:
- Лише loopback за замовчуванням. Сервер слухає
127.0.0.1і відмовляється від non-loopbackADMIN_HOSTбезADMIN_ALLOW_REMOTE=1. - Cross-site запити з браузера відхиляються. Запити, які браузер позначає
як cross-site (
Sec-Fetch-Site), або які містять чужий чи opaqueOrigin, відхиляються ще до виконання логіки маршруту. Це закриває реальну проблему попередніх версій: стороння сторінка могла непомітно змусити ваш запущений інстанс стартувати індексацію, виконати платні Ask/search запити або змінити стан колекції — браузер блокував читання відповіді, але робота все одно виконувалася. - JSON-ендпоінти вимагають
Content-Type: application/json. Будь-що інше з тілом запиту відхиляється кодом415до парсингу. - Заголовок Host валідується за loopback host/port (або вашим списком
ADMIN_ALLOWED_HOSTS), що блокує атаки DNS-rebinding. - Ask v1/v2 вимагають bearer-ключ. Integration-ключі мають явні області
дії за операціями й колекціями; відсутній, прострочений, відкликаний або
непридатний для вибраної колекції ключ відхиляється до звернення в Qdrant
чи Gemini. Якщо ключів немає, Integration API fail-closed повертає
503. - Ask має rate limiting для кожного ключа. Обидві версії Ask спільно використовують один token bucket ключа. Див. Обмеження частоти запитів нижче.
QDRANT_URLне можна спрямувати на адресу cloud-metadata, а зміна цього значення через HTTP вимагає прямого loopback-з'єднання. Кожне створення Qdrant-клієнта відхиляє не-http(s)схему, вбудовані облікові дані в URL або відому адресу cloud-metadata (169.254.169.254та її документовані IPv6-форми,metadata.google.internal) ще до будь-якого мережевого запиту. ОкремоPATCH /api/settingsприймає змінуQDRANT_URLлише від прямого loopback-з'єднання до цього процесу — незалежно відADMIN_ALLOW_REMOTE— тож віддалений викликач відкритого Admin API не може непомітно перенаправити Semidex на Qdrant-endpoint, контрольований атакувальником. Це не блокує loopback, LAN, RFC1918 чи Docker-internal адреси; самостійно розміщений Qdrant на таких адресах — звичайна підтримувана ціль, а не ризик, від якого захищає ця перевірка. Повний обсяг і обмеження — у §12j пов'язаного аудиту.- Індексація через дашборд/API обмежена схваленими оператором каталогами.
POST /api/jobs/indexканонічно визначає реальний шлях і приймає його лише всерединіINDEX_ALLOWED_ROOTS. Якщо корені не налаштовані, індексація через HTTP/дашборд вимкнена. Пряме довірене CLI-індексування не обмежується. - Встановлено таймаути прийому запиту та обмеження кількості заголовків.
Чого поки немає — найважливіше:
- Admin API не має автентифікації чи авторизації. Будь-який процес на
машині може викликати маршрути дашборда, налаштувань, індексації, керування
колекціями та неверсійований
/api/search, включно з деструктивними. Bearer-ключі захищають лишеPOST /api/v1/askіPOST /api/v2/ask; вони не роблять Admin API безпечним для віддаленого адміністрування. - Адмін-маршрути не мають обмеження колекцій для окремих викликачів. Integration-ключі обмежують Ask точним переліком дозволених колекцій, але Admin API зберігає повний локальний доступ оператора до всіх налаштованих колекцій.
- Немає rate limiting на Admin API.
/api/searchта інші адмін-маршрути необмежені. Rate limiting Ask захищає лише Integration-поверхню, не адмін/дашборд.
Практична порада: вважайте Admin-поверхню semidex-lite serve локальним
сервісом для одного довіреного користувача. Для сайту, бота чи асистента
викликайте автентифікований Ask API зі свого backend і залишайте там
ідентифікацію та авторизацію кінцевих користувачів. Якщо використовується
reverse proxy, відкривайте лише потрібні версійовані Ask endpoints; ніколи не
проксіюйте весь Admin-порт в інтернет або недовірену локальну мережу.
Повний аналіз, помаршрутний інвентар і запланована послідовність хардненгу —
у файлі
docs/security/semidex-lite-public-api-audit-2026-08.md
у репозиторії.
Дозволені корені індексації
Перед запуском індексації з дашборда або через POST /api/jobs/index
налаштуйте каталоги, які Admin API може читати. У файлі середовища значенням
є JSON-масив (зворотні слеші Windows потрібно екранувати):
INDEX_ALLOWED_ROOTS=["C:\\Users\\me\\Documents\\knowledge","D:\\shared-docs"]У Settings → System вводьте по одному абсолютному каталогу в рядку. Налаштування застосовується одразу у Full і Lite; порожній список працює fail-closed і вимикає індексацію через HTTP/дашборд. Вибір папки лише заповнює поле цільового шляху й ніколи не розширює allow-list.
Налаштовані корені та цільовий шлях мають існувати. Semidex визначає їх через реальну файлову систему до порівняння компонентів шляху, тому symbolic link або Windows junction, що веде за межі дозволеного кореня, відхиляється. Це перевірка стану на конкретний момент, а не повноцінна race-proof пісочниця: інший локальний процес, здатний замінювати файли чи каталоги під час обходу дочірнім індексатором, може створити TOCTOU-гонку. Дозволяйте запис у ці каталоги лише довіреним користувачам.
Обмеження стосується тільки Admin HTTP route. Локальний оператор, який прямо
запускає npx semidex-lite index <path>, сам відповідає за вибраний шлях.
Відкриття сервера за межі loopback
Якщо ви свідомо встановлюєте ADMIN_ALLOW_REMOTE=1, потрібно також задати
ADMIN_ALLOWED_HOSTS — точні хости, які використовуватимуть клієнти. Інакше
сервер не запуститься:
ADMIN_ALLOW_REMOTE=1
ADMIN_ALLOWED_HOSTS=semidex.example.com,192.168.1.10:8642Вказуйте порт, якщо клієнти підключаються не на стандартний. Це список дозволених Host, а не контроль доступу — він запобігає DNS-rebinding та атакам через заголовок Host, але не робить API безпечним для відкриття без вашого власного автентифікованого шару попереду.
За reverse proxy з термінацією TLS origin браузера
(https://ваш-домен) відрізняється від того, що бачить цей процес на
незашифрованому сокеті, тому додатково вкажіть точні дозволені origin:
ADMIN_ALLOWED_ORIGINS=https://semidex.example.comX-Forwarded-Proto і X-Forwarded-Host свідомо не є довіреними — вони
контролюються атакувальником на безпосередньо доступному listener — тож це
потрібно налаштувати явно, а не виводити автоматично.
CLI
Нижче наведено рекомендований запуск із локальної залежності проєкту через
npx. Так проєкт використовує версію semidex-lite, зафіксовану у власному
package.json і lockfile, а не випадкову системну версію. Якщо ви свідомо
встановили пакет глобально через npm install -g semidex-lite, можете прибрати
npx і запускати ті самі команди як semidex-lite ....
npx semidex-lite --help # перелік хмарних команд
npx semidex-lite doctor [--probe-inference] # перевірка середовища без змін
npx semidex-lite serve # запуск адмін-API та панелі
npx semidex-lite index <path> # індексація файла або папкиdoctor
За замовчуванням працює лише на читання: перевіряє версію Node, наявність
.env, наявність облікових даних Qdrant Cloud/Gemini та доступність Qdrant
Cloud за допомогою дешевого запиту. Команда нічого не створює, не змінює і не
видаляє.
Параметр --probe-inference виконує справжній цикл обчислення ембедингу через
тимчасову колекцію Qdrant Cloud. Колекція створюється і видаляється в межах
одного запуску команди. Це перевіряє, чи справді Cloud Inference працює з
вибраною dense-моделлю. Перед виконанням команда показує попередження.
serve
Запускає адмін-API та панель на ADMIN_HOST:ADMIN_PORT (за замовчуванням
127.0.0.1:8642). Якщо QDRANT_URL, QDRANT_KEY або GEMINI_API_KEY відсутні
чи недоступні, сервер запускається в обмеженому режимі: панель повідомляє, що
саме не налаштовано, замість того щоб відмовитися від запуску. До виправлення
конфігурації не працюють лише залежні операції: пошук, індексація або Ask.
index
COLLECTION=my-docs npx semidex-lite index ./docs
COLLECTION=my-docs npx semidex-lite index ./docs --prune-staleУ PowerShell змінну колекції потрібно задати окремо:
$env:COLLECTION='my-docs'
npx semidex-lite index ./docsІндексує файл або папку у вказану колекцію Qdrant Cloud. Підтримує Markdown,
звичайний текст, PDF та формати, які може конвертувати Pandoc. Параметр
--prune-stale видаляє точки файлів, яких більше немає за вказаним шляхом.
Використовуйте його лише під час індексації повного кореня джерел, а не його
частини.
Параметрів --onnx-embed, --llm-summaries і --tag-gen немає: це локальні
можливості, які не входять до цього пакета.
key
Керує ключами Integration API — bearer-токенами, якими ваш backend викликає Ask. Повна модель — у розділі Автентифікація Integration API нижче.
npx semidex-lite key add --name assistant-backend --collection my-docs
npx semidex-lite key list
npx semidex-lite key revoke <id>key add показує сирий токен один раз — він ніколи не зберігається
(зберігається лише SHA-256 digest) і повторно показаний бути не може.
key list показує лише публічні метадані, ніколи токен чи digest.
key revoke діє негайно, без перезапуску.
Автентифікація Integration API
[!IMPORTANT] Примітка про міграцію для наявних користувачів Ask API. Ask тепер потребує bearer-токена. Доки ви не створите перший ключ,
POST /api/v1/search,POST /api/v1/askіPOST /api/v2/askповертають503 integration_auth_not_configured. Створіть ключ командоюsemidex-lite key add …і надсилайте його якAuthorization: Bearer <token>. Більше нічого не змінюється: адмін-панель, налаштування, індексація та перегляд колекцій працюють як раніше, без жодних облікових даних.
Admin API проти Integration API
semidex-lite обслуговує дві різні поверхні з навмисно різними правилами.
Сам Integration API складається з двох незалежних груп ендпоінтів — Search
(/api/v1/search, лише Qdrant, без виклику генерації) і Ask
(/api/v1/ask//api/v2/ask, платна генерація через Gemini) — і ключ треба
явно налаштувати на кожну з них окремо (див.
Створення ключа нижче):
| | Admin API | Integration API |
|---|---|---|
| Маршрути | Панель, налаштування, завдання індексації, колекції, проби, /api/search (неверсійний, лише для панелі) | POST /api/v1/search, POST /api/v1/ask, POST /api/v2/ask |
| Хто викликає | Ви, з браузера на цій машині | Ваш backend, server-to-server |
| Обліковий запис | Немає — захист через loopback-прив'язку | Bearer-ключ, обов'язково |
| Відкритість | Ніколи не відкривати поза loopback | Доступний через ваш backend |
Admin-маршрути ніколи не блокуються integration-ключем: відсутній або пошкоджений key store вимикає Search/Ask, а не вашу панель.
/api/search і /api/v1/search виглядають схожими, але це РІЗНІ контракти:
/api/search— неверсійний, використовується лише вбудованою панеллю цього пакета (ui-src/search.js). Без bearer-ключа, лише loopback, без публічної гарантії сумісності — форма відповіді може змінитися в будь-якому релізі без попередження./api/v1/search— версійний, автентифікований і стабільний. Саме цей ендпоінт має викликати ваш backend. Див. Search:POST /api/v1/searchнижче.
Створення ключа
npx semidex-lite key add --name assistant-backend \
--collection my-docs \
--collection support-docs \
--expires 90dПараметри:
--name— мітка, обов'язково.--collection— можна повторювати, обов'язково. Ключ без жодної колекції відхиляється: порожня область дії ніколи не повинна тихо означати необмежений доступ. Щоб явно надати всі колекції, вкажіть--collection "*".--operation— можна повторювати. За замовчуваннямgenerate(Ask v1/v2), якщо прапорець не вказано — наявний ключ, створений до появи Search, ніколи автоматично не отримує доступ до неї: доступ до Search з'являється лише в ключа, явно створеного (або перествореного) з--operation search. Вкажіть--operation searchлише для Search,--operation generateлише для Ask, або обидва прапорці разом для ключа, який може викликати обидва:# Лише Search — цей backend ніколи не викликає Ask/Gemini. npx semidex-lite key add --name search-widget --collection my-docs --operation search # Лише Ask/generate (попередня поведінка за замовчуванням, без змін). npx semidex-lite key add --name chat-backend --collection my-docs --operation generate # Обидва — один ключ для backend, який робить і Search, і Ask. npx semidex-lite key add --name full-backend --collection my-docs \ --operation search --operation generate--expires— ISO-дата (2027-01-01) або тривалість (90d,12h). Пропустіть, щоб ключ не мав терміну дії.--requests-per-minute— стійкий ліміт частоти для цього ключа, ціле число від 1 до 6000. Пропустіть для значення за замовчуванням (30/хв). Див. Обмеження частоти запитів нижче.--burst— місткість token bucket (burst) для цього ключа, ціле число від 1 до 1000. Пропустіть для значення за замовчуванням (5).
Токен друкується один раз. Зберігайте його в секрет-менеджері вашого backend —
ніколи у браузерному JavaScript, localStorage, URL чи системі контролю
версій.
Надсилання токена
curl -N -X POST "http://127.0.0.1:8642/api/v1/ask" \
-H "Authorization: Bearer $SEMIDEX_TOKEN" \
-H "Content-Type: application/json" \
-d '{"collection":"my-docs","question":"Які умови повернення товару?"}'const response = await fetch('http://127.0.0.1:8642/api/v1/ask', {
method: 'POST',
headers: {
// Читайте з власного сховища секретів — ніколи не вписуйте токен у код.
Authorization: `Bearer ${process.env.SEMIDEX_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ collection, question: userMessage }),
});Токен приймається лише у заголовку Authorization. Він ігнорується в
query-рядку, cookie та тілі запиту, бо ці місця логуються, кешуються й
поширюються так, як для облікових даних неприпустимо.
Обмеження ключа колекціями
Ключ має доступ лише до тих колекцій, з якими його створено. Збіг точний:
--collection docs дає доступ до docs, але не до docs-a. Колекція поза
областю дії та колекція, якої не існує, повертають однаковий 403, тож
викликач не може дізнатися, які колекції у вас є.
Саме цей механізм слід використовувати, коли один інстанс semidex-lite обслуговує кількох асистентів: дайте кожному backend власний ключ із власними колекціями.
Коди відповідей
| Статус | Код | Значення |
|---|---|---|
| 503 | integration_auth_not_configured | Ключів не налаштовано (або key store нечитабельний). Створіть ключ. |
| 401 | unauthorized | Відсутній, некоректний, невідомий, хибний, відкликаний або прострочений токен. Навмисно нерозрізнювані — щоб не можна було перебирати ключі. |
| 429 | rate_limited | Автентифіковано, але цей ключ перевищив свій ліміт частоти запитів. Див. Обмеження частоти запитів нижче. |
| 403 | forbidden | Автентифіковано, але цей ключ не має доступу до цієї колекції чи операції. |
| 200 | — | Авторизовано; починається SSE-потік. |
401, 429 або 403 визначаються до будь-якого запиту в Qdrant, будь-якого
обчислення ембедингів і будь-якого виклику Gemini — відхилений запит нічого
вам не коштує. 429 визначається навіть раніше за 403 (і до того, як
взагалі розбирається тіло запиту): автентифікований запит завжди витрачає
одну одиницю ліміту, навіть якщо він виявиться некоректним або
націленим на колекцію поза областю дії ключа.
Обмеження частоти запитів
Кожен автентифікований запит Integration API (/api/v1/search,
/api/v1/ask, /api/v2/ask) обмежується за ключем через token bucket:
30 запитів/хв, burst 5, за замовчуванням. Усі три ендпоінти
використовують ОДИН спільний bucket на ключ — виклики Search і Ask по
черзі не подвоюють ефективну частоту.
Задайте інший ліміт для ключа при його створенні:
npx semidex-lite key add --name high-volume-backend \
--collection my-docs \
--requests-per-minute 300 \
--burst 20--requests-per-minute приймає 1–6000; --burst приймає 1–1000. Ліміт
ключа фіксується при створенні — команди key edit не існує; щоб змінити
ліміт, відкличте й створіть ключ заново. key list завжди показує
ефективний ліміт кожного ключа (30/хв burst 5 для ключа, створеного без
цих прапорців), а не сире незадане значення.
При перевищенні ліміту запит повертає 429 з тілом
{ "error": { "code": "rate_limited", "message": "..." } } і заголовком
Retry-After (ціле число секунд — зачекайте щонайменше стільки перед
повторною спробою). Жодна деталь відповіді не розкриває ідентичність ключа
чи його налаштований ліміт.
Семантика скидання та ротації:
- Стан ліміту частоти живе лише в пам'яті процесу сервера. Перезапуск
semidex-lite serveскидає bucket кожного ключа до повного — немає збереженого лічильника "використано цієї хвилини", який переживає перезапуск. - Відкликання ключа не потребує явного очищення: відкликаний токен не
проходить автентифікацію (
401) ще до того, як запускається етап обмеження частоти, тож він більше ніколи не торкається bucket'а цього ключа. Сам bucket пізніше автоматично видаляється збирачем сміття після простою. - Створення нового ключа завжди починається з повного bucket'а
(
burstтокенів доступні одразу) — немає спільного чи успадкованого стану між ключами, навіть для однойменної інтеграції, повторно створеної після відкликання.
Обмеження — прочитайте перед плануванням потужності:
- Немає спільного стану між процесами/репліками. Лімітер зберігається у
пам'яті одного процесу. Якщо ви запускаєте кілька процесів
semidex-lite serveза балансувальником навантаження, кожен процес застосовує налаштований ліміт незалежно — реальна сумарна частота для ключа стаєrequestsPerMinute × кількість процесів, а не тим числом, яке ви налаштували. - Це не захист від DDoS. Це обмежує частоту запитів легітимного, вже
автентифікованого ключа. Це не захищає від переповнення з'єднаннями чи
неавтентифікованого трафіку (він відхиляється раніше, на етапі
401/503, до запуску цього етапу) і не зупиняє того, хто має доступ оператора, від створення нових ключів. - Немає гарантії витрат. Ліміт кількості запитів — не стеля витрат: Ask-запити мають різну вартість Gemini/Qdrant Cloud залежно від вмісту. Для реальної стелі витрат використовуйте власні сповіщення про витрати у провайдера.
Чим semidex-lite і далі не володіє
Автентифікація не змінює власності над розмовою: ваш застосунок і далі володіє історією чату та зберігає її. semidex-lite не зберігає розмови для жодного з endpoint'ів — див. Інтеграція backend: багатоходовий Ask. Ключ ідентифікує backend, що викликає, а не кінцевого користувача; зіставлення користувачів із правами лишається завданням вашого backend.
Ще не реалізовано: віддалена автентифікація Admin API та керування ключами з панелі.
JS-клієнт (semidex-lite/client)
Це рекомендований спосіб інтеграції з semidex-lite — причини нижче.
semidex-lite постачає невеликий, без жодних залежностей ESM-клієнт,
що покриває всі три ендпоінти Integration API (Search, Ask v1, Ask v2). Це
рекомендований спосіб викликати semidex-lite з backend на Node.js — приклади
з прямим fetch/curl теж наведені в цьому README, для інших мов або коли
потрібен повний контроль, але клієнт уже коректно обробляє тонкі місця:
розбір SSE через довільні межі мережевих фрагментів, тайм-аути запитів без
витоку таймерів, підтримку AbortSignal і один типізований клас помилки на
всі випадки збою замість перевірки кодів статусу вручну.
import { createSemidexClient } from 'semidex-lite/client';
const semidex = createSemidexClient({
baseUrl: 'http://127.0.0.1:8642', // без query-рядка, фрагмента чи userinfo
apiKey: process.env.SEMIDEX_TOKEN, // ніколи не передавайте це в браузерний JavaScript
});
// Search — звичайний Promise.
const result = await semidex.search({ collection: 'my-docs', query: 'умови повернення', top: 5 });
console.log(result.results.map((r) => r.sourceFile));
// Ask v1 — асинхронний генератор: sources, нуль або більше answer_delta, потім done.
for await (const event of semidex.askV1({ collection: 'my-docs', question: 'Який термін повернення?' })) {
if (event.type === 'answer_delta') process.stdout.write(event.text);
if (event.type === 'done') console.log('\nцитати:', event.citations);
}
// Ask v2 — той самий контракт подій, плюс блок `conversation`, яким володіє викликач.
// conversationId/summary/recentMessages — ваша відповідальність зберігати —
// див. "Чим semidex-lite і далі не володіє" вище та
// examples/backend-integration-server.mjs, де реальний застосунок зберігав би це.
for await (const event of semidex.askV2({
collection: 'my-docs',
question: 'А які є винятки з цього правила?',
conversation: { conversationId, summary, recentMessages },
})) {
// ... такі самі форми подій, як у askV1(), плюс event.conversation у `done`
}Обробка помилок. Будь-яка помилка — відповідь не 2xx, термінальна
SSE-подія error, тайм-аут, скасований запит, мережева помилка — постає як
ОДИН і той самий типізований SemidexApiError, а не окрема перевірка коду
статусу чи різна форма залежно від того, який саме ендпоінт відмовив:
import { createSemidexClient, SemidexApiError } from 'semidex-lite/client';
try {
await semidex.search({ collection: 'my-docs', query: 'x' });
} catch (err) {
if (err instanceof SemidexApiError) {
console.error(err.status, err.code, err.retryable, err.message);
// err ніколи не містить apiKey чи будь-який інший секрет.
}
}Редіректи ніколи не виконуються. Якщо налаштований baseUrl (або
що-небудь перед ним) відповідає 3xx, клієнт відхиляє запит з
SemidexApiError (retryable: true) замість того, щоб перейти за
редіректом — ваш apiKey/заголовок Authorization та тіло запиту ніколи не
повторюються за адресою з Location, на будь-якому origin, включно з тим
самим.
Повний робочий приклад — мінімальний backend, який викликає браузер,
який сам володіє ключем Semidex та явним зіставленням теми на колекцію,
викликає semidex-lite/client і транслює в браузер одну розмову Ask v2 як
власний потік Server-Sent Events — постачається у файлі
examples/backend-integration-server.mjs.
Він конкретно демонструє архітектуру, яку передбачає кожна інтеграція в
цьому README:
браузер (без секретів) --> ВАШ backend (тримає SEMIDEX_TOKEN, володіє зіставленням колекцій) --> Semidex LiteЗапустіть його проти власного semidex-lite serve:
npx semidex-lite key add --name demo-backend --collection my-docs --operation search --operation generate
SEMIDEX_TOKEN=<token> SEMIDEX_BASE_URL=http://127.0.0.1:8642 SEMIDEX_DOCS_COLLECTION=my-docs \
node examples/backend-integration-server.mjsОголошення типів (.d.ts) постачаються разом із клієнтом для редакторів/
tsc — без кроку збірки, без компіляції TypeScript у цьому репозиторії.
Search: POST /api/v1/search
Версійний, автентифікований аналог внутрішнього пошуку панелі. Використовує
ту саму реалізацію retrieval, що й /api/search (див.
Admin API проти Integration API вище
щодо відмінності) — той самий гібридний dense+sparse ранжування, ті самі
обмежені семантики top/window, та сама фільтрація за тегами/файлом —
але за bearer-ключем замість довіри лише до loopback.
curl -N -X POST "http://127.0.0.1:8642/api/v1/search" \
-H "Authorization: Bearer $SEMIDEX_TOKEN" \
-H "Content-Type: application/json" \
-d '{"collection":"my-docs","query":"умови повернення","top":5}'const response = await fetch('http://127.0.0.1:8642/api/v1/search', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.SEMIDEX_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ collection: 'my-docs', query: 'умови повернення', top: 5 }),
});
const { results } = await response.json();Для backend на Node.js надавайте перевагу
JS-клієнту (semidex.search({ collection, query, top }))
замість прямого fetch — він перевіряє вхідні дані та безкоштовно дає
типізовані помилки.
Поля запиту:
| Поле | Тип | За замовчуванням | Примітка |
|---|---|---|---|
| collection | рядок | — | обов'язково |
| query | рядок | — | обов'язково |
| top | ціле 1–20 | 3 | |
| window | ціле 0–5 | 0 | додаткові фрагменти до/після кожного збігу |
| windowFormat | "compact" | "full" | "compact", коли window > 0, інакше ігнорується | |
| sourceFile | рядок | — | фільтр за одним відомим файлом |
| tags | рядок[] | — | фільтр за тегом (будь-який збіг) |
На відміну від /api/search, /api/v1/search відхиляє будь-яке поле тіла
поза цим списком з 400 bad_request — свідомо жорсткіший публічний
контракт із самого початку, тож помилка в назві поля ніколи мовчки нічого
не робить.
Відповідь містить apiVersion: "v1" і явну, задокументовану форму
результату (sourceFile, chunkIndex, totalChunks, section, text,
context, tags, score, nodeId, nodePath, nodeType, isMatch, а
також windowChunks, коли window > 0) — ніколи сирий внутрішній об'єкт.
Помилки використовують той самий конверт
{ error: { apiVersion, code, message, retryable } }, що й Ask v1/v2 (див.
Коди відповідей вище щодо кодів автентифікації/області
дії/обмеження частоти, спільних для всіх трьох ендпоінтів Integration API);
специфічні для Search коди — це bad_request, not_found, forbidden,
not_implemented, embedding_failed, embedding_unresolved і
embedding_unsupported — Search ніколи не повертає код, пов'язаний із
генерацією (busy, dependency_unavailable, жоден код budget_*), бо
ніколи не викликає провайдера генерації.
Ключ має бути налаштований на операцію search, щоб викликати цей
ендпоінт — див. Створення ключа вище. Наявний ключ,
створений до появи Search, без прапорця --operation, доступний лише для
generate і отримає тут 403 forbidden, доки його не перествориять із
--operation search.
Ask: відповіді на основі вашої бази знань
Ask дає змогу поставити запитання до однієї проіндексованої колекції та отримати відповідь, сформовану Gemini на основі знайдених у ній фрагментів. Модель не отримує всю колекцію: semidex-lite спочатку знаходить обмежений набір релевантних джерел і лише потім передає їх разом із запитанням у Gemini.
Як формується відповідь
Запитання користувача
-> ембединг запиту через модель, закріплену за колекцією
-> гібридний dense+sparse пошук у Qdrant Cloud
-> відбір і нумерація релевантних фрагментів
-> складання обмеженого evidence-контексту
-> Gemini отримує системні правила + evidence + запитання
-> streaming-відповідь із посиланнями [1], [2] ...Модель ембедингів, використана під час запиту, визначається профілем самої колекції. Це важливо: пошук не повинен використовувати іншу модель або іншу розмірність векторів, ніж індексація цієї колекції.
Ask передає у Gemini дві окремі частини:
- системний prompt — правила поведінки моделі;
- користувацький prompt — пронумеровані знайдені фрагменти та запитання.
Системний prompt надсилається через нативний systemInstruction Gemini. Він
вимагає відповідати лише за наданими джерелами, додавати посилання до фактичних
тверджень, відповідати мовою запитання, ігнорувати інструкції всередині
проіндексованих документів і відмовлятися від відповіді, якщо evidence не
містить достатньої інформації. Це зменшує ризик вигаданих відповідей і prompt
injection, але не дає абсолютної гарантії: результат LLM усе одно потрібно
оцінювати за наведеними джерелами.
У поточній версії системний prompt є внутрішньою частиною Ask runtime. Його не
можна змінити через адмін-панель, .env або запит Ask API. Можливість безпечно
задавати власні інструкції може бути додана пізніше разом із валідацією та
обмеженнями, але зараз це не частина публічного контракту.
Інтеграція Ask через власний backend
Основний спосіб використання Ask - виклик із backend вашого асистента. Так сайт,
бот або застосунок може вибрати потрібну базу знань, автентифікувати
користувача, вести історію діалогу та застосувати власні правила доступу перед
зверненням до Semidex. Ask доступний через версійований endpoint
POST /api/v1/ask.
Після локального запуску serve backend може викликати його через fetch:
const collectionByAssistant = {
support: 'company-support',
education: 'course-materials',
};
const collection = collectionByAssistant[assistantId];
if (!collection) throw new Error('Unknown assistant');
const response = await fetch('http://127.0.0.1:8642/api/v1/ask', {
method: 'POST',
headers: {
// Зчитуйте зі свого сховища секретів — ніколи не вписуйте токен у код.
// Див. розділ "Автентифікація Integration API" вище.
Authorization: `Bearer ${process.env.SEMIDEX_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ collection, question: userMessage }),
});Для backend на Node.js надавайте перевагу
JS-клієнту
(for await (const event of semidex.askV1({ collection, question })))
замість прямого fetch — він коректно розбирає SSE-потік через межі
фрагментів і дає один типізований клас помилки на всі випадки збою замість
самописної перевірки Content-Type.
Визначайте або перевіряйте дозволену колекцію на своєму backend. Не дозволяйте неавтентифікованому браузеру передавати довільну назву колекції, інакше він може отримати доступ до бази знань іншого асистента.
Для ручної перевірки API можна використати curl:
curl.exe -N -X POST "http://127.0.0.1:8642/api/v1/ask" `
-H "Authorization: Bearer $env:SEMIDEX_TOKEN" `
-H "Content-Type: application/json" `
-d '{"collection":"my-docs","question":"Які основні вимоги описані в документації?"}'Обов'язкові поля запиту:
collection— назва однієї колекції;question— непорожнє запитання.
Необов'язкове поле scope.sourceFile обмежує пошук одним відомим файлом:
{
"collection": "my-docs",
"question": "Які умови повернення товару?",
"scope": {
"sourceFile": "policies/returns.md"
}
}Відповідь передається як Server-Sent Events (SSE):
sources— фрагменти, відібрані як evidence;answer_delta— частини відповіді під час генерації;done— фінальна відповідь, citations, модель, використання токенів і час;error— структурована помилка.
Ask API v1 є stateless: кожен запит незалежний, sessionId, історія діалогу й
довгострокова пам'ять поки не підтримуються. Він вимагає bearer-токен і
обмежується за частотою на рівні ключа (див.
Автентифікація Integration API та
Обмеження частоти запитів вище). Не відкривайте
порт адмін-сервера безпосередньо в інтернет; для зовнішньої інтеграції
розміщуйте перед ним власний автентифікований backend або reverse proxy з
відповідними обмеженнями.
Інтеграція backend: багатоходовий Ask (/api/v2/ask)
/api/v2/ask — основний спосіб інтеграції багатоходового розмовного
асистента із Semidex Lite — використовуйте його, коли користувач може
ставити уточнювальні запитання. /api/v1/ask залишається доступним і є
правильним вибором для stateless одноходових запитів без жодної історії
розмови (див. вище); v2 є
додатковим, а не заміною. v2 так само stateless, як і v1 — різниця в тому,
що у кожному запиті ВАШ backend надсилає обмежений підсумок і вікно останніх
повідомлень як об'єкт conversation, і Semidex використовує його лише для
цього одного запиту: щоб переформулювати неоднозначні уточнювальні питання
перед retrieval, дати моделі контекст розмови і — лише коли історія стала
достатньо довгою — повернути свіжо перерахований підсумок для збереження.
Semidex Lite не зберігає діалоги на сервері в цій версії, для жодного з
endpoint'ів. Дашборд наразі не має UI для Ask (див. нижче) — можливо,
його буде реалізовано пізніше, — тож наразі /api/v1/ask і /api/v2/ask,
викликані напряму або через власний backend, є способами використати Ask.
{
"collection": "company-support",
"question": "А які винятки?",
"conversation": {
"id": "conv_123",
"summary": "Обговорювали 14-денний термін повернення.",
"recentMessages": [
{ "role": "user", "content": "Скільки часу є на повернення товару?" },
{ "role": "assistant", "content": "У вас є 14 днів від дати доставки." }
]
}
}Хто володіє чатом і хто керує контекстним вікном
Ваш backend є джерелом істини для розмови. Щонайменше він має зберігати
append-only архів повідомлень, поточний rolling summary та індекс першого
повідомлення архіву, яке ще не покрите цим підсумком. Перед кожним запитом він
формує recentMessages від цієї межі й надсилає Semidex підсумок та обмежене
вікно. Semidex не завантажує попередні ходи за conversation.id і не може
відновити чат, який викликач не передав у запиті.
Керування token budget у межах одного запиту виконує Semidex. Викликачу не потрібно самостійно токенізувати повідомлення або знати точний розмір контекстного вікна налаштованої моделі Gemini. Semidex:
- отримує ліміт контексту активної моделі генерації;
- рахує питання, summary, останні повідомлення, службову частину prompt і retrieval evidence в межах одного бюджету;
- резервує місце для evidence та згенерованої відповіді;
- вилучає найстаріші raw-повідомлення лише з поточного prompt, якщо вся передана історія не вміщується (збережений архів викликача не змінюється);
- за потреби переформульовує контекстне уточнення в самостійний retrieval query, але залишає оригінальне питання для фінальної відповіді;
- після успішної відповіді намагається стиснути summary, коли досягнуто налаштованого порога.
Викликач усе одно контролює, що потрапляє в цей процес: яку розмову й колекцію
авторизовано, який збережений summary і recent messages передано та чи буде
збережено повернений стан. Налаштування compaction, які надає Semidex
(ASK_SUMMARY_COMPACTION_THRESHOLD, ASK_SUMMARY_RETAINED_MESSAGES і
ASK_SUMMARY_COMPACTION_TIMEOUT_MS), керують моментом і способом спроби
стиснення, але не перетворюють Semidex на базу даних чатів.
ASK_SUMMARY_COMPACTION_THRESHOLDза замовчуванням дорівнює8повідомленням і визначає, коли Semidex починає намагатися виконати compaction.ASK_SUMMARY_RETAINED_MESSAGESза замовчуванням дорівнює4і визначає, скільки найновіших raw-повідомлень залишаються поза оновленим summary.ASK_SUMMARY_COMPACTION_TIMEOUT_MSза замовчуванням дорівнює6000; timeout залишає поточний summary і boundary без змін та не скасовує вже успішно згенеровану відповідь.
Ці операційні налаштування відрізняються від фіксованих протокольних меж, описаних нижче. Збільшення порога compaction не збільшує ліміт запиту у 200 повідомлень.
Життєвий цикл успішного ходу виглядає так:
backend завантажує архів + summary + boundary
-> формує нестиснені recentMessages
-> POST /api/v2/ask
-> Semidex обмежує контекст, шукає evidence і стримить відповідь
-> Semidex може повернути updatedSummary + compactedMessageCount
-> backend атомарно додає user/assistant хід та оновлює summary + boundaryconversation.id— непрозорий рядок, який генерує й контролює ВАШ backend (наприклад, UUID). Semidex лише повертає його назад у поліconversation.idподіїdone— він ніколи не використовується для пошуку, авторизації чи збереження чогось на сервері.conversation.recentMessages— найновіші ходи{role, content}з ВАШОЇ власної збереженої історії,roleобмежено"user"/"assistant". Semidex додатково обрізає це під реальне вікно контексту моделі за потреби і не зберігає це на диску та не передає стороннім сервісам понад те, що необхідно для обробки цього одного запиту — але "цей один запит" може включати до трьох окремих викликів Gemini, кожен з яких бачить частину або весь цей вміст: необов'язковий виклик переформулювання запиту (щоб уточнити неоднозначне уточнювальне запитання перед retrieval), основний виклик відповіді та необов'язковий виклик compaction підсумку (лише коли розмова перевищилаASK_SUMMARY_COMPACTION_THRESHOLD). Semidex сам нічого з цього не записує на диск чи в будь-яке сховище даних, і нічого з цього не переживає межі цього одного HTTP-запиту в самому процесі Semidex — але воно виходить за межі процесу Semidex, щоб дістатися API Gemini, і підпорядковується власним умовам обробки даних Google для цього API. Якщо це неприйнятно для ваших даних, не надсилайте їх якrecentMessages/summaryвзагалі.conversation.summary— ваш раніше збережений підсумок розмови (пропустіть на першому ході нової розмови). Semidex трактує його, як іrecentMessages, як недовірений контекст розмови — ніколи як retrieval evidence, ніколи як перевірений факт, він ніколи не може перевизначити власні системні інструкції Semidex.done.conversation.summaryChanged/updatedSummary/compactedMessageCount— Semidex перераховує підсумок лише коли розмова перевищує налаштовувану довжину (ASK_SUMMARY_COMPACTION_THRESHOLD), а не в кожному запиті. КолиsummaryChangedдорівнюєtrue: збережітьupdatedSummaryяк нове значення для наступного ходу, і посуньте власну межу request-view рівно наcompactedMessageCount— кількість НАЙСТАРІШИХ повідомлень ізrecentMessages, які ви щойно надіслали і які тепер покритіupdatedSummary. Пропуск цього кроку означає, що ви й далі надсилатимете повідомлення, які Semidex уже згорнув у підсумок, тож compaction фактично ніколи не зменшить обсяг того, що ви надсилаєте, і розмова зростатиме без обмежень. Виконуйте це як одне атомарне оновлення разом зі збереженням підсумку — наприклад,summarizedThroughArchiveIndex += compactedMessageCountуexamples/conversation-manager.mjs, який зберігає повний, лише-для-додавання архів і виводить обмежене вікноrecentMessagesіз цієї межі, а не змінює один масив на місці. КолиsummaryChangedдорівнюєfalse— використовуйте той підсумок і ту межу, які у вас вже є;compactedMessageCountу цьому випадку відсутній.
Протокольні межі. Це фіксовані, не налаштовувані обмеження, які
/api/v2/ask перевіряє на етапі парсингу запиту — запит, що перевищує
будь-яке з них, відхиляється з кодом 400 ще до будь-якого retrieval чи
генерації:
conversation.recentMessages— не більше 200 елементів.contentкожного повідомлення — не більше 50 000 символів.conversation.summary— не більше 8000 символів.conversation.id— не більше 256 символів.
Коли compaction не встигає. Compaction — best-effort: якщо провайдер
генерації недоступний або постійно падає саме на виклику compaction,
summaryChanged залишається false хід за ходом, тож вікно
recentMessages, яке ваш backend продовжує надсилати, ніколи не
зменшується — тоді як ваш архів продовжує зростати на один хід щоразу,
коли сама відповідь успішна. Врешті це вікно перевищить протокольну межу
у 200 елементів, наведену вище.
examples/conversation-manager.mjs
виявляє це локально, перед відправкою, і повертає помилку
client_bounded_context_exceeded замість того, щоб або мовчки обрізати
історію, або дозволити /api/v2/ask відхилити завеликий запит. Звичайний
retry не відновлює цей стан — те саме завелике вікно знову буде
відхилено локально щоразу, тож жоден запит не досягає Semidex, і
провайдер, що відновився, не отримує запиту для compaction. Єдині виходи
— почати нову розмову або застосувати власне ручне/позасистемне
відновлення compaction (самостійно підсумувати й обрізати збережений
архів) — жоден із них не реалізований у цьому демо.
У цьому пакеті постачається готовий до запуску, без залежностей клієнт і
демо — див.
examples/ask-v2-sse-client.mjs (невеликий
SSE-streaming клієнт: відкриває запит, коректно парсить sources/
answer_delta/done/error навіть коли фрейм розділений між мережевими
chunk'ами, і повертає простий об'єкт результату) та
examples/conversation-manager.mjs
(мінімальний приклад ВОЛОДІННЯ станом розмови навколо цього клієнта).
Запустіть CLI-демо напряму проти власного запущеного сервера.
Якщо ви клонували цей репозиторій або працюєте всередині самого
packages/lite/:
QDRANT_URL=... QDRANT_KEY=... GEMINI_API_KEY=... npx semidex-lite serve &
npx semidex-lite key add --name demo --collection my-docs # скопіюйте виведений токен
SEMIDEX_TOKEN=<token> node examples/run-conversation-demo.mjs my-docs "Скільки часу є на повернення товару?" "А які винятки?"Якщо ви встановили semidex-lite як залежність власного проєкту
(npm install semidex-lite), приклад знаходиться всередині node_modules/,
тож шлях інший:
QDRANT_URL=... QDRANT_KEY=... GEMINI_API_KEY=... npx semidex-lite serve &
npx semidex-lite key add --name demo --collection my-docs # скопіюйте виведений токен
SEMIDEX_TOKEN=<token> node node_modules/semidex-lite/examples/run-conversation-demo.mjs my-docs "Скільки часу є на повернення товару?" "А які винятки?"Окремої підкоманди semidex-lite CLI для цього демо немає — це вихідний
код прикладу, який ви запускаєте напряму через node, а не бінарник
пакета.
Компактна форма того, що робить ваш backend навколо цього клієнта:
// Власна персистентність — НЕ частина Semidex. Див.
// examples/conversation-manager.mjs для повної (in-memory, лише демо)
// реалізації цієї форми.
const conversation = await chatStore.loadConversation(conversationId, userId);
const collection = assistantRegistry.resolveAllowedCollection(assistantId); // ніколи не довіряйте назві колекції з браузера
const token = await secrets.getIntegrationApiToken(); // власне сховище секретів — ніколи не вписуйте в код і не логуйте
const result = await askV2({ baseUrl, collection, question: userMessage, conversation, token });
await chatStore.commitTurn({
conversationId, ownerId: userId, expectedVersion: conversation.version,
messages: [{ role: 'user', content: userMessage }, { role: 'assistant', content: result.answer }],
// Посунення межі — НЕ опціональне. Пропуск означає, що кожен наступний
// запит продовжуватиме надсилати повідомлення, які Semidex уже згорнув
// у підсумок, і compaction ніколи фактично не зменшить обсяг того, що
// ви надсилаєте.
...(result.summaryChanged ? {
updatedSummary: result.updatedSummary,
summarizedThroughArchiveIndex: conversation.summarizedThroughArchiveIndex + result.compactedMessageCount,
} : {}),
});examples/conversation-manager.mjs зберігає стан в in-memory Map — це
демо, а не production-персистентність. Вона втрачається при кожному
перезапуску і ніколи не поширюється між кількома репліками сервера. Реальний
backend має замінити її на PostgreSQL, Redis, MongoDB, SQLite чи будь-яке
інше сховище, яке вже використовується, бажано через один атомарний виклик у
формі commitTurn (ніколи не через два окремі виклики з перевіркою версії
для "додати повідомлення" та "оновити підсумок" — якщо перший виклик
інкрементує збережену версію, аргумент версії другого виклику вже застарілий,
і повідомлення можуть зберегтися без відповідного підсумку).
/api/v1/ask залишається доступним без змін для stateless одноходових
запитів без жодного стану розмови — v2 є додатковим, а не заміною.
Див.
Ask API v2 — Bounded Conversational Context
для повного обґрунтування дизайну, контракту ConversationStore, який Semidex
розроблений підтримувати в майбутньому релізі, і правил
token-budgeting/rewriting/compaction. Design-документи в цьому репозиторії —
лише англійською.
Ручна перевірка без побудови інтеграції
Вбудований дашборд наразі не має панелі Ask — можливо, її буде реалізовано
пізніше, але поки що він надає лише ручний пошук по проіндексованій
колекції через власний внутрішній, неверсійний /api/search (без
генерації, без citations, без SSE, без bearer-ключа — див.
Admin API проти Integration API вище).
Наразі немає браузерного способу викликати версійні /api/v1/search,
/api/v1/ask чи /api/v2/ask; приклади curl вище та runnable-приклади в
examples/ — це способи перевірити їх вручну до побудови повної інтеграції:
- Налаштуйте
QDRANT_URL,QDRANT_KEYіGEMINI_API_KEY. - Проіндексуйте файл або папку в колекцію через
semidex-lite index. - Запустіть сервер командою
npx semidex-lite serve. - Створіть ключ (
semidex-lite key add ...) і викличте/api/v1/search,/api/v1/askчи/api/v2/askнапряму черезcurl, або запустітьexamples/backend-integration-server.mjs/examples/run-conversation-demo.mjs, щоб перевірити їх через JS-клієнт.
Live acceptance релізу лише для супровідників
Супровідники репозиторію можуть перевірити саме запакований і чисто встановлений npm-артефакт, а не вихідний код checkout:
SEMIDEX_LITE_RELEASE_LIVE=1 npm run accept:lite-release-liveУ Windows PowerShell:
$env:SEMIDEX_LITE_RELEASE_LIVE = "1"
npm run accept:lite-release-liveЦя перевірка навмисно не входить до npm test, CI чи опублікованого пакета.
Вона потребує справжніх QDRANT_URL, QDRANT_KEY і GEMINI_API_KEY,
завантажує npm-залежності, виконує реальні Qdrant Cloud inference та Gemini
generation, створює два ключі Integration API в ізольованому тимчасовому
SEMIDEX_HOME, а також створює й видаляє одну унікальну колекцію harness
(doctor probe окремо керує власною короткочасною probe-колекцією). Harness
відмовляється працювати при збігу назви, видаляє лише точну колекцію
власного запуску, не друкує bearer-токени чи ключі провайдерів і повертає
ACCEPT лише після успішного cleanup. JSON-звіт за замовчуванням записується
в .tmp/semidex-lite-release-live-report.json.
Це доповнення до scripts/ask-v2-live-acceptance.mjs: старий скрипт тестує
розширену багатокрокову розмову й compaction безпосередньо з вихідного коду,
а release harness перевіряє пакування, чисту інсталяцію, CLI,
автентифікацію/scoping/rate limiting та Ask v1/v2 як один готовий продукт.
SEMIDEX_HOME
Дані застосунку (config.json, settings.json, кеш токенайзера) зберігаються
поза встановленим пакетом у стандартній для відповідної ОС директорії:
- Windows:
%LOCALAPPDATA%\semidex-lite - macOS:
~/Library/Application Support/semidex-lite - Linux:
$XDG_DATA_HOME/semidex-lite(або~/.local/share/semidex-lite, якщо змінну не задано)
Шлях можна змінити за допомогою змінної оточення SEMIDEX_HOME. Ця директорія
належить лише Lite і ніколи не використовується спільно з повною версією
semidex.
Обмеження
- Немає локальних провайдерів ембедингів або генерації (Ollama, локальний ONNX): підтримуються лише Qdrant Cloud Inference та Gemini.
- Немає генерації тегів, комбінованого LLM-проходу context+tags і перевірок CUDA/DirectML.
- Розширені налаштування chunking/retrieval із Settings UI повної версії
semidex тут не показуються. Dense-модель і підтримувані несекретні параметри
можна налаштувати, але облікові дані Qdrant та Gemini поки потрібно задавати
через системні змінні оточення або локальний
.env. - Settings API надає менший дозволений перелік параметрів, ніж повна версія
semidex. Спроба записати непідтримуваний параметр повертає
not_available_in_lite.
Якщо вам потрібні можливості поза цими межами, використовуйте повну версію semidex.
Ліцензія
Semidex Lite поширюється за ліцензією MIT. Ви можете використовувати, копіювати, змінювати, публікувати й поширювати код, зокрема у комерційних продуктах, за умовами ліцензії та зі збереженням необхідного повідомлення про авторські права. Програмне забезпечення надається «як є», без гарантій.
