mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-03 02:22:24 +00:00
Fix Galileo logging to match Langfuse across all endpoint types.
Stop skipping ingest when output is empty and log embeddings with a placeholder so embedding, speech, and other non-text responses are recorded like Langfuse. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
79fd09e2db
commit
44efc95522
2 changed files with 57 additions and 11 deletions
|
|
@ -517,7 +517,7 @@ class GalileoObserve(CustomLogger):
|
|||
"""
|
||||
Mirror Langfuse _get_langfuse_input_output_content for Galileo ingest.
|
||||
|
||||
Returns (input_text, output_text, messages_for_span). output_text None skips ingest.
|
||||
Returns (input_text, output_text, messages_for_span).
|
||||
"""
|
||||
call_type = kwargs.get("call_type")
|
||||
prompt = self._build_prompt(kwargs)
|
||||
|
|
@ -530,10 +530,11 @@ class GalileoObserve(CustomLogger):
|
|||
return self._prompt_to_input_text(prompt), status_message, prompt
|
||||
|
||||
if response_obj is not None and (
|
||||
call_type == "embedding"
|
||||
call_type in ("embedding", "aembedding")
|
||||
or isinstance(response_obj, litellm.EmbeddingResponse)
|
||||
):
|
||||
return self._prompt_to_input_text(prompt), None, prompt
|
||||
# Match Langfuse OTEL: log embeddings without serializing vectors.
|
||||
return self._prompt_to_input_text(prompt), "embedding-output", prompt
|
||||
|
||||
if response_obj is not None and isinstance(response_obj, litellm.ModelResponse):
|
||||
output = self._get_chat_content_for_galileo(response_obj)
|
||||
|
|
@ -627,7 +628,7 @@ class GalileoObserve(CustomLogger):
|
|||
kwargs.get("messages") or [],
|
||||
)
|
||||
|
||||
return self._prompt_to_input_text(prompt), None, kwargs.get("messages") or []
|
||||
return self._prompt_to_input_text(prompt), "", kwargs.get("messages") or []
|
||||
|
||||
def get_output_str_from_response(
|
||||
self, response_obj: Any, kwargs: Dict[str, Any]
|
||||
|
|
@ -713,10 +714,7 @@ class GalileoObserve(CustomLogger):
|
|||
kwargs=kwargs, response_obj=response_obj
|
||||
)
|
||||
if output_text is None:
|
||||
verbose_logger.debug(
|
||||
"Galileo Logger: skipping %s — no text output to log", _call_type
|
||||
)
|
||||
return
|
||||
output_text = ""
|
||||
|
||||
raw_start = slo.get("startTime")
|
||||
raw_end = slo.get("endTime")
|
||||
|
|
|
|||
|
|
@ -357,12 +357,18 @@ def test_galileo_record_to_v2_span_with_tags_and_offset():
|
|||
|
||||
def test_galileo_get_output_str_variants(galileo_v2_env):
|
||||
logger = GalileoObserve()
|
||||
assert logger.get_output_str_from_response(None, {}) is None
|
||||
assert logger.get_output_str_from_response(None, {}) == ""
|
||||
assert (
|
||||
logger.get_output_str_from_response(
|
||||
EmbeddingResponse(), {"call_type": "embedding"}
|
||||
)
|
||||
is None
|
||||
== "embedding-output"
|
||||
)
|
||||
assert (
|
||||
logger.get_output_str_from_response(
|
||||
EmbeddingResponse(), {"call_type": "aembedding"}
|
||||
)
|
||||
== "embedding-output"
|
||||
)
|
||||
|
||||
text_resp = TextCompletionResponse()
|
||||
|
|
@ -414,7 +420,7 @@ def test_galileo_get_output_str_variants(galileo_v2_env):
|
|||
{"call_type": "acompletion", "messages": [{"role": "user", "content": "hi"}]},
|
||||
)
|
||||
|
||||
assert logger.get_output_str_from_response("not-a-supported-type", {}) is None
|
||||
assert logger.get_output_str_from_response("not-a-supported-type", {}) == ""
|
||||
|
||||
|
||||
def test_galileo_get_input_output_error_status_message(galileo_v2_env):
|
||||
|
|
@ -445,6 +451,48 @@ def test_galileo_get_output_str_rerank_response(galileo_v2_env):
|
|||
assert '"relevance_score": 0.98' in output
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_galileo_async_log_success_embedding(galileo_v2_env):
|
||||
import datetime
|
||||
|
||||
logger = GalileoObserve()
|
||||
embedding_response = EmbeddingResponse(
|
||||
data=[{"object": "embedding", "embedding": [0.1, 0.2, 0.3], "index": 0}]
|
||||
)
|
||||
|
||||
mock_response = MagicMock()
|
||||
mock_response.is_success = True
|
||||
mock_response.status_code = 201
|
||||
|
||||
with patch.object(logger.async_httpx_handler, "post", return_value=mock_response):
|
||||
await logger.async_log_success_event(
|
||||
kwargs={
|
||||
"call_type": "aembedding",
|
||||
"model": "text-embedding-3-small",
|
||||
"input": "hello world",
|
||||
"standard_logging_object": {
|
||||
"call_type": "aembedding",
|
||||
"model": "text-embedding-3-small",
|
||||
"prompt_tokens": 2,
|
||||
"completion_tokens": 0,
|
||||
"total_tokens": 2,
|
||||
"response_cost": 0.0,
|
||||
"startTime": datetime.datetime(
|
||||
2026, 5, 25, 12, 0, 0, tzinfo=datetime.timezone.utc
|
||||
).timestamp(),
|
||||
"endTime": datetime.datetime(
|
||||
2026, 5, 25, 12, 0, 1, tzinfo=datetime.timezone.utc
|
||||
).timestamp(),
|
||||
},
|
||||
},
|
||||
response_obj=embedding_response,
|
||||
start_time=datetime.datetime(2026, 5, 25, 12, 0, 0),
|
||||
end_time=datetime.datetime(2026, 5, 25, 12, 0, 1),
|
||||
)
|
||||
|
||||
assert logger.in_memory_records == []
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_galileo_async_log_success_rerank(galileo_v2_env):
|
||||
import datetime
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue