@@ -85,6 +85,8 @@ export interface FlowBaseOptions {
8585 logger : ConsolaInstance
8686 subagentMarker ?: SubagentMarker | null
8787 requestId : string
88+ requestSessionAffinity ?: string
89+ requestTraceId ?: string
8890 sessionId ?: string
8991 compactType ?: CompactType
9092}
@@ -103,14 +105,24 @@ export const handleWithChatCompletions = async (
103105 anthropicPayload : AnthropicMessagesPayload ,
104106 options : FlowBaseOptions ,
105107) => {
106- const { logger, subagentMarker, requestId, sessionId, compactType } = options
108+ const {
109+ logger,
110+ requestSessionAffinity,
111+ requestTraceId,
112+ subagentMarker,
113+ requestId,
114+ sessionId,
115+ compactType,
116+ } = options
107117 const openAIPayload = translateToOpenAI ( anthropicPayload )
108118 prepareCopilotChatCompletionsPayload ( openAIPayload )
109- const recordUsage = createCopilotUsageRecorder ( {
119+ const recordUsage = createMessagesFlowUsageRecorder ( {
120+ anthropicPayload,
110121 endpoint : "chat_completions" ,
111122 fallbackSessionId : sessionId ,
112123 model : openAIPayload . model ,
113- payload : anthropicPayload ,
124+ requestSessionAffinity,
125+ requestTraceId,
114126 } )
115127 debugJson ( logger , "Translated OpenAI request payload:" , openAIPayload )
116128
@@ -130,6 +142,23 @@ export const handleWithChatCompletions = async (
130142 } )
131143 }
132144
145+ return handleChatCompletionsStream ( {
146+ c,
147+ logger,
148+ model : openAIPayload . model ,
149+ recordUsage,
150+ response,
151+ } )
152+ }
153+
154+ const handleChatCompletionsStream = ( options : {
155+ c : Context
156+ logger : ConsolaInstance
157+ model : string
158+ recordUsage : ( usage : UsageTokens ) => void
159+ response : AsyncIterable < { data ?: string } >
160+ } ) => {
161+ const { c, logger, model, recordUsage, response } = options
133162 logger . debug ( "Streaming response from Copilot" )
134163 applyForwardableResponseHeaders ( c , getAttachedResponseHeaders ( response ) , {
135164 "content-type" : null ,
@@ -180,7 +209,7 @@ export const handleWithChatCompletions = async (
180209 const premium = await resolvePremiumInfo ( response , "messages/chat-stream" )
181210 writeStreamLog (
182211 {
183- model : openAIPayload . model ,
212+ model,
184213 chunks : chunkCount ,
185214 done : true ,
186215 premium,
@@ -226,7 +255,14 @@ export const handleWithResponsesApi = async (
226255 anthropicPayload : AnthropicMessagesPayload ,
227256 options : ResponsesFlowOptions ,
228257) => {
229- const { logger, selectedModel, compactType, ...requestOptions } = options
258+ const {
259+ logger,
260+ requestSessionAffinity,
261+ requestTraceId,
262+ selectedModel,
263+ compactType,
264+ ...requestOptions
265+ } = options
230266
231267 const responsesPayload =
232268 translateAnthropicMessagesToResponsesPayload ( anthropicPayload )
@@ -235,6 +271,8 @@ export const handleWithResponsesApi = async (
235271 fallbackSessionId : requestOptions . sessionId ,
236272 model : responsesPayload . model ,
237273 payload : anthropicPayload ,
274+ requestSessionAffinity,
275+ requestTraceId,
238276 } )
239277
240278 applyResponsesApiContextManagement (
@@ -304,6 +342,8 @@ export const handleWithMessagesApi = async (
304342 const {
305343 logger,
306344 anthropicBetaHeader,
345+ requestSessionAffinity,
346+ requestTraceId,
307347 subagentMarker,
308348 selectedModel,
309349 requestId,
@@ -317,6 +357,8 @@ export const handleWithMessagesApi = async (
317357 fallbackSessionId : sessionId ,
318358 model : anthropicPayload . model ,
319359 payload : anthropicPayload ,
360+ requestSessionAffinity,
361+ requestTraceId,
320362 } )
321363
322364 debugJson ( logger , "Translated Messages payload:" , anthropicPayload )
@@ -475,6 +517,7 @@ const createNativeStreamBody = (options: {
475517 const { body, model, premium, logger, recordUsage } = options
476518 let chunkCount = 0
477519 let usage : UsageTokens = { }
520+ let usageRecorded = false
478521 let buffer = ""
479522 const decoder = new TextDecoder ( )
480523 const reader = body . getReader ( )
@@ -491,7 +534,9 @@ const createNativeStreamBody = (options: {
491534 }
492535
493536 writeStreamLog ( { model, chunks : chunkCount , done : true , premium } , true )
494- recordUsage ( usage )
537+ if ( ! usageRecorded ) {
538+ recordUsage ( usage )
539+ }
495540
496541 controller . close ( )
497542 return
@@ -526,6 +571,10 @@ const createNativeStreamBody = (options: {
526571 normalizeAnthropicUsage ( parsedEvent . usage ) ,
527572 )
528573 }
574+ if ( ! usageRecorded && parsedEvent ?. type === "message_delta" ) {
575+ recordUsage ( usage )
576+ usageRecorded = true
577+ }
529578 return data !== "" && data !== "[DONE]"
530579 } ) . length
531580 if ( newEvents > 0 ) {
@@ -625,12 +674,33 @@ const createCopilotUsageRecorder = (options: {
625674 fallbackSessionId ?: string
626675 model : string
627676 payload : AnthropicMessagesPayload
677+ requestSessionAffinity ?: string
678+ requestTraceId ?: string
628679} ) : ( ( usage : UsageTokens ) => void ) =>
629680 createCopilotTokenUsageRecorder ( {
630681 endpoint : options . endpoint ,
631682 fallbackSessionId : options . fallbackSessionId ,
632683 model : options . model ,
633- sessionId : getMetadataSessionId ( options . payload ) ,
684+ sessionId :
685+ options . requestSessionAffinity ?? getMetadataSessionId ( options . payload ) ,
686+ traceId : options . requestTraceId ,
687+ } )
688+
689+ const createMessagesFlowUsageRecorder = ( options : {
690+ anthropicPayload : AnthropicMessagesPayload
691+ endpoint : TokenUsageEndpoint
692+ fallbackSessionId ?: string
693+ model : string
694+ requestSessionAffinity ?: string
695+ requestTraceId ?: string
696+ } ) =>
697+ createCopilotUsageRecorder ( {
698+ endpoint : options . endpoint ,
699+ fallbackSessionId : options . fallbackSessionId ,
700+ model : options . model ,
701+ payload : options . anthropicPayload ,
702+ requestSessionAffinity : options . requestSessionAffinity ,
703+ requestTraceId : options . requestTraceId ,
634704 } )
635705
636706const getMetadataSessionId = (
0 commit comments