Skip to content

Persist relevance scores in the SQLite semantic ref index - #324

Open
Bernhard Merkle (bmerkle) wants to merge 1 commit into
microsoft:mainfrom
bmerkle:fix-semrefindex-score-persistence
Open

Bernhard Merkle (bmerkle) wants to merge 1 commit into
microsoft:mainfrom
bmerkle:fix-semrefindex-score-persistence

Conversation

@bmerkle

Copy link
Copy Markdown
Collaborator

Fixes #321.

SqliteTermToSemanticRefIndex discarded the score of a ScoredSemanticRefOrdinal on write and returned a hardcoded 1.0 on read, so result ordering differed from the memory backend.

  • Add score REAL NOT NULL DEFAULT 1.0 to SemanticRefIndex; existing databases are migrated in init_db_schema via ALTER TABLE (old rows get 1.0).
  • Persist/return scores in add_term, add_terms_batch, lookup_term, serialize, deserialize; order lookups by rowid to match the memory backend's insertion order.
  • Tests (both backends): score round-trip, deserialize keeps scores, duplicate-pair parity, legacy-table migration.

Note: the "duplicate pairs" divergence mentioned in the issue does not occur — the table has no unique constraint, so INSERT OR IGNORE never dedupes and both backends append duplicates. A test pins this.

🤖 Generated with Claude Code

SqliteTermToSemanticRefIndex dropped the score of a
ScoredSemanticRefOrdinal on write and returned a fabricated 1.0 on read,
so rankings diverged from the memory backend. Add a score column
(migrating existing databases) and persist/return it in add_term,
add_terms_batch, lookup_term, serialize and deserialize.

Fixes microsoft#321

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings September 28, 2026 22:18

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

The schema migration can fail when multiple providers concurrently initialize the same legacy database.

Review effort: Balanced
Findings: 1 High severity · 1 Low severity

Open (2)
What changed in this PR

Persists semantic-reference relevance scores in SQLite to match the memory backend.

Changes:

  • Adds and migrates the SQLite score column.
  • Preserves scores across writes, lookups, and serialization.
  • Adds backend parity and migration tests.
File Description
src/​typeagent/​storage/​sqlite/​schema.py Adds score schema and legacy migration.
src/​typeagent/​storage/​sqlite/​semrefindex.py Persists and returns relevance scores.
tests/​test_semrefindex.py Tests score preservation and migration.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +284 to +288
columns = [row[1] for row in cursor.execute("PRAGMA table_info(SemanticRefIndex)")]
if "score" not in columns:
cursor.execute(
"ALTER TABLE SemanticRefIndex ADD COLUMN score REAL NOT NULL DEFAULT 1.0"
)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unlikely but seems like a simple enough change

Comment thread tests/test_semrefindex.py
Comment on lines +475 to +477
import sqlite3

from typeagent.storage.sqlite.schema import init_db_schema
CREATE TABLE IF NOT EXISTS SemanticRefIndex (
term TEXT NOT NULL, -- lowercased, not-unique/normalized
semref_id INTEGER NOT NULL,
score REAL NOT NULL DEFAULT 1.0,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't defaulting the score to 1.0 might hide more valid results? Should this be 0 or some really small value?

Comment on lines +284 to +288
columns = [row[1] for row in cursor.execute("PRAGMA table_info(SemanticRefIndex)")]
if "score" not in columns:
cursor.execute(
"ALTER TABLE SemanticRefIndex ADD COLUMN score REAL NOT NULL DEFAULT 1.0"
)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unlikely but seems like a simple enough change

) -> tuple[SemanticRefOrdinal, float]:
if isinstance(ordinal, ScoredSemanticRefOrdinal):
return ordinal.semantic_ref_ordinal, ordinal.score
return ordinal, 1.0

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe make this a constant and reuse this in/from schema.py when initializing the SemanticRefIndex table.

# Fallback for direct integer
semref_id = semref_ordinal_data
insertion_data.append((term, semref_id))
score = 1.0

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same const reuse here and on line #157

This branch has not been deployed

No deployments
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.

SqliteTermToSemanticRefIndex discards relevance scores

3 participants