litellm/tests/unit/responses/test_responses_api_lifecycle.py
devin-ai-integration[bot] 04d97abffb
test: make 77 legacy live tests offline in litellm_utils, router_unit and responses dirs (#45298)
* test: make 77 legacy live tests offline in litellm_utils, router_unit and responses dirs

* test: restore lost coverage in litellm_utils offline replacements

Bound live health-check tasks to the concurrency limit so eager task
creation behind a semaphore fails, route the Langfuse trace-id check
through completion -> Logging.get_trace_id with all four metadata
combinations, run the callback dedup checks through acompletion, pass
the dynamic key via litellm_params, cover the env-inferred default
model list, and add the per-provider audio transcription config lookup

* test: restore router coverage lost in the offline move

Add the missing non-stream LIT-3058 header/count-once test, pin the UTC minute on every
usage-counter test, assert router-level client reuse for transcription, and replace the
private selector-attribute checks with routing outcomes per strategy. Assert outbound bodies
for speech, rerank, image, assistants and moderation, add timeouts to event waits, and drop
the router_unit_tests husk files that no longer hold tests

* test: restore sync stream, router sync stream, error event and field-type coverage for migrated responses tests

Add the sync streaming logging and sync Router.responses streaming cases the
offline replacements dropped, port the legacy per-event and response field-type
validation, assert raw headers, the float created_at conversion, MCP tool headers,
the search_context_size input, and add an offline replacement for the in-stream
context-window error event. Remove helpers left dead in the legacy file.

* test: run numpydoc-backed unit tests in GHA and drop a test that pinned a crash

* test: restore sequence number, item id and content part checks in the responses stream validator

* test: match router logging events to the test's own deployment so late events from other tests are ignored

* test: restore the s3 cold storage history test to its original assertions

---------

Co-authored-by: yuneng <yuneng@berri.ai>
2026-10-08 02:37:55 -07:00

109 lines
3.6 KiB
Python

from typing import Final, Literal, TypeAlias
import httpx
import openai
import pytest
import respx
import litellm
from tests.unit.proxy.conftest import httpx_transport
pytestmark: Final = pytest.mark.usefixtures(httpx_transport.__name__)
Provider: TypeAlias = Literal["anthropic", "gemini"]
Operation: TypeAlias = Literal["delete", "get", "cancel"]
CancelProvider: TypeAlias = Literal["openai", "azure"]
def _invoke_unsupported_response_operation(provider: Provider, operation: Operation) -> object:
match operation:
case "delete":
return litellm.delete_responses(
response_id="resp_unsupported",
custom_llm_provider=provider,
api_key="sk-test",
)
case "get":
return litellm.get_responses(
response_id="resp_unsupported",
custom_llm_provider=provider,
api_key="sk-test",
)
case "cancel":
return litellm.cancel_responses(
response_id="resp_unsupported",
custom_llm_provider=provider,
api_key="sk-test",
)
async def _invoke_cancel_response(sync_mode: bool, call_kwargs: dict[str, object]) -> object:
if sync_mode:
return litellm.cancel_responses(**call_kwargs)
return await litellm.acancel_responses(**call_kwargs)
@pytest.mark.parametrize(
("provider", "operation"),
(
("anthropic", "delete"),
("anthropic", "get"),
("anthropic", "cancel"),
("gemini", "delete"),
("gemini", "get"),
("gemini", "cancel"),
),
)
def test_unsupported_response_lifecycle_operation_fails_before_http(provider: Provider, operation: Operation) -> None:
with respx.mock() as mock_router:
with pytest.raises(litellm.APIConnectionError) as exc_info:
_invoke_unsupported_response_operation(provider, operation)
calls: Final = tuple(mock_router.calls)
assert exc_info.value.status_code == 500
assert f"not supported for {provider}" in str(exc_info.value)
assert calls == ()
@pytest.mark.parametrize(
("provider", "sync_mode"),
(("openai", True), ("openai", False), ("azure", True), ("azure", False)),
)
@pytest.mark.asyncio
async def test_cancel_responses_404_surfaces_openai_api_error_with_exact_url(
provider: CancelProvider, sync_mode: bool
) -> None:
response_id: Final = "resp_missing"
error_body: Final = {
"error": {
"message": "Response was not found",
"type": "invalid_request_error",
"code": "not_found",
}
}
api_base: Final = (
"https://api.openai.com/v1" if provider == "openai" else "https://example-resource.openai.azure.com"
)
expected_url: Final = (
f"{api_base}/responses/{response_id}/cancel"
if provider == "openai"
else f"{api_base}/openai/responses/{response_id}/cancel?api-version=2025-03-01-preview"
)
with respx.mock() as mock_router:
route: Final = mock_router.post(expected_url).mock(
return_value=httpx.Response(status_code=404, json=error_body)
)
call_kwargs: Final = {
"custom_llm_provider": provider,
"api_key": "sk-test",
"api_base": api_base,
"api_version": "2025-03-01-preview",
"response_id": response_id,
}
with pytest.raises(openai.APIError) as exc_info:
await _invoke_cancel_response(sync_mode, call_kwargs)
requests: Final = tuple(route.calls)
assert exc_info.value.status_code == 404
assert len(requests) == 1
assert str(requests[0].request.url) == expected_url