Scott Cogan commited on
Commit
d541b87
·
1 Parent(s): 9aa8d3a

requirements update for llm compat

Browse files
Files changed (1) hide show
  1. app.py +129 -160
app.py CHANGED
@@ -286,148 +286,138 @@ class BasicAgent:
286
 
287
  logger.info("BasicAgent initialized with fallback LLM support.")
288
 
289
- @retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=4, max=60))
290
- def call_model(self, state: AgentState) -> AgentState:
291
- """Call the model to generate a response with retry logic and fallback support."""
292
- try:
293
- messages = state["messages"]
294
- logger.info("\n=== Model Input ===")
295
- log_message(self.sys_msg, " ")
296
- for msg in messages:
297
- log_message(msg, " ")
298
-
299
- # Try primary LLM first
300
  try:
301
- response = self.primary_llm.invoke(
302
- [self.sys_msg] + messages,
303
- tools=[{"type": "function", "function": {
304
- "name": "google_search",
305
- "description": "Search for information on the web",
306
- "parameters": {
307
- "type": "object",
308
- "properties": {
309
- "query": {
310
- "type": "string",
311
- "description": "The search query"
312
- }
313
- },
314
- "required": ["query"]
315
- }
316
- }}]
317
- )
318
- except Exception as e:
319
- error_str = str(e)
320
- if "429" in error_str:
321
- if "GenerateRequestsPerDayPerProjectPerModel-FreeTier" in error_str:
322
- logger.warning("Daily quota limit reached for primary LLM, trying fallback")
323
- if hasattr(self, 'fallback_llm') and self.fallback_llm is not None:
324
- try:
325
- response = self.fallback_llm.invoke(
326
- [self.sys_msg] + messages,
327
- tools=[{"type": "function", "function": {
328
- "name": "google_search",
329
- "description": "Search for information on the web",
330
- "parameters": {
331
- "type": "object",
332
- "properties": {
333
- "query": {
334
- "type": "string",
335
- "description": "The search query"
336
- }
337
- },
338
- "required": ["query"]
339
- }
340
- }}]
341
- )
342
- logger.info("Successfully used fallback LLM")
343
- except Exception as fallback_error:
344
- logger.error(f"Fallback LLM also failed: {str(fallback_error)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
345
  return {
346
- "messages": [AIMessage(content="All LLM services are currently unavailable. Please try again later.")],
347
  "next": END
348
  }
349
  else:
350
- logger.warning("No fallback LLM available")
351
- return {
352
- "messages": [AIMessage(content="I've reached my daily limit for processing requests. Please try again tomorrow or contact support for assistance.")],
353
- "next": END
354
- }
355
  else:
356
- # For other rate limits, wait and retry
357
- wait_time = 60
358
- logger.warning(f"Rate limit hit, waiting {wait_time} seconds before retry...")
359
- time.sleep(wait_time)
360
- raise # Re-raise to trigger retry
361
- else:
362
- raise
363
-
364
- logger.info("\n=== Model Output ===")
365
- log_message(response, " ")
366
-
367
- if not response or not response.content:
368
- logger.error("Empty response from model")
369
- raise ValueError("Empty response from model")
370
-
371
- # Check if the response contains a tool call
372
- if hasattr(response, 'tool_calls') and response.tool_calls:
373
- return {"messages": [response], "next": "tools"}
374
- else:
375
- # If no tool call, check if it's a final answer
376
- content = response.content.strip()
377
 
378
- # Clean up the content to ensure it's in the correct format
379
- if content.startswith("**Final Answer**: "):
380
- content = content.replace("**Final Answer**: ", "").strip()
381
 
382
- # For numbers, ensure they're in the correct format
383
- if content.replace(".", "").isdigit():
384
- # Remove any decimal places for whole numbers
385
- if float(content).is_integer():
386
- content = str(int(float(content)))
387
 
388
- # Check if the content is a valid final answer
389
- if content.isdigit() or (content.startswith('[') and content.endswith(']')):
390
- return {"messages": [AIMessage(content=content)], "next": END}
391
  else:
392
- # If not a final answer, continue the conversation
393
- return {"messages": [response], "next": "agent"}
394
 
395
- except Exception as e:
396
- logger.error(f"Error in call_model: {str(e)}")
397
- error_str = str(e)
398
- if "429" in error_str:
399
- if "GenerateRequestsPerDayPerProjectPerModel-FreeTier" in error_str:
400
- logger.warning("Daily quota limit reached, trying fallback")
401
- if hasattr(self, 'fallback_llm') and self.fallback_llm is not None:
402
- try:
403
- response = self.fallback_llm.invoke(
404
- [self.sys_msg] + messages,
405
- tools=[{"type": "function", "function": {
406
- "name": "google_search",
407
- "description": "Search for information on the web",
408
- "parameters": {
409
- "type": "object",
410
- "properties": {
411
- "query": {
412
- "type": "string",
413
- "description": "The search query"
414
- }
415
- },
416
- "required": ["query"]
417
- }
418
- }}]
419
- )
420
- return {"messages": [response], "next": "tools"}
421
- except Exception as fallback_error:
422
- logger.error(f"Fallback LLM also failed: {str(fallback_error)}")
423
- return {"messages": [AIMessage(content="All LLM services are currently unavailable. Please try again later.")], "next": END}
424
  else:
425
- return {"messages": [AIMessage(content="I've reached my daily limit for processing requests. Please try again tomorrow or contact support for assistance.")], "next": END}
 
 
 
 
 
 
 
 
 
426
  else:
427
- logger.warning("Rate limit hit, waiting before retry...")
428
- time.sleep(60) # Wait for 60 seconds before retry
429
- raise # Re-raise to trigger retry
430
- raise # Re-raise other exceptions to trigger retry
 
 
 
 
 
 
 
 
 
 
431
 
432
  def call_tools(self, state: AgentState) -> AgentState:
433
  """Call the tools based on the model's response."""
@@ -476,38 +466,17 @@ class BasicAgent:
476
  }
