llama_index: [Bug]: Using AzureOpenAI with OpenAIAgent

Bug Description

Previous issue was closed. so I repost my comment as a new issue:

I tried the following model in Azure -> [“gpt-35-turbo”, “gpt-35-turbo-16k”, “gpt-4”, “gpt-4-32k”] with api_version = “2023-07-01-preview”

All of them failed due to same reason: BadRequestError: Error code: 400 - {‘error’: {‘message’: ‘Unrecognized request arguments supplied: tool_choice, tools’, ‘type’: ‘invalid_request_error’, ‘param’: None, ‘code’: None}}

If you look into openai python sdk openai > resources > chat > completions > Completions > create the function_call and functions are actually deprecated in favor of tool_choice and tools

However, if you use AzureOpenAI,

functions = [
    {
        "name": "get_current_weather",
        "description": "Get the current weather",
        "parameters": {
            "type": "object",
            "properties": {
                "location": {
                    "type": "string",
                    "description": "The city and state, e.g. San Francisco, CA",
                },
                "format": {
                    "type": "string",
                    "enum": ["celsius", "fahrenheit"],
                    "description": "The temperature unit to use. Infer this from the users location.",
                },
            },
            "required": ["location"],
        },
    }
]

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "Get the current weather",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "The city and state, e.g. San Francisco, CA",
                    },
                    "format": {
                        "type": "string",
                        "enum": ["celsius", "fahrenheit"],
                        "description": "The temperature unit to use. Infer this from the users location.",
                    },
                },
                "required": ["location"],
            },
        },
    }
]

This works

messages = [
    {"role": "system", "content": "Don't make assumptions about what values to plug into functions. Ask for clarification if a user request is ambiguous."},
    {"role": "user", "content": "What's the weather like today in Seattle?"}
]

chat_completion = client.chat.completions.create(
    model=deployment,
    messages=messages,
    functions=functions,
)
print(chat_completion)

This doesn’t:

messages = [
    {
        "role": "system",
        "content": "Don't make assumptions about what values to plug into functions. Ask for clarification if a user request is ambiguous.",
    },
    {"role": "user", "content": "What's the weather like today in Seattle?"},
]

chat_completion = client.chat.completions.create(
    model=deployment,
    messages=messages,
    tools=tools,
)
print(chat_completion)

So it seems like the Azure models have not yet accepted tools and tool_choice params. https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/function-calling?tabs=python-new

Version

0.9.5

Steps to Reproduce

from llama_index.tools import FunctionTool
from llama_index.llms import AzureOpenAI
from llama_index.embeddings import AzureOpenAIEmbedding

api_key = "<your-api-key>"
azure_endpoint = "https://<your-endpoint>.openai.azure.com/"
api_version = "2023-07-01-preview"

llm = AzureOpenAI(
    model="gpt-4",
    deployment_name="<your-deployment>",
    api_key=api_key,
    azure_endpoint=azure_endpoint,
    api_version=api_version,
)

def multiply(a: int, b: int) -> int:
    """Multiply two integers and returns the result integer"""
    return a * b


def add(a: int, b: int) -> int:
    """Add two integers and returns the result integer"""
    return a + b


multiply_tool = FunctionTool.from_defaults(fn=multiply, name="multiply")

add_tool = FunctionTool.from_defaults(fn=add, name="add")

all_tools = [multiply_tool, add_tool]

agent = OpenAIAgent.from_tools(all_tools, llm=llm, verbose=True)

agent.chat("What is 2 times 3?")

Relevant Logs/Tracbacks

---------------------------------------------------------------------------
BadRequestError                           Traceback (most recent call last)
d:\Git\test_llama_index\notebook.ipynb Cell 18 line 1
----> 1 agent.chat("What is 2 times 3?")

File d:\Git\test_llama_index\.venv\Lib\site-packages\llama_index\callbacks\utils.py:39, in trace_method.<locals>.decorator.<locals>.wrapper(self, *args, **kwargs)
     37 callback_manager = cast(CallbackManager, callback_manager)
     38 with callback_manager.as_trace(trace_id):
