feat: Enhance Observability with ArizePhoenixTracer Integration (#5058)

* Added ArizePhoenixTracer

* [autofix.ci] apply automated fixes

* Fixed Ruff Style Check Errors

* add Arize icon

* Fixed Mypy Errors

* Refactor ArizeIcon component to use arrow function syntax

Co-authored-by: Jeff Carpenter <jeffrey.carpenter@datastax.com>
Co-authored-by: Ítalo Johnny <italojohnnydosanjos@gmail.com>
Co-authored-by: anovazzi1 <otavio2204@gmail.com>
This commit is contained in:
Ali Saleh 2024-12-05 21:09:02 +05:00 • committed by GitHub
commit 7c01b5a040
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 453 additions and 0 deletions

View file

@ -112,6 +112,8 @@ dependencies = [
"mem0ai>=0.1.26,<1.0.0",
"aiofile>=3.9.0,<4.0.0",
"sseclient-py==1.8.0",
"arize-phoenix-otel>=0.6.1",
"openinference-instrumentation-langchain>=0.1.29",
]
[project.urls]

View file

@ -0,0 +1,324 @@
from __future__ import annotations
import json
import math
import os
import traceback
import types
from datetime import datetime, timezone
from typing import TYPE_CHECKING, Any
from langchain_core.documents import Document
from langchain_core.messages import BaseMessage, HumanMessage, SystemMessage
from loguru import logger
from openinference.semconv.trace import OpenInferenceMimeTypeValues, SpanAttributes
from opentelemetry.semconv.trace import SpanAttributes as OTELSpanAttributes
from opentelemetry.trace import Span, Status, StatusCode
from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator
from typing_extensions import override
from langflow.schema.data import Data
from langflow.schema.message import Message
from langflow.services.tracing.base import BaseTracer
if TYPE_CHECKING:
from collections.abc import Sequence
from uuid import UUID
from langchain.callbacks.base import BaseCallbackHandler
from opentelemetry.propagators.textmap import CarrierT
from opentelemetry.util.types import AttributeValue
from langflow.graph.vertex.base import Vertex
from langflow.services.tracing.schema import Log
class ArizePhoenixTracer(BaseTracer):
flow_id: str
def __init__(self, trace_name: str, trace_type: str, project_name: str, trace_id: UUID):
"""Initializes the ArizePhoenixTracer instance and sets up a root span."""
self.trace_name = trace_name
self.trace_type = trace_type
self.project_name = project_name
self.trace_id = trace_id
self.flow_id = trace_name.split(" - ")[-1]
try:
self._ready = self.setup_arize_phoenix()
if not self._ready:
return
self.tracer = self.tracer_provider.get_tracer(__name__)
self.propagator = TraceContextTextMapPropagator()
self.carrier: dict[Any, CarrierT] = {}
with self.tracer.start_as_current_span(
name=self.flow_id,
start_time=self._get_current_timestamp(),
) as root_span:
root_span.set_attribute(SpanAttributes.OPENINFERENCE_SPAN_KIND, self.trace_type)
root_span.set_status(Status(StatusCode.OK))
self.propagator.inject(carrier=self.carrier)
self.child_spans: dict[str, Span] = {}
except Exception: # noqa: BLE001
logger.opt(exception=True).debug("Error setting up Arize/Phoenix tracer")
self._ready = False
@property
def ready(self):
"""Indicates if the tracer is ready for usage."""
return self._ready
def setup_arize_phoenix(self) -> bool:
"""Configures Arize/Phoenix specific environment variables and registers the tracer provider."""
arize_phoenix_batch = os.getenv("ARIZE_PHOENIX_BATCH", "False").lower() in (
"true",
"t",
"yes",
"y",
"1",
)
# Arize Config
arize_api_key = os.getenv("ARIZE_API_KEY", None)
arize_space_id = os.getenv("ARIZE_SPACE_ID", None)
arize_collector_endpoint = os.getenv("ARIZE_COLLECTOR_ENDPOINT", "https://otlp.arize.com")
enable_arize_tracing = bool(arize_api_key and arize_space_id)
arize_endpoint = f"{arize_collector_endpoint}/v1"
arize_headers = {
"api_key": arize_api_key,
"space_id": arize_space_id,
"authorization": f"Bearer {arize_api_key}",
}
# Phoenix Config
phoenix_api_key = os.getenv("PHOENIX_API_KEY", None)
phoenix_collector_endpoint = os.getenv("PHOENIX_COLLECTOR_ENDPOINT", "https://app.phoenix.arize.com")
enable_phoenix_tracing = bool(phoenix_api_key)
phoenix_endpoint = f"{phoenix_collector_endpoint}/v1/traces"
phoenix_headers = {
"api_key": phoenix_api_key,
"authorization": f"Bearer {phoenix_api_key}",
}
if not (enable_arize_tracing or enable_phoenix_tracing):
return False
try:
from phoenix.otel import (
PROJECT_NAME,
BatchSpanProcessor,
GRPCSpanExporter,
HTTPSpanExporter,
Resource,
SimpleSpanProcessor,
TracerProvider,
)
project_name = self.project_name or self.flow_id
attributes = {PROJECT_NAME: project_name, "model_id": project_name}
resource = Resource.create(attributes=attributes)
tracer_provider = TracerProvider(resource=resource, verbose=False)
span_processor = BatchSpanProcessor if arize_phoenix_batch else SimpleSpanProcessor
if enable_arize_tracing:
tracer_provider.add_span_processor(
span_processor=span_processor(
span_exporter=GRPCSpanExporter(endpoint=arize_endpoint, headers=arize_headers),
)
)
if enable_phoenix_tracing:
tracer_provider.add_span_processor(
span_processor=span_processor(
span_exporter=HTTPSpanExporter(
endpoint=phoenix_endpoint,
headers=phoenix_headers,
),
)
)
self.tracer_provider = tracer_provider
except ImportError:
logger.exception(
"Could not import arize-phoenix-otel. Please install it with `pip install arize-phoenix-otel`."
)
return False
try:
from openinference.instrumentation.langchain import LangChainInstrumentor
LangChainInstrumentor().instrument(tracer_provider=self.tracer_provider, skip_dep_check=True)
except ImportError:
logger.exception(
"Could not import LangChainInstrumentor."
"Please install it with `pip install openinference-instrumentation-langchain`."
)
return False
return True
@override
def add_trace(
self,
trace_id: str,
trace_name: str,
trace_type: str,
inputs: dict[str, Any],
metadata: dict[str, Any] | None = None,
vertex: Vertex | None = None,
) -> None:
"""Adds a trace span, attaching inputs and metadata as attributes."""
if not self._ready:
return
span_context = self.propagator.extract(carrier=self.carrier)
child_span = self.tracer.start_span(
name=trace_name,
context=span_context,
start_time=self._get_current_timestamp(),
)
if trace_type == "prompt":
child_span.set_attribute(SpanAttributes.OPENINFERENCE_SPAN_KIND, "chain")
else:
child_span.set_attribute(SpanAttributes.OPENINFERENCE_SPAN_KIND, trace_type)
if "session_id" in inputs and len(inputs["session_id"]) > 0 and inputs["session_id"] != self.flow_id:
child_span.set_attribute(SpanAttributes.SESSION_ID, inputs["session_id"])
else:
child_span.set_attribute(SpanAttributes.SESSION_ID, self.flow_id)
processed_inputs = self._convert_to_arize_phoenix_types(inputs) if inputs else {}
if processed_inputs:
child_span.set_attribute(SpanAttributes.INPUT_VALUE, self._safe_json_dumps(processed_inputs))
child_span.set_attribute(SpanAttributes.INPUT_MIME_TYPE, OpenInferenceMimeTypeValues.JSON.value)
processed_metadata = self._convert_to_arize_phoenix_types(metadata) if metadata else {}
if processed_metadata:
for key, value in processed_metadata.items():
child_span.set_attribute(f"{SpanAttributes.METADATA}.{key}", value)
self.child_spans[trace_id] = child_span
@override
def end_trace(
self,
trace_id: str,
trace_name: str,
outputs: dict[str, Any] | None = None,
error: Exception | None = None,
logs: Sequence[Log | dict] = (),
) -> None:
"""Ends a trace span, attaching outputs, errors, and logs as attributes."""
if not self._ready or trace_id not in self.child_spans:
return
child_span = self.child_spans[trace_id]
processed_outputs = self._convert_to_arize_phoenix_types(outputs) if outputs else {}
if processed_outputs:
child_span.set_attribute(SpanAttributes.OUTPUT_VALUE, self._safe_json_dumps(processed_outputs))
child_span.set_attribute(SpanAttributes.OUTPUT_MIME_TYPE, OpenInferenceMimeTypeValues.JSON.value)
logs_dicts = [log if isinstance(log, dict) else log.model_dump() for log in logs]
processed_logs = (
self._convert_to_arize_phoenix_types({log.get("name"): log for log in logs_dicts}) if logs else {}
)
if processed_logs:
for key, value in processed_logs.items():
child_span.set_attribute(f"logs.{key}", value)
if error:
error_string = self._error_to_string(error)
child_span.set_status(Status(StatusCode.ERROR, error_string))
child_span.set_attribute("error.message", error_string)
if isinstance(error, Exception):
child_span.record_exception(error)
else:
exception_type = error.__class__.__name__
exception_message = str(error)
if not exception_message:
exception_message = repr(error)
attributes: dict[str, AttributeValue] = {
OTELSpanAttributes.EXCEPTION_TYPE: exception_type,
OTELSpanAttributes.EXCEPTION_MESSAGE: exception_message,
OTELSpanAttributes.EXCEPTION_ESCAPED: False,
OTELSpanAttributes.EXCEPTION_STACKTRACE: error_string,
}
child_span.add_event(name="exception", attributes=attributes)
else:
child_span.set_status(Status(StatusCode.OK))
child_span.end(end_time=self._get_current_timestamp())
self.child_spans.pop(trace_id)
@override
def end(
self,
inputs: dict[str, Any],
outputs: dict[str, Any],
error: Exception | None = None,
metadata: dict[str, Any] | None = None,
) -> None:
"""Ends tracing with the specified inputs, outputs, errors, and metadata as attributes."""
if not self._ready:
return
def _convert_to_arize_phoenix_types(self, io_dict: dict[str, Any]):
"""Converts data types to Arize/Phoenix compatible formats."""
return {key: self._convert_to_arize_phoenix_type(value) for key, value in io_dict.items()}
def _convert_to_arize_phoenix_type(self, value):
"""Recursively converts a value to a Arize/Phoenix compatible type."""
if isinstance(value, dict):
for key, _value in value.copy().items():
_value = self._convert_to_arize_phoenix_type(_value)
value[key] = _value
elif isinstance(value, list):
value = [self._convert_to_arize_phoenix_type(v) for v in value]
elif isinstance(value, Message):
value = value.text
elif isinstance(value, Data):
value = value.get_text()
elif isinstance(value, (BaseMessage | HumanMessage | SystemMessage)):
value = value.content
elif isinstance(value, Document):
value = value.page_content
elif isinstance(value, (types.GeneratorType | types.NoneType)):
value = str(value)
elif isinstance(value, float) and not math.isfinite(value):
value = "NaN"
return value
def _error_to_string(self, error: Exception | None):
"""Converts an error to a string with traceback details."""
error_message = None
if error:
string_stacktrace = traceback.format_exception(error)
error_message = f"{error.__class__.__name__}: {error}\n\n{string_stacktrace}"
return error_message
def _get_current_timestamp(self) -> int:
"""Gets the current UTC timestamp in nanoseconds."""
return int(datetime.now(timezone.utc).timestamp() * 1_000_000_000)
def _safe_json_dumps(self, obj: Any, **kwargs: Any) -> str:
"""A convenience wrapper around `json.dumps` that ensures that any object can be safely encoded."""
return json.dumps(obj, default=str, ensure_ascii=False, **kwargs)
def get_langchain_callback(self) -> BaseCallbackHandler | None:
"""Returns the LangChain callback handler if applicable."""
return None

View file

@ -40,6 +40,12 @@ def _get_langfuse_tracer():
return LangFuseTracer
def _get_arize_phoenix_tracer():
from langflow.services.tracing.arize_phoenix import ArizePhoenixTracer
return ArizePhoenixTracer
class TracingService(Service):
name = "tracing_service"
@ -111,6 +117,7 @@ class TracingService(Service):
await asyncio.to_thread(self._initialize_langsmith_tracer)
await asyncio.to_thread(self._initialize_langwatch_tracer)
await asyncio.to_thread(self._initialize_langfuse_tracer)
await asyncio.to_thread(self._initialize_arize_phoenix_tracer)
except Exception as e: # noqa: BLE001
logger.debug(f"Error initializing tracers: {e}")
@ -145,6 +152,16 @@ class TracingService(Service):
trace_id=self.run_id,
)
def _initialize_arize_phoenix_tracer(self) -> None:
self.project_name = os.getenv("ARIZE_PHOENIX_PROJECT", "Langflow")
arize_phoenix_tracer = _get_arize_phoenix_tracer()
self._tracers["arize_phoenix"] = arize_phoenix_tracer(
trace_name=self.run_name,
trace_type="chain",
project_name=self.project_name,
trace_id=self.run_id,
)
def set_run_name(self, name: str) -> None:
self.run_name = name

View file

@ -0,0 +1,20 @@
const SvgArize = (props) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width="823"
height="807"
viewBox="0 0 823 807"
{...props}
>
<path
fill="#EE2C8B"
d="m451 30 2.227 1.594C461.463 38.183 467.08 47.396 473 56l1.594 2.312c2.669 3.876 5.322 7.762 7.969 11.653 3.713 5.453 7.478 10.871 11.25 16.285a37210.514 37210.514 0 0 0 6.14 8.81q5.026 7.218 10.047 14.44l4 5.75 40 57.5 2.001 2.877q1.995 2.866 3.988 5.733 5.091 7.32 10.187 14.636c5.837 8.383 11.674 16.766 17.449 25.191 4.57 6.666 9.198 13.29 13.837 19.906 6.52 9.3 13.001 18.627 19.484 27.953q3.117 4.482 6.238 8.962c5.834 8.379 11.668 16.758 17.441 25.18 4.57 6.665 9.198 13.288 13.837 19.905 6.52 9.3 13.001 18.627 19.484 27.953q3.117 4.482 6.238 8.962c5.834 8.379 11.668 16.758 17.441 25.18 4.57 6.665 9.198 13.288 13.837 19.905 6.52 9.3 13.001 18.627 19.484 27.953q3.117 4.482 6.238 8.962c5.834 8.379 11.668 16.758 17.441 25.18a2035 2035 0 0 0 12.063 17.375c14.872 21.225 14.872 21.225 21.42 30.702q1.839 2.658 3.685 5.31C809.298 540 817 558.968 812.813 583.062c-1.319 6.836-3.477 12.834-6.813 18.938l-1.375 2.578c-8.507 15.073-22.217 26.158-38.77 31.422-5.58 1.46-11.108 2.529-16.855 3l-1.926.16c-18.082.783-36.106-6.549-49.886-17.91-7.241-6.766-12.611-15.118-18.188-23.25l-1.622-2.355a2641 2641 0 0 1-8.073-11.81c-3.332-4.89-6.718-9.741-10.117-14.585-9.663-13.772-19.3-27.561-28.813-41.437-4.57-6.666-9.198-13.29-13.837-19.906-6.52-9.3-13.001-18.627-19.484-27.953q-3.117-4.482-6.238-8.962c-5.834-8.379-11.668-16.758-17.441-25.18-4.57-6.665-9.198-13.288-13.837-19.905-6.52-9.3-13.001-18.627-19.484-27.953q-3.117-4.482-6.238-8.962c-5.834-8.379-11.668-16.758-17.441-25.18-4.57-6.665-9.198-13.288-13.837-19.905-6.52-9.3-13.001-18.627-19.484-27.953q-3.117-4.482-6.238-8.962c-11.473-16.476-11.473-16.476-16.231-23.42-1.741-2.54-3.49-5.075-5.239-7.611a863 863 0 0 1-2.813-4.125C444.018 259.285 435.899 248.007 422 241l-1.879-1.027c-7.75-3.633-18.27-3.348-26.246-.598C381.837 245.114 373.447 254.18 366 265l-1.467 2.114c-2.73 3.943-5.433 7.904-8.123 11.874-3.36 4.954-6.782 9.863-10.223 14.762-9.662 13.772-19.3 27.561-28.812 41.438-4.57 6.665-9.198 13.288-13.837 19.905-6.52 9.3-13.001 18.627-19.484 27.953q-3.117 4.482-6.238 8.962c-5.834 8.379-11.668 16.758-17.441 25.18-4.57 6.665-9.198 13.288-13.837 19.905-6.52 9.3-13.001 18.627-19.484 27.953q-3.117 4.482-6.238 8.962c-5.834 8.379-11.668 16.758-17.441 25.18-4.57 6.665-9.198 13.288-13.837 19.905-6.52 9.3-13.001 18.627-19.484 27.953q-3.117 4.482-6.238 8.962c-5.834 8.379-11.667 16.758-17.441 25.18Q143.69 585.096 141 589l-1.68 2.442C123.518 614.38 108.918 633 79.855 638.398c-17.483 2.552-35.658-1.514-50.261-11.457-16.17-12.103-25.2-27.585-29.281-47.254-2.315-27.315 8.336-45.857 23.585-67.304a856 856 0 0 0 8.477-12.196 1951 1951 0 0 1 12.313-17.75 9072 9072 0 0 0 21.258-30.483q3.117-4.482 6.238-8.962c5.834-8.379 11.668-16.758 17.441-25.18 4.57-6.665 9.198-13.288 13.837-19.905 6.52-9.3 13.001-18.627 19.484-27.953q3.117-4.482 6.238-8.962c5.834-8.379 11.668-16.758 17.441-25.18 4.57-6.665 9.198-13.288 13.837-19.905 6.52-9.3 13.001-18.627 19.484-27.953q3.117-4.482 6.238-8.962c5.834-8.379 11.668-16.758 17.441-25.18 4.57-6.665 9.198-13.288 13.837-19.905 6.527-9.31 13.016-18.647 19.506-27.984a38524 38524 0 0 1 12.02-17.282q5.092-7.32 10.188-14.637c5.837-8.383 11.674-16.766 17.449-25.191 4.57-6.666 9.198-13.29 13.837-19.906 6.52-9.3 13.001-18.627 19.484-27.953q3.117-4.482 6.238-8.962c5.877-8.44 11.751-16.883 17.566-25.367q1.948-2.834 3.9-5.664 1.419-2.062 2.833-4.125c15.224-22.165 29.843-36.059 57.115-41.332C416.54 12.698 435.684 18.879 451 30"
></path>
<path
fill="#EE2C8A"
d="M460.543 655.941C476.174 669.038 488.656 688.385 491 709c1.912 26.065-2.94 48.653-20 69-14.01 16.165-34.113 26.37-55.375 28.75q-2.31.156-4.625.25l-2.117.121c-20.961.653-42.347-7.16-57.883-21.121l-2.785-2.5c-16.102-15.238-25.322-36.43-26.246-58.55.178-22.55 6.822-42.883 22.031-59.95.665-.76 1.33-1.519 2.016-2.3 9.578-10.271 21.872-16.981 34.984-21.7l2.855-1.031c26.5-7.725 55.232-.53 76.688 15.972"
></path>
</svg>
);
export default SvgArize;

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9 KiB

