11import { type ActionFunctionArgs } from '@remix-run/cloudflare' ;
2+ import { createDataStream } from 'ai' ;
23import { MAX_RESPONSE_SEGMENTS , MAX_TOKENS } from '~/lib/.server/llm/constants' ;
34import { CONTINUE_PROMPT } from '~/lib/.server/llm/prompts' ;
45import { streamText , type Messages , type StreamingOptions } from '~/lib/.server/llm/stream-text' ;
@@ -9,17 +10,15 @@ export async function action(args: ActionFunctionArgs) {
910 return chatAction ( args ) ;
1011}
1112
12- function parseCookies ( cookieHeader : string ) {
13- const cookies : any = { } ;
13+ function parseCookies ( cookieHeader : string ) : Record < string , string > {
14+ const cookies : Record < string , string > = { } ;
1415
15- // Split the cookie string by semicolons and spaces
1616 const items = cookieHeader . split ( ';' ) . map ( ( cookie ) => cookie . trim ( ) ) ;
1717
1818 items . forEach ( ( item ) => {
1919 const [ name , ...rest ] = item . split ( '=' ) ;
2020
2121 if ( name && rest ) {
22- // Decode the name and value, and join value parts in case it contains '='
2322 const decodedName = decodeURIComponent ( name . trim ( ) ) ;
2423 const decodedValue = decodeURIComponent ( rest . join ( '=' ) . trim ( ) ) ;
2524 cookies [ decodedName ] = decodedValue ;
@@ -36,21 +35,49 @@ async function chatAction({ context, request }: ActionFunctionArgs) {
3635 } > ( ) ;
3736
3837 const cookieHeader = request . headers . get ( 'Cookie' ) ;
39-
40- // Parse the cookie's value (returns an object or null if no cookie exists)
4138 const apiKeys = JSON . parse ( parseCookies ( cookieHeader || '' ) . apiKeys || '{}' ) ;
4239 const providerSettings : Record < string , IProviderSetting > = JSON . parse (
4340 parseCookies ( cookieHeader || '' ) . providers || '{}' ,
4441 ) ;
4542
4643 const stream = new SwitchableStream ( ) ;
4744
45+ const cumulativeUsage = {
46+ completionTokens : 0 ,
47+ promptTokens : 0 ,
48+ totalTokens : 0 ,
49+ } ;
50+
4851 try {
4952 const options : StreamingOptions = {
5053 toolChoice : 'none' ,
51- onFinish : async ( { text : content , finishReason } ) => {
54+ onFinish : async ( { text : content , finishReason, usage } ) => {
55+ console . log ( 'usage' , usage ) ;
56+
57+ if ( usage ) {
58+ cumulativeUsage . completionTokens += usage . completionTokens || 0 ;
59+ cumulativeUsage . promptTokens += usage . promptTokens || 0 ;
60+ cumulativeUsage . totalTokens += usage . totalTokens || 0 ;
61+ }
62+
5263 if ( finishReason !== 'length' ) {
53- return stream . close ( ) ;
64+ return stream
65+ . switchSource (
66+ createDataStream ( {
67+ async execute ( dataStream ) {
68+ dataStream . writeMessageAnnotation ( {
69+ type : 'usage' ,
70+ value : {
71+ completionTokens : cumulativeUsage . completionTokens ,
72+ promptTokens : cumulativeUsage . promptTokens ,
73+ totalTokens : cumulativeUsage . totalTokens ,
74+ } ,
75+ } ) ;
76+ } ,
77+ onError : ( error : any ) => `Custom error: ${ error . message } ` ,
78+ } ) ,
79+ )
80+ . then ( ( ) => stream . close ( ) ) ;
5481 }
5582
5683 if ( stream . switches >= MAX_RESPONSE_SEGMENTS ) {
@@ -73,7 +100,7 @@ async function chatAction({ context, request }: ActionFunctionArgs) {
73100 providerSettings,
74101 } ) ;
75102
76- return stream . switchSource ( result . toAIStream ( ) ) ;
103+ return stream . switchSource ( result . toDataStream ( ) ) ;
77104 } ,
78105 } ;
79106
@@ -86,7 +113,7 @@ async function chatAction({ context, request }: ActionFunctionArgs) {
86113 providerSettings,
87114 } ) ;
88115
89- stream . switchSource ( result . toAIStream ( ) ) ;
116+ stream . switchSource ( result . toDataStream ( ) ) ;
90117
91118 return new Response ( stream . readable , {
92119 status : 200 ,
@@ -95,7 +122,7 @@ async function chatAction({ context, request }: ActionFunctionArgs) {
95122 } ,
96123 } ) ;
97124 } catch ( error : any ) {
98- console . log ( error ) ;
125+ console . error ( error ) ;
99126
100127 if ( error . message ?. includes ( 'API key' ) ) {
101128 throw new Response ( 'Invalid or missing API key' , {
0 commit comments