langtrace-python-sdk 1.3.6__py3-none-any.whl → 2.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. examples/anthropic_example/completion.py +6 -6
  2. examples/cohere_example/chat.py +5 -4
  3. examples/cohere_example/chat_stream.py +2 -4
  4. examples/cohere_example/{embed_create.py → embed.py} +4 -3
  5. examples/cohere_example/rerank.py +31 -0
  6. examples/cohere_example/tools.py +40 -0
  7. examples/openai_example/chat_completion.py +41 -0
  8. examples/{openai → openai_example}/embeddings_create.py +3 -2
  9. examples/{openai → openai_example}/function_calling.py +3 -5
  10. examples/{openai → openai_example}/images_generate.py +1 -1
  11. examples/openai_example/tool_calling.py +67 -0
  12. examples/{openai → openai_example}/tool_calling_nonstreaming.py +2 -1
  13. langtrace_python_sdk/__init__.py +14 -1
  14. langtrace_python_sdk/constants/instrumentation/cohere.py +6 -1
  15. langtrace_python_sdk/instrumentation/anthropic/instrumentation.py +16 -4
  16. langtrace_python_sdk/instrumentation/anthropic/patch.py +26 -6
  17. langtrace_python_sdk/instrumentation/chroma/instrumentation.py +14 -2
  18. langtrace_python_sdk/instrumentation/chroma/patch.py +16 -4
  19. langtrace_python_sdk/instrumentation/cohere/instrumentation.py +24 -7
  20. langtrace_python_sdk/instrumentation/cohere/patch.py +295 -95
  21. langtrace_python_sdk/instrumentation/langchain/instrumentation.py +14 -3
  22. langtrace_python_sdk/instrumentation/langchain/patch.py +16 -4
  23. langtrace_python_sdk/instrumentation/langchain_community/instrumentation.py +15 -2
  24. langtrace_python_sdk/instrumentation/langchain_community/patch.py +20 -3
  25. langtrace_python_sdk/instrumentation/langchain_core/instrumentation.py +14 -4
  26. langtrace_python_sdk/instrumentation/langchain_core/patch.py +19 -7
  27. langtrace_python_sdk/instrumentation/llamaindex/instrumentation.py +15 -11
  28. langtrace_python_sdk/instrumentation/llamaindex/patch.py +20 -10
  29. langtrace_python_sdk/instrumentation/openai/instrumentation.py +20 -9
  30. langtrace_python_sdk/instrumentation/openai/patch.py +112 -78
  31. langtrace_python_sdk/instrumentation/pinecone/instrumentation.py +14 -3
  32. langtrace_python_sdk/instrumentation/pinecone/patch.py +17 -4
  33. langtrace_python_sdk/langtrace.py +40 -35
  34. langtrace_python_sdk/utils/llm.py +17 -4
  35. langtrace_python_sdk/utils/with_root_span.py +21 -5
  36. langtrace_python_sdk/version.py +1 -1
  37. {langtrace_python_sdk-1.3.6.dist-info → langtrace_python_sdk-2.0.0.dist-info}/METADATA +2 -2
  38. {langtrace_python_sdk-1.3.6.dist-info → langtrace_python_sdk-2.0.0.dist-info}/RECORD +53 -45
  39. tests/anthropic/cassettes/test_anthropic.yaml +85 -0
  40. tests/anthropic/cassettes/test_anthropic_streaming.yaml +456 -0
  41. tests/anthropic/cassettes/test_async_anthropic_streaming.yaml +328 -0
  42. tests/anthropic/conftest.py +38 -0
  43. tests/anthropic/test_anthropic.py +108 -72
  44. tests/conftest.py +17 -0
  45. tests/openai/conftest.py +5 -13
  46. tests/openai/test_chat_completion.py +21 -0
  47. tests/openai/test_image_generation.py +20 -8
  48. examples/openai/chat_completion.py +0 -58
  49. /examples/{openai → openai_example}/__init__.py +0 -0
  50. /examples/{openai → openai_example}/async_tool_calling_nonstreaming.py +0 -0
  51. /examples/{openai → openai_example}/async_tool_calling_streaming.py +0 -0
  52. /examples/{openai → openai_example}/tool_calling_streaming.py +0 -0
  53. {langtrace_python_sdk-1.3.6.dist-info → langtrace_python_sdk-2.0.0.dist-info}/WHEEL +0 -0
  54. {langtrace_python_sdk-1.3.6.dist-info → langtrace_python_sdk-2.0.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,328 @@
1
+ interactions:
2
+ - request:
3
+ body: '{"max_tokens": 1024, "messages": [{"role": "user", "content": "How are
4
+ you today?"}], "model": "claude-3-opus-20240229", "stream": true}'
5
+ headers:
6
+ accept:
7
+ - application/json
8
+ accept-encoding:
9
+ - gzip, deflate
10
+ anthropic-version:
11
+ - '2023-06-01'
12
+ connection:
13
+ - keep-alive
14
+ content-length:
15
+ - '136'
16
+ content-type:
17
+ - application/json
18
+ host:
19
+ - api.anthropic.com
20
+ user-agent:
21
+ - AsyncAnthropic/Python 0.25.6
22
+ x-stainless-arch:
23
+ - arm64
24
+ x-stainless-async:
25
+ - async:asyncio
26
+ x-stainless-lang:
27
+ - python
28
+ x-stainless-os:
29
+ - MacOS
30
+ x-stainless-package-version:
31
+ - 0.25.6
32
+ x-stainless-runtime:
33
+ - CPython
34
+ x-stainless-runtime-version:
35
+ - 3.11.5
36
+ method: POST
37
+ uri: https://api.anthropic.com/v1/messages
38
+ response:
39
+ body:
40
+ string: 'event: message_start
41
+
42
+ data: {"type":"message_start","message":{"id":"msg_01WefXuaU9ctjjQqxDgV2v4r","type":"message","role":"assistant","model":"claude-3-opus-20240229","stop_sequence":null,"usage":{"input_tokens":12,"output_tokens":1},"content":[],"stop_reason":null}}
43
+
44
+
45
+ event: content_block_start
46
+
47
+ data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""} }
48
+
49
+
50
+ event: ping
51
+
52
+ data: {"type": "ping"}
53
+
54
+
55
+ event: content_block_delta
56
+
57
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"As"} }
58
+
59
+
60
+ event: content_block_delta
61
+
62
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"
63
+ an"} }
64
+
65
+
66
+ event: content_block_delta
67
+
68
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"
69
+ AI"} }
70
+
71
+
72
+ event: content_block_delta
73
+
74
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"
75
+ language"}}
76
+
77
+
78
+ event: content_block_delta
79
+
80
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"
81
+ model"} }
82
+
83
+
84
+ event: content_block_delta
85
+
86
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":","} }
87
+
88
+
89
+ event: content_block_delta
90
+
91
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"
92
+ I"} }
93
+
94
+
95
+ event: content_block_delta
96
+
97
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"
98
+ don"} }
99
+
100
+
101
+ event: content_block_delta
102
+
103
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"''t"} }
104
+
105
+
106
+ event: content_block_delta
107
+
108
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"
109
+ have"}}
110
+
111
+
112
+ event: content_block_delta
113
+
114
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"
115
+ feelings"} }
116
+
117
+
118
+ event: content_block_delta
119
+
120
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":","} }
121
+
122
+
123
+ event: content_block_delta
124
+
125
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"
126
+ but"} }
127
+
128
+
129
+ event: content_block_delta
130
+
131
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"
132
+ I"} }
133
+
134
+
135
+ event: content_block_delta
136
+
137
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"''m"} }
138
+
139
+
140
+ event: content_block_delta
141
+
142
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"
143
+ functioning"}}
144
+
145
+
146
+ event: content_block_delta
147
+
148
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"
149
+ well"} }
150
+
151
+
152
+ event: content_block_delta
153
+
154
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"
155
+ an"} }
156
+
157
+
158
+ event: content_block_delta
159
+
160
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"d
161
+ ready"} }
162
+
163
+
164
+ event: content_block_delta
165
+
166
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"
167
+ to"} }
168
+
169
+
170
+ event: content_block_delta
171
+
172
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"
173
+ assist"} }
174
+
175
+
176
+ event: content_block_delta
177
+
178
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"
179
+ you"} }
180
+
181
+
182
+ event: content_block_delta
183
+
184
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"
185
+ with"} }
186
+
187
+
188
+ event: content_block_delta
189
+
190
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"
191
+ any"} }
192
+
193
+
194
+ event: content_block_delta
195
+
196
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"
197
+ questions"} }
198
+
199
+
200
+ event: content_block_delta
201
+
202
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"
203
+ or"}}
204
+
205
+
206
+ event: content_block_delta
207
+
208
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"
209
+ tasks"} }
210
+
211
+
212
+ event: content_block_delta
213
+
214
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"
215
+ you"}}
216
+
217
+
218
+ event: content_block_delta
219
+
220
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"
221
+ may"} }
222
+
223
+
224
+ event: content_block_delta
225
+
226
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"
227
+ have"} }
228
+
229
+
230
+ event: content_block_delta
231
+
232
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"."} }
233
+
234
+
235
+ event: content_block_delta
236
+
237
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"
238
+ How"} }
239
+
240
+
241
+ event: content_block_delta
242
+
243
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"
244
+ can"} }
245
+
246
+
247
+ event: content_block_delta
248
+
249
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"
250
+ I"} }
251
+
252
+
253
+ event: content_block_delta
254
+
255
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"
256
+ help"} }
257
+
258
+
259
+ event: content_block_delta
260
+
261
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"
262
+ you"} }
263
+
264
+
265
+ event: content_block_delta
266
+
267
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"
268
+ today"} }
269
+
270
+
271
+ event: content_block_delta
272
+
273
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"?"} }
274
+
275
+
276
+ event: content_block_stop
277
+
278
+ data: {"type":"content_block_stop","index":0 }
279
+
280
+
281
+ event: message_delta
282
+
283
+ data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"output_tokens":41} }
284
+
285
+
286
+ event: message_stop
287
+
288
+ data: {"type":"message_stop" }
289
+
290
+
291
+ '
292
+ headers:
293
+ CF-Cache-Status:
294
+ - DYNAMIC
295
+ CF-RAY:
296
+ - 87963ba7cab90d92-MRS
297
+ Cache-Control:
298
+ - no-cache
299
+ Connection:
300
+ - keep-alive
301
+ Content-Type:
302
+ - text/event-stream; charset=utf-8
303
+ Date:
304
+ - Wed, 24 Apr 2024 12:52:54 GMT
305
+ Server:
306
+ - cloudflare
307
+ Transfer-Encoding:
308
+ - chunked
309
+ anthropic-ratelimit-requests-limit:
310
+ - '50'
311
+ anthropic-ratelimit-requests-remaining:
312
+ - '49'
313
+ anthropic-ratelimit-requests-reset:
314
+ - '2024-04-24T12:53:29Z'
315
+ anthropic-ratelimit-tokens-limit:
316
+ - '20000'
317
+ anthropic-ratelimit-tokens-remaining:
318
+ - '19000'
319
+ anthropic-ratelimit-tokens-reset:
320
+ - '2024-04-24T12:53:29Z'
321
+ request-id:
322
+ - req_0165QboZpxrrNfD1oVun4cPB
323
+ via:
324
+ - 1.1 google
325
+ status:
326
+ code: 200
327
+ message: OK
328
+ version: 1
@@ -0,0 +1,38 @@
1
+ """Unit tests configuration module."""
2
+
3
+ import pytest
4
+ import os
5
+ from anthropic import Anthropic, AsyncAnthropic
6
+
7
+ from langtrace_python_sdk.instrumentation.anthropic.instrumentation import (
8
+ AnthropicInstrumentation,
9
+ )
10
+
11
+
12
+ @pytest.fixture(autouse=True)
13
+ def environment():
14
+ if not os.getenv("ANTHROPIC_API_KEY"):
15
+ os.environ["ANTHROPIC_API_KEY"] = "test_api_key"
16
+
17
+
18
+ @pytest.fixture
19
+ def anthropic_client():
20
+ return Anthropic()
21
+
22
+
23
+ @pytest.fixture
24
+ def async_anthropic_client():
25
+ return AsyncAnthropic()
26
+
27
+
28
+ @pytest.fixture(scope="module")
29
+ def vcr_config():
30
+ return {"filter_headers": ["authorization", "x-api-key"]}
31
+
32
+
33
+ @pytest.fixture(scope="session", autouse=True)
34
+ def instrument():
35
+ AnthropicInstrumentation().instrument()
36
+
37
+ yield
38
+ # exporter.shutdown()
@@ -1,73 +1,109 @@
1
- import unittest
2
- from unittest.mock import MagicMock, call
3
- from langtrace_python_sdk.instrumentation.anthropic.patch import messages_create
4
- from opentelemetry.trace import SpanKind
5
- import importlib.metadata
6
- from langtrace_python_sdk.constants.instrumentation.anthropic import APIS
7
- from opentelemetry.trace.status import Status, StatusCode
1
+ import pytest
8
2
  import json
