litellm/tests/local_testing/test_embedding.py

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1352 lines
55 KiB
Python
Raw Permalink Normal View History

import json
import os
import sys
import traceback
import openai
import pytest
2023-11-23 05:50:44 +08:00
from dotenv import load_dotenv
load_dotenv()
2023-08-19 02:05:05 +08:00
sys.path.insert(
0, os.path.abspath("../..")
) # Adds the parent directory to the system path
from unittest.mock import AsyncMock, MagicMock, patch
import litellm
from litellm import completion, completion_cost, embedding
2023-09-30 00:59:31 +08:00
litellm.set_verbose = False
2023-08-19 02:05:05 +08:00
2023-12-25 16:40:38 +08:00
def test_openai_embedding():
try:
2023-12-25 16:40:38 +08:00
litellm.set_verbose = True
2023-08-19 02:05:05 +08:00
response = embedding(
2023-12-25 16:40:38 +08:00
model="text-embedding-ada-002",
input=["good morning from litellm", "this is another item"],
metadata={"anything": "good day"},
2023-08-19 02:05:05 +08:00
)
2023-11-23 05:50:44 +08:00
litellm_response = dict(response)
litellm_response_keys = set(litellm_response.keys())
2023-12-25 16:40:38 +08:00
litellm_response_keys.discard("_response_ms")
2023-11-25 10:28:38 +08:00
2023-11-23 05:50:44 +08:00
print(litellm_response_keys)
print("LiteLLM Response\n")
2023-11-25 10:28:38 +08:00
# print(litellm_response)
2023-12-25 16:40:38 +08:00
# same request with OpenAI 1.0+
2023-11-23 05:50:44 +08:00
import openai
2023-12-25 16:40:38 +08:00
client = openai.OpenAI(api_key=os.environ["OPENAI_API_KEY"])
2023-11-23 05:50:44 +08:00
response = client.embeddings.create(
2023-12-25 16:40:38 +08:00
model="text-embedding-ada-002",
input=["good morning from litellm", "this is another item"],
2023-11-23 05:50:44 +08:00
)
response = dict(response)
openai_response_keys = set(response.keys())
2023-11-25 10:28:38 +08:00
print(openai_response_keys)
2023-12-25 16:40:38 +08:00
assert (
litellm_response_keys == openai_response_keys
) # ENSURE the Keys in litellm response is exactly what the openai package returns
assert (
len(litellm_response["data"]) == 2
) # expect two embedding responses from litellm_response since input had two
2023-11-23 05:50:44 +08:00
print(openai_response_keys)
except Exception as e:
2023-08-19 02:05:05 +08:00
pytest.fail(f"Error occurred: {e}")
2023-12-25 16:40:38 +08:00
2023-11-26 03:10:02 +08:00
# test_openai_embedding()
2023-08-29 00:20:50 +08:00
2023-12-25 16:40:38 +08:00
2024-01-26 06:30:49 +08:00
def test_openai_embedding_3():
try:
litellm.set_verbose = True
response = embedding(
model="text-embedding-3-small",
input=["good morning from litellm", "this is another item"],
metadata={"anything": "good day"},
2024-01-27 02:37:01 +08:00
dimensions=5,
2024-01-26 06:30:49 +08:00
)
2024-01-27 02:37:01 +08:00
print(f"response:", response)
2024-01-26 06:30:49 +08:00
litellm_response = dict(response)
litellm_response_keys = set(litellm_response.keys())
litellm_response_keys.discard("_response_ms")
print(litellm_response_keys)
print("LiteLLM Response\n")
# print(litellm_response)
# same request with OpenAI 1.0+
import openai
client = openai.OpenAI(api_key=os.environ["OPENAI_API_KEY"])
response = client.embeddings.create(
model="text-embedding-3-small",
input=["good morning from litellm", "this is another item"],
2024-01-27 02:37:01 +08:00
dimensions=5,
2024-01-26 06:30:49 +08:00
)
response = dict(response)
openai_response_keys = set(response.keys())
print(openai_response_keys)
assert (
litellm_response_keys == openai_response_keys
) # ENSURE the Keys in litellm response is exactly what the openai package returns
assert (
len(litellm_response["data"]) == 2
) # expect two embedding responses from litellm_response since input had two
print(openai_response_keys)
except Exception as e:
pytest.fail(f"Error occurred: {e}")
LiteLLM Minor Fixes & Improvements (09/24/2024) (#5880) * LiteLLM Minor Fixes & Improvements (09/23/2024) (#5842) * feat(auth_utils.py): enable admin to allow client-side credentials to be passed Makes it easier for devs to experiment with finetuned fireworks ai models * feat(router.py): allow setting configurable_clientside_auth_params for a model Closes https://github.com/BerriAI/litellm/issues/5843 * build(model_prices_and_context_window.json): fix anthropic claude-3-5-sonnet max output token limit Fixes https://github.com/BerriAI/litellm/issues/5850 * fix(azure_ai/): support content list for azure ai Fixes https://github.com/BerriAI/litellm/issues/4237 * fix(litellm_logging.py): always set saved_cache_cost Set to 0 by default * fix(fireworks_ai/cost_calculator.py): add fireworks ai default pricing handles calling 405b+ size models * fix(slack_alerting.py): fix error alerting for failed spend tracking Fixes regression with slack alerting error monitoring * fix(vertex_and_google_ai_studio_gemini.py): handle gemini no candidates in streaming chunk error * docs(bedrock.md): add llama3-1 models * test: fix tests * fix(azure_ai/chat): fix transformation for azure ai calls * feat(azure_ai/embed): Add azure ai embeddings support Closes https://github.com/BerriAI/litellm/issues/5861 * fix(azure_ai/embed): enable async embedding * feat(azure_ai/embed): support azure ai multimodal embeddings * fix(azure_ai/embed): support async multi modal embeddings * feat(together_ai/embed): support together ai embedding calls * feat(rerank/main.py): log source documents for rerank endpoints to langfuse improves rerank endpoint logging * fix(langfuse.py): support logging `/audio/speech` input to langfuse * test(test_embedding.py): fix test * test(test_completion_cost.py): fix helper util
2024-09-26 13:11:57 +08:00
@pytest.mark.parametrize(
"model, api_base, api_key",
[
2025-09-28 03:41:35 +08:00
# ("azure/text-embedding-ada-002", None, None),
2026-03-29 09:42:55 +08:00
(
"together_ai/BAAI/bge-base-en-v1.5",
None,
None,
), # Updated to current Together AI embedding model
LiteLLM Minor Fixes & Improvements (09/24/2024) (#5880) * LiteLLM Minor Fixes & Improvements (09/23/2024) (#5842) * feat(auth_utils.py): enable admin to allow client-side credentials to be passed Makes it easier for devs to experiment with finetuned fireworks ai models * feat(router.py): allow setting configurable_clientside_auth_params for a model Closes https://github.com/BerriAI/litellm/issues/5843 * build(model_prices_and_context_window.json): fix anthropic claude-3-5-sonnet max output token limit Fixes https://github.com/BerriAI/litellm/issues/5850 * fix(azure_ai/): support content list for azure ai Fixes https://github.com/BerriAI/litellm/issues/4237 * fix(litellm_logging.py): always set saved_cache_cost Set to 0 by default * fix(fireworks_ai/cost_calculator.py): add fireworks ai default pricing handles calling 405b+ size models * fix(slack_alerting.py): fix error alerting for failed spend tracking Fixes regression with slack alerting error monitoring * fix(vertex_and_google_ai_studio_gemini.py): handle gemini no candidates in streaming chunk error * docs(bedrock.md): add llama3-1 models * test: fix tests * fix(azure_ai/chat): fix transformation for azure ai calls * feat(azure_ai/embed): Add azure ai embeddings support Closes https://github.com/BerriAI/litellm/issues/5861 * fix(azure_ai/embed): enable async embedding * feat(azure_ai/embed): support azure ai multimodal embeddings * fix(azure_ai/embed): support async multi modal embeddings * feat(together_ai/embed): support together ai embedding calls * feat(rerank/main.py): log source documents for rerank endpoints to langfuse improves rerank endpoint logging * fix(langfuse.py): support logging `/audio/speech` input to langfuse * test(test_embedding.py): fix test * test(test_completion_cost.py): fix helper util
2024-09-26 13:11:57 +08:00
],
)
@pytest.mark.parametrize("sync_mode", [True, False])
@pytest.mark.asyncio
2025-10-12 00:33:19 +08:00
async def test_together_ai_embedding(model, api_base, api_key, sync_mode):
2023-10-14 12:09:08 +08:00
try:
LiteLLM Minor Fixes & Improvements (09/24/2024) (#5880) * LiteLLM Minor Fixes & Improvements (09/23/2024) (#5842) * feat(auth_utils.py): enable admin to allow client-side credentials to be passed Makes it easier for devs to experiment with finetuned fireworks ai models * feat(router.py): allow setting configurable_clientside_auth_params for a model Closes https://github.com/BerriAI/litellm/issues/5843 * build(model_prices_and_context_window.json): fix anthropic claude-3-5-sonnet max output token limit Fixes https://github.com/BerriAI/litellm/issues/5850 * fix(azure_ai/): support content list for azure ai Fixes https://github.com/BerriAI/litellm/issues/4237 * fix(litellm_logging.py): always set saved_cache_cost Set to 0 by default * fix(fireworks_ai/cost_calculator.py): add fireworks ai default pricing handles calling 405b+ size models * fix(slack_alerting.py): fix error alerting for failed spend tracking Fixes regression with slack alerting error monitoring * fix(vertex_and_google_ai_studio_gemini.py): handle gemini no candidates in streaming chunk error * docs(bedrock.md): add llama3-1 models * test: fix tests * fix(azure_ai/chat): fix transformation for azure ai calls * feat(azure_ai/embed): Add azure ai embeddings support Closes https://github.com/BerriAI/litellm/issues/5861 * fix(azure_ai/embed): enable async embedding * feat(azure_ai/embed): support azure ai multimodal embeddings * fix(azure_ai/embed): support async multi modal embeddings * feat(together_ai/embed): support together ai embedding calls * feat(rerank/main.py): log source documents for rerank endpoints to langfuse improves rerank endpoint logging * fix(langfuse.py): support logging `/audio/speech` input to langfuse * test(test_embedding.py): fix test * test(test_completion_cost.py): fix helper util
2024-09-26 13:11:57 +08:00
os.environ["LITELLM_LOCAL_MODEL_COST_MAP"] = "True"
litellm.model_cost = litellm.get_model_cost_map(url="")
# litellm.set_verbose = True
if sync_mode:
response = embedding(
model=model,
input=["good morning from litellm"],
api_base=api_base,
api_key=api_key,
)
else:
response = await litellm.aembedding(
model=model,
input=["good morning from litellm"],
api_base=api_base,
api_key=api_key,
)
# print(await response)
2023-10-14 12:09:08 +08:00
print(response)
LiteLLM Minor Fixes & Improvements (09/24/2024) (#5880) * LiteLLM Minor Fixes & Improvements (09/23/2024) (#5842) * feat(auth_utils.py): enable admin to allow client-side credentials to be passed Makes it easier for devs to experiment with finetuned fireworks ai models * feat(router.py): allow setting configurable_clientside_auth_params for a model Closes https://github.com/BerriAI/litellm/issues/5843 * build(model_prices_and_context_window.json): fix anthropic claude-3-5-sonnet max output token limit Fixes https://github.com/BerriAI/litellm/issues/5850 * fix(azure_ai/): support content list for azure ai Fixes https://github.com/BerriAI/litellm/issues/4237 * fix(litellm_logging.py): always set saved_cache_cost Set to 0 by default * fix(fireworks_ai/cost_calculator.py): add fireworks ai default pricing handles calling 405b+ size models * fix(slack_alerting.py): fix error alerting for failed spend tracking Fixes regression with slack alerting error monitoring * fix(vertex_and_google_ai_studio_gemini.py): handle gemini no candidates in streaming chunk error * docs(bedrock.md): add llama3-1 models * test: fix tests * fix(azure_ai/chat): fix transformation for azure ai calls * feat(azure_ai/embed): Add azure ai embeddings support Closes https://github.com/BerriAI/litellm/issues/5861 * fix(azure_ai/embed): enable async embedding * feat(azure_ai/embed): support azure ai multimodal embeddings * fix(azure_ai/embed): support async multi modal embeddings * feat(together_ai/embed): support together ai embedding calls * feat(rerank/main.py): log source documents for rerank endpoints to langfuse improves rerank endpoint logging * fix(langfuse.py): support logging `/audio/speech` input to langfuse * test(test_embedding.py): fix test * test(test_completion_cost.py): fix helper util
2024-09-26 13:11:57 +08:00
print(response._hidden_params)
2023-11-25 12:46:35 +08:00
response_keys = set(dict(response).keys())
2023-12-25 16:40:38 +08:00
response_keys.discard("_response_ms")
assert set(["usage", "model", "object", "data"]) == set(
response_keys
) # assert litellm response has expected keys from OpenAI embedding response
2023-10-14 12:09:08 +08:00
LiteLLM Minor Fixes & Improvements (09/24/2024) (#5880) * LiteLLM Minor Fixes & Improvements (09/23/2024) (#5842) * feat(auth_utils.py): enable admin to allow client-side credentials to be passed Makes it easier for devs to experiment with finetuned fireworks ai models * feat(router.py): allow setting configurable_clientside_auth_params for a model Closes https://github.com/BerriAI/litellm/issues/5843 * build(model_prices_and_context_window.json): fix anthropic claude-3-5-sonnet max output token limit Fixes https://github.com/BerriAI/litellm/issues/5850 * fix(azure_ai/): support content list for azure ai Fixes https://github.com/BerriAI/litellm/issues/4237 * fix(litellm_logging.py): always set saved_cache_cost Set to 0 by default * fix(fireworks_ai/cost_calculator.py): add fireworks ai default pricing handles calling 405b+ size models * fix(slack_alerting.py): fix error alerting for failed spend tracking Fixes regression with slack alerting error monitoring * fix(vertex_and_google_ai_studio_gemini.py): handle gemini no candidates in streaming chunk error * docs(bedrock.md): add llama3-1 models * test: fix tests * fix(azure_ai/chat): fix transformation for azure ai calls * feat(azure_ai/embed): Add azure ai embeddings support Closes https://github.com/BerriAI/litellm/issues/5861 * fix(azure_ai/embed): enable async embedding * feat(azure_ai/embed): support azure ai multimodal embeddings * fix(azure_ai/embed): support async multi modal embeddings * feat(together_ai/embed): support together ai embedding calls * feat(rerank/main.py): log source documents for rerank endpoints to langfuse improves rerank endpoint logging * fix(langfuse.py): support logging `/audio/speech` input to langfuse * test(test_embedding.py): fix test * test(test_completion_cost.py): fix helper util
2024-09-26 13:11:57 +08:00
request_cost = litellm.completion_cost(
completion_response=response, call_type="embedding"
)
print("Calculated request cost=", request_cost)
assert isinstance(response.usage, litellm.Usage)
2025-06-14 10:00:25 +08:00
except litellm.BadRequestError:
print(
"Bad request error occurred - Together AI raises 404s for their embedding models"
)
2025-06-14 10:00:25 +08:00
pass
2023-10-14 12:09:08 +08:00
except Exception as e:
pytest.fail(f"Error occurred: {e}")
2023-12-25 16:40:38 +08:00
# test_openai_azure_embedding_simple()
LiteLLM Minor Fixes & Improvements (09/24/2024) (#5880) * LiteLLM Minor Fixes & Improvements (09/23/2024) (#5842) * feat(auth_utils.py): enable admin to allow client-side credentials to be passed Makes it easier for devs to experiment with finetuned fireworks ai models * feat(router.py): allow setting configurable_clientside_auth_params for a model Closes https://github.com/BerriAI/litellm/issues/5843 * build(model_prices_and_context_window.json): fix anthropic claude-3-5-sonnet max output token limit Fixes https://github.com/BerriAI/litellm/issues/5850 * fix(azure_ai/): support content list for azure ai Fixes https://github.com/BerriAI/litellm/issues/4237 * fix(litellm_logging.py): always set saved_cache_cost Set to 0 by default * fix(fireworks_ai/cost_calculator.py): add fireworks ai default pricing handles calling 405b+ size models * fix(slack_alerting.py): fix error alerting for failed spend tracking Fixes regression with slack alerting error monitoring * fix(vertex_and_google_ai_studio_gemini.py): handle gemini no candidates in streaming chunk error * docs(bedrock.md): add llama3-1 models * test: fix tests * fix(azure_ai/chat): fix transformation for azure ai calls * feat(azure_ai/embed): Add azure ai embeddings support Closes https://github.com/BerriAI/litellm/issues/5861 * fix(azure_ai/embed): enable async embedding * feat(azure_ai/embed): support azure ai multimodal embeddings * fix(azure_ai/embed): support async multi modal embeddings * feat(together_ai/embed): support together ai embedding calls * feat(rerank/main.py): log source documents for rerank endpoints to langfuse improves rerank endpoint logging * fix(langfuse.py): support logging `/audio/speech` input to langfuse * test(test_embedding.py): fix test * test(test_completion_cost.py): fix helper util
2024-09-26 13:11:57 +08:00
import base64
import requests
litellm.set_verbose = True
url = "https://dummyimage.com/100/100/fff&text=Test+image"
response = requests.get(url)
file_data = response.content
encoded_file = base64.b64encode(file_data).decode("utf-8")
base64_image = f"data:image/png;base64,{encoded_file}"
from openai.types.embedding import Embedding
def _azure_ai_image_mock_response(*args, **kwargs):
new_response = MagicMock()
new_response.headers = {"azureml-model-group": "offer-cohere-embed-multili-paygo"}
new_response.json.return_value = {
"data": [Embedding(embedding=[1234], index=0, object="embedding")],
"model": "",
"object": "list",
"usage": {"prompt_tokens": 1, "total_tokens": 2},
}
return new_response
@pytest.mark.parametrize("sync_mode", [True]) # , False
LiteLLM Minor Fixes & Improvements (09/24/2024) (#5880) * LiteLLM Minor Fixes & Improvements (09/23/2024) (#5842) * feat(auth_utils.py): enable admin to allow client-side credentials to be passed Makes it easier for devs to experiment with finetuned fireworks ai models * feat(router.py): allow setting configurable_clientside_auth_params for a model Closes https://github.com/BerriAI/litellm/issues/5843 * build(model_prices_and_context_window.json): fix anthropic claude-3-5-sonnet max output token limit Fixes https://github.com/BerriAI/litellm/issues/5850 * fix(azure_ai/): support content list for azure ai Fixes https://github.com/BerriAI/litellm/issues/4237 * fix(litellm_logging.py): always set saved_cache_cost Set to 0 by default * fix(fireworks_ai/cost_calculator.py): add fireworks ai default pricing handles calling 405b+ size models * fix(slack_alerting.py): fix error alerting for failed spend tracking Fixes regression with slack alerting error monitoring * fix(vertex_and_google_ai_studio_gemini.py): handle gemini no candidates in streaming chunk error * docs(bedrock.md): add llama3-1 models * test: fix tests * fix(azure_ai/chat): fix transformation for azure ai calls * feat(azure_ai/embed): Add azure ai embeddings support Closes https://github.com/BerriAI/litellm/issues/5861 * fix(azure_ai/embed): enable async embedding * feat(azure_ai/embed): support azure ai multimodal embeddings * fix(azure_ai/embed): support async multi modal embeddings * feat(together_ai/embed): support together ai embedding calls * feat(rerank/main.py): log source documents for rerank endpoints to langfuse improves rerank endpoint logging * fix(langfuse.py): support logging `/audio/speech` input to langfuse * test(test_embedding.py): fix test * test(test_completion_cost.py): fix helper util
2024-09-26 13:11:57 +08:00
@pytest.mark.asyncio
async def test_azure_ai_embedding_image(sync_mode):
model = "azure_ai/Cohere-embed-v3-multilingual-2"
api_base = os.getenv("AZURE_AI_API_BASE")
api_key = os.getenv("AZURE_AI_API_KEY")
LiteLLM Minor Fixes & Improvements (09/24/2024) (#5880) * LiteLLM Minor Fixes & Improvements (09/23/2024) (#5842) * feat(auth_utils.py): enable admin to allow client-side credentials to be passed Makes it easier for devs to experiment with finetuned fireworks ai models * feat(router.py): allow setting configurable_clientside_auth_params for a model Closes https://github.com/BerriAI/litellm/issues/5843 * build(model_prices_and_context_window.json): fix anthropic claude-3-5-sonnet max output token limit Fixes https://github.com/BerriAI/litellm/issues/5850 * fix(azure_ai/): support content list for azure ai Fixes https://github.com/BerriAI/litellm/issues/4237 * fix(litellm_logging.py): always set saved_cache_cost Set to 0 by default * fix(fireworks_ai/cost_calculator.py): add fireworks ai default pricing handles calling 405b+ size models * fix(slack_alerting.py): fix error alerting for failed spend tracking Fixes regression with slack alerting error monitoring * fix(vertex_and_google_ai_studio_gemini.py): handle gemini no candidates in streaming chunk error * docs(bedrock.md): add llama3-1 models * test: fix tests * fix(azure_ai/chat): fix transformation for azure ai calls * feat(azure_ai/embed): Add azure ai embeddings support Closes https://github.com/BerriAI/litellm/issues/5861 * fix(azure_ai/embed): enable async embedding * feat(azure_ai/embed): support azure ai multimodal embeddings * fix(azure_ai/embed): support async multi modal embeddings * feat(together_ai/embed): support together ai embedding calls * feat(rerank/main.py): log source documents for rerank endpoints to langfuse improves rerank endpoint logging * fix(langfuse.py): support logging `/audio/speech` input to langfuse * test(test_embedding.py): fix test * test(test_completion_cost.py): fix helper util
2024-09-26 13:11:57 +08:00
try:
os.environ["LITELLM_LOCAL_MODEL_COST_MAP"] = "True"
litellm.model_cost = litellm.get_model_cost_map(url="")
input = base64_image
if sync_mode:
client = HTTPHandler()
else:
client = AsyncHTTPHandler()
with patch.object(
client, "post", side_effect=_azure_ai_image_mock_response
) as mock_client:
if sync_mode:
response = embedding(
model=model,
input=[input],
api_base=api_base,
api_key=api_key,
client=client,
)
else:
response = await litellm.aembedding(
model=model,
input=[input],
api_base=api_base,
api_key=api_key,
client=client,
)
print(response)
assert len(response.data) == 1
print(response._hidden_params)
response_keys = set(dict(response).keys())
response_keys.discard("_response_ms")
assert set(["usage", "model", "object", "data"]) == set(
response_keys
) # assert litellm response has expected keys from OpenAI embedding response
request_cost = litellm.completion_cost(completion_response=response)
print("Calculated request cost=", request_cost)
assert isinstance(response.usage, litellm.Usage)
except Exception as e:
pytest.fail(f"Error occurred: {e}")
2023-10-14 12:09:08 +08:00
2023-11-23 06:23:52 +08:00
def test_openai_azure_embedding_timeouts():
try:
response = embedding(
2025-09-28 03:41:35 +08:00
model="azure/text-embedding-ada-002",
2023-11-23 06:23:52 +08:00
input=["good morning from litellm"],
2023-12-25 16:40:38 +08:00
timeout=0.00001,
2023-11-23 06:23:52 +08:00
)
print(response)
except openai.APITimeoutError:
print("Good job got timeout error!")
pass
except Exception as e:
2023-12-25 16:40:38 +08:00
pytest.fail(
f"Expected timeout error, did not get the correct error. Instead got {e}"
)
2023-11-23 06:23:52 +08:00
# test_openai_azure_embedding_timeouts()
2023-12-25 16:40:38 +08:00
def test_openai_embedding_timeouts():
try:
response = embedding(
model="text-embedding-ada-002",
input=["good morning from litellm"],
2023-12-25 16:40:38 +08:00
timeout=0.00001,
)
print(response)
except openai.APITimeoutError:
print("Good job got OpenAI timeout error!")
pass
except Exception as e:
2023-12-25 16:40:38 +08:00
pytest.fail(
f"Expected timeout error, did not get the correct error. Instead got {e}"
)
2023-11-25 09:04:59 +08:00
# test_openai_embedding_timeouts()
2023-12-25 16:40:38 +08:00
2023-10-14 12:09:08 +08:00
def test_openai_azure_embedding():
try:
2026-03-29 10:17:38 +08:00
api_key = os.environ["AZURE_AI_API_KEY"]
api_base = os.environ["AZURE_AI_API_BASE"]
2023-12-25 16:40:38 +08:00
api_version = os.environ["AZURE_API_VERSION"]
2023-10-14 12:09:08 +08:00
2023-12-25 16:40:38 +08:00
os.environ["AZURE_API_VERSION"] = ""
2026-03-29 10:17:38 +08:00
os.environ["AZURE_AI_API_BASE"] = ""
os.environ["AZURE_AI_API_KEY"] = ""
2023-10-14 12:09:08 +08:00
response = embedding(
2025-09-28 03:41:35 +08:00
model="azure/text-embedding-ada-002",
2023-10-14 12:09:08 +08:00
input=["good morning from litellm", "this is another item"],
api_key=api_key,
api_base=api_base,
api_version=api_version,
)
print(response)
2023-12-25 16:40:38 +08:00
os.environ["AZURE_API_VERSION"] = api_version
2026-03-29 10:17:38 +08:00
os.environ["AZURE_AI_API_BASE"] = api_base
os.environ["AZURE_AI_API_KEY"] = api_key
2023-10-14 12:09:08 +08:00
except Exception as e:
pytest.fail(f"Error occurred: {e}")
2024-07-24 23:04:27 +08:00
LiteLLM Minor Fixes & Improvements (09/24/2024) (#5880) * LiteLLM Minor Fixes & Improvements (09/23/2024) (#5842) * feat(auth_utils.py): enable admin to allow client-side credentials to be passed Makes it easier for devs to experiment with finetuned fireworks ai models * feat(router.py): allow setting configurable_clientside_auth_params for a model Closes https://github.com/BerriAI/litellm/issues/5843 * build(model_prices_and_context_window.json): fix anthropic claude-3-5-sonnet max output token limit Fixes https://github.com/BerriAI/litellm/issues/5850 * fix(azure_ai/): support content list for azure ai Fixes https://github.com/BerriAI/litellm/issues/4237 * fix(litellm_logging.py): always set saved_cache_cost Set to 0 by default * fix(fireworks_ai/cost_calculator.py): add fireworks ai default pricing handles calling 405b+ size models * fix(slack_alerting.py): fix error alerting for failed spend tracking Fixes regression with slack alerting error monitoring * fix(vertex_and_google_ai_studio_gemini.py): handle gemini no candidates in streaming chunk error * docs(bedrock.md): add llama3-1 models * test: fix tests * fix(azure_ai/chat): fix transformation for azure ai calls * feat(azure_ai/embed): Add azure ai embeddings support Closes https://github.com/BerriAI/litellm/issues/5861 * fix(azure_ai/embed): enable async embedding * feat(azure_ai/embed): support azure ai multimodal embeddings * fix(azure_ai/embed): support async multi modal embeddings * feat(together_ai/embed): support together ai embedding calls * feat(rerank/main.py): log source documents for rerank endpoints to langfuse improves rerank endpoint logging * fix(langfuse.py): support logging `/audio/speech` input to langfuse * test(test_embedding.py): fix test * test(test_completion_cost.py): fix helper util
2024-09-26 13:11:57 +08:00
from openai.types.embedding import Embedding
2024-08-28 22:50:44 +08:00
def _openai_mock_response(*args, **kwargs):
new_response = MagicMock()
new_response.headers = {"hello": "world"}
new_response.parse.return_value = (
openai.types.create_embedding_response.CreateEmbeddingResponse(
LiteLLM Minor Fixes & Improvements (09/24/2024) (#5880) * LiteLLM Minor Fixes & Improvements (09/23/2024) (#5842) * feat(auth_utils.py): enable admin to allow client-side credentials to be passed Makes it easier for devs to experiment with finetuned fireworks ai models * feat(router.py): allow setting configurable_clientside_auth_params for a model Closes https://github.com/BerriAI/litellm/issues/5843 * build(model_prices_and_context_window.json): fix anthropic claude-3-5-sonnet max output token limit Fixes https://github.com/BerriAI/litellm/issues/5850 * fix(azure_ai/): support content list for azure ai Fixes https://github.com/BerriAI/litellm/issues/4237 * fix(litellm_logging.py): always set saved_cache_cost Set to 0 by default * fix(fireworks_ai/cost_calculator.py): add fireworks ai default pricing handles calling 405b+ size models * fix(slack_alerting.py): fix error alerting for failed spend tracking Fixes regression with slack alerting error monitoring * fix(vertex_and_google_ai_studio_gemini.py): handle gemini no candidates in streaming chunk error * docs(bedrock.md): add llama3-1 models * test: fix tests * fix(azure_ai/chat): fix transformation for azure ai calls * feat(azure_ai/embed): Add azure ai embeddings support Closes https://github.com/BerriAI/litellm/issues/5861 * fix(azure_ai/embed): enable async embedding * feat(azure_ai/embed): support azure ai multimodal embeddings * fix(azure_ai/embed): support async multi modal embeddings * feat(together_ai/embed): support together ai embedding calls * feat(rerank/main.py): log source documents for rerank endpoints to langfuse improves rerank endpoint logging * fix(langfuse.py): support logging `/audio/speech` input to langfuse * test(test_embedding.py): fix test * test(test_completion_cost.py): fix helper util
2024-09-26 13:11:57 +08:00
data=[Embedding(embedding=[1234, 45667], index=0, object="embedding")],
model="azure/test",
object="list",
usage=openai.types.create_embedding_response.Usage(
prompt_tokens=1, total_tokens=2
),
2024-08-28 22:50:44 +08:00
)
)
2024-08-28 22:50:44 +08:00
return new_response
2024-08-28 22:50:44 +08:00
def test_openai_azure_embedding_optional_arg():
with patch.object(
openai.resources.embeddings.Embeddings,
"create",
side_effect=_openai_mock_response,
) as mock_client:
_ = litellm.embedding(
model="azure/test",
input=["test"],
api_version="test",
api_base="test",
azure_ad_token="test",
)
mock_client.assert_called_once_with(
2026-03-29 09:42:55 +08:00
model="test",
input=["test"],
extra_body={"azure_ad_token": "test"},
timeout=600,
extra_headers={"X-Stainless-Raw-Response": "true"},
)
# Verify azure_ad_token is passed in extra_body, not as a direct parameter
2024-08-28 22:50:44 +08:00
assert "azure_ad_token" not in mock_client.call_args.kwargs
assert mock_client.call_args.kwargs["extra_body"]["azure_ad_token"] == "test"
2023-10-14 12:09:08 +08:00
# test_openai_azure_embedding()
# test_openai_embedding()
2023-12-25 16:40:38 +08:00
2023-09-30 00:59:31 +08:00
# test_cohere_embedding()
2023-08-29 00:20:50 +08:00
2023-12-25 16:40:38 +08:00
2024-08-10 03:08:25 +08:00
@pytest.mark.parametrize("custom_llm_provider", ["cohere", "cohere_chat"])
@pytest.mark.asyncio()
async def test_cohere_embedding3(custom_llm_provider):
2023-11-03 01:17:40 +08:00
try:
2023-12-25 16:40:38 +08:00
litellm.set_verbose = True
2024-08-10 03:08:25 +08:00
response = await litellm.aembedding(
model=f"{custom_llm_provider}/embed-english-v3.0",
2023-12-25 16:40:38 +08:00
input=["good morning from litellm", "this is another item"],
2024-08-10 03:08:25 +08:00
timeout=None,
max_retries=0,
2023-11-03 01:17:40 +08:00
)
print(f"response:", response)
2024-01-13 09:35:33 +08:00
2023-11-03 01:17:40 +08:00
except Exception as e:
pytest.fail(f"Error occurred: {e}")
2023-12-25 16:40:38 +08:00
2024-01-26 06:36:11 +08:00
# test_cohere_embedding3()
2023-11-03 01:17:40 +08:00
2023-12-25 16:40:38 +08:00
@pytest.mark.parametrize(
"model",
[
"bedrock/amazon.titan-embed-text-v1",
"bedrock/amazon.titan-embed-image-v1",
"bedrock/amazon.titan-embed-text-v2:0",
],
)
@pytest.mark.parametrize("sync_mode", [True, False]) # ,
@pytest.mark.asyncio
async def test_bedrock_embedding_titan(model, sync_mode):
try:
# this tests if we support str input for bedrock embedding
litellm.set_verbose = True
litellm.enable_cache()
import time
current_time = str(time.time())
# DO NOT MAKE THE INPUT A LIST in this test
if sync_mode:
response = embedding(
model=model,
input=f"good morning from litellm, attempting to embed data {current_time}", # input should always be a string in this test
aws_region_name="us-west-2",
)
else:
response = await litellm.aembedding(
model=model,
input=f"good morning from litellm, attempting to embed data {current_time}", # input should always be a string in this test
aws_region_name="us-west-2",
)
print("response:", response)
assert isinstance(
response["data"][0]["embedding"], list
), "Expected response to be a list"
print("type of first embedding:", type(response["data"][0]["embedding"][0]))
assert all(
isinstance(x, float) for x in response["data"][0]["embedding"]
), "Expected response to be a list of floats"
except Exception as e:
pytest.fail(f"Error occurred: {e}")
@pytest.mark.parametrize(
"model",
[
"bedrock/amazon.titan-embed-text-v1",
"bedrock/amazon.titan-embed-image-v1",
"bedrock/amazon.titan-embed-text-v2:0",
],
)
@pytest.mark.parametrize("sync_mode", [True]) # True,
@pytest.mark.asyncio
async def test_bedrock_embedding_titan_caching(model, sync_mode):
try:
# this tests if we support str input for bedrock embedding
litellm.set_verbose = True
litellm.enable_cache()
import time
current_time = str(time.time())
# DO NOT MAKE THE INPUT A LIST in this test
if sync_mode:
response = embedding(
model=model,
input=f"good morning from litellm, attempting to embed data {current_time}", # input should always be a string in this test
aws_region_name="us-west-2",
)
else:
response = await litellm.aembedding(
model=model,
input=f"good morning from litellm, attempting to embed data {current_time}", # input should always be a string in this test
aws_region_name="us-west-2",
)
print("response:", response)
2023-12-25 16:40:38 +08:00
assert isinstance(
response["data"][0]["embedding"], list
), "Expected response to be a list"
print("type of first embedding:", type(response["data"][0]["embedding"][0]))
2023-12-25 16:40:38 +08:00
assert all(
isinstance(x, float) for x in response["data"][0]["embedding"]
), "Expected response to be a list of floats"
# this also tests if we can return a cache response for this scenario
import time
start_time = time.time()
if sync_mode:
response = embedding(
model=model,
input=f"good morning from litellm, attempting to embed data {current_time}", # input should always be a string in this test
)
else:
response = await litellm.aembedding(
model=model,
input=f"good morning from litellm, attempting to embed data {current_time}", # input should always be a string in this test
)
print(response)
end_time = time.time()
print(response._hidden_params)
print(f"Embedding 2 response time: {end_time - start_time} seconds")
assert end_time - start_time < 0.1
litellm.disable_cache()
assert isinstance(response.usage, litellm.Usage)
except Exception as e:
pytest.fail(f"Error occurred: {e}")
2023-12-25 16:40:38 +08:00
2024-01-13 09:35:33 +08:00
# test_bedrock_embedding_titan()
2023-12-25 16:40:38 +08:00
def test_bedrock_embedding_cohere():
try:
2023-12-25 16:40:38 +08:00
litellm.set_verbose = False
response = embedding(
2023-12-25 16:40:38 +08:00
model="cohere.embed-multilingual-v3",
input=[
"good morning from litellm, attempting to embed data",
"lets test a second string for good measure",
],
2026-03-31 12:08:51 +08:00
aws_region_name="us-west-2",
)
2023-12-25 16:40:38 +08:00
assert isinstance(
response["data"][0]["embedding"], list
), "Expected response to be a list"
print(f"type of first embedding:", type(response["data"][0]["embedding"][0]))
assert all(
isinstance(x, float) for x in response["data"][0]["embedding"]
), "Expected response to be a list of floats"
# print(f"response:", response)
assert isinstance(response.usage, litellm.Usage)
except Exception as e:
pytest.fail(f"Error occurred: {e}")
2023-12-25 16:40:38 +08:00
# test_bedrock_embedding_cohere()
2023-12-25 16:40:38 +08:00
def test_demo_tokens_as_input_to_embeddings_fails_for_titan():
litellm.set_verbose = True
with pytest.raises(
litellm.BadRequestError,
match='litellm.BadRequestError: BedrockException - {"message":"Malformed input request: expected type: String, found: JSONArray, please reformat your input and try again."}',
):
litellm.embedding(model="amazon.titan-embed-text-v1", input=[[1]])
with pytest.raises(
litellm.BadRequestError,
match='litellm.BadRequestError: BedrockException - {"message":"Malformed input request: expected type: String, found: Integer, please reformat your input and try again."}',
):
litellm.embedding(
model="amazon.titan-embed-text-v1",
input=[1],
)
2023-09-30 02:57:37 +08:00
# comment out hf tests - since hf endpoints are unstable
def test_hf_embedding():
try:
# huggingface/microsoft/codebert-base
# huggingface/facebook/bart-large
response = embedding(
2023-12-25 16:40:38 +08:00
model="huggingface/sentence-transformers/all-MiniLM-L6-v2",
input=["good morning from litellm", "this is another item"],
)
print(f"response:", response)
assert isinstance(response.usage, litellm.Usage)
except Exception as e:
2023-12-03 02:57:33 +08:00
# Note: Huggingface inference API is unstable and fails with "model loading errors all the time"
pass
2023-12-25 16:40:38 +08:00
2023-11-03 01:17:40 +08:00
# test_hf_embedding()
2023-09-30 02:57:37 +08:00
from unittest.mock import MagicMock, patch
def tgi_mock_post(*args, **kwargs):
import json
expected_data = {
"inputs": {
"source_sentence": "good morning from litellm",
"sentences": ["this is another item"],
}
}
assert (
json.loads(kwargs["data"]) == expected_data
), "Data does not match the expected data"
mock_response = MagicMock()
mock_response.status_code = 200
mock_response.headers = {"Content-Type": "application/json"}
mock_response.json.return_value = [0.7708950042724609]
return mock_response
from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler, HTTPHandler
@pytest.mark.asyncio
@patch(
"litellm.llms.huggingface.embedding.handler.async_get_hf_task_embedding_for_model"
)
@patch("litellm.llms.huggingface.embedding.handler.get_hf_task_embedding_for_model")
@pytest.mark.parametrize("sync_mode", [True, False])
async def test_hf_embedding_sentence_sim(
mock_async_get_hf_task_embedding_for_model,
mock_get_hf_task_embedding_for_model,
sync_mode,
):
try:
# huggingface/microsoft/codebert-base
# huggingface/facebook/bart-large
mock_get_hf_task_embedding_for_model.return_value = "sentence-similarity"
mock_async_get_hf_task_embedding_for_model.return_value = "sentence-similarity"
if sync_mode is True:
client = HTTPHandler(concurrent_limit=1)
else:
client = AsyncHTTPHandler(concurrent_limit=1)
with patch.object(client, "post", side_effect=tgi_mock_post) as mock_client:
data = {
"model": "huggingface/sentence-transformers/TaylorAI/bge-micro-v2",
"input": ["good morning from litellm", "this is another item"],
"client": client,
}
if sync_mode is True:
response = embedding(**data)
else:
response = await litellm.aembedding(**data)
print(f"response:", response)
mock_client.assert_called_once()
assert isinstance(response.usage, litellm.Usage)
except Exception as e:
# Note: Huggingface inference API is unstable and fails with "model loading errors all the time"
raise e
2023-12-25 16:40:38 +08:00
2023-10-24 04:59:37 +08:00
# test async embeddings
def test_aembedding():
2023-11-30 11:36:42 +08:00
try:
import asyncio
2023-12-25 16:40:38 +08:00
2023-11-30 11:36:42 +08:00
async def embedding_call():
try:
response = await litellm.aembedding(
2023-12-25 16:40:38 +08:00
model="text-embedding-ada-002",
input=["good morning from litellm", "this is another item"],
2023-11-30 11:36:42 +08:00
)
print(response)
return response
2023-11-30 11:36:42 +08:00
except Exception as e:
pytest.fail(f"Error occurred: {e}")
2023-12-25 16:40:38 +08:00
response = asyncio.run(embedding_call())
print("Before caclulating cost, response", response)
cost = litellm.completion_cost(completion_response=response)
print("COST=", cost)
assert cost == float("1e-06")
2023-11-30 11:36:42 +08:00
except Exception as e:
pytest.fail(f"Error occurred: {e}")
2023-10-24 04:59:37 +08:00
2023-12-25 16:40:38 +08:00
# test_aembedding()
2023-11-30 11:43:47 +08:00
def test_aembedding_azure():
try:
import asyncio
2023-12-25 16:40:38 +08:00
2023-11-30 11:43:47 +08:00
async def embedding_call():
try:
response = await litellm.aembedding(
2025-09-28 03:41:35 +08:00
model="azure/text-embedding-ada-002",
2023-12-25 16:40:38 +08:00
input=["good morning from litellm", "this is another item"],
2023-11-30 11:43:47 +08:00
)
print(response)
print(
"hidden params - custom_llm_provider",
response._hidden_params["custom_llm_provider"],
)
assert response._hidden_params["custom_llm_provider"] == "azure"
assert isinstance(response.usage, litellm.Usage)
2023-11-30 11:43:47 +08:00
except Exception as e:
pytest.fail(f"Error occurred: {e}")
2023-12-25 16:40:38 +08:00
2023-11-30 11:43:47 +08:00
asyncio.run(embedding_call())
except Exception as e:
pytest.fail(f"Error occurred: {e}")
2023-12-25 16:40:38 +08:00
2023-11-30 11:43:47 +08:00
# test_aembedding_azure()
2023-11-26 03:10:02 +08:00
2023-12-25 16:40:38 +08:00
@pytest.mark.skip(reason="AWS Suspended Account")
2023-12-25 16:40:38 +08:00
def test_sagemaker_embeddings():
try:
response = litellm.embedding(
model="sagemaker/berri-benchmarking-gpt-j-6b-fp16",
input=["good morning from litellm", "this is another item"],
input_cost_per_second=0.000420,
2023-12-25 16:40:38 +08:00
)
print(f"response: {response}")
cost = completion_cost(completion_response=response)
assert (
cost > 0.0 and cost < 1.0
) # should never be > $1 for a single embedding call
except Exception as e:
pytest.fail(f"Error occurred: {e}")
@pytest.mark.skip(reason="AWS Suspended Account")
@pytest.mark.asyncio
async def test_sagemaker_aembeddings():
try:
response = await litellm.aembedding(
model="sagemaker/berri-benchmarking-gpt-j-6b-fp16",
input=["good morning from litellm", "this is another item"],
input_cost_per_second=0.000420,
)
print(f"response: {response}")
cost = completion_cost(completion_response=response)
assert (
cost > 0.0 and cost < 1.0
) # should never be > $1 for a single embedding call
2023-12-25 16:40:38 +08:00
except Exception as e:
pytest.fail(f"Error occurred: {e}")
2023-12-25 16:40:38 +08:00
2023-12-28 19:12:36 +08:00
def test_mistral_embeddings():
try:
litellm.set_verbose = True
response = litellm.embedding(
model="mistral/mistral-embed",
input=["good morning from litellm"],
)
print(f"response: {response}")
assert isinstance(response.usage, litellm.Usage)
except litellm.RateLimitError as e:
pass
2023-12-28 19:12:36 +08:00
except Exception as e:
pytest.fail(f"Error occurred: {e}")
2024-04-28 10:07:34 +08:00
def test_fireworks_embeddings():
try:
litellm.set_verbose = True
response = litellm.embedding(
model="fireworks_ai/nomic-ai/nomic-embed-text-v1.5",
input=["good morning from litellm"],
)
print(f"response: {response}")
assert isinstance(response.usage, litellm.Usage)
cost = completion_cost(completion_response=response)
print("cost", cost)
assert cost > 0.0
print(response._hidden_params)
assert response._hidden_params["response_cost"] > 0.0
2025-06-12 01:09:28 +08:00
except litellm.RateLimitError as e:
pass
except litellm.InternalServerError as e:
pass
except Exception as e:
pytest.fail(f"Error occurred: {e}")
2025-11-23 01:41:09 +08:00
def test_watsonx_embeddings(monkeypatch):
from litellm.llms.custom_httpx.http_handler import HTTPHandler
2025-11-23 01:41:09 +08:00
# Mock the IAM token generation to avoid actual API calls
monkeypatch.setenv("WATSONX_API_KEY", "mock-api-key")
monkeypatch.setenv("WATSONX_TOKEN", "mock-watsonx-token")
monkeypatch.setenv("WATSONX_API_BASE", "https://us-south.ml.cloud.ibm.com")
monkeypatch.setenv("WATSONX_PROJECT_ID", "mock-project-id")
client = HTTPHandler()
2026-03-29 09:42:55 +08:00
2025-11-23 01:41:09 +08:00
# Track the actual request made
captured_request = {}
def mock_wx_embed_request(url: str, **kwargs):
2025-11-23 01:41:09 +08:00
# Capture request details for verification
captured_request["url"] = url
captured_request["headers"] = kwargs.get("headers", {})
captured_request["data"] = kwargs.get("data")
2026-03-29 09:42:55 +08:00
mock_response = MagicMock()
mock_response.status_code = 200
mock_response.headers = {"Content-Type": "application/json"}
mock_response.json.return_value = {
"model_id": "ibm/slate-30m-english-rtrvr",
"created_at": "2024-01-01T00:00:00.00Z",
"results": [{"embedding": [0.0] * 254}],
"input_token_count": 8,
}
return mock_response
try:
litellm.set_verbose = True
with patch.object(client, "post", side_effect=mock_wx_embed_request):
response = litellm.embedding(
model="watsonx/ibm/slate-30m-english-rtrvr",
input=["good morning from litellm"],
client=client,
)
print(f"response: {response}")
assert isinstance(response.usage, litellm.Usage)
2026-03-29 09:42:55 +08:00
2025-11-23 01:41:09 +08:00
# Verify the request was made correctly
assert "Authorization" in captured_request["headers"]
2026-03-29 09:42:55 +08:00
assert (
captured_request["headers"]["Authorization"] == "Bearer mock-watsonx-token"
)
2025-11-23 01:41:09 +08:00
assert "us-south.ml.cloud.ibm.com" in captured_request["url"]
except litellm.RateLimitError as e:
pass
except Exception as e:
pytest.fail(f"Error occurred: {e}")
@pytest.mark.asyncio
2025-11-23 01:41:09 +08:00
async def test_watsonx_aembeddings(monkeypatch):
from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler
2025-11-23 01:41:09 +08:00
# Mock the IAM token generation to avoid actual API calls
monkeypatch.setenv("WATSONX_API_KEY", "mock-api-key")
monkeypatch.setenv("WATSONX_TOKEN", "mock-watsonx-token")
monkeypatch.setenv("WATSONX_API_BASE", "https://us-south.ml.cloud.ibm.com")
monkeypatch.setenv("WATSONX_PROJECT_ID", "mock-project-id")
client = AsyncHTTPHandler()
def mock_async_client(*args, **kwargs):
mocked_client = MagicMock()
async def mock_send(request, *args, stream: bool = False, **kwags):
mock_response = MagicMock()
mock_response.status_code = 200
mock_response.headers = {"Content-Type": "application/json"}
mock_response.json.return_value = {
"model_id": "ibm/slate-30m-english-rtrvr",
"created_at": "2024-01-01T00:00:00.00Z",
"results": [{"embedding": [0.0] * 254}],
"input_token_count": 8,
}
mock_response.is_error = False
return mock_response
mocked_client.send = mock_send
return mocked_client
try:
litellm.set_verbose = True
with patch.object(client, "post", side_effect=mock_async_client) as mock_client:
response = await litellm.aembedding(
model="watsonx/ibm/slate-30m-english-rtrvr",
input=["good morning from litellm"],
client=client,
)
mock_client.assert_called_once()
print(f"response: {response}")
assert isinstance(response.usage, litellm.Usage)
2024-05-14 09:27:39 +08:00
except litellm.RateLimitError as e:
pass
except Exception as e:
pytest.fail(f"Error occurred: {e}")
2023-12-28 19:12:36 +08:00
# test_mistral_embeddings()
2023-12-28 19:39:27 +08:00
2024-04-04 11:52:35 +08:00
@pytest.mark.skip(
reason="Community maintained embedding provider - they are quite unstable"
)
2023-12-28 19:39:27 +08:00
def test_voyage_embeddings():
try:
litellm.set_verbose = True
response = litellm.embedding(
model="voyage/voyage-01",
input=["good morning from litellm"],
)
print(f"response: {response}")
except Exception as e:
pytest.fail(f"Error occurred: {e}")
@pytest.mark.parametrize("sync_mode", [True, False])
@pytest.mark.parametrize(
"input", ["good morning from litellm", ["good morning from litellm"]] #
)
2024-08-28 08:35:56 +08:00
@pytest.mark.asyncio
async def test_gemini_embeddings(sync_mode, input):
2024-08-28 08:35:56 +08:00
try:
litellm.set_verbose = True
if sync_mode:
response = litellm.embedding(
model="gemini/gemini-embedding-001",
input=input,
)
else:
response = await litellm.aembedding(
model="gemini/gemini-embedding-001",
input=input,
)
2024-08-28 08:35:56 +08:00
print(f"response: {response}")
# stubbed endpoint is setup to return this
assert isinstance(response.data[0]["embedding"], list)
assert response.usage.prompt_tokens > 0
2024-08-28 08:35:56 +08:00
except Exception as e:
pytest.fail(f"Error occurred: {e}")
2023-12-28 19:39:27 +08:00
# test_voyage_embeddings()
2024-01-02 18:11:51 +08:00
# def test_xinference_embeddings():
# try:
# litellm.set_verbose = True
# response = litellm.embedding(
# model="xinference/bge-base-en",
# input=["good morning from litellm"],
# )
# print(f"response: {response}")
# except Exception as e:
# pytest.fail(f"Error occurred: {e}")
# test_xinference_embeddings()
2023-12-28 19:39:27 +08:00
# test_sagemaker_embeddings()
# def local_proxy_embeddings():
2023-11-26 03:10:02 +08:00
# litellm.set_verbose=True
# response = embedding(
2023-12-25 16:40:38 +08:00
# model="openai/custom_embedding",
2023-11-26 03:10:02 +08:00
# input=["good morning from litellm"],
# api_base="http://0.0.0.0:8000/"
# )
# print(response)
# local_proxy_embeddings()
@pytest.mark.parametrize("sync_mode", [True, False])
@pytest.mark.asyncio
@pytest.mark.flaky(retries=6, delay=1)
@pytest.mark.skip(reason="Skipping test due to flakyness")
async def test_hf_embedddings_with_optional_params(sync_mode):
litellm.set_verbose = True
if sync_mode:
client = HTTPHandler(concurrent_limit=1)
mock_obj = MagicMock()
else:
client = AsyncHTTPHandler(concurrent_limit=1)
mock_obj = AsyncMock()
with patch.object(client, "post", new=mock_obj) as mock_client:
try:
if sync_mode:
response = embedding(
model="huggingface/jinaai/jina-embeddings-v2-small-en",
input=["good morning from litellm"],
top_p=10,
top_k=10,
wait_for_model=True,
client=client,
)
else:
response = await litellm.aembedding(
model="huggingface/jinaai/jina-embeddings-v2-small-en",
input=["good morning from litellm"],
top_p=10,
top_k=10,
wait_for_model=True,
client=client,
)
except Exception as e:
print(e)
mock_client.assert_called_once()
print(f"mock_client.call_args.kwargs: {mock_client.call_args.kwargs}")
assert "options" in mock_client.call_args.kwargs["data"]
json_data = json.loads(mock_client.call_args.kwargs["data"])
assert "wait_for_model" in json_data["options"]
assert json_data["options"]["wait_for_model"] is True
assert json_data["parameters"]["top_p"] == 10
assert json_data["parameters"]["top_k"] == 10
def test_hosted_vllm_embedding(monkeypatch):
monkeypatch.setenv("HOSTED_VLLM_API_BASE", "http://localhost:8000")
from litellm.llms.custom_httpx.http_handler import HTTPHandler
client = HTTPHandler()
with patch.object(client, "post") as mock_post:
try:
embedding(
model="hosted_vllm/jina-embeddings-v3",
input=["Hello world"],
client=client,
)
except Exception as e:
print(e)
mock_post.assert_called_once()
json_data = json.loads(mock_post.call_args.kwargs["data"])
assert json_data["input"] == ["Hello world"]
assert json_data["model"] == "jina-embeddings-v3"
def test_llamafile_embedding(monkeypatch):
monkeypatch.setenv("LLAMAFILE_API_BASE", "http://localhost:8080/v1")
from litellm.llms.custom_httpx.http_handler import HTTPHandler
client = HTTPHandler()
with patch.object(client, "post") as mock_post:
try:
embedding(
model="llamafile/jina-embeddings-v3",
input=["Hello world"],
client=client,
)
except Exception as e:
print(e)
mock_post.assert_called_once()
json_data = json.loads(mock_post.call_args.kwargs["data"])
assert json_data["input"] == ["Hello world"]
assert json_data["model"] == "jina-embeddings-v3"
@pytest.mark.asyncio
@pytest.mark.parametrize("sync_mode", [True, False])
async def test_lm_studio_embedding(monkeypatch, sync_mode):
monkeypatch.setenv("LM_STUDIO_API_BASE", "http://localhost:8000")
from litellm.llms.custom_httpx.http_handler import HTTPHandler, AsyncHTTPHandler
client = HTTPHandler() if sync_mode else AsyncHTTPHandler()
with patch.object(client, "post") as mock_post:
try:
if sync_mode:
embedding(
model="lm_studio/jina-embeddings-v3",
input=["Hello world"],
client=client,
)
else:
await litellm.aembedding(
model="lm_studio/jina-embeddings-v3",
input=["Hello world"],
client=client,
)
except Exception as e:
print(e)
mock_post.assert_called_once()
json_data = json.loads(mock_post.call_args.kwargs["data"])
assert json_data["input"] == ["Hello world"]
assert json_data["model"] == "jina-embeddings-v3"
@pytest.mark.parametrize(
"model",
[
"text-embedding-ada-002",
2025-09-28 03:41:35 +08:00
"azure/text-embedding-ada-002",
],
)
def test_embedding_response_ratelimit_headers(model):
response = embedding(
model=model,
input=["Hello world"],
)
hidden_params = response._hidden_params
additional_headers = hidden_params.get("additional_headers", {})
print("additional_headers", additional_headers)
# Azure is flaky with returning x-ratelimit-remaining-requests, we need to verify the upstream api returns this header
# if upstream api returns this header, we need to verify the header is transformed by litellm
if (
"llm_provider-x-ratelimit-limit-requests" in additional_headers
or "x-ratelimit-limit-requests" in additional_headers
):
assert "x-ratelimit-remaining-requests" in additional_headers
assert int(additional_headers["x-ratelimit-remaining-requests"]) > 0
assert "x-ratelimit-remaining-tokens" in additional_headers
assert int(additional_headers["x-ratelimit-remaining-tokens"]) > 0
@pytest.mark.parametrize(
"input, input_type",
[
(
[
"data:image/jpeg;base64,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
],
"image",
),
(["hello world"], "text"),
],
)
def test_cohere_img_embeddings(input, input_type):
litellm.set_verbose = True
try:
response = embedding(
model="cohere/embed-english-v3.0",
input=input,
)
if input_type == "image":
assert response.usage.prompt_tokens_details.image_tokens > 0
else:
assert response.usage.prompt_tokens_details.text_tokens > 0
except litellm.InternalServerError as e:
# Cohere API is experiencing internal server errors - this is expected
# and our exception mapping is working correctly
if "internal server error" in str(e).lower():
pytest.skip("Cohere API is currently experiencing internal server errors")
else:
raise e
LiteLLM Minor Fixes & Improvements (11/23/2024) (#6870) * feat(pass_through_endpoints/): support logging anthropic/gemini pass through calls to langfuse/s3/etc. * fix(utils.py): allow disabling end user cost tracking with new param Allows proxy admin to disable cost tracking for end user - keeps prometheus metrics small * docs(configs.md): add disable_end_user_cost_tracking reference to docs * feat(key_management_endpoints.py): add support for restricting access to `/key/generate` by team/proxy level role Enables admin to restrict key creation, and assign team admins to handle distributing keys * test(test_key_management.py): add unit testing for personal / team key restriction checks * docs: add docs on restricting key creation * docs(finetuned_models.md): add new guide on calling finetuned models * docs(input.md): cleanup anthropic supported params Closes https://github.com/BerriAI/litellm/issues/6856 * test(test_embedding.py): add test for passing extra headers via embedding * feat(cohere/embed): pass client to async embedding * feat(rerank.py): add `/v1/rerank` if missing for cohere base url Closes https://github.com/BerriAI/litellm/issues/6844 * fix(main.py): pass extra_headers param to openai Fixes https://github.com/BerriAI/litellm/issues/6836 * fix(litellm_logging.py): don't disable global callbacks when dynamic callbacks are set Fixes issue where global callbacks - e.g. prometheus were overriden when langfuse was set dynamically * fix(handler.py): fix linting error * fix: fix typing * build: add conftest to proxy_admin_ui_tests/ * test: fix test * fix: fix linting errors * test: fix test * fix: fix pass through testing
2024-11-23 17:47:40 +08:00
@pytest.mark.parametrize("sync_mode", [True, False])
@pytest.mark.asyncio
async def test_embedding_with_extra_headers(sync_mode):
input = ["hello world"]
from litellm.llms.custom_httpx.http_handler import HTTPHandler, AsyncHTTPHandler
if sync_mode:
client = HTTPHandler()
else:
client = AsyncHTTPHandler()
data = {
"model": "cohere/embed-english-v3.0",
"input": input,
"extra_headers": {"my-test-param": "hello-world"},
"client": client,
}
with patch.object(client, "post") as mock_post:
try:
if sync_mode:
embedding(**data)
else:
await litellm.aembedding(**data)
except Exception as e:
print(e)
mock_post.assert_called_once()
assert "my-test-param" in mock_post.call_args.kwargs["headers"]
@pytest.mark.parametrize(
"input_data, expected_payload_input",
[
# Case 1: Input with only text strings
(
["hello world", "foo bar"],
["hello world", "foo bar"],
),
# Case 2: Input with a mix of text and a base64 encoded image
(
[
"A picture of a cat",
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=",
],
[
{"text": "A picture of a cat"},
{
"image": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII="
},
],
),
# Case 3: Input with only a base64 encoded image
(
[
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII="
],
[
{
"image": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII="
}
],
),
],
)
def test_jina_ai_img_embeddings(input_data, expected_payload_input):
"""
Tests the input transformation logic for Jina AI embeddings using mocks.
This test verifies that when litellm.embedding is called with a jina_ai model,
the 'input' field in the request payload is formatted correctly based on whether
the input contains text or base64 encoded images.
"""
# We patch the `post` method of the HTTPHandler. This intercepts the network
# request before it's actually sent.
with patch("litellm.llms.custom_httpx.http_handler.HTTPHandler.post") as mock_post:
# Configure the mock to return a successful, minimal valid response.
# This prevents litellm from raising an error when processing the response.
mock_response = MagicMock()
mock_response.status_code = 200
mock_response.json.return_value = {
"object": "list",
"data": [
{
"object": "embedding",
"index": 0,
"embedding": [0.1] * 768, # Dummy embedding vector
}
],
"model": "jina-embeddings-v4",
}
mock_post.return_value = mock_response
# Call the function we want to test
try:
2026-03-29 09:42:55 +08:00
litellm.embedding(model="jina_ai/jina-embeddings-v4", input=input_data)
except Exception as e:
pytest.fail(
f"litellm.embedding call failed with an unexpected exception: {e}"
)
# --- Assertions ---
# 1. Check that our mock `post` method was called exactly once.
mock_post.assert_called_once()
# 2. Extract the keyword arguments passed to the mock call.
# The request payload is in the 'data' keyword argument.
kwargs = mock_post.call_args.kwargs
assert "data" in kwargs
# 3. Parse the JSON payload string into a Python dictionary.
sent_data = json.loads(kwargs["data"])
# 4. This is the core of our test:
# Assert that the 'input' field in the payload matches our expectation.
assert "input" in sent_data
assert sent_data["input"] == expected_payload_input
def test_encoding_format_defaults_to_float_for_openai_sdk(monkeypatch):
"""
When encoding_format is not provided, LiteLLM sends `float` for OpenAI-path embeddings.
2026-03-29 09:42:55 +08:00
Optional global override: `LITELLM_DEFAULT_EMBEDDING_ENCODING_FORMAT`.
"""
monkeypatch.delenv("LITELLM_DEFAULT_EMBEDDING_ENCODING_FORMAT", raising=False)
2026-03-29 09:42:55 +08:00
with patch(
"litellm.llms.openai.openai.OpenAIChatCompletion._get_openai_client"
) as mock_get_client:
# Create a mock client instance
mock_client_instance = MagicMock()
mock_get_client.return_value = mock_client_instance
2026-03-29 09:42:55 +08:00
# Mock the embeddings.with_raw_response.create method
mock_response = MagicMock()
mock_response.parse.return_value = MagicMock(
model_dump=lambda: {
2026-03-29 09:42:55 +08:00
"data": [{"embedding": [0.1, 0.2, 0.3], "index": 0}],
"model": "text-embedding-ada-002",
"object": "list",
"usage": {"prompt_tokens": 1, "total_tokens": 1},
}
)
mock_response.headers = {}
2026-03-29 09:42:55 +08:00
mock_client_instance.embeddings.with_raw_response.create.return_value = (
mock_response
)
# Call the embedding function without encoding_format
response = embedding(
model="text-embedding-ada-002",
input="Hello world",
)
2026-03-29 09:42:55 +08:00
# Get the call arguments to verify what was sent to OpenAI SDK
call_args = mock_client_instance.embeddings.with_raw_response.create.call_args
2026-03-29 09:42:55 +08:00
assert (
call_args is not None
), "OpenAI SDK embeddings.create should have been called"
call_kwargs = call_args[1] # Get kwargs
2026-03-29 09:42:55 +08:00
assert "encoding_format" in call_kwargs
2026-03-29 09:42:55 +08:00
assert (
call_kwargs["encoding_format"] == "float"
), "encoding_format should default to float when not provided by user"
2026-03-29 09:42:55 +08:00
print("✅ PASS: encoding_format='float' is correctly passed to OpenAI SDK")
def test_encoding_format_explicit_value_preserved():
"""
Test that explicitly provided encoding_format values are preserved.
2026-03-29 09:42:55 +08:00
When user provides encoding_format='float' or 'base64', it should be
sent as-is to the OpenAI SDK.
"""
2026-03-29 09:42:55 +08:00
with patch(
"litellm.llms.openai.openai.OpenAIChatCompletion._get_openai_client"
) as mock_get_client:
# Create a mock client instance
mock_client_instance = MagicMock()
mock_get_client.return_value = mock_client_instance
2026-03-29 09:42:55 +08:00
# Mock the embeddings.with_raw_response.create method
mock_response = MagicMock()
mock_response.parse.return_value = MagicMock(
model_dump=lambda: {
2026-03-29 09:42:55 +08:00
"data": [{"embedding": [0.1, 0.2, 0.3], "index": 0}],
"model": "text-embedding-ada-002",
"object": "list",
"usage": {"prompt_tokens": 1, "total_tokens": 1},
}
)
mock_response.headers = {}
2026-03-29 09:42:55 +08:00
mock_client_instance.embeddings.with_raw_response.create.return_value = (
mock_response
)
# Test with explicit encoding_format='float'
response = embedding(
2026-03-29 09:42:55 +08:00
model="text-embedding-ada-002", input="Hello world", encoding_format="float"
)
2026-03-29 09:42:55 +08:00
# Verify the encoding_format was passed correctly
call_args = mock_client_instance.embeddings.with_raw_response.create.call_args
call_kwargs = call_args[1]
2026-03-29 09:42:55 +08:00
assert (
"encoding_format" in call_kwargs
), "encoding_format should be in the request"
assert (
call_kwargs["encoding_format"] == "float"
), "encoding_format should be 'float' when explicitly provided"
print("✅ PASS: encoding_format='float' is correctly preserved")