|
@@ -1,4 +1,6 @@
|
|
|
import copy
|
|
import copy
|
|
|
|
|
+import json
|
|
|
|
|
+import os
|
|
|
import time
|
|
import time
|
|
|
import warnings
|
|
import warnings
|
|
|
from functools import partial
|
|
from functools import partial
|
|
@@ -77,11 +79,6 @@ class LLM(RetryMixin, DebugMixin):
|
|
|
self.cost_metric_supported: bool = True
|
|
self.cost_metric_supported: bool = True
|
|
|
self.config: LLMConfig = copy.deepcopy(config)
|
|
self.config: LLMConfig = copy.deepcopy(config)
|
|
|
|
|
|
|
|
- # list of LLM completions (for logging purposes). Each completion is a dict with the following keys:
|
|
|
|
|
- # - 'messages': list of messages
|
|
|
|
|
- # - 'response': response from the LLM
|
|
|
|
|
- self.llm_completions: list[dict[str, Any]] = []
|
|
|
|
|
-
|
|
|
|
|
# litellm actually uses base Exception here for unknown model
|
|
# litellm actually uses base Exception here for unknown model
|
|
|
self.model_info: ModelInfo | None = None
|
|
self.model_info: ModelInfo | None = None
|
|
|
try:
|
|
try:
|
|
@@ -95,6 +92,13 @@ class LLM(RetryMixin, DebugMixin):
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
logger.warning(f'Could not get model info for {config.model}:\n{e}')
|
|
logger.warning(f'Could not get model info for {config.model}:\n{e}')
|
|
|
|
|
|
|
|
|
|
+ if self.config.log_completions:
|
|
|
|
|
+ if self.config.log_completions_folder is None:
|
|
|
|
|
+ raise RuntimeError(
|
|
|
|
|
+ 'log_completions_folder is required when log_completions is enabled'
|
|
|
|
|
+ )
|
|
|
|
|
+ os.makedirs(self.config.log_completions_folder, exist_ok=True)
|
|
|
|
|
+
|
|
|
# Set the max tokens in an LM-specific way if not set
|
|
# Set the max tokens in an LM-specific way if not set
|
|
|
if self.config.max_input_tokens is None:
|
|
if self.config.max_input_tokens is None:
|
|
|
if (
|
|
if (
|
|
@@ -194,14 +198,24 @@ class LLM(RetryMixin, DebugMixin):
|
|
|
|
|
|
|
|
# log for evals or other scripts that need the raw completion
|
|
# log for evals or other scripts that need the raw completion
|
|
|
if self.config.log_completions:
|
|
if self.config.log_completions:
|
|
|
- self.llm_completions.append(
|
|
|
|
|
- {
|
|
|
|
|
- 'messages': messages,
|
|
|
|
|
- 'response': resp,
|
|
|
|
|
- 'timestamp': time.time(),
|
|
|
|
|
- 'cost': self._completion_cost(resp),
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ assert self.config.log_completions_folder is not None
|
|
|
|
|
+ log_file = os.path.join(
|
|
|
|
|
+ self.config.log_completions_folder,
|
|
|
|
|
+ # use the metric model name (for draft editor)
|
|
|
|
|
+ f'{self.metrics.model_name}-{time.time()}.json',
|
|
|
)
|
|
)
|
|
|
|
|
+ with open(log_file, 'w') as f:
|
|
|
|
|
+ json.dump(
|
|
|
|
|
+ {
|
|
|
|
|
+ 'messages': messages,
|
|
|
|
|
+ 'response': resp,
|
|
|
|
|
+ 'args': args,
|
|
|
|
|
+ 'kwargs': kwargs,
|
|
|
|
|
+ 'timestamp': time.time(),
|
|
|
|
|
+ 'cost': self._completion_cost(resp),
|
|
|
|
|
+ },
|
|
|
|
|
+ f,
|
|
|
|
|
+ )
|
|
|
|
|
|
|
|
message_back: str = resp['choices'][0]['message']['content']
|
|
message_back: str = resp['choices'][0]['message']['content']
|
|
|
|
|
|
|
@@ -400,7 +414,6 @@ class LLM(RetryMixin, DebugMixin):
|
|
|
|
|
|
|
|
def reset(self):
|
|
def reset(self):
|
|
|
self.metrics.reset()
|
|
self.metrics.reset()
|
|
|
- self.llm_completions = []
|
|
|
|
|
|
|
|
|
|
def format_messages_for_llm(self, messages: Message | list[Message]) -> list[dict]:
|
|
def format_messages_for_llm(self, messages: Message | list[Message]) -> list[dict]:
|
|
|
if isinstance(messages, Message):
|
|
if isinstance(messages, Message):
|