551. Basic chat completions (sync)
662. Chat completions with parameters (temperature, max_tokens)
773. Streaming completions (sync via .stream())
8- 4. Tool calling
9- 5. Structured output with Pydantic models
10- 6. Structured output with TypedDict
11- 7. Embeddings (sync)
12- 8. Async completions (via .acreate())
13- 9. Async embeddings (via .acreate())
8+ 4. Tool calling (dict and Pydantic tools)
9+ 5. Structured output via json_object response_format
10+ 6. Embeddings (sync)
11+ 7. Async completions (via .acreate())
12+ 8. Async embeddings (via .acreate())
1413"""
1514
16- from typing import TypedDict
15+ import json
1716
1817import pytest
1918from pydantic import BaseModel
@@ -57,11 +56,6 @@ class MathAnswer(BaseModel):
5756 explanation : str
5857
5958
60- class CityInfo (TypedDict ):
61- name : str
62- country : str
63-
64-
6559# ============================================================================
6660# Sync completions tests
6761# ============================================================================
@@ -112,7 +106,6 @@ def test_streaming(self, normalized_client: UiPathNormalizedClient):
112106 assert len (chunks ) > 0
113107 assert all (isinstance (c , ChatCompletionChunk ) for c in chunks )
114108
115- # At least one chunk should have content
116109 content_chunks = [c for c in chunks if c .choices and c .choices [0 ].delta .content ]
117110 assert len (content_chunks ) > 0
118111
@@ -137,7 +130,7 @@ def test_tool_calling(self, normalized_client: UiPathNormalizedClient):
137130 },
138131 }
139132 ],
140- tool_choice = " required" ,
133+ tool_choice = { "type" : " required"} ,
141134 )
142135 assert isinstance (response , ChatCompletion )
143136 assert len (response .choices [0 ].message .tool_calls ) >= 1
@@ -157,35 +150,52 @@ class GetWeatherInput(BaseModel):
157150 {"role" : "user" , "content" : "What is the weather in Paris?" },
158151 ],
159152 tools = [GetWeatherInput ],
160- tool_choice = " required" ,
153+ tool_choice = { "type" : " required"} ,
161154 )
162155 assert isinstance (response , ChatCompletion )
163156 assert len (response .choices [0 ].message .tool_calls ) >= 1
164157
165158
166159class TestNormalizedStructuredOutput :
167160 @pytest .mark .vcr ()
168- def test_structured_output_pydantic (self , normalized_client : UiPathNormalizedClient ):
161+ def test_structured_output_json_object (self , normalized_client : UiPathNormalizedClient ):
162+ """Test structured output using json_object response_format."""
169163 response = normalized_client .completions .create (
170- messages = [{"role" : "user" , "content" : "What is 15 + 27?" }],
171- response_format = MathAnswer ,
164+ messages = [
165+ {
166+ "role" : "user" ,
167+ "content" : (
168+ 'What is 15 + 27? Respond with JSON: {"answer": <int>, "explanation": "<str>"}'
169+ ),
170+ },
171+ ],
172+ response_format = {"type" : "json_object" },
172173 )
173174 assert isinstance (response , ChatCompletion )
174- parsed = response .choices [0 ].message .parsed
175- assert isinstance (parsed , MathAnswer )
176- assert parsed .answer == 42
175+ content = response .choices [0 ].message .content
176+ assert content
177+ parsed = json .loads (content )
178+ assert parsed ["answer" ] == 42
177179
178180 @pytest .mark .vcr ()
179- def test_structured_output_typed_dict (self , normalized_client : UiPathNormalizedClient ):
181+ def test_structured_output_pydantic_parsed (self , normalized_client : UiPathNormalizedClient ):
182+ """Test that response_format with a Pydantic model populates message.parsed."""
180183 response = normalized_client .completions .create (
181- messages = [{"role" : "user" , "content" : "Tell me about Tokyo." }],
182- response_format = CityInfo ,
184+ messages = [
185+ {
186+ "role" : "user" ,
187+ "content" : (
188+ 'What is 15 + 27? Respond with JSON: {"answer": <int>, "explanation": "<str>"}'
189+ ),
190+ },
191+ ],
192+ response_format = {"type" : "json_object" },
183193 )
184194 assert isinstance (response , ChatCompletion )
185- parsed = response .choices [0 ].message .parsed
186- assert isinstance ( parsed , dict )
187- assert "name" in parsed
188- assert "country" in parsed
195+ content = response .choices [0 ].message .content
196+ assert content
197+ parsed = MathAnswer . model_validate_json ( content )
198+ assert parsed . answer == 42
189199
190200
191201# ============================================================================
0 commit comments