Skip to content

Commit 621c51b

Browse files
gustavocidornelaswhoseoyster
authored andcommitted
Add OpenAIChatCompletion step to tracing
1 parent 4142b33 commit 621c51b

File tree

3 files changed

+90
-25
lines changed

3 files changed

+90
-25
lines changed

openlayer/llm_monitors.py

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
from . import constants, utils
1111
from .services import data_streamer
12+
from .tracing import tracer
1213

1314
logger = logging.getLogger(__name__)
1415

@@ -182,37 +183,36 @@ def modified_create_chat_completion(*args, **kwargs) -> str:
182183
if not stream:
183184
start_time = time.time()
184185
response = self.create_chat_completion(*args, **kwargs)
185-
latency = (time.time() - start_time) * 1000
186+
end_time = time.time()
187+
latency = (end_time - start_time) * 1000
186188

187189
try:
188190
# Extract data
189-
prompt, input_data = self.format_input(kwargs["messages"])
190191
output_data = response.choices[0].message.content.strip()
191192
num_of_tokens = response.usage.total_tokens
192193
cost = self.get_cost_estimate(
193194
model=kwargs.get("model"),
194195
num_input_tokens=response.usage.prompt_tokens,
195196
num_output_tokens=response.usage.completion_tokens,
196197
)
197-
198-
# Prepare config
199-
config = self.data_config.copy()
200-
config["prompt"] = prompt
201-
if not self.monitor_output_only:
202-
config.update({"inputVariableNames": list(input_data.keys())})
203-
204-
self._append_row_to_df(
205-
input_data=input_data,
206-
output_data=output_data,
207-
num_of_tokens=num_of_tokens,
208-
latency=latency,
209-
cost=cost,
210-
)
211-
212-
self.data_streamer.stream_data(
213-
data=self.df.tail(1).to_dict(orient="records"),
214-
config=config,
215-
)
198+
with tracer.create_step(
199+
step_type="openai_chat_completion", name="chat_completion"
200+
) as step:
201+
step.update_data(
202+
end_time=end_time,
203+
inputs={
204+
"prompt": kwargs["messages"],
205+
},
206+
output=output_data,
207+
latency=latency,
208+
tokens=num_of_tokens,
209+
cost=cost,
210+
prompt_tokens=response.usage.prompt_tokens,
211+
completion_tokens=response.usage.completion_tokens,
212+
model=kwargs.get("model"),
213+
model_parameters=kwargs.get("model_parameters"),
214+
raw_output=response.model_dump(),
215+
)
216216
# pylint: disable=broad-except
217217
except Exception as e:
218218
logger.error("Failed to monitor chat request. %s", e)

openlayer/tracing/steps.py

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ def __init__(
1717
self.output = output
1818
self.metadata = metadata
1919

20-
self.step_type = "user_call"
20+
self.step_type = None
2121
self.start_time = time.time()
2222
self.end_time = None
2323
self.ground_truth = None
@@ -49,3 +49,61 @@ def to_dict(self) -> Dict[str, Any]:
4949
"startTime": self.start_time,
5050
"endTime": self.end_time,
5151
}
52+
53+
54+
class UserCallStep(Step):
55+
def __init__(
56+
self,
57+
name: str,
58+
inputs: Optional[Any] = None,
59+
output: Optional[Any] = None,
60+
metadata: Dict[str, any] = {},
61+
) -> None:
62+
super().__init__(name=name, inputs=inputs, output=output, metadata=metadata)
63+
self.step_type = "user_call"
64+
65+
66+
class OpenAIChatCompletionStep(Step):
67+
def __init__(
68+
self,
69+
name: str,
70+
inputs: Optional[Any] = None,
71+
output: Optional[Any] = None,
72+
metadata: Dict[str, any] = {},
73+
) -> None:
74+
super().__init__(name=name, inputs=inputs, output=output, metadata=metadata)
75+
76+
self.step_type = "openai_chat_completion"
77+
self.prompt_tokens: int = None
78+
self.completion_tokens: int = None
79+
self.cost: float = None
80+
self.model: str = None
81+
self.model_parameters: Dict[str, Any] = None
82+
self.raw_output: str = None
83+
84+
def to_dict(self) -> Dict[str, Any]:
85+
"""Dictionary representation of the OpenAIChatCompletionStep."""
86+
step_dict = super().to_dict()
87+
step_dict.update(
88+
{
89+
"promptTokens": self.prompt_tokens,
90+
"completionTokens": self.completion_tokens,
91+
"cost": self.cost,
92+
"model": self.model,
93+
"modelParameters": self.model_parameters,
94+
"rawOutput": self.raw_output,
95+
}
96+
)
97+
return step_dict
98+
99+
100+
# ----------------------------- Factory function ----------------------------- #
101+
def step_factory(step_type: str, *args, **kwargs) -> Step:
102+
"""Factory function to create a step based on the step_type."""
103+
if step_type not in ["user_call", "openai_chat_completion"]:
104+
raise ValueError(f"Step type {step_type} not recognized.")
105+
step_type_mapping = {
106+
"user_call": UserCallStep,
107+
"openai_chat_completion": OpenAIChatCompletionStep,
108+
}
109+
return step_type_mapping[step_type](*args, **kwargs)

openlayer/tracing/tracer.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,15 @@
1717
@contextmanager
1818
def create_step(
1919
name: str,
20+
step_type: str = "user_call",
2021
inputs: Optional[Any] = None,
2122
output: Optional[Any] = None,
2223
metadata: Dict[str, any] = {},
2324
) -> Generator[steps.Step, None, None]:
2425
"""Starts a trace and yields a Step object."""
25-
new_step = steps.Step(name=name, inputs=inputs, output=output, metadata=metadata)
26-
26+
new_step = steps.step_factory(
27+
step_type=step_type, name=name, inputs=inputs, output=output, metadata=metadata
28+
)
2729
parent_step = _current_step.get(None)
2830
is_root_step = parent_step is None
2931

@@ -65,7 +67,12 @@ def wrapper(*func_args, **func_kwargs):
6567
output = func(*func_args, **func_kwargs)
6668
end_time = time.time()
6769
latency = (end_time - step.start_time) * 1000 # in ms
68-
inputs = func_signature.bind(*func_args, **func_kwargs).arguments
70+
71+
bound = func_signature.bind(*func_args, **func_kwargs)
72+
bound.apply_defaults()
73+
inputs = dict(bound.arguments)
74+
inputs.pop("self", None)
75+
inputs.pop("cls", None)
6976

7077
step.update_data(
7178
inputs=inputs,

0 commit comments

Comments
 (0)