@@ -9,27 +9,94 @@ export function traceOpenAI(openai: OpenAI): OpenAI {
9
9
this : typeof openai . chat . completions ,
10
10
...args : Parameters < typeof createFunction >
11
11
) : Promise < Stream < OpenAI . Chat . Completions . ChatCompletionChunk > | OpenAI . Chat . Completions . ChatCompletion > {
12
- const [ params , options = { stream : false } ] = args ;
12
+ const [ params , options ] = args ;
13
+ const stream = params ?. stream ?? false ;
14
+
13
15
try {
14
16
const startTime = performance . now ( ) ;
15
- if ( options . stream ) {
16
- console . log ( 'streaming not implemented yet' ) ;
17
- return createFunction . apply ( this , args ) as unknown as Promise <
18
- Stream < OpenAI . Chat . Completions . ChatCompletionChunk >
19
- > ;
17
+
18
+ // Call the original `create` function
19
+ let response = await createFunction . apply ( this , args ) ;
20
+
21
+ if ( stream ) {
22
+ // Handle streaming responses
23
+ const chunks : OpenAI . Chat . Completions . ChatCompletionChunk [ ] = [ ] ;
24
+ let collectedOutputData : any [ ] = [ ] ;
25
+ let firstTokenTime : number | undefined ;
26
+ let completionTokens : number = 0 ;
27
+ if ( isAsyncIterable ( response ) ) {
28
+ async function * tracedOutputGenerator ( ) : AsyncGenerator <
29
+ OpenAI . Chat . Completions . ChatCompletionChunk ,
30
+ void ,
31
+ unknown
32
+ > {
33
+ for await ( const rawChunk of response as AsyncIterable < OpenAI . Chat . Completions . ChatCompletionChunk > ) {
34
+ if ( chunks . length === 0 ) {
35
+ firstTokenTime = performance . now ( ) ;
36
+ }
37
+ chunks . push ( rawChunk ) ;
38
+ const delta = rawChunk . choices [ 0 ] ?. delta ;
39
+ if ( delta ?. content ) {
40
+ collectedOutputData . push ( delta ?. content ) ;
41
+ } else if ( delta ?. tool_calls ) {
42
+ const tool_call = delta . tool_calls [ 0 ] ;
43
+ if ( tool_call ?. function ?. name ) {
44
+ const functionName : string =
45
+ '{\n "name": ' + '"' + tool_call . function . name + '"' + '\n "arguments": ' ;
46
+ collectedOutputData . push ( functionName ) ;
47
+ } else if ( tool_call ?. function ?. arguments ) {
48
+ collectedOutputData . push ( tool_call . function . arguments ) ;
49
+ }
50
+ }
51
+
52
+ if ( rawChunk . choices [ 0 ] ?. finish_reason === 'tool_calls' ) {
53
+ collectedOutputData . push ( '\n}' ) ;
54
+ }
55
+ completionTokens += 1 ;
56
+ yield rawChunk ;
57
+ }
58
+ const endTime = performance . now ( ) ;
59
+ const traceData = {
60
+ name : 'OpenAI Chat Completion' ,
61
+ inputs : { prompt : params . messages } ,
62
+ output : collectedOutputData . join ( '' ) ,
63
+ latency : endTime - startTime ,
64
+ model : chunks [ 0 ] ?. model as string ,
65
+ modelParameters : getModelParameters ( args ) ,
66
+ rawOutput : chunks . map ( ( chunk ) => JSON . stringify ( chunk , null , 2 ) ) . join ( '\n' ) ,
67
+ metadata : { timeToFistToken : firstTokenTime ? firstTokenTime - startTime : null } ,
68
+ provider : 'OpenAI' ,
69
+ completionTokens : completionTokens ,
70
+ promptTokens : 0 ,
71
+ tokens : completionTokens ,
72
+ } ;
73
+ addChatCompletionStepToTrace ( traceData ) ;
74
+ }
75
+ return tracedOutputGenerator ( ) as unknown as Stream < OpenAI . Chat . Completions . ChatCompletionChunk > ;
76
+ }
20
77
} else {
21
- const response = ( await createFunction . apply ( this , args ) ) as OpenAI . Chat . Completions . ChatCompletion ;
78
+ // Handle non-streaming responses
79
+ response = response as OpenAI . Chat . Completions . ChatCompletion ;
22
80
const completion = response . choices [ 0 ] ;
23
81
const endTime = performance . now ( ) ;
82
+
83
+ let output : string = '' ;
84
+ if ( completion ?. message ?. content ) {
85
+ output = completion . message . content ;
86
+ } else if ( completion ?. message . tool_calls ) {
87
+ const tool_call = completion . message . tool_calls [ 0 ] ;
88
+ output = JSON . stringify ( tool_call ?. function , null , 2 ) ;
89
+ }
90
+
24
91
const traceData = {
25
92
name : 'OpenAI Chat Completion' ,
26
93
inputs : { prompt : params . messages } ,
27
- output : completion ?. message . content ,
94
+ output : output ,
28
95
latency : endTime - startTime ,
29
- tokens : response ? .usage ?. total_tokens ?? null ,
30
- promptTokens : response ? .usage ?. prompt_tokens ?? null ,
31
- completionTokens : response ? .usage ?. completion_tokens ?? null ,
32
- model : response ? .model ,
96
+ tokens : response . usage ?. total_tokens ?? null ,
97
+ promptTokens : response . usage ?. prompt_tokens ?? null ,
98
+ completionTokens : response . usage ?. completion_tokens ?? null ,
99
+ model : response . model ,
33
100
modelParameters : getModelParameters ( args ) ,
34
101
rawOutput : JSON . stringify ( response , null , 2 ) ,
35
102
metadata : { } ,
@@ -42,6 +109,8 @@ export function traceOpenAI(openai: OpenAI): OpenAI {
42
109
console . error ( 'Failed to trace the create chat completion request with Openlayer' , error ) ;
43
110
throw error ;
44
111
}
112
+ // Ensure a return statement is present
113
+ return undefined as any ;
45
114
} as typeof createFunction ;
46
115
47
116
return openai ;
@@ -63,3 +132,6 @@ function getModelParameters(args: any): Record<string, any> {
63
132
top_p : params ?. topP ?? 1 ,
64
133
} ;
65
134
}
135
+
136
+ const isAsyncIterable = ( x : any ) =>
137
+ x != null && typeof x === 'object' && typeof x [ Symbol . asyncIterator ] === 'function' ;
0 commit comments