@@ -75,6 +75,23 @@ def completion(
7575 ],
7676)
7777
78+ TASK_OBJECT_DETECTION = completion (
79+ '{"name": "object_detection", "result": {"objects": [{"label": "bus", "box": [0, 0, 10, 10]}]}}'
80+ )
81+ TASK_GUI_DETECTION = completion (
82+ '{"name": "gui_detection", "result": {"elements": [{"label": "button", "box": [1, 2, 3, 4]}]}}'
83+ )
84+ TASK_TRANSCRIBE = completion ('{"name": "speech_to_text", "result": {"text": "hello world"}}' )
85+ TASK_WEB_SEARCH = completion (
86+ '{"name": "web_search", "result": {"results": [{"title": "AI agents", "url": "https://example.com"}]}}'
87+ )
88+ TASK_SCRAPE = completion ('{"name": "scraper", "result": {"text": "Hacker News"}}' )
89+ TASK_TRANSLATE = completion ('{"name": "translate", "result": "Bonjour"}' )
90+ FORECAST_PRECONTEXT = completion (
91+ "Here is the forecast." , precontext = [{"name" : "forecast" , "result" : {"forecast" : [1 , 2 , 3 ]}}]
92+ )
93+ FORECAST_FALLBACK = completion ("I couldn't run the forecast tool; here's a manual estimate." )
94+
7895
7996def _chunk (delta : Dict [str , Any ], finish_reason = None ) -> Dict [str , Any ]:
8097 return {
@@ -99,12 +116,72 @@ def _chunk(delta: Dict[str, Any], finish_reason=None) -> Dict[str, Any]:
99116 _chunk ({}, finish_reason = "stop" ),
100117]
101118
119+ # Tool-call arguments split across chunks, as the wire actually streams them.
120+ STREAM_TOOL_CALL_CHUNKS : List [Dict [str , Any ]] = [
121+ _chunk (
122+ {
123+ "tool_calls" : [
124+ {
125+ "index" : 0 ,
126+ "id" : "call_1" ,
127+ "type" : "function" ,
128+ "function" : {"name" : "get_weather" , "arguments" : '{"ci' },
129+ }
130+ ]
131+ }
132+ ),
133+ _chunk ({"tool_calls" : [{"index" : 0 , "function" : {"arguments" : 'ty": "Pa' }}]}),
134+ _chunk ({"tool_calls" : [{"index" : 0 , "function" : {"arguments" : 'ris"}' }}]}, finish_reason = "tool_calls" ),
135+ ]
136+
137+ # A <precontext> block with invalid JSON inside — interfaze must swallow it, not crash.
138+ STREAM_MALFORMED_PRECONTEXT_CHUNKS : List [Dict [str , Any ]] = [
139+ _chunk ({"content" : "<precontext>[not valid json]</precontext>" }),
140+ _chunk ({"content" : "Answer anyway." }, finish_reason = "stop" ),
141+ ]
142+
143+ # A heartbeat/ping chunk with no choices at all, as some SSE proxies emit mid-stream.
144+ STREAM_CHUNK_NO_CHOICES : Dict [str , Any ] = {
145+ "id" : "req-test" ,
146+ "object" : "chat.completion.chunk" ,
147+ "created" : 1_700_000_000 ,
148+ "model" : "interfaze-beta" ,
149+ "choices" : [],
150+ }
151+ STREAM_ROLE_THEN_CONTENT_CHUNKS : List [Dict [str , Any ]] = [
152+ _chunk ({"role" : "assistant" , "content" : "" }),
153+ _chunk ({"content" : "Hi there." }, finish_reason = "stop" ),
154+ ]
155+
156+ # Two parallel tool calls in a single delta — one arrives complete, one still has no arguments.
157+ STREAM_PARALLEL_TOOL_CALL_CHUNKS : List [Dict [str , Any ]] = [
158+ _chunk (
159+ {
160+ "tool_calls" : [
161+ {
162+ "index" : 0 ,
163+ "id" : "call_1" ,
164+ "type" : "function" ,
165+ "function" : {"name" : "get_weather" , "arguments" : '{"city": "Paris"}' },
166+ },
167+ {"index" : 1 , "id" : "call_2" , "type" : "function" , "function" : {"name" : "get_time" }},
168+ ]
169+ },
170+ finish_reason = "tool_calls" ,
171+ ),
172+ ]
173+
102174
103175def mock_json (body : Dict [str , Any ]) -> respx .Route :
104176 """Route POST /chat/completions -> a JSON completion; returns the route (inspect .calls)."""
105177 return respx .post (CHAT_URL ).mock (return_value = httpx .Response (200 , json = body ))
106178
107179
180+ def mock_status (status : int , body : Dict [str , Any ]) -> respx .Route :
181+ """Route POST /chat/completions -> an error status with a realistic error body."""
182+ return respx .post (CHAT_URL ).mock (return_value = httpx .Response (status , json = body ))
183+
184+
108185def sse_bytes (chunks : List [Dict [str , Any ]]) -> bytes :
109186 return ("" .join (f"data: { json .dumps (c )} \n \n " for c in chunks ) + "data: [DONE]\n \n " ).encode ()
110187
@@ -117,6 +194,11 @@ def mock_sse(chunks: List[Dict[str, Any]]) -> respx.Route:
117194 )
118195
119196
197+ def error_body (message : str , type_ : str , code : str ) -> Dict [str , Any ]:
198+ """Shape of a real Interfaze error response body."""
199+ return {"error" : {"message" : message , "type" : type_ , "code" : code }}
200+
201+
120202def last_body (route : respx .Route ) -> Dict [str , Any ]:
121203 return json .loads (route .calls .last .request .content )
122204
0 commit comments