Skip to content

feat: Making feast vector store with open ai search api compatible#6121

Open
patelchaitany wants to merge 3 commits into
feast-dev:masterfrom
patelchaitany:enh/openai-compatibel-store-api
Open

feat: Making feast vector store with open ai search api compatible#6121
patelchaitany wants to merge 3 commits into
feast-dev:masterfrom
patelchaitany:enh/openai-compatibel-store-api

Conversation

@patelchaitany

@patelchaitany patelchaitany commented Mar 17, 2026

Copy link
Copy Markdown
Contributor

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.

  1. New OpenAI-compatible endpoint
  • Added POST /v1/vector_stores/{vector_store_id}/search that matches the OpenAI vector store search API
  • vector_store_id just maps to a feature view name
  • Takes a query string, embeds it via LiteLLM, calls retrieve_online_documents_v2, and returns results in OpenAI's vector_store.search_results.page format
  • LiteLLM embedding config goes in feature_store.yaml under a new embedding_model section (model, api_key, api_base, api_version, dimensions
  1. Metadata filtering
  • New filter_models.py with two Pydantic models: ComparisonFilter (eq, ne, gt, gte, lt, lte, in, nin) and CompoundFilter (and/or, nestable)
  • Threaded filters through the entire retrieval stack down to each online store
  • Each store translates filters into its native query language:
    • Elasticsearch: Query DSL clauses (term, range, terms, bool)
    • Milvus: boolean expressions like field == 'value'
    • Postgres: parameterized SQL subqueries with entity_key IN (SELECT ...)
    • SQLite: same approach as Postgres, SQLite syntax
  1. Numeric storage fix
  • Without this change, all values are stored as text, so '9' > '100' is true in filters
  • New enable_openai_compatible_store config flag on every store backend
  • When enabled, adds a value_num column that stores int, float, double, and bool values natively alongside the existing `value_text
  1. Bug fixes picked up along the way
  • SQLite BM25 search was reading raw value instead of value_text
  • SQLite's query param renamed to embedding since that's what it actually is
  • Added input escaping for Milvus query strings
  1. Tests
  • 160 lines of unit tests for filter models (valid/invalid operators, value types, nested compounds)
  • ~320 lines of integration tests covering filtered vector search, filtered text search, OpenAI response shape, and error cases (no embedding config, nonexistent feature view, empty results)

Which issue(s) this PR fixes:

#5615

Misc


Open with Devin

@ntkathole ntkathole changed the title feat: making feast vector store with open ai search api compatible feat: Making feast vector store with open ai search api compatible Mar 17, 2026
@patelchaitany patelchaitany force-pushed the enh/openai-compatibel-store-api branch 4 times, most recently from e45f167 to c8392a9 Compare March 23, 2026 11:17
@patelchaitany patelchaitany changed the title feat: Making feast vector store with open ai search api compatible feat: Making feast vector store with open ai search api compatible Mar 23, 2026
@patelchaitany patelchaitany force-pushed the enh/openai-compatibel-store-api branch 5 times, most recently from 7e8adfb to 3f541ad Compare March 24, 2026 11:29
@patelchaitany patelchaitany marked this pull request as ready for review March 24, 2026 11:29
@patelchaitany patelchaitany requested review from a team as code owners March 24, 2026 11:29
@patelchaitany patelchaitany requested review from HaoXuAI, nquinn408 and redhatHameed and removed request for a team March 24, 2026 11:29
devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

@patelchaitany patelchaitany force-pushed the enh/openai-compatibel-store-api branch from cd692b9 to 086bed5 Compare March 30, 2026 12:05
devin-ai-integration[bot]

This comment was marked as resolved.

@patelchaitany patelchaitany force-pushed the enh/openai-compatibel-store-api branch from 086bed5 to 9778002 Compare March 30, 2026 12:51
devin-ai-integration[bot]

This comment was marked as resolved.

@patelchaitany patelchaitany force-pushed the enh/openai-compatibel-store-api branch 2 times, most recently from 68c843c to e29e557 Compare March 31, 2026 07:05
devin-ai-integration[bot]

This comment was marked as resolved.

@patelchaitany patelchaitany force-pushed the enh/openai-compatibel-store-api branch 2 times, most recently from f0552c7 to dafe9fd Compare March 31, 2026 09:26
devin-ai-integration[bot]

This comment was marked as resolved.

Comment thread sdk/python/feast/feature_server.py Outdated
query=request.query,
max_num_results=request.max_num_results or 10,
filters=(
request.filters.model_dump() if request.filters else None

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.

request.filters.model_dump() can be removed once line 146 uses ComparisonFilter/CompoundFilter directly

@patelchaitany patelchaitany force-pushed the enh/openai-compatibel-store-api branch 2 times, most recently from e7ba622 to 502286f Compare May 14, 2026 11:26
@patelchaitany patelchaitany requested a review from ntkathole May 15, 2026 04:06
---
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

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.

update date before merge

@ntkathole

ntkathole commented May 15, 2026

Copy link
Copy Markdown
Member

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

@patelchaitany patelchaitany force-pushed the enh/openai-compatibel-store-api branch from 502286f to 925eb2e Compare May 15, 2026 05:49
@patelchaitany patelchaitany requested a review from ntkathole May 15, 2026 05:50
@patelchaitany patelchaitany force-pushed the enh/openai-compatibel-store-api branch from 925eb2e to c2782f5 Compare May 15, 2026 06:31

@ntkathole ntkathole left a comment

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.

looks good to me!

@franciscojavierarceo will you be able to take a look once?

@patelchaitany

Copy link
Copy Markdown
Contributor Author

@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.

@patelchaitany patelchaitany requested a review from ntkathole May 19, 2026 07:32
@patelchaitany patelchaitany force-pushed the enh/openai-compatibel-store-api branch 4 times, most recently from 58dcb47 to ba5ad00 Compare May 20, 2026 11:19
@ntkathole ntkathole force-pushed the enh/openai-compatibel-store-api branch from ba5ad00 to 180d80f Compare May 21, 2026 05:05
@patelchaitany

Copy link
Copy Markdown
Contributor Author

@franciscojavierarceo, can you please review it.

@franciscojavierarceo franciscojavierarceo left a comment

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.

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.

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.

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

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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).

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.

yeah this seems acceptable if it's opt-in but we should also support sentence transformers.

@patelchaitany

Copy link
Copy Markdown
Contributor Author

@franciscojavierarceo, I have created an RFC as you requested Docs

@patelchaitany

Copy link
Copy Markdown
Contributor Author

@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 franciscojavierarceo left a comment

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.

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(

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.

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?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Okay I will add those test as well as the authentication checks for this newly added api endpoints

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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

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.

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=(

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 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")

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 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]

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.

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?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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.

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 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants