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

OpenAI agent cannot properly parse various complex function argument types #1208

Closed
mykola-mokhnach-parloa opened this issue Dec 11, 2024 · 4 comments
Labels
bug Something isn't working

Comments

@mykola-mokhnach-parloa
Copy link

mykola-mokhnach-parloa commented Dec 11, 2024

Steps to reproduce:

  1. Define a new function similar to the example in
    fnc_ctx = llm.FunctionContext()
  2. Set the function argument type to Optional[str] or Union[int, type(None)] or float | None
  3. Run the pipeline

Actual result:
An exception stack trace is raised because the build_oai_function_description type support is limited to only (str, int, float, bool), even though the FunctionContext class is capable of recognizing the above nullable types.

Expected result:
The build_oai_function_description mentioned above should be able to properly parse nullable types like ones above.
For example, we expect Optional[str] to be transformed into ["string", "null"] OpenAI type definition.

@davidzhao
Copy link
Member

fixed in #1211

@mykola-mokhnach-parloa
Copy link
Author

Unfortunately the above change does not completely fix the original issue. The build_oai_function_description function remains the same and is still unable to parse optional type definitions. It would be great to have an actual unit test that verifies parsing of above cases and confirms the actual fix.

@jayeshp19
Copy link
Collaborator

Hi @mykola-mokhnach-parloa,

I’m unable to reproduce this issue. Could you please share the parameters you're passing or a screenshot of the error you're encountering?

Fetching the first argument does not work for type definitions like Union[None, str] or None | str, where None is passed as the first argument. The validation should check if the argument length is between 1 and 2, and then take the first non-None type argument.

I believe these cases are already handled in this function:

def _is_optional_type(typ) -> Tuple[bool, Any]:
"""return is_optional, inner_type"""
origin = typing.get_origin(typ)
if origin in {typing.Union, getattr(__builtins__, "UnionType", typing.Union)}:
args = typing.get_args(typ)
is_optional = type(None) in args
inner_arg = None
for arg in args:
if arg is not type(None):
inner_arg = arg
break
return is_optional, inner_arg
return False, None

cc: @theomonnom

@mykola-mokhnach-parloa
Copy link
Author

mykola-mokhnach-parloa commented Dec 12, 2024

thanks for your reply @jayeshp19

I expect the below unit tests to pass (currently some of them fail):

import pytest
from typing import Union, Optional
from inspect import _empty

from livekit.agents.llm import FunctionInfo, FunctionArgInfo
from livekit.agents.llm.function_context import _is_optional_type
from livekit.agents import llm


def test_typing():
    assert _is_optional_type(Optional[int]) == (True, int)
    assert _is_optional_type(Union[str, None]) == (True, str)
    assert _is_optional_type(None | float) == (True, float)
    assert _is_optional_type(Union[str, int]) == (False, None)


@pytest.mark.parametrize(
    ("arg_typ", "oai_type"),
    [
        (int, "number"),
        (Optional[int], ["number", "null"]),
        (None | int, ["number", "null"]),
        (Union[None, int], ["number", "null"]),
        (Union[str, None], ["string", "null"]),
    ]
)
def test_description_building(arg_typ: type, oai_type: str | list[str]):
    fi = FunctionInfo(
        name='foo',
        description='foo',
        auto_retry=False,
        callable=lambda: None,
        arguments={
            'arg': FunctionArgInfo(
                name='foo',
                description='foo',
                type=arg_typ,
                default=_empty,
                choices=(),
            ),
        }
    )
    assert llm._oai_api.build_oai_function_description(
        fi
    )['function']['parameters']['properties']['foo']['type'] == oai_type

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

3 participants