Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

python KeyError in dsl/parallel.py tools when model calls invalid function #1174

Open
4 of 8 tasks
rog555 opened this issue Nov 13, 2024 · 0 comments
Open
4 of 8 tasks
Labels
bug Something isn't working

Comments

@rog555
Copy link

rog555 commented Nov 13, 2024

  • This is actually a bug report.
  • I am not getting good LLM Results
  • I have tried asking for help in the community on discord or discussions and have not received a response.
  • I have tried searching the documentation and have not found an answer.

What Model are you using?

  • gpt-3.5-turbo
  • gpt-4-turbo
  • gpt-4
  • gpt-4o-mini

Describe the bug
Model sometimes sends completion with invalid tool causing KeyError in dsl/parallel.py

To Reproduce

  1. pip install respx
  2. Create test_invalid_function.py with following code:
test_invalid_function.py
import pytest
from typing import Iterable

import httpx
import instructor
from openai import OpenAI
from openai.types.chat.chat_completion import ChatCompletion
from openai.types.chat.chat_completion import Choice
from openai.types.chat.chat_completion_message import ChatCompletionMessage
from openai.types.chat.chat_completion_message_tool_call import ChatCompletionMessageToolCall
from openai.types.chat.chat_completion_message_tool_call import Function
from pydantic import BaseModel
from respx import MockRouter


class GoogleSearch(BaseModel):
    query: str


@pytest.mark.respx()
def test_parallel_invalid_function(respx_mock: MockRouter) -> None:

    completion = ChatCompletion(
        id="test_id",
        created=1234567890,
        model="gpt-4o-mini",
        object="chat.completion",
        choices=[
            Choice(
                index=0,
                message=ChatCompletionMessage(
                    content=None,
                    role="assistant",
                    tool_calls=[
                        ChatCompletionMessageToolCall(
                            id="1",
                            function=Function(
                                name="InvalidFunction",
                                arguments='{"query": "some search"}',
                            ),
                            type="function",
                        )
                    ],
                ),
                finish_reason="tool_calls",
                logprobs=None
            )
        ],
    )

    respx_mock.post("/v1/chat/completions").mock(
        return_value=httpx.Response(
            200, json=completion.model_dump(mode="json")
        )
    )

    client = OpenAI(api_key='foobar')
    client = instructor.patch(client, mode=instructor.Mode.PARALLEL_TOOLS)

    resp = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "assistant", "content": 'what is foobar?'}],
        response_model=Iterable[GoogleSearch],
    )

    assert len(list(resp)) == 1
  1. pytest test_invalid_function.py

Expected behavior

Ideally instructor should retry/reask telling model to use correct tool names and arguments.

Its possible to use a hook to remove tool names that are not valid, eg the below, but this isnt ideal and can end up with no tool calls and empty content in completion if single invalid function called.

Hook to remove invalid tools from completion
class Response(BaseModel):
    valid_names: List[str] = Field(default_factory=list)

    def callback(self, response):
        if not self.valid_names:
            return
        # sometimes model injects invalid tool names
        if isinstance(completion, ChatCompletion):
            for choice in completion.choices:
                remove_idxs = []
                if choice.message.tool_calls is None:
                    continue
                for idx in range(len(choice.message.tool_calls)):
                    tool_call = choice.message.tool_calls[idx]
                    if not isinstance(tool_call, ChatCompletionMessageToolCall):
                        continue
                    if tool_call.type != 'function':
                        continue
                    if tool_call.function.name not in self.valid_names:
                        remove_idxs.append(idx)
                choice.message.tool_calls = [
                    tc for idx, tc in enumerate(choice.message.tool_calls)
                    if idx not in remove_idxs
                ]

response = Response(valid_names=['GoogleFunction'])
client.on("completion:response", response.callback)

Screenshots

image
@github-actions github-actions bot added the bug Something isn't working label Nov 13, 2024
@rog555 rog555 changed the title KeyError in dsl/parallel.py tools when model calls invalid function python KeyError in dsl/parallel.py tools when model calls invalid function Nov 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

1 participant