from langchain.agents import AgentExecutor, create_openai_functions_agent from langchain.tools import Tool from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder from langchain_openai import ChatOpenAI import yaml import tempfile import subprocess import os import shutil import ast from textwrap import indent import re import requests # Local imports from tools.read_file_tool import read_file from tools.git_tool import scan_repository_structure, read_code_file from tools.mcp_logger import log_mcp_entry from langchain.agents import initialize_agent from langchain.agents.agent_types import AgentType # --- Load model config --- def load_model_config(path="model_config.yaml"): with open(path, "r") as f: config = yaml.safe_load(f) config["llm"]["api_key"] = os.environ.get("GROQ_API_KEY", "") return config["llm"] # --- Init Groq LLM --- llm_config = load_model_config() llm = ChatOpenAI( model=llm_config["model"], base_url=llm_config["base_url"], api_key=llm_config["api_key"], temperature=0.7 ) # --- Tools --- def hello_world_tool(input: str) -> str: return f"Hello, {input}! This is your agent speaking." def smart_scan_repo(path_or_url: str) -> str: if path_or_url.startswith("http"): try: temp_dir = tempfile.mkdtemp() subprocess.run(["git", "clone", path_or_url, temp_dir], check=True, capture_output=True) summary = scan_repository_structure(temp_dir) shutil.rmtree(temp_dir) return summary except Exception as e: return f"Error cloning remote repo: {e}" else: return scan_repository_structure(path_or_url) def analyze_code_file(file_path: str) -> str: try: content = read_code_file(file_path) analysis = f"### File Analysis: {file_path}\n" analysis += f"- Lines: {len(content.splitlines())}\n" if file_path.endswith(".py"): try: tree = ast.parse(content) funcs = [node.name for node in ast.walk(tree) if isinstance(node, ast.FunctionDef)] classes = [node.name for node in ast.walk(tree) if isinstance(node, ast.ClassDef)] analysis += f"- Functions: {', '.join(funcs) or 'None'}\n" analysis += f"- Classes: {', '.join(classes) or 'None'}\n" except SyntaxError as e: analysis += f"- āš ļø Skipped AST parsing due to syntax error: {e}\n" preview = content[:500].strip().replace("\n", "\n ") analysis += f"\n### Sample Preview:\n {preview}...\n" return analysis except Exception as e: return f"Error analyzing file: {e}" tool_list = [ Tool(name="HelloTool", func=hello_world_tool, description="Sends a hello message."), Tool(name="ReadFileTool", func=read_file, description="Reads a file content."), Tool(name="SmartRepoScanner", func=smart_scan_repo, description="Scans a GitHub or local repo."), Tool(name="AnalyzeCodeFile", func=analyze_code_file, description="Analyzes structure of a single code file."), ] # --- Prompt + Agent Setup --- prompt = ChatPromptTemplate.from_messages([ ("system", "You are a helpful AI assistant. Do not ask questions, just complete your task and return results."), MessagesPlaceholder(variable_name="chat_history"), ("user", "{input}"), MessagesPlaceholder(variable_name="agent_scratchpad"), ]) agent_executor = initialize_agent( tools=tool_list, llm=llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True, return_intermediate_steps=True # <- Add this ) # --- Utility Output Formatter --- def pretty_print(output: str): output = output.replace("**", "") output = re.sub(r"\[(.*?)\]\((.*?)\)", r"\1: \2", output) output = re.sub(r"```(.*?)```", r"\1", output, flags=re.DOTALL) output = output.replace("* ", "- ") print("\n" + indent(output.strip(), " ") + "\n") # --- GitHub Repo Fetcher --- def get_user_repos(username: str, limit: int = 3): try: url = f"https://api.github.com/users/{username}/repos" response = requests.get(url) response.raise_for_status() repos = response.json() return [repo["clone_url"] for repo in repos[:limit]] except Exception: return [] # --- Entry point for UI --- def run_analysis(repo_url: str) -> str: chat_history = [] response = agent_executor.invoke({ "input": f"Scan this Git repository: {repo_url}", "chat_history": chat_history }) return response["output"] if "output" in response else str(response) # --- CLI Entry --- if __name__ == "__main__": chat_history = [] username = "GirishKGit" repos = get_user_repos(username, limit=3) for repo_url in repos: print(f"\nšŸ” Scanning Repository: {repo_url}\n") user_input = f"Scan this Git repository: {repo_url}" response = agent_executor.invoke({ "input": user_input, "chat_history": chat_history }) print("āœ… Analysis Completed. Here's the Summary:") pretty_print(response["output"]) log_mcp_entry( agent_id="PolicyAgent", user_query=user_input, response=response["output"], model_name=llm_config["model"], base_url=llm_config["base_url"] )