langtrace-python-sdk 2.1.1__py3-none-any.whl → 2.1.4__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.
@@ -2,13 +2,12 @@
2
2
  This example demonstrates how to use Pinecone with Langtrace.
3
3
  """
4
4
 
5
- from langtrace_python_sdk import langtrace
6
5
  from dotenv import find_dotenv, load_dotenv
7
6
  from openai import OpenAI
8
- from pinecone import Pinecone
7
+ from pinecone import Pinecone, ServerlessSpec
9
8
 
10
-
11
- from langtrace_python_sdk import with_langtrace_root_span, send_user_feedback
9
+ from langtrace_python_sdk import langtrace, with_langtrace_root_span
10
+ from langtrace_python_sdk.utils.with_root_span import send_user_feedback
12
11
 
13
12
  _ = load_dotenv(find_dotenv())
14
13
 
@@ -17,9 +16,20 @@ langtrace.init()
17
16
  client = OpenAI()
18
17
  pinecone = Pinecone()
19
18
 
19
+ PINECONE_INDEX_NAME = "test-index"
20
+
21
+
22
+ def create_index():
23
+ pinecone.create_index(
24
+ name=PINECONE_INDEX_NAME,
25
+ dimension=1536,
26
+ metric="cosine",
27
+ spec=ServerlessSpec(cloud="aws", region="us-east-1"),
28
+ )
29
+
20
30
 
21
31
  @with_langtrace_root_span()
22
- def basic(span_id, trace_id):
32
+ def basic(span_id=None, trace_id=None):
23
33
 
24
34
  result = client.embeddings.create(
25
35
  model="text-embedding-ada-002",
@@ -32,9 +42,8 @@ def basic(span_id, trace_id):
32
42
  unique_id = "unique_random_id"
33
43
  data_to_upsert = {"id": unique_id, "values": embedding}
34
44
 
35
- index = pinecone.Index("test-index")
45
+ index = pinecone.Index(PINECONE_INDEX_NAME)
36
46
  res = index.upsert(vectors=[data_to_upsert], namespace="test-namespace")
37
- print("res", res)
38
47
 
39
48
  resp = index.query(
40
49
  vector=embedding, top_k=1, include_values=False, namespace="test-namespace"
@@ -42,4 +51,7 @@ def basic(span_id, trace_id):
42
51
  send_user_feedback(
43
52
  {"spanId": span_id, "traceId": trace_id, "userScore": 1, "userId": "123"}
44
53
  )
45
- print(resp)
54
+ return [res, resp]
55
+
56
+
57
+ # create_index()
@@ -9,6 +9,8 @@ from opentelemetry.trace.span import format_trace_id
9
9
  from langtrace_python_sdk.constants.exporter.langtrace_exporter import (
10
10
  LANGTRACE_REMOTE_URL,
11
11
  )
12
+ from colorama import Fore
13
+ from requests.exceptions import RequestException
12
14
 
13
15
 
14
16
  class LangTraceExporter(SpanExporter):
@@ -55,9 +57,6 @@ class LangTraceExporter(SpanExporter):
55
57
  self.api_key = api_key or os.environ.get("LANGTRACE_API_KEY")
56
58
  self.api_host: str = api_host or LANGTRACE_REMOTE_URL
57
59
 
58
- if not self.api_key:
59
- raise ValueError("No API key provided")
60
-
61
60
  def export(self, spans: typing.Sequence[ReadableSpan]) -> SpanExportResult:
62
61
  """
63
62
  Exports a batch of telemetry data.
@@ -68,10 +67,17 @@ class LangTraceExporter(SpanExporter):
68
67
  Returns:
69
68
  The result of the export SUCCESS or FAILURE
70
69
  """
70
+ if not self.api_key:
71
+ print(Fore.RED)
72
+ print(
73
+ "Missing Langtrace API key, proceed to https://langtrace.ai to create one"
74
+ )
75
+ print("Set the API key as an environment variable LANGTRACE_API_KEY")
76
+ print(Fore.RESET)
77
+ return
71
78
  data = [
72
79
  {
73
80
  "traceId": format_trace_id(span.get_span_context().trace_id),
74
- "instrumentationLibrary": span.instrumentation_info.__repr__(),
75
81
  "droppedEventsCount": span.dropped_events,
76
82
  "droppedAttributesCount": span.dropped_attributes,
77
83
  "droppedLinksCount": span.dropped_links,
@@ -83,14 +89,23 @@ class LangTraceExporter(SpanExporter):
83
89
 
84
90
  # Send data to remote URL
85
91
  try:
86
- requests.post(
92
+ response = requests.post(
87
93
  url=f"{self.api_host}/api/trace",
88
94
  data=json.dumps(data),
89
95
  headers={"Content-Type": "application/json", "x-api-key": self.api_key},
96
+ timeout=20,
97
+ )
98
+
99
+ if not response.ok:
100
+ raise RequestException(response.text)
101
+
102
+ print(
103
+ Fore.GREEN + f"Exported {len(spans)} spans successfully." + Fore.RESET
90
104
  )
91
- print(f"sent to {self.api_host}/api/trace with {len(data)} spans")
92
105
  return SpanExportResult.SUCCESS
93
- except Exception as e:
106
+ except RequestException as err:
107
+ print(Fore.RED + "Failed to export spans.")
108
+ print(Fore.RED + f"Error: {err}" + Fore.RESET)
94
109
  return SpanExportResult.FAILURE
95
110
 
96
111
  def shutdown(self) -> None:
@@ -18,7 +18,6 @@ import importlib.metadata
18
18
  import logging
19
19
  from typing import Collection
20
20
 
21
- import pinecone
22
21
  from langtrace.trace_attributes import PineconeMethods
23
22
  from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
24
23
  from opentelemetry.trace import get_tracer
@@ -42,21 +41,12 @@ class PineconeInstrumentation(BaseInstrumentor):
42
41
  tracer = get_tracer(__name__, "", tracer_provider)
43
42
  version = importlib.metadata.version("pinecone-client")
44
43
  for operation_name, details in APIS.items():
45
- method_ref = details["METHOD"]
46
- method = None
47
- if method_ref is PineconeMethods.UPSERT.value:
48
- method = pinecone.Index.upsert
49
- elif method_ref is PineconeMethods.QUERY.value:
50
- method = pinecone.Index.query
51
- elif method_ref is PineconeMethods.DELETE.value:
52
- method = pinecone.Index.delete
53
44
  operation = details["OPERATION"]
54
-
55
45
  # Dynamically creating the patching call
56
46
  wrap_function_wrapper(
57
47
  "pinecone.data.index",
58
48
  f"Index.{operation}",
59
- generic_patch(method, operation_name, version, tracer),
49
+ generic_patch(operation_name, version, tracer),
60
50
  )
61
51
 
62
52
  def _uninstrument(self, **kwargs):
@@ -14,9 +14,9 @@ See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
15
  """
