File size: 7,893 Bytes
df5ab56
bcb08ab
df5ab56
 
 
 
 
 
 
 
 
 
 
 
bcb08ab
df5ab56
4ba071b
df5ab56
 
 
bcb08ab
df5ab56
bcb08ab
df5ab56
bcb08ab
df5ab56
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bcb08ab
df5ab56
bcb08ab
 
 
 
 
 
 
 
 
 
 
df5ab56
319a6d6
bcb08ab
 
df5ab56
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4ba071b
df5ab56
 
 
 
 
 
 
4ba071b
df5ab56
 
4ba071b
df5ab56
4ba071b
 
 
 
 
 
 
df5ab56
4ba071b
 
 
 
 
 
 
 
 
 
df5ab56
 
4ba071b
 
df5ab56
4ba071b
df5ab56
 
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
266
267
268
269
import contextlib
from langchain_core.tools import tool
from langchain_community.tools import DuckDuckGoSearchRun, WikipediaQueryRun
from langchain_community.utilities import WikipediaAPIWrapper
from langchain_tavily import TavilySearch
from langchain_chroma import Chroma
from langchain_community.document_loaders import TextLoader, PyPDFLoader, CSVLoader, JSONLoader
from langchain_community.document_loaders.image import UnstructuredImageLoader
from langchain_community.document_loaders.youtube import YoutubeLoader, TranscriptFormat
#from langchain_unstructured import UnstructuredLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_huggingface import HuggingFaceEmbeddings
from transformers import pipeline
import asyncio
import os
import io
import ast
from dotenv import load_dotenv
#from PIL import Image
#from io import StringIO

load_dotenv()
os.environ["TAVILY_API_KEY"] = os.getenv("TAVILY_API_KEY")
os.environ["UNSTRUCTURED_API_KEY"] = os.getenv("UNSTRUCTURED_API_KEY")

# Retriever
@tool 
def retriever(query: str, file_path: str) -> str:
    """
    Retrieve relevant information from a text, PDF, CSV JSON or image file using semantic search.

    Args:
        query (str): The search query string.
        file_path (str): Path to the text file to be searched.

    Returns:
        str: The most relevant text chunks from the file based on the query.
    """
    try:
        if file_path.endswith(".pdf"):
            loader = PyPDFLoader(file_path)
        elif file_path.endswith(".csv"):
            loader = CSVLoader(file_path)
        elif file_path.endswith(".json"):
            loader = JSONLoader(file_path)
        elif file_path.endswith((".png", ".jpeg", ".jpg")):
            loader = UnstructuredImageLoader(file_path)
        else:
            loader = TextLoader(file_path)
        # Load data into document objects
        doc_list = []
        docs = loader.load()
        doc_list.extend(docs)
        # Chunks
        text_splitter= RecursiveCharacterTextSplitter(
            chunk_size=100,
            chunk_overlap=20,
            length_function=len
        )
        chunks = text_splitter.split_documents(doc_list)
        # Define embeddings and load them into vectorstore
        embeddings = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")
        vectorstore = Chroma.from_documents(
            documents=chunks,
            embedding=embeddings,
        )
        retriever = vectorstore.as_retriever(search_kwargs = {"k":1})
        doc_result = retriever.invoke(query)
        result = '\n\n'.join(doc.page_content for doc in doc_result)
        return result
    except Exception:
        return "No results found."

# Websearch tools
@tool
def web_search(query: str) -> str:
    """
    Perform a web search using DuckDuckGo.

    Args:
        query (str): The search query string.

    Returns:
        str: The result of the web search as a string. 
        If an exception occurs, returns a fallback string indicating no results were found.
    """
    search_engine = DuckDuckGoSearchRun()
    try:
        response = search_engine.invoke(query)
        return response
    except:
        return f"No results found on the web for this query: {query}."

