|
|
@@ -10,6 +10,11 @@ from datetime import datetime
|
|
|
from typing import Dict, Any, List, Optional, Literal
|
|
|
import uuid
|
|
|
|
|
|
+# 导入 TokenUsage(延迟导入避免循环依赖)
|
|
|
+def _get_token_usage_class():
|
|
|
+ from ..llm.usage import TokenUsage
|
|
|
+ return TokenUsage
|
|
|
+
|
|
|
|
|
|
@dataclass
|
|
|
class Trace:
|
|
|
@@ -44,6 +49,9 @@ class Trace:
|
|
|
total_tokens: int = 0 # 总 tokens(向后兼容,= prompt + completion)
|
|
|
total_prompt_tokens: int = 0 # 总输入 tokens
|
|
|
total_completion_tokens: int = 0 # 总输出 tokens
|
|
|
+ total_reasoning_tokens: int = 0 # 总推理 tokens(o1/o3, DeepSeek R1, Gemini thinking)
|
|
|
+ total_cache_creation_tokens: int = 0 # 总缓存创建 tokens(Claude)
|
|
|
+ total_cache_read_tokens: int = 0 # 总缓存读取 tokens(Claude)
|
|
|
total_cost: float = 0.0
|
|
|
total_duration_ms: int = 0 # 总耗时(毫秒)
|
|
|
|
|
|
@@ -97,6 +105,9 @@ class Trace:
|
|
|
"total_tokens": self.total_tokens,
|
|
|
"total_prompt_tokens": self.total_prompt_tokens,
|
|
|
"total_completion_tokens": self.total_completion_tokens,
|
|
|
+ "total_reasoning_tokens": self.total_reasoning_tokens,
|
|
|
+ "total_cache_creation_tokens": self.total_cache_creation_tokens,
|
|
|
+ "total_cache_read_tokens": self.total_cache_read_tokens,
|
|
|
"total_cost": self.total_cost,
|
|
|
"total_duration_ms": self.total_duration_ms,
|
|
|
"last_sequence": self.last_sequence,
|
|
|
@@ -139,6 +150,9 @@ class Message:
|
|
|
# 元数据
|
|
|
prompt_tokens: Optional[int] = None # 输入 tokens
|
|
|
completion_tokens: Optional[int] = None # 输出 tokens
|
|
|
+ reasoning_tokens: Optional[int] = None # 推理 tokens(o1/o3, DeepSeek R1, Gemini thinking)
|
|
|
+ cache_creation_tokens: Optional[int] = None # 缓存创建 tokens(Claude)
|
|
|
+ cache_read_tokens: Optional[int] = None # 缓存读取 tokens(Claude)
|
|
|
cost: Optional[float] = None
|
|
|
duration_ms: Optional[int] = None
|
|
|
created_at: datetime = field(default_factory=datetime.now)
|
|
|
@@ -148,9 +162,25 @@ class Message:
|
|
|
|
|
|
@property
|
|
|
def tokens(self) -> int:
|
|
|
- """动态计算总 tokens(向后兼容)"""
|
|
|
+ """动态计算总 tokens(向后兼容,input + output)"""
|
|
|
return (self.prompt_tokens or 0) + (self.completion_tokens or 0)
|
|
|
|
|
|
+ @property
|
|
|
+ def all_tokens(self) -> int:
|
|
|
+ """所有 tokens(包括 reasoning)"""
|
|
|
+ return self.tokens + (self.reasoning_tokens or 0)
|
|
|
+
|
|
|
+ def get_usage(self):
|
|
|
+ """获取 TokenUsage 对象"""
|
|
|
+ TokenUsage = _get_token_usage_class()
|
|
|
+ return TokenUsage(
|
|
|
+ input_tokens=self.prompt_tokens or 0,
|
|
|
+ output_tokens=self.completion_tokens or 0,
|
|
|
+ reasoning_tokens=self.reasoning_tokens or 0,
|
|
|
+ cache_creation_tokens=self.cache_creation_tokens or 0,
|
|
|
+ cache_read_tokens=self.cache_read_tokens or 0,
|
|
|
+ )
|
|
|
+
|
|
|
@classmethod
|
|
|
def from_dict(cls, data: Dict[str, Any]) -> "Message":
|
|
|
"""从字典创建 Message(处理向后兼容)"""
|
|
|
@@ -174,6 +204,9 @@ class Message:
|
|
|
tool_call_id: Optional[str] = None,
|
|
|
prompt_tokens: Optional[int] = None,
|
|
|
completion_tokens: Optional[int] = None,
|
|
|
+ reasoning_tokens: Optional[int] = None,
|
|
|
+ cache_creation_tokens: Optional[int] = None,
|
|
|
+ cache_read_tokens: Optional[int] = None,
|
|
|
cost: Optional[float] = None,
|
|
|
duration_ms: Optional[int] = None,
|
|
|
finish_reason: Optional[str] = None,
|
|
|
@@ -192,6 +225,9 @@ class Message:
|
|
|
tool_call_id=tool_call_id,
|
|
|
prompt_tokens=prompt_tokens,
|
|
|
completion_tokens=completion_tokens,
|
|
|
+ reasoning_tokens=reasoning_tokens,
|
|
|
+ cache_creation_tokens=cache_creation_tokens,
|
|
|
+ cache_read_tokens=cache_read_tokens,
|
|
|
cost=cost,
|
|
|
duration_ms=duration_ms,
|
|
|
finish_reason=finish_reason,
|
|
|
@@ -261,7 +297,7 @@ class Message:
|
|
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
|
"""转换为字典"""
|
|
|
- return {
|
|
|
+ result = {
|
|
|
"message_id": self.message_id,
|
|
|
"trace_id": self.trace_id,
|
|
|
"role": self.role,
|
|
|
@@ -278,6 +314,14 @@ class Message:
|
|
|
"finish_reason": self.finish_reason,
|
|
|
"created_at": self.created_at.isoformat() if self.created_at else None,
|
|
|
}
|
|
|
+ # 只添加非空的可选字段
|
|
|
+ if self.reasoning_tokens:
|
|
|
+ result["reasoning_tokens"] = self.reasoning_tokens
|
|
|
+ if self.cache_creation_tokens:
|
|
|
+ result["cache_creation_tokens"] = self.cache_creation_tokens
|
|
|
+ if self.cache_read_tokens:
|
|
|
+ result["cache_read_tokens"] = self.cache_read_tokens
|
|
|
+ return result
|
|
|
|
|
|
|
|
|
# ===== 已弃用:Step 模型(保留用于向后兼容)=====
|