Skip to content

Phase 8: file-type-aware embedding model routing, dual-model search, and incremental indexing#4

Merged
jsilvanus merged 5 commits into
mainfrom
copilot/enhance-plan-implement-models
Mar 31, 2026
Merged

Phase 8: file-type-aware embedding model routing, dual-model search, and incremental indexing#4
jsilvanus merged 5 commits into
mainfrom
copilot/enhance-plan-implement-models

Conversation

Copilot AI commented Mar 31, 2026

Copy link
Copy Markdown
Contributor

Different content types benefit from different embedding models — code-aware models (e.g. nomic-embed-code) capture syntax-rich vocabulary better than general text models. This adds a routing layer that selects the optimal model per blob based on file extension, dual-model search that queries both embedding spaces and merges results, and an incremental --since flag for the indexer.

New modules

  • src/core/embedding/fileType.ts — classifies paths as 'code' | 'text' | 'other' via CODE_EXTENSIONS / TEXT_EXTENSIONS sets; exports getFileCategory(path)
  • src/core/embedding/router.tsRoutingProvider wraps two providers; providerForFile(path) picks the active one for indexing

Core changes

  • IndexerOptions gains optional codeProvider; when set, RoutingProvider is used per blob; single-provider path unchanged
  • IndexerOptions gains optional since; restricts which commits are indexed (see CLI section)
  • embeddings schema gains nullable file_type column recording which category produced the embedding
  • StoreBlobArgs accepts optional fileType and persists it in the transaction
  • vectorSearch gains optional model filter and a new mergeSearchResults export for combining results from different embedding spaces

CLI / config

Two new env vars; default behaviour (single model) is unaffected:

# Dual-model indexing
GITSEMA_TEXT_MODEL=nomic-embed-text \
GITSEMA_CODE_MODEL=nomic-embed-code \
  gitsema index

# gitsema status now shows both models when they differ
# scripts/embed.ts accepts optional filePath to demo routing

When GITSEMA_CODE_MODEL differs from GITSEMA_TEXT_MODEL, gitsema search embeds the query with both models, runs two vector searches (each filtered to embeddings produced by the matching model), and merges results keeping the best score per blob.

# Incremental indexing — accepts a date, tag, or commit
gitsema index --since 2024-01-01   # date → --after= filter
gitsema index --since v1.2.0       # tag  → v1.2.0..HEAD range
gitsema index --since HEAD~100     # symbolic ref or commit hash

Plan

docs/PLAN.md gains a new Phase 8 section with the full design rationale; former Phase 8 (Performance) → 9, Smarter semantics → 10, Advanced + MCP → 11.

Original prompt

Do we have different models for different type of files? Code aware embed for code and text for txt and embed?

Please enhance plan. Add a phase after phase 7 (new 8) that implements two models. Plan this.

Agent-Logs-Url: https://github.com/jsilvanus/gitsema/sessions/752fcb60-2360-4515-941e-46be607ff46c

Co-authored-by: jsilvanus <22452468+jsilvanus@users.noreply.github.com>
Copilot AI changed the title [WIP] Enhance plan to implement two different models for file types Phase 8: file-type-aware embedding model routing Mar 31, 2026
Copilot AI requested a review from jsilvanus March 31, 2026 11:56
@jsilvanus

Copy link
Copy Markdown
Owner

@copilot implement phase 8.

Copilot AI commented Mar 31, 2026

Copy link
Copy Markdown
Contributor Author

@copilot implement phase 8.

Phase 8 is fully implemented (cf39221). Here's what was built:

New modules

  • src/core/embedding/fileType.ts — classifies any file path as code, text, or other by extension (30+ code extensions, markdown/rst/txt/etc. as text)
  • src/core/embedding/router.tsRoutingProvider wraps two providers and picks the right one per file; search queries always use the text provider

Core wiring

  • IndexerOptions has optional codeProvider; when set, each blob is routed to its best-fit model; single-model behaviour unchanged
  • embeddings table gains a nullable file_type column (both in Drizzle schema and the raw DDL); existing databases are auto-migrated on next startup via ALTER TABLE
  • blobStore stores the fileType alongside each embedding

CLI

# Dual-model indexing
GITSEMA_TEXT_MODEL=nomic-embed-text \
GITSEMA_CODE_MODEL=nomic-embed-code \
  gitsema index

# gitsema status reports both models when they differ
# scripts/embed.ts <text> <filePath> shows which model was selected

Single-model (GITSEMA_MODEL) usage is fully backward-compatible.

@jsilvanus jsilvanus marked this pull request as ready for review March 31, 2026 12:27
@jsilvanus

Copy link
Copy Markdown
Owner

@copilot search queries should use both models and search with both!

Fix merge conflicts.

If we added time based constraints, it should so support tag and commit in addition to time.

