Spaces:
Running
Running
| """Tests for the headless ``api_chat`` endpoint exposed via ``gr.api``.""" | |
| from __future__ import annotations | |
| import pytest | |
| import chat as chat_mod | |
| def _fake_stream(yields): | |
| """Build a fake ``stream_response`` that yields the given frames. | |
| Each frame is ``(ops, assistant_content, reasoning_content, | |
| tool_calls, request_id)`` — the same tuple shape ``core.chat | |
| .stream_response`` produces. | |
| """ | |
| def _gen(_kwargs): | |
| for frame in yields: | |
| yield frame | |
| return _gen | |
| def test_api_chat_yields_cumulative_then_terminal_history(monkeypatch): | |
| frames = [ | |
| ([{"type": "content_delta", "delta": "Hel"}], "Hel", "", [], "rid"), | |
| ([{"type": "content_delta", "delta": "lo"}], "Hello", "", [], "rid"), | |
| ] | |
| monkeypatch.setattr(chat_mod, "stream_response", _fake_stream(frames)) | |
| out = list(chat_mod.api_chat(message="hi", history=[])) | |
| # 2 streaming yields + 1 terminal frame (reflecting persisted history). | |
| assert len(out) == 3 | |
| assert out[0][0] == "Hel" | |
| assert out[1][0] == "Hello" | |
| final_content, final_reasoning, final_tool_calls, final_history = out[-1] | |
| assert final_content == "Hello" | |
| assert final_reasoning == "" | |
| assert final_tool_calls == [] | |
| assert final_history[-2] == {"role": "user", "content": "hi"} | |
| assert final_history[-1]["role"] == "assistant" | |
| assert final_history[-1]["content"] == "Hello" | |
| def test_api_chat_round_trips_history(monkeypatch): | |
| frames = [( | |
| [{"type": "content_delta", "delta": "you said hi"}], | |
| "you said hi", "", [], "rid", | |
| )] | |
| monkeypatch.setattr(chat_mod, "stream_response", _fake_stream(frames)) | |
| history = [ | |
| {"role": "user", "content": "hi"}, | |
| {"role": "assistant", "content": "hello"}, | |
| ] | |
| out = list(chat_mod.api_chat(message="recap", history=history)) | |
| final_history = out[-1][3] | |
| assert [m["role"] for m in final_history] == [ | |
| "user", "assistant", "user", "assistant", | |
| ] | |
| assert final_history[2] == {"role": "user", "content": "recap"} | |
| # Caller's list is never mutated. | |
| assert len(history) == 2 | |
| def test_api_chat_surfaces_tool_calls(monkeypatch): | |
| tc = { | |
| "id": "call_1", | |
| "type": "function", | |
| "function": {"name": "ping", "arguments": "{}"}, | |
| } | |
| frames = [ | |
| ([{"type": "tool_calls", "tool_calls": [tc]}], "", "", [tc], "rid"), | |
| ] | |
| monkeypatch.setattr(chat_mod, "stream_response", _fake_stream(frames)) | |
| out = list(chat_mod.api_chat(message="call it", history=[])) | |
| final_content, _, final_tool_calls, final_history = out[-1] | |
| assert final_content == "" | |
| assert final_tool_calls == [tc] | |
| # The assistant turn was persisted with tool_calls attached. | |
| assert final_history[-1]["tool_calls"] == [tc] | |
| def test_api_chat_propagates_reasoning(monkeypatch): | |
| frames = [( | |
| [{"type": "reasoning_delta", "delta": "let me think"}], | |
| "answer", "let me think", [], "rid", | |
| )] | |
| monkeypatch.setattr(chat_mod, "stream_response", _fake_stream(frames)) | |
| out = list(chat_mod.api_chat(message="q", history=[])) | |
| _, reasoning, _, history = out[-1] | |
| assert reasoning == "let me think" | |
| assert history[-1].get("reasoning_content") == "let me think" | |
| def test_api_chat_rejects_empty_message(): | |
| with pytest.raises(ValueError, match="non-empty"): | |
| list(chat_mod.api_chat(message=" ")) | |
| def test_api_chat_rejects_invalid_functions_json(): | |
| with pytest.raises(ValueError, match="not valid JSON"): | |
| list(chat_mod.api_chat(message="hi", functions_json_str="{not json")) | |
| def test_api_chat_emits_terminal_frame_when_stream_yields_nothing(monkeypatch): | |
| monkeypatch.setattr(chat_mod, "stream_response", _fake_stream([])) | |
| out = list(chat_mod.api_chat(message="hi", history=[])) | |
| assert len(out) == 1 | |
| content, reasoning, tool_calls, history = out[0] | |
| assert content == "" | |
| assert reasoning == "" | |
| assert tool_calls == [] | |
| # The user turn is preserved; the assistant turn is the empty | |
| # placeholder finalize_response persists when nothing streamed. | |
| assert history[0] == {"role": "user", "content": "hi"} | |
| assert history[-1]["role"] == "assistant" | |