477
 
478
  # Process through the graph with retry logic
479
- max_retries = 3
480
- retry_count = 0
481
- last_error = None
482
-
483
- while retry_count < max_retries:
484
- try:
485
- logger.info(f"\n=== Attempt {retry_count + 1}/{max_retries} ===")
486
- result = self.app.invoke(initial_state)
487
- final_message = result["messages"][-1]
488
-
489
- if isinstance(final_message, AIMessage) and final_message.content:
490
- logger.info(f"\n=== Final Answer ===")
491
- logger.info(f"Answer: {final_message.content}")
492
- return final_message.content
493
- else:
494
- logger.error("Empty or invalid response")
495
- raise ValueError("Empty or invalid response")
496
-
497
- except Exception as e:
498
- last_error = e
499
- retry_count += 1
500
- if "429" in str(e):
501
- wait_time = 60 * retry_count
502
- logger.warning(f"Rate limit hit, waiting {wait_time} seconds before retry {retry_count}/{max_retries}")
503
- await asyncio.sleep(wait_time)
504
- else:
505
- logger.error(f"Error in processing, retry {retry_count}/{max_retries}: {str(e)}")
506
- await asyncio.sleep(5)
507
-
508
- logger.error(f"All retries failed. Last error: {str(last_error)}")
509
- return "Unable to generate answer after multiple attempts"
510
 
 
 
 
 
 
 
 
 
511
  except Exception as e:
512
  logger.error(f"Fatal error in agent: {str(e)}")
513
  return f"Error: {str(e)}"
 
286
 
287
  logger.info("BasicAgent initialized with fallback LLM support.")
288
 
289
+ def _call_model_with_retry(self, state: AgentState) -> AgentState:
290
+ """Internal method to handle retries for model calls."""
291
+ max_retries = 3
292
+ retry_count = 0
293
+ last_error = None
294
+
295
+ while retry_count < max_retries:
 
 
 
 
296
  try:
