mirror of
https://github.com/BerriAI/litellm.git
synced 2026-08-28 05:25:59 +00:00
fix(managed_files.py): don't raise error if managed object is not found + (Feat) Azure AI - Search Vector Stores + (Fix) Batches - “User default_user_id does not have access to the object” when object not in db + (fix) Vector Stores - show config.yaml vector stores on UI (#15873)
* fix(managed_files.py): don't raise error if managed object is not found * feat(vector_stores): add azure ai search vector store support Enables direct querying a vector store on azure * fix(azure/vector_stores): working azure ai search api vector stores allows azure direct querying on vector stores * test: update env vars * docs(docs/): document new azure ai vector store search * docs(azure_ai_vector_stores.md): add table * docs: clarify support for 'create' vector stores * fix(vector_stores/endpoints.py): Fixes https://github.com/BerriAI/litellm/issues/14606 * fix: fix linting errors
This commit is contained in:
parent
214c10f6ef
commit
f8d6a6edb9
13 changed files with 657 additions and 60 deletions
245
docs/my-website/docs/providers/azure_ai_vector_stores.md
Normal file
245
docs/my-website/docs/providers/azure_ai_vector_stores.md
Normal file
|
|
@ -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
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="sdk" label="SDK">
|
||||
|
||||
### 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)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="proxy" label="PROXY">
|
||||
|
||||
### 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?",
|
||||
}'
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## 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`.
|
||||
|
||||
|
|
@ -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
|
|||
<Tabs>
|
||||
<TabItem value="basic" label="Basic Usage">
|
||||
|
||||
#### 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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="azure-ai-provider" label="Azure AI Provider">
|
||||
|
||||
#### 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)
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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},
|
||||
|
|
|
|||
4
litellm/llms/azure_ai/vector_stores/__init__.py
Normal file
4
litellm/llms/azure_ai/vector_stores/__init__.py
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
from litellm.llms.azure_ai.vector_stores.transformation import AzureAIVectorStoreConfig
|
||||
|
||||
__all__ = ["AzureAIVectorStoreConfig"]
|
||||
|
||||
237
litellm/llms/azure_ai/vector_stores/transformation.py
Normal file
237
litellm/llms/azure_ai/vector_stores/transformation.py
Normal file
|
|
@ -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
|
||||
|
|
@ -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"
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
39
tests/vector_store_tests/test_azure_ai_vector_store.py
Normal file
39
tests/vector_store_tests/test_azure_ai_vector_store.py
Normal file
|
|
@ -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))
|
||||
Loading…
Add table
Reference in a new issue