1+ import ast
12import asyncio
23import json
34import os
@@ -120,13 +121,17 @@ async def generate_client(
120121 ** function_args ,
121122 ** request .parameters .model_dump (),
122123 }
123-
124124 # Perform the asynchronous call
125125 return await asyncio .to_thread (
126126 client .chat .completions .create , ** combined_args
127127 )
128128
129- except openai ._exceptions .APIError as e :
129+ except openai ._exceptions .APIConnectionError as e :
130+ raise HTTPException (
131+ status_code = 404 , detail = f"There was an error reaching the endpoint: { e } "
132+ )
133+
134+ except openai ._exceptions .APIStatusError as e :
130135 raise HTTPException (status_code = e .status_code , detail = e .response .json ())
131136
132137 def prepare_messages (self , request : AzureRequest ):
@@ -174,6 +179,7 @@ async def handle_tool_response(
174179
175180 function_call_buffer = ""
176181 saving = False
182+ normal_call_chunks = []
177183 for chunk in response :
178184 if chunk .choices [0 ].delta .content is not None :
179185 if (
@@ -224,7 +230,13 @@ async def handle_tool_response(
224230 yield finish_chunk
225231
226232 else :
227- yield chunk .model_dump ()
233+ normal_call_chunks .append (chunk )
234+ if chunk .choices [0 ].finish_reason == "stop" :
235+ for chunk in normal_call_chunks :
236+ normal_call_chunks .append (chunk )
237+ if chunk .choices [0 ].finish_reason == "stop" :
238+ for chunk in normal_call_chunks :
239+ yield chunk .model_dump ()
228240
229241 def create_tool_name_chunk (self , function_name : str , kwargs : dict ) -> dict :
230242 return ChatCompletionChunk (
@@ -433,14 +445,15 @@ def add_tool_instructions(self, tools: list) -> str:
433445 tool_prompt += """
434446If you choose to use a function to produce this response, ONLY reply in the following format with no prefix or suffix:
435447§{"type": "function", "name": "FUNCTION_NAME", "parameters": {"PARAMETER_NAME": PARAMETER_VALUE}}
448+ IMPORTANT: IT IS VITAL THAT YOU NEVER ADD A PREFIX OR A SUFFIX TO THE FUNCTION CALL.
436449
437450Here is an example of the output I desiere when performing function call:
438451§{"type": "function", "name": "python_repl_ast", "parameters": {"query": "print(df.shape)"}}
452+ NOTE: There is no prefix before the symbol '§' and nothing comes after the call is done.
439453
440454 Reminder:
441455 - Function calls MUST follow the specified format.
442456 - Only call one function at a time.
443- - NEVER call more than one function at a time.
444457 - Required parameters MUST be specified.
445458 - Put the entire function call reply on one line.
446459 - If there is no function call available, answer the question like normal with your current knowledge and do not tell the user about function calls.
@@ -456,10 +469,10 @@ def add_function_instructions(self, functions: list) -> str:
456469
457470 for func in functions :
458471 function_prompt += (
459- f"Use the function '{ func ['name' ]} ' to '{ func ['description' ]} ': \n "
472+ f"Use the function '{ func ['name' ]} ' to: '{ func ['description' ]} '\n "
460473 )
461474 params_info = json .dumps (func ["parameters" ], indent = 4 )
462- function_prompt += f"Parameters format: \n { params_info } \n \n "
475+ function_prompt += f"{ params_info } \n \n "
463476
464477 function_prompt += """
465478If you choose to use a function to produce this response, ONLY reply in the following format with no prefix or suffix:
@@ -477,74 +490,74 @@ def add_function_instructions(self, functions: list) -> str:
477490- If there is no function call available, answer the question like normal with your current knowledge and do not tell the user about function calls.
478491- If you have already called a function and got the response for the user's question, please reply with the response.
479492"""
480-
481493 return function_prompt
482494
483495 def add_conversation (self , openai_message : list , llama_message : str ) -> str :
484496 conversation_parts = []
485497 for message in openai_message :
486498 if message ["role" ] == "system" :
487499 continue
488- elif "tool_calls" in message :
489- for tool_call in message ["tool_calls" ]:
490- function_name = tool_call ["function" ]["name" ]
491- arguments = tool_call ["function" ]["arguments" ]
492- conversation_parts .append (
493- f"""
494- <|start_header_id|>assistant<|end_header_id|>
495- <function={ function_name } >{ arguments } </function>
496- <|eom_id|>
497- """
498- )
499- elif "tool_call_id" in message :
500- tool_response = message ["content" ]
501- conversation_parts .append (
502- f"""
503- <|start_header_id|>ipython<|end_header_id|>
504- { tool_response }
505- <|eot_id|>
506- """
507- )
508- elif "function_call" in message :
509- function_name = message ["function_call" ]["name" ]
510- arguments = message ["function_call" ]["arguments" ]
511- conversation_parts .append (
512- f"""
513- <|start_header_id|>assistant<|end_header_id|>
514- <function={ function_name } >{ arguments } </function>
515- <|eom_id|>
516- """
517- )
518- elif (
519- message ["role" ] in ["assistant" , "user" ]
520- and message ["content" ] is not None
521- ):
522- conversation_parts .append (
523- f"""
524- <|start_header_id|>{ message ['role' ]} <|end_header_id|>
525- { message ['content' ]}
526- <|eot_id|>
527- """
528- )
529- elif message ["role" ] == "function" :
530- function_response = message ["content" ]
531- conversation_parts .append (
532- f"""
533- <|start_header_id|>ipython<|end_header_id|>
534- { function_response }
535- <|eot_id|>
536- """
537- )
538- elif (
539- message ["role" ] in ["assistant" , "user" ]
540- and message ["content" ] is not None
541- ):
542- conversation_parts .append (
543- f"""
544- <|start_header_id|>{ message ['role' ]} <|end_header_id|>
545- { message ['content' ]}
546- <|eot_id|>
547- """
548- )
500+ elif message ["role" ] == "user" and isinstance (message ["content" ], str ):
501+ try :
502+ # Attempt to safely evaluate the string to a Python object
503+ content_as_list = ast .literal_eval (message ["content" ])
504+ if isinstance (content_as_list , list ):
505+ # If the content is a list, process each nested message
506+ for nested_message in content_as_list :
507+ conversation_parts .append (
508+ self .format_message (nested_message )
509+ )
510+ else :
511+ # If the content is not a list, append it directly
512+ conversation_parts .append (self .format_message (message ))
513+ except (ValueError , SyntaxError ):
514+ # If evaluation fails or content is not a list/dict string, append the message directly
515+ conversation_parts .append (self .format_message (message ))
516+ else :
517+ # For all other messages, use the existing formatting logic
518+ conversation_parts .append (self .format_message (message ))
549519
550520 return llama_message + "" .join (conversation_parts )
521+
522+ def format_message (self , message : dict ) -> str :
523+ """Format a single message for the conversation."""
524+ if "tool_calls" in message :
525+ for tool_call in message ["tool_calls" ]:
526+ function_name = tool_call ["function" ]["name" ]
527+ arguments = tool_call ["function" ]["arguments" ]
528+ return f"""
529+ <|start_header_id|>assistant<|end_header_id|>
530+ <function={ function_name } >{ arguments } </function>
531+ <|eom_id|>
532+ """
533+ elif "tool_call_id" in message :
534+ tool_response = message ["content" ]
535+ return f"""
536+ <|start_header_id|>ipython<|end_header_id|>
537+ { tool_response }
538+ <|eot_id|>
539+ """
540+ elif "function_call" in message :
541+ function_name = message ["function_call" ]["name" ]
542+ arguments = message ["function_call" ]["arguments" ]
543+ return f"""
544+ <|start_header_id|>assistant<|end_header_id|>
545+ <function={ function_name } >{ arguments } </function>
546+ <|eom_id|>
547+ """
548+ elif (
549+ message ["role" ] in ["assistant" , "user" ] and message ["content" ] is not None
550+ ):
551+ return f"""
552+ <|start_header_id|>{ message ['role' ]} <|end_header_id|>
553+ { message ['content' ]}
554+ <|eot_id|>
555+ """
556+ elif message ["role" ] == "function" :
557+ function_response = message ["content" ]
558+ return f"""
559+ <|start_header_id|>ipython<|end_header_id|>
560+ { function_response }
561+ <|eot_id|>
562+ """
563+ return ""
0 commit comments