jesusvilela commited on
Commit
3cf6d34
·
verified ·
1 Parent(s): 376713a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +42 -45
app.py CHANGED
@@ -39,7 +39,7 @@ try: import whisper; WHISPER_AVAILABLE = True
39
  except ImportError: WHISPER_AVAILABLE = False; print("WARNING: OpenAI Whisper not found, Audio Transcription tool will be disabled.")
40
 
41
  # Google GenAI (Used by LangChain integration AND direct client)
42
- from google.genai.types import HarmCategory, HarmBlockThreshold
43
  from google.ai import generativelanguage as glm # For FileState enum
44
 
45
  # LangChain
@@ -62,7 +62,7 @@ LANGGRAPH_FLAVOR_AVAILABLE = False
62
  LG_StateGraph: Optional[Type[Any]] = None
63
  LG_ToolExecutor_Class: Optional[Type[Any]] = None
64
  LG_END: Optional[Any] = None
65
- LG_ToolInvocation: Optional[Type[Any]] = None
66
  add_messages: Optional[Any] = None
67
  MemorySaver_Class: Optional[Type[Any]] = None
68
 
@@ -83,27 +83,33 @@ try:
83
  print("Using langgraph.prebuilt.ToolNode for LangGraph tool execution.")
84
  except ImportError:
85
  try:
86
- from langgraph.prebuilt import ToolExecutor
87
  LG_ToolExecutor_Class = ToolExecutor
88
  print("Using langgraph.prebuilt.ToolExecutor (fallback) for LangGraph tool execution.")
89
  except ImportError as e_lg_exec_inner:
90
  print(f"Failed to import ToolNode and ToolExecutor from langgraph.prebuilt: {e_lg_exec_inner}")
91
  LG_ToolExecutor_Class = None
92
 
93
- if LG_ToolExecutor_Class is not None:
 
94
  from langgraph.prebuilt import ToolInvocation as LGToolInvocationActual
 
 
 
 
 
95
  from langgraph.graph.message import add_messages as lg_add_messages
96
  from langgraph.checkpoint.memory import MemorySaver as LGMemorySaver
97
  LANGGRAPH_FLAVOR_AVAILABLE = True
98
  LG_StateGraph, LG_END, LG_ToolInvocation, add_messages, MemorySaver_Class = \
99
  StateGraph, END, LGToolInvocationActual, lg_add_messages, LGMemorySaver
100
- print("Successfully imported LangGraph components.")
101
  else:
102
  LANGGRAPH_FLAVOR_AVAILABLE = False
103
- LG_StateGraph, LG_END, LG_ToolInvocation, add_messages, MemorySaver_Class = (None,) * 5
104
- print(f"WARNING: No suitable LangGraph tool executor (ToolNode/ToolExecutor) found. LangGraph agent will be disabled.")
105
 
106
- except ImportError as e:
107
  LANGGRAPH_FLAVOR_AVAILABLE = False
108
  LG_StateGraph, LG_ToolExecutor_Class, LG_END, LG_ToolInvocation, add_messages, MemorySaver_Class = (None,) * 6
109
  print(f"WARNING: Core LangGraph components (StateGraph, END) not found or import error: {e}. LangGraph agent will be disabled.")
@@ -142,7 +148,6 @@ else:
142
 
143
  # --- Helper Functions (Unchanged) ---
144
  def _strip_exact_match_answer(text: Any) -> str:
145
- # ... (Your original _strip_exact_match_answer function)
146
  if not isinstance(text, str): text = str(text)
147
  text_lower_check = text.lower()
148
  if text_lower_check.startswith("final answer:"):
@@ -160,17 +165,14 @@ def _strip_exact_match_answer(text: Any) -> str:
160
  return text.strip()
161
 
162
  def _is_full_url(url_string: str) -> bool:
163
- # ... (Your original _is_full_url function)
164
  try: result = urlparse(url_string); return all([result.scheme, result.netloc])
165
  except ValueError: return False
166
 
167
  def _is_youtube_url(url: str) -> bool:
168
- # ... (Your original _is_youtube_url function)
169
  parsed_url = urlparse(url)
170
  return parsed_url.netloc.lower().endswith(("youtube.com", "youtu.be"))
171
 
172
  def _download_file(file_identifier: str, task_id_for_file: Optional[str] = None) -> str:
173
- # ... (Your original _download_file function - unchanged) ...
174
  os.makedirs(LOCAL_FILE_STORE_PATH, exist_ok=True)
175
  logger.debug(f"Download request: '{file_identifier}', task_id: {task_id_for_file}")
