Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions examples/contracts/log_indexer.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,27 +66,29 @@ def get_closest_vector(self, text: str) -> dict | None:
def add_log(self, log: str, log_id: int) -> None:
key = log_id
if key in self.log_vector_ids:
self.vector_store.get_by_id(self.log_vector_ids[key]).value = StoreValue(
text=log, log_id=key
)
return
# VecDBElement.key (the stored vector used by knn()) is
# read-only - only .value can be updated in place. Re-embedding
# requires removing the stale entry and inserting a fresh one,
# otherwise a changed log stays ranked by its old embedding.
self.vector_store.get_by_id(self.log_vector_ids[key]).remove()

emb = self.get_embedding(log)
vector_id = self.vector_store.insert(emb, StoreValue(text=log, log_id=key))
self.log_vector_ids[key] = vector_id
self.removed_log_ids[key] = False

@gl.public.write
def update_log(self, log_id: int, log: str) -> None:
key = log_id
if key in self.log_vector_ids:
self.vector_store.get_by_id(self.log_vector_ids[key]).value = StoreValue(
text=log, log_id=key
)
return
# See add_log: .key is read-only, must remove+reinsert to
# actually re-embed rather than leaving the stale vector.
self.vector_store.get_by_id(self.log_vector_ids[key]).remove()

emb = self.get_embedding(log)
vector_id = self.vector_store.insert(emb, StoreValue(text=log, log_id=key))
self.log_vector_ids[key] = vector_id
self.removed_log_ids[key] = False

@gl.public.write
def remove_log(self, id: int) -> None:
Expand Down
62 changes: 62 additions & 0 deletions tests/integration/icontracts/tests/test_log_indexer.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,65 @@ def test_log_indexer(setup_validators):
assert float(closest_vector_log_2["similarity"]) > 0.99
assert closest_vector_log_2["id"] == 3
assert closest_vector_log_2["text"] == "This is the third log"

# ########################################
# ### Removed log_id becomes visible again
# ### after being re-added
# ########################################
transaction_response_remove_log_2 = contract.remove_log(args=[3]).transact()
assert tx_execution_succeeded(transaction_response_remove_log_2)

# tombstoned: must not be found anymore
closest_vector_after_remove = contract.get_closest_vector(
args=["This is the third log"]
).call()
assert (
closest_vector_after_remove is None
or closest_vector_after_remove["id"] != 3
)

# re-add the same log_id via add_log (takes the "already indexed"
# in-place-update branch, since log_id 3 is still in log_vector_ids)
transaction_response_readd_log_2 = contract.add_log(
args=["This is the third log, again", 3]
).transact()
assert tx_execution_succeeded(transaction_response_readd_log_2)

# must be visible again — the tombstone from remove_log must have
# been cleared, not left permanently set
closest_vector_after_readd = contract.get_closest_vector(
args=["This is the third log, again"]
).call()
assert closest_vector_after_readd is not None
assert closest_vector_after_readd["id"] == 3
assert closest_vector_after_readd["text"] == "This is the third log, again"

# ########################################
# ### Updating an existing log_id must
# ### re-embed, not just re-label
# ########################################
# log_id 3 currently holds "This is the third log, again" — an
# unrelated topic. If add_log's "already indexed" branch only updated
# .value (the old bug: VecDBElement.key, the stored vector, is
# read-only and can't be changed in place) instead of removing and
# re-inserting with a fresh embedding, this log would still be found
# by queries about its OLD topic and missed by queries about its new
# one, despite .text correctly reporting the new content.
transaction_response_retopic_log_2 = contract.add_log(
args=["Quantum computers use superconducting qubits", 3]
).transact()
assert tx_execution_succeeded(transaction_response_retopic_log_2)

# found by a query about the NEW topic — this alone proves re-embedding
# happened: the OLD embedding ("This is the third log, again") has
# nothing in common with quantum computing, so this could only match
# if a fresh embedding was actually computed and stored.
closest_vector_new_topic = contract.get_closest_vector(
args=["How do quantum computers work"]
).call()
assert closest_vector_new_topic is not None
assert closest_vector_new_topic["id"] == 3
assert (
closest_vector_new_topic["text"]
== "Quantum computers use superconducting qubits"
)