diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f51121ef7..84f13fe54 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -72,7 +72,7 @@ repos: - id: deptry pass_filenames: false always_run: true - entry: bash -c "uv run --frozen --all-extras --dev deptry src --ignore DEP001" + entry: bash -c "uv run --frozen --all-extras --dev deptry src --ignore DEP001 --extend-exclude 'codegen-examples/.*'" - repo: https://github.com/renovatebot/pre-commit-hooks rev: 39.164.1 diff --git a/codegen-examples/examples/langchain_agent/README.md b/codegen-examples/examples/langchain_agent/README.md new file mode 100644 index 000000000..ad2645f7a --- /dev/null +++ b/codegen-examples/examples/langchain_agent/README.md @@ -0,0 +1,82 @@ +# Codegen LangChain Agent Example + +

+ + + +

+ +

+ Build an intelligent code agent with LangChain and Codegen +

+ +
+ +[![Documentation](https://img.shields.io/badge/Docs-docs.codegen.com-purple?style=flat-square)](https://docs.codegen.com/tutorials/build-code-agent) +[![License](https://img.shields.io/badge/Code%20License-Apache%202.0-gray?&color=gray)](https://github.com/codegen-sh/codegen-sdk/tree/develop?tab=Apache-2.0-1-ov-file) + +
+ +This example demonstrates how to build an intelligent code agent using Codegen's LangChain integration. The agent can analyze and manipulate codebases using natural language commands. + +## Quick Start + +```python +from codegen import Codebase +from codegen.extensions.langchain import create_codebase_agent + +# Initialize codebase +codebase = Codebase.from_repo("fastapi/fastapi") + +# Create the agent +agent = create_codebase_agent(codebase=codebase, model_name="gpt-4", verbose=True) + +# Ask the agent to analyze code +result = agent.invoke({"input": "What are the dependencies of the FastAPI class?", "config": {"configurable": {"session_id": "demo"}}}) +print(result["output"]) +``` + +## Installation + +```bash +# Install dependencies +pip install modal-client codegen langchain langchain-openai + +# Run the example +python run.py +``` + +## Available Tools + +The agent comes with several built-in tools for code operations: + +- `ViewFileTool`: View file contents and metadata +- `ListDirectoryTool`: List directory contents +- `SearchTool`: Search code using regex +- `EditFileTool`: Edit file contents +- `CreateFileTool`: Create new files +- `DeleteFileTool`: Delete files +- `RenameFileTool`: Rename files and update imports +- `MoveSymbolTool`: Move functions/classes between files +- `RevealSymbolTool`: Analyze symbol dependencies +- `SemanticEditTool`: Make semantic code edits +- `CommitTool`: Commit changes to disk + +## Example Operations + +The agent can perform various code analysis and manipulation tasks: + +```python +# Analyze dependencies +agent.invoke({"input": "What are the dependencies of the reveal_symbol function?", "config": {"configurable": {"session_id": "demo"}}}) + +# Find usage patterns +agent.invoke({"input": "Show me examples of dependency injection in the codebase", "config": {"configurable": {"session_id": "demo"}}}) + +# Move code +agent.invoke({"input": "Move the validate_email function to validation_utils.py", "config": {"configurable": {"session_id": "demo"}}}) +``` + +## Learn More + +- [Full Tutorial](https://docs.codegen.com/tutorials/build-code-agent) diff --git a/codegen-examples/examples/langchain_agent/run.py b/codegen-examples/examples/langchain_agent/run.py new file mode 100644 index 000000000..cff0d15f1 --- /dev/null +++ b/codegen-examples/examples/langchain_agent/run.py @@ -0,0 +1,107 @@ +"""Demo implementation of an agent with Codegen tools.""" + +from codegen import Codebase +from codegen.extensions.langchain.tools import ( + CommitTool, + CreateFileTool, + DeleteFileTool, + EditFileTool, + ListDirectoryTool, + MoveSymbolTool, + RenameFileTool, + RevealSymbolTool, + SearchTool, + SemanticEditTool, + ViewFileTool, +) +from codegen.sdk.enums import ProgrammingLanguage +from langchain import hub +from langchain.agents import AgentExecutor +from langchain.agents.openai_functions_agent.base import OpenAIFunctionsAgent +from langchain_core.chat_history import ChatMessageHistory +from langchain_core.runnables.history import RunnableWithMessageHistory +from langchain_openai import ChatOpenAI + + +def create_codebase_agent( + codebase: Codebase, + model_name: str = "gpt-4o", + temperature: float = 0, + verbose: bool = True, +) -> RunnableWithMessageHistory: + """Create an agent with all codebase tools. + + Args: + codebase: The codebase to operate on + model_name: Name of the model to use (default: gpt-4) + temperature: Model temperature (default: 0) + verbose: Whether to print agent's thought process (default: True) + + Returns: + Initialized agent with message history + """ + # Initialize language model + llm = ChatOpenAI( + model_name=model_name, + temperature=temperature, + ) + + # Get all codebase tools + tools = [ + ViewFileTool(codebase), + ListDirectoryTool(codebase), + SearchTool(codebase), + EditFileTool(codebase), + CreateFileTool(codebase), + DeleteFileTool(codebase), + RenameFileTool(codebase), + MoveSymbolTool(codebase), + RevealSymbolTool(codebase), + SemanticEditTool(codebase), + CommitTool(codebase), + ] + + # Get the prompt to use + prompt = hub.pull("hwchase17/openai-functions-agent") + + # Create the agent + agent = OpenAIFunctionsAgent( + llm=llm, + tools=tools, + prompt=prompt, + ) + + # Create the agent executor + agent_executor = AgentExecutor( + agent=agent, + tools=tools, + verbose=verbose, + ) + + # Create message history handler + message_history = ChatMessageHistory() + + # Wrap with message history + return RunnableWithMessageHistory( + agent_executor, + lambda session_id: message_history, + input_messages_key="input", + history_messages_key="chat_history", + ) + + +if __name__ == "__main__": + # Initialize codebase + print("Initializing codebase...") + codebase = Codebase.from_repo("fastapi/fastapi", programming_language=ProgrammingLanguage.PYTHON) + + # Create agent with history + print("Creating agent...") + agent = create_codebase_agent(codebase) + + print("\nAsking agent to analyze symbol relationships...") + result = agent.invoke( + {"input": "What are the dependencies of the reveal_symbol function?"}, + config={"configurable": {"session_id": "demo"}}, + ) + print("Messages:", result["messages"]) diff --git a/pyproject.toml b/pyproject.toml index 4c6649552..2717a487d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -151,6 +151,7 @@ dev-dependencies = [ "loguru>=0.7.3", "httpx<0.28.2,>=0.28.1", "jupyterlab>=4.3.5", + "modal>=0.73.25", ] diff --git a/src/codegen/extensions/langchain/agent.py b/src/codegen/extensions/langchain/agent.py index 514658ce5..458903c24 100644 --- a/src/codegen/extensions/langchain/agent.py +++ b/src/codegen/extensions/langchain/agent.py @@ -8,7 +8,6 @@ from langchain_openai import ChatOpenAI from codegen import Codebase -from codegen.sdk.enums import ProgrammingLanguage from .tools import ( CommitTool, @@ -27,7 +26,7 @@ def create_codebase_agent( codebase: Codebase, - model_name: str = "gpt-4", + model_name: str = "gpt-4o", temperature: float = 0, verbose: bool = True, ) -> RunnableWithMessageHistory: @@ -90,20 +89,3 @@ def create_codebase_agent( input_messages_key="input", history_messages_key="chat_history", ) - - -if __name__ == "__main__": - # Initialize codebase - print("Initializing codebase...") - codebase = Codebase.from_repo("fastapi/fastapi", programming_language=ProgrammingLanguage.PYTHON) - - # Create agent with history - print("Creating agent...") - agent = create_codebase_agent(codebase) - - print("\nAsking agent to analyze symbol relationships...") - result = agent.invoke( - {"input": "What are the dependencies of the reveal_symbol function?"}, - config={"configurable": {"session_id": "demo"}}, - ) - print("Messages:", result["messages"]) diff --git a/src/codegen/extensions/modal/README.md b/src/codegen/extensions/modal/README.md deleted file mode 100644 index 108df5e42..000000000 --- a/src/codegen/extensions/modal/README.md +++ /dev/null @@ -1,68 +0,0 @@ -# Repository Analyzer API - -A simple Modal API endpoint that analyzes GitHub repositories using Codegen. The API returns basic metrics about any public GitHub repository including: - -- Total number of files -- Number of functions -- Number of classes - -## Running Locally - -1. Install dependencies: - -```bash -uv add modal -``` - -2. Start the API server: - -```bash -modal serve src/codegen/extensions/modal/api.py -``` - -3. Test with curl: - -```bash -# Replace with your local Modal endpoint URL -curl "{URL}?repo_name=fastapi/fastapi" -``` - -## Response Format - -The API returns JSON in this format: - -```json -{ - "status": "success", - "error": "", - "num_files": 123, - "num_functions": 456, - "num_classes": 78 -} -``` - -If there's an error, you'll get: - -```json -{ - "status": "error", - "error": "Error message here", - "num_files": 0, - "num_functions": 0, - "num_classes": 0 -} -``` - -## Development - -The API is built using: - -- Modal for serverless deployment -- FastAPI for the web endpoint -- Codegen for repository analysis - -To deploy changes: - -```bash -modal deploy src/codegen/extensions/modal/api.py -``` diff --git a/src/codegen/extensions/modal/api.py b/src/codegen/extensions/modal/api.py deleted file mode 100644 index 13d33ff4f..000000000 --- a/src/codegen/extensions/modal/api.py +++ /dev/null @@ -1,56 +0,0 @@ -"""Modal API endpoint for repository analysis.""" - -import modal -from pydantic import BaseModel - -from codegen import Codebase - -# Create image with dependencies -image = modal.Image.debian_slim(python_version="3.13").apt_install("git").pip_install("fastapi[standard]", "codegen>=0.5.30") - -# Create Modal app -app = modal.App("codegen-repo-analyzer") - - -class RepoMetrics(BaseModel): - """Response model for repository metrics.""" - - num_files: int = 0 - num_functions: int = 0 - num_classes: int = 0 - status: str = "success" - error: str = "" - - -@app.function(image=image) -@modal.web_endpoint(method="GET") -def analyze_repo(repo_name: str) -> RepoMetrics: - """Analyze a GitHub repository and return metrics. - - Args: - repo_name: Repository name in format 'owner/repo' - - Returns: - RepoMetrics object containing repository metrics or error information - """ - try: - # Validate input - if "/" not in repo_name: - return RepoMetrics(status="error", error="Repository name must be in format 'owner/repo'") - - # Initialize codebase - codebase = Codebase.from_repo(repo_name) - - # Calculate metrics - num_files = len(codebase.files(extensions="*")) # Get all files - num_functions = len(codebase.functions) - num_classes = len(codebase.classes) - - return RepoMetrics( - num_files=num_files, - num_functions=num_functions, - num_classes=num_classes, - ) - - except Exception as e: - return RepoMetrics(status="error", error=str(e)) diff --git a/src/codegen/extensions/modal/pyproject.toml b/src/codegen/extensions/modal/pyproject.toml deleted file mode 100644 index 899030322..000000000 --- a/src/codegen/extensions/modal/pyproject.toml +++ /dev/null @@ -1,6 +0,0 @@ -[project] -name = "codegen-repo-analyzer" -version = "0.1.0" -description = "Modal API endpoint for analyzing GitHub repositories using Codegen" -requires-python = ">=3.13" -dependencies = ["modal>=0.73.25", "fastapi[standard]", "codegen>=0.5.30"]