litellm/tests/integration/providers/test_assemblyai_passthrough.py
devin-ai-integration[bot] 1dbde0845e
test: migrate SDK and proxy-booting legacy CircleCI tests to their tiers (#45758)
* test: move guardrails, image, audio, batches, search legacy tests to unit

* test: restore the custom guardrail and remaining chunk 1 unit rows

* test: move the ocr matrix to e2e

* test: drop comments from moved tests and fix vertex ocr credentials

* test: move litellm utils and unified google legacy tests to their tiers

* test: move logging callback and pass-through unit legacy tests to their tiers

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* test: move store_model_in_db legacy tests and start the admin ui migration

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* test: finish the proxy job rows and drop their CircleCI jobs

Moves the remaining multi-instance, openai endpoint, otel, pass-through and
top-level proxy tests into tests/integration with scripted upstreams, rewrites
the weak replacements to assert the outbound wire and exact responses, and
removes the build_and_test job group this PR owns from CircleCI

* test: tighten the key generation and error log assertions, rename migrated unit files to their mapped names

* test: shard the pagerduty unit tests and record the rust harness file on the coverage allowlist

* test: drive the SSO sign-in tests through a scripted IdP and isolate the cost center stand-in

* test: type the e2e beta header, prompt cache and secret manager values

* test: assert the missing model error message on the OpenAI routes

* test: drop the deleted otel base class import and restore mcp redaction and exact langfuse usage checks

* test: make the langfuse fixture directory a package for the layout check

* test: restore collected no-match cases, real langfuse and gcs payload checks, and gate the aws secret lane

* test: keep the ocr e2e pdf fixture next to the test

* test: put meta below covers on the streaming tool search e2e

* test: run the OCR matrix against a scripted upstream and fix two runner-only e2e failures

* test: count only OCR calls in the OCR matrix recorder

* test: pin OCR parse bodies and billed cost, group new e2e cases into classes

* ci: restore the python image anchor dropped in the main sync

---------

Co-authored-by: yuneng <yuneng@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-10-11 00:17:21 -07:00

93 lines
4.1 KiB
Python

import json
import os
from collections.abc import Iterator
from dataclasses import dataclass
from pathlib import Path
from typing import Final
import pytest
from integration._support.client import Gateway, gateway_from_environment
from integration._support.forward_proxy import tunnelling_forward_proxy
from integration._support.process import owned_proxy
from integration._support.tls import server_context, write_self_signed_cert
from integration._support.wire import Reply, Request, Wire, wire_server
_HOSTS: Final = ("api.assemblyai.com", "api.eu.assemblyai.com")
_PROVIDER_KEY: Final = "synthetic-assemblyai-key"
_TRANSCRIPT_REQUEST: Final = {"audio_url": "https://assembly.ai/wildfires.mp3", "speech_models": ["universal-2"]}
_QUEUED: Final = {"id": "transcript-1", "status": "queued", "audio_url": "https://assembly.ai/wildfires.mp3"}
_COMPLETED: Final = {"id": "transcript-1", "status": "completed", "text": "Smoke from wildfires in Canada."}
_SCRUBBED: Final = frozenset({"HTTPS_PROXY", "HTTP_PROXY", "ALL_PROXY", "NO_PROXY", "SSL_CERT_FILE", "ASSEMBLYAI_API_KEY"})
@dataclass(frozen=True, slots=True)
class _AssemblyAI:
wire: Wire
proxy: Gateway
def _respond(request: Request) -> Reply:
if (request.method, request.target) == ("POST", "/v2/transcript"):
return Reply(body=json.dumps(_QUEUED).encode())
if (request.method, request.target) == ("GET", "/v2/transcript/transcript-1"):
return Reply(body=json.dumps(_COMPLETED).encode())
return Reply(status=404, body=b'{"error": "unexpected AssemblyAI request"}')
@pytest.fixture(scope="module")
def assemblyai(tmp_path_factory: pytest.TempPathFactory) -> Iterator[_AssemblyAI]:
directory: Final = tmp_path_factory.mktemp("assemblyai")
cert, key = write_self_signed_cert(directory, _HOSTS)
with (
wire_server(_respond, tls=server_context(cert, key)) as wire,
tunnelling_forward_proxy(int(wire.url.rsplit(":", 1)[1])) as tunnel,
gateway_from_environment() as rig,
owned_proxy(
rig,
directory,
{"HTTPS_PROXY": tunnel.url, "NO_PROXY": "127.0.0.1,localhost", "SSL_CERT_FILE": str(cert)},
remove_environment=tuple(name for name in os.environ if name.upper() in _SCRUBBED),
) as proxy,
proxy.scenario() as scenario,
):
scenario.model(
model="assemblyai/*",
custom_llm_provider="assemblyai",
api_key=_PROVIDER_KEY,
api_base="https://api.assemblyai.com",
use_in_pass_through=True,
)
yield _AssemblyAI(wire, proxy)
def test_a_db_assemblyai_deployment_serves_the_transcript_routes(assemblyai: _AssemblyAI) -> None:
proxy: Final = assemblyai.proxy
with proxy.scenario() as scenario:
key: Final = scenario.key()
assemblyai.wire.drain()
created: Final = proxy.request("POST", "/assemblyai/v2/transcript", _TRANSCRIPT_REQUEST, key=key)
polled: Final = proxy.request("GET", "/assemblyai/v2/transcript/transcript-1", key=key)
sent: Final = assemblyai.wire.drain()
assert (created.status_code, created.json()) == (200, _QUEUED), created.text
assert (polled.status_code, polled.json()) == (200, _COMPLETED), polled.text
posts: Final = tuple(request for request in sent if request.method == "POST")
assert [request.target for request in posts] == ["/v2/transcript"]
assert json.loads(posts[0].body) == _TRANSCRIPT_REQUEST
assert {(request.method, request.target) for request in sent} == {
("POST", "/v2/transcript"),
("GET", "/v2/transcript/transcript-1"),
}
assert posts[0].headers["authorization"] == _PROVIDER_KEY
@pytest.mark.parametrize("route", ["/assemblyai/v2/transcript", "/eu.assemblyai/v2/transcript"])
def test_an_invalid_proxy_key_is_rejected_before_assemblyai_is_called(assemblyai: _AssemblyAI, route: str) -> None:
assemblyai.wire.drain()
response: Final = assemblyai.proxy.request("POST", route, _TRANSCRIPT_REQUEST, key="sk-12222")
assert response.status_code == 401, response.text
assert [request for request in assemblyai.wire.drain() if request.method == "POST"] == []