""" Tests for Responses API router. """ import json import pytest from fastapi.testclient import TestClient from httpx import AsyncClient @pytest.mark.unit def test_create_response_non_streaming(client: TestClient) -> None: """Test non-streaming response creation.""" request_data = { "model": "lorem-tester", "input": [{"role": "user", "content": "Hello, how are you?"}], "stream": False, } response = client.post("/v1/responses", json=request_data) assert response.status_code == 200 data = response.json() # Verify response structure assert "id" in data assert data["object"] == "response" assert "created_at" in data assert data["model"] == "lorem-tester" assert data["status"] == "completed" assert "output" in data assert "usage" in data # Verify output array assert isinstance(data["output"], list) assert len(data["output"]) >= 1 # At least message item # Verify usage assert data["usage"]["input_tokens"] > 0 assert data["usage"]["output_tokens"] > 0 assert data["usage"]["total_tokens"] > 0 @pytest.mark.unit def test_create_response_with_reasoning(client: TestClient) -> None: """Test response with reasoning enabled.""" request_data = { "model": "lorem-tester", "input": [{"role": "user", "content": "Explain something"}], "reasoning": {"effort": "medium", "summary": "auto"}, "stream": False, } response = client.post("/v1/responses", json=request_data) assert response.status_code == 200 data = response.json() # Should have reasoning item and message item assert len(data["output"]) >= 2 # Check for reasoning item reasoning_items = [item for item in data["output"] if item["type"] == "reasoning"] assert len(reasoning_items) >= 1 reasoning_item = reasoning_items[0] assert "summary" in reasoning_item assert isinstance(reasoning_item["summary"], list) assert len(reasoning_item["summary"]) > 0 # Check for message item message_items = [item for item in data["output"] if item["type"] == "message"] assert len(message_items) >= 1 # Verify reasoning tokens counted assert data["usage"]["reasoning_tokens"] > 0 @pytest.mark.unit def test_create_response_with_tools(client: TestClient) -> None: """Test response with tools available.""" request_data = { "model": "lorem-tester", "input": [{"role": "user", "content": "Use a tool to help"}], "tools": [ {"name": "search_knowledge", "description": "Search knowledge base", "parameters": {}} ], "stream": False, } response = client.post("/v1/responses", json=request_data) assert response.status_code == 200 data = response.json() # May or may not have function_call items (randomized in lorem-tester) # But should always have message item message_items = [item for item in data["output"] if item["type"] == "message"] assert len(message_items) >= 1 @pytest.mark.unit def test_create_response_invalid_model(client: TestClient) -> None: """Test response with non-existent model.""" request_data = { "model": "nonexistent-model", "input": [{"role": "user", "content": "Hello"}], "stream": False, } response = client.post("/v1/responses", json=request_data) assert response.status_code == 404 data = response.json() assert "detail" in data @pytest.mark.unit def test_create_response_validation_error(client: TestClient) -> None: """Test response with invalid request data.""" # Missing required 'model' field request_data = {"input": [{"role": "user", "content": "Hello"}]} response = client.post("/v1/responses", json=request_data) assert response.status_code == 422 @pytest.mark.unit @pytest.mark.asyncio async def test_create_response_streaming(async_client: AsyncClient) -> None: """Test streaming response creation.""" request_data = { "model": "lorem-tester", "input": [{"role": "user", "content": "Hello"}], "stream": True, } async with async_client.stream( "POST", "/v1/responses", json=request_data, timeout=20.0, ) as response: assert response.status_code == 200 assert response.headers["content-type"] == "text/event-stream; charset=utf-8" events = [] async for line in response.aiter_lines(): if not line.strip(): continue # Parse SSE format: "event: event_type" and "data: {json}" if line.startswith("event: "): event_type = line[7:].strip() elif line.startswith("data: "): data_str = line[6:].strip() try: data = json.loads(data_str) events.append({"event": event_type, "data": data}) except json.JSONDecodeError: pass # Skip malformed data # Should have multiple events assert len(events) > 0 # Should have response.done event as last event assert events[-1]["event"] == "response.done" # response.done should have complete response done_data = events[-1]["data"] assert "response" in done_data assert done_data["response"]["status"] == "completed" @pytest.mark.unit @pytest.mark.asyncio async def test_create_response_streaming_with_reasoning(async_client: AsyncClient) -> None: """Test streaming response with reasoning.""" request_data = { "model": "lorem-tester", "input": [{"role": "user", "content": "Explain"}], "reasoning": {"effort": "medium", "summary": "auto"}, "stream": True, } async with async_client.stream( "POST", "/v1/responses", json=request_data, timeout=20.0, ) as response: assert response.status_code == 200 events = [] event_type = None async for line in response.aiter_lines(): if not line.strip(): continue if line.startswith("event: "): event_type = line[7:].strip() elif line.startswith("data: "): data_str = line[6:].strip() try: data = json.loads(data_str) if event_type: events.append({"event": event_type, "data": data}) except json.JSONDecodeError: pass # Should have reasoning events reasoning_events = [ e for e in events if e["event"] == "response.reasoning_summary_text.delta" ] assert len(reasoning_events) > 0 # Should have output text events output_events = [e for e in events if e["event"] == "response.output_text.delta"] assert len(output_events) > 0 @pytest.mark.unit def test_create_response_temperature_parameter(client: TestClient) -> None: """Test temperature parameter handling.""" request_data = { "model": "lorem-tester", "input": [{"role": "user", "content": "Test"}], "temperature": 0.5, "stream": False, } response = client.post("/v1/responses", json=request_data) assert response.status_code == 200 @pytest.mark.unit def test_create_response_max_tokens_parameter(client: TestClient) -> None: """Test max_output_tokens parameter handling.""" request_data = { "model": "lorem-tester", "input": [{"role": "user", "content": "Test"}], "max_output_tokens": 100, "stream": False, } response = client.post("/v1/responses", json=request_data) assert response.status_code == 200