1+ import { DebugLogger } from "~/lib/debug-logger"
12import {
23 type ChatCompletionResponse ,
34 type ChatCompletionChunk ,
@@ -518,22 +519,33 @@ function ensureToolCallResponseMatch(messages: Array<Message>): Array<Message> {
518519export function translateOpenAIToGemini (
519520 response : ChatCompletionResponse ,
520521) : GeminiResponse {
521- const candidates : Array < GeminiCandidate > = response . choices . map (
522- ( choice , index ) => ( {
522+ const result = {
523+ candidates : response . choices . map ( ( choice , index ) => ( {
523524 content : translateOpenAIMessageToGeminiContent ( choice . message ) ,
524525 finishReason : mapOpenAIFinishReasonToGemini ( choice . finish_reason ) ,
525526 index,
526- } ) ,
527- )
528-
529- return {
530- candidates,
527+ } ) ) ,
531528 usageMetadata : {
532529 promptTokenCount : response . usage ?. prompt_tokens || 0 ,
533530 candidatesTokenCount : response . usage ?. completion_tokens || 0 ,
534531 totalTokenCount : response . usage ?. total_tokens || 0 ,
535532 } ,
536533 }
534+
535+ // Debug: Log original GitHub Copilot response and translated Gemini response for comparison
536+ if ( process . env . DEBUG_GEMINI_REQUESTS === "true" ) {
537+ DebugLogger . logResponseComparison ( response , result , {
538+ context : "Non-Stream Response Translation" ,
539+ filePrefix : "debug-nonstream-comparison" ,
540+ } ) . catch ( ( error : unknown ) => {
541+ console . error (
542+ "[DEBUG] Failed to log non-stream response comparison:" ,
543+ error ,
544+ )
545+ } )
546+ }
547+
548+ return result
537549}
538550
539551function translateOpenAIMessageToGeminiContent (
@@ -568,6 +580,13 @@ function translateOpenAIMessageToGeminiContent(
568580 // Handle tool calls
569581 if ( message . tool_calls ) {
570582 for ( const toolCall of message . tool_calls ) {
583+ // Debug: Log tool call arguments to verify what GitHub Copilot returns
584+ if ( process . env . DEBUG_GEMINI_REQUESTS === "true" ) {
585+ console . log (
586+ `[DEBUG] Tool call - name: ${ toolCall . function . name } , arguments: "${ toolCall . function . arguments } ", type: ${ typeof toolCall . function . arguments } , truthy: ${ Boolean ( toolCall . function . arguments ) } ` ,
587+ )
588+ }
589+
571590 parts . push ( {
572591 functionCall : {
573592 name : toolCall . function . name ,
@@ -595,7 +614,94 @@ function generateToolCallId(functionName: string): string {
595614 return `call_${ functionName } _${ Date . now ( ) } _${ Math . random ( ) . toString ( 36 ) . slice ( 2 , 11 ) } `
596615}
597616
598- // Helper function to process tool calls in streaming chunks
617+ // Global accumulator for streaming tool call arguments
618+ const streamingToolCallAccumulator = new Map <
619+ number ,
620+ {
621+ name : string
622+ arguments : string
623+ id ?: string
624+ }
625+ > ( )
626+
627+ // Helper function to try parsing and creating a function call
628+ function tryCreateFunctionCall (
629+ name : string ,
630+ argumentsStr : string ,
631+ ) : GeminiPart | null {
632+ try {
633+ const args = JSON . parse ( argumentsStr ) as Record < string , unknown >
634+ return {
635+ functionCall : {
636+ name,
637+ args,
638+ } ,
639+ }
640+ } catch {
641+ return null
642+ }
643+ }
644+
645+ // Helper function to handle tool call with function name
646+ function handleToolCallWithName ( toolCall : {
647+ index : number
648+ id ?: string
649+ function : {
650+ name : string
651+ arguments ?: string
652+ }
653+ } ) : GeminiPart | null {
654+ const accumulatedArgs = toolCall . function . arguments || ""
655+
656+ streamingToolCallAccumulator . set ( toolCall . index , {
657+ name : toolCall . function . name ,
658+ arguments : accumulatedArgs ,
659+ id : toolCall . id ,
660+ } )
661+
662+ // If we already have arguments, try to process immediately (for non-streaming models like Gemini)
663+ if ( accumulatedArgs ) {
664+ const functionCall = tryCreateFunctionCall (
665+ toolCall . function . name ,
666+ accumulatedArgs ,
667+ )
668+ if ( functionCall ) {
669+ // Clear the accumulator for this index since we've successfully processed it
670+ streamingToolCallAccumulator . delete ( toolCall . index )
671+ return functionCall
672+ }
673+ }
674+
675+ return null
676+ }
677+
678+ // Helper function to handle tool call argument accumulation
679+ function handleToolCallAccumulation ( toolCall : {
680+ index : number
681+ function ?: {
682+ arguments ?: string
683+ }
684+ } ) : GeminiPart | null {
685+ const existingAccumulated = streamingToolCallAccumulator . get ( toolCall . index )
686+
687+ if ( existingAccumulated && toolCall . function ?. arguments ) {
688+ existingAccumulated . arguments += toolCall . function . arguments
689+
690+ const functionCall = tryCreateFunctionCall (
691+ existingAccumulated . name ,
692+ existingAccumulated . arguments ,
693+ )
694+ if ( functionCall ) {
695+ // Clear the accumulator for this index since we've successfully processed it
696+ streamingToolCallAccumulator . delete ( toolCall . index )
697+ return functionCall
698+ }
699+ }
700+
701+ return null
702+ }
703+
704+ // Helper function to process tool calls in streaming chunks with argument accumulation
599705function processToolCalls (
600706 toolCalls : Array < {
601707 index : number
@@ -610,33 +716,34 @@ function processToolCalls(
610716 const parts : Array < GeminiPart > = [ ]
611717
612718 for ( const toolCall of toolCalls ) {
613- // Enhanced validation: check for empty/whitespace-only names
614- if (
615- ! toolCall . function ?. name
616- || typeof toolCall . function . name !== "string"
617- || toolCall . function . name . trim ( ) === ""
618- ) {
619- continue
719+ // Debug: Log streaming tool call arguments to verify what GitHub Copilot returns
720+ if ( process . env . DEBUG_GEMINI_REQUESTS === "true" ) {
721+ console . log (
722+ `[DEBUG STREAM] Tool call - name: ${ toolCall . function ?. name } , arguments: "${ toolCall . function ?. arguments } ", type: ${ typeof toolCall . function ?. arguments } , truthy: ${ Boolean ( toolCall . function ?. arguments ) } ` ,
723+ )
620724 }
621725
622- let args : Record < string , unknown >
623- try {
624- args = JSON . parse ( toolCall . function . arguments || "{}" ) as Record <
625- string ,
626- unknown
627- >
628- } catch {
629- // In streaming, arguments might be incomplete JSON
630- // Skip this chunk and wait for complete arguments
726+ // If this chunk has a function name, it's the start of a new tool call
727+ if ( toolCall . function ?. name && toolCall . function . name . trim ( ) !== "" ) {
728+ const functionCall = handleToolCallWithName ( {
729+ index : toolCall . index ,
730+ id : toolCall . id ,
731+ function : {
732+ name : toolCall . function . name ,
733+ arguments : toolCall . function . arguments ,
734+ } ,
735+ } )
736+ if ( functionCall ) {
737+ parts . push ( functionCall )
738+ }
631739 continue
632740 }
633741
634- parts . push ( {
635- functionCall : {
636- name : toolCall . function . name ,
637- args,
638- } ,
639- } )
742+ // If we have existing accumulated data and this chunk has arguments, append them
743+ const functionCall = handleToolCallAccumulation ( toolCall )
744+ if ( functionCall ) {
745+ parts . push ( functionCall )
746+ }
640747 }
641748
642749 return parts
@@ -806,6 +913,16 @@ export function translateOpenAIChunkToGemini(chunk: ChatCompletionChunk): {
806913 )
807914 const response = buildGeminiResponse ( candidate , shouldInclude , chunk )
808915
916+ // Debug: Log original GitHub Copilot chunk and translated Gemini chunk for comparison
917+ if ( process . env . DEBUG_GEMINI_REQUESTS === "true" ) {
918+ DebugLogger . logResponseComparison ( chunk , response , {
919+ context : "Streaming Chunk Translation" ,
920+ filePrefix : "debug-stream-comparison" ,
921+ } ) . catch ( ( error : unknown ) => {
922+ console . error ( "[DEBUG] Failed to log streaming chunk comparison:" , error )
923+ } )
924+ }
925+
809926 return response
810927}
811928
0 commit comments