Demo · 9 September 2026

TenderLens

TenderLens accepts HTML, PDF, XML, JSON and TXT, maps documents to one contract and splits them into stable chunks. Indexing runs through NATS JetStream. bge-m3 in MWS produces embeddings, pgvector finds nearby fragments, and gpt-oss-120b generates an answer only from retrieved context and returns sources.

Implementation history: since 20 August 2026. Public deployment: since 7 September 2026. Real MWS models connected on 9 September 2026.Version 6c005b6e10 interface screenshots

Published deployment with real MWS models, pgvector, NATS and a synthetic corpus

01 / The task

Document search with FastAPI and pgvector

The task is to build verifiable search across heterogeneous tender files. The path from source document to answer remains traceable as document → chunk → embedding → hit → citation, so every result can be mapped back to a specific fragment.

Use case: Searching and reviewing a document set.

Work done

  • Document normalization, event-driven indexing and chunk storage in PostgreSQL.
  • Search and Ask APIs with sources, an explicit no-context response and a relevance threshold.
  • An MWS provider for embeddings and Chat Completions, with deployment-ID and vector-dimension validation.
  • A public deployment with per-session and global rate limits. The MWS key stays on the server.

02 / Implementation

What sets the implementation apart

Every answer follows one trace: document → chunk → bge-m3 → pgvector → gpt-oss-120b → citation. Replayed events do not duplicate chunks. An unrelated query is rejected before generation. The corpus is synthetic; the queue, indexer, database, vector search and both MWS models run for real.

Screenshots

Screens, forms, errors

Screenshots, diagrams and source evidence retain their original language; captions and descriptions are translated.

03 / Technology

Stack

Python / FastAPI 36.6%Python parsers / indexing 35.2%JavaScript / HTML / CSS 15%Python / pgvector 9.8%Python / NATS 3.4%

Shares use Git application-file bytes at pinned revisions. Each file belongs to one subsystem under scripts/generate-stack-share.mjs rules; tests, assets, dependencies and generated files are excluded. Rounded to 0.1%. This describes code composition, not proficiency.

FastAPI / Pydantic

HTTP contracts, validation, Search and Ask.

MWS Model Hub

bge-m3 for embeddings; gpt-oss-120b for context-grounded answers.

NATS JetStream

Event delivery to the indexer and replay.

PostgreSQL / pgvector

Documents, attachments, chunks and cosine search.

SQLAlchemy / Python

Normalization, extraction, chunking and content_hash checks.

04 / Decisions

How the project works

  • The seed publishes TenderChangedV1 through NATS; the indexer extracts text and atomically replaces chunks when content_hash changes.
  • bge-m3 returns 1024-dimensional embeddings. The indexer stores the model name with every chunk, and PostgreSQL VECTOR(1024) performs cosine search.
  • Ask sends only retrieved fragments to gpt-oss-120b. Documents in the prompt are marked as data, not instructions.
  • The 0.40 threshold was selected after measuring relevant and unrelated queries. If no suitable chunks exist, the generation endpoint is not called.
  • The external crawler is disabled on the public deployment. The corpus is synthetic; the queue, database, vector search and MWS inference run for real.
Components of the published demo
Components of the published demo

05 / Verification

Demo workflows

  1. Find the server document: the first result should mention a 36-month warranty and show the bge-m3 label.
  2. Switch to Ask: gpt-oss-120b should answer from the retrieved fragments and show its sources.
  3. Choose No context: when the score is below 0.40, the service returns a refusal without calling the generation model.
  4. Check corpus state: four documents, 16 chunks and ready on every record.

Check results

  • Ten screenshots of version 6c005b6 were published: desktop, mobile, Search, Ask, no-context and validation.
  • Four ready documents were reindexed into 16 bge-m3 vectors with dimension 1024.
  • In the 9 September 2026 gallery browser check, desktop Search took 298 ms, Ask 1145 ms and no-context 115 ms; mobile Search took 137 ms and Ask 1936 ms. These are single measurements, not a benchmark.
  • Relevant test queries produced top cosine scores of 0.604–0.927; two unrelated queries produced 0.278 and 0.370. The production threshold is 0.40.
  • A current snapshot of the two TenderLens containers shows about 121.4 MiB RAM combined at idle. Models run in MWS and consume no VPS RAM or GPU.
  • At the 9 September 2026 snapshot, about 5.05 GiB RAM and 105.7 GiB disk space were available on the VPS.

Limitations

  • Four synthetic documents and 16 chunks form a small demo corpus rather than a real procurement database.
  • Model quality was not measured on an independent dataset. The displayed cosine scores cover only the checks run on 9 September 2026.
  • The limit is five Search/Ask requests per minute per session and twelve requests per minute for the whole process. User queries are not stored.
  • Inference depends on two MWS deployments. When the provider is unavailable, readiness fails and there is no automatic fallback to fake mode.
  • Worker health confirms its startup connection; there is no separate stuck-computation detector.

Technical notes

Modules, API, data model, setup and check results.

Open RU textbook →Documentation →Materials ↓