fix(llm): serialize OpenAI tool call messages

This commit is contained in:
Iams4kura
2026-09-04 10:40:20 +08:00
parent e04ea0b9cc
commit 3f702ed40c
5 changed files with 72 additions and 4 deletions
+17
View File
@@ -2,6 +2,7 @@
from __future__ import annotations
import json
from dataclasses import dataclass, field
from enum import Enum
from typing import Any
@@ -44,6 +45,22 @@ class Message:
]
return result
def to_openai_dict(self) -> dict[str, Any]:
result = self.to_dict()
if self.tool_calls:
result["tool_calls"] = [
{
"id": tc.id,
"type": "function",
"function": {
"name": tc.name,
"arguments": json.dumps(tc.arguments),
},
}
for tc in self.tool_calls
]
return result
@dataclass(frozen=True)
class ToolDefinition:
+1 -1
View File
@@ -70,7 +70,7 @@ class _AstraflowBaseProvider(LLMProvider):
try:
params: dict[str, Any] = {
"model": llm_input.model or self.default_model,
"messages": [msg.to_dict() for msg in llm_input.messages],
"messages": [msg.to_openai_dict() for msg in llm_input.messages],
}
if llm_input.temperature != 1.0:
params["temperature"] = llm_input.temperature
+1 -1
View File
@@ -83,7 +83,7 @@ class AtlasProvider(LLMProvider):
try:
params: dict[str, Any] = {
"model": llm_input.model or self.default_model,
"messages": [msg.to_dict() for msg in llm_input.messages],
"messages": [msg.to_openai_dict() for msg in llm_input.messages],
}
if llm_input.temperature != 1.0:
params["temperature"] = llm_input.temperature
+1 -1
View File
@@ -72,7 +72,7 @@ class OpenAIProvider(LLMProvider):
try:
params: dict[str, Any] = {
"model": input.model or "gpt-4o-mini",
"messages": [msg.to_dict() for msg in input.messages],
"messages": [msg.to_openai_dict() for msg in input.messages],
"temperature": input.temperature,
}
if input.max_tokens:
+52 -1
View File
@@ -2,7 +2,9 @@ from types import SimpleNamespace
import pytest
from llm.core.types import LLMInput, Message, Role, ToolDefinition
from llm.core.types import LLMInput, Message, Role, ToolCall, ToolDefinition
from llm.providers.astraflow import AstraflowProvider
from llm.providers.atlas import AtlasProvider
from llm.providers.claude import ClaudeProvider
from llm.providers.constants import EMPTY_FILTERED_RESPONSE_ERROR
from llm.providers.openai import OpenAIProvider
@@ -84,6 +86,55 @@ def test_openai_provider_serializes_tools_for_chat_completions():
]
@pytest.mark.parametrize(
"provider_type",
[OpenAIProvider, AstraflowProvider, AtlasProvider],
)
def test_openai_compatible_provider_serializes_assistant_tool_calls(provider_type):
provider = provider_type(api_key="test")
client = _OpenAIClient()
provider.client = client
provider.generate(
LLMInput(
messages=[
Message(role=Role.USER, content="Find ECC"),
Message(
role=Role.ASSISTANT,
content="",
tool_calls=[
ToolCall(
id="call_1",
name="search",
arguments={"query": "ecc"},
)
],
),
Message(
role=Role.TOOL,
content="result",
tool_call_id="call_1",
),
]
)
)
assert client.completions.params["messages"][1] == {
"role": "assistant",
"content": "",
"tool_calls": [
{
"id": "call_1",
"type": "function",
"function": {
"name": "search",
"arguments": '{"query": "ecc"}',
},
}
],
}
def test_openai_provider_can_be_constructed_without_credentials(monkeypatch):
monkeypatch.delenv("OPENAI_API_KEY", raising=False)