Created
June 9, 2025 15:02
-
-
Save capableguptadotcom/0eca2f2c5b6667e536d8b486934054c2 to your computer and use it in GitHub Desktop.
tool calling
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import json | |
| import subprocess | |
| from openai import OpenAI | |
| # Initialize OpenAI client | |
| client = OpenAI() | |
| # Custom tool: Format Python code using Black | |
| def format_code(code: str) -> dict: | |
| try: | |
| # Run Black on the input code (using stdin for simplicity) | |
| result = subprocess.run( | |
| ["black", "-"], | |
| input=code, | |
| text=True, | |
| capture_output=True | |
| ) | |
| if result.returncode == 0: | |
| return {"status": "success", "formatted_code": result.stdout, "logs": "Ran black on input code"} | |
| else: | |
| return {"status": "error", "message": result.stderr} | |
| except Exception as e: | |
| return {"status": "error", "message": str(e)} | |
| # Custom tool: Generate code coverage using pytest-cov | |
| def generate_coverage(project_path: str) -> dict: | |
| try: | |
| # Run pytest with coverage | |
| result = subprocess.run( | |
| ["pytest", "--cov", project_path, "--cov-report=json"], | |
| text=True, | |
| capture_output=True | |
| ) | |
| if result.returncode == 0: | |
| # Parse coverage report (simplified) | |
| with open("coverage.json", "r") as f: | |
| coverage_data = json.load(f) | |
| total_coverage = coverage_data["totals"]["percent_covered"] | |
| return { | |
| "status": "success", | |
| "coverage": f"{total_coverage:.2f}%", | |
| "logs": f"Ran pytest --cov {project_path}" | |
| } | |
| else: | |
| return {"status": "error", "message": result.stderr} | |
| except Exception as e: | |
| return {"status": "error", "message": str(e)} | |
| # Tool definitions for OpenAI API | |
| tools = [ | |
| { | |
| "type": "function", | |
| "function": { | |
| "name": "format_code", | |
| "description": "Formats Python code using Black.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "code": {"type": "string", "description": "Python code to format"} | |
| }, | |
| "required": ["code"] | |
| } | |
| } | |
| }, | |
| { | |
| "type": "function", | |
| "function": { | |
| "name": "generate_coverage", | |
| "description": "Generates code coverage report using pytest-cov.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "project_path": {"type": "string", "description": "Path to Python project"} | |
| }, | |
| "required": ["project_path"] | |
| } | |
| } | |
| } | |
| ] | |
| # Example user query | |
| user_query = """ | |
| Please format the following Python code and then generate a coverage report for my project at './my_project': | |
| def hello(): | |
| print('Hello') | |
| """ | |
| # Call the OpenAI API | |
| response = client.chat.completions.create( | |
| model="o3", | |
| messages=[ | |
| {"role": "system", "content": "You are a coding assistant. Use the format_code tool to format Python code and the generate_coverage tool to run pytest-cov. Execute tools in sequence if needed and return results."}, | |
| {"role": "user", "content": user_query} | |
| ], | |
| tools=tools, | |
| tool_choice="auto" | |
| ) | |
| # Process tool calls | |
| for choice in response.choices: | |
| if choice.message.tool_calls: | |
| for tool_call in choice.message.tool_calls: | |
| function_name = tool_call.function.name | |
| arguments = json.loads(tool_call.function.arguments) | |
| if function_name == "format_code": | |
| result = format_code(arguments["code"]) | |
| print(f"Format Code Result: {result}") | |
| elif function_name == "generate_coverage": | |
| result = generate_coverage(arguments["project_path"]) | |
| print(f"Coverage Result: {result}") | |
| ########################################################## | |
| import os | |
| import json | |
| import markdown | |
| from openai import OpenAI | |
| from typing import Dict, Optional | |
| import importlib.util | |
| import inspect | |
| # Initialize OpenAI client | |
| client = OpenAI() | |
| # Custom tool: Load documentation dynamically | |
| def load_documentation(module_name: str, keyword: Optional[str] = None) -> Dict: | |
| """ | |
| Loads documentation from a Markdown file or Python module docstrings. | |
| Args: | |
| module_name: Name of the module (e.g., 'module1' for docs/module1.md or 'my_module' for src/my_module.py). | |
| keyword: Optional keyword to filter specific sections or functions (e.g., 'add_numbers'). | |
| Returns: | |
| Dict with status, content, and logs. | |
| """ | |
| try: | |
| logs = [] | |
| content = "" | |
| # Check Markdown documentation in docs/ | |
| md_path = f"docs/{module_name}.md" | |
| if os.path.exists(md_path): | |
| with open(md_path, "r", encoding="utf-8") as f: | |
| md_content = f.read() | |
| # Convert Markdown to plain text (optional) | |
| content = markdown.markdown(md_content) | |
| logs.append(f"Loaded Markdown documentation from {md_path}") | |
| # Filter by keyword if provided | |
| if keyword: | |
| lines = content.split("\n") | |
| content = "\n".join(line for line in lines if keyword.lower() in line.lower()) | |
| logs.append(f"Filtered content for keyword: {keyword}") | |
| # Check Python module docstrings in src/ | |
| py_module_path = f"src/{module_name}.py" | |
| if os.path.exists(py_module_path): | |
| # Dynamically import the module | |
| spec = importlib.util.spec_from_file_location(module_name, py_module_path) | |
| module = importlib.util.module_from_spec(spec) | |
| spec.loader.exec_module(module) | |
| # Extract docstrings for functions | |
| for name, obj in inspect.getmembers(module, inspect.isfunction): | |
| if keyword and keyword.lower() not in name.lower(): | |
| continue | |
| doc = inspect.getdoc(obj) or "No docstring available" | |
| content += f"\nFunction: {name}\n{doc}\n" | |
| logs.append(f"Loaded docstring for function {name} from {py_module_path}") | |
| if not content: | |
| return { | |
| "status": "error", | |
| "message": f"No documentation found for module {module_name}", | |
| "logs": logs | |
| } | |
| return { | |
| "status": "success", | |
| "content": content, | |
| "logs": "\n".join(logs) | |
| } | |
| except Exception as e: | |
| return { | |
| "status": "error", | |
| "message": str(e), | |
| "logs": logs | |
| } | |
| # Tool definition for OpenAI API | |
| tools = [ | |
| { | |
| "type": "function", | |
| "function": { | |
| "name": "load_documentation", | |
| "description": "Loads documentation for a specified module or function from Markdown files in docs/ or Python docstrings in src/.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "module_name": { | |
| "type": "string", | |
| "description": "Name of the module (e.g., 'module1' or 'my_module')." | |
| }, | |
| "keyword": { | |
| "type": "string", | |
| "description": "Optional keyword to filter documentation (e.g., function name like 'add_numbers')." | |
| } | |
| }, | |
| "required": ["module_name"] | |
| } | |
| } | |
| } | |
| ] | |
| # System prompt to guide the LLM | |
| system_prompt = """ | |
| You are a coding assistant for a Python project. When answering questions about project modules or functions, call the `load_documentation` tool to dynamically load relevant documentation from Markdown files in `docs/` or Python docstrings in `src/`. Use the `module_name` parameter to specify the module (e.g., 'module1' or 'my_module') and the optional `keyword` parameter to filter for specific functions or sections (e.g., 'add_numbers'). Summarize the documentation in your response and include any relevant logs to confirm which documentation was loaded. | |
| """ | |
| # Example user query | |
| user_query = "Tell me about the add_numbers function in my_module." | |
| # Call the OpenAI API | |
| response = client.chat.completions.create( | |
| model="o3", | |
| messages=[ | |
| {"role": "system", "content": system_prompt}, | |
| {"role": "user", "content": user_query} | |
| ], | |
| tools=tools, | |
| tool_choice="auto" | |
| ) | |
| # Process tool calls | |
| for choice in response.choices: | |
| if choice.message.tool_calls: | |
| for tool_call in choice.message.tool_calls: | |
| function_name = tool_call.function.name | |
| arguments = json.loads(tool_call.function.arguments) | |
| if function_name == "load_documentation": | |
| result = load_documentation( | |
| module_name=arguments["module_name"], | |
| keyword=arguments.get("keyword") | |
| ) | |
| print(f"Tool Result: {json.dumps(result, indent=2)}") | |
| else: | |
| print(f"LLM Response: {choice.message.content}") | |
| ##################################################################### | |
| # Best Practices for Framing and Using Tools | |
| # Clear Tool Descriptions: | |
| # Write precise descriptions in the JSON schema to help the LLM understand when to use each tool. | |
| # Example: Instead of “Formats code,” use “Formats Python code using Black to ensure consistent style.” | |
| # Modular Tools: | |
| # Break down complex tasks into smaller, single-purpose tools (e.g., separate formatting and coverage generation). | |
| # This allows the LLM to chain tools flexibly. | |
| # Structured Outputs: | |
| # Return tool results in a consistent format (e.g., {"status": "success", "result": ..., "logs": ...}) to make it easy for the LLM to parse and use. | |
| # Error Handling: | |
| # Include robust error handling in tools to catch invalid inputs or execution failures. | |
| # Return meaningful error messages to the LLM for user feedback. | |
| # Security: | |
| # Run tools in a sandboxed environment to prevent malicious code execution. | |
| # Validate inputs (e.g., check if project_path exists) before running commands. | |
| # Logging and Transparency: | |
| # Log all commands executed by tools and include them in the output for debugging. | |
| # Example: logs: "Ran black on input code" helps verify execution. | |
| # Test Tool Integration: | |
| # Test each tool independently before integrating with the LLM. | |
| # Use mock API calls to simulate LLM tool invocation. | |
| # Optimize for LLM Reasoning: | |
| # Use clear system prompts to guide the LLM on tool usage and sequencing. | |
| # Example: “If formatting is requested, call format_code first, then proceed to other tools.” | |
| # Things to Avoid | |
| # Vague Tool Definitions: | |
| # Avoid ambiguous descriptions like “processes code.” Be specific (e.g., “Formats Python code using Black”). | |
| # Overly Complex Tools: | |
| # Don’t combine multiple tasks into one tool (e.g., formatting and coverage in a single function). This reduces flexibility and makes debugging harder. | |
| # Unsecured Execution: | |
| # Avoid running LLM-generated commands directly on the host machine. Always use a sandbox (e.g., Docker). | |
| # Ignoring Errors: | |
| # Don’t let tools silently fail. Always return error messages to the LLM for proper user feedback. | |
| # Missing Logs: | |
| # Avoid tools that don’t log their actions. Without logs, it’s hard to verify what commands were executed. | |
| # Hardcoding Dependencies: | |
| # Don’t hardcode paths or assumptions (e.g., assuming pytest is installed). Check for dependencies dynamically or document requirements. | |
| # Over-Reliance on LLM Decision-Making: | |
| # The LLM may not always choose the correct tool or sequence. Provide explicit instructions in the system prompt to guide it | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment