symfony/ai-qdrant-store
Qdrant vector store integration for Symfony AI Store, with links to official Qdrant documentation on collections, points, and unified query search, plus Symfony AI contributing and issue resources.
symfony/ai-qdrant-store) behind the symfony/ai store abstraction. Architecturally it is a swappable vector-persistence adapter, not a framework-level concern — still the right seam, and nothing in v0.15.0 changes that.#2753 Bump version constraints to 0.15). No new interfaces, no behavioural changes, no deprecations. The architectural picture is therefore unchanged from v0.14.0, which remains the release that matters for design decisions:
VectorDocumentInterface (#2495) — the document contract between domain objects and the store. This is still the strategically significant change: one canonical document type consumed by any store implementation, reduced lock-in to Qdrant payload shapes, simpler test doubles.count() capability (#1755) — first-class cardinality queries replacing ad-hoc scroll/scan loops for migration validation, monitoring, and "results available" UI logic.symfony/ai 0.15 line shipped without breaking the store contract. That is a data point in favour of the abstraction holding, not a guarantee. Treat it as one green cycle in an ongoing pre-1.0 risk, not as evidence the risk is retired.symfony/ai-qdrant-store 0.15 targets the symfony/ai 0.15 line. Verify the parent package constraint before bumping, and expect to move symfony/ai at the same time.VectorDocumentInterface and wiring count() — remains outstanding and undiminished. It is not made easier or harder by 0.15.0, and it should not be quietly dropped because the release itself was trivial.count() capability-support verification still stands: not every store implementation necessarily implements counting, and Qdrant's counting behaviour (exact vs approximate, filtered vs unfiltered) still needs characterising before it enters a request path.VectorDocumentInterface and the signature shifts, every implementing class breaks at once. Keep the thin domain DTO and adapt at the boundary. This cycle validated the approach; keep it anyway.symfony/ai and symfony/ai-qdrant-store minor versions is now the most likely failure mode of this upgrade. Composer will usually catch it, but only if we bump both together rather than letting --update-with-dependencies resolve them independently.search() result set is still a visible correctness bug.symfony/ai moves in lockstep?VectorDocumentInterface required or optional for consumers — must custom document classes implement it, or will existing array shapes continue to be accepted? (Carried over; still unanswered by the changelog.)count(), and is there capability detection, or will calls fail hard? (Carried over.)count() exact by default, and what is the cost profile for filtered counts on collections in the 10M+ point range? (Carried over.)count() contract define behaviour when the collection does not exist (exception vs zero)? (Carried over.)symfony/ai contracts, serving both Symfony (native bundle config) and Laravel (bound in a service provider). The abstraction is what makes the package viable for us.framework.ai.stores.qdrant), now on the 0.15 line.HttpClientInterface (or Guzzle adapter), collection name, and API key from config; publish a config/ai.php mirroring the Symfony parameter bag.QDRANT_API_URL, QDRANT_API_KEY, QDRANT_COLLECTION.The phased plan is unchanged. v0.15.0 adds no step and removes none — it is a no-op on the migration path, provided the parent package moves with it.
count() as the primary reconciliation check instead of scroll-and-tally scripts — compare source count, target count, and count-after-filter on a few payload predicates.VectorDocumentInterface at the boundary, keep an internal domain DTO plus an adapter, backfill incrementally per call site. Still the largest remaining piece of work; unaffected by 0.15.0.symfony/ai alignment is now the whole story. 0.15.0 exists solely to bump constraints to the 0.15 line, so a store-only bump will likely fail resolution or produce an unsupported pairing. Bump symfony/ai and symfony/ai-qdrant-store together.count() contract are all untouched. The audit target is unchanged: any decorator, mock, or fake store in our test suite that needs a count() method (from 0.14.0).symfony/ai and symfony/ai-qdrant-store to the 0.15 line in a single commit; run unit + integration suites. Expect a near-zero diff and a fast review.count() into migration reconciliation and a nightly drift check (source vs target cardinality). Feature-flag any user-facing use.Steps 1–2 are the 0.15.0 work; steps 3–5 are carry-over from the 0.14.0 assessment and should be tracked as separate backlog items so the trivial upgrade does not mask them.
count() return handling and interface conformance of the adapter.search() and count() agree on the same filter; assert interface round-trip (document in → document out preserves payload).count() on a production-shaped collection to establish the cost ceiling before it touches a request path.{ "scripts": { "update:ai": "composer require symfony/ai:^0.15 symfony/ai-qdrant-store:^0.15 --update-with-dependencies" } }
count() as a cheap post-migration verification gate.limit, hnsw_ef), schema mismatches (vector dimensions, payload fields).symfony/ai and symfony/ai-qdrant-store were moved together. Log resolved versions in the health endpoint so support can confirm the pairing without a shell.count() disagrees with expected totals, check (a) approximate-count mode, (b) write lag / eventual consistency, (c) filter translation differences between our adapter and Qdrant's filter syntax. Log the generated filter alongside the count.http://<host>:6333/dashboard; Symfony debug toolbar / Laravel Telescope for request inspection.symfony/ai docs remain high-level on store specifics — cross-reference Qdrant docs. Internal runbook should cover: version-pairing rules, filtered-query examples, gRPC vs REST configuration, count semantics, and escalation paths.shard_count, replicas_count); application servers scale independently since the store is stateless.hnsw_ef, hnsw_m) for recall/latency trade-off; batch upserts (upsert_points) to cut round trips; cache frequent queries via the framework cache component with explicit TTLs.count() scaling caution: cheap on small/filtered sets, expensive on very large unfiltered collections. Use approximate counting for UI and dashboards; reserve exact counts for reconciliation jobs. Never place a filtered exact count in a synchronous request path without a measured ceiling.count(). Mitigate with a circuit breaker around the store client, bounded retries with jitter, graceful degradation to a cached result set or "no results" state rather than a 500, and a health-check endpoint that gates traffic.How can I help you explore Laravel packages today?