9
- from langtrace.trace_attributes import Event, LLMSpanAttributes
10
-
11
- from tests.utils import common_setup
12
-
13
- class TestAnthropic(unittest.TestCase):
14
-
15
- data = {
16
- "content" : [MagicMock(text="Some text", type="text")],
17
- "system_fingerprint" : "None",
18
- "usage" : MagicMock(input_tokens=23, output_tokens=44),
19
- "chunks" : [MagicMock(delta="Some text", message="text")]}
20
-
21
-
22
- def setUp(self):
23
-
24
- # Mock the original method
25
- self.anthropic_mock, self.tracer, self.span = common_setup(self.data, None)
26
-
27
- def tearDown(self):
28
- pass
29
-
30
- def test_anthropic(self):
31
- # Arrange
32
- version = importlib.metadata.version('anthropic')
33
- kwargs = {
34
- "model": "claude-3-opus-20240229",
35
- "messages" : [{"role": "user", "content": "How are you today?"}],
36
- "stream": False
37
- }
38
-
39
- # Act
40
- wrapped_function = messages_create("anthropic.messages.create", version, self.tracer)
41
- result = wrapped_function(self.anthropic_mock, MagicMock(), (), kwargs)
42
-
43
-
44
- # Assert
45
- self.assertTrue(self.tracer.start_as_current_span.called_once_with("anthropic.messages.create", kind=SpanKind.CLIENT))
46
- self.assertTrue(self.span.set_status.has_calls([call(Status(StatusCode.OK))]))
47
-
48
- expected_attributes = {
49
- "langtrace.sdk.name": "langtrace-python-sdk",
50
- "langtrace.service.name": "Anthropic",
51
- "langtrace.service.type": "llm",
52
- "langtrace.service.version": version,
53
- "langtrace.version": "1.0.0",
54
- "url.full": "/v1/messages",
55
- "llm.api": APIS["MESSAGES_CREATE"]["ENDPOINT"],
56
- "llm.model": kwargs.get("model"),
57
- "llm.prompts": json.dumps(kwargs.get("messages", [])),
58
- "llm.stream": kwargs.get("stream"),
59
- }
60
-
61
- self.assertTrue(
62
- self.span.set_attribute.has_calls(
63
- [call(key, value) for key, value in expected_attributes.items()], any_order=True
64
- )
65
- )
66
-
67
- expected_result_data = {"system_fingerprint": "None" }
68
-
69
- self.assertEqual(result.system_fingerprint, expected_result_data["system_fingerprint"])
70
-
71
-
72
- if __name__ == '__main__':
73
- unittest.main()
3
+ import importlib
4
+ from langtrace_python_sdk.constants.instrumentation.anthropic import APIS
5
+
6
+
7
+ @pytest.mark.vcr()
8
+ def test_anthropic(anthropic_client, exporter):
9
+ llm_model_value = "claude-3-opus-20240229"
10
+ messages_value = [{"role": "user", "content": "How are you today?"}]
11
+
12
+ kwargs = {
13
+ "model": llm_model_value,
14
+ "messages": messages_value,
15
+ # "system": "Respond only in Yoda-speak.",
16
+ "stream": False,
17
+ "max_tokens": 1024,
18
+ }
19
+ response = anthropic_client.messages.create(**kwargs)
20
+ spans = exporter.get_finished_spans()
21
+ completion_span = spans[-1]
22
+
23
+ assert completion_span.name == "anthropic.messages.create"
24
+ attributes = completion_span.attributes
25
+
26
+ assert attributes.get("langtrace.sdk.name") == "langtrace-python-sdk"
27
+ assert attributes.get("langtrace.service.name") == "Anthropic"
28
+ assert attributes.get("langtrace.service.type") == "llm"
29
+ assert attributes.get("langtrace.service.version") == importlib.metadata.version(
30
+ "anthropic"
31
+ )
32
+ assert attributes.get("langtrace.version") == "1.0.0"
33
+ assert attributes.get("url.full") == "https://api.anthropic.com"
34
+ assert attributes.get("llm.api") == APIS["MESSAGES_CREATE"]["ENDPOINT"]
35
+ assert attributes.get("llm.model") == llm_model_value
36
+ assert attributes.get("llm.prompts") == json.dumps(messages_value)
37
+ assert attributes.get("llm.stream") is False
38
+
39
+ tokens = json.loads(attributes.get("llm.token.counts"))
40
+ output_tokens = tokens.get("output_tokens")
41
+ prompt_tokens = tokens.get("input_tokens")
42
+ total_tokens = tokens.get("total_tokens")
43
+
44
+ assert output_tokens and prompt_tokens and total_tokens
45
+ assert output_tokens + prompt_tokens == total_tokens
46
+
47
+ langtrace_responses = json.loads(attributes.get("llm.responses"))
48
+ assert isinstance(langtrace_responses, list)
49
+ for langtrace_response in langtrace_responses:
50
+ assert isinstance(langtrace_response, dict)
51
+ assert "role" in langtrace_response
52
+ assert "content" in langtrace_response
53
+
54
+
55
+ @pytest.mark.vcr()
56
+ def test_anthropic_streaming(anthropic_client, exporter):
57
+ llm_model_value = "claude-3-opus-20240229"
58
+ messages_value = [{"role": "user", "content": "How are you today?"}]
59
+
60
+ kwargs = {
61
+ "model": llm_model_value,
62
+ "messages": messages_value,
63
+ # "system": "Respond only in Yoda-speak.",
64
+ "stream": True,
65
+ "max_tokens": 1024,
66
+ }
67
+ response = anthropic_client.messages.create(**kwargs)
68
+ chunk_count = 0
69
+
70
+ for chunk in response:
71
+ if chunk:
72
+ chunk_count += 1
73
+
74
+ spans = exporter.get_finished_spans()
75
+ streaming_span = spans[-1]
76
+
77
+ assert streaming_span.name == "anthropic.messages.create"
78
+ attributes = streaming_span.attributes
79
+
80
+ assert attributes.get("langtrace.sdk.name") == "langtrace-python-sdk"
81
+ assert attributes.get("langtrace.service.name") == "Anthropic"
82
+ assert attributes.get("langtrace.service.type") == "llm"
83
+ assert attributes.get("langtrace.service.version") == importlib.metadata.version(
84
+ "anthropic"
85
+ )
86
+ assert attributes.get("langtrace.version") == "1.0.0"
87
+ assert attributes.get("url.full") == "https://api.anthropic.com"
88
+ assert attributes.get("llm.api") == APIS["MESSAGES_CREATE"]["ENDPOINT"]
89
+ assert attributes.get("llm.model") == llm_model_value
90
+ assert attributes.get("llm.prompts") == json.dumps(messages_value)
91
+ assert attributes.get("llm.stream") is True
92
+ events = streaming_span.events
93
+
94
+ assert len(events) - 2 == chunk_count # -2 for start and end events
95
+
96
+ tokens = json.loads(attributes.get("llm.token.counts"))
97
+ output_tokens = tokens.get("output_tokens")
98
+ prompt_tokens = tokens.get("input_tokens")
99
+ total_tokens = tokens.get("total_tokens")
100
+
101
+ assert output_tokens and prompt_tokens and total_tokens
102
+ assert output_tokens + prompt_tokens == total_tokens
103
+
104
+ langtrace_responses = json.loads(attributes.get("llm.responses"))
105
+ assert isinstance(langtrace_responses, list)
106
+ for langtrace_response in langtrace_responses:
107
+ assert isinstance(langtrace_response, dict)
108
+ assert "role" in langtrace_response
109
+ assert "content" in langtrace_response
tests/conftest.py ADDED
@@ -0,0 +1,17 @@
1
+ import pytest
2
+ from opentelemetry.sdk.trace import TracerProvider
3
+ from opentelemetry.sdk.trace.export.in_memory_span_exporter import InMemorySpanExporter
4
+ from opentelemetry.sdk.trace.export import SimpleSpanProcessor
5
+ from opentelemetry import trace
6
+
7
+
8
+ @pytest.fixture(scope="session")
9
+ def exporter():
10
+ exporter = InMemorySpanExporter()
11
+ processor = SimpleSpanProcessor(exporter)
12
+
13
+ provider = TracerProvider()
14
+ provider.add_span_processor(processor)
15
+ trace.set_tracer_provider(provider)
16
+
17
+ return exporter
tests/openai/conftest.py CHANGED
@@ -3,10 +3,6 @@
3
3
  import pytest
