Commit ·
4754c75
1
Parent(s): b527097
add tool to get webpage content + replace DDG by Brave search + replace buggy remote code interpreter by local runtime + avoid limit of TPM
Browse files- .gitignore +3 -1
- app.py +8 -1
- langgraph_dir/agent.py +11 -9
- langgraph_dir/config.py +4 -1
- langgraph_dir/custom_tools.py +51 -3
- langgraph_dir/prompt.py +1 -0
- requirements.txt +4 -2
.gitignore
CHANGED
|
@@ -1 +1,3 @@
|
|
| 1 |
-
__pycache__
|
|
|
|
|
|
|
|
|
| 1 |
+
__pycache__
|
| 2 |
+
.DS_Store
|
| 3 |
+
tmp*
|
app.py
CHANGED
|
@@ -2,6 +2,8 @@ import os
|
|
| 2 |
import gradio as gr
|
| 3 |
import requests
|
| 4 |
import pandas as pd
|
|
|
|
|
|
|
| 5 |
|
| 6 |
|
| 7 |
# (Keep Constants as is)
|
|
@@ -89,7 +91,7 @@ async def run_and_submit_all(profile: gr.OAuthProfile | None):
|
|
| 89 |
if file_name:
|
| 90 |
# add the URL of the data source to the question (so that the agent can deal with it)
|
| 91 |
file_url = f"{DEFAULT_API_URL}/files/{task_id}"
|
| 92 |
-
question_text += f
|
| 93 |
# get the extension of the file to help the agent
|
| 94 |
try:
|
| 95 |
ext = file_name.split('.')[-1]
|
|
@@ -104,6 +106,11 @@ async def run_and_submit_all(profile: gr.OAuthProfile | None):
|
|
| 104 |
submitted_answer = agent(question_text)
|
| 105 |
answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
|
| 106 |
results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 107 |
except Exception as e:
|
| 108 |
print(f"Error running agent on task {task_id}: {e}")
|
| 109 |
results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": f"AGENT ERROR: {e}"})
|
|
|
|
| 2 |
import gradio as gr
|
| 3 |
import requests
|
| 4 |
import pandas as pd
|
| 5 |
+
from time import sleep
|
| 6 |
+
from tqdm import tqdm
|
| 7 |
|
| 8 |
|
| 9 |
# (Keep Constants as is)
|
|
|
|
| 91 |
if file_name:
|
| 92 |
# add the URL of the data source to the question (so that the agent can deal with it)
|
| 93 |
file_url = f"{DEFAULT_API_URL}/files/{task_id}"
|
| 94 |
+
question_text += f'\nFile URL: "{file_url}"'
|
| 95 |
# get the extension of the file to help the agent
|
| 96 |
try:
|
| 97 |
ext = file_name.split('.')[-1]
|
|
|
|
| 106 |
submitted_answer = agent(question_text)
|
| 107 |
answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
|
| 108 |
results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
|
| 109 |
+
|
| 110 |
+
# wait 1 minute before next call to avoid reaching limit of token per minute (TPM)
|
| 111 |
+
print('\n\n-> Sleeping for 1 minute to avoid reaching limit of token per minute (TPM)')
|
| 112 |
+
for _ in tqdm(range(60)): # tqdm to see time we have to wait
|
| 113 |
+
sleep(1)
|
| 114 |
except Exception as e:
|
| 115 |
print(f"Error running agent on task {task_id}: {e}")
|
| 116 |
results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": f"AGENT ERROR: {e}"})
|
langgraph_dir/agent.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
|
|
| 1 |
import json
|
| 2 |
|
| 3 |
from typing import Literal
|
|
@@ -6,11 +7,11 @@ from langgraph.graph import MessagesState
|
|
| 6 |
from langchain_core.messages import SystemMessage, HumanMessage, ToolMessage
|
| 7 |
from langgraph.graph import StateGraph, START, END
|
| 8 |
from langchain.agents import load_tools
|
| 9 |
-
from langchain_community.tools
|
| 10 |
|
| 11 |
from .prompt import system_prompt
|
| 12 |
from .custom_tools import (multiply, add, subtract, divide, modulus, power,
|
| 13 |
-
query_image, automatic_speech_recognition)
|
| 14 |
|
| 15 |
|
| 16 |
class LangGraphAgent:
|
|
@@ -20,20 +21,21 @@ class LangGraphAgent:
|
|
| 20 |
show_prompt=True):
|
| 21 |
|
| 22 |
# =========== LLM definition ===========
|
| 23 |
-
llm = ChatOpenAI(model=model_name, temperature=0) # needs OPENAI_API_KEY
|
| 24 |
print(f"LangGraphAgent initialized with model \"{model_name}\"")
|
| 25 |
|
| 26 |
# =========== Augment the LLM with tools ===========
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
|
|
|
| 30 |
]
|
| 31 |
-
community_tools = load_tools(community_tool_names)
|
| 32 |
-
community_tools += [ExecPython(runtime_revision_id='01JT97GJ20BC83Y75WMAS364ZT')] # Riza code interpreter (needs RIZA_API_KEY) (not supported by load_tools, custom runtime with basic packages (pandas, numpy, etc.))
|
| 33 |
custom_tools = [
|
| 34 |
-
multiply, add, subtract, divide, modulus, power, #
|
| 35 |
query_image, # Ask anything about an image using a VLM
|
| 36 |
automatic_speech_recognition, # Transcribe an audio file to text
|
|
|
|
|
|
|
| 37 |
]
|
| 38 |
|
| 39 |
tools = community_tools + custom_tools
|
|
|
|
| 1 |
+
import os
|
| 2 |
import json
|
| 3 |
|
| 4 |
from typing import Literal
|
|
|
|
| 7 |
from langchain_core.messages import SystemMessage, HumanMessage, ToolMessage
|
| 8 |
from langgraph.graph import StateGraph, START, END
|
| 9 |
from langchain.agents import load_tools
|
| 10 |
+
from langchain_community.tools import BraveSearch
|
| 11 |
|
| 12 |
from .prompt import system_prompt
|
| 13 |
from .custom_tools import (multiply, add, subtract, divide, modulus, power,
|
| 14 |
+
query_image, automatic_speech_recognition, get_webpage_content, python_repl_tool)
|
| 15 |
|
| 16 |
|
| 17 |
class LangGraphAgent:
|
|
|
|
| 21 |
show_prompt=True):
|
| 22 |
|
| 23 |
# =========== LLM definition ===========
|
| 24 |
+
llm = ChatOpenAI(model=model_name, temperature=0) # needs OPENAI_API_KEY in env
|
| 25 |
print(f"LangGraphAgent initialized with model \"{model_name}\"")
|
| 26 |
|
| 27 |
# =========== Augment the LLM with tools ===========
|
| 28 |
+
community_tools = [
|
| 29 |
+
BraveSearch.from_api_key( # Web search (more performant than DuckDuckGo)
|
| 30 |
+
api_key=os.getenv("BRAVE_SEARCH_API_KEY"), # needs BRAVE_SEARCH_API_KEY in env
|
| 31 |
+
search_kwargs={"count": 3}),
|
| 32 |
]
|
|
|
|
|
|
|
| 33 |
custom_tools = [
|
| 34 |
+
multiply, add, subtract, divide, modulus, power, # Basic arithmetic
|
| 35 |
query_image, # Ask anything about an image using a VLM
|
| 36 |
automatic_speech_recognition, # Transcribe an audio file to text
|
| 37 |
+
get_webpage_content, # Load a web page and return it to markdown
|
| 38 |
+
python_repl_tool, # Python code interpreter
|
| 39 |
]
|
| 40 |
|
| 41 |
tools = community_tools + custom_tools
|
langgraph_dir/config.py
CHANGED
|
@@ -1,3 +1,6 @@
|
|
| 1 |
# OPENAI_MODEL_NAME = "gpt-4.1-nano" # Overall Score: 10.0% (2/20 correct)
|
| 2 |
OPENAI_MODEL_NAME = "gpt-4.1-mini"
|
| 3 |
-
# OPENAI_MODEL_NAME = "gpt-4.1"
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
# OPENAI_MODEL_NAME = "gpt-4.1-nano" # Overall Score: 10.0% (2/20 correct)
|
| 2 |
OPENAI_MODEL_NAME = "gpt-4.1-mini"
|
| 3 |
+
# OPENAI_MODEL_NAME = "gpt-4.1"
|
| 4 |
+
|
| 5 |
+
# QUERY_IMAGE_MODEL_NAME = "gpt-4.1-mini"
|
| 6 |
+
QUERY_IMAGE_MODEL_NAME = "o4-mini"
|
langgraph_dir/custom_tools.py
CHANGED
|
@@ -1,7 +1,11 @@
|
|
| 1 |
import requests
|
| 2 |
-
from
|
| 3 |
from huggingface_hub import InferenceClient
|
| 4 |
from openai import OpenAI
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
|
| 6 |
|
| 7 |
# --- Basic operations --- #
|
|
@@ -116,10 +120,11 @@ def query_image(query: str, image_url: str) -> str:
|
|
| 116 |
return completion.choices[0].message
|
| 117 |
|
| 118 |
elif PROVIDER == 'openai':
|
| 119 |
-
|
| 120 |
|
|
|
|
| 121 |
response = client.responses.create(
|
| 122 |
-
model=
|
| 123 |
input=[{
|
| 124 |
"role": "user",
|
| 125 |
"content": [
|
|
@@ -180,3 +185,46 @@ def automatic_speech_recognition(file_url: str, file_extension: str) -> str:
|
|
| 180 |
|
| 181 |
except Exception as e:
|
| 182 |
return f"automatic_speech_recognition failed: {e}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import requests
|
| 2 |
+
from pydantic import BaseModel, Field
|
| 3 |
from huggingface_hub import InferenceClient
|
| 4 |
from openai import OpenAI
|
| 5 |
+
from bs4 import BeautifulSoup
|
| 6 |
+
from markdownify import markdownify as md
|
| 7 |
+
from langchain_core.tools import tool, Tool
|
| 8 |
+
from langchain_experimental.utilities import PythonREPL
|
| 9 |
|
| 10 |
|
| 11 |
# --- Basic operations --- #
|
|
|
|
| 120 |
return completion.choices[0].message
|
| 121 |
|
| 122 |
elif PROVIDER == 'openai':
|
| 123 |
+
from .config import QUERY_IMAGE_MODEL_NAME
|
| 124 |
|
| 125 |
+
client = OpenAI()
|
| 126 |
response = client.responses.create(
|
| 127 |
+
model=QUERY_IMAGE_MODEL_NAME,
|
| 128 |
input=[{
|
| 129 |
"role": "user",
|
| 130 |
"content": [
|
|
|
|
| 185 |
|
| 186 |
except Exception as e:
|
| 187 |
return f"automatic_speech_recognition failed: {e}"
|
| 188 |
+
|
| 189 |
+
|
| 190 |
+
@tool
|
| 191 |
+
def get_webpage_content(page_url: str) -> str:
|
| 192 |
+
"""Load a web page and return it to markdown if possible
|
| 193 |
+
|
| 194 |
+
Args:
|
| 195 |
+
page_url (str): the URL of web page to get
|
| 196 |
+
"""
|
| 197 |
+
try:
|
| 198 |
+
r = requests.get(page_url)
|
| 199 |
+
soup = BeautifulSoup((r.text), 'html.parser')
|
| 200 |
+
if soup.body:
|
| 201 |
+
# convert to markdown
|
| 202 |
+
out = md(str(soup.body))
|
| 203 |
+
else:
|
| 204 |
+
# return the raw content
|
| 205 |
+
out = r.text
|
| 206 |
+
return out
|
| 207 |
+
except Exception as e:
|
| 208 |
+
return f"get_webpage_content failed: {e}"
|
| 209 |
+
|
| 210 |
+
|
| 211 |
+
# ======= Python code interpreter =======
|
| 212 |
+
# WARNING: Python REPL can execute arbitrary code on the host machine (e.g., delete files, make network requests). Use with caution.
|
| 213 |
+
|
| 214 |
+
class PythonREPLInput(BaseModel):
|
| 215 |
+
code: str = Field(description="The Python code string to execute.")
|
| 216 |
+
|
| 217 |
+
python_repl = PythonREPL()
|
| 218 |
+
|
| 219 |
+
python_repl_tool = Tool(
|
| 220 |
+
name="python_repl",
|
| 221 |
+
description="""A Python REPL shell (Read-Eval-Print Loop).
|
| 222 |
+
Use this to execute single or multi-line python commands.
|
| 223 |
+
Input should be syntactically valid Python code.
|
| 224 |
+
Always end your code with `print(...)` to see the output.
|
| 225 |
+
Do NOT execute code that could be harmful to the host system.
|
| 226 |
+
You are allowed to download files from URLs.
|
| 227 |
+
Do NOT send commands that block indefinitely (e.g., `input()`).""",
|
| 228 |
+
func=python_repl.run,
|
| 229 |
+
args_schema=PythonREPLInput
|
| 230 |
+
)
|
langgraph_dir/prompt.py
CHANGED
|
@@ -6,4 +6,5 @@ YOUR FINAL ANSWER should be a number OR as few words as possible OR a comma sepa
|
|
| 6 |
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.
|
| 7 |
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.
|
| 8 |
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.
|
|
|
|
| 9 |
"""
|
|
|
|
| 6 |
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.
|
| 7 |
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.
|
| 8 |
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.
|
| 9 |
+
If you use the python_repl tool (code interpreter), always end your code with `print(...)` to see the output.
|
| 10 |
"""
|
requirements.txt
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
gradio
|
| 2 |
requests
|
| 3 |
llama-index
|
| 4 |
-
llama-index-llms-huggingface-api
|
| 5 |
llama_index.tools.wikipedia
|
| 6 |
llama_index.tools.duckduckgo
|
| 7 |
llama_index.tools.code_interpreter
|
|
@@ -10,4 +10,6 @@ langgraph
|
|
| 10 |
langchain-openai
|
| 11 |
langchain-community
|
| 12 |
duckduckgo-search
|
| 13 |
-
|
|
|
|
|
|
|
|
|
| 1 |
gradio
|
| 2 |
requests
|
| 3 |
llama-index
|
| 4 |
+
llama-index-llms-huggingface-api
|
| 5 |
llama_index.tools.wikipedia
|
| 6 |
llama_index.tools.duckduckgo
|
| 7 |
llama_index.tools.code_interpreter
|
|
|
|
| 10 |
langchain-openai
|
| 11 |
langchain-community
|
| 12 |
duckduckgo-search
|
| 13 |
+
markdownify
|
| 14 |
+
beautifulsoup4
|
| 15 |
+
langchain_experimental
|