176
  original_filename = os.path.basename(urlparse(file_identifier).path) if _is_full_url(file_identifier) else os.path.basename(file_identifier)
@@ -244,7 +246,7 @@ def _download_file(file_identifier: str, task_id_for_file: Optional[str] = None)
244
  name_without_ext, current_ext = os.path.splitext(effective_save_path)
245
  if not current_ext:
246
  content_type_header = r.headers.get('content-type', '')
247
- content_type_val = content_type_header.split(';')[0].strip() if content_type_header else ''
248
  if content_type_val:
249
  guessed_ext = mimetypes.guess_extension(content_type_val)
250
  if guessed_ext: effective_save_path += guessed_ext; logger.info(f"Added guessed ext: {guessed_ext}")
@@ -380,27 +382,18 @@ def initialize_agent_and_tools(force_reinit=False):
380
  logger.info("Initializing agent and tools...")
381
  if not GOOGLE_API_KEY: raise ValueError("GOOGLE_API_KEY not set for LangChain LLM.")
382
 
383
- # Using INTEGER VALUES for HarmCategory keys and HarmBlockThreshold enum .value for values.
384
- llm_safety_settings_corrected_final = {
385
- HarmCategory.HARM_CATEGORY_HARASSMENT.value: HarmBlockThreshold.BLOCK_NONE.value,
386
- HarmCategory.HARM_CATEGORY_HATE_SPEECH.value: HarmBlockThreshold.BLOCK_NONE.value,
387
- HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT.value: HarmBlockThreshold.BLOCK_NONE.value,
388
- HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT.value: HarmBlockThreshold.BLOCK_NONE.value,
389
- }
390
-
391
  try:
392
  LLM_INSTANCE = ChatGoogleGenerativeAI(
393
  model=GEMINI_MODEL_NAME,
394
  google_api_key=GOOGLE_API_KEY,
395
  temperature=0.0,
396
- #safety_settings=llm_safety_settings_corrected_final,
397
  timeout=120,
398
- convert_system_message_to_human=True # This flag might be interacting with how system prompts are handled
399
  )
400
- logger.info(f"LangChain LLM (Planner) initialized: {GEMINI_MODEL_NAME}")
401
  except Exception as e:
402
- logger.error(f"LangChain LLM init failed: {e}", exc_info=True)
403
- logger.error(f"Type of safety_settings attempted: {type(llm_safety_settings_corrected_final)}, content: {llm_safety_settings_corrected_final}")
404
  raise
405
 
406
  TOOLS = []
@@ -426,27 +419,28 @@ def initialize_agent_and_tools(force_reinit=False):
426
  tools="\n".join([f"- {t.name}: {t.description}" for t in TOOLS]), input="{input}"
427
  )
428
  def agent_node(state: AgentState):
429
- current_input = state.get('input', '')
430
- # The LANGGRAPH_PROMPT_TEMPLATE_STR serves as the system message, with the current task input.
431
- system_message_content = prompt_content_lg_init.replace("{input}", current_input)
432
 
433
- # Messages for LLM: System prompt followed by history
434
- messages_for_llm = [SystemMessage(content=system_message_content)] + state.get('messages', [])
 
435
 
436
- logger.debug(f"LangGraph agent_node - messages_for_llm: {messages_for_llm}")
437
- if not messages_for_llm or (isinstance(messages_for_llm[0], SystemMessage) and not messages_for_llm[0].content.strip()):
438
- logger.error("LLM call would fail: first message is SystemMessage with no/empty content or messages_for_llm is empty.")
439
- return {"messages": [AIMessage(content="[ERROR] Agent node: System message content is empty.")]}
440
 
441
- bound_llm = LLM_INSTANCE.bind_tools(TOOLS) # type: ignore
442
- response = bound_llm.invoke(messages_for_llm)
443
- return {"messages": [response]}
444
 
445
  if not LG_ToolExecutor_Class: raise ValueError("LG_ToolExecutor_Class is None for LangGraph.")
446
  tool_executor_instance_lg = LG_ToolExecutor_Class(tools=TOOLS)
447
 
448
 
449
  def tool_node(state: AgentState):
 
450
  last_msg = state['messages'][-1] if state.get('messages') and isinstance(state['messages'][-1], AIMessage) else None
451
  if not last_msg or not last_msg.tool_calls: return {"messages": []}
452
  tool_results = []
@@ -466,9 +460,10 @@ def initialize_agent_and_tools(force_reinit=False):
466
  tool_results.append(ToolMessage(content=f"Error for tool {name}: {str(e_tool_node_lg)}", tool_call_id=tc_id, name=name))
467
  return {"messages": tool_results}