@tool
def wiki_search(query: str) -> str:
    """
    Search Wikipedia for the given query and return a summary.

    Args:
        query (str): The search query string.

    Returns:
        str: A summary or relevant information from Wikipedia about the query.
    """
    wikipedia = WikipediaQueryRun(api_wrapper=WikipediaAPIWrapper())
    response = wikipedia.run(query)
    return response

@tool
def youtube_analysis(yt_url: str) -> str:
    """
    Analyze a YouTube video and return its transcript and metadata.

    Args:
        yt_url (str): The URL of the YouTube video.

    Returns:
        str: A string containing video information and transcript chunks.
    """

    loader = YoutubeLoader.from_youtube_url(
    yt_url,
    add_video_info=True,
    transcript_format=TranscriptFormat.CHUNKS,
    chunk_size_seconds=30,
    )
    return "\n\n".join(map(repr, loader.load()))

# Calculator tools    
@tool
def add_numbers(a: int|float, b:int|float)-> int|float:
    """
    Add two numbers.

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

    Returns:
        int | float: The sum of a and b.
    """
    return a + b
@tool
def subtract_numbers(a: int|float, b:int|float)-> int|float:
    """
    Subtract one number from another.

    Args:
        a (int | float): The number to subtract from.
        b (int | float): The number to subtract.

    Returns:
        int | float: The result of a minus b.
    """ 
    return a - b

@tool
def multiply_numbers(a: int|float, b:int|float)-> int|float:
    """
    Multiply two numbers.

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

    Returns:
        int | float: The product of a and b.
    """
    return a * b

@tool
def divide_numbers(a: int|float, b:int|float)-> float|None:
    """
    Divide one number by another.

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

    Returns:
        int | float: The result of a divided by b.
        Returns None if b is zero.
    """
    try:
        return a / b
    except ZeroDivisionError:
        return None

@tool
def modulus_numbers(a: int|float, b:int|float)-> int|float:
    """
    Compute the modulus of two numbers.

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

    Returns:
        int | float: The remainder after dividing a by b.
    """  
    return a % b

# Image recognition

@tool
def detect_objects(image_path: str) -> str:
    """
    Detects objects in an image and returns a list with labels and confidence scores.
    
    Args:
        image_path (str): Path to the input image file.
    
    Returns:
        str: Detected objects with confidence scores.
    """
    # Load object detection pipeline (using a pre-trained model like DETR)
    object_detector = pipeline("object-detection", model="facebook/detr-resnet-50")
    results = object_detector(image_path)
    output = []
    for r in results:
        label = r["label"]
        score = round(r["score"], 3)
        box = r["box"]
        output.append(f"{label} (score={score}, box={box})")
    return "\n".join(output)

# Code execution
@tool
def run_python(code: str) -> str:
    """
    Executes Python code safely and returns stdout or the last expression result.
    
    Args:
        code (str): The Python code to execute.
    
    Returns:
        str: Captured stdout and/or result.
    """
    
    stdout = io.StringIO()
    local_vars = {}
    
    try:
        # Parse code into AST
        parsed = ast.parse(code, mode="exec")
        last_expr = None
        if parsed.body and isinstance(parsed.body[-1], ast.Expr):
            # If last node is an expression, separate it
            last_expr = parsed.body.pop()
        
        with contextlib.redirect_stdout(stdout):
            # Run everything except the last expression
            exec(compile(parsed, filename="<ast>", mode="exec"), {}, local_vars)
            
            # Evaluate last expression if present
            if last_expr is not None:
                _result = eval(compile(ast.Expression(last_expr.value), 
                                       filename="<ast>", mode="eval"), {}, local_vars)
                local_vars["_result"] = _result
        
        # Return _result if set
        if "_result" in local_vars:
            return str(local_vars["_result"])
        
        # Otherwise, return stdout
        return stdout.getvalue().strip() or "Code executed successfully."
    
    except Exception as e:
        return f"Execution error: {e}"