124 lines
3.2 KiB
Python
124 lines
3.2 KiB
Python
from types import SimpleNamespace
|
|
|
|
import pytest
|
|
|
|
from app.utils.openai_chat_compat import (
|
|
create_chat_completion,
|
|
extract_chat_completion_text,
|
|
is_gpt5_family,
|
|
)
|
|
|
|
|
|
class CompletionRecorder:
|
|
def __init__(self, result=None, error=None):
|
|
self.result = result or object()
|
|
self.error = error
|
|
self.calls = []
|
|
|
|
def create(self, **kwargs):
|
|
self.calls.append(kwargs)
|
|
if self.error is not None:
|
|
raise self.error
|
|
return self.result
|
|
|
|
|
|
def client_for(recorder):
|
|
return SimpleNamespace(chat=SimpleNamespace(completions=recorder))
|
|
|
|
|
|
def test_gpt5_uses_completion_token_limit_without_temperature():
|
|
recorder = CompletionRecorder()
|
|
messages = [{"role": "user", "content": "hello"}]
|
|
|
|
result = create_chat_completion(
|
|
client_for(recorder),
|
|
model="gpt-5-2025-08-07",
|
|
messages=messages,
|
|
temperature=0.2,
|
|
max_tokens=123,
|
|
response_format={"type": "json_object"},
|
|
)
|
|
|
|
assert result is recorder.result
|
|
assert recorder.calls == [
|
|
{
|
|
"model": "gpt-5-2025-08-07",
|
|
"messages": messages,
|
|
"max_completion_tokens": 123,
|
|
"response_format": {"type": "json_object"},
|
|
}
|
|
]
|
|
|
|
|
|
def test_legacy_model_preserves_original_request_shape():
|
|
recorder = CompletionRecorder()
|
|
messages = [{"role": "user", "content": "hello"}]
|
|
|
|
create_chat_completion(
|
|
client_for(recorder),
|
|
model="third-party-chat-model",
|
|
messages=messages,
|
|
temperature=0.7,
|
|
max_tokens=456,
|
|
response_format={"type": "json_object"},
|
|
)
|
|
|
|
assert recorder.calls == [
|
|
{
|
|
"model": "third-party-chat-model",
|
|
"messages": messages,
|
|
"temperature": 0.7,
|
|
"max_tokens": 456,
|
|
"response_format": {"type": "json_object"},
|
|
}
|
|
]
|
|
|
|
|
|
def test_provider_error_is_propagated_without_guessing_or_retrying():
|
|
provider_error = RuntimeError("unsupported max_tokens due to a server outage")
|
|
recorder = CompletionRecorder(error=provider_error)
|
|
|
|
with pytest.raises(RuntimeError) as captured:
|
|
create_chat_completion(
|
|
client_for(recorder),
|
|
model="legacy-model",
|
|
messages=[],
|
|
max_tokens=10,
|
|
)
|
|
|
|
assert captured.value is provider_error
|
|
assert len(recorder.calls) == 1
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("model", "expected"),
|
|
[
|
|
("gpt-5", True),
|
|
(" GPT-5.1-mini ", True),
|
|
("gpt-4.1", False),
|
|
("my-gpt-5-proxy", False),
|
|
(None, False),
|
|
],
|
|
)
|
|
def test_gpt5_family_detection(model, expected):
|
|
assert is_gpt5_family(model) is expected
|
|
|
|
|
|
def test_extracts_text_from_supported_content_shapes():
|
|
response = SimpleNamespace(
|
|
choices=[
|
|
SimpleNamespace(
|
|
message=SimpleNamespace(
|
|
content=[
|
|
{"text": {"value": "first"}},
|
|
{"content": " second"},
|
|
SimpleNamespace(text=" third"),
|
|
]
|
|
)
|
|
)
|
|
]
|
|
)
|
|
|
|
assert extract_chat_completion_text(response) == "first second third"
|
|
assert extract_chat_completion_text(SimpleNamespace(choices=[])) == ""
|