4
4
  import os
5
5
  from openai import OpenAI, AsyncOpenAI
6
- from opentelemetry.sdk.trace import TracerProvider
7
- from opentelemetry.sdk.trace.export.in_memory_span_exporter import InMemorySpanExporter
8
- from opentelemetry.sdk.trace.export import SimpleSpanProcessor
9
- from opentelemetry import trace
10
6
  from langtrace_python_sdk.instrumentation.openai.instrumentation import (
11
7
  OpenAIInstrumentation,
12
8
  )
@@ -33,13 +29,9 @@ def vcr_config():
33
29
  return {"filter_headers": ["authorization", "api-key"]}
34
30
 
35
31
 
36
- @pytest.fixture(scope="session")
37
- def exporter():
38
- exporter = InMemorySpanExporter()
39
- processor = SimpleSpanProcessor(exporter)
40
-
41
- provider = TracerProvider()
42
- provider.add_span_processor(processor)
43
- trace.set_tracer_provider(provider)
32
+ @pytest.fixture(scope="session", autouse=True)
33
+ def instrument():
44
34
  OpenAIInstrumentation().instrument()
45
- return exporter
35
+
36
+ yield
37
+ # exporter.shutdown()
@@ -42,6 +42,13 @@ def test_chat_completion(exporter, openai_client):
42
42
  assert output_tokens and prompt_tokens and total_tokens
