Skip to content

v2.0.0

Choose a tag to compare

@tonytonycoder11 tonytonycoder11 released this 31 Jul 14:43
6e94866

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 own EmbeddingStorage ranks 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 stable rag-base, not on
    the rag-vector beta. Koog's namespace becomes 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; and createShardKey / deleteShardKey manage 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.
  • ShardKey and shardKey on search and scroll, 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.
  • ReplicaState decodes an unrecognized state from a newer Qdrant to UNKNOWN rather than failing the
    whole cluster-info response, the same tolerance CollectionStatus already had.
  • Formula reranking and MMR (M35), scoped in M16 and not shipped with it. formula(expression) rescores the
    candidates a prefetch produced 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
    Expression AST 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 in 0..1, a positive decay scale, a midpoint in 0..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
    downloadShardSnapshot and uploadShardSnapshot. 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
    QdrantClient the REST factory does, over Qdrant's Collections, Points, Snapshots and Health
    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 .proto files, vendored verbatim at v1.18.2, rather than
    taken from io.qdrant:client. grpc-kotlin emits suspend functions and Flows, 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-core is a Kotlin Multiplatform library (M25). It builds for the JVM and for eight
    Kotlin/Native targets: iosArm64, iosSimulatorArm64, iosX64, macosArm64, macosX64,
    linuxArm64, linuxX64 and mingwX64. 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 into commonMain and changed one declaration. The engines stay JVM-only, because Ktor CIO and
    grpc-java are.
  • A commonTest suite 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. The kdrant-core coordinate
    now carries Gradle module metadata and the JVM classes live in kdrant-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 to kdrant-core-jvm, which the BOM now constrains as well. The
    migration changed no public API: the *.api dump is identical either side of it.

  • The default dispatcher is platform-dependent, and is the one declaration the migration had to split.
    It stays Dispatchers.IO on the JVM. On Kotlin/Native it is Dispatchers.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.jar holds 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 QdrantTransport operations 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 an UnsupportedOperationException naming 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 including 1.2.0 remain 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.