File size: 11,669 Bytes
a1c1d9a 7319d31 a1c1d9a 7319d31 a1c1d9a 7319d31 7b71bd5 7319d31 a1c1d9a 7319d31 a1c1d9a 7319d31 a1c1d9a 7319d31 a1c1d9a 7319d31 a1c1d9a 7319d31 a1c1d9a 7319d31 a1c1d9a 7b71bd5 a1c1d9a 7b71bd5 a1c1d9a 7b71bd5 a1c1d9a |
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 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 |
# tools/langchain_tools.py
"""
LangChain-compatible tool wrappers for our existing tools
Complete integration of multimodal, search, math, and YouTube tools
"""
from langchain_core.tools import tool
from typing import Optional
import os
from dotenv import load_dotenv
# Load environment variables FIRST, before any tool imports
load_dotenv()
from .multimodal_tools import MultimodalTools, analyze_transcript as _analyze_transcript, analyze_excel, analyze_python
from .search_tools import SearchTools
from .math_tools import MathTools
from .youtube_tools import YouTubeTools
from .wikipedia_tools import search_wikipedia, get_wikipedia_page, wikipedia_summary
# Initialize tool instances (now env vars are available)
multimodal_tools = MultimodalTools()
search_tools = SearchTools()
youtube_tools = YouTubeTools()
# =============================================================================
# MULTIMODAL TOOLS
# =============================================================================
@tool
def extract_text(image_path: str) -> str:
"""Extract text from an image using OCR"""
return multimodal_tools.extract_text_from_image(image_path)
@tool
def analyze_image_tool(image_path: str, question: str = "Describe this image in detail") -> str:
"""Analyze an image and answer questions about it"""
return multimodal_tools.analyze_image(image_path, question)
@tool
def analyze_audio_tool(transcript: str, question: str = "Summarize this audio content") -> str:
"""Analyze audio content via transcript"""
return multimodal_tools.analyze_audio_transcript(transcript, question)
@tool
def analyze_excel_tool(file_path: str, question: str) -> str:
"""Analyze Excel or CSV files to answer questions about the data"""
return analyze_excel(file_path, question)
# =============================================================================
# SEARCH TOOLS
# =============================================================================
@tool
def search_tool(query: str, max_results: int = 10) -> str:
"""Search the web for information"""
results = search_tools.search(query, max_results)
if not results:
return "No search results found"
# Format results for the LLM
formatted_results = []
for i, result in enumerate(results, 1):
title = result.get('title', 'No title')
content = result.get('content', 'No content')
url = result.get('url', 'No URL')
formatted_results.append(f"{i}. {title}\n{content[:200]}...\nSource: {url}\n")
return "\n".join(formatted_results)
@tool
def search_news_tool(query: str, max_results: int = 10) -> str:
"""Search for news articles about a topic"""
results = search_tools.search_news(query, max_results)
if not results:
return "No news results found"
# Format results for the LLM
formatted_results = []
for i, result in enumerate(results, 1):
title = result.get('title', 'No title')
content = result.get('content', 'No content')
url = result.get('url', 'No URL')
formatted_results.append(f"{i}. {title}\n{content[:200]}...\nSource: {url}\n")
return "\n".join(formatted_results)
@tool
def search_academic_tool(query: str, max_results: int = 10) -> str:
"""Search for academic research and papers"""
results = search_tools.search_academic(query, max_results)
if not results:
return "No academic results found"
# Format results for the LLM
formatted_results = []
for i, result in enumerate(results, 1):
title = result.get('title', 'No title')
content = result.get('content', 'No content')
url = result.get('url', 'No URL')
formatted_results.append(f"{i}. {title}\n{content[:200]}...\nSource: {url}\n")
return "\n".join(formatted_results)
# =============================================================================
# YOUTUBE TOOLS
# =============================================================================
@tool
def extract_youtube_transcript(url: str, language_code: str = 'en') -> str:
"""Extract transcript/captions from a YouTube video"""
captions = youtube_tools.get_captions(url, language_code)
if captions:
return captions
else:
return "No captions available for this video"
@tool
def get_youtube_info(url: str) -> str:
"""Get information about a YouTube video"""
info = youtube_tools.get_video_info(url)
if info:
return f"Title: {info.get('title', 'Unknown')}\nAuthor: {info.get('author', 'Unknown')}\nDuration: {info.get('length', 0)} seconds\nViews: {info.get('views', 0):,}"
else:
return "Could not retrieve video information"
@tool
def get_youtube_playlist_info(playlist_url: str) -> str:
"""Get information about a YouTube playlist"""
info = youtube_tools.get_playlist_info(playlist_url)
if info:
return f"Playlist: {info.get('title', 'Unknown')}\nVideos: {info.get('video_count', 0)}\nOwner: {info.get('owner', 'Unknown')}"
else:
return "Could not retrieve playlist information"
# =============================================================================
# MATH TOOLS - Basic Operations
# =============================================================================
@tool
def add(a: float, b: float) -> float:
"""Add two numbers"""
return MathTools.add(a, b)
@tool
def subtract(a: float, b: float) -> float:
"""Subtract two numbers"""
return MathTools.subtract(a, b)
@tool
def multiply(a: float, b: float) -> float:
"""Multiply two numbers"""
return MathTools.multiply(a, b)
@tool
def divide(a: float, b: float) -> str:
"""Divide two numbers"""
result = MathTools.divide(a, b)
return str(result)
@tool
def power(base: float, exponent: float) -> float:
"""Calculate base raised to the power of exponent"""
return MathTools.power(base, exponent)
# =============================================================================
# MATH TOOLS - Advanced Operations
# =============================================================================
@tool
def factorial(n: int) -> str:
"""Calculate factorial of a number"""
result = MathTools.factorial(n)
return str(result)
@tool
def square_root(n: float) -> str:
"""Calculate square root of a number"""
result = MathTools.square_root(n)
return str(result)
@tool
def percentage(part: float, whole: float) -> str:
"""Calculate percentage"""
result = MathTools.percentage(part, whole)
return str(result)
@tool
def average(numbers: str) -> str:
"""Calculate average of numbers (provide as comma-separated string)"""
try:
number_list = [float(x.strip()) for x in numbers.split(',')]
result = MathTools.average(number_list)
return str(result)
except Exception as e:
return f"Error parsing numbers: {str(e)}"
@tool
def calculate_expression(expression: str) -> str:
"""Calculate a mathematical expression safely"""
from .math_tools import calculate_expression as calc_expr
return str(calc_expr(expression))
@tool
def absolute_value(n: float) -> float:
"""Calculate absolute value of a number"""
return MathTools.absolute(n)
@tool
def round_number(n: float, decimals: int = 2) -> float:
"""Round number to specified decimal places"""
return MathTools.round_number(n, decimals)
@tool
def min_value(numbers: str) -> str:
"""Find minimum value in a list of numbers (provide as comma-separated string)"""
try:
number_list = [float(x.strip()) for x in numbers.split(',')]
result = MathTools.min_value(number_list)
return str(result)
except Exception as e:
return f"Error parsing numbers: {str(e)}"
@tool
def max_value(numbers: str) -> str:
"""Find maximum value in a list of numbers (provide as comma-separated string)"""
try:
number_list = [float(x.strip()) for x in numbers.split(',')]
result = MathTools.max_value(number_list)
return str(result)
except Exception as e:
return f"Error parsing numbers: {str(e)}"
@tool
def compound_interest(principal: float, rate: float, time: float, compounds_per_year: int = 1) -> str:
"""Calculate compound interest"""
result = MathTools.calculate_compound_interest(principal, rate, time, compounds_per_year)
return str(result)
@tool
def solve_quadratic(a: float, b: float, c: float) -> str:
"""Solve quadratic equation ax² + bx + c = 0"""
result = MathTools.solve_quadratic(a, b, c)
return str(result)
@tool
def analyze_python_tool(file_path: str, question: str = "What is the final output of this code?") -> str:
"""Read and analyze Python code files, can execute code to get results"""
return analyze_python(file_path, question)
# =============================================================================
# TOOL COLLECTIONS FOR EASY IMPORT
# =============================================================================
@tool
def analyze_youtube_frames(url: str, question: str = "Describe what happens in this video") -> str:
"""Extract frames from YouTube video and analyze visual content"""
youtube_tools = YouTubeTools()
return youtube_tools.analyze_video_content(url, question)
@tool
def extract_video_slides(url: str) -> str:
"""Extract and analyze slides from educational YouTube videos"""
youtube_tools = YouTubeTools()
results = youtube_tools.analyze_video_slides(url)
if 'error' in results:
return results['error']
# Format slide analysis for LLM
slide_content = []
for frame in results.get('frames_analyzed', []):
if 'slide' in frame.get('analysis', '').lower():
slide_content.append(f"Slide {frame['frame_number']}: {frame['analysis']}")
if slide_content:
return "\n\n".join(slide_content)
else:
return results.get('analysis_summary', 'No slides detected in video')
# Add Wikipedia tools
@tool
def search_wikipedia_tool(query: str, language: str = 'en') -> str:
"""Search Wikipedia for information about a topic"""
return search_wikipedia(query, language)
@tool
def get_wikipedia_page_tool(page_title: str, language: str = 'en') -> str:
"""Retrieve a specific Wikipedia page by title"""
return get_wikipedia_page(page_title, language)
@tool
def wikipedia_summary_tool(query: str, language: str = 'en') -> str:
"""Get a concise Wikipedia summary about a topic"""
return wikipedia_summary(query, language)
# Core tools (matching original template)
CORE_TOOLS = [
extract_text,
analyze_image_tool,
analyze_audio_tool,
extract_youtube_transcript,
add,
subtract,
multiply,
divide,
search_tool
]
# Extended tools with new Excel functionality
EXTENDED_TOOLS = CORE_TOOLS + [
analyze_excel_tool, # NEW: Excel/CSV analysis
analyze_youtube_frames, # ✅ NEW: Frame extraction and analysis
extract_video_slides, # ✅ NEW: Slide detection
analyze_python_tool,
search_news_tool,
search_wikipedia_tool, # ✅ NEW: Wikipedia search
get_wikipedia_page_tool, # ✅ NEW: Specific Wikipedia pages
wikipedia_summary_tool, # ✅ NEW: Wikipedia summaries
search_academic_tool,
get_youtube_info,
get_youtube_playlist_info,
calculate_expression,
factorial,
square_root,
percentage,
average
]
# All available tools
ALL_TOOLS = EXTENDED_TOOLS + [
power,
absolute_value,
round_number,
min_value,
max_value,
compound_interest,
solve_quadratic
]
# Default export (for backwards compatibility)
tools = CORE_TOOLS
|