v2.0.0
Tier 5, complete, and the release the transport seam was built for. kdrant-transport-grpc is an
opt-in gRPC engine behind the same QdrantClient, and kdrant-core compiles for the JVM and eight
Kotlin/Native targets. Adding a second engine changed no line of kdrant-core.
Two things make this a major. kdrant-core's JVM classes moved to kdrant-core-jvm, because the
module is multiplatform now: a Gradle build changes only the version number, a Maven build naming
kdrant-core has to move. And ScrollRequest and SearchRequest gained a shardKey parameter, which
changed their generated constructor and copy, so code that called copy() on either against a 1.x
jar has to be recompiled. Source stays compatible. The multiplatform migration itself changed no public
API at all. See STABILITY.md.
Added
kdrant-koog(M37), a new module: a Koog document storage backed by
Kdrant, so a Koog RAG agent can keep its documents in Qdrant. It implements Koog's search-side storage
interfaces (WriteStorage,LookupStorage,SearchStorage,DeletionStorage) rather than
VectorStorageBackend, which has no search method: Koog's ownEmbeddingStorageranks by streaming
every stored document out of the backend and scoring in memory, and doing that through a vector
database would mean paying for an index and then pulling the whole collection over the network on
every query. Here Qdrant runs the search. The module depends only on Koog's stablerag-base, not on
therag-vectorbeta. Koog'snamespacebecomes a payload field and a filter, so one collection can
hold several of them.- Cluster and sharding (M32), the gap the migration guide used to name as having no Kdrant equivalent.
collectionClusterInfo(name)reads how a collection's shards are spread across peers, including the
transfers in flight;updateCollectionCluster(name, operation)moves, replicates, aborts or drops a
shard; andcreateShardKey/deleteShardKeymanage custom sharding keys. The placement calls return
once the transfer is accepted, not once it has finished, which the KDoc says rather than leaving
it to be discovered. ShardKeyandshardKeyonsearchandscroll, so a query that concerns one region or one tenant
reads that key's shards instead of all of them. A numeric key stays a number on the wire; quoting it
would make Qdrant read it as a different key.ReplicaStatedecodes an unrecognized state from a newer Qdrant toUNKNOWNrather than failing the
whole cluster-info response, the same toleranceCollectionStatusalready had.- Formula reranking and MMR (M35), scoped in M16 and not shipped with it.
formula(expression)rescores the
candidates aprefetchproduced with arithmetic over their score and payload: multiply by a
popularity field, add a bonus for points matching a condition, decay by recency or by distance. The
ExpressionAST covers Qdrant's full operator set, including the three decay curves and
geo_distance.mmr(diversity)reranks a vector query for variety instead of letting ten results
about the same thing crowd the top.
Both are validated against Qdrant's published schema by the contract tests, and the bounds Qdrant
documents (diversity in0..1, a positive decay scale, a midpoint in0..1) are checked where they
are written rather than on the round trip. - Shard-scope snapshots (M36), deferred out of M20 when snapshots first shipped:
createShardSnapshot,
listShardSnapshots,deleteShardSnapshot,recoverShardSnapshot, plus streaming
downloadShardSnapshotanduploadShardSnapshot. On a sharded collection the existing
whole-collection snapshot is every shard at once, which on a large collection is the difference
between a backup that fits in a window and one that does not. Shard ids come from
collectionClusterInfo. kdrant-transport-grpc(M31), the opt-in gRPC engine.KdrantGrpc(host)returns the same
QdrantClientthe REST factory does, over Qdrant'sCollections,Points,SnapshotsandHealth
services on port 6334. REST stays the recommended engine; reach for this one when throughput or
long-lived streaming is the bottleneck, which is the case the README used to concede to the official
client. Nothing changes for a REST user: the module is separate, and a build that does not ask for it
resolves no gRPC, no protobuf and no Netty.
The stubs are generated from Qdrant's own.protofiles, vendored verbatim at v1.18.2, rather than
taken fromio.qdrant:client. grpc-kotlin emits suspend functions andFlows, which is the shape the
transport seam already has, and generating decides the dependency set instead of inheriting a shaded
Netty jar that is most of the official client's footprint.- Both engines are held to one shared client contract (M31,
kdrant-testkit), which runs the same 30
behavioural tests against a real Qdrant over each protocol. The REST tests that came before it
asserted HTTP bodies, which a gRPC engine cannot satisfy by construction. kdrant-coreis a Kotlin Multiplatform library (M25). It builds for the JVM and for eight
Kotlin/Native targets:iosArm64,iosSimulatorArm64,iosX64,macosArm64,macosX64,
linuxArm64,linuxX64andmingwX64. Kotlin/JS is deliberately not among them: there is no JS
engine, so the target would ship models with nothing to send them over, and its test tooling is the
only npm dependency graph this repository would have. The models, DSLs, error hierarchy and client
logic were already free of the JVM, which is what the transport seam was for, so the migration moved
sources intocommonMainand changed one declaration. The engines stay JVM-only, because Ktor CIO and
grpc-java are.- A
commonTestsuite that runs on every target, covering the places a platform could actually
differ: the hand-written serializers, the uint64 point id, integer payload values above 2^53, and the
config's validation.
Changed
-
kdrant-core's artifact layout changed with the multiplatform move. Thekdrant-corecoordinate
now carries Gradle module metadata and the JVM classes live inkdrant-core-jvm. A Gradle build
resolves the right variant from the same coordinate and needs no change; a Maven build names the
artifact directly and must move tokdrant-core-jvm, which the BOM now constrains as well. The
migration changed no public API: the*.apidump is identical either side of it. -
The default dispatcher is platform-dependent, and is the one declaration the migration had to split.
It staysDispatchers.IOon the JVM. On Kotlin/Native it isDispatchers.Default, because the
coroutines library still keeps its native IO dispatcher internal. Passing your own dispatcher works
as before, everywhere. -
Releases are built on macOS. Only a macOS host can compile the Apple targets, so a Linux runner would
publish a release quietly missing its iOS and macOS klibs. -
kdrant-core's-javadoc.jarholds Dokka's HTML output rather than Javadoc HTML: the Dokka Javadoc
generator refuses a multiplatform project. Maven Central requires the jar to exist rather than to be
Javadoc, and HTML is what a Kotlin reader wants. -
Fourteen
QdrantTransportoperations have no gRPC equivalent, because the seam was shaped by
Qdrant's REST API and Qdrant serves these over HTTP only:telemetry,metrics,listIssues,
clearIssues,recoverSnapshot, the snapshot and storage-snapshot transfers, and the six
shard-scope snapshot operations. On the
gRPC engine each throws anUnsupportedOperationExceptionnaming the operation and pointing at REST,
rather than degrading quietly. A snapshot download that returns nothing is a backup that does not
exist. The REST engine is unchanged. -
Releases publish to Maven Central only. The secondary publication to GitHub Packages is gone: it
carried the same artifacts to a registry that requires authentication even for public packages, so it
was a second place to keep in sync and no second way for anyone to depend on Kdrant. Versions up to
and including1.2.0remain on GitHub Packages and are not withdrawn.
Internal
- Release notes are extracted from this file by the release workflow rather than written by hand. A
release body composed separately is a second copy of what the changelog owns, and the two eventually
disagree; derived from here it cannot. The workflow fails the release if the tag has no section. - Every published jar is recorded as a linked artifact, so the repository's Packages panel names what it
built and where it went. Metadata, not a distribution channel: nothing is hosted there. - The set of artifacts the provenance attestation covers is derived from the build instead of listed in
the workflow. A hardcoded list stops covering a module the day one is added and nothing goes red,
which is what happened when the gRPC engine arrived.