Skip to content

Python: add Milvus vector store (agent-framework-milvus) - #9346

Open
Nantha kumar (Nanduu24) wants to merge 8 commits into
microsoft:mainfrom
Nanduu24:python-milvus-vector-store
Open

Nantha kumar (Nanduu24) wants to merge 8 commits into
microsoft:mainfrom
Nanduu24:python-milvus-vector-store

Conversation

@Nanduu24

Copy link
Copy Markdown

Summary

Adds a new agent-framework-milvus package providing a Milvus vector store for Agent Framework, implementing the BaseVectorStore / BaseVectorCollection / BaseVectorSearch interfaces. This makes Milvus — one of the most widely used open-source vector databases — available out of the box alongside the existing Postgres, Qdrant, Redis, MongoDB, SQL Server and other vector-store packages.

It follows the structure of the existing agent-framework-qdrant package (settings via load_settings, a store that mints collection clients, borrowed-vs-managed client ownership, operation_options validation, native filtering).

What's included

  • MilvusStore / MilvusCollection / MilvusSettings (+ py.typed).
  • Schema from the data model — the key field becomes the primary key (INT64 for int keys, VARCHAR otherwise), data fields become typed scalar fields, and vector fields become FLOAT_VECTOR fields, each with its own index.
  • Mappings — DistanceFunction → Milvus metric types (cosine_similarity/cosine_distance → COSINE, dot_prod → IP, euclidean_distance/euclidean_squared_distance → L2); IndexKind → Milvus index types (hnsw, flat, ivf_flat, disk_ann, default → AUTOINDEX).
  • Full surface — ensure_collection_exists / collection_exists / ensure_collection_deleted, upsert, get (by key or portable filter), delete, and vector search.
  • Portable filters translated to Milvus boolean expressions: eq, ne, gt, gte, lt, lte, between, in, not_in, and and/or/not groups. Operators that Milvus can't implement with correct null/collection semantics raise NotImplementedError rather than silently returning wrong results.
  • Connector-side score_threshold that respects each metric's comparison direction (larger-is-closer for COSINE/IP, smaller-is-closer for L2), since core does not post-filter.
  • The synchronous MilvusClient is wrapped in asyncio.to_thread, which keeps the connector compatible across the supported pymilvus range while presenting the framework's async interface.
  • README, a runnable sample (samples/milvus_vectors.py), and the package registered in the workspace [tool.uv.sources].

Testing

  • Unit tests (tests/milvus/): filter translation, settings resolution/precedence, and collection behavior (upsert/get/delete/search, schema building, serialization, lifecycle, client ownership) — hermetic, no live service.
  • Integration tests gated behind MILVUS_TEST_URI for a real server.
  • Verified end-to-end against a live Milvus database (create → index → upsert → semantic search → portable-filter search → get with vectors → list → delete → lifecycle), all passing.
  • ruff check, ruff format --check, and Pyright (strict) are clean on the package; uv lock --locked succeeds.

Motivation

Milvus is a top-tier open-source vector database with no first-party Agent Framework integration, and no existing PR or issue was addressing it. This fills that gap with a connector that matches the conventions of the other vector-store packages.

Adds a new agent-framework-milvus package providing MilvusStore, MilvusCollection
and MilvusSettings on the BaseVectorStore / BaseVectorCollection interfaces, so
Milvus is available as a vector store alongside the Postgres, Qdrant, Redis,
MongoDB and other connectors.

- The data model definition drives the collection schema: the key field becomes
  the primary key (INT64/VARCHAR), data fields become typed scalar fields, and
  vector fields become FLOAT_VECTOR fields, each with an index.
- Distance functions map to Milvus metrics (COSINE/IP/L2) and index kinds to
  Milvus index types (HNSW/FLAT/IVF_FLAT/DISKANN/AUTOINDEX).
- Portable filters (eq, ne, gt, gte, lt, lte, between, in, not_in and and/or/not
  groups) are translated to Milvus boolean expressions; connector-side score
  thresholds respect each metric's comparison direction.
