@@ -142,7 +142,6 @@ function openai_mapper.map_messages(contract_messages, options)
142142
143143 while i <= # contract_messages do
144144 local msg = contract_messages [i ]
145-
146145 -- Clear metadata
147146 msg .metadata = nil
148147
@@ -176,26 +175,53 @@ function openai_mapper.map_messages(contract_messages, options)
176175
177176 i = i + 1 -- Move to next message
178177
179- -- Check if next messages are function_calls that should be consolidated
180- while i <= # contract_messages and contract_messages [i ].role == " function_call" do
181- local func_msg = contract_messages [i ]
182-
183- if func_msg .function_call and func_msg .function_call .id then
184- local arguments = func_msg .function_call .arguments
185- if type (arguments ) == " table" and not next (arguments ) then
186- arguments = { invoke = true }
187- end
188-
189- table.insert (assistant_msg .tool_calls , {
190- id = func_msg .function_call .id ,
191- type = " function" ,
192- [" function" ] = {
193- name = func_msg .function_call .name ,
194- arguments = (type (arguments ) == " table" ) and json .encode (arguments ) or tostring (arguments )
195- }
178+ -- Collect ALL function_calls that belong to this assistant message
179+ -- Look ahead through remaining messages to find all related function calls
180+ local function_calls_found = {}
181+ local j = i
182+ while j <= # contract_messages do
183+ local current_msg = contract_messages [j ]
184+
185+ if current_msg .role == " function_call" and current_msg .function_call and current_msg .function_call .id then
186+ -- Collect this function call
187+ table.insert (function_calls_found , {
188+ index = j ,
189+ msg = current_msg
196190 })
191+ elseif current_msg .role == " assistant" then
192+ -- Stop when we hit another assistant message
193+ break
194+ elseif current_msg .role == " function_result" then
195+ -- This handles interleaved function_call/function_result patterns
196+ elseif current_msg .role == " user" then
197+ -- Stop when we hit a user message (new conversation turn)
198+ break
197199 end
198- i = i + 1
200+ j = j + 1
201+ end
202+
203+ -- Process all collected function calls
204+ for _ , func_call_info in ipairs (function_calls_found ) do
205+ local func_msg = func_call_info .msg
206+ local arguments = func_msg .function_call .arguments
207+ if type (arguments ) == " table" and not next (arguments ) then
208+ arguments = { invoke = true }
209+ end
210+
211+ table.insert (assistant_msg .tool_calls , {
212+ id = func_msg .function_call .id ,
213+ type = " function" ,
214+ [" function" ] = {
215+ name = func_msg .function_call .name ,
216+ arguments = (type (arguments ) == " table" ) and json .encode (arguments ) or tostring (arguments )
217+ }
218+ })
219+ end
220+
221+ -- Mark all collected function calls as processed by creating a set of indices
222+ local processed_indices = {}
223+ for _ , func_call_info in ipairs (function_calls_found ) do
224+ processed_indices [func_call_info .index ] = true
199225 end
200226
201227 -- Remove tool_calls field if empty
@@ -204,6 +230,15 @@ function openai_mapper.map_messages(contract_messages, options)
204230 end
205231
206232 table.insert (processed_messages , assistant_msg )
233+
234+ -- Continue processing, but skip the function calls we already processed
235+ while i <= # contract_messages do
236+ if processed_indices [i ] then
237+ i = i + 1
238+ else
239+ break
240+ end
241+ end
207242 elseif msg .role == " function_result" then
208243 -- Convert function results to tool messages - use simple string content
209244 local tool_content = " "
@@ -257,14 +292,13 @@ function openai_mapper.map_messages(contract_messages, options)
257292 })
258293 i = i + 1
259294 elseif msg .role == " function_call" then
260- -- Standalone function_call (shouldn't happen with proper consolidation above)
295+ -- Skip orphaned function calls - they should have been collected by assistant message processing
261296 i = i + 1
262297 else
263298 -- Skip unknown message types
264299 i = i + 1
265300 end
266301 end
267-
268302 return processed_messages
269303end
270304
0 commit comments