openweb / tests /test_providers.py
Leon4gr45's picture
Upload folder using huggingface_hub
33962ff verified
Raw History Blame Contribute Delete
4.73 kB
import httpx
import pytest
from config.schema import Provider
from vrag.providers import Adapter, ProviderFailure
@pytest.fixture
def remote(monkeypatch):
monkeypatch.setenv('TEST_URL', 'https://inference.example/v1')
monkeypatch.setenv('VM_ALLOWED_HOSTS', 'inference.example')
monkeypatch.setattr('socket.getaddrinfo', lambda *a, **k: [(2, 1, 6, '', ('8.8.8.8', 443))])
def provider(adapter='openai-chat-vision', caps=None):
return Provider(adapter=adapter, base_url_env='TEST_URL', model='test', capabilities=caps or ['image_understanding'], trust='approved_sensitive')
@pytest.mark.asyncio
async def test_chat_never_invents_boxes(remote):
async def mock(req):
assert req.url.path == '/v1/chat/completions'
return httpx.Response(200, json={'choices': [{'message': {'content': 'a hand at [0,0,1,1]'}}]})
out = await Adapter(httpx.MockTransport(mock)).call('vlm', provider(), {'image': 'data:image/jpeg;base64,eA=='})
assert out.detections == []
assert out.decisions[0].source == 'vlm'
@pytest.mark.asyncio
async def test_embeddings(remote):
out = await Adapter(httpx.MockTransport(lambda r: httpx.Response(200, json={'data': [{'embedding': [1,2,3]}]}))).call('emb', provider('openai-embeddings', ['text_embeddings']), {'text': 'hello'})
assert out.embedding == [1,2,3]
@pytest.mark.asyncio
async def test_destination_denied(remote, monkeypatch):
monkeypatch.setenv('TEST_URL', 'http://169.254.169.254')
with pytest.raises(ProviderFailure, match='destination_denied'):
await Adapter().call('x', provider(), {})
@pytest.mark.asyncio
async def test_no_secret_in_errors(remote):
with pytest.raises(ProviderFailure, match='provider_error'):
await Adapter(httpx.MockTransport(lambda r: httpx.Response(401, text='secret'))).call('x', provider(), {})
@pytest.mark.asyncio
async def test_remote_whisper_multipart(remote):
def mock(req):
assert req.url.path == '/v1/audio/transcriptions'
assert b'name="file"' in req.content and b'speech.webm' in req.content
return httpx.Response(200,json={'text':'hello'})
out=await Adapter(httpx.MockTransport(mock)).call('stt',provider('openai-transcription',['transcription']),{'audio':b'webm'})
assert out.text=='hello'
@pytest.mark.asyncio
async def test_detector_provenance_and_frame_validation(remote):
payload={'image':'x','frame_id':1,'camera_id':'camera2','width':640,'height':480}
data={'frame_id':1,'camera_id':'camera2','width':640,'height':480,'detections':[{'label':'hand','bbox_xyxy':[.1,.2,.4,.6],'confidence':.9}]}
a=Adapter(httpx.MockTransport(lambda r:httpx.Response(200,json=data)))
p=provider('detection-http',['object_detection','bounding_boxes'])
out=await a.call('detector',p,payload)
assert out.detections[0].source=='detector' and out.detections[0].object_id is None
data['frame_id']=2
with pytest.raises(ProviderFailure,match='invalid_output'):
await a.call('detector',p,payload)
@pytest.mark.asyncio
async def test_private_ip_and_redirect_are_denied(remote,monkeypatch):
monkeypatch.setattr('socket.getaddrinfo',lambda *a,**k:[(2,1,6,'',('127.0.0.1',443))])
with pytest.raises(ProviderFailure,match='destination_denied'):
await Adapter().call('x',provider(),{})
monkeypatch.setattr('socket.getaddrinfo',lambda *a,**k:[(2,1,6,'',('8.8.8.8',443))])
calls=[]
def mock(req):
calls.append(req.url)
return httpx.Response(302,headers={'location':'https://evil.example'})
with pytest.raises(ProviderFailure):
await Adapter(httpx.MockTransport(mock)).call('x',provider(),{})
assert len(calls)==1
@pytest.mark.asyncio
async def test_timeout_circuit_breaker_and_concurrency(remote):
import asyncio
p=provider().model_copy(update={'timeout_ms':100,'concurrency':1})
async def slow(req):
await asyncio.sleep(1)
return httpx.Response(200,json={})
a=Adapter(httpx.MockTransport(slow))
first=asyncio.create_task(a.call('slow',p,{}))
await asyncio.sleep(.01)
with pytest.raises(ProviderFailure,match='busy'):
await a.call('slow',p,{})
with pytest.raises(ProviderFailure,match='timeout'):
await first
for _ in range(2):
with pytest.raises(ProviderFailure,match='timeout'):
await a.call('slow',p,{})
with pytest.raises(ProviderFailure,match='circuit_open'):
await a.call('slow',p,{})
@pytest.mark.asyncio
async def test_sensitive_destination_checked_on_each_call(remote):
p=provider().model_copy(update={'trust':'public_only'})
with pytest.raises(ProviderFailure,match='destination_denied'):
await Adapter().call('public',p,{'image':'sensitive'})