diff --git a/docs/my-website/docs/providers/azure_ai_vector_stores.md b/docs/my-website/docs/providers/azure_ai_vector_stores.md new file mode 100644 index 00000000000..d3abb78bbe4 --- /dev/null +++ b/docs/my-website/docs/providers/azure_ai_vector_stores.md @@ -0,0 +1,245 @@ +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Azure AI Search - Vector Store + +Use Azure AI Search as a vector store for RAG. + +## Quick Start + +You need three things: +1. An Azure AI Search service +2. An embedding model (to convert your queries to vectors) +3. A search index with vector fields + +## Usage + + + + +### Basic Search + +```python +from litellm import vector_stores +import os + +# Set your credentials +os.environ["AZURE_SEARCH_API_KEY"] = "your-search-api-key" +os.environ["AZURE_AI_SEARCH_EMBEDDING_API_BASE"] = "your-embedding-endpoint" +os.environ["AZURE_AI_SEARCH_EMBEDDING_API_KEY"] = "your-embedding-api-key" + +# Search the vector store +response = vector_stores.search( + vector_store_id="my-vector-index", # Your Azure AI Search index name + query="What is the capital of France?", + custom_llm_provider="azure_ai", + azure_search_service_name="your-search-service", + litellm_embedding_model="azure/text-embedding-3-large", + litellm_embedding_config={ + "api_base": os.getenv("AZURE_AI_SEARCH_EMBEDDING_API_BASE"), + "api_key": os.getenv("AZURE_AI_SEARCH_EMBEDDING_API_KEY"), + }, + api_key=os.getenv("AZURE_SEARCH_API_KEY"), +) + +print(response) +``` + +### Async Search + +```python +from litellm import vector_stores + +response = await vector_stores.asearch( + vector_store_id="my-vector-index", + query="What is the capital of France?", + custom_llm_provider="azure_ai", + azure_search_service_name="your-search-service", + litellm_embedding_model="azure/text-embedding-3-large", + litellm_embedding_config={ + "api_base": os.getenv("AZURE_AI_SEARCH_EMBEDDING_API_BASE"), + "api_key": os.getenv("AZURE_AI_SEARCH_EMBEDDING_API_KEY"), + }, + api_key=os.getenv("AZURE_SEARCH_API_KEY"), +) + +print(response) +``` + +### Advanced Options + +```python +from litellm import vector_stores + +response = vector_stores.search( + vector_store_id="my-vector-index", + query="What is the capital of France?", + custom_llm_provider="azure_ai", + azure_search_service_name="your-search-service", + litellm_embedding_model="azure/text-embedding-3-large", + litellm_embedding_config={ + "api_base": os.getenv("AZURE_AI_SEARCH_EMBEDDING_API_BASE"), + "api_key": os.getenv("AZURE_AI_SEARCH_EMBEDDING_API_KEY"), + }, + api_key=os.getenv("AZURE_SEARCH_API_KEY"), + top_k=10, # Number of results to return + azure_search_vector_field="contentVector", # Custom vector field name +) + +print(response) +``` + + + + + +### Setup Config + +Add this to your config.yaml: + +```yaml +vector_store_registry: + - vector_store_name: "azure-ai-search-litellm-website-knowledgebase" + litellm_params: + vector_store_id: "test-litellm-app_1761094730750" + custom_llm_provider: "azure_ai" + api_key: os.environ/AZURE_SEARCH_API_KEY + litellm_embedding_model: "azure/text-embedding-3-large" + litellm_embedding_config: + api_base: https://krris-mh44uf7y-eastus2.cognitiveservices.azure.com/ + api_key: os.environ/AZURE_API_KEY + api_version: "2025-09-01" +``` + +### Start Proxy + +```bash +litellm --config /path/to/config.yaml +``` + +### Search via API + +```bash +curl -X POST 'http://0.0.0.0:4000/v1/vector_stores/my-vector-index/search' \ +-H 'Content-Type: application/json' \ +-H 'Authorization: Bearer sk-1234' \ +-d '{ + "query": "What is the capital of France?", +}' +``` + + + + +## Required Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `vector_store_id` | string | Your Azure AI Search index name | +| `custom_llm_provider` | string | Set to `"azure_ai"` | +| `azure_search_service_name` | string | Name of your Azure AI Search service | +| `litellm_embedding_model` | string | Model to generate query embeddings (e.g., `"azure/text-embedding-3-large"`) | +| `litellm_embedding_config` | dict | Config for the embedding model (api_base, api_key, api_version) | +| `api_key` | string | Your Azure AI Search API key | + +## Supported Features + +| Feature | Status | Notes | +|---------|--------|-------| +| Logging | ✅ Supported | Full logging support available | +| Guardrails | ❌ Not Yet Supported | Guardrails are not currently supported for vector stores | +| Cost Tracking | ✅ Supported | Cost is $0 according to Azure | +| Unified API | ✅ Supported | Call via OpenAI compatible `/v1/vector_stores/search` endpoint | +| Passthrough | ❌ Not yet supported | | + +## Response Format + +The response follows the standard LiteLLM vector store format: + +```json +{ + "object": "vector_store.search_results.page", + "search_query": "What is the capital of France?", + "data": [ + { + "score": 0.95, + "content": [ + { + "text": "Paris is the capital of France...", + "type": "text" + } + ], + "file_id": "doc_123", + "filename": "Document doc_123", + "attributes": { + "document_id": "doc_123" + } + } + ] +} +``` + +## How It Works + +When you search: + +1. LiteLLM converts your query to a vector using the embedding model you specified +2. It sends the vector to Azure AI Search +3. Azure AI Search finds the most similar documents in your index +4. Results come back with similarity scores + +The embedding model can be any model supported by LiteLLM - Azure OpenAI, OpenAI, Bedrock, etc. + +## Setting Up Your Azure AI Search Index + +Your index needs a vector field. Here's what that looks like: + +```json +{ + "name": "my-vector-index", + "fields": [ + { + "name": "id", + "type": "Edm.String", + "key": true + }, + { + "name": "content", + "type": "Edm.String" + }, + { + "name": "contentVector", + "type": "Collection(Edm.Single)", + "searchable": true, + "dimensions": 1536, + "vectorSearchProfile": "myVectorProfile" + } + ] +} +``` + +The vector dimensions must match your embedding model. For example: +- `text-embedding-3-large`: 1536 dimensions +- `text-embedding-3-small`: 1536 dimensions +- `text-embedding-ada-002`: 1536 dimensions + + +## Common Issues + +**"Failed to generate embedding for query"** + +Your embedding model config is wrong. Check: +- `litellm_embedding_config` has the right api_base and api_key +- The embedding model name is correct +- Your credentials work + +**"Index not found"** + +The `vector_store_id` doesn't match any index in your search service. Check: +- The index name is correct +- You're using the right search service name + +**"Field 'contentVector' not found"** + +Your index uses a different vector field name. Pass it via `azure_search_vector_field`. + diff --git a/docs/my-website/docs/vector_stores/create.md b/docs/my-website/docs/vector_stores/create.md index f9bdcb9b34c..c97f88a2543 100644 --- a/docs/my-website/docs/vector_stores/create.md +++ b/docs/my-website/docs/vector_stores/create.md @@ -12,7 +12,7 @@ Create a vector store which can be used to store and search document chunks for | Cost Tracking | ✅ | Tracked per vector store operation | | Logging | ✅ | Works across all integrations | | End-user Tracking | ✅ | | -| Support LLM Providers | **OpenAI, Azure OpenAI, Bedrock, Vertex RAG Engine** | Full vector stores API support across providers | +| Support LLM Providers | **OpenAI** | Full vector stores API support across providers | ## Usage @@ -21,7 +21,7 @@ Create a vector store which can be used to store and search document chunks for -#### Non-streaming example +#### Async example ```python showLineNumbers title="Create Vector Store - Basic" import litellm @@ -32,7 +32,7 @@ response = await litellm.vector_stores.acreate( print(response) ``` -#### Synchronous example +#### Sync example ```python showLineNumbers title="Create Vector Store - Sync" import litellm diff --git a/docs/my-website/docs/vector_stores/search.md b/docs/my-website/docs/vector_stores/search.md index 5c3d02be3da..5d0a2b737b9 100644 --- a/docs/my-website/docs/vector_stores/search.md +++ b/docs/my-website/docs/vector_stores/search.md @@ -12,7 +12,7 @@ Search a vector store for relevant chunks based on a query and file attributes f | Cost Tracking | ✅ | Tracked per search operation | | Logging | ✅ | Works across all integrations | | End-user Tracking | ✅ | | -| Support LLM Providers | **OpenAI, Azure OpenAI, Bedrock, Vertex RAG Engine** | Full vector stores API support across providers | +| Support LLM Providers | **OpenAI, Azure OpenAI, Bedrock, Vertex RAG Engine, Azure AI** | Full vector stores API support across providers | ## Usage @@ -105,6 +105,35 @@ response = await litellm.vector_stores.asearch( print(response) ``` + + + + +#### Using Azure AI Search +```python showLineNumbers title="Search Vector Store - Azure AI Provider" +import litellm +import os + +# Set credentials +os.environ["AZURE_SEARCH_API_KEY"] = "your-search-api-key" + +response = await litellm.vector_stores.asearch( + vector_store_id="my-vector-index", + query="What is the capital of France?", + custom_llm_provider="azure_ai", + azure_search_service_name="your-search-service", + litellm_embedding_model="azure/text-embedding-3-large", + litellm_embedding_config={ + "api_base": "your-embedding-endpoint", + "api_key": "your-embedding-api-key", + }, + api_key=os.getenv("AZURE_SEARCH_API_KEY"), +) +print(response) +``` + +[See full Azure AI vector store documentation](../providers/azure_ai_vector_stores.md) + diff --git a/docs/my-website/sidebars.js b/docs/my-website/sidebars.js index 2dcc5b48b28..6811aeada1d 100644 --- a/docs/my-website/sidebars.js +++ b/docs/my-website/sidebars.js @@ -400,6 +400,7 @@ const sidebars = { type: "category", label: "/vector_stores", items: [ + "vector_stores/create", "vector_stores/search", ] }, @@ -450,6 +451,7 @@ const sidebars = { "providers/azure_ocr", "providers/azure_ai_speech", "providers/azure_ai_img", + "providers/azure_ai_vector_stores", ] }, { diff --git a/enterprise/litellm_enterprise/proxy/hooks/managed_files.py b/enterprise/litellm_enterprise/proxy/hooks/managed_files.py index 1dfa746b4f0..c55a4f03898 100644 --- a/enterprise/litellm_enterprise/proxy/hooks/managed_files.py +++ b/enterprise/litellm_enterprise/proxy/hooks/managed_files.py @@ -152,7 +152,7 @@ class _PROXY_LiteLLMManagedFiles(CustomLogger, BaseFileEndpoints): "status": file_object.status, }, "update": {}, # don't do anything if it already exists - } + }, ) async def get_unified_file_id( @@ -224,9 +224,10 @@ class _PROXY_LiteLLMManagedFiles(CustomLogger, BaseFileEndpoints): where={"unified_object_id": unified_object_id} ) ) + if managed_object: return managed_object.created_by == user_id - return False + return True # don't raise error if managed object is not found async def get_user_created_file_ids( self, user_api_key_dict: UserAPIKeyAuth, model_object_ids: List[str] diff --git a/enterprise/litellm_enterprise/proxy/vector_stores/endpoints.py b/enterprise/litellm_enterprise/proxy/vector_stores/endpoints.py index bb4b546b8d3..fdb1dba372f 100644 --- a/enterprise/litellm_enterprise/proxy/vector_stores/endpoints.py +++ b/enterprise/litellm_enterprise/proxy/vector_stores/endpoints.py @@ -9,6 +9,7 @@ All /vector_store management endpoints """ import copy +import json from typing import List, Optional from fastapi import APIRouter, Depends, HTTPException @@ -16,7 +17,11 @@ from fastapi import APIRouter, Depends, HTTPException import litellm from litellm._logging import verbose_proxy_logger from litellm.litellm_core_utils.safe_json_dumps import safe_dumps -from litellm.proxy._types import UserAPIKeyAuth +from litellm.proxy._types import ( + LiteLLM_ManagedVectorStoresTable, + ResponseLiteLLM_ManagedVectorStore, + UserAPIKeyAuth, +) from litellm.proxy.auth.user_api_key_auth import user_api_key_auth from litellm.types.vector_stores import ( LiteLLM_ManagedVectorStore, @@ -29,6 +34,7 @@ from litellm.vector_stores.vector_store_registry import VectorStoreRegistry router = APIRouter() + ######################################################## # Management Endpoints ######################################################## @@ -79,7 +85,9 @@ async def new_vector_store( litellm_params_json: Optional[str] = None _input_litellm_params: dict = vector_store.get("litellm_params", {}) or {} if _input_litellm_params is not None: - litellm_params_dict = GenericLiteLLMParams(**_input_litellm_params).model_dump(exclude_none=True) + litellm_params_dict = GenericLiteLLMParams( + **_input_litellm_params + ).model_dump(exclude_none=True) litellm_params_json = safe_dumps(litellm_params_dict) del vector_store["litellm_params"] @@ -227,6 +235,7 @@ async def delete_vector_store( "/vector_store/info", tags=["vector store management"], dependencies=[Depends(user_api_key_auth)], + response_model=ResponseLiteLLM_ManagedVectorStore, ) async def get_vector_store_info( data: VectorStoreInfoRequest, @@ -239,8 +248,39 @@ async def get_vector_store_info( raise HTTPException(status_code=500, detail="Database not connected") try: - vector_store = await prisma_client.db.litellm_managedvectorstorestable.find_unique( - where={"vector_store_id": data.vector_store_id} + if litellm.vector_store_registry is not None: + vector_store = litellm.vector_store_registry.get_litellm_managed_vector_store_from_registry( + vector_store_id=data.vector_store_id + ) + if vector_store is not None: + vector_store_metadata = vector_store.get("vector_store_metadata") + # Parse metadata if it's a JSON string + parsed_metadata: Optional[dict] = None + if isinstance(vector_store_metadata, str): + parsed_metadata = json.loads(vector_store_metadata) + elif isinstance(vector_store_metadata, dict): + parsed_metadata = vector_store_metadata + + vector_store_pydantic_obj = LiteLLM_ManagedVectorStoresTable( + vector_store_id=vector_store.get("vector_store_id") or "", + custom_llm_provider=vector_store.get("custom_llm_provider") or "", + vector_store_name=vector_store.get("vector_store_name") or None, + vector_store_description=vector_store.get( + "vector_store_description" + ) + or None, + vector_store_metadata=parsed_metadata, + created_at=vector_store.get("created_at") or None, + updated_at=vector_store.get("updated_at") or None, + litellm_credential_name=vector_store.get("litellm_credential_name"), + litellm_params=vector_store.get("litellm_params") or None, + ) + return {"vector_store": vector_store_pydantic_obj} + + vector_store = ( + await prisma_client.db.litellm_managedvectorstorestable.find_unique( + where={"vector_store_id": data.vector_store_id} + ) ) if vector_store is None: raise HTTPException( @@ -248,7 +288,7 @@ async def get_vector_store_info( detail=f"Vector store with ID {data.vector_store_id} not found", ) - vector_store_dict = vector_store.model_dump() + vector_store_dict = vector_store.model_dump() # type: ignore[attr-defined] return {"vector_store": vector_store_dict} except Exception as e: verbose_proxy_logger.exception(f"Error getting vector store info: {str(e)}") @@ -274,7 +314,9 @@ async def update_vector_store( update_data = data.model_dump(exclude_unset=True) vector_store_id = update_data.pop("vector_store_id") if update_data.get("vector_store_metadata") is not None: - update_data["vector_store_metadata"] = safe_dumps(update_data["vector_store_metadata"]) + update_data["vector_store_metadata"] = safe_dumps( + update_data["vector_store_metadata"] + ) updated = await prisma_client.db.litellm_managedvectorstorestable.update( where={"vector_store_id": vector_store_id}, diff --git a/litellm/llms/azure_ai/vector_stores/__init__.py b/litellm/llms/azure_ai/vector_stores/__init__.py new file mode 100644 index 00000000000..74ffe1afb17 --- /dev/null +++ b/litellm/llms/azure_ai/vector_stores/__init__.py @@ -0,0 +1,4 @@ +from litellm.llms.azure_ai.vector_stores.transformation import AzureAIVectorStoreConfig + +__all__ = ["AzureAIVectorStoreConfig"] + diff --git a/litellm/llms/azure_ai/vector_stores/transformation.py b/litellm/llms/azure_ai/vector_stores/transformation.py new file mode 100644 index 00000000000..f99d2c4c4b2 --- /dev/null +++ b/litellm/llms/azure_ai/vector_stores/transformation.py @@ -0,0 +1,237 @@ +from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union + +import httpx + +import litellm +from litellm.llms.azure.common_utils import BaseAzureLLM +from litellm.llms.base_llm.vector_store.transformation import BaseVectorStoreConfig +from litellm.types.router import GenericLiteLLMParams +from litellm.types.vector_stores import ( + VectorStoreCreateOptionalRequestParams, + VectorStoreCreateResponse, + VectorStoreResultContent, + VectorStoreSearchOptionalRequestParams, + VectorStoreSearchResponse, + VectorStoreSearchResult, +) + +if TYPE_CHECKING: + from litellm.litellm_core_utils.litellm_logging import Logging as _LiteLLMLoggingObj + + LiteLLMLoggingObj = _LiteLLMLoggingObj +else: + LiteLLMLoggingObj = Any + + +class AzureAIVectorStoreConfig(BaseVectorStoreConfig, BaseAzureLLM): + """ + Configuration for Azure AI Search Vector Store + + This implementation uses the Azure AI Search API for vector store operations. + Supports vector search with embeddings generated via litellm.embeddings. + """ + + def __init__(self): + super().__init__() + + def validate_environment( + self, headers: dict, litellm_params: Optional[GenericLiteLLMParams] + ) -> dict: + + basic_headers = self._base_validate_azure_environment(headers, litellm_params) + basic_headers.update({"Content-Type": "application/json"}) + return basic_headers + + def get_complete_url( + self, + api_base: Optional[str], + litellm_params: dict, + ) -> str: + """ + Get the base endpoint for Azure AI Search API + + Expected format: https://{search_service_name}.search.windows.net + """ + if api_base: + return api_base.rstrip("/") + + # Get search service name from litellm_params + search_service_name = litellm_params.get("azure_search_service_name") + + if not search_service_name: + raise ValueError( + "Azure AI Search service name is required. " + "Provide it via litellm_params['azure_search_service_name'] or api_base parameter" + ) + + # Azure AI Search endpoint + return f"https://{search_service_name}.search.windows.net" + + def transform_search_vector_store_request( + self, + vector_store_id: str, + query: Union[str, List[str]], + vector_store_search_optional_params: VectorStoreSearchOptionalRequestParams, + api_base: str, + litellm_logging_obj: LiteLLMLoggingObj, + litellm_params: dict, + ) -> Tuple[str, Dict[str, Any]]: + """ + Transform search request for Azure AI Search API + + Generates embeddings using litellm.embeddings and constructs Azure AI Search request + """ + # Convert query to string if it's a list + if isinstance(query, list): + query = " ".join(query) + + # Get embedding model from litellm_params (required) + embedding_model = litellm_params.get("litellm_embedding_model") + if not embedding_model: + raise ValueError( + "embedding_model is required in litellm_params for Azure AI Search. " + "Example: litellm_params['embedding_model'] = 'azure/text-embedding-3-large'" + ) + + embedding_config = litellm_params.get("litellm_embedding_config", {}) + if not embedding_config: + raise ValueError( + "embedding_config is required in litellm_params for Azure AI Search. " + "Example: litellm_params['embedding_config'] = {'api_base': 'https://krris-mh44uf7y-eastus2.cognitiveservices.azure.com/', 'api_key': 'os.environ/AZURE_API_KEY', 'api_version': '2025-09-01'}" + ) + + # Get vector field name (defaults to contentVector) + vector_field = litellm_params.get("azure_search_vector_field", "contentVector") + + # Get top_k (number of results to return) + top_k = vector_store_search_optional_params.get("top_k", 10) + + # Generate embedding for the query using litellm.embeddings + try: + embedding_response = litellm.embedding( + model=embedding_model, + input=[query], + **embedding_config, + ) + query_vector = embedding_response.data[0]["embedding"] + except Exception as e: + raise Exception(f"Failed to generate embedding for query: {str(e)}") + + # Azure AI Search endpoint for search + index_name = vector_store_id # vector_store_id is the index name + url = f"{api_base}/indexes/{index_name}/docs/search?api-version=2024-07-01" + + # Build the request body for Azure AI Search with vector search + request_body = { + "search": "*", # Get all documents (filtered by vector similarity) + "vectorQueries": [ + { + "vector": query_vector, + "fields": vector_field, + "kind": "vector", + "k": top_k, # Number of nearest neighbors to return + } + ], + "select": "id,content", # Fields to return (customize based on schema) + "top": top_k, + } + + ######################################################### + # Update logging object with details of the request + ######################################################### + litellm_logging_obj.model_call_details["input"] = query + litellm_logging_obj.model_call_details["embedding_model"] = embedding_model + litellm_logging_obj.model_call_details["top_k"] = top_k + + return url, request_body + + def transform_search_vector_store_response( + self, response: httpx.Response, litellm_logging_obj: LiteLLMLoggingObj + ) -> VectorStoreSearchResponse: + """ + Transform Azure AI Search API response to standard vector store search response + + Handles the format from Azure AI Search which returns: + { + "value": [ + { + "id": "...", + "content": "...", + "@search.score": 0.95, + ... (other fields) + } + ] + } + """ + try: + response_json = response.json() + + # Extract results from Azure AI Search API response + results = response_json.get("value", []) + + # Transform results to standard format + search_results: List[VectorStoreSearchResult] = [] + for result in results: + # Extract document ID + document_id = result.get("id", "") + + # Extract text content + text_content = result.get("content", "") + + content = [ + VectorStoreResultContent( + text=text_content, + type="text", + ) + ] + + # Get the search score (relevance score from Azure AI Search) + score = result.get("@search.score", 0.0) + + # Use document ID as both file_id and filename + file_id = document_id + filename = f"Document {document_id}" + + # Build attributes with all available metadata + # Exclude system fields and already-processed fields + attributes = {} + for key, value in result.items(): + if key not in ["id", "content", "contentVector", "@search.score"]: + attributes[key] = value + + # Always include document_id in attributes + attributes["document_id"] = document_id + + result_obj = VectorStoreSearchResult( + score=score, + content=content, + file_id=file_id, + filename=filename, + attributes=attributes, + ) + search_results.append(result_obj) + + return VectorStoreSearchResponse( + object="vector_store.search_results.page", + search_query=litellm_logging_obj.model_call_details.get("input", ""), + data=search_results, + ) + + except Exception as e: + raise self.get_error_class( + error_message=str(e), + status_code=response.status_code, + headers=response.headers, + ) + + def transform_create_vector_store_request( + self, + vector_store_create_optional_params: VectorStoreCreateOptionalRequestParams, + api_base: str, + ) -> Tuple[str, Dict]: + raise NotImplementedError + + def transform_create_vector_store_response( + self, response: httpx.Response + ) -> VectorStoreCreateResponse: + raise NotImplementedError diff --git a/litellm/proxy/_new_secret_config.yaml b/litellm/proxy/_new_secret_config.yaml index 7725446d10c..0ee11fdb3f4 100644 --- a/litellm/proxy/_new_secret_config.yaml +++ b/litellm/proxy/_new_secret_config.yaml @@ -2,35 +2,20 @@ model_list: - model_name: bedrock-anthropic-claude-sonnet-4-5-20250929-v1 litellm_params: model: bedrock/global.anthropic.claude-sonnet-4-5-20250929-v1:0 + - model_name: embedding-model + 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" vector_store_registry: - - vector_store_name: "vertex-ai-litellm-website-knowledgebase" + - vector_store_name: "azure-ai-search-litellm-website-knowledgebase" litellm_params: vector_store_id: "test-litellm-app_1761094730750" - custom_llm_provider: "vertex_ai/search_api" - vertex_project: "test-litellm-app" - vertex_location: "us-central1" - vector_store_description: "Vertex AI vector store for the Litellm website knowledgebase" - vector_store_metadata: - source: "https://www.litellm.com/docs" -mcp_servers: - github_mcp: - url: "https://api.githubcopilot.com/mcp" - auth_type: oauth2 - authorization_url: https://github.com/login/oauth/authorize - token_url: https://github.com/login/oauth/access_token - client_id: os.environ/GITHUB_OAUTH_CLIENT_ID - client_secret: os.environ/GITHUB_OAUTH_CLIENT_SECRET - scopes: ["public_repo", "user:email"] - -general_settings: - pass_through_endpoints: - - path: "/fake-openai-proxy-10" # Route on LiteLLM Proxy - target: "https://webhook.site/74bbcc59-a61f-4028-81e2-9e06814e81fe" # Target endpoint - headers: # Headers to forward - Authorization: "bearer sk-1234" - content-type: application/json - accept: application/json - auth: true - include_subpath: true - cost_per_request: 0 + custom_llm_provider: "azure_ai" + litellm_embedding_model: "azure/text-embedding-3-large" + litellm_embedding_config: + api_base: https://krris-mh44uf7y-eastus2.cognitiveservices.azure.com/ + api_key: os.environ/AZURE_API_KEY + api_version: "2025-09-01" \ No newline at end of file diff --git a/litellm/proxy/_types.py b/litellm/proxy/_types.py index 36446e65e66..a02e15e0103 100644 --- a/litellm/proxy/_types.py +++ b/litellm/proxy/_types.py @@ -323,11 +323,9 @@ class LiteLLMRoutes(enum.Enum): "/v1/vector_stores", "/vector_stores/{vector_store_id}/search", "/v1/vector_stores/{vector_store_id}/search", - # search "/search", "/v1/search", - # OCR "/ocr", "/v1/ocr", @@ -3496,3 +3494,19 @@ class EnterpriseLicenseData(TypedDict, total=False): allowed_features: List[str] max_users: int max_teams: int + + +class LiteLLM_ManagedVectorStoresTable(LiteLLMPydanticObjectBase): + vector_store_id: str + custom_llm_provider: str + vector_store_name: Optional[str] + vector_store_description: Optional[str] + vector_store_metadata: Optional[Dict[str, Any]] + created_at: Optional[datetime] + updated_at: Optional[datetime] + litellm_credential_name: Optional[str] + litellm_params: Optional[Dict[str, Any]] + + +class ResponseLiteLLM_ManagedVectorStore(TypedDict, total=False): + vector_store: LiteLLM_ManagedVectorStoresTable diff --git a/litellm/utils.py b/litellm/utils.py index cb712b69684..5ad1553b03f 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -3062,26 +3062,24 @@ def _remove_unsupported_params( def filter_out_litellm_params(kwargs: dict) -> dict: """ Filter out LiteLLM internal parameters from kwargs dict. - - Returns a new dict containing only non-LiteLLM parameters that should be + + Returns a new dict containing only non-LiteLLM parameters that should be passed to external provider APIs. - + Args: kwargs: Dictionary that may contain LiteLLM internal parameters - + Returns: Dictionary with LiteLLM internal parameters filtered out - + Example: >>> kwargs = {"query": "test", "shared_session": session_obj, "metadata": {}} >>> filtered = filter_out_litellm_params(kwargs) >>> # filtered = {"query": "test"} """ - + return { - key: value - for key, value in kwargs.items() - if key not in all_litellm_params + key: value for key, value in kwargs.items() if key not in all_litellm_params } @@ -7536,6 +7534,12 @@ class ProviderConfigManager: ) return PGVectorStoreConfig() + elif litellm.LlmProviders.AZURE_AI == provider: + from litellm.llms.azure_ai.vector_stores.transformation import ( + AzureAIVectorStoreConfig, + ) + + return AzureAIVectorStoreConfig() return None @staticmethod @@ -7689,15 +7693,9 @@ class ProviderConfigManager: """ Get Search configuration for a given provider. """ - from litellm.llms.dataforseo.search.transformation import ( - DataForSEOSearchConfig, - ) - from litellm.llms.exa_ai.search.transformation import ( - ExaAISearchConfig, - ) - from litellm.llms.google_pse.search.transformation import ( - GooglePSESearchConfig, - ) + from litellm.llms.dataforseo.search.transformation import DataForSEOSearchConfig + from litellm.llms.exa_ai.search.transformation import ExaAISearchConfig + from litellm.llms.google_pse.search.transformation import GooglePSESearchConfig from litellm.llms.parallel_ai.search.transformation import ( ParallelAISearchConfig, ) diff --git a/litellm/vector_stores/main.py b/litellm/vector_stores/main.py index 3cbbfea1804..2e121615297 100644 --- a/litellm/vector_stores/main.py +++ b/litellm/vector_stores/main.py @@ -287,6 +287,7 @@ async def asearch( Async: Search a vector store for relevant chunks based on a query and file attributes filter. """ local_vars = locals() + try: loop = asyncio.get_event_loop() kwargs["asearch"] = True diff --git a/tests/vector_store_tests/test_azure_ai_vector_store.py b/tests/vector_store_tests/test_azure_ai_vector_store.py new file mode 100644 index 00000000000..52eb6635a98 --- /dev/null +++ b/tests/vector_store_tests/test_azure_ai_vector_store.py @@ -0,0 +1,39 @@ +import pytest +import litellm +import json +import os +from litellm.vector_stores import ( + search as vector_store_search, + asearch as vector_store_asearch, +) + + +@pytest.mark.parametrize("sync_mode", [True, False]) +@pytest.mark.asyncio +async def test_basic_search_vector_store(sync_mode): + litellm._turn_on_debug() + litellm.set_verbose = True + base_request_args = { + "vector_store_id": "my-vector-index", + "custom_llm_provider": "azure_ai", + "azure_search_service_name": "azure-kb-search", + "litellm_embedding_model": "azure/text-embedding-3-large", + "litellm_embedding_config": { + "api_base": os.getenv("AZURE_AI_SEARCH_EMBEDDING_API_BASE"), + "api_key": os.getenv("AZURE_AI_SEARCH_EMBEDDING_API_KEY"), + }, + "api_key": os.getenv("AZURE_SEARCH_API_KEY"), + } + default_query = base_request_args.pop("query", "Basic ping") + print(f"base_request_args: {base_request_args}") + try: + if sync_mode: + response = vector_store_search(query=default_query, **base_request_args) + else: + response = await vector_store_asearch( + query=default_query, **base_request_args + ) + except litellm.InternalServerError: + pytest.skip("Skipping test due to litellm.InternalServerError") + + print("litellm response=", json.dumps(response, indent=4, default=str))