297
+ messages = state["messages"]
298
+ logger.info("\n=== Model Input ===")
299
+ log_message(self.sys_msg, " ")
300
+ for msg in messages:
301
+ log_message(msg, " ")
302
+
303
+ # Try primary LLM first
304
+ try:
305
+ response = self.primary_llm.invoke(
306
+ [self.sys_msg] + messages,
307
+ tools=[{"type": "function", "function": {
308
+ "name": "google_search",
309
+ "description": "Search for information on the web",
310
+ "parameters": {
311
+ "type": "object",
312
+ "properties": {
313
+ "query": {
314
+ "type": "string",
315
+ "description": "The search query"
316
+ }
317
+ },
318
+ "required": ["query"]
319
+ }
320
+ }}]
321
+ )
322
+ except Exception as e:
323
+ error_str = str(e)
324
+ if "429" in error_str:
325
+ if "GenerateRequestsPerDayPerProjectPerModel-FreeTier" in error_str:
326
+ logger.warning("Daily quota limit reached for primary LLM, trying fallback")
327
+ if hasattr(self, 'fallback_llm') and self.fallback_llm is not None:
328
+ try:
329
+ response = self.fallback_llm.invoke(
330
+ [self.sys_msg] + messages,
331
+ tools=[{"type": "function", "function": {
332
+ "name": "google_search",
333
+ "description": "Search for information on the web",
334
+ "parameters": {
335
+ "type": "object",
336
+ "properties": {
337
+ "query": {
338
+ "type": "string",
339
+ "description": "The search query"
340
+ }
341
+ },
342
+ "required": ["query"]
343
+ }
344
+ }}]
345
+ )
346
+ logger.info("Successfully used fallback LLM")
347
+ except Exception as fallback_error:
348
+ logger.error(f"Fallback LLM also failed: {str(fallback_error)}")
349
+ return {
350
+ "messages": [AIMessage(content="All LLM services are currently unavailable. Please try again later.")],
351
+ "next": END
352
+ }
353
+ else:
354
+ logger.warning("No fallback LLM available")
355
  return {
356
+ "messages": [AIMessage(content="I've reached my daily limit for processing requests. Please try again tomorrow or contact support for assistance.")],
357
  "next": END
358
  }
359
  else:
360
+ # For other rate limits, wait and retry
361
+ wait_time = 60 * (retry_count + 1) # Exponential backoff
362
+ logger.warning(f"Rate limit hit, waiting {wait_time} seconds before retry...")
363
+ time.sleep(wait_time)
364
+ raise # Re-raise to trigger retry
365
  else:
366
+ raise
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
367
 
368
+ logger.info("\n=== Model Output ===")
369
+ log_message(response, " ")
 
370
 
371
+ if not response or not response.content:
372
+ logger.error("Empty response from model")
373
+ raise ValueError("Empty response from model")
 
 
374
 
375
+ # Check if the response contains a tool call
376
+ if hasattr(response, 'tool_calls') and response.tool_calls:
377
+ return {"messages": [response], "next": "tools"}
378
  else:
379
+ # If no tool call, check if it's a final answer
380
+ content = response.content.strip()
381
 
382
+ # Clean up the content to ensure it's in the correct format
383
+ if content.startswith("**Final Answer**: "):
384
+ content = content.replace("**Final Answer**: ", "").strip()
385
+
386
+ # For numbers, ensure they're in the correct format
387
+ if content.replace(".", "").isdigit():
388
+ # Remove any decimal places for whole numbers
389
+ if float(content).is_integer():
390
+ content = str(int(float(content)))
391
+
392
+ # Check if the content is a valid final answer
393
+ if content.isdigit() or (content.startswith('[') and content.endswith(']')):
394
+ return {"messages": [AIMessage(content=content)], "next": END}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
395
  else:
396
+ # If not a final answer, continue the conversation
397
+ return {"messages": [response], "next": "agent"}
398
+
399
+ except Exception as e:
400
+ last_error = e
401
+ retry_count += 1
402
+ logger.error(f"Error in processing, retry {retry_count}/{max_retries}: {str(e)}")
403
+ if retry_count < max_retries:
404
+ wait_time = 5 * retry_count # Simple backoff
405
+ time.sleep(wait_time)
406
  else:
407
+ logger.error(f"All retries failed. Last error: {str(last_error)}")
408
+ return {
409
+ "messages": [AIMessage(content="Unable to generate answer after multiple attempts. Please try again later.")],
410
+ "next": END
411
+ }
412
+
413
+ return {
414
+ "messages": [AIMessage(content="Unable to generate answer after multiple attempts. Please try again later.")],
415
+ "next": END
416
+ }
417
+
418
+ def call_model(self, state: AgentState) -> AgentState:
419
+ """Call the model to generate a response with retry logic and fallback support."""
420
+ return self._call_model_with_retry(state)
421
 
422
  def call_tools(self, state: AgentState) -> AgentState:
423
  """Call the tools based on the model's response."""
 
466
  }
467
 
468
  # Process through the graph with retry logic
469
+ result = self.app.invoke(initial_state)
470
+ final_message = result["messages"][-1]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
471
 
472
+ if isinstance(final_message, AIMessage) and final_message.content:
473
+ logger.info(f"\n=== Final Answer ===")
474
+ logger.info(f"Answer: {final_message.content}")
475
+ return final_message.content
476
+ else:
477
+ logger.error("Empty or invalid response")
478
+ raise ValueError("Empty or invalid response")
479
+
480
  except Exception as e:
481
  logger.error(f"Fatal error in agent: {str(e)}")
482
  return f"Error: {str(e)}"