llm_entities.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. from __future__ import annotations
  2. from collections.abc import Mapping, Sequence
  3. from decimal import Decimal
  4. from enum import StrEnum
  5. from typing import Any, Optional, TypedDict, Union
  6. from pydantic import BaseModel, Field
  7. from core.model_runtime.entities.message_entities import AssistantPromptMessage, PromptMessage
  8. from core.model_runtime.entities.model_entities import ModelUsage, PriceInfo
  9. class LLMMode(StrEnum):
  10. """
  11. Enum class for large language model mode.
  12. """
  13. COMPLETION = "completion"
  14. CHAT = "chat"
  15. class LLMUsageMetadata(TypedDict, total=False):
  16. """
  17. TypedDict for LLM usage metadata.
  18. All fields are optional.
  19. """
  20. prompt_tokens: int
  21. completion_tokens: int
  22. total_tokens: int
  23. prompt_unit_price: Union[float, str]
  24. completion_unit_price: Union[float, str]
  25. total_price: Union[float, str]
  26. currency: str
  27. prompt_price_unit: Union[float, str]
  28. completion_price_unit: Union[float, str]
  29. prompt_price: Union[float, str]
  30. completion_price: Union[float, str]
  31. latency: float
  32. class LLMUsage(ModelUsage):
  33. """
  34. Model class for llm usage.
  35. """
  36. prompt_tokens: int
  37. prompt_unit_price: Decimal
  38. prompt_price_unit: Decimal
  39. prompt_price: Decimal
  40. completion_tokens: int
  41. completion_unit_price: Decimal
  42. completion_price_unit: Decimal
  43. completion_price: Decimal
  44. total_tokens: int
  45. total_price: Decimal
  46. currency: str
  47. latency: float
  48. @classmethod
  49. def empty_usage(cls):
  50. return cls(
  51. prompt_tokens=0,
  52. prompt_unit_price=Decimal("0.0"),
  53. prompt_price_unit=Decimal("0.0"),
  54. prompt_price=Decimal("0.0"),
  55. completion_tokens=0,
  56. completion_unit_price=Decimal("0.0"),
  57. completion_price_unit=Decimal("0.0"),
  58. completion_price=Decimal("0.0"),
  59. total_tokens=0,
  60. total_price=Decimal("0.0"),
  61. currency="USD",
  62. latency=0.0,
  63. )
  64. @classmethod
  65. def from_metadata(cls, metadata: LLMUsageMetadata) -> LLMUsage:
  66. """
  67. Create LLMUsage instance from metadata dictionary with default values.
  68. Args:
  69. metadata: TypedDict containing usage metadata
  70. Returns:
  71. LLMUsage instance with values from metadata or defaults
  72. """
  73. prompt_tokens = metadata.get("prompt_tokens", 0)
  74. completion_tokens = metadata.get("completion_tokens", 0)
  75. total_tokens = metadata.get("total_tokens", 0)
  76. # If total_tokens is not provided but prompt and completion tokens are,
  77. # calculate total_tokens
  78. if total_tokens == 0 and (prompt_tokens > 0 or completion_tokens > 0):
  79. total_tokens = prompt_tokens + completion_tokens
  80. return cls(
  81. prompt_tokens=prompt_tokens,
  82. completion_tokens=completion_tokens,
  83. total_tokens=total_tokens,
  84. prompt_unit_price=Decimal(str(metadata.get("prompt_unit_price", 0))),
  85. completion_unit_price=Decimal(str(metadata.get("completion_unit_price", 0))),
  86. total_price=Decimal(str(metadata.get("total_price", 0))),
  87. currency=metadata.get("currency", "USD"),
  88. prompt_price_unit=Decimal(str(metadata.get("prompt_price_unit", 0))),
  89. completion_price_unit=Decimal(str(metadata.get("completion_price_unit", 0))),
  90. prompt_price=Decimal(str(metadata.get("prompt_price", 0))),
  91. completion_price=Decimal(str(metadata.get("completion_price", 0))),
  92. latency=metadata.get("latency", 0.0),
  93. )
  94. def plus(self, other: LLMUsage) -> LLMUsage:
  95. """
  96. Add two LLMUsage instances together.
  97. :param other: Another LLMUsage instance to add
  98. :return: A new LLMUsage instance with summed values
  99. """
  100. if self.total_tokens == 0:
  101. return other
  102. else:
  103. return LLMUsage(
  104. prompt_tokens=self.prompt_tokens + other.prompt_tokens,
  105. prompt_unit_price=other.prompt_unit_price,
  106. prompt_price_unit=other.prompt_price_unit,
  107. prompt_price=self.prompt_price + other.prompt_price,
  108. completion_tokens=self.completion_tokens + other.completion_tokens,
  109. completion_unit_price=other.completion_unit_price,
  110. completion_price_unit=other.completion_price_unit,
  111. completion_price=self.completion_price + other.completion_price,
  112. total_tokens=self.total_tokens + other.total_tokens,
  113. total_price=self.total_price + other.total_price,
  114. currency=other.currency,
  115. latency=self.latency + other.latency,
  116. )
  117. def __add__(self, other: LLMUsage) -> LLMUsage:
  118. """
  119. Overload the + operator to add two LLMUsage instances.
  120. :param other: Another LLMUsage instance to add
  121. :return: A new LLMUsage instance with summed values
  122. """
  123. return self.plus(other)
  124. class LLMResult(BaseModel):
  125. """
  126. Model class for llm result.
  127. """
  128. id: Optional[str] = None
  129. model: str
  130. prompt_messages: Sequence[PromptMessage] = Field(default_factory=list)
  131. message: AssistantPromptMessage
  132. usage: LLMUsage
  133. system_fingerprint: Optional[str] = None
  134. class LLMStructuredOutput(BaseModel):
  135. """
  136. Model class for llm structured output.
  137. """
  138. structured_output: Optional[Mapping[str, Any]] = None
  139. class LLMResultWithStructuredOutput(LLMResult, LLMStructuredOutput):
  140. """
  141. Model class for llm result with structured output.
  142. """
  143. class LLMResultChunkDelta(BaseModel):
  144. """
  145. Model class for llm result chunk delta.
  146. """
  147. index: int
  148. message: AssistantPromptMessage
  149. usage: Optional[LLMUsage] = None
  150. finish_reason: Optional[str] = None
  151. class LLMResultChunk(BaseModel):
  152. """
  153. Model class for llm result chunk.
  154. """
  155. model: str
  156. prompt_messages: Sequence[PromptMessage] = Field(default_factory=list)
  157. system_fingerprint: Optional[str] = None
  158. delta: LLMResultChunkDelta
  159. class LLMResultChunkWithStructuredOutput(LLMResultChunk, LLMStructuredOutput):
  160. """
  161. Model class for llm result chunk with structured output.
  162. """
  163. class NumTokensResult(PriceInfo):
  164. """
  165. Model class for number of tokens result.
  166. """
  167. tokens: int