From 0ebef87bfab25575c728fc0beb66644b89be38f7 Mon Sep 17 00:00:00 2001 From: LearningCircuit <185559241+LearningCircuit@users.noreply.github.com> Date: Sun, 9 Nov 2025 08:24:05 +0100 Subject: [PATCH 01/27] moonshotai/kimi-k2-thinking --- .github/workflows/ai-code-reviewer.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ai-code-reviewer.yml b/.github/workflows/ai-code-reviewer.yml index 2a9d68f..76a8f9e 100644 --- a/.github/workflows/ai-code-reviewer.yml +++ b/.github/workflows/ai-code-reviewer.yml @@ -34,7 +34,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} - AI_MODEL: ${{ vars.AI_MODEL || 'z-ai/glm-4.6' }} + AI_MODEL: ${{ vars.AI_MODEL || 'moonshotai/kimi-k2-thinking' }} AI_TEMPERATURE: ${{ vars.AI_TEMPERATURE || '0.1' }} AI_MAX_TOKENS: ${{ vars.AI_MAX_TOKENS || '2000' }} MAX_DIFF_SIZE: ${{ vars.MAX_DIFF_SIZE || '800000' }} From bce90c5cf2109dcb6f28d5719c9a08cd1cab4583 Mon Sep 17 00:00:00 2001 From: LearningCircuit <185559241+LearningCircuit@users.noreply.github.com> Date: Sun, 9 Nov 2025 08:28:25 +0100 Subject: [PATCH 02/27] Update ai-reviewer.sh --- ai-reviewer.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ai-reviewer.sh b/ai-reviewer.sh index 765d664..d55f5be 100644 --- a/ai-reviewer.sh +++ b/ai-reviewer.sh @@ -17,7 +17,7 @@ if [ -z "$API_KEY" ]; then fi # Configuration with defaults -AI_MODEL="${AI_MODEL:-z-ai/glm-4.6}" +AI_MODEL="${AI_MODEL:-moonshotai/kimi-k2-thinking}" AI_TEMPERATURE="${AI_TEMPERATURE:-0.1}" AI_MAX_TOKENS="${AI_MAX_TOKENS:-2000}" MAX_DIFF_SIZE="${MAX_DIFF_SIZE:-800000}" # 800KB default limit (~200K tokens, matching model context size) From 247071c679cc20b8fb5bedca445575daa435bace Mon Sep 17 00:00:00 2001 From: LearningCircuit <185559241+LearningCircuit@users.noreply.github.com> Date: Sun, 9 Nov 2025 09:07:28 +0100 Subject: [PATCH 03/27] feat: add comprehensive debug logging for thinking model responses - Add debug logging in workflow to capture raw AI response on JSON parsing failures - Add content extraction attempts from thinking model responses - Add detailed API response logging in ai-reviewer.sh script - Add thinking format detection and JSON extraction attempts - Include content preview and length logging for debugging - Enhance error messages with debugging information This will help identify the exact format of kimi-k2-thinking model responses and determine if we can extract usable JSON from the thinking format. --- .github/workflows/ai-code-reviewer.yml | 20 +++++++++++++- ai-reviewer.sh | 37 ++++++++++++++++++++++++++ test_diff.txt | 26 ++++++++++++++++++ 3 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 test_diff.txt diff --git a/.github/workflows/ai-code-reviewer.yml b/.github/workflows/ai-code-reviewer.yml index 76a8f9e..a93e2b8 100644 --- a/.github/workflows/ai-code-reviewer.yml +++ b/.github/workflows/ai-code-reviewer.yml @@ -63,8 +63,26 @@ jobs: # Parse JSON response if ! echo "$AI_RESPONSE" | jq . >/dev/null 2>&1; then echo "⚠️ AI response is not valid JSON. Cannot process review." + + # Log raw response for debugging (redact sensitive info) + echo "=== DEBUG: Raw AI Response (first 2000 chars) ===" + echo "$AI_RESPONSE" | head -c 2000 + echo "" + echo "=== END DEBUG ===" + + # Try to extract JSON from thinking model response + if echo "$AI_RESPONSE" | grep -q '"content"'; then + echo "=== DEBUG: Attempting to extract content from response ===" + EXTRACTED_CONTENT=$(echo "$AI_RESPONSE" | jq -r '.choices[0].message.content // empty' 2>/dev/null || echo "") + if [ -n "$EXTRACTED_CONTENT" ]; then + echo "Extracted content (first 1000 chars):" + echo "$EXTRACTED_CONTENT" | head -c 1000 + echo "" + fi + fi + # Don't post raw response as it may contain sensitive data - echo "AI response could not be processed. Please check the workflow logs." | gh pr comment ${{ github.event.pull_request.number }} --repo ${{ github.repository }} -F - + echo "AI response could not be processed. Please check the workflow logs for debugging information." | gh pr comment ${{ github.event.pull_request.number }} --repo ${{ github.repository }} -F - exit 1 fi diff --git a/ai-reviewer.sh b/ai-reviewer.sh index d55f5be..6dbb3d3 100644 --- a/ai-reviewer.sh +++ b/ai-reviewer.sh @@ -203,6 +203,9 @@ fi # Check if response is valid JSON if ! echo "$RESPONSE" | jq . >/dev/null 2>&1; then + echo "=== API DEBUG: Raw response from $AI_MODEL ===" >&2 + echo "$RESPONSE" >&2 + echo "=== END API DEBUG ===" >&2 echo '{"review":"## 🤖 AI Code Review\n\n❌ **Error**: Invalid JSON response from API","fail_pass_workflow":"uncertain","labels_added":[]}' exit 1 fi @@ -210,6 +213,14 @@ fi # Extract the content CONTENT=$(echo "$RESPONSE" | jq -r '.choices[0].message.content // "error"') +# Debug: Log the extracted content from thinking model +echo "=== CONTENT DEBUG: Extracted from $AI_MODEL ===" >&2 +echo "Content length: $(echo "$CONTENT" | wc -c)" >&2 +echo "Content preview (first 500 chars):" >&2 +echo "$CONTENT" | head -c 500 >&2 +echo "" >&2 +echo "=== END CONTENT DEBUG ===" >&2 + if [ "$CONTENT" = "error" ]; then # Try to extract error details from the API response ERROR_MSG=$(echo "$RESPONSE" | jq -r '.error.message // "Invalid API response format"') @@ -238,17 +249,43 @@ if [ -z "$CONTENT" ]; then exit 0 fi +# Debug: Check if content looks like thinking format +if echo "$CONTENT" | grep -q "thinking\|\|"; then + echo "=== THINKING FORMAT DETECTED ===" >&2 + echo "Content appears to contain thinking/reasoning format" >&2 + echo "Attempting to extract JSON from thinking response..." >&2 + echo "===================================" >&2 +fi + # Validate that CONTENT is valid JSON if ! echo "$CONTENT" | jq . >/dev/null 2>&1; then + echo "=== JSON VALIDATION FAILED ===" >&2 + echo "Content is not valid JSON" >&2 + + # Try to extract JSON from thinking response + if echo "$CONTENT" | grep -q '{.*}'; then + echo "Attempting to extract JSON from thinking content..." >&2 + # Look for JSON-like patterns in the content + EXTRACTED_JSON=$(echo "$CONTENT" | grep -o '{[^{}]*"review"[^{}]*}' | head -1) + if [ -n "$EXTRACTED_JSON" ] && echo "$EXTRACTED_JSON" | jq . >/dev/null 2>&1; then + echo "Successfully extracted JSON from thinking response!" >&2 + echo "$EXTRACTED_JSON" + exit 0 + fi + fi + # If not JSON, wrap it in JSON structure JSON_CONTENT="{\"review\":\"## 🤖 AI Code Review\n\n$CONTENT\n\n---\n*Review by [FAIR](https://github.com/LearningCircuit/Friendly-AI-Reviewer) - needs human verification*\",\"fail_pass_workflow\":\"uncertain\",\"labels_added\":[]}" echo "$JSON_CONTENT" else + echo "=== CONTENT IS VALID JSON ===" >&2 # If already JSON, validate it has the required structure if ! echo "$CONTENT" | jq -e '.review' >/dev/null 2>&1; then + echo "JSON missing required 'review' field" >&2 JSON_CONTENT="{\"review\":\"## 🤖 AI Code Review\n\n$CONTENT\n\n---\n*Review by [FAIR](https://github.com/LearningCircuit/Friendly-AI-Reviewer) - needs human verification*\",\"fail_pass_workflow\":\"uncertain\",\"labels_added\":[]}" echo "$JSON_CONTENT" else + echo "JSON has required structure, returning as-is" >&2 # If already valid JSON with required structure, return as-is echo "$CONTENT" fi diff --git a/test_diff.txt b/test_diff.txt new file mode 100644 index 0000000..f7c9329 --- /dev/null +++ b/test_diff.txt @@ -0,0 +1,26 @@ +diff --git a/.github/workflows/ai-code-reviewer.yml b/.github/workflows/ai-code-reviewer.yml +index 2a9d68f..76a8f9e 100644 +--- a/.github/workflows/ai-code-reviewer.yml ++++ b/.github/workflows/ai-code-reviewer.yml +@@ -34,7 +34,7 @@ jobs: + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} +- AI_MODEL: ${{ vars.AI_MODEL || 'z-ai/glm-4.6' }} ++ AI_MODEL: ${{ vars.AI_MODEL || 'moonshotai/kimi-k2-thinking' }} + AI_TEMPERATURE: ${{ vars.AI_TEMPERATURE || '0.1' }} + AI_MAX_TOKENS: ${{ vars.AI_MAX_TOKENS || '2000' }} + MAX_DIFF_SIZE: ${{ vars.MAX_DIFF_SIZE || '800000' }} +diff --git a/ai-reviewer.sh b/ai-reviewer.sh +index 765d664..d55f5be 100644 +--- a/ai-reviewer.sh ++++ b/ai-reviewer.sh +@@ -17,7 +17,7 @@ if [ -z "$API_KEY" ]; then + fi + + # Configuration with defaults +-AI_MODEL="${AI_MODEL:-z-ai/glm-4.6}" ++AI_MODEL="${AI_MODEL:-moonshotai/kimi-k2-thinking}" + AI_TEMPERATURE="${AI_TEMPERATURE:-0.1}" + AI_MAX_TOKENS="${AI_MAX_TOKENS:-2000}" + MAX_DIFF_SIZE="${MAX_DIFF_SIZE:-800000}" # 800KB default limit (~200K tokens, matching model context size) From 1b7a5e987ca0de36348a9bc3872b9b1610311cd0 Mon Sep 17 00:00:00 2001 From: LearningCircuit <185559241+LearningCircuit@users.noreply.github.com> Date: Sun, 9 Nov 2025 09:17:23 +0100 Subject: [PATCH 04/27] fix: parse JSON from thinking model responses and add heart icon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add logic to extract JSON from markdown code blocks in thinking model responses - Remove ```json wrapper and clean up thinking content - Replace robot icon (🤖) with heart icon (❤️) throughout codebase - Add repository link (https://github.com/LearningCircuit/Friendly-AI-Reviewer) - Update footer to "made with ❤️" for friendlier tone - Enhance JSON parsing to handle thinking model format properly This should fix the kimi-k2-thinking model compatibility issues while making the AI reviewer more friendly and approachable. --- .github/workflows/ai-code-reviewer.yml | 2 +- ai-reviewer.sh | 64 ++++++++++++++++++++------ 2 files changed, 50 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ai-code-reviewer.yml b/.github/workflows/ai-code-reviewer.yml index a93e2b8..a287d85 100644 --- a/.github/workflows/ai-code-reviewer.yml +++ b/.github/workflows/ai-code-reviewer.yml @@ -6,7 +6,7 @@ on: jobs: comprehensive-review: - name: 🤖 AI Code Review + name: ❤️ AI Code Review runs-on: ubuntu-latest if: github.event.action == 'labeled' && github.event.label.name == 'ai_code_review' permissions: diff --git a/ai-reviewer.sh b/ai-reviewer.sh index 6dbb3d3..b9d849c 100644 --- a/ai-reviewer.sh +++ b/ai-reviewer.sh @@ -10,7 +10,7 @@ set -e API_KEY="${OPENROUTER_API_KEY}" if [ -z "$API_KEY" ]; then - echo "## 🤖 AI Code Review + echo "## ❤️ AI Code Review ❌ **Error**: Missing OPENROUTER_API_KEY environment variable" exit 1 @@ -27,7 +27,7 @@ EXCLUDE_FILE_PATTERNS="${EXCLUDE_FILE_PATTERNS:-*.lock,*.min.js,*.min.css,packag DIFF_CONTENT=$(cat) if [ -z "$DIFF_CONTENT" ]; then - echo "## 🤖 AI Code Review + echo "## ❤️ AI Code Review ❌ **Error**: No diff content to analyze" exit 1 @@ -46,7 +46,7 @@ fi # Validate diff size to prevent excessive API usage DIFF_SIZE=${#DIFF_CONTENT} if [ "$DIFF_SIZE" -gt "$MAX_DIFF_SIZE" ]; then - echo "## 🤖 AI Code Review + echo "## ❤️ AI Code Review ❌ **Error**: Diff is too large ($DIFF_SIZE bytes, max: $MAX_DIFF_SIZE bytes) Please split this PR into smaller changes for review." @@ -56,9 +56,9 @@ fi # Fetch previous AI review comments for context (if PR_NUMBER and REPO_FULL_NAME are set) PREVIOUS_REVIEWS="" if [ -n "$PR_NUMBER" ] && [ -n "$REPO_FULL_NAME" ] && [ -n "$GITHUB_TOKEN" ]; then - # Fetch comments that start with "## 🤖 AI Code Review" + # Fetch comments that start with "## ❤️ AI Code Review" PREVIOUS_REVIEWS=$(gh api "repos/$REPO_FULL_NAME/issues/$PR_NUMBER/comments" \ - --jq '.[] | select(.body | startswith("## 🤖 AI Code Review")) | "### Previous Review (" + .created_at + "):\n" + .body + "\n---\n"' 2>/dev/null | head -c 50000 || echo "") + --jq '.[] | select(.body | startswith("## ❤️ AI Code Review")) | "### Previous Review (" + .created_at + "):\n" + .body + "\n---\n"' 2>/dev/null | head -c 50000 || echo "") fi # Fetch GitHub Actions check runs status (if PR_NUMBER and REPO_FULL_NAME are set) @@ -197,7 +197,7 @@ RESPONSE=$(curl -s -X POST "https://openrouter.ai/api/v1/chat/completions" \ # Check if API call was successful if [ -z "$RESPONSE" ]; then - echo '{"review":"## 🤖 AI Code Review\n\n❌ **Error**: API call failed - no response received","fail_pass_workflow":"uncertain","labels_added":[]}' + echo '{"review":"## ❤️ AI Code Review\n\n❌ **Error**: API call failed - no response received","fail_pass_workflow":"uncertain","labels_added":[]}' exit 1 fi @@ -206,7 +206,7 @@ if ! echo "$RESPONSE" | jq . >/dev/null 2>&1; then echo "=== API DEBUG: Raw response from $AI_MODEL ===" >&2 echo "$RESPONSE" >&2 echo "=== END API DEBUG ===" >&2 - echo '{"review":"## 🤖 AI Code Review\n\n❌ **Error**: Invalid JSON response from API","fail_pass_workflow":"uncertain","labels_added":[]}' + echo '{"review":"## ❤️ AI Code Review\n\n❌ **Error**: Invalid JSON response from API","fail_pass_workflow":"uncertain","labels_added":[]}' exit 1 fi @@ -227,7 +227,7 @@ if [ "$CONTENT" = "error" ]; then ERROR_CODE=$(echo "$RESPONSE" | jq -r '.error.code // ""') # Return error as JSON - ERROR_CONTENT="## 🤖 AI Code Review\n\n❌ **Error**: $ERROR_MSG" + ERROR_CONTENT="## ❤️ AI Code Review\n\n❌ **Error**: $ERROR_MSG" if [ -n "$ERROR_CODE" ]; then ERROR_CONTENT="$ERROR_CONTENT\n\nError code: \`$ERROR_CODE\`" fi @@ -245,16 +245,39 @@ fi # Ensure CONTENT is not empty if [ -z "$CONTENT" ]; then - echo '{"review":"## 🤖 AI Code Review\n\n❌ **Error**: AI returned empty response","fail_pass_workflow":"uncertain","labels_added":[]}' + echo '{"review":"## ❤️ AI Code Review\n\n❌ **Error**: AI returned empty response","fail_pass_workflow":"uncertain","labels_added":[]}' exit 0 fi # Debug: Check if content looks like thinking format if echo "$CONTENT" | grep -q "thinking\|\|"; then echo "=== THINKING FORMAT DETECTED ===" >&2 - echo "Content appears to contain thinking/reasoning format" >&2 + echo "Content appears to contain thinking/reasoning format with markdown" >&2 echo "Attempting to extract JSON from thinking response..." >&2 echo "===================================" >&2 + + # Try to extract JSON from thinking format with markdown code blocks + CLEANED_CONTENT="$CONTENT" + + # Remove markdown code blocks (```json ... ```) + if echo "$CLEANED_CONTENT" | grep -q '```json'; then + echo "Removing markdown code blocks..." >&2 + CLEANED_CONTENT=$(echo "$CLEANED_CONTENT" | sed '/^```json$/,/^```$/d' | tr -d '\n') + fi + + # Extract JSON object from content if it's wrapped in text + if echo "$CLEANED_CONTENT" | grep -q '{.*}'; then + echo "Extracting JSON object from content..." >&2 + EXTRACTED_JSON=$(echo "$CLEANED_CONTENT" | grep -o '{.*}' | head -1) + if [ -n "$EXTRACTED_JSON" ] && echo "$EXTRACTED_JSON" | jq . >/dev/null 2>&1; then + echo "Successfully extracted clean JSON from thinking response!" >&2 + # Update the review content to include heart icon and repo link + UPDATED_JSON=$(echo "$EXTRACTED_JSON" | jq --arg repo_link "https://github.com/LearningCircuit/Friendly-AI-Reviewer" ' + .review = "## ❤️ AI Code Review\n\n" + .review + "\n\n---\n*Review by [FAIR](" + $repo_link + ") - made with ❤️*"') + echo "$UPDATED_JSON" + exit 0 + fi + fi fi # Validate that CONTENT is valid JSON @@ -275,18 +298,29 @@ if ! echo "$CONTENT" | jq . >/dev/null 2>&1; then fi # If not JSON, wrap it in JSON structure - JSON_CONTENT="{\"review\":\"## 🤖 AI Code Review\n\n$CONTENT\n\n---\n*Review by [FAIR](https://github.com/LearningCircuit/Friendly-AI-Reviewer) - needs human verification*\",\"fail_pass_workflow\":\"uncertain\",\"labels_added\":[]}" + JSON_CONTENT="{\"review\":\"## ❤️ AI Code Review\n\n$CONTENT\n\n---\n*Review by [FAIR](https://github.com/LearningCircuit/Friendly-AI-Reviewer) - needs human verification*\",\"fail_pass_workflow\":\"uncertain\",\"labels_added\":[]}" echo "$JSON_CONTENT" else echo "=== CONTENT IS VALID JSON ===" >&2 # If already JSON, validate it has the required structure if ! echo "$CONTENT" | jq -e '.review' >/dev/null 2>&1; then echo "JSON missing required 'review' field" >&2 - JSON_CONTENT="{\"review\":\"## 🤖 AI Code Review\n\n$CONTENT\n\n---\n*Review by [FAIR](https://github.com/LearningCircuit/Friendly-AI-Reviewer) - needs human verification*\",\"fail_pass_workflow\":\"uncertain\",\"labels_added\":[]}" + JSON_CONTENT="{\"review\":\"## ❤️ AI Code Review\n\n$CONTENT\n\n---\n*Review by [FAIR](https://github.com/LearningCircuit/Friendly-AI-Reviewer) - needs human verification*\",\"fail_pass_workflow\":\"uncertain\",\"labels_added\":[]}" echo "$JSON_CONTENT" else - echo "JSON has required structure, returning as-is" >&2 - # If already valid JSON with required structure, return as-is - echo "$CONTENT" + echo "JSON has required structure, updating with heart icon and repo link" >&2 + # Update the existing JSON to include heart icon and repo link + UPDATED_JSON=$(echo "$CONTENT" | jq --arg repo_link "https://github.com/LearningCircuit/Friendly-AI-Reviewer" ' + if .review | startswith("## ❤️") and (.review | contains("Review by [FAIR]") | not) then + .review = .review + "\n\n---\n*Review by [FAIR](" + $repo_link + ") - made with ❤️*" + elif .review | startswith("## ") and (.review | contains("AI Code Review") | not) then + .review = "## ❤️ " + (.review | sub("^## "; "")) + "\n\n---\n*Review by [FAIR](" + $repo_link + ") - made with ❤️*" + elif .review | contains("Review by [FAIR]") | not then + .review = .review + "\n\n---\n*Review by [FAIR](" + $repo_link + ") - made with ❤️*" + else + . + end + ') + echo "$UPDATED_JSON" fi fi From 21f0815df4e946dceeea3888b0b8b81e96bae1f3 Mon Sep 17 00:00:00 2001 From: LearningCircuit <185559241+LearningCircuit@users.noreply.github.com> Date: Sun, 9 Nov 2025 09:34:09 +0100 Subject: [PATCH 05/27] fix: simplify thinking model response handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove complex JSON extraction logic - Allow model to think naturally with thinking tags - Strip thinking content with simple sed command - Clean up prompt instructions for better reliability 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- ai-reviewer.sh | 105 ++++++++++++++----------------------------------- 1 file changed, 30 insertions(+), 75 deletions(-) diff --git a/ai-reviewer.sh b/ai-reviewer.sh index b9d849c..90771f8 100644 --- a/ai-reviewer.sh +++ b/ai-reviewer.sh @@ -143,29 +143,22 @@ Code diff to analyze: # Read diff content DIFF_CONTENT=$(cat "$DIFF_FILE") -# Simple text prompt -PROMPT="Please analyze this code diff and provide a comprehensive review. +# Simple text prompt requesting JSON response +PROMPT="You are an expert code reviewer. Please analyze this code diff and provide a comprehensive review. Focus on security, performance, code quality, and best practices. -IMPORTANT: Respond with valid JSON only using this exact format: +Required JSON format: { - \"review\": \"Detailed review in markdown format\", + \"review\": \"## ❤️ AI Code Review\\n\\n[Your detailed review in markdown format]\\n\\n---\\n*Review by [FAIR](https://github.com/LearningCircuit/Friendly-AI-Reviewer) - made with ❤️*\", \"fail_pass_workflow\": \"pass\", \"labels_added\": [\"bug\", \"feature\", \"enhancement\"] } -For the labels_added field: -- First check if any existing repository labels (listed above) are appropriate -- Prefer existing labels over creating new ones when possible -- Only suggest new labels when no existing ones fit the changes -- Keep labels concise and descriptive - -Focus action items on critical fixes only, not trivial nitpicks. - -IMPORTANT: End your review with a clear final assessment section like: ---- -## Final Assessment: APPROVED / CHANGES REQUESTED / NEEDS REVISION +Instructions: +1. Respond with a single valid JSON object +2. Include the heart emoji and repo link in the review field +3. For labels_added, prefer existing repository labels when possible Code to review: $PROMPT_PREFIX @@ -210,6 +203,14 @@ if ! echo "$RESPONSE" | jq . >/dev/null 2>&1; then exit 1 fi +# Always log the API response structure for debugging thinking models +echo "=== API STRUCTURE DEBUG from $AI_MODEL ===" >&2 +echo "Response keys: $(echo "$RESPONSE" | jq -r 'keys | join(", ")')" >&2 +echo "Choices count: $(echo "$RESPONSE" | jq '.choices | length')" >&2 +echo "First choice keys: $(echo "$RESPONSE" | jq -r '.choices[0] | keys | join(", ")')" >&2 +echo "Content type: $(echo "$RESPONSE" | jq -r '.choices[0].message | type')" >&2 +echo "=== END API STRUCTURE DEBUG ===" >&2 + # Extract the content CONTENT=$(echo "$RESPONSE" | jq -r '.choices[0].message.content // "error"') @@ -249,78 +250,32 @@ if [ -z "$CONTENT" ]; then exit 0 fi -# Debug: Check if content looks like thinking format -if echo "$CONTENT" | grep -q "thinking\|\|"; then - echo "=== THINKING FORMAT DETECTED ===" >&2 - echo "Content appears to contain thinking/reasoning format with markdown" >&2 - echo "Attempting to extract JSON from thinking response..." >&2 - echo "===================================" >&2 - - # Try to extract JSON from thinking format with markdown code blocks - CLEANED_CONTENT="$CONTENT" - - # Remove markdown code blocks (```json ... ```) - if echo "$CLEANED_CONTENT" | grep -q '```json'; then - echo "Removing markdown code blocks..." >&2 - CLEANED_CONTENT=$(echo "$CLEANED_CONTENT" | sed '/^```json$/,/^```$/d' | tr -d '\n') - fi - - # Extract JSON object from content if it's wrapped in text - if echo "$CLEANED_CONTENT" | grep -q '{.*}'; then - echo "Extracting JSON object from content..." >&2 - EXTRACTED_JSON=$(echo "$CLEANED_CONTENT" | grep -o '{.*}' | head -1) - if [ -n "$EXTRACTED_JSON" ] && echo "$EXTRACTED_JSON" | jq . >/dev/null 2>&1; then - echo "Successfully extracted clean JSON from thinking response!" >&2 - # Update the review content to include heart icon and repo link - UPDATED_JSON=$(echo "$EXTRACTED_JSON" | jq --arg repo_link "https://github.com/LearningCircuit/Friendly-AI-Reviewer" ' - .review = "## ❤️ AI Code Review\n\n" + .review + "\n\n---\n*Review by [FAIR](" + $repo_link + ") - made with ❤️*"') - echo "$UPDATED_JSON" - exit 0 - fi - fi +# Remove thinking tags and content - everything between and +if echo "$CONTENT" | grep -q ""; then + echo "=== REMOVING THINKING CONTENT ===" >&2 + CONTENT=$(echo "$CONTENT" | sed '//,//d') fi - # Validate that CONTENT is valid JSON if ! echo "$CONTENT" | jq . >/dev/null 2>&1; then echo "=== JSON VALIDATION FAILED ===" >&2 echo "Content is not valid JSON" >&2 + echo "=== RAW CONTENT FOR DEBUG ===" >&2 + echo "$CONTENT" | head -c 500 >&2 + echo "" >&2 + echo "=== END DEBUG ===" >&2 - # Try to extract JSON from thinking response - if echo "$CONTENT" | grep -q '{.*}'; then - echo "Attempting to extract JSON from thinking content..." >&2 - # Look for JSON-like patterns in the content - EXTRACTED_JSON=$(echo "$CONTENT" | grep -o '{[^{}]*"review"[^{}]*}' | head -1) - if [ -n "$EXTRACTED_JSON" ] && echo "$EXTRACTED_JSON" | jq . >/dev/null 2>&1; then - echo "Successfully extracted JSON from thinking response!" >&2 - echo "$EXTRACTED_JSON" - exit 0 - fi - fi - - # If not JSON, wrap it in JSON structure - JSON_CONTENT="{\"review\":\"## ❤️ AI Code Review\n\n$CONTENT\n\n---\n*Review by [FAIR](https://github.com/LearningCircuit/Friendly-AI-Reviewer) - needs human verification*\",\"fail_pass_workflow\":\"uncertain\",\"labels_added\":[]}" + # Fallback to error response + JSON_CONTENT="{\"review\":\"## ❤️ AI Code Review\n\n❌ **Error**: Invalid JSON response from AI model\n\n---\n*Review by [FAIR](https://github.com/LearningCircuit/Friendly-AI-Reviewer) - needs human verification*\",\"fail_pass_workflow\":\"uncertain\",\"labels_added\":[]}" echo "$JSON_CONTENT" else echo "=== CONTENT IS VALID JSON ===" >&2 - # If already JSON, validate it has the required structure + # Validate it has the required structure if ! echo "$CONTENT" | jq -e '.review' >/dev/null 2>&1; then echo "JSON missing required 'review' field" >&2 - JSON_CONTENT="{\"review\":\"## ❤️ AI Code Review\n\n$CONTENT\n\n---\n*Review by [FAIR](https://github.com/LearningCircuit/Friendly-AI-Reviewer) - needs human verification*\",\"fail_pass_workflow\":\"uncertain\",\"labels_added\":[]}" + JSON_CONTENT="{\"review\":\"## ❤️ AI Code Review\n\n❌ **Error**: AI response missing required review field\n\n---\n*Review by [FAIR](https://github.com/LearningCircuit/Friendly-AI-Reviewer) - needs human verification*\",\"fail_pass_workflow\":\"uncertain\",\"labels_added\":[]}" echo "$JSON_CONTENT" else - echo "JSON has required structure, updating with heart icon and repo link" >&2 - # Update the existing JSON to include heart icon and repo link - UPDATED_JSON=$(echo "$CONTENT" | jq --arg repo_link "https://github.com/LearningCircuit/Friendly-AI-Reviewer" ' - if .review | startswith("## ❤️") and (.review | contains("Review by [FAIR]") | not) then - .review = .review + "\n\n---\n*Review by [FAIR](" + $repo_link + ") - made with ❤️*" - elif .review | startswith("## ") and (.review | contains("AI Code Review") | not) then - .review = "## ❤️ " + (.review | sub("^## "; "")) + "\n\n---\n*Review by [FAIR](" + $repo_link + ") - made with ❤️*" - elif .review | contains("Review by [FAIR]") | not then - .review = .review + "\n\n---\n*Review by [FAIR](" + $repo_link + ") - made with ❤️*" - else - . - end - ') - echo "$UPDATED_JSON" + echo "JSON has required structure, using as-is" >&2 + echo "$CONTENT" fi fi From 23c534c81eefd95e0281a4bdc9ec19efb13c0cdf Mon Sep 17 00:00:00 2001 From: LearningCircuit <185559241+LearningCircuit@users.noreply.github.com> Date: Sun, 9 Nov 2025 10:25:52 +0100 Subject: [PATCH 06/27] fix: escape forward slash in sed command - Fix sed syntax error when removing thinking tags - Properly escape end tag pattern --- ai-reviewer.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ai-reviewer.sh b/ai-reviewer.sh index 90771f8..5506278 100644 --- a/ai-reviewer.sh +++ b/ai-reviewer.sh @@ -253,7 +253,7 @@ fi # Remove thinking tags and content - everything between and if echo "$CONTENT" | grep -q ""; then echo "=== REMOVING THINKING CONTENT ===" >&2 - CONTENT=$(echo "$CONTENT" | sed '//,//d') + CONTENT=$(echo "$CONTENT" | sed '//,/<\/thinking>/d') fi # Validate that CONTENT is valid JSON if ! echo "$CONTENT" | jq . >/dev/null 2>&1; then From d0593dab47b630a9e78cace4168c9b9daf0bafea Mon Sep 17 00:00:00 2001 From: LearningCircuit <185559241+LearningCircuit@users.noreply.github.com> Date: Sun, 9 Nov 2025 10:31:46 +0100 Subject: [PATCH 07/27] debug: enable debug mode to see full AI response - Set DEBUG_MODE to true to capture ai_response.txt - This will help debug why model returns empty content --- .github/workflows/ai-code-reviewer.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ai-code-reviewer.yml b/.github/workflows/ai-code-reviewer.yml index a287d85..5dee265 100644 --- a/.github/workflows/ai-code-reviewer.yml +++ b/.github/workflows/ai-code-reviewer.yml @@ -42,7 +42,7 @@ jobs: PR_NUMBER: ${{ github.event.pull_request.number }} REPO_FULL_NAME: ${{ github.repository }} FAIL_ON_REQUESTED_CHANGES: ${{ vars.FAIL_ON_REQUESTED_CHANGES || 'false' }} - DEBUG_MODE: ${{ vars.DEBUG_MODE || 'false' }} + DEBUG_MODE: ${{ vars.DEBUG_MODE || 'true' }} run: | # Run the AI reviewer and save response to file echo "Running AI code review..." From c5688304606c778f6771bf8e6b3536891aea3e22 Mon Sep 17 00:00:00 2001 From: LearningCircuit <185559241+LearningCircuit@users.noreply.github.com> Date: Sun, 9 Nov 2025 10:42:13 +0100 Subject: [PATCH 08/27] fix: remove markdown code blocks from AI response - Add logic to strip json code blocks from model response - Model was returning JSON wrapped in markdown formatting - This should now properly extract clean JSON for processing --- ai-reviewer.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ai-reviewer.sh b/ai-reviewer.sh index 5506278..c0067fa 100644 --- a/ai-reviewer.sh +++ b/ai-reviewer.sh @@ -255,6 +255,12 @@ if echo "$CONTENT" | grep -q ""; then echo "=== REMOVING THINKING CONTENT ===" >&2 CONTENT=$(echo "$CONTENT" | sed '//,/<\/thinking>/d') fi + +# Remove markdown code blocks - everything between ```json and ``` +if echo "$CONTENT" | grep -q "```json"; then + echo "=== REMOVING MARKDOWN CODE BLOCKS ===" >&2 + CONTENT=$(echo "$CONTENT" | sed '/^```json$/,/^```$/d') +fi # Validate that CONTENT is valid JSON if ! echo "$CONTENT" | jq . >/dev/null 2>&1; then echo "=== JSON VALIDATION FAILED ===" >&2 From 13e7589cd96dd2d003fe4ed39ae88d68593fabae Mon Sep 17 00:00:00 2001 From: LearningCircuit <185559241+LearningCircuit@users.noreply.github.com> Date: Sun, 9 Nov 2025 10:55:37 +0100 Subject: [PATCH 09/27] debug: remove content limits to see full AI response - Remove 500 character limit from content debug output - Show full content in JSON validation failure debug - This will help identify the exact parsing issue --- ai-reviewer.sh | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/ai-reviewer.sh b/ai-reviewer.sh index c0067fa..a42c598 100644 --- a/ai-reviewer.sh +++ b/ai-reviewer.sh @@ -217,9 +217,8 @@ CONTENT=$(echo "$RESPONSE" | jq -r '.choices[0].message.content // "error"') # Debug: Log the extracted content from thinking model echo "=== CONTENT DEBUG: Extracted from $AI_MODEL ===" >&2 echo "Content length: $(echo "$CONTENT" | wc -c)" >&2 -echo "Content preview (first 500 chars):" >&2 -echo "$CONTENT" | head -c 500 >&2 -echo "" >&2 +echo "Full content:" >&2 +echo "$CONTENT" >&2 echo "=== END CONTENT DEBUG ===" >&2 if [ "$CONTENT" = "error" ]; then @@ -266,8 +265,7 @@ if ! echo "$CONTENT" | jq . >/dev/null 2>&1; then echo "=== JSON VALIDATION FAILED ===" >&2 echo "Content is not valid JSON" >&2 echo "=== RAW CONTENT FOR DEBUG ===" >&2 - echo "$CONTENT" | head -c 500 >&2 - echo "" >&2 + echo "$CONTENT" >&2 echo "=== END DEBUG ===" >&2 # Fallback to error response From 18175ee5e5610b47e3b21a9a7ca9eeb7f4d6b872 Mon Sep 17 00:00:00 2001 From: LearningCircuit <185559241+LearningCircuit@users.noreply.github.com> Date: Sun, 9 Nov 2025 11:30:11 +0100 Subject: [PATCH 10/27] fix: resolve bash syntax error and enable markdown code block removal - Use perl to remove markdown code blocks instead of sed to avoid backtick escaping issues - Remove 500 character limit from debug output to see full AI response - Change grep pattern to avoid backtick syntax errors - This should now properly extract JSON from code blocks --- ai-reviewer.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ai-reviewer.sh b/ai-reviewer.sh index a42c598..c1b1677 100644 --- a/ai-reviewer.sh +++ b/ai-reviewer.sh @@ -256,9 +256,10 @@ if echo "$CONTENT" | grep -q ""; then fi # Remove markdown code blocks - everything between ```json and ``` -if echo "$CONTENT" | grep -q "```json"; then +# Remove markdown code blocks if present +if echo "$CONTENT" | grep -q "json"; then echo "=== REMOVING MARKDOWN CODE BLOCKS ===" >&2 - CONTENT=$(echo "$CONTENT" | sed '/^```json$/,/^```$/d') + CONTENT=$(echo "$CONTENT" | perl -ne 'print unless /^```json$/ .. /^```$/') fi # Validate that CONTENT is valid JSON if ! echo "$CONTENT" | jq . >/dev/null 2>&1; then From 7b60b3142b58b29c77c08f659cc080e4d8c3ab9c Mon Sep 17 00:00:00 2001 From: LearningCircuit <185559241+LearningCircuit@users.noreply.github.com> Date: Sun, 9 Nov 2025 12:17:00 +0100 Subject: [PATCH 11/27] debug: output raw AI response to logs instead of file - Remove ai_response.txt file creation and cleanup - Output raw AI response directly to workflow logs in debug mode - Much simpler than using artifacts or files --- .github/workflows/ai-code-reviewer.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ai-code-reviewer.yml b/.github/workflows/ai-code-reviewer.yml index 5dee265..a30fe5e 100644 --- a/.github/workflows/ai-code-reviewer.yml +++ b/.github/workflows/ai-code-reviewer.yml @@ -48,10 +48,11 @@ jobs: echo "Running AI code review..." AI_RESPONSE=$(cat diff.txt | bash ai-reviewer.sh) - # Save response for debugging (only in debug mode) + # Output raw response for debugging (only in debug mode) if [ "$DEBUG_MODE" = "true" ]; then - echo "Debug: AI response saved to ai_response.txt" - echo "$AI_RESPONSE" > ai_response.txt + echo "=== RAW AI RESPONSE FOR DEBUG ===" + echo "$AI_RESPONSE" + echo "=== END RAW AI RESPONSE ===" fi # Check if AI_RESPONSE is empty @@ -133,7 +134,7 @@ jobs: echo "The review has been posted above. Please address the requested changes." exit 1 - - name: Cleanup + - name: Cleanup if: always() run: | rm -f diff.txt From 9b5bb8698f8bcd7042529ea1c5686dd8427d2445 Mon Sep 17 00:00:00 2001 From: LearningCircuit <185559241+LearningCircuit@users.noreply.github.com> Date: Sun, 9 Nov 2025 12:45:50 +0100 Subject: [PATCH 12/27] fix: properly handle thinking model responses with multiline regex Fixed three critical bugs causing "AI returned empty response" errors: 1. Replaced line-based sed deletion with proper Perl regex that handles both inline and multiline thinking tags without destroying content after the closing tag 2. Fixed markdown detection to only match actual code fence markers (^\s*```json) instead of any occurrence of the word "json" 3. Fixed markdown removal to extract content from code blocks instead of deleting everything between the markers 4. Added enhanced empty check that catches whitespace-only content 5. Added whitespace trimming to ensure clean JSON output This properly handles all response formats from thinking models: - Inline: ...{json} - Multiline with markdown: \n...\n\n```json\n{...}\n``` - Plain JSON (no markers) - Any combination of the above --- ai-reviewer.sh | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/ai-reviewer.sh b/ai-reviewer.sh index c1b1677..016f9a6 100644 --- a/ai-reviewer.sh +++ b/ai-reviewer.sh @@ -250,17 +250,25 @@ if [ -z "$CONTENT" ]; then fi # Remove thinking tags and content - everything between and -if echo "$CONTENT" | grep -q ""; then - echo "=== REMOVING THINKING CONTENT ===" >&2 - CONTENT=$(echo "$CONTENT" | sed '//,/<\/thinking>/d') -fi +# Use perl for proper multiline and inline handling +CONTENT=$(echo "$CONTENT" | perl -0pe 's/.*?<\/thinking>\s*//gs') -# Remove markdown code blocks - everything between ```json and ``` -# Remove markdown code blocks if present -if echo "$CONTENT" | grep -q "json"; then +# Remove markdown code blocks if present (check for actual backticks at line start) +if echo "$CONTENT" | grep -qE '^\s*```json'; then echo "=== REMOVING MARKDOWN CODE BLOCKS ===" >&2 - CONTENT=$(echo "$CONTENT" | perl -ne 'print unless /^```json$/ .. /^```$/') + # Remove the opening ```json and closing ``` lines, keep the content + CONTENT=$(echo "$CONTENT" | perl -0pe 's/^\s*```json\s*\n//g; s/\n```\s*$//g') +fi + +# Trim leading and trailing whitespace +CONTENT=$(echo "$CONTENT" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') + +# Enhanced empty check (catches whitespace-only content) +if [ -z "$CONTENT" ] || [ -z "$(echo "$CONTENT" | tr -d '[:space:]')" ]; then + echo '{"review":"## ❤️ AI Code Review\n\n❌ **Error**: AI returned empty response after processing","fail_pass_workflow":"uncertain","labels_added":[]}' + exit 0 fi + # Validate that CONTENT is valid JSON if ! echo "$CONTENT" | jq . >/dev/null 2>&1; then echo "=== JSON VALIDATION FAILED ===" >&2 From 195e886d141afbc25773da49ad13227b3aea9652 Mon Sep 17 00:00:00 2001 From: LearningCircuit <185559241+LearningCircuit@users.noreply.github.com> Date: Sun, 9 Nov 2025 12:47:51 +0100 Subject: [PATCH 13/27] fix: correct YAML indentation for Cleanup step --- .github/workflows/ai-code-reviewer.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ai-code-reviewer.yml b/.github/workflows/ai-code-reviewer.yml index a30fe5e..c442f96 100644 --- a/.github/workflows/ai-code-reviewer.yml +++ b/.github/workflows/ai-code-reviewer.yml @@ -134,7 +134,7 @@ jobs: echo "The review has been posted above. Please address the requested changes." exit 1 - - name: Cleanup + - name: Cleanup if: always() run: | rm -f diff.txt From e2f3d22285c437d202502baa20bd7b2f883e75ae Mon Sep 17 00:00:00 2001 From: LearningCircuit <185559241+LearningCircuit@users.noreply.github.com> Date: Sun, 9 Nov 2025 12:50:52 +0100 Subject: [PATCH 14/27] fix: increase AI_MAX_TOKENS to 8000 to prevent response truncation --- .github/workflows/ai-code-reviewer.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ai-code-reviewer.yml b/.github/workflows/ai-code-reviewer.yml index c442f96..2b7d5ce 100644 --- a/.github/workflows/ai-code-reviewer.yml +++ b/.github/workflows/ai-code-reviewer.yml @@ -36,7 +36,7 @@ jobs: OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} AI_MODEL: ${{ vars.AI_MODEL || 'moonshotai/kimi-k2-thinking' }} AI_TEMPERATURE: ${{ vars.AI_TEMPERATURE || '0.1' }} - AI_MAX_TOKENS: ${{ vars.AI_MAX_TOKENS || '2000' }} + AI_MAX_TOKENS: ${{ vars.AI_MAX_TOKENS || '8000' }} MAX_DIFF_SIZE: ${{ vars.MAX_DIFF_SIZE || '800000' }} EXCLUDE_FILE_PATTERNS: ${{ vars.EXCLUDE_FILE_PATTERNS || '*.lock,*.min.js,*.min.css,package-lock.json,yarn.lock' }} PR_NUMBER: ${{ github.event.pull_request.number }} From 156c4d34539fb756a6ed5a30fffe568b4e1a6265 Mon Sep 17 00:00:00 2001 From: LearningCircuit <185559241+LearningCircuit@users.noreply.github.com> Date: Sun, 9 Nov 2025 12:51:47 +0100 Subject: [PATCH 15/27] fix: remove max_tokens limit to prevent response truncation --- .github/workflows/ai-code-reviewer.yml | 1 - ai-reviewer.sh | 4 +--- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/ai-code-reviewer.yml b/.github/workflows/ai-code-reviewer.yml index 2b7d5ce..abb0cc0 100644 --- a/.github/workflows/ai-code-reviewer.yml +++ b/.github/workflows/ai-code-reviewer.yml @@ -36,7 +36,6 @@ jobs: OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} AI_MODEL: ${{ vars.AI_MODEL || 'moonshotai/kimi-k2-thinking' }} AI_TEMPERATURE: ${{ vars.AI_TEMPERATURE || '0.1' }} - AI_MAX_TOKENS: ${{ vars.AI_MAX_TOKENS || '8000' }} MAX_DIFF_SIZE: ${{ vars.MAX_DIFF_SIZE || '800000' }} EXCLUDE_FILE_PATTERNS: ${{ vars.EXCLUDE_FILE_PATTERNS || '*.lock,*.min.js,*.min.css,package-lock.json,yarn.lock' }} PR_NUMBER: ${{ github.event.pull_request.number }} diff --git a/ai-reviewer.sh b/ai-reviewer.sh index 016f9a6..9455c8b 100644 --- a/ai-reviewer.sh +++ b/ai-reviewer.sh @@ -19,7 +19,6 @@ fi # Configuration with defaults AI_MODEL="${AI_MODEL:-moonshotai/kimi-k2-thinking}" AI_TEMPERATURE="${AI_TEMPERATURE:-0.1}" -AI_MAX_TOKENS="${AI_MAX_TOKENS:-2000}" MAX_DIFF_SIZE="${MAX_DIFF_SIZE:-800000}" # 800KB default limit (~200K tokens, matching model context size) EXCLUDE_FILE_PATTERNS="${EXCLUDE_FILE_PATTERNS:-*.lock,*.min.js,*.min.css,package-lock.json,yarn.lock}" @@ -184,8 +183,7 @@ RESPONSE=$(curl -s -X POST "https://openrouter.ai/api/v1/chat/completions" \ \"content\": $(echo "$PROMPT" | jq -Rs .) } ], - \"temperature\": $AI_TEMPERATURE, - \"max_tokens\": $AI_MAX_TOKENS + \"temperature\": $AI_TEMPERATURE }") # Check if API call was successful From 940934a4868eb82decf90389661d56324c8dc5e6 Mon Sep 17 00:00:00 2001 From: LearningCircuit <185559241+LearningCircuit@users.noreply.github.com> Date: Sun, 9 Nov 2025 13:20:36 +0100 Subject: [PATCH 16/27] refactor: address AI review recommendations - Set DEBUG_MODE default back to 'false' for security - Add AI_MAX_TOKENS limit of 6000 to control costs and prevent truncation - Remove test_diff.txt file - Extract repeated strings (REVIEW_HEADER, REVIEW_FOOTER) into constants - Create generate_error_response() helper function to reduce code duplication - Replace all hardcoded error JSON responses with helper function calls --- .github/workflows/ai-code-reviewer.yml | 3 ++- ai-reviewer.sh | 29 +++++++++++++++++--------- test_diff.txt | 26 ----------------------- 3 files changed, 21 insertions(+), 37 deletions(-) delete mode 100644 test_diff.txt diff --git a/.github/workflows/ai-code-reviewer.yml b/.github/workflows/ai-code-reviewer.yml index abb0cc0..103e60b 100644 --- a/.github/workflows/ai-code-reviewer.yml +++ b/.github/workflows/ai-code-reviewer.yml @@ -36,12 +36,13 @@ jobs: OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} AI_MODEL: ${{ vars.AI_MODEL || 'moonshotai/kimi-k2-thinking' }} AI_TEMPERATURE: ${{ vars.AI_TEMPERATURE || '0.1' }} + AI_MAX_TOKENS: ${{ vars.AI_MAX_TOKENS || '6000' }} MAX_DIFF_SIZE: ${{ vars.MAX_DIFF_SIZE || '800000' }} EXCLUDE_FILE_PATTERNS: ${{ vars.EXCLUDE_FILE_PATTERNS || '*.lock,*.min.js,*.min.css,package-lock.json,yarn.lock' }} PR_NUMBER: ${{ github.event.pull_request.number }} REPO_FULL_NAME: ${{ github.repository }} FAIL_ON_REQUESTED_CHANGES: ${{ vars.FAIL_ON_REQUESTED_CHANGES || 'false' }} - DEBUG_MODE: ${{ vars.DEBUG_MODE || 'true' }} + DEBUG_MODE: ${{ vars.DEBUG_MODE || 'false' }} run: | # Run the AI reviewer and save response to file echo "Running AI code review..." diff --git a/ai-reviewer.sh b/ai-reviewer.sh index 9455c8b..86f643b 100644 --- a/ai-reviewer.sh +++ b/ai-reviewer.sh @@ -5,12 +5,21 @@ set -e +# Constants +REVIEW_HEADER="## ❤️ AI Code Review" +REVIEW_FOOTER="---\n*Review by [FAIR](https://github.com/LearningCircuit/Friendly-AI-Reviewer) - needs human verification*" + +# Helper function to generate error response JSON +generate_error_response() { + local error_msg="$1" + echo "{\"review\":\"$REVIEW_HEADER\n\n❌ **Error**: $error_msg\n\n$REVIEW_FOOTER\",\"fail_pass_workflow\":\"uncertain\",\"labels_added\":[]}" +} # Get API key from environment variable API_KEY="${OPENROUTER_API_KEY}" if [ -z "$API_KEY" ]; then - echo "## ❤️ AI Code Review + echo "$REVIEW_HEADER ❌ **Error**: Missing OPENROUTER_API_KEY environment variable" exit 1 @@ -19,6 +28,7 @@ fi # Configuration with defaults AI_MODEL="${AI_MODEL:-moonshotai/kimi-k2-thinking}" AI_TEMPERATURE="${AI_TEMPERATURE:-0.1}" +AI_MAX_TOKENS="${AI_MAX_TOKENS:-6000}" MAX_DIFF_SIZE="${MAX_DIFF_SIZE:-800000}" # 800KB default limit (~200K tokens, matching model context size) EXCLUDE_FILE_PATTERNS="${EXCLUDE_FILE_PATTERNS:-*.lock,*.min.js,*.min.css,package-lock.json,yarn.lock}" @@ -183,12 +193,13 @@ RESPONSE=$(curl -s -X POST "https://openrouter.ai/api/v1/chat/completions" \ \"content\": $(echo "$PROMPT" | jq -Rs .) } ], - \"temperature\": $AI_TEMPERATURE + \"temperature\": $AI_TEMPERATURE, + \"max_tokens\": $AI_MAX_TOKENS }") # Check if API call was successful if [ -z "$RESPONSE" ]; then - echo '{"review":"## ❤️ AI Code Review\n\n❌ **Error**: API call failed - no response received","fail_pass_workflow":"uncertain","labels_added":[]}' + generate_error_response "API call failed - no response received" exit 1 fi @@ -197,7 +208,7 @@ if ! echo "$RESPONSE" | jq . >/dev/null 2>&1; then echo "=== API DEBUG: Raw response from $AI_MODEL ===" >&2 echo "$RESPONSE" >&2 echo "=== END API DEBUG ===" >&2 - echo '{"review":"## ❤️ AI Code Review\n\n❌ **Error**: Invalid JSON response from API","fail_pass_workflow":"uncertain","labels_added":[]}' + generate_error_response "Invalid JSON response from API" exit 1 fi @@ -243,7 +254,7 @@ fi # Ensure CONTENT is not empty if [ -z "$CONTENT" ]; then - echo '{"review":"## ❤️ AI Code Review\n\n❌ **Error**: AI returned empty response","fail_pass_workflow":"uncertain","labels_added":[]}' + generate_error_response "AI returned empty response" exit 0 fi @@ -263,7 +274,7 @@ CONTENT=$(echo "$CONTENT" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') # Enhanced empty check (catches whitespace-only content) if [ -z "$CONTENT" ] || [ -z "$(echo "$CONTENT" | tr -d '[:space:]')" ]; then - echo '{"review":"## ❤️ AI Code Review\n\n❌ **Error**: AI returned empty response after processing","fail_pass_workflow":"uncertain","labels_added":[]}' + generate_error_response "AI returned empty response after processing" exit 0 fi @@ -276,15 +287,13 @@ if ! echo "$CONTENT" | jq . >/dev/null 2>&1; then echo "=== END DEBUG ===" >&2 # Fallback to error response - JSON_CONTENT="{\"review\":\"## ❤️ AI Code Review\n\n❌ **Error**: Invalid JSON response from AI model\n\n---\n*Review by [FAIR](https://github.com/LearningCircuit/Friendly-AI-Reviewer) - needs human verification*\",\"fail_pass_workflow\":\"uncertain\",\"labels_added\":[]}" - echo "$JSON_CONTENT" + generate_error_response "Invalid JSON response from AI model" else echo "=== CONTENT IS VALID JSON ===" >&2 # Validate it has the required structure if ! echo "$CONTENT" | jq -e '.review' >/dev/null 2>&1; then echo "JSON missing required 'review' field" >&2 - JSON_CONTENT="{\"review\":\"## ❤️ AI Code Review\n\n❌ **Error**: AI response missing required review field\n\n---\n*Review by [FAIR](https://github.com/LearningCircuit/Friendly-AI-Reviewer) - needs human verification*\",\"fail_pass_workflow\":\"uncertain\",\"labels_added\":[]}" - echo "$JSON_CONTENT" + generate_error_response "AI response missing required review field" else echo "JSON has required structure, using as-is" >&2 echo "$CONTENT" diff --git a/test_diff.txt b/test_diff.txt deleted file mode 100644 index f7c9329..0000000 --- a/test_diff.txt +++ /dev/null @@ -1,26 +0,0 @@ -diff --git a/.github/workflows/ai-code-reviewer.yml b/.github/workflows/ai-code-reviewer.yml -index 2a9d68f..76a8f9e 100644 ---- a/.github/workflows/ai-code-reviewer.yml -+++ b/.github/workflows/ai-code-reviewer.yml -@@ -34,7 +34,7 @@ jobs: - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} -- AI_MODEL: ${{ vars.AI_MODEL || 'z-ai/glm-4.6' }} -+ AI_MODEL: ${{ vars.AI_MODEL || 'moonshotai/kimi-k2-thinking' }} - AI_TEMPERATURE: ${{ vars.AI_TEMPERATURE || '0.1' }} - AI_MAX_TOKENS: ${{ vars.AI_MAX_TOKENS || '2000' }} - MAX_DIFF_SIZE: ${{ vars.MAX_DIFF_SIZE || '800000' }} -diff --git a/ai-reviewer.sh b/ai-reviewer.sh -index 765d664..d55f5be 100644 ---- a/ai-reviewer.sh -+++ b/ai-reviewer.sh -@@ -17,7 +17,7 @@ if [ -z "$API_KEY" ]; then - fi - - # Configuration with defaults --AI_MODEL="${AI_MODEL:-z-ai/glm-4.6}" -+AI_MODEL="${AI_MODEL:-moonshotai/kimi-k2-thinking}" - AI_TEMPERATURE="${AI_TEMPERATURE:-0.1}" - AI_MAX_TOKENS="${AI_MAX_TOKENS:-2000}" - MAX_DIFF_SIZE="${MAX_DIFF_SIZE:-800000}" # 800KB default limit (~200K tokens, matching model context size) From 86fe9f0ee9d59177a3d1a45606d8a79791086c3c Mon Sep 17 00:00:00 2001 From: LearningCircuit <185559241+LearningCircuit@users.noreply.github.com> Date: Sun, 9 Nov 2025 13:25:53 +0100 Subject: [PATCH 17/27] style: remove emoji from header, keep heart in footer only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Change header from '## ❤️ AI Code Review' to '## AI Code Review' - Update footer to use 'F-AI-R' instead of 'FAIR' to avoid confusion with Meta's FAIR research lab - Keep heart emoji only in footer signature: 'made with ❤️' - Update workflow job name to remove emoji - More professional appearance while maintaining friendly signature --- .github/workflows/ai-code-reviewer.yml | 2 +- ai-reviewer.sh | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ai-code-reviewer.yml b/.github/workflows/ai-code-reviewer.yml index 103e60b..af417d8 100644 --- a/.github/workflows/ai-code-reviewer.yml +++ b/.github/workflows/ai-code-reviewer.yml @@ -6,7 +6,7 @@ on: jobs: comprehensive-review: - name: ❤️ AI Code Review + name: AI Code Review runs-on: ubuntu-latest if: github.event.action == 'labeled' && github.event.label.name == 'ai_code_review' permissions: diff --git a/ai-reviewer.sh b/ai-reviewer.sh index 86f643b..91f365a 100644 --- a/ai-reviewer.sh +++ b/ai-reviewer.sh @@ -6,8 +6,8 @@ set -e # Constants -REVIEW_HEADER="## ❤️ AI Code Review" -REVIEW_FOOTER="---\n*Review by [FAIR](https://github.com/LearningCircuit/Friendly-AI-Reviewer) - needs human verification*" +REVIEW_HEADER="## AI Code Review" +REVIEW_FOOTER="---\n*Review by [F-AI-R](https://github.com/LearningCircuit/Friendly-AI-Reviewer) - made with ❤️*" # Helper function to generate error response JSON generate_error_response() { @@ -159,14 +159,14 @@ Focus on security, performance, code quality, and best practices. Required JSON format: { - \"review\": \"## ❤️ AI Code Review\\n\\n[Your detailed review in markdown format]\\n\\n---\\n*Review by [FAIR](https://github.com/LearningCircuit/Friendly-AI-Reviewer) - made with ❤️*\", + \"review\": \"## AI Code Review\\n\\n[Your detailed review in markdown format]\\n\\n---\\n*Review by [F-AI-R](https://github.com/LearningCircuit/Friendly-AI-Reviewer) - made with ❤️*\", \"fail_pass_workflow\": \"pass\", \"labels_added\": [\"bug\", \"feature\", \"enhancement\"] } Instructions: 1. Respond with a single valid JSON object -2. Include the heart emoji and repo link in the review field +2. Include the F-AI-R footer with heart emoji at the end of the review field 3. For labels_added, prefer existing repository labels when possible Code to review: From 8e41e39b2263207889dcaabe5e47c9439bb3bbdd Mon Sep 17 00:00:00 2001 From: LearningCircuit <185559241+LearningCircuit@users.noreply.github.com> Date: Sun, 9 Nov 2025 13:27:16 +0100 Subject: [PATCH 18/27] style: use 'Friendly AI Reviewer' instead of 'F-AI-R' in footer More readable and clear, avoids awkward hyphenation --- ai-reviewer.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ai-reviewer.sh b/ai-reviewer.sh index 91f365a..c8810a2 100644 --- a/ai-reviewer.sh +++ b/ai-reviewer.sh @@ -7,7 +7,7 @@ set -e # Constants REVIEW_HEADER="## AI Code Review" -REVIEW_FOOTER="---\n*Review by [F-AI-R](https://github.com/LearningCircuit/Friendly-AI-Reviewer) - made with ❤️*" +REVIEW_FOOTER="---\n*Review by [Friendly AI Reviewer](https://github.com/LearningCircuit/Friendly-AI-Reviewer) - made with ❤️*" # Helper function to generate error response JSON generate_error_response() { @@ -159,14 +159,14 @@ Focus on security, performance, code quality, and best practices. Required JSON format: { - \"review\": \"## AI Code Review\\n\\n[Your detailed review in markdown format]\\n\\n---\\n*Review by [F-AI-R](https://github.com/LearningCircuit/Friendly-AI-Reviewer) - made with ❤️*\", + \"review\": \"## AI Code Review\\n\\n[Your detailed review in markdown format]\\n\\n---\\n*Review by [Friendly AI Reviewer](https://github.com/LearningCircuit/Friendly-AI-Reviewer) - made with ❤️*\", \"fail_pass_workflow\": \"pass\", \"labels_added\": [\"bug\", \"feature\", \"enhancement\"] } Instructions: 1. Respond with a single valid JSON object -2. Include the F-AI-R footer with heart emoji at the end of the review field +2. Include the Friendly AI Reviewer footer with heart emoji at the end of the review field 3. For labels_added, prefer existing repository labels when possible Code to review: From 7b58b0e7e3a76b6471547ae1dc08d0b1d877b338 Mon Sep 17 00:00:00 2001 From: LearningCircuit <185559241+LearningCircuit@users.noreply.github.com> Date: Sun, 9 Nov 2025 13:33:14 +0100 Subject: [PATCH 19/27] fix: use constants consistently for review header and footer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace all hardcoded '## ❤️ AI Code Review' with REVIEW_HEADER constant - Replace hardcoded footer text with REVIEW_FOOTER constant - Update previous review fetching to match new header format - Ensures consistent branding across all error messages and outputs --- ai-reviewer.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ai-reviewer.sh b/ai-reviewer.sh index c8810a2..2d1cfb6 100644 --- a/ai-reviewer.sh +++ b/ai-reviewer.sh @@ -36,7 +36,7 @@ EXCLUDE_FILE_PATTERNS="${EXCLUDE_FILE_PATTERNS:-*.lock,*.min.js,*.min.css,packag DIFF_CONTENT=$(cat) if [ -z "$DIFF_CONTENT" ]; then - echo "## ❤️ AI Code Review + echo "$REVIEW_HEADER ❌ **Error**: No diff content to analyze" exit 1 @@ -55,7 +55,7 @@ fi # Validate diff size to prevent excessive API usage DIFF_SIZE=${#DIFF_CONTENT} if [ "$DIFF_SIZE" -gt "$MAX_DIFF_SIZE" ]; then - echo "## ❤️ AI Code Review + echo "$REVIEW_HEADER ❌ **Error**: Diff is too large ($DIFF_SIZE bytes, max: $MAX_DIFF_SIZE bytes) Please split this PR into smaller changes for review." @@ -65,9 +65,9 @@ fi # Fetch previous AI review comments for context (if PR_NUMBER and REPO_FULL_NAME are set) PREVIOUS_REVIEWS="" if [ -n "$PR_NUMBER" ] && [ -n "$REPO_FULL_NAME" ] && [ -n "$GITHUB_TOKEN" ]; then - # Fetch comments that start with "## ❤️ AI Code Review" + # Fetch comments that start with the review header PREVIOUS_REVIEWS=$(gh api "repos/$REPO_FULL_NAME/issues/$PR_NUMBER/comments" \ - --jq '.[] | select(.body | startswith("## ❤️ AI Code Review")) | "### Previous Review (" + .created_at + "):\n" + .body + "\n---\n"' 2>/dev/null | head -c 50000 || echo "") + --jq '.[] | select(.body | startswith("## AI Code Review")) | "### Previous Review (" + .created_at + "):\n" + .body + "\n---\n"' 2>/dev/null | head -c 50000 || echo "") fi # Fetch GitHub Actions check runs status (if PR_NUMBER and REPO_FULL_NAME are set) @@ -236,11 +236,11 @@ if [ "$CONTENT" = "error" ]; then ERROR_CODE=$(echo "$RESPONSE" | jq -r '.error.code // ""') # Return error as JSON - ERROR_CONTENT="## ❤️ AI Code Review\n\n❌ **Error**: $ERROR_MSG" + ERROR_CONTENT="$REVIEW_HEADER\n\n❌ **Error**: $ERROR_MSG" if [ -n "$ERROR_CODE" ]; then ERROR_CONTENT="$ERROR_CONTENT\n\nError code: \`$ERROR_CODE\`" fi - ERROR_CONTENT="$ERROR_CONTENT\n\n---\n*Review by [FAIR](https://github.com/LearningCircuit/Friendly-AI-Reviewer) - needs human verification*" + ERROR_CONTENT="$ERROR_CONTENT\n\n$REVIEW_FOOTER" echo "{\"review\":\"$ERROR_CONTENT\",\"fail_pass_workflow\":\"uncertain\",\"labels_added\":[]}" From efacc8f1644a3c363e870c806c207e657992724d Mon Sep 17 00:00:00 2001 From: LearningCircuit <185559241+LearningCircuit@users.noreply.github.com> Date: Sun, 9 Nov 2025 13:55:00 +0100 Subject: [PATCH 20/27] fix: increase AI_MAX_TOKENS to 64000 to prevent response truncation --- .github/workflows/ai-code-reviewer.yml | 2 +- ai-reviewer.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ai-code-reviewer.yml b/.github/workflows/ai-code-reviewer.yml index af417d8..db53033 100644 --- a/.github/workflows/ai-code-reviewer.yml +++ b/.github/workflows/ai-code-reviewer.yml @@ -36,7 +36,7 @@ jobs: OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} AI_MODEL: ${{ vars.AI_MODEL || 'moonshotai/kimi-k2-thinking' }} AI_TEMPERATURE: ${{ vars.AI_TEMPERATURE || '0.1' }} - AI_MAX_TOKENS: ${{ vars.AI_MAX_TOKENS || '6000' }} + AI_MAX_TOKENS: ${{ vars.AI_MAX_TOKENS || '64000' }} MAX_DIFF_SIZE: ${{ vars.MAX_DIFF_SIZE || '800000' }} EXCLUDE_FILE_PATTERNS: ${{ vars.EXCLUDE_FILE_PATTERNS || '*.lock,*.min.js,*.min.css,package-lock.json,yarn.lock' }} PR_NUMBER: ${{ github.event.pull_request.number }} diff --git a/ai-reviewer.sh b/ai-reviewer.sh index 2d1cfb6..0aaa9bb 100644 --- a/ai-reviewer.sh +++ b/ai-reviewer.sh @@ -28,7 +28,7 @@ fi # Configuration with defaults AI_MODEL="${AI_MODEL:-moonshotai/kimi-k2-thinking}" AI_TEMPERATURE="${AI_TEMPERATURE:-0.1}" -AI_MAX_TOKENS="${AI_MAX_TOKENS:-6000}" +AI_MAX_TOKENS="${AI_MAX_TOKENS:-64000}" MAX_DIFF_SIZE="${MAX_DIFF_SIZE:-800000}" # 800KB default limit (~200K tokens, matching model context size) EXCLUDE_FILE_PATTERNS="${EXCLUDE_FILE_PATTERNS:-*.lock,*.min.js,*.min.css,package-lock.json,yarn.lock}" From 68777f917fa5a32ff35b443c849453378730571b Mon Sep 17 00:00:00 2001 From: LearningCircuit <185559241+LearningCircuit@users.noreply.github.com> Date: Sun, 9 Nov 2025 14:00:19 +0100 Subject: [PATCH 21/27] feat: include human comments and limit AI review context - Fetch human comments from PR (up to 20k chars) for valuable context - Only include most recent AI review instead of all reviews (limit 10k chars) - Reduces token usage while providing more relevant context - Human feedback is more valuable than previous AI reviews --- ai-reviewer.sh | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/ai-reviewer.sh b/ai-reviewer.sh index 0aaa9bb..cedeaa5 100644 --- a/ai-reviewer.sh +++ b/ai-reviewer.sh @@ -62,12 +62,20 @@ Please split this PR into smaller changes for review." exit 1 fi -# Fetch previous AI review comments for context (if PR_NUMBER and REPO_FULL_NAME are set) +# Fetch previous AI review (only the most recent one) for context PREVIOUS_REVIEWS="" if [ -n "$PR_NUMBER" ] && [ -n "$REPO_FULL_NAME" ] && [ -n "$GITHUB_TOKEN" ]; then - # Fetch comments that start with the review header + # Fetch only the most recent AI review comment PREVIOUS_REVIEWS=$(gh api "repos/$REPO_FULL_NAME/issues/$PR_NUMBER/comments" \ - --jq '.[] | select(.body | startswith("## AI Code Review")) | "### Previous Review (" + .created_at + "):\n" + .body + "\n---\n"' 2>/dev/null | head -c 50000 || echo "") + --jq '[.[] | select(.body | startswith("## AI Code Review"))] | last | if . then "### Previous AI Review (" + .created_at + "):\n" + .body + "\n---\n" else "" end' 2>/dev/null | head -c 10000 || echo "") +fi + +# Fetch human comments for context +HUMAN_COMMENTS="" +if [ -n "$PR_NUMBER" ] && [ -n "$REPO_FULL_NAME" ] && [ -n "$GITHUB_TOKEN" ]; then + # Fetch comments from humans (not the bot) + HUMAN_COMMENTS=$(gh api "repos/$REPO_FULL_NAME/issues/$PR_NUMBER/comments" \ + --jq '[.[] | select(.body | startswith("## AI Code Review") | not)] | map("**" + .user.login + "** (" + .created_at + "):\n" + .body) | join("\n\n---\n\n")' 2>/dev/null | head -c 20000 || echo "") fi # Fetch GitHub Actions check runs status (if PR_NUMBER and REPO_FULL_NAME are set) @@ -135,10 +143,20 @@ If none of these labels are appropriate for the changes, you may suggest new one " fi -# Add previous reviews context if available +# Add human comments context if available +if [ -n "$HUMAN_COMMENTS" ]; then + PROMPT_PREFIX="${PROMPT_PREFIX} +Human Comments on this PR: +$HUMAN_COMMENTS + +Please consider these human comments when reviewing the code. +" +fi + +# Add previous AI review context if available (only most recent) if [ -n "$PREVIOUS_REVIEWS" ]; then PROMPT_PREFIX="${PROMPT_PREFIX} -Previous AI Reviews (for context on what was already reviewed): +Previous AI Review (for context on what was already reviewed): $PREVIOUS_REVIEWS " fi From 8fa95c02753c5fbe98e23632da4b163301ae1af3 Mon Sep 17 00:00:00 2001 From: LearningCircuit <185559241+LearningCircuit@users.noreply.github.com> Date: Sun, 9 Nov 2025 14:10:14 +0100 Subject: [PATCH 22/27] feat: include PR description and commit messages in review context - Add PR title and description (up to 2k chars) for business context - Add commit messages from up to 15 most recent commits (up to 2.5k chars) - Exclude merge commits to reduce noise - Include commit bodies to understand development journey - Total additional cost: ~1,125 tokens (only 2% of budget) Benefits: - AI understands WHY changes were made, not just WHAT - Sees edge cases discovered during development - Aligns with industry best practices (all major AI review tools include this) - Provides critical context missing from diff alone --- ai-reviewer.sh | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/ai-reviewer.sh b/ai-reviewer.sh index cedeaa5..3724efe 100644 --- a/ai-reviewer.sh +++ b/ai-reviewer.sh @@ -107,6 +107,31 @@ if [ -n "$PR_NUMBER" ] && [ -n "$REPO_FULL_NAME" ] && [ -n "$GITHUB_TOKEN" ]; th fi fi +# Fetch PR title and description +PR_DESCRIPTION="" +if [ -n "$PR_NUMBER" ] && [ -n "$REPO_FULL_NAME" ] && [ -n "$GITHUB_TOKEN" ]; then + echo "🔍 Fetching PR title and description..." >&2 + PR_DESCRIPTION=$(gh api "repos/$REPO_FULL_NAME/pulls/$PR_NUMBER" \ + --jq '"**PR Title**: " + .title + "\n\n**Description**:\n" + (.body // "No description provided")' 2>/dev/null | head -c 2000 || echo "") + + if [ -n "$PR_DESCRIPTION" ]; then + echo "✅ Successfully fetched PR description" >&2 + fi +fi + +# Fetch commit messages (limit to 15 most recent, exclude merges) +COMMIT_MESSAGES="" +if [ -n "$PR_NUMBER" ] && [ -n "$REPO_FULL_NAME" ] && [ -n "$GITHUB_TOKEN" ]; then + echo "🔍 Fetching commit messages..." >&2 + COMMIT_MESSAGES=$(gh api "repos/$REPO_FULL_NAME/pulls/$PR_NUMBER/commits" --paginate \ + --jq '[.[] | select(.commit.message | startswith("Merge") | not)] | .[-15:] | .[] | "- " + (.commit.message | split("\n")[0]) + (if (.commit.message | split("\n\n")[1]) then "\n " + (.commit.message | split("\n\n")[1]) else "" end)' 2>/dev/null | head -c 2500 || echo "") + + if [ -n "$COMMIT_MESSAGES" ]; then + COMMIT_COUNT=$(echo "$COMMIT_MESSAGES" | grep -c "^- " || echo "0") + echo "✅ Successfully fetched $COMMIT_COUNT commit messages" >&2 + fi +fi + # Create the JSON request with proper escaping using jq # Write diff to temporary file to avoid "Argument list too long" error DIFF_FILE=$(mktemp) @@ -143,6 +168,26 @@ If none of these labels are appropriate for the changes, you may suggest new one " fi +# Add PR description if available +if [ -n "$PR_DESCRIPTION" ]; then + PROMPT_PREFIX="${PROMPT_PREFIX} +Pull Request Context: +$PR_DESCRIPTION + +" +fi + +# Add commit messages if available +if [ -n "$COMMIT_MESSAGES" ]; then + PROMPT_PREFIX="${PROMPT_PREFIX} +Commit History (showing development journey): +$COMMIT_MESSAGES + +Please consider the commit history to understand what was tried, what issues were discovered, and how the solution evolved. + +" +fi + # Add human comments context if available if [ -n "$HUMAN_COMMENTS" ]; then PROMPT_PREFIX="${PROMPT_PREFIX} From 40df378f51c87156fbe4a703f10afb5c513276d0 Mon Sep 17 00:00:00 2001 From: LearningCircuit <185559241+LearningCircuit@users.noreply.github.com> Date: Sun, 9 Nov 2025 14:18:17 +0100 Subject: [PATCH 23/27] docs: update README with accurate costs and new features - Add 'What's New' section highlighting thinking model support and rich context - Update cost estimates based on real usage data (/bin/bash.01-/bin/bash.05 per review) - Document all context sources (PR description, commits, human comments) - Add configuration details for new variables (AI_MAX_TOKENS, DEBUG_MODE) - Explain why 64k token limit is cost-effective (it's a ceiling, not typical usage) - Update security section to list all data sent to AI - Clarify default model is moonshotai/kimi-k2-thinking --- README.md | 72 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 56 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index f498017..84b4826 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,21 @@ -# Friendly AI Reviewer (FAIR) Setup Guide +# Friendly AI Reviewer Setup Guide - Creates highly-customizable AI Reviews as PR comments. -- Installation: Just 2 files copied to your repo and a open router API Key in your secrets. -- Costs: $0.01 - $0.05 per review (depends highly on model) -- ** Example output ** https://github.com/LearningCircuit/local-deep-research/pull/959#issuecomment-3445396169 +- Installation: Just 2 files copied to your repo and an OpenRouter API Key in your secrets. +- Costs: $0.01 - $0.05 per review (even for large PRs with full context) +- **Example output**: https://github.com/LearningCircuit/local-deep-research/pull/959#issuecomment-3445396169 This guide explains how to set up the automated AI PR review system using OpenRouter to analyze pull requests with your choice of AI model. +## What's New + +**Latest Updates:** +- **Thinking Model Support**: Now supports advanced reasoning models like Kimi K2 that use `` tags +- **Rich Context**: Includes PR descriptions, commit messages, and human comments for comprehensive reviews +- **Higher Token Limits**: Default 64k tokens for complete reviews without truncation +- **Smart Context Management**: Only fetches most recent AI review to save tokens +- **Enhanced Error Handling**: Robust parsing of various AI response formats + ## Overview The AI Code Reviewer provides automated, comprehensive code reviews covering: @@ -45,12 +54,20 @@ The review is posted as a single comprehensive comment on your pull request with ### 3. Configure Workflow (Optional) -The workflow is pre-configured with sensible defaults, but you can customize it by editing `.github/workflows/ai-code-reviewer.yml`: +The workflow is pre-configured with sensible defaults, but you can customize it by setting repository variables in **Settings** → **Secrets and variables** → **Actions** → **Variables**: -- **AI_MODEL**: Change the AI model (see [OpenRouter models](https://openrouter.ai/models)) +- **AI_MODEL**: Change the AI model (default: `moonshotai/kimi-k2-thinking`) + - See [OpenRouter models](https://openrouter.ai/models) for options + - Recommended: Models with reasoning capabilities (Kimi K2, o1, etc.) - **AI_TEMPERATURE**: Adjust randomness (default: `0.1` for consistent reviews) -- **AI_MAX_TOKENS**: Maximum response length (default: `2000`) +- **AI_MAX_TOKENS**: Maximum response length (default: `64000`) + - High limit ensures comprehensive reviews without truncation + - For large PRs with thinking models, this prevents cut-off responses + - Adjust lower for cost savings on smaller PRs - **MAX_DIFF_SIZE**: Maximum diff size in bytes (default: `800000` / 800KB) +- **DEBUG_MODE**: Enable debug logging (default: `false`) + - ⚠️ Warning: Exposes code diff in workflow logs when enabled + - Only enable temporarily for troubleshooting ## Usage @@ -79,11 +96,31 @@ The AI posts a comprehensive comment analyzing your code across all focus areas. ## Cost Estimation -Costs vary by model, but most code-focused models on OpenRouter are very affordable: -- Typical small PR (< 1000 lines): $0.001 - $0.01 -- Large PR (1000-5000 lines): $0.01 - $0.05 +Costs with the default Kimi K2 thinking model are very affordable. Based on real usage data: + +**Typical Costs:** +- Small PR (< 1000 lines): $0.01 - $0.02 +- Medium PR (1000-3000 lines): $0.02 - $0.04 +- Large PR (3000+ lines): $0.04 - $0.06 + +**Example from a 20-commit PR with full context:** +- Input: ~5,000-9,000 tokens (diff + PR description + commits + human comments) +- Output: ~2,000-6,000 tokens (comprehensive review) +- **Total cost: $0.01 - $0.05 per review** + +**Why So Affordable:** +- Kimi K2 has competitive pricing (~$0.001-$0.003 per 1k tokens) +- Smart context management (only most recent AI review, limited commit history) +- Most PRs are smaller than you think in token count +- The 64k token limit is a ceiling, not typical usage + +**Cost varies based on:** +- PR size (larger diffs = more input tokens) +- Review complexity (detailed reviews = more output tokens) +- Number of human comments and commit messages included +- OpenRouter provider routing (prices vary slightly by provider) -Check [OpenRouter pricing](https://openrouter.ai/models) for specific model costs. +Check [OpenRouter pricing](https://openrouter.ai/models) for current Kimi K2 rates. ## Customization @@ -138,11 +175,14 @@ If you get a "Diff is too large" error: The workflow fetches and sends these repository elements to the AI: 1. **Code Changes**: Full diff of modified files -2. **Labels**: All repository labels with descriptions and colors -3. **Previous Comments**: All previous AI review comments from the PR -4. **CI/CD Status**: GitHub Actions check runs and build statuses -5. **PR Metadata**: Pull request details, head SHA, repository information -6. **Files**: May include sensitive configuration files, keys, or credentials +2. **PR Description**: Title and description text from the pull request +3. **Commit Messages**: Up to 15 most recent commit messages (excluding merges) +4. **Human Comments**: All comments from human reviewers on the PR +5. **Labels**: All repository labels with descriptions and colors +6. **Previous AI Review**: Most recent AI review comment only (limited to 10k chars) +7. **CI/CD Status**: GitHub Actions check runs and build statuses +8. **PR Metadata**: Pull request details, head SHA, repository information +9. **Files**: May include sensitive configuration files, keys, or credentials ### Recommended Mitigations From 66f6cf6a1e6c4369ff074bf4f5518d2776e3378a Mon Sep 17 00:00:00 2001 From: LearningCircuit <185559241+LearningCircuit@users.noreply.github.com> Date: Sun, 9 Nov 2025 14:21:33 +0100 Subject: [PATCH 24/27] fix: respect DEBUG_MODE flag for all debug output - Wrap all debug logging in DEBUG_MODE checks - Only output API structure, content, validation messages when DEBUG_MODE=true - Prevents exposing sensitive code diffs in workflow logs by default - Debug output now properly controlled by the environment variable --- ai-reviewer.sh | 58 +++++++++++++++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 22 deletions(-) diff --git a/ai-reviewer.sh b/ai-reviewer.sh index 3724efe..e9d70d0 100644 --- a/ai-reviewer.sh +++ b/ai-reviewer.sh @@ -275,23 +275,27 @@ if ! echo "$RESPONSE" | jq . >/dev/null 2>&1; then exit 1 fi -# Always log the API response structure for debugging thinking models -echo "=== API STRUCTURE DEBUG from $AI_MODEL ===" >&2 -echo "Response keys: $(echo "$RESPONSE" | jq -r 'keys | join(", ")')" >&2 -echo "Choices count: $(echo "$RESPONSE" | jq '.choices | length')" >&2 -echo "First choice keys: $(echo "$RESPONSE" | jq -r '.choices[0] | keys | join(", ")')" >&2 -echo "Content type: $(echo "$RESPONSE" | jq -r '.choices[0].message | type')" >&2 -echo "=== END API STRUCTURE DEBUG ===" >&2 +# Log the API response structure for debugging thinking models (if debug mode enabled) +if [ "$DEBUG_MODE" = "true" ]; then + echo "=== API STRUCTURE DEBUG from $AI_MODEL ===" >&2 + echo "Response keys: $(echo "$RESPONSE" | jq -r 'keys | join(", ")')" >&2 + echo "Choices count: $(echo "$RESPONSE" | jq '.choices | length')" >&2 + echo "First choice keys: $(echo "$RESPONSE" | jq -r '.choices[0] | keys | join(", ")')" >&2 + echo "Content type: $(echo "$RESPONSE" | jq -r '.choices[0].message | type')" >&2 + echo "=== END API STRUCTURE DEBUG ===" >&2 +fi # Extract the content CONTENT=$(echo "$RESPONSE" | jq -r '.choices[0].message.content // "error"') -# Debug: Log the extracted content from thinking model -echo "=== CONTENT DEBUG: Extracted from $AI_MODEL ===" >&2 -echo "Content length: $(echo "$CONTENT" | wc -c)" >&2 -echo "Full content:" >&2 -echo "$CONTENT" >&2 -echo "=== END CONTENT DEBUG ===" >&2 +# Log the extracted content from thinking model (if debug mode enabled) +if [ "$DEBUG_MODE" = "true" ]; then + echo "=== CONTENT DEBUG: Extracted from $AI_MODEL ===" >&2 + echo "Content length: $(echo "$CONTENT" | wc -c)" >&2 + echo "Full content:" >&2 + echo "$CONTENT" >&2 + echo "=== END CONTENT DEBUG ===" >&2 +fi if [ "$CONTENT" = "error" ]; then # Try to extract error details from the API response @@ -327,7 +331,9 @@ CONTENT=$(echo "$CONTENT" | perl -0pe 's/.*?<\/thinking>\s*//gs') # Remove markdown code blocks if present (check for actual backticks at line start) if echo "$CONTENT" | grep -qE '^\s*```json'; then - echo "=== REMOVING MARKDOWN CODE BLOCKS ===" >&2 + if [ "$DEBUG_MODE" = "true" ]; then + echo "=== REMOVING MARKDOWN CODE BLOCKS ===" >&2 + fi # Remove the opening ```json and closing ``` lines, keep the content CONTENT=$(echo "$CONTENT" | perl -0pe 's/^\s*```json\s*\n//g; s/\n```\s*$//g') fi @@ -343,22 +349,30 @@ fi # Validate that CONTENT is valid JSON if ! echo "$CONTENT" | jq . >/dev/null 2>&1; then - echo "=== JSON VALIDATION FAILED ===" >&2 - echo "Content is not valid JSON" >&2 - echo "=== RAW CONTENT FOR DEBUG ===" >&2 - echo "$CONTENT" >&2 - echo "=== END DEBUG ===" >&2 + if [ "$DEBUG_MODE" = "true" ]; then + echo "=== JSON VALIDATION FAILED ===" >&2 + echo "Content is not valid JSON" >&2 + echo "=== RAW CONTENT FOR DEBUG ===" >&2 + echo "$CONTENT" >&2 + echo "=== END DEBUG ===" >&2 + fi # Fallback to error response generate_error_response "Invalid JSON response from AI model" else - echo "=== CONTENT IS VALID JSON ===" >&2 + if [ "$DEBUG_MODE" = "true" ]; then + echo "=== CONTENT IS VALID JSON ===" >&2 + fi # Validate it has the required structure if ! echo "$CONTENT" | jq -e '.review' >/dev/null 2>&1; then - echo "JSON missing required 'review' field" >&2 + if [ "$DEBUG_MODE" = "true" ]; then + echo "JSON missing required 'review' field" >&2 + fi generate_error_response "AI response missing required review field" else - echo "JSON has required structure, using as-is" >&2 + if [ "$DEBUG_MODE" = "true" ]; then + echo "JSON has required structure, using as-is" >&2 + fi echo "$CONTENT" fi fi From ebe3641e9caf273d73b2aa5f71ddafc621cc2f1d Mon Sep 17 00:00:00 2001 From: LearningCircuit <185559241+LearningCircuit@users.noreply.github.com> Date: Sun, 9 Nov 2025 14:38:54 +0100 Subject: [PATCH 25/27] feat: improve AI review focus and conciseness - Add guidance to prioritize high-value issues over minor optimizations - Encourage concise, structured responses with bullet points - Promote approving with recommendations for non-critical improvements - Reduce repetition by referencing earlier sections --- ai-reviewer.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ai-reviewer.sh b/ai-reviewer.sh index e9d70d0..91b3a01 100644 --- a/ai-reviewer.sh +++ b/ai-reviewer.sh @@ -220,6 +220,8 @@ PROMPT="You are an expert code reviewer. Please analyze this code diff and provi Focus on security, performance, code quality, and best practices. +Focus on high-value issues. Style suggestions are welcome if impactful, but not minor optimizations. Be concise and dense - use bullet points for clear structure. Avoid repetition - reference earlier sections instead of re-explaining. For non-critical improvements, consider approving with recommendations rather than requesting changes. + Required JSON format: { \"review\": \"## AI Code Review\\n\\n[Your detailed review in markdown format]\\n\\n---\\n*Review by [Friendly AI Reviewer](https://github.com/LearningCircuit/Friendly-AI-Reviewer) - made with ❤️*\", From 38720418cda0e4925dadb68f766db71c82f4048a Mon Sep 17 00:00:00 2001 From: LearningCircuit <185559241+LearningCircuit@users.noreply.github.com> Date: Sun, 9 Nov 2025 14:59:18 +0100 Subject: [PATCH 26/27] refine: reduce repetition in AI reviews and clarify assumptions - Only repeat critical issues in summary sections - Require AI to state assumptions when flagging code outside diff --- ai-reviewer.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ai-reviewer.sh b/ai-reviewer.sh index 91b3a01..a4fafc5 100644 --- a/ai-reviewer.sh +++ b/ai-reviewer.sh @@ -220,7 +220,7 @@ PROMPT="You are an expert code reviewer. Please analyze this code diff and provi Focus on security, performance, code quality, and best practices. -Focus on high-value issues. Style suggestions are welcome if impactful, but not minor optimizations. Be concise and dense - use bullet points for clear structure. Avoid repetition - reference earlier sections instead of re-explaining. For non-critical improvements, consider approving with recommendations rather than requesting changes. +Focus on high-value issues. Style suggestions are welcome if impactful, but not minor optimizations. Be concise and dense - use bullet points for clear structure. Avoid repetition - in summary sections, only repeat critical issues (security, bugs, breaking changes). If flagging issues about code not in the diff, clearly state your assumptions. For non-critical improvements, consider approving with recommendations rather than requesting changes. Required JSON format: { From 3465b23207dec77ecbc9b9ba737c6341bb4857f5 Mon Sep 17 00:00:00 2001 From: LearningCircuit <185559241+LearningCircuit@users.noreply.github.com> Date: Sun, 9 Nov 2025 15:12:35 +0100 Subject: [PATCH 27/27] refactor: simplify review structure and clarify assumptions - Remove prescriptive Focus Areas sections to prevent bloated output - Use importance-based grouping instead of artificial categories - Strengthen assumption guidance: require stating what and why --- ai-reviewer.sh | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/ai-reviewer.sh b/ai-reviewer.sh index a4fafc5..cb811b9 100644 --- a/ai-reviewer.sh +++ b/ai-reviewer.sh @@ -138,13 +138,11 @@ DIFF_FILE=$(mktemp) echo "$DIFF_CONTENT" > "$DIFF_FILE" # Build the user prompt using the diff file -PROMPT_PREFIX="Please analyze this code diff and provide a comprehensive review in markdown format: +PROMPT_PREFIX="Please analyze this code diff and provide a comprehensive review in markdown format. -Focus Areas: -- Security: Look for hardcoded secrets, SQL injection, XSS, authentication issues, input validation problems -- Performance: Check for inefficient algorithms, N+1 queries, missing indexes, memory issues, blocking operations -- Code Quality: Evaluate readability, maintainability, proper error handling, naming conventions, documentation -- Best Practices: Ensure adherence to coding standards, proper patterns, type safety, dead code removal +Focus on security, performance, code quality, and best practices. + +Keep the review scannable and grouped by importance. Lead with critical issues if any exist. " # Add GitHub Actions check status if available @@ -220,7 +218,7 @@ PROMPT="You are an expert code reviewer. Please analyze this code diff and provi Focus on security, performance, code quality, and best practices. -Focus on high-value issues. Style suggestions are welcome if impactful, but not minor optimizations. Be concise and dense - use bullet points for clear structure. Avoid repetition - in summary sections, only repeat critical issues (security, bugs, breaking changes). If flagging issues about code not in the diff, clearly state your assumptions. For non-critical improvements, consider approving with recommendations rather than requesting changes. +Focus on high-value issues. Style suggestions are welcome if impactful, but not minor optimizations. Be concise and dense - use bullet points for clear structure. Avoid repetition - in summary sections, only repeat critical issues (security, bugs, breaking changes). If flagging issues about code not visible in the diff, clearly state what you're assuming and why. For non-critical improvements, consider approving with recommendations rather than requesting changes. Required JSON format: {