Scott Cogan commited on
Commit
8cfdb1c
·
1 Parent(s): 6373484

requirements update for llm compat

Browse files
Files changed (1) hide show
  1. app.py +128 -162
app.py CHANGED
@@ -209,23 +209,24 @@ def log_message(message: BaseMessage, prefix: str = ""):
209
 
210
  class BasicAgent:
211
  def __init__(self):
212
- # Initialize primary LLM (Gemini)
213
- self.primary_llm = ChatGoogleGenerativeAI(
214
- model="gemini-2.5-flash-preview-05-20",
215
- max_tokens=8192,
216
- temperature=0,
217
- convert_system_message_to_human=True # Enable system message conversion
218
- )
219
-
220
- # Initialize fallback LLM (if available)
221
- self.fallback_llm = None
222
  if os.getenv("OPENAI_API_KEY"):
223
  from langchain_openai import ChatOpenAI
224
- self.fallback_llm = ChatOpenAI(
225
  model="gpt-3.5-turbo",
226
  temperature=0,
227
  max_tokens=4096
228
  )
 
 
 
 
 
 
 
 
 
 
229
 
230
  # Create tool executor
231
  self.tools = {
@@ -301,42 +302,82 @@ class BasicAgent:
301
  for msg in messages:
302
  log_message(msg, " ")
303
 
304
- # Try primary LLM first
305
  try:
306
- # Convert system message to human message for Gemini
307
- if isinstance(self.sys_msg, SystemMessage):
308
- system_content = f"System Instructions: {self.sys_msg.content}"
309
- messages_with_system = [HumanMessage(content=system_content)] + messages
310
- else:
311
- messages_with_system = [self.sys_msg] + messages
312
-
313
- # Create tool configuration for Gemini
314
- genai_tool = {
315
- "function_declarations": [{
316
- "name": "google_search",
317
- "description": "Search for information on the web",
318
- "parameters": {
319
- "type": "object",
320
- "properties": {
321
- "query": {
322
- "type": "string",
323
- "description": "The search query"
324
- }
325
- },
326
- "required": ["query"]
 
327
  }
328
  }]
329
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
330
 
331
- logger.info("Attempting to use primary LLM (Gemini)")
332
  try:
333
- # First try with explicit tool usage prompt
334
- messages_with_tool_prompt = messages_with_system + [
335
- HumanMessage(content="Please use the google_search tool to find information about Mercedes Sosa's studio albums between 2000 and 2009.")
336
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
337
 
338
- response = self.primary_llm.invoke(
339
- messages_with_tool_prompt,
340
  tools=[genai_tool]
341
  )
342
 
@@ -344,99 +385,28 @@ class BasicAgent:
344
  raise ValueError("Invalid response format from Gemini")
345
 
346
  # Check if response contains tool call
347
- if not hasattr(response, 'tool_calls') or not response.tool_calls:
348
- # If no tool call, try without tools
349
- response = self.primary_llm.invoke(messages_with_tool_prompt)
350
- if not response or not hasattr(response, 'content'):
351
- raise ValueError("Invalid response format from Gemini")
352
- logger.info("Successfully used primary LLM without tools")
353
  else:
354
- logger.info("Successfully used primary LLM with tools")
355
-
356
- except Exception as e:
357
- error_str = str(e)
358
- if "429" in error_str:
359
- # Handle rate limit
360
- logger.warning("Rate limit hit for Gemini, waiting before retry...")
361
- time.sleep(60) # Wait 60 seconds before retry
362
- raise
363
- elif "list index out of range" in error_str:
364
- # Try without tools if tool configuration fails
365
- response = self.primary_llm.invoke(messages_with_system)
366
  if not response or not hasattr(response, 'content'):
367
  raise ValueError("Invalid response format from Gemini")
368
- logger.info("Successfully used primary LLM without tools")
369
- else:
370
- raise
371
-
372
- except Exception as e:
373
- error_str = str(e)
374
- logger.error(f"Primary LLM error: {error_str}")
375
-
376
- # Check if we should try fallback
377
- if hasattr(self, 'fallback_llm') and self.fallback_llm is not None:
378
- try:
379
- logger.info("Attempting to use fallback LLM (OpenAI)")
380
- # Add explicit tool usage prompt
381
- messages_with_tool_prompt = [self.sys_msg] + messages + [
382
- HumanMessage(content="Please use the google_search tool to find information about Mercedes Sosa's studio albums between 2000 and 2009.")
383
- ]
384
 
385
- # For OpenAI, we can use the system message directly
386
- response = self.fallback_llm.invoke(
387
- messages_with_tool_prompt,
388
- tools=[{
389
- "type": "function",
390
- "function": {
391
- "name": "google_search",
392
- "description": "Search for information on the web",
393
- "parameters": {
394
- "type": "object",
395
- "properties": {
396
- "query": {
397
- "type": "string",
398
- "description": "The search query"
399
- }
400
- },
401
- "required": ["query"]
402
- }
403
- }
404
- }]
405
- )
406
-
407
- if not response or not hasattr(response, 'content'):
408
- raise ValueError("Invalid response format from fallback LLM")
409
-
410
- # Check if response contains tool call
411
- if not hasattr(response, 'tool_calls') or not response.tool_calls:
412
- # If no tool call, try without tools
413
- response = self.fallback_llm.invoke(messages_with_tool_prompt)
414
- if not response or not hasattr(response, 'content'):
415
- raise ValueError("Invalid response format from fallback LLM")
416
- logger.info("Successfully used fallback LLM without tools")
417
- else:
418
- logger.info("Successfully used fallback LLM with tools")
419
- except Exception as fallback_error:
420
- logger.error(f"Fallback LLM error: {str(fallback_error)}")
421
- if "429" in str(fallback_error):
422
- return {
423
- "messages": [AIMessage(content="All LLM services are currently rate limited. Please try again later.")],
424
- "next": END
425
- }
426
- else:
427
- return {
428
- "messages": [AIMessage(content="All LLM services are currently unavailable. Please try again later.")],
429
- "next": END
430
- }
431
- else:
432
- # If no fallback available or error not related to rate limits
433
- if "429" in error_str:
434
- wait_time = 60 * (retry_count + 1) # Exponential backoff
435
- logger.warning(f"Rate limit hit, waiting {wait_time} seconds before retry...")
436
- time.sleep(wait_time)
437
- raise # Re-raise to trigger retry
438
  else:
439
- raise
 
 
 
440
 
441
  logger.info("\n=== Model Output ===")
442
  log_message(response, " ")
@@ -445,41 +415,37 @@ class BasicAgent:
445
  logger.error("Empty response from model")
446
  raise ValueError("Empty response from model")
447
 
448
- # Check if the response contains a tool call
449
- if hasattr(response, 'tool_calls') and response.tool_calls:
450
- return {"messages": [response], "next": "tools"}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
451
  else:
452
- # If no tool call, check if it's a final answer
453
- content = response.content.strip()
454
-
455
- # If the model is just acknowledging or explaining, prompt it to use the tool
456
- if any(phrase in content.lower() for phrase in ["let me", "i'll", "i will", "sure", "okay", "alright"]):
457
- logger.info("Model provided acknowledgment instead of tool call, prompting for search")
458
- return {
459
- "messages": messages + [
460
- AIMessage(content="Please use the google_search tool to find the information."),
461
- HumanMessage(content="Please search for the information using the google_search tool.")
462
- ],
463
- "next": "agent"
464
- }
465
 
466
- # Clean up the content to ensure it's in the correct format
467
- if content.startswith("**Final Answer**: "):
468
- content = content.replace("**Final Answer**: ", "").strip()
469
-
470
- # For numbers, ensure they're in the correct format
471
- if content.replace(".", "").isdigit():
472
- # Remove any decimal places for whole numbers
473
- if float(content).is_integer():
474
- content = str(int(float(content)))
475
-
476
- # Check if the content is a valid final answer
477
- if content.isdigit() or (content.startswith('[') and content.endswith(']')):
478
- return {"messages": [AIMessage(content=content)], "next": END}
479
- else:
480
- # If not a final answer, continue the conversation
481
- return {"messages": [response], "next": "agent"}
482
-
483
  except Exception as e:
484
  last_error = e
485
  retry_count += 1
 
209
 
210
  class BasicAgent:
211
  def __init__(self):
212
+ # Initialize primary LLM (OpenAI)
 
 
 
 
 
 
 
 
 
213
  if os.getenv("OPENAI_API_KEY"):
214
  from langchain_openai import ChatOpenAI
215
+ self.primary_llm = ChatOpenAI(
216
  model="gpt-3.5-turbo",
217
  temperature=0,
218
  max_tokens=4096
219
  )
220
+ else:
221
+ self.primary_llm = None
222
+
223
+ # Initialize fallback LLM (Gemini)
224
+ self.fallback_llm = ChatGoogleGenerativeAI(
225
+ model="gemini-2.5-flash-preview-05-20",
226
+ max_tokens=8192,
227
+ temperature=0,
228
+ convert_system_message_to_human=True # Enable system message conversion
229
+ )
230
 
231
  # Create tool executor
232
  self.tools = {
 
302
  for msg in messages:
303
  log_message(msg, " ")
304
 
305
+ # Try primary LLM first (OpenAI)
306
  try:
307
+ if self.primary_llm is None:
308
+ raise ValueError("Primary LLM not initialized")
309
+
310
+ logger.info("Attempting to use primary LLM (OpenAI)")
311
+ # For OpenAI, we can use the system message directly
312
+ response = self.primary_llm.invoke(
313
+ [self.sys_msg] + messages,
314
+ tools=[{
315
+ "type": "function",
316
+ "function": {
317
+ "name": "google_search",
318
+ "description": "Search for information on the web",
319
+ "parameters": {
320
+ "type": "object",
321
+ "properties": {
322
+ "query": {
323
+ "type": "string",
324
+ "description": "The search query"
325
+ }
326
+ },
327
+ "required": ["query"]
328
+ }
329
  }
330
  }]
331
+ )
332
+
333
+ if not response or not hasattr(response, 'content'):
334
+ raise ValueError("Invalid response format from OpenAI")
335
+
336
+ # Check if response contains tool call
337
+ if hasattr(response, 'tool_calls') and response.tool_calls:
338
+ logger.info("Successfully used primary LLM with tools")
339
+ return {"messages": [response], "next": "tools"}
340
+ else:
341
+ # If no tool call, try without tools
342
+ response = self.primary_llm.invoke([self.sys_msg] + messages)
343
+ if not response or not hasattr(response, 'content'):
344
+ raise ValueError("Invalid response format from OpenAI")
345
+ logger.info("Successfully used primary LLM without tools")
346
+
347
+ except Exception as e:
348
+ error_str = str(e)
349
+ logger.error(f"Primary LLM error: {error_str}")
350
 
351
+ # Try fallback LLM (Gemini)
352
  try:
353
+ logger.info("Attempting to use fallback LLM (Gemini)")
354
+ # Convert system message to human message for Gemini
355
+ if isinstance(self.sys_msg, SystemMessage):
356
+ system_content = f"System Instructions: {self.sys_msg.content}"
357
+ messages_with_system = [HumanMessage(content=system_content)] + messages
358
+ else:
359
+ messages_with_system = [self.sys_msg] + messages
360
+
361
+ # Create tool configuration for Gemini
362
+ genai_tool = {
363
+ "function_declarations": [{
364
+ "name": "google_search",
365
+ "description": "Search for information on the web",
366
+ "parameters": {
367
+ "type": "object",
368
+ "properties": {
369
+ "query": {
370
+ "type": "string",
371
+ "description": "The search query"
372
+ }
373
+ },
374
+ "required": ["query"]
375
+ }
376
+ }]
377
+ }
378
 
379
+ response = self.fallback_llm.invoke(
380
+ messages_with_system,
381
  tools=[genai_tool]
382
  )
383
 
 
385
  raise ValueError("Invalid response format from Gemini")
386
 
387
  # Check if response contains tool call
388
+ if hasattr(response, 'tool_calls') and response.tool_calls:
389
+ logger.info("Successfully used fallback LLM with tools")
390
+ return {"messages": [response], "next": "tools"}
 
 
 
391
  else:
392
+ # If no tool call, try without tools
393
+ response = self.fallback_llm.invoke(messages_with_system)
 
 
 
 
 
 
 
 
 
 
394
  if not response or not hasattr(response, 'content'):
395
  raise ValueError("Invalid response format from Gemini")
396
+ logger.info("Successfully used fallback LLM without tools")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
397
 
398
+ except Exception as fallback_error:
399
+ logger.error(f"Fallback LLM error: {str(fallback_error)}")
400
+ if "429" in str(fallback_error):
401
+ return {
402
+ "messages": [AIMessage(content="All LLM services are currently rate limited. Please try again later.")],
403
+ "next": END
404
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
405
  else:
406
+ return {
407
+ "messages": [AIMessage(content="All LLM services are currently unavailable. Please try again later.")],
408
+ "next": END
409
+ }
410
 
411
  logger.info("\n=== Model Output ===")
412
  log_message(response, " ")
 
415
  logger.error("Empty response from model")
416
  raise ValueError("Empty response from model")
417
 
418
+ # Process the response content
419
+ content = response.content.strip()
420
+
421
+ # If the model is just acknowledging or explaining, prompt it to use the tool
422
+ if any(phrase in content.lower() for phrase in ["let me", "i'll", "i will", "sure", "okay", "alright"]):
423
+ logger.info("Model provided acknowledgment instead of tool call, prompting for search")
424
+ return {
425
+ "messages": messages + [
426
+ AIMessage(content="Please use the google_search tool to find the information."),
427
+ HumanMessage(content="Please search for the information using the google_search tool.")
428
+ ],
429
+ "next": "agent"
430
+ }
431
+
432
+ # Clean up the content to ensure it's in the correct format
433
+ if content.startswith("**Final Answer**: "):
434
+ content = content.replace("**Final Answer**: ", "").strip()
435
+
436
+ # For numbers, ensure they're in the correct format
437
+ if content.replace(".", "").isdigit():
438
+ # Remove any decimal places for whole numbers
439
+ if float(content).is_integer():
440
+ content = str(int(float(content)))
441
+
442
+ # Check if the content is a valid final answer
443
+ if content.isdigit() or (content.startswith('[') and content.endswith(']')):
444
+ return {"messages": [AIMessage(content=content)], "next": END}
445
  else:
446
+ # If not a final answer, continue the conversation
447
+ return {"messages": [response], "next": "agent"}
 
 
 
 
 
 
 
 
 
 
 
448
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
449
  except Exception as e:
450
  last_error = e
451
  retry_count += 1