@@ -450,24 +450,10 @@ func processOpenAIMessages(messages gjson.Result, modelID, origin string) ([]Kir
450450 // Merge adjacent messages with the same role
451451 messagesArray := kirocommon .MergeAdjacentMessages (messages .Array ())
452452
453- // Build tool_call_id to name mapping from assistant messages
454- toolCallIDToName := make (map [string ]string )
455- for _ , msg := range messagesArray {
456- if msg .Get ("role" ).String () == "assistant" {
457- toolCalls := msg .Get ("tool_calls" )
458- if toolCalls .IsArray () {
459- for _ , tc := range toolCalls .Array () {
460- if tc .Get ("type" ).String () == "function" {
461- id := tc .Get ("id" ).String ()
462- name := tc .Get ("function.name" ).String ()
463- if id != "" && name != "" {
464- toolCallIDToName [id ] = name
465- }
466- }
467- }
468- }
469- }
470- }
453+ // Track pending tool results that should be attached to the next user message
454+ // This is critical for LiteLLM-translated requests where tool results appear
455+ // as separate "tool" role messages between assistant and user messages
456+ var pendingToolResults []KiroToolResult
471457
472458 for i , msg := range messagesArray {
473459 role := msg .Get ("role" ).String ()
@@ -480,6 +466,10 @@ func processOpenAIMessages(messages gjson.Result, modelID, origin string) ([]Kir
480466
481467 case "user" :
482468 userMsg , toolResults := buildUserMessageFromOpenAI (msg , modelID , origin )
469+ // Merge any pending tool results from preceding "tool" role messages
470+ toolResults = append (pendingToolResults , toolResults ... )
471+ pendingToolResults = nil // Reset pending tool results
472+
483473 if isLastMessage {
484474 currentUserMsg = & userMsg
485475 currentToolResults = toolResults
@@ -505,6 +495,24 @@ func processOpenAIMessages(messages gjson.Result, modelID, origin string) ([]Kir
505495
506496 case "assistant" :
507497 assistantMsg := buildAssistantMessageFromOpenAI (msg )
498+
499+ // If there are pending tool results, we need to insert a synthetic user message
500+ // before this assistant message to maintain proper conversation structure
501+ if len (pendingToolResults ) > 0 {
502+ syntheticUserMsg := KiroUserInputMessage {
503+ Content : "Tool results provided." ,
504+ ModelID : modelID ,
505+ Origin : origin ,
506+ UserInputMessageContext : & KiroUserInputMessageContext {
507+ ToolResults : pendingToolResults ,
508+ },
509+ }
510+ history = append (history , KiroHistoryMessage {
511+ UserInputMessage : & syntheticUserMsg ,
512+ })
513+ pendingToolResults = nil
514+ }
515+
508516 if isLastMessage {
509517 history = append (history , KiroHistoryMessage {
510518 AssistantResponseMessage : & assistantMsg ,
@@ -524,7 +532,7 @@ func processOpenAIMessages(messages gjson.Result, modelID, origin string) ([]Kir
524532 case "tool" :
525533 // Tool messages in OpenAI format provide results for tool_calls
526534 // These are typically followed by user or assistant messages
527- // Process them and merge into the next user message's tool results
535+ // Collect them as pending and attach to the next user message
528536 toolCallID := msg .Get ("tool_call_id" ).String ()
529537 content := msg .Get ("content" ).String ()
530538
@@ -534,9 +542,21 @@ func processOpenAIMessages(messages gjson.Result, modelID, origin string) ([]Kir
534542 Content : []KiroTextContent {{Text : content }},
535543 Status : "success" ,
536544 }
537- // Tool results should be included in the next user message
538- // For now, collect them and they'll be handled when we build the current message
539- currentToolResults = append (currentToolResults , toolResult )
545+ // Collect pending tool results to attach to the next user message
546+ pendingToolResults = append (pendingToolResults , toolResult )
547+ }
548+ }
549+ }
550+
551+ // Handle case where tool results are at the end with no following user message
552+ if len (pendingToolResults ) > 0 {
553+ currentToolResults = append (currentToolResults , pendingToolResults ... )
554+ // If there's no current user message, create a synthetic one for the tool results
555+ if currentUserMsg == nil {
556+ currentUserMsg = & KiroUserInputMessage {
557+ Content : "Tool results provided." ,
558+ ModelID : modelID ,
559+ Origin : origin ,
540560 }
541561 }
542562 }
@@ -551,9 +571,6 @@ func buildUserMessageFromOpenAI(msg gjson.Result, modelID, origin string) (KiroU
551571 var toolResults []KiroToolResult
552572 var images []KiroImage
553573
554- // Track seen toolCallIds to deduplicate
555- seenToolCallIDs := make (map [string ]bool )
556-
557574 if content .IsArray () {
558575 for _ , part := range content .Array () {
559576 partType := part .Get ("type" ).String ()
@@ -589,9 +606,6 @@ func buildUserMessageFromOpenAI(msg gjson.Result, modelID, origin string) (KiroU
589606 contentBuilder .WriteString (content .String ())
590607 }
591608
592- // Check for tool_calls in the message (shouldn't be in user messages, but handle edge cases)
593- _ = seenToolCallIDs // Used for deduplication if needed
594-
595609 userMsg := KiroUserInputMessage {
596610 Content : contentBuilder .String (),
597611 ModelID : modelID ,
0 commit comments