Mayank2027 commited on
Commit
0e8de62
·
verified ·
1 Parent(s): 1ba5ec4

Create tools.py

Browse files
Files changed (1) hide show
  1. tools.py +2007 -0
tools.py ADDED
@@ -0,0 +1,2007 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ tools.py - Comprehensive tool implementations for unrestricted AI agent web app.
3
+ Runs on Hugging Face Spaces with local llama.cpp (Llama 3.1 8B Instruct Q4_K_M).
4
+ """
5
+
6
+ import os
7
+ import sys
8
+ import re
9
+ import json
10
+ import math
11
+ import random
12
+ import string
13
+ import hashlib
14
+ import base64
15
+ import uuid
16
+ import time
17
+ import datetime
18
+ import subprocess
19
+ import socket
20
+ import zipfile
21
+ import shutil
22
+ import platform
23
+ import tempfile
24
+ import traceback
25
+ import difflib
26
+ import html
27
+ import urllib.parse
28
+ from pathlib import Path
29
+ from typing import Dict, Any, List, Optional
30
+
31
+ # Third-party imports
32
+ import requests
33
+ from bs4 import BeautifulSoup
34
+ import yfinance as yf
35
+ from duckduckgo_search import DDGS
36
+ import pytesseract
37
+ from PIL import Image
38
+ import qrcode
39
+ from barcode import Code128
40
+ from barcode.writer import ImageWriter
41
+ from deep_translator import GoogleTranslator
42
+ from reportlab.lib.pagesizes import letter
43
+ from reportlab.pdfgen import canvas
44
+ from docx import Document
45
+ from docx.shared import Pt
46
+ import dns.resolver
47
+ import psutil
48
+ import textblob
49
+ from sumy.parsers.plaintext import PlaintextParser
50
+ from sumy.nlp.tokenizers import Tokenizer
51
+ from sumy.summarizers.lsa import LsaSummarizer
52
+ import spacy
53
+ import pint
54
+ from youtube_transcript_api import YouTubeTranscriptApi
55
+
56
+ # Local imports
57
+ from config import GEMINI_API_KEY
58
+ from utils import safe_exec, safe_command_exec, generate_password as utils_generate_password, hash_string
59
+
60
+ # Initialize spaCy (load once)
61
+ try:
62
+ nlp = spacy.load("en_core_web_sm")
63
+ except Exception:
64
+ nlp = None
65
+
66
+ # Initialize pint unit registry
67
+ ureg = pint.UnitRegistry()
68
+
69
+ # =============================================================================
70
+ # HELPER FUNCTIONS
71
+ # =============================================================================
72
+
73
+ def _truncate(text: str, max_len: int = 12000) -> str:
74
+ """Truncate text if it exceeds max_len characters."""
75
+ if text is None:
76
+ return "Error: None result"
77
+ if len(text) > max_len:
78
+ return text[:max_len] + "\n...[truncated]"
79
+ return text
80
+
81
+ def _error_msg(e: Exception) -> str:
82
+ """Format exception into a clean error string."""
83
+ return f"Error: {type(e).__name__}: {str(e)}"
84
+
85
+ def _human_readable_size(size_bytes: int) -> str:
86
+ """Convert bytes to human readable format."""
87
+ for unit in ["B", "KB", "MB", "GB", "TB"]:
88
+ if size_bytes < 1024.0:
89
+ return f"{size_bytes:.2f} {unit}"
90
+ size_bytes /= 1024.0
91
+ return f"{size_bytes:.2f} PB"
92
+
93
+ # =============================================================================
94
+ # WEB TOOLS
95
+ # =============================================================================
96
+
97
+ def web_search(query: str, max_results: int = 5) -> str:
98
+ """Search the web using DuckDuckGo and return results."""
99
+ try:
100
+ max_results = min(int(max_results), 10)
101
+ with DDGS() as ddgs:
102
+ results = list(ddgs.text(query, max_results=max_results))
103
+ if not results:
104
+ return "No results found."
105
+ lines = []
106
+ for i, r in enumerate(results, 1):
107
+ title = r.get("title", "No title")
108
+ href = r.get("href", "No URL")
109
+ body = r.get("body", "No description")
110
+ lines.append(f"{i}. {title}\n URL: {href}\n {body}")
111
+ return "\n\n".join(lines)
112
+ except Exception as e:
113
+ return _error_msg(e)
114
+
115
+ def browse_page(url: str) -> str:
116
+ """Fetch and extract text content from a web page using requests + BeautifulSoup."""
117
+ try:
118
+ headers = {
119
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
120
+ }
121
+ resp = requests.get(url, headers=headers, timeout=30)
122
+ resp.raise_for_status()
123
+ soup = BeautifulSoup(resp.text, "html.parser")
124
+ for tag in soup(["script", "style", "nav", "footer", "header"]):
125
+ tag.decompose()
126
+ text = soup.get_text(separator="\n", strip=True)
127
+ text = "\n".join(line.strip() for line in text.splitlines() if line.strip())
128
+ return _truncate(text)
129
+ except Exception as e:
130
+ return _error_msg(e)
131
+
132
+ def playwright_screenshot(url: str, output_path: str = "/tmp/screenshot.png") -> str:
133
+ """Take a screenshot of a webpage using Playwright."""
134
+ try:
135
+ from playwright.sync_api import sync_playwright
136
+ with sync_playwright() as p:
137
+ browser = p.chromium.launch()
138
+ page = browser.new_page(viewport={"width": 1280, "height": 720})
139
+ page.goto(url, wait_until="networkidle", timeout=60000)
140
+ page.screenshot(path=output_path, full_page=True)
141
+ browser.close()
142
+ return f"Screenshot saved to: {output_path}"
143
+ except Exception as e:
144
+ return _error_msg(e)
145
+
146
+ def playwright_pdf(url: str, output_path: str = "/tmp/page.pdf") -> str:
147
+ """Save a webpage as PDF using Playwright."""
148
+ try:
149
+ from playwright.sync_api import sync_playwright
150
+ with sync_playwright() as p:
151
+ browser = p.chromium.launch()
152
+ page = browser.new_page()
153
+ page.goto(url, wait_until="networkidle", timeout=60000)
154
+ page.pdf(path=output_path, format="A4")
155
+ browser.close()
156
+ return f"PDF saved to: {output_path}"
157
+ except Exception as e:
158
+ return _error_msg(e)
159
+
160
+ # =============================================================================
161
+ # DOCUMENT TOOLS
162
+ # =============================================================================
163
+
164
+ def create_pdf(content: str, output_path: str = "/tmp/output.pdf", title: str = "Document") -> str:
165
+ """Create a PDF file from text content using ReportLab."""
166
+ try:
167
+ c = canvas.Canvas(output_path, pagesize=letter)
168
+ width, height = letter
169
+ c.setFont("Helvetica-Bold", 16)
170
+ c.drawString(72, height - 72, title)
171
+ c.setFont("Helvetica", 12)
172
+ y = height - 100
173
+ for line in content.split("\n"):
174
+ if y < 72:
175
+ c.showPage()
176
+ y = height - 72
177
+ c.drawString(72, y, line[:90])
178
+ y -= 14
179
+ c.save()
180
+ return f"PDF created: {output_path}"
181
+ except Exception as e:
182
+ return _error_msg(e)
183
+
184
+ def create_docx(content: str, output_path: str = "/tmp/output.docx", title: str = "Document") -> str:
185
+ """Create a DOCX file from text content using python-docx."""
186
+ try:
187
+ doc = Document()
188
+ heading = doc.add_heading(title, level=1)
189
+ for line in content.split("\n"):
190
+ if line.strip():
191
+ p = doc.add_paragraph(line.strip())
192
+ p.style.font.size = Pt(11)
193
+ doc.save(output_path)
194
+ return f"DOCX created: {output_path}"
195
+ except Exception as e:
196
+ return _error_msg(e)
197
+
198
+ # =============================================================================
199
+ # IMAGE TOOLS
200
+ # =============================================================================
201
+
202
+ def describe_image(image_path: str, prompt: str = "Describe this image in detail.") -> str:
203
+ """Describe an image using Gemini Vision API."""
204
+ try:
205
+ if not GEMINI_API_KEY:
206
+ return "Error: GEMINI_API_KEY not configured in config module."
207
+ import google.generativeai as genai
208
+ genai.configure(api_key=GEMINI_API_KEY)
209
+ model = genai.GenerativeModel("gemini-1.5-flash")
210
+ img = Image.open(image_path)
211
+ response = model.generate_content([prompt, img])
212
+ return response.text if response.text else "No description generated."
213
+ except Exception as e:
214
+ return _error_msg(e)
215
+
216
+ def ocr_image(image_path: str, language: str = "eng") -> str:
217
+ """Extract text from an image using pytesseract OCR."""
218
+ try:
219
+ img = Image.open(image_path)
220
+ text = pytesseract.image_to_string(img, lang=language)
221
+ return text.strip() if text.strip() else "No text detected in image."
222
+ except Exception as e:
223
+ return _error_msg(e)
224
+
225
+ # =============================================================================
226
+ # TRANSLATION TOOLS
227
+ # =============================================================================
228
+
229
+ def translate_text(text: str, target_language: str = "en", source_language: str = "auto") -> str:
230
+ """Translate text using deep_translator GoogleTranslator."""
231
+ try:
232
+ translator = GoogleTranslator(source=source_language, target=target_language)
233
+ result = translator.translate(text)
234
+ return result if result else "Translation failed."
235
+ except Exception as e:
236
+ return _error_msg(e)
237
+
238
+ # =============================================================================
239
+ # DATA TOOLS
240
+ # =============================================================================
241
+
242
+ def get_weather(location: str) -> str:
243
+ """Get weather information from wttr.in."""
244
+ try:
245
+ url = f"https://wttr.in/{urllib.parse.quote(location)}?format=j1"
246
+ resp = requests.get(url, timeout=15)
247
+ resp.raise_for_status()
248
+ data = resp.json()
249
+ current = data["current_condition"][0]
250
+ area = data["nearest_area"][0]
251
+ loc_name = f"{area['areaName'][0]['value']}, {area['country'][0]['value']}"
252
+ temp_c = current["temp_C"]
253
+ temp_f = current["temp_F"]
254
+ desc = current["weatherDesc"][0]["value"]
255
+ humidity = current["humidity"]
256
+ wind = current["windspeedKmph"]
257
+ return (
258
+ f"Weather for {loc_name}:\n"
259
+ f" Temperature: {temp_c}°C / {temp_f}°F\n"
260
+ f" Condition: {desc}\n"
261
+ f" Humidity: {humidity}%\n"
262
+ f" Wind: {wind} km/h"
263
+ )
264
+ except Exception as e:
265
+ return _error_msg(e)
266
+
267
+ def get_news(query: str = "latest news", max_results: int = 5) -> str:
268
+ """Get news using DuckDuckGo news search."""
269
+ try:
270
+ max_results = min(int(max_results), 10)
271
+ with DDGS() as ddgs:
272
+ results = list(ddgs.news(query, max_results=max_results))
273
+ if not results:
274
+ return "No news found."
275
+ lines = []
276
+ for i, r in enumerate(results, 1):
277
+ title = r.get("title", "No title")
278
+ source = r.get("source", "Unknown")
279
+ date = r.get("date", "Unknown date")
280
+ url = r.get("url", "No URL")
281
+ body = r.get("body", "No summary")
282
+ lines.append(f"{i}. {title}\n Source: {source} | {date}\n {body}\n URL: {url}")
283
+ return "\n\n".join(lines)
284
+ except Exception as e:
285
+ return _error_msg(e)
286
+
287
+ def get_stock_price(ticker: str) -> str:
288
+ """Get stock price information using yfinance."""
289
+ try:
290
+ stock = yf.Ticker(ticker)
291
+ info = stock.info
292
+ hist = stock.history(period="1d")
293
+ if hist.empty:
294
+ return f"No data found for ticker: {ticker}"
295
+ current = hist["Close"].iloc[-1]
296
+ prev_close = info.get("previousClose", current)
297
+ change = current - prev_close
298
+ change_pct = (change / prev_close * 100) if prev_close else 0
299
+ name = info.get("longName", ticker)
300
+ currency = info.get("currency", "USD")
301
+ return (
302
+ f"{name} ({ticker})\n"
303
+ f" Price: {current:.2f} {currency}\n"
304
+ f" Change: {change:+.2f} ({change_pct:+.2f}%)\n"
305
+ f" Market Cap: {info.get('marketCap', 'N/A')}\n"
306
+ f" 52W High: {info.get('fiftyTwoWeekHigh', 'N/A')}\n"
307
+ f" 52W Low: {info.get('fiftyTwoWeekLow', 'N/A')}"
308
+ )
309
+ except Exception as e:
310
+ return _error_msg(e)
311
+
312
+ # =============================================================================
313
+ # MATH / CODE TOOLS
314
+ # =============================================================================
315
+
316
+ def calculator(expression: str) -> str:
317
+ """Evaluate a mathematical expression safely using math functions."""
318
+ try:
319
+ allowed_names = {
320
+ "abs": abs, "round": round, "max": max, "min": min,
321
+ "sum": sum, "pow": pow, "len": len,
322
+ "sin": math.sin, "cos": math.cos, "tan": math.tan,
323
+ "asin": math.asin, "acos": math.acos, "atan": math.atan,
324
+ "sinh": math.sinh, "cosh": math.cosh, "tanh": math.tanh,
325
+ "exp": math.exp, "log": math.log, "log10": math.log10,
326
+ "log2": math.log2, "sqrt": math.sqrt, "ceil": math.ceil,
327
+ "floor": math.floor, "factorial": math.factorial,
328
+ "pi": math.pi, "e": math.e, "tau": math.tau,
329
+ "degrees": math.degrees, "radians": math.radians,
330
+ "gcd": math.gcd, "lcm": math.lcm if hasattr(math, "lcm") else None,
331
+ "isclose": math.isclose, "inf": math.inf, "nan": math.nan,
332
+ }
333
+ cleaned = re.sub(r"[^0-9+\-*/().,\s\w]", "", expression)
334
+ result = eval(cleaned, {"__builtins__": {}}, allowed_names)
335
+ return f"Result: {result}"
336
+ except Exception as e:
337
+ return _error_msg(e)
338
+
339
+ def python_interpreter(code: str) -> str:
340
+ """Execute Python code safely using safe_exec wrapper."""
341
+ try:
342
+ result = safe_exec(code)
343
+ return _truncate(str(result))
344
+ except Exception as e:
345
+ return _error_msg(e)
346
+
347
+ def execute_command(command: str, timeout: int = 30) -> str:
348
+ """Execute a shell command safely with timeout using safe_command_exec."""
349
+ try:
350
+ result = safe_command_exec(command, timeout=int(timeout))
351
+ return _truncate(str(result))
352
+ except Exception as e:
353
+ return _error_msg(e)
354
+
355
+ def code_formatter(code: str, language: str = "python") -> str:
356
+ """Format code using black (Python only)."""
357
+ try:
358
+ if language.lower() != "python":
359
+ return f"Error: code_formatter only supports Python, not {language}"
360
+ import black
361
+ formatted = black.format_str(code, mode=black.FileMode())
362
+ return formatted
363
+ except Exception as e:
364
+ return _error_msg(e)
365
+
366
+ # =============================================================================
367
+ # FILE OPERATIONS
368
+ # =============================================================================
369
+
370
+ def read_file(file_path: str) -> str:
371
+ """Read contents of a file."""
372
+ try:
373
+ path = Path(file_path)
374
+ if not path.exists():
375
+ return f"Error: File not found: {file_path}"
376
+ content = path.read_text(encoding="utf-8", errors="replace")
377
+ return _truncate(content)
378
+ except Exception as e:
379
+ return _error_msg(e)
380
+
381
+ def write_file(file_path: str, content: str) -> str:
382
+ """Write content to a file."""
383
+ try:
384
+ path = Path(file_path)
385
+ path.parent.mkdir(parents=True, exist_ok=True)
386
+ path.write_text(content, encoding="utf-8")
387
+ return f"File written: {file_path} ({len(content)} chars)"
388
+ except Exception as e:
389
+ return _error_msg(e)
390
+
391
+ def list_directory(directory: str = ".") -> str:
392
+ """List contents of a directory."""
393
+ try:
394
+ path = Path(directory)
395
+ if not path.exists():
396
+ return f"Error: Directory not found: {directory}"
397
+ items = []
398
+ for item in sorted(path.iterdir()):
399
+ item_type = "DIR" if item.is_dir() else "FILE"
400
+ size = item.stat().st_size if item.is_file() else "-"
401
+ items.append(f"{item_type:4} {size:>12} {item.name}")
402
+ return "\n".join(items) if items else "Directory is empty."
403
+ except Exception as e:
404
+ return _error_msg(e)
405
+
406
+ def create_directory(directory: str) -> str:
407
+ """Create a directory (including parents)."""
408
+ try:
409
+ Path(directory).mkdir(parents=True, exist_ok=True)
410
+ return f"Directory created: {directory}"
411
+ except Exception as e:
412
+ return _error_msg(e)
413
+
414
+ def delete_file(file_path: str) -> str:
415
+ """Delete a file."""
416
+ try:
417
+ path = Path(file_path)
418
+ if not path.exists():
419
+ return f"Error: File not found: {file_path}"
420
+ if path.is_dir():
421
+ shutil.rmtree(path)
422
+ return f"Directory deleted: {file_path}"
423
+ path.unlink()
424
+ return f"File deleted: {file_path}"
425
+ except Exception as e:
426
+ return _error_msg(e)
427
+
428
+ def move_file(source: str, destination: str) -> str:
429
+ """Move a file or directory."""
430
+ try:
431
+ shutil.move(source, destination)
432
+ return f"Moved: {source} -> {destination}"
433
+ except Exception as e:
434
+ return _error_msg(e)
435
+
436
+ def copy_file(source: str, destination: str) -> str:
437
+ """Copy a file or directory."""
438
+ try:
439
+ src = Path(source)
440
+ dst = Path(destination)
441
+ if src.is_dir():
442
+ shutil.copytree(src, dst, dirs_exist_ok=True)
443
+ else:
444
+ dst.parent.mkdir(parents=True, exist_ok=True)
445
+ shutil.copy2(src, dst)
446
+ return f"Copied: {source} -> {destination}"
447
+ except Exception as e:
448
+ return _error_msg(e)
449
+
450
+ def file_info(file_path: str) -> str:
451
+ """Get detailed information about a file."""
452
+ try:
453
+ path = Path(file_path)
454
+ if not path.exists():
455
+ return f"Error: File not found: {file_path}"
456
+ stat = path.stat()
457
+ info = {
458
+ "name": path.name,
459
+ "absolute_path": str(path.absolute()),
460
+ "size_bytes": stat.st_size,
461
+ "size_human": _human_readable_size(stat.st_size),
462
+ "is_file": path.is_file(),
463
+ "is_dir": path.is_dir(),
464
+ "is_symlink": path.is_symlink(),
465
+ "modified": datetime.datetime.fromtimestamp(stat.st_mtime).isoformat(),
466
+ "created": datetime.datetime.fromtimestamp(stat.st_ctime).isoformat(),
467
+ "accessed": datetime.datetime.fromtimestamp(stat.st_atime).isoformat(),
468
+ "permissions": oct(stat.st_mode)[-3:],
469
+ }
470
+ return json.dumps(info, indent=2)
471
+ except Exception as e:
472
+ return _error_msg(e)
473
+
474
+ def compress_files(files: str, output_path: str = "/tmp/archive.zip") -> str:
475
+ """Compress files into a zip archive. files can be comma-separated paths or a directory."""
476
+ try:
477
+ paths = [p.strip() for p in files.split(",")]
478
+ with zipfile.ZipFile(output_path, "w", zipfile.ZIP_DEFLATED) as zf:
479
+ for p in paths:
480
+ path = Path(p)
481
+ if path.is_dir():
482
+ for file in path.rglob("*"):
483
+ if file.is_file():
484
+ zf.write(file, file.relative_to(path.parent))
485
+ elif path.is_file():
486
+ zf.write(path, path.name)
487
+ return f"Archive created: {output_path}"
488
+ except Exception as e:
489
+ return _error_msg(e)
490
+
491
+ def decompress_archive(archive_path: str, output_dir: str = "/tmp/extracted") -> str:
492
+ """Decompress a zip archive."""
493
+ try:
494
+ Path(output_dir).mkdir(parents=True, exist_ok=True)
495
+ with zipfile.ZipFile(archive_path, "r") as zf:
496
+ zf.extractall(output_dir)
497
+ return f"Archive extracted to: {output_dir}"
498
+ except Exception as e:
499
+ return _error_msg(e)
500
+
501
+ # =============================================================================
502
+ # ENCODING TOOLS
503
+ # =============================================================================
504
+
505
+ def base64_encode(text: str) -> str:
506
+ """Encode text to base64."""
507
+ try:
508
+ encoded = base64.b64encode(text.encode("utf-8")).decode("utf-8")
509
+ return encoded
510
+ except Exception as e:
511
+ return _error_msg(e)
512
+
513
+ def base64_decode(text: str) -> str:
514
+ """Decode base64 to text."""
515
+ try:
516
+ decoded = base64.b64decode(text.encode("utf-8")).decode("utf-8", errors="replace")
517
+ return decoded
518
+ except Exception as e:
519
+ return _error_msg(e)
520
+
521
+ def hash_data(data: str, algorithm: str = "sha256") -> str:
522
+ """Hash data using various algorithms (md5, sha1, sha256, sha512, blake2b)."""
523
+ try:
524
+ algo = algorithm.lower()
525
+ if algo == "md5":
526
+ h = hashlib.md5(data.encode("utf-8"))
527
+ elif algo == "sha1":
528
+ h = hashlib.sha1(data.encode("utf-8"))
529
+ elif algo == "sha256":
530
+ h = hashlib.sha256(data.encode("utf-8"))
531
+ elif algo == "sha512":
532
+ h = hashlib.sha512(data.encode("utf-8"))
533
+ elif algo == "blake2b":
534
+ h = hashlib.blake2b(data.encode("utf-8"))
535
+ else:
536
+ return f"Error: Unsupported algorithm '{algorithm}'. Use: md5, sha1, sha256, sha512, blake2b"
537
+ return f"{algo.upper()}: {h.hexdigest()}"
538
+ except Exception as e:
539
+ return _error_msg(e)
540
+
541
+ # =============================================================================
542
+ # GENERATOR TOOLS
543
+ # =============================================================================
544
+
545
+ def generate_password(length: int = 16, use_special: bool = True) -> str:
546
+ """Generate a secure random password."""
547
+ try:
548
+ length = max(4, min(int(length), 128))
549
+ chars = string.ascii_letters + string.digits
550
+ if use_special:
551
+ chars += "!@#$%^&*()_+-=[]{}|;:,.<>?"
552
+ pwd = "".join(random.SystemRandom().choice(chars) for _ in range(length))
553
+ return pwd
554
+ except Exception as e:
555
+ return _error_msg(e)
556
+
557
+ def generate_qr(data: str, output_path: str = "/tmp/qr.png") -> str:
558
+ """Generate a QR code image."""
559
+ try:
560
+ qr = qrcode.QRCode(version=1, box_size=10, border=4)
561
+ qr.add_data(data)
562
+ qr.make(fit=True)
563
+ img = qr.make_image(fill_color="black", back_color="white")
564
+ img.save(output_path)
565
+ return f"QR code saved to: {output_path}"
566
+ except Exception as e:
567
+ return _error_msg(e)
568
+
569
+ def generate_barcode(data: str, output_path: str = "/tmp/barcode.png") -> str:
570
+ """Generate a Code128 barcode image."""
571
+ try:
572
+ barcode_obj = Code128(data, writer=ImageWriter())
573
+ barcode_obj.save(output_path.replace(".png", ""))
574
+ return f"Barcode saved to: {output_path}"
575
+ except Exception as e:
576
+ return _error_msg(e)
577
+
578
+ def url_shortener(long_url: str) -> str:
579
+ """Shorten a URL using TinyURL."""
580
+ try:
581
+ api_url = f"http://tinyurl.com/api-create.php?url={urllib.parse.quote(long_url)}"
582
+ resp = requests.get(api_url, timeout=15)
583
+ resp.raise_for_status()
584
+ return f"Short URL: {resp.text}"
585
+ except Exception as e:
586
+ return _error_msg(e)
587
+
588
+ def random_number(min_val: int = 0, max_val: int = 100) -> str:
589
+ """Generate a random number in a range."""
590
+ try:
591
+ result = random.randint(int(min_val), int(max_val))
592
+ return str(result)
593
+ except Exception as e:
594
+ return _error_msg(e)
595
+
596
+ def uuid_generator(count: int = 1) -> str:
597
+ """Generate UUID(s)."""
598
+ try:
599
+ count = max(1, min(int(count), 20))
600
+ uuids = [str(uuid.uuid4()) for _ in range(count)]
601
+ return "\n".join(uuids)
602
+ except Exception as e:
603
+ return _error_msg(e)
604
+
605
+ # =============================================================================
606
+ # NETWORK TOOLS
607
+ # =============================================================================
608
+
609
+ def dns_lookup(domain: str, record_type: str = "A") -> str:
610
+ """Perform DNS lookup for a domain."""
611
+ try:
612
+ answers = dns.resolver.resolve(domain, record_type)
613
+ results = [str(rdata) for rdata in answers]
614
+ return f"{record_type} records for {domain}:\n" + "\n".join(results)
615
+ except Exception as e:
616
+ return _error_msg(e)
617
+
618
+ def ip_lookup(ip_address: str = "") -> str:
619
+ """Get IP geolocation info from ipapi.co."""
620
+ try:
621
+ url = f"https://ipapi.co/{ip_address}/json/" if ip_address else "https://ipapi.co/json/"
622
+ resp = requests.get(url, timeout=15)
623
+ resp.raise_for_status()
624
+ data = resp.json()
625
+ if "error" in data:
626
+ return f"Error: {data.get('reason', 'Unknown error')}"
627
+ lines = [f"{k}: {v}" for k, v in data.items() if v is not None]
628
+ return "\n".join(lines)
629
+ except Exception as e:
630
+ return _error_msg(e)
631
+
632
+ def whois_lookup(domain: str) -> str:
633
+ """Perform WHOIS lookup for a domain."""
634
+ try:
635
+ import whois
636
+ w = whois.whois(domain)
637
+ info = {}
638
+ for key in ["domain_name", "registrar", "creation_date", "expiration_date",
639
+ "name_servers", "status", "emails", "org", "country"]:
640
+ val = getattr(w, key, None)
641
+ if val is not None:
642
+ info[key] = str(val)
643
+ return json.dumps(info, indent=2, default=str)
644
+ except Exception as e:
645
+ return _error_msg(e)
646
+
647
+ def ping_host(host: str, count: int = 4) -> str:
648
+ """Ping a host."""
649
+ try:
650
+ count = min(int(count), 20)
651
+ cmd = ["ping", "-c", str(count), host]
652
+ result = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
653
+ return result.stdout if result.stdout else result.stderr
654
+ except Exception as e:
655
+ return _error_msg(e)
656
+
657
+ def port_scan(host: str, ports: str = "80,443,22,21,25,3306,8080") -> str:
658
+ """Scan ports on a host."""
659
+ try:
660
+ port_list = [int(p.strip()) for p in ports.split(",")]
661
+ results = []
662
+ for port in port_list:
663
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
664
+ sock.settimeout(2)
665
+ result = sock.connect_ex((host, port))
666
+ status = "OPEN" if result == 0 else "CLOSED"
667
+ results.append(f"Port {port}: {status}")
668
+ sock.close()
669
+ return "\n".join(results)
670
+ except Exception as e:
671
+ return _error_msg(e)
672
+
673
+ # =============================================================================
674
+ # TEXT PROCESSING TOOLS
675
+ # =============================================================================
676
+
677
+ def encode_url(text: str) -> str:
678
+ """URL-encode a string."""
679
+ try:
680
+ return urllib.parse.quote(text)
681
+ except Exception as e:
682
+ return _error_msg(e)
683
+
684
+ def decode_url(text: str) -> str:
685
+ """URL-decode a string."""
686
+ try:
687
+ return urllib.parse.unquote(text)
688
+ except Exception as e:
689
+ return _error_msg(e)
690
+
691
+ def html_to_text(html_content: str) -> str:
692
+ """Convert HTML to plain text."""
693
+ try:
694
+ soup = BeautifulSoup(html_content, "html.parser")
695
+ for tag in soup(["script", "style"]):
696
+ tag.decompose()
697
+ text = soup.get_text(separator="\n", strip=True)
698
+ return "\n".join(line.strip() for line in text.splitlines() if line.strip())
699
+ except Exception as e:
700
+ return _error_msg(e)
701
+
702
+ def markdown_to_html(markdown_text: str) -> str:
703
+ """Convert Markdown to HTML."""
704
+ try:
705
+ import markdown
706
+ html_result = markdown.markdown(markdown_text)
707
+ return html_result
708
+ except Exception as e:
709
+ return _error_msg(e)
710
+
711
+ def json_formatter(json_text: str, indent: int = 2) -> str:
712
+ """Format and validate JSON."""
713
+ try:
714
+ data = json.loads(json_text)
715
+ return json.dumps(data, indent=int(indent), ensure_ascii=False)
716
+ except Exception as e:
717
+ return _error_msg(e)
718
+
719
+ def csv_to_json(csv_text: str) -> str:
720
+ """Convert CSV text to JSON."""
721
+ try:
722
+ import csv
723
+ import io
724
+ reader = csv.DictReader(io.StringIO(csv_text))
725
+ rows = list(reader)
726
+ return json.dumps(rows, indent=2, ensure_ascii=False)
727
+ except Exception as e:
728
+ return _error_msg(e)
729
+
730
+ def word_count(text: str) -> str:
731
+ """Count words, characters, lines in text."""
732
+ try:
733
+ words = len(text.split())
734
+ chars = len(text)
735
+ chars_no_space = len(text.replace(" ", "").replace("\n", ""))
736
+ lines = len(text.splitlines())
737
+ return (
738
+ f"Words: {words}\n"
739
+ f"Characters (with spaces): {chars}\n"
740
+ f"Characters (no spaces): {chars_no_space}\n"
741
+ f"Lines: {lines}"
742
+ )
743
+ except Exception as e:
744
+ return _error_msg(e)
745
+
746
+ def text_diff(text1: str, text2: str) -> str:
747
+ """Compare two texts and show differences using difflib."""
748
+ try:
749
+ lines1 = text1.splitlines()
750
+ lines2 = text2.splitlines()
751
+ diff = difflib.unified_diff(lines1, lines2, lineterm="")
752
+ result = "\n".join(diff)
753
+ return result if result else "No differences found."
754
+ except Exception as e:
755
+ return _error_msg(e)
756
+
757
+ def regex_extract(text: str, pattern: str) -> str:
758
+ """Extract matches using a regex pattern."""
759
+ try:
760
+ matches = re.findall(pattern, text)
761
+ if not matches:
762
+ return "No matches found."
763
+ return "\n".join(str(m) for m in matches)
764
+ except Exception as e:
765
+ return _error_msg(e)
766
+
767
+ def regex_replace(text: str, pattern: str, replacement: str = "") -> str:
768
+ """Replace text using a regex pattern."""
769
+ try:
770
+ result = re.sub(pattern, replacement, text)
771
+ return result
772
+ except Exception as e:
773
+ return _error_msg(e)
774
+
775
+ def sentiment_analysis(text: str) -> str:
776
+ """Analyze sentiment of text using TextBlob."""
777
+ try:
778
+ blob = textblob.TextBlob(text)
779
+ polarity = blob.sentiment.polarity
780
+ subjectivity = blob.sentiment.subjectivity
781
+ if polarity > 0.1:
782
+ sentiment = "Positive"
783
+ elif polarity < -0.1:
784
+ sentiment = "Negative"
785
+ else:
786
+ sentiment = "Neutral"
787
+ return (
788
+ f"Sentiment: {sentiment}\n"
789
+ f"Polarity: {polarity:.4f} (-1 to +1)\n"
790
+ f"Subjectivity: {subjectivity:.4f} (0 to 1)"
791
+ )
792
+ except Exception as e:
793
+ return _error_msg(e)
794
+
795
+ def summarize_text(text: str, sentences_count: int = 3) -> str:
796
+ """Summarize text using sumy LSA summarizer."""
797
+ try:
798
+ sentences_count = max(1, min(int(sentences_count), 20))
799
+ parser = PlaintextParser.from_string(text, Tokenizer("english"))
800
+ summarizer = LsaSummarizer()
801
+ summary = summarizer(parser.document, sentences_count)
802
+ return " ".join(str(s) for s in summary)
803
+ except Exception as e:
804
+ return _error_msg(e)
805
+
806
+ def extract_entities(text: str) -> str:
807
+ """Extract named entities from text using spaCy."""
808
+ try:
809
+ if nlp is None:
810
+ return "Error: spaCy model not loaded. Run: python -m spacy download en_core_web_sm"
811
+ doc = nlp(text)
812
+ entities = []
813
+ for ent in doc.ents:
814
+ entities.append(f"{ent.text} ({ent.label_})")
815
+ if not entities:
816
+ return "No entities found."
817
+ return "\n".join(entities)
818
+ except Exception as e:
819
+ return _error_msg(e)
820
+
821
+ # =============================================================================
822
+ # CONVERSION TOOLS
823
+ # =============================================================================
824
+
825
+ def currency_converter(amount: float, from_currency: str = "USD", to_currency: str = "EUR") -> str:
826
+ """Convert currency using exchangerate-api.com."""
827
+ try:
828
+ url = f"https://api.exchangerate-api.com/v4/latest/{from_currency.upper()}"
829
+ resp = requests.get(url, timeout=15)
830
+ resp.raise_for_status()
831
+ data = resp.json()
832
+ rate = data["rates"].get(to_currency.upper())
833
+ if rate is None:
834
+ return f"Error: Currency '{to_currency}' not found."
835
+ result = float(amount) * rate
836
+ return f"{amount} {from_currency.upper()} = {result:.4f} {to_currency.upper()} (rate: {rate})"
837
+ except Exception as e:
838
+ return _error_msg(e)
839
+
840
+ def unit_converter(value: float, from_unit: str, to_unit: str) -> str:
841
+ """Convert between units using pint."""
842
+ try:
843
+ quantity = ureg.Quantity(float(value), from_unit)
844
+ result = quantity.to(to_unit)
845
+ return f"{value} {from_unit} = {result.magnitude:.6f} {to_unit}"
846
+ except Exception as e:
847
+ return _error_msg(e)
848
+
849
+ def timestamp_converter(timestamp: str, to_format: str = "iso") -> str:
850
+ """Convert Unix timestamp or ISO string to various formats."""
851
+ try:
852
+ try:
853
+ ts = float(timestamp)
854
+ dt = datetime.datetime.fromtimestamp(ts, tz=datetime.timezone.utc)
855
+ except ValueError:
856
+ dt = datetime.datetime.fromisoformat(timestamp.replace("Z", "+00:00"))
857
+
858
+ if to_format.lower() == "iso":
859
+ return dt.isoformat()
860
+ elif to_format.lower() == "rfc":
861
+ return dt.strftime("%a, %d %b %Y %H:%M:%S GMT")
862
+ elif to_format.lower() == "date":
863
+ return dt.strftime("%Y-%m-%d")
864
+ elif to_format.lower() == "time":
865
+ return dt.strftime("%H:%M:%S")
866
+ elif to_format.lower() == "unix":
867
+ return str(int(dt.timestamp()))
868
+ else:
869
+ return dt.strftime(to_format)
870
+ except Exception as e:
871
+ return _error_msg(e)
872
+
873
+ # =============================================================================
874
+ # MEDIA TOOLS
875
+ # =============================================================================
876
+
877
+ def youtube_transcript(video_url: str, language: str = "en") -> str:
878
+ """Get transcript from a YouTube video."""
879
+ try:
880
+ patterns = [
881
+ r"v=([a-zA-Z0-9_-]{11})",
882
+ r"youtu\.be/([a-zA-Z0-9_-]{11})",
883
+ r"shorts/([a-zA-Z0-9_-]{11})",
884
+ ]
885
+ video_id = None
886
+ for p in patterns:
887
+ m = re.search(p, video_url)
888
+ if m:
889
+ video_id = m.group(1)
890
+ break
891
+ if not video_id:
892
+ return "Error: Could not extract YouTube video ID from URL."
893
+
894
+ transcript_list = YouTubeTranscriptApi.get_transcript(video_id, languages=[language])
895
+ lines = [f"[{t['start']:.1f}s] {t['text']}" for t in transcript_list]
896
+ return _truncate("\n".join(lines))
897
+ except Exception as e:
898
+ return _error_msg(e)
899
+
900
+ def extract_links(text: str) -> str:
901
+ """Extract URLs from text."""
902
+ try:
903
+ pattern = r"https?://[^\s<>"{}|\^`\[\]]+"
904
+ matches = re.findall(pattern, text)
905
+ if not matches:
906
+ return "No links found."
907
+ return "\n".join(matches)
908
+ except Exception as e:
909
+ return _error_msg(e)
910
+
911
+ def extract_emails(text: str) -> str:
912
+ """Extract email addresses from text."""
913
+ try:
914
+ pattern = r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}"
915
+ matches = re.findall(pattern, text)
916
+ if not matches:
917
+ return "No emails found."
918
+ return "\n".join(set(matches))
919
+ except Exception as e:
920
+ return _error_msg(e)
921
+
922
+ def extract_phones(text: str) -> str:
923
+ """Extract phone numbers from text."""
924
+ try:
925
+ pattern = r"(?:\+?1[-.\s]?)?\(?[0-9]{3}\)?[-.\s]?[0-9]{3}[-.\s]?[0-9]{4}"
926
+ matches = re.findall(pattern, text)
927
+ if not matches:
928
+ return "No phone numbers found."
929
+ return "\n".join(set(matches))
930
+ except Exception as e:
931
+ return _error_msg(e)
932
+
933
+ # =============================================================================
934
+ # SYSTEM TOOLS
935
+ # =============================================================================
936
+
937
+ def git_status(repo_path: str = ".") -> str:
938
+ """Get git status of a repository."""
939
+ try:
940
+ result = subprocess.run(
941
+ ["git", "-C", repo_path, "status", "-sb"],
942
+ capture_output=True, text=True, timeout=15
943
+ )
944
+ return result.stdout if result.stdout else result.stderr
945
+ except Exception as e:
946
+ return _error_msg(e)
947
+
948
+ def git_log(repo_path: str = ".", count: int = 10) -> str:
949
+ """Get git log of a repository."""
950
+ try:
951
+ count = min(int(count), 50)
952
+ result = subprocess.run(
953
+ ["git", "-C", repo_path, "log", f"--max-count={count}", "--oneline", "--decorate"],
954
+ capture_output=True, text=True, timeout=15
955
+ )
956
+ return result.stdout if result.stdout else result.stderr
957
+ except Exception as e:
958
+ return _error_msg(e)
959
+
960
+ def system_info() -> str:
961
+ """Get system information using platform and psutil."""
962
+ try:
963
+ info = {
964
+ "platform": platform.platform(),
965
+ "system": platform.system(),
966
+ "release": platform.release(),
967
+ "version": platform.version(),
968
+ "machine": platform.machine(),
969
+ "processor": platform.processor(),
970
+ "hostname": platform.node(),
971
+ "python_version": platform.python_version(),
972
+ "cpu_count": psutil.cpu_count(),
973
+ "cpu_percent": psutil.cpu_percent(interval=1),
974
+ "memory_total_gb": round(psutil.virtual_memory().total / (1024**3), 2),
975
+ "memory_available_gb": round(psutil.virtual_memory().available / (1024**3), 2),
976
+ "memory_percent": psutil.virtual_memory().percent,
977
+ "disk_usage_percent": psutil.disk_usage("/").percent,
978
+ "boot_time": datetime.datetime.fromtimestamp(psutil.boot_time()).isoformat(),
979
+ }
980
+ return json.dumps(info, indent=2)
981
+ except Exception as e:
982
+ return _error_msg(e)
983
+
984
+ # =============================================================================
985
+ # TOOL_MAP
986
+ # =============================================================================
987
+
988
+ TOOL_MAP = {
989
+ # Web
990
+ "web_search": web_search,
991
+ "browse_page": browse_page,
992
+ "playwright_screenshot": playwright_screenshot,
993
+ "playwright_pdf": playwright_pdf,
994
+ # Documents
995
+ "create_pdf": create_pdf,
996
+ "create_docx": create_docx,
997
+ # Images
998
+ "describe_image": describe_image,
999
+ "ocr_image": ocr_image,
1000
+ # Translation
1001
+ "translate_text": translate_text,
1002
+ # Data
1003
+ "get_weather": get_weather,
1004
+ "get_news": get_news,
1005
+ "get_stock_price": get_stock_price,
1006
+ # Math/Code
1007
+ "calculator": calculator,
1008
+ "python_interpreter": python_interpreter,
1009
+ "execute_command": execute_command,
1010
+ "code_formatter": code_formatter,
1011
+ # File Ops
1012
+ "read_file": read_file,
1013
+ "write_file": write_file,
1014
+ "list_directory": list_directory,
1015
+ "create_directory": create_directory,
1016
+ "delete_file": delete_file,
1017
+ "move_file": move_file,
1018
+ "copy_file": copy_file,
1019
+ "file_info": file_info,
1020
+ "compress_files": compress_files,
1021
+ "decompress_archive": decompress_archive,
1022
+ # Encoding
1023
+ "base64_encode": base64_encode,
1024
+ "base64_decode": base64_decode,
1025
+ "hash_data": hash_data,
1026
+ # Generators
1027
+ "generate_password": generate_password,
1028
+ "generate_qr": generate_qr,
1029
+ "generate_barcode": generate_barcode,
1030
+ "url_shortener": url_shortener,
1031
+ "random_number": random_number,
1032
+ "uuid_generator": uuid_generator,
1033
+ # Network
1034
+ "dns_lookup": dns_lookup,
1035
+ "ip_lookup": ip_lookup,
1036
+ "whois_lookup": whois_lookup,
1037
+ "ping_host": ping_host,
1038
+ "port_scan": port_scan,
1039
+ # Text Processing
1040
+ "encode_url": encode_url,
1041
+ "decode_url": decode_url,
1042
+ "html_to_text": html_to_text,
1043
+ "markdown_to_html": markdown_to_html,
1044
+ "json_formatter": json_formatter,
1045
+ "csv_to_json": csv_to_json,
1046
+ "word_count": word_count,
1047
+ "text_diff": text_diff,
1048
+ "regex_extract": regex_extract,
1049
+ "regex_replace": regex_replace,
1050
+ "sentiment_analysis": sentiment_analysis,
1051
+ "summarize_text": summarize_text,
1052
+ "extract_entities": extract_entities,
1053
+ # Conversion
1054
+ "currency_converter": currency_converter,
1055
+ "unit_converter": unit_converter,
1056
+ "timestamp_converter": timestamp_converter,
1057
+ # Media
1058
+ "youtube_transcript": youtube_transcript,
1059
+ "extract_links": extract_links,
1060
+ "extract_emails": extract_emails,
1061
+ "extract_phones": extract_phones,
1062
+ # System
1063
+ "git_status": git_status,
1064
+ "git_log": git_log,
1065
+ "system_info": system_info,
1066
+ }
1067
+
1068
+ # =============================================================================
1069
+ # TOOL_DEFINITIONS (OpenAI format)
1070
+ # =============================================================================
1071
+
1072
+ TOOL_DEFINITIONS = [
1073
+ # ===================== WEB =====================
1074
+ {
1075
+ "type": "function",
1076
+ "function": {
1077
+ "name": "web_search",
1078
+ "description": "Search the web using DuckDuckGo and return search results with titles, URLs, and snippets.",
1079
+ "parameters": {
1080
+ "type": "object",
1081
+ "properties": {
1082
+ "query": {"type": "string", "description": "Search query string"},
1083
+ "max_results": {"type": "integer", "description": "Maximum number of results (1-10)", "default": 5}
1084
+ },
1085
+ "required": ["query"]
1086
+ }
1087
+ }
1088
+ },
1089
+ {
1090
+ "type": "function",
1091
+ "function": {
1092
+ "name": "browse_page",
1093
+ "description": "Fetch and extract clean text content from a web page using requests and BeautifulSoup.",
1094
+ "parameters": {
1095
+ "type": "object",
1096
+ "properties": {
1097
+ "url": {"type": "string", "description": "URL of the page to browse"}
1098
+ },
1099
+ "required": ["url"]
1100
+ }
1101
+ }
1102
+ },
1103
+ {
1104
+ "type": "function",
1105
+ "function": {
1106
+ "name": "playwright_screenshot",
1107
+ "description": "Take a full-page screenshot of a webpage using Playwright and save as PNG.",
1108
+ "parameters": {
1109
+ "type": "object",
1110
+ "properties": {
1111
+ "url": {"type": "string", "description": "URL to screenshot"},
1112
+ "output_path": {"type": "string", "description": "Path to save the screenshot", "default": "/tmp/screenshot.png"}
1113
+ },
1114
+ "required": ["url"]
1115
+ }
1116
+ }
1117
+ },
1118
+ {
1119
+ "type": "function",
1120
+ "function": {
1121
+ "name": "playwright_pdf",
1122
+ "description": "Save a webpage as a PDF using Playwright.",
1123
+ "parameters": {
1124
+ "type": "object",
1125
+ "properties": {
1126
+ "url": {"type": "string", "description": "URL to convert to PDF"},
1127
+ "output_path": {"type": "string", "description": "Path to save the PDF", "default": "/tmp/page.pdf"}
1128
+ },
1129
+ "required": ["url"]
1130
+ }
1131
+ }
1132
+ },
1133
+ # ===================== DOCUMENTS =====================
1134
+ {
1135
+ "type": "function",
1136
+ "function": {
1137
+ "name": "create_pdf",
1138
+ "description": "Create a PDF file from text content using ReportLab.",
1139
+ "parameters": {
1140
+ "type": "object",
1141
+ "properties": {
1142
+ "content": {"type": "string", "description": "Text content for the PDF"},
1143
+ "output_path": {"type": "string", "description": "Output file path", "default": "/tmp/output.pdf"},
1144
+ "title": {"type": "string", "description": "PDF title", "default": "Document"}
1145
+ },
1146
+ "required": ["content"]
1147
+ }
1148
+ }
1149
+ },
1150
+ {
1151
+ "type": "function",
1152
+ "function": {
1153
+ "name": "create_docx",
1154
+ "description": "Create a DOCX file from text content using python-docx.",
1155
+ "parameters": {
1156
+ "type": "object",
1157
+ "properties": {
1158
+ "content": {"type": "string", "description": "Text content for the document"},
1159
+ "output_path": {"type": "string", "description": "Output file path", "default": "/tmp/output.docx"},
1160
+ "title": {"type": "string", "description": "Document title", "default": "Document"}
1161
+ },
1162
+ "required": ["content"]
1163
+ }
1164
+ }
1165
+ },
1166
+ # ===================== IMAGES =====================
1167
+ {
1168
+ "type": "function",
1169
+ "function": {
1170
+ "name": "describe_image",
1171
+ "description": "Describe an image using Gemini Vision API. Requires GEMINI_API_KEY.",
1172
+ "parameters": {
1173
+ "type": "object",
1174
+ "properties": {
1175
+ "image_path": {"type": "string", "description": "Path to the image file"},
1176
+ "prompt": {"type": "string", "description": "Custom prompt for description", "default": "Describe this image in detail."}
1177
+ },
1178
+ "required": ["image_path"]
1179
+ }
1180
+ }
1181
+ },
1182
+ {
1183
+ "type": "function",
1184
+ "function": {
1185
+ "name": "ocr_image",
1186
+ "description": "Extract text from an image using pytesseract OCR.",
1187
+ "parameters": {
1188
+ "type": "object",
1189
+ "properties": {
1190
+ "image_path": {"type": "string", "description": "Path to the image file"},
1191
+ "language": {"type": "string", "description": "OCR language code", "default": "eng"}
1192
+ },
1193
+ "required": ["image_path"]
1194
+ }
1195
+ }
1196
+ },
1197
+ # ===================== TRANSLATION =====================
1198
+ {
1199
+ "type": "function",
1200
+ "function": {
1201
+ "name": "translate_text",
1202
+ "description": "Translate text using Google Translate via deep_translator.",
1203
+ "parameters": {
1204
+ "type": "object",
1205
+ "properties": {
1206
+ "text": {"type": "string", "description": "Text to translate"},
1207
+ "target_language": {"type": "string", "description": "Target language code (e.g., en, es, fr, de, ja)", "default": "en"},
1208
+ "source_language": {"type": "string", "description": "Source language code or 'auto'", "default": "auto"}
1209
+ },
1210
+ "required": ["text"]
1211
+ }
1212
+ }
1213
+ },
1214
+ # ===================== DATA =====================
1215
+ {
1216
+ "type": "function",
1217
+ "function": {
1218
+ "name": "get_weather",
1219
+ "description": "Get current weather information for a location using wttr.in.",
1220
+ "parameters": {
1221
+ "type": "object",
1222
+ "properties": {
1223
+ "location": {"type": "string", "description": "City name or location"}
1224
+ },
1225
+ "required": ["location"]
1226
+ }
1227
+ }
1228
+ },
1229
+ {
1230
+ "type": "function",
1231
+ "function": {
1232
+ "name": "get_news",
1233
+ "description": "Get latest news using DuckDuckGo news search.",
1234
+ "parameters": {
1235
+ "type": "object",
1236
+ "properties": {
1237
+ "query": {"type": "string", "description": "News search query", "default": "latest news"},
1238
+ "max_results": {"type": "integer", "description": "Maximum results (1-10)", "default": 5}
1239
+ },
1240
+ "required": []
1241
+ }
1242
+ }
1243
+ },
1244
+ {
1245
+ "type": "function",
1246
+ "function": {
1247
+ "name": "get_stock_price",
1248
+ "description": "Get stock price and info using yfinance.",
1249
+ "parameters": {
1250
+ "type": "object",
1251
+ "properties": {
1252
+ "ticker": {"type": "string", "description": "Stock ticker symbol (e.g., AAPL, TSLA, GOOGL)"}
1253
+ },
1254
+ "required": ["ticker"]
1255
+ }
1256
+ }
1257
+ },
1258
+ # ===================== MATH/CODE =====================
1259
+ {
1260
+ "type": "function",
1261
+ "function": {
1262
+ "name": "calculator",
1263
+ "description": "Evaluate a mathematical expression safely using math functions.",
1264
+ "parameters": {
1265
+ "type": "object",
1266
+ "properties": {
1267
+ "expression": {"type": "string", "description": "Mathematical expression to evaluate (e.g., 'sin(pi/2) + sqrt(16)')"}
1268
+ },
1269
+ "required": ["expression"]
1270
+ }
1271
+ }
1272
+ },
1273
+ {
1274
+ "type": "function",
1275
+ "function": {
1276
+ "name": "python_interpreter",
1277
+ "description": "Execute Python code safely and return the result.",
1278
+ "parameters": {
1279
+ "type": "object",
1280
+ "properties": {
1281
+ "code": {"type": "string", "description": "Python code to execute"}
1282
+ },
1283
+ "required": ["code"]
1284
+ }
1285
+ }
1286
+ },
1287
+ {
1288
+ "type": "function",
1289
+ "function": {
1290
+ "name": "execute_command",
1291
+ "description": "Execute a shell command safely with timeout.",
1292
+ "parameters": {
1293
+ "type": "object",
1294
+ "properties": {
1295
+ "command": {"type": "string", "description": "Shell command to execute"},
1296
+ "timeout": {"type": "integer", "description": "Timeout in seconds", "default": 30}
1297
+ },
1298
+ "required": ["command"]
1299
+ }
1300
+ }
1301
+ },
1302
+ {
1303
+ "type": "function",
1304
+ "function": {
1305
+ "name": "code_formatter",
1306
+ "description": "Format Python code using black.",
1307
+ "parameters": {
1308
+ "type": "object",
1309
+ "properties": {
1310
+ "code": {"type": "string", "description": "Python code to format"},
1311
+ "language": {"type": "string", "description": "Programming language (only 'python' supported)", "default": "python"}
1312
+ },
1313
+ "required": ["code"]
1314
+ }
1315
+ }
1316
+ },
1317
+ # ===================== FILE OPS =====================
1318
+ {
1319
+ "type": "function",
1320
+ "function": {
1321
+ "name": "read_file",
1322
+ "description": "Read the contents of a file.",
1323
+ "parameters": {
1324
+ "type": "object",
1325
+ "properties": {
1326
+ "file_path": {"type": "string", "description": "Path to the file"}
1327
+ },
1328
+ "required": ["file_path"]
1329
+ }
1330
+ }
1331
+ },
1332
+ {
1333
+ "type": "function",
1334
+ "function": {
1335
+ "name": "write_file",
1336
+ "description": "Write content to a file (creates directories if needed).",
1337
+ "parameters": {
1338
+ "type": "object",
1339
+ "properties": {
1340
+ "file_path": {"type": "string", "description": "Path to write the file"},
1341
+ "content": {"type": "string", "description": "Content to write"}
1342
+ },
1343
+ "required": ["file_path", "content"]
1344
+ }
1345
+ }
1346
+ },
1347
+ {
1348
+ "type": "function",
1349
+ "function": {
1350
+ "name": "list_directory",
1351
+ "description": "List files and directories in a path.",
1352
+ "parameters": {
1353
+ "type": "object",
1354
+ "properties": {
1355
+ "directory": {"type": "string", "description": "Directory path", "default": "."}
1356
+ },
1357
+ "required": []
1358
+ }
1359
+ }
1360
+ },
1361
+ {
1362
+ "type": "function",
1363
+ "function": {
1364
+ "name": "create_directory",
1365
+ "description": "Create a directory (including parent directories).",
1366
+ "parameters": {
1367
+ "type": "object",
1368
+ "properties": {
1369
+ "directory": {"type": "string", "description": "Directory path to create"}
1370
+ },
1371
+ "required": ["directory"]
1372
+ }
1373
+ }
1374
+ },
1375
+ {
1376
+ "type": "function",
1377
+ "function": {
1378
+ "name": "delete_file",
1379
+ "description": "Delete a file or directory.",
1380
+ "parameters": {
1381
+ "type": "object",
1382
+ "properties": {
1383
+ "file_path": {"type": "string", "description": "Path to delete"}
1384
+ },
1385
+ "required": ["file_path"]
1386
+ }
1387
+ }
1388
+ },
1389
+ {
1390
+ "type": "function",
1391
+ "function": {
1392
+ "name": "move_file",
1393
+ "description": "Move a file or directory to a new location.",
1394
+ "parameters": {
1395
+ "type": "object",
1396
+ "properties": {
1397
+ "source": {"type": "string", "description": "Source path"},
1398
+ "destination": {"type": "string", "description": "Destination path"}
1399
+ },
1400
+ "required": ["source", "destination"]
1401
+ }
1402
+ }
1403
+ },
1404
+ {
1405
+ "type": "function",
1406
+ "function": {
1407
+ "name": "copy_file",
1408
+ "description": "Copy a file or directory.",
1409
+ "parameters": {
1410
+ "type": "object",
1411
+ "properties": {
1412
+ "source": {"type": "string", "description": "Source path"},
1413
+ "destination": {"type": "string", "description": "Destination path"}
1414
+ },
1415
+ "required": ["source", "destination"]
1416
+ }
1417
+ }
1418
+ },
1419
+ {
1420
+ "type": "function",
1421
+ "function": {
1422
+ "name": "file_info",
1423
+ "description": "Get detailed information about a file (size, dates, permissions).",
1424
+ "parameters": {
1425
+ "type": "object",
1426
+ "properties": {
1427
+ "file_path": {"type": "string", "description": "Path to the file"}
1428
+ },
1429
+ "required": ["file_path"]
1430
+ }
1431
+ }
1432
+ },
1433
+ {
1434
+ "type": "function",
1435
+ "function": {
1436
+ "name": "compress_files",
1437
+ "description": "Compress files or directories into a zip archive.",
1438
+ "parameters": {
1439
+ "type": "object",
1440
+ "properties": {
1441
+ "files": {"type": "string", "description": "Comma-separated file/directory paths"},
1442
+ "output_path": {"type": "string", "description": "Output zip file path", "default": "/tmp/archive.zip"}
1443
+ },
1444
+ "required": ["files"]
1445
+ }
1446
+ }
1447
+ },
1448
+ {
1449
+ "type": "function",
1450
+ "function": {
1451
+ "name": "decompress_archive",
1452
+ "description": "Extract a zip archive to a directory.",
1453
+ "parameters": {
1454
+ "type": "object",
1455
+ "properties": {
1456
+ "archive_path": {"type": "string", "description": "Path to the zip archive"},
1457
+ "output_dir": {"type": "string", "description": "Directory to extract to", "default": "/tmp/extracted"}
1458
+ },
1459
+ "required": ["archive_path"]
1460
+ }
1461
+ }
1462
+ },
1463
+ # ===================== ENCODING =====================
1464
+ {
1465
+ "type": "function",
1466
+ "function": {
1467
+ "name": "base64_encode",
1468
+ "description": "Encode text to base64.",
1469
+ "parameters": {
1470
+ "type": "object",
1471
+ "properties": {
1472
+ "text": {"type": "string", "description": "Text to encode"}
1473
+ },
1474
+ "required": ["text"]
1475
+ }
1476
+ }
1477
+ },
1478
+ {
1479
+ "type": "function",
1480
+ "function": {
1481
+ "name": "base64_decode",
1482
+ "description": "Decode base64 to text.",
1483
+ "parameters": {
1484
+ "type": "object",
1485
+ "properties": {
1486
+ "text": {"type": "string", "description": "Base64 string to decode"}
1487
+ },
1488
+ "required": ["text"]
1489
+ }
1490
+ }
1491
+ },
1492
+ {
1493
+ "type": "function",
1494
+ "function": {
1495
+ "name": "hash_data",
1496
+ "description": "Hash data using md5, sha1, sha256, sha512, or blake2b.",
1497
+ "parameters": {
1498
+ "type": "object",
1499
+ "properties": {
1500
+ "data": {"type": "string", "description": "Data to hash"},
1501
+ "algorithm": {"type": "string", "description": "Hash algorithm", "default": "sha256", "enum": ["md5", "sha1", "sha256", "sha512", "blake2b"]}
1502
+ },
1503
+ "required": ["data"]
1504
+ }
1505
+ }
1506
+ },
1507
+ # ===================== GENERATORS =====================
1508
+ {
1509
+ "type": "function",
1510
+ "function": {
1511
+ "name": "generate_password",
1512
+ "description": "Generate a secure random password.",
1513
+ "parameters": {
1514
+ "type": "object",
1515
+ "properties": {
1516
+ "length": {"type": "integer", "description": "Password length (4-128)", "default": 16},
1517
+ "use_special": {"type": "boolean", "description": "Include special characters", "default": True}
1518
+ },
1519
+ "required": []
1520
+ }
1521
+ }
1522
+ },
1523
+ {
1524
+ "type": "function",
1525
+ "function": {
1526
+ "name": "generate_qr",
1527
+ "description": "Generate a QR code image.",
1528
+ "parameters": {
1529
+ "type": "object",
1530
+ "properties": {
1531
+ "data": {"type": "string", "description": "Data to encode in QR code"},
1532
+ "output_path": {"type": "string", "description": "Output image path", "default": "/tmp/qr.png"}
1533
+ },
1534
+ "required": ["data"]
1535
+ }
1536
+ }
1537
+ },
1538
+ {
1539
+ "type": "function",
1540
+ "function": {
1541
+ "name": "generate_barcode",
1542
+ "description": "Generate a Code128 barcode image.",
1543
+ "parameters": {
1544
+ "type": "object",
1545
+ "properties": {
1546
+ "data": {"type": "string", "description": "Data to encode in barcode"},
1547
+ "output_path": {"type": "string", "description": "Output image path", "default": "/tmp/barcode.png"}
1548
+ },
1549
+ "required": ["data"]
1550
+ }
1551
+ }
1552
+ },
1553
+ {
1554
+ "type": "function",
1555
+ "function": {
1556
+ "name": "url_shortener",
1557
+ "description": "Shorten a URL using TinyURL.",
1558
+ "parameters": {
1559
+ "type": "object",
1560
+ "properties": {
1561
+ "long_url": {"type": "string", "description": "URL to shorten"}
1562
+ },
1563
+ "required": ["long_url"]
1564
+ }
1565
+ }
1566
+ },
1567
+ {
1568
+ "type": "function",
1569
+ "function": {
1570
+ "name": "random_number",
1571
+ "description": "Generate a random integer in a range.",
1572
+ "parameters": {
1573
+ "type": "object",
1574
+ "properties": {
1575
+ "min_val": {"type": "integer", "description": "Minimum value", "default": 0},
1576
+ "max_val": {"type": "integer", "description": "Maximum value", "default": 100}
1577
+ },
1578
+ "required": []
1579
+ }
1580
+ }
1581
+ },
1582
+ {
1583
+ "type": "function",
1584
+ "function": {
1585
+ "name": "uuid_generator",
1586
+ "description": "Generate one or more UUID v4 strings.",
1587
+ "parameters": {
1588
+ "type": "object",
1589
+ "properties": {
1590
+ "count": {"type": "integer", "description": "Number of UUIDs (1-20)", "default": 1}
1591
+ },
1592
+ "required": []
1593
+ }
1594
+ }
1595
+ },
1596
+ # ===================== NETWORK =====================
1597
+ {
1598
+ "type": "function",
1599
+ "function": {
1600
+ "name": "dns_lookup",
1601
+ "description": "Perform DNS lookup for a domain.",
1602
+ "parameters": {
1603
+ "type": "object",
1604
+ "properties": {
1605
+ "domain": {"type": "string", "description": "Domain to lookup"},
1606
+ "record_type": {"type": "string", "description": "DNS record type", "default": "A", "enum": ["A", "AAAA", "MX", "TXT", "NS", "CNAME", "SOA"]}
1607
+ },
1608
+ "required": ["domain"]
1609
+ }
1610
+ }
1611
+ },
1612
+ {
1613
+ "type": "function",
1614
+ "function": {
1615
+ "name": "ip_lookup",
1616
+ "description": "Get IP geolocation information from ipapi.co.",
1617
+ "parameters": {
1618
+ "type": "object",
1619
+ "properties": {
1620
+ "ip_address": {"type": "string", "description": "IP address (empty for own IP)", "default": ""}
1621
+ },
1622
+ "required": []
1623
+ }
1624
+ }
1625
+ },
1626
+ {
1627
+ "type": "function",
1628
+ "function": {
1629
+ "name": "whois_lookup",
1630
+ "description": "Perform WHOIS lookup for a domain.",
1631
+ "parameters": {
1632
+ "type": "object",
1633
+ "properties": {
1634
+ "domain": {"type": "string", "description": "Domain to lookup"}
1635
+ },
1636
+ "required": ["domain"]
1637
+ }
1638
+ }
1639
+ },
1640
+ {
1641
+ "type": "function",
1642
+ "function": {
1643
+ "name": "ping_host",
1644
+ "description": "Ping a host and return results.",
1645
+ "parameters": {
1646
+ "type": "object",
1647
+ "properties": {
1648
+ "host": {"type": "string", "description": "Host to ping"},
1649
+ "count": {"type": "integer", "description": "Number of pings", "default": 4}
1650
+ },
1651
+ "required": ["host"]
1652
+ }
1653
+ }
1654
+ },
1655
+ {
1656
+ "type": "function",
1657
+ "function": {
1658
+ "name": "port_scan",
1659
+ "description": "Scan ports on a host.",
1660
+ "parameters": {
1661
+ "type": "object",
1662
+ "properties": {
1663
+ "host": {"type": "string", "description": "Host to scan"},
1664
+ "ports": {"type": "string", "description": "Comma-separated port numbers", "default": "80,443,22,21,25,3306,8080"}
1665
+ },
1666
+ "required": ["host"]
1667
+ }
1668
+ }
1669
+ },
1670
+ # ===================== TEXT PROCESSING =====================
1671
+ {
1672
+ "type": "function",
1673
+ "function": {
1674
+ "name": "encode_url",
1675
+ "description": "URL-encode a string.",
1676
+ "parameters": {
1677
+ "type": "object",
1678
+ "properties": {
1679
+ "text": {"type": "string", "description": "Text to encode"}
1680
+ },
1681
+ "required": ["text"]
1682
+ }
1683
+ }
1684
+ },
1685
+ {
1686
+ "type": "function",
1687
+ "function": {
1688
+ "name": "decode_url",
1689
+ "description": "URL-decode a string.",
1690
+ "parameters": {
1691
+ "type": "object",
1692
+ "properties": {
1693
+ "text": {"type": "string", "description": "Text to decode"}
1694
+ },
1695
+ "required": ["text"]
1696
+ }
1697
+ }
1698
+ },
1699
+ {
1700
+ "type": "function",
1701
+ "function": {
1702
+ "name": "html_to_text",
1703
+ "description": "Convert HTML content to plain text.",
1704
+ "parameters": {
1705
+ "type": "object",
1706
+ "properties": {
1707
+ "html_content": {"type": "string", "description": "HTML content to convert"}
1708
+ },
1709
+ "required": ["html_content"]
1710
+ }
1711
+ }
1712
+ },
1713
+ {
1714
+ "type": "function",
1715
+ "function": {
1716
+ "name": "markdown_to_html",
1717
+ "description": "Convert Markdown text to HTML.",
1718
+ "parameters": {
1719
+ "type": "object",
1720
+ "properties": {
1721
+ "markdown_text": {"type": "string", "description": "Markdown text to convert"}
1722
+ },
1723
+ "required": ["markdown_text"]
1724
+ }
1725
+ }
1726
+ },
1727
+ {
1728
+ "type": "function",
1729
+ "function": {
1730
+ "name": "json_formatter",
1731
+ "description": "Format and validate JSON text with indentation.",
1732
+ "parameters": {
1733
+ "type": "object",
1734
+ "properties": {
1735
+ "json_text": {"type": "string", "description": "JSON string to format"},
1736
+ "indent": {"type": "integer", "description": "Indentation spaces", "default": 2}
1737
+ },
1738
+ "required": ["json_text"]
1739
+ }
1740
+ }
1741
+ },
1742
+ {
1743
+ "type": "function",
1744
+ "function": {
1745
+ "name": "csv_to_json",
1746
+ "description": "Convert CSV text to JSON.",
1747
+ "parameters": {
1748
+ "type": "object",
1749
+ "properties": {
1750
+ "csv_text": {"type": "string", "description": "CSV text to convert"}
1751
+ },
1752
+ "required": ["csv_text"]
1753
+ }
1754
+ }
1755
+ },
1756
+ {
1757
+ "type": "function",
1758
+ "function": {
1759
+ "name": "word_count",
1760
+ "description": "Count words, characters, and lines in text.",
1761
+ "parameters": {
1762
+ "type": "object",
1763
+ "properties": {
1764
+ "text": {"type": "string", "description": "Text to analyze"}
1765
+ },
1766
+ "required": ["text"]
1767
+ }
1768
+ }
1769
+ },
1770
+ {
1771
+ "type": "function",
1772
+ "function": {
1773
+ "name": "text_diff",
1774
+ "description": "Compare two texts and show differences using unified diff format.",
1775
+ "parameters": {
1776
+ "type": "object",
1777
+ "properties": {
1778
+ "text1": {"type": "string", "description": "First text"},
1779
+ "text2": {"type": "string", "description": "Second text"}
1780
+ },
1781
+ "required": ["text1", "text2"]
1782
+ }
1783
+ }
1784
+ },
1785
+ {
1786
+ "type": "function",
1787
+ "function": {
1788
+ "name": "regex_extract",
1789
+ "description": "Extract all matches of a regex pattern from text.",
1790
+ "parameters": {
1791
+ "type": "object",
1792
+ "properties": {
1793
+ "text": {"type": "string", "description": "Text to search"},
1794
+ "pattern": {"type": "string", "description": "Regex pattern"}
1795
+ },
1796
+ "required": ["text", "pattern"]
1797
+ }
1798
+ }
1799
+ },
1800
+ {
1801
+ "type": "function",
1802
+ "function": {
1803
+ "name": "regex_replace",
1804
+ "description": "Replace text using a regex pattern.",
1805
+ "parameters": {
1806
+ "type": "object",
1807
+ "properties": {
1808
+ "text": {"type": "string", "description": "Text to modify"},
1809
+ "pattern": {"type": "string", "description": "Regex pattern to match"},
1810
+ "replacement": {"type": "string", "description": "Replacement string", "default": ""}
1811
+ },
1812
+ "required": ["text", "pattern"]
1813
+ }
1814
+ }
1815
+ },
1816
+ {
1817
+ "type": "function",
1818
+ "function": {
1819
+ "name": "sentiment_analysis",
1820
+ "description": "Analyze sentiment of text using TextBlob.",
1821
+ "parameters": {
1822
+ "type": "object",
1823
+ "properties": {
1824
+ "text": {"type": "string", "description": "Text to analyze"}
1825
+ },
1826
+ "required": ["text"]
1827
+ }
1828
+ }
1829
+ },
1830
+ {
1831
+ "type": "function",
1832
+ "function": {
1833
+ "name": "summarize_text",
1834
+ "description": "Summarize text using LSA summarizer.",
1835
+ "parameters": {
1836
+ "type": "object",
1837
+ "properties": {
1838
+ "text": {"type": "string", "description": "Text to summarize"},
1839
+ "sentences_count": {"type": "integer", "description": "Number of sentences in summary", "default": 3}
1840
+ },
1841
+ "required": ["text"]
1842
+ }
1843
+ }
1844
+ },
1845
+ {
1846
+ "type": "function",
1847
+ "function": {
1848
+ "name": "extract_entities",
1849
+ "description": "Extract named entities from text using spaCy.",
1850
+ "parameters": {
1851
+ "type": "object",
1852
+ "properties": {
1853
+ "text": {"type": "string", "description": "Text to analyze"}
1854
+ },
1855
+ "required": ["text"]
1856
+ }
1857
+ }
1858
+ },
1859
+ # ===================== CONVERSION =====================
1860
+ {
1861
+ "type": "function",
1862
+ "function": {
1863
+ "name": "currency_converter",
1864
+ "description": "Convert currency using exchangerate-api.com.",
1865
+ "parameters": {
1866
+ "type": "object",
1867
+ "properties": {
1868
+ "amount": {"type": "number", "description": "Amount to convert"},
1869
+ "from_currency": {"type": "string", "description": "Source currency code", "default": "USD"},
1870
+ "to_currency": {"type": "string", "description": "Target currency code", "default": "EUR"}
1871
+ },
1872
+ "required": ["amount"]
1873
+ }
1874
+ }
1875
+ },
1876
+ {
1877
+ "type": "function",
1878
+ "function": {
1879
+ "name": "unit_converter",
1880
+ "description": "Convert between units using pint (e.g., meters to feet, kg to lbs).",
1881
+ "parameters": {
1882
+ "type": "object",
1883
+ "properties": {
1884
+ "value": {"type": "number", "description": "Value to convert"},
1885
+ "from_unit": {"type": "string", "description": "Source unit (e.g., meter, kg, celsius)"},
1886
+ "to_unit": {"type": "string", "description": "Target unit (e.g., foot, lb, fahrenheit)"}
1887
+ },
1888
+ "required": ["value", "from_unit", "to_unit"]
1889
+ }
1890
+ }
1891
+ },
1892
+ {
1893
+ "type": "function",
1894
+ "function": {
1895
+ "name": "timestamp_converter",
1896
+ "description": "Convert between Unix timestamps and human-readable formats.",
1897
+ "parameters": {
1898
+ "type": "object",
1899
+ "properties": {
1900
+ "timestamp": {"type": "string", "description": "Unix timestamp or ISO datetime string"},
1901
+ "to_format": {"type": "string", "description": "Output format: iso, rfc, date, time, unix, or custom strftime", "default": "iso"}
1902
+ },
1903
+ "required": ["timestamp"]
1904
+ }
1905
+ }
1906
+ },
1907
+ # ===================== MEDIA =====================
1908
+ {
1909
+ "type": "function",
1910
+ "function": {
1911
+ "name": "youtube_transcript",
1912
+ "description": "Get transcript from a YouTube video URL.",
1913
+ "parameters": {
1914
+ "type": "object",
1915
+ "properties": {
1916
+ "video_url": {"type": "string", "description": "YouTube video URL"},
1917
+ "language": {"type": "string", "description": "Transcript language code", "default": "en"}
1918
+ },
1919
+ "required": ["video_url"]
1920
+ }
1921
+ }
1922
+ },
1923
+ {
1924
+ "type": "function",
1925
+ "function": {
1926
+ "name": "extract_links",
1927
+ "description": "Extract all URLs from text.",
1928
+ "parameters": {
1929
+ "type": "object",
1930
+ "properties": {
1931
+ "text": {"type": "string", "description": "Text to extract links from"}
1932
+ },
1933
+ "required": ["text"]
1934
+ }
1935
+ }
1936
+ },
1937
+ {
1938
+ "type": "function",
1939
+ "function": {
1940
+ "name": "extract_emails",
1941
+ "description": "Extract all email addresses from text.",
1942
+ "parameters": {
1943
+ "type": "object",
1944
+ "properties": {
1945
+ "text": {"type": "string", "description": "Text to extract emails from"}
1946
+ },
1947
+ "required": ["text"]
1948
+ }
1949
+ }
1950
+ },
1951
+ {
1952
+ "type": "function",
1953
+ "function": {
1954
+ "name": "extract_phones",
1955
+ "description": "Extract phone numbers from text.",
1956
+ "parameters": {
1957
+ "type": "object",
1958
+ "properties": {
1959
+ "text": {"type": "string", "description": "Text to extract phone numbers from"}
1960
+ },
1961
+ "required": ["text"]
1962
+ }
1963
+ }
1964
+ },
1965
+ # ===================== SYSTEM =====================
1966
+ {
1967
+ "type": "function",
1968
+ "function": {
1969
+ "name": "git_status",
1970
+ "description": "Get git status of a repository.",
1971
+ "parameters": {
1972
+ "type": "object",
1973
+ "properties": {
1974
+ "repo_path": {"type": "string", "description": "Path to git repository", "default": "."}
1975
+ },
1976
+ "required": []
1977
+ }
1978
+ }
1979
+ },
1980
+ {
1981
+ "type": "function",
1982
+ "function": {
1983
+ "name": "git_log",
1984
+ "description": "Get git commit history of a repository.",
1985
+ "parameters": {
1986
+ "type": "object",
1987
+ "properties": {
1988
+ "repo_path": {"type": "string", "description": "Path to git repository", "default": "."},
1989
+ "count": {"type": "integer", "description": "Number of commits (max 50)", "default": 10}
1990
+ },
1991
+ "required": []
1992
+ }
1993
+ }
1994
+ },
1995
+ {
1996
+ "type": "function",
1997
+ "function": {
1998
+ "name": "system_info",
1999
+ "description": "Get system information including CPU, memory, disk, and platform details.",
2000
+ "parameters": {
2001
+ "type": "object",
2002
+ "properties": {},
2003
+ "required": []
2004
+ }
2005
+ }
2006
+ },
2007
+ ]