doc2vec 2.3.0 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -11,7 +11,7 @@ The primary goal is to prepare documentation content for Retrieval-Augmented Gen
11
11
  ## Key Features
12
12
 
13
13
  * **Website Crawling:** Recursively crawls websites starting from a given base URL.
14
- * **Sitemap Support:** Extracts URLs from XML sitemaps to discover pages not linked in navigation.
14
+ * **Sitemap Support:** Extracts URLs from XML sitemaps to discover pages not linked in navigation. When sitemaps include `<lastmod>` dates, pages are skipped without any HTTP requests if the date hasn't changed since the last sync.
15
15
  * **PDF Support:** Automatically downloads and processes PDF files linked from websites.
16
16
  * **GitHub Issues Integration:** Retrieves GitHub issues and comments, processing them into searchable chunks.
17
17
  * **Zendesk Integration:** Fetches support tickets and knowledge base articles from Zendesk, converting them to searchable chunks.
@@ -28,6 +28,7 @@ The primary goal is to prepare documentation content for Retrieval-Augmented Gen
28
28
  * **Content Extraction:** Uses Puppeteer for rendering JavaScript-heavy pages and `@mozilla/readability` to extract the main article content.
29
29
  * **Smart H1 Preservation:** Automatically extracts and preserves page titles (H1 headings) that Readability might strip as "page chrome", ensuring proper heading hierarchy.
30
30
  * **Flexible Content Selectors:** Supports multiple content container patterns (`.docs-content`, `.doc-content`, `.markdown-body`, `article`, etc.) for better compatibility with various documentation sites.
31
+ * **Tabbed Content Support:** Automatically detects WAI-ARIA tabs (`role="tab"` / `role="tabpanel"`) and injects tab labels into panel content so each tab's context is preserved after conversion to Markdown.
31
32
  * **HTML to Markdown:** Converts extracted HTML to clean Markdown using `turndown`, preserving code blocks and basic formatting.
32
33
  * **Clean Heading Text:** Automatically removes anchor links (like `[](#section-id)`) from heading text for cleaner hierarchy display.
33
34
  * **Intelligent Chunking:** Splits Markdown content into manageable chunks based on headings and token limits, preserving context.
@@ -35,7 +36,13 @@ The primary goal is to prepare documentation content for Retrieval-Augmented Gen
35
36
  * **Vector Storage:** Supports storing chunks, metadata, and embeddings in:
36
37
  * **SQLite:** Using `better-sqlite3` and the `sqlite-vec` extension for efficient vector search.
37
38
  * **Qdrant:** A dedicated vector database, using the `@qdrant/js-client-rest`.
38
- * **Change Detection:** Uses content hashing to detect changes and only re-embeds and updates chunks that have actually been modified.
39
+ * **Multi-Layer Change Detection:** Four layers of change detection minimize unnecessary re-processing:
40
+ 1. **Sitemap `lastmod`:** When available, compares the sitemap's `<lastmod>` date against the stored value — skips without any HTTP request. Child URLs inherit `lastmod` from their most specific parent directory.
41
+ 2. **ETag via HEAD request:** For URLs without `lastmod`, sends a lightweight HEAD request and compares the ETag header against the stored value. Adaptive backoff prevents rate limiting (starts at 0ms delay, increases on 429 responses, decays on success).
42
+ 3. **Content hash comparison:** After full page load, compares chunk content hashes against stored values — skips embedding if content is unchanged.
43
+ 4. **Embedding:** Only re-embeds chunks when content has actually changed.
44
+
45
+ ETag and lastmod values are only stored when chunking and embedding succeed, ensuring failed pages are retried on the next run.
39
46
  * **Incremental Updates:** For GitHub and Zendesk sources, tracks the last run date to only fetch new or updated issues/tickets.
40
47
  * **Cleanup:** Removes obsolete chunks from the database corresponding to pages or files that are no longer found during processing.
41
48
  * **Configuration:** Driven by a YAML configuration file (`config.yaml`) specifying sites, repositories, local directories, Zendesk instances, database types, metadata, and other parameters.
@@ -138,6 +145,9 @@ Configuration is managed through two files:
138
145
  OPENAI_API_KEY="sk-..."
139
146
  OPENAI_MODEL="text-embedding-3-large" # Optional, defaults to text-embedding-3-large
140
147
 
148
+ # Optional: Embedding dimension size (defaults to 3072)
149
+ EMBEDDING_DIMENSION="3072"
150
+
141
151
  # Required: Your Azure OpenAI credentials (if using Azure provider)
142
152
  AZURE_OPENAI_KEY="your-azure-key"
143
153
  AZURE_OPENAI_ENDPOINT="https://your-resource.openai.azure.com"
