adriansanz commited on
Commit
7a97fe6
·
verified ·
1 Parent(s): ad5c359

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +76 -172
app.py CHANGED
@@ -9,6 +9,15 @@ import requests
9
  from typing import Dict
10
  import cv2
11
  from time import sleep
 
 
 
 
 
 
 
 
 
12
 
13
  class GeminiLLM(LLM):
14
  """Wrapper para usar Google Gemini como un LLM de LangChain."""
@@ -422,30 +431,18 @@ def find_non_commutative_pairs(table: Dict[str, Dict[str, str]]) -> List[tuple]:
422
 
423
  # --- Basic Agent Definition ---
424
  # ----- THIS IS WERE YOU CAN BUILD WHAT YOU WANT ------
425
- from langchain_core.prompts import PromptTemplate
426
- from langchain.chains import LLMChain
427
 
428
- import time
429
 
430
- from langchain_core.prompts import PromptTemplate
431
- from langchain.chains import LLMChain
432
- import time
433
- import functools
434
- import hashlib
435
- import json
436
-
437
- import re
438
- import json
439
- import hashlib
440
- import time
441
- from typing import Callable
442
 
443
  class BasicAgent:
444
- def __init__(self, llm=None, prompt_template=None):
445
  self.llm = llm or GeminiLLM()
446
- prompt_template = prompt_template or PromptTemplate.from_template("{question}")
447
- self.chain = LLMChain(prompt=prompt_template, llm=self.llm)
448
-
449
  self.tools = {
450
  "wiki_search": wiki_search,
451
  "load_file": load_file,
@@ -468,8 +465,21 @@ class BasicAgent:
468
  "filter_by_numeric_range": filter_by_numeric_range,
469
  "classify_items_by_list": classify_items_by_list,
470
  }
471
-
472
  self._cache = {}
 
 
 
 
 
 
 
 
 
 
 
 
 
473
 
474
  def register_tool(self, name: str, func: Callable):
475
  self.tools[name] = func
@@ -482,174 +492,68 @@ class BasicAgent:
482
 
483
  def call_tool(self, tool_name: str, *args, **kwargs):
484
  func = self.tools.get(tool_name)
485
- if func is None:
486
- msg = f"Tool '{tool_name}' not found."
487
- print(f"[LOG] {msg}")
488
- return msg
489
 
490
  key = self._cache_key(tool_name, args, kwargs)
491
  if key in self._cache:
492
- print(f"[LOG] Returning cached result for tool '{tool_name}'")
493
  return self._cache[key]
494
 
495
- print(f"[LOG] Calling tool: '{tool_name}' with args={args} kwargs={kwargs}")
496
  try:
497
  result = func(*args, **kwargs)
498
- print(f"[LOG] Tool '{tool_name}' returned: {result}")
499
  self._cache[key] = result
500
  return result
501
  except Exception as e:
502
- print(f"[ERROR] Tool '{tool_name}' raised exception: {e}")
503
  return f"Error executing tool '{tool_name}': {e}"
504
 
505
- def __call__(self, question: str) -> str:
506
- print(f"[LOG] Processing question: {question!r}")
507
-
508
- # Patrón para detectar: tool:<tool_name>(arg1,arg2,...)
509
- pattern = r"tool:(\w+)\((.*?)\)"
510
-
511
- def tool_replacer(match):
512
- tool_name = match.group(1)
513
- args_str = match.group(2)
514
- # Parseamos args separando por comas, eliminamos espacios externos
515
- args = [arg.strip() for arg in args_str.split(",")] if args_str else []
516
- print(f"[LOG] Detected tool call: {tool_name} with args={args}")
517
- result = self.call_tool(tool_name, *args)
518
- return str(result)
519
-
520
- # Reemplazamos todas las invocaciones de tools en la pregunta
521
- processed_question = re.sub(pattern, tool_replacer, question)
522
-
523
- time.sleep(3) # Simula latencia opcional
524
-
525
  try:
526
- response = self.chain.run({"question": processed_question})
527
- print(f"[LOG] LLM response: {response}")
528
- return response
529
- except Exception as e:
530
- print(f"[ERROR] LLM chain failed: {e}")
531
- return f"Error processing question: {e}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
532
 
 
 
 
 
 
 
 
 
533
 
534
- def run_and_submit_all(profile: gr.OAuthProfile | None):
535
- """
536
- Fetches all questions, runs the BasicAgent on them, submits all answers,
537
- and displays the results.
538
- """
539
- # --- Determine HF Space Runtime URL and Repo URL ---
540
- space_id = os.getenv("SPACE_ID") # Get the SPACE_ID for sending link to the code
541
 