View file

@ -0,0 +1,8 @@
import React, { forwardRef } from "react";
import SvgArize from "./Arize";
export const ArizeIcon = forwardRef<SVGSVGElement, React.PropsWithChildren<{}>>(
(props, ref) => {
return <SvgArize ref={ref} {...props} />;
},
);

View file

@ -233,6 +233,7 @@ import { FaApple, FaDiscord, FaGithub } from "react-icons/fa";
import { AWSIcon } from "../icons/AWS";
import { AirbyteIcon } from "../icons/Airbyte";
import { AnthropicIcon } from "../icons/Anthropic";
import { ArizeIcon } from "../icons/Arize";
import { AssemblyAIIcon } from "../icons/AssemblyAI";
import { AstraDBIcon } from "../icons/AstraDB";
import { AzureIcon } from "../icons/Azure";
@ -704,6 +705,7 @@ export const nodeIconsLucide: iconsType = {
SerpSearch: SerpSearchIcon,
SearchAPI: SearchAPIIcon,
Wikipedia: WikipediaIcon,
Arize: ArizeIcon,
//Node Icons
model_specs: FileSliders,

74
uv.lock generated
View file

@ -215,6 +215,23 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/81/29/5ecc3a15d5a33e31b26c11426c45c501e439cb865d0bff96315d86443b78/appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c", size = 4321 },
]
[[package]]
name = "arize-phoenix-otel"
version = "0.6.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "openinference-semantic-conventions" },
{ name = "opentelemetry-exporter-otlp" },
{ name = "opentelemetry-proto" },
{ name = "opentelemetry-sdk" },
{ name = "opentelemetry-semantic-conventions" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b7/70/41eef869b46882aae54e9b8c757223da93160633bba82752158a77b1566a/arize_phoenix_otel-0.6.1.tar.gz", hash = "sha256:f5f7a257159f49b398dfe2ba45bb70e22766cdf61bb365c0c47dc55f329bfe95", size = 9691 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/9c/f3/f2390a9d7b3ee46b28a8ccee6510a489ae0974f31b51c67d79cab4c45ef8/arize_phoenix_otel-0.6.1-py3-none-any.whl", hash = "sha256:fc00dd847a248cece5f20dffeb8fabfab72c1c38f198bcee4260618c6af1d8a5", size = 10783 },
]
[[package]]
name = "asgi-lifespan"
version = "2.1.0"
@ -3522,6 +3539,7 @@ version = "1.1.1"
source = { editable = "." }
dependencies = [
{ name = "aiofile" },
{ name = "arize-phoenix-otel" },
{ name = "assemblyai" },
{ name = "astra-assistants", extra = ["tools"] },
{ name = "atlassian-python-api" },
@ -3578,6 +3596,7 @@ dependencies = [
{ name = "networkx" },
{ name = "nltk" },
{ name = "numexpr" },
{ name = "openinference-instrumentation-langchain" },
{ name = "opensearch-py" },
{ name = "pgvector" },
{ name = "pyarrow" },
@ -3666,6 +3685,7 @@ dev = [
[package.metadata]
requires-dist = [
{ name = "aiofile", specifier = ">=3.9.0,<4.0.0" },
{ name = "arize-phoenix-otel", specifier = ">=0.6.1" },
{ name = "assemblyai", specifier = ">=0.33.0,<1.0.0" },
{ name = "astra-assistants", extras = ["tools"], specifier = "~=2.2.6" },
{ name = "atlassian-python-api", specifier = ">=3.41.16,<4.0.0" },
@ -3730,6 +3750,7 @@ requires-dist = [
{ name = "networkx", specifier = ">=3.1,<4.0.0" },
{ name = "nltk", specifier = ">=3.9.1,<4.0.0" },
{ name = "numexpr", specifier = ">=2.8.6,<3.0.0" },
{ name = "openinference-instrumentation-langchain", specifier = ">=0.1.29" },
{ name = "opensearch-py", specifier = ">=2.7.1,<3.0.0" },
{ name = "pgvector", specifier = ">=0.2.3,<1.0.0" },
{ name = "pyarrow", specifier = ">=14.0.0,<18.0.0" },
@ -4931,6 +4952,46 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/39/1e/9dc3ccee95d0e16e54e353d3c355bb7cc506d56a2dbb0a07bc739cc48eac/openai-1.52.0-py3-none-any.whl", hash = "sha256:0c249f20920183b0a2ca4f7dba7b0452df3ecd0fa7985eb1d91ad884bc3ced9c", size = 386947 },
]
[[package]]
name = "openinference-instrumentation"
version = "0.1.19"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "openinference-semantic-conventions" },
{ name = "opentelemetry-api" },
{ name = "opentelemetry-sdk" },
]
sdist = { url = "https://files.pythonhosted.org/packages/f6/96/b2c98e52caf35d1f9f1e5499646498667afbf17155c7ea8e990956bca98b/openinference_instrumentation-0.1.19.tar.gz", hash = "sha256:48e91e655dfe49a320f898bb1ec9e68d919e2e222b152cd33366fc94502e9a27", size = 11801 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/dd/5c/5632de07c9ec5a18f9f16c1b50814cfee46cdefca9ff9417e859a42c38f8/openinference_instrumentation-0.1.19-py3-none-any.whl", hash = "sha256:f1a6e31c0c7668f5e3849f7d4a0156409456f35ea1cb19d7495b31f2f5b7d3e5", size = 14569 },
]
[[package]]
name = "openinference-instrumentation-langchain"
version = "0.1.29"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "openinference-instrumentation" },
{ name = "openinference-semantic-conventions" },
{ name = "opentelemetry-api" },
{ name = "opentelemetry-instrumentation" },
{ name = "opentelemetry-semantic-conventions" },
{ name = "wrapt" },
]
sdist = { url = "https://files.pythonhosted.org/packages/10/d2/185e4dd18431f35faba90482f511328d283e71ed12737e050664f7b5efeb/openinference_instrumentation_langchain-0.1.29.tar.gz", hash = "sha256:f6c10079c91f810cff39ff24c278e41d16df0c3706b230e1859f46cad20f8e0b", size = 45868 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/eb/ba/b0855a5f9bb565e462d85ecfad63d108c4c4bc209a34850623273f7dbcef/openinference_instrumentation_langchain-0.1.29-py3-none-any.whl", hash = "sha256:9faaedb62f90ca8099b48c0b9ded97fe109a646b756f4bf7c98479b500a8446d", size = 17043 },
]
[[package]]
name = "openinference-semantic-conventions"
version = "0.1.12"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/5f/ae/0bf5e0dcda695fd66428da05880e137c67f6fcbc494b31edec72635dc9e1/openinference_semantic_conventions-0.1.12.tar.gz", hash = "sha256:f1a378429873128892f44505a994e2b29277a8dc1be9a56b1726b1069d70dcdc", size = 9240 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f0/fb/ea6c04bb12b2e5ade3faf32b24d45ffce9a21a1468ea179de96ceec1fb3d/openinference_semantic_conventions-0.1.12-py3-none-any.whl", hash = "sha256:3489262a148ba25b3203e500dedcf8955f0c68072f7e45db3a958c0d12585e62", size = 9061 },
]
[[package]]
name = "opensearch-py"
version = "2.7.1"
@ -4960,6 +5021,19 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/fb/1f/737dcdbc9fea2fa96c1b392ae47275165a7c641663fbb08a8d252968eed2/opentelemetry_api-1.27.0-py3-none-any.whl", hash = "sha256:953d5871815e7c30c81b56d910c707588000fff7a3ca1c73e6531911d53065e7", size = 63970 },
]
[[package]]
name = "opentelemetry-exporter-otlp"
version = "1.27.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "opentelemetry-exporter-otlp-proto-grpc" },
{ name = "opentelemetry-exporter-otlp-proto-http" },
]
sdist = { url = "https://files.pythonhosted.org/packages/fc/d3/8156cc14e8f4573a3572ee7f30badc7aabd02961a09acc72ab5f2c789ef1/opentelemetry_exporter_otlp-1.27.0.tar.gz", hash = "sha256:4a599459e623868cc95d933c301199c2367e530f089750e115599fccd67cb2a1", size = 6166 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/59/6d/95e1fc2c8d945a734db32e87a5aa7a804f847c1657a21351df9338bd1c9c/opentelemetry_exporter_otlp-1.27.0-py3-none-any.whl", hash = "sha256:7688791cbdd951d71eb6445951d1cfbb7b6b2d7ee5948fac805d404802931145", size = 7001 },
]
[[package]]
name = "opentelemetry-exporter-otlp-proto-common"
version = "1.27.0"