@@ -216,6 +226,10 @@ Configuration is managed through two files:
216
226
  * `qdrant_port`: (Optional) Port for the Qdrant REST API. Defaults to `443` if `qdrant_url` starts with `https`, otherwise `6333`.
217
227
  * `collection_name`: (Optional) Name of the Qdrant collection to use. Defaults to `<product_name>_<version>` (lowercased, spaces replaced with underscores).
218
228
 
229
+ Optional embedding configuration:
230
+ * `embedding.provider`: Provider for embeddings (`openai` or `azure`).
231
+ * `embedding.dimension`: Embedding vector size. Defaults to `3072` when not set.
232
+
219
233
  **Example (`config.yaml`):**
220
234
  ```yaml
221
235
  # Optional: Configure embedding provider
@@ -223,6 +237,7 @@ Configuration is managed through two files:
223
237
  # Defaults to OpenAI if not specified
224
238
  embedding:
225
239
  provider: 'openai' # or 'azure'
240
+ dimension: 3072 # Optional, defaults to 3072
226
241
  openai:
227
242
  api_key: '${OPENAI_API_KEY}' # Optional, uses env var by default
228
243
  model: 'text-embedding-3-large' # Optional, defaults to text-embedding-3-large
@@ -496,12 +511,16 @@ If you don't specify a config path, it will look for config.yaml in the current
496
511
  2. **Process by Source Type:**
497
512
  - **For Websites:**
498
513
  * Start at the base `url`.
499
- * If `sitemap_url` is provided, fetch and parse the sitemap to extract additional URLs.
500
- * Use Puppeteer (`processPage`) to fetch and render HTML for web pages.
514
+ * If `sitemap_url` is provided, fetch and parse the sitemap to extract URLs and `lastmod` dates.
515
+ * Pre-seed the crawl queue with known URLs from the database (ensures link discovery isn't lost when pages are skipped).
516
+ * For each URL, apply multi-layer change detection:
517
+ 1. If `lastmod` is available (from sitemap), compare against stored value — skip if unchanged.
518
+ 2. Otherwise, send a HEAD request and compare the ETag — skip if unchanged. Adaptive backoff prevents rate limiting.
519
+ 3. If no skip, use Puppeteer (`processPage`) to fetch and render the full page.
501
520
  * For PDF URLs, download and extract text using Mozilla's PDF.js.
502
521
  * Use Readability to extract main content from HTML pages.
503
522
  * Sanitize HTML and convert to Markdown using Turndown.
504
- * Use `axios`/`cheerio` on HTML pages to find new links to add to the crawl queue.
523
+ * Discover links from the rendered DOM to add to the crawl queue.
505
524
  * Keep track of all visited URLs.
506
525
  - **For GitHub Repositories:**
507
526
  * Fetch issues and comments using the GitHub API.
@@ -520,14 +539,63 @@ If you don't specify a config path, it will look for config.yaml in the current
520
539
  * Track last run date to support incremental updates.
521
540
  3. **Process Content:** For each processed page, issue, or file:
522
541
  * **Chunk:** Split Markdown into smaller `DocumentChunk` objects based on headings and size.
523
- * **Hash Check:** Generate a hash of the chunk content. Check if a chunk with the same ID exists in the DB and if its hash matches.
524
- * **Embed (if needed):** If the chunk is new or changed, call the OpenAI API (`createEmbeddings`) to get the vector embedding.
525
- * **Store:** Insert or update the chunk, metadata, hash, and embedding in the database (SQLite `vec_items` table or Qdrant collection).
542
+ * **URL-Level Change Detection:** Compute content hashes for all new chunks and compare them against stored hashes for that URL. If all hashes match, the entire URL is skipped (no embedding or DB writes needed).
543
+ * **Re-process (if changed):** If any hash differs, delete all existing chunks for the URL and re-embed/insert all new chunks. This ensures consistent `chunk_index`/`total_chunks` values and eliminates orphaned chunks when content shifts (e.g., a paragraph is added in the middle).
544
+ * **Embed:** Call the OpenAI API (`createEmbeddings`) to get the vector embedding for each chunk.
545
+ * **Store:** Insert the chunk, metadata, hash, and embedding in the database (SQLite `vec_items` table or Qdrant collection).
526
546
  4. **Cleanup:** After processing, remove any obsolete chunks from the database.
527
547
  4. **Complete:** Log completion status.
528
548
 
529
549
  ## Recent Changes
530
550
 
551
+ ### Multi-Layer Change Detection for Websites
552
+ - **Sitemap `lastmod` support:** When a sitemap includes `<lastmod>` dates, pages are skipped entirely if the date hasn't changed — no HEAD request, no Puppeteer load, no chunking. One sitemap fetch replaces hundreds of individual HEAD requests.
553
+ - **`lastmod` inheritance:** Child URLs without their own `<lastmod>` inherit from the most specific parent directory URL in the sitemap (e.g., `/docs/2.10.x/reference/cli/` inherits from `/docs/2.10.x/`).
554
+ - **ETag-based change detection:** For URLs not in the sitemap, a HEAD request compares the ETag header against the stored value. Pages with unchanged ETags are skipped without a full page load.
555
+ - **Adaptive HEAD request backoff:** Starts with no delay between HEAD requests. On 429 (rate limit), backs off starting at 200ms and doubles up to 5s. On success, the delay halves back toward zero. Prevents burst rate limiting while maximizing throughput.
556
+ - **Processing success gating:** ETag and lastmod values are only stored in metadata when chunking and embedding succeed. If processing fails, the next run will retry the page instead of incorrectly skipping it.
557
+ - **`parseSitemap` now returns a `Map<string, string | undefined>`** (URL → lastmod) instead of `string[]`, enabling lastmod data to flow through the pipeline.
558
+ - **`processPageContent` callback returns `boolean`** (true = success, false = failure) to signal whether metadata should be persisted.
559
+
560
+ ### Puppeteer Resilience Improvements
561
+ - Browser restart escalation when protocol errors persist after page recreation
562
+ - Periodic browser restart every 50 pages to prevent memory accumulation
563
+ - Added `--disable-dev-shm-usage` Chrome flag (critical in Docker where `/dev/shm` defaults to 64MB)
564
+ - Added `--disable-gpu` and `--disable-extensions` flags
565
+
566
+ ### HTTP 429 Retry for Full Page Processing
567
+ - Added `Retry-After` header parsing (supports seconds and HTTP-date formats)
568
+ - Per-URL retry tracking (max 3 attempts) with configurable delay (30s default, 120s cap)
569
+ - Queue re-insertion on retry to allow other URLs to be processed while waiting
570
+
571
+ ### Qdrant Filter Fix
572
+ - Fixed `removeChunksByUrlQdrant` to use `match: { value: url }` (exact match) instead of `match: { text: url }` (full-text tokenized search), which was causing cross-URL chunk deletion
573
+
574
+ ### URL Processing Fix
575
+ - Fixed `shouldProcessUrl` to return `true` for paths ending with `/`, preventing version-like URLs (e.g., `/app/2.1.x/`) from being incorrectly rejected by `path.extname`
576
+
577
+ ### Tabbed Content Support
578
+ - Automatically detects tabbed UI components using the WAI-ARIA tabs pattern (`role="tab"` + `role="tabpanel"`)
579
+ - Injects tab labels (e.g., "Anthropic v1/messages", "OpenAI-compatible") as bold headings into each panel's content
580
+ - Makes hidden tab panels visible so all tab content is captured, not just the selected tab
581
+ - Handles pages with multiple tab groups that share the same panel IDs
582
+ - Falls back to positional matching when `aria-controls` attributes are missing
583
+ - Works in both the Puppeteer crawl pipeline and the standalone `convertHtmlToMarkdown` method
584
+
585
+ ### URL-Level Change Detection
586
+ - Replaced per-chunk hash comparison with URL-level change detection across all source types (website, local directory, code, Zendesk)
587
+ - Computes content hashes for all chunks of a URL and compares them against stored hashes in a single DB query
588
+ - Unchanged URLs are skipped entirely (no embedding API calls, no DB writes)
589
+ - Changed URLs get all old chunks deleted and fresh chunks inserted with correct `chunk_index` and `total_chunks`
590
+ - Eliminates orphaned chunks and inconsistent metadata that occurred when content shifted (e.g., a paragraph added in the middle)
591
+ - Consolidated four duplicated chunk processing loops into a single shared `processChunksForUrl` method
592
+
593
+ ### Puppeteer Resilience
594
+ - Added `protocolTimeout: 60000` to browser launch to fail faster on stuck protocol calls (down from default 180s)
595
+ - Added `evaluateWithTimeout` helper that wraps `page.evaluate` calls with a 30-second timeout to prevent indefinite hangs on pages with heavy/infinite JavaScript
596
+ - Added `about:blank` navigation between pages to clear lingering JavaScript, timers, WebSocket connections, and event listeners
597
+ - Added automatic page recreation after errors: when a page times out or errors, the next URL gets a fresh tab instead of reusing the potentially corrupted page
598
+
531
599
  ### Word Document Support
532
600
  - Added support for legacy `.doc` files using the `word-extractor` library
533
601
  - Added support for modern `.docx` files using the `mammoth` library