43
43
  assert output_tokens + prompt_tokens == total_tokens
44
44
 
45
+ langtrace_responses = json.loads(attributes.get("llm.responses"))
46
+ assert isinstance(langtrace_responses, list)
47
+ for langtrace_response in langtrace_responses:
48
+ assert isinstance(langtrace_response, dict)
49
+ assert "role" in langtrace_response
50
+ assert "content" in langtrace_response
51
+
45
52
 
46
53
  @pytest.mark.vcr()
47
54
  def test_chat_completion_streaming(exporter, openai_client):
@@ -91,6 +98,13 @@ def test_chat_completion_streaming(exporter, openai_client):
91
98
  assert output_tokens and prompt_tokens and total_tokens
92
99
  assert output_tokens + prompt_tokens == total_tokens
93
100
 
101
+ langtrace_responses = json.loads(attributes.get("llm.responses"))
102
+ assert isinstance(langtrace_responses, list)
103
+ for langtrace_response in langtrace_responses:
104
+ assert isinstance(langtrace_response, dict)
105
+ assert "role" in langtrace_response
106
+ assert "content" in langtrace_response
107
+
94
108
 
95
109
  @pytest.mark.vcr()
96
110
  @pytest.mark.asyncio()
@@ -140,3 +154,10 @@ async def test_async_chat_completion_streaming(exporter, async_openai_client):
140
154
 
141
155
  assert output_tokens and prompt_tokens and total_tokens
142
156
  assert output_tokens + prompt_tokens == total_tokens
157
+
158
+ langtrace_responses = json.loads(attributes.get("llm.responses"))
159
+ assert isinstance(langtrace_responses, list)
160
+ for langtrace_response in langtrace_responses:
161
+ assert isinstance(langtrace_response, dict)
162
+ assert "role" in langtrace_response
163
+ assert "content" in langtrace_response