16
16
 
17
+ import json
18
+
17
19
  from langtrace.trace_attributes import DatabaseSpanAttributes
18
- from langtrace_python_sdk.utils import set_span_attribute
19
- from langtrace_python_sdk.utils.silently_fail import silently_fail
20
20
  from opentelemetry import baggage
21
21
  from opentelemetry.trace import SpanKind
22
22
  from opentelemetry.trace.status import Status, StatusCode
@@ -26,15 +26,16 @@ from langtrace_python_sdk.constants.instrumentation.common import (
26
26
  SERVICE_PROVIDERS,
27
27
  )
28
28
  from langtrace_python_sdk.constants.instrumentation.pinecone import APIS
29
- import json
29
+ from langtrace_python_sdk.utils import set_span_attribute
30
+ from langtrace_python_sdk.utils.silently_fail import silently_fail
30
31
 
31
32
 
32
- def generic_patch(original_method, method, version, tracer):
33
+ def generic_patch(operation_name, version, tracer):
33
34
  """
34
35
  A generic patch method that wraps a function with a span"""
35
36
 
36
37
  def traced_method(wrapped, instance, args, kwargs):
37
- api = APIS[method]
38
+ api = APIS[operation_name]
38
39
  service_provider = SERVICE_PROVIDERS["PINECONE"]
39
40
  extra_attributes = baggage.get_baggage(LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY)
40
41
 
@@ -56,7 +57,7 @@ def generic_patch(original_method, method, version, tracer):
56
57
 
57
58
  if span.is_recording():
58
59
  set_span_attribute(span, "server.address", instance._config.host)
59
- if method == "QUERY":
60
+ if operation_name == "QUERY":
60
61
  set_query_input_attributes(span, kwargs)
61
62
 
62
63
  for field, value in attributes.model_dump(by_alias=True).items():
@@ -64,7 +65,7 @@ def generic_patch(original_method, method, version, tracer):
64
65
  span.set_attribute(field, value)
65
66
  try:
66
67
  # Attempt to call the original method
67
- result = original_method(instance, *args, **kwargs)
68
+ result = wrapped(*args, **kwargs)
68
69
  if result:
69
70
  if span.is_recording():
70
71
  set_query_response_attributes(span, result)
@@ -17,7 +17,7 @@ limitations under the License.
17
17
  import importlib.metadata
18
18
  import logging
19
19
  from typing import Collection
20
-
20
+ from opentelemetry.instrumentation.utils import unwrap
21
21
  from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
22
22
  from opentelemetry.trace import get_tracer
23
23
  from wrapt import wrap_function_wrapper
@@ -28,7 +28,7 @@ from langtrace_python_sdk.instrumentation.weaviate.patch import (
28
28
  )
29
29
  from langtrace_python_sdk.constants.instrumentation.weaviate import APIS
30
30
 
31
- logging.basicConfig(level=logging.FATAL)
31
+ logging.basicConfig(level=logging.DEBUG) # Set to DEBUG for detailed logging
32
32
 
33
33
 
34
34
  class WeaviateInstrumentation(BaseInstrumentor):
@@ -47,17 +47,19 @@ class WeaviateInstrumentation(BaseInstrumentor):
47
47
  for api_name, api_config in APIS.items():
48
48
  module_path, function_name = api_name.rsplit(".", 1)
49
49
  if api_config.get("OPERATION") == "query":
50
- wrap_function_wrapper(
51
- api_config["MODULE"],
52
- api_config["METHOD"],
53
- generic_query_patch(api_name, version, tracer),
54
- )
50
+ if getattr(api_config["MODULE"], api_config["METHOD"], None):
51
+ wrap_function_wrapper(
52
+ api_config["MODULE"],
53
+ api_config["METHOD"],
54
+ generic_query_patch(api_name, version, tracer),
55
+ )
55
56
  elif api_config.get("OPERATION") == "create":
56
- wrap_function_wrapper(
57
- api_config["MODULE"],
58
- api_config["METHOD"],
59
- generic_collection_patch(api_name, version, tracer),
60
- )
57
+ if getattr(api_config["MODULE"], api_config["METHOD"], None):
58
+ wrap_function_wrapper(
59
+ api_config["MODULE"],
60
+ api_config["METHOD"],
61
+ generic_collection_patch(api_name, version, tracer),
62
+ )
61
63
 
62
64
  def _instrument_module(self, module_name):
63
65
  pass
@@ -68,6 +68,8 @@ from langtrace_python_sdk.instrumentation.weaviate.instrumentation import (
68
68
  WeaviateInstrumentation,
69
69
  )
70
70
 
71
+ from colorama import Fore
72
+
71
73
 
