Skip to content

Commit fc477bb

Browse files
review the openai tests
1 parent 4533df1 commit fc477bb

File tree

1 file changed

+128
-68
lines changed

1 file changed

+128
-68
lines changed

tests/models/test_openai.py

Lines changed: 128 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -2242,9 +2242,9 @@ def test_model_profile_strict_not_supported():
22422242
)
22432243

22442244
m = OpenAIChatModel('gpt-4o', provider=OpenAIProvider(api_key='foobar'))
2245-
tool_param = m._map_tool_definition(my_tool) # type: ignore[reportPrivateUsage]
2245+
tool_definition = m._map_tool_definition(my_tool) # type: ignore[reportPrivateUsage]
22462246

2247-
assert tool_param == snapshot(
2247+
assert tool_definition == snapshot(
22482248
{
22492249
'type': 'function',
22502250
'function': {
@@ -2262,9 +2262,9 @@ def test_model_profile_strict_not_supported():
22622262
provider=OpenAIProvider(api_key='foobar'),
22632263
profile=OpenAIModelProfile(openai_supports_strict_tool_definition=False).update(openai_model_profile('gpt-4o')),
22642264
)
2265-
tool_param = m._map_tool_definition(my_tool) # type: ignore[reportPrivateUsage]
2265+
tool_definition = m._map_tool_definition(my_tool) # type: ignore[reportPrivateUsage]
22662266

2267-
assert tool_param == snapshot(
2267+
assert tool_definition == snapshot(
22682268
{
22692269
'type': 'function',
22702270
'function': {
@@ -2947,25 +2947,19 @@ def test_deprecated_openai_model(openai_api_key: str):
29472947
OpenAIModel('gpt-4o', provider=provider) # type: ignore[reportDeprecated]
29482948

29492949

2950-
def test_model_profile_freeform_function_calling_support():
2951-
"""Test that model profiles correctly indicate freeform function calling support."""
2952-
# GPT-5 models should support freeform function calling
2953-
gpt5_profile = openai_model_profile('gpt-5')
2954-
assert gpt5_profile.openai_supports_freeform_function_calling is True
2950+
@pytest.mark.parametrize('model_name', ['gpt-5', 'gpt-5-mini', 'gpt-5-nano'])
2951+
def test_model_profile_gpt5_freeform_function_calling_support(model_name: str):
2952+
profile: OpenAIModelProfile = openai_model_profile(model_name)
2953+
assert profile.openai_supports_freeform_function_calling
29552954

2956-
gpt5_turbo_profile = openai_model_profile('gpt-5-turbo')
2957-
assert gpt5_turbo_profile.openai_supports_freeform_function_calling is True
29582955

2959-
# Other models should not support freeform function calling
2960-
gpt4_profile = openai_model_profile('gpt-4o')
2961-
assert gpt4_profile.openai_supports_freeform_function_calling is False
2956+
@pytest.mark.parametrize('model_name', ['gpt-4.1', 'gpt-4o', 'gpt-o4-mini'])
2957+
def test_model_profile_gpt4_freeform_function_calling_support(model_name: str):
2958+
gpt4_profile = openai_model_profile(model_name)
2959+
assert not gpt4_profile.openai_supports_freeform_function_calling
29622960

2963-
gpt35_profile = openai_model_profile('gpt-3.5-turbo')
2964-
assert gpt35_profile.openai_supports_freeform_function_calling is False
29652961

2966-
2967-
def test_tool_mapping_with_freeform_text_mode():
2968-
"""Test that OpenAI Chat model ignores text_format and maps as regular function."""
2962+
def test_chat_model_ignores_text_mode_text_when_tool_mapping():
29692963
my_tool = ToolDefinition(
29702964
name='analyze_text',
29712965
description='Analyze the provided text',
@@ -2978,11 +2972,10 @@ def test_tool_mapping_with_freeform_text_mode():
29782972
text_format='text',
29792973
)
29802974

2981-
# OpenAI Chat model ignores text_format and maps as regular function tool
2982-
m = OpenAIChatModel('gpt-5', provider=OpenAIProvider(api_key='foobar'))
2983-
tool_param = m._map_tool_definition(my_tool) # type: ignore[reportPrivateUsage]
2975+
model = OpenAIChatModel('gpt-5', provider=OpenAIProvider(api_key='foobar'))
2976+
tool_definition = model._map_tool_definition(my_tool) # type: ignore[reportPrivateUsage]
29842977

2985-
assert tool_param == snapshot(
2978+
assert tool_definition == snapshot(
29862979
{
29872980
'type': 'function',
29882981
'function': {
@@ -2999,8 +2992,7 @@ def test_tool_mapping_with_freeform_text_mode():
29992992
)
30002993

30012994

3002-
def test_tool_mapping_with_freeform_lark_grammar():
3003-
"""Test that OpenAI Chat model ignores lark CFG and maps as regular function."""
2995+
def test_chat_model_ignores_text_mode_lark_when_tool_mapping():
30042996
my_tool = ToolDefinition(
30052997
name='parse_data',
30062998
description='Parse structured data',
@@ -3013,11 +3005,10 @@ def test_tool_mapping_with_freeform_lark_grammar():
30133005
text_format=FunctionTextFormat(syntax='lark', grammar='start: "hello" " " "world"'),
30143006
)
30153007

3016-
# OpenAI Chat model ignores text_format and maps as regular function tool
3017-
m = OpenAIChatModel('gpt-5', provider=OpenAIProvider(api_key='foobar'))
3018-
tool_param = m._map_tool_definition(my_tool) # type: ignore[reportPrivateUsage]
3008+
model = OpenAIChatModel('gpt-5', provider=OpenAIProvider(api_key='foobar'))
3009+
tool_definition = model._map_tool_definition(my_tool) # type: ignore[reportPrivateUsage]
30193010

3020-
assert tool_param == snapshot(
3011+
assert tool_definition == snapshot(
30213012
{
30223013
'type': 'function',
30233014
'function': {
@@ -3034,8 +3025,7 @@ def test_tool_mapping_with_freeform_lark_grammar():
30343025
)
30353026

30363027

3037-
def test_tool_mapping_with_freeform_regex():
3038-
"""Test that OpenAI Chat model ignores regex CFG and maps as regular function."""
3028+
def test_chat_model_ignores_text_mode_regex_when_tool_mapping():
30393029
my_tool = ToolDefinition(
30403030
name='extract_pattern',
30413031
description='Extract data matching pattern',
@@ -3048,11 +3038,10 @@ def test_tool_mapping_with_freeform_regex():
30483038
text_format=FunctionTextFormat(syntax='regex', grammar=r'\d{4}-\d{2}-\d{2}'),
30493039
)
30503040

3051-
# OpenAI Chat model ignores text_format and maps as regular function tool
3052-
m = OpenAIChatModel('gpt-5', provider=OpenAIProvider(api_key='foobar'))
3053-
tool_param = m._map_tool_definition(my_tool) # type: ignore[reportPrivateUsage]
3041+
model = OpenAIChatModel('gpt-5', provider=OpenAIProvider(api_key='foobar'))
3042+
tool_definition = model._map_tool_definition(my_tool) # type: ignore[reportPrivateUsage]
30543043

3055-
assert tool_param == snapshot(
3044+
assert tool_definition == snapshot(
30563045
{
30573046
'type': 'function',
30583047
'function': {
@@ -3069,8 +3058,85 @@ def test_tool_mapping_with_freeform_regex():
30693058
)
30703059

30713060

3072-
def test_tool_mapping_regular_function_unchanged():
3073-
"""Test that regular tools without text_format are still mapped to function params."""
3061+
def test_responses_model_uses_text_mode_text_when_tool_mapping():
3062+
my_tool = ToolDefinition(
3063+
name='analyze_text',
3064+
description='Analyze the provided text',
3065+
parameters_json_schema={
3066+
'type': 'object',
3067+
'properties': {'content': {'type': 'string'}},
3068+
'required': ['content'],
3069+
'additionalProperties': False,
3070+
},
3071+
text_format='text',
3072+
)
3073+
3074+
model = OpenAIResponsesModel('gpt-5', provider=OpenAIProvider(api_key='foobar'))
3075+
tool_definition = model._map_tool_definition(my_tool) # type: ignore[reportPrivateUsage]
3076+
3077+
assert tool_definition == snapshot(
3078+
{
3079+
'name': 'analyze_text',
3080+
'type': 'custom',
3081+
'description': 'Analyze the provided text',
3082+
'format': {'type': 'text'},
3083+
}
3084+
)
3085+
3086+
3087+
def test_responses_model_uses_text_mode_lark_when_tool_mapping():
3088+
my_tool = ToolDefinition(
3089+
name='parse_data',
3090+
description='Parse structured data',
3091+
parameters_json_schema={
3092+
'type': 'object',
3093+
'properties': {'data': {'type': 'string'}},
3094+
'required': ['data'],
3095+
'additionalProperties': False,
3096+
},
3097+
text_format=FunctionTextFormat(syntax='lark', grammar='start: "hello" " " "world"'),
3098+
)
3099+
3100+
model = OpenAIResponsesModel('gpt-5', provider=OpenAIProvider(api_key='foobar'))
3101+
tool_definition = model._map_tool_definition(my_tool) # type: ignore[reportPrivateUsage]
3102+
3103+
assert tool_definition == snapshot(
3104+
{
3105+
'name': 'parse_data',
3106+
'type': 'custom',
3107+
'description': 'Parse structured data',
3108+
'format': {'type': 'grammar', 'syntax': 'lark', 'definition': 'start: "hello" " " "world"'},
3109+
}
3110+
)
3111+
3112+
3113+
def test_responses_model_uses_text_mode_regex_when_tool_mapping():
3114+
my_tool = ToolDefinition(
3115+
name='extract_pattern',
3116+
description='Extract data matching pattern',
3117+
parameters_json_schema={
3118+
'type': 'object',
3119+
'properties': {'text': {'type': 'string'}},
3120+
'required': ['text'],
3121+
'additionalProperties': False,
3122+
},
3123+
text_format=FunctionTextFormat(syntax='regex', grammar=r'\d{4}-\d{2}-\d{2}'),
3124+
)
3125+
3126+
model = OpenAIResponsesModel('gpt-5', provider=OpenAIProvider(api_key='foobar'))
3127+
tool_definition = model._map_tool_definition(my_tool) # type: ignore[reportPrivateUsage]
3128+
3129+
assert tool_definition == snapshot(
3130+
{
3131+
'name': 'extract_pattern',
3132+
'type': 'custom',
3133+
'description': 'Extract data matching pattern',
3134+
'format': {'type': 'grammar', 'syntax': 'regex', 'definition': '\\d{4}-\\d{2}-\\d{2}'},
3135+
}
3136+
)
3137+
3138+
3139+
def test_chat_model_tool_mapping_regular_function_unchanged():
30743140
my_tool = ToolDefinition(
30753141
name='regular_tool',
30763142
description='A regular tool',
@@ -3081,10 +3147,10 @@ def test_tool_mapping_regular_function_unchanged():
30813147
},
30823148
)
30833149

3084-
m = OpenAIChatModel('gpt-5', provider=OpenAIProvider(api_key='foobar'))
3085-
tool_param = m._map_tool_definition(my_tool) # type: ignore[reportPrivateUsage]
3150+
model = OpenAIChatModel('gpt-5', provider=OpenAIProvider(api_key='foobar'))
3151+
tool_definition = model._map_tool_definition(my_tool) # type: ignore[reportPrivateUsage]
30863152

3087-
assert tool_param == snapshot(
3153+
assert tool_definition == snapshot(
30883154
{
30893155
'type': 'function',
30903156
'function': {
@@ -3096,12 +3162,8 @@ def test_tool_mapping_regular_function_unchanged():
30963162
)
30973163

30983164

3099-
def test_parallel_tool_calling_with_regular_tools():
3100-
"""Test that OpenAI Chat model handles parallel tool calling normally with regular tools."""
3101-
from pydantic_ai.models import ModelRequestParameters
3102-
3103-
# Regular tool
3104-
regular_tool = ToolDefinition(
3165+
def test_responses_model_tool_mapping_regular_function_unchanged():
3166+
my_tool = ToolDefinition(
31053167
name='regular_tool',
31063168
description='A regular tool',
31073169
parameters_json_schema={
@@ -3111,21 +3173,21 @@ def test_parallel_tool_calling_with_regular_tools():
31113173
},
31123174
)
31133175

3114-
m = OpenAIChatModel('gpt-5', provider=OpenAIProvider(api_key='foobar'))
3115-
3116-
# With only regular tools - should use model settings default
3117-
params_regular_only = ModelRequestParameters(function_tools=[regular_tool])
3118-
# OpenAI Chat model doesn't have _get_parallel_tool_calling method like Responses model
3119-
# This test verifies the model can handle regular tools without issues
3120-
3176+
model = OpenAIResponsesModel('gpt-5', provider=OpenAIProvider(api_key='foobar'))
3177+
tool_definition = model._map_tool_definition(my_tool) # type: ignore[reportPrivateUsage]
31213178

3122-
# OpenAI Chat model doesn't validate text_format, so no error tests needed
3123-
# Error validation tests are in the OpenAI Responses model section
3179+
assert tool_definition == snapshot(
3180+
{
3181+
'name': 'regular_tool',
3182+
'parameters': {'type': 'object', 'properties': {'param': {'type': 'string'}}, 'required': ['param']},
3183+
'type': 'function',
3184+
'description': 'A regular tool',
3185+
'strict': False,
3186+
}
3187+
)
31243188

31253189

3126-
def test_tool_definition_single_string_argument_detection():
3127-
"""Test ToolDefinition correctly detects single string argument tools."""
3128-
# Valid single string argument
3190+
def test_tool_definition_single_string_argument():
31293191
valid_tool = ToolDefinition(
31303192
name='valid_tool',
31313193
description='Valid single string tool',
@@ -3136,26 +3198,28 @@ def test_tool_definition_single_string_argument_detection():
31363198
'additionalProperties': False,
31373199
},
31383200
)
3139-
assert valid_tool.only_takes_string_argument is True
3201+
assert valid_tool.only_takes_string_argument
31403202
assert valid_tool.single_string_argument_name == 'content'
31413203

3142-
# Multiple parameters - invalid
3204+
3205+
def test_tool_definition_multiple_argument_single_string_argument():
31433206
multi_param_tool = ToolDefinition(
31443207
name='multi_tool',
31453208
description='Multi param tool',
31463209
parameters_json_schema={
31473210
'type': 'object',
3211+
'param1': {'type': 'string'},
31483212
'properties': {
3149-
'param1': {'type': 'string'},
31503213
'param2': {'type': 'string'},
31513214
},
31523215
'required': ['param1', 'param2'],
31533216
},
31543217
)
3155-
assert multi_param_tool.only_takes_string_argument is False
3218+
assert not multi_param_tool.only_takes_string_argument
31563219
assert multi_param_tool.single_string_argument_name is None
31573220

3158-
# Non-string parameter - invalid
3221+
3222+
def test_tool_definition_single_non_string_argument_single_string_argument():
31593223
non_string_tool = ToolDefinition(
31603224
name='non_string_tool',
31613225
description='Non-string param tool',
@@ -3165,9 +3229,5 @@ def test_tool_definition_single_string_argument_detection():
31653229
'required': ['count'],
31663230
},
31673231
)
3168-
assert non_string_tool.only_takes_string_argument is False
3232+
assert not non_string_tool.only_takes_string_argument
31693233
assert non_string_tool.single_string_argument_name is None
3170-
3171-
3172-
# OpenAI Chat model doesn't support custom tool calls, so no custom tool call tests needed here
3173-
# Those tests are in the OpenAI Responses model section

0 commit comments

Comments
 (0)