11"""Module with the logic to create and manage traces and steps."""
22
3+ import contextvars
34import inspect
4- from typing import Any , Dict , Optional , Generator
5+ import logging
6+ import time
57from contextlib import contextmanager
6- import contextvars
78from functools import wraps
9+ from typing import Any , Dict , Generator , List , Optional , Tuple
810
9- from . import steps
10- from . import traces
11- import time
11+ from ..services import data_streamer
12+ from . import steps , traces
13+
14+ logger = logging .getLogger (__name__ )
15+
16+ _streamer = None
17+ try :
18+ _streamer = data_streamer .DataStreamer (publish = True )
19+ except Exception as exc :
20+ logger .error (
21+ "You have not provided enough information to upload traces to Openlayer."
22+ "\n %s \n "
23+ "To upload the traces, please provide the missing information and try again." ,
24+ exc ,
25+ )
1226
1327_current_step = contextvars .ContextVar ("current_step" )
1428_current_trace = contextvars .ContextVar ("current_trace" )
@@ -23,36 +37,85 @@ def create_step(
2337 metadata : Dict [str , any ] = {},
2438) -> Generator [steps .Step , None , None ]:
2539 """Starts a trace and yields a Step object."""
26- new_step = steps .step_factory (
40+ new_step : steps . Step = steps .step_factory (
2741 step_type = step_type , name = name , inputs = inputs , output = output , metadata = metadata
2842 )
29- parent_step = _current_step .get (None )
30- is_root_step = parent_step is None
43+
44+ parent_step : Optional [steps .Step ] = _current_step .get (None )
45+ is_root_step : bool = parent_step is None
3146
3247 if parent_step is None :
33- print ("Starting a new trace..." )
48+ logger . debug ("Starting a new trace..." )
3449 current_trace = traces .Trace ()
3550 _current_trace .set (current_trace ) # Set the current trace in context
3651 current_trace .add_step (new_step )
3752 else :
38- print (f"Adding step { name } to parent step { parent_step .name } " )
53+ logger . debug (f"Adding step { name } to parent step { parent_step .name } " )
3954 current_trace = _current_trace .get ()
4055 parent_step .add_nested_step (new_step )
4156
4257 token = _current_step .set (new_step )
43-
4458 try :
4559 yield new_step
4660 finally :
4761 _current_step .reset (token )
4862 if is_root_step :
49- print ("Ending the trace..." )
50- print ("-" * 80 )
51- print (current_trace .to_dict ())
52- print ("-" * 80 )
63+ logger .debug ("Ending the trace..." )
64+ trace_data , input_variable_names = process_trace_for_upload (current_trace )
65+ config = {
66+ "outputColumnName" : "output" ,
67+ "inputVariableNames" : input_variable_names ,
68+ "label" : "production" ,
69+ "groundTruthColumnName" : "groundTruth" ,
70+ "latencyColumnName" : "latency" ,
71+ }
72+ if isinstance (new_step , steps .OpenAIChatCompletionStep ):
73+ config .update (
74+ {
75+ "costColumnName" : "cost" ,
76+ "numOfTokenColumnName" : "tokens" ,
77+ "prompt" : new_step .inputs .get ("prompt" ),
78+ }
79+ )
80+ if _streamer :
81+ _streamer .stream_data (data = trace_data , config = config )
82+ else :
83+ logger .warning (
84+ "Trace computed but not uploaded to Openlayer. "
85+ "You have not provided enough information to upload traces to"
86+ " Openlayer."
87+ )
5388 else :
54- # TODO: stream to Openlayer
55- print (f"Ending step { name } " )
89+ logger .debug (f"Ending step { name } " )
90+
91+
92+ def process_trace_for_upload (trace : traces .Trace ) -> Tuple [Dict [str , Any ], List [str ]]:
93+ """Post processing of the trace data before uploading to Openlayer.
94+
95+ This is done to ensure backward compatibility with data on Openlayer.
96+ """
97+ root_step = trace .steps [0 ]
98+
99+ input_variables = root_step .inputs
100+ input_variable_names = list (input_variables .keys ())
101+
102+ trace_data = {
103+ ** input_variables ,
104+ "output" : root_step .output ,
105+ "groundTruth" : root_step .ground_truth ,
106+ "latency" : root_step .latency ,
107+ "steps" : trace .to_dict (),
108+ }
109+ # Extra fields for openai_chat_completion step
110+ if isinstance (root_step , steps .OpenAIChatCompletionStep ):
111+ trace_data .update (
112+ {
113+ "cost" : root_step .cost ,
114+ "tokens" : root_step .prompt_tokens + root_step .completion_tokens ,
115+ }
116+ )
117+
118+ return trace_data , input_variable_names
56119
57120
58121def trace (* step_args , ** step_kwargs ):
@@ -74,7 +137,7 @@ def wrapper(*func_args, **func_kwargs):
74137 inputs .pop ("self" , None )
75138 inputs .pop ("cls" , None )
76139
77- step .update_data (
140+ step .log (
78141 inputs = inputs ,
79142 output = output ,
80143 end_time = end_time ,
0 commit comments