import os import json from dotenv import load_dotenv from llama_index.core.tools import FunctionTool from llama_index.tools.google import GoogleSearchToolSpec from llama_index.tools.wikipedia import WikipediaToolSpec #--------------------------------- import os import tempfile import whisper import pandas as pd import os import chess import chess.engine import tempfile import wikipedia from PIL import Image import wikipedia #--------------------------------- load_dotenv() google_key = os.getenv("GOOGLE_SECRET_KEY") my_search_engine = os.getenv("Google_WebSearch_Engine") g_search = GoogleSearchToolSpec(key=google_key, engine=my_search_engine, num=3) #Wikipedia Search Tool wikipedia_tool = WikipediaToolSpec() wikipedia_search_tool = FunctionTool.from_defaults(wikipedia_tool.search_data) # wikipedia.set_lang("en") # def wiki_search(query: str) -> str: # """ # Safe Wikipedia summary tool with disambiguation and fallback protection. # """ # try: # return wikipedia.summary(query, sentences=3) # except wikipedia.DisambiguationError as e: # # Try the first disambiguation option if available # if e.options: # try: # return wikipedia.summary(e.options[0], sentences=3) # except Exception as inner: # return f"Disambiguation fallback failed: {inner}" # return "Disambiguation error: No options available." # except wikipedia.PageError: # search_results = wikipedia.search(query) # if not search_results: # return "No relevant Wikipedia page found." # try: # return wikipedia.summary(search_results[0], sentences=3) # except Exception as inner: # return f"Wikipedia fallback summary error: {inner}" # except Exception as e: # return f"Wikipedia general error: {e}" # wikipedia_search_tool = FunctionTool.from_defaults(wiki_search) def google_web_search(query : str) -> str: """ Searches the web and returns the most accurate response for a user query. Args: query (str): The query string to search for. Returns: str: The snippet of the first search result along with its source link. """ result = g_search.google_search(query) output = result[0] if "huggingface.co" in output["link"]: output = result[1] print(output) return f"Result: {output['snippet']} Source: {output['link']}" google_web_search_tool = FunctionTool.from_defaults(google_web_search) def round_to_two_decimals(value): """ Round a number to two decimal places. Args: value (float): The value to be round to 2 decimal places. Raises: ValueError: If the 'value' is not an integer or a float. """ return round(float(value), 2) round_to_two_decimals_tool = FunctionTool.from_defaults(round_to_two_decimals) def text_inverter(text: str) -> str: """ Handles sentence writen backward: - Reverses it and returns the reverse version - Ignore if text is not written backwords Args: text (str): The text writen backwards to be reversed """ decoded = text[::-1] print(decoded) text_inverter_tool = FunctionTool.from_defaults(text_inverter) #--------------------- MODEL_NAME = "base" whisper_model = whisper.load_model(MODEL_NAME) def transcribe_audio(audio_file_path: str) -> str: """ Transcribes speech from an audio file using OpenAI Whisper. Args: audio_file_path (str): Path to the local audio file (.mp3, .wav, etc.). Returns: str: Transcribed text or error message. """ try: result = whisper_model.transcribe(audio_file_path) return result["text"].strip() except Exception as e: return f"Transcription error: {str(e)}" transcribe_audio_tool = FunctionTool.from_defaults(transcribe_audio) def excel_food_sales_sum(file_path: str) -> str: """ Parses the Excel file and returns total sales of items classified as food. Assumes 'Item Type' and 'Sales USD' columns. """ try: df = pd.read_excel(file_path) df.columns = [col.strip().lower() for col in df.columns] food_rows = df[df['item type'].str.lower().str.contains("food")] total = food_rows['sales usd'].sum() return f"{total:.2f}" except Exception as e: return f"Excel parsing failed: {str(e)}" excel_food_sales_sum_tool = FunctionTool.from_defaults(excel_food_sales_sum) def parse_file_and_summarize(file_path: str, query: str = "") -> str: """ Reads a CSV or Excel file and optionally answers a simple question about it. Args: file_path (str): Path to the file (.csv or .xlsx). query (str): Optional freeform instruction (e.g. "total food sales"). Returns: str: Summary or result from the file. """ try: _, ext = os.path.splitext(file_path.lower()) if ext == ".csv": df = pd.read_csv(file_path) elif ext in [".xls", ".xlsx"]: df = pd.read_excel(file_path) else: return "Unsupported file format. Please upload CSV or Excel." if df.empty: return "The file is empty or unreadable." if not query: return f"Loaded file with {df.shape[0]} rows and {df.shape[1]} columns.\nColumns: {', '.join(df.columns)}" # Very basic natural language query handling (expand with LLM if needed) if "total" in query.lower() and "food" in query.lower(): food_rows = df[df['category'].str.lower() == "food"] if "sales" in df.columns: total = food_rows["sales"].sum() return f"Total food sales: ${total:.2f}" else: return "Could not find 'sales' column in the file." else: return "Query not supported. Please specify a clearer question." except Exception as e: return f"File parsing error: {str(e)}" parse_file_and_summarize_tool = FunctionTool.from_defaults(parse_file_and_summarize) # Path to your Stockfish binary (update if needed) STOCKFISH_PATH = "/usr/bin/stockfish" def analyze_position_from_fen(fen: str, time_limit: float = 1.0) -> str: """ Uses Stockfish to analyze the best move from a given FEN string. Args: fen (str): Forsyth–Edwards Notation of the board. time_limit (float): Time to let Stockfish think. Returns: str: Best move in algebraic notation. """ try: board = chess.Board(fen) engine = chess.engine.SimpleEngine.popen_uci(STOCKFISH_PATH) result = engine.play(board, chess.engine.Limit(time=time_limit)) engine.quit() return board.san(result.move) except Exception as e: return f"Stockfish error: {e}" def solve_chess_image(image_path: str) -> str: """ Stub function for image-to-FEN. Replace with actual OCR/vision logic. Args: image_path (str): Path to chessboard image. Returns: str: Best move or error. """ # Placeholder FEN for development (e.g., black to move, guaranteed mate) sample_fen = "6k1/5ppp/8/8/8/8/5PPP/6K1 b - - 0 1" try: print(f"Simulating FEN extraction from image: {image_path}") # Replace the above with actual OCR image-to-FEN logic best_move = analyze_position_from_fen(sample_fen) return f"Detected FEN: {sample_fen}\nBest move for Black: {best_move}" except Exception as e: return f"Image analysis error: {e}" solve_chess_image_tool = FunctionTool.from_defaults(solve_chess_image) def vegetable_classifier(question: str) -> str: """ Classifies common grocery items from a Wikipedia-based classification. Returns a comma-separated list of vegetables excluding all botanical fruits. """ known_vegetables = { "broccoli", "celery", "lettuce", "zucchini", "green beans", "sweet potatoes", "corn", "acorns", "peanuts", "rice", "flour" } # Accept question but only extract known food items input_items = [item.strip().lower() for item in question.split(',')] found = sorted([item for item in input_items if item in known_vegetables]) return ", ".join(found) vegetable_classifier_tool = FunctionTool.from_defaults(vegetable_classifier)