|
1 | 1 | import unittest |
2 | | -from unittest.mock import MagicMock |
3 | | - |
4 | | -from posthog.ai.otel.spans import is_ai_span |
5 | 2 |
|
| 3 | +from parameterized import parameterized |
6 | 4 |
|
7 | | -def _make_span(name: str = "test", attributes: dict | None = None) -> MagicMock: |
8 | | - span = MagicMock() |
9 | | - span.name = name |
10 | | - span.attributes = attributes or {} |
11 | | - return span |
| 5 | +from posthog.ai.otel.spans import is_ai_span |
| 6 | +from posthog.test.ai.otel.conftest import make_span |
12 | 7 |
|
13 | 8 |
|
14 | 9 | class TestIsAISpan(unittest.TestCase): |
15 | | - def test_matches_gen_ai_name_prefix(self): |
16 | | - self.assertTrue(is_ai_span(_make_span("gen_ai.chat"))) |
17 | | - |
18 | | - def test_matches_llm_name_prefix(self): |
19 | | - self.assertTrue(is_ai_span(_make_span("llm.call"))) |
20 | | - |
21 | | - def test_matches_ai_name_prefix(self): |
22 | | - self.assertTrue(is_ai_span(_make_span("ai.completion"))) |
23 | | - |
24 | | - def test_matches_traceloop_name_prefix(self): |
25 | | - self.assertTrue(is_ai_span(_make_span("traceloop.workflow"))) |
26 | | - |
27 | | - def test_rejects_non_ai_name(self): |
28 | | - self.assertFalse(is_ai_span(_make_span("http.request"))) |
29 | | - self.assertFalse(is_ai_span(_make_span("db.query"))) |
30 | | - self.assertFalse(is_ai_span(_make_span("my_function"))) |
31 | | - |
32 | | - def test_matches_gen_ai_attribute_key(self): |
33 | | - span = _make_span("http.request", {"gen_ai.system": "openai"}) |
34 | | - self.assertTrue(is_ai_span(span)) |
35 | | - |
36 | | - def test_matches_llm_attribute_key(self): |
37 | | - span = _make_span("http.request", {"llm.model": "gpt-4"}) |
38 | | - self.assertTrue(is_ai_span(span)) |
39 | | - |
40 | | - def test_matches_ai_attribute_key(self): |
41 | | - span = _make_span("http.request", {"ai.provider": "anthropic"}) |
42 | | - self.assertTrue(is_ai_span(span)) |
43 | | - |
44 | | - def test_matches_traceloop_attribute_key(self): |
45 | | - span = _make_span("http.request", {"traceloop.entity.name": "chain"}) |
46 | | - self.assertTrue(is_ai_span(span)) |
| 10 | + @parameterized.expand( |
| 11 | + [ |
| 12 | + ("gen_ai", "gen_ai.chat"), |
| 13 | + ("llm", "llm.call"), |
| 14 | + ("ai", "ai.completion"), |
| 15 | + ("traceloop", "traceloop.workflow"), |
| 16 | + ] |
| 17 | + ) |
| 18 | + def test_matches_ai_name_prefix(self, _name, span_name): |
| 19 | + self.assertTrue(is_ai_span(make_span(span_name))) |
| 20 | + |
| 21 | + @parameterized.expand( |
| 22 | + [ |
| 23 | + ("gen_ai", {"gen_ai.system": "openai"}), |
| 24 | + ("llm", {"llm.model": "gpt-4"}), |
| 25 | + ("ai", {"ai.provider": "anthropic"}), |
| 26 | + ("traceloop", {"traceloop.entity.name": "chain"}), |
| 27 | + ] |
| 28 | + ) |
| 29 | + def test_matches_ai_attribute_key(self, _name, attrs): |
| 30 | + self.assertTrue(is_ai_span(make_span("http.request", attrs))) |
| 31 | + |
| 32 | + @parameterized.expand( |
| 33 | + [ |
| 34 | + ("http", "http.request"), |
| 35 | + ("db", "db.query"), |
| 36 | + ("custom", "my_function"), |
| 37 | + ] |
| 38 | + ) |
| 39 | + def test_rejects_non_ai_name(self, _name, span_name): |
| 40 | + self.assertFalse(is_ai_span(make_span(span_name))) |
47 | 41 |
|
48 | 42 | def test_rejects_non_ai_attributes(self): |
49 | | - span = _make_span("http.request", {"http.method": "GET", "http.url": "/"}) |
| 43 | + span = make_span("http.request", {"http.method": "GET", "http.url": "/"}) |
50 | 44 | self.assertFalse(is_ai_span(span)) |
51 | 45 |
|
52 | 46 | def test_empty_span(self): |
53 | | - self.assertFalse(is_ai_span(_make_span("test", {}))) |
| 47 | + self.assertFalse(is_ai_span(make_span("test", {}))) |
54 | 48 |
|
55 | 49 | def test_none_attributes(self): |
56 | | - span = _make_span("test") |
| 50 | + span = make_span("test") |
57 | 51 | span.attributes = None |
58 | 52 | self.assertFalse(is_ai_span(span)) |
0 commit comments