import os from dotenv import load_dotenv from typing import List, Dict, Any, Optional import tempfile import pandas as pd import numpy as np """Langraph""" from langgraph.graph import START, StateGraph, MessagesState from langgraph.prebuilt import ToolNode, tools_condition from langchain_google_genai import ChatGoogleGenerativeAI from langchain_groq import ChatGroq from langchain_huggingface import ( ChatHuggingFace, HuggingFaceEndpoint, ) from langchain_core.messages import SystemMessage, HumanMessage from langchain_core.tools import tool from langchain_openai import ChatOpenAI from langchain_community.utilities import SerpAPIWrapper load_dotenv() ### =============== SEARCH TOOLS =============== ### @tool def serpapi_search(query: str) -> str: """Search the web using SerpAPI. Args: query: The search query.""" try: # Get API key from environment variable api_key = os.getenv("SERPAPI_API_KEY") if not api_key: return {"search_results": "Error: SERPAPI_API_KEY not found in environment variables."} # Initialize SerpAPIWrapper with the API key search = SerpAPIWrapper(serpapi_api_key=api_key) # Perform the search results = search.run(query) if not results or results.strip() == "": return {"search_results": "No search results found."} return {"search_results": results} except Exception as e: return {"search_results": f"Error performing search: {str(e)}"} ### =============== DOCUMENT PROCESSING TOOLS =============== ### # File handling still requires external tools @tool def save_and_read_file(content: str, filename: Optional[str] = None) -> str: """ Save content to a file and return the path. Args: content (str): the content to save to the file filename (str, optional): the name of the file. If not provided, a random name file will be created. """ temp_dir = tempfile.gettempdir() if filename is None: temp_file = tempfile.NamedTemporaryFile(delete=False, dir=temp_dir) filepath = temp_file.name else: filepath = os.path.join(temp_dir, filename) with open(filepath, "w") as f: f.write(content) return f"File saved to {filepath}. You can read this file to process its contents." # load the system prompt from the file with open("system_prompt.txt", "r", encoding="utf-8") as f: system_prompt = f.read() print(system_prompt) # System message sys_msg = SystemMessage(content=system_prompt) tools = [ serpapi_search, save_and_read_file, ] # Build graph function def build_graph(provider: str = "openai"): """Build the graph""" # Load environment variables from .env file if provider == "openai": llm = ChatOpenAI(model="gpt-4o", temperature=0) elif provider == "groq": llm = ChatGroq(model="qwen-qwq-32b", temperature=0) elif provider == "huggingface": llm = ChatHuggingFace( llm=HuggingFaceEndpoint( repo_id="TinyLlama/TinyLlama-1.1B-Chat-v1.0", task="text-generation", max_new_tokens=1024, do_sample=False, repetition_penalty=1.03, temperature=0, ), verbose=True, ) else: raise ValueError("Invalid provider. Choose 'openai', 'groq', or 'huggingface'.") # Bind tools to LLM llm_with_tools = llm.bind_tools(tools) # Node def assistant(state: MessagesState): """Assistant node""" # Add system message at the beginning of messages messages = [sys_msg] + state["messages"] response = llm_with_tools.invoke(messages) # Return the response as is return {"messages": state["messages"] + [response]} builder = StateGraph(MessagesState) builder.add_node("assistant", assistant) builder.add_node("tools", ToolNode(tools)) builder.add_edge(START, "assistant") builder.add_conditional_edges( "assistant", tools_condition, ) builder.add_edge("tools", "assistant") # Compile graph return builder.compile() # test if __name__ == "__main__": question = "When was a picture of St. Thomas Aquinas first added to the Wikipedia page on the Principle of double effect?" graph = build_graph(provider="openai") messages = [HumanMessage(content=question)] messages = graph.invoke({"messages": messages}) for m in messages["messages"]: m.pretty_print()