diff --git a/litellm/integrations/langfuse.py b/litellm/integrations/langfuse.py index 2a0cd745303..7aab330b3ef 100644 --- a/litellm/integrations/langfuse.py +++ b/litellm/integrations/langfuse.py @@ -7,6 +7,7 @@ from datetime import datetime dotenv.load_dotenv() # Loading env variables using dotenv import traceback +from packaging.version import Version class LangFuseLogger: @@ -47,10 +48,7 @@ class LangFuseLogger: optional_params = kwargs.get("optional_params", {}) optional_params.pop("functions", None) - print_verbose( - f"Langfuse Logging - typw: {type(optional_params), optional_params}" - ) - print_verbose(f"Langfuse Logging - optional params: {optional_params}") + optional_params.pop("tools", None) # langfuse only accepts str, int, bool, float for logging for param, value in optional_params.items(): @@ -65,26 +63,26 @@ class LangFuseLogger: input = prompt output = response_obj["choices"][0]["message"].json() - trace = self.Langfuse.trace( - name=metadata.get("generation_name", "litellm-completion"), - input=input, - output=output, + self._log_langfuse_v2( + metadata, + output, + start_time, + end_time, + kwargs, + optional_params, + input, + response_obj, + ) if self._is_langfuse_v2() else self._log_langfuse_v1( + metadata, + output, + start_time, + end_time, + kwargs, + optional_params, + input, + response_obj, ) - trace.generation( - name=metadata.get("generation_name", "litellm-completion"), - startTime=start_time, - endTime=end_time, - model=kwargs["model"], - modelParameters=optional_params, - input=input, - output=output, - usage={ - "prompt_tokens": response_obj["usage"]["prompt_tokens"], - "completion_tokens": response_obj["usage"]["completion_tokens"], - }, - metadata=metadata, - ) self.Langfuse.flush() print_verbose( f"Langfuse Layer Logging - final response object: {response_obj}" @@ -98,3 +96,78 @@ class LangFuseLogger: self, kwargs, response_obj, start_time, end_time, print_verbose ): self.log_event(kwargs, response_obj, start_time, end_time, print_verbose) + + def _is_langfuse_v2(self): + import langfuse + + return Version(langfuse.version.__version__) >= Version("2.0.0") + + def _log_langfuse_v1( + self, + metadata, + output, + start_time, + end_time, + kwargs, + optional_params, + input, + response_obj, + ): + from langfuse.model import CreateTrace, CreateGeneration + + trace = self.Langfuse.trace( + CreateTrace( + name=metadata.get("generation_name", "litellm-completion"), + input=input, + output=output, + ) + ) + + trace.generation( + CreateGeneration( + name=metadata.get("generation_name", "litellm-completion"), + startTime=start_time, + endTime=end_time, + model=kwargs["model"], + modelParameters=optional_params, + input=input, + output=output, + usage={ + "prompt_tokens": response_obj["usage"]["prompt_tokens"], + "completion_tokens": response_obj["usage"]["completion_tokens"], + }, + metadata=metadata, + ) + ) + + def _log_langfuse_v2( + self, + metadata, + output, + start_time, + end_time, + kwargs, + optional_params, + input, + response_obj, + ): + trace = self.Langfuse.trace( + name=metadata.get("generation_name", "litellm-completion"), + input=input, + output=output, + ) + + trace.generation( + name=metadata.get("generation_name", "litellm-completion"), + startTime=start_time, + endTime=end_time, + model=kwargs["model"], + modelParameters=optional_params, + input=input, + output=output, + usage={ + "prompt_tokens": response_obj["usage"]["prompt_tokens"], + "completion_tokens": response_obj["usage"]["completion_tokens"], + }, + metadata=metadata, + ) diff --git a/litellm/tests/test_langfuse.py b/litellm/tests/test_langfuse.py index bed1fe1fc84..5b9e824100a 100644 --- a/litellm/tests/test_langfuse.py +++ b/litellm/tests/test_langfuse.py @@ -1,3 +1,4 @@ +import json import sys import os import io, asyncio @@ -91,7 +92,7 @@ def pre_langfuse_setup(): return -# @pytest.mark.skip(reason="beta test - checking langfuse output") +@pytest.mark.skip(reason="beta test - checking langfuse output") def test_langfuse_logging_async(): try: pre_langfuse_setup() @@ -118,9 +119,10 @@ def test_langfuse_logging_async(): pytest.fail(f"An exception occurred - {e}") -# test_langfuse_logging_async() +test_langfuse_logging_async() +@pytest.mark.skip(reason="beta test - checking langfuse output") def test_langfuse_logging(): try: pre_langfuse_setup() @@ -142,9 +144,10 @@ def test_langfuse_logging(): pytest.fail(f"An exception occurred - {e}") -# test_langfuse_logging() +test_langfuse_logging() +@pytest.mark.skip(reason="beta test - checking langfuse output") def test_langfuse_logging_stream(): try: litellm.set_verbose = True @@ -170,7 +173,7 @@ def test_langfuse_logging_stream(): print(e) -# test_langfuse_logging_stream() +test_langfuse_logging_stream() @pytest.mark.skip(reason="beta test - checking langfuse output") @@ -195,6 +198,10 @@ def test_langfuse_logging_custom_generation_name(): print(e) +test_langfuse_logging_custom_generation_name() + + +@pytest.mark.skip(reason="beta test - checking langfuse output") def test_langfuse_logging_function_calling(): litellm.set_verbose = True function1 = [ @@ -229,3 +236,64 @@ def test_langfuse_logging_function_calling(): test_langfuse_logging_function_calling() + + +def test_langfuse_logging_tool_calling(): + litellm.set_verbose = True + + def get_current_weather(location, unit="fahrenheit"): + """Get the current weather in a given location""" + if "tokyo" in location.lower(): + return json.dumps( + {"location": "Tokyo", "temperature": "10", "unit": "celsius"} + ) + elif "san francisco" in location.lower(): + return json.dumps( + {"location": "San Francisco", "temperature": "72", "unit": "fahrenheit"} + ) + elif "paris" in location.lower(): + return json.dumps( + {"location": "Paris", "temperature": "22", "unit": "celsius"} + ) + else: + return json.dumps({"location": location, "temperature": "unknown"}) + + messages = [ + { + "role": "user", + "content": "What's the weather like in San Francisco, Tokyo, and Paris?", + } + ] + tools = [ + { + "type": "function", + "function": { + "name": "get_current_weather", + "description": "Get the current weather in a given location", + "parameters": { + "type": "object", + "properties": { + "location": { + "type": "string", + "description": "The city and state, e.g. San Francisco, CA", + }, + "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}, + }, + "required": ["location"], + }, + }, + } + ] + + response = litellm.completion( + model="gpt-3.5-turbo-1106", + messages=messages, + tools=tools, + tool_choice="auto", # auto is default, but we'll be explicit + ) + print("\nLLM Response1:\n", response) + response_message = response.choices[0].message + tool_calls = response.choices[0].message.tool_calls + + +test_langfuse_logging_tool_calling()