Skip to content

Commit 0f8b10b

Browse files
authored
feat(ai): add error handling to python ai sdk (#174)
1 parent 45dc933 commit 0f8b10b

File tree

6 files changed

+47
-1
lines changed

6 files changed

+47
-1
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 3.10.0 - 2025-01-24
2+
3+
1. Add `$ai_error` and `$ai_is_error` properties to LangChain callback handler, OpenAI, and Anthropic.
4+
15
## 3.9.2 - 2025-01-22
26

37
1. Fix importing of LangChain callback handler under certain circumstances.

posthog/ai/langchain/callbacks.py

+2
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,8 @@ def on_llm_error(
305305
"$ai_latency": latency,
306306
"$ai_trace_id": trace_id,
307307
"$ai_base_url": run.get("base_url"),
308+
"$ai_is_error": True,
309+
"$ai_error": error.__str__(),
308310
**self._properties,
309311
}
310312
if self._distinct_id is None:

posthog/ai/utils.py

+12
Original file line numberDiff line numberDiff line change
@@ -116,12 +116,17 @@ def call_llm_and_track_usage(
116116
error = None
117117
http_status = 200
118118
usage: Dict[str, Any] = {}
119+
error_params: Dict[str, any] = {}
119120

120121
try:
121122
response = call_method(**kwargs)
122123
except Exception as exc:
123124
error = exc
124125
http_status = getattr(exc, "status_code", 0) # default to 0 becuase its likely an SDK error
126+
error_params = {
127+
"$ai_is_error": True,
128+
"$ai_error": exc.__str__(),
129+
}
125130
finally:
126131
end_time = time.time()
127132
latency = end_time - start_time
@@ -149,6 +154,7 @@ def call_llm_and_track_usage(
149154
"$ai_trace_id": posthog_trace_id,
150155
"$ai_base_url": str(base_url),
151156
**(posthog_properties or {}),
157+
**(error_params or {}),
152158
}
153159

154160
if posthog_distinct_id is None:
@@ -186,12 +192,17 @@ async def call_llm_and_track_usage_async(
186192
error = None
187193
http_status = 200
188194
usage: Dict[str, Any] = {}
195+
error_params: Dict[str, any] = {}
189196

190197
try:
191198
response = await call_async_method(**kwargs)
192199
except Exception as exc:
193200
error = exc
194201
http_status = getattr(exc, "status_code", 0) # default to 0 because its likely an SDK error
202+
error_params = {
203+
"$ai_is_error": True,
204+
"$ai_error": exc.__str__(),
205+
}
195206
finally:
196207
end_time = time.time()
197208
latency = end_time - start_time
@@ -219,6 +230,7 @@ async def call_llm_and_track_usage_async(
219230
"$ai_trace_id": posthog_trace_id,
220231
"$ai_base_url": str(base_url),
221232
**(posthog_properties or {}),
233+
**(error_params or {}),
222234
}
223235

224236
if posthog_distinct_id is None:

posthog/test/ai/anthropic/test_anthropic.py

+14
Original file line numberDiff line numberDiff line change
@@ -325,3 +325,17 @@ async def test_async_streaming_system_prompt(mock_client, mock_anthropic_stream)
325325
{"role": "system", "content": "You must always answer with 'Bar'."},
326326
{"role": "user", "content": "Foo"},
327327
]
328+
329+
330+
def test_error(mock_client, mock_anthropic_response):
331+
with patch("anthropic.resources.Messages.create", side_effect=Exception("Test error")):
332+
client = Anthropic(api_key="test-key", posthog_client=mock_client)
333+
with pytest.raises(Exception):
334+
client.messages.create(model="claude-3-opus-20240229", messages=[{"role": "user", "content": "Hello"}])
335+
336+
assert mock_client.capture.call_count == 1
337+
338+
call_args = mock_client.capture.call_args[1]
339+
props = call_args["properties"]
340+
assert props["$ai_is_error"] is True
341+
assert props["$ai_error"] == "Test error"

posthog/test/ai/openai/test_openai.py

+14
Original file line numberDiff line numberDiff line change
@@ -173,3 +173,17 @@ def test_privacy_mode_global(mock_client, mock_openai_response):
173173
props = call_args["properties"]
174174
assert props["$ai_input"] is None
175175
assert props["$ai_output_choices"] is None
176+
177+
178+
def test_error(mock_client, mock_openai_response):
179+
with patch("openai.resources.chat.completions.Completions.create", side_effect=Exception("Test error")):
180+
client = OpenAI(api_key="test-key", posthog_client=mock_client)
181+
with pytest.raises(Exception):
182+
client.chat.completions.create(model="gpt-4", messages=[{"role": "user", "content": "Hello"}])
183+
184+
assert mock_client.capture.call_count == 1
185+
186+
call_args = mock_client.capture.call_args[1]
187+
props = call_args["properties"]
188+
assert props["$ai_is_error"] is True
189+
assert props["$ai_error"] == "Test error"

posthog/version.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
VERSION = "3.9.3"
1+
VERSION = "3.10.0"
22

33
if __name__ == "__main__":
44
print(VERSION, end="") # noqa: T201

0 commit comments

Comments
 (0)