Copilot AI commented Mar 31, 2026

Copy link
Copy Markdown
Contributor Author

@copilot search queries should use both models and search with both!

Fix merge conflicts....

Done in 3a55300. Three things addressed:

Search uses both models (when configured)
gitsema search now embeds the query with both GITSEMA_TEXT_MODEL and GITSEMA_CODE_MODEL, runs two separate vector searches (each filtered to embeddings produced by the matching model), then merges and deduplicates results keeping the best score per blob. Single-model behaviour is unchanged.

search.ts was inconsistent — it still used GITSEMA_MODEL / single-provider. Updated to use GITSEMA_TEXT_MODEL / GITSEMA_CODE_MODEL to match index.ts.

--since now accepts dates, tags, and commits

gitsema index --since 2024-01-01        # date → git rev-list --after=
gitsema index --since v1.2.0            # tag → v1.2.0..HEAD range
gitsema index --since HEAD~100          # symbolic ref → HEAD~100..HEAD
gitsema index --since abc1234           # commit hash → abc1234..HEAD

Copilot AI changed the title Phase 8: file-type-aware embedding model routing Phase 8: file-type-aware embedding model routing, dual-model search, and incremental indexing Mar 31, 2026
@jsilvanus

Copy link
Copy Markdown
Owner

@copilot merge conflicts! Fix!

…implement-models

# Conflicts:
#	src/cli/commands/search.ts
#	src/core/indexing/blobStore.ts
#	src/core/indexing/indexer.ts
#	src/core/search/vectorSearch.ts

Co-authored-by: jsilvanus <22452468+jsilvanus@users.noreply.github.com>

Copilot AI commented Mar 31, 2026

Copy link
Copy Markdown
Contributor Author

@copilot merge conflicts! Fix!

Fixed in 1f6e5bf. Merged origin/main (Phases 6 & 7) into this branch and resolved four conflicts:

  • blobStore.ts — both FileCategory (Phase 8) and CommitEntry (Phase 6) imports kept
  • indexer.ts — Phase 8's since option kept alongside Phase 6's commit-map Phase B walk
  • vectorSearch.ts — merged model filter option (Phase 8) with recent/alpha/before/after time options (Phase 7); mergeSearchResults retained
  • search.ts — dual-model path now passes all time options (recent, alpha, before, after) through to both vectorSearch calls; orphaned old single-model block removed

@jsilvanus jsilvanus merged commit 2132b3a into main Mar 31, 2026
@jsilvanus jsilvanus deleted the copilot/enhance-plan-implement-models branch April 1, 2026 20:50
Copilot AI added a commit that referenced this pull request May 25, 2026
…x MCP version

- Issue #4: Remove redundant core/ subdirectory in search/ (re-export
  shims pointing to analysis/). Update internal imports to reference
  analysis/ directly. Delete empty core/ directory.
- Issue #8 (Low): Delete phase41plus.ts placeholder file
- Issue #9 (Low): Fix MCP server version from hardcoded '0.0.1' to
  dynamically read from package.json

Agent-Logs-Url: https://github.com/jsilvanus/gitsema/sessions/727a3fda-8cf6-4606-9e8f-134ee622a27f

Co-authored-by: jsilvanus <22452468+jsilvanus@users.noreply.github.com>
jsilvanus pushed a commit that referenced this pull request Jun 21, 2026
Codex review correctly flagged that ModelProfile.level is persisted and
displayed but never read by indexStartCommand or searchCommand — verified
against src/cli/commands/index.ts and search.ts. Goal #4 was overstated as
fully done; documented it as a deviation (stored but inert) instead.
jsilvanus pushed a commit that referenced this pull request Jul 1, 2026
gitsema models add <name> --level <level> saved the value but nothing
consumed it. index start now falls back to a model's saved level via
resolveModelLevelChunker() (skipping the fallback, not guessing, when
--text-model/--code-model have genuinely conflicting saved levels), and
search falls back via mapModelLevelToSearchLevel() using the text model
only (queries always embed via the text model, so no conflict there),
applied ahead of the existing embed_config auto-recall.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QC6rh1w375EogFiwncv5uD
jsilvanus pushed a commit that referenced this pull request Jul 1, 2026
CLAUDE.md's "search always uses the text provider" design note was stale:
searchCommand's dualModel branch already embeds the query with both the
text and code providers and merges results whenever --text-model and
--code-model differ. The Phase 77 Goal #4 fallback only consulted the text
model's saved level, silently ignoring the code model's — corrected to
mirror index start's conflict-safe agreement logic via a new
resolveAgreedLevel() helper (skip the fallback, don't guess, when the two
models' saved levels disagree). Updated CLAUDE.md's stale design note and
added test coverage for the dual-model conflict cases.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QC6rh1w375EogFiwncv5uD
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants