|
49 | 49 | from langfuse.api.resources.utils.resources.pagination.types.meta_response import ( |
50 | 50 | MetaResponse, |
51 | 51 | ) |
| 52 | +from langfuse.api.resources.media import GetMediaResponse |
52 | 53 | from langfuse.model import ( |
53 | 54 | ChatMessageDict, |
54 | 55 | ChatPromptClient, |
|
74 | 75 | from langfuse.environment import get_common_release_envs |
75 | 76 | from langfuse.logging import clean_logger |
76 | 77 | from langfuse.model import Dataset, MapValue, Observation, TraceWithFullDetails |
| 78 | +from langfuse.media import LangfuseMedia |
77 | 79 | from langfuse.request import LangfuseClient |
78 | 80 | from langfuse.types import MaskFunction, ScoreDataType, SpanLevel |
79 | 81 | from langfuse.utils import _convert_usage_input, _create_prompt_context, _get_timestamp |
@@ -111,6 +113,13 @@ class FetchObservationResponse: |
111 | 113 | data: Observation |
112 | 114 |
|
113 | 115 |
|
| 116 | +@dataclass |
| 117 | +class FetchMediaResponse: |
| 118 | + """Response object for fetch_media method.""" |
| 119 | + |
| 120 | + data: GetMediaResponse |
| 121 | + |
| 122 | + |
114 | 123 | @dataclass |
115 | 124 | class FetchSessionsResponse: |
116 | 125 | """Response object for fetch_sessions method.""" |
@@ -885,19 +894,78 @@ def fetch_observation( |
885 | 894 | handle_fern_exception(e) |
886 | 895 | raise e |
887 | 896 |
|
888 | | - def fetch_media(self, id: str): |
| 897 | + def fetch_media(self, id: str) -> FetchMediaResponse: |
889 | 898 | """Get media content by ID. |
890 | 899 |
|
891 | 900 | Args: |
892 | 901 | id: The identifier of the media content to fetch. |
893 | 902 |
|
894 | 903 | Returns: |
895 | | - Media object |
| 904 | + FetchMediaResponse: The media data of the given id on `data`. |
896 | 905 |
|
897 | 906 | Raises: |
898 | | - Exception: If the media content could not be found or if an error occurred during the request. |
| 907 | + Exception: If the media content with the given id could not be found within the authenticated project or if an error occurred during the request. |
| 908 | + """ |
| 909 | + try: |
| 910 | + return FetchMediaResponse(data=self.client.media.get(id)) |
| 911 | + except Exception as e: |
| 912 | + handle_fern_exception(e) |
| 913 | + raise e |
| 914 | + |
| 915 | + def resolve_media_references( |
| 916 | + self, |
| 917 | + *, |
| 918 | + obj: Any, |
| 919 | + resolve_with: Literal["base64_data_uri"], |
| 920 | + max_depth: int = 10, |
| 921 | + content_fetch_timeout_seconds: int = 10, |
| 922 | + ): |
| 923 | + """Replace media reference strings in an object with base64 data URIs. |
| 924 | +
|
| 925 | + This method recursively traverses an object (up to max_depth) looking for media reference strings |
| 926 | + in the format "@@@langfuseMedia:...@@@". When found, it (synchronously) fetches the actual media content using |
| 927 | + the provided Langfuse client and replaces the reference string with a base64 data URI. |
| 928 | +
|
| 929 | + If fetching media content fails for a reference string, a warning is logged and the reference |
| 930 | + string is left unchanged. |
| 931 | +
|
| 932 | + Args: |
| 933 | + obj: The object to process. Can be a primitive value, array, or nested object. |
| 934 | + If the object has a __dict__ attribute, a dict will be returned instead of the original object type. |
| 935 | + resolve_with: The representation of the media content to replace the media reference string with. |
| 936 | + Currently only "base64_data_uri" is supported. |
| 937 | + max_depth: int: The maximum depth to traverse the object. Default is 10. |
| 938 | + content_fetch_timeout_seconds: int: The timeout in seconds for fetching media content. Default is 10. |
| 939 | +
|
| 940 | + Returns: |
| 941 | + A deep copy of the input object with all media references replaced with base64 data URIs where possible. |
| 942 | + If the input object has a __dict__ attribute, a dict will be returned instead of the original object type. |
| 943 | +
|
| 944 | + Example: |
| 945 | + obj = { |
| 946 | + "image": "@@@langfuseMedia:type=image/jpeg|id=123|source=bytes@@@", |
| 947 | + "nested": { |
| 948 | + "pdf": "@@@langfuseMedia:type=application/pdf|id=456|source=bytes@@@" |
| 949 | + } |
| 950 | + } |
| 951 | +
|
| 952 | + result = await LangfuseMedia.resolve_media_references(obj, langfuse_client) |
| 953 | +
|
| 954 | + # Result: |
| 955 | + # { |
| 956 | + # "image": "data:image/jpeg;base64,/9j/4AAQSkZJRg...", |
| 957 | + # "nested": { |
| 958 | + # "pdf": "data:application/pdf;base64,JVBERi0xLjcK..." |
| 959 | + # } |
| 960 | + # } |
899 | 961 | """ |
900 | | - return self.client.media.get(id) |
| 962 | + return LangfuseMedia.resolve_media_references( |
| 963 | + langfuse_client=self, |
| 964 | + obj=obj, |
| 965 | + resolve_with=resolve_with, |
| 966 | + max_depth=max_depth, |
| 967 | + content_fetch_timeout_seconds=content_fetch_timeout_seconds, |
| 968 | + ) |
901 | 969 |
|
902 | 970 | def get_observation( |
903 | 971 | self, |
|
0 commit comments