D3MI4N commited on
Commit
5ada353
·
1 Parent(s): 81917a3

first working version

Browse files
Files changed (9) hide show
  1. .gitignore +28 -0
  2. Dockerfile +10 -0
  3. app.py +28 -6
  4. qa_graph.py +43 -0
  5. requirements.txt +11 -1
  6. test_gaia.py +8 -0
  7. tools/__init__.py +2 -0
  8. tools/calculator_tool.py +34 -0
  9. tools/search_tool.py +23 -0
.gitignore ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 1) Python virtual environment
2
+ .venv/
3
+ venv/
4
+
5
+ # 2) IDE/editor settings
6
+ .vscode/
7
+ *.suo
8
+ *.swp
9
+ *.idea/
10
+
11
+ # 3) OS files
12
+ .DS_Store
13
+ Thumbs.db
14
+
15
+ # 4) Local test outputs or caches
16
+ local_results.csv
17
+ __pycache__/
18
+ *.py[cod]
19
+
20
+ # 5) Credentials & configs
21
+ # If you’re using config.py or config.yaml to store API keys, do NOT commit your real keys:
22
+ config.py
23
+ config.yaml
24
+ .env
25
+
26
+ # 6) Any Docker or Kubernetes local files
27
+ docker-compose.override.yml
28
+ *.log
Dockerfile ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.10-slim
2
+
3
+ WORKDIR /app
4
+
5
+ COPY requirements.txt .
6
+ RUN pip install -r requirements.txt
7
+
8
+ COPY . .
9
+
10
+ CMD ["python", "app.py"]
app.py CHANGED
@@ -3,21 +3,38 @@ import gradio as gr
3
  import requests
4
  import inspect
5
  import pandas as pd
 
 
6
 
7
  # (Keep Constants as is)
8
  # --- Constants ---
9
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
10
 
11
  # --- Basic Agent Definition ---
12
- # ----- THIS IS WERE YOU CAN BUILD WHAT YOU WANT ------
 
 
 
 
 
 
 
 
 
 
13
  class BasicAgent:
14
  def __init__(self):
15
- print("BasicAgent initialized.")
 
16
  def __call__(self, question: str) -> str:
17
- print(f"Agent received question (first 50 chars): {question[:50]}...")
18
- fixed_answer = "This is a default answer."
19
- print(f"Agent returning fixed answer: {fixed_answer}")
20
- return fixed_answer
 
 
 
 
21
 
22
  def run_and_submit_all( profile: gr.OAuthProfile | None):
23
  """
@@ -73,6 +90,11 @@ def run_and_submit_all( profile: gr.OAuthProfile | None):
73
  results_log = []
74
  answers_payload = []
75
  print(f"Running agent on {len(questions_data)} questions...")
 
 
 
 
 
76
  for item in questions_data:
77
  task_id = item.get("task_id")
78
  question_text = item.get("question")
 
3
  import requests
4
  import inspect
5
  import pandas as pd
6
+ from qa_graph import graph # my LangGraph-based pipeline
7
+ from langgraph.graph import StateGraph, START, END
8
 
9
  # (Keep Constants as is)
10
  # --- Constants ---
11
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
12
 
13
  # --- Basic Agent Definition ---
14
+ # # ----- THIS IS WERE YOU CAN BUILD WHAT YOU WANT ------
15
+ # class BasicAgent:
16
+ # def __init__(self):
17
+ # print("BasicAgent initialized.")
18
+ # def __call__(self, question: str) -> str:
19
+ # print(f"Agent received question (first 50 chars): {question[:50]}...")
20
+ # fixed_answer = "This is a default answer."
21
+ # print(f"Agent returning fixed answer: {fixed_answer}")
22
+ # return fixed_answer
23
+
24
+
25
  class BasicAgent:
26
  def __init__(self):
27
+ print("Graph-based agent initialized.")
28
+
29
  def __call__(self, question: str) -> str:
30
+ print("Received question:", question)
31
+ # Prepare the initial state
32
+ state = {"question": question, "answer": ""}
33
+ # Execute the graph
34
+ out = graph.invoke({"question":question,"answer":""})
35
+ answer = out["answer"]
36
+ return answer
37
+
38
 
39
  def run_and_submit_all( profile: gr.OAuthProfile | None):
40
  """
 
90
  results_log = []
91
  answers_payload = []
92
  print(f"Running agent on {len(questions_data)} questions...")
93
+
94
+ MAX_QUESTIONS = 3
95
+ questions_data = questions_data[:MAX_QUESTIONS]
96
+ print(f"Limiting to first {MAX_QUESTIONS} questions.")
97
+
98
  for item in questions_data:
99
  task_id = item.get("task_id")
100
  question_text = item.get("question")
