Skip to main content

Quickstart: Ahnlich AI

Send plain text (or images) and let Ahnlich do the vector math for you — this is the gentlest way in, with no need to understand embeddings or dimensions. Pick your language — or the CLI for a no-code path — once below, and every sample on this page follows your choice.

Bringing your own vectors instead? Use the Ahnlich DB quickstart — same flow, but you supply the vectors.

Prerequisites

You need the Ahnlich AI proxy running (it embeds text for you automatically). The fastest way is Docker:

docker run -d --name ahnlich-ai -p 1370:1370 ghcr.io/deven96/ahnlich-ai:latest

See Installation for binaries and other options.

Install the client

pip install ahnlich-client-py

1. Connect

Open a client against the AI proxy. No boilerplate, no config.

connect.py
import asyncio
from grpclib.client import Channel
from ahnlich_client_py.grpc.services.ai_service import AiServiceStub


async def main():
# Open a gRPC channel to the AI proxy (default port 1370).
# `async with` closes the connection automatically when you're done.
async with Channel(host="127.0.0.1", port=1370) as channel:
# The stub is your typed client — every Ahnlich call goes through it.
client = AiServiceStub(channel)
# ready to talk to Ahnlich

2. Create a store

Pick an embedding model and let Ahnlich handle the vectors for you.

create_store.py
from ahnlich_client_py.grpc.ai import query as ai_query
from ahnlich_client_py.grpc.ai.models import AiModel

await client.create_store(ai_query.CreateStore(
store="books", # name of the store
index_model=AiModel.ALL_MINI_LM_L6_V2, # model used to embed stored data
query_model=AiModel.ALL_MINI_LM_L6_V2, # model used to embed search queries
predicates=["author", "genre"], # metadata fields you can filter on
store_original=True, # keep the raw text alongside vectors
error_if_exists=True, # fail if a store with this name exists
))
tip

predicates are metadata fields you can later filter on (e.g. author, genre). Field names must match exactly between insert and query.

3. Insert data

Send raw text with metadata. Embeddings are generated automatically.

insert.py
from ahnlich_client_py.grpc import keyval, metadata
from ahnlich_client_py.grpc.ai import preprocess

await client.set(ai_query.Set(
store="books",
inputs=[
# Each entry = the text to embed (key) + its metadata (value).
keyval.AiStoreEntry(
key=keyval.StoreInput(raw_string="A galactic empire in decline..."),
value=keyval.StoreValue(value={
# These keys must match the store's `predicates` to be filterable.
"genre": metadata.MetadataValue(raw_string="SciFi"),
"author": metadata.MetadataValue(raw_string="Asimov"),
}),
)
],
# ModelPreprocessing lets the proxy tokenize/normalize the text before embedding.
preprocess_action=preprocess.PreprocessAction.ModelPreprocessing,
))

4. Update data

UPSERT finds a single matching entry using a predicate condition and updates its raw input or metadata.

upsert.py
from ahnlich_client_py.grpc import ai_query, keyval, metadata, predicates
from ahnlich_client_py.grpc.ai import preprocess

# Update the book with genre "SciFi" - change its author metadata
await client.upsert(ai_query.Upsert(
store="books",
# New text to embed (replaces the old key)
new_input=keyval.StoreInput(raw_string="The Foundation trilogy explores psychohistory"),
# New metadata (merges with existing by default in AI)
new_value=keyval.StoreValue(value={
"author": metadata.MetadataValue(raw_string="Isaac Asimov"),
}),
# Find the entry to update using a predicate
condition=predicates.PredicateCondition(predicates=[
predicates.Predicate(key="genre", value=metadata.MetadataValue(raw_string="SciFi"))
]),
preprocess_action=preprocess.PreprocessAction.ModelPreprocessing,
))

Query by meaning, with results ranked by similarity.

search.py
from ahnlich_client_py.grpc.algorithm import algorithms
from ahnlich_client_py.grpc.ai.preprocess import PreprocessAction

# Search by meaning — the query text is embedded with the store's query_model.
response = await client.get_sim_n(ai_query.GetSimN(
store="books",
search_input=keyval.StoreInput(raw_string="space opera classics"),
closest_n=3, # return the 3 nearest matches
algorithm=algorithms.Algorithm.CosineSimilarity, # distance metric
preprocess_action=PreprocessAction.ModelPreprocessing,
))

# Results are ranked most-similar first.
for entry in response.entries:
print(entry.key.raw_string, entry.similarity.value)

You now have semantic search working end to end. The sections below cover the rest of the everyday operations.

Combine similarity with a metadata condition — only return matches where a predicate holds (e.g. genre = SciFi).

filter.py
from ahnlich_client_py.grpc import predicates

# genre = "SciFi"
condition = predicates.PredicateCondition(
value=predicates.Predicate(
equals=predicates.Equals(
key="genre",
value=metadata.MetadataValue(raw_string="SciFi"),
)
)
)

response = await client.get_sim_n(ai_query.GetSimN(
store="books",
search_input=keyval.StoreInput(raw_string="space opera classics"),
closest_n=3,
algorithm=algorithms.Algorithm.CosineSimilarity,
condition=condition, # only entries where genre = SciFi
preprocess_action=PreprocessAction.ModelPreprocessing,
))
tip

A metadata field must be declared as a predicate on the store (or indexed with CREATEPREDINDEX) before you can filter on it. See step 8.

7. Retrieve by key

Fetch entries by their original input — handy when you already know the exact text. Requires the store to have been created with store_original = true.

get_key.py
response = await client.get_key(ai_query.GetKey(
store="books",
keys=[keyval.StoreInput(raw_string="A galactic empire in decline...")],
))

for entry in response.entries:
print(entry.key.raw_string, entry.value.value)

8. Speed up filters with an index

Create a predicate index so metadata filters (step 6) stay fast as your store grows. You can add predicates that weren't declared at creation time.

index.py
await client.create_pred_index(ai_query.CreatePredIndex(
store="books",
predicates=["author", "genre"],
))

9. Inspect your stores

List every store on the server, along with its size, dimensions, and models.

list_stores.py
stores = await client.list_stores(ai_query.ListStores())
for store in stores.stores:
print(store.name, store.size_in_bytes)

10. Clean up

Delete individual entries with del key, or remove the whole store with drop store.

cleanup.py
# delete a single entry by its key
await client.del_key(ai_query.DelKey(
store="books",
keys=[keyval.StoreInput(raw_string="A galactic empire in decline...")],
))

# or drop the entire store
await client.drop_store(ai_query.DropStore(store="books", error_if_not_exists=True))

That's the full lifecycle — create, populate, search, filter, and clean up.

Where to next?

  • Usage — drive Ahnlich from the CLI, plus the full command reference.
  • Client Libraries — deeper SDK docs for each language.
  • Components — how the DB, AI proxy, and CLI fit together.

Found a bug or something unclear? Open an issue.