Skip to content

Conversation

@caffeinism
Copy link
Contributor

@caffeinism caffeinism commented Jul 5, 2025

I have implemented the issue (#50)

I also changed the OpenAI related messages internally from BaseModel to the actual internal data format of openai python sdk. This shouldn't be an issue in the data format actually used by users, since we only added tools and tool_choice to CreateAgentDto.

In the chat completion unit test, message.role = "ai" was incorrect for the openai message format, so i changed it to "assistant".


Example:

Server

class AgentFactory(BaseAgentFactory):
    async def create_agent(self, dto: CreateAgentDto):
        llm = self.create_llm(
            ...
        )
        return llm.bind_tools(dto.tools)

Client

tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "Get current temperature for a given location.",
        "parameters": {
            "type": "object",
            "properties": {
                "location": {
                    "type": "string",
                    "description": "City and country e.g. Bogotá, Colombia"
                }
            },
            "required": [
                "location"
            ],
            "additionalProperties": False
        },
        "strict": True
    }
}]

chat_completion = await openai_client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[
        {
            "role": "user",
            "content": 'What is the weather like in London today?',
        }
    ],
    tools=tools,
)

# ChatCompletion(id='...', choices=[Choice(finish_reason='tool_calls', index=0, logprobs=None, message=ChatCompletionMessage(content=None, refusal=None, role='assistant', annotations=None, audio=None, function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='...', function=Function(arguments='{"location": "London, UK"}', name='get_weather'), type='function')]))], created=1751731311, model='gpt-4o-mini', object='chat.completion', service_tier=None, system_fingerprint='', usage=CompletionUsage(completion_tokens=-1, prompt_tokens=-1, total_tokens=-1, completion_tokens_details=None, prompt_tokens_details=None))

Copy link
Owner

@samuelint samuelint left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A functional test is missing to verify that the new feature works as intended.
Adding one would help ensure the feature behaves correctly and prevent regressions in future changes.

@samuelint
Copy link
Owner

For tests to pass in the pipeline, please have secrets set in your GitHub pipeline.

ANTHROPIC_API_KEY
GROQ_API_KEY
OPENAI_API_KEY

image

@caffeinism
Copy link
Contributor Author

For tests to pass in the pipeline, please have secrets set in your GitHub pipeline.

ANTHROPIC_API_KEY

GROQ_API_KEY

OPENAI_API_KEY

image

Oh, so that should be added to my repository?

@samuelint
Copy link
Owner

For tests to pass in the pipeline, please have secrets set in your GitHub pipeline.
ANTHROPIC_API_KEY
GROQ_API_KEY
OPENAI_API_KEY
image

Oh, so that should be added to my repository?

Yes, since the project is forked, it should be set in your repository. This is a known limitation on GitHub, where my secrets cannot be used in forked pull-requests.

@caffeinism
Copy link
Contributor Author

For tests to pass in the pipeline, please have secrets set in your GitHub pipeline.
ANTHROPIC_API_KEY
GROQ_API_KEY
OPENAI_API_KEY
image

Oh, so that should be added to my repository?

Yes, since the project is forked, it should be set in your repository. This is a known limitation on GitHub, where my secrets cannot be used in forked pull-requests.

For tests, what format should I add tests in? I'm not very familiar with something like this. In functional tests, I'm thinking of doing simple tool calls like addition to verify behavior, but for unit tests, I'm not sure how I should write them down. Should I just add the various tests in the format they're currently added in (I've added one commit as an example)?

For repository secrets, I've added them to my own repository, but it's a bit confusing to me that secrets from my own repository are used for actions that run on PRs here.

@caffeinism caffeinism force-pushed the support-tool-response branch from d115c6e to 1cfb88b Compare July 7, 2025 06:57
@caffeinism
Copy link
Contributor Author

I added a simple unittest and functional test for the function call functionality. I also found where the behavior of the server differs from the actual openai server, so I changed it (only the first chunk should contain role)

Copy link
Owner

@samuelint samuelint left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great job, one last minor comment and it will be ready to be merged.

assert result.delta.role == "assistant"

def test_message_have_assistant_role_by_default(self):
def test_delta_message_have_none_role_by_default(self):
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test was there for a reason. It might break something, I think the behaviour should remains as before except if you have a good reason for it.

assert result.choices[0].delta.role == "assistant"

def test_message_have_assistant_role_by_default(self):
def test_delta_message_have_none_role_by_default(self):
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test was there for a reason. It might break something, I think the behaviour should remains as before except if you have a good reason for it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If so, is it okay to leave “assistant” as the default, but in the first actual behavior, only send “assistant” for the first chunk and None for the rest?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change is due to:

I also found where the behavior of the server differs from the actual openai server, so I changed it (only the first chunk should contain role)

When returning a chunk, the openai server returns None by default, and only the very first chunk returns role.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's ok if this is the default openai api behaviour.

Please add a unit test in the langchain_openai_api_bridge/chat_completion/langchain_stream_adapter.py unit test.

Have a test function name that explain that behaviour, "Only the first chunk has role assistant, afterward role is None". That will document the use case.

result = to_openai_chat_completion_chunk_object(event)

assert result.choices[0].delta.function_call is not None
assert result.choices[0].delta.function_call.name == "my_func"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test does not validate the args property. I think it should have a test for the name and another for the args



def test_chat_completion_function_call_weather_stream(openai_client: OpenAI):
tools = [{
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since tools section is the same in each tests, it could be re-used instead of copy/paste

@samuelint
Copy link
Owner

For tests to pass in the pipeline, please have secrets set in your GitHub pipeline.
ANTHROPIC_API_KEY
GROQ_API_KEY
OPENAI_API_KEY
image

Oh, so that should be added to my repository?

Yes, since the project is forked, it should be set in your repository. This is a known limitation on GitHub, where my secrets cannot be used in forked pull-requests.

For tests, what format should I add tests in? I'm not very familiar with something like this. In functional tests, I'm thinking of doing simple tool calls like addition to verify behavior, but for unit tests, I'm not sure how I should write them down. Should I just add the various tests in the format they're currently added in (I've added one commit as an example)?

For repository secrets, I've added them to my own repository, but it's a bit confusing to me that secrets from my own repository are used for actions that run on PRs here.

What you did is fine, you've respected how other tests are made. The goal of functional tests is to validate the feature work from the API standpoint.

@caffeinism caffeinism force-pushed the support-tool-response branch from 34bf370 to 219e233 Compare July 8, 2025 03:56
assert result.choices[0].delta.role == "assistant"

def test_message_have_assistant_role_by_default(self):
def test_delta_message_have_none_role_by_default(self):
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's ok if this is the default openai api behaviour.

Please add a unit test in the langchain_openai_api_bridge/chat_completion/langchain_stream_adapter.py unit test.

Have a test function name that explain that behaviour, "Only the first chunk has role assistant, afterward role is None". That will document the use case.

@caffeinism
Copy link
Contributor Author

I've add it

@samuelint samuelint changed the base branch from main to feat-Support-function-call-requests/responses-(from-fork) July 11, 2025 14:35
@samuelint samuelint merged commit e0b6a06 into samuelint:feat-Support-function-call-requests/responses-(from-fork) Jul 11, 2025
1 check failed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants