wt002 commited on
Commit
4aa21d9
·
verified ·
1 Parent(s): ffb9aa5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +260 -15
app.py CHANGED
@@ -1,35 +1,280 @@
1
 
2
  import os
3
- import inspect
4
  import gradio as gr
5
  import requests
 
6
  import pandas as pd
7
- from langchain_core.messages import HumanMessage
8
- from agent import build_graph
9
-
10
-
 
 
 
 
 
 
 
11
 
12
  # (Keep Constants as is)
13
  # --- Constants ---
14
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
15
 
16
- # --- Basic Agent Definition ---
17
- # ----- THIS IS WERE YOU CAN BUILD WHAT YOU WANT ------
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
 
20
  class BasicAgent:
21
- """A langgraph agent."""
22
  def __init__(self):
23
- print("BasicAgent initialized.")
24
- self.graph = build_graph()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
 
26
  def __call__(self, question: str) -> str:
27
  print(f"Agent received question (first 50 chars): {question[:50]}...")
28
- # Wrap the question in a HumanMessage from langchain_core
29
- messages = [HumanMessage(content=question)]
30
- response = self.graph.invoke(messages)
31
- answer = response['messages'][-1].content
32
- return answer[14:]
33
 
34
 
35
 
 
1
 
2
  import os
 
3
  import gradio as gr
4
  import requests
5
+ import inspect
6
  import pandas as pd
7
+ from smolagents import tool, Tool, CodeAgent, DuckDuckGoSearchTool, HfApiModel, VisitWebpageTool, SpeechToTextTool, FinalAnswerTool
8
+ from dotenv import load_dotenv
9
+ import heapq
10
+ from collections import Counter
11
+ import re
12
+ from io import BytesIO
13
+ from youtube_transcript_api import YouTubeTranscriptApi
14
+ from langchain_community.tools.tavily_search import TavilySearchResults
15
+ from langchain_community.document_loaders import WikipediaLoader
16
+ from langchain_community.utilities import WikipediaAPIWrapper
17
+ from langchain_community.document_loaders import ArxivLoader
18
 
19
  # (Keep Constants as is)
20
  # --- Constants ---
21
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
22
 
23
+ #Load environment variables
24
+ load_dotenv()
25
+
26
+
27
+ import os
28
+ import io
29
+ import contextlib
30
+ from transformers import pipeline, AutoTokenizer, AutoModelForCausalLM
31
+
32
+ class MistralToolCallingAgentTool:
33
+ name = "mistral_tool_agent"
34
+ description = "Uses Mistral-7B-Instruct to answer questions using code or reasoning"
35
+
36
+ def __init__(self):
37
+ self.model_id = "mistralai/Mistral-7B-Instruct"
38
+ self.tokenizer = AutoTokenizer.from_pretrained(self.model_id)
39
+ self.model = AutoModelForCausalLM.from_pretrained(self.model_id, device_map="auto", torch_dtype="auto")
40
+ self.pipeline = pipeline(
41
+ "text-generation", model=self.model, tokenizer=self.tokenizer,
42
+ max_new_tokens=512, temperature=0.2
43
+ )
44
+
45
+ def _run_code(self, code: str) -> str:
46
+ buffer = io.StringIO()
47
+ try:
48
+ with contextlib.redirect_stdout(buffer):
49
+ exec(code, {})
50
+ return buffer.getvalue().strip()
51
+ except Exception as e:
52
+ return f"Error during execution: {e}"
53
+
54
+ def run(self, question: str) -> str:
55
+ prompt = f"""You are a helpful assistant. Use code to solve questions that involve calculations.
56
+ If code is needed, return a block like <tool>code</tool>. End your answer with <final>answer</final>.
57
+
58
+ Question: {question}
59
+ Answer:"""
60
+ result = self.pipeline(prompt)[0]["generated_text"]
61
+
62
+ # Process result
63
+ if "<tool>" in result and "</tool>" in result:
64
+ code = result.split("<tool>")[1].split("</tool>")[0].strip()
65
+ output = self._run_code(code)
66
+ return f"Code result: {output}"
67
+
68
+ elif "<final>" in result and "</final>" in result:
69
+ final = result.split("<final>")[1].split("</final>")[0].strip()
70
+ return f"FINAL ANSWER: {final}"
71
+
72
+ return "Mistral agent could not determine how to respond."
73
+
74
+ #from smolagents import Tool
75
+ #from langchain_community.document_loaders import WikipediaLoader
76
+
77
+ class WikiSearchTool(Tool):
78
+ name = "wiki_search"
79
+ description = "Search Wikipedia for a query and return up to 2 results."
80
+ inputs = {
81
+ "query": {"type": "string", "description": "The search term for Wikipedia."}
82
+ }
83
+ output_type = "string"
84
+
85
+ def forward(self, query: str) -> str:
86
+ search_docs = WikipediaLoader(query=query, load_max_docs=2).load()
87
+
88
+ formatted_search_docs = "\n\n---\n\n".join(
89
+ [
90
+ f'<Document source="{doc.metadata.get("source", "Wikipedia")}" page="{doc.metadata.get("page", "")}"/>\n{doc.page_content}\n</Document>'
91
+ for doc in search_docs
92
+ ]
93
+ )
94
+ return formatted_search_docs
95
+
96
+
97
+
98
+
99
+ class StringReverseTool(Tool):
100
+ name = "reverse_message"
101
+ description = "When you received a strange text, try to reverse it and perform action described in reversed message."
102
+ inputs = {
103
+ "message": {
104
+ "type": "string",
105
+ "description": "A message, which looks like strange and can be reversed to get actions to execute."
106
+ }
107
+ }
108
+ output_type = "string"
109
+
110
+ def __init__(self):
111
+ return
112
+
113
+ def forward(self, message: str):
114
+ return message[::-1]
115
+
116
+ class KeywordsExtractorTool(Tool):
117
+ """Extracts top 5 keywords from a given text based on frequency."""
118
 
119
+ name = "keywords_extractor"
120
+ description = "This tool returns the 5 most frequent keywords occur in provided block of text."
121
+
122
+ inputs = {
123
+ "text": {
124
+ "type": "string",
125
+ "description": "Text to analyze for keywords.",
126
+ }
127
+ }
128
+ output_type = "string"
129
+
130
+ def forward(self, text: str) -> str:
131
+ try:
132
+ all_words = re.findall(r'\b\w+\b', text.lower())
133
+ conjunctions = {'a', 'and', 'of', 'is', 'in', 'to', 'the'}
134
+ filtered_words = []
135
+ for w in all_words:
136
+ if w not in conjunctions:
137
+ filtered_words.push(w)
138
+ word_counts = Counter(filtered_words)
139
+ k = 5
140
+ return heapq.nlargest(k, word_counts.items(), key=lambda x: x[1])
141
+ except Exception as e:
142
+ return f"Error during extracting most common words: {e}"
143
+
144
+ @tool
145
+ def parse_excel_to_json(task_id: str) -> dict:
146
+ """
147
+ For a given task_id fetch and parse an Excel file and save parsed data in structured JSON file.
148
+ Args:
149
+ task_id: An task ID to fetch.
150
+
151
+ Returns:
152
+ {
153
+ "task_id": str,
154
+ "sheets": {
155
+ "SheetName1": [ {col1: val1, col2: val2, ...}, ... ],
156
+ ...
157
+ },
158
+ "status": "Success" | "Error"
159
+ }
160
+ """
161
+ url = f"https://agents-course-unit4-scoring.hf.space/files/{task_id}"
162
+
163
+ try:
164
+ response = requests.get(url, timeout=100)
165
+ if response.status_code != 200:
166
+ return {"task_id": task_id, "sheets": {}, "status": f"{response.status_code} - Failed"}
167
+
168
+ xls_content = pd.ExcelFile(BytesIO(response.content))
169
+ json_sheets = {}
170
+
171
+ for sheet in xls_content.sheet_names:
172
+ df = xls_content.parse(sheet)
173
+ df = df.dropna(how="all")
174
+ rows = df.head(20).to_dict(orient="records")
175
+ json_sheets[sheet] = rows
176
+
177
+ return {
178
+ "task_id": task_id,
179
+ "sheets": json_sheets,
180
+ "status": "Success"
181
+ }
182
+
183
+ except Exception as e:
184
+ return {
185
+ "task_id": task_id,
186
+ "sheets": {},
187
+ "status": f"Error in parsing Excel file: {str(e)}"
188
+ }
189
+
190
+
191
+
192
+ class VideoTranscriptionTool(Tool):
193
+ """Fetch transcripts from YouTube videos"""
194
+ name = "transcript_video"
195
+ description = "Fetch text transcript from YouTube movies with optional timestamps"
196
+ inputs = {
197
+ "url": {"type": "string", "description": "YouTube video URL or ID"},
198
+ "include_timestamps": {"type": "boolean", "description": "If timestamps should be included in output", "nullable": True}
199
+ }
200
+ output_type = "string"
201
+
202
+ def forward(self, url: str, include_timestamps: bool = False) -> str:
203
+
204
+ if "youtube.com/watch" in url:
205
+ video_id = url.split("v=")[1].split("&")[0]
206
+ elif "youtu.be/" in url:
207
+ video_id = url.split("youtu.be/")[1].split("?")[0]
208
+ elif len(url.strip()) == 11: # Direct ID
209
+ video_id = url.strip()
210
+ else:
211
+ return f"YouTube URL or ID: {url} is invalid!"
212
+
213
+ try:
214
+ transcription = YouTubeTranscriptApi.get_transcript(video_id)
215
+
216
+ if include_timestamps:
217
+ formatted_transcription = []
218
+ for part in transcription:
219
+ timestamp = f"{int(part['start']//60)}:{int(part['start']%60):02d}"
220
+ formatted_transcription.append(f"[{timestamp}] {part['text']}")
221
+ return "\n".join(formatted_transcription)
222
+ else:
223
+ return " ".join([part['text'] for part in transcription])
224
+
225
+ except Exception as e:
226
+ return f"Error in extracting YouTube transcript: {str(e)}"
227
 
228
  class BasicAgent:
 
229
  def __init__(self):
230
+ token = os.environ.get("HF_API_TOKEN")
231
+ model = HfApiModel(
232
+ temperature=0.1,
233
+ token=token
234
+ )
235
+
236
+ # Existing tools
237
+ search_tool = DuckDuckGoSearchTool()
238
+ wiki_search_tool = WikiSearchTool()
239
+ str_reverse_tool = StringReverseTool()
240
+ keywords_extract_tool = KeywordsExtractorTool()
241
+ speech_to_text_tool = SpeechToTextTool()
242
+ visit_webpage_tool = VisitWebpageTool()
243
+ final_answer_tool = FinalAnswerTool()
244
+ video_transcription_tool = VideoTranscriptionTool()
245
+
246
+ # ✅ New Mistral-based Tool
247
+ mistral_tool = MistralToolCallingAgentTool()
248
+
249
+ system_prompt = f"""
250
+ You are my general AI assistant. Your task is to answer the question I asked.
251
+ First, provide an explanation of your reasoning, step by step, to arrive at the answer.
252
+ Then, return your final answer in a single line, formatted as follows: "FINAL ANSWER: [YOUR FINAL ANSWER]".
253
+ [YOUR FINAL ANSWER] should be a number, a string, or a comma-separated list of numbers and/or strings, depending on the question.
254
+ If the answer is a number, do not use commas or units (e.g., $, %) unless specified.
255
+ If the answer is a string, do not use articles or abbreviations (e.g., for cities), and write digits in plain text unless specified.
256
+ If the answer is a comma-separated list, apply the above rules for each element based on whether it is a number or a string.
257
+ """
258
+
259
+ self.agent = CodeAgent(
260
+ model=model,
261
+ tools=[
262
+ search_tool, wiki_search_tool, str_reverse_tool,
263
+ keywords_extract_tool, speech_to_text_tool,
264
+ visit_webpage_tool, final_answer_tool,
265
+ parse_excel_to_json, video_transcription_tool,
266
+ mistral_tool # 🔧 Add here
267
+ ],
268
+ add_base_tools=True
269
+ )
270
+ self.agent.prompt_templates["system_prompt"] = self.agent.prompt_templates["system_prompt"] + system_prompt
271
 
272
  def __call__(self, question: str) -> str:
273
  print(f"Agent received question (first 50 chars): {question[:50]}...")
274
+ answer = self.agent.run(question)
275
+ print(f"Agent returning answer: {answer}")
276
+ return answer
277
+
 
278
 
279
 
280