diff --git a/docs/my-website/docs/providers/bedrock_vector_store.md b/docs/my-website/docs/providers/bedrock_vector_store.md index 39e1aec5ab83..5fae0c76c11a 100644 --- a/docs/my-website/docs/providers/bedrock_vector_store.md +++ b/docs/my-website/docs/providers/bedrock_vector_store.md @@ -138,6 +138,125 @@ print(response.choices[0].message.content) +## Filter Results + +Filter by metadata attributes. + +**Operators** (OpenAI-style, auto-translated): +- `eq`, `ne`, `gt`, `gte`, `lt`, `lte`, `in`, `nin` + +**AWS operators** (use directly): +- `equals`, `notEquals`, `greaterThan`, `greaterThanOrEquals`, `lessThan`, `lessThanOrEquals`, `in`, `notIn`, `startsWith`, `listContains`, `stringContains` + + + + +```python +response = await litellm.acompletion( + model="anthropic/claude-3-5-sonnet", + messages=[{"role": "user", "content": "What are the latest updates?"}], + tools=[{ + "type": "file_search", + "vector_store_ids": ["YOUR_KNOWLEDGE_BASE_ID"], + "filters": { + "key": "category", + "value": "updates", + "operator": "eq" + } + }] +) +``` + + + + + +```python +response = await litellm.acompletion( + model="anthropic/claude-3-5-sonnet", + messages=[{"role": "user", "content": "What are the policies?"}], + tools=[{ + "type": "file_search", + "vector_store_ids": ["YOUR_KNOWLEDGE_BASE_ID"], + "filters": { + "and": [ + {"key": "category", "value": "policy", "operator": "eq"}, + {"key": "year", "value": 2024, "operator": "gte"} + ] + } + }] +) +``` + + + + + +```python +response = await litellm.acompletion( + model="anthropic/claude-3-5-sonnet", + messages=[{"role": "user", "content": "Show me technical docs"}], + tools=[{ + "type": "file_search", + "vector_store_ids": ["YOUR_KNOWLEDGE_BASE_ID"], + "filters": { + "or": [ + {"key": "category", "value": "api", "operator": "eq"}, + {"key": "category", "value": "sdk", "operator": "eq"} + ] + } + }] +) +``` + + + + + +```python +response = await litellm.acompletion( + model="anthropic/claude-3-5-sonnet", + messages=[{"role": "user", "content": "Find docs"}], + tools=[{ + "type": "file_search", + "vector_store_ids": ["YOUR_KNOWLEDGE_BASE_ID"], + "filters": { + "and": [ + {"key": "title", "value": "Guide", "operator": "stringContains"}, + {"key": "tags", "value": "important", "operator": "listContains"} + ] + } + }] +) +``` + + + + + +```bash +curl http://localhost:4000/v1/chat/completions \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $LITELLM_API_KEY" \ + -d '{ + "model": "claude-3-5-sonnet", + "messages": [{"role": "user", "content": "What are our policies?"}], + "tools": [{ + "type": "file_search", + "vector_store_ids": ["YOUR_KNOWLEDGE_BASE_ID"], + "filters": { + "and": [ + {"key": "department", "value": "engineering", "operator": "eq"}, + {"key": "type", "value": "policy", "operator": "eq"} + ] + } + }] + }' +``` + + + + ## Accessing Search Results See how to access vector store search results in your response: diff --git a/litellm/llms/base_llm/vector_store/transformation.py b/litellm/llms/base_llm/vector_store/transformation.py index 9d7ba7d61a8e..8bb54f048757 100644 --- a/litellm/llms/base_llm/vector_store/transformation.py +++ b/litellm/llms/base_llm/vector_store/transformation.py @@ -2,9 +2,11 @@ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union import httpx +from numpy import isin from litellm.types.router import GenericLiteLLMParams from litellm.types.vector_stores import ( + VECTOR_STORE_OPENAI_PARAMS, VectorStoreCreateOptionalRequestParams, VectorStoreCreateResponse, VectorStoreSearchOptionalRequestParams, @@ -24,6 +26,20 @@ class BaseVectorStoreConfig: + + def get_supported_openai_params( + self, model: str + ) -> List[VECTOR_STORE_OPENAI_PARAMS]: + return [] + + def map_openai_params( + self, + non_default_params: dict, + optional_params: dict, + drop_params: bool, + ) -> dict: + return optional_params + @abstractmethod def transform_search_vector_store_request( self, @@ -34,6 +50,7 @@ def transform_search_vector_store_request( litellm_logging_obj: LiteLLMLoggingObj, litellm_params: dict, ) -> Tuple[str, Dict]: + pass @abstractmethod diff --git a/litellm/llms/bedrock/vector_stores/transformation.py b/litellm/llms/bedrock/vector_stores/transformation.py index c05b6ba3fb1e..c485defe8a81 100644 --- a/litellm/llms/bedrock/vector_stores/transformation.py +++ b/litellm/llms/bedrock/vector_stores/transformation.py @@ -13,6 +13,7 @@ ) from litellm.types.router import GenericLiteLLMParams from litellm.types.vector_stores import ( + VECTOR_STORE_OPENAI_PARAMS, VectorStoreResultContent, VectorStoreSearchOptionalRequestParams, VectorStoreSearchResponse, @@ -32,6 +33,134 @@ def __init__(self) -> None: BaseVectorStoreConfig.__init__(self) BaseAWSLLM.__init__(self) + def get_supported_openai_params( + self, model: str + ) -> List[VECTOR_STORE_OPENAI_PARAMS]: + return ["filters", "max_num_results", "ranking_options"] + + def _map_operator_to_aws(self, operator: str) -> str: + """ + Map OpenAI-style operators to AWS Bedrock operator names. + + OpenAI uses: eq, ne, gt, gte, lt, lte, in, nin + AWS uses: equals, notEquals, greaterThan, greaterThanOrEquals, lessThan, lessThanOrEquals, in, notIn, startsWith, listContains, stringContains + """ + operator_mapping = { + "eq": "equals", + "ne": "notEquals", + "gt": "greaterThan", + "gte": "greaterThanOrEquals", + "lt": "lessThan", + "lte": "lessThanOrEquals", + "in": "in", + "nin": "notIn", + # AWS-specific operators (pass through) + "equals": "equals", + "notEquals": "notEquals", + "greaterThan": "greaterThan", + "greaterThanOrEquals": "greaterThanOrEquals", + "lessThan": "lessThan", + "lessThanOrEquals": "lessThanOrEquals", + "notIn": "notIn", + "startsWith": "startsWith", + "listContains": "listContains", + "stringContains": "stringContains", + } + return operator_mapping.get(operator, operator) + + def _map_operator_filter(self, filter_dict: dict) -> dict: + """ + Map a single OpenAI operator filter to AWS KB format. + + OpenAI format: {"key": , "value": , "operator": } + AWS KB format: {"operator": {"key": , "value": }} + """ + aws_operator = self._map_operator_to_aws(filter_dict["operator"]) + return { + aws_operator: { + "key": filter_dict["key"], + "value": filter_dict["value"], + } + } + + def _map_and_or_filters(self, value: dict) -> dict: + """ + Map OpenAI and/or filters to AWS KB format. + + OpenAI format: {"and" | "or": [{"key": , "value": , "operator": }]} + AWS KB format: {"andAll" | "orAll": [{"operator": {"key": , "value": }}]} + + Note: AWS requires andAll/orAll to have at least 2 elements. + For single filters, unwrap and return just the operator. + """ + aws_filters = {} + + if "and" in value: + and_filters = value["and"] + # If only 1 filter, return just the operator (AWS requires andAll to have >=2 elements) + if len(and_filters) == 1: + return self._map_operator_filter(and_filters[0]) + + aws_filters["andAll"] = [ + { + self._map_operator_to_aws(and_filters[i]["operator"]): { + "key": and_filters[i]["key"], + "value": and_filters[i]["value"], + } + } + for i in range(len(and_filters)) + ] + + if "or" in value: + or_filters = value["or"] + # If only 1 filter, return just the operator (AWS requires orAll to have >=2 elements) + if len(or_filters) == 1: + return self._map_operator_filter(or_filters[0]) + + aws_filters["orAll"] = [ + { + self._map_operator_to_aws(or_filters[i]["operator"]): { + "key": or_filters[i]["key"], + "value": or_filters[i]["value"], + } + } + for i in range(len(or_filters)) + ] + + return aws_filters + + def map_openai_params( + self, + non_default_params: dict, + optional_params: dict, + drop_params: bool, + ) -> dict: + for param, value in non_default_params.items(): + if param == "max_num_results": + optional_params["numberOfResults"] = value + elif param == "filters" and value is not None: + + # map the openai filters to the aws kb filters format + # openai filters = {"key": , "value": , "operator": } OR {"and" | "or": [{"key": , "value": , "operator": }]} + # aws kb filters = {"operator": {"": }} OR {"andAll | orAll": [{"operator": {"": }}]} + # 1. check if filter is in openai format + # 2. if it is, map it to the aws kb filters format + # 3. if it is not, assume it is in aws kb filters format and add it to the optional_params + aws_filters: Optional[Dict] = None + + if isinstance(value, dict): + if "operator" in value.keys(): + # Single operator - map directly (no wrapping needed) + aws_filters = self._map_operator_filter(value) + elif "and" in value.keys() or "or" in value.keys(): + aws_filters = self._map_and_or_filters(value) + else: + # Assume it's already in AWS KB format + aws_filters = value + optional_params["filters"] = aws_filters + + return optional_params + def validate_environment( self, headers: dict, litellm_params: Optional[GenericLiteLLMParams] ) -> dict: @@ -39,13 +168,13 @@ def validate_environment( headers.setdefault("Content-Type", "application/json") return headers - def get_complete_url( - self, api_base: Optional[str], litellm_params: dict - ) -> str: + def get_complete_url(self, api_base: Optional[str], litellm_params: dict) -> str: aws_region_name = litellm_params.get("aws_region_name") endpoint_url, _ = self.get_runtime_endpoint( api_base=api_base, - aws_bedrock_runtime_endpoint=litellm_params.get("aws_bedrock_runtime_endpoint"), + aws_bedrock_runtime_endpoint=litellm_params.get( + "aws_bedrock_runtime_endpoint" + ), aws_region_name=self.get_aws_region_name_for_non_llm_api_calls( aws_region_name=aws_region_name ), @@ -86,7 +215,9 @@ def transform_search_vector_store_request( # Create a properly typed retrieval configuration typed_retrieval_config: BedrockKBRetrievalConfiguration = {} if "vectorSearchConfiguration" in retrieval_config: - typed_retrieval_config["vectorSearchConfiguration"] = retrieval_config["vectorSearchConfiguration"] + typed_retrieval_config["vectorSearchConfiguration"] = retrieval_config[ + "vectorSearchConfiguration" + ] request_body["retrievalConfiguration"] = typed_retrieval_config litellm_logging_obj.model_call_details["query"] = query @@ -117,8 +248,12 @@ def _get_file_id_from_metadata(self, metadata: Dict[str, Any]) -> str: source_uri = metadata.get("x-amz-bedrock-kb-source-uri", "") if metadata else "" if source_uri: return source_uri - - chunk_id = metadata.get("x-amz-bedrock-kb-chunk-id", "unknown") if metadata else "unknown" + + chunk_id = ( + metadata.get("x-amz-bedrock-kb-chunk-id", "unknown") + if metadata + else "unknown" + ) return f"bedrock-kb-{chunk_id}" def _get_filename_from_metadata(self, metadata: Dict[str, Any]) -> str: @@ -127,18 +262,26 @@ def _get_filename_from_metadata(self, metadata: Dict[str, Any]) -> str: Tries to extract filename from source URI, falls back to domain name or data source ID. """ source_uri = metadata.get("x-amz-bedrock-kb-source-uri", "") if metadata else "" - + if source_uri: try: parsed_uri = urlparse(source_uri) - filename = parsed_uri.path.split('/')[-1] if parsed_uri.path and parsed_uri.path != '/' else parsed_uri.netloc - if not filename or filename == '/': + filename = ( + parsed_uri.path.split("/")[-1] + if parsed_uri.path and parsed_uri.path != "/" + else parsed_uri.netloc + ) + if not filename or filename == "/": filename = parsed_uri.netloc return filename except Exception: return source_uri - - data_source_id = metadata.get("x-amz-bedrock-kb-data-source-id", "unknown") if metadata else "unknown" + + data_source_id = ( + metadata.get("x-amz-bedrock-kb-data-source-id", "unknown") + if metadata + else "unknown" + ) return f"bedrock-kb-document-{data_source_id}" def _get_attributes_from_metadata(self, metadata: Dict[str, Any]) -> Dict[str, Any]: @@ -161,13 +304,13 @@ def transform_search_vector_store_response( text = content.get("text") if content else None if text is None: continue - + # Extract metadata and use helper functions metadata = item.get("metadata", {}) or {} file_id = self._get_file_id_from_metadata(metadata) filename = self._get_filename_from_metadata(metadata) attributes = self._get_attributes_from_metadata(metadata) - + results.append( VectorStoreSearchResult( score=item.get("score"), diff --git a/litellm/llms/custom_httpx/llm_http_handler.py b/litellm/llms/custom_httpx/llm_http_handler.py index b67e8823d0cf..758d6669c025 100644 --- a/litellm/llms/custom_httpx/llm_http_handler.py +++ b/litellm/llms/custom_httpx/llm_http_handler.py @@ -1593,8 +1593,6 @@ def search( headers=headers or {}, ) - - # Transform the request data = provider_config.transform_search_request( query=query, @@ -1682,7 +1680,7 @@ async def async_search( query=query, optional_params=optional_params, ) - + # Get complete URL (pass data for providers that need request body for URL construction) complete_url = provider_config.get_complete_url( api_base=api_base, @@ -1704,6 +1702,7 @@ async def async_search( if client is None or not isinstance(client, AsyncHTTPHandler): # For search providers, use special Search provider type from litellm.types.llms.custom_http import httpxSpecialProvider + async_httpx_client = get_async_httpx_client( llm_provider=httpxSpecialProvider.Search ) @@ -1726,7 +1725,7 @@ async def async_search( response = await async_httpx_client.post( url=complete_url, headers=headers, - json=data, + json=data, # type: ignore timeout=timeout, ) except Exception as e: @@ -4074,16 +4073,16 @@ def video_generation_handler( try: # Use JSON when no files, otherwise use form data with files if files and len(files) > 0: - # Use multipart/form-data when files are present - response = sync_httpx_client.post( - url=api_base, - headers=headers, - data=data, - files=files, - timeout=timeout, - ) + # Use multipart/form-data when files are present + response = sync_httpx_client.post( + url=api_base, + headers=headers, + data=data, + files=files, + timeout=timeout, + ) - # --- END MOCK VIDEO RESPONSE --- + # --- END MOCK VIDEO RESPONSE --- else: response = sync_httpx_client.post( url=api_base, @@ -4350,7 +4349,7 @@ async def async_video_content_handler( e=e, provider_config=video_content_provider_config, ) - + def video_remix_handler( self, video_id: str, @@ -4577,6 +4576,7 @@ def video_list_handler( else: # For sync calls, we'll use the async handler in a sync context import asyncio + return asyncio.run( self.async_video_list_handler( after=after, @@ -4677,7 +4677,7 @@ async def async_video_list_handler( e=e, provider_config=video_list_provider_config, ) - + async def async_video_delete_handler( self, video_id: str, @@ -4815,12 +4815,14 @@ def video_status_handler( ) # Transform the request using the provider config - url, data = video_status_provider_config.transform_video_status_retrieve_request( - video_id=video_id, - model=model, - api_base=api_base, - litellm_params=litellm_params, - headers=headers, + url, data = ( + video_status_provider_config.transform_video_status_retrieve_request( + video_id=video_id, + model=model, + api_base=api_base, + litellm_params=litellm_params, + headers=headers, + ) ) ## LOGGING @@ -4840,10 +4842,12 @@ def video_status_handler( headers=headers, ) - return video_status_provider_config.transform_video_status_retrieve_response( - model=model, - raw_response=response, - logging_obj=logging_obj, + return ( + video_status_provider_config.transform_video_status_retrieve_response( + model=model, + raw_response=response, + logging_obj=logging_obj, + ) ) except Exception as e: @@ -4893,12 +4897,14 @@ async def async_video_status_handler( ) # Transform the request using the provider config - url, data = video_status_provider_config.transform_video_status_retrieve_request( - video_id=video_id, - model=model, - api_base=api_base, - litellm_params=litellm_params, - headers=headers, + url, data = ( + video_status_provider_config.transform_video_status_retrieve_request( + video_id=video_id, + model=model, + api_base=api_base, + litellm_params=litellm_params, + headers=headers, + ) ) ## LOGGING @@ -4918,10 +4924,12 @@ async def async_video_status_handler( headers=headers, ) - return video_status_provider_config.transform_video_status_retrieve_response( - model=model, - raw_response=response, - logging_obj=logging_obj, + return ( + video_status_provider_config.transform_video_status_retrieve_response( + model=model, + raw_response=response, + logging_obj=logging_obj, + ) ) except Exception as e: @@ -5001,6 +5009,7 @@ async def async_vector_store_search_handler( ) try: + response = await async_httpx_client.post( url=url, headers=headers, diff --git a/litellm/proxy/_new_secret_config.yaml b/litellm/proxy/_new_secret_config.yaml index 318cb56619b9..dae07760557b 100644 --- a/litellm/proxy/_new_secret_config.yaml +++ b/litellm/proxy/_new_secret_config.yaml @@ -1,29 +1,13 @@ -model_list: - - model_name: gpt-5-mini - litellm_params: - model: bedrock/global.anthropic.claude-sonnet-4-5-20250929-v1:0 - - model_name: embedding-model +model_list: + - model_name: claude-3-5-sonnet litellm_params: - model: azure/text-embedding-3-large - api_base: https://krris-mh44uf7y-eastus2.cognitiveservices.azure.com/ - api_key: os.environ/AZURE_API_KEY - api_version: "2025-09-01" + model: anthropic/claude-3-5-sonnet-20240620 + api_key: os.environ/ANTHROPIC_API_KEY -guardrails: - - guardrail_name: "enkryptai-guard" +vector_store_registry: + - vector_store_name: "bedrock-company-docs" litellm_params: - guardrail: enkryptai - mode: "pre_call" - api_key: os.environ/ENKRYPTAI_API_KEY - default_on: true - policy_name: "Sample Airline Guardrail" - detectors: - toxicity: - enabled: true - nsfw: - enabled: true - pii: - enabled: true - entities: ["email", "phone", "secrets"] - injection_attack: - enabled: true + vector_store_id: "HNYBPIYVWK" + custom_llm_provider: "bedrock" + vector_store_description: "Bedrock Knowledge Base for company documents" + filters: {"and": [{"key": "category", "value": "company", "operator": "eq"}]} \ No newline at end of file diff --git a/litellm/types/vector_stores.py b/litellm/types/vector_stores.py index 58a2e7f90062..144b1853382f 100644 --- a/litellm/types/vector_stores.py +++ b/litellm/types/vector_stores.py @@ -76,6 +76,7 @@ class VectorStoreResultContent(TypedDict, total=False): class VectorStoreSearchResult(TypedDict, total=False): """Result of a vector store search""" + score: Optional[float] content: Optional[List[VectorStoreResultContent]] file_id: Optional[str] @@ -92,44 +93,55 @@ class VectorStoreSearchResponse(TypedDict, total=False): search_query: Optional[str] data: Optional[List[VectorStoreSearchResult]] + class VectorStoreSearchOptionalRequestParams(TypedDict, total=False): """TypedDict for Optional parameters supported by the vector store search API.""" + filters: Optional[Dict] - max_num_results: Optional[int] + max_num_results: Optional[int] ranking_options: Optional[Dict] rewrite_query: Optional[bool] + class VectorStoreSearchRequest(VectorStoreSearchOptionalRequestParams, total=False): """Request body for searching a vector store""" + query: Union[str, List[str]] # Vector Store Creation Types class VectorStoreExpirationPolicy(TypedDict, total=False): """The expiration policy for a vector store""" - anchor: Literal["last_active_at"] # Anchor timestamp after which the expiration policy applies + + anchor: Literal[ + "last_active_at" + ] # Anchor timestamp after which the expiration policy applies days: int # Number of days after anchor time that the vector store will expire class VectorStoreAutoChunkingStrategy(TypedDict, total=False): """Auto chunking strategy configuration""" + type: Literal["auto"] # Always "auto" class VectorStoreStaticChunkingStrategyConfig(TypedDict, total=False): """Static chunking strategy configuration""" + max_chunk_size_tokens: int # Maximum number of tokens per chunk chunk_overlap_tokens: int # Number of tokens to overlap between chunks class VectorStoreStaticChunkingStrategy(TypedDict, total=False): """Static chunking strategy""" + type: Literal["static"] # Always "static" static: VectorStoreStaticChunkingStrategyConfig class VectorStoreChunkingStrategy(TypedDict, total=False): """Union type for chunking strategies""" + # This can be either auto or static type: Literal["auto", "static"] static: Optional[VectorStoreStaticChunkingStrategyConfig] @@ -137,6 +149,7 @@ class VectorStoreChunkingStrategy(TypedDict, total=False): class VectorStoreFileCounts(TypedDict, total=False): """File counts for a vector store""" + in_progress: int completed: int failed: int @@ -146,20 +159,27 @@ class VectorStoreFileCounts(TypedDict, total=False): class VectorStoreCreateOptionalRequestParams(TypedDict, total=False): """TypedDict for Optional parameters supported by the vector store create API.""" + name: Optional[str] # Name of the vector store file_ids: Optional[List[str]] # List of File IDs that the vector store should use - expires_after: Optional[VectorStoreExpirationPolicy] # Expiration policy for the vector store - chunking_strategy: Optional[VectorStoreChunkingStrategy] # Chunking strategy for the files + expires_after: Optional[ + VectorStoreExpirationPolicy + ] # Expiration policy for the vector store + chunking_strategy: Optional[ + VectorStoreChunkingStrategy + ] # Chunking strategy for the files metadata: Optional[Dict[str, str]] # Set of key-value pairs for metadata class VectorStoreCreateRequest(VectorStoreCreateOptionalRequestParams, total=False): """Request body for creating a vector store""" + pass # All fields are optional for vector store creation class VectorStoreCreateResponse(TypedDict, total=False): """Response after creating a vector store""" + id: str # ID of the vector store object: Literal["vector_store"] # Always "vector_store" created_at: int # Unix timestamp of when the vector store was created @@ -169,5 +189,15 @@ class VectorStoreCreateResponse(TypedDict, total=False): status: Literal["expired", "in_progress", "completed"] # Status of the vector store expires_after: Optional[VectorStoreExpirationPolicy] # Expiration policy expires_at: Optional[int] # Unix timestamp of when the vector store expires - last_active_at: Optional[int] # Unix timestamp of when the vector store was last active - metadata: Optional[Dict[str, str]] # Metadata associated with the vector store \ No newline at end of file + last_active_at: Optional[ + int + ] # Unix timestamp of when the vector store was last active + metadata: Optional[Dict[str, str]] # Metadata associated with the vector store + + +VECTOR_STORE_OPENAI_PARAMS = Literal[ + "filters", + "max_num_results", + "ranking_options", + "rewrite_query", +] diff --git a/litellm/vector_stores/main.py b/litellm/vector_stores/main.py index a0d0e338edc4..5915044536c9 100644 --- a/litellm/vector_stores/main.py +++ b/litellm/vector_stores/main.py @@ -430,7 +430,8 @@ def search( # Get VectorStoreSearchOptionalRequestParams with only valid parameters vector_store_search_optional_params: VectorStoreSearchOptionalRequestParams = ( VectorStoreRequestUtils.get_requested_vector_store_search_optional_param( - local_vars + local_vars, + vector_store_provider_config=vector_store_provider_config, ) ) diff --git a/litellm/vector_stores/utils.py b/litellm/vector_stores/utils.py index b7eb7790adde..9430c68ccc94 100644 --- a/litellm/vector_stores/utils.py +++ b/litellm/vector_stores/utils.py @@ -1,5 +1,6 @@ from typing import Any, Dict, cast, get_type_hints +from litellm.llms.base_llm.vector_store.transformation import BaseVectorStoreConfig from litellm.types.vector_stores import ( VectorStoreCreateOptionalRequestParams, VectorStoreSearchOptionalRequestParams, @@ -12,6 +13,7 @@ class VectorStoreRequestUtils: @staticmethod def get_requested_vector_store_search_optional_param( params: Dict[str, Any], + vector_store_provider_config: BaseVectorStoreConfig, ) -> VectorStoreSearchOptionalRequestParams: """ Filter parameters to only include those defined in VectorStoreSearchOptionalRequestParams. @@ -27,7 +29,13 @@ def get_requested_vector_store_search_optional_param( k: v for k, v in params.items() if k in valid_keys and v is not None } - return cast(VectorStoreSearchOptionalRequestParams, filtered_params) + optional_params = vector_store_provider_config.map_openai_params( + non_default_params=params, + optional_params=filtered_params, + drop_params=False, + ) + + return cast(VectorStoreSearchOptionalRequestParams, optional_params) @staticmethod def get_requested_vector_store_create_optional_param( @@ -48,4 +56,3 @@ def get_requested_vector_store_create_optional_param( } return cast(VectorStoreCreateOptionalRequestParams, filtered_params) -