bhatanerohan commited on
Commit
8323d3a
ยท
verified ยท
1 Parent(s): 957d1ee

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +78 -64
app.py CHANGED
@@ -1,16 +1,21 @@
1
- import os, json, time
 
 
2
  from functools import lru_cache
3
 
4
  import gradio as gr
5
  import requests
6
  import pandas as pd
7
  from openai import OpenAI, RateLimitError, APIError
8
- from duckduckgo_search import DDGS # add to requirements.txt
 
9
 
10
- # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
11
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
12
  OPENAI_MODEL = "gpt-4o-mini"
13
- # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
 
 
 
14
  def duckduckgo_search(query: str, max_results: int = 5) -> str:
15
  bullets = []
16
  with DDGS() as ddgs:
@@ -24,62 +29,84 @@ DDG_SCHEMA = {
24
  "parameters": {
25
  "type": "object",
26
  "properties": {
27
- "query": {"type": "string"},
28
  "max_results": {"type": "integer", "default": 5},
29
  },
30
  "required": ["query"],
31
  },
32
  }
33
 
34
- # โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
35
- # โ”‚ AGENT (now supports optional image_url) โ”‚
36
- # โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ
37
- class GPT4oMiniAgentWithDDG:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  def __init__(self, retries:int = 3, backoff:float = 2.0):
39
- api_key = os.getenv("OPENAI_API_KEY")
40
- if not api_key:
41
- raise EnvironmentError("Add OPENAI_API_KEY in your Space secrets!")
42
- self.client = OpenAI(api_key=api_key)
43
- self.retries = retries
44
- self.backoff = backoff
45
- self.prompt = (
46
  "You are a concise, accurate assistant. "
47
- "If certain, answer immediately; otherwise call duckduckgo_search."
48
  )
49
 
50
  @lru_cache(maxsize=512)
51
- def __call__(self, question: str, image_url: str | None = None) -> str:
52
  user_content = [{"type": "text", "text": question}]
53
- if image_url:
54
- user_content.append(
55
- {"type": "image_url", "image_url": {"url": image_url}}
56
- )
 
 
 
 
 
 
 
 
 
57
 
58
  msgs = [
59
- {"role": "system", "content": self.prompt},
60
- {"role": "user", "content": user_content},
61
  ]
62
 
63
- # 1st pass โ€“ model may request the tool
64
  resp = self._chat(msgs, tools=[DDG_SCHEMA], tool_choice="auto")
65
 
66
- # Run tool(s) if requested
67
  if resp.choices[0].message.tool_calls:
68
  for call in resp.choices[0].message.tool_calls:
69
  args = json.loads(call.function.arguments or "{}")
70
- tool_out = duckduckgo_search(**args) if call.function.name=="duckduckgo_search" else ""
71
  msgs.append({
72
- "role": "tool",
73
- "tool_call_id": call.id,
74
- "name": call.function.name,
75
- "content": tool_out
76
  })
77
  resp = self._chat(msgs)
78
 
79
  return resp.choices[0].message.content.strip()
80
 
81
  def _chat(self, messages, **kw):
82
- for i in range(1, self.retries + 1):
83
  try:
