@anvia/qdrant

Qdrant vector store adapter for Anvia.

@anvia/qdrant connects Anvia's vector store interface to Qdrant. Use it for high-performance vector search with Qdrant's filtering and payload features.

Install

pnpm add @anvia/qdrant

The Qdrant client (@qdrant/js-client-rest) is a transitive dependency. You need a running Qdrant instance.

Quick Start

import { QdrantVectorStore } from "@anvia/qdrant";
import { createFastEmbedEmbeddingModel } from "@anvia/fastembed";

const model = await createFastEmbedEmbeddingModel();

// Connect (creates collection if missing)
const store = await QdrantVectorStore.connect({
  collectionName: "documents",
  vectorSize: 384,  // must match your embedding model's dimensions
});

// Upsert embedded documents
await store.upsertDocuments([
  {
    id: "doc-1",
    document: "Anvia is a TypeScript AI agent framework.",
    embeddings: await model.embedTexts(["Anvia is a TypeScript AI agent framework."]),
  },
]);

// Search
const index = store.index(model);
const results = await index.search({ query: "What is Anvia?", topK: 5 });
console.log(results[0].document);

Connection Options

type QdrantVectorStoreConnectOptions = {
  client?: QdrantClientLike;   // pre-configured Qdrant client
  collectionName: string;      // required
  vectorSize: number;          // required: embedding dimensions
  createIfMissing?: boolean;   // default: true
  distance?: QdrantDistance;   // default: "Cosine"
};
// Default: creates collection if missing, cosine distance
const store = await QdrantVectorStore.connect({
  collectionName: "docs",
  vectorSize: 384,
});

// Don't create if missing
const store = await QdrantVectorStore.connect({
  collectionName: "docs",
  vectorSize: 384,
  createIfMissing: false,
});

// Custom distance metric
const store = await QdrantVectorStore.connect({
  collectionName: "docs",
  vectorSize: 384,
  distance: "Euclid",
});

// Pre-configured client
import { QdrantClient } from "@qdrant/js-client-rest";

const store = await QdrantVectorStore.connect({
  client: new QdrantClient({ url: "http://localhost:6333" }),
  collectionName: "docs",
  vectorSize: 384,
});

Upserting Documents

await store.upsertDocuments([
  {
    id: "doc-1",
    document: { title: "Getting Started", content: "..." },
    metadata: { category: "guide", version: 2 },
    embeddings: await model.embedTexts(["Getting Started content..."]),
  },
]);
  • Documents are stored as Qdrant points with the document content and metadata in the payload
  • Documents with multiple embeddings get indexed as doc-1#embedding:0, doc-1#embedding:1, etc.
  • Point IDs are deterministic SHA-256 hashes of the document ID

Searching

const index = store.index(model);

// Basic search
const results = await index.search({ query: "agent tools", topK: 5 });

// With threshold
const results = await index.search({
  query: "agent tools",
  topK: 10,
  threshold: 0.7,
});

// With metadata filter
const results = await index.search({
  query: "agent tools",
  topK: 5,
  filter: { type: "eq", key: "category", value: "guide" },
});

Metadata Filters

// Equality
{ type: "eq", key: "category", value: "guide" }

// Range (numeric values)
{ type: "gt", key: "version", value: 2 }
{ type: "lt", key: "version", value: 5 }

// Logical combinators
{ type: "and", filters: [...] }
{ type: "or", filters: [...] }

Filters are translated to Qdrant's native filter format (must, should).

Distance Metrics

MetricQdrant valueNotes
Cosine (default)"Cosine"Best for text embeddings
Euclid"Euclid"L2 distance
Dot"Dot"Dot product similarity

Agent Tool

Expose the vector index as an agent tool:

const searchTool = index.asTool({
  name: "search_docs",
  description: "Search the documentation.",
});

const agent = new AgentBuilder("support", model)
  .tool(searchTool)
  .build();

Error Handling

  • connect() throws if Qdrant is unreachable
  • connect({ createIfMissing: false }) throws if the collection does not exist
  • upsertDocuments() throws if a document has zero embeddings
  • Metadata keys starting with __anvia_ are reserved and rejected