| from __future__ import annotations |
|
|
| import httpx |
| import pytest |
| import respx |
|
|
| from app.llm.base import ProviderError, RateLimitError |
| from app.llm.groq import GroqProvider |
|
|
| URL = "https://api.groq.com/openai/v1/chat/completions" |
|
|
|
|
| def _provider() -> GroqProvider: |
| return GroqProvider(api_key="k") |
|
|
|
|
| @respx.mock |
| async def test_groq_returns_content(): |
| respx.post(URL).mock( |
| return_value=httpx.Response( |
| 200, |
| json={"choices": [{"finish_reason": "stop", "message": {"content": "hola"}}]}, |
| ) |
| ) |
| r = await _provider().chat(messages=[{"role": "user", "content": "x"}], tools=[], model="m") |
| assert r.content == "hola" |
| assert r.tool_calls == [] |
|
|
|
|
| @respx.mock |
| async def test_groq_parses_tool_calls(): |
| respx.post(URL).mock( |
| return_value=httpx.Response( |
| 200, |
| json={ |
| "choices": [ |
| { |
| "finish_reason": "tool_calls", |
| "message": { |
| "content": None, |
| "tool_calls": [ |
| { |
| "id": "c1", |
| "type": "function", |
| "function": { |
| "name": "lookup_order", |
| "arguments": '{"email":"a@b.c"}', |
| }, |
| } |
| ], |
| }, |
| } |
| ] |
| }, |
| ) |
| ) |
| r = await _provider().chat( |
| messages=[{"role": "user", "content": "x"}], tools=[], model="llama-3.1-8b-instant" |
| ) |
| assert r.tool_calls[0].name == "lookup_order" |
| assert r.tool_calls[0].arguments == {"email": "a@b.c"} |
|
|
|
|
| @respx.mock |
| async def test_groq_captures_token_usage(): |
| respx.post(URL).mock( |
| return_value=httpx.Response( |
| 200, |
| json={ |
| "choices": [{"finish_reason": "stop", "message": {"content": "ok"}}], |
| "usage": {"prompt_tokens": 120, "completion_tokens": 30, "total_tokens": 150}, |
| }, |
| ) |
| ) |
| r = await _provider().chat(messages=[{"role": "user", "content": "x"}], tools=[], model="m") |
| assert r.usage["total_tokens"] == 150 |
| assert r.usage["prompt_tokens"] == 120 |
|
|
|
|
| @respx.mock |
| async def test_groq_429_raises_ratelimit(): |
| respx.post(URL).mock(return_value=httpx.Response(429, text="slow down")) |
| with pytest.raises(RateLimitError): |
| await _provider().chat(messages=[{"role": "user", "content": "x"}], tools=[], model="m") |
|
|
|
|
| @respx.mock |
| async def test_groq_500_raises_provider_error(): |
| respx.post(URL).mock(return_value=httpx.Response(500, text="boom")) |
| with pytest.raises(ProviderError): |
| await _provider().chat(messages=[{"role": "user", "content": "x"}], tools=[], model="m") |
|
|
|
|
| @respx.mock |
| async def test_groq_malformed_200_raises_provider_error_for_failover(): |
| |
| respx.post(URL).mock(return_value=httpx.Response(200, json={"unexpected": True})) |
| with pytest.raises(ProviderError): |
| await _provider().chat(messages=[{"role": "user", "content": "x"}], tools=[], model="m") |
|
|