LogIndexer Contract
The LogIndexer contract is an Intelligent Contract example that uses the Vector Store database (VecDB) provided by the genlayer_embeddings package to index text logs with vector embeddings. The contract demonstrates how to store, retrieve, update, and remove logs, then search them by similarity.
This is the same contract used in GenLayer's own test suite — a complete, verified, copy-pasteable example. See the Vector Store feature page for the full VecDB/VecDBElement API reference.
# v0.3.0
# {
# "Seq": [
# { "Depends": "py-lib-genlayer-embeddings:kr2rb2dcp01mw9khpg3tg2jasx4f82mcsy3eg08rjj1zdcm9q350" },
# { "Depends": "py-genlayer:9b8kjyda2ycxyq4ea6g4yfpnydxhd52gqba5rb8dw7krkh5mn9p0" }
# ]
# }
import numpy as np
import genlayer as gl
from genlayer.types import *
from genlayer.storage import TreeMap
import genlayer_embeddings as gle
from dataclasses import dataclass
import typing
@gl.storage.allow
@dataclass
class StoreValue:
log_id: u256
text: str
# contract class
class LogIndexer(gl.contract.Contract):
# The v0.3 embeddings runner's VecDB takes an explicit metric type.
vector_store: gle.VecDB[
np.float32, typing.Literal[384], StoreValue, gle.EuclideanDistance
]
log_vector_ids: TreeMap[u256, u32]
removed_log_ids: TreeMap[u256, bool]
def __init__(self):
pass
def get_embedding_generator(self):
return gle.SentenceTransformer("all-MiniLM-L6-v2")
def get_embedding(
self, txt: str
) -> np.ndarray[tuple[typing.Literal[384]], np.dtypes.Float32DType]:
return self.get_embedding_generator()(txt)
@gl.public.view
def get_closest_vector(self, text: str) -> dict | None:
emb = self.get_embedding(text)
for result in self.vector_store.knn(emb, len(self.vector_store)):
log_id = result.value.log_id
if log_id in self.removed_log_ids and self.removed_log_ids[log_id]:
continue
if log_id not in self.log_vector_ids:
continue
if self.log_vector_ids[log_id] != result.id:
continue
return {
"vector": list(str(x) for x in result.key),
"similarity": str(1 - result.distance),
"id": result.value.log_id,
"text": result.value.text,
}
return None
@gl.public.write
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
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
@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
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
@gl.public.write
def remove_log(self, id: int) -> None:
key = id
if key in self.log_vector_ids:
self.removed_log_ids[key] = TrueCode Explanation
- Data Structure: Uses
StoreValuedataclass to store log ID and text. - Vector Store: Initializes a
VecDBwith 384-dimensional float32 vectors andEuclideanDistanceas the metric. - Embedding Generation: Uses
gle.SentenceTransformerfor text embedding — this returns a plainstr -> np.ndarraycallable, not a class instance. - Duplicate protection:
log_vector_idsmaps eachlog_idto itsVecDBelement id, soadd_log/update_logoverwrite the existing entry's.valuein place instead of inserting a duplicate vector when alog_idis reused. - Tombstones instead of hard deletes:
remove_logmarks the id inremoved_log_idsrather than calling.remove()on theVecDBelement —get_closest_vectorfilters tombstoned and orphaned entries out of theknn()results. - Methods:
get_closest_vector(): Finds the closest non-removed log entry, scanningknn()results nearest-first.add_log(): Adds a new log with its embedding (or overwrites iflog_idalready exists).update_log(): Same asadd_log— replaces the text at thatlog_id.remove_log(): Tombstones a log by its ID.
Key Components
- Vector Database: Uses
VecDBfor efficient similarity-based searches via a cover tree. - Embedding Model: Utilizes
SentenceTransformerfor text vectorization. - CRUD Operations: Implements Create, Read, Update, Delete functionality.
- Similarity Search: Supports k-nearest neighbors (KNN) queries.
Deploying the Contract
To deploy the LogIndexer contract:
- Deploy the Contract: No initial parameters are needed.
- The contract will initialize with an empty vector store.
If deployment fails with a generic "Could not load contract schema" error, see the "Debugging a Could not load contract schema error" section on the Vector Store feature page for how to see the real traceback.
Checking the Contract State
After deployment, you can:
- Use
get_closest_vector()to find similar logs. - Query will return
Noneif no logs are stored (or all matching logs have been removed).
Executing Transactions
The contract supports several operations:
-
Adding Logs:
- Call
add_log(log, log_id)with text and ID. - Creates embedding and stores in
VecDB, or overwrites the existing entry iflog_idis already indexed.
- Call
-
Finding Similar Logs:
- Use
get_closest_vector(text)to find matches. - Returns vector, similarity score, ID, and text — or
None.
- Use
-
Updating Logs:
- Call
update_log(log_id, log)to modify entries. - Overwrites the stored text for that
log_id.
- Call
-
Removing Logs:
- Use
remove_log(id)to tombstone an entry. - Removes it from future
get_closest_vector()results.
- Use
Understanding Vector Storage
This contract demonstrates several important concepts:
- Vector Embeddings: Converts text to numerical vectors.
- Similarity Search: Uses vector distance for finding related content.
- Persistent Storage: Maintains vector database state.
- Efficient Querying: Supports fast nearest neighbor searches via a cover tree.
Performance Considerations
- Embedding generation may be computationally intensive.
knn()searches scale with database size, though the cover tree prunes much of it.- Vector dimension affects storage requirements.
SentenceTransformercaches the loaded model internally, so repeated calls with the same model name are cheap.
Technical Details
- Uses 384-dimensional float32 vectors.
- Implements the
all-MiniLM-L6-v2model. - Stores both vector embeddings and metadata (
StoreValue). knn()returns exact nearest neighbors (not approximate) via the cover tree.
You can monitor the contract's behavior through transaction logs, which will show vector operations and search results as they occur.