-
Notifications
You must be signed in to change notification settings - Fork 9
Add FAQ automation system with GitHub Actions integration #14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
alexeygrigorev
merged 39 commits into
DataTalksClub:main
from
frederick-douglas-pearce:feature/faq-automation-clean
Oct 27, 2025
Merged
Changes from 3 commits
Commits
Show all changes
39 commits
Select commit
Hold shift + click to select a range
bdbadfa
Add FAQ automation system with GitHub Actions integration
frederick-douglas-pearce 2e223aa
Fix minsearch version requirement (0.0.7 instead of 0.4.1)
frederick-douglas-pearce 786d850
Configure setuptools to only package faq_automation
frederick-douglas-pearce 2f5f40e
Change default model to gpt-5-nano for structured output support
frederick-douglas-pearce c882962
Remove test artifacts and add *.egg-info/ to gitignore
frederick-douglas-pearce 2502ad7
Add *.egg-info/ to gitignore
frederick-douglas-pearce daea8fa
Add contribution banner to course pages
frederick-douglas-pearce 789404f
Increase contribution banner font size to match questions
frederick-douglas-pearce eab1597
Update issue template with dropdown for course selection
frederick-douglas-pearce c52a3a9
Add manual trigger for testing FAQ automation workflow
frederick-douglas-pearce d38e9c1
Fix issue number handling for manual workflow triggers
frederick-douglas-pearce a010417
Fix: Create FAQ bot branches from main instead of current branch
frederick-douglas-pearce e7f7faa
docs: Update Development section with correct uv commands and API keyβ¦
frederick-douglas-pearce efbad4d
fix: Correct uv commands in Makefile and improve issue body parsing
frederick-douglas-pearce 69f4f97
docs: Update README - remove Quick Start and correct LLM to GPT-5
frederick-douglas-pearce 05e3ec2
docs: Update issue links to DataTalksClub/faq repository
frederick-douglas-pearce b0a5f32
docs: Remove GitHub Discussions reference from Support section
frederick-douglas-pearce adafd4c
docs: Fix escaped backticks in CONTRIBUTING.md example code blocks
frederick-douglas-pearce 797d741
docs: Fix nested code blocks using 4 backticks for outer block
frederick-douglas-pearce a5566c5
docs: Update testing documentation links for consistency
frederick-douglas-pearce df1aa9f
docs: Remove outer code block from example to render link correctly
frederick-douglas-pearce b652fc2
docs: Add FAQ automation tests to tests/README.md
frederick-douglas-pearce ca8de38
refactor: Organize FAQ automation tests into classes
frederick-douglas-pearce e7f6212
docs: Add FAQ automation examples to test method examples
frederick-douglas-pearce 00f2782
docs: Update test commands to match Makefile and remove --extra dev
frederick-douglas-pearce 1032a77
Fix OpenAI API syntax to match notebook implementation
frederick-douglas-pearce 574af38
Replace JS parsing logic with Python in FAQ automation workflow
frederick-douglas-pearce ce8925a
Replace bash scripting with Python for GitHub Actions outputs
frederick-douglas-pearce c605fc6
Update workflow to use modern uv sync command
frederick-douglas-pearce 7966260
Simplify FAQ automation workflow by removing --course argument
frederick-douglas-pearce 08d614a
Fix workflow to use uv run for Python commands
frederick-douglas-pearce e98b56b
Remove dead code: parse_issue_body() and its tests
frederick-douglas-pearce b1728e7
Add answer content example to FAQ file format section
frederick-douglas-pearce 1341bef
Add course field to FAQ example in contributing guide
frederick-douglas-pearce c6ff515
Update test documentation to reflect full test suite scope
frederick-douglas-pearce ad44dc7
Add comprehensive integration tests for FAQ automation workflow
frederick-douglas-pearce f203777
Add auto-close for FAQ issues when PR is merged
frederick-douglas-pearce d9e1e82
docs: Document auto-close behavior for FAQ issues when PRs merge
frederick-douglas-pearce 56a99c3
refactor: Remove manual workflow trigger to simplify FAQ automation
frederick-douglas-pearce File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| name: FAQ Proposal | ||
| description: Propose a new FAQ entry or update to existing FAQ | ||
| title: "[FAQ] " | ||
| labels: ["faq-proposal"] | ||
| body: | ||
| - type: markdown | ||
| attributes: | ||
| value: | | ||
| ## FAQ Proposal | ||
|
|
||
| Thank you for helping improve our FAQ! Please provide a clear question and answer below. | ||
|
|
||
| Our FAQ bot will automatically analyze your proposal and determine if it should: | ||
| - Create a new FAQ entry | ||
| - Update an existing FAQ entry | ||
| - Mark as duplicate of an existing FAQ | ||
|
|
||
| - type: input | ||
| id: course | ||
| attributes: | ||
| label: Course | ||
| description: Which course is this FAQ for? | ||
| placeholder: "machine-learning-zoomcamp" | ||
| validations: | ||
| required: true | ||
|
|
||
| - type: textarea | ||
| id: question | ||
| attributes: | ||
| label: Question | ||
| description: What is the FAQ question? | ||
| placeholder: "How do I install the required dependencies?" | ||
| validations: | ||
| required: true | ||
|
|
||
| - type: textarea | ||
| id: answer | ||
| attributes: | ||
| label: Answer | ||
| description: What is the answer to this question? | ||
| placeholder: | | ||
| To install the required dependencies, run: | ||
| ```bash | ||
| uv pip install -r requirements.txt | ||
| ``` | ||
| validations: | ||
| required: true | ||
|
|
||
| - type: checkboxes | ||
| id: checklist | ||
| attributes: | ||
| label: Checklist | ||
| description: Please confirm the following | ||
| options: | ||
| - label: I have searched existing FAQs and this question is not already answered | ||
| required: true | ||
| - label: The answer provides accurate, helpful information | ||
| required: true | ||
| - label: I have included any relevant code examples or links | ||
| required: false |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,214 @@ | ||
| name: FAQ Automation | ||
|
|
||
| on: | ||
| issues: | ||
| types: [opened] | ||
|
|
||
| permissions: | ||
| contents: write | ||
| issues: write | ||
| pull-requests: write | ||
|
|
||
| jobs: | ||
| process-faq-proposal: | ||
| if: contains(github.event.issue.labels.*.name, 'faq-proposal') | ||
| runs-on: ubuntu-latest | ||
|
|
||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v4 | ||
|
|
||
| - name: Set up Python | ||
| uses: actions/setup-python@v5 | ||
| with: | ||
| python-version: '3.13' | ||
|
|
||
| - name: Install uv | ||
| uses: astral-sh/setup-uv@v4 | ||
|
|
||
| - name: Install dependencies | ||
| run: | | ||
| uv pip install --system -e . | ||
|
|
||
| - name: Extract issue fields | ||
| id: extract | ||
| uses: actions/github-script@v7 | ||
| with: | ||
| script: | | ||
| const issue = context.payload.issue; | ||
| const body = issue.body || ''; | ||
|
|
||
| // Parse the structured issue body | ||
| const lines = body.split('\n'); | ||
| let question = ''; | ||
| let answer = ''; | ||
| let course = 'machine-learning-zoomcamp'; // default | ||
| let currentSection = null; | ||
|
|
||
| for (let i = 0; i < lines.length; i++) { | ||
| const line = lines[i].trim(); | ||
|
|
||
| // Look for course field | ||
| if (line.includes('### Course')) { | ||
| // Next non-empty line has the course | ||
| for (let j = i + 1; j < lines.length; j++) { | ||
| const courseLine = lines[j].trim(); | ||
| if (courseLine && !courseLine.startsWith('#')) { | ||
| course = courseLine; | ||
| break; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Look for question field | ||
| if (line.includes('### Question')) { | ||
| currentSection = 'question'; | ||
| question = ''; | ||
| continue; | ||
| } | ||
|
|
||
| // Look for answer field | ||
| if (line.includes('### Answer')) { | ||
| currentSection = 'answer'; | ||
| answer = ''; | ||
| continue; | ||
| } | ||
|
|
||
| // Look for checklist (stop collecting) | ||
| if (line.includes('### Checklist')) { | ||
| currentSection = null; | ||
| continue; | ||
| } | ||
|
|
||
| // Collect content | ||
| if (currentSection === 'question' && line && !line.startsWith('#')) { | ||
| question += (question ? '\n' : '') + line; | ||
| } else if (currentSection === 'answer' && line && !line.startsWith('#')) { | ||
| answer += (answer ? '\n' : '') + line; | ||
| } | ||
| } | ||
|
|
||
| core.setOutput('course', course); | ||
| core.setOutput('question', question.trim()); | ||
| core.setOutput('answer', answer.trim()); | ||
|
|
||
| console.log('Extracted course:', course); | ||
| console.log('Extracted question:', question.substring(0, 100)); | ||
| console.log('Extracted answer:', answer.substring(0, 100)); | ||
|
|
||
| - name: Process FAQ with AI | ||
| id: process | ||
| env: | ||
| OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} | ||
| run: | | ||
| # Create issue body file for CLI | ||
| cat > /tmp/issue_body.txt << 'EOF' | ||
| ### Question | ||
| ${{ steps.extract.outputs.question }} | ||
|
|
||
| ### Answer | ||
| ${{ steps.extract.outputs.answer }} | ||
| EOF | ||
|
|
||
| # Run FAQ automation CLI | ||
| python -m faq_automation.cli \ | ||
| --issue-body "$(cat /tmp/issue_body.txt)" \ | ||
| --issue-number ${{ github.event.issue.number }} \ | ||
| --course "${{ steps.extract.outputs.course }}" \ | ||
| --model "gpt-4" \ | ||
| --output-dir /tmp | ||
|
|
||
| # Read the output | ||
| if [ -f /tmp/faq_decision.json ]; then | ||
| echo "decision=$(cat /tmp/faq_decision.json | jq -c .)" >> $GITHUB_OUTPUT | ||
| else | ||
| echo "Error: FAQ decision file not found" | ||
| exit 1 | ||
| fi | ||
|
|
||
| - name: Handle NEW or UPDATE action | ||
| if: fromJson(steps.process.outputs.decision).action != 'DUPLICATE' | ||
| uses: actions/github-script@v7 | ||
| with: | ||
| script: | | ||
| const decision = ${{ steps.process.outputs.decision }}; | ||
| const action = decision.action; | ||
| const issueNumber = decision.issue_number; | ||
| const prBody = decision.pr_body; | ||
| const filePath = decision.file_path; | ||
|
|
||
| // Create branch name | ||
| const branchName = `faq-bot/issue-${issueNumber}`; | ||
|
|
||
| // Configure git | ||
| await exec.exec('git', ['config', 'user.name', 'FAQ Bot']); | ||
| await exec.exec('git', ['config', 'user.email', '[email protected]']); | ||
|
|
||
| // Create and switch to new branch | ||
| await exec.exec('git', ['checkout', '-b', branchName]); | ||
|
|
||
| // Add modified files | ||
| await exec.exec('git', ['add', filePath]); | ||
|
|
||
| // Commit changes | ||
| const commitMsg = `${action}: ${decision.decision.question.substring(0, 72)}`; | ||
| await exec.exec('git', ['commit', '-m', commitMsg]); | ||
|
|
||
| // Push branch | ||
| await exec.exec('git', ['push', 'origin', branchName]); | ||
|
|
||
| // Create pull request | ||
| const { data: pr } = await github.rest.pulls.create({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| title: `[FAQ Bot] ${action}: ${decision.decision.question.substring(0, 72)}`, | ||
| head: branchName, | ||
| base: 'main', | ||
| body: prBody | ||
| }); | ||
|
|
||
| // Comment on issue with PR link | ||
| await github.rest.issues.createComment({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| issue_number: issueNumber, | ||
| body: `β FAQ ${action} proposal created in PR #${pr.number}\n\nPlease review and approve the changes.` | ||
| }); | ||
|
|
||
| - name: Handle DUPLICATE action | ||
| if: fromJson(steps.process.outputs.decision).action == 'DUPLICATE' | ||
| uses: actions/github-script@v7 | ||
| with: | ||
frederick-douglas-pearce marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| script: | | ||
| const decision = ${{ steps.process.outputs.decision }}; | ||
| const comment = decision.comment; | ||
| const issueNumber = decision.issue_number; | ||
|
|
||
| // Post comment | ||
| await github.rest.issues.createComment({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| issue_number: issueNumber, | ||
| body: comment | ||
| }); | ||
|
|
||
| // Close issue | ||
| await github.rest.issues.update({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| issue_number: issueNumber, | ||
| state: 'closed', | ||
| state_reason: 'completed' | ||
| }); | ||
|
|
||
| - name: Handle errors | ||
| if: failure() | ||
| uses: actions/github-script@v7 | ||
| with: | ||
| script: | | ||
| await github.rest.issues.createComment({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| issue_number: context.issue.number, | ||
| body: `β FAQ Bot encountered an error processing this proposal.\n\nPlease check the [workflow run](${context.payload.repository.html_url}/actions/runs/${context.runId}) for details.\n\nA maintainer will review this manually.` | ||
| }); | ||
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,4 +2,5 @@ | |
| _site | ||
| __pycache__ | ||
| .envrc | ||
| .ipynb_checkpoints/ | ||
| .ipynb_checkpoints/ | ||
| CLAUDE.md | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.