Skip to content

assess-replies: media fixes, faster grading, debugging knobs#91

Merged
sgeinitz merged 5 commits into
mainfrom
fix/assess-replies
May 17, 2026
Merged

assess-replies: media fixes, faster grading, debugging knobs#91
sgeinitz merged 5 commits into
mainfrom
fix/assess-replies

Conversation

@sgeinitz

@sgeinitz sgeinitz commented May 15, 2026

Copy link
Copy Markdown
Owner

Summary

Iterative hardening of the assess-replies pipeline (with spillover improvements to get-media-recordings and export-anon-data). Five commits, each independently reviewable:

  • d8c4342 — fix two media-format failures, then cut per-call cost: route audio downloads through ffmpeg to 16 kHz mono PCM WAV (Canvas serves DASH manifests, not raw audio; Gemma misclassifies AAC m4a); rasterize PDF attachments to PNG via PyMuPDF (Gemma doesn't accept PDFs); embed the student's first name in reply filenames; cap image long edge at 1024 px (3.8 MB phone photos → 107 KB); wall-clock timer per student + total run time.
  • 554fcb3_isValidMediaFile preflight skips students with missing/empty audio or image so Gemma isn't fed garbage for 5+ minutes per student. Adds -M, --choose-model so the instructor can interactively pick from local gemma4:* models at runtime (trade some assessment quality for throughput).
  • 60c00a6-S, --save-transcript writes each audio transcription to a sibling .txt next to the .wav so the instructor can listen and read side-by-side when diagnosing noisy or mistranscribed recordings. Applies to both assess-replies and get-media-recordings.
  • c415418 — print a manual-privacy-review reminder at the end of export-anon-data. The redactor only recognizes name/id columns; student_id/student_name/free-text columns (transcripts, reply_text, feedback, conversation subjects) pass through verbatim. A full fix is more work; for now we warn so the instructor doesn't share blindly.
  • 7db8c2e — chunk audio > 30s before transcription. Gemma 4's audio encoder has a ~30s window and silently truncates anything beyond it (observed: 79s WAV → transcript = 5x repetition of the student's opening; the actual answer in 30-79s was never seen). transcribe_audio now probes duration via ffprobe and chunks long audio into 30s segments with ffmpeg, transcribing each separately. Also adds repeat_penalty=1.3 + num_predict=512 to break/cap runaway repetition loops Gemma falls into on stuttered speech (observed: a single chunk generating 640KB of looped output over 40 minutes; now capped at ~17s wall-clock for the 79s WAV). Both assess-replies and get-media-recordings inherit the fix automatically.

New dep: pymupdf==1.27.2.3 for PDF rasterization.

Test coverage went 294 → 320 (added TestFirstNameForFilename, TestRasterizePdfToPng, TestResizeImageInPlace, TestIsValidMediaFile, TestPickLocalGemmaModel, TestTranscribeAudio — the last grew by 3 cases covering the chunking path).

Test plan

  • python -m pytest test_canvigator.py — all 320 pass
  • flake8 both passes return 0
  • python canvigator.py assess-replies --help shows -M, --choose-model and -S, --save-transcript
  • python canvigator.py get-media-recordings --help shows -S, --save-transcript
  • Run assess-replies on a quiz where at least one student didn't save their recording — confirm they're skipped immediately with no valid audio — skipped... processed in 0.0min
  • Run assess-replies -S — confirm *.txt files appear next to *.wav files in data/<course>/replies/
  • Run assess-replies -M — confirm interactive picker shows only gemma4:* entries
  • Run assess-replies on a quiz with at least one >30s audio reply — confirm the transcript contains content from beyond the first 30s of the WAV (not a 5x loop of the opening sentence)
  • Run export-anon-data — confirm the new NOTE: This export is NOT automatically privacy-safe ... line prints at the end

🤖 Generated with Claude Code

sgeinitz and others added 4 commits May 14, 2026 19:00
…plies

- _isValidMediaFile preflight skips students with missing/empty audio or
  image, replacing the reply_text fallback that was costing 5+ min per
  student on bogus inputs.
- --choose-model (-M) prompts the instructor to pick from local gemma4
  models at runtime, threaded through assessFollowUpReplies.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Saves each audio transcription to a sibling .txt file next to the .wav
so the instructor can listen and read side-by-side when diagnosing noisy
or mistranscribed recordings. Threaded through transcribe_audio →
assess_replies / getMediaRecordings → the two CLI dispatchers. Empty
transcripts still produce a zero-byte .txt (informative debug signal);
write failures are logged and never block grading.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
_anonymizeCsvFile only redacts the standard 'name'/'id' column pair (and
the pairing-style person<N>/id<N> shape). CSVs that use student_id /
student_name columns — *_followup_*, *_reminder_sent_*, recordings, and
conversations — pass through verbatim, along with free-text columns like
transcript, reply_text, feedback, last_message, conversation_subject.
A full fix is more work; for now the export prints a final NOTE telling
the instructor to manually verify the CSVs before sharing.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c4154181c3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread canvigator_llm.py
Gemma 4's audio encoder has a ~30s window; longer recordings were silently
truncated, producing transcripts that looped on the early-speech content
and never reached the answer. transcribe_audio now probes duration with
ffprobe and, when audio exceeds 30s, splits it into 30s segments via ffmpeg
and joins per-segment transcripts.

Even within a chunk, Gemma 4 can fall into runaway repetition loops on
stuttered/noisy speech (observed: 640KB of looped output over 40 minutes
on a single chunk). Adding repeat_penalty=1.3 + num_predict=512 to the
chat options breaks most loops at formation and hard-caps any that slip
through. End-to-end runtime on a 79s WAV: 40 min → 17 s, with the actual
answer captured.

Benefits both assess-replies and get-media-recordings without further
changes since both flow through transcribe_audio. Adds 3 tests
(long-audio chunks, short-audio single pass, probe-failure fallback).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@sgeinitz sgeinitz merged commit 5457bbb into main May 17, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant