@@ -30,6 +30,17 @@ def strip_side_channels(content: str) -> Tuple[str, Optional[str], Optional[List
3030 return text .strip (), ("\n " .join (thinks ) if thinks else None ), (pre or None )
3131
3232
33+ _FENCE = re .compile (r"^```(?:json)?\s*|\s*```$" , re .IGNORECASE )
34+
35+
36+ def strip_json_fence (content : str ) -> str :
37+ """Interfaze wraps ``json_object`` content in a ```json fence; unwrap it."""
38+ t = content .strip ()
39+ if not t .startswith ("```" ):
40+ return content
41+ return _FENCE .sub ("" , t ).strip ()
42+
43+
3344_SIDE_OPEN = ("<think>" , "<precontext>" )
3445_SIDE_CLOSE = {"<think>" : "</think>" , "<precontext>" : "</precontext>" }
3546
@@ -127,8 +138,10 @@ def accumulate(self, chunk: ChatCompletionChunk) -> None:
127138 if tc .function and tc .function .arguments :
128139 acc ["arguments" ] += tc .function .arguments
129140
130- def build (self ) -> InterfazeChatCompletion :
141+ def build (self , strip_fence : bool = False ) -> InterfazeChatCompletion :
131142 text , reasoning , precontext = strip_side_channels (self .content )
143+ if strip_fence :
144+ text = strip_json_fence (text )
132145 tool_calls = [
133146 {"id" : t ["id" ], "type" : "function" , "function" : {"name" : t ["name" ], "arguments" : t ["arguments" ]}}
134147 for t in self .tool_calls .values ()
@@ -156,9 +169,10 @@ def build(self) -> InterfazeChatCompletion:
156169class InterfazeStream :
157170 """Sync streaming helper — iterate chunks, then ``get_final_completion()``."""
158171
159- def __init__ (self , client : OpenAI , kwargs : Dict [str , Any ]) -> None :
172+ def __init__ (self , client : OpenAI , kwargs : Dict [str , Any ], strip_fence : bool = False ) -> None :
160173 self ._client = client
161174 self ._kwargs = kwargs
175+ self ._strip_fence = strip_fence
162176 self ._state = _State ()
163177 self ._started = False
164178 self ._done = False
@@ -215,15 +229,16 @@ def get_final_completion(self) -> InterfazeChatCompletion:
215229 raise InterfazeError (
216230 "Call get_final_completion() after fully iterating, or instead of iterating."
217231 )
218- return self ._state .build ()
232+ return self ._state .build (self . _strip_fence )
219233
220234
221235class AsyncInterfazeStream :
222236 """Async streaming helper — ``async for`` chunks, then ``await get_final_completion()``."""
223237
224- def __init__ (self , client : AsyncOpenAI , kwargs : Dict [str , Any ]) -> None :
238+ def __init__ (self , client : AsyncOpenAI , kwargs : Dict [str , Any ], strip_fence : bool = False ) -> None :
225239 self ._client = client
226240 self ._kwargs = kwargs
241+ self ._strip_fence = strip_fence
227242 self ._state = _State ()
228243 self ._started = False
229244 self ._done = False
@@ -279,4 +294,4 @@ async def get_final_completion(self) -> InterfazeChatCompletion:
279294 raise InterfazeError (
280295 "Call get_final_completion() after fully iterating, or instead of iterating."
281296 )
282- return self ._state .build ()
297+ return self ._state .build (self . _strip_fence )
0 commit comments