File size: 7,522 Bytes
97bdee5
3f8f511
97bdee5
5579ccc
 
 
 
 
22c8505
 
 
 
 
 
 
 
 
 
 
 
 
8569e5d
97bdee5
8569e5d
97bdee5
8569e5d
97bdee5
 
 
8569e5d
97bdee5
 
 
8569e5d
97bdee5
 
 
 
8569e5d
97bdee5
 
 
8569e5d
97bdee5
 
 
8569e5d
97bdee5
 
 
 
8569e5d
97bdee5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3f8f511
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5579ccc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97bdee5
8569e5d
97bdee5
3f8f511
 
 
 
 
 
 
 
 
 
 
 
5579ccc
 
 
 
 
 
 
 
 
 
 
22c8505
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
from langchain.tools import Tool
from langchain.utilities import WikipediaAPIWrapper, ArxivAPIWrapper, DuckDuckGoSearchRun
import math
import whisper
from youtube_transcript_api import YouTubeTranscriptApi
from PIL import Image
import pytesseract
import pandas as pd
from dotenv import load_dotenv

from langgraph.graph import StateGraph, START, END
from langgraph.prebuilt import ToolNode, tools_condition
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, SystemMessage
from typing import TypedDict, Dict, Any, Optional, List

load_dotenv()

## ----- API KEYS  ----- ##

openai_api_key = os.getenv("OPENAI_API_KEY")

## ----- TOOLS DEFINITION ----- ##

# ** Math Tools ** #

def add_numbers(a: float, b: float) -> float:
    """
    Add two floating-point numbers.

    Args:
        a (float): The first number.
        b (float): The second number.

    Returns:
        float: The result of the addition.
    """
    return a + b

def subtract_numbers(a: float, b: float) -> float:
    """
    Subtract the second floating-point number from the first.

    Args:
        a (float): The first number.
        b (float): The second number.

    Returns:
        float: The result of the subtraction.
    """
    return a - b

def multiply_numbers(a: float, b: float) -> float:
    """
    Multiply two floating-point numbers.

    Args:
        a (float): The first number.
        b (float): The second number.

    Returns:
        float: The result of the multiplication.
    """
    return a * b

def divide_numbers(a: float, b: float) -> float:
    """
    Divide the first floating-point number by the second.

    Args:
        a (float): The numerator.
        b (float): The denominator.

    Returns:
        float: The result of the division.

    Raises:
        ValueError: If division by zero is attempted.
    """
    if b == 0:
        raise ValueError("Division by zero")
    return a / b

def power(a: float, b: float) -> float:
    """
    Raise the first number to the power of the second.

    Args:
        a (float): The base.
        b (float): The exponent.

    Returns:
        float: The result of the exponentiation.
    """
    return a ** b

def modulus(a: float, b: float) -> float:
    """
    Compute the modulus (remainder) of the division of a by b.

    Args:
        a (float): The dividend.
        b (float): The divisor.

    Returns:
        float: The remainder after division.
    """
    return a % b

def square_root(a: float) -> float:
    """
    Compute the square root of a number.

    Args:
        a (float): The number.

    Returns:
        float: The square root.

    Raises:
        ValueError: If a is negative.
    """
    if a < 0:
        raise ValueError("Cannot compute square root of a negative number")
    return math.sqrt(a)

def logarithm(a: float, base: float = math.e) -> float:
    """
    Compute the logarithm of a number with a specified base.

    Args:
        a (float): The number.
        base (float, optional): The logarithmic base (default is natural log).

    Returns:
        float: The logarithm.

    Raises:
        ValueError: If a or base is not positive.
    """
    if a <= 0 or base <= 0:
        raise ValueError("Logarithm arguments must be positive")
    return math.log(a, base)

# ** Search Tools ** #

# DuckDuckGo Web Search
duckduckgo_search = DuckDuckGoSearchRun()
web_search_tool = Tool.from_function(
    func=duckduckgo_search.run,
    name="Web Search",
    description="Use this tool to search the internet for general-purpose queries."
)

