@@ -4,6 +4,7 @@ import { text } from 'node:stream/consumers';
4
4
import FastifyProxy from '@fastify/http-proxy' ;
5
5
import { Actor } from 'apify' ;
6
6
import Fastify from 'fastify' ;
7
+ import type PinoPretty from 'pino-pretty' ;
7
8
8
9
await Actor . init ( ) ;
9
10
@@ -17,7 +18,17 @@ if (!OPENROUTER_API_KEY) {
17
18
}
18
19
19
20
const server = Fastify ( {
20
- logger : true ,
21
+ logger : {
22
+ transport : {
23
+ target : 'pino-pretty' ,
24
+ options : {
25
+ colorize : true ,
26
+ minimumLevel : 'debug' ,
27
+ ignore : 'hostname,time,pid,reqId' ,
28
+ messageFormat : '{if reqId}[{reqId}] {end}{msg}' ,
29
+ } as PinoPretty . PrettyOptions ,
30
+ } ,
31
+ } ,
21
32
} ) ;
22
33
23
34
server . get ( '/' , async ( request , reply ) => {
@@ -53,7 +64,8 @@ server.register(FastifyProxy, {
53
64
} ,
54
65
proxyPayloads : false , // Disable proxy payload, request body will be decoded and modified by preHandler
55
66
replyOptions : {
56
- onResponse : ( request , reply , res ) => {
67
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
68
+ onResponse : async ( request , reply , res ) => {
57
69
// @ts -expect-error stream is not defined in the type definitions
58
70
const stream = res . stream as NodeJS . ReadableStream ;
59
71
@@ -63,36 +75,49 @@ server.register(FastifyProxy, {
63
75
// Direct stream to the reply, don't wait for JSON parse
64
76
reply . send ( stream ) ;
65
77
66
- // Wait for end of stream and read as text
67
- text ( streamClone )
68
- . then ( ( response ) => {
69
- const isStream = response . startsWith ( 'data:' ) || response . startsWith ( ': OPENROUTER PROCESSING' ) ;
70
-
71
- if ( isStream ) {
72
- request . log . info ( 'Stream response mode' ) ;
73
- const lines = response . split ( '\n' ) . filter ( ( line ) => line . trim ( ) ) ;
74
- const data = JSON . parse ( lines [ lines . length - 2 ] . replace ( 'data: ' , '' ) ) ;
75
- return data . usage ?. cost || 0 ;
76
- }
77
-
78
- request . log . info ( 'Single response mode' ) ;
79
- const json = JSON . parse ( response ) ;
80
- request . log . info ( `Cost ${ json . usage . cost } ` ) ;
81
- return json . usage ?. cost || 0 ;
82
- } )
83
- . then ( chargeUser )
84
- . catch ( console . error ) ;
78
+ let response ;
79
+ try {
80
+ // Wait for end of stream and read as text
81
+ response = await text ( streamClone ) ;
82
+ } catch ( error ) {
83
+ request . log . error ( { error } , 'Cannot read response' ) ;
84
+ return ;
85
+ }
86
+
87
+ let jsonString ;
88
+ const isStream = response . startsWith ( 'data:' ) || response . startsWith ( ': OPENROUTER PROCESSING' ) ;
89
+ if ( isStream ) {
90
+ request . log . info ( 'Stream response mode' ) ;
91
+ const lines = response . split ( '\n' ) . filter ( ( line ) => line . trim ( ) ) ;
92
+ jsonString = lines [ lines . length - 2 ] . replace ( 'data: ' , '' ) ;
93
+ } else {
94
+ jsonString = response ;
95
+ }
96
+
97
+ let data ;
98
+ try {
99
+ data = JSON . parse ( jsonString ) ;
100
+ } catch ( error ) {
101
+ request . log . error ( { error, jsonString } , 'Failed to parse JSON response' ) ;
102
+ return ;
103
+ }
104
+
105
+ // eslint-disable-next-line prefer-destructuring
106
+ const cost = data . usage . cost ;
107
+ if ( ! cost ) {
108
+ request . log . error ( { data } , 'Cannot read cost from response' ) ;
109
+ return ;
110
+ }
111
+
112
+ const costWithFee = cost * 1.1 ; // Add 10% fee
113
+ const count = Math . max ( Math . round ( costWithFee / 0.0001 ) , 1 ) ;
114
+ request . log . info ( { originalCost : cost , costWithFee } , `Charging $0.0001 x ${ count } times` ) ;
115
+
116
+ await Actor . charge ( { eventName : 'credit-0-0001' , count } ) ;
85
117
} ,
86
118
} ,
87
119
} ) ;
88
120
89
- async function chargeUser ( amount : number ) {
90
- const chargePrice = amount * 1.1 ; // Add 10% fee
91
- const count = Math . max ( Math . round ( chargePrice / 0.0001 ) , 1 ) ;
92
- console . log ( `Charging $${ chargePrice } , by charge $0.0001 x ${ count } times` ) ;
93
- await Actor . charge ( { eventName : 'credit-0-0001' , count } ) ;
94
- }
95
-
96
121
process . on ( 'SIGTERM' , async ( ) => {
97
122
console . log ( 'Received SIGTERM, shutting down gracefully' ) ;
98
123
await server . close ( ) ;
0 commit comments