84
  return self.client.chat.completions.create(
85
  model=OPENAI_MODEL,
@@ -92,52 +119,39 @@ class GPT4oMiniAgentWithDDG:
92
  time.sleep(self.backoff * i)
93
  raise RuntimeError("OpenAI API failed after retries.")
94
 
95
- # โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
96
- # โ”‚ RUN + SUBMIT โ”‚
97
- # โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ
98
- def run_and_submit_all(profile: gr.OAuthProfile | None):
99
  if not profile:
100
  return "Please log in โ†‘", None
101
  username = profile.username
102
- agent = GPT4oMiniAgentWithDDG()
103
- space_id = os.getenv("SPACE_ID", "local")
104
  agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
105
 
106
- # โ‘  Fetch
107
  qs = requests.get(f"{DEFAULT_API_URL}/questions", timeout=15).json()
108
 
109
- # โ‘ก Answer
110
- answers, rows = [], []
111
  for item in qs:
112
- qid = item["task_id"]
113
- text = item["question"]
114
- img = item.get("filename") # <-- NEW
115
- ans = agent(text, img)
116
  answers.append({"task_id": qid, "submitted_answer": ans})
117
- rows.append({"Task ID": qid, "Question": text, "Image URL": img or "", "Answer": ans})
118
-
119
- # โ‘ข Submit
120
- payload = {
121
- "username": username,
122
- "agent_code": agent_code,
123
- "answers": answers
124
- }
125
- res = requests.post(f"{DEFAULT_API_URL}/submit", json=payload, timeout=60).json()
126
- status = f"Score {res['score']} % ({res['correct_count']}/{res['total_attempted']})"
127
 
 
 
 
128
  return status, pd.DataFrame(rows)
129
 
130
- # โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
131
- # โ”‚ GRADIO UI โ”‚
132
- # โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ
133
  with gr.Blocks() as demo:
134
- gr.Markdown("# Unit-4 Agent Runner โ€“ Image Ready")
135
  gr.LoginButton()
136
- run_btn = gr.Button("Run Evaluation & Submit All Answers")
137
- status_box = gr.Textbox(label="Status", interactive=False)
138
- results_grid = gr.DataFrame(label="Log", wrap=True)
139
-
140
- run_btn.click(run_and_submit_all, outputs=[status_box, results_grid])
141
 
142
  if __name__ == "__main__":
143
  demo.launch(debug=True, share=False)
 
1
+ # app.py โ€“ handles images, txt/py, PDFs, any fileโ€ฆ
2
+
3
+ import os, json, time, io, mimetypes
4
  from functools import lru_cache
5
 
6
  import gradio as gr
7
  import requests
8
  import pandas as pd
9
  from openai import OpenAI, RateLimitError, APIError
10
+ from duckduckgo_search import DDGS
11
+ from PyPDF2 import PdfReader # <- new dependency
12
 
 
13
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
14
  OPENAI_MODEL = "gpt-4o-mini"
15
+ TEXT_CHAR_LIMIT = 8_000
16
+ PDF_PAGE_LIMIT = 3
17
+
18
+ # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ helpers โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
19
  def duckduckgo_search(query: str, max_results: int = 5) -> str:
20
  bullets = []
21
  with DDGS() as ddgs:
 
29
  "parameters": {
30
  "type": "object",
31
  "properties": {
32
+ "query": {"type": "string"},
33
  "max_results": {"type": "integer", "default": 5},
34
  },
35
  "required": ["query"],
36
  },
37
  }
38
 
39
+ def fetch_text_file(url: str) -> str:
40
+ try:
41
+ txt = requests.get(url, timeout=15).text
42
+ return txt[:TEXT_CHAR_LIMIT]
43
+ except Exception as e:
44
+ return f"[Could not download text file: {e}]"
45
+
46
+ def fetch_pdf_text(url: str) -> str:
47
+ try:
48
+ resp = requests.get(url, timeout=20)
49
+ resp.raise_for_status()
50
+ reader = PdfReader(io.BytesIO(resp.content))
51
+ pages = []
52
+ for i, page in enumerate(reader.pages[:PDF_PAGE_LIMIT]):
53
+ pages.append(page.extract_text() or "")
54
+ return ("\n\n".join(pages))[:TEXT_CHAR_LIMIT]
55
+ except Exception as e:
56
+ return f"[Could not read PDF: {e}]"
57
+
58
+ # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ agent โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
59
+ class GPT4oMiniAgentWithFiles:
60
  def __init__(self, retries:int = 3, backoff:float = 2.0):
61
+ key = os.getenv("OPENAI_API_KEY")
62
+ if not key:
63
+ raise EnvironmentError("OPENAI_API_KEY missing in Secrets.")
64
+ self.client, self.retries, self.backoff = OpenAI(api_key=key), retries, backoff
65
+ self.sys_prompt = (
 
 
66
  "You are a concise, accurate assistant. "
67
+ "If certain, answer directly; otherwise call duckduckgo_search."
68
  )
