mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-11 08:38:24 +00:00
* fix(llms): sanitize trailing slashes in provider get_complete_url * test(llms): move trailing-slash url tests into each provider's mapped unit file * fix(llms): strip trailing slashes in Perplexity search and Ollama embedding URLs and run the Topaz tests in CI * fix(llms): strip trailing slashes in search, DashScope, VolcEngine, GigaChat and Gradient AI URL builders * fix(fastcrw): strip a trailing slash from api_base and expect one slash in the GigaChat tests * fix(gigachat): strip a trailing slash from the pass-through api_base * fix(llms): join api_base and route with one shared helper A deployment api_base that ends in / made several get_complete_url builders emit a double slash, which Envoy and other gateways answer with 404. Add join_api_base in url_utils and use it in those builders, with a regression test that drives every get_complete_url under litellm/llms with a trailing-slash api_base * refactor(llms): move per-provider api_base strips onto join_api_base Use the shared join_api_base helper in the search, chat, embedding, image variation, GigaChat and VolcEngine URL builders instead of a per-provider rstrip and suffix check, so one helper owns the single-slash join * ci: run the trailing-slash URL builder test in the llms shard * test(integration): cover trailing-slash api_base across provider URL builders Wire cells for chat, streaming, messages, responses, embeddings, search and pass-through with and without a trailing slash, an owned-proxy module for the flag-gated providers, env bases and a worker kill, two chaos bursts, and a Topaz SDK cell * fix(llms): send every provider route through join_api_base Every place under litellm/llms and litellm/main.py that joined a caller-settable api_base with a route by concatenation or f-string now goes through join_api_base, so an api_base with a trailing slash sends the same upstream path as one without it Routes made only of a caller-supplied id, model, or passthrough path stay on a plain join over an already normalized base, because the helper's suffix dedup would turn a lookup by id into a list or create call The sweep test now also reaches the factory-made JSON provider builders, and a new loopback test drives the public entrypoints with an api_base ending in /gw/ and checks that every request path keeps /gw/ with no double slash * fix(llms): keep the route skip only where a builder already had it join_api_base is a pure join again. The new ensure_api_route skips the route only when the URL path already ends in it, and it is used only where the merge base or the trailing-slash test PR head already skipped. Evals cancel, Predibase, and Azure AI threads append their route again, EdenAI model listing and Gemini RAG ingestion join through join_api_base, and Azure AI Anthropic checks the path so a host named anthropic-* builds /anthropic/v1/messages * fix(azure_ai): read /anthropic from the URL path when building the count_tokens URL * test(llms): run the Gemini RAG trailing-slash case through aingest The sync ingest wrapper needs a current event loop, which an earlier async test in the same worker clears * test(llms): cover the Azure realtime, Bedrock rerank, and oobabooga joins with a trailing-slash api_base * test(integration): cover api_base joins across providers, query strings and named hosts * feat(url_utils): add join_api_base and ensure_api_route helpers * fix(url_utils): keep the scheme slashes when a stripped suffix was the whole host join_api_base and ensure_api_route now keep the // of a base left with only a scheme, so http://v1 with /v1 stripped joins to http://v1/files as before. The Azure v1 client base_url goes through ensure_api_route so a base ending in /openai/v1 no longer doubles it * fix(azure_ai): build the count_tokens URL from the messages URL * fix(azure_ai): match anthropic as a whole path segment in the messages URL * fix(llms): keep main's Azure AI anthropic match and non-string Bedrock endpoints Azure AI chat and messages go back to cutting the base at the first /anthropic, as on main, and count_tokens keeps its path check, so a gateway path like /anthropic-proxy builds the same URLs as main. A base that already ends in /v1/messages still gets count_tokens next to it. A non-string aws_bedrock_runtime_endpoint (an httpx.URL VPC endpoint) is used as its text again in AgentCore, Bedrock responses and the RAGFlow base, instead of falling back to the default host. get_volcengine_base_url and GigaChat's get_api_base strip a trailing slash again for outside callers. The cache-hit wire test is skipped as a known proxy bug, since one cache hit writes two cache_hit spend rows. * fix(llms): drop a slash before the api_base query and tighten the route test checks * test(llms): annotate new test locals as Final and flatten the builder generator * fix(url_utils): keep a scheme-only api_base hostless so it fails before sending --------- Co-authored-by: Raushankumar0720 <raushan.singh.cg@gmail.com>
641 lines
21 KiB
Python
641 lines
21 KiB
Python
from typing import Final
|
|
from unittest.mock import AsyncMock, Mock
|
|
|
|
import httpx
|
|
import pytest
|
|
import respx
|
|
|
|
import litellm
|
|
from litellm.llms.predibase.chat.handler import PredibaseChatCompletion
|
|
from litellm.llms.predibase.chat.transformation import PredibaseConfig
|
|
from litellm.llms.predibase.common_utils import PredibaseError
|
|
from litellm.utils import Choices, Message, ModelResponse
|
|
|
|
|
|
def _build_model_response() -> ModelResponse:
|
|
return ModelResponse(
|
|
choices=[
|
|
Choices(
|
|
finish_reason=None,
|
|
index=0,
|
|
message=Message(role="assistant", content=""),
|
|
)
|
|
]
|
|
)
|
|
|
|
|
|
def test_predibase_transform_request_non_stream():
|
|
config = PredibaseConfig()
|
|
request_data = config.transform_request(
|
|
model="predibase-model",
|
|
messages=[{"role": "user", "content": "hello"}],
|
|
optional_params={"temperature": 0.2},
|
|
litellm_params={},
|
|
headers={},
|
|
)
|
|
|
|
assert request_data["inputs"]
|
|
assert request_data["parameters"]["temperature"] == 0.2
|
|
assert request_data["parameters"]["details"] is True
|
|
assert "stream" not in request_data["parameters"]
|
|
|
|
|
|
def test_predibase_transform_request_custom_prompt():
|
|
config = PredibaseConfig()
|
|
|
|
request_data = config.transform_request(
|
|
model="predibase-model",
|
|
messages=[{"role": "user", "content": "hello"}],
|
|
optional_params={},
|
|
litellm_params={
|
|
"custom_prompt_dict": {
|
|
"predibase-model": {
|
|
"roles": {"user": {"pre_message": "<user>", "post_message": "</user>"}},
|
|
"initial_prompt_value": "<s>",
|
|
"final_prompt_value": "</s>",
|
|
}
|
|
}
|
|
},
|
|
headers={},
|
|
)
|
|
|
|
assert request_data["inputs"] == "<s><user>hello</user></s>"
|
|
|
|
|
|
def test_predibase_get_complete_url_stream_and_non_stream():
|
|
config = PredibaseConfig()
|
|
litellm_params = {"predibase_tenant_id": "tenant-123"}
|
|
|
|
non_stream_url = config.get_complete_url(
|
|
api_base="https://serving.example.com",
|
|
api_key="test-key",
|
|
model="predibase-model",
|
|
optional_params={"stream": False},
|
|
litellm_params=litellm_params,
|
|
)
|
|
stream_url = config.get_complete_url(
|
|
api_base="https://serving.example.com",
|
|
api_key="test-key",
|
|
model="predibase-model",
|
|
optional_params={"stream": True},
|
|
litellm_params=litellm_params,
|
|
)
|
|
|
|
assert non_stream_url.endswith("/generate")
|
|
assert stream_url.endswith("/generate_stream")
|
|
|
|
|
|
@pytest.mark.parametrize(("model", "stream"), [("generate", False), ("generate_stream", True)])
|
|
def test_predibase_get_complete_url_appends_the_route_to_a_model_named_like_it(model: str, stream: bool) -> None:
|
|
url: Final = PredibaseConfig().get_complete_url(
|
|
api_base="https://serving.example.com",
|
|
api_key="test-key",
|
|
model=model,
|
|
optional_params={"stream": stream},
|
|
litellm_params={"predibase_tenant_id": "tenant-123"},
|
|
)
|
|
|
|
assert url.endswith(f"/llms/{model}/{model}")
|
|
|
|
|
|
def test_predibase_get_complete_url_missing_tenant_id():
|
|
config = PredibaseConfig()
|
|
|
|
with pytest.raises(ValueError, match="Missing Predibase Tenant ID"):
|
|
config.get_complete_url(
|
|
api_base=None,
|
|
api_key="test-key",
|
|
model="predibase-model",
|
|
optional_params={},
|
|
litellm_params={},
|
|
)
|
|
|
|
|
|
def test_predibase_get_complete_url_with_tenant_id_key():
|
|
config = PredibaseConfig()
|
|
|
|
url = config.get_complete_url(
|
|
api_base="https://serving.example.com",
|
|
api_key="test-key",
|
|
model="predibase-model",
|
|
optional_params={},
|
|
litellm_params={"tenant_id": "tenant-xyz"},
|
|
)
|
|
|
|
assert "tenant-xyz" in url
|
|
assert url.endswith("/generate")
|
|
|
|
|
|
def test_predibase_authentication_error_does_not_expose_the_api_key(respx_mock: respx.MockRouter) -> None:
|
|
route = respx_mock.post(
|
|
"https://serving.app.predibase.com/c4768f95/deployments/v2/llms/llama-3-8b-instruct/generate"
|
|
).mock(return_value=httpx.Response(401, json={"message": "invalid API key"}))
|
|
|
|
with pytest.raises(litellm.AuthenticationError) as exc_info:
|
|
litellm.completion(
|
|
model="predibase/llama-3-8b-instruct",
|
|
messages=[{"role": "user", "content": "What is the meaning of life?"}],
|
|
predibase_tenant_id="c4768f95",
|
|
api_key="hf-rawapikey",
|
|
max_retries=0,
|
|
)
|
|
|
|
assert "hf-rawapikey" not in str(exc_info.value)
|
|
assert route.calls.last.request.headers["Authorization"] == "Bearer hf-rawapikey"
|
|
|
|
|
|
def test_predibase_transform_response_success_best_of(monkeypatch):
|
|
config = PredibaseConfig()
|
|
logging_obj = Mock()
|
|
encoding = Mock()
|
|
encoding.encode.return_value = [1, 2, 3]
|
|
monkeypatch.setattr("litellm.token_counter", lambda messages: 5)
|
|
|
|
raw_response = httpx.Response(
|
|
status_code=200,
|
|
json={
|
|
"generated_text": "<|assistant|>primary-output</s>",
|
|
"details": {
|
|
"finish_reason": "eos_token",
|
|
"tokens": [{"logprob": -0.2}, {"logprob": None}],
|
|
"best_of_sequences": [
|
|
{
|
|
"generated_text": "<s>secondary-output</s>",
|
|
"finish_reason": "length",
|
|
"tokens": [{"logprob": -0.5}],
|
|
}
|
|
],
|
|
},
|
|
},
|
|
headers={"x-request-id": "req-123"},
|
|
)
|
|
|
|
result = config.transform_response(
|
|
model="predibase-model",
|
|
raw_response=raw_response,
|
|
model_response=_build_model_response(),
|
|
logging_obj=logging_obj,
|
|
request_data={"inputs": "hello", "parameters": {}},
|
|
messages=[{"role": "user", "content": "hello"}],
|
|
optional_params={"best_of": 2},
|
|
litellm_params={},
|
|
encoding=encoding,
|
|
api_key="test-key",
|
|
)
|
|
|
|
assert result.choices[0].message.content == "primary-output"
|
|
assert len(result.choices) == 2
|
|
assert result.choices[1].message.content == "secondary-output"
|
|
assert result.usage.prompt_tokens == 5
|
|
assert result.usage.completion_tokens == 3
|
|
assert (
|
|
result._hidden_params["additional_headers"]["llm_provider-x-request-id"]
|
|
== "req-123"
|
|
)
|
|
|
|
|
|
def test_predibase_transform_response_invalid_json():
|
|
config = PredibaseConfig()
|
|
|
|
with pytest.raises(PredibaseError) as exc:
|
|
config.transform_response(
|
|
model="predibase-model",
|
|
raw_response=httpx.Response(status_code=200, content=b"not-json"),
|
|
model_response=_build_model_response(),
|
|
logging_obj=Mock(),
|
|
request_data={},
|
|
messages=[{"role": "user", "content": "hello"}],
|
|
optional_params={},
|
|
litellm_params={},
|
|
encoding=Mock(),
|
|
api_key="test-key",
|
|
)
|
|
|
|
assert exc.value.status_code == 422
|
|
|
|
|
|
def test_predibase_transform_response_error_field():
|
|
config = PredibaseConfig()
|
|
|
|
with pytest.raises(PredibaseError) as exc:
|
|
config.transform_response(
|
|
model="predibase-model",
|
|
raw_response=httpx.Response(
|
|
status_code=400, json={"error": "invalid request"}
|
|
),
|
|
model_response=_build_model_response(),
|
|
logging_obj=Mock(),
|
|
request_data={},
|
|
messages=[{"role": "user", "content": "hello"}],
|
|
optional_params={},
|
|
litellm_params={},
|
|
encoding=Mock(),
|
|
api_key="test-key",
|
|
)
|
|
|
|
assert exc.value.status_code == 400
|
|
|
|
|
|
def test_predibase_transform_response_missing_generated_text():
|
|
config = PredibaseConfig()
|
|
|
|
with pytest.raises(PredibaseError, match="'generated_text' is not a key"):
|
|
config.transform_response(
|
|
model="predibase-model",
|
|
raw_response=httpx.Response(status_code=200, json={"details": {}}),
|
|
model_response=_build_model_response(),
|
|
logging_obj=Mock(),
|
|
request_data={},
|
|
messages=[{"role": "user", "content": "hello"}],
|
|
optional_params={},
|
|
litellm_params={},
|
|
encoding=Mock(),
|
|
api_key="test-key",
|
|
)
|
|
|
|
|
|
def test_predibase_transform_response_non_dict_payload():
|
|
config = PredibaseConfig()
|
|
raw_response = Mock()
|
|
raw_response.text = "[]"
|
|
raw_response.status_code = 200
|
|
raw_response.headers = {}
|
|
raw_response.json.return_value = []
|
|
|
|
with pytest.raises(PredibaseError, match="'completion_response' is not a dictionary"):
|
|
config.transform_response(
|
|
model="predibase-model",
|
|
raw_response=raw_response,
|
|
model_response=_build_model_response(),
|
|
logging_obj=Mock(),
|
|
request_data={},
|
|
messages=[{"role": "user", "content": "hello"}],
|
|
optional_params={},
|
|
litellm_params={},
|
|
encoding=Mock(),
|
|
api_key="test-key",
|
|
)
|
|
|
|
|
|
def test_predibase_transform_response_best_of_with_empty_generated_text(monkeypatch):
|
|
config = PredibaseConfig()
|
|
logging_obj = Mock()
|
|
encoding = Mock()
|
|
encoding.encode.return_value = [1]
|
|
monkeypatch.setattr("litellm.token_counter", lambda messages: 1)
|
|
|
|
raw_response = httpx.Response(
|
|
status_code=200,
|
|
json={
|
|
"generated_text": "primary-output",
|
|
"details": {
|
|
"finish_reason": "stop",
|
|
"tokens": [],
|
|
"best_of_sequences": [
|
|
{
|
|
"generated_text": "",
|
|
"finish_reason": "length",
|
|
"tokens": [],
|
|
}
|
|
],
|
|
},
|
|
},
|
|
)
|
|
|
|
result = config.transform_response(
|
|
model="predibase-model",
|
|
raw_response=raw_response,
|
|
model_response=_build_model_response(),
|
|
logging_obj=logging_obj,
|
|
request_data={"inputs": "hello", "parameters": {}},
|
|
messages=[{"role": "user", "content": "hello"}],
|
|
optional_params={"best_of": 2},
|
|
litellm_params={},
|
|
encoding=encoding,
|
|
api_key="test-key",
|
|
)
|
|
|
|
assert len(result.choices) == 2
|
|
assert result.choices[1].message.content is None
|
|
|
|
|
|
def test_predibase_transform_response_best_of_from_request_data(monkeypatch):
|
|
config = PredibaseConfig()
|
|
logging_obj = Mock()
|
|
encoding = Mock()
|
|
encoding.encode.return_value = [1]
|
|
monkeypatch.setattr("litellm.token_counter", lambda messages: 1)
|
|
|
|
raw_response = httpx.Response(
|
|
status_code=200,
|
|
json={
|
|
"generated_text": "primary-output",
|
|
"details": {
|
|
"finish_reason": "stop",
|
|
"tokens": [],
|
|
"best_of_sequences": [
|
|
{
|
|
"generated_text": "secondary-output",
|
|
"finish_reason": "length",
|
|
"tokens": [],
|
|
}
|
|
],
|
|
},
|
|
},
|
|
)
|
|
|
|
result = config.transform_response(
|
|
model="predibase-model",
|
|
raw_response=raw_response,
|
|
model_response=_build_model_response(),
|
|
logging_obj=logging_obj,
|
|
request_data={"inputs": "hello", "parameters": {"best_of": 2}},
|
|
messages=[{"role": "user", "content": "hello"}],
|
|
optional_params={},
|
|
litellm_params={},
|
|
encoding=encoding,
|
|
api_key="test-key",
|
|
)
|
|
|
|
assert len(result.choices) == 2
|
|
assert result.choices[1].message.content == "secondary-output"
|
|
|
|
|
|
def test_predibase_transform_response_best_of_invalid_value_falls_back(monkeypatch):
|
|
config = PredibaseConfig()
|
|
logging_obj = Mock()
|
|
encoding = Mock()
|
|
encoding.encode.return_value = [1]
|
|
monkeypatch.setattr("litellm.token_counter", lambda messages: 1)
|
|
|
|
raw_response = httpx.Response(
|
|
status_code=200,
|
|
json={
|
|
"generated_text": "primary-output",
|
|
"details": {
|
|
"finish_reason": "stop",
|
|
"tokens": [],
|
|
"best_of_sequences": [
|
|
{
|
|
"generated_text": "secondary-output",
|
|
"finish_reason": "length",
|
|
"tokens": [],
|
|
}
|
|
],
|
|
},
|
|
},
|
|
)
|
|
|
|
result = config.transform_response(
|
|
model="predibase-model",
|
|
raw_response=raw_response,
|
|
model_response=_build_model_response(),
|
|
logging_obj=logging_obj,
|
|
request_data={"inputs": "hello", "parameters": {}},
|
|
messages=[{"role": "user", "content": "hello"}],
|
|
optional_params={"best_of": "invalid-int"},
|
|
litellm_params={},
|
|
encoding=encoding,
|
|
api_key="test-key",
|
|
)
|
|
|
|
# Invalid best_of should safely fall back to 0 and not append extra choices.
|
|
assert len(result.choices) == 1
|
|
assert result.choices[0].message.content == "primary-output"
|
|
|
|
|
|
def test_predibase_transform_response_empty_output_sets_completion_tokens_zero(monkeypatch):
|
|
config = PredibaseConfig()
|
|
logging_obj = Mock()
|
|
encoding = Mock()
|
|
monkeypatch.setattr("litellm.token_counter", lambda messages: 3)
|
|
|
|
raw_response = httpx.Response(
|
|
status_code=200,
|
|
json={"generated_text": "", "details": {"tokens": [], "finish_reason": "stop"}},
|
|
)
|
|
|
|
result = config.transform_response(
|
|
model="predibase-model",
|
|
raw_response=raw_response,
|
|
model_response=_build_model_response(),
|
|
logging_obj=logging_obj,
|
|
request_data={"inputs": "hello", "parameters": {}},
|
|
messages=[{"role": "user", "content": "hello"}],
|
|
optional_params={},
|
|
litellm_params={},
|
|
encoding=encoding,
|
|
api_key="test-key",
|
|
)
|
|
|
|
assert result.usage.prompt_tokens == 3
|
|
assert result.usage.completion_tokens == 0
|
|
|
|
|
|
def test_predibase_get_complete_url_uses_env_base_url(monkeypatch):
|
|
config = PredibaseConfig()
|
|
monkeypatch.setenv("PREDIBASE_API_BASE", "https://env.predibase.com")
|
|
|
|
url = config.get_complete_url(
|
|
api_base=None,
|
|
api_key="test-key",
|
|
model="predibase-model",
|
|
optional_params={},
|
|
litellm_params={"predibase_tenant_id": "tenant-123"},
|
|
)
|
|
|
|
assert url.startswith("https://env.predibase.com/tenant-123/")
|
|
|
|
|
|
def test_predibase_transform_response_usage_fallbacks(monkeypatch):
|
|
config = PredibaseConfig()
|
|
logging_obj = Mock()
|
|
encoding = Mock()
|
|
encoding.encode.side_effect = RuntimeError("encoding failure")
|
|
monkeypatch.setattr(
|
|
"litellm.token_counter", lambda messages: (_ for _ in ()).throw(RuntimeError())
|
|
)
|
|
|
|
raw_response = httpx.Response(
|
|
status_code=200,
|
|
json={"generated_text": "ok", "details": {"tokens": [], "finish_reason": "stop"}},
|
|
)
|
|
|
|
result = config.transform_response(
|
|
model="predibase-model",
|
|
raw_response=raw_response,
|
|
model_response=_build_model_response(),
|
|
logging_obj=logging_obj,
|
|
request_data={"inputs": "hello", "parameters": {}},
|
|
messages=[{"role": "user", "content": "hello"}],
|
|
optional_params={},
|
|
litellm_params={},
|
|
encoding=encoding,
|
|
api_key="test-key",
|
|
)
|
|
|
|
assert result.usage.prompt_tokens == 0
|
|
assert result.usage.completion_tokens == 0
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_predibase_async_completion_uses_default_config_when_none(monkeypatch):
|
|
handler = PredibaseChatCompletion()
|
|
mock_response = httpx.Response(status_code=200, json={"generated_text": "ok"})
|
|
|
|
async_handler = Mock()
|
|
async_handler.post = AsyncMock(return_value=mock_response)
|
|
monkeypatch.setattr(
|
|
"litellm.llms.predibase.chat.handler.get_async_httpx_client",
|
|
lambda **kwargs: async_handler,
|
|
)
|
|
|
|
default_config = Mock()
|
|
default_config.transform_response.return_value = _build_model_response()
|
|
monkeypatch.setattr("litellm.PredibaseConfig", lambda: default_config)
|
|
|
|
result = await handler.async_completion(
|
|
model="predibase-model",
|
|
messages=[{"role": "user", "content": "hello"}],
|
|
api_base="https://serving.example.com/x/generate",
|
|
model_response=_build_model_response(),
|
|
print_verbose=Mock(),
|
|
encoding=Mock(),
|
|
api_key="test-key",
|
|
logging_obj=Mock(),
|
|
stream=False,
|
|
data={"inputs": "hello", "parameters": {}},
|
|
optional_params={},
|
|
timeout=10,
|
|
litellm_params={},
|
|
headers={"Authorization": "Bearer test"},
|
|
)
|
|
|
|
assert result is default_config.transform_response.return_value
|
|
default_config.transform_response.assert_called_once()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_predibase_async_completion_uses_passed_config(monkeypatch):
|
|
handler = PredibaseChatCompletion()
|
|
mock_response = httpx.Response(status_code=200, json={"generated_text": "ok"})
|
|
|
|
async_handler = Mock()
|
|
async_handler.post = AsyncMock(return_value=mock_response)
|
|
monkeypatch.setattr(
|
|
"litellm.llms.predibase.chat.handler.get_async_httpx_client",
|
|
lambda **kwargs: async_handler,
|
|
)
|
|
|
|
passed_config = Mock()
|
|
passed_config.transform_response.return_value = _build_model_response()
|
|
|
|
result = await handler.async_completion(
|
|
model="predibase-model",
|
|
messages=[{"role": "user", "content": "hello"}],
|
|
api_base="https://serving.example.com/x/generate",
|
|
model_response=_build_model_response(),
|
|
print_verbose=Mock(),
|
|
encoding=Mock(),
|
|
api_key="test-key",
|
|
logging_obj=Mock(),
|
|
stream=False,
|
|
data={"inputs": "hello", "parameters": {}},
|
|
optional_params={},
|
|
timeout=10,
|
|
litellm_params={},
|
|
headers={"Authorization": "Bearer test"},
|
|
predibase_config=passed_config,
|
|
)
|
|
|
|
assert result is passed_config.transform_response.return_value
|
|
passed_config.transform_response.assert_called_once()
|
|
|
|
|
|
def test_predibase_completion_sync_returns_transform_response(monkeypatch):
|
|
handler = PredibaseChatCompletion()
|
|
expected = _build_model_response()
|
|
|
|
def fake_validate_environment(self, **kwargs):
|
|
return {"Authorization": "Bearer test"}
|
|
|
|
def fake_get_complete_url(self, **kwargs):
|
|
return "https://serving.example.com/tenant/deployments/v2/llms/model/generate"
|
|
|
|
def fake_transform_request(self, **kwargs):
|
|
return {"inputs": "hello", "parameters": {}}
|
|
|
|
def fake_transform_response(self, **kwargs):
|
|
return expected
|
|
|
|
monkeypatch.setattr(PredibaseConfig, "validate_environment", fake_validate_environment)
|
|
monkeypatch.setattr(PredibaseConfig, "get_complete_url", fake_get_complete_url)
|
|
monkeypatch.setattr(PredibaseConfig, "transform_request", fake_transform_request)
|
|
monkeypatch.setattr(PredibaseConfig, "transform_response", fake_transform_response)
|
|
monkeypatch.setattr(
|
|
"litellm.module_level_client.post",
|
|
lambda *args, **kwargs: httpx.Response(status_code=200, json={"generated_text": "ok"}),
|
|
)
|
|
|
|
result = handler.completion(
|
|
model="predibase-model",
|
|
messages=[{"role": "user", "content": "hello"}],
|
|
api_base="https://serving.example.com",
|
|
custom_prompt_dict={},
|
|
model_response=_build_model_response(),
|
|
print_verbose=Mock(),
|
|
encoding=Mock(),
|
|
api_key="test-key",
|
|
logging_obj=Mock(),
|
|
optional_params={},
|
|
litellm_params={},
|
|
tenant_id="tenant-123",
|
|
timeout=10,
|
|
acompletion=False,
|
|
)
|
|
|
|
assert result is expected
|
|
|
|
|
|
def test_predibase_completion_passes_existing_config_to_async_completion(monkeypatch):
|
|
handler = PredibaseChatCompletion()
|
|
captured = {}
|
|
|
|
def fake_validate_environment(self, **kwargs):
|
|
captured["config_instance"] = self
|
|
return {"Authorization": "Bearer test"}
|
|
|
|
def fake_get_complete_url(self, **kwargs):
|
|
return "https://serving.example.com/tenant/deployments/v2/llms/model/generate"
|
|
|
|
def fake_transform_request(self, **kwargs):
|
|
return {"inputs": "hello", "parameters": {}}
|
|
|
|
def fake_async_completion(**kwargs):
|
|
captured["async_kwargs"] = kwargs
|
|
return "async-result"
|
|
|
|
monkeypatch.setattr(PredibaseConfig, "validate_environment", fake_validate_environment)
|
|
monkeypatch.setattr(PredibaseConfig, "get_complete_url", fake_get_complete_url)
|
|
monkeypatch.setattr(PredibaseConfig, "transform_request", fake_transform_request)
|
|
monkeypatch.setattr(handler, "async_completion", fake_async_completion)
|
|
|
|
result = handler.completion(
|
|
model="predibase-model",
|
|
messages=[{"role": "user", "content": "hello"}],
|
|
api_base="https://serving.example.com",
|
|
custom_prompt_dict={},
|
|
model_response=_build_model_response(),
|
|
print_verbose=Mock(),
|
|
encoding=Mock(),
|
|
api_key="test-key",
|
|
logging_obj=Mock(),
|
|
optional_params={},
|
|
litellm_params={},
|
|
tenant_id="tenant-123",
|
|
timeout=10,
|
|
acompletion=True,
|
|
)
|
|
|
|
assert result == "async-result"
|
|
assert captured["async_kwargs"]["predibase_config"] is captured["config_instance"]
|