- Upsert, get (by key or filter), delete, vector search and collection lifecycle,
  with the sync MilvusClient wrapped in asyncio.to_thread.
- README, a runnable sample, and unit tests (filters, settings, collection) plus
  integration tests gated behind MILVUS_TEST_URI. Registered in the workspace.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Connector correctness, package registration, CI coverage, integration testing, and documentation issues remain unresolved.

9 open findings
What changed in this PR

Adds a first-party Milvus vector-store connector implementing Agent Framework’s collection, store, filtering, lifecycle, and vector-search abstractions.

Changes:

  • Adds Milvus schema, CRUD, filtering, search, settings, and client ownership support.
  • Adds package documentation, samples, and unit-test scaffolding.
  • Registers and locks the new workspace package and dependencies.
File Description
python/​uv.lock Locks Milvus and transitive dependencies.
python/​pyproject.toml Registers the workspace source.
python/​packages/​milvus/​tests/​milvus/​test_settings.py Tests settings resolution.
python/​packages/​milvus/​tests/​milvus/​test_filters.py Tests filter translation.
python/​packages/​milvus/​tests/​milvus/​test_collection.py Tests collection behavior.
python/​packages/​milvus/​tests/​milvus/​conftest.py Defines test fixtures.
python/​packages/​milvus/​samples/​README.md Documents the sample.
python/​packages/​milvus/​samples/​milvus_vectors.py Demonstrates Milvus usage.
python/​packages/​milvus/​README.md Documents installation and APIs.
python/​packages/​milvus/​pyproject.toml Defines package metadata and tasks.
python/​packages/​milvus/​LICENSE Adds the MIT license.
python/​packages/​milvus/​agent_framework_milvus/​py.typed Marks typed-package support.
python/​packages/​milvus/​agent_framework_milvus/​_vector_store.py Implements the connector.
python/​packages/​milvus/​agent_framework_milvus/​__init__.py Exports public APIs.

🧠 Review effort: Balanced


💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

Comment thread python/packages/milvus/agent_framework_milvus/_vector_store.py Outdated
Comment thread python/packages/milvus/agent_framework_milvus/_vector_store.py Outdated
Comment thread python/packages/milvus/agent_framework_milvus/_vector_store.py
Comment thread python/packages/milvus/agent_framework_milvus/_vector_store.py
Comment thread python/packages/milvus/pyproject.toml Outdated
Comment thread python/packages/milvus/pyproject.toml
Comment thread python/packages/milvus/tests/milvus/conftest.py
Comment thread python/packages/milvus/README.md Outdated
Comment thread python/packages/milvus/pyproject.toml
- ensure_collection_exists: handle concurrent creation (treat an already-created
  collection as success instead of failing the ensure operation).
- Filter string literals are serialized with json.dumps so quotes, backslashes and
  control characters cannot break out of the Milvus expression.
- Reject is_indexed / is_full_text_indexed on data fields until scalar/full-text
  index creation is implemented, rather than silently ignoring them.
- Validate score_threshold as a finite, non-boolean number before searching.
- Qualify the Milvus Lite (local .db) docs: it requires the pymilvus[milvus_lite]
  extra; fix the README usage snippet (sequence upsert, generate_vectors, subscript
  SearchResponse fields).
- Add live integration tests exercising server_collection (CRUD, search, filters),
  selected by -m integration.
- Add a Milvus PR unit-test job and path filter so regressions are caught on PRs.
- Register the package in PACKAGE_STATUS.md (alpha / no).
@allyson-tcesp

Copy link
Copy Markdown

Great! Thx

@eavanvalkenburg

Copy link
Copy Markdown
Member

/review

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MAF Automated Review — Iteration 1

Result: Findings reported
Scope: full PR (2 commit(s)): d121cfe0e10a, 73796abec852
Model: gpt-5.6-sol

Overview

The review found 2 verified inline finding(s).

Reviewed the supplied pull-request change set across correctness, security/reliability, architecture, and failure behavior.
2 verified findings remained after source verification (2 medium) across 1 file. Details are attached to the affected lines below.

