diff --git a/langfuse/api/__init__.py b/langfuse/api/__init__.py index 3dd6de4e4..ce613a95d 100644 --- a/langfuse/api/__init__.py +++ b/langfuse/api/__init__.py @@ -136,6 +136,14 @@ OptionalObservationBody, OrganizationProject, OrganizationProjectsResponse, + OtelAttribute, + OtelAttributeValue, + OtelResource, + OtelResourceSpan, + OtelScope, + OtelScopeSpan, + OtelSpan, + OtelTraceResponse, PaginatedAnnotationQueueItems, PaginatedAnnotationQueues, PaginatedDatasetItems, @@ -153,6 +161,7 @@ Prompt, PromptMeta, PromptMetaListResponse, + PromptType, Prompt_Chat, Prompt_Text, ResourceMeta, @@ -221,6 +230,7 @@ metrics, models, observations, + opentelemetry, organizations, projects, prompt_version, @@ -370,6 +380,14 @@ "OptionalObservationBody", "OrganizationProject", "OrganizationProjectsResponse", + "OtelAttribute", + "OtelAttributeValue", + "OtelResource", + "OtelResourceSpan", + "OtelScope", + "OtelScopeSpan", + "OtelSpan", + "OtelTraceResponse", "PaginatedAnnotationQueueItems", "PaginatedAnnotationQueues", "PaginatedDatasetItems", @@ -387,6 +405,7 @@ "Prompt", "PromptMeta", "PromptMetaListResponse", + "PromptType", "Prompt_Chat", "Prompt_Text", "ResourceMeta", @@ -455,6 +474,7 @@ "metrics", "models", "observations", + "opentelemetry", "organizations", "projects", "prompt_version", diff --git a/langfuse/api/client.py b/langfuse/api/client.py index 619e649fa..646279b5a 100644 --- a/langfuse/api/client.py +++ b/langfuse/api/client.py @@ -30,6 +30,10 @@ from .resources.metrics.client import AsyncMetricsClient, MetricsClient from .resources.models.client import AsyncModelsClient, ModelsClient from .resources.observations.client import AsyncObservationsClient, ObservationsClient +from .resources.opentelemetry.client import ( + AsyncOpentelemetryClient, + OpentelemetryClient, +) from .resources.organizations.client import ( AsyncOrganizationsClient, OrganizationsClient, @@ -136,6 +140,7 @@ def __init__( self.metrics = MetricsClient(client_wrapper=self._client_wrapper) self.models = ModelsClient(client_wrapper=self._client_wrapper) self.observations = ObservationsClient(client_wrapper=self._client_wrapper) + self.opentelemetry = OpentelemetryClient(client_wrapper=self._client_wrapper) self.organizations = OrganizationsClient(client_wrapper=self._client_wrapper) self.projects = ProjectsClient(client_wrapper=self._client_wrapper) self.prompt_version = PromptVersionClient(client_wrapper=self._client_wrapper) @@ -240,6 +245,9 @@ def __init__( self.metrics = AsyncMetricsClient(client_wrapper=self._client_wrapper) self.models = AsyncModelsClient(client_wrapper=self._client_wrapper) self.observations = AsyncObservationsClient(client_wrapper=self._client_wrapper) + self.opentelemetry = AsyncOpentelemetryClient( + client_wrapper=self._client_wrapper + ) self.organizations = AsyncOrganizationsClient( client_wrapper=self._client_wrapper ) diff --git a/langfuse/api/reference.md b/langfuse/api/reference.md index f2e9baa07..afbd2ff8f 100644 --- a/langfuse/api/reference.md +++ b/langfuse/api/reference.md @@ -2442,7 +2442,7 @@ client.health.health() **Legacy endpoint for batch ingestion for Langfuse Observability.** --> Please use the OpenTelemetry endpoint (`/api/public/otel`). Learn more: https://langfuse.com/integrations/native/opentelemetry +-> Please use the OpenTelemetry endpoint (`/api/public/otel/v1/traces`). Learn more: https://langfuse.com/integrations/native/opentelemetry Within each batch, there can be multiple events. Each event has a type, an id, a timestamp, metadata and a body. @@ -3612,6 +3612,152 @@ client.observations.get_many() + + + + +## Opentelemetry +
client.opentelemetry.export_traces(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +**OpenTelemetry Traces Ingestion Endpoint** + +This endpoint implements the OTLP/HTTP specification for trace ingestion, providing native OpenTelemetry integration for Langfuse Observability. + +**Supported Formats:** +- Binary Protobuf: `Content-Type: application/x-protobuf` +- JSON Protobuf: `Content-Type: application/json` +- Supports gzip compression via `Content-Encoding: gzip` header + +**Specification Compliance:** +- Conforms to [OTLP/HTTP Trace Export](https://opentelemetry.io/docs/specs/otlp/#otlphttp) +- Implements `ExportTraceServiceRequest` message format + +**Documentation:** +- Integration guide: https://langfuse.com/integrations/native/opentelemetry +- Data model: https://langfuse.com/docs/observability/data-model +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from langfuse import ( + OtelAttribute, + OtelAttributeValue, + OtelResource, + OtelResourceSpan, + OtelScope, + OtelScopeSpan, + OtelSpan, +) +from langfuse.client import FernLangfuse + +client = FernLangfuse( + x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME", + x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION", + x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY", + username="YOUR_USERNAME", + password="YOUR_PASSWORD", + base_url="https://yourhost.com/path/to/api", +) +client.opentelemetry.export_traces( + resource_spans=[ + OtelResourceSpan( + resource=OtelResource( + attributes=[ + OtelAttribute( + key="service.name", + value=OtelAttributeValue( + string_value="my-service", + ), + ), + OtelAttribute( + key="service.version", + value=OtelAttributeValue( + string_value="1.0.0", + ), + ), + ], + ), + scope_spans=[ + OtelScopeSpan( + scope=OtelScope( + name="langfuse-sdk", + version="2.60.3", + ), + spans=[ + OtelSpan( + trace_id="0123456789abcdef0123456789abcdef", + span_id="0123456789abcdef", + name="my-operation", + kind=1, + start_time_unix_nano="1747872000000000000", + end_time_unix_nano="1747872001000000000", + attributes=[ + OtelAttribute( + key="langfuse.observation.type", + value=OtelAttributeValue( + string_value="generation", + ), + ) + ], + status={}, + ) + ], + ) + ], + ) + ], +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**resource_spans:** `typing.Sequence[OtelResourceSpan]` — Array of resource spans containing trace data as defined in the OTLP specification + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
diff --git a/langfuse/api/resources/__init__.py b/langfuse/api/resources/__init__.py index ae77cad9d..49e7a40ee 100644 --- a/langfuse/api/resources/__init__.py +++ b/langfuse/api/resources/__init__.py @@ -15,6 +15,7 @@ metrics, models, observations, + opentelemetry, organizations, projects, prompt_version, @@ -175,6 +176,16 @@ from .metrics import MetricsResponse from .models import CreateModelRequest, PaginatedModels from .observations import Observations, ObservationsViews +from .opentelemetry import ( + OtelAttribute, + OtelAttributeValue, + OtelResource, + OtelResourceSpan, + OtelScope, + OtelScopeSpan, + OtelSpan, + OtelTraceResponse, +) from .organizations import ( DeleteMembershipRequest, MembershipDeletionResponse, @@ -210,6 +221,7 @@ Prompt, PromptMeta, PromptMetaListResponse, + PromptType, Prompt_Chat, Prompt_Text, TextPrompt, @@ -389,6 +401,14 @@ "OptionalObservationBody", "OrganizationProject", "OrganizationProjectsResponse", + "OtelAttribute", + "OtelAttributeValue", + "OtelResource", + "OtelResourceSpan", + "OtelScope", + "OtelScopeSpan", + "OtelSpan", + "OtelTraceResponse", "PaginatedAnnotationQueueItems", "PaginatedAnnotationQueues", "PaginatedDatasetItems", @@ -406,6 +426,7 @@ "Prompt", "PromptMeta", "PromptMetaListResponse", + "PromptType", "Prompt_Chat", "Prompt_Text", "ResourceMeta", @@ -474,6 +495,7 @@ "metrics", "models", "observations", + "opentelemetry", "organizations", "projects", "prompt_version", diff --git a/langfuse/api/resources/ingestion/client.py b/langfuse/api/resources/ingestion/client.py index d5aa2f952..c009c507b 100644 --- a/langfuse/api/resources/ingestion/client.py +++ b/langfuse/api/resources/ingestion/client.py @@ -33,7 +33,7 @@ def batch( """ **Legacy endpoint for batch ingestion for Langfuse Observability.** - -> Please use the OpenTelemetry endpoint (`/api/public/otel`). Learn more: https://langfuse.com/integrations/native/opentelemetry + -> Please use the OpenTelemetry endpoint (`/api/public/otel/v1/traces`). Learn more: https://langfuse.com/integrations/native/opentelemetry Within each batch, there can be multiple events. Each event has a type, an id, a timestamp, metadata and a body. @@ -151,7 +151,7 @@ async def batch( """ **Legacy endpoint for batch ingestion for Langfuse Observability.** - -> Please use the OpenTelemetry endpoint (`/api/public/otel`). Learn more: https://langfuse.com/integrations/native/opentelemetry + -> Please use the OpenTelemetry endpoint (`/api/public/otel/v1/traces`). Learn more: https://langfuse.com/integrations/native/opentelemetry Within each batch, there can be multiple events. Each event has a type, an id, a timestamp, metadata and a body. diff --git a/langfuse/api/resources/opentelemetry/__init__.py b/langfuse/api/resources/opentelemetry/__init__.py new file mode 100644 index 000000000..bada2052f --- /dev/null +++ b/langfuse/api/resources/opentelemetry/__init__.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +from .types import ( + OtelAttribute, + OtelAttributeValue, + OtelResource, + OtelResourceSpan, + OtelScope, + OtelScopeSpan, + OtelSpan, + OtelTraceResponse, +) + +__all__ = [ + "OtelAttribute", + "OtelAttributeValue", + "OtelResource", + "OtelResourceSpan", + "OtelScope", + "OtelScopeSpan", + "OtelSpan", + "OtelTraceResponse", +] diff --git a/langfuse/api/resources/opentelemetry/client.py b/langfuse/api/resources/opentelemetry/client.py new file mode 100644 index 000000000..de17949d4 --- /dev/null +++ b/langfuse/api/resources/opentelemetry/client.py @@ -0,0 +1,317 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +from ...core.api_error import ApiError +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.pydantic_utilities import pydantic_v1 +from ...core.request_options import RequestOptions +from ..commons.errors.access_denied_error import AccessDeniedError +from ..commons.errors.error import Error +from ..commons.errors.method_not_allowed_error import MethodNotAllowedError +from ..commons.errors.not_found_error import NotFoundError +from ..commons.errors.unauthorized_error import UnauthorizedError +from .types.otel_resource_span import OtelResourceSpan +from .types.otel_trace_response import OtelTraceResponse + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class OpentelemetryClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def export_traces( + self, + *, + resource_spans: typing.Sequence[OtelResourceSpan], + request_options: typing.Optional[RequestOptions] = None, + ) -> OtelTraceResponse: + """ + **OpenTelemetry Traces Ingestion Endpoint** + + This endpoint implements the OTLP/HTTP specification for trace ingestion, providing native OpenTelemetry integration for Langfuse Observability. + + **Supported Formats:** + - Binary Protobuf: `Content-Type: application/x-protobuf` + - JSON Protobuf: `Content-Type: application/json` + - Supports gzip compression via `Content-Encoding: gzip` header + + **Specification Compliance:** + - Conforms to [OTLP/HTTP Trace Export](https://opentelemetry.io/docs/specs/otlp/#otlphttp) + - Implements `ExportTraceServiceRequest` message format + + **Documentation:** + - Integration guide: https://langfuse.com/integrations/native/opentelemetry + - Data model: https://langfuse.com/docs/observability/data-model + + Parameters + ---------- + resource_spans : typing.Sequence[OtelResourceSpan] + Array of resource spans containing trace data as defined in the OTLP specification + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + OtelTraceResponse + + Examples + -------- + from langfuse import ( + OtelAttribute, + OtelAttributeValue, + OtelResource, + OtelResourceSpan, + OtelScope, + OtelScopeSpan, + OtelSpan, + ) + from langfuse.client import FernLangfuse + + client = FernLangfuse( + x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME", + x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION", + x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY", + username="YOUR_USERNAME", + password="YOUR_PASSWORD", + base_url="https://yourhost.com/path/to/api", + ) + client.opentelemetry.export_traces( + resource_spans=[ + OtelResourceSpan( + resource=OtelResource( + attributes=[ + OtelAttribute( + key="service.name", + value=OtelAttributeValue( + string_value="my-service", + ), + ), + OtelAttribute( + key="service.version", + value=OtelAttributeValue( + string_value="1.0.0", + ), + ), + ], + ), + scope_spans=[ + OtelScopeSpan( + scope=OtelScope( + name="langfuse-sdk", + version="2.60.3", + ), + spans=[ + OtelSpan( + trace_id="0123456789abcdef0123456789abcdef", + span_id="0123456789abcdef", + name="my-operation", + kind=1, + start_time_unix_nano="1747872000000000000", + end_time_unix_nano="1747872001000000000", + attributes=[ + OtelAttribute( + key="langfuse.observation.type", + value=OtelAttributeValue( + string_value="generation", + ), + ) + ], + status={}, + ) + ], + ) + ], + ) + ], + ) + """ + _response = self._client_wrapper.httpx_client.request( + "api/public/otel/v1/traces", + method="POST", + json={"resourceSpans": resource_spans}, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return pydantic_v1.parse_obj_as(OtelTraceResponse, _response.json()) # type: ignore + if _response.status_code == 400: + raise Error(pydantic_v1.parse_obj_as(typing.Any, _response.json())) # type: ignore + if _response.status_code == 401: + raise UnauthorizedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 403: + raise AccessDeniedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 405: + raise MethodNotAllowedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 404: + raise NotFoundError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + + +class AsyncOpentelemetryClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def export_traces( + self, + *, + resource_spans: typing.Sequence[OtelResourceSpan], + request_options: typing.Optional[RequestOptions] = None, + ) -> OtelTraceResponse: + """ + **OpenTelemetry Traces Ingestion Endpoint** + + This endpoint implements the OTLP/HTTP specification for trace ingestion, providing native OpenTelemetry integration for Langfuse Observability. + + **Supported Formats:** + - Binary Protobuf: `Content-Type: application/x-protobuf` + - JSON Protobuf: `Content-Type: application/json` + - Supports gzip compression via `Content-Encoding: gzip` header + + **Specification Compliance:** + - Conforms to [OTLP/HTTP Trace Export](https://opentelemetry.io/docs/specs/otlp/#otlphttp) + - Implements `ExportTraceServiceRequest` message format + + **Documentation:** + - Integration guide: https://langfuse.com/integrations/native/opentelemetry + - Data model: https://langfuse.com/docs/observability/data-model + + Parameters + ---------- + resource_spans : typing.Sequence[OtelResourceSpan] + Array of resource spans containing trace data as defined in the OTLP specification + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + OtelTraceResponse + + Examples + -------- + import asyncio + + from langfuse import ( + OtelAttribute, + OtelAttributeValue, + OtelResource, + OtelResourceSpan, + OtelScope, + OtelScopeSpan, + OtelSpan, + ) + from langfuse.client import AsyncFernLangfuse + + client = AsyncFernLangfuse( + x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME", + x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION", + x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY", + username="YOUR_USERNAME", + password="YOUR_PASSWORD", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.opentelemetry.export_traces( + resource_spans=[ + OtelResourceSpan( + resource=OtelResource( + attributes=[ + OtelAttribute( + key="service.name", + value=OtelAttributeValue( + string_value="my-service", + ), + ), + OtelAttribute( + key="service.version", + value=OtelAttributeValue( + string_value="1.0.0", + ), + ), + ], + ), + scope_spans=[ + OtelScopeSpan( + scope=OtelScope( + name="langfuse-sdk", + version="2.60.3", + ), + spans=[ + OtelSpan( + trace_id="0123456789abcdef0123456789abcdef", + span_id="0123456789abcdef", + name="my-operation", + kind=1, + start_time_unix_nano="1747872000000000000", + end_time_unix_nano="1747872001000000000", + attributes=[ + OtelAttribute( + key="langfuse.observation.type", + value=OtelAttributeValue( + string_value="generation", + ), + ) + ], + status={}, + ) + ], + ) + ], + ) + ], + ) + + + asyncio.run(main()) + """ + _response = await self._client_wrapper.httpx_client.request( + "api/public/otel/v1/traces", + method="POST", + json={"resourceSpans": resource_spans}, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return pydantic_v1.parse_obj_as(OtelTraceResponse, _response.json()) # type: ignore + if _response.status_code == 400: + raise Error(pydantic_v1.parse_obj_as(typing.Any, _response.json())) # type: ignore + if _response.status_code == 401: + raise UnauthorizedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 403: + raise AccessDeniedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 405: + raise MethodNotAllowedError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + if _response.status_code == 404: + raise NotFoundError( + pydantic_v1.parse_obj_as(typing.Any, _response.json()) + ) # type: ignore + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) diff --git a/langfuse/api/resources/opentelemetry/types/__init__.py b/langfuse/api/resources/opentelemetry/types/__init__.py new file mode 100644 index 000000000..4ca603db6 --- /dev/null +++ b/langfuse/api/resources/opentelemetry/types/__init__.py @@ -0,0 +1,21 @@ +# This file was auto-generated by Fern from our API Definition. + +from .otel_attribute import OtelAttribute +from .otel_attribute_value import OtelAttributeValue +from .otel_resource import OtelResource +from .otel_resource_span import OtelResourceSpan +from .otel_scope import OtelScope +from .otel_scope_span import OtelScopeSpan +from .otel_span import OtelSpan +from .otel_trace_response import OtelTraceResponse + +__all__ = [ + "OtelAttribute", + "OtelAttributeValue", + "OtelResource", + "OtelResourceSpan", + "OtelScope", + "OtelScopeSpan", + "OtelSpan", + "OtelTraceResponse", +] diff --git a/langfuse/api/resources/opentelemetry/types/otel_attribute.py b/langfuse/api/resources/opentelemetry/types/otel_attribute.py new file mode 100644 index 000000000..91b9e2b70 --- /dev/null +++ b/langfuse/api/resources/opentelemetry/types/otel_attribute.py @@ -0,0 +1,55 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ....core.datetime_utils import serialize_datetime +from ....core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +from .otel_attribute_value import OtelAttributeValue + + +class OtelAttribute(pydantic_v1.BaseModel): + """ + Key-value attribute pair for resources, scopes, or spans + """ + + key: typing.Optional[str] = pydantic_v1.Field(default=None) + """ + Attribute key (e.g., "service.name", "langfuse.observation.type") + """ + + value: typing.Optional[OtelAttributeValue] = pydantic_v1.Field(default=None) + """ + Attribute value + """ + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} diff --git a/langfuse/api/resources/opentelemetry/types/otel_attribute_value.py b/langfuse/api/resources/opentelemetry/types/otel_attribute_value.py new file mode 100644 index 000000000..51f026495 --- /dev/null +++ b/langfuse/api/resources/opentelemetry/types/otel_attribute_value.py @@ -0,0 +1,72 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ....core.datetime_utils import serialize_datetime +from ....core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 + + +class OtelAttributeValue(pydantic_v1.BaseModel): + """ + Attribute value wrapper supporting different value types + """ + + string_value: typing.Optional[str] = pydantic_v1.Field( + alias="stringValue", default=None + ) + """ + String value + """ + + int_value: typing.Optional[int] = pydantic_v1.Field(alias="intValue", default=None) + """ + Integer value + """ + + double_value: typing.Optional[float] = pydantic_v1.Field( + alias="doubleValue", default=None + ) + """ + Double value + """ + + bool_value: typing.Optional[bool] = pydantic_v1.Field( + alias="boolValue", default=None + ) + """ + Boolean value + """ + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} diff --git a/langfuse/api/resources/opentelemetry/types/otel_resource.py b/langfuse/api/resources/opentelemetry/types/otel_resource.py new file mode 100644 index 000000000..0d76d5a15 --- /dev/null +++ b/langfuse/api/resources/opentelemetry/types/otel_resource.py @@ -0,0 +1,52 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ....core.datetime_utils import serialize_datetime +from ....core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +from .otel_attribute import OtelAttribute + + +class OtelResource(pydantic_v1.BaseModel): + """ + Resource attributes identifying the source of telemetry + """ + + attributes: typing.Optional[typing.List[OtelAttribute]] = pydantic_v1.Field( + default=None + ) + """ + Resource attributes like service.name, service.version, etc. + """ + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} diff --git a/langfuse/api/resources/opentelemetry/types/otel_resource_span.py b/langfuse/api/resources/opentelemetry/types/otel_resource_span.py new file mode 100644 index 000000000..e270ba7d8 --- /dev/null +++ b/langfuse/api/resources/opentelemetry/types/otel_resource_span.py @@ -0,0 +1,60 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ....core.datetime_utils import serialize_datetime +from ....core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +from .otel_resource import OtelResource +from .otel_scope_span import OtelScopeSpan + + +class OtelResourceSpan(pydantic_v1.BaseModel): + """ + Represents a collection of spans from a single resource as per OTLP specification + """ + + resource: typing.Optional[OtelResource] = pydantic_v1.Field(default=None) + """ + Resource information + """ + + scope_spans: typing.Optional[typing.List[OtelScopeSpan]] = pydantic_v1.Field( + alias="scopeSpans", default=None + ) + """ + Array of scope spans + """ + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} diff --git a/langfuse/api/resources/opentelemetry/types/otel_scope.py b/langfuse/api/resources/opentelemetry/types/otel_scope.py new file mode 100644 index 000000000..71e9b75b8 --- /dev/null +++ b/langfuse/api/resources/opentelemetry/types/otel_scope.py @@ -0,0 +1,62 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ....core.datetime_utils import serialize_datetime +from ....core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +from .otel_attribute import OtelAttribute + + +class OtelScope(pydantic_v1.BaseModel): + """ + Instrumentation scope information + """ + + name: typing.Optional[str] = pydantic_v1.Field(default=None) + """ + Instrumentation scope name + """ + + version: typing.Optional[str] = pydantic_v1.Field(default=None) + """ + Instrumentation scope version + """ + + attributes: typing.Optional[typing.List[OtelAttribute]] = pydantic_v1.Field( + default=None + ) + """ + Additional scope attributes + """ + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} diff --git a/langfuse/api/resources/opentelemetry/types/otel_scope_span.py b/langfuse/api/resources/opentelemetry/types/otel_scope_span.py new file mode 100644 index 000000000..854951a60 --- /dev/null +++ b/langfuse/api/resources/opentelemetry/types/otel_scope_span.py @@ -0,0 +1,56 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ....core.datetime_utils import serialize_datetime +from ....core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +from .otel_scope import OtelScope +from .otel_span import OtelSpan + + +class OtelScopeSpan(pydantic_v1.BaseModel): + """ + Collection of spans from a single instrumentation scope + """ + + scope: typing.Optional[OtelScope] = pydantic_v1.Field(default=None) + """ + Instrumentation scope information + """ + + spans: typing.Optional[typing.List[OtelSpan]] = pydantic_v1.Field(default=None) + """ + Array of spans + """ + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} diff --git a/langfuse/api/resources/opentelemetry/types/otel_span.py b/langfuse/api/resources/opentelemetry/types/otel_span.py new file mode 100644 index 000000000..08b7be7fb --- /dev/null +++ b/langfuse/api/resources/opentelemetry/types/otel_span.py @@ -0,0 +1,104 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ....core.datetime_utils import serialize_datetime +from ....core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +from .otel_attribute import OtelAttribute + + +class OtelSpan(pydantic_v1.BaseModel): + """ + Individual span representing a unit of work or operation + """ + + trace_id: typing.Optional[typing.Any] = pydantic_v1.Field( + alias="traceId", default=None + ) + """ + Trace ID (16 bytes, hex-encoded string in JSON or Buffer in binary) + """ + + span_id: typing.Optional[typing.Any] = pydantic_v1.Field( + alias="spanId", default=None + ) + """ + Span ID (8 bytes, hex-encoded string in JSON or Buffer in binary) + """ + + parent_span_id: typing.Optional[typing.Any] = pydantic_v1.Field( + alias="parentSpanId", default=None + ) + """ + Parent span ID if this is a child span + """ + + name: typing.Optional[str] = pydantic_v1.Field(default=None) + """ + Span name describing the operation + """ + + kind: typing.Optional[int] = pydantic_v1.Field(default=None) + """ + Span kind (1=INTERNAL, 2=SERVER, 3=CLIENT, 4=PRODUCER, 5=CONSUMER) + """ + + start_time_unix_nano: typing.Optional[typing.Any] = pydantic_v1.Field( + alias="startTimeUnixNano", default=None + ) + """ + Start time in nanoseconds since Unix epoch + """ + + end_time_unix_nano: typing.Optional[typing.Any] = pydantic_v1.Field( + alias="endTimeUnixNano", default=None + ) + """ + End time in nanoseconds since Unix epoch + """ + + attributes: typing.Optional[typing.List[OtelAttribute]] = pydantic_v1.Field( + default=None + ) + """ + Span attributes including Langfuse-specific attributes (langfuse.observation.*) + """ + + status: typing.Optional[typing.Any] = pydantic_v1.Field(default=None) + """ + Span status object + """ + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} diff --git a/langfuse/api/resources/opentelemetry/types/otel_trace_response.py b/langfuse/api/resources/opentelemetry/types/otel_trace_response.py new file mode 100644 index 000000000..ef9897f06 --- /dev/null +++ b/langfuse/api/resources/opentelemetry/types/otel_trace_response.py @@ -0,0 +1,44 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ....core.datetime_utils import serialize_datetime +from ....core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 + + +class OtelTraceResponse(pydantic_v1.BaseModel): + """ + Response from trace export request. Empty object indicates success. + """ + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} diff --git a/langfuse/api/resources/prompts/__init__.py b/langfuse/api/resources/prompts/__init__.py index 77c27486d..ea2f2f56a 100644 --- a/langfuse/api/resources/prompts/__init__.py +++ b/langfuse/api/resources/prompts/__init__.py @@ -16,6 +16,7 @@ Prompt, PromptMeta, PromptMetaListResponse, + PromptType, Prompt_Chat, Prompt_Text, TextPrompt, @@ -37,6 +38,7 @@ "Prompt", "PromptMeta", "PromptMetaListResponse", + "PromptType", "Prompt_Chat", "Prompt_Text", "TextPrompt", diff --git a/langfuse/api/resources/prompts/types/__init__.py b/langfuse/api/resources/prompts/types/__init__.py index 3067f9f04..6678ec262 100644 --- a/langfuse/api/resources/prompts/types/__init__.py +++ b/langfuse/api/resources/prompts/types/__init__.py @@ -19,6 +19,7 @@ from .prompt import Prompt, Prompt_Chat, Prompt_Text from .prompt_meta import PromptMeta from .prompt_meta_list_response import PromptMetaListResponse +from .prompt_type import PromptType from .text_prompt import TextPrompt __all__ = [ @@ -37,6 +38,7 @@ "Prompt", "PromptMeta", "PromptMetaListResponse", + "PromptType", "Prompt_Chat", "Prompt_Text", "TextPrompt", diff --git a/langfuse/api/resources/prompts/types/prompt_meta.py b/langfuse/api/resources/prompts/types/prompt_meta.py index bbb028fb2..35f8a06cf 100644 --- a/langfuse/api/resources/prompts/types/prompt_meta.py +++ b/langfuse/api/resources/prompts/types/prompt_meta.py @@ -5,10 +5,16 @@ from ....core.datetime_utils import serialize_datetime from ....core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +from .prompt_type import PromptType class PromptMeta(pydantic_v1.BaseModel): name: str + type: PromptType = pydantic_v1.Field() + """ + Indicates whether the prompt is a text or chat prompt. + """ + versions: typing.List[int] labels: typing.List[str] tags: typing.List[str] diff --git a/langfuse/api/resources/prompts/types/prompt_type.py b/langfuse/api/resources/prompts/types/prompt_type.py new file mode 100644 index 000000000..958d544a6 --- /dev/null +++ b/langfuse/api/resources/prompts/types/prompt_type.py @@ -0,0 +1,19 @@ +# This file was auto-generated by Fern from our API Definition. + +import enum +import typing + +T_Result = typing.TypeVar("T_Result") + + +class PromptType(str, enum.Enum): + CHAT = "chat" + TEXT = "text" + + def visit( + self, chat: typing.Callable[[], T_Result], text: typing.Callable[[], T_Result] + ) -> T_Result: + if self is PromptType.CHAT: + return chat() + if self is PromptType.TEXT: + return text()