-
Notifications
You must be signed in to change notification settings - Fork 4.7k
Expand file tree
/
Copy pathtest_response_input_builder.py
More file actions
151 lines (109 loc) · 5.02 KB
/
test_response_input_builder.py
File metadata and controls
151 lines (109 loc) · 5.02 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
from __future__ import annotations
from typing import Any
from unittest.mock import MagicMock
import pytest
from openai.lib import validate_response_input, get_response_input_items
def _make_reasoning_item(item_id: str = "rs_001") -> Any:
item = MagicMock()
item.type = "reasoning"
item.id = item_id
item.model_dump.return_value = {"type": "reasoning", "id": item_id, "summary": []}
return item
def _make_message_item(item_id: str = "msg_001") -> Any:
item = MagicMock()
item.type = "message"
item.role = "assistant"
item.id = item_id
item.model_dump.return_value = {"type": "message", "role": "assistant", "id": item_id, "content": []}
return item
def _make_response(output: list[Any]) -> Any:
response = MagicMock()
response.output = output
return response
# ---------------------------------------------------------------------------
# get_response_input_items
# ---------------------------------------------------------------------------
def test_get_response_input_items_reasoning_and_message() -> None:
"""Returns both reasoning and message items in order."""
reasoning = _make_reasoning_item("rs_1")
message = _make_message_item("msg_1")
response = _make_response([reasoning, message])
result = get_response_input_items(response)
assert len(result) == 2
assert result[0] == {"type": "reasoning", "id": "rs_1", "summary": []}
assert result[1] == {"type": "message", "role": "assistant", "id": "msg_1", "content": []}
def test_get_response_input_items_message_only() -> None:
"""Returns message items when there are no reasoning items."""
message = _make_message_item("msg_2")
response = _make_response([message])
result = get_response_input_items(response)
assert len(result) == 1
assert result[0] == {"type": "message", "role": "assistant", "id": "msg_2", "content": []}
def test_get_response_input_items_empty() -> None:
"""Returns empty list for empty output."""
response = _make_response([])
result = get_response_input_items(response)
assert result == []
# ---------------------------------------------------------------------------
# validate_response_input
# ---------------------------------------------------------------------------
def test_validate_passes_for_consecutive_pair_dicts() -> None:
"""No error when reasoning immediately precedes assistant message (dict form)."""
items = [
{"type": "reasoning", "id": "rs_1", "summary": []},
{"type": "message", "role": "assistant", "id": "msg_1", "content": []},
]
validate_response_input(items) # should not raise
def test_validate_raises_for_orphaned_message_dicts() -> None:
"""ValueError raised when assistant message is not preceded by reasoning (dict form)."""
items = [
{"type": "message", "role": "user", "content": "hello"},
{"type": "reasoning", "id": "rs_1", "summary": []},
{"type": "message", "role": "user", "content": "follow-up"},
{"type": "message", "role": "assistant", "id": "msg_orphan", "content": []},
]
with pytest.raises(ValueError, match="msg_orphan"):
validate_response_input(items)
def test_validate_raises_error_describes_constraint() -> None:
"""Error message explains the reasoning+message pairing constraint."""
items = [
{"type": "reasoning", "id": "rs_1", "summary": []},
{"type": "message", "role": "user", "content": "hi"},
{"type": "message", "role": "assistant", "id": "msg_bad", "content": []},
]
with pytest.raises(ValueError, match="consecutive pair"):
validate_response_input(items)
def test_validate_passes_for_user_only_messages() -> None:
"""No error when there are only user messages and no reasoning items."""
items = [
{"type": "message", "role": "user", "content": "hello"},
{"type": "message", "role": "user", "content": "how are you"},
]
validate_response_input(items) # should not raise
def test_validate_passes_for_empty_input() -> None:
"""No error for empty input list."""
validate_response_input([]) # should not raise
def test_validate_passes_with_object_form() -> None:
"""Works with object-form items (not dicts), consecutive pair."""
reasoning = MagicMock()
reasoning.type = "reasoning"
reasoning.id = "rs_obj"
message = MagicMock()
message.type = "message"
message.role = "assistant"
message.id = "msg_obj"
validate_response_input([reasoning, message]) # should not raise
def test_validate_raises_with_object_form_orphaned() -> None:
"""Raises ValueError with object-form items when message is orphaned."""
reasoning = MagicMock()
reasoning.type = "reasoning"
reasoning.id = "rs_obj"
user_msg = MagicMock()
user_msg.type = "message"
user_msg.role = "user"
asst_msg = MagicMock()
asst_msg.type = "message"
asst_msg.role = "assistant"
asst_msg.id = "msg_orphan_obj"
with pytest.raises(ValueError, match="msg_orphan_obj"):
validate_response_input([reasoning, user_msg, asst_msg])