# Wikipedia Search
wikipedia_search = WikipediaAPIWrapper()
wikipedia_tool = Tool.from_function(
    func=wikipedia_search.run,
    name="Wikipedia Search",
    description="Use this tool to search Wikipedia for factual or encyclopedic information."
)

# ArXiv Search
arxiv_search = ArxivAPIWrapper()
arxiv_tool = Tool.from_function(
    func=arxiv_search.run,
    name="ArXiv Search",
    description="Use this tool to search ArXiv for scientific papers. Input should be a research topic or query."
)

# ** Audio Transcription Tool ** #

model = whisper.load_model("base")

@tool
def transcribe_audio(file_path: str) -> str:
    """Transcribe spoken words from an audio file into text."""
    result = model.transcribe(file_path)
    return result["text"]

# ** youtube-transcript-api Tool ** #

@tool
def get_youtube_transcript(video_id: str) -> str:
    """Get transcript of a YouTube video from its video ID."""
    transcript = YouTubeTranscriptApi.get_transcript(video_id)
    return " ".join([entry["text"] for entry in transcript])

# ** Image Tool ** #

@tool
def extract_text_from_image(image_path: str) -> str:
    """Extract text from an image using OCR."""
    return pytesseract.image_to_string(Image.open(image_path))

# ** Code Execution Tool ** #

@tool
def execute_python_code(code: str) -> str:
    """Execute a Python code string and return the output."""
    try:
        local_vars = {}
        exec(code, {}, local_vars)
        return str(local_vars)
    except Exception as e:
        return f"Error: {e}"

# ** Excel Parsing Tool ** #

@tool
def total_sales_from_excel(file_path: str) -> str:
    """Compute total food sales from an Excel file."""
    df = pd.read_excel(file_path)
    food_df = df[df["Category"] == "Food"]
    total_sales = food_df["Sales"].sum()
    return f"{total_sales:.2f} USD"
    

## ----- TOOLS LIST ----- ##

tools = [
    # Math
    Tool.from_function(func=add_numbers, name="Add Numbers", description="Add two numbers."),
    Tool.from_function(func=subtract_numbers, name="Subtract Numbers", description="Subtract two numbers."),
    Tool.from_function(func=multiply_numbers, name="Multiply Numbers", description="Multiply two numbers."),
    Tool.from_function(func=divide_numbers, name="Divide Numbers", description="Divide two numbers."),
    Tool.from_function(func=power, name="Power", description="Raise one number to the power of another."),
    Tool.from_function(func=modulus, name="Modulus", description="Compute the modulus (remainder) of a division."),
    Tool.from_function(func=square_root, name="Square Root", description="Compute the square root of a number."),
    Tool.from_function(func=logarithm, name="Logarithm", description="Compute the logarithm of a number with a given base."),
    # Search
    web_search_tool,
    wikipedia_tool,
    arxiv_tool,
    # Audio
    Tool.from_function(func=transcribe_audio, name="Transcribe Audio", description="Transcribe audio files to text."),
    # Youtube
    Tool.from_function(func=get_youtube_transcript, name="YouTube Transcript", description="Extract transcript from YouTube video."),
    # Image
    Tool.from_function(func=extract_text_from_image, name="Image OCR", description="Extract text from an image file."),
    # Code Execution 
    Tool.from_function(func=execute_python_code, name="Python Code Executor", description="Run and return output from a Python script."),
    # Excel parsing
    Tool.from_function(func=total_sales_from_excel, name="Excel Sales Parser", description="Compute total food sales from Excel file."),
]


## ----- LLM MODEL ----- ##

llm = ChatOpenAI(model="gpt-4o", temperature=0)
llm_with_tools = llm.bind_tools(tools)

## ----- SYSTEM PROMPT ----- ##

with open("system_prompt.txt", "r", encoding="utf-8") as f:
    system_prompt = f.read()
print(system_prompt)

# System message
sys_msg = SystemMessage(content=system_prompt)

## ----- GRAPH AGENT PIPELINE ----- ##