Skip to content

Commit de0886b

Browse files
committed
Updated classroom.yml
(cherry picked from commit f03f8e4)
1 parent 108b47c commit de0886b

File tree

1 file changed

+168
-0
lines changed

1 file changed

+168
-0
lines changed

.github/workflows/classroom.yml

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,174 @@ permissions:
1010
pull-requests: write
1111

1212
jobs:
13+
ai_feedback:
14+
name: AI-Powered Feedback
15+
runs-on: ubuntu-latest
16+
env:
17+
OPENROUTER_MODEL: ${{ vars.OPENROUTER_MODEL }}
18+
SYSTEM_PROMPT: ${{ vars.SYSTEM_PROMPT }}
19+
steps:
20+
- name: Checkout repository
21+
uses: actions/checkout@v5
22+
23+
- name: Read assignment instructions
24+
id: instructions
25+
run: |
26+
# Reads the content of the README.md file into an output variable.
27+
# The `EOF` marker is used to handle multi-line file content.
28+
echo "instructions=$(cat README.md | sed 's/\"/\\\"/g' | sed 's/$/\\n/' | tr -d '\n' | sed 's/\\n/\\\\n/g')" >> $GITHUB_OUTPUT
29+
30+
- name: Read source code
31+
id: source_code
32+
run: |
33+
{
34+
echo 'source_code<<EOF'
35+
find src/main/java -type f -name "*.java" | while read -r file; do
36+
echo "=== File: $file ==="
37+
cat "$file"
38+
echo
39+
done
40+
echo 'EOF'
41+
} >> "$GITHUB_OUTPUT"
42+
43+
- name: Read test code
44+
id: test_code
45+
run: |
46+
{
47+
echo 'test_code<<EOF'
48+
if [ -d "src/test/java" ]; then
49+
find src/test/java -type f -name "*.java" | while read -r file; do
50+
echo "=== File: $file ==="
51+
cat "$file"
52+
echo
53+
done
54+
else
55+
echo "No test code found."
56+
fi
57+
echo 'EOF'
58+
} >> "$GITHUB_OUTPUT"
59+
- name: Generate AI Feedback
60+
id: ai_feedback
61+
run: |
62+
# This step sends the collected data to the OpenRouter API.
63+
INSTRUCTIONS=$(jq -Rs . <<'EOF'
64+
${{ steps.instructions.outputs.instructions }}
65+
EOF
66+
)
67+
SOURCE_CODE=$(jq -Rs . <<'EOF'
68+
${{ steps.source_code.outputs.source_code }}
69+
EOF
70+
)
71+
TEST_CODE=$(jq -Rs . <<'EOF'
72+
${{ steps.test_code.outputs.test_code }}
73+
EOF
74+
)
75+
76+
if [ -z "$INSTRUCTIONS" ] || [ -z "$SOURCE_CODE" ] || [ -z "$TEST_CODE" ]; then
77+
echo "Error: One or more required variables are not set."
78+
exit 1
79+
fi
80+
81+
# Assigning to USER_CONTENT with variable expansion
82+
PAYLOAD="Please provide feedback on the following Java assignment.
83+
84+
--- Assignment Instructions ---
85+
${INSTRUCTIONS}
86+
87+
--- Source files ---
88+
${SOURCE_CODE}
89+
90+
--- Test files ---
91+
${TEST_CODE}"
92+
93+
JSON_CONTENT=$(jq -n \
94+
--argjson model "$OPENROUTER_MODEL" \
95+
--arg system_prompt "$SYSTEM_PROMPT" \
96+
--arg payload "$PAYLOAD" \
97+
'{
98+
models: $model,
99+
messages: [
100+
{role: "system", content: $system_prompt},
101+
{role: "user", content: $payload}
102+
]
103+
}')
104+
105+
echo "$JSON_CONTENT"
106+
107+
API_RESPONSE=$(echo "$JSON_CONTENT" | curl https://openrouter.ai/api/v1/chat/completions \
108+
-H "Authorization: Bearer ${{ secrets.OPENROUTER_API_KEY }}" \
109+
-H "Content-Type: application/json" \
110+
-d @-)
111+
112+
echo "$API_RESPONSE"
113+
114+
FEEDBACK_CONTENT=$(echo "$API_RESPONSE" | jq -r '.choices[0].message.content')
115+
echo "feedback<<EOF" >> $GITHUB_OUTPUT
116+
echo "$FEEDBACK_CONTENT" >> $GITHUB_OUTPUT
117+
echo "EOF" >> $GITHUB_OUTPUT
118+
- name: Post Feedback as PR Comment ✍️
119+
uses: actions/github-script@v7
120+
env:
121+
FEEDBACK_BODY: ${{ steps.ai_feedback.outputs.feedback }}
122+
with:
123+
github-token: ${{ secrets.GITHUB_TOKEN }}
124+
script: |
125+
const { owner, repo } = context.repo;
126+
const targetTitle = "Feedback";
127+
const signature = "🤖 AI Feedback";
128+
129+
const { data: pullRequests } = await github.rest.pulls.list({
130+
owner,
131+
repo,
132+
state: "open",
133+
per_page: 100
134+
});
135+
136+
const matchingPR = pullRequests.find(pr => pr.title.trim().toLowerCase() === targetTitle.toLowerCase());
137+
if (!matchingPR) {
138+
throw new Error(`No open pull request found with title '${targetTitle}'`);
139+
}
140+
141+
const prNumber = matchingPR.number;
142+
143+
const { data: comments } = await github.rest.issues.listComments({
144+
owner,
145+
repo,
146+
issue_number: prNumber,
147+
per_page: 100
148+
});
149+
150+
const existing = comments.find(c =>
151+
c.user?.login === "github-actions[bot]" &&
152+
c.body?.includes(signature)
153+
);
154+
155+
const timestamp = new Date().toISOString();
156+
const newEntry = `🕒 _Posted on ${timestamp}_\n\n${process.env.FEEDBACK_BODY}\n\n---\n`;
157+
158+
if (existing) {
159+
// Extract previous entries and wrap them in a collapsible block
160+
const previousContent = existing.body.replace(/^### 🤖 AI Feedback\s*/, '').trim();
161+
const collapsed = `<details><summary>Previous Feedback</summary>\n\n${previousContent}\n</details>`;
162+
163+
const updatedBody = `### ${signature}\n\n${newEntry}${collapsed}`;
164+
await github.rest.issues.updateComment({
165+
owner,
166+
repo,
167+
comment_id: existing.id,
168+
body: updatedBody
169+
});
170+
console.log(`🔄 Updated existing comment on PR #${prNumber}`);
171+
} else {
172+
const body = `### ${signature}\n\n${newEntry}`;
173+
await github.rest.issues.createComment({
174+
owner,
175+
repo,
176+
issue_number: prNumber,
177+
body
178+
});
179+
console.log(`🆕 Posted new comment on PR #${prNumber}`);
180+
}
13181
run-autograding-tests:
14182
name: AI-Powered Feedback and Autograding
15183
runs-on: ubuntu-latest

0 commit comments

Comments
 (0)