|
1 | | -from collections import defaultdict |
2 | | -import httpx |
3 | 1 | import logging |
4 | 2 | import typing |
5 | 3 | import warnings |
| 4 | +from collections import defaultdict |
6 | 5 |
|
| 6 | +import httpx |
7 | 7 | import pydantic |
8 | 8 |
|
9 | 9 | try: # Test that langchain is installed before proceeding |
|
15 | 15 | ) |
16 | 16 | from typing import Any, Dict, List, Optional, Sequence, Union, cast |
17 | 17 | from uuid import UUID, uuid4 |
| 18 | + |
18 | 19 | from langfuse.api.resources.ingestion.types.sdk_log_body import SdkLogBody |
19 | 20 | from langfuse.client import ( |
| 21 | + StatefulGenerationClient, |
20 | 22 | StatefulSpanClient, |
21 | 23 | StatefulTraceClient, |
22 | | - StatefulGenerationClient, |
23 | 24 | ) |
24 | 25 | from langfuse.extract_model import _extract_model_name |
| 26 | +from langfuse.types import MaskFunction |
25 | 27 | from langfuse.utils import _get_timestamp |
26 | 28 | from langfuse.utils.base_callback_handler import LangfuseBaseCallbackHandler |
27 | | -from langfuse.types import MaskFunction |
28 | 29 |
|
29 | 30 | try: |
30 | 31 | from langchain.callbacks.base import ( |
31 | 32 | BaseCallbackHandler as LangchainBaseCallbackHandler, |
32 | 33 | ) |
33 | 34 | from langchain.schema.agent import AgentAction, AgentFinish |
34 | 35 | from langchain.schema.document import Document |
35 | | - from langchain_core.outputs import ( |
36 | | - ChatGeneration, |
37 | | - LLMResult, |
38 | | - ) |
39 | 36 | from langchain_core.messages import ( |
40 | 37 | AIMessage, |
41 | 38 | BaseMessage, |
42 | 39 | ChatMessage, |
| 40 | + FunctionMessage, |
43 | 41 | HumanMessage, |
44 | 42 | SystemMessage, |
45 | 43 | ToolMessage, |
46 | | - FunctionMessage, |
| 44 | + ) |
| 45 | + from langchain_core.outputs import ( |
| 46 | + ChatGeneration, |
| 47 | + LLMResult, |
47 | 48 | ) |
48 | 49 | except ImportError: |
49 | 50 | raise ModuleNotFoundError( |
@@ -149,7 +150,9 @@ def on_llm_new_token( |
149 | 150 |
|
150 | 151 | self.updated_completion_start_time_memo.add(run_id) |
151 | 152 |
|
152 | | - def get_langchain_run_name(self, serialized: Optional[Dict[str, Any]], **kwargs: Any) -> str: |
| 153 | + def get_langchain_run_name( |
| 154 | + self, serialized: Optional[Dict[str, Any]], **kwargs: Any |
| 155 | + ) -> str: |
153 | 156 | """Retrieve the name of a serialized LangChain runnable. |
154 | 157 |
|
155 | 158 | The prioritization for the determination of the run name is as follows: |
@@ -1055,16 +1058,24 @@ def _parse_usage_model(usage: typing.Union[pydantic.BaseModel, dict]): |
1055 | 1058 | ] |
1056 | 1059 |
|
1057 | 1060 | usage_model = usage.copy() # Copy all existing key-value pairs |
1058 | | - for model_key, langfuse_key in conversion_list: |
1059 | | - if model_key in usage_model: |
1060 | | - captured_count = usage_model.pop(model_key) |
1061 | | - final_count = ( |
1062 | | - sum(captured_count) |
1063 | | - if isinstance(captured_count, list) |
1064 | | - else captured_count |
1065 | | - ) # For Bedrock, the token count is a list when streamed |
1066 | | - |
1067 | | - usage_model[langfuse_key] = final_count # Translate key and keep the value |
| 1061 | + |
| 1062 | + # Skip OpenAI usage types as they are handled server side |
| 1063 | + if not all( |
| 1064 | + openai_key in usage_model |
| 1065 | + for openai_key in ["prompt_tokens", "completion_tokens", "total_tokens"] |
| 1066 | + ): |
| 1067 | + for model_key, langfuse_key in conversion_list: |
| 1068 | + if model_key in usage_model: |
| 1069 | + captured_count = usage_model.pop(model_key) |
| 1070 | + final_count = ( |
| 1071 | + sum(captured_count) |
| 1072 | + if isinstance(captured_count, list) |
| 1073 | + else captured_count |
| 1074 | + ) # For Bedrock, the token count is a list when streamed |
| 1075 | + |
| 1076 | + usage_model[langfuse_key] = ( |
| 1077 | + final_count # Translate key and keep the value |
| 1078 | + ) |
1068 | 1079 |
|
1069 | 1080 | if isinstance(usage_model, dict): |
1070 | 1081 | if "input_token_details" in usage_model: |
|
0 commit comments