@@ -24,7 +24,7 @@ def __getattr__(self, val): # type: ignore[no-untyped-def]
24
24
25
25
@pytest .mark .requires ("oci" )
26
26
@pytest .mark .parametrize (
27
- "test_model_id" , ["cohere.command-r-16k" , "meta.llama-3-70b-instruct" ]
27
+ "test_model_id" , ["cohere.command-r-16k" , "meta.llama-3.3 -70b-instruct" ]
28
28
)
29
29
def test_llm_chat (monkeypatch : MonkeyPatch , test_model_id : str ) -> None :
30
30
"""Test valid chat call to OCI Generative AI LLM service."""
@@ -77,25 +77,34 @@ def mocked_response(*args): # type: ignore[no-untyped-def]
77
77
{
78
78
"chat_response" : MockResponseDict (
79
79
{
80
+ "api_format" : "GENERIC" ,
80
81
"choices" : [
81
82
MockResponseDict (
82
83
{
83
84
"message" : MockResponseDict (
84
85
{
86
+ "role" : "ASSISTANT" ,
87
+ "name" : None ,
85
88
"content" : [
86
89
MockResponseDict (
87
90
{
88
91
"text" : response_text , # noqa: E501
92
+ "type" : "TEXT" ,
89
93
}
90
94
)
91
95
],
92
96
"tool_calls" : [
93
97
MockResponseDict (
94
98
{
95
- "type" : "function " ,
99
+ "type" : "FUNCTION " ,
96
100
"id" : "call_123" ,
97
- "function" : {
98
- "name" : "get_weather" , # noqa: E501
101
+ "name" : "get_weather" , # noqa: E501
102
+ "arguments" : '{"location": "current location"}' , # noqa: E501
103
+ "attribute_map" : {
104
+ "id" : "id" ,
105
+ "type" : "type" ,
106
+ "name" : "name" ,
107
+ "arguments" : "arguments" , # noqa: E501
99
108
},
100
109
}
101
110
)
@@ -106,10 +115,10 @@ def mocked_response(*args): # type: ignore[no-untyped-def]
106
115
}
107
116
)
108
117
],
109
- "time_created" : "2024-09-01T00 :00:00Z " ,
118
+ "time_created" : "2025-08-14T10 :00:01.100000+00:00 " ,
110
119
}
111
120
),
112
- "model_id" : "meta.llama-3.1 -70b-instruct" ,
121
+ "model_id" : "meta.llama-3.3 -70b-instruct" ,
113
122
"model_version" : "1.0.0" ,
114
123
}
115
124
),
@@ -164,11 +173,15 @@ def mocked_response(*args, **kwargs): # type: ignore[no-untyped-def]
164
173
"tool_calls" : [
165
174
MockResponseDict (
166
175
{
167
- "type" : "function " ,
176
+ "type" : "FUNCTION " ,
168
177
"id" : "call_456" ,
169
- "function" : {
170
- "name" : "get_weather" , # noqa: E501
171
- "arguments" : '{"location": "San Francisco"}' , # noqa: E501
178
+ "name" : "get_weather" , # noqa: E501
179
+ "arguments" : '{"location": "San Francisco"}' , # noqa: E501
180
+ "attribute_map" : {
181
+ "id" : "id" ,
182
+ "type" : "type" ,
183
+ "name" : "name" ,
184
+ "arguments" : "arguments" , # noqa: E501
172
185
},
173
186
}
174
187
)
@@ -179,7 +192,7 @@ def mocked_response(*args, **kwargs): # type: ignore[no-untyped-def]
179
192
}
180
193
)
181
194
],
182
- "time_created" : "2024-09-01T00 :00:00Z " ,
195
+ "time_created" : "2025-08-14T10 :00:01.100000+00:00 " ,
183
196
}
184
197
),
185
198
"model_id" : "meta.llama-3-70b-instruct" ,
@@ -285,36 +298,62 @@ def test_meta_tool_conversion(monkeypatch: MonkeyPatch) -> None:
285
298
from pydantic import BaseModel , Field
286
299
287
300
oci_gen_ai_client = MagicMock ()
288
- llm = ChatOCIGenAI (model_id = "meta.llama-3-70b-instruct" , client = oci_gen_ai_client )
301
+ llm = ChatOCIGenAI (model_id = "meta.llama-3.3 -70b-instruct" , client = oci_gen_ai_client )
289
302
290
303
def mocked_response (* args , ** kwargs ): # type: ignore[no-untyped-def]
304
+ request = args [0 ]
305
+ # Check the conversion of tools to oci generic API spec
306
+ # Function tool
307
+ assert request .chat_request .tools [0 ].parameters ["properties" ] == {
308
+ "x" : {"description" : "Input number" , "type" : "integer" }
309
+ }
310
+ # Pydantic tool
311
+ assert request .chat_request .tools [1 ].parameters ["properties" ] == {
312
+ "x" : {"description" : "Input number" , "type" : "integer" },
313
+ "y" : {"description" : "Input string" , "type" : "string" },
314
+ }
315
+
291
316
return MockResponseDict (
292
317
{
293
318
"status" : 200 ,
294
319
"data" : MockResponseDict (
295
320
{
296
321
"chat_response" : MockResponseDict (
297
322
{
323
+ "api_format" : "GENERIC" ,
298
324
"choices" : [
299
325
MockResponseDict (
300
326
{
301
327
"message" : MockResponseDict (
302
328
{
303
- "content" : [
329
+ "role" : "ASSISTANT" ,
330
+ "content" : None ,
331
+ "tool_calls" : [
304
332
MockResponseDict (
305
- {"text" : "Response" }
333
+ {
334
+ "arguments" : '{"x": "10"}' , # noqa: E501
335
+ "id" : "chatcmpl-tool-d123" , # noqa: E501
336
+ "name" : "function_tool" ,
337
+ "type" : "FUNCTION" ,
338
+ "attribute_map" : {
339
+ "id" : "id" ,
340
+ "type" : "type" ,
341
+ "name" : "name" ,
342
+ "arguments" : "arguments" , # noqa: E501
343
+ },
344
+ }
306
345
)
307
- ]
346
+ ],
308
347
}
309
348
),
310
- "finish_reason" : "completed " ,
349
+ "finish_reason" : "tool_calls " ,
311
350
}
312
351
)
313
352
],
314
- "time_created" : "2024-09-01T00 :00:00Z " ,
353
+ "time_created" : "2025-08-14T10 :00:01.100000+00:00 " ,
315
354
}
316
355
),
317
- "model_id" : "meta.llama-3-70b-instruct" ,
356
+ "model_id" : "meta.llama-3.3 -70b-instruct" ,
318
357
"model_version" : "1.0.0" ,
319
358
}
320
359
),
@@ -348,7 +387,10 @@ class PydanticTool(BaseModel):
348
387
tools = [function_tool , PydanticTool ],
349
388
).invoke (messages )
350
389
351
- assert response .content == "Response"
390
+ # For tool calls, the response content should be empty.
391
+ assert response .content == ""
392
+ assert len (response .tool_calls ) == 1
393
+ assert response .tool_calls [0 ]["name" ] == "function_tool"
352
394
353
395
354
396
@pytest .mark .requires ("oci" )
@@ -411,13 +453,13 @@ class WeatherResponse(BaseModel):
411
453
conditions : str = Field (description = "Weather conditions" )
412
454
413
455
oci_gen_ai_client = MagicMock ()
414
- llm = ChatOCIGenAI (model_id = "cohere.command-r-16k " , client = oci_gen_ai_client )
456
+ llm = ChatOCIGenAI (model_id = "cohere.command-latest " , client = oci_gen_ai_client )
415
457
416
458
def mocked_response (* args , ** kwargs ): # type: ignore[no-untyped-def]
417
459
# Verify that response_format contains the schema
418
460
request = args [0 ]
419
- assert request .response_format ["type" ] == "JSON_OBJECT"
420
- assert "schema" in request .response_format
461
+ assert request .chat_request . response_format ["type" ] == "JSON_OBJECT"
462
+ assert "schema" in request .chat_request . response_format
421
463
422
464
return MockResponseDict (
423
465
{
@@ -426,16 +468,17 @@ def mocked_response(*args, **kwargs): # type: ignore[no-untyped-def]
426
468
{
427
469
"chat_response" : MockResponseDict (
428
470
{
471
+ "api_format" : "COHERE" ,
429
472
"text" : '{"temperature": 25.5, "conditions": "Sunny"}' ,
430
- "finish_reason" : "completed " ,
473
+ "finish_reason" : "COMPLETE " ,
431
474
"is_search_required" : None ,
432
475
"search_queries" : None ,
433
476
"citations" : None ,
434
477
"documents" : None ,
435
478
"tool_calls" : None ,
436
479
}
437
480
),
438
- "model_id" : "cohere.command-r-16k " ,
481
+ "model_id" : "cohere.command-latest " ,
439
482
"model_version" : "1.0.0" ,
440
483
}
441
484
),
@@ -462,13 +505,19 @@ def test_auth_file_location(monkeypatch: MonkeyPatch) -> None:
462
505
from unittest .mock import patch
463
506
464
507
with patch ("oci.config.from_file" ) as mock_from_file :
465
- custom_config_path = "/custom/path/config"
466
- ChatOCIGenAI (
467
- model_id = "cohere.command-r-16k" , auth_file_location = custom_config_path
468
- )
469
- mock_from_file .assert_called_once_with (
470
- file_location = custom_config_path , profile_name = "DEFAULT"
471
- )
508
+ with patch (
509
+ "oci.generative_ai_inference.generative_ai_inference_client.validate_config"
510
+ ):
511
+ with patch ("oci.base_client.validate_config" ):
512
+ with patch ("oci.signer.load_private_key" ):
513
+ custom_config_path = "/custom/path/config"
514
+ ChatOCIGenAI (
515
+ model_id = "cohere.command-r-16k" ,
516
+ auth_file_location = custom_config_path ,
517
+ )
518
+ mock_from_file .assert_called_once_with (
519
+ file_location = custom_config_path , profile_name = "DEFAULT"
520
+ )
472
521
473
522
474
523
@pytest .mark .requires ("oci" )
@@ -524,3 +573,17 @@ def mocked_response(*args, **kwargs): # type: ignore[no-untyped-def]
524
573
assert isinstance (response ["parsed" ], WeatherResponse )
525
574
assert response ["parsed" ].temperature == 25.5
526
575
assert response ["parsed" ].conditions == "Sunny"
576
+
577
+
578
+ def test_get_provider ():
579
+ """Test determining the provider based on the model_id."""
580
+ model_provider_map = {
581
+ "cohere.command-latest" : "CohereProvider" ,
582
+ "meta.llama-3.3-70b-instruct" : "MetaProvider" ,
583
+ "xai.grok-3" : "GenericProvider" ,
584
+ }
585
+ for model_id , provider_name in model_provider_map .items ():
586
+ assert (
587
+ ChatOCIGenAI (model_id = model_id )._provider .__class__ .__name__
588
+ == provider_name
589
+ )
0 commit comments