From 1aa2e19ee4dc46a861c65ba7ebb506d41c307908 Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Tue, 15 Sep 2026 19:52:00 -0700 Subject: [PATCH 1/6] test(together_ai): stop depending on a serverless model we do not control Together moved openai/gpt-oss-20b off serverless, so three tests started failing with a 400 model_not_available from the live API. None of them was really testing Together: they cover provider-prefix parsing, prompt shaping and streaming, all litellm side. Mock the transport and assert those, so the tests answer to our code instead of a vendor catalog. --- tests/local_testing/test_completion.py | 109 ++++++++++++++++++------- 1 file changed, 80 insertions(+), 29 deletions(-) diff --git a/tests/local_testing/test_completion.py b/tests/local_testing/test_completion.py index 43ed57f63af..a6ac112a3b7 100644 --- a/tests/local_testing/test_completion.py +++ b/tests/local_testing/test_completion.py @@ -11,6 +11,7 @@ import io from unittest.mock import AsyncMock, MagicMock, patch +import httpx import pytest import litellm @@ -57,21 +58,49 @@ def test_response_model_none(): assert isinstance(x, litellm.ModelResponse) +TOGETHER_AI_CHAT_URL = "https://api.together.ai/v1/chat/completions" + + +def _together_ai_chat_response(content="Hello!"): + return httpx.Response( + 200, + json={ + "id": "chatcmpl-together", + "object": "chat.completion", + "created": 1, + "model": "openai/gpt-oss-20b", + "choices": [ + { + "index": 0, + "message": {"role": "assistant", "content": content}, + "finish_reason": "stop", + } + ], + "usage": { + "prompt_tokens": 1, + "completion_tokens": 1, + "total_tokens": 2, + }, + }, + request=httpx.Request("POST", TOGETHER_AI_CHAT_URL), + ) + + def test_completion_custom_provider_model_name(): - try: - litellm.cache = None + litellm.cache = None + with patch.object( + HTTPHandler, "post", return_value=_together_ai_chat_response() + ) as mock_post: response = completion( model="together_ai/openai/gpt-oss-20b", messages=messages, logger_fn=logger_fn, + api_key="fake-key", ) - # Add assertions here to check the-response - print(response) - print(response["choices"][0]["finish_reason"]) - except litellm.Timeout as e: - pass - except Exception as e: - pytest.fail(f"Error occurred: {e}") + + assert mock_post.call_args.kwargs["url"] == TOGETHER_AI_CHAT_URL + assert json.loads(mock_post.call_args.kwargs["data"])["model"] == "openai/gpt-oss-20b" + assert response.choices[0].finish_reason == "stop" def _openai_mock_response(*args, **kwargs) -> litellm.ModelResponse: @@ -2804,12 +2833,11 @@ def test_completion_together_ai_llama(): # test_completion_together_ai() def test_customprompt_together_ai(): - try: - litellm.set_verbose = False - litellm.num_retries = 0 - print("in test_customprompt_together_ai") - print(litellm.success_callback) - print(litellm._async_success_callback) + litellm.set_verbose = False + litellm.num_retries = 0 + with patch.object( + HTTPHandler, "post", return_value=_together_ai_chat_response() + ) as mock_post: response = completion( model="together_ai/openai/gpt-oss-20b", messages=messages, @@ -2827,14 +2855,14 @@ def test_customprompt_together_ai(): "post_message": "<|im_end|>", }, }, + api_key="fake-key", ) - print(response) - except litellm.exceptions.Timeout as e: - print(f"Timeout Error") - pass - except Exception as e: - print(f"ERROR TYPE {type(e)}") - pytest.fail(f"Error occurred: {e}") + + body = json.loads(mock_post.call_args.kwargs["data"]) + assert body["messages"] == messages + assert "prompt" not in body + assert "roles" not in body + assert response.choices[0].finish_reason == "stop" # test_customprompt_together_ai() @@ -3648,19 +3676,42 @@ def test_completion_together_ai_stream(): litellm.set_verbose = True user_message = "Write 1pg about YC & litellm" messages = [{"content": user_message, "role": "user"}] - try: + sse_body = ( + 'data: {"id":"chatcmpl-together","object":"chat.completion.chunk","created":1,' + '"model":"openai/gpt-oss-20b","choices":[{"index":0,"delta":{"role":"assistant",' + '"content":"YC"},"finish_reason":null}]}\n\n' + 'data: {"id":"chatcmpl-together","object":"chat.completion.chunk","created":1,' + '"model":"openai/gpt-oss-20b","choices":[{"index":0,"delta":{"content":" and ' + 'litellm"},"finish_reason":null}]}\n\n' + 'data: {"id":"chatcmpl-together","object":"chat.completion.chunk","created":1,' + '"model":"openai/gpt-oss-20b","choices":[{"index":0,"delta":{},' + '"finish_reason":"stop"}]}\n\n' + "data: [DONE]\n\n" + ) + stream_response = httpx.Response( + 200, + content=sse_body.encode(), + headers={"content-type": "text/event-stream"}, + request=httpx.Request("POST", TOGETHER_AI_CHAT_URL), + ) + + with patch.object( + HTTPHandler, "post", return_value=stream_response + ) as mock_post: response = completion( model="together_ai/openai/gpt-oss-20b", messages=messages, stream=True, max_tokens=5, + api_key="fake-key", ) - print(response) - for chunk in response: - print(chunk) - # print(string_response) - except Exception as e: - pytest.fail(f"Error occurred: {e}") + chunks = list(response) + + assert json.loads(mock_post.call_args.kwargs["data"])["stream"] is True + assert "".join( + chunk.choices[0].delta.content or "" for chunk in chunks + ) == "YC and litellm" + assert chunks[-1].choices[0].finish_reason == "stop" # test_completion_together_ai_stream() From 8c046e13bdcbd61f570f10f674ff57b2dcf19afb Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Tue, 15 Sep 2026 20:17:42 -0700 Subject: [PATCH 2/6] test(together_ai): move request-shape checks to the mapped file, drop the live ones Together moved openai/gpt-oss-20b off serverless and three tests in test_completion.py died on a live 400. None of them needed Together to be up: streaming is already covered live by tests/e2e/llm_translation/test_together_ai_e2e.py, which picks its model from the cost map instead of pinning one, and the other two are request-shape questions. Delete all three and assert the two shapes in the mapped transformation file: the provider prefix is stripped without eating the rest of a slashed model name, and custom role wrappers never reach the request. --- tests/local_testing/test_completion.py | 125 ------------------ .../test_together_ai_chat_transformation.py | 64 +++++++++ 2 files changed, 64 insertions(+), 125 deletions(-) diff --git a/tests/local_testing/test_completion.py b/tests/local_testing/test_completion.py index a6ac112a3b7..25c6c50251d 100644 --- a/tests/local_testing/test_completion.py +++ b/tests/local_testing/test_completion.py @@ -11,7 +11,6 @@ import io from unittest.mock import AsyncMock, MagicMock, patch -import httpx import pytest import litellm @@ -58,51 +57,6 @@ def test_response_model_none(): assert isinstance(x, litellm.ModelResponse) -TOGETHER_AI_CHAT_URL = "https://api.together.ai/v1/chat/completions" - - -def _together_ai_chat_response(content="Hello!"): - return httpx.Response( - 200, - json={ - "id": "chatcmpl-together", - "object": "chat.completion", - "created": 1, - "model": "openai/gpt-oss-20b", - "choices": [ - { - "index": 0, - "message": {"role": "assistant", "content": content}, - "finish_reason": "stop", - } - ], - "usage": { - "prompt_tokens": 1, - "completion_tokens": 1, - "total_tokens": 2, - }, - }, - request=httpx.Request("POST", TOGETHER_AI_CHAT_URL), - ) - - -def test_completion_custom_provider_model_name(): - litellm.cache = None - with patch.object( - HTTPHandler, "post", return_value=_together_ai_chat_response() - ) as mock_post: - response = completion( - model="together_ai/openai/gpt-oss-20b", - messages=messages, - logger_fn=logger_fn, - api_key="fake-key", - ) - - assert mock_post.call_args.kwargs["url"] == TOGETHER_AI_CHAT_URL - assert json.loads(mock_post.call_args.kwargs["data"])["model"] == "openai/gpt-oss-20b" - assert response.choices[0].finish_reason == "stop" - - def _openai_mock_response(*args, **kwargs) -> litellm.ModelResponse: new_response = MagicMock() new_response.headers = {"hello": "world"} @@ -2832,40 +2786,6 @@ def test_completion_together_ai_llama(): # test_completion_together_ai() -def test_customprompt_together_ai(): - litellm.set_verbose = False - litellm.num_retries = 0 - with patch.object( - HTTPHandler, "post", return_value=_together_ai_chat_response() - ) as mock_post: - response = completion( - model="together_ai/openai/gpt-oss-20b", - messages=messages, - roles={ - "system": { - "pre_message": "<|im_start|>system\n", - "post_message": "<|im_end|>", - }, - "assistant": { - "pre_message": "<|im_start|>assistant\n", - "post_message": "<|im_end|>", - }, - "user": { - "pre_message": "<|im_start|>user\n", - "post_message": "<|im_end|>", - }, - }, - api_key="fake-key", - ) - - body = json.loads(mock_post.call_args.kwargs["data"]) - assert body["messages"] == messages - assert "prompt" not in body - assert "roles" not in body - assert response.choices[0].finish_reason == "stop" - - -# test_customprompt_together_ai() def response_format_tests(response: litellm.ModelResponse): @@ -3672,51 +3592,6 @@ async def test_acompletion_stream_watsonx(): # test_maritalk() -def test_completion_together_ai_stream(): - litellm.set_verbose = True - user_message = "Write 1pg about YC & litellm" - messages = [{"content": user_message, "role": "user"}] - sse_body = ( - 'data: {"id":"chatcmpl-together","object":"chat.completion.chunk","created":1,' - '"model":"openai/gpt-oss-20b","choices":[{"index":0,"delta":{"role":"assistant",' - '"content":"YC"},"finish_reason":null}]}\n\n' - 'data: {"id":"chatcmpl-together","object":"chat.completion.chunk","created":1,' - '"model":"openai/gpt-oss-20b","choices":[{"index":0,"delta":{"content":" and ' - 'litellm"},"finish_reason":null}]}\n\n' - 'data: {"id":"chatcmpl-together","object":"chat.completion.chunk","created":1,' - '"model":"openai/gpt-oss-20b","choices":[{"index":0,"delta":{},' - '"finish_reason":"stop"}]}\n\n' - "data: [DONE]\n\n" - ) - stream_response = httpx.Response( - 200, - content=sse_body.encode(), - headers={"content-type": "text/event-stream"}, - request=httpx.Request("POST", TOGETHER_AI_CHAT_URL), - ) - - with patch.object( - HTTPHandler, "post", return_value=stream_response - ) as mock_post: - response = completion( - model="together_ai/openai/gpt-oss-20b", - messages=messages, - stream=True, - max_tokens=5, - api_key="fake-key", - ) - chunks = list(response) - - assert json.loads(mock_post.call_args.kwargs["data"])["stream"] is True - assert "".join( - chunk.choices[0].delta.content or "" for chunk in chunks - ) == "YC and litellm" - assert chunks[-1].choices[0].finish_reason == "stop" - - -# test_completion_together_ai_stream() - - def test_moderation(): response = litellm.moderation(input="i'm ishaan cto of litellm") print(response) diff --git a/tests/test_litellm/llms/together_ai/chat/test_together_ai_chat_transformation.py b/tests/test_litellm/llms/together_ai/chat/test_together_ai_chat_transformation.py index 7eb7dc41d4f..a7347edb2c7 100644 --- a/tests/test_litellm/llms/together_ai/chat/test_together_ai_chat_transformation.py +++ b/tests/test_litellm/llms/together_ai/chat/test_together_ai_chat_transformation.py @@ -1108,3 +1108,67 @@ def test_get_optional_params_preserves_max_for_declared_levels_model(): ) assert optional_params["reasoning_effort"] == "max" + + +def _together_chat_transport() -> tuple[HTTPHandler, list[httpx.Request]]: + captured_requests: list[httpx.Request] = [] + + def respond(request: httpx.Request) -> httpx.Response: + captured_requests.append(request) + return httpx.Response( + 200, + json={ + "id": "chatcmpl-together", + "object": "chat.completion", + "created": 1234567890, + "model": TOOL_CALLING_MODEL, + "choices": [ + { + "index": 0, + "message": {"role": "assistant", "content": "Hello!"}, + "finish_reason": "stop", + } + ], + "usage": {"prompt_tokens": 10, "completion_tokens": 5, "total_tokens": 15}, + }, + ) + + client = HTTPHandler(client=httpx.Client(transport=httpx.MockTransport(respond))) + return client, captured_requests + + +def test_only_the_provider_prefix_is_stripped_from_a_slashed_model_name(): + client, captured_requests = _together_chat_transport() + + litellm.completion( + model=f"together_ai/{TOOL_CALLING_MODEL}", + messages=[{"role": "user", "content": "Hello!"}], + api_key="fake-key", + client=client, + ) + + assert "/" in TOOL_CALLING_MODEL + assert str(captured_requests[0].url) == "https://api.together.ai/v1/chat/completions" + assert json.loads(captured_requests[0].content)["model"] == TOOL_CALLING_MODEL + + +def test_custom_role_wrappers_never_reach_the_request(): + client, captured_requests = _together_chat_transport() + messages = [{"role": "user", "content": "Hello!"}] + + litellm.completion( + model=f"together_ai/{TOOL_CALLING_MODEL}", + messages=messages, + roles={ + "system": {"pre_message": "<|im_start|>system\n", "post_message": "<|im_end|>"}, + "assistant": {"pre_message": "<|im_start|>assistant\n", "post_message": "<|im_end|>"}, + "user": {"pre_message": "<|im_start|>user\n", "post_message": "<|im_end|>"}, + }, + api_key="fake-key", + client=client, + ) + + request_body = json.loads(captured_requests[0].content) + assert request_body["messages"] == messages + assert "prompt" not in request_body + assert "roles" not in request_body From f10d95fb95371b1cebbec3ccd9fa3cce71fdc8ff Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Wed, 16 Sep 2026 13:58:38 -0700 Subject: [PATCH 3/6] test(together_ai): drop the prefix-strip assertion, e2e covers it live tests/e2e/llm_translation/test_together_ai_e2e.py registers its model with the full registry key, so a slashed together_ai// goes through the prefix strip on every e2e run and an over-strip would fail against the real API. The unit assertion was a second copy of that. The roles check stays, since nothing in e2e exercises it. --- .../chat/test_together_ai_chat_transformation.py | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/tests/test_litellm/llms/together_ai/chat/test_together_ai_chat_transformation.py b/tests/test_litellm/llms/together_ai/chat/test_together_ai_chat_transformation.py index a7347edb2c7..1df8c96fb50 100644 --- a/tests/test_litellm/llms/together_ai/chat/test_together_ai_chat_transformation.py +++ b/tests/test_litellm/llms/together_ai/chat/test_together_ai_chat_transformation.py @@ -1137,21 +1137,6 @@ def _together_chat_transport() -> tuple[HTTPHandler, list[httpx.Request]]: return client, captured_requests -def test_only_the_provider_prefix_is_stripped_from_a_slashed_model_name(): - client, captured_requests = _together_chat_transport() - - litellm.completion( - model=f"together_ai/{TOOL_CALLING_MODEL}", - messages=[{"role": "user", "content": "Hello!"}], - api_key="fake-key", - client=client, - ) - - assert "/" in TOOL_CALLING_MODEL - assert str(captured_requests[0].url) == "https://api.together.ai/v1/chat/completions" - assert json.loads(captured_requests[0].content)["model"] == TOOL_CALLING_MODEL - - def test_custom_role_wrappers_never_reach_the_request(): client, captured_requests = _together_chat_transport() messages = [{"role": "user", "content": "Hello!"}] From b4781317012c57c1bd7e186d1478b465316c6dfb Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Wed, 16 Sep 2026 14:20:49 -0700 Subject: [PATCH 4/6] test(together_ai): select the live model from the cost map `together_ai/openai/gpt-oss-20b` was hardcoded in two live tests and is no longer served, so both failed on a vendor catalog change rather than on anything litellm did. Both call sites now resolve the cheapest non-deprecated together_ai chat entry at runtime, filtered on the capabilities the tests actually exercise, mirroring what tests/e2e/llm_translation/test_together_ai_e2e.py already does. The selector lives in tests/_live_test_helpers.py so both lanes share one implementation. --- tests/_live_test_helpers.py | 36 +++++++++++++++++++++ tests/llm_translation/test_together_ai.py | 7 +++- tests/local_testing/test_text_completion.py | 7 +++- 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/tests/_live_test_helpers.py b/tests/_live_test_helpers.py index a79b81e82c1..6b39f37921d 100644 --- a/tests/_live_test_helpers.py +++ b/tests/_live_test_helpers.py @@ -1,4 +1,7 @@ import os +from collections.abc import Mapping +from datetime import date +from typing import Any import pytest @@ -8,3 +11,36 @@ def _skip_live_prompt_caching_test(): pytest.skip("Live prompt-caching E2E tests are opt-in") if os.environ.get("CASSETTE_REDIS_URL"): pytest.skip("Live prompt-caching E2E tests cannot run under VCR replay") + + +def cheapest_together_chat_model(*capability_flags: str) -> str: + import litellm + + today = date.today().isoformat() + + def qualifies(name: str, entry: Mapping[str, Any]) -> bool: + deprecation_date = entry.get("deprecation_date") + return ( + name.startswith("together_ai/") + and entry.get("litellm_provider") == "together_ai" + and entry.get("mode") == "chat" + and (deprecation_date is None or deprecation_date > today) + and (entry.get("input_cost_per_token") or 0.0) > 0 + and (entry.get("output_cost_per_token") or 0.0) > 0 + and all(bool(entry.get(flag)) for flag in capability_flags) + ) + + candidates = sorted( + ( + name + for name, entry in litellm.model_cost.items() + if isinstance(entry, Mapping) and qualifies(name, entry) + ), + key=lambda name: ( + litellm.model_cost[name].get("input_cost_per_token") or 0.0, + litellm.model_cost[name].get("output_cost_per_token") or 0.0, + name, + ), + ) + assert candidates, f"no live together_ai chat model in the cost map satisfies {capability_flags}" + return candidates[0] diff --git a/tests/llm_translation/test_together_ai.py b/tests/llm_translation/test_together_ai.py index fd7ad40ed11..7a49d46b528 100644 --- a/tests/llm_translation/test_together_ai.py +++ b/tests/llm_translation/test_together_ai.py @@ -3,6 +3,7 @@ Test TogetherAI LLM """ from base_llm_unit_tests import BaseLLMChatTest +from tests._live_test_helpers import cheapest_together_chat_model import json import os from datetime import datetime @@ -16,7 +17,11 @@ import pytest class TestTogetherAI(BaseLLMChatTest): def get_base_completion_call_args(self) -> dict: litellm.set_verbose = True - return {"model": "together_ai/openai/gpt-oss-20b"} + return { + "model": cheapest_together_chat_model( + "supports_function_calling", "supports_response_schema" + ) + } def test_tool_call_no_arguments(self, tool_call_no_arguments): """Test that tool calls with no arguments is translated correctly. Relevant issue: https://github.com/BerriAI/litellm/issues/6833""" diff --git a/tests/local_testing/test_text_completion.py b/tests/local_testing/test_text_completion.py index a814ce6d303..b15037a2fcd 100644 --- a/tests/local_testing/test_text_completion.py +++ b/tests/local_testing/test_text_completion.py @@ -1,7 +1,11 @@ import asyncio import json +import os +import sys import traceback +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))) + from dotenv import load_dotenv load_dotenv() @@ -12,6 +16,7 @@ from unittest.mock import MagicMock, patch import pytest import litellm +from tests._live_test_helpers import cheapest_together_chat_model from litellm import ( RateLimitError, TextCompletionResponse, @@ -4030,7 +4035,7 @@ def test_async_text_completion_together_ai(): async def test_get_response(): try: response = await litellm.atext_completion( - model="together_ai/openai/gpt-oss-20b", + model=cheapest_together_chat_model(), prompt="good morning", max_tokens=10, ) From ba6c9fa61d87bd76634bdaba7ce4ef578771f923 Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Wed, 16 Sep 2026 14:26:16 -0700 Subject: [PATCH 5/6] fix(test): drop the redundant sys.path.insert CI runs these lanes as `python -m pytest` from the repo root, so the root is already on sys.path and `tests._live_test_helpers` imports without help. The insert only tripped the TQ003 test-quality budget. --- tests/local_testing/test_text_completion.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/local_testing/test_text_completion.py b/tests/local_testing/test_text_completion.py index b15037a2fcd..6808dfd768b 100644 --- a/tests/local_testing/test_text_completion.py +++ b/tests/local_testing/test_text_completion.py @@ -1,11 +1,7 @@ import asyncio import json -import os -import sys import traceback -sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))) - from dotenv import load_dotenv load_dotenv() From 515bf8c9d564731cfb285d47818db2fc081e1ba7 Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Wed, 16 Sep 2026 14:32:08 -0700 Subject: [PATCH 6/6] refactor(test): validate cost-map entries into a typed model The selector read raw cost-map dicts as `Mapping[str, Any]`. It now validates each together_ai entry into a frozen Pydantic model and takes the two capabilities as keyword booleans, so nothing in the helper is coarsely typed or stringly addressed. --- tests/_live_test_helpers.py | 56 +++++++++++++++-------- tests/llm_translation/test_together_ai.py | 2 +- 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/tests/_live_test_helpers.py b/tests/_live_test_helpers.py index 6b39f37921d..629f8ac9fdb 100644 --- a/tests/_live_test_helpers.py +++ b/tests/_live_test_helpers.py @@ -1,9 +1,8 @@ import os -from collections.abc import Mapping from datetime import date -from typing import Any import pytest +from pydantic import BaseModel, ConfigDict def _skip_live_prompt_caching_test(): @@ -13,34 +12,53 @@ def _skip_live_prompt_caching_test(): pytest.skip("Live prompt-caching E2E tests cannot run under VCR replay") -def cheapest_together_chat_model(*capability_flags: str) -> str: + +class TogetherCostEntry(BaseModel): + model_config = ConfigDict(frozen=True, extra="ignore") + + litellm_provider: str | None = None + mode: str | None = None + deprecation_date: str | None = None + input_cost_per_token: float | None = None + output_cost_per_token: float | None = None + supports_function_calling: bool | None = None + supports_response_schema: bool | None = None + + +def cheapest_together_chat_model( + *, function_calling: bool = False, response_schema: bool = False +) -> str: import litellm today = date.today().isoformat() - def qualifies(name: str, entry: Mapping[str, Any]) -> bool: - deprecation_date = entry.get("deprecation_date") + def qualifies(name: str, entry: TogetherCostEntry) -> bool: return ( name.startswith("together_ai/") - and entry.get("litellm_provider") == "together_ai" - and entry.get("mode") == "chat" - and (deprecation_date is None or deprecation_date > today) - and (entry.get("input_cost_per_token") or 0.0) > 0 - and (entry.get("output_cost_per_token") or 0.0) > 0 - and all(bool(entry.get(flag)) for flag in capability_flags) + and entry.litellm_provider == "together_ai" + and entry.mode == "chat" + and (entry.deprecation_date is None or entry.deprecation_date > today) + and (entry.input_cost_per_token or 0.0) > 0 + and (entry.output_cost_per_token or 0.0) > 0 + and (not function_calling or bool(entry.supports_function_calling)) + and (not response_schema or bool(entry.supports_response_schema)) ) + registry: dict[str, TogetherCostEntry] = { + name: TogetherCostEntry.model_validate(raw) + for name, raw in litellm.model_cost.items() + if isinstance(raw, dict) and name.startswith("together_ai/") + } candidates = sorted( - ( - name - for name, entry in litellm.model_cost.items() - if isinstance(entry, Mapping) and qualifies(name, entry) - ), + (name for name, entry in registry.items() if qualifies(name, entry)), key=lambda name: ( - litellm.model_cost[name].get("input_cost_per_token") or 0.0, - litellm.model_cost[name].get("output_cost_per_token") or 0.0, + registry[name].input_cost_per_token or 0.0, + registry[name].output_cost_per_token or 0.0, name, ), ) - assert candidates, f"no live together_ai chat model in the cost map satisfies {capability_flags}" + assert candidates, ( + "no live together_ai chat model in the cost map satisfies " + f"function_calling={function_calling} response_schema={response_schema}" + ) return candidates[0] diff --git a/tests/llm_translation/test_together_ai.py b/tests/llm_translation/test_together_ai.py index 7a49d46b528..0b4e9d3952c 100644 --- a/tests/llm_translation/test_together_ai.py +++ b/tests/llm_translation/test_together_ai.py @@ -19,7 +19,7 @@ class TestTogetherAI(BaseLLMChatTest): litellm.set_verbose = True return { "model": cheapest_together_chat_model( - "supports_function_calling", "supports_response_schema" + function_calling=True, response_schema=True ) }