qa_graph.py ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # simple_qa_graph.py
2
+
3
+ from typing import TypedDict
4
+ from langgraph.graph import StateGraph, START, END
5
+ from tools.calculator_tool import calculator_tool
6
+ from tools.search_tool import search_tool
7
+ import re
8
+
9
+ # 1) Define the shape of our state
10
+ class QAState(TypedDict):
11
+ question: str # incoming question
12
+ answer: str # place to store the tool’s output
13
+
14
+ # 2) Define our single agent node
15
+ def QAAgent(state: QAState) -> QAState:
16
+ q = state["question"].strip()
17
+ # if it looks like math, use the calculator, else do web search:
18
+ if re.fullmatch(r"[0-9\s\+\-\*\/\.\(\)]+", q):
19
+ state["answer"] = calculator_tool(q)
20
+ else:
21
+ state["answer"] = search_tool(q)
22
+ return state
23
+
24
+ # 3) Wire it up in a graph: START → QAAgent → END
25
+ builder = StateGraph(QAState)
26
+ builder.set_entry_point("QAAgent")
27
+ builder.add_node("QAAgent", QAAgent)
28
+ builder.add_edge(START, "QAAgent")
29
+ builder.add_edge("QAAgent", END)
30
+
31
+ graph = builder.compile()
32
+
33
+ # 4) Run it locally
34
+ if __name__ == "__main__":
35
+ # try a math question
36
+ s1: QAState = {"question": "2 + 2", "answer": ""}
37
+ out1 = graph.invoke(s1)
38
+ print("Q:", s1["question"], "→ A:", out1["answer"])
39
+
40
+ # try a search question
41
+ s2: QAState = {"question": "What is the capital of France?", "answer": ""}
42
+ out2 = graph.invoke(s2)
43
+ print("Q:", s2["question"], "→ A:", out2["answer"])
requirements.txt CHANGED
@@ -1,2 +1,12 @@
1
  gradio
2
- requests
 
 
 
 
 
 
 
 
 
 
 
1
  gradio
2
+ requests
3
+ langgraph
4
+ openai
5
+ tavily-python
6
+ langchain
7
+ pandas
8
+ python-dotenv
9
+ huggingface_hub
10
+ transformers
11
+ langchain-huggingface
12
+ IPython
test_gaia.py ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ from qa_graph import graph
2
+ import requests
3
+ import pandas as pd
4
+
5
+ QUESTIONS = requests.get("https://agents-course-unit4-scoring.hf.space/questions").json()
6
+ for q in QUESTIONS[:3]:
7
+ ans = graph.invoke({"question":q["question"],"answer":""})["answer"]
8
+ print(q["task_id"], ans)
tools/__init__.py ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ from .calculator_tool import calculator_tool
2
+ from .search_tool import search_tool
tools/calculator_tool.py ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from langchain.tools import tool
2
+ import ast
3
+ import operator
4
+
5
+ def safe_eval(expr):
6
+ ops = {
7
+ ast.Add: operator.add,
8
+ ast.Sub: operator.sub,
9
+ ast.Mult: operator.mul,
10
+ ast.Div: operator.truediv,
11
+ ast.Pow: operator.pow,
12
+ ast.USub: operator.neg,
13
+ }
14
+
15
+ def _eval(node):
16
+ if isinstance(node, ast.Constant):
17
+ return node.n
18
+ elif isinstance(node, ast.BinOp):
19
+ return ops[type(node.op)](_eval(node.left), _eval(node.right))
20
+ elif isinstance(node, ast.UnaryOp):
21
+ return ops[type(node.op)](_eval(node.operand))
22
+ else:
23
+ raise TypeError(f"Unsupported expression: {node}")
24
+
25
+ node = ast.parse(expr, mode='eval').body
26
+ return _eval(node)
27
+
28
+ @tool(description="Performs basic arithmetic calculations on a query string and returns the result as a string.")
29
+ def calculator_tool(query: str) -> str:
30
+ try:
31
+ result = safe_eval(query)
32
+ return str(result)
33
+ except Exception as e:
34
+ return f"Error evaluating expression: {e}"
tools/search_tool.py ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from tavily import TavilyClient
2
+ from langchain.tools import tool
3
+ from config import TAVILY_API_KEY
4
+
5
+ class SearchTool:
6
+ def __init__(self, api_key: str):
7
+ self.client = TavilyClient(api_key=api_key)
8
+
9
+ def search(self, query: str):
10
+ response = self.client.search(query)
11
+ # Extract a string summary of results (you can adapt this as needed)
12
+ results = response.get("results", [])
13
+ if not results:
14
+ return "No results found."
15
+ # For simplicity, join first 3 results' titles or snippets
16
+ summaries = [res.get("title", "") or res.get("snippet", "") for res in results[:3]]
17
+ return " | ".join(summaries)
18
+
19
+ search_tool_instance = SearchTool(api_key=TAVILY_API_KEY)
20
+
21
+ @tool(description="Use this tool to search for information on the web using Tavily API and return a summary of results.")
22
+ def search_tool(query: str) -> str:
23
+ return search_tool_instance.search(query)