This commit is contained in:
Muspi Merol 2026-10-07 19:53:59 +00:00 • committed by GitHub
commit 59441699b9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 68 additions and 9 deletions

View file

@ -196,19 +196,25 @@ class LangfuseOtelLogger(OpenTelemetry):
"content": getattr(getattr(item, "content", [{}])[0], "text", ""),
}
)
elif item_type == "function_call":
arguments_str = getattr(item, "arguments", "{}")
arguments_obj = (
safe_json_loads(arguments_str, default={})
if isinstance(arguments_str, str)
else arguments_str
)
elif item_type in ("function_call", "custom_tool_call"):
# A custom (freeform) tool carries its payload as a raw string under `input`,
# where a function call carries a JSON string under `arguments`.
if item_type == "custom_tool_call":
payload_key, payload_value = "input", getattr(item, "input", "")
else:
arguments_str = getattr(item, "arguments", "{}")
payload_key = "arguments"
payload_value = (
safe_json_loads(arguments_str, default={})
if isinstance(arguments_str, str)
else arguments_str
)
langfuse_tool_call = {
"id": getattr(item, "id", ""),
"name": getattr(item, "name", ""),
"call_id": getattr(item, "call_id", ""),
"type": "function_call",
"arguments": arguments_obj,
"type": item_type,
payload_key: payload_value,
}
output_items_data.append(langfuse_tool_call)
if output_items_data:

View file

@ -1005,6 +1005,59 @@ class TestLangfuseOtelResponsesAPI:
assert output_data[0]["name"] == "get_weather"
assert output_data[0]["arguments"] == {}
def test_responses_api_with_custom_tool_calls(self):
"""Test Langfuse OTEL logger with Responses API custom_tool_call output."""
from litellm.types.integrations.langfuse_otel import LangfuseSpanAttributes
from litellm.types.responses.main import CustomToolCallOutputItem
# Create Responses API response with a custom (freeform) tool call
response_obj = ResponsesAPIResponse(
id="response-790",
created_at=1625247700,
output=[
CustomToolCallOutputItem(
id="ctc-123",
type="custom_tool_call",
name="exec",
call_id="call-def",
input="console.log('hello')",
status="completed",
)
],
)
kwargs = {
"call_type": "responses",
"messages": [{"role": "user", "content": "Print hello"}],
"model": "gpt-4o",
"optional_params": {},
}
mock_span = MagicMock()
with patch(
"litellm.integrations.arize._utils.safe_set_attribute"
) as mock_safe_set_attribute:
LangfuseOtelLogger._set_langfuse_specific_attributes(
mock_span, kwargs, response_obj
)
output_calls = [
call
for call in mock_safe_set_attribute.call_args_list
if call.args[1] == LangfuseSpanAttributes.OBSERVATION_OUTPUT.value
]
assert len(output_calls) > 0, "observation.output should be set"
output_data = json.loads(output_calls[0].args[2])
assert isinstance(output_data, list)
assert len(output_data) == 1
assert output_data[0]["type"] == "custom_tool_call"
assert output_data[0]["id"] == "ctc-123"
assert output_data[0]["name"] == "exec"
assert output_data[0]["call_id"] == "call-def"
assert output_data[0]["input"] == "console.log('hello')"
if __name__ == "__main__":
pytest.main([__file__])