From 7fa263b53faa5f8ceaab5d951a39bc7bcb604c51 Mon Sep 17 00:00:00 2001 From: Kaushik Deka Date: Mon, 10 Feb 2025 04:31:33 +0000 Subject: [PATCH 1/2] fix function calling type to follow open ai, add tests --- tests/local_testing/test_function_calling.py | 58 ++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/tests/local_testing/test_function_calling.py b/tests/local_testing/test_function_calling.py index 2452b362d4..012692ea11 100644 --- a/tests/local_testing/test_function_calling.py +++ b/tests/local_testing/test_function_calling.py @@ -769,3 +769,61 @@ async def test_function_calling_with_dbrx(): json_data = json.loads(mock_completion.call_args.kwargs["data"]) assert "tools" in json_data assert "tool_choice" in json_data + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + "model", + [ + "gpt-4-1106-preview", + "mistral/mistral-small-latest", + ], +) +async def test_function_calling_tool_type(model): + """ + Test the API's response when streaming tool calls. + - The first streamed chunk contains a tool call with an ID and type "function". + - Subsequent chunks may only contain arguments, with ID and type being None. + """ + response = completion( + model=model, + messages=[{ + "role": "user", + "content": "Search for recent AI breakthroughs" + }], + tools=[{ + "type": "function", + "function": { + "name": "web_search", + "description": "Searches the web", + "parameters": { + "type": "object", + "properties": { + "query": {"type": "string"} + }, + "required": ["query"] + } + } + }], + stream=True, + temperature=0.00000001 + ) + + + async for chunk in response: + print(json.dumps(chunk, indent=2, default=str)) + if "choices" not in chunk or not chunk.choices: + raise ValueError("Unexpected chunk structure: 'choices' missing or empty") + + delta = chunk.choices[0].delta + tool_calls = getattr(delta, "tool_calls", None) + + if not tool_calls or not isinstance(tool_calls, list): + continue + + tool_call = tool_calls[0] + id = getattr(tool_call, "id", None) + type = getattr(tool_call, "type", None) + + if id: + assert type == "function", f"Expected type 'function' for id {id}, got '{type}'" \ No newline at end of file From 2023ca01c2fe015656b7bbc7bd0a2deb06fae885 Mon Sep 17 00:00:00 2001 From: Kaushik Deka Date: Mon, 10 Feb 2025 04:34:23 +0000 Subject: [PATCH 2/2] file commit miss --- litellm/litellm_core_utils/streaming_handler.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/litellm/litellm_core_utils/streaming_handler.py b/litellm/litellm_core_utils/streaming_handler.py index 5e9fb7aa76..2996162bdf 100644 --- a/litellm/litellm_core_utils/streaming_handler.py +++ b/litellm/litellm_core_utils/streaming_handler.py @@ -1263,8 +1263,9 @@ class CustomStreamWrapper: and "function" in tool and isinstance(tool["function"], dict) and ("type" not in tool or tool["type"] is None) + and tool.get('id') ): - # if function returned but type set to None - mistral's api returns type: None + # Refer to the test test_function_calling_tool_type tool["type"] = "function" model_response.choices[0].delta = Delta(**_json_delta) except Exception as e: