Kaladin4 commited on
Commit
ab36e53
·
1 Parent(s): d789127

ft: add basic agent

Browse files
README.md CHANGED
@@ -1,12 +1,13 @@
1
  ---
2
- title: AgentCourseU4
3
- emoji: 💻
4
- colorFrom: red
5
- colorTo: green
6
  sdk: gradio
7
  sdk_version: 5.29.1
8
  app_file: app.py
9
  pinned: false
 
10
  ---
11
 
12
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: FinalTest
3
+ emoji:
4
+ colorFrom: blue
5
+ colorTo: indigo
6
  sdk: gradio
7
  sdk_version: 5.29.1
8
  app_file: app.py
9
  pinned: false
10
+ short_description: this space will host the code for the unit 4 ofthe HF course
11
  ---
12
 
13
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
__pycache__/agent.cpython-312.pyc ADDED
Binary file (4.4 kB). View file
 
__pycache__/config.cpython-312.pyc ADDED
Binary file (2.78 kB). View file
 
__pycache__/tools.cpython-312.pyc ADDED
Binary file (9.49 kB). View file
 
__pycache__/utils.cpython-312.pyc ADDED
Binary file (1.07 kB). View file
 
agent.py ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from se_agents.agent import Agent
2
+ from config import Config
3
+ from se_agents.tools import *
4
+ from tools import *
5
+ from se_agents.runner import Runner
6
+ from pydantic import BaseModel
7
+
8
+
9
+ class Task(BaseModel):
10
+ task_id: str
11
+ question: str
12
+ Level: str
13
+ file_name: str
14
+
15
+
16
+ async def run_agent(query):
17
+ agent = Agent(
18
+ api_key=Config.get_openrouter_api_key(),
19
+ base_url=Config.get_openrouter_api_url(),
20
+ model=Config.get_model(),
21
+ tools=[
22
+ ExaSearch(Config.get_exa_api_key()),
23
+ ExaCrawl(Config.get_exa_api_key()),
24
+ ExaSearchBase(Config.get_exa_api_key()),
25
+ ExaSearchContent(Config.get_exa_api_key()),
26
+ ExaSearchHighlights(Config.get_exa_api_key()),
27
+ OpenAIVisionTool(),
28
+ ThinkTool(),
29
+ FinalOutput(),
30
+ YoutubeVideoInterpreter(),
31
+ TaskFileDownloader(),
32
+ RetriveInfoTaskFile(),
33
+ SpeechToText(),
34
+ CodeInterpreter(),
35
+ ],
36
+ description="You are a general AI assistant. You are direct and concise.",
37
+ rules="""Answer with the following template: [YOUR FINAL ANSWER].
38
+ YOUR FINAL ANSWER should be a number OR as few words as possible OR a comma separated list of numbers and/or strings, you do not explain your answer, you just provided it in the least amount of characters as possible.
39
+ For every task that is given to you first think about it using the 'think' tool.
40
+ If you are asked for a number, don't use comma to write your number neither use units such as $ or percent sign unless specified otherwise, do not write the number as a string instead used the number notation. Example: Not Sixty three, Do 63.
41
+ If you are asked for a string, don't use articles, neither abbreviations (e.g. for cities), and write the digits in plain text unless specified otherwise.
42
+ If you are asked for a comma separated list, apply the above rules depending of whether the element to be put in the list is a number or a string.
43
+ If the task at hand has any file associated with it you never give a direct answer, first use the think tool, to process the request, once the thinking is complete you must first download using the task_file_downloader tool, and the use the proper tool to analyze the file.
44
+ If the task as any audio file associated with it, you must use the speech_to_text tool to convert it to a .txt file.
45
+ If the task involves the interpretation of code, you must use the code_interpreter tool to get the content of the code file.""",
46
+ add_default_rules=False,
47
+ add_think_instructions=True,
48
+ add_final_output_instructions=True,
49
+ )
50
+ runner = Runner(agent, enforce_final=True)
51
+ final_response = ""
52
+ async for response in runner.run(query):
53
+ if response.type == "response":
54
+ # This will typically contain the final output when enforce_final=True,
55
+ # or intermediate text otherwise.
56
+ final_response += response.content
57
+ return final_response
58
+ # Note: 'thinking' is now handled via ThinkTool, resulting in 'tool_call' and 'tool_response' events.
59
+ elif response.type == "tool_call":
60
+ # Includes calls to ThinkTool, FinalOutput, and others.
61
+ print(f"\n\n\033[92m🟡 Tool Call:\n{response.content}\033[0m\n")
62
+ elif response.type == "tool_response":
63
+ print(f"\n\n\033[94m🟢 Tool Response:\n{response.content}\033[0m\n")
64
+ elif response.type == "tool_error":
65
+ print(f"\n\n\033[91m🔴 Tool Error:\n{response.content}\033[0m\n")
app.py ADDED
@@ -0,0 +1,161 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import requests
3
+ import pandas as pd
4
+ import se_agents as se
5
+ from config import Config
6
+ from agent import run_agent
7
+ import asyncio
8
+ from colorama import Fore, Style, init
9
+
10
+ init()
11
+
12
+ USERNAME = "kaladin4"
13
+
14
+
15
+ async def run_and_submit_all():
16
+ """
17
+ Fetches all questions, runs the BasicAgent on them, submits all answers,
18
+ and displays the results.
19
+ """
20
+ # --- Determine HF Space Runtime URL and Repo URL ---
21
+ questions_url = f"{Config.get_default_api_url()}/questions"
22
+ submit_url = f"{Config.get_default_api_url()}/submit"
23
+
24
+ # 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)
25
+ agent_code = f"https://huggingface.co/spaces/Kaladin4/FinalTest/tree/main"
26
+
27
+ # 2. Fetch Questions
28
+ print(f"Fetching questions from: {questions_url}")
29
+ try:
30
+ response = requests.get(questions_url, timeout=15)
31
+ response.raise_for_status()
32
+ questions_data = response.json()
33
+ if not questions_data:
34
+ print("Fetched questions list is empty.")
35
+ return "Fetched questions list is empty or invalid format.", None
36
+ print(f"Fetched {len(questions_data)} questions.")
37
+ except requests.exceptions.RequestException as e:
38
+ print(f"Error fetching questions: {e}")
39
+ return f"Error fetching questions: {e}", None
40
+ except requests.exceptions.JSONDecodeError as e:
41
+ print(f"Error decoding JSON response from questions endpoint: {e}")
42
+ print(f"Response text: {response.text[:500]}")
43
+ return f"Error decoding server response for questions: {e}", None
44
+ except Exception as e:
45
+ print(f"An unexpected error occurred fetching questions: {e}")
46
+ return f"An unexpected error occurred fetching questions: {e}", None
47
+
48
+ # 3. Run your Agent
49
+ results_log = []
50
+ answers_payload = []
51
+ print(f"Running agent on {len(questions_data)} questions...")
52
+ for item in questions_data:
53
+ task_id = item.get("task_id")
54
+ question_text = (
55
+ f"{Fore.GREEN}Task Id:{Style.RESET_ALL} {task_id}\n"
56
+ f"{Fore.BLUE}Task:{Style.RESET_ALL} {item.get('question')}\n"
57
+ f"{Fore.YELLOW}File:{Style.RESET_ALL} {item.get('file_name')}"
58
+ )
59
+ print(question_text)
60
+ if not task_id or question_text is None:
61
+ print(f"Skipping item with missing task_id or question: {item}")
62
+ continue
63
+ try:
64
+ submitted_answer = await run_agent(question_text)
65
+ answers_payload.append(
66
+ {"task_id": task_id, "submitted_answer": submitted_answer}
67
+ )
68
+ results_log.append(
69
+ {
70
+ "Task ID": task_id,
71
+ "Question": question_text,
72
+ "Submitted Answer": submitted_answer,
73
+ }
74
+ )
75
+ except Exception as e:
76
+ print(f"Error running agent on task {task_id}: {e}")
77
+ results_log.append(
78
+ {
79
+ "Task ID": task_id,
80
+ "Question": question_text,
81
+ "Submitted Answer": f"AGENT ERROR: {e}",
82
+ }
83
+ )
84
+
85
+ if not answers_payload:
86
+ print("Agent did not produce any answers to submit.")
87
+ return "Agent did not produce any answers to submit.", pd.DataFrame(results_log)
88
+
89
+ # 4. Prepare Submission
90
+ submission_data = {
91
+ "username": USERNAME,
92
+ "agent_code": agent_code,
93
+ "answers": answers_payload,
94
+ }
95
+ status_update = f"Agent finished. Submitting {len(answers_payload)} answers for user '{USERNAME}'..."
96
+ print(status_update)
97
+
98
+ # 5. Submit
99
+ print(f"Submitting {len(answers_payload)} answers to: {submit_url}")
100
+ try:
101
+ response = requests.post(submit_url, json=submission_data, timeout=60)
102
+ response.raise_for_status()
103
+ result_data = response.json()
104
+ final_status = (
105
+ f"Submission Successful!\n"
106
+ f"User: {result_data.get('username')}\n"
107
+ f"Overall Score: {result_data.get('score', 'N/A')}% "
108
+ f"({result_data.get('correct_count', '?')}/{result_data.get('total_attempted', '?')} correct)\n"
109
+ f"Message: {result_data.get('message', 'No message received.')}"
110
+ )
111
+ print("Submission successful.")
112
+ results_df = pd.DataFrame(results_log)
113
+ return final_status, results_df
114
+ except requests.exceptions.HTTPError as e:
115
+ error_detail = f"Server responded with status {e.response.status_code}."
116
+ try:
117
+ error_json = e.response.json()
118
+ error_detail += f" Detail: {error_json.get('detail', e.response.text)}"
119
+ except requests.exceptions.JSONDecodeError:
120
+ error_detail += f" Response: {e.response.text[:500]}"
121
+ status_message = f"Submission Failed: {error_detail}"
122
+ print(status_message)
123
+ results_df = pd.DataFrame(results_log)
124
+ return status_message, results_df
125
+ except requests.exceptions.Timeout:
126
+ status_message = "Submission Failed: The request timed out."
127
+ print(status_message)
128
+ results_df = pd.DataFrame(results_log)
129
+ return status_message, results_df
130
+ except requests.exceptions.RequestException as e:
131
+ status_message = f"Submission Failed: Network error - {e}"
132
+ print(status_message)
133
+ results_df = pd.DataFrame(results_log)
134
+ return status_message, results_df
135
+ except Exception as e:
136
+ status_message = f"An unexpected error occurred during submission: {e}"
137
+ print(status_message)
138
+ results_df = pd.DataFrame(results_log)
139
+ return status_message, results_df
140
+
141
+
142
+ async def main():
143
+ print("\n" + "-" * 30 + " App Starting " + "-" * 30)
144
+ # Check for SPACE_HOST and SPACE_ID at startup for information
145
+
146
+ print("-" * (60 + len(" App Starting ")) + "\n")
147
+
148
+ print("Starting agent evaluation process...")
149
+ final_status, results_df = await run_and_submit_all()
150
+
151
+ print("\n" + "=" * 50)
152
+ print("FINAL STATUS:")
153
+ print(final_status)
154
+ print("=" * 50 + "\n")
155
+
156
+ print("Results Summary:")
157
+ print(results_df)
158
+
159
+
160
+ if __name__ == "__main__":
161
+ asyncio.run(main())
config.py ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from dotenv import load_dotenv
2
+ import os
3
+ from urllib.parse import unquote
4
+
5
+ load_dotenv()
6
+
7
+
8
+ class Config:
9
+ @staticmethod
10
+ def get_gemini_api_key():
11
+ return os.getenv("GEMINI_API_KEY")
12
+
13
+ @staticmethod
14
+ def get_model():
15
+ return os.getenv("OPENROUTER_MODEL")
16
+
17
+ @staticmethod
18
+ def get_exa_api_key():
19
+ return os.getenv("EXA_API_KEY")
20
+
21
+ @staticmethod
22
+ def get_openrouter_api_key():
23
+ return os.getenv("OPENROUTER_API_KEY_1")
24
+
25
+ @staticmethod
26
+ def get_default_api_url():
27
+ url = os.getenv("DEFAULT_API_URL")
28
+ if url:
29
+ if "\\x3a" in url:
30
+ return url.replace("\\x3a", ":")
31
+ return url
32
+ return None
33
+
34
+ @staticmethod
35
+ def get_openrouter_api_url():
36
+ url = os.getenv("OPENROUTER_BASE_URL")
37
+ if url:
38
+ if "\\x3a" in url:
39
+ return url.replace("\\x3a", ":")
40
+ return url
41
+ return None
42
+
43
+ @staticmethod
44
+ def get_task_file_folder():
45
+ return os.getenv("TASK_FILES_FOLDER")
46
+
47
+ @staticmethod
48
+ def get_openai_api_key():
49
+ return os.getenv("OPENAI_API_KEY")
50
+
51
+ @staticmethod
52
+ def get_defaulassociatedt_api_url():
53
+ url = os.getenv("DEFAULT_API_URL")
54
+ if url:
55
+ if "\\x3a" in url:
56
+ return url.replace("\\x3a", ":")
57
+ return url
58
+ return None
requirements.txt ADDED
@@ -0,0 +1,184 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ aiofiles==24.1.0
2
+ aiohappyeyeballs==2.6.1
3
+ aiohttp==3.11.18
4
+ aiosignal==1.3.2
5
+ annotated-types==0.7.0
6
+ anyio==4.9.0
7
+ asgiref==3.8.1
8
+ asyncer==0.0.8
9
+ attrs==25.3.0
10
+ Authlib==1.5.2
11
+ backoff==2.2.1
12
+ bcrypt==4.3.0
13
+ beautifulsoup4==4.13.4
14
+ build==1.2.2.post1
15
+ cachetools==5.5.2
16
+ certifi==2025.4.26
17
+ cffi==1.17.1
18
+ chardet==5.2.0
19
+ charset-normalizer==3.4.2
20
+ chromadb==1.0.9
21
+ click==8.1.8
22
+ coloredlogs==15.0.1
23
+ cryptography==44.0.3
24
+ dataclasses-json==0.6.7
25
+ datasets==3.6.0
26
+ Deprecated==1.2.18
27
+ dill==0.3.8
28
+ distro==1.9.0
29
+ duckduckgo_search==8.0.1
30
+ durationpy==0.9
31
+ emoji==2.14.1
32
+ et_xmlfile==2.0.0
33
+ eval_type_backport==0.2.2
34
+ exa-py==1.12.1
35
+ fastapi==0.115.9
36
+ ffmpy==0.5.0
37
+ filelock==3.18.0
38
+ filetype==1.2.0
39
+ firecrawl-py==2.5.3
40
+ flatbuffers==25.2.10
41
+ frozenlist==1.6.0
42
+ fsspec==2025.3.0
43
+ google-auth==2.40.1
44
+ google-genai==1.14.0
45
+ googleapis-common-protos==1.70.0
46
+ gradio==5.29.0
47
+ gradio_client==1.10.0
48
+ greenlet==3.2.2
49
+ groovy==0.1.2
50
+ grpcio==1.71.0
51
+ h11==0.16.0
52
+ html5lib==1.1
53
+ httpcore==1.0.9
54
+ httptools==0.6.4
55
+ httpx==0.28.1
56
+ httpx-sse==0.4.0
57
+ huggingface-hub==0.30.2
58
+ humanfriendly==10.0
59
+ idna==3.10
60
+ importlib_metadata==8.6.1
61
+ importlib_resources==6.5.2
62
+ iniconfig==2.1.0
63
+ itsdangerous==2.2.0
64
+ Jinja2==3.1.6
65
+ jiter==0.9.0
66
+ joblib==1.5.0
67
+ jsonpatch==1.33
68
+ jsonpointer==3.0.0
69
+ jsonschema==4.23.0
70
+ jsonschema-specifications==2025.4.1
71
+ kubernetes==32.0.1
72
+ langchain==0.3.25
73
+ langchain-community==0.3.23
74
+ langchain-core==0.3.59
75
+ langchain-openai==0.3.17
76
+ langchain-text-splitters==0.3.8
77
+ langdetect==1.0.9
78
+ langsmith==0.3.42
79
+ lxml==5.4.0
80
+ markdown-it-py==3.0.0
81
+ MarkupSafe==3.0.2
82
+ marshmallow==3.26.1
83
+ mdurl==0.1.2
84
+ mmh3==5.1.0
85
+ mpmath==1.3.0
86
+ multidict==6.4.3
87
+ multiprocess==0.70.16
88
+ mypy_extensions==1.1.0
89
+ nest-asyncio==1.6.0
90
+ networkx==3.4.2
91
+ nltk==3.9.1
92
+ numpy==2.2.5
93
+ oauthlib==3.2.2
94
+ olefile==0.47
95
+ onnxruntime==1.22.0
96
+ openai==1.77.0
97
+ openpyxl==3.1.5
98
+ opentelemetry-api==1.33.0
99
+ opentelemetry-exporter-otlp-proto-common==1.33.0
100
+ opentelemetry-exporter-otlp-proto-grpc==1.33.0
101
+ opentelemetry-instrumentation==0.54b0
102
+ opentelemetry-instrumentation-asgi==0.54b0
103
+ opentelemetry-instrumentation-fastapi==0.54b0
104
+ opentelemetry-proto==1.33.0
105
+ opentelemetry-sdk==1.33.0
106
+ opentelemetry-semantic-conventions==0.54b0
107
+ opentelemetry-util-http==0.54b0
108
+ orjson==3.10.18
109
+ overrides==7.7.0
110
+ packaging==24.2
111
+ pandas==2.2.3
112
+ pillow==11.2.1
113
+ pluggy==1.5.0
114
+ posthog==4.0.1
115
+ primp==0.15.0
116
+ propcache==0.3.1
117
+ protobuf==5.29.4
118
+ psutil==7.0.0
119
+ pyarrow==20.0.0
120
+ pyasn1==0.6.1
121
+ pyasn1_modules==0.4.2
122
+ pycparser==2.22
123
+ pydantic==2.11.4
124
+ pydantic-settings==2.9.1
125
+ pydantic_core==2.33.2
126
+ pydub==0.25.1
127
+ Pygments==2.19.1
128
+ pypdf==5.4.0
129
+ PyPika==0.48.9
130
+ pyproject_hooks==1.2.0
131
+ pytest==8.3.5
132
+ pytest-mock==3.14.0
133
+ python-dateutil==2.9.0.post0
134
+ python-dotenv==1.1.0
135
+ python-iso639==2025.2.18
136
+ python-magic==0.4.27
137
+ python-multipart==0.0.20
138
+ python-oxmsg==0.0.2
139
+ pytz==2025.2
140
+ PyYAML==6.0.2
141
+ RapidFuzz==3.13.0
142
+ referencing==0.36.2
143
+ regex==2024.11.6
144
+ requests==2.32.3
145
+ requests-oauthlib==2.0.0
146
+ requests-toolbelt==1.0.0
147
+ rich==14.0.0
148
+ rpds-py==0.25.0
149
+ rsa==4.9.1
150
+ ruff==0.11.8
151
+ safehttpx==0.1.6
152
+ se-agents @ git+https://github.com/generalsoftwareinc/SE-Agents.git@ac05cdf08c06eef8d9dd8abe9933ed372d25e414
153
+ semantic-version==2.10.0
154
+ shellingham==1.5.4
155
+ six==1.17.0
156
+ sniffio==1.3.1
157
+ soupsieve==2.7
158
+ SQLAlchemy==2.0.40
159
+ starlette==0.45.3
160
+ sympy==1.14.0
161
+ tenacity==9.1.2
162
+ tiktoken==0.9.0
163
+ tokenizers==0.21.1
164
+ tomlkit==0.13.2
165
+ tqdm==4.67.1
166
+ typer==0.15.3
167
+ typing-inspect==0.9.0
168
+ typing-inspection==0.4.0
169
+ typing_extensions==4.13.2
170
+ tzdata==2025.2
171
+ unstructured==0.17.2
172
+ unstructured-client==0.34.0
173
+ urllib3==2.4.0
174
+ uvicorn==0.34.2
175
+ uvloop==0.21.0
176
+ watchfiles==1.0.5
177
+ webencodings==0.5.1
178
+ websocket-client==1.8.0
179
+ websockets==15.0.1
180
+ wrapt==1.17.2
181
+ xxhash==3.5.0
182
+ yarl==1.20.0
183
+ zipp==3.21.0
184
+ zstandard==0.23.0
tools.py ADDED
@@ -0,0 +1,211 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Custom tools go in here"""
2
+
3
+ from google import genai
4
+ from se_agents.tools import Tool
5
+ from se_agents.agent import Agent
6
+ from se_agents.runner import Runner
7
+ from openai import OpenAI
8
+ import types
9
+ import requests
10
+ from config import Config
11
+ from langchain_community.document_loaders import (
12
+ UnstructuredExcelLoader,
13
+ TextLoader,
14
+ PyPDFLoader,
15
+ )
16
+ from langchain.text_splitter import RecursiveCharacterTextSplitter
17
+ from langchain_openai import OpenAIEmbeddings
18
+ from langchain_community.vectorstores import Chroma
19
+ from openai import Client
20
+ from pathlib import Path
21
+ from utils import read_file_content
22
+ from google.genai import types
23
+
24
+
25
+ class YoutubeVideoInterpreter(Tool):
26
+ def __init__(self):
27
+ super().__init__(
28
+ name="youtube_video_interpreter",
29
+ description="Given a certain youtube video url, it analyzes the video and response any question asked by the user",
30
+ parameters={
31
+ "query": {
32
+ "type": "string",
33
+ "description": "question about the video",
34
+ "required": True,
35
+ },
36
+ "video_url": {
37
+ "type": "string",
38
+ "description": "the url of the video",
39
+ "required": True,
40
+ },
41
+ },
42
+ )
43
+
44
+ def execute(self, **kwargs) -> str:
45
+ params = self._process_parameters(**kwargs)
46
+ query = params.get("query")
47
+ video_url = params.get("video_url")
48
+ client = genai.Client(api_key=Config.get_gemini_api_key())
49
+ response = client.models.generate_content(
50
+ model="models/gemini-2.0-flash",
51
+ contents=types.Content(
52
+ parts=[
53
+ types.Part(file_data=types.FileData(file_uri=video_url)),
54
+ types.Part(text=query),
55
+ ]
56
+ ),
57
+ )
58
+ return response.text
59
+
60
+
61
+ class TaskFileDownloader(Tool):
62
+ def __init__(self):
63
+ super().__init__(
64
+ name="task_file_downloader",
65
+ description="Given a certain Taks id, it downloads the complementary file, outputs the path of the file",
66
+ parameters={
67
+ "task_id": {
68
+ "type": "string",
69
+ "description": "the id of the task",
70
+ "required": True,
71
+ },
72
+ "complementary_file": {
73
+ "type": "string",
74
+ "description": "the name with extension of the file",
75
+ "required": True,
76
+ },
77
+ },
78
+ )
79
+
80
+ def execute(self, **kwargs) -> str:
81
+ params = self._process_parameters(**kwargs)
82
+ task_id = params.get("task_id")
83
+ complementary_file_ext = params.get("complementary_file").split(".")[-1]
84
+ response = requests.get(f"{Config.get_default_api_url()}/files/{task_id}")
85
+ # Verify the request was successful
86
+ if response.status_code == 200:
87
+ # Save the file
88
+ with open(
89
+ f"{Config.get_task_file_folder()}/{task_id}_complementary_file.{complementary_file_ext}",
90
+ "wb",
91
+ ) as file:
92
+ file.write(response.content)
93
+ return f"{Config.get_task_file_folder()}/{task_id}_complementary_file.{complementary_file_ext}"
94
+ else:
95
+ return f"Failed to retrieve file. Status code: {response.status_code}"
96
+
97
+
98
+ class RetriveInfoTaskFile(Tool):
99
+
100
+ def __init__(self):
101
+ super().__init__(
102
+ name="retrive_info_task_file",
103
+ description="Given a certain Taks file path, outputs info related to the file",
104
+ parameters={
105
+ "complementary_file": {
106
+ "type": "string",
107
+ "description": "the path with extension of the file",
108
+ "required": True,
109
+ },
110
+ "query": {
111
+ "type": "string",
112
+ "description": "question about the file",
113
+ "required": True,
114
+ },
115
+ },
116
+ )
117
+ self.retriver = {
118
+ ".xlsx": UnstructuredExcelLoader,
119
+ ".pdf": PyPDFLoader,
120
+ ".txt": TextLoader,
121
+ }
122
+
123
+ def execute(self, **kwargs) -> str:
124
+ params = self._process_parameters(**kwargs)
125
+ complementary_file = params.get("complementary_file")
126
+ query = params.get("query")
127
+ complementary_file_path = Path(complementary_file)
128
+
129
+ # Validate file format
130
+ if complementary_file_path.suffix not in self.retriver:
131
+ return f"Unsupported file format: {complementary_file_path.suffix}"
132
+
133
+ # Load and process the document
134
+ loader = self.retriver[complementary_file_path.suffix](complementary_file_path)
135
+ documents = loader.load()
136
+ text_splitter = RecursiveCharacterTextSplitter(
137
+ chunk_size=1000, chunk_overlap=100
138
+ )
139
+ docs = text_splitter.split_documents(documents)
140
+
141
+ # Embed and retrieve relevant information
142
+ embeddings = OpenAIEmbeddings()
143
+ vectorstore = Chroma.from_documents(docs, embeddings)
144
+ retriever = vectorstore.as_retriever()
145
+ results = retriever.invoke(query)
146
+
147
+ # Return formatted results or a fallback message
148
+ return (
149
+ "\n\n".join([doc.page_content for doc in results[:3]])
150
+ if results
151
+ else "No relevant information found."
152
+ )
153
+
154
+
155
+ class SpeechToText(Tool):
156
+ def __init__(self):
157
+ super().__init__(
158
+ name="speech_to_text",
159
+ description="Given a certain audio file path, outputs the path to a .txt file with the transcription",
160
+ parameters={
161
+ "audio_file_path": {
162
+ "type": "string",
163
+ "description": "the name with extension of the file",
164
+ "required": True,
165
+ },
166
+ },
167
+ )
168
+
169
+ async def execute(self, **kwargs) -> str:
170
+ params = self._process_parameters(**kwargs)
171
+ audio_file_path = params.get("audio_file_path")
172
+ client = OpenAI(api_key=Config.get_openai_api_key())
173
+ if Path(audio_file_path).exists():
174
+ with open(audio_file_path, "rb") as audio_file:
175
+ transcription = client.audio.transcriptions.create(
176
+ model="gpt-4o-transcribe", file=audio_file
177
+ )
178
+
179
+ # Write transcription to a .txt file
180
+ txt_file_path = Path(audio_file_path).with_suffix(".txt")
181
+ with open(txt_file_path, "w") as txt_file:
182
+ txt_file.write(transcription.text)
183
+
184
+ return f"Transcription saved to {txt_file_path}"
185
+ else:
186
+ return "Audio file does not exist"
187
+
188
+
189
+ class CodeInterpreter(Tool):
190
+ def __init__(self):
191
+ super().__init__(
192
+ name="code_interpreter",
193
+ description="Given a certain code file path, outputs the content of the file",
194
+ parameters={
195
+ "code_file_path": {
196
+ "type": "string",
197
+ "description": "the name with extension of the file",
198
+ "required": True,
199
+ },
200
+ },
201
+ )
202
+
203
+ async def execute(self, **kwargs) -> str:
204
+ params = self._process_parameters(**kwargs)
205
+ code_file_path = params.get("code_file_path")
206
+ client = OpenAI(api_key=Config.get_openai_api_key())
207
+ if Path(code_file_path).exists():
208
+ content = read_file_content(code_file_path)
209
+ return content
210
+ else:
211
+ return "Code file does not exist"
utils.py ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ def read_file_content(file_path: str) -> str:
2
+ """
3
+ Reads and returns the content of a file.
4
+
5
+ Args:
6
+ file_path (str): The path to the file to read.
7
+
8
+ Returns:
9
+ str: The content of the file.
10
+
11
+ Raises:
12
+ FileNotFoundError: If the file does not exist.
13
+ IOError: If the file cannot be read.
14
+ """
15
+ try:
16
+ with open(file_path, "r") as file:
17
+ content = file.read()
18
+ return content
19
+ except FileNotFoundError:
20
+ raise FileNotFoundError(f"The file '{file_path}' does not exist.")
21
+ except IOError:
22
+ raise IOError(f"Unable to read the file '{file_path}'.")