468
 
 
469
  workflow_lg = LG_StateGraph(AgentState) # type: ignore
470
  workflow_lg.add_node("agent", agent_node)
471
- workflow_lg.add_node("tools", tool_node)
472
  workflow_lg.set_entry_point("agent")
473
  def should_continue_lg(state: AgentState): return "tools" if state['messages'][-1].tool_calls else LG_END
474
  workflow_lg.add_conditional_edges("agent", should_continue_lg, {"tools": "tools", LG_END: LG_END}) # type: ignore
@@ -513,7 +508,6 @@ def get_agent_response(prompt: str, task_id: Optional[str]=None, thread_id: Opti
513
  try:
514
  if is_langgraph_agent_get:
515
  logger.debug(f"Using LangGraph agent (Memory: {LANGGRAPH_MEMORY_SAVER is not None}) for thread: {thread_id_to_use}")
516
- # The 'input' for LangGraph state is the fully constructed prompt for the task
517
  input_for_lg_get = {"input": prompt, "messages": []}
518
  final_state_lg_get = AGENT_INSTANCE.invoke(input_for_lg_get, {"configurable": {"thread_id": thread_id_to_use}}) # type: ignore
519
  if not final_state_lg_get or 'messages' not in final_state_lg_get or not final_state_lg_get['messages']:
@@ -523,10 +517,13 @@ def get_agent_response(prompt: str, task_id: Optional[str]=None, thread_id: Opti
523
  return str(message_item_lg_get.content)
524
  logger.warning("LangGraph: No suitable final AIMessage without tool_calls.")
525
  return str(final_state_lg_get['messages'][-1].content) if final_state_lg_get['messages'] else "[ERROR] LangGraph: Empty messages."
526
- elif isinstance(AGENT_INSTANCE, AgentExecutor):
527
- logger.debug("Using ReAct agent.")
528
- response_react_get = AGENT_INSTANCE.invoke({"input": prompt})
529
- return str(response_react_get.get("output", "[ERROR] ReAct: No 'output' key."))
 
 
 
530
  else:
531
  logger.error(f"Unknown agent type: {agent_name_get}"); return f"[ERROR] Unknown agent type: {agent_name_get}"
532
  except Exception as e_agent_run_get:
@@ -633,7 +630,7 @@ with gr.Blocks(css=".gradio-container {max-width:1280px !important;margin:auto !
633
  demo.load(update_ui_on_load_fn_within_context, [], [agent_status_display, missing_secrets_display])
634
 
635
  if __name__ == "__main__":
636
- logger.info(f"Application starting up (v7 - Corrected HarmCategory/BlockThreshold Import & SafetySettings format)...")
637
  if not PYPDF2_AVAILABLE: logger.warning("PyPDF2 (PDF tool) NOT AVAILABLE.")
638
  if not PIL_TESSERACT_AVAILABLE: logger.warning("Pillow/Pytesseract (OCR tool) NOT AVAILABLE.")
639
  if not WHISPER_AVAILABLE: logger.warning("Whisper (Audio tool) NOT AVAILABLE.")
 
39
  except ImportError: WHISPER_AVAILABLE = False; print("WARNING: OpenAI Whisper not found, Audio Transcription tool will be disabled.")
40
 
41
  # Google GenAI (Used by LangChain integration AND direct client)
42
+ from google.genai.types import HarmCategory, HarmBlockThreshold # Correct import
43
  from google.ai import generativelanguage as glm # For FileState enum
44
 
45
  # LangChain
 
62
  LG_StateGraph: Optional[Type[Any]] = None
63
  LG_ToolExecutor_Class: Optional[Type[Any]] = None
64
  LG_END: Optional[Any] = None
65
+ LG_ToolInvocation: Optional[Type[Any]] = None # This import might fail based on logs
66
  add_messages: Optional[Any] = None
67
  MemorySaver_Class: Optional[Type[Any]] = None
68
 
 
83
  print("Using langgraph.prebuilt.ToolNode for LangGraph tool execution.")
84
  except ImportError:
85
  try:
86
+ from langgraph.prebuilt import ToolExecutor # This was failing in logs
87
  LG_ToolExecutor_Class = ToolExecutor
88
  print("Using langgraph.prebuilt.ToolExecutor (fallback) for LangGraph tool execution.")
89
  except ImportError as e_lg_exec_inner:
90
  print(f"Failed to import ToolNode and ToolExecutor from langgraph.prebuilt: {e_lg_exec_inner}")
91
  LG_ToolExecutor_Class = None
92
 
93
+ # This ToolInvocation import was also failing based on logs
94
+ try:
95
  from langgraph.prebuilt import ToolInvocation as LGToolInvocationActual
96
+ except ImportError as e_tool_inv:
97
+ print(f"WARNING: Failed to import ToolInvocation from langgraph.prebuilt: {e_tool_inv}")
98
+ LGToolInvocationActual = None # type: ignore
99
+
100
+ if LG_ToolExecutor_Class is not None and LGToolInvocationActual is not None:
101
  from langgraph.graph.message import add_messages as lg_add_messages
102
  from langgraph.checkpoint.memory import MemorySaver as LGMemorySaver
103
  LANGGRAPH_FLAVOR_AVAILABLE = True
104
  LG_StateGraph, LG_END, LG_ToolInvocation, add_messages, MemorySaver_Class = \
105
  StateGraph, END, LGToolInvocationActual, lg_add_messages, LGMemorySaver
106
+ print("Successfully imported essential LangGraph components.")
107
  else:
108
  LANGGRAPH_FLAVOR_AVAILABLE = False
109
+ LG_StateGraph, LG_END, LG_ToolInvocation, add_messages, MemorySaver_Class = (None,) * 5 # type: ignore
110
+ print(f"WARNING: One or more LangGraph components (ToolExecutor/Node or ToolInvocation) not found. LangGraph agent will be disabled.")
111
 
112
+ except ImportError as e: # Catch import error for StateGraph, END itself
113
  LANGGRAPH_FLAVOR_AVAILABLE = False
114
  LG_StateGraph, LG_ToolExecutor_Class, LG_END, LG_ToolInvocation, add_messages, MemorySaver_Class = (None,) * 6
115
  print(f"WARNING: Core LangGraph components (StateGraph, END) not found or import error: {e}. LangGraph agent will be disabled.")
 
148
 
149
  # --- Helper Functions (Unchanged) ---
150
  def _strip_exact_match_answer(text: Any) -> str:
 
151
  if not isinstance(text, str): text = str(text)
152
  text_lower_check = text.lower()
153
  if text_lower_check.startswith("final answer:"):
 
165
  return text.strip()
166
 
167
  def _is_full_url(url_string: str) -> bool:
 
168
  try: result = urlparse(url_string); return all([result.scheme, result.netloc])
169
  except ValueError: return False
170
 
171
  def _is_youtube_url(url: str) -> bool:
 
172
  parsed_url = urlparse(url)
173
  return parsed_url.netloc.lower().endswith(("youtube.com", "youtu.be"))
174
 
175
  def _download_file(file_identifier: str, task_id_for_file: Optional[str] = None) -> str:
 
176
  os.makedirs(LOCAL_FILE_STORE_PATH, exist_ok=True)
177
  logger.debug(f"Download request: '{file_identifier}', task_id: {task_id_for_file}")
178
  original_filename = os.path.basename(urlparse(file_identifier).path) if _is_full_url(file_identifier) else os.path.basename(file_identifier)
 
246
  name_without_ext, current_ext = os.path.splitext(effective_save_path)
247
  if not current_ext:
248
  content_type_header = r.headers.get('content-type', '')
249
+ content_type_val = content_type_header.split(';').strip() if content_type_header else ''
250
  if content_type_val:
251
  guessed_ext = mimetypes.guess_extension(content_type_val)
252
  if guessed_ext: effective_save_path += guessed_ext; logger.info(f"Added guessed ext: {guessed_ext}")
 
382
  logger.info("Initializing agent and tools...")
383
  if not GOOGLE_API_KEY: raise ValueError("GOOGLE_API_KEY not set for LangChain LLM.")
384
 
 
 
 
 
 
 
 
 
385
  try:
386
  LLM_INSTANCE = ChatGoogleGenerativeAI(
387
  model=GEMINI_MODEL_NAME,
388
  google_api_key=GOOGLE_API_KEY,
389
  temperature=0.0,
390
+ # safety_settings=... # Temporarily removed to isolate 'contents' error
391
  timeout=120,
392
+ convert_system_message_to_human=False # Set to False as it's deprecated and might cause issues
393
  )
394
+ logger.info(f"LangChain LLM (Planner) initialized: {GEMINI_MODEL_NAME} (Using default safety settings, convert_system_message_to_human=False)")
395
  except Exception as e:
396
+ logger.error(f"LangChain LLM init FAILED: {e}", exc_info=True)
 
397
  raise
398
 
399
  TOOLS = []
 
419
  tools="\n".join([f"- {t.name}: {t.description}" for t in TOOLS]), input="{input}"
420
  )
421
  def agent_node(state: AgentState):
422
+ current_task_input_lg = state.get('input', '')
423
+ system_message_content_lg = prompt_content_lg_init.replace("{input}", current_task_input_lg)
 
424
 
425
+ # Ensure the first message passed to LLM is appropriate
426
+ # If convert_system_message_to_human is False, Gemini can often take a SystemMessage first.
427
+ messages_for_llm_lg = [SystemMessage(content=system_message_content_lg)] + state.get('messages', [])
428
 
429
+ logger.debug(f"LangGraph agent_node - messages_for_llm: {messages_for_llm_lg}")
430
+ if not messages_for_llm_lg or not messages_for_llm_lg.content.strip():
431
+ logger.error("LLM call would fail in agent_node: first message is empty or no messages.")
432
+ return {"messages": [AIMessage(content="[ERROR] Agent node received no content to process.")]}
433
 
434
+ bound_llm_lg = LLM_INSTANCE.bind_tools(TOOLS) # type: ignore
435
+ response_lg = bound_llm_lg.invoke(messages_for_llm_lg)
436
+ return {"messages": [response_lg]}
437
 
438
  if not LG_ToolExecutor_Class: raise ValueError("LG_ToolExecutor_Class is None for LangGraph.")
439
  tool_executor_instance_lg = LG_ToolExecutor_Class(tools=TOOLS)
440
 
441
 
442
  def tool_node(state: AgentState):
443
+ # ... (tool_node logic from previous version - should be okay if ToolNode/ToolExecutor is correctly instantiated)
444
  last_msg = state['messages'][-1] if state.get('messages') and isinstance(state['messages'][-1], AIMessage) else None
445
  if not last_msg or not last_msg.tool_calls: return {"messages": []}
446
  tool_results = []
 
460
  tool_results.append(ToolMessage(content=f"Error for tool {name}: {str(e_tool_node_lg)}", tool_call_id=tc_id, name=name))
461
  return {"messages": tool_results}
462
 
463
+
464
  workflow_lg = LG_StateGraph(AgentState) # type: ignore
465
  workflow_lg.add_node("agent", agent_node)
466
+ workflow_lg.add_node("tools", tool_node)
467
  workflow_lg.set_entry_point("agent")
468
  def should_continue_lg(state: AgentState): return "tools" if state['messages'][-1].tool_calls else LG_END
469
  workflow_lg.add_conditional_edges("agent", should_continue_lg, {"tools": "tools", LG_END: LG_END}) # type: ignore
 
508
  try:
509
  if is_langgraph_agent_get:
510
  logger.debug(f"Using LangGraph agent (Memory: {LANGGRAPH_MEMORY_SAVER is not None}) for thread: {thread_id_to_use}")
 
511
  input_for_lg_get = {"input": prompt, "messages": []}
512
  final_state_lg_get = AGENT_INSTANCE.invoke(input_for_lg_get, {"configurable": {"thread_id": thread_id_to_use}}) # type: ignore
513
  if not final_state_lg_get or 'messages' not in final_state_lg_get or not final_state_lg_get['messages']:
 
517
  return str(message_item_lg_get.content)
518
  logger.warning("LangGraph: No suitable final AIMessage without tool_calls.")
519
  return str(final_state_lg_get['messages'][-1].content) if final_state_lg_get['messages'] else "[ERROR] LangGraph: Empty messages."
520
+ elif isinstance(AGENT_INSTANCE, AgentExecutor): # ReAct agent
521
+ logger.debug("Using ReAct agent for get_agent_response.")
522
+ react_input = {"input": prompt}
523
+ logger.debug(f"ReAct input: {react_input}")
524
+ response_react_get = AGENT_INSTANCE.invoke(react_input)
525
+ logger.debug(f"ReAct response: {response_react_get}")
526
+ return str(response_react_get.get("output", "[ERROR] ReAct: No 'output' key in response."))
527
  else:
528
  logger.error(f"Unknown agent type: {agent_name_get}"); return f"[ERROR] Unknown agent type: {agent_name_get}"
529
  except Exception as e_agent_run_get:
 
630
  demo.load(update_ui_on_load_fn_within_context, [], [agent_status_display, missing_secrets_display])
631
 
632
  if __name__ == "__main__":
633
+ logger.info(f"Application starting up (v7 - Corrected GenAI Types Import & Removed Safety Settings from LLM)...")
634
  if not PYPDF2_AVAILABLE: logger.warning("PyPDF2 (PDF tool) NOT AVAILABLE.")
635
  if not PIL_TESSERACT_AVAILABLE: logger.warning("Pillow/Pytesseract (OCR tool) NOT AVAILABLE.")
636
  if not WHISPER_AVAILABLE: logger.warning("Whisper (Audio tool) NOT AVAILABLE.")