542
- if profile:
543
- username = f"{profile.username}"
544
- print(f"User logged in: {username}")
545
- else:
546
- print("User not logged in.")
547
- return "Please Login to Hugging Face with the button.", None
548
-
549
- api_url = DEFAULT_API_URL
550
- questions_url = f"{api_url}/questions"
551
- submit_url = f"{api_url}/submit"
552
-
553
- # 1. Instantiate Agent ( modify this part to create your agent)
554
- try:
555
- agent = BasicAgent()
556
- except Exception as e:
557
- print(f"Error instantiating agent: {e}")
558
- return f"Error initializing agent: {e}", None
559
- # In the case of an app running as a hugging Face space, this link points toward your codebase ( usefull for others so please keep it public)
560
- agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
561
- print(agent_code)
562
-
563
- # 2. Fetch Questions
564
- print(f"Fetching questions from: {questions_url}")
565
- try:
566
- response = requests.get(questions_url, timeout=15)
567
- response.raise_for_status()
568
- questions_data = response.json()
569
- if not questions_data:
570
- print("Fetched questions list is empty.")
571
- return "Fetched questions list is empty or invalid format.", None
572
- print(f"Fetched {len(questions_data)} questions.")
573
- except requests.exceptions.RequestException as e:
574
- print(f"Error fetching questions: {e}")
575
- return f"Error fetching questions: {e}", None
576
- except requests.exceptions.JSONDecodeError as e:
577
- print(f"Error decoding JSON response from questions endpoint: {e}")
578
- print(f"Response text: {response.text[:500]}")
579
- return f"Error decoding server response for questions: {e}", None
580
- except Exception as e:
581
- print(f"An unexpected error occurred fetching questions: {e}")
582
- return f"An unexpected error occurred fetching questions: {e}", None
583
-
584
- # 3. Run your Agent
585
- results_log = []
586
- answers_payload = []
587
- print(f"Running agent on {len(questions_data)} questions...")
588
- for item in questions_data:
589
- task_id = item.get("task_id")
590
- question_text = item.get("question")
591
- if not task_id or question_text is None:
592
- print(f"Skipping item with missing task_id or question: {item}")
593
- continue
594
- try:
595
- submitted_answer = agent(question_text)
596
- answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
597
- results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
598
- except Exception as e:
599
- print(f"Error running agent on task {task_id}: {e}")
600
- results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": f"AGENT ERROR: {e}"})
601
-
602
- if not answers_payload:
603
- print("Agent did not produce any answers to submit.")
604
- return "Agent did not produce any answers to submit.", pd.DataFrame(results_log)
605
-
606
- # 4. Prepare Submission
607
- submission_data = {"username": username.strip(), "agent_code": agent_code, "answers": answers_payload}
608
- status_update = f"Agent finished. Submitting {len(answers_payload)} answers for user '{username}'..."
609
- print(status_update)
610
-
611
- # 5. Submit
612
- print(f"Submitting {len(answers_payload)} answers to: {submit_url}")
613
- try:
614
- response = requests.post(submit_url, json=submission_data, timeout=60)
615
- response.raise_for_status()
616
- result_data = response.json()
617
- final_status = (
618
- f"Submission Successful!\n"
619
- f"User: {result_data.get('username')}\n"
620
- f"Overall Score: {result_data.get('score', 'N/A')}% "
621
- f"({result_data.get('correct_count', '?')}/{result_data.get('total_attempted', '?')} correct)\n"
622
- f"Message: {result_data.get('message', 'No message received.')}"
623
- )
624
- print("Submission successful.")
625
- results_df = pd.DataFrame(results_log)
626
- return final_status, results_df
627
- except requests.exceptions.HTTPError as e:
628
- error_detail = f"Server responded with status {e.response.status_code}."
629
- try:
630
- error_json = e.response.json()
631
- error_detail += f" Detail: {error_json.get('detail', e.response.text)}"
632
- except requests.exceptions.JSONDecodeError:
633
- error_detail += f" Response: {e.response.text[:500]}"
634
- status_message = f"Submission Failed: {error_detail}"
635
- print(status_message)
636
- results_df = pd.DataFrame(results_log)
637
- return status_message, results_df
638
- except requests.exceptions.Timeout:
639
- status_message = "Submission Failed: The request timed out."
640
- print(status_message)
641
- results_df = pd.DataFrame(results_log)
642
- return status_message, results_df
643
- except requests.exceptions.RequestException as e:
644
- status_message = f"Submission Failed: Network error - {e}"
645
- print(status_message)
646
- results_df = pd.DataFrame(results_log)
647
- return status_message, results_df
648
- except Exception as e:
649
- status_message = f"An unexpected error occurred during submission: {e}"
650
- print(status_message)
651
- results_df = pd.DataFrame(results_log)
652
- return status_message, results_df
653
 
654
 
655
  # --- Build Gradio Interface using Blocks ---
 