Affected areas: python/packages/milvus/agent_framework_milvus/_vector_store.py

Comment thread python/packages/milvus/agent_framework_milvus/_vector_store.py
Comment thread python/packages/milvus/agent_framework_milvus/_vector_store.py Outdated
Addresses review feedback on microsoft#9346:

- ensure_collection_exists now validates an existing collection's fields, vector
  dimensions and metric types against the data model definition (and does the same
  after an already-exists race) instead of accepting a stale or unrelated schema.
- Portable filter operands are validated against the declared field type: a
  boolean operand on a numeric field (or any type mismatch) is rejected with a
  clear error rather than emitting an invalid comparison such as 'price == true';
  ordered comparisons require a numeric field.
- Added unit tests for schema mismatch (dimension, metric) and operand-type
  rejection.
…on_exists

Per review on microsoft#9346: create_collection builds the collection and then separately
creates indexes and loads it. Treating any exception as success when the collection
exists afterwards could mask an index or load failure, leaving an unindexed/unloaded
collection. Now only a genuine already-exists/concurrent-create MilvusException is
swallowed (then the existing collection is validated); all other create/index/load
failures are re-raised. Added tests for the index/load-failure and non-Milvus cases.
)
resolved_token = settings.get("token")
resolved_password = settings.get("password")
client_kwargs: dict[str, Any] = {"uri": settings.get("uri") or DEFAULT_URI}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
client_kwargs: dict[str, Any] = {"uri": settings.get("uri") or DEFAULT_URI}
client_kwargs: dict[str, Any] = {"uri": settings.get("uri", DEFAULT_URI)}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kept settings.get("uri") or DEFAULT_URI: load_settings includes optional keys as None when unset, so settings.get("uri", DEFAULT_URI) would return None rather than the default (verified). Happy to change if you'd prefer a different shape.

Comment thread python/packages/milvus/agent_framework_milvus/_vector_store.py Outdated
Comment thread python/packages/milvus/agent_framework_milvus/_vector_store.py Outdated
Comment on lines +251 to +254
if client is not None and any(
value is not None for value in (uri, token, user, password, db_name, env_file_path, env_file_encoding)
):
raise ValueError("Pass client or connection settings, not both.")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be handled after parsing the settings, some or all of this might come from the environment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This mirrors the Qdrant connector's guard and intentionally inspects only the explicit constructor args — env-sourced settings are ambient and aren't treated as 'passed'. Happy to drop the guard (and let an explicit client win) if you'd prefer; let me know.

Comment thread python/packages/milvus/agent_framework_milvus/_vector_store.py Outdated
Comment thread python/packages/milvus/agent_framework_milvus/_vector_store.py
Comment thread python/packages/milvus/tests/milvus/conftest.py Outdated
Comment thread python/packages/milvus/tests/milvus/test_integration.py
Comment thread python/packages/milvus/pyproject.toml
Addresses review feedback on microsoft#9346:

- Switch from the sync MilvusClient wrapped in asyncio.to_thread to the native
  AsyncMilvusClient, awaiting operations directly.
- Let the native Milvus client-creation error propagate instead of wrapping it.
- Replace the brittle already-exists message match: on a create failure, validate
  the existing collection and re-raise the original error if it does not match,
  so only a genuine concurrent-create with a complete, matching collection is
  accepted.
- Tests use MILVUS_URI (not a separate MILVUS_TEST_URI) to gate integration tests;
  the env-clearing fixture is skipped for integration tests.
- Require pymilvus>=2.5.3 (AsyncMilvusClient).
Verified pymilvus 2.5.3+ and its dependencies (grpcio, numpy, …) resolve, install
and import on Python 3.15, matching the framework's new 3.15 support.

This branch was successfully deployed

1 active deployment
github-app-auth — c2976c4a Deployed Oct 15, 2026 by Nanduu24 via add_label #25493
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Usage: [Issues, PRs], Target: documentation in the code base and learn docs python Usage: [Issues, PRs], Target: Python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants