@@ -23,6 +23,85 @@ export type UsageData = {
2323 cost : number
2424}
2525
26+ export function createRequestAuditRecord ( body : unknown ) {
27+ // TODO: Add a separate append-only message_request BigQuery table for full
28+ // raw request bodies, inserted before streaming starts. Keeping only this
29+ // summary here avoids retaining huge chat requests until provider streams end.
30+ if ( typeof body !== 'object' || body === null || Array . isArray ( body ) ) {
31+ return { invalid_request_shape : true }
32+ }
33+
34+ const typedBody = body as Partial < ChatCompletionRequestBody >
35+ const messages = Array . isArray ( typedBody . messages )
36+ ? typedBody . messages
37+ : undefined
38+ const tools = Array . isArray ( typedBody . tools ) ? typedBody . tools : undefined
39+
40+ const messageRoleCounts = messages ?. reduce < Record < string , number > > (
41+ ( counts , message ) => {
42+ const role =
43+ typeof message === 'object' && message !== null && 'role' in message
44+ ? String ( message . role )
45+ : 'unknown'
46+ counts [ role ] = ( counts [ role ] ?? 0 ) + 1
47+ return counts
48+ } ,
49+ { } ,
50+ )
51+
52+ return {
53+ model : typeof typedBody . model === 'string' ? typedBody . model : undefined ,
54+ stream :
55+ typeof typedBody . stream === 'boolean' ? typedBody . stream : undefined ,
56+ temperature :
57+ typeof typedBody . temperature === 'number'
58+ ? typedBody . temperature
59+ : undefined ,
60+ max_tokens :
61+ typeof typedBody . max_tokens === 'number'
62+ ? typedBody . max_tokens
63+ : undefined ,
64+ max_completion_tokens :
65+ typeof typedBody . max_completion_tokens === 'number'
66+ ? typedBody . max_completion_tokens
67+ : undefined ,
68+ top_p : typeof typedBody . top_p === 'number' ? typedBody . top_p : undefined ,
69+ reasoning_effort :
70+ typeof typedBody . reasoning_effort === 'string'
71+ ? typedBody . reasoning_effort
72+ : undefined ,
73+ reasoning_enabled :
74+ typeof typedBody . reasoning ?. enabled === 'boolean'
75+ ? typedBody . reasoning . enabled
76+ : undefined ,
77+ reasoning_effort_nested :
78+ typeof typedBody . reasoning ?. effort === 'string'
79+ ? typedBody . reasoning . effort
80+ : undefined ,
81+ usage_include :
82+ typeof typedBody . usage ?. include === 'boolean'
83+ ? typedBody . usage . include
84+ : undefined ,
85+ codebuff_metadata :
86+ typeof typedBody . codebuff_metadata === 'object' &&
87+ typedBody . codebuff_metadata !== null
88+ ? { ...typedBody . codebuff_metadata }
89+ : undefined ,
90+ message_count : messages ?. length ?? 0 ,
91+ message_role_counts : messageRoleCounts ,
92+ messages_omitted : ! ! messages ,
93+ tool_count : tools ?. length ?? 0 ,
94+ tool_names : tools
95+ ?. map ( ( tool ) =>
96+ typeof tool === 'object' && tool !== null
97+ ? tool . function ?. name
98+ : undefined ,
99+ )
100+ . filter ( ( name ) : name is string => typeof name === 'string' ) ,
101+ tools_omitted : ! ! tools ,
102+ }
103+ }
104+
26105export function extractRequestMetadata ( params : {
27106 body : unknown
28107 logger : Logger
@@ -35,14 +114,20 @@ export function extractRequestMetadata(params: {
35114 const rawClientId = metadata ?. client_id
36115 const clientId = typeof rawClientId === 'string' ? rawClientId : null
37116 if ( ! clientId ) {
38- logger . warn ( { body } , 'Received request without client_id' )
117+ logger . warn (
118+ { request : createRequestAuditRecord ( body ) } ,
119+ 'Received request without client_id' ,
120+ )
39121 }
40122
41123 const rawRunId = metadata ?. run_id
42124 const clientRequestId : string | null =
43125 typeof rawRunId === 'string' ? rawRunId : null
44126 if ( ! clientRequestId ) {
45- logger . warn ( { body } , 'Received request without run_id' )
127+ logger . warn (
128+ { request : createRequestAuditRecord ( body ) } ,
129+ 'Received request without run_id' ,
130+ )
46131 }
47132
48133 const n = metadata ?. n
0 commit comments