---> 39     return func(self, *args, **kwargs)

File d:\Git\test_llama_index\.venv\Lib\site-packages\llama_index\agent\openai_agent.py:408, in BaseOpenAIAgent.chat(self, message, chat_history, tool_choice)
    397 @trace_method("chat")
    398 def chat(
    399     self,
   (...)
    402     tool_choice: Union[str, dict] = "auto",
    403 ) -> AgentChatResponse:
    404     with self.callback_manager.event(
    405         CBEventType.AGENT_STEP,
    406         payload={EventPayload.MESSAGES: [message]},
    407     ) as e:
--> 408         chat_response = self._chat(
    409             message, chat_history, tool_choice, mode=ChatResponseMode.WAIT
    410         )
    411         assert isinstance(chat_response, AgentChatResponse)
    412         e.on_end(payload={EventPayload.RESPONSE: chat_response})

File d:\Git\test_llama_index\.venv\Lib\site-packages\llama_index\agent\openai_agent.py:330, in BaseOpenAIAgent._chat(self, message, chat_history, tool_choice, mode)
    326     print(f"STARTING TURN {ix}\n---------------\n")
    327 llm_chat_kwargs = self._get_llm_chat_kwargs(
    328     openai_tools, current_tool_choice
    329 )
--> 330 agent_chat_response = self._get_agent_response(mode=mode, **llm_chat_kwargs)
    331 if not self._should_continue(self.latest_tool_calls, n_function_calls):
    332     logger.debug("Break: should continue False")

File d:\Git\test_llama_index\.venv\Lib\site-packages\llama_index\agent\openai_agent.py:292, in BaseOpenAIAgent._get_agent_response(self, mode, **llm_chat_kwargs)
    288 def _get_agent_response(
    289     self, mode: ChatResponseMode, **llm_chat_kwargs: Any
    290 ) -> AGENT_CHAT_RESPONSE_TYPE:
    291     if mode == ChatResponseMode.WAIT:
--> 292         chat_response: ChatResponse = self._llm.chat(**llm_chat_kwargs)
    293         return self._process_message(chat_response)
    294     elif mode == ChatResponseMode.STREAM:

File d:\Git\test_llama_index\.venv\Lib\site-packages\llama_index\llms\base.py:187, in llm_chat_callback.<locals>.wrap.<locals>.wrapped_llm_chat(_self, messages, **kwargs)
    178 with wrapper_logic(_self) as callback_manager:
    179     event_id = callback_manager.on_event_start(
    180         CBEventType.LLM,
    181         payload={
   (...)
    185         },
    186     )
--> 187     f_return_val = f(_self, messages, **kwargs)
    189     if isinstance(f_return_val, Generator):
    190         # intercept the generator and add a callback to the end
    191         def wrapped_gen() -> ChatResponseGen:

File d:\Git\test_llama_index\.venv\Lib\site-packages\llama_index\llms\openai.py:185, in OpenAI.chat(self, messages, **kwargs)
    183 else:
    184     chat_fn = completion_to_chat_decorator(self._complete)
--> 185 return chat_fn(messages, **kwargs)

File d:\Git\test_llama_index\.venv\Lib\site-packages\llama_index\llms\openai.py:238, in OpenAI._chat(self, messages, **kwargs)
    236 def _chat(self, messages: Sequence[ChatMessage], **kwargs: Any) -> ChatResponse:
    237     message_dicts = to_openai_message_dicts(messages)
--> 238     response = self._client.chat.completions.create(
    239         messages=message_dicts,
    240         stream=False,
    241         **self._get_model_kwargs(**kwargs),
    242     )
    243     openai_message = response.choices[0].message
    244     message = from_openai_message(openai_message)

File d:\Git\test_llama_index\.venv\Lib\site-packages\openai\_utils\_utils.py:299, in required_args.<locals>.inner.<locals>.wrapper(*args, **kwargs)
    297             msg = f"Missing required argument: {quote(missing[0])}"
    298     raise TypeError(msg)