9
  from typing import Dict
10
  import cv2
11
  from time import sleep
12
+ from langchain_core.prompts import PromptTemplate
13
+ from langchain.chains import LLMChain
14
+ import time
15
+ import functools
16
+ import hashlib
17
+ import re
18
+ import json
19
+ import hashlib
20
+ from typing import Callable
21
 
22
  class GeminiLLM(LLM):
23
  """Wrapper para usar Google Gemini como un LLM de LangChain."""
 
431
 
432
  # --- Basic Agent Definition ---
433
  # ----- THIS IS WERE YOU CAN BUILD WHAT YOU WANT ------
 
 
434
 
 
435
 
436
+ # --- Helper para describir las tools ---
437
+ def describe_tool(func: Callable) -> str:
438
+ name = func.__name__
439
+ sig = str(inspect.signature(func))
440
+ doc = func.__doc__.strip().split('\n')[0] if func.__doc__ else "No description"
441
+ return f"- {name}{sig}: {doc}"
 
 
 
 
 
 
442
 
443
  class BasicAgent:
444
+ def __init__(self, llm=None, max_iterations=5):
445
  self.llm = llm or GeminiLLM()
 
 
 
446
  self.tools = {
447
  "wiki_search": wiki_search,
448
  "load_file": load_file,
 
465
  "filter_by_numeric_range": filter_by_numeric_range,
466
  "classify_items_by_list": classify_items_by_list,
467
  }
468
+ # Cache para llamadas a tools
469
  self._cache = {}
470
+ self.max_iterations = max_iterations
471
+
472
+ # Construir prompt dinámico con info de tools
473
+ tools_desc = "\n".join(describe_tool(f) for f in self.tools.values())
474
+ prompt_str = (
475
+ "You can use the following tools by calling them with syntax:\n"
476
+ "tool:<tool_name>(arg1,arg2,...)\n\n"
477
+ "Available tools:\n"
478
+ f"{tools_desc}\n\n"
479
+ "Question: {{question}}\nAnswer:"
480
+ )
481
+ self.prompt_template = PromptTemplate.from_template(prompt_str)
482
+ self.chain = LLMChain(prompt=self.prompt_template, llm=self.llm)
483
 
484
  def register_tool(self, name: str, func: Callable):
485
  self.tools[name] = func
 
492
 
493
  def call_tool(self, tool_name: str, *args, **kwargs):
494
  func = self.tools.get(tool_name)
495
+ if not func:
496
+ return f"Tool '{tool_name}' not found."
 
 
497
 
498
  key = self._cache_key(tool_name, args, kwargs)
499
  if key in self._cache:
 
500
  return self._cache[key]
501
 
 
502
  try:
503
  result = func(*args, **kwargs)
 
504
  self._cache[key] = result
505
  return result
506
  except Exception as e:
 
507
  return f"Error executing tool '{tool_name}': {e}"
508
 
509
+ def _parse_arg(self, arg: str):
510
+ arg = arg.strip()
511
+ if arg.lower() == "true":
512
+ return True
513
+ if arg.lower() == "false":
514
+ return False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
515
  try:
516
+ return int(arg)
517
+ except:
518
+ pass
519
+ try:
520
+ return float(arg)
521
+ except:
522
+ pass
523
+ if (arg.startswith('"') and arg.endswith('"')) or (arg.startswith("'") and arg.endswith("'")):
524
+ return arg[1:-1]
525
+ # Intentar JSON para listas o dicts
526
+ try:
527
+ return json.loads(arg)
528
+ except:
529
+ pass
530
+ return arg
531
+
532
+ def _run_once(self, text: str) -> (str, bool):
533
+ # Ejecuta una iteración: LLM + ejecución tools
534
+ llm_out = self.chain.run({"question": text})
535
+ pattern = r"tool:(\w+)\((.*?)\)"
536
+ tools_called = False
537
 
538
+ def repl(m):
539
+ nonlocal tools_called
540
+ tools_called = True
541
+ tool_name = m.group(1)
542
+ args_raw = m.group(2)
543
+ args = [self._parse_arg(a) for a in re.findall(r'(?:[^,"]|"(?:\\.|[^"])*")+', args_raw)] if args_raw.strip() else []
544
+ res = self.call_tool(tool_name, *args)
545
+ return str(res)
546
 
547
+ processed = re.sub(pattern, repl, llm_out)
548
+ return processed, tools_called
 
 
 
 
 
549
 
550
+ def __call__(self, question: str) -> str:
551
+ text = question
552
+ for i in range(self.max_iterations):
553
+ text, used_tools = self._run_once(text)
554
+ if not used_tools:
555
+ break
556
+ return text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
557
 
558
 
559
  # --- Build Gradio Interface using Blocks ---