Commit
·
62ed5af
1
Parent(s):
81917a3
Attempting LlamaIndex approach
Browse files- .gitignore +3 -0
- agents.py +75 -0
- app.py +105 -39
- requirements.txt +5 -1
- tools.py +119 -0
.gitignore
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.env
|
| 2 |
+
__pycache__/
|
| 3 |
+
files/
|
agents.py
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
from llama_index.core.agent.workflow import ReActAgent, FunctionAgent
|
| 3 |
+
from llama_index.core import PromptTemplate
|
| 4 |
+
from llama_index.llms.huggingface_api import HuggingFaceInferenceAPI
|
| 5 |
+
from tools import (
|
| 6 |
+
search_tool,
|
| 7 |
+
describe_image_tool,
|
| 8 |
+
parse_excel_tool,
|
| 9 |
+
access_webpage_tool,
|
| 10 |
+
string_functions_tool
|
| 11 |
+
)
|
| 12 |
+
|
| 13 |
+
thinking_agent = ReActAgent(
|
| 14 |
+
name="Thinking Agent",
|
| 15 |
+
description="An agent that can think and reason about tasks, and then handoff the task to Technician Agent for execution, or to Manager Agent for review.",
|
| 16 |
+
llm=HuggingFaceInferenceAPI(
|
| 17 |
+
model_name="Qwen/Qwen2.5-Coder-32B-Instruct",
|
| 18 |
+
provider="auto",
|
| 19 |
+
token=os.environ.get("HF_TOKEN")
|
| 20 |
+
),
|
| 21 |
+
system_prompt="You are a thinking agent that can reason about tasks and communicate the necessary steps to complete them to Technician Agent, if necessary. If you believe the task is completed and the question is answered, you must handoff the answer to Manager Agent for final review.",
|
| 22 |
+
can_handoff_to=["Technician Agent", "Manager Agent"]
|
| 23 |
+
)
|
| 24 |
+
|
| 25 |
+
technician_agent = ReActAgent(
|
| 26 |
+
name="Technician Agent",
|
| 27 |
+
description="An agent that can perform various technical tasks such as searching the web, describing images, parsing Excel files, string operations, and accessing webpages.",
|
| 28 |
+
tools=[search_tool, describe_image_tool, parse_excel_tool, access_webpage_tool, string_functions_tool],
|
| 29 |
+
llm=HuggingFaceInferenceAPI(
|
| 30 |
+
model_name="Qwen/Qwen2.5-Coder-32B-Instruct",
|
| 31 |
+
provider="auto",
|
| 32 |
+
token=os.environ.get("HF_TOKEN")
|
| 33 |
+
),
|
| 34 |
+
system_prompt="You are a helpful agent that answers questions based on the provided tools. Use the tools to gather information and provide accurate answers, and send those answers to Thinking Agent. If the task is too complex or requires further reasoning, handoff the task to Thinking Agent for analysis with the reasoning as why you cannot complete it. You must always handoff to Thinking Agent",
|
| 35 |
+
can_handoff_to=["Thinking Agent"]
|
| 36 |
+
)
|
| 37 |
+
|
| 38 |
+
manager_agent = ReActAgent(
|
| 39 |
+
name="Manager Agent",
|
| 40 |
+
description="A high-level agent that can manage tasks and coordinate between other agents.",
|
| 41 |
+
llm=HuggingFaceInferenceAPI(
|
| 42 |
+
model_name="Qwen/Qwen2.5-Coder-32B-Instruct",
|
| 43 |
+
provider="auto",
|
| 44 |
+
token=os.environ.get("HF_TOKEN")
|
| 45 |
+
),
|
| 46 |
+
can_handoff_to=["Thinking Agent"],
|
| 47 |
+
# system_prompt="You are a manager agent that oversees tasks and coordinates between other agents. Do not include thoughts in your response. Just the answer. " \
|
| 48 |
+
# "You will be given a question and you need to respond with the correct answer provided by the other agents. " \
|
| 49 |
+
# "Your response should be a number OR as few words as possible OR a comma separated list of numbers and/or strings. " \
|
| 50 |
+
# "For example, if the question is 'What color are the stars on the American flag?' Your response would be 'White'. " \
|
| 51 |
+
# "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. " \
|
| 52 |
+
# "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. " \
|
| 53 |
+
# "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. " \
|
| 54 |
+
# "Do not engage in conversation, just respond with the answer unless the question explicitly asks for a certain style of response. " \
|
| 55 |
+
# "If the results are questionable, you can send the task back to Thinking Agent for further analysis. If the answer can not be concluded, respond with 'I don't know'"
|
| 56 |
+
)
|
| 57 |
+
|
| 58 |
+
minimal_system_prompt = """
|
| 59 |
+
You are the Manager Agent. 💼
|
| 60 |
+
Respond with **only the final answer**, in as few words as possible.
|
| 61 |
+
Do **not** include any reasoning, thoughts, or tool calls.
|
| 62 |
+
If it's a number: use plain digits (no commas, %, etc.).
|
| 63 |
+
If it's a string: no articles, no abbreviations.
|
| 64 |
+
If it's a list: comma-separated, each element following the rules above.
|
| 65 |
+
If unsure, reply: I don't know.
|
| 66 |
+
Below is the current conversation consisting of interleaving human and assistant messages.
|
| 67 |
+
"""
|
| 68 |
+
|
| 69 |
+
manager_prompt = PromptTemplate(template=minimal_system_prompt)
|
| 70 |
+
|
| 71 |
+
manager_agent.update_prompts({"react_header", manager_prompt.get_template()})
|
| 72 |
+
|
| 73 |
+
prompt_dict = manager_agent.get_prompts()
|
| 74 |
+
for k, v in prompt_dict.items():
|
| 75 |
+
print(f"Prompt: {k}\n\nValue: {v.template}")
|
app.py
CHANGED
|
@@ -1,25 +1,67 @@
|
|
|
|
|
| 1 |
import os
|
| 2 |
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 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
"""
|
| 24 |
Fetches all questions, runs the BasicAgent on them, submits all answers,
|
| 25 |
and displays the results.
|
|
@@ -27,20 +69,23 @@ def run_and_submit_all( profile: gr.OAuthProfile | None):
|
|
| 27 |
# --- Determine HF Space Runtime URL and Repo URL ---
|
| 28 |
space_id = os.getenv("SPACE_ID") # Get the SPACE_ID for sending link to the code
|
| 29 |
|
| 30 |
-
if profile:
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
else:
|
| 34 |
-
|
| 35 |
-
|
| 36 |
|
| 37 |
api_url = DEFAULT_API_URL
|
| 38 |
questions_url = f"{api_url}/questions"
|
| 39 |
submit_url = f"{api_url}/submit"
|
| 40 |
|
| 41 |
-
# 1. Instantiate Agent ( modify this part to create your agent)
|
| 42 |
try:
|
| 43 |
-
agent =
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
except Exception as e:
|
| 45 |
print(f"Error instantiating agent: {e}")
|
| 46 |
return f"Error initializing agent: {e}", None
|
|
@@ -73,14 +118,34 @@ 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")
|
| 79 |
if not task_id or question_text is None:
|
| 80 |
print(f"Skipping item with missing task_id or question: {item}")
|
| 81 |
continue
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 82 |
try:
|
| 83 |
-
|
|
|
|
| 84 |
answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
|
| 85 |
results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
|
| 86 |
except Exception as e:
|
|
@@ -92,26 +157,27 @@ def run_and_submit_all( profile: gr.OAuthProfile | None):
|
|
| 92 |
return "Agent did not produce any answers to submit.", pd.DataFrame(results_log)
|
| 93 |
|
| 94 |
# 4. Prepare Submission
|
| 95 |
-
submission_data = {"username": username.strip(), "agent_code": agent_code, "answers": answers_payload}
|
| 96 |
-
status_update = f"Agent finished. Submitting {len(answers_payload)} answers for user '{username}'..."
|
| 97 |
-
print(status_update)
|
| 98 |
|
| 99 |
# 5. Submit
|
| 100 |
print(f"Submitting {len(answers_payload)} answers to: {submit_url}")
|
| 101 |
try:
|
| 102 |
-
response = requests.post(submit_url, json=submission_data, timeout=60)
|
| 103 |
-
response.raise_for_status()
|
| 104 |
-
result_data = response.json()
|
| 105 |
-
final_status = (
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
)
|
| 112 |
-
print("Submission successful.")
|
| 113 |
results_df = pd.DataFrame(results_log)
|
| 114 |
-
return
|
|
|
|
| 115 |
except requests.exceptions.HTTPError as e:
|
| 116 |
error_detail = f"Server responded with status {e.response.status_code}."
|
| 117 |
try:
|
|
@@ -158,7 +224,7 @@ with gr.Blocks() as demo:
|
|
| 158 |
"""
|
| 159 |
)
|
| 160 |
|
| 161 |
-
gr.LoginButton()
|
| 162 |
|
| 163 |
run_button = gr.Button("Run Evaluation & Submit All Answers")
|
| 164 |
|
|
|
|
| 1 |
+
import base64
|
| 2 |
import os
|
| 3 |
import gradio as gr
|
| 4 |
import requests
|
| 5 |
import inspect
|
| 6 |
+
import dotenv
|
| 7 |
+
dotenv.load_dotenv() # Load environment variables from .env file
|
| 8 |
import pandas as pd
|
| 9 |
+
from llama_index.llms.huggingface_api import HuggingFaceInferenceAPI
|
| 10 |
+
from llama_index.core.agent.workflow import AgentWorkflow, AgentOutput, ToolCall, ToolCallResult, AgentInput, AgentStream
|
| 11 |
+
from agents import thinking_agent, technician_agent, manager_agent
|
| 12 |
+
import asyncio
|
| 13 |
# (Keep Constants as is)
|
| 14 |
# --- Constants ---
|
| 15 |
DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
|
| 16 |
|
| 17 |
+
llm = HuggingFaceInferenceAPI(model_name="deepseek-ai/DeepSeek-R1", provider="auto", token=os.environ.get("HF_TOKEN"))
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
#def run_and_submit_all( profile: gr.OAuthProfile | None):
|
| 21 |
+
|
| 22 |
+
async def run_agent_query(agent: AgentWorkflow, question: str):
|
| 23 |
+
"""
|
| 24 |
+
Runs the agent on a single question and returns the answer.
|
| 25 |
+
This function is intended to be used with Gradio for interactive querying.
|
| 26 |
+
"""
|
| 27 |
+
|
| 28 |
+
handler = agent.run(user_msg=question)
|
| 29 |
+
current_agent = None
|
| 30 |
+
final_response = None
|
| 31 |
+
current_tool_calls = ""
|
| 32 |
+
async for event in handler.stream_events():
|
| 33 |
+
if (
|
| 34 |
+
hasattr(event, "current_agent_name")
|
| 35 |
+
and event.current_agent_name != current_agent
|
| 36 |
+
):
|
| 37 |
+
current_agent = event.current_agent_name
|
| 38 |
+
print(f"\n{'='*50}")
|
| 39 |
+
print(f"🤖 Agent: {current_agent}")
|
| 40 |
+
print(f"{'='*50}\n")
|
| 41 |
+
|
| 42 |
+
if isinstance(event, AgentStream):
|
| 43 |
+
if event.delta:
|
| 44 |
+
print(event.delta, end="", flush=True)
|
| 45 |
+
elif isinstance(event, AgentInput):
|
| 46 |
+
print("📥 Input:", event.input)
|
| 47 |
+
elif isinstance(event, AgentOutput):
|
| 48 |
+
if event.response.content:
|
| 49 |
+
print("📤 Output:", event.response.content)
|
| 50 |
+
final_response = event.response.content
|
| 51 |
+
if event.tool_calls:
|
| 52 |
+
print(
|
| 53 |
+
"🛠️ Planning to use tools:",
|
| 54 |
+
[call.tool_name for call in event.tool_calls],
|
| 55 |
+
)
|
| 56 |
+
elif isinstance(event, ToolCallResult):
|
| 57 |
+
print(f"🔧 Tool Result ({event.tool_name}):")
|
| 58 |
+
print(f" Arguments: {event.tool_kwargs}")
|
| 59 |
+
print(f" Output: {event.tool_output}")
|
| 60 |
+
elif isinstance(event, ToolCall):
|
| 61 |
+
print(f"🔨 Calling Tool: {event.tool_name}")
|
| 62 |
+
print(f" With arguments: {event.tool_kwargs}")
|
| 63 |
+
return final_response if final_response else "No response from agent."
|
| 64 |
+
def run_and_submit_all():
|
| 65 |
"""
|
| 66 |
Fetches all questions, runs the BasicAgent on them, submits all answers,
|
| 67 |
and displays the results.
|
|
|
|
| 69 |
# --- Determine HF Space Runtime URL and Repo URL ---
|
| 70 |
space_id = os.getenv("SPACE_ID") # Get the SPACE_ID for sending link to the code
|
| 71 |
|
| 72 |
+
# if profile:
|
| 73 |
+
# username= f"{profile.username}"
|
| 74 |
+
# print(f"User logged in: {username}")
|
| 75 |
+
# else:
|
| 76 |
+
# print("User not logged in.")
|
| 77 |
+
# return "Please Login to Hugging Face with the button.", None
|
| 78 |
|
| 79 |
api_url = DEFAULT_API_URL
|
| 80 |
questions_url = f"{api_url}/questions"
|
| 81 |
submit_url = f"{api_url}/submit"
|
| 82 |
|
|
|
|
| 83 |
try:
|
| 84 |
+
agent = AgentWorkflow(
|
| 85 |
+
agents=[thinking_agent, technician_agent, manager_agent],
|
| 86 |
+
root_agent=manager_agent.name,
|
| 87 |
+
handoff_output_prompt="handoff_result: Passed to {to_agent}. Reason: {reason}. Please continue processing using the original user question."
|
| 88 |
+
)
|
| 89 |
except Exception as e:
|
| 90 |
print(f"Error instantiating agent: {e}")
|
| 91 |
return f"Error initializing agent: {e}", None
|
|
|
|
| 118 |
results_log = []
|
| 119 |
answers_payload = []
|
| 120 |
print(f"Running agent on {len(questions_data)} questions...")
|
| 121 |
+
for item in questions_data[:4]:
|
| 122 |
task_id = item.get("task_id")
|
| 123 |
question_text = item.get("question")
|
| 124 |
if not task_id or question_text is None:
|
| 125 |
print(f"Skipping item with missing task_id or question: {item}")
|
| 126 |
continue
|
| 127 |
+
file_name = item.get("file_name")
|
| 128 |
+
if file_name:
|
| 129 |
+
try:
|
| 130 |
+
response = requests.get(f"{api_url}/files/{task_id}", timeout=15)
|
| 131 |
+
response.raise_for_status()
|
| 132 |
+
save_path = os.path.join("files", file_name)
|
| 133 |
+
os.makedirs("files", exist_ok=True)
|
| 134 |
+
with open(save_path, "wb") as f:
|
| 135 |
+
f.write(response.content)
|
| 136 |
+
question_text += f" (File: {save_path})"
|
| 137 |
+
except requests.exceptions.RequestException as e:
|
| 138 |
+
print(f"Error fetching file for task {task_id}: {e}")
|
| 139 |
+
return f"Error fetching file for task {task_id}: {e}", None
|
| 140 |
+
except requests.exceptions.JSONDecodeError as e:
|
| 141 |
+
print(f"Error decoding JSON response for file {file_name}: {e}")
|
| 142 |
+
return f"Error decoding JSON response for file {file_name}: {e}", None
|
| 143 |
+
except Exception as e:
|
| 144 |
+
print(f"An unexpected error occurred fetching file {file_name}: {e}")
|
| 145 |
+
return f"An unexpected error occurred fetching file {file_name}: {e}", None
|
| 146 |
try:
|
| 147 |
+
print(f"Running agent on task {task_id} with question: {question_text}")
|
| 148 |
+
submitted_answer = asyncio.run(run_agent_query(agent, question_text))
|
| 149 |
answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
|
| 150 |
results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
|
| 151 |
except Exception as e:
|
|
|
|
| 157 |
return "Agent did not produce any answers to submit.", pd.DataFrame(results_log)
|
| 158 |
|
| 159 |
# 4. Prepare Submission
|
| 160 |
+
# submission_data = {"username": username.strip(), "agent_code": agent_code, "answers": answers_payload}
|
| 161 |
+
# status_update = f"Agent finished. Submitting {len(answers_payload)} answers for user '{username}'..."
|
| 162 |
+
# print(status_update)
|
| 163 |
|
| 164 |
# 5. Submit
|
| 165 |
print(f"Submitting {len(answers_payload)} answers to: {submit_url}")
|
| 166 |
try:
|
| 167 |
+
# response = requests.post(submit_url, json=submission_data, timeout=60)
|
| 168 |
+
# response.raise_for_status()
|
| 169 |
+
# result_data = response.json()
|
| 170 |
+
# final_status = (
|
| 171 |
+
# f"Submission Successful!\n"
|
| 172 |
+
# f"User: {result_data.get('username')}\n"
|
| 173 |
+
# f"Overall Score: {result_data.get('score', 'N/A')}% "
|
| 174 |
+
# f"({result_data.get('correct_count', '?')}/{result_data.get('total_attempted', '?')} correct)\n"
|
| 175 |
+
# f"Message: {result_data.get('message', 'No message received.')}"
|
| 176 |
+
# )
|
| 177 |
+
# print("Submission successful.")
|
| 178 |
results_df = pd.DataFrame(results_log)
|
| 179 |
+
return "Nothing submitted", results_df
|
| 180 |
+
# return final_status, results_df
|
| 181 |
except requests.exceptions.HTTPError as e:
|
| 182 |
error_detail = f"Server responded with status {e.response.status_code}."
|
| 183 |
try:
|
|
|
|
| 224 |
"""
|
| 225 |
)
|
| 226 |
|
| 227 |
+
# gr.LoginButton()
|
| 228 |
|
| 229 |
run_button = gr.Button("Run Evaluation & Submit All Answers")
|
| 230 |
|
requirements.txt
CHANGED
|
@@ -1,2 +1,6 @@
|
|
| 1 |
gradio
|
| 2 |
-
requests
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
gradio
|
| 2 |
+
requests
|
| 3 |
+
llama-index-multi-modal-llms-huggingface
|
| 4 |
+
llama-index
|
| 5 |
+
llama-index-llms-huggingface-api
|
| 6 |
+
llama-index-readers-whisper
|
tools.py
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from llama_index.core.tools import FunctionTool
|
| 2 |
+
from llama_index.tools.tavily_research import TavilyToolSpec
|
| 3 |
+
from llama_index.core.schema import ImageDocument
|
| 4 |
+
from llama_index.readers.whisper import WhisperReader
|
| 5 |
+
|
| 6 |
+
import os
|
| 7 |
+
|
| 8 |
+
tool_spec = TavilyToolSpec(
|
| 9 |
+
api_key=os.environ.get("TAVILY_API_KEY"),
|
| 10 |
+
)
|
| 11 |
+
|
| 12 |
+
search_tool = FunctionTool.from_defaults(tool_spec.search)
|
| 13 |
+
|
| 14 |
+
from llama_index.multi_modal_llms.huggingface import HuggingFaceMultiModal
|
| 15 |
+
|
| 16 |
+
def describe_image(image_path: str, prompt: str = "Describe the following image:") -> str:
|
| 17 |
+
"""
|
| 18 |
+
Function to describe an image using a multi-modal LLM.
|
| 19 |
+
:param image_path: Path to the image file or base64 encoded image data.
|
| 20 |
+
:param prompt: Prompt to use for the description. Defaults to "Describe the following image:".
|
| 21 |
+
:return: Description of the image.
|
| 22 |
+
"""
|
| 23 |
+
image = ImageDocument(image_path=image_path)
|
| 24 |
+
try:
|
| 25 |
+
llm = HuggingFaceMultiModal.from_model_name("Qwen/Qwen2-VL-2B-Instruct", use_fast=True)
|
| 26 |
+
return llm.complete(
|
| 27 |
+
prompt=f"{prompt}",
|
| 28 |
+
image_documents=[image]
|
| 29 |
+
).text
|
| 30 |
+
except Exception as e:
|
| 31 |
+
return f"Error describing image: {e}"
|
| 32 |
+
|
| 33 |
+
describe_image_tool = FunctionTool.from_defaults(describe_image)
|
| 34 |
+
|
| 35 |
+
# Tool to parse xls/xlsx files
|
| 36 |
+
def parse_excel(file_path: str) -> str:
|
| 37 |
+
"""
|
| 38 |
+
Function to parse an Excel file and return its content as a string.
|
| 39 |
+
:param file_path: Path to the Excel file (xls or xlsx).
|
| 40 |
+
:return: Content of the Excel file as a string.
|
| 41 |
+
"""
|
| 42 |
+
import pandas as pd
|
| 43 |
+
|
| 44 |
+
df = pd.read_excel(io=file_path)
|
| 45 |
+
|
| 46 |
+
# Convert DataFrame to string
|
| 47 |
+
return df.to_string() if not df.empty else "The Excel file is empty."
|
| 48 |
+
|
| 49 |
+
parse_excel_tool = FunctionTool.from_defaults(parse_excel)
|
| 50 |
+
|
| 51 |
+
def access_webpage(url: str) -> str:
|
| 52 |
+
"""
|
| 53 |
+
Function to access a webpage and return its content.
|
| 54 |
+
:param url: URL of the webpage to access.
|
| 55 |
+
:return: Content of the webpage.
|
| 56 |
+
"""
|
| 57 |
+
import requests
|
| 58 |
+
try:
|
| 59 |
+
print(f"Accessing webpage: {url}")
|
| 60 |
+
response = requests.get(url)
|
| 61 |
+
print(f"Response status code: {response.status_code}")
|
| 62 |
+
response.raise_for_status() # Raise an error for bad responses
|
| 63 |
+
return response.text
|
| 64 |
+
except requests.RequestException as e:
|
| 65 |
+
return f"Error accessing {url}: {e}"
|
| 66 |
+
except Exception as e:
|
| 67 |
+
return f"An unexpected error occurred: {e}"
|
| 68 |
+
|
| 69 |
+
access_webpage_tool = FunctionTool.from_defaults(access_webpage)
|
| 70 |
+
|
| 71 |
+
def string_functions(input_string: str, operation: str) -> str:
|
| 72 |
+
"""
|
| 73 |
+
Function to perform string operations.
|
| 74 |
+
:param input_string: The input string to operate on.
|
| 75 |
+
:param operation: The operation to perform (e.g., "uppercase", "lowercase", "reverse", "length", "count_vowels", "count_consonants", "count_words", "count_sentences").
|
| 76 |
+
:return: Result of the string operation.
|
| 77 |
+
"""
|
| 78 |
+
if operation == "uppercase":
|
| 79 |
+
return input_string.upper()
|
| 80 |
+
elif operation == "lowercase":
|
| 81 |
+
return input_string.lower()
|
| 82 |
+
elif operation == "reverse":
|
| 83 |
+
return input_string[::-1]
|
| 84 |
+
elif operation == "length":
|
| 85 |
+
return str(len(input_string))
|
| 86 |
+
elif operation == "count_vowels":
|
| 87 |
+
vowels = "aeiouAEIOU"
|
| 88 |
+
return str(sum(1 for char in input_string if char in vowels))
|
| 89 |
+
elif operation == "count_consonants":
|
| 90 |
+
vowels = "aeiouAEIOU"
|
| 91 |
+
return str(sum(1 for char in input_string if char.isalpha() and char not in vowels))
|
| 92 |
+
elif operation == "count_words":
|
| 93 |
+
return str(len(input_string.split()))
|
| 94 |
+
elif operation == "count_sentences":
|
| 95 |
+
import re
|
| 96 |
+
sentences = re.split(r'[.!?]+', input_string)
|
| 97 |
+
return str(len([s for s in sentences if s.strip()]))
|
| 98 |
+
else:
|
| 99 |
+
return "Invalid operation. Supported operations: uppercase, lowercase, reverse."
|
| 100 |
+
|
| 101 |
+
string_functions_tool = FunctionTool.from_defaults(string_functions)
|
| 102 |
+
|
| 103 |
+
def transcribe_audio(audio_path: str) -> str:
|
| 104 |
+
"""
|
| 105 |
+
Function to transcribe audio using a multi-modal LLM.
|
| 106 |
+
:param audio_path: Path to the audio file.
|
| 107 |
+
:return: Transcription of the audio.
|
| 108 |
+
"""
|
| 109 |
+
try:
|
| 110 |
+
reader = WhisperReader(api_key=os.environ.get("OPENAI_API_KEY"))
|
| 111 |
+
documents = reader.load_data(file=audio_path)
|
| 112 |
+
if not documents:
|
| 113 |
+
return "No audio content found."
|
| 114 |
+
# Assuming the first document contains the transcription
|
| 115 |
+
return documents[0].text if documents else "No transcription available."
|
| 116 |
+
except Exception as e:
|
| 117 |
+
return f"Error transcribing audio: {e}"
|
| 118 |
+
|
| 119 |
+
transcribe_audio_tool = FunctionTool.from_defaults(transcribe_audio)
|