@@ -36,20 +36,42 @@ class LLMOutputHandler {
3636 * @param codebolt - Optional codebolt API instance
3737 */
3838 constructor ( llmResponse : any , codebolt ?: CodeboltAPI ) {
39+ // Validate llmResponse structure
40+ if ( ! llmResponse ) {
41+ throw new Error ( "LLM response is null or undefined" ) ;
42+ }
43+
44+ if ( ! llmResponse . completion ) {
45+ throw new Error ( "LLM response completion is missing" ) ;
46+ }
47+
48+ if ( ! llmResponse . completion . choices || ! Array . isArray ( llmResponse . completion . choices ) ) {
49+ throw new Error ( "LLM response choices array is missing or invalid" ) ;
50+ }
51+
3952 this . llmResponse = llmResponse ;
4053 this . codebolt = codebolt ;
4154
4255 // Check if any message has tool_calls, if none do, mark as completed
4356 let hasToolCalls = false ;
44- this . llmResponse . completion . choices . forEach ( ( message : any ) => {
45- if ( message . message ?. tool_calls && message . message . tool_calls . length > 0 ) {
46- hasToolCalls = true ;
47- }
48- } ) ;
57+ try {
58+ this . llmResponse . completion . choices . forEach ( ( message : any ) => {
59+ if ( message . message ?. tool_calls && message . message . tool_calls . length > 0 ) {
60+ hasToolCalls = true ;
61+ }
62+ } ) ;
63+ } catch ( error ) {
64+ console . error ( "Error checking for tool calls:" , error ) ;
65+ hasToolCalls = false ;
66+ }
4967
5068 // If no messages have tool_calls, mark as completed
5169 if ( ! hasToolCalls ) {
52- this . sendMessageToUser ( ) ;
70+ try {
71+ this . sendMessageToUser ( ) ;
72+ } catch ( error ) {
73+ console . error ( "Error sending message to user:" , error ) ;
74+ }
5375 this . completed = true ;
5476 }
5577 }
@@ -119,11 +141,32 @@ class LLMOutputHandler {
119141 */
120142 async runTools ( ) : Promise < ToolResult [ ] > {
121143 try {
144+ // Validate llmResponse structure
145+ if ( ! this . llmResponse || ! this . llmResponse . completion ) {
146+ throw new Error ( "Invalid LLM response structure" ) ;
147+ }
148+
149+ if ( ! this . llmResponse . completion . choices || ! Array . isArray ( this . llmResponse . completion . choices ) ) {
150+ throw new Error ( "LLM response choices array is missing or invalid" ) ;
151+ }
152+
153+ if ( this . llmResponse . completion . choices . length === 0 ) {
154+ console . warn ( "No choices found in LLM response" ) ;
155+ return this . toolResults ;
156+ }
157+
122158 let toolResults : ToolResult [ ] = [ ] ;
123159 let taskCompletedBlock : any ;
124160 let userRejectedToolUse = false ;
125161 console . log ( "Calling run tool: " , JSON . stringify ( this . llmResponse . completion ) ) ;
162+
126163 const contentBlock = this . llmResponse . completion . choices [ 0 ] ;
164+
165+ // Validate contentBlock structure
166+ if ( ! contentBlock || ! contentBlock . message ) {
167+ console . warn ( "Invalid content block structure" ) ;
168+ return this . toolResults ;
169+ }
127170
128171 if ( contentBlock . message ?. tool_calls ) {
129172 for ( const tool of contentBlock . message . tool_calls ) {
@@ -208,7 +251,7 @@ class LLMOutputHandler {
208251
209252 this . toolResults . push ( {
210253 role : "tool" ,
211- tool_call_id : tool . id ,
254+ tool_call_id : tool ? .id || "unknown" ,
212255 content : String ( error ) ,
213256
214257 } ) ;
@@ -218,16 +261,40 @@ class LLMOutputHandler {
218261 }
219262
220263 if ( taskCompletedBlock ) {
264+ let taskArgs = { } ;
265+ try {
266+ // Validate taskCompletedBlock structure
267+ if ( ! taskCompletedBlock . function ) {
268+ throw new Error ( "Task completed block function is missing" ) ;
269+ }
270+
271+ if ( ! taskCompletedBlock . function . name ) {
272+ throw new Error ( "Task completed block function name is missing" ) ;
273+ }
274+
275+ // Parse arguments safely
276+ const argumentsString = taskCompletedBlock . function . arguments || "{}" ;
277+ if ( typeof argumentsString !== 'string' ) {
278+ throw new Error ( "Task completed block arguments must be a string" ) ;
279+ }
280+
281+ taskArgs = JSON . parse ( argumentsString ) ;
282+ } catch ( parseError ) {
283+ const errorMessage = parseError instanceof Error ? parseError . message : 'Unknown parsing error' ;
284+ console . error ( "Failed to parse taskCompletedBlock arguments:" , errorMessage ) ;
285+ taskArgs = { } ;
286+ }
287+
221288 let [ _ , result ] = await this . executeTool (
222289 taskCompletedBlock . function . name ,
223- JSON . parse ( taskCompletedBlock . function . arguments || "{}" )
290+ taskArgs
224291 ) ;
225292
226293 if ( result === "" ) {
227294 this . completed = true ;
228295 result = "The user is satisfied with the result." ;
229296 }
230- let toolResult = this . getToolResult ( taskCompletedBlock . id , result )
297+ let toolResult = this . getToolResult ( taskCompletedBlock ? .id || "unknown" , result )
231298 this . toolResults . push ( {
232299 role : "tool" ,
233300 tool_call_id : toolResult . tool_call_id ,
@@ -262,7 +329,17 @@ class LLMOutputHandler {
262329 }
263330 return this . toolResults
264331 } catch ( error ) {
265- return this . toolResults
332+ const errorMessage = error instanceof Error ? error . message : 'Unknown error occurred' ;
333+ console . error ( `Error in runTools: ${ errorMessage } ` , error ) ;
334+
335+ // Add error information to tool results for debugging
336+ this . toolResults . push ( {
337+ role : "tool" ,
338+ tool_call_id : "error" ,
339+ content : `Error executing tools: ${ errorMessage } `
340+ } ) ;
341+
342+ return this . toolResults ;
266343 }
267344 }
268345
@@ -298,11 +375,46 @@ class LLMOutputHandler {
298375 * @returns ToolDetails object with name, input, and ID
299376 */
300377 private getToolDetail ( tool : any ) : ToolDetails {
301- return {
302- toolName : tool . function . name ,
303- toolInput : JSON . parse ( tool . function . arguments || "{}" ) ,
304- toolUseId : tool . id
305- } ;
378+ try {
379+ // Validate tool object
380+ if ( ! tool ) {
381+ throw new Error ( "Tool object is null or undefined" ) ;
382+ }
383+
384+ // Validate tool.function
385+ if ( ! tool . function ) {
386+ throw new Error ( "Tool function is null or undefined" ) ;
387+ }
388+
389+ // Validate tool.function.name
390+ if ( ! tool . function . name || typeof tool . function . name !== 'string' ) {
391+ throw new Error ( "Tool function name is missing or invalid" ) ;
392+ }
393+
394+ // Validate tool.id
395+ if ( ! tool . id || typeof tool . id !== 'string' ) {
396+ throw new Error ( "Tool ID is missing or invalid" ) ;
397+ }
398+
399+ // Parse tool arguments safely
400+ let toolInput : any = { } ;
401+ if ( tool . function . arguments ) {
402+ try {
403+ toolInput = JSON . parse ( tool . function . arguments ) ;
404+ } catch ( parseError ) {
405+ throw new Error ( `Failed to parse tool arguments: ${ parseError instanceof Error ? parseError . message : 'Invalid JSON' } ` ) ;
406+ }
407+ }
408+
409+ return {
410+ toolName : tool . function . name ,
411+ toolInput : toolInput ,
412+ toolUseId : tool . id
413+ } ;
414+ } catch ( error ) {
415+ const errorMessage = error instanceof Error ? error . message : 'Unknown error occurred' ;
416+ throw new Error ( `Failed to extract tool details: ${ errorMessage } ` ) ;
417+ }
306418 }
307419
308420 /**
@@ -341,15 +453,25 @@ class LLMOutputHandler {
341453 let userMessage = undefined ;
342454
343455 try {
344- const parsed = JSON . parse ( content ) ;
345- console . log ( "Parsed Content: " , parsed ) ;
346-
347- if ( parsed . payload && parsed . payload . content ) {
348- content = `The browser action has been executed. The screenshot have been captured for your analysis. The tool response is provided in the next user message` ;
349- userMessage = parsed . payload . content ;
456+ // Validate content before parsing
457+ if ( typeof content !== 'string' ) {
458+ throw new Error ( "Content must be a string" ) ;
459+ }
460+
461+ // Only attempt to parse if content looks like JSON
462+ if ( content . trim ( ) . startsWith ( '{' ) || content . trim ( ) . startsWith ( '[' ) ) {
463+ const parsed = JSON . parse ( content ) ;
464+ console . log ( "Parsed Content: " , parsed ) ;
465+
466+ if ( parsed && typeof parsed === 'object' && parsed . payload && parsed . payload . content ) {
467+ content = `The browser action has been executed. The screenshot have been captured for your analysis. The tool response is provided in the next user message` ;
468+ userMessage = parsed . payload . content ;
469+ }
350470 }
351471 } catch ( error ) {
352- // Content is not JSON, use as-is
472+ const errorMessage = error instanceof Error ? error . message : 'Unknown JSON parsing error' ;
473+ console . warn ( `Failed to parse tool result content as JSON: ${ errorMessage } . Using content as-is.` ) ;
474+ // Content is not valid JSON, use as-is
353475 }
354476
355477 return {
0 commit comments