Skip to content

Commit b4a7cd1

Browse files
authored
Standard logging payload (#441)
* Add observability logging for LiteLLM calls Signed-off-by: Trevor Grant <[email protected]> * Add environment-controlled LiteLLM logging modes Signed-off-by: Trevor Grant <[email protected]> --------- Signed-off-by: Trevor Grant <[email protected]>
1 parent e311b1d commit b4a7cd1

File tree

4 files changed

+91
-1
lines changed

4 files changed

+91
-1
lines changed

webapp/packages/api/user-service/agent_factory/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,12 @@
1212

1313
from services.database_service import get_database_service
1414
from services.llm_service import call_llm
15+
from services.litellm_logger import ensure_litellm_logging
1516
from config import settings
1617
from config.provider_config import PROVIDER_CONFIG
1718

19+
ensure_litellm_logging()
20+
1821
async def generate_agent_code(request: GenerateCodeRequest):
1922
"""
2023
Generates agent code based on the provided configuration.

webapp/packages/api/user-service/services/chat_service.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
from litellm import acompletion, ModelResponse
88
import litellm
99

10+
from services.litellm_logger import ensure_litellm_logging
11+
12+
ensure_litellm_logging()
13+
1014
# Configure litellm
1115
litellm.drop_params = True
1216
litellm.set_verbose = False
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import json
2+
import os
3+
from typing import Any
4+
5+
import litellm
6+
from litellm.integrations.custom_logger import CustomLogger
7+
8+
from services.observability_service import get_observability_service
9+
10+
11+
class ObservabilityLiteLLMLogger(CustomLogger):
12+
"""LiteLLM callback that forwards StandardLoggingPayload to observability."""
13+
14+
def __init__(self) -> None:
15+
super().__init__()
16+
self.observability = get_observability_service()
17+
self.logging_mode = _get_logging_mode()
18+
19+
async def async_log_success_event(self, kwargs, response_obj, start_time, end_time):
20+
await self._log_standard_payload(kwargs, level="INFO")
21+
22+
async def async_log_failure_event(self, kwargs, response_obj, start_time, end_time):
23+
await self._log_standard_payload(kwargs, level="ERROR")
24+
25+
async def _log_standard_payload(self, kwargs: Any, level: str) -> None:
26+
if self.logging_mode == "NONE":
27+
return
28+
29+
payload = kwargs.get("standard_logging_object") if isinstance(kwargs, dict) else None
30+
if payload is None:
31+
return
32+
33+
try:
34+
serialized_payload = json.loads(json.dumps(payload, default=str))
35+
except Exception:
36+
serialized_payload = {"error": "Failed to serialize StandardLoggingPayload"}
37+
38+
status = serialized_payload.get("status", "unknown") if serialized_payload else "unknown"
39+
40+
if self.logging_mode == "COST_ONLY":
41+
serialized_payload = {
42+
key: serialized_payload.get(key)
43+
for key in ("response_cost", "cost_breakdown")
44+
if serialized_payload.get(key) is not None
45+
}
46+
message = f"LiteLLM call completed with status: {status}"
47+
48+
self.observability.log(
49+
level=level,
50+
event_type="litellm_call",
51+
message=message,
52+
metadata={"standardLoggingPayload": serialized_payload},
53+
)
54+
55+
56+
_configured_logger = False
57+
58+
59+
def _get_logging_mode() -> str:
60+
mode = os.getenv("LITELLM_LOGGING_MODE", "NONE").upper()
61+
return mode if mode in {"NONE", "ALL", "COST_ONLY"} else "NONE"
62+
63+
64+
def ensure_litellm_logging() -> None:
65+
"""Attach the observability logger to LiteLLM callbacks if not already set."""
66+
global _configured_logger
67+
if _configured_logger or _get_logging_mode() == "NONE":
68+
return
69+
70+
callbacks = getattr(litellm, "callbacks", [])
71+
if not any(isinstance(cb, ObservabilityLiteLLMLogger) for cb in callbacks):
72+
callbacks.append(ObservabilityLiteLLMLogger())
73+
litellm.callbacks = callbacks
74+
75+
_configured_logger = True
76+
77+
78+
# Ensure the logger is registered when the module is imported.
79+
ensure_litellm_logging()

webapp/packages/api/user-service/services/llm_service.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1-
import litellm
21
import asyncio
32
import json
43
from config.provider_config import PROVIDER_CONFIG
54
from typing import Any, Dict, List, Tuple, Optional
5+
import litellm
6+
7+
from services.litellm_logger import ensure_litellm_logging
8+
9+
ensure_litellm_logging()
610

711
async def call_llm(
812
provider: str,

0 commit comments

Comments
 (0)