|
1 | 1 | [](https://github.com/kangwonlee/gemini-python-tutor/actions) |
2 | 2 | [](https://github.com/kangwonlee/gemini-python-tutor/releases) |
3 | | -[](https://hub.docker.com/r/beachgoer/gemini-python-tutor) |
4 | | -[](https://hub.docker.com/r/beachgoer/gemini-python-tutor) |
5 | 3 |
|
6 | | -# AI Python Code Tutor |
| 4 | +# AI Code Tutor |
7 | 5 |
|
8 | | -This GitHub Action leverages AI to analyze test results and Python code, delivering personalized feedback for student assignments. It identifies errors, suggests improvements, and explains concepts clearly. |
| 6 | +This GitHub Action uses AI to provide personalized feedback for student assignments in C/C++ and Python. It analyzes test results and code, identifying errors, suggesting optimizations, and explaining concepts clearly. Ideal for GitHub Classroom, it saves instructors time and ensures consistent, on-demand feedback. |
9 | 7 |
|
10 | | -The AI tutor detects logic errors, recommends efficient algorithms, simplifies complex topics, and links to relevant documentation. It saves instructors time, ensures consistent feedback, and empowers students to learn anytime, anywhere. |
| 8 | +The AI tutor processes JSON test reports from `pytest-json-report`, generated by `pytest` tests wrapping C/C++ or Python code. It detects logic errors, recommends efficient algorithms, and links to relevant documentation. |
11 | 9 |
|
12 | 10 | ## Key Features |
13 | | - |
14 | | -- AI-powered feedback for Python code assignments. |
15 | | -- Supports multiple JSON test report files from `pytest-json-report`. |
16 | | -- Analyzes multiple student code files. |
17 | | -- Customizable explanation language (e.g., English, Korean). |
| 11 | +- AI-powered feedback for C/C++ and Python assignments. |
| 12 | +- Supports multiple JSON test reports from `pytest-json-report`. |
| 13 | +- Analyzes multiple student code files (`.c`, `.cpp`, `.py`). |
| 14 | +- Flexible LLM selection (Claude, Gemini, Grok, Nvidia NIM, Perplexity) with Gemini fallback. |
| 15 | +- Customizable feedback language (e.g., English, Korean). |
18 | 16 | - Excludes common README content to optimize API usage. |
19 | 17 |
|
20 | 18 | ## Prerequisites |
21 | | - |
22 | | -To use this action, you’ll need: |
23 | | - |
24 | | -- **pytest-json-report plugin**: Generates JSON test reports for analysis. |
25 | | - - Install it with: |
| 19 | +- **Python Dependencies**: |
| 20 | + - Install required packages: |
26 | 21 | ```bash |
27 | | - pip install pytest-json-report |
| 22 | + pip install pytest pytest-json-report pytest-xdist requests |
28 | 23 | ``` |
29 | | - - Generate a JSON report with: |
| 24 | + - Generate JSON reports with: |
30 | 25 | ```bash |
31 | | - python -m pytest --json-report --json-indent=4 --json-report-file=report.json tests/test_my_test_file.py |
| 26 | + python -m pytest --json-report --json-report-indent=4 --json-report-file=report.json tests/test_file.py |
32 | 27 | ``` |
33 | 28 | - See [pytest-json-report documentation](https://pypi.org/project/pytest-json-report/). |
34 | | - |
35 | | -- **Google API Key**: Set as `GOOGLE_API_KEY` in your repository secrets. |
| 29 | +- **API Key**: At least one API key for supported LLMs (Claude, Gemini, Grok, Nvidia NIM, Perplexity), set as repository secrets (e.g., `INPUT_CLAUDE_API_KEY`, `INPUT_GOOGLE_API_KEY`). |
| 30 | +- **Docker**: For C/C++ testing, use a Docker image with `clang`, `cmake`, and `pytest`. |
36 | 31 |
|
37 | 32 | ## Usage |
38 | | - |
39 | 33 | 1. Add a workflow file (e.g., `.github/workflows/classroom.yml`) to your repository. |
40 | | -2. Configure it as follows: |
41 | | - |
42 | | -``` yaml |
43 | | -on: [push] |
| 34 | +2. Configure it to run tests and invoke the AI tutor. Example for C/C++ assignments: |
44 | 35 |
|
| 36 | +```yaml |
| 37 | +name: Grade Assignment |
| 38 | +on: [push, pull_request, workflow_dispatch] |
45 | 39 | jobs: |
46 | 40 | grade: |
47 | 41 | runs-on: ubuntu-latest |
| 42 | + env: |
| 43 | + CONTAINER_WORKSPACE: /app/workspace |
| 44 | + CONTAINER_TESTS: /tests |
| 45 | + CONTAINER_SRC: /app/workspace/src |
| 46 | + C_FILENAME: main.c |
| 47 | + WORKSPACE_OUTPUT: ${{ runner.temp }}/output |
| 48 | + CONTAINER_OUTPUT: /output |
48 | 49 | steps: |
49 | 50 | - uses: actions/checkout@v4 |
50 | | - - name: Install dependencies |
51 | | - run: pip install pytest pytest-json-report |
52 | | - - name: Run tests |
| 51 | + - name: Set up environment |
| 52 | + run: pip install pytest==8.3.5 pytest-json-report==1.5.0 pytest-xdist==3.6.1 requests==2.32.4 |
| 53 | + - name: Create output folder |
| 54 | + run: mkdir -p ${{ env.WORKSPACE_OUTPUT }} |
| 55 | + - name: Run C/C++ tests |
53 | 56 | run: | |
54 | | - python -m pytest --json-report --json-report-file=report.json tests/test_my_test_file.py |
55 | | - - name: AI Python Tutor |
56 | | - uses: kangwonlee/gemini-python-tutor@v0.2.1 |
57 | | - if: always() # Runs even if, or especially when, tests fail |
| 57 | + docker run --rm \ |
| 58 | + --volume ${{ github.workspace }}:${{ env.CONTAINER_WORKSPACE }}:ro \ |
| 59 | + --volume ${{ env.WORKSPACE_OUTPUT }}:${{ env.CONTAINER_OUTPUT }}:rw \ |
| 60 | + --workdir ${{ env.CONTAINER_TESTS }} \ |
| 61 | + ghcr.io/kangwonlee/edu-base-cpp:4e0d6d8 \ |
| 62 | + /bin/sh -c "cmake . -DCMAKE_BUILD_TYPE=Debug -DSTUDENT_DIR=${{ env.CONTAINER_WORKSPACE }} && make && python3 -m pytest --json-report --json-report-indent=4 --json-report-file=${{ env.CONTAINER_OUTPUT }}/report.json test_dynamic.py" |
| 63 | + - name: AI Code Tutor |
| 64 | + uses: kangwonlee/gemini-python-tutor@v0.3.7 |
| 65 | + if: always() |
58 | 66 | with: |
59 | | - report-files: report.json |
60 | | - api-key: ${{ secrets.GOOGLE_API_KEY }} |
61 | | - student-files: exercise.py |
62 | | - readme-path: README.md |
| 67 | + report-files: ${{ env.WORKSPACE_OUTPUT }}/report.json |
| 68 | + student-files: ${{ env.CONTAINER_SRC }}/${{ env.C_FILENAME }} |
| 69 | + readme-path: ${{ env.CONTAINER_WORKSPACE }}/README.md |
63 | 70 | explanation-in: English |
64 | | - timeout-minutes: 5 |
| 71 | + model: gemini |
| 72 | + INPUT_CLAUDE_API_KEY: ${{ secrets.INPUT_CLAUDE_API_KEY }} |
| 73 | + INPUT_GOOGLE_API_KEY: ${{ secrets.INPUT_GOOGLE_API_KEY }} |
| 74 | + INPUT_GROK_API_KEY: ${{ secrets.INPUT_GROK_API_KEY }} |
| 75 | + INPUT_NVIDIA_API_KEY: ${{ secrets.INPUT_NVIDIA_API_KEY }} |
| 76 | + INPUT_PERPLEXITY_API_KEY: ${{ secrets.INPUT_PERPLEXITY_API_KEY }} |
| 77 | + timeout-minutes: 10 |
65 | 78 | ``` |
66 | 79 |
|
67 | 80 | ### Notes |
68 | | -- The action processes JSON output from `pytest-json-report` to evaluate test results and provide feedback. |
69 | | -- To save API usage, exclude common README content by marking it with: |
| 81 | +- **C/C++ Testing**: Tests can run in a Docker container with `pytest` wrapping C/C++ code (e.g., via `ctypes` for shared libraries, as in `test_dynamic.py`). Ensure JSON reports are generated. |
| 82 | +- **Model Selection**: Set `model` to prefer an LLM (e.g., `gemini`). If its key is unavailable, the action falls back to Gemini if `INPUT_GOOGLE_API_KEY` is set, or uses any one of available key. |
| 83 | +- **Secrets**: Store API keys as repository secrets with `INPUT_` prefix (e.g., `INPUT_GOOGLE_API_KEY`) in Settings > Secrets and variables > Actions. |
| 84 | +- **README Optimization**: Exclude common README content with: |
70 | 85 | - Start: ``From here is common to all assignments.`` |
71 | 86 | - End: ``Until here is common to all assignments.`` |
72 | | - - Use double backticks (``) around these markers. |
| 87 | + - Use double backticks (``). |
73 | 88 |
|
74 | | -### Optimizing pytest for LLMs |
75 | | -- Use descriptive test names (e.g., `test_calculate_sum_correctly`). |
76 | | -- Add clear assertion messages (e.g., `assert x == 5, "Expected 5, got {x}"`). |
77 | | -- Keep tests simple and focused for better AI interpretation. |
| 89 | +### Optimizing pytest for AI Feedback |
| 90 | +- Use descriptive test names (e.g., `test_sum_range_for__valid_input`). |
| 91 | +- Include clear assertion messages (e.g., `assert result == 10, f"Expected 10, got {result}"`). |
| 92 | +- Keep tests focused for accurate AI interpretation. |
78 | 93 |
|
79 | 94 | ## Inputs |
80 | | - |
81 | | -| Input | Description | Required | Default | |
82 | | -|-------------------|--------------------------------------------------|----------|-----------------| |
83 | | -| `report-files` | Comma-separated list of JSON report files | Yes | `report.json` | |
84 | | -| `api-key` | Google API key for Gemini | Yes | N/A | |
85 | | -| `student-files` | Comma-separated list of student Python files | Yes | `exercise.py` | |
86 | | -| `readme-path` | Path to assignment instructions (README.md) | No | `README.md` | |
87 | | -| `explanation-in` | Language for feedback (e.g., English, Korean) | No | `English` | |
88 | | -| `model` | Gemini model (e.g., `gemini-2.0-flash`) | No | `gemini-2.0-flash` | |
89 | | - |
| 95 | +| Input | Description | Required | Default | |
| 96 | +|-------------------------|--------------------------------------------------|----------|-----------------| |
| 97 | +| `report-files` | Comma-separated JSON report files | Yes | `report.json` | |
| 98 | +| `student-files` | Comma-separated student code files (`.c`, `.cpp`, `.py`) | Yes | `exercise.py` | |
| 99 | +| `readme-path` | Path to assignment instructions (README.md) | No | `README.md` | |
| 100 | +| `explanation-in` | Feedback language (e.g., English, Korean) | No | `English` | |
| 101 | +| `model` | Preferred LLM (e.g., `gemini`, `claude`) | No | None | |
| 102 | +| `INPUT_CLAUDE_API_KEY` | Claude API key | No* | None | |
| 103 | +| `INPUT_GOOGLE_API_KEY` | Google Gemini API key | No* | None | |
| 104 | +| `INPUT_GROK_API_KEY` | Grok API key | No* | None | |
| 105 | +| `INPUT_NVIDIA_API_KEY` | Nvidia NIM API key | No* | None | |
| 106 | +| `INPUT_PERPLEXITY_API_KEY` | Perplexity API key | No* | None | |
| 107 | + |
| 108 | +*At least one API key is required. |
90 | 109 |
|
91 | 110 | ### Example with Multiple Files |
92 | | - |
93 | | -``` yaml |
| 111 | +```yaml |
94 | 112 | with: |
95 | | - report-files: 'report1.json, report2.json, reports/*.json' |
96 | | - api-key: ${{ secrets.GOOGLE_API_KEY }} |
97 | | - student-files: 'exercise1.py, exercise2.py' |
| 113 | + report-files: 'report1.json,report2.json,reports/*.json' |
| 114 | + student-files: 'src/main.c,src/utils.c' |
98 | 115 | readme-path: README.md |
99 | 116 | explanation-in: English |
| 117 | + model: gemini |
| 118 | + INPUT_GOOGLE_API_KEY: ${{ secrets.INPUT_GOOGLE_API_KEY }} |
| 119 | + INPUT_CLAUDE_API_KEY: ${{ secrets.INPUT_CLAUDE_API_KEY }} |
100 | 120 | ``` |
101 | 121 |
|
102 | 122 | ## Outputs |
103 | | - |
104 | | -- **Feedback**: Written to `$GITHUB_STEP_SUMMARY` (if set) in Markdown format, visible in the GitHub Job Summary. |
| 123 | +- **Feedback**: Markdown written to `$GITHUB_STEP_SUMMARY`, visible in the GitHub Job Summary, and saved as `feedback.md` in artifacts. |
105 | 124 |
|
106 | 125 | ## Limitations |
107 | | - |
108 | | -- Developed primarily to support Python code assignments. |
109 | | -- Requires `pytest-json-report` for test reports. |
| 126 | +- Primarily supports C/C++ and Python assignments via `pytest-json-report`. |
| 127 | +- Requires at least one valid API key. |
| 128 | +- C/C++ feedback relies on `pytest` tests wrapping compiled code. |
110 | 129 |
|
111 | 130 | ## Future Enhancements |
112 | | - |
113 | | -- Expand natural language support with auto-detection. |
114 | | -- Add options for AI models (e.g., Gemini Advanced, Grok, Perplexity). |
115 | | -- Facilitate supporting more programming languages. |
116 | | -- Include a `verbose` mode for detailed feedback. |
| 131 | +- Auto-detect feedback language. |
| 132 | +- Support additional programming languages. |
| 133 | +- Add verbose mode for detailed feedback. |
117 | 134 |
|
118 | 135 | ## Troubleshooting |
119 | | - |
120 | | -Check the action logs in the GitHub Actions tab for details. |
| 136 | +Check GitHub Actions logs for details. |
121 | 137 |
|
122 | 138 | ### Common Errors |
123 | | -- **API Key Issues**: |
124 | | - - "Invalid API key": Verify `GOOGLE_API_KEY` in secrets. |
125 | | -- **Report File Issues**: |
126 | | - - "Report file not found": Ensure JSON report exists. |
127 | | -- **Student File Issues**: |
128 | | - - "Student file not found": Check file paths and `.py` extensions. |
| 139 | +- **API Key Issues**: "No API keys provided" – Ensure at least one API key is set in secrets. |
| 140 | +- **Report File Issues**: "Report file not found" – Verify JSON report exists. |
| 141 | +- **Student File Issues**: "Student file not found" – Check file paths and extensions. |
129 | 142 |
|
130 | 143 | ### Debugging Tips |
131 | | -- View logs in the "AI Python Tutor" job. |
| 144 | +- View logs in the "AI Code Tutor" job. |
132 | 145 | - Test locally with [act](https://github.com/nektos/act). |
133 | | -- Environment variable `INPUT_FAIL-EXPECTED` available for testing and debugging |
| 146 | +- Use `INPUT_FAIL-EXPECTED=true` for debugging expected test failures. |
134 | 147 |
|
135 | 148 | ## Contact |
136 | | - |
137 | | -Questions? Please contact [https://github.com/kangwonlee](https://github.com/kangwonlee). |
| 149 | +Questions? Contact [https://github.com/kangwonlee](https://github.com/kangwonlee). |
138 | 150 |
|
139 | 151 | ## License |
140 | | - |
141 | | -BSD 3-Clause License + Do Not Harm. |
| 152 | +BSD 3-Clause License + Do Not Harm. |
142 | 153 | Copyright (c) 2024 Kangwon Lee |
143 | 154 |
|
144 | 155 | ## Acknowledgements |
145 | | - |
146 | | -* Built using [python-github-action-template](https://github.com/cicirello/python-github-action-template) by Vincent A. Cicirello (MIT License). |
147 | | -* Gemini 2.0 Flash and Grok 3 helped with the code and documentation. |
148 | | -* Registered as #C-2024-034203, #C-2024-035473, #C-2025-016393, and #C-2025-027967 with the Korea Copyright Commission. |
| 156 | +- Built using [python-github-action-template](https://github.com/cicirello/python-github-action-template) by Vincent A. Cicirello (MIT License). |
| 157 | +- Gemini 2.0 Flash and Grok 3 assisted with code and documentation. |
| 158 | +- Registered as #C-2024-034203, #C-2024-035473, #C-2025-016393, and #C-2025-027967 with the Korea Copyright Commission. |
0 commit comments