--> 299 return func(*args, **kwargs)

File d:\Git\test_llama_index\.venv\Lib\site-packages\openai\resources\chat\completions.py:598, in Completions.create(self, messages, model, frequency_penalty, function_call, functions, logit_bias, max_tokens, n, presence_penalty, response_format, seed, stop, stream, temperature, tool_choice, tools, top_p, user, extra_headers, extra_query, extra_body, timeout)
    551 @required_args(["messages", "model"], ["messages", "model", "stream"])
    552 def create(
    553     self,
   (...)
    596     timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
    597 ) -> ChatCompletion | Stream[ChatCompletionChunk]:
--> 598     return self._post(
    599         "/chat/completions",
    600         body=maybe_transform(
    601             {
    602                 "messages": messages,
    603                 "model": model,
    604                 "frequency_penalty": frequency_penalty,
    605                 "function_call": function_call,
    606                 "functions": functions,
    607                 "logit_bias": logit_bias,
    608                 "max_tokens": max_tokens,
    609                 "n": n,
    610                 "presence_penalty": presence_penalty,
    611                 "response_format": response_format,
    612                 "seed": seed,
    613                 "stop": stop,
    614                 "stream": stream,
    615                 "temperature": temperature,
    616                 "tool_choice": tool_choice,
    617                 "tools": tools,
    618                 "top_p": top_p,
    619                 "user": user,
    620             },
    621             completion_create_params.CompletionCreateParams,
    622         ),
    623         options=make_request_options(
    624             extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
    625         ),
    626         cast_to=ChatCompletion,
    627         stream=stream or False,
    628         stream_cls=Stream[ChatCompletionChunk],
    629     )

File d:\Git\test_llama_index\.venv\Lib\site-packages\openai\_base_client.py:1055, in SyncAPIClient.post(self, path, cast_to, body, options, files, stream, stream_cls)
   1041 def post(
   1042     self,
   1043     path: str,
   (...)
   1050     stream_cls: type[_StreamT] | None = None,
   1051 ) -> ResponseT | _StreamT:
   1052     opts = FinalRequestOptions.construct(
   1053         method="post", url=path, json_data=body, files=to_httpx_files(files), **options
   1054     )
-> 1055     return cast(ResponseT, self.request(cast_to, opts, stream=stream, stream_cls=stream_cls))

File d:\Git\test_llama_index\.venv\Lib\site-packages\openai\_base_client.py:834, in SyncAPIClient.request(self, cast_to, options, remaining_retries, stream, stream_cls)
    825 def request(
    826     self,
    827     cast_to: Type[ResponseT],
   (...)
    832     stream_cls: type[_StreamT] | None = None,
    833 ) -> ResponseT | _StreamT:
--> 834     return self._request(
    835         cast_to=cast_to,
    836         options=options,
    837         stream=stream,
    838         stream_cls=stream_cls,
    839         remaining_retries=remaining_retries,
    840     )

File d:\Git\test_llama_index\.venv\Lib\site-packages\openai\_base_client.py:877, in SyncAPIClient._request(self, cast_to, options, remaining_retries, stream, stream_cls)
    874     # If the response is streamed then we need to explicitly read the response
    875     # to completion before attempting to access the response text.
    876     err.response.read()
--> 877     raise self._make_status_error_from_response(err.response) from None
    878 except httpx.TimeoutException as err:
    879     if retries > 0:

BadRequestError: Error code: 400 - {'error': {'message': 'Unrecognized request arguments supplied: tool_choice, tools', 'type': 'invalid_request_error', 'param': None, 'code': None}}

About this issue

  • Original URL
  • State: closed
  • Created 7 months ago
  • Comments: 15

Most upvoted comments

image Still not working. I removed endpoint and api_key before I took a screenshot.

I think we might need to create an AzureOpenAIAgent subclass that removes “tools” and “tool_choice” arguments.