diff --git a/.github/workflows/dagger_hackathon.yml b/.github/workflows/dagger_hackathon.yml index a1e1a1b2..f02a06bd 100644 --- a/.github/workflows/dagger_hackathon.yml +++ b/.github/workflows/dagger_hackathon.yml @@ -1,37 +1,51 @@ -name: dagger_hackathon # Name of the workflow +name: dagger_hackathon on: - push: - branches: - - feature/dagger-hackathon-line-extraction + pull_request: + branches: [main] + types: [opened, synchronize, reopened] jobs: - dagger-plan: - runs-on: ubuntu-latest # Define the runner environment + dagger-run-unit-tests: + runs-on: ubuntu-latest env: GITHUB_TOKEN: ${{ secrets.DAGGER_HACKATHON_GITHUB_TOKEN }} - OPENAI_AZURE_VERSION: "2024-12-01-preview" OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - OPENAI_MODEL: "gpt-4o" - OPENAI_BASE_URL: "https://vdfvdf.openai.azure.com/" + CURRENT_BRANCH: ${{ github.head_ref }} steps: - - name: Checkout repository # Fetch the repository code + - name: Checkout repo uses: actions/checkout@v4 - - name: Install Dagger CLI # Download and install the Dagger CLI + - name: Install Dagger CLI run: | curl -fsSL https://dl.dagger.io/dagger/install.sh | BIN_DIR=$HOME/.local/bin sh - echo "$HOME/.local/bin" >> $GITHUB_PATH # Add Dagger to the system PATH + echo "$HOME/.local/bin" >> $GITHUB_PATH - - name: Run Dagger plan + - name: Create .env file working-directory: docs/dagger/dagger-hackathon/ run: | - dagger call debug-unit-test-agent --directory-arg="." \ - --github-token="GITHUB_TOKEN" \ - --github-branch=feature/dagger-hackathon-line-extraction \ - --github-repo=codetocloudorg/platform-engineering \ - --azure-model="gpt-4o" \ - --azure-endpoint="https://vdfvdf.openai.azure.com/" \ - --azure-api-key="OPENAI_API_KEY" \ No newline at end of file + cat < .env + OPENAI_BASE_URL=https://vdfvdf.openai.azure.com/ + OPENAI_MODEL=gpt-4o + OPENAI_API_KEY=${OPENAI_API_KEY} + OPENAI_AZURE_VERSION=2024-12-01-preview + GITHUB_TOKEN=${GITHUB_TOKEN} + EOF + + - name: Dagger Run Unit Tests + working-directory: docs/dagger/dagger-hackathon/ + env: + DAGGER_SECRET_GITHUB_TOKEN: ${{ secrets.DAGGER_HACKATHON_GITHUB_TOKEN }} + run: | + export AZURE_OPENAI_API_KEY="${OPENAI_API_KEY}" + export AZURE_OPENAI_ENDPOINT="https://vdfvdf.openai.azure.com/" + dagger call \ + --source="." \ + --github_branch="${CURRENT_BRANCH}" \ + --github_repo="codetocloudorg/platform-engineering" \ + --github_token=DAGGER_SECRET_GITHUB_TOKEN \ + --azure_api_key="OPENAI_API_KEY" \ + --azure_endpoint="https://vdfvdf.openai.azure.com/" \ + fix-my-tests-agent diff --git a/docs/dagger/dagger-hackathon/.env-example b/docs/dagger/dagger-hackathon/.env-example new file mode 100644 index 00000000..6c9fc2c0 --- /dev/null +++ b/docs/dagger/dagger-hackathon/.env-example @@ -0,0 +1,4 @@ +OPENAI_BASE_URL="" +OPENAI_MODEL="gpt-4o" +OPENAI_API_KEY="" +OPENAI_AZURE_VERSION="2024-12-01-preview" \ No newline at end of file diff --git a/docs/dagger/dagger-hackathon/README.md b/docs/dagger/dagger-hackathon/README.md index 99d9d5da..2f10e0f4 100644 --- a/docs/dagger/dagger-hackathon/README.md +++ b/docs/dagger/dagger-hackathon/README.md @@ -1,6 +1,6 @@ # 🧑‍💻 Dagger Hackathon: June 18th, 2025 -Welcome to the Dagger hackathon hosted by the CNCF and Code to Cloud! By the end of this hack, you will have a good understanding of how to use Dagger and the various use cases it can help support. +Welcome to the Dagger hackathon hosted by the [CNCF](https://community.cncf.io/cloud-native-calgary/) and [Code to Cloud](https://www.codetocloud.io/)! By the end of this hack, you will have a good understanding of how to use Dagger and the various use cases it can help support. ## 🎯 Expected Outcomes @@ -10,7 +10,9 @@ Welcome to the Dagger hackathon hosted by the CNCF and Code to Cloud! By the end ✅ Dagger pipeline triggers LLM workflow if tests fail -✅ LLM provides feedback on why tests could be failing in a PR +✅ LLM provides code suggestion to fix failing unit tests + +✅ Code suggestion is added to PR ## ✅ Prerequisites @@ -18,28 +20,193 @@ There are two options for this hackathon. We highly recommend the first option t ### 1️⃣ Option 1: Github Codespace +See Getting Started + ### 2️⃣ Option 2: Local +anakin + Make sure to follow the prerequisites defined [here](../README.md) in the main Dagger README. -## 🔨 Implementation +## 🔨 Getting Started + +--- + +### ✅ Step 1: Create a Dagger Cloud Account + +- [Register for a account](https://dagger.io/cloud) +- Under settings, create a `DAGGER_CLOUD_TOKEN` and copy the secret password somewhere temporary + +> **Note:** This will be used to enable logging on the Dagger pipeline. Logs can be viewed in [Dagger Cloud](https://dagger.io/cloud) after step 5. + +--- + +### ✅ Step 2: Fork This Repository + +- Create a fork of this repository to your personal GitHub account + If you already have a fork, either rebase to this repository or, if you don't have any work you want to keep, just delete and recreate. + +- In the UI, create a new feature branch off `main` + +--- + +### ✅ Step 2: Setup Codespace *(if you are setting up locally skip to Step 3)* + +- In the GitHub UI, change to the newly created feature branch +- Create a codespace on the feature branch + + image + +- This will auto-open VS Code in browser. If you don't want to work from here and have the VS Code app installed, you can close this and open it in the app + + ![image](https://github.com/user-attachments/assets/6b6b0702-b083-4e33-8868-675bfd6e5fc9) + +--- + +### ✅ Step 3: Setup Locally *(if you did Step 2 then skip to Step 4)* + +#### 🔧 Install required prerequisites + +- Python 3.11+ +- [Docker Desktop](https://www.docker.com/products/docker-desktop/) or another container runtime (e.g. Colima on macOS and Linux) +- [Dagger CLI](https://docs.dagger.io/install/) + +#### 🔧 Additional Setup + +- Start your container runtime +- Clone the forked repository locally and switch to the newly created feature branch + +--- + +### ✅ Step 4: Break Some Code and Open A PR + +anakin + +Now that we have all of our prerequisites setup, **this is where the fun begins!** + +The below steps will take you through making a breaking change to a file in our application which will set us up in the next step to have our agent try and troubleshoot it. -All the code for the below can be found [here](./dagger-hackathon-pipeline/). +- Open a terminal +- Create an environment variable for `DAGGER_CLOUD_TOKEN`, e.g. + ```bash + export DAGGER_CLOUD_TOKEN="XXX" + ``` +- Change directory into `docs/dagger/dagger-hackathon/` +- Open the file `docs/dagger/dagger-hackathon/src/addition.py` and break the function + For example, modify to: `return a + b * 4` +- Run the unittests + ```bash + python -m unittest discover tests -v + ``` + and confirm it fails +- Push the modified `addition.py` to your new feature branch +- In the GitHub UI, open a PR on the feature branch and compare to `main` -Make sure you have the repo cloned and you are in the `docs\dagger\dagger-hackathon-pipeline` directory: +--- -```bash -# Clone the repository from GitHub -git clone https://github.com/codetocloudorg/platform-engineering.git +### ✅ Step 5: Run The Fix My Tests Agent -# Change directory to the Dagger hackathon pipeline documentation folder -cd ./docs/dagger/dagger-hackathon-pipeline +spongebob + +- Rename `docs/dagger/dagger-hackathon/.env-example` to `.env` +- Populate the placeholder keys with real values (we will provide for Hackathon day) +- Create environment variables for `GITHUB_TOKEN`, `AZURE_API_KEY`, `AZURE_API_ENDPOINT` + A GitHub Token can be created in GitHub under Settings → Developer Settings → Create a classic token + Example: + ```bash + export GITHUB_TOKEN="XXX" + ``` + +- Put in your values and run in CLI: + ```bash + dagger call \ + --source="." \ + --github_branch="BRANCH-NAME" \ + --github_repo="USERNAME/REPO-NAME" \ + --github_token="GITHUB_TOKEN" \ + --azure_api_key="AZURE_API_KEY" \ + --azure_endpoint="AZURE_API_ENDPOINT" \ + fix-my-tests-agent + ``` + +> **💡 Tip:** This will be used to enable logging on the Dagger pipeline. Logs can be viewed in [Dagger Cloud](https://dagger.io/cloud) after step 5. + +- If you see an error, it might be that the Agent has hallucinated — try again + You can also debug via the Dagger Traces in Dagger Cloud + +> **💡 Tip:** There is a prompt located at `docs\dagger\dagger-hackathon\dagger-hackathon-pipeline\debug_unit_test_prompt.md` that greatly influences the LLMs behavior. Make some changes to the prompt and see how it influences the results! + +--- + +### ✅ Step 6: Explore Dagger Traces + +fry + +Being able to navigate and explore logs in Dagger Cloud is a great skill to have to be able to effectively troubleshoot and build pipelines in Dagger. Now that we have ran our first command in Dagger, lets jump into the logs to see what happened. + +- Navigate to [Dagger Cloud](https://dagger.io/cloud) +- You should see your trace(s) + +![image](./assets/dagger_cloud.png) + +- Select a trace and explore the logs +- If all has gone well, you should see something like this at the bottom of the trace detailing the problematic file, line number, and fix for the failing unit test. + +![image](./assets/agent_final_output.png) + +--- + +### ✅ Step 7: Confirm The Agent Fixed the Code + +fry + +- If the Agent finished successfully, you should see something like below in the CLI + ``` + AgentResponse(pr_metadata=PrMetadataResult(pr_number='1', commit_id='c4293304b9fdccf2022e7e15e09e5b1604cecf04'), pr_suggestions=GitHubPrSuggestionResult(body='```suggestion\nreturn a + b\n```', comment_url='https://github.com/david-hurley/platform-engineering/pull/1#discussion_r2127910154')) + ``` +- Click the PR URL and see if there is a suggested code change + +--- + +### ✅ Step 8: Trigger GitHub Action + +- Delete the comment the Agent left on the PR in Step 6 +- [Create GitHub secrets](https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions#creating-secrets-for-a-repository) for everything in `.env` and name them `DAGGER_HACKATHON_GITHUB_TOKEN` and `OPENAI_API_KEY` for the GitHub token and Azure OpenAI API key respectively. +- Make sure to update your GitHub Action to trigger on a pull request instead of a manual trigger. + +Update the trigger from this: + +```yaml +on: + workflow_dispatch: +``` + +To this: +```yaml +on: + pull_request: + branches: [main] + types: [opened, synchronize, reopened] ``` -### Step 1: Create a Feature Branch +- Push your changes to Git and navigate back to your forked repo to see the GitHub Action running + +![image](./assets/run_unit_tests.png) + +--- + +### ✅ Step 9: Extend the Agent + +The last step is to extend the Dagger agent to actually make the code fix itself and push the changes to the PR for the human to review. That way there is still a human-in-the-loop to validate the AI output. -### 🤖 Step 2: Select your LLM Provider +--- -### Step 3: Create a PR +## ⚠️ Gotchas and Future Improvements -### Step 4: Run the Dagger Function \ No newline at end of file +- The agent can sometimes return incorrect paths to the file with breaking changes, incorrect line number of the breaking change, or incorrect fix (e.g. too verbose) +- Current state of this does not work with multi-line code changes or multiple breaking changes. + Only supports **1 breaking change on 1 line** +- The way commit ID is retrieved is not robust — if a breaking change is pushed to a branch with an open PR and it is commit ID A, and then another file is pushed (commit ID B), + the code is setup to grab the **latest commit ID**, not the one with the breaking change + This may result in failure (i.e. no diff) +- The agent leaves a comment on the PR as the idenity of whoever generated to token. In other words, if you leverage your GitHub token in the above steps, then the agent will leave a comment on the PR as you. This ideally should be a generic account in the future. diff --git a/docs/dagger/dagger-hackathon/assets/agent_final_output.png b/docs/dagger/dagger-hackathon/assets/agent_final_output.png new file mode 100644 index 00000000..65d4b41c Binary files /dev/null and b/docs/dagger/dagger-hackathon/assets/agent_final_output.png differ diff --git a/docs/dagger/dagger-hackathon/assets/anakin_meme.webp b/docs/dagger/dagger-hackathon/assets/anakin_meme.webp new file mode 100644 index 00000000..831cb487 Binary files /dev/null and b/docs/dagger/dagger-hackathon/assets/anakin_meme.webp differ diff --git a/docs/dagger/dagger-hackathon/assets/dagger_cloud.png b/docs/dagger/dagger-hackathon/assets/dagger_cloud.png new file mode 100644 index 00000000..19c4c9b3 Binary files /dev/null and b/docs/dagger/dagger-hackathon/assets/dagger_cloud.png differ diff --git a/docs/dagger/dagger-hackathon/assets/fry_meme.jpg b/docs/dagger/dagger-hackathon/assets/fry_meme.jpg new file mode 100644 index 00000000..a29ee473 Binary files /dev/null and b/docs/dagger/dagger-hackathon/assets/fry_meme.jpg differ diff --git a/docs/dagger/dagger-hackathon/assets/one_does_not_simply_meme.jpg b/docs/dagger/dagger-hackathon/assets/one_does_not_simply_meme.jpg new file mode 100644 index 00000000..0c43a2c3 Binary files /dev/null and b/docs/dagger/dagger-hackathon/assets/one_does_not_simply_meme.jpg differ diff --git a/docs/dagger/dagger-hackathon/assets/pikachu_meme.jpg b/docs/dagger/dagger-hackathon/assets/pikachu_meme.jpg new file mode 100644 index 00000000..b70e3b9c Binary files /dev/null and b/docs/dagger/dagger-hackathon/assets/pikachu_meme.jpg differ diff --git a/docs/dagger/dagger-hackathon/assets/run_unit_tests.png b/docs/dagger/dagger-hackathon/assets/run_unit_tests.png new file mode 100644 index 00000000..6bca4fef Binary files /dev/null and b/docs/dagger/dagger-hackathon/assets/run_unit_tests.png differ diff --git a/docs/dagger/dagger-hackathon/assets/spongebob_meme.jpg b/docs/dagger/dagger-hackathon/assets/spongebob_meme.jpg new file mode 100644 index 00000000..555c579e Binary files /dev/null and b/docs/dagger/dagger-hackathon/assets/spongebob_meme.jpg differ diff --git a/docs/dagger/dagger-hackathon/dagger-hackathon-pipeline/debug_unit_test_prompt.md b/docs/dagger/dagger-hackathon/dagger-hackathon-pipeline/debug_unit_test_prompt.md new file mode 100644 index 00000000..799ea52d --- /dev/null +++ b/docs/dagger/dagger-hackathon/dagger-hackathon-pipeline/debug_unit_test_prompt.md @@ -0,0 +1,17 @@ +## Instructions +You are a Python testing expert. + +Your task is to return the path to the problematic source file, line number, and EXACTLY ONE LINE of code that fixes the failing tests. + +The final reply must include these details, do not try and modify any files. +- When you return the path, make sure you give the full path to the file: docs/dagger/dagger-hackathon (e.g.: docs/dagger/dagger-hackathon/src/addition.py). +- Think hard about the line number, it should be the line number of the bug code in the file specified by the path that is failing. +- Check that the line number exists in the file and that the proposed change is meant for that line. + +**Return ONLY the path to the problematic source file (e.g.: docs/dagger/dagger-hackathon), line number that failing code is happneing on, and EXACTLY ONE LINE of code that fixes the failing tests. Nothing else.** + +## Test Results +{{.test_results}} + +## Example Output: +Path: docs/dagger/dagger-hackathon/src/subtraction.py Line number: 7 Code to fix the issue: return a - b \ No newline at end of file diff --git a/docs/dagger/dagger-hackathon/dagger-hackathon-pipeline/src/dagger_hackathon/main.py b/docs/dagger/dagger-hackathon/dagger-hackathon-pipeline/src/dagger_hackathon/main.py index 10eab37b..faef296d 100644 --- a/docs/dagger/dagger-hackathon/dagger-hackathon-pipeline/src/dagger_hackathon/main.py +++ b/docs/dagger/dagger-hackathon/dagger-hackathon-pipeline/src/dagger_hackathon/main.py @@ -1,10 +1,10 @@ import dagger -from dagger import dag, field, function, object_type -import os import json +from dagger import dag, field, function, object_type, DefaultPath +from typing import Annotated @object_type -class UnitTestResult: +class UnitTestResults: result: str = field() success: str = field() @@ -13,96 +13,43 @@ class PrMetadataResult: pr_number: str = field() commit_id: str = field() -# needs to be list of ProposedCodeChanges - see https://docs.dagger.io/api/custom-types/ @object_type -class ProposedCodeChanges: +class ProposedCodeChange: path: str = field() line: str = field() change: str = field() # same comment as above @object_type -class GitHubPrSuggestionResults: +class GitHubPrSuggestionResult: body: str = field() comment_url: str = field() @object_type -class AgentResponses: +class AgentResponse: pr_metadata: PrMetadataResult = field() - pr_suggestions: GitHubPrSuggestionResults = field() + pr_suggestions: GitHubPrSuggestionResult = field() @object_type class DaggerHackathon: + source: dagger.Directory + github_branch: str + github_repo: str + github_token: dagger.Secret + azure_api_key: dagger.Secret + azure_endpoint: str + azure_model: str = "gpt-4o" @function - async def CreateStructuredResponse( - self, - directory_arg: dagger.Directory, - response_to_structure: str, - azure_api_key: dagger.Secret, - azure_endpoint: str, - azure_model: str - ) -> ProposedCodeChanges: - """Structure LLM Response""" - - command = [ - "python", "dagger-hackathon-pipeline/src/dagger_hackathon/structure_data.py", - "--response", response_to_structure, - "--endpoint", azure_endpoint, - "--model", azure_model - ] - container = ( - dag.container() - .from_("python:3.11") - .with_mounted_directory("/app", directory_arg) - .with_workdir("/app") - .with_secret_variable("AZURE_OPENAI_API_KEY", azure_api_key) - .with_exec(["pip", "install", "--upgrade", "pip"]) - .with_exec(["pip", "install", "openai==1.82.0", "pydantic==2.11.5"]) - .with_exec(command) - ) - - output = await container.stdout() - - response_data = json.loads(output) - - output = ProposedCodeChanges( - path=response_data["path"], - line=response_data["line"], - change=response_data["change"] - ) - - return output - - - @function - async def RunUnitTests(self, directory_arg: dagger.Directory) -> UnitTestResult: - """Run all unit tests and return the results""" - - container = ( - dag.container() - .from_("python:3.11") - .with_mounted_directory("/app", directory_arg) - .with_workdir("/app") - .with_exec(["sh", "-c", "python -m unittest discover tests -v || true"]) - ) - - result = await container.stderr() - - if "OK" in result: - success = "true" - else: - success = "false" - - return UnitTestResult(result=result, success=success) - - @function - async def GetPrMetadata(self, github_branch: str, github_repo: str, github_token: dagger.Secret) -> PrMetadataResult: - """Get the PR number and commit ID""" + async def GetPrMetadata(self) -> PrMetadataResult: + """Get the PR number and commit ID + Returns: + PrMetadataResult + """ base_container = ( dag.container() .from_("alpine:latest") - .with_secret_variable("GITHUB_TOKEN", github_token) + .with_secret_variable("GITHUB_TOKEN", self.github_token) .with_exec(["apk", "add", "--no-cache", "curl", "bash", "git", "openssl"]) .with_exec([ "sh", "-c", @@ -117,45 +64,128 @@ async def GetPrMetadata(self, github_branch: str, github_repo: str, github_token base_container .with_exec([ "gh", "pr", "list", - "--head", f"{github_branch}", + "--head", f"{self.github_branch}", "--limit", "1", "--json", "number", "--jq", ".[0].number", - "--repo", f"{github_repo}" + "--repo", f"{self.github_repo}" ]) .stdout() ) - commit_id = await ( + commit_logs = await ( base_container .with_exec([ "gh", "pr", "view", pr_number.strip(), - "--json", "headRefOid", - "-q", ".headRefOid", - "--repo", f"{github_repo}" + "--json", "commits", + "--repo", f"{self.github_repo}" ]) .stdout() ) - output = PrMetadataResult(pr_number=pr_number.strip(), commit_id=commit_id.strip()) + commit_logs_json = json.loads(commit_logs) + + head_commit_id = commit_logs_json["commits"][-1]["oid"] + + return PrMetadataResult(pr_number=pr_number.strip(), commit_id=head_commit_id.strip()) + + @function + async def RunUnitTests(self) -> UnitTestResults: + """Run all unit tests and return the results + Returns: + UnitTestResults + """ + + container = ( + dag.container() + .from_("python:3.11") + .with_mounted_directory("/src", self.source) + .with_workdir("/src") + .with_exec(["sh", "-c", "python -m unittest discover tests -v || true"]) + ) + + unit_test_results = await container.stderr() + + if "OK" in unit_test_results: + success = "true" + else: + success = "false" + + return UnitTestResults(result=unit_test_results, success=success) + + @function + async def StructureLlmResponse( + self, + response_to_structure: str + ) -> ProposedCodeChange: + """Structure LLM Response + Args: + response_to_structure: LLM response to structure + Returns: + ProposedCodeChange + """ + + command_to_execute = [ + "python", "dagger-hackathon-pipeline/src/dagger_hackathon/structure_llm_response.py", + "--response", response_to_structure, + "--endpoint", self.azure_endpoint, + "--model", self.azure_model + ] + container = ( + dag.container() + .from_("python:3.11") + .with_mounted_directory("/app", self.source) + .with_workdir("/app") + .with_secret_variable("AZURE_OPENAI_API_KEY", self.azure_api_key) + .with_exec(["pip", "install", "--upgrade", "pip"]) + .with_exec(["pip", "install", "openai==1.82.0", "pydantic==2.11.5"]) + .with_exec(["sh", "-c", "export $(grep -v '^#' .env | xargs)"]) + .with_exec(["sh", "-c", "echo $OPENAI_API_KEY"]) + .with_exec(command_to_execute) + ) + + structured_results = await container.stdout() + + structured_results_json = json.loads(structured_results) - return output + return ProposedCodeChange( + path=structured_results_json["path"], + line=structured_results_json["line"], + change=structured_results_json["change"] + ) @function - async def CreateCodeSuggestion( + async def CreatePrSuggestion( self, - directory_arg: dagger.Directory, - github_token: dagger.Secret, pr_metadata: PrMetadataResult, - proposed_code_changes: ProposedCodeChanges, - ) -> GitHubPrSuggestionResults: - """Create a code suggestion for a PR""" + proposed_code_changes: ProposedCodeChange, + ) -> GitHubPrSuggestionResult: + """Create a code suggestion for a PR + Args: + pr_metadata: PR Metadata + proposed_code_changes: Proposed Code Changes + Returns: + GitHubPrSuggestionResult + """ + + command_to_execute = [ + "gh", "api", + "--method", "POST", + "-H", "Accept: application/vnd.github+json", + "-H", "X-GitHub-Api-Version: 2022-11-28", + f"/repos/codetocloudorg/platform-engineering/pulls/{pr_metadata.pr_number}/comments", + "-f", f"body=```suggestion\n{proposed_code_changes.change}\n```", + "-f", f"commit_id={pr_metadata.commit_id}", + "-f", f"path={proposed_code_changes.path}", + "-F", f"line={proposed_code_changes.line}", + "-f", "side=RIGHT" + ] container = ( dag.container() .from_("alpine:latest") - .with_secret_variable("GITHUB_TOKEN", github_token) + .with_secret_variable("GITHUB_TOKEN", self.github_token) .with_exec(["apk", "add", "--no-cache", "curl", "bash", "git", "openssl"]) .with_exec([ "sh", "-c", @@ -164,47 +194,30 @@ async def CreateCodeSuggestion( "| tar -xz && mv gh_2.70.0_linux_amd64/bin/gh /usr/local/bin/" ) ]) - .with_exec([ - "gh", "api", - "--method", "POST", - "-H", "Accept: application/vnd.github+json", - "-H", "X-GitHub-Api-Version: 2022-11-28", - f"/repos/codetocloudorg/platform-engineering/pulls/{pr_metadata.pr_number}/comments", - "-f", f"body=```suggestion\n{proposed_code_changes.change}\n```", - "-f", f"commit_id={pr_metadata.commit_id}", - "-f", f"path={proposed_code_changes.path}", - "-F", f"line={proposed_code_changes.line}", - "-f", "side=RIGHT" - ]) + .with_exec(command_to_execute) ) - output = await container.stdout() + pr_comment_logs = await container.stdout() - response_data = json.loads(output) + pr_comment_logs_json = json.loads(pr_comment_logs) - output = GitHubPrSuggestionResults( - body=response_data["body"], - comment_url=response_data["html_url"] + return GitHubPrSuggestionResult( + body=pr_comment_logs_json["body"], + comment_url=pr_comment_logs_json["html_url"] ) - return output - @function - async def DebugUnitTestAgent( - self, - directory_arg: dagger.Directory, - github_branch: str, - github_repo: str, - github_token: dagger.Secret, - azure_api_key: dagger.Secret, - azure_endpoint: str, - azure_model: str + async def FixMyTestsAgent( + self ) -> str: - """Debug the unit test agent""" + """Orchestrator Agent to Debug Unit Tests + Returns: + AgentResponse + """ - pr_metadata = await self.GetPrMetadata(github_branch, github_repo, github_token) + pr_metadata = await self.GetPrMetadata() - unit_test_results = await self.RunUnitTests(directory_arg) + unit_test_results = await self.RunUnitTests() if unit_test_results.success == "true": return f"All unit tests passed - no code changes needed" @@ -216,7 +229,7 @@ async def DebugUnitTestAgent( "unit_test_runner", dag.container() .from_("python:3.11") - .with_mounted_directory("/app", directory_arg) + .with_mounted_directory("/app", self.source) .with_workdir("/app"), "a container used to troubleshoot failing unit tests" ) @@ -228,18 +241,22 @@ async def DebugUnitTestAgent( analyze_results = ( dag.llm() .with_env(environment) - .with_prompt_file(dag.current_module().source().file("test_debug_prompt.txt")) + .with_prompt_file(dag.current_module().source().file("debug_unit_test_prompt.md")) ) - proposed_code_changes = await self.CreateStructuredResponse(directory_arg, await analyze_results.last_reply(), azure_api_key, azure_endpoint, azure_model) - - # proposed_code_changes = ProposedCodeChanges( - # path="docs/dagger/dagger-hackathon/src/addition.py", - # line="2", - # change="return a + b" - # ) + analyzed_results_output = await analyze_results.last_reply() + + proposed_code_change = await self.StructureLlmResponse( + await analyze_results.last_reply() + ) - created_pr_suggestions = await self.CreateCodeSuggestion(directory_arg, github_token, pr_metadata, proposed_code_changes) + created_pr_suggestion = await self.CreatePrSuggestion( + pr_metadata, + proposed_code_change + ) - return str(created_pr_suggestions) + return str(AgentResponse( + pr_metadata=pr_metadata, + pr_suggestions=created_pr_suggestion + )) \ No newline at end of file diff --git a/docs/dagger/dagger-hackathon/dagger-hackathon-pipeline/src/dagger_hackathon/structure_data.py b/docs/dagger/dagger-hackathon/dagger-hackathon-pipeline/src/dagger_hackathon/structure_llm_response.py similarity index 100% rename from docs/dagger/dagger-hackathon/dagger-hackathon-pipeline/src/dagger_hackathon/structure_data.py rename to docs/dagger/dagger-hackathon/dagger-hackathon-pipeline/src/dagger_hackathon/structure_llm_response.py diff --git a/docs/dagger/dagger-hackathon/dagger-hackathon-pipeline/test_debug_prompt.txt b/docs/dagger/dagger-hackathon/dagger-hackathon-pipeline/test_debug_prompt.txt deleted file mode 100644 index f8dc3426..00000000 --- a/docs/dagger/dagger-hackathon/dagger-hackathon-pipeline/test_debug_prompt.txt +++ /dev/null @@ -1,14 +0,0 @@ -You are a Python testing expert. Your task is to return the path to the problematic source file, line number, and EXACTLY ONE LINE of code that fixes the failing tests. -Make sure to work in the /app folder, this will be your working folder. -When you return the path, make sure you give the full path to the file: docs/dagger/dagger-hackathon (e.g.: docs/dagger/dagger-hackathon/src/addition.py) -Think hard about the line number, it should be the line number of the bug code in the file specified by the path that is failing. Check that the line number exists in the file and that the proposed change is meant for that line -You must return a final reply with these details. -Do not try and modify any files. - -Test Results: -{{.test_results}} - -IMPORTANT: Return ONLY the path to the problematic source file (e.g.: docs/dagger/dagger-hackathon), line number that failing code is happneing on, and EXACTLY ONE LINE of code that fixes the failing tests. Nothing else. - -EXAMPLE OUTPUT: -Path: docs/dagger/dagger-hackathon/src/subtraction.py Line number: 7 Code to fix the issue: return a - b \ No newline at end of file diff --git a/docs/dagger/dagger-hackathon/src/addition.py b/docs/dagger/dagger-hackathon/src/addition.py index 12ee7431..8eeaa2d5 100644 --- a/docs/dagger/dagger-hackathon/src/addition.py +++ b/docs/dagger/dagger-hackathon/src/addition.py @@ -1,2 +1,2 @@ def add(a, b): - return a - b + return a - b*2 diff --git a/docs/dagger/dagger-hackathon/tests/test_addition.py b/docs/dagger/dagger-hackathon/tests/test_addition.py index 2ff1d562..562f1910 100644 --- a/docs/dagger/dagger-hackathon/tests/test_addition.py +++ b/docs/dagger/dagger-hackathon/tests/test_addition.py @@ -4,7 +4,7 @@ class TestAddition(unittest.TestCase): def test_positive_numbers(self): """Test addition with positive numbers""" - self.assertEqual(add(1, 2), 3) + self.assertEqual(add(5, 6), 11) if __name__ == '__main__': unittest.main() \ No newline at end of file