feat: Making feast vector store with open ai search api compatible#6121
feat: Making feast vector store with open ai search api compatible#6121patelchaitany wants to merge 3 commits into
Conversation
e45f167 to
c8392a9
Compare
7e8adfb to
3f541ad
Compare
cd692b9 to
086bed5
Compare
086bed5 to
9778002
Compare
68c843c to
e29e557
Compare
f0552c7 to
dafe9fd
Compare
| query=request.query, | ||
| max_num_results=request.max_num_results or 10, | ||
| filters=( | ||
| request.filters.model_dump() if request.filters else None |
There was a problem hiding this comment.
request.filters.model_dump() can be removed once line 146 uses ComparisonFilter/CompoundFilter directly
e7ba622 to
502286f
Compare
| --- | ||
| title: "Making Feast Speak OpenAI: Vector Search Without the Glue Code" | ||
| description: "Feast now exposes an OpenAI-compatible vector store search endpoint. Send a plain text query, get results back in the standard OpenAI format. No client-side embeddings required." | ||
| date: 2026-04-28 |
|
Also add documentation for OpenAI-compatible Vector search endpoint in docs/reference/alpha-vector-database.md and docs/reference/feature-servers/python-feature-server.md Also, missing docs for how user can use different embedding provider other than litellm |
502286f to
925eb2e
Compare
925eb2e to
c2782f5
Compare
ntkathole
left a comment
There was a problem hiding this comment.
looks good to me!
@franciscojavierarceo will you be able to take a look once?
|
@ntkathole, The CI for the mcp-feature-server-runtime was failing because fastapi_mcp does not handle circular references properly. To fix this, I made changes in mcp_server.py where we inserted a custom schema resolver that handles how reference schemas are resolved. I also verified its logic against the original fastapi_mcp logic -- both produce the same output when there are no self-referencing schemas. |
58dcb47 to
ba5ad00
Compare
ba5ad00 to
180d80f
Compare
|
@franciscojavierarceo, can you please review it. |
franciscojavierarceo
left a comment
There was a problem hiding this comment.
can you generate an RFC out of this please? 🙏
|
|
||
| If you've tried to connect an AI agent to Feast's vector search, you've probably hit this wall: the agent needs to search your feature store, but Feast expects a raw embedding vector. The agent doesn't have one. It has a question in English. | ||
|
|
||
| Until now, the workaround was ugly. You'd call an embedding provider (OpenAI, Ollama, whatever) to turn the text into a float array, then pass that array to Feast's `retrieve-online-documents` endpoint. Every client had to know both APIs, carry both sets of credentials, and run glue code whose only job was bridging the gap. |
There was a problem hiding this comment.
we should deprecate the retrieve-online-documents API and rename it as search API to highlight the more generic primitive we want to support, which will offer a lot more long term flexibility.
CC @ntkathole
There was a problem hiding this comment.
Okay I have added the new endpoint /search and deprecated the retrieve-online-documents as it is still available for the backward compatibility.
|
|
||
| When Feast receives this request, it: | ||
|
|
||
| 1. Embeds the query server-side using the model configured in `feature_store.yaml` (via [LiteLLM](https://docs.litellm.ai/), which supports OpenAI, Ollama, Azure, Cohere, HuggingFace, and 100+ other providers). |
There was a problem hiding this comment.
yeah this seems acceptable if it's opt-in but we should also support sentence transformers.
|
@franciscojavierarceo, I have created an RFC as you requested Docs |
|
@franciscojavierarceo, let me know if you need any more changes, or if this is good to go. |
Signed-off-by: Chaitany patel <patelchaitany93@gmail.com>
Signed-off-by: Chaitany patel <patelchaitany93@gmail.com>
…gistration fastapi_mcp 0.4.0 resolve_schema_references() has no cycle detection. Feast's OpenAPI schema contains self-referential protobuf types (Value -> Struct -> Value) which trigger a RecursionError. The error is silently caught, so the /mcp route never gets registered and CI gets a 404. Add _resolve_schema_references_safe() that tracks a seen-refs set to break circular chains, and monkey-patch it into fastapi_mcp before FastApiMCP processes the schema. Non-circular schemas produce identical output to the original. Signed-off-by: Chaitany patel <patelchaitany93@gmail.com>
franciscojavierarceo
left a comment
There was a problem hiding this comment.
Thanks for pushing this through. I think we need another pass before merge. The main blockers are that the new OpenAI-compatible endpoint bypasses the existing online-read permission path, exposes Feast distances as OpenAI scores, and accepts OpenAI-shaped request fields that are not actually applied. I also left one existing remote-store mutation issue because it still reproduces on this head.
CI note: the latest unit-test-python (3.12, macos-14) check is still failing with two 120s CLI/apply timeouts, so this is not green yet.
| "/v1/vector_stores/{vector_store_id}/search" | ||
| ): | ||
| try: | ||
| result = await store.retrieve_online_documents_openai( |
There was a problem hiding this comment.
i think there's a bug here: this new endpoint bypasses _get_features, so auth-enabled servers never run the existing READ_ONLINE check for the target feature view. /search goes through _get_features() before retrieval, but this path calls store.retrieve_online_documents_openai(...) directly. Can we resolve the feature view, assert AuthzedAction.READ_ONLINE, and add an auth regression test for this endpoint?
There was a problem hiding this comment.
Okay I will add those test as well as the authentication checks for this newly added api endpoints
There was a problem hiding this comment.
Auth regression test is out of scope for this PR, will address in a follow-up
| for key, values in response_dict.items(): | ||
| val = values[i] if i < len(values) else None | ||
| if key == "distance": | ||
| score = float(val) if val is not None else 0.0 |
There was a problem hiding this comment.
i think this is returning the wrong OpenAI semantics. distance is lower-is-better for the default pgvector L2 path, but the OpenAI response field is score, which clients treat as higher-is-better relevance and use with score_threshold. We should either normalize per metric before putting it in score, or avoid claiming OpenAI score semantics here.
| query=request.query, | ||
| max_num_results=request.max_num_results or 10, | ||
| filters=request.filters, | ||
| ranking_options=( |
There was a problem hiding this comment.
This accepts and forwards ranking_options, including score_threshold, but retrieve_online_documents_openai() never applies it. That makes clients think thresholding is enforced when it is silently ignored. Can we either implement score_threshold now or reject unsupported ranking_options/rewrite_query with a 4xx until they are wired?
| if requested_features is None: | ||
| requested_features = [] | ||
| if "distance" not in requested_features: | ||
| requested_features.append("distance") |
There was a problem hiding this comment.
This still mutates the caller-owned requested_features list. When this is called through FeatureStore._retrieve_from_online_store_v2, Feast later builds features_to_request = requested_features + ["distance"], so distance can appear twice in the response metadata. Please copy before appending, or keep synthetic fields owned by one layer only.
| if features_to_retrieve: | ||
| feature_names = features_to_retrieve | ||
| else: | ||
| feature_names = [f.name for f in feature_view.features] |
There was a problem hiding this comment.
By default this requests every feature from the feature view, which includes the embedding/vector field itself. The response conversion below then copies every non-entity, non-distance field into attributes, so OpenAI-compatible search responses can include the raw embedding vector as metadata. Can we exclude vector fields by default and only return scalar metadata/content fields unless explicitly requested?
There was a problem hiding this comment.
Sure, I remove the vector field from the feature_names
| } | ||
| ``` | ||
|
|
||
| String equality filters work on all backends. Numeric and boolean filters require `enable_openai_compatible_store: true` in the online store config. |
There was a problem hiding this comment.
This doc says string equality filters work on all backends without the new store flag, but the Postgres and SQLite implementations reject any filter when enable_openai_compatible_store is false. Either the runtime should allow text-only filters on existing schemas, or the docs should say all filtering requires the flag for those backends.
What this PR does / why we need it:
This PR making the feast vector store api with open ai search api compatible so.
This are the changes are made.
POST /v1/vector_stores/{vector_store_id}/searchthat matches the OpenAI vector store search APIvector_store_idjust maps to a feature view nameretrieve_online_documents_v2, and returns results in OpenAI'svector_store.search_results.pageformatfeature_store.yamlunder a newembedding_modelsection (model, api_key, api_base, api_version, dimensionsfilter_models.pywith two Pydantic models:ComparisonFilter(eq, ne, gt, gte, lt, lte, in, nin) andCompoundFilter(and/or, nestable)field == 'value'entity_key IN (SELECT ...)enable_openai_compatible_storeconfig flag on every store backendvalue_numcolumn that stores int, float, double, and bool values natively alongside the existing `value_textvalueinstead ofvalue_textqueryparam renamed toembeddingsince that's what it actually isWhich issue(s) this PR fixes:
#5615
Misc