1010from datetime import datetime
1111from hashlib import sha256
1212from time import time_ns
13- from typing import Any , Dict , List , Literal , Optional , Union , cast , overload
13+ from typing import (
14+ Any ,
15+ Dict ,
16+ List ,
17+ Literal ,
18+ Optional ,
19+ Union ,
20+ Type ,
21+ cast ,
22+ overload ,
23+ get_args ,
24+ )
1425
1526import backoff
1627import httpx
3950from langfuse ._client .constants import (
4051 ObservationTypeLiteral ,
4152 ObservationTypeLiteralNoEvent ,
53+ ObservationTypeGenerationLike ,
4254)
4355from langfuse ._client .resource_manager import LangfuseResourceManager
4456from langfuse ._client .span import (
@@ -656,10 +668,14 @@ def _create_observation_from_otel_span(
656668 LangfuseGuardrail ,
657669 ]:
658670 """Create the appropriate observation type from an OTEL span."""
659- if as_type == "generation" :
660- return LangfuseGeneration (
671+ if as_type in get_args (ObservationTypeGenerationLike ):
672+ observation_class = self ._get_span_class (as_type )
673+ # Type ignore to prevent overloads of internal _get_span_class function,
674+ # issue is that LangfuseEvent could be returned
675+ return observation_class ( # type: ignore[return-value]
661676 otel_span = otel_span ,
662677 langfuse_client = self ,
678+ environment = self ._environment ,
663679 input = input ,
664680 output = output ,
665681 metadata = metadata ,
@@ -673,21 +689,12 @@ def _create_observation_from_otel_span(
673689 cost_details = cost_details ,
674690 prompt = prompt ,
675691 )
676- elif as_type == "span" :
677- return LangfuseSpan (
678- otel_span = otel_span ,
679- langfuse_client = self ,
680- environment = self ._environment ,
681- input = input ,
682- output = output ,
683- metadata = metadata ,
684- version = version ,
685- level = level ,
686- status_message = status_message ,
687- )
688692 else :
689- # For all other observation types (agent, tool, etc.), create a span and set the type
690- span = LangfuseSpan (
693+ # For other types (e.g. span, guardrail), create appropriate class without generation properties
694+ observation_class = self ._get_span_class (as_type )
695+ # Type ignore to prevent overloads of internal _get_span_class function,
696+ # issue is that LangfuseEvent could be returned
697+ return observation_class ( # type: ignore[return-value]
691698 otel_span = otel_span ,
692699 langfuse_client = self ,
693700 environment = self ._environment ,
@@ -698,10 +705,9 @@ def _create_observation_from_otel_span(
698705 level = level ,
699706 status_message = status_message ,
700707 )
701- # Set the observation type on the span
702- span ._observation_type = as_type
703- span ._otel_span .set_attribute ("langfuse.observation.type" , as_type )
704- return span
708+ # span._observation_type = as_type
709+ # span._otel_span.set_attribute("langfuse.observation.type", as_type)
710+ # return span
705711
706712 def start_generation (
707713 self ,
@@ -1075,8 +1081,8 @@ def start_as_current_observation(
10751081 status_message: Optional status message for the observation
10761082 end_on_exit (default: True): Whether to end the span automatically when leaving the context manager. If False, the span must be manually ended to avoid memory leaks.
10771083
1078- # TODO: also add the generation like types here!
1079- The following parameters are only available when as_type="generation":
1084+ The following parameters are available when as_type is: "generation", "agent",
1085+ "tool", "chain", "retriever", "evaluator" or "embedding".
10801086 completion_start_time: When the model started generating the response
10811087 model: Name/identifier of the AI model used (e.g., "gpt-4")
10821088 model_parameters: Parameters used for the model (e.g., temperature, max_tokens)
@@ -1285,7 +1291,18 @@ def start_as_current_observation(
12851291 def _get_span_class (
12861292 self ,
12871293 as_type : ObservationTypeLiteral ,
1288- ) -> type :
1294+ ) -> Union [
1295+ Type [LangfuseAgent ],
1296+ Type [LangfuseTool ],
1297+ Type [LangfuseChain ],
1298+ Type [LangfuseRetriever ],
1299+ Type [LangfuseEvaluator ],
1300+ Type [LangfuseEmbedding ],
1301+ Type [LangfuseGuardrail ],
1302+ Type [LangfuseGeneration ],
1303+ Type [LangfuseEvent ],
1304+ Type [LangfuseSpan ],
1305+ ]:
12891306 """Get the appropriate span class based on as_type."""
12901307 normalized_type = as_type .lower ()
12911308
@@ -1400,7 +1417,15 @@ def _start_as_current_otel_span_with_processed_media(
14001417 "status_message" : status_message ,
14011418 }
14021419
1403- if span_class == LangfuseGeneration :
1420+ if span_class in [
1421+ LangfuseGeneration ,
1422+ LangfuseAgent ,
1423+ LangfuseTool ,
1424+ LangfuseChain ,
1425+ LangfuseRetriever ,
1426+ LangfuseEvaluator ,
1427+ LangfuseEmbedding ,
1428+ ]:
14041429 common_args .update (
14051430 {
14061431 "completion_start_time" : completion_start_time ,
@@ -1411,19 +1436,7 @@ def _start_as_current_otel_span_with_processed_media(
14111436 "prompt" : prompt ,
14121437 }
14131438 )
1414- # TODO: for some, create generation-like classes with those props.
1415- elif span_class in [
1416- LangfuseSpan ,
1417- LangfuseAgent ,
1418- LangfuseTool ,
1419- LangfuseChain ,
1420- LangfuseRetriever ,
1421- LangfuseEvaluator ,
1422- LangfuseEmbedding ,
1423- LangfuseGuardrail ,
1424- ]:
1425- # set their type internally in the class
1426- pass
1439+ # For span and guardrail types, no generation properties needed
14271440
14281441 yield span_class (** common_args )
14291442
0 commit comments