72
74
  def init(
73
75
  api_key: str = None,
@@ -77,6 +79,7 @@ def init(
77
79
  api_host: Optional[str] = None,
78
80
  disable_instrumentations: Optional[DisableInstrumentations] = None,
79
81
  ):
82
+ print(Fore.GREEN + "Initializing Langtrace SDK.." + Fore.RESET)
80
83
  provider = TracerProvider(resource=Resource.create({"service.name": sys.argv[0]}))
81
84
 
82
85
  remote_write_exporter = (
@@ -90,20 +93,24 @@ def init(
90
93
  simple_processor_console = SimpleSpanProcessor(console_exporter)
91
94
 
92
95
  if write_spans_to_console:
96
+ print(Fore.BLUE + "Writing spans to console" + Fore.RESET)
93
97
  provider.add_span_processor(simple_processor_console)
94
98
 
95
99
  elif custom_remote_exporter is not None:
100
+ print(Fore.BLUE + f"Exporting spans to custom remote exporter.." + Fore.RESET)
96
101
  if batch:
97
102
  provider.add_span_processor(batch_processor_remote)
98
103
  else:
99
104
  provider.add_span_processor(simple_processor_remote)
100
105
 
101
106
  elif api_host is not None:
107
+ print(Fore.BLUE + f"Exporting spans to custom host: {api_host}.." + Fore.RESET)
102
108
  if batch:
103
109
  provider.add_span_processor(batch_processor_remote)
104
110
  else:
105
111
  provider.add_span_processor(simple_processor_remote)
106
112
  else:
113
+ print(Fore.BLUE + "Exporting spans to Langtrace cloud.." + Fore.RESET)
107
114
  provider.add_span_processor(batch_processor_remote)
108
115
 
109
116
  # Initialize tracer
@@ -15,25 +15,25 @@ limitations under the License.
15
15
  """
16
16
 
17
17
  import asyncio
18
+ import os
18
19
  from functools import wraps
19
20
  from typing import Optional
20
21
 
22
+ import requests
23
+ from opentelemetry import baggage, context, trace
24
+ from opentelemetry.trace import SpanKind
25
+
21
26
  from langtrace_python_sdk.constants.exporter.langtrace_exporter import (
22
27
  LANGTRACE_REMOTE_URL,
23
28
  )
29
+ from langtrace_python_sdk.constants.instrumentation.common import (
30
+ LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY,
31
+ )
24
32
  from langtrace_python_sdk.utils.types import (
25
33
  EvaluationAPIData,
26
34
  LangTraceApiError,
27
35
  LangTraceEvaluation,
28
36
  )
29
- from opentelemetry import baggage, context, trace
30
- from opentelemetry.trace import SpanKind
31
-
32
- from langtrace_python_sdk.constants.instrumentation.common import (
33
- LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY,
34
- )
35
- import os
36
- import requests
37
37
 
38
38
 
39
39
  def with_langtrace_root_span(
@@ -126,7 +126,6 @@ def send_user_feedback(data: EvaluationAPIData) -> None:
126
126
  timeout=None,
127
127
  )
128
128
  response.raise_for_status()
129
- print("SENDING FEEDBACK PUT", response.status_code, response.text)
130
129
 
131
130
  else:
132
131
  # Make a POST request to create a new evaluation
@@ -137,7 +136,6 @@ def send_user_feedback(data: EvaluationAPIData) -> None:
137
136
  headers=headers,
138
137
  timeout=None,
139
138
  )
140
- print("Response", response.status_code, response.text)
141
139
  response.raise_for_status()
142
140
 
143
141
  except requests.RequestException as err:
@@ -161,7 +159,7 @@ def _get_evaluation(span_id: str) -> Optional[LangTraceEvaluation]:
161
159
  headers={"x-api-key": os.environ["LANGTRACE_API_KEY"]},
162
160
  timeout=None,
163
161
  )
164
- evaluations = response.json()["evaluations"]
162
+ evaluations = response.json().get("evaluations", [])
165
163
  response.raise_for_status()
166
164
  return None if not evaluations else evaluations[0]
167
165
 
@@ -1 +1 @@
1
- __version__ = "2.1.1"
1
+ __version__ = "2.1.4"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: langtrace-python-sdk
3
- Version: 2.1.1
3
+ Version: 2.1.4
4
4
  Summary: Python SDK for LangTrace
5
5
  Project-URL: Homepage, https://github.com/Scale3-Labs/langtrace-python-sdk
6
6
  Author-email: Scale3 Labs <engineering@scale3labs.com>
@@ -10,15 +10,14 @@ Classifier: License :: OSI Approved :: Apache Software License
10
10
  Classifier: Operating System :: OS Independent
11
11
  Classifier: Programming Language :: Python :: 3
12
12
  Requires-Python: >=3.9
13
- Requires-Dist: opentelemetry-api
13
+ Requires-Dist: colorama>=0.4.6
14
+ Requires-Dist: opentelemetry-api>=1.24.0
14
15
  Requires-Dist: opentelemetry-exporter-otlp-proto-grpc>=1.24.0
15
16
  Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.24.0
16
- Requires-Dist: opentelemetry-instrumentation
17
- Requires-Dist: opentelemetry-sdk
18
- Requires-Dist: pinecone-client
19
- Requires-Dist: tiktoken
20
- Requires-Dist: trace-attributes>=4.0.2
21
- Requires-Dist: weaviate-client
17
+ Requires-Dist: opentelemetry-instrumentation>=0.45b0
18
+ Requires-Dist: opentelemetry-sdk>=1.24.0
19
+ Requires-Dist: tiktoken>=0.1.1
20
+ Requires-Dist: trace-attributes>=4.0.4
22
21
  Provides-Extra: dev
23
22
  Requires-Dist: anthropic; extra == 'dev'
24
23
  Requires-Dist: chromadb; extra == 'dev'
@@ -27,6 +26,7 @@ Requires-Dist: langchain; extra == 'dev'
27
26
  Requires-Dist: langchain-community; extra == 'dev'
28
27
  Requires-Dist: langchain-openai; extra == 'dev'
29
28
  Requires-Dist: openai; extra == 'dev'
29
+ Requires-Dist: pinecone-client; extra == 'dev'
30
30
  Requires-Dist: python-dotenv; extra == 'dev'
31
31
  Requires-Dist: qdrant-client; extra == 'dev'
32
32
  Requires-Dist: weaviate-client; extra == 'dev'
@@ -32,13 +32,13 @@ examples/openai_example/tool_calling_nonstreaming.py,sha256=Yc848IooZRXNynHL6z0k
32
32
  examples/openai_example/tool_calling_streaming.py,sha256=mV1RbyAoVhumGRPpqPWQ6PMhnJyeifrlELd2-K1qJ_w,7015
33
33
  examples/perplexity_example/basic.py,sha256=bp7n27gaugJkaFVyt8pjaEfi66lYcqP6eFFjPewUShY,668
34
34
  examples/pinecone_example/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
- examples/pinecone_example/basic.py,sha256=ymSguqRssQ4fbzJ2MMV-z9FZwlTvUyApt2AykiJQiF4,1137
35
+ examples/pinecone_example/basic.py,sha256=b-5-cSf3_N6Gk4zGVecGF47mBcFY9ZrC5ztrIZVgTQo,1430
36
36
  examples/qdrant_example/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
37
  examples/qdrant_example/basic.py,sha256=DCMjHSuBZKkhEjCkwy5d5La9WMyW0lCWqtcZWiFCEm4,1425
38
38
  examples/weaviate_example/query_text.py,sha256=s3f0hYYi-xdbh_LlTBDHegQiVi-4bW_eACb2EnEj8J4,64533
39
39
  langtrace_python_sdk/__init__.py,sha256=R4dv73QARUw4WRfCdSKkKzZTLDumF9jcSdwtVrzWhb4,962
40
- langtrace_python_sdk/langtrace.py,sha256=2VVVeWX3b20jwDY9iqDTxeE-Zunfgt2gfgRs3SreZLU,6993
41
- langtrace_python_sdk/version.py,sha256=zPJIgPGcoSNiD0qme18OnYJYE3A9VVytlhO-V5DaAW0,22
40
+ langtrace_python_sdk/langtrace.py,sha256=jWTG9F-vleKkDbC-l22eY6rKH0eHUyJCmgXpoLcPbbY,7409
41
+ langtrace_python_sdk/version.py,sha256=1yR20YsjyDpnFQgDIQmHfutaSsaW0F7mDqjloRVRIG8,22
42
42
  langtrace_python_sdk/constants/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
43
43
  langtrace_python_sdk/constants/exporter/langtrace_exporter.py,sha256=5MNjnAOg-4am78J3gVMH6FSwq5N8TOj72ugkhsw4vi0,46
44
44
  langtrace_python_sdk/constants/instrumentation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -52,7 +52,7 @@ langtrace_python_sdk/constants/instrumentation/pinecone.py,sha256=Xaqqw-xBO0JJLG
52
52
  langtrace_python_sdk/constants/instrumentation/qdrant.py,sha256=yL7BopNQTXW7L7Z-gVM2PdusKD7r9qqcATvczFd7NtQ,1999
53
53
  langtrace_python_sdk/constants/instrumentation/weaviate.py,sha256=Iytf2OpB_irZYEmvOQ7Pf483EdG5Bh59GxaBlXck0yY,1501
54
54
  langtrace_python_sdk/extensions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
55
- langtrace_python_sdk/extensions/langtrace_exporter.py,sha256=s-4zc7lba1va_JUReFdLgyBKlvwV1KmBt_OzVmWReQg,3610
55
+ langtrace_python_sdk/extensions/langtrace_exporter.py,sha256=Fj--DzBr4AwTM_iS3lRz0NhqESEyTOZ3SBCA4OevFkE,4127
56
56
  langtrace_python_sdk/instrumentation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
57
57
  langtrace_python_sdk/instrumentation/anthropic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
58
58
  langtrace_python_sdk/instrumentation/anthropic/instrumentation.py,sha256=-srgE8qumAn0ulQYZxMa8ch-9IBH0XgBW_rfEnGk6LI,1684
@@ -85,13 +85,13 @@ langtrace_python_sdk/instrumentation/openai/__init__.py,sha256=47DEQpj8HBSa-_TIm
85
85
  langtrace_python_sdk/instrumentation/openai/instrumentation.py,sha256=G2HSZfr6DuP2n-8v0h91M60m0DJWFBcru4-1FQJl-5A,2726
86
86
  langtrace_python_sdk/instrumentation/openai/patch.py,sha256=xi87lLHoj81xSx6MiqTAKWr3MLxXpMf84NzJ5uDm134,37288
87
87
  langtrace_python_sdk/instrumentation/pinecone/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
88
- langtrace_python_sdk/instrumentation/pinecone/instrumentation.py,sha256=52GbkfRK-sxXBXhHmHRRO2BGNd6zZVHDp8njLS1CQX0,2314
89
- langtrace_python_sdk/instrumentation/pinecone/patch.py,sha256=VNZ_fCRpVQNXeg0G8hFUidBelV3L91-A_OtkKT7ZWUM,4889
88
+ langtrace_python_sdk/instrumentation/pinecone/instrumentation.py,sha256=mxQXe3oAOPLsMJGlEzAe6zpgK7RtWfqmcNmGW_gQXX4,1900
89
+ langtrace_python_sdk/instrumentation/pinecone/patch.py,sha256=8uenVh4TCTJVuBGepm7nhTZBuW7HyjlZS4wPx7zi9cg,4879
90
90
  langtrace_python_sdk/instrumentation/qdrant/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
91
91
  langtrace_python_sdk/instrumentation/qdrant/instrumentation.py,sha256=vl2eKSP55aqDo1JiRlvOUBrr6kddvG9Z5dCYew2OG08,1816
92
92
  langtrace_python_sdk/instrumentation/qdrant/patch.py,sha256=KcUWmvnS3PgPStzs1oWmlZsyomKBl3dmg8nXuR0T1C0,4654
93
93
  langtrace_python_sdk/instrumentation/weaviate/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
94
- langtrace_python_sdk/instrumentation/weaviate/instrumentation.py,sha256=i5xi0uWY1pPvGWyedFEvOtbRVT7ybFnMia-9TYFigOA,2309
94
+ langtrace_python_sdk/instrumentation/weaviate/instrumentation.py,sha256=4BnHRzXOKEauy2TNTZwpIagDFUp03CUyBZCdE2vinxk,2596
95
95
  langtrace_python_sdk/instrumentation/weaviate/patch.py,sha256=ig2fc33hNydEcH5cJWRycFnMXiXiJr731J-Vg5Ze4No,5634
96
96
  langtrace_python_sdk/types/__init__.py,sha256=-j8cuz3bhUdOqj7N2c0w5y-j3UmcxwEgNh8BWeXwHoI,813
97
97
  langtrace_python_sdk/utils/__init__.py,sha256=MbBqT4NpDCNTqqvZ0lG4tRiFFZ-tlM8Dwophg4xyrRw,148
@@ -100,7 +100,7 @@ langtrace_python_sdk/utils/misc.py,sha256=CD9NWRLxLpFd0YwlHJqzlpFNedXVWtAKGOjQWn
100
100
  langtrace_python_sdk/utils/prompt_registry.py,sha256=7FFB4Pj0414qgf02h5zL5vXBZgNBf74g4Iq7GdFaIO0,2689
101
101
  langtrace_python_sdk/utils/silently_fail.py,sha256=F_9EteXCO9Cyq-8MA1OT2Zy_dx8n06nt31I7t7ui24E,478
102
102
  langtrace_python_sdk/utils/types.py,sha256=l-N6o7cnWUyrD6dBvW7W3Pf5CkPo5QaoT__k1XLbrQg,383
103
- langtrace_python_sdk/utils/with_root_span.py,sha256=9OkVMzoH8Ydqt7OpWPONiGrmHHBnEOXY034St2UToCE,6095
103
+ langtrace_python_sdk/utils/with_root_span.py,sha256=rSPrTnKndJo6cFUWL4907IkJFbtLwqMS87paADUxD6A,5957
104
104
  tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
105
105
  tests/conftest.py,sha256=0Jo6iCZTXbdvyJVhG9UpYGkLabL75378oauCzmt-Sa8,603
106
106
  tests/utils.py,sha256=hP8sTH-M8WO6zlLkSFHPf6483ZcKEcnxul6JIIb1pLM,1396
@@ -119,9 +119,9 @@ tests/cohere/cassettes/test_cohere_chat.yaml,sha256=iQIhJwmWe2AQDmdguL6L0SZOPMD6
119
119
  tests/cohere/cassettes/test_cohere_chat_streaming.yaml,sha256=tMlz7IPZYRCJUeMCwyXrA4O4zpcqIPpQ5hU9I5f352Q,8174
120
120
  tests/cohere/cassettes/test_cohere_embed.yaml,sha256=Svcyw8WkVMVw4cocWQzGOdUVmWi5AM6Gb9t9VLlyOjM,27320
121
121
  tests/cohere/cassettes/test_cohere_rerank.yaml,sha256=RsJ8804lTktI0FKEFCw0M4ztStZbCxb_z_VRmrw8ooQ,2393
122
- tests/langchain/test_langchain.py,sha256=p1YkeL1U23b0E3vhEcXonwMa3QGUswQjKNReMbV0ndA,2576
123
- tests/langchain/test_langchain_community.py,sha256=B7Z7YRx7v9507wEyDnXV1NKVYo4r2XU0YplugfYVsJ0,2615
124
- tests/langchain/test_langchain_core.py,sha256=LBeoK27-uDethSvBGykYAlCVfEwljQrN7sZL91TVCPw,4467
122
+ tests/langchain/conftest.py,sha256=f29apdevxg7AM0mPQ1LoEd-yStGruqGLTQUp29heLJo,1116
123
+ tests/langchain/test_langchain.py,sha256=QP11RdNDX_ztF1ppPnUiui3SsxYYdzVsDgtI-1OGH48,893
124
+ tests/langchain/cassettes/test_langchain.yaml,sha256=KPBTVIYMUPFaSNpwrTDgWzsu4p3hHj_yNDoudDa-Jis,3755
125
125
  tests/openai/conftest.py,sha256=BkehS6heg-O91Nzoc4546OSiAzy8KgSgk7VCO3A11zM,700
126
126
  tests/openai/test_chat_completion.py,sha256=-TwCAF2czsiGZqETQvMOgmIJgdFWycJ9nOeO5q65AJs,5804
127
127
  tests/openai/test_embeddings.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -137,7 +137,7 @@ tests/pinecone/cassettes/test_query.yaml,sha256=b5v9G3ssUy00oG63PlFUR3JErF2Js-5A
137
137
  tests/pinecone/cassettes/test_upsert.yaml,sha256=neWmQ1v3d03V8WoLl8FoFeeCYImb8pxlJBWnFd_lITU,38607
138
138
  tests/qdrant/conftest.py,sha256=9n0uHxxIjWk9fbYc4bx-uP8lSAgLBVx-cV9UjnsyCHM,381
139
139
  tests/qdrant/test_qdrant.py,sha256=pzjAjVY2kmsmGfrI2Gs2xrolfuaNHz7l1fqGQCjp5_o,3353
140
- langtrace_python_sdk-2.1.1.dist-info/METADATA,sha256=QyEMBrnv_vd8SjrZdVVtifuCLr9VlCjUKCefnT28O7c,11722
141
- langtrace_python_sdk-2.1.1.dist-info/WHEEL,sha256=zEMcRr9Kr03x1ozGwg5v9NQBKn3kndp6LSoSlVg-jhU,87
142
- langtrace_python_sdk-2.1.1.dist-info/licenses/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
143
- langtrace_python_sdk-2.1.1.dist-info/RECORD,,
140
+ langtrace_python_sdk-2.1.4.dist-info/METADATA,sha256=ZUsesq7cmXEYg7N5GOkKPYxz2wANwYTwXxfX5McSkdo,11769
141
+ langtrace_python_sdk-2.1.4.dist-info/WHEEL,sha256=zEMcRr9Kr03x1ozGwg5v9NQBKn3kndp6LSoSlVg-jhU,87
142
+ langtrace_python_sdk-2.1.4.dist-info/licenses/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
143
+ langtrace_python_sdk-2.1.4.dist-info/RECORD,,
@@ -0,0 +1,106 @@
1
+ interactions:
2
+ - request:
3
+ body: '{"messages": [{"content": "You are world class technical documentation
4
+ writer.", "role": "system"}, {"content": "how can langsmith help with testing?",
5
+ "role": "user"}], "model": "gpt-3.5-turbo", "n": 1, "stream": false, "temperature":
6
+ 0.7}'
7
+ headers:
8
+ accept:
9
+ - application/json
10
+ accept-encoding:
11
+ - gzip, deflate
12
+ connection:
13
+ - keep-alive
14
+ content-length:
15
+ - '240'
16
+ content-type:
17
+ - application/json
18
+ host:
19
+ - api.openai.com
20
+ user-agent:
21
+ - OpenAI/Python 1.30.5
22
+ x-stainless-arch:
23
+ - arm64
24
+ x-stainless-async:
25
+ - 'false'
26
+ x-stainless-lang:
27
+ - python
28
+ x-stainless-os:
29
+ - MacOS
30
+ x-stainless-package-version:
31
+ - 1.30.5
32
+ x-stainless-runtime:
33
+ - CPython
34
+ x-stainless-runtime-version:
35
+ - 3.12.3
36
+ method: POST
37
+ uri: https://api.openai.com/v1/chat/completions
38
+ response:
39
+ body:
40
+ string: !!binary |
41
+ H4sIAAAAAAAAA3xVTW8bOQy9+1cQc7aNfLR16tv2A4tFt9hL9rRdBLSGnlGikVSSsuMt8t8X1Iwd
42
+ pwh6MWxRfHp8fKR/zAAa3zZraFyP6oYcFu//br/s3+271XUeVvfb+0f6cvv4x8MHyjf4tZlbRtrc
43
+ k9Nj1tKlIQdSn+IYdkyoZKiXq8vVxc3NxeqmBobUUrC0Luvievl2oYU3aXFxefV2yuyTdyTNGv6Z
44
+ AQD8qJ/GMbb02KzhYn48GUgEO2rWp0sADadgJw2KeFGM2syfgy5FpVhp/4mxk8FrD14AIac98bYE
45
+ 0JQCaI8KDiP0FDLs7ZaSqI8d+Ag7ZJ+KwB4Psv4Wv8XLJfxWNA1WMtyOF9fw/IIhbQiKUAuaYM9e
46
+ CfCUYdAgjn1WgW1iOKTCIGmre2QCzDl4hyauLOHDAQLtiLGrbFQgohbGAAFjV7AjyJwciVjcYcaN
47
+ D149ydxwK5eRQH3WoZBYUTmgj/A5dsFLP4cBH0Z8IBRPXHnFFBdKro/eYQAlHGCgYUMsVpaJy35T
48
+ DDqB9nTSbCK0NK2ullUh+ISK8DtF4lrZz3J1Y2Ri2drlDZp+KUJmamnrI7XAJZAAxhYyqhJHmdee
49
+ 2atW7ffi3UM4wOjHZzChc6lfKnzbezm2LCOrdyUgh4P1zxxiacfKxFE0N8hoGabvxVvLICB3BDik
50
+ EhXSFlq/Ixaqj1cdricdPqIQfMWIHQ0U9Re2Sdxh9P9RLXeoGWc9fKVl1gVG91B7kTl1TCJG5sge
51
+ nfpd9UbFpCiFCWyWmXqK4nf2q5qNLO8XvrSS3izhU3LFyni1pxgknU1UO901Jud11O9MUoLKvBJL
52
+ 2tNJ8wVTqGPj4zbxUF+qYzENlHkzHI4GMvD2nJR5HUGUi9PC1MII8jwcJxWiLRCK7lA5uIDs9WDp
53
+ VYajhi/Aqwx/mWAhzH+qXpQJh+AjvTYbc/BD5rQ7tnRSfT41psfojFMIuEnjxJi5qnJnU+jjLoVd
54
+ 1eaVJi+baRM+nVZoSF3mtLF1G0sIp/Otj176OyaUFG1diqY8pj/NAP6tq7q82L5N5jRkvdP0QNEA
55
+ r1YjXPP853AWvHw/RTUphrPAm3eziWEjB1Ea7rY+dsSZfd3cxnP2NPsfAAD//wMAt4ZeGbgGAAA=
56
+ headers:
57
+ CF-Cache-Status:
58
+ - DYNAMIC
59
+ CF-RAY:
60
+ - 88c03f46bf3b23c5-LHR
61
+ Connection:
62
+ - keep-alive
63
+ Content-Encoding:
64
+ - gzip
65
+ Content-Type:
66
+ - application/json
67
+ Date:
68
+ - Thu, 30 May 2024 16:54:42 GMT
69
+ Server:
70
+ - cloudflare
71
+ Set-Cookie:
72
+ - __cf_bm=9en.DN2CPQUgM8CubXIqqiVZkm43q.oBCL4n7bXxViw-1717088082-1.0.1.1-nl9dbPbwwGsUORjX1Y8Ta7S2v.MBG3o1JWUgYQU98tFUNcY9hFcmfcXWFu_bcohUX1nun6S55Wg2rg3MX7PHPQ;
73
+ path=/; expires=Thu, 30-May-24 17:24:42 GMT; domain=.api.openai.com; HttpOnly;
74
+ Secure; SameSite=None
75
+ - _cfuvid=bJ_po6.pyb_9JWu27O8yfh1AX1atGjC5_sFN497eMI8-1717088082420-0.0.1.1-604800000;
76
+ path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None
77
+ Transfer-Encoding:
78
+ - chunked
79
+ alt-svc:
80
+ - h3=":443"; ma=86400
81
+ openai-organization:
82
+ - scale3-1
83
+ openai-processing-ms:
84
+ - '4295'
85
+ openai-version:
86
+ - '2020-10-01'
87
+ strict-transport-security:
88
+ - max-age=15724800; includeSubDomains
89
+ x-ratelimit-limit-requests:
90
+ - '10000'
91
+ x-ratelimit-limit-tokens:
92
+ - '2000000'
93
+ x-ratelimit-remaining-requests:
94
+ - '9999'
95
+ x-ratelimit-remaining-tokens:
96
+ - '1999960'
97
+ x-ratelimit-reset-requests:
98
+ - 6ms
99
+ x-ratelimit-reset-tokens:
100
+ - 1ms
101
+ x-request-id:
102
+ - req_4b43c2d3cf0dc839647c8b775b75d6f5
103
+ status:
104
+ code: 200
105
+ message: OK
106
+ version: 1
@@ -0,0 +1,42 @@
1
+ """Unit tests configuration module."""
2
+
3
+ import os
4
+ import pytest
5
+
6
+ from langtrace_python_sdk.instrumentation.openai.instrumentation import (
7
+ OpenAIInstrumentation,
8
+ )
9
+ from langtrace_python_sdk.instrumentation.langchain.instrumentation import (
10
+ LangchainInstrumentation,
11
+ )
12
+ from langtrace_python_sdk.instrumentation.langchain_core.instrumentation import (
13
+ LangchainCoreInstrumentation,
14
+ )
15
+
16
+ from langtrace_python_sdk.instrumentation.langchain_community.instrumentation import (
17
+ LangchainCommunityInstrumentation,
18
+ )
19
+
20
+
21
+ @pytest.fixture(autouse=True)
22
+ def clear_exporter(exporter):
23
+ exporter.clear()
24
+
25
+
26
+ @pytest.fixture(autouse=True)
27
+ def environment():
28
+ if not os.environ.get("OPENAI_API_KEY"):
29
+ os.environ["OPENAI_API_KEY"] = "test"
30
+
31
+
32
+ @pytest.fixture(scope="module")
33
+ def vcr_config():
34
+ return {"filter_headers": ["authorization", "x-api-key"]}
35
+
36
+
37
+ @pytest.fixture(scope="session", autouse=True)
38
+ def instrument():
39
+ OpenAIInstrumentation().instrument()
40
+ LangchainInstrumentation().instrument()
41
+ LangchainCoreInstrumentation().instrument()
42
+ LangchainCommunityInstrumentation().instrument()
@@ -1,82 +1,28 @@
1
- import unittest
2
- from unittest.mock import MagicMock, call
3
- from langtrace_python_sdk.instrumentation.langchain.patch import generic_patch
4
- from opentelemetry.trace import SpanKind
5
- from opentelemetry.trace import get_tracer
6
- import importlib.metadata
7
- from langtrace_python_sdk.constants.instrumentation.openai import APIS
8
- from opentelemetry.trace.status import Status, StatusCode
9
- from tests.utils import common_setup
10
- import json
11
-
12
-
13
- class TestGenericPatch(unittest.TestCase):
14
- data = {"key": "value"}
15
-
16
- def setUp(self):
17
- self.langchain_mock, self.tracer, self.span = common_setup(self.data, None)
18
-
19
- def tearDown(self):
20
- # Clean up after each test case
21
- pass
22
-
23
- def test_generic_patch(self):
24
- # Arrange
25
- method_name = "example_method"
26
- trace_output = False
27
- trace_input = False # Change as per your requirement
28
- args = (1, 2, 3)
29
- task = "split_text"
30
- kwargs = {"key": "value"}
31
- version = importlib.metadata.version("langchain")
32
-
33
- # Act
34
- wrapped_function = generic_patch(
35
- "langchain.text_splitter",
36
- task,
37
- self.tracer,
38
- version,
39
- trace_output,
40
- trace_input,
41
- )
42
- result = wrapped_function(self.langchain_mock, MagicMock(), args, kwargs)
43
-
44
- # Assert
45
- self.assertTrue(
46
- self.tracer.start_as_current_span.called_once_with(
47
- method_name, kind=SpanKind.CLIENT
48
- )
49
- )
50
-
51
- service_provider = "Langchain"
52
- expected_attributes = {
53
- "langtrace.sdk.name": "langtrace-python-sdk",
54
- "langtrace.service.name": service_provider,
55
- "langtrace.service.type": "framework",
56
- "langtrace.service.version": version,
57
- "langtrace.version": "1.0.0",
58
- "langchain.task.name": task,
59
- }
60
-
61
- self.assertTrue(
62
- self.span.set_attribute.has_calls(
63
- [call(key, value) for key, value in expected_attributes.items()],
64
- any_order=True,
65
- )
66
- )
67
-
68
- actual_calls = self.span.set_attribute.call_args_list
69
-
70
- for key, value in expected_attributes.items():
71
- self.assertIn(call(key, value), actual_calls)
72
-
73
- self.assertEqual(self.span.set_status.call_count, 1)
74
- self.assertTrue(self.span.set_status.has_calls([call(Status(StatusCode.OK))]))
75
-
76
- expected_result_data = {"key": "value"}
77
-
78
- self.assertEqual(result.key, expected_result_data["key"])
79
-
80
-
81
- if __name__ == "__main__":
82
- unittest.main()
1
+ import pytest
2
+ from langchain_openai import ChatOpenAI
3
+ from langchain_core.prompts.chat import ChatPromptTemplate
4
+ from langchain_core.output_parsers import StrOutputParser
5
+
6
+
7
+ @pytest.mark.vcr()
8
+ def test_langchain(exporter):
9
+ llm = ChatOpenAI()
10
+ prompt = ChatPromptTemplate.from_messages(
11
+ [
12
+ ("system", "You are world class technical documentation writer."),
13
+ ("user", "{input}"),
14
+ ]
15
+ )
16
+ output_parser = StrOutputParser()
17
+ chain = prompt | llm | output_parser
18
+ chain.invoke({"input": "how can langsmith help with testing?"})
19
+ spans = exporter.get_finished_spans()
20
+
21
+ assert [
22
+ "ChatPromptTemplate.invoke",
23
+ "openai.chat.completions.create",
24
+ "StrOutputParser.parse",
25
+ "StrOutputParser.parse_result",
26
+ "StrOutputParser.invoke",
27
+ "RunnableSequence.invoke",
28
+ ] == [span.name for span in spans]
@@ -1,82 +0,0 @@
1
- import unittest
2
- from unittest.mock import MagicMock, Mock, patch, call
3
- from langtrace_python_sdk.instrumentation.langchain_community.patch import generic_patch
4
- from opentelemetry.trace import SpanKind
5
- from opentelemetry.trace import get_tracer
6
- import importlib.metadata
7
- import openai
8
- from langtrace_python_sdk.constants.instrumentation.openai import APIS
9
- from opentelemetry.trace.status import Status, StatusCode
10
- import json
11
- from tests.utils import common_setup
12
-
13
-
14
- class TestGenericPatch(unittest.TestCase):
15
- data = {"key": "value"}
16
-
17
- def setUp(self):
18
- self.langchain_mock, self.tracer, self.span = common_setup(self.data, None)
19
-
20
- def tearDown(self):
21
- # Clean up after each test case
22
- pass
23
-
24
- def test_generic_patch(self):
25
- # Arrange
26
- method_name = "example_method"
27
- trace_output = False
28
- trace_input = False
29
- args = (1, 2, 3)
30
- task = "vector_store"
31
- kwargs = {"key": "value"}
32
- version = importlib.metadata.version("langchain-community")
33
-
34
- # Act
35
- wrapped_function = generic_patch(
36
- "langchain_community.vectorstores.faiss",
37
- task,
38
- self.tracer,
39
- version,
40
- trace_output,
41
- trace_input,
42
- )
43
- result = wrapped_function(self.langchain_mock, MagicMock(), args, kwargs)
44
-
45
- # Assert
46
- self.assertTrue(
47
- self.tracer.start_as_current_span.called_once_with(
48
- method_name, kind=SpanKind.CLIENT
49
- )
50
- )
51
-
52
- service_provider = "Langchain Community"
53
- expected_attributes = {
54
- "langtrace.sdk.name": "langtrace-python-sdk",
55
- "langtrace.service.name": service_provider,
56
- "langtrace.service.type": "framework",
57
- "langtrace.service.version": version,
58
- "langtrace.version": "1.0.0",
59
- "langchain.task.name": task,
60
- }
61
-
62
- self.assertTrue(
63
- self.span.set_attribute.has_calls(
64
- [call(key, value) for key, value in expected_attributes.items()],
65
- any_order=True,
66
- )
67
- )
68
-
69
- actual_calls = self.span.set_attribute.call_args_list
70
-
71
- for key, value in expected_attributes.items():
72
- self.assertIn(call(key, value), actual_calls)
73
-
74
- self.assertEqual(self.span.set_status.call_count, 1)
75
- self.assertTrue(self.span.set_status.has_calls([call(Status(StatusCode.OK))]))
76
-
77
- expected_result_data = {"key": "value"}
78
- self.assertEqual(result.key, expected_result_data["key"])
79
-
80
-
81
- if __name__ == "__main__":
82
- unittest.main()
@@ -1,141 +0,0 @@
1
- import unittest
2
- from unittest.mock import MagicMock, Mock, patch, call
3
- from langtrace_python_sdk.instrumentation.langchain_core.patch import (
4
- generic_patch,
5
- runnable_patch,
6
- )
7
- from opentelemetry.trace import SpanKind
8
- from opentelemetry.trace import get_tracer
9
- import importlib.metadata
10
- import openai
11
- from langtrace_python_sdk.constants.instrumentation.openai import APIS
12
- from opentelemetry.trace.status import Status, StatusCode
13
- import json
14
- from tests.utils import common_setup
15
-
16
-
17
- class TestGenericPatch(unittest.TestCase):
18
- data = {"items": "value"}
19
-
20
- def setUp(self):
21
- self.langchain_mock, self.tracer, self.span = common_setup(self.data, None)
22
-
23
- def tearDown(self):
24
- # Clean up after each test case
25
- pass
26
-
27
- def test_generic_patch(self):
28
- # Arrange
29
- method_name = "example_method"
30
- trace_output = False
31
- trace_input = True
32
- task = "retriever"
33
- args = (1, 2, 3)
34
- kwargs = {"key": "value"}
35
- version = importlib.metadata.version("langchain-core")
36
-
37
- # Act
38
- wrapped_function = generic_patch(
39
- "langchain_core.retrievers",
40
- task,
41
- self.tracer,
42
- version,
43
- trace_output,
44
- trace_input,
45
- )
46
- result = wrapped_function(self.langchain_mock, MagicMock(), args, kwargs)
47
-
48
- # Assert
49
- self.assertTrue(
50
- self.tracer.start_as_current_span.called_once_with(
51
- method_name, kind=SpanKind.CLIENT
52
- )
53
- )
54
-
55
- service_provider = "Langchain Core"
56
- expected_attributes = {
57
- "langtrace.sdk.name": "langtrace-python-sdk",
58
- "langtrace.service.name": service_provider,
59
- "langtrace.service.type": "framework",
60
- "langtrace.service.version": version,
61
- "langtrace.version": "1.0.0",
62
- "langchain.task.name": task,
63
- }
64
-
65
- self.assertTrue(
66
- self.span.set_attribute.has_calls(
67
- [call(key, value) for key, value in expected_attributes.items()],
68
- any_order=True,
69
- )
70
- )
71
-
72
- actual_calls = self.span.set_attribute.call_args_list
73
- for key, value in expected_attributes.items():
74
- self.assertIn(call(key, value), actual_calls)
75
-
76
- self.assertEqual(self.span.set_status.call_count, 1)
77
- self.assertTrue(self.span.set_status.has_calls([call(Status(StatusCode.OK))]))
78
-
79
- expected_result_data = {"items": "value"}
80
- self.assertEqual(result.items, expected_result_data["items"])
81
-
82
- def test_runnable_patch(self):
83
- # Arrange
84
- method_name = "example_method"
85
- trace_output = False
86
- trace_input = True
87
- args = (1, 2, 3)
88
- kwargs = {"key": "value"}
89
- version = importlib.metadata.version("langchain-core")
90
-
91
- # Act
92
- wrapped_function = runnable_patch(
93
- "langchain_core.runnables.passthrough",
94
- "runnablepassthrough",
95
- self.tracer,
96
- version,
97
- trace_output,
98
- trace_input,
99
- )
100
-
101
- result = wrapped_function(self.langchain_mock, MagicMock(), args, kwargs)
102
-
103
- # Assert
104
- self.assertTrue(
105
- self.tracer.start_as_current_span.called_once_with(
106
- method_name, kind=SpanKind.CLIENT
107
- )
108
- )
109
-
110
- service_provider = "Langchain Core"
111
- expected_attributes = {
112
- "langtrace.sdk.name": "langtrace-python-sdk",
113
- "langtrace.service.name": service_provider,
114
- "langtrace.service.type": "framework",
115
- "langtrace.service.version": version,
116
- "langtrace.version": "1.0.0",
117
- "langchain.task.name": "runnablepassthrough",
118
- }
119
-
120
- self.assertTrue(
121
- self.span.set_attribute.has_calls(
122
- [call(key, value) for key, value in expected_attributes.items()],
123
- any_order=True,
124
- )
125
- )
126
-
127
- actual_calls = self.span.set_attribute.call_args_list
128
-
129
- for key, value in expected_attributes.items():
130
- self.assertIn(call(key, value), actual_calls)
131
-
132
- self.assertEqual(self.span.set_status.call_count, 1)
133
- self.assertTrue(self.span.set_status.has_calls([call(Status(StatusCode.OK))]))
134
-
135
- expected_result_data = {"items": "value"}
136
-
137
- self.assertEqual(result.items, expected_result_data["items"])
138
-
139
-
140
- if __name__ == "__main__":
141
- unittest.main()