69
 
70
  @lru_cache(maxsize=512)
71
+ def __call__(self, question:str, file_url:str|None=None) -> str:
72
  user_content = [{"type": "text", "text": question}]
73
+ if file_url:
74
+ kind = (file_url.split("?")[0].split("#")[0]).lower()
75
+ ext = os.path.splitext(kind)[1]
76
+ if ext in {".png", ".jpg", ".jpeg", ".gif", ".webp"}:
77
+ user_content.append({"type":"image_url","image_url":{"url":file_url}})
78
+ elif ext == ".pdf":
79
+ text = fetch_pdf_text(file_url)
80
+ user_content.append({"type":"text","text": f"(PDF extract)\n{text}"})
81
+ elif ext in {".txt", ".py", ".md", ".json", ".csv", ".html"}:
82
+ text = fetch_text_file(file_url)
83
+ user_content.append({"type":"text","text": f"(File content)\n{text}"})
84
+ else:
85
+ user_content.append({"type":"text","text": f"[File available here] {file_url}"})
86
 
87
  msgs = [
88
+ {"role":"system","content":self.sys_prompt},
89
+ {"role":"user","content":user_content},
90
  ]
91
 
 
92
  resp = self._chat(msgs, tools=[DDG_SCHEMA], tool_choice="auto")
93
 
 
94
  if resp.choices[0].message.tool_calls:
95
  for call in resp.choices[0].message.tool_calls:
96
  args = json.loads(call.function.arguments or "{}")
97
+ tool_out = duckduckgo_search(**args)
98
  msgs.append({
99
+ "role":"tool",
100
+ "tool_call_id":call.id,
101
+ "name":call.function.name,
102
+ "content":tool_out,
103
  })
104
  resp = self._chat(msgs)
105
 
106
  return resp.choices[0].message.content.strip()
107
 
108
  def _chat(self, messages, **kw):
109
+ for i in range(1, self.retries+1):
110
  try:
111
  return self.client.chat.completions.create(
112
  model=OPENAI_MODEL,
 
119
  time.sleep(self.backoff * i)
120
  raise RuntimeError("OpenAI API failed after retries.")
121
 
122
+ # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ run + submit โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
123
+ def run_and_submit_all(profile: gr.OAuthProfile|None):
 
 
124
  if not profile:
125
  return "Please log in โ†‘", None
126
  username = profile.username
127
+ agent = GPT4oMiniAgentWithFiles()
128
+ space_id = os.getenv("SPACE_ID","local")
129
  agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
130
 
 
131
  qs = requests.get(f"{DEFAULT_API_URL}/questions", timeout=15).json()
132
 
133
+ rows, answers = [], []
 
134
  for item in qs:
135
+ qid = item["task_id"]
136
+ text = item["question"]
137
+ file_url = item.get("filename") or item.get("file_url")
138
+ ans = agent(text, file_url)
139
  answers.append({"task_id": qid, "submitted_answer": ans})
140
+ rows.append({"Task ID": qid, "Question": text, "File": file_url or "", "Answer": ans})
 
 
 
 
 
 
 
 
 
141
 
142
+ payload = {"username": username, "agent_code": agent_code, "answers": answers}
143
+ res = requests.post(f"{DEFAULT_API_URL}/submit", json=payload, timeout=60).json()
144
+ status = f"Score {res['score']} % ({res['correct_count']}/{res['total_attempted']})"
145
  return status, pd.DataFrame(rows)
146
 
147
+ # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ UI โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
 
 
148
  with gr.Blocks() as demo:
149
+ gr.Markdown("# Unit-4 Agent โ€“ handles images, text/code files & PDFs")
150
  gr.LoginButton()
151
+ btn = gr.Button("Run Evaluation & Submit All Answers")
152
+ status = gr.Textbox(label="Status", interactive=False)
153
+ table = gr.DataFrame(label="Log", wrap=True)
154
+ btn.click(run_and_submit_all, outputs=[status, table])
 
155
 
156
  if __name__ == "__main__":
157
  demo.launch(debug=True, share=False)