55
66import Router from '@koa/router'
77import type { Context } from 'koa'
8+ import { PassThrough } from 'stream'
89import { loadBalancer } from '../loadbalancer'
910import { requestForwarder } from '../forwarder'
1011import { streamHandler } from '../stream'
@@ -33,6 +34,16 @@ function generateRequestId(): string {
3334 return `cmpl-${ Date . now ( ) . toString ( 36 ) } -${ Math . random ( ) . toString ( 36 ) . slice ( 2 , 8 ) } `
3435}
3536
37+ /**
38+ * Extract user input from prompt
39+ */
40+ function extractUserInput ( prompt : string | string [ ] ) : string | undefined {
41+ if ( Array . isArray ( prompt ) ) {
42+ return prompt . filter ( p => p ) . join ( ' ' )
43+ }
44+ return prompt || undefined
45+ }
46+
3647/**
3748 * Convert prompt to messages format
3849 */
@@ -159,6 +170,37 @@ router.post('/completions', async (ctx: Context) => {
159170 type : 'api_error' ,
160171 } ,
161172 }
173+
174+ storeManager . addLog ( 'error' , `Request failed: ${ result . error } ` , {
175+ requestId,
176+ providerId : provider . id ,
177+ accountId : account . id ,
178+ model : request . model ,
179+ latency,
180+ } )
181+
182+ storeManager . addRequestLog ( {
183+ timestamp : startTime ,
184+ status : 'error' ,
185+ statusCode : result . status || 500 ,
186+ method : 'POST' ,
187+ url : '/v1/completions' ,
188+ model : request . model ,
189+ actualModel,
190+ providerId : provider . id ,
191+ providerName : provider . name ,
192+ accountId : account . id ,
193+ accountName : account . name ,
194+ requestBody : JSON . stringify ( request ) ,
195+ userInput : extractUserInput ( request . prompt ) ,
196+ responseStatus : result . status || 500 ,
197+ latency,
198+ isStream : request . stream || false ,
199+ errorMessage : result . error ,
200+ } )
201+
202+ storeManager . recordRequestInStats ( false , latency , request . model , provider . id , account . id )
203+
162204 return
163205 }
164206
@@ -180,14 +222,82 @@ router.post('/completions', async (ctx: Context) => {
180222 isStream : request . stream ,
181223 } )
182224
225+ const userInput = extractUserInput ( request . prompt )
226+ const responseBodyForLog = ! request . stream && result . body
227+ ? JSON . stringify ( result . body )
228+ : undefined
229+
230+ let logEntryId : string | undefined
231+
232+ if ( ! request . stream ) {
233+ const logEntry = storeManager . addRequestLog ( {
234+ timestamp : startTime ,
235+ status : 'success' ,
236+ statusCode : 200 ,
237+ method : 'POST' ,
238+ url : '/v1/completions' ,
239+ model : request . model ,
240+ actualModel,
241+ providerId : provider . id ,
242+ providerName : provider . name ,
243+ accountId : account . id ,
244+ accountName : account . name ,
245+ requestBody : JSON . stringify ( request ) ,
246+ userInput,
247+ responseStatus : 200 ,
248+ responseBody : responseBodyForLog ,
249+ latency,
250+ isStream : false ,
251+ } )
252+ logEntryId = logEntry . id
253+ } else {
254+ const logEntry = storeManager . addRequestLog ( {
255+ timestamp : startTime ,
256+ status : 'success' ,
257+ statusCode : 200 ,
258+ method : 'POST' ,
259+ url : '/v1/completions' ,
260+ model : request . model ,
261+ actualModel,
262+ providerId : provider . id ,
263+ providerName : provider . name ,
264+ accountId : account . id ,
265+ accountName : account . name ,
266+ requestBody : JSON . stringify ( request ) ,
267+ userInput,
268+ responseStatus : 200 ,
269+ latency,
270+ isStream : true ,
271+ } )
272+ logEntryId = logEntry . id
273+ }
274+
275+ storeManager . recordRequestInStats ( true , latency , request . model , provider . id , account . id )
276+
183277 if ( request . stream && result . stream ) {
184278 ctx . set ( 'Content-Type' , 'text/event-stream' )
185279 ctx . set ( 'Cache-Control' , 'no-cache' )
186280 ctx . set ( 'Connection' , 'keep-alive' )
187281 ctx . set ( 'X-Accel-Buffering' , 'no' )
188282
189283 const transformStream = streamHandler . createTransformStream ( actualModel , requestId )
284+
285+ // Collect stream content for log update
286+ let collectedContent = ''
287+ transformStream . on ( 'data' , ( chunk : Buffer ) => {
288+ collectedContent += chunk . toString ( )
289+ } )
290+
190291 result . stream . pipe ( transformStream )
292+
293+ transformStream . once ( 'end' , ( ) => {
294+ if ( logEntryId ) {
295+ storeManager . updateRequestLog ( logEntryId , {
296+ responseBody : collectedContent || undefined ,
297+ } )
298+ }
299+ } )
300+
191301 ctx . body = transformStream
192302 } else {
193303 ctx . set ( 'Content-Type' , 'application/json' )
@@ -197,13 +307,46 @@ router.post('/completions', async (ctx: Context) => {
197307 const latency = Date . now ( ) - startTime
198308 proxyStatusManager . recordRequestFailure ( latency )
199309
310+ const errorMessage = error instanceof Error ? error . message : 'Unknown error'
311+
200312 ctx . status = 500
201313 ctx . body = {
202314 error : {
203- message : error instanceof Error ? error . message : 'Unknown error' ,
315+ message : errorMessage ,
204316 type : 'internal_error' ,
205317 } ,
206318 }
319+
320+ storeManager . addLog ( 'error' , `Request exception: ${ errorMessage } ` , {
321+ requestId,
322+ providerId : provider . id ,
323+ accountId : account . id ,
324+ model : request . model ,
325+ latency,
326+ error : errorMessage ,
327+ } )
328+
329+ storeManager . addRequestLog ( {
330+ timestamp : startTime ,
331+ status : 'error' ,
332+ statusCode : 500 ,
333+ method : 'POST' ,
334+ url : '/v1/completions' ,
335+ model : request . model ,
336+ actualModel,
337+ providerId : provider . id ,
338+ providerName : provider . name ,
339+ accountId : account . id ,
340+ accountName : account . name ,
341+ requestBody : JSON . stringify ( request ) ,
342+ userInput : extractUserInput ( request . prompt ) ,
343+ responseStatus : 500 ,
344+ latency,
345+ isStream : request . stream || false ,
346+ errorMessage,
347+ } )
348+
349+ storeManager . recordRequestInStats ( false , latency , request . model , provider . id , account . id )
207350 }
208351} )
209352
0 commit comments