1212 INFERENCE_MAX_TOOL_ITERATIONS ,
1313)
1414from bugzooka .integrations .inference import InferenceAPIUnavailableError
15- from bugzooka .analysis .prompts import JIRA_TOOL_PROMPT
1615
1716
1817logger = logging .getLogger (__name__ )
@@ -45,7 +44,7 @@ def __init__(self, api_key=None, base_url=None, verify_ssl=None, timeout=None):
4544 # Timeout configuration
4645 if timeout is None :
4746 timeout = float (os .getenv ("GEMINI_TIMEOUT" , "60.0" ))
48-
47+
4948 logger .debug ("Gemini client timeout set to %.1f seconds" , timeout )
5049
5150 # SSL verification configuration
@@ -80,19 +79,23 @@ def chat_completions_create(self, messages, model="gemini-2.0-flash", **kwargs):
8079 """
8180 try :
8281 logger .debug ("Calling Gemini API: %s, Model=%s" , self .base_url , model )
83-
82+
8483 response = self .client .chat .completions .create (
8584 model = model , messages = messages , ** kwargs
8685 )
87-
86+
8887 # Log token usage information
89- if hasattr (response , ' usage' ) and response .usage :
88+ if hasattr (response , " usage" ) and response .usage :
9089 usage = response .usage
91- logger .info ("📊 Token usage - Prompt: %d, Completion: %d, Total: %d" ,
92- usage .prompt_tokens , usage .completion_tokens , usage .total_tokens )
90+ logger .info (
91+ "📊 Token usage - Prompt: %d, Completion: %d, Total: %d" ,
92+ usage .prompt_tokens ,
93+ usage .completion_tokens ,
94+ usage .total_tokens ,
95+ )
9396 else :
9497 logger .debug ("No usage information available in response" )
95-
98+
9699 logger .debug ("Gemini API call successful" )
97100 return response
98101 except Exception as e :
@@ -159,19 +162,23 @@ async def execute_tool_call(tool_name, tool_args, available_tools):
159162 # Log result
160163 result_str = str (result )
161164 result_length = len (result_str )
162-
165+
163166 # Check for empty or minimal results
164167 if not result_str or result_str .strip () in ["" , "null" , "None" , "{}" , "[]" ]:
165168 logger .warning ("⚠️ Tool %s returned empty or null result" , tool_name )
166169 elif len (result_str .strip ()) < 50 :
167- logger .warning ("⚠️ Tool %s returned small result (%d chars): %s" ,
168- tool_name , result_length , result_str )
170+ logger .warning (
171+ "⚠️ Tool %s returned small result (%d chars): %s" ,
172+ tool_name ,
173+ result_length ,
174+ result_str ,
175+ )
169176 else :
170177 logger .info ("✅ Tool %s completed (%d chars)" , tool_name , result_length )
171-
178+
172179 # Log full output at DEBUG level
173180 logger .debug ("Tool %s output: %s" , tool_name , result_str )
174-
181+
175182 return result_str
176183 except Exception as e :
177184 error_msg = f"Error executing tool '{ tool_name } ': { str (e )} "
@@ -182,20 +189,17 @@ async def execute_tool_call(tool_name, tool_args, available_tools):
182189
183190
184191async def analyze_with_gemini_agentic (
185- messages : list ,
186- tools = None ,
187- model = "gemini-2.0-flash" ,
188- max_iterations = None
192+ messages : list , tools = None , model = "gemini-2.0-flash" , max_iterations = None
189193):
190194 """
191195 Generic agentic loop for Gemini with tool calling support.
192-
196+
193197 This function implements the agentic pattern where Gemini can iteratively:
194198 1. Analyze the current context
195199 2. Decide to call tools if needed
196200 3. Process tool results
197201 4. Generate final answer
198-
202+
199203 :param messages: List of message dictionaries (system, user, assistant prompts)
200204 :param tools: List of LangChain tools available for Gemini to call (optional)
201205 :param model: Gemini model to use (default: gemini-2.0-flash)
@@ -204,42 +208,20 @@ async def analyze_with_gemini_agentic(
204208 """
205209 if max_iterations is None :
206210 max_iterations = INFERENCE_MAX_TOOL_ITERATIONS
207-
211+
208212 try :
209213 gemini_client = GeminiClient ()
210214
211- prompt_config = product_config ["prompt" ][product ]
212-
213- # Check if Jira MCP tools are available and inject Jira prompt
214- has_jira = tools and any (
215- hasattr (t , "name" ) and t .name == "search_jira_issues" for t in tools
216- )
217- if has_jira :
218- logger .info ("Jira MCP tools detected - injecting Jira prompt" )
219- system_prompt = prompt_config ["system" ] + JIRA_TOOL_PROMPT ["system_suffix" ]
220- else :
221- system_prompt = prompt_config ["system" ]
222-
223- try :
224- formatted_content = prompt_config ["user" ].format (
225- error_summary = error_summary
226- )
227- except KeyError :
228- formatted_content = prompt_config ["user" ].format (summary = error_summary )
229-
230- messages = [
231- {"role" : "system" , "content" : system_prompt },
232- {"role" : "user" , "content" : formatted_content },
233- {"role" : "assistant" , "content" : prompt_config ["assistant" ]},
234- ]
235-
236215 # Convert LangChain tools to OpenAI format if provided
237216 openai_tools = None
238217 if tools :
239218 openai_tools = convert_langchain_tools_to_openai_format (tools )
240219 tool_names = [t ["function" ]["name" ] for t in openai_tools ]
241- logger .info ("Starting Gemini analysis with %d tools: %s" ,
242- len (openai_tools ), ", " .join (tool_names ))
220+ logger .info (
221+ "Starting Gemini analysis with %d tools: %s" ,
222+ len (openai_tools ),
223+ ", " .join (tool_names ),
224+ )
243225
244226 logger .debug ("Starting agentic loop with %d messages" , len (messages ))
245227
@@ -273,15 +255,20 @@ async def analyze_with_gemini_agentic(
273255 content = response_message .content
274256 if content :
275257 logger .info ("Analysis complete after %d iteration(s)" , iteration )
276- logger .debug ("Response: %s" , content [:200 ] + "..." if len (content ) > 200 else content )
258+ logger .debug (
259+ "Response: %s" ,
260+ content [:200 ] + "..." if len (content ) > 200 else content ,
261+ )
277262 else :
278263 logger .warning ("Gemini returned None content, using empty string" )
279264 content = ""
280265 return content
281266
282267 # Gemini wants to call tools - execute them
283268 tool_names_called = [tc .function .name for tc in tool_calls ]
284- logger .info ("Calling %d tool(s): %s" , len (tool_calls ), ", " .join (tool_names_called ))
269+ logger .info (
270+ "Calling %d tool(s): %s" , len (tool_calls ), ", " .join (tool_names_called )
271+ )
285272
286273 # Add the assistant's message with tool calls to conversation
287274 messages .append (
@@ -305,7 +292,7 @@ async def analyze_with_gemini_agentic(
305292 # Execute each tool call and add results to messages
306293 for tool_call in tool_calls :
307294 function_name = tool_call .function .name
308-
295+
309296 try :
310297 function_args = json .loads (tool_call .function .arguments )
311298 except json .JSONDecodeError as e :
@@ -330,9 +317,11 @@ async def analyze_with_gemini_agentic(
330317 # Continue loop to let Gemini process tool results
331318
332319 # If we hit max iterations without a final answer
333- logger .warning ("Reached maximum iterations (%d) without final answer" , max_iterations )
320+ logger .warning (
321+ "Reached maximum iterations (%d) without final answer" , max_iterations
322+ )
334323 return "Analysis incomplete: Maximum tool calling iterations reached. Please try again with a simpler query."
335-
324+
336325 except Exception as e :
337326 logger .error ("Error in Gemini agentic loop: %s" , str (e ), exc_info = True )
338327 raise InferenceAPIUnavailableError (
@@ -346,7 +335,7 @@ async def analyze_log_with_gemini(
346335 error_summary : str ,
347336 model = "gemini-2.0-flash" ,
348337 tools = None ,
349- max_iterations = None
338+ max_iterations = None ,
350339):
351340 """
352341 Analyzes log summaries using Gemini API with product-specific prompts and optional tool calling.
@@ -361,7 +350,7 @@ async def analyze_log_with_gemini(
361350 """
362351 try :
363352 logger .info ("Starting log analysis for product: %s" , product )
364-
353+
365354 prompt_config = product_config ["prompt" ][product ]
366355 try :
367356 formatted_content = prompt_config ["user" ].format (
@@ -370,7 +359,10 @@ async def analyze_log_with_gemini(
370359 except KeyError :
371360 formatted_content = prompt_config ["user" ].format (summary = error_summary )
372361
373- logger .debug ("Error summary: %s" , error_summary [:150 ] + "..." if len (error_summary ) > 150 else error_summary )
362+ logger .debug (
363+ "Error summary: %s" ,
364+ error_summary [:150 ] + "..." if len (error_summary ) > 150 else error_summary ,
365+ )
374366
375367 messages = [
376368 {"role" : "system" , "content" : prompt_config ["system" ]},
@@ -380,10 +372,7 @@ async def analyze_log_with_gemini(
380372
381373 # Use the generic agentic loop
382374 return await analyze_with_gemini_agentic (
383- messages = messages ,
384- tools = tools ,
385- model = model ,
386- max_iterations = max_iterations
375+ messages = messages , tools = tools , model = model , max_iterations = max_iterations
387376 )
388377
389378 except Exception as e :
0 commit comments