Skip to content

Commit 749face

Browse files
Remove references to responses API as we don't use that anymore; update unit test accordingly
1 parent a329380 commit 749face

File tree

2 files changed

+91
-116
lines changed

2 files changed

+91
-116
lines changed

formfyxer/passive_voice_detection.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""Passive voice detection utilities backed by OpenAI's Responses API."""
1+
"""Passive voice detection utilities backed by OpenAI's ChatCompletion API."""
22

33
from __future__ import annotations
44

@@ -160,11 +160,7 @@ def _normalize_input(text: Union[str, Sequence[str]]) -> List[str]:
160160

161161

162162
def _extract_text_from_response(response) -> str:
163-
"""Extract text content from OpenAI Responses API response object.
164-
165-
This function handles different response formats from the OpenAI Responses API,
166-
trying multiple approaches to extract the text content. It checks for various
167-
attributes and nested structures that may contain the response text.
163+
"""Extract text content from OpenAI ChatCompletion API response object.
168164
169165
Args:
170166
response: OpenAI API response object with potentially nested content
@@ -234,7 +230,7 @@ def detect_passive_voice_segments(
234230
235231
Note:
236232
This implementation uses a single prompt per sentence to classify rather
237-
than the responses API after testing and finding better performance with this
233+
than the ChatCompletion API after testing and finding better performance with this
238234
simple approach.
239235
"""
240236

formfyxer/tests/test_passive_voice_detection.py

Lines changed: 88 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
"""Unit tests for passive voice detection with mocked OpenAI responses."""
22

3-
import json
43
import unittest
54
from unittest.mock import Mock, patch, MagicMock
65
from pathlib import Path
@@ -15,30 +14,29 @@ def setUp(self):
1514
"""Set up test fixtures."""
1615
self.mock_client = Mock()
1716

18-
def _create_mock_response(self, results_data):
19-
"""Create a mock OpenAI response with the given results data.
17+
def _create_mock_chat_response(self, content):
18+
"""Create a mock OpenAI chat completion response.
2019
2120
Args:
22-
results_data: List of dicts with 'sentence' and 'fragments' keys
21+
content: The content string ("passive" or "active")
2322
2423
Returns:
25-
Mock response object that mimics OpenAI's response structure
24+
Mock response object that mimics OpenAI's chat completion structure
2625
"""
2726
mock_response = Mock()
28-
mock_response.output_text = json.dumps({"results": results_data})
27+
mock_choice = Mock()
28+
mock_message = Mock()
29+
mock_message.content = content
30+
mock_choice.message = mock_message
31+
mock_response.choices = [mock_choice]
2932
return mock_response
3033

3134
def test_detect_passive_voice_basic_passive(self):
3235
"""Test detection of basic passive voice constructions."""
3336
# Mock response for passive voice sentence
34-
mock_response = self._create_mock_response([
35-
{
36-
"sentence": "The ball was thrown by John.",
37-
"fragments": ["was thrown"]
38-
}
39-
])
37+
mock_response = self._create_mock_chat_response("passive")
4038

41-
self.mock_client.responses.create.return_value = mock_response
39+
self.mock_client.chat.completions.create.return_value = mock_response
4240

4341
result = detect_passive_voice_segments(
4442
"The ball was thrown by John.",
@@ -48,24 +46,19 @@ def test_detect_passive_voice_basic_passive(self):
4846
self.assertEqual(len(result), 1)
4947
sentence, fragments = result[0]
5048
self.assertEqual(sentence, "The ball was thrown by John.")
51-
self.assertEqual(fragments, ["was thrown"])
49+
self.assertEqual(fragments, ["The ball was thrown by John."]) # Now returns full sentence
5250

5351
# Verify the API was called correctly
54-
self.mock_client.responses.create.assert_called_once()
55-
call_args = self.mock_client.responses.create.call_args
52+
self.mock_client.chat.completions.create.assert_called_once()
53+
call_args = self.mock_client.chat.completions.create.call_args
5654
self.assertEqual(call_args[1]['model'], 'gpt-5-nano')
5755

5856
def test_detect_passive_voice_active_sentence(self):
5957
"""Test that active voice sentences return empty fragments."""
6058
# Mock response for active voice sentence
61-
mock_response = self._create_mock_response([
62-
{
63-
"sentence": "John threw the ball.",
64-
"fragments": []
65-
}
66-
])
59+
mock_response = self._create_mock_chat_response("active")
6760

68-
self.mock_client.responses.create.return_value = mock_response
61+
self.mock_client.chat.completions.create.return_value = mock_response
6962

7063
result = detect_passive_voice_segments(
7164
"John threw the ball.",
@@ -79,23 +72,14 @@ def test_detect_passive_voice_active_sentence(self):
7972

8073
def test_detect_passive_voice_multiple_sentences(self):
8174
"""Test detection with multiple sentences."""
82-
# Mock response for multiple sentences
83-
mock_response = self._create_mock_response([
84-
{
85-
"sentence": "The report was completed yesterday.",
86-
"fragments": ["was completed"]
87-
},
88-
{
89-
"sentence": "The team finished the project.",
90-
"fragments": []
91-
},
92-
{
93-
"sentence": "The documents were reviewed by the manager.",
94-
"fragments": ["were reviewed"]
95-
}
96-
])
97-
98-
self.mock_client.responses.create.return_value = mock_response
75+
# Mock responses for each sentence call
76+
responses = [
77+
self._create_mock_chat_response("passive"), # "The report was completed yesterday."
78+
self._create_mock_chat_response("active"), # "The team finished the project."
79+
self._create_mock_chat_response("passive") # "The documents were reviewed by the manager."
80+
]
81+
82+
self.mock_client.chat.completions.create.side_effect = responses
9983

10084
text = "The report was completed yesterday. The team finished the project. The documents were reviewed by the manager."
10185
result = detect_passive_voice_segments(text, openai_client=self.mock_client)
@@ -105,7 +89,7 @@ def test_detect_passive_voice_multiple_sentences(self):
10589
# Check first sentence (passive)
10690
sentence1, fragments1 = result[0]
10791
self.assertEqual(sentence1, "The report was completed yesterday.")
108-
self.assertEqual(fragments1, ["was completed"])
92+
self.assertEqual(fragments1, ["The report was completed yesterday."])
10993

11094
# Check second sentence (active)
11195
sentence2, fragments2 = result[1]
@@ -115,27 +99,18 @@ def test_detect_passive_voice_multiple_sentences(self):
11599
# Check third sentence (passive)
116100
sentence3, fragments3 = result[2]
117101
self.assertEqual(sentence3, "The documents were reviewed by the manager.")
118-
self.assertEqual(fragments3, ["were reviewed"])
102+
self.assertEqual(fragments3, ["The documents were reviewed by the manager."])
119103

120104
def test_detect_passive_voice_edge_cases(self):
121105
"""Test edge cases that should not be flagged as passive."""
122-
# Mock response for edge cases that are not passive
123-
mock_response = self._create_mock_response([
124-
{
125-
"sentence": "Business is very well known career field.",
126-
"fragments": []
127-
},
128-
{
129-
"sentence": "I am immersing myself in my culture.",
130-
"fragments": []
131-
},
132-
{
133-
"sentence": "The president was satisfied with the results.",
134-
"fragments": []
135-
}
136-
])
137-
138-
self.mock_client.responses.create.return_value = mock_response
106+
# Mock responses for edge cases that are not passive
107+
responses = [
108+
self._create_mock_chat_response("active"),
109+
self._create_mock_chat_response("active"),
110+
self._create_mock_chat_response("active")
111+
]
112+
113+
self.mock_client.chat.completions.create.side_effect = responses
139114

140115
sentences = [
141116
"Business is very well known career field.",
@@ -156,39 +131,32 @@ def test_detect_passive_voice_sequence_input(self):
156131
"She baked the cake this morning."
157132
]
158133

159-
mock_response = self._create_mock_response([
160-
{
161-
"sentence": "The cake was baked this morning.",
162-
"fragments": ["was baked"]
163-
},
164-
{
165-
"sentence": "She baked the cake this morning.",
166-
"fragments": []
167-
}
168-
])
134+
responses = [
135+
self._create_mock_chat_response("passive"),
136+
self._create_mock_chat_response("active")
137+
]
169138

170-
self.mock_client.responses.create.return_value = mock_response
139+
self.mock_client.chat.completions.create.side_effect = responses
171140

172141
result = detect_passive_voice_segments(sentences, openai_client=self.mock_client)
173142

174143
self.assertEqual(len(result), 2)
175-
self.assertEqual(result[0][1], ["was baked"]) # First should have fragments
144+
self.assertEqual(result[0][1], ["The cake was baked this morning."]) # First should have full sentence
176145
self.assertEqual(result[1][1], []) # Second should have no fragments
177146

178-
def test_detect_passive_voice_invalid_json_response(self):
179-
"""Test handling of invalid JSON responses."""
180-
# Mock response with invalid JSON
181-
mock_response = Mock()
182-
mock_response.output_text = "Invalid JSON response"
147+
def test_detect_passive_voice_invalid_response(self):
148+
"""Test handling of invalid responses."""
149+
# Mock response with unrecognized classification
150+
mock_response = self._create_mock_chat_response("invalid_classification")
183151

184-
self.mock_client.responses.create.return_value = mock_response
152+
self.mock_client.chat.completions.create.return_value = mock_response
185153

186154
result = detect_passive_voice_segments(
187155
"The ball was thrown.",
188156
openai_client=self.mock_client
189157
)
190158

191-
# Should return empty fragments for all sentences when JSON is invalid
159+
# Should return empty fragments for all sentences when response is invalid
192160
self.assertEqual(len(result), 1)
193161
sentence, fragments = result[0]
194162
self.assertEqual(sentence, "The ball was thrown.")
@@ -197,12 +165,9 @@ def test_detect_passive_voice_invalid_json_response(self):
197165
def test_detect_passive_voice_empty_response(self):
198166
"""Test handling of empty responses."""
199167
# Mock response with empty content
200-
mock_response = Mock()
201-
mock_response.output_text = ""
202-
mock_response.output = []
203-
mock_response.data = []
168+
mock_response = self._create_mock_chat_response("")
204169

205-
self.mock_client.responses.create.return_value = mock_response
170+
self.mock_client.chat.completions.create.return_value = mock_response
206171

207172
result = detect_passive_voice_segments(
208173
"The ball was thrown.",
@@ -215,23 +180,41 @@ def test_detect_passive_voice_empty_response(self):
215180
self.assertEqual(sentence, "The ball was thrown.")
216181
self.assertEqual(fragments, [])
217182

218-
def test_detect_passive_voice_partial_json_response(self):
219-
"""Test handling of partial JSON in response with extra text."""
220-
# Mock response with JSON embedded in extra text
221-
mock_response = Mock()
222-
mock_response.output_text = 'Here is the analysis: {"results": [{"sentence": "The door was opened.", "fragments": ["was opened"]}]} Hope this helps!'
183+
def test_detect_passive_voice_response_with_extra_text(self):
184+
"""Test handling of response with extra text around the classification."""
185+
# Mock response with extra text around the main classification
186+
mock_response = self._create_mock_chat_response("The sentence is: passive")
187+
188+
self.mock_client.chat.completions.create.return_value = mock_response
189+
190+
result = detect_passive_voice_segments(
191+
"The door was opened.",
192+
openai_client=self.mock_client
193+
)
194+
195+
# Current implementation requires exact match, so extra text causes it to be treated as active
196+
self.assertEqual(len(result), 1)
197+
sentence, fragments = result[0]
198+
self.assertEqual(sentence, "The door was opened.")
199+
self.assertEqual(fragments, []) # Should be empty due to inexact match
223200

224-
self.mock_client.responses.create.return_value = mock_response
201+
def test_detect_passive_voice_exact_passive_response(self):
202+
"""Test that exact 'passive' responses are handled correctly."""
203+
# Mock response with exact "passive" classification
204+
mock_response = self._create_mock_chat_response("passive")
205+
206+
self.mock_client.chat.completions.create.return_value = mock_response
225207

226208
result = detect_passive_voice_segments(
227209
"The door was opened.",
228210
openai_client=self.mock_client
229211
)
230212

213+
# Should return full sentence as fragment for exact "passive" match
231214
self.assertEqual(len(result), 1)
232215
sentence, fragments = result[0]
233216
self.assertEqual(sentence, "The door was opened.")
234-
self.assertEqual(fragments, ["was opened"])
217+
self.assertEqual(fragments, ["The door was opened."])
235218

236219
def test_detect_passive_voice_short_sentences_filtered(self):
237220
"""Test that sentences with 2 or fewer words are filtered out."""
@@ -242,11 +225,9 @@ def test_detect_passive_voice_short_sentences_filtered(self):
242225

243226
def test_detect_passive_voice_custom_model(self):
244227
"""Test that custom model parameter is passed correctly."""
245-
mock_response = self._create_mock_response([
246-
{"sentence": "Test sentence here.", "fragments": []}
247-
])
228+
mock_response = self._create_mock_chat_response("active")
248229

249-
self.mock_client.responses.create.return_value = mock_response
230+
self.mock_client.chat.completions.create.return_value = mock_response
250231

251232
detect_passive_voice_segments(
252233
"Test sentence here.",
@@ -255,19 +236,17 @@ def test_detect_passive_voice_custom_model(self):
255236
)
256237

257238
# Verify the custom model was used
258-
call_args = self.mock_client.responses.create.call_args
239+
call_args = self.mock_client.chat.completions.create.call_args
259240
self.assertEqual(call_args[1]['model'], 'gpt-4')
260241

261242
@patch('formfyxer.passive_voice_detection._load_prompt')
262243
def test_prompt_loading(self, mock_load_prompt):
263244
"""Test that the prompt is loaded correctly."""
264245
mock_load_prompt.return_value = "Test prompt content"
265246

266-
mock_response = self._create_mock_response([
267-
{"sentence": "Test sentence with more words.", "fragments": []}
268-
])
247+
mock_response = self._create_mock_chat_response("active")
269248

270-
self.mock_client.responses.create.return_value = mock_response
249+
self.mock_client.chat.completions.create.return_value = mock_response
271250

272251
detect_passive_voice_segments(
273252
"Test sentence with more words.",
@@ -278,20 +257,20 @@ def test_prompt_loading(self, mock_load_prompt):
278257
mock_load_prompt.assert_called_once()
279258

280259
# Verify the loaded prompt was used in the API call
281-
call_args = self.mock_client.responses.create.call_args
282-
input_items = call_args[1]['input']
283-
system_message = input_items[0]
284-
self.assertEqual(system_message['content'][0]['text'], "Test prompt content")
260+
call_args = self.mock_client.chat.completions.create.call_args
261+
messages = call_args[1]['messages']
262+
self.assertEqual(len(messages), 1)
263+
self.assertIn("Test prompt content", messages[0]['content'])
285264

286265
def test_sentence_splitting(self):
287266
"""Test that text is properly split into sentences."""
288-
mock_response = self._create_mock_response([
289-
{"sentence": "First sentence here.", "fragments": []},
290-
{"sentence": "Second sentence here.", "fragments": []},
291-
{"sentence": "Third sentence here!", "fragments": []}
292-
])
267+
responses = [
268+
self._create_mock_chat_response("active"),
269+
self._create_mock_chat_response("active"),
270+
self._create_mock_chat_response("active")
271+
]
293272

294-
self.mock_client.responses.create.return_value = mock_response
273+
self.mock_client.chat.completions.create.side_effect = responses
295274

296275
text = "First sentence here. Second sentence here. Third sentence here!"
297276
result = detect_passive_voice_segments(text, openai_client=self.mock_client)

0 commit comments

Comments
 (0)