feat(quiz): quiz-context + course-summary → agents, retire quiz-gen fallback (#145) - #299

Merged
AndresL230 merged 3 commits into
mainfrom
feat/145-quiz-agent
Jul 2, 2026
Merged

feat(quiz): quiz-context + course-summary → agents, retire quiz-gen fallback (#145)#299
AndresL230 merged 3 commits into
mainfrom
feat/145-quiz-agent

Conversation

@Jose-Gael-Cruz-Lopez

@Jose-Gael-Cruz-LopezJose-Gael-Cruz-Lopez commented Jul 1, 2026

Copy link
Copy Markdown
Member

Closes#145 (Agent-migration epic #152, milestone #2).

What

Removes the last three raw call_gemini* seams from the quiz surface, folding them into the agent layer:

SiteBeforeAfter
quiz.py post-submit background taskcall_gemini_json (quiz-context regen)new agents/quiz_context.py (quiz_context_agent)
course_context_service.pycall_gemini (class summary)new agents/course_summary.py (course_summary_agent)
quiz.py_legacy_generate_quizcall_gemini_json fallbackremoved → graceful 502

Notes

  • QuizContext mirrors quiz_context_update.txt exactly (weak_areas, common_mistakes, questions_seen_summary, recommended_difficulty, notes); _update_context saves .model_dump(), so the stored quiz_context.context_json shape is unchanged.
  • Course summary keeps its deterministic template fallback on agent failure (no second LLM call), so a summary is always produced.
  • Quiz-gen fallback removed: on agent guardrail-trip or unexpected failure, generate_quiz returns 502 rather than serving a quiz from a second LLM path. The pre-agent 404 (unknown concept node) is unchanged, and the fast/smart model_pref override still works on the agent path.
  • Both sync call sites use the run_agent_sync bridge (from [P2] Agent migration: remaining one-shot LLM calls (study guide, social, health) → agents/seam #147).

⚠️ Coordination (#128/#129)

This PR is scoped to the LLM seams only. It does not touch quiz scoring, apply_graph_update, or the quiz_attempts writes in submit_quiz — the #128/#129 territory. The only change inside submit_quiz is swapping the background context-update's LLM call for the agent.

Testing

  • Rewrote the legacy-fallback quiz tests → degrade-to-502; repointed the 4 submit context-update patches onto quiz_context_agent; removed the obsolete legacy prompt-augmentation class + legacy-model tests; added quiz-context-save, course-summary success/fallback, and degrade tests.
  • Full backend suite: 826 passed (the 2 test_storage_service failures pre-exist on main — missing SUPABASE env). ruff clean.

Out of scope: flashcards (#146), deleting services/gemini_service.py (#151).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added AI-powered course summaries for instructors.
    • Added AI-generated quiz context updates to improve follow-up learning notes.
  • Bug Fixes

    • Quiz generation now fails gracefully with a clear temporary-unavailable response instead of switching to an older fallback path.
    • Course and quiz context updates now use more consistent, structured outputs.

…uiz-gen fallback (#145)
Removes the last raw call_gemini* seams from the quiz surface:
- New agents/quiz_context.py (QuizContext: weak_areas, common_mistakes,
questions_seen_summary, recommended_difficulty, notes — mirrors
quiz_context_update.txt). routes/quiz.py's post-submit background task runs it
via run_agent_sync and saves .model_dump() (unchanged context_json shape).
- New agents/course_summary.py (CourseSummary.summary). course_context_service's
_generate_summary_with_gemini runs it via run_agent_sync and keeps the
deterministic template fallback on agent failure (no second LLM call).
- Removed _legacy_generate_quiz + the MODEL_LITE/MODEL_SMART/call_gemini_json
import. On agent guardrail-trip or failure, generate_quiz now returns 502
instead of a raw-Gemini fallback; the pre-agent 404 (unknown node) is
unchanged. _providers registers course_summary (flash) + quiz_context (lite).
Coordination: does NOT touch scoring / apply_graph_update / quiz_attempts writes
(#128/#129 territory) — only the LLM seams.
Tests: rewrote the legacy-fallback quiz tests to degrade-to-502; repointed the
submit context-update patches onto quiz_context_agent; removed the obsolete
legacy prompt-augmentation + legacy-model tests; added quiz-context save,
course-summary success/fallback tests. Full suite 826 passed (2 pre-existing
storage-env failures). ruff clean.
Spec: specs/145-quiz-course-context-agents.md
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented Jul 1, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@AndresL230, you've reached your PR review limit, so we couldn't start this review.

Next review available in:31 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9c528394-d83a-42ad-98cc-2b4618ab8797

📥 Commits

Reviewing files that changed from the base of the PR and between 4e1b878 and e8127ae.

📒 Files selected for processing (3)
  • backend/routes/quiz.py
  • backend/tests/test_quiz_routes.py
  • backend/tests/test_shared_course_context.py
📝 Walkthrough

Walkthrough

This PR migrates quiz generation and course-context summary regeneration from raw Gemini calls to typed Pydantic AI agents. It adds course_summary and quiz_context agents, registers them in the provider task registry, wires them into routes/quiz.py and course_context_service.py, removes the legacy quiz-generation fallback (returning HTTP 502 on failure instead), and updates tests plus a new migration spec.

Changes

Agent migration

Layer / File(s)Summary
Provider task registry
backend/agents/_providers.py
Adds course_summary and quiz_context to AgentTask and their default models to _DEFAULTS.
Course summary agent
backend/agents/course_summary.py
New CourseSummary model, versioned system prompt, and course_summary_agent configured via model_for.
Quiz context agent
backend/agents/quiz_context.py
New QuizContext model mirroring legacy JSON schema, versioned prompt, and quiz_context_agent.
Quiz route rework
backend/routes/quiz.py
Removes _legacy_generate_quiz fallback (now returns 502 on agent failure) and replaces call_gemini_json in _update_context with quiz_context_agent via run_agent_sync.
Course context service rework
backend/services/course_context_service.py
Replaces call_gemini with course_summary_agent via run_agent_sync, retaining a deterministic fallback on failure.
Quiz route tests
backend/tests/test_quiz_routes.py
Mocks quiz_context_agent.run instead of call_gemini_json, adds 502-degradation tests replacing legacy fallback tests, removes legacy model-selection tests.
Course context tests
backend/tests/test_shared_course_context.py
Replaces prompt-augmentation tests with TestCourseSummaryAgent covering agent success/failure paths.
Migration spec
specs/145-quiz-course-context-agents.md
New spec documenting goals, test changes, and acceptance criteria for the agent migration.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Sequence Diagram(s)

sequenceDiagram
participant Client
participant QuizRoute as routes/quiz.py
participant QuizContextAgent as quiz_context_agent
participant CourseContextService as course_context_service.py
participant CourseSummaryAgent as course_summary_agent
Client->>QuizRoute: POST /api/quiz/generate
QuizRoute->>QuizRoute: run quiz agent
alt agent fails (UsageLimitExceeded / unexpected error)
QuizRoute-->>Client: HTTP 502
else success
QuizRoute-->>Client: quiz payload
end
Client->>QuizRoute: submit quiz answers
QuizRoute->>QuizContextAgent: run_agent_sync(quiz_context_agent.run(prompt))
QuizContextAgent-->>QuizRoute: QuizContext output
QuizRoute->>QuizRoute: save_quiz_context(result.output.model_dump())
CourseContextService->>CourseSummaryAgent: run_agent_sync(course_summary_agent.run(user_message))
alt agent succeeds
CourseSummaryAgent-->>CourseContextService: CourseSummary
else agent fails
CourseContextService->>CourseContextService: deterministic fallback summary
end
Loading

Possibly related issues

Possibly related PRs

  • SaplingLearn/Sapling#53: Both modify backend/services/course_context_service.py's course-level summary generation logic.
  • SaplingLearn/Sapling#71: Both modify backend/routes/quiz.py quiz generation/error-handling around agent-based and legacy fallback behavior.
  • SaplingLearn/Sapling#78: Both modify backend/agents/_providers.py's AgentTask/_DEFAULTS task registry.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 43.75% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly summarizes the main agent-migration change and fallback removal.
Description check✅ PassedThe description covers the goal, changes, related issue, testing, and reviewer notes, though with custom headings.
Linked Issues check✅ PassedThe PR matches #145 by removing the remaining call_gemini* seams and routing quiz and course-context flows through agents.
Out of Scope Changes check✅ PassedThe added files and edits stay aligned with the agent migration scope and avoid the referenced quiz scoring and graph-write areas.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/145-quiz-agent

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jul 1, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-staginge8127aeCommit Preview URL

Branch Preview URL
Jul 02 2026, 06:11 AM

Comment threadbackend/tests/test_shared_course_context.py Fixed

@coderabbitaicoderabbitaiBot 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.

Actionable comments posted: 1

🧹 Nitpick comments (3)
backend/routes/quiz.py (1)

202-207: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Update the stale legacy-fallback comment.

Lines 203-205 still say this routes to the legacy fallback, but the new catch block returns HTTP 502. Please align the comment with the removed fallback path.

Suggested comment update
- # All questions dropped — degrade to legacy rather than serve- # an empty quiz. Raise a sentinel that generate_quiz catches- # and routes to the legacy fallback.+ # All questions dropped — do not serve an empty quiz. Raise a+ # sentinel that generate_quiz catches and degrades to HTTP 502.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@backend/routes/quiz.py` around lines 202 - 207, The comment in the quiz
generation path is stale because the `RuntimeError` from `quiz_agent` no longer
routes to a legacy fallback and is now handled by the `generate_quiz` catch
block as an HTTP 502. Update the inline comment near the `wire_questions`
validation in `generate_quiz`/the surrounding fallback logic to describe the
current behavior only, removing any mention of legacy fallback and keeping the
wording aligned with the new error handling.
backend/services/course_context_service.py (2)

53-54: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

No guard against a falsy/empty agent summary.

If the agent returns an empty or whitespace-only summary, no exception is raised, so the deterministic fallback never kicks in and a blank summary gets persisted to offering_summary.summary_text.

🛡️ Suggested fix
 try:
result = run_agent_sync(course_summary_agent.run(user_message))
- return result.output.summary+ summary = result.output.summary+ if not summary or not summary.strip():+ raise ValueError("course_summary_agent returned empty summary")+ return summary
except Exception:
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@backend/services/course_context_service.py` around lines 53 - 54, The course
summary flow in course_context_service’s summary generation path currently
returns result.output.summary directly, which allows empty or whitespace-only
values to pass through. Update the logic around
run_agent_sync(course_summary_agent.run(user_message)) to validate
result.output.summary and raise/trigger the existing fallback when it is falsy
or blank, so the deterministic summary is used instead of persisting an empty
offering summary.

52-61: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Log the agent failure before falling back
Add a warning log with exc_info=True before returning the template so agent/runtime failures are visible instead of disappearing into the fallback path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@backend/services/course_context_service.py` around lines 52 - 61, The
fallback in course_context_service’s summary generation swallows agent/runtime
failures silently. Update the try/except around
run_agent_sync(course_summary_agent.run(user_message)) so the exception is
logged as a warning with exc_info=True before returning the template summary,
keeping the fallback behavior but making failures visible; use the surrounding
course_context_service method and course_summary_agent.run as the key locations.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@backend/tests/test_quiz_routes.py`:
- Around line 23-29: The submit-route tests are only neutralizing
`quiz_context_agent.run`, but `_update_context` in `routes.quiz` still persists
context via `save_quiz_context(...)`, which can trigger hidden writes or masked
failures. Update the affected tests/helpers around `_noop_ctx_agent()` to also
patch `routes.quiz.save_quiz_context` to a no-op wherever the submit flow is
exercised, so the background context update is fully disabled.
---
Nitpick comments:
In `@backend/routes/quiz.py`:
- Around line 202-207: The comment in the quiz generation path is stale because
the `RuntimeError` from `quiz_agent` no longer routes to a legacy fallback and
is now handled by the `generate_quiz` catch block as an HTTP 502. Update the
inline comment near the `wire_questions` validation in `generate_quiz`/the
surrounding fallback logic to describe the current behavior only, removing any
mention of legacy fallback and keeping the wording aligned with the new error
handling.
In `@backend/services/course_context_service.py`:
- Around line 53-54: The course summary flow in course_context_service’s summary
generation path currently returns result.output.summary directly, which allows
empty or whitespace-only values to pass through. Update the logic around
run_agent_sync(course_summary_agent.run(user_message)) to validate
result.output.summary and raise/trigger the existing fallback when it is falsy
or blank, so the deterministic summary is used instead of persisting an empty
offering summary.
- Around line 52-61: The fallback in course_context_service’s summary generation
swallows agent/runtime failures silently. Update the try/except around
run_agent_sync(course_summary_agent.run(user_message)) so the exception is
logged as a warning with exc_info=True before returning the template summary,
keeping the fallback behavior but making failures visible; use the surrounding
course_context_service method and course_summary_agent.run as the key locations.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c7510e3b-9aed-40df-a904-a8939c75af36

📥 Commits

Reviewing files that changed from the base of the PR and between 5de742e and 4e1b878.

📒 Files selected for processing (8)
  • backend/agents/_providers.py
  • backend/agents/course_summary.py
  • backend/agents/quiz_context.py
  • backend/routes/quiz.py
  • backend/services/course_context_service.py
  • backend/tests/test_quiz_routes.py
  • backend/tests/test_shared_course_context.py
  • specs/145-quiz-course-context-agents.md

Comment threadbackend/tests/test_quiz_routes.py
Jose-Gael-Cruz-Lopezand others added 2 commits July 1, 2026 13:17
…_context in submit mocks
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The all-questions-drift branch's comment still described routing to the
legacy fallback, which #145 deleted. The RuntimeError is now caught by
generate_quiz's bare-except and returned as HTTP 502 (per
test_degrades_when_all_questions_drift). Comment-only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@AndresL230
AndresL230 merged commit f472300 into mainJul 2, 2026
6 checks passed
@AndresL230
AndresL230 deleted the feat/145-quiz-agent branch August 2, 2026 18:30
Sign up for freeto 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.

[P2] Agent migration: quiz generation + course-context regen → agent

2 participants

@Jose-Gael-Cruz-Lopez@AndresL230
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

feat(quiz): quiz-context + course-summary → agents, retire quiz-gen fallback (#145) - #299

Merged
AndresL230 merged 3 commits into
mainfrom
feat/145-quiz-agent
Jul 2, 2026
Merged

feat(quiz): quiz-context + course-summary → agents, retire quiz-gen fallback (#145)#299
AndresL230 merged 3 commits into
mainfrom
feat/145-quiz-agent

Conversation

@Jose-Gael-Cruz-Lopez

@Jose-Gael-Cruz-LopezJose-Gael-Cruz-Lopez commented Jul 1, 2026

Copy link
Copy Markdown
Member

Closes#145 (Agent-migration epic #152, milestone #2).

What

Removes the last three raw call_gemini* seams from the quiz surface, folding them into the agent layer:

SiteBeforeAfter
quiz.py post-submit background taskcall_gemini_json (quiz-context regen)new agents/quiz_context.py (quiz_context_agent)
course_context_service.pycall_gemini (class summary)new agents/course_summary.py (course_summary_agent)
quiz.py_legacy_generate_quizcall_gemini_json fallbackremoved → graceful 502

Notes

  • QuizContext mirrors quiz_context_update.txt exactly (weak_areas, common_mistakes, questions_seen_summary, recommended_difficulty, notes); _update_context saves .model_dump(), so the stored quiz_context.context_json shape is unchanged.
  • Course summary keeps its deterministic template fallback on agent failure (no second LLM call), so a summary is always produced.
  • Quiz-gen fallback removed: on agent guardrail-trip or unexpected failure, generate_quiz returns 502 rather than serving a quiz from a second LLM path. The pre-agent 404 (unknown concept node) is unchanged, and the fast/smart model_pref override still works on the agent path.
  • Both sync call sites use the run_agent_sync bridge (from [P2] Agent migration: remaining one-shot LLM calls (study guide, social, health) → agents/seam #147).

⚠️ Coordination (#128/#129)

This PR is scoped to the LLM seams only. It does not touch quiz scoring, apply_graph_update, or the quiz_attempts writes in submit_quiz — the #128/#129 territory. The only change inside submit_quiz is swapping the background context-update's LLM call for the agent.

Testing

  • Rewrote the legacy-fallback quiz tests → degrade-to-502; repointed the 4 submit context-update patches onto quiz_context_agent; removed the obsolete legacy prompt-augmentation class + legacy-model tests; added quiz-context-save, course-summary success/fallback, and degrade tests.
  • Full backend suite: 826 passed (the 2 test_storage_service failures pre-exist on main — missing SUPABASE env). ruff clean.

Out of scope: flashcards (#146), deleting services/gemini_service.py (#151).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added AI-powered course summaries for instructors.
    • Added AI-generated quiz context updates to improve follow-up learning notes.
  • Bug Fixes

    • Quiz generation now fails gracefully with a clear temporary-unavailable response instead of switching to an older fallback path.
    • Course and quiz context updates now use more consistent, structured outputs.

…uiz-gen fallback (#145)
Removes the last raw call_gemini* seams from the quiz surface:
- New agents/quiz_context.py (QuizContext: weak_areas, common_mistakes,
questions_seen_summary, recommended_difficulty, notes — mirrors
quiz_context_update.txt). routes/quiz.py's post-submit background task runs it
via run_agent_sync and saves .model_dump() (unchanged context_json shape).
- New agents/course_summary.py (CourseSummary.summary). course_context_service's
_generate_summary_with_gemini runs it via run_agent_sync and keeps the
deterministic template fallback on agent failure (no second LLM call).
- Removed _legacy_generate_quiz + the MODEL_LITE/MODEL_SMART/call_gemini_json
import. On agent guardrail-trip or failure, generate_quiz now returns 502
instead of a raw-Gemini fallback; the pre-agent 404 (unknown node) is
unchanged. _providers registers course_summary (flash) + quiz_context (lite).
Coordination: does NOT touch scoring / apply_graph_update / quiz_attempts writes
(#128/#129 territory) — only the LLM seams.
Tests: rewrote the legacy-fallback quiz tests to degrade-to-502; repointed the
submit context-update patches onto quiz_context_agent; removed the obsolete
legacy prompt-augmentation + legacy-model tests; added quiz-context save,
course-summary success/fallback tests. Full suite 826 passed (2 pre-existing
storage-env failures). ruff clean.
Spec: specs/145-quiz-course-context-agents.md
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented Jul 1, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@AndresL230, you've reached your PR review limit, so we couldn't start this review.

Next review available in:31 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9c528394-d83a-42ad-98cc-2b4618ab8797

📥 Commits

Reviewing files that changed from the base of the PR and between 4e1b878 and e8127ae.

📒 Files selected for processing (3)
  • backend/routes/quiz.py
  • backend/tests/test_quiz_routes.py
  • backend/tests/test_shared_course_context.py
📝 Walkthrough

Walkthrough

This PR migrates quiz generation and course-context summary regeneration from raw Gemini calls to typed Pydantic AI agents. It adds course_summary and quiz_context agents, registers them in the provider task registry, wires them into routes/quiz.py and course_context_service.py, removes the legacy quiz-generation fallback (returning HTTP 502 on failure instead), and updates tests plus a new migration spec.

Changes

Agent migration

Layer / File(s)Summary
Provider task registry
backend/agents/_providers.py
Adds course_summary and quiz_context to AgentTask and their default models to _DEFAULTS.
Course summary agent
backend/agents/course_summary.py
New CourseSummary model, versioned system prompt, and course_summary_agent configured via model_for.
Quiz context agent
backend/agents/quiz_context.py
New QuizContext model mirroring legacy JSON schema, versioned prompt, and quiz_context_agent.
Quiz route rework
backend/routes/quiz.py
Removes _legacy_generate_quiz fallback (now returns 502 on agent failure) and replaces call_gemini_json in _update_context with quiz_context_agent via run_agent_sync.
Course context service rework
backend/services/course_context_service.py
Replaces call_gemini with course_summary_agent via run_agent_sync, retaining a deterministic fallback on failure.
Quiz route tests
backend/tests/test_quiz_routes.py
Mocks quiz_context_agent.run instead of call_gemini_json, adds 502-degradation tests replacing legacy fallback tests, removes legacy model-selection tests.
Course context tests
backend/tests/test_shared_course_context.py
Replaces prompt-augmentation tests with TestCourseSummaryAgent covering agent success/failure paths.
Migration spec
specs/145-quiz-course-context-agents.md
New spec documenting goals, test changes, and acceptance criteria for the agent migration.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Sequence Diagram(s)

sequenceDiagram
participant Client
participant QuizRoute as routes/quiz.py
participant QuizContextAgent as quiz_context_agent
participant CourseContextService as course_context_service.py
participant CourseSummaryAgent as course_summary_agent
Client->>QuizRoute: POST /api/quiz/generate
QuizRoute->>QuizRoute: run quiz agent
alt agent fails (UsageLimitExceeded / unexpected error)
QuizRoute-->>Client: HTTP 502
else success
QuizRoute-->>Client: quiz payload
end
Client->>QuizRoute: submit quiz answers
QuizRoute->>QuizContextAgent: run_agent_sync(quiz_context_agent.run(prompt))
QuizContextAgent-->>QuizRoute: QuizContext output
QuizRoute->>QuizRoute: save_quiz_context(result.output.model_dump())
CourseContextService->>CourseSummaryAgent: run_agent_sync(course_summary_agent.run(user_message))
alt agent succeeds
CourseSummaryAgent-->>CourseContextService: CourseSummary
else agent fails
CourseContextService->>CourseContextService: deterministic fallback summary
end
Loading

Possibly related issues

Possibly related PRs

  • SaplingLearn/Sapling#53: Both modify backend/services/course_context_service.py's course-level summary generation logic.
  • SaplingLearn/Sapling#71: Both modify backend/routes/quiz.py quiz generation/error-handling around agent-based and legacy fallback behavior.
  • SaplingLearn/Sapling#78: Both modify backend/agents/_providers.py's AgentTask/_DEFAULTS task registry.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 43.75% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly summarizes the main agent-migration change and fallback removal.
Description check✅ PassedThe description covers the goal, changes, related issue, testing, and reviewer notes, though with custom headings.
Linked Issues check✅ PassedThe PR matches #145 by removing the remaining call_gemini* seams and routing quiz and course-context flows through agents.
Out of Scope Changes check✅ PassedThe added files and edits stay aligned with the agent migration scope and avoid the referenced quiz scoring and graph-write areas.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/145-quiz-agent

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jul 1, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-staginge8127aeCommit Preview URL

Branch Preview URL
Jul 02 2026, 06:11 AM

Comment threadbackend/tests/test_shared_course_context.py Fixed

@coderabbitaicoderabbitaiBot 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.

Actionable comments posted: 1

🧹 Nitpick comments (3)
backend/routes/quiz.py (1)

202-207: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Update the stale legacy-fallback comment.

Lines 203-205 still say this routes to the legacy fallback, but the new catch block returns HTTP 502. Please align the comment with the removed fallback path.

Suggested comment update
- # All questions dropped — degrade to legacy rather than serve- # an empty quiz. Raise a sentinel that generate_quiz catches- # and routes to the legacy fallback.+ # All questions dropped — do not serve an empty quiz. Raise a+ # sentinel that generate_quiz catches and degrades to HTTP 502.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@backend/routes/quiz.py` around lines 202 - 207, The comment in the quiz
generation path is stale because the `RuntimeError` from `quiz_agent` no longer
routes to a legacy fallback and is now handled by the `generate_quiz` catch
block as an HTTP 502. Update the inline comment near the `wire_questions`
validation in `generate_quiz`/the surrounding fallback logic to describe the
current behavior only, removing any mention of legacy fallback and keeping the
wording aligned with the new error handling.
backend/services/course_context_service.py (2)

53-54: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

No guard against a falsy/empty agent summary.

If the agent returns an empty or whitespace-only summary, no exception is raised, so the deterministic fallback never kicks in and a blank summary gets persisted to offering_summary.summary_text.

🛡️ Suggested fix
 try:
result = run_agent_sync(course_summary_agent.run(user_message))
- return result.output.summary+ summary = result.output.summary+ if not summary or not summary.strip():+ raise ValueError("course_summary_agent returned empty summary")+ return summary
except Exception:
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@backend/services/course_context_service.py` around lines 53 - 54, The course
summary flow in course_context_service’s summary generation path currently
returns result.output.summary directly, which allows empty or whitespace-only
values to pass through. Update the logic around
run_agent_sync(course_summary_agent.run(user_message)) to validate
result.output.summary and raise/trigger the existing fallback when it is falsy
or blank, so the deterministic summary is used instead of persisting an empty
offering summary.

52-61: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Log the agent failure before falling back
Add a warning log with exc_info=True before returning the template so agent/runtime failures are visible instead of disappearing into the fallback path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@backend/services/course_context_service.py` around lines 52 - 61, The
fallback in course_context_service’s summary generation swallows agent/runtime
failures silently. Update the try/except around
run_agent_sync(course_summary_agent.run(user_message)) so the exception is
logged as a warning with exc_info=True before returning the template summary,
keeping the fallback behavior but making failures visible; use the surrounding
course_context_service method and course_summary_agent.run as the key locations.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@backend/tests/test_quiz_routes.py`:
- Around line 23-29: The submit-route tests are only neutralizing
`quiz_context_agent.run`, but `_update_context` in `routes.quiz` still persists
context via `save_quiz_context(...)`, which can trigger hidden writes or masked
failures. Update the affected tests/helpers around `_noop_ctx_agent()` to also
patch `routes.quiz.save_quiz_context` to a no-op wherever the submit flow is
exercised, so the background context update is fully disabled.
---
Nitpick comments:
In `@backend/routes/quiz.py`:
- Around line 202-207: The comment in the quiz generation path is stale because
the `RuntimeError` from `quiz_agent` no longer routes to a legacy fallback and
is now handled by the `generate_quiz` catch block as an HTTP 502. Update the
inline comment near the `wire_questions` validation in `generate_quiz`/the
surrounding fallback logic to describe the current behavior only, removing any
mention of legacy fallback and keeping the wording aligned with the new error
handling.
In `@backend/services/course_context_service.py`:
- Around line 53-54: The course summary flow in course_context_service’s summary
generation path currently returns result.output.summary directly, which allows
empty or whitespace-only values to pass through. Update the logic around
run_agent_sync(course_summary_agent.run(user_message)) to validate
result.output.summary and raise/trigger the existing fallback when it is falsy
or blank, so the deterministic summary is used instead of persisting an empty
offering summary.
- Around line 52-61: The fallback in course_context_service’s summary generation
swallows agent/runtime failures silently. Update the try/except around
run_agent_sync(course_summary_agent.run(user_message)) so the exception is
logged as a warning with exc_info=True before returning the template summary,
keeping the fallback behavior but making failures visible; use the surrounding
course_context_service method and course_summary_agent.run as the key locations.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c7510e3b-9aed-40df-a904-a8939c75af36

📥 Commits

Reviewing files that changed from the base of the PR and between 5de742e and 4e1b878.

📒 Files selected for processing (8)
  • backend/agents/_providers.py
  • backend/agents/course_summary.py
  • backend/agents/quiz_context.py
  • backend/routes/quiz.py
  • backend/services/course_context_service.py
  • backend/tests/test_quiz_routes.py
  • backend/tests/test_shared_course_context.py
  • specs/145-quiz-course-context-agents.md

Comment threadbackend/tests/test_quiz_routes.py
Jose-Gael-Cruz-Lopezand others added 2 commits July 1, 2026 13:17
…_context in submit mocks
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The all-questions-drift branch's comment still described routing to the
legacy fallback, which #145 deleted. The RuntimeError is now caught by
generate_quiz's bare-except and returned as HTTP 502 (per
test_degrades_when_all_questions_drift). Comment-only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@AndresL230
AndresL230 merged commit f472300 into mainJul 2, 2026
6 checks passed
@AndresL230
AndresL230 deleted the feat/145-quiz-agent branch August 2, 2026 18:30
Sign up for freeto 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.

[P2] Agent migration: quiz generation + course-context regen → agent

2 participants

@Jose-Gael-Cruz-Lopez@AndresL230
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(quiz): quiz-context + course-summary → agents, retire quiz-gen fallback (#145) - #299

Merged
AndresL230 merged 3 commits into
mainfrom
feat/145-quiz-agent
Jul 2, 2026
Merged

feat(quiz): quiz-context + course-summary → agents, retire quiz-gen fallback (#145)#299
AndresL230 merged 3 commits into
mainfrom
feat/145-quiz-agent

Conversation

@Jose-Gael-Cruz-Lopez

@Jose-Gael-Cruz-LopezJose-Gael-Cruz-Lopez commented Jul 1, 2026

Copy link
Copy Markdown
Member

Closes#145 (Agent-migration epic #152, milestone #2).

What

Removes the last three raw call_gemini* seams from the quiz surface, folding them into the agent layer:

SiteBeforeAfter
quiz.py post-submit background taskcall_gemini_json (quiz-context regen)new agents/quiz_context.py (quiz_context_agent)
course_context_service.pycall_gemini (class summary)new agents/course_summary.py (course_summary_agent)
quiz.py_legacy_generate_quizcall_gemini_json fallbackremoved → graceful 502

Notes

  • QuizContext mirrors quiz_context_update.txt exactly (weak_areas, common_mistakes, questions_seen_summary, recommended_difficulty, notes); _update_context saves .model_dump(), so the stored quiz_context.context_json shape is unchanged.
  • Course summary keeps its deterministic template fallback on agent failure (no second LLM call), so a summary is always produced.
  • Quiz-gen fallback removed: on agent guardrail-trip or unexpected failure, generate_quiz returns 502 rather than serving a quiz from a second LLM path. The pre-agent 404 (unknown concept node) is unchanged, and the fast/smart model_pref override still works on the agent path.
  • Both sync call sites use the run_agent_sync bridge (from [P2] Agent migration: remaining one-shot LLM calls (study guide, social, health) → agents/seam #147).

⚠️ Coordination (#128/#129)

This PR is scoped to the LLM seams only. It does not touch quiz scoring, apply_graph_update, or the quiz_attempts writes in submit_quiz — the #128/#129 territory. The only change inside submit_quiz is swapping the background context-update's LLM call for the agent.

Testing

  • Rewrote the legacy-fallback quiz tests → degrade-to-502; repointed the 4 submit context-update patches onto quiz_context_agent; removed the obsolete legacy prompt-augmentation class + legacy-model tests; added quiz-context-save, course-summary success/fallback, and degrade tests.
  • Full backend suite: 826 passed (the 2 test_storage_service failures pre-exist on main — missing SUPABASE env). ruff clean.

Out of scope: flashcards (#146), deleting services/gemini_service.py (#151).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added AI-powered course summaries for instructors.
    • Added AI-generated quiz context updates to improve follow-up learning notes.
  • Bug Fixes

    • Quiz generation now fails gracefully with a clear temporary-unavailable response instead of switching to an older fallback path.
    • Course and quiz context updates now use more consistent, structured outputs.

…uiz-gen fallback (#145)
Removes the last raw call_gemini* seams from the quiz surface:
- New agents/quiz_context.py (QuizContext: weak_areas, common_mistakes,
questions_seen_summary, recommended_difficulty, notes — mirrors
quiz_context_update.txt). routes/quiz.py's post-submit background task runs it
via run_agent_sync and saves .model_dump() (unchanged context_json shape).
- New agents/course_summary.py (CourseSummary.summary). course_context_service's
_generate_summary_with_gemini runs it via run_agent_sync and keeps the
deterministic template fallback on agent failure (no second LLM call).
- Removed _legacy_generate_quiz + the MODEL_LITE/MODEL_SMART/call_gemini_json
import. On agent guardrail-trip or failure, generate_quiz now returns 502
instead of a raw-Gemini fallback; the pre-agent 404 (unknown node) is
unchanged. _providers registers course_summary (flash) + quiz_context (lite).
Coordination: does NOT touch scoring / apply_graph_update / quiz_attempts writes
(#128/#129 territory) — only the LLM seams.
Tests: rewrote the legacy-fallback quiz tests to degrade-to-502; repointed the
submit context-update patches onto quiz_context_agent; removed the obsolete
legacy prompt-augmentation + legacy-model tests; added quiz-context save,
course-summary success/fallback tests. Full suite 826 passed (2 pre-existing
storage-env failures). ruff clean.
Spec: specs/145-quiz-course-context-agents.md
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented Jul 1, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@AndresL230, you've reached your PR review limit, so we couldn't start this review.

Next review available in:31 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9c528394-d83a-42ad-98cc-2b4618ab8797

📥 Commits

Reviewing files that changed from the base of the PR and between 4e1b878 and e8127ae.

📒 Files selected for processing (3)
  • backend/routes/quiz.py
  • backend/tests/test_quiz_routes.py
  • backend/tests/test_shared_course_context.py
📝 Walkthrough

Walkthrough

This PR migrates quiz generation and course-context summary regeneration from raw Gemini calls to typed Pydantic AI agents. It adds course_summary and quiz_context agents, registers them in the provider task registry, wires them into routes/quiz.py and course_context_service.py, removes the legacy quiz-generation fallback (returning HTTP 502 on failure instead), and updates tests plus a new migration spec.

Changes

Agent migration

Layer / File(s)Summary
Provider task registry
backend/agents/_providers.py
Adds course_summary and quiz_context to AgentTask and their default models to _DEFAULTS.
Course summary agent
backend/agents/course_summary.py
New CourseSummary model, versioned system prompt, and course_summary_agent configured via model_for.
Quiz context agent
backend/agents/quiz_context.py
New QuizContext model mirroring legacy JSON schema, versioned prompt, and quiz_context_agent.
Quiz route rework
backend/routes/quiz.py
Removes _legacy_generate_quiz fallback (now returns 502 on agent failure) and replaces call_gemini_json in _update_context with quiz_context_agent via run_agent_sync.
Course context service rework
backend/services/course_context_service.py
Replaces call_gemini with course_summary_agent via run_agent_sync, retaining a deterministic fallback on failure.
Quiz route tests
backend/tests/test_quiz_routes.py
Mocks quiz_context_agent.run instead of call_gemini_json, adds 502-degradation tests replacing legacy fallback tests, removes legacy model-selection tests.
Course context tests
backend/tests/test_shared_course_context.py
Replaces prompt-augmentation tests with TestCourseSummaryAgent covering agent success/failure paths.
Migration spec
specs/145-quiz-course-context-agents.md
New spec documenting goals, test changes, and acceptance criteria for the agent migration.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Sequence Diagram(s)

sequenceDiagram
participant Client
participant QuizRoute as routes/quiz.py
participant QuizContextAgent as quiz_context_agent
participant CourseContextService as course_context_service.py
participant CourseSummaryAgent as course_summary_agent
Client->>QuizRoute: POST /api/quiz/generate
QuizRoute->>QuizRoute: run quiz agent
alt agent fails (UsageLimitExceeded / unexpected error)
QuizRoute-->>Client: HTTP 502
else success
QuizRoute-->>Client: quiz payload
end
Client->>QuizRoute: submit quiz answers
QuizRoute->>QuizContextAgent: run_agent_sync(quiz_context_agent.run(prompt))
QuizContextAgent-->>QuizRoute: QuizContext output
QuizRoute->>QuizRoute: save_quiz_context(result.output.model_dump())
CourseContextService->>CourseSummaryAgent: run_agent_sync(course_summary_agent.run(user_message))
alt agent succeeds
CourseSummaryAgent-->>CourseContextService: CourseSummary
else agent fails
CourseContextService->>CourseContextService: deterministic fallback summary
end
Loading

Possibly related issues

Possibly related PRs

  • SaplingLearn/Sapling#53: Both modify backend/services/course_context_service.py's course-level summary generation logic.
  • SaplingLearn/Sapling#71: Both modify backend/routes/quiz.py quiz generation/error-handling around agent-based and legacy fallback behavior.
  • SaplingLearn/Sapling#78: Both modify backend/agents/_providers.py's AgentTask/_DEFAULTS task registry.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 43.75% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly summarizes the main agent-migration change and fallback removal.
Description check✅ PassedThe description covers the goal, changes, related issue, testing, and reviewer notes, though with custom headings.
Linked Issues check✅ PassedThe PR matches #145 by removing the remaining call_gemini* seams and routing quiz and course-context flows through agents.
Out of Scope Changes check✅ PassedThe added files and edits stay aligned with the agent migration scope and avoid the referenced quiz scoring and graph-write areas.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/145-quiz-agent

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jul 1, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-staginge8127aeCommit Preview URL

Branch Preview URL
Jul 02 2026, 06:11 AM

Comment threadbackend/tests/test_shared_course_context.py Fixed

@coderabbitaicoderabbitaiBot 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.

Actionable comments posted: 1

🧹 Nitpick comments (3)
backend/routes/quiz.py (1)

202-207: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Update the stale legacy-fallback comment.

Lines 203-205 still say this routes to the legacy fallback, but the new catch block returns HTTP 502. Please align the comment with the removed fallback path.

Suggested comment update
- # All questions dropped — degrade to legacy rather than serve- # an empty quiz. Raise a sentinel that generate_quiz catches- # and routes to the legacy fallback.+ # All questions dropped — do not serve an empty quiz. Raise a+ # sentinel that generate_quiz catches and degrades to HTTP 502.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@backend/routes/quiz.py` around lines 202 - 207, The comment in the quiz
generation path is stale because the `RuntimeError` from `quiz_agent` no longer
routes to a legacy fallback and is now handled by the `generate_quiz` catch
block as an HTTP 502. Update the inline comment near the `wire_questions`
validation in `generate_quiz`/the surrounding fallback logic to describe the
current behavior only, removing any mention of legacy fallback and keeping the
wording aligned with the new error handling.
backend/services/course_context_service.py (2)

53-54: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

No guard against a falsy/empty agent summary.

If the agent returns an empty or whitespace-only summary, no exception is raised, so the deterministic fallback never kicks in and a blank summary gets persisted to offering_summary.summary_text.

🛡️ Suggested fix
 try:
result = run_agent_sync(course_summary_agent.run(user_message))
- return result.output.summary+ summary = result.output.summary+ if not summary or not summary.strip():+ raise ValueError("course_summary_agent returned empty summary")+ return summary
except Exception:
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@backend/services/course_context_service.py` around lines 53 - 54, The course
summary flow in course_context_service’s summary generation path currently
returns result.output.summary directly, which allows empty or whitespace-only
values to pass through. Update the logic around
run_agent_sync(course_summary_agent.run(user_message)) to validate
result.output.summary and raise/trigger the existing fallback when it is falsy
or blank, so the deterministic summary is used instead of persisting an empty
offering summary.

52-61: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Log the agent failure before falling back
Add a warning log with exc_info=True before returning the template so agent/runtime failures are visible instead of disappearing into the fallback path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@backend/services/course_context_service.py` around lines 52 - 61, The
fallback in course_context_service’s summary generation swallows agent/runtime
failures silently. Update the try/except around
run_agent_sync(course_summary_agent.run(user_message)) so the exception is
logged as a warning with exc_info=True before returning the template summary,
keeping the fallback behavior but making failures visible; use the surrounding
course_context_service method and course_summary_agent.run as the key locations.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@backend/tests/test_quiz_routes.py`:
- Around line 23-29: The submit-route tests are only neutralizing
`quiz_context_agent.run`, but `_update_context` in `routes.quiz` still persists
context via `save_quiz_context(...)`, which can trigger hidden writes or masked
failures. Update the affected tests/helpers around `_noop_ctx_agent()` to also
patch `routes.quiz.save_quiz_context` to a no-op wherever the submit flow is
exercised, so the background context update is fully disabled.
---
Nitpick comments:
In `@backend/routes/quiz.py`:
- Around line 202-207: The comment in the quiz generation path is stale because
the `RuntimeError` from `quiz_agent` no longer routes to a legacy fallback and
is now handled by the `generate_quiz` catch block as an HTTP 502. Update the
inline comment near the `wire_questions` validation in `generate_quiz`/the
surrounding fallback logic to describe the current behavior only, removing any
mention of legacy fallback and keeping the wording aligned with the new error
handling.
In `@backend/services/course_context_service.py`:
- Around line 53-54: The course summary flow in course_context_service’s summary
generation path currently returns result.output.summary directly, which allows
empty or whitespace-only values to pass through. Update the logic around
run_agent_sync(course_summary_agent.run(user_message)) to validate
result.output.summary and raise/trigger the existing fallback when it is falsy
or blank, so the deterministic summary is used instead of persisting an empty
offering summary.
- Around line 52-61: The fallback in course_context_service’s summary generation
swallows agent/runtime failures silently. Update the try/except around
run_agent_sync(course_summary_agent.run(user_message)) so the exception is
logged as a warning with exc_info=True before returning the template summary,
keeping the fallback behavior but making failures visible; use the surrounding
course_context_service method and course_summary_agent.run as the key locations.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c7510e3b-9aed-40df-a904-a8939c75af36

📥 Commits

Reviewing files that changed from the base of the PR and between 5de742e and 4e1b878.

📒 Files selected for processing (8)
  • backend/agents/_providers.py
  • backend/agents/course_summary.py
  • backend/agents/quiz_context.py
  • backend/routes/quiz.py
  • backend/services/course_context_service.py
  • backend/tests/test_quiz_routes.py
  • backend/tests/test_shared_course_context.py
  • specs/145-quiz-course-context-agents.md

Comment threadbackend/tests/test_quiz_routes.py
Jose-Gael-Cruz-Lopezand others added 2 commits July 1, 2026 13:17
…_context in submit mocks
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The all-questions-drift branch's comment still described routing to the
legacy fallback, which #145 deleted. The RuntimeError is now caught by
generate_quiz's bare-except and returned as HTTP 502 (per
test_degrades_when_all_questions_drift). Comment-only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@AndresL230
AndresL230 merged commit f472300 into mainJul 2, 2026
6 checks passed
@AndresL230
AndresL230 deleted the feat/145-quiz-agent branch August 2, 2026 18:30
Sign up for freeto 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.

[P2] Agent migration: quiz generation + course-context regen → agent

2 participants

@Jose-Gael-Cruz-Lopez@AndresL230
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(quiz): quiz-context + course-summary → agents, retire quiz-gen fallback (#145) - #299

Merged
AndresL230 merged 3 commits into
mainfrom
feat/145-quiz-agent
Jul 2, 2026
Merged

feat(quiz): quiz-context + course-summary → agents, retire quiz-gen fallback (#145)#299
AndresL230 merged 3 commits into
mainfrom
feat/145-quiz-agent

Conversation

@Jose-Gael-Cruz-Lopez

@Jose-Gael-Cruz-LopezJose-Gael-Cruz-Lopez commented Jul 1, 2026

Copy link
Copy Markdown
Member

Closes#145 (Agent-migration epic #152, milestone #2).

What

Removes the last three raw call_gemini* seams from the quiz surface, folding them into the agent layer:

SiteBeforeAfter
quiz.py post-submit background taskcall_gemini_json (quiz-context regen)new agents/quiz_context.py (quiz_context_agent)
course_context_service.pycall_gemini (class summary)new agents/course_summary.py (course_summary_agent)
quiz.py_legacy_generate_quizcall_gemini_json fallbackremoved → graceful 502

Notes

  • QuizContext mirrors quiz_context_update.txt exactly (weak_areas, common_mistakes, questions_seen_summary, recommended_difficulty, notes); _update_context saves .model_dump(), so the stored quiz_context.context_json shape is unchanged.
  • Course summary keeps its deterministic template fallback on agent failure (no second LLM call), so a summary is always produced.
  • Quiz-gen fallback removed: on agent guardrail-trip or unexpected failure, generate_quiz returns 502 rather than serving a quiz from a second LLM path. The pre-agent 404 (unknown concept node) is unchanged, and the fast/smart model_pref override still works on the agent path.
  • Both sync call sites use the run_agent_sync bridge (from [P2] Agent migration: remaining one-shot LLM calls (study guide, social, health) → agents/seam #147).

⚠️ Coordination (#128/#129)

This PR is scoped to the LLM seams only. It does not touch quiz scoring, apply_graph_update, or the quiz_attempts writes in submit_quiz — the #128/#129 territory. The only change inside submit_quiz is swapping the background context-update's LLM call for the agent.

Testing

  • Rewrote the legacy-fallback quiz tests → degrade-to-502; repointed the 4 submit context-update patches onto quiz_context_agent; removed the obsolete legacy prompt-augmentation class + legacy-model tests; added quiz-context-save, course-summary success/fallback, and degrade tests.
  • Full backend suite: 826 passed (the 2 test_storage_service failures pre-exist on main — missing SUPABASE env). ruff clean.

Out of scope: flashcards (#146), deleting services/gemini_service.py (#151).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added AI-powered course summaries for instructors.
    • Added AI-generated quiz context updates to improve follow-up learning notes.
  • Bug Fixes

    • Quiz generation now fails gracefully with a clear temporary-unavailable response instead of switching to an older fallback path.
    • Course and quiz context updates now use more consistent, structured outputs.

…uiz-gen fallback (#145)
Removes the last raw call_gemini* seams from the quiz surface:
- New agents/quiz_context.py (QuizContext: weak_areas, common_mistakes,
questions_seen_summary, recommended_difficulty, notes — mirrors
quiz_context_update.txt). routes/quiz.py's post-submit background task runs it
via run_agent_sync and saves .model_dump() (unchanged context_json shape).
- New agents/course_summary.py (CourseSummary.summary). course_context_service's
_generate_summary_with_gemini runs it via run_agent_sync and keeps the
deterministic template fallback on agent failure (no second LLM call).
- Removed _legacy_generate_quiz + the MODEL_LITE/MODEL_SMART/call_gemini_json
import. On agent guardrail-trip or failure, generate_quiz now returns 502
instead of a raw-Gemini fallback; the pre-agent 404 (unknown node) is
unchanged. _providers registers course_summary (flash) + quiz_context (lite).
Coordination: does NOT touch scoring / apply_graph_update / quiz_attempts writes
(#128/#129 territory) — only the LLM seams.
Tests: rewrote the legacy-fallback quiz tests to degrade-to-502; repointed the
submit context-update patches onto quiz_context_agent; removed the obsolete
legacy prompt-augmentation + legacy-model tests; added quiz-context save,
course-summary success/fallback tests. Full suite 826 passed (2 pre-existing
storage-env failures). ruff clean.
Spec: specs/145-quiz-course-context-agents.md
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented Jul 1, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@AndresL230, you've reached your PR review limit, so we couldn't start this review.

Next review available in:31 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9c528394-d83a-42ad-98cc-2b4618ab8797

📥 Commits

Reviewing files that changed from the base of the PR and between 4e1b878 and e8127ae.

📒 Files selected for processing (3)
  • backend/routes/quiz.py
  • backend/tests/test_quiz_routes.py
  • backend/tests/test_shared_course_context.py
📝 Walkthrough

Walkthrough

This PR migrates quiz generation and course-context summary regeneration from raw Gemini calls to typed Pydantic AI agents. It adds course_summary and quiz_context agents, registers them in the provider task registry, wires them into routes/quiz.py and course_context_service.py, removes the legacy quiz-generation fallback (returning HTTP 502 on failure instead), and updates tests plus a new migration spec.

Changes

Agent migration

Layer / File(s)Summary
Provider task registry
backend/agents/_providers.py
Adds course_summary and quiz_context to AgentTask and their default models to _DEFAULTS.
Course summary agent
backend/agents/course_summary.py
New CourseSummary model, versioned system prompt, and course_summary_agent configured via model_for.
Quiz context agent
backend/agents/quiz_context.py
New QuizContext model mirroring legacy JSON schema, versioned prompt, and quiz_context_agent.
Quiz route rework
backend/routes/quiz.py
Removes _legacy_generate_quiz fallback (now returns 502 on agent failure) and replaces call_gemini_json in _update_context with quiz_context_agent via run_agent_sync.
Course context service rework
backend/services/course_context_service.py
Replaces call_gemini with course_summary_agent via run_agent_sync, retaining a deterministic fallback on failure.
Quiz route tests
backend/tests/test_quiz_routes.py
Mocks quiz_context_agent.run instead of call_gemini_json, adds 502-degradation tests replacing legacy fallback tests, removes legacy model-selection tests.
Course context tests
backend/tests/test_shared_course_context.py
Replaces prompt-augmentation tests with TestCourseSummaryAgent covering agent success/failure paths.
Migration spec
specs/145-quiz-course-context-agents.md
New spec documenting goals, test changes, and acceptance criteria for the agent migration.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Sequence Diagram(s)

sequenceDiagram
participant Client
participant QuizRoute as routes/quiz.py
participant QuizContextAgent as quiz_context_agent
participant CourseContextService as course_context_service.py
participant CourseSummaryAgent as course_summary_agent
Client->>QuizRoute: POST /api/quiz/generate
QuizRoute->>QuizRoute: run quiz agent
alt agent fails (UsageLimitExceeded / unexpected error)
QuizRoute-->>Client: HTTP 502
else success
QuizRoute-->>Client: quiz payload
end
Client->>QuizRoute: submit quiz answers
QuizRoute->>QuizContextAgent: run_agent_sync(quiz_context_agent.run(prompt))
QuizContextAgent-->>QuizRoute: QuizContext output
QuizRoute->>QuizRoute: save_quiz_context(result.output.model_dump())
CourseContextService->>CourseSummaryAgent: run_agent_sync(course_summary_agent.run(user_message))
alt agent succeeds
CourseSummaryAgent-->>CourseContextService: CourseSummary
else agent fails
CourseContextService->>CourseContextService: deterministic fallback summary
end
Loading

Possibly related issues

Possibly related PRs

  • SaplingLearn/Sapling#53: Both modify backend/services/course_context_service.py's course-level summary generation logic.
  • SaplingLearn/Sapling#71: Both modify backend/routes/quiz.py quiz generation/error-handling around agent-based and legacy fallback behavior.
  • SaplingLearn/Sapling#78: Both modify backend/agents/_providers.py's AgentTask/_DEFAULTS task registry.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 43.75% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly summarizes the main agent-migration change and fallback removal.
Description check✅ PassedThe description covers the goal, changes, related issue, testing, and reviewer notes, though with custom headings.
Linked Issues check✅ PassedThe PR matches #145 by removing the remaining call_gemini* seams and routing quiz and course-context flows through agents.
Out of Scope Changes check✅ PassedThe added files and edits stay aligned with the agent migration scope and avoid the referenced quiz scoring and graph-write areas.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/145-quiz-agent

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jul 1, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-staginge8127aeCommit Preview URL

Branch Preview URL
Jul 02 2026, 06:11 AM

Comment threadbackend/tests/test_shared_course_context.py Fixed

@coderabbitaicoderabbitaiBot 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.

Actionable comments posted: 1

🧹 Nitpick comments (3)
backend/routes/quiz.py (1)

202-207: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Update the stale legacy-fallback comment.

Lines 203-205 still say this routes to the legacy fallback, but the new catch block returns HTTP 502. Please align the comment with the removed fallback path.

Suggested comment update
- # All questions dropped — degrade to legacy rather than serve- # an empty quiz. Raise a sentinel that generate_quiz catches- # and routes to the legacy fallback.+ # All questions dropped — do not serve an empty quiz. Raise a+ # sentinel that generate_quiz catches and degrades to HTTP 502.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@backend/routes/quiz.py` around lines 202 - 207, The comment in the quiz
generation path is stale because the `RuntimeError` from `quiz_agent` no longer
routes to a legacy fallback and is now handled by the `generate_quiz` catch
block as an HTTP 502. Update the inline comment near the `wire_questions`
validation in `generate_quiz`/the surrounding fallback logic to describe the
current behavior only, removing any mention of legacy fallback and keeping the
wording aligned with the new error handling.
backend/services/course_context_service.py (2)

53-54: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

No guard against a falsy/empty agent summary.

If the agent returns an empty or whitespace-only summary, no exception is raised, so the deterministic fallback never kicks in and a blank summary gets persisted to offering_summary.summary_text.

🛡️ Suggested fix
 try:
result = run_agent_sync(course_summary_agent.run(user_message))
- return result.output.summary+ summary = result.output.summary+ if not summary or not summary.strip():+ raise ValueError("course_summary_agent returned empty summary")+ return summary
except Exception:
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@backend/services/course_context_service.py` around lines 53 - 54, The course
summary flow in course_context_service’s summary generation path currently
returns result.output.summary directly, which allows empty or whitespace-only
values to pass through. Update the logic around
run_agent_sync(course_summary_agent.run(user_message)) to validate
result.output.summary and raise/trigger the existing fallback when it is falsy
or blank, so the deterministic summary is used instead of persisting an empty
offering summary.

52-61: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Log the agent failure before falling back
Add a warning log with exc_info=True before returning the template so agent/runtime failures are visible instead of disappearing into the fallback path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@backend/services/course_context_service.py` around lines 52 - 61, The
fallback in course_context_service’s summary generation swallows agent/runtime
failures silently. Update the try/except around
run_agent_sync(course_summary_agent.run(user_message)) so the exception is
logged as a warning with exc_info=True before returning the template summary,
keeping the fallback behavior but making failures visible; use the surrounding
course_context_service method and course_summary_agent.run as the key locations.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@backend/tests/test_quiz_routes.py`:
- Around line 23-29: The submit-route tests are only neutralizing
`quiz_context_agent.run`, but `_update_context` in `routes.quiz` still persists
context via `save_quiz_context(...)`, which can trigger hidden writes or masked
failures. Update the affected tests/helpers around `_noop_ctx_agent()` to also
patch `routes.quiz.save_quiz_context` to a no-op wherever the submit flow is
exercised, so the background context update is fully disabled.
---
Nitpick comments:
In `@backend/routes/quiz.py`:
- Around line 202-207: The comment in the quiz generation path is stale because
the `RuntimeError` from `quiz_agent` no longer routes to a legacy fallback and
is now handled by the `generate_quiz` catch block as an HTTP 502. Update the
inline comment near the `wire_questions` validation in `generate_quiz`/the
surrounding fallback logic to describe the current behavior only, removing any
mention of legacy fallback and keeping the wording aligned with the new error
handling.
In `@backend/services/course_context_service.py`:
- Around line 53-54: The course summary flow in course_context_service’s summary
generation path currently returns result.output.summary directly, which allows
empty or whitespace-only values to pass through. Update the logic around
run_agent_sync(course_summary_agent.run(user_message)) to validate
result.output.summary and raise/trigger the existing fallback when it is falsy
or blank, so the deterministic summary is used instead of persisting an empty
offering summary.
- Around line 52-61: The fallback in course_context_service’s summary generation
swallows agent/runtime failures silently. Update the try/except around
run_agent_sync(course_summary_agent.run(user_message)) so the exception is
logged as a warning with exc_info=True before returning the template summary,
keeping the fallback behavior but making failures visible; use the surrounding
course_context_service method and course_summary_agent.run as the key locations.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c7510e3b-9aed-40df-a904-a8939c75af36

📥 Commits

Reviewing files that changed from the base of the PR and between 5de742e and 4e1b878.

📒 Files selected for processing (8)
  • backend/agents/_providers.py
  • backend/agents/course_summary.py
  • backend/agents/quiz_context.py
  • backend/routes/quiz.py
  • backend/services/course_context_service.py
  • backend/tests/test_quiz_routes.py
  • backend/tests/test_shared_course_context.py
  • specs/145-quiz-course-context-agents.md

Comment threadbackend/tests/test_quiz_routes.py
Jose-Gael-Cruz-Lopezand others added 2 commits July 1, 2026 13:17
…_context in submit mocks
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The all-questions-drift branch's comment still described routing to the
legacy fallback, which #145 deleted. The RuntimeError is now caught by
generate_quiz's bare-except and returned as HTTP 502 (per
test_degrades_when_all_questions_drift). Comment-only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@AndresL230
AndresL230 merged commit f472300 into mainJul 2, 2026
6 checks passed
@AndresL230
AndresL230 deleted the feat/145-quiz-agent branch August 2, 2026 18:30
Sign up for freeto 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.

[P2] Agent migration: quiz generation + course-context regen → agent

2 participants

@Jose-Gael-Cruz-Lopez@AndresL230
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

feat(quiz): quiz-context + course-summary → agents, retire quiz-gen fallback (#145) - #299

Merged
AndresL230 merged 3 commits into
mainfrom
feat/145-quiz-agent
Jul 2, 2026
Merged

feat(quiz): quiz-context + course-summary → agents, retire quiz-gen fallback (#145)#299
AndresL230 merged 3 commits into
mainfrom
feat/145-quiz-agent

Conversation

@Jose-Gael-Cruz-Lopez

@Jose-Gael-Cruz-LopezJose-Gael-Cruz-Lopez commented Jul 1, 2026

Copy link
Copy Markdown
Member

Closes#145 (Agent-migration epic #152, milestone #2).

What

Removes the last three raw call_gemini* seams from the quiz surface, folding them into the agent layer:

SiteBeforeAfter
quiz.py post-submit background taskcall_gemini_json (quiz-context regen)new agents/quiz_context.py (quiz_context_agent)
course_context_service.pycall_gemini (class summary)new agents/course_summary.py (course_summary_agent)
quiz.py_legacy_generate_quizcall_gemini_json fallbackremoved → graceful 502

Notes

  • QuizContext mirrors quiz_context_update.txt exactly (weak_areas, common_mistakes, questions_seen_summary, recommended_difficulty, notes); _update_context saves .model_dump(), so the stored quiz_context.context_json shape is unchanged.
  • Course summary keeps its deterministic template fallback on agent failure (no second LLM call), so a summary is always produced.
  • Quiz-gen fallback removed: on agent guardrail-trip or unexpected failure, generate_quiz returns 502 rather than serving a quiz from a second LLM path. The pre-agent 404 (unknown concept node) is unchanged, and the fast/smart model_pref override still works on the agent path.
  • Both sync call sites use the run_agent_sync bridge (from [P2] Agent migration: remaining one-shot LLM calls (study guide, social, health) → agents/seam #147).

⚠️ Coordination (#128/#129)

This PR is scoped to the LLM seams only. It does not touch quiz scoring, apply_graph_update, or the quiz_attempts writes in submit_quiz — the #128/#129 territory. The only change inside submit_quiz is swapping the background context-update's LLM call for the agent.

Testing

  • Rewrote the legacy-fallback quiz tests → degrade-to-502; repointed the 4 submit context-update patches onto quiz_context_agent; removed the obsolete legacy prompt-augmentation class + legacy-model tests; added quiz-context-save, course-summary success/fallback, and degrade tests.
  • Full backend suite: 826 passed (the 2 test_storage_service failures pre-exist on main — missing SUPABASE env). ruff clean.

Out of scope: flashcards (#146), deleting services/gemini_service.py (#151).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added AI-powered course summaries for instructors.
    • Added AI-generated quiz context updates to improve follow-up learning notes.
  • Bug Fixes

    • Quiz generation now fails gracefully with a clear temporary-unavailable response instead of switching to an older fallback path.
    • Course and quiz context updates now use more consistent, structured outputs.

…uiz-gen fallback (#145)
Removes the last raw call_gemini* seams from the quiz surface:
- New agents/quiz_context.py (QuizContext: weak_areas, common_mistakes,
questions_seen_summary, recommended_difficulty, notes — mirrors
quiz_context_update.txt). routes/quiz.py's post-submit background task runs it
via run_agent_sync and saves .model_dump() (unchanged context_json shape).
- New agents/course_summary.py (CourseSummary.summary). course_context_service's
_generate_summary_with_gemini runs it via run_agent_sync and keeps the
deterministic template fallback on agent failure (no second LLM call).
- Removed _legacy_generate_quiz + the MODEL_LITE/MODEL_SMART/call_gemini_json
import. On agent guardrail-trip or failure, generate_quiz now returns 502
instead of a raw-Gemini fallback; the pre-agent 404 (unknown node) is
unchanged. _providers registers course_summary (flash) + quiz_context (lite).
Coordination: does NOT touch scoring / apply_graph_update / quiz_attempts writes
(#128/#129 territory) — only the LLM seams.
Tests: rewrote the legacy-fallback quiz tests to degrade-to-502; repointed the
submit context-update patches onto quiz_context_agent; removed the obsolete
legacy prompt-augmentation + legacy-model tests; added quiz-context save,
course-summary success/fallback tests. Full suite 826 passed (2 pre-existing
storage-env failures). ruff clean.
Spec: specs/145-quiz-course-context-agents.md
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented Jul 1, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@AndresL230, you've reached your PR review limit, so we couldn't start this review.

Next review available in:31 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9c528394-d83a-42ad-98cc-2b4618ab8797

📥 Commits

Reviewing files that changed from the base of the PR and between 4e1b878 and e8127ae.

📒 Files selected for processing (3)
  • backend/routes/quiz.py
  • backend/tests/test_quiz_routes.py
  • backend/tests/test_shared_course_context.py
📝 Walkthrough

Walkthrough

This PR migrates quiz generation and course-context summary regeneration from raw Gemini calls to typed Pydantic AI agents. It adds course_summary and quiz_context agents, registers them in the provider task registry, wires them into routes/quiz.py and course_context_service.py, removes the legacy quiz-generation fallback (returning HTTP 502 on failure instead), and updates tests plus a new migration spec.

Changes

Agent migration

Layer / File(s)Summary
Provider task registry
backend/agents/_providers.py
Adds course_summary and quiz_context to AgentTask and their default models to _DEFAULTS.
Course summary agent
backend/agents/course_summary.py
New CourseSummary model, versioned system prompt, and course_summary_agent configured via model_for.
Quiz context agent
backend/agents/quiz_context.py
New QuizContext model mirroring legacy JSON schema, versioned prompt, and quiz_context_agent.
Quiz route rework
backend/routes/quiz.py
Removes _legacy_generate_quiz fallback (now returns 502 on agent failure) and replaces call_gemini_json in _update_context with quiz_context_agent via run_agent_sync.
Course context service rework
backend/services/course_context_service.py
Replaces call_gemini with course_summary_agent via run_agent_sync, retaining a deterministic fallback on failure.
Quiz route tests
backend/tests/test_quiz_routes.py
Mocks quiz_context_agent.run instead of call_gemini_json, adds 502-degradation tests replacing legacy fallback tests, removes legacy model-selection tests.
Course context tests
backend/tests/test_shared_course_context.py
Replaces prompt-augmentation tests with TestCourseSummaryAgent covering agent success/failure paths.
Migration spec
specs/145-quiz-course-context-agents.md
New spec documenting goals, test changes, and acceptance criteria for the agent migration.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Sequence Diagram(s)

sequenceDiagram
participant Client
participant QuizRoute as routes/quiz.py
participant QuizContextAgent as quiz_context_agent
participant CourseContextService as course_context_service.py
participant CourseSummaryAgent as course_summary_agent
Client->>QuizRoute: POST /api/quiz/generate
QuizRoute->>QuizRoute: run quiz agent
alt agent fails (UsageLimitExceeded / unexpected error)
QuizRoute-->>Client: HTTP 502
else success
QuizRoute-->>Client: quiz payload
end
Client->>QuizRoute: submit quiz answers
QuizRoute->>QuizContextAgent: run_agent_sync(quiz_context_agent.run(prompt))
QuizContextAgent-->>QuizRoute: QuizContext output
QuizRoute->>QuizRoute: save_quiz_context(result.output.model_dump())
CourseContextService->>CourseSummaryAgent: run_agent_sync(course_summary_agent.run(user_message))
alt agent succeeds
CourseSummaryAgent-->>CourseContextService: CourseSummary
else agent fails
CourseContextService->>CourseContextService: deterministic fallback summary
end
Loading

Possibly related issues

Possibly related PRs

  • SaplingLearn/Sapling#53: Both modify backend/services/course_context_service.py's course-level summary generation logic.
  • SaplingLearn/Sapling#71: Both modify backend/routes/quiz.py quiz generation/error-handling around agent-based and legacy fallback behavior.
  • SaplingLearn/Sapling#78: Both modify backend/agents/_providers.py's AgentTask/_DEFAULTS task registry.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 43.75% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly summarizes the main agent-migration change and fallback removal.
Description check✅ PassedThe description covers the goal, changes, related issue, testing, and reviewer notes, though with custom headings.
Linked Issues check✅ PassedThe PR matches #145 by removing the remaining call_gemini* seams and routing quiz and course-context flows through agents.
Out of Scope Changes check✅ PassedThe added files and edits stay aligned with the agent migration scope and avoid the referenced quiz scoring and graph-write areas.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/145-quiz-agent

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jul 1, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-staginge8127aeCommit Preview URL

Branch Preview URL
Jul 02 2026, 06:11 AM

Comment threadbackend/tests/test_shared_course_context.py Fixed

@coderabbitaicoderabbitaiBot 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.

Actionable comments posted: 1

🧹 Nitpick comments (3)
backend/routes/quiz.py (1)

202-207: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Update the stale legacy-fallback comment.

Lines 203-205 still say this routes to the legacy fallback, but the new catch block returns HTTP 502. Please align the comment with the removed fallback path.

Suggested comment update
- # All questions dropped — degrade to legacy rather than serve- # an empty quiz. Raise a sentinel that generate_quiz catches- # and routes to the legacy fallback.+ # All questions dropped — do not serve an empty quiz. Raise a+ # sentinel that generate_quiz catches and degrades to HTTP 502.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@backend/routes/quiz.py` around lines 202 - 207, The comment in the quiz
generation path is stale because the `RuntimeError` from `quiz_agent` no longer
routes to a legacy fallback and is now handled by the `generate_quiz` catch
block as an HTTP 502. Update the inline comment near the `wire_questions`
validation in `generate_quiz`/the surrounding fallback logic to describe the
current behavior only, removing any mention of legacy fallback and keeping the
wording aligned with the new error handling.
backend/services/course_context_service.py (2)

53-54: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

No guard against a falsy/empty agent summary.

If the agent returns an empty or whitespace-only summary, no exception is raised, so the deterministic fallback never kicks in and a blank summary gets persisted to offering_summary.summary_text.

🛡️ Suggested fix
 try:
result = run_agent_sync(course_summary_agent.run(user_message))
- return result.output.summary+ summary = result.output.summary+ if not summary or not summary.strip():+ raise ValueError("course_summary_agent returned empty summary")+ return summary
except Exception:
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@backend/services/course_context_service.py` around lines 53 - 54, The course
summary flow in course_context_service’s summary generation path currently
returns result.output.summary directly, which allows empty or whitespace-only
values to pass through. Update the logic around
run_agent_sync(course_summary_agent.run(user_message)) to validate
result.output.summary and raise/trigger the existing fallback when it is falsy
or blank, so the deterministic summary is used instead of persisting an empty
offering summary.

52-61: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Log the agent failure before falling back
Add a warning log with exc_info=True before returning the template so agent/runtime failures are visible instead of disappearing into the fallback path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@backend/services/course_context_service.py` around lines 52 - 61, The
fallback in course_context_service’s summary generation swallows agent/runtime
failures silently. Update the try/except around
run_agent_sync(course_summary_agent.run(user_message)) so the exception is
logged as a warning with exc_info=True before returning the template summary,
keeping the fallback behavior but making failures visible; use the surrounding
course_context_service method and course_summary_agent.run as the key locations.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@backend/tests/test_quiz_routes.py`:
- Around line 23-29: The submit-route tests are only neutralizing
`quiz_context_agent.run`, but `_update_context` in `routes.quiz` still persists
context via `save_quiz_context(...)`, which can trigger hidden writes or masked
failures. Update the affected tests/helpers around `_noop_ctx_agent()` to also
patch `routes.quiz.save_quiz_context` to a no-op wherever the submit flow is
exercised, so the background context update is fully disabled.
---
Nitpick comments:
In `@backend/routes/quiz.py`:
- Around line 202-207: The comment in the quiz generation path is stale because
the `RuntimeError` from `quiz_agent` no longer routes to a legacy fallback and
is now handled by the `generate_quiz` catch block as an HTTP 502. Update the
inline comment near the `wire_questions` validation in `generate_quiz`/the
surrounding fallback logic to describe the current behavior only, removing any
mention of legacy fallback and keeping the wording aligned with the new error
handling.
In `@backend/services/course_context_service.py`:
- Around line 53-54: The course summary flow in course_context_service’s summary
generation path currently returns result.output.summary directly, which allows
empty or whitespace-only values to pass through. Update the logic around
run_agent_sync(course_summary_agent.run(user_message)) to validate
result.output.summary and raise/trigger the existing fallback when it is falsy
or blank, so the deterministic summary is used instead of persisting an empty
offering summary.
- Around line 52-61: The fallback in course_context_service’s summary generation
swallows agent/runtime failures silently. Update the try/except around
run_agent_sync(course_summary_agent.run(user_message)) so the exception is
logged as a warning with exc_info=True before returning the template summary,
keeping the fallback behavior but making failures visible; use the surrounding
course_context_service method and course_summary_agent.run as the key locations.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c7510e3b-9aed-40df-a904-a8939c75af36

📥 Commits

Reviewing files that changed from the base of the PR and between 5de742e and 4e1b878.

📒 Files selected for processing (8)
  • backend/agents/_providers.py
  • backend/agents/course_summary.py
  • backend/agents/quiz_context.py
  • backend/routes/quiz.py
  • backend/services/course_context_service.py
  • backend/tests/test_quiz_routes.py
  • backend/tests/test_shared_course_context.py
  • specs/145-quiz-course-context-agents.md

Comment threadbackend/tests/test_quiz_routes.py
Jose-Gael-Cruz-Lopezand others added 2 commits July 1, 2026 13:17
…_context in submit mocks
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The all-questions-drift branch's comment still described routing to the
legacy fallback, which #145 deleted. The RuntimeError is now caught by
generate_quiz's bare-except and returned as HTTP 502 (per
test_degrades_when_all_questions_drift). Comment-only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@AndresL230
AndresL230 merged commit f472300 into mainJul 2, 2026
6 checks passed
@AndresL230
AndresL230 deleted the feat/145-quiz-agent branch August 2, 2026 18:30
Sign up for freeto 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.

[P2] Agent migration: quiz generation + course-context regen → agent

2 participants

@Jose-Gael-Cruz-Lopez@AndresL230
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(quiz): quiz-context + course-summary → agents, retire quiz-gen fallback (#145) - #299

Merged
AndresL230 merged 3 commits into
mainfrom
feat/145-quiz-agent
Jul 2, 2026
Merged

feat(quiz): quiz-context + course-summary → agents, retire quiz-gen fallback (#145)#299
AndresL230 merged 3 commits into
mainfrom
feat/145-quiz-agent

Conversation

@Jose-Gael-Cruz-Lopez

@Jose-Gael-Cruz-LopezJose-Gael-Cruz-Lopez commented Jul 1, 2026

Copy link
Copy Markdown
Member

Closes#145 (Agent-migration epic #152, milestone #2).

What

Removes the last three raw call_gemini* seams from the quiz surface, folding them into the agent layer:

SiteBeforeAfter
quiz.py post-submit background taskcall_gemini_json (quiz-context regen)new agents/quiz_context.py (quiz_context_agent)
course_context_service.pycall_gemini (class summary)new agents/course_summary.py (course_summary_agent)
quiz.py_legacy_generate_quizcall_gemini_json fallbackremoved → graceful 502

Notes

  • QuizContext mirrors quiz_context_update.txt exactly (weak_areas, common_mistakes, questions_seen_summary, recommended_difficulty, notes); _update_context saves .model_dump(), so the stored quiz_context.context_json shape is unchanged.
  • Course summary keeps its deterministic template fallback on agent failure (no second LLM call), so a summary is always produced.
  • Quiz-gen fallback removed: on agent guardrail-trip or unexpected failure, generate_quiz returns 502 rather than serving a quiz from a second LLM path. The pre-agent 404 (unknown concept node) is unchanged, and the fast/smart model_pref override still works on the agent path.
  • Both sync call sites use the run_agent_sync bridge (from [P2] Agent migration: remaining one-shot LLM calls (study guide, social, health) → agents/seam #147).

⚠️ Coordination (#128/#129)

This PR is scoped to the LLM seams only. It does not touch quiz scoring, apply_graph_update, or the quiz_attempts writes in submit_quiz — the #128/#129 territory. The only change inside submit_quiz is swapping the background context-update's LLM call for the agent.

Testing

  • Rewrote the legacy-fallback quiz tests → degrade-to-502; repointed the 4 submit context-update patches onto quiz_context_agent; removed the obsolete legacy prompt-augmentation class + legacy-model tests; added quiz-context-save, course-summary success/fallback, and degrade tests.
  • Full backend suite: 826 passed (the 2 test_storage_service failures pre-exist on main — missing SUPABASE env). ruff clean.

Out of scope: flashcards (#146), deleting services/gemini_service.py (#151).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added AI-powered course summaries for instructors.
    • Added AI-generated quiz context updates to improve follow-up learning notes.
  • Bug Fixes

    • Quiz generation now fails gracefully with a clear temporary-unavailable response instead of switching to an older fallback path.
    • Course and quiz context updates now use more consistent, structured outputs.

…uiz-gen fallback (#145)
Removes the last raw call_gemini* seams from the quiz surface:
- New agents/quiz_context.py (QuizContext: weak_areas, common_mistakes,
questions_seen_summary, recommended_difficulty, notes — mirrors
quiz_context_update.txt). routes/quiz.py's post-submit background task runs it
via run_agent_sync and saves .model_dump() (unchanged context_json shape).
- New agents/course_summary.py (CourseSummary.summary). course_context_service's
_generate_summary_with_gemini runs it via run_agent_sync and keeps the
deterministic template fallback on agent failure (no second LLM call).
- Removed _legacy_generate_quiz + the MODEL_LITE/MODEL_SMART/call_gemini_json
import. On agent guardrail-trip or failure, generate_quiz now returns 502
instead of a raw-Gemini fallback; the pre-agent 404 (unknown node) is
unchanged. _providers registers course_summary (flash) + quiz_context (lite).
Coordination: does NOT touch scoring / apply_graph_update / quiz_attempts writes
(#128/#129 territory) — only the LLM seams.
Tests: rewrote the legacy-fallback quiz tests to degrade-to-502; repointed the
submit context-update patches onto quiz_context_agent; removed the obsolete
legacy prompt-augmentation + legacy-model tests; added quiz-context save,
course-summary success/fallback tests. Full suite 826 passed (2 pre-existing
storage-env failures). ruff clean.
Spec: specs/145-quiz-course-context-agents.md
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented Jul 1, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@AndresL230, you've reached your PR review limit, so we couldn't start this review.

Next review available in:31 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9c528394-d83a-42ad-98cc-2b4618ab8797

📥 Commits

Reviewing files that changed from the base of the PR and between 4e1b878 and e8127ae.

📒 Files selected for processing (3)
  • backend/routes/quiz.py
  • backend/tests/test_quiz_routes.py
  • backend/tests/test_shared_course_context.py
📝 Walkthrough

Walkthrough

This PR migrates quiz generation and course-context summary regeneration from raw Gemini calls to typed Pydantic AI agents. It adds course_summary and quiz_context agents, registers them in the provider task registry, wires them into routes/quiz.py and course_context_service.py, removes the legacy quiz-generation fallback (returning HTTP 502 on failure instead), and updates tests plus a new migration spec.

Changes

Agent migration

Layer / File(s)Summary
Provider task registry
backend/agents/_providers.py
Adds course_summary and quiz_context to AgentTask and their default models to _DEFAULTS.
Course summary agent
backend/agents/course_summary.py
New CourseSummary model, versioned system prompt, and course_summary_agent configured via model_for.
Quiz context agent
backend/agents/quiz_context.py
New QuizContext model mirroring legacy JSON schema, versioned prompt, and quiz_context_agent.
Quiz route rework
backend/routes/quiz.py
Removes _legacy_generate_quiz fallback (now returns 502 on agent failure) and replaces call_gemini_json in _update_context with quiz_context_agent via run_agent_sync.
Course context service rework
backend/services/course_context_service.py
Replaces call_gemini with course_summary_agent via run_agent_sync, retaining a deterministic fallback on failure.
Quiz route tests
backend/tests/test_quiz_routes.py
Mocks quiz_context_agent.run instead of call_gemini_json, adds 502-degradation tests replacing legacy fallback tests, removes legacy model-selection tests.
Course context tests
backend/tests/test_shared_course_context.py
Replaces prompt-augmentation tests with TestCourseSummaryAgent covering agent success/failure paths.
Migration spec
specs/145-quiz-course-context-agents.md
New spec documenting goals, test changes, and acceptance criteria for the agent migration.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Sequence Diagram(s)

sequenceDiagram
participant Client
participant QuizRoute as routes/quiz.py
participant QuizContextAgent as quiz_context_agent
participant CourseContextService as course_context_service.py
participant CourseSummaryAgent as course_summary_agent
Client->>QuizRoute: POST /api/quiz/generate
QuizRoute->>QuizRoute: run quiz agent
alt agent fails (UsageLimitExceeded / unexpected error)
QuizRoute-->>Client: HTTP 502
else success
QuizRoute-->>Client: quiz payload
end
Client->>QuizRoute: submit quiz answers
QuizRoute->>QuizContextAgent: run_agent_sync(quiz_context_agent.run(prompt))
QuizContextAgent-->>QuizRoute: QuizContext output
QuizRoute->>QuizRoute: save_quiz_context(result.output.model_dump())
CourseContextService->>CourseSummaryAgent: run_agent_sync(course_summary_agent.run(user_message))
alt agent succeeds
CourseSummaryAgent-->>CourseContextService: CourseSummary
else agent fails
CourseContextService->>CourseContextService: deterministic fallback summary
end
Loading

Possibly related issues

Possibly related PRs

  • SaplingLearn/Sapling#53: Both modify backend/services/course_context_service.py's course-level summary generation logic.
  • SaplingLearn/Sapling#71: Both modify backend/routes/quiz.py quiz generation/error-handling around agent-based and legacy fallback behavior.
  • SaplingLearn/Sapling#78: Both modify backend/agents/_providers.py's AgentTask/_DEFAULTS task registry.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 43.75% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly summarizes the main agent-migration change and fallback removal.
Description check✅ PassedThe description covers the goal, changes, related issue, testing, and reviewer notes, though with custom headings.
Linked Issues check✅ PassedThe PR matches #145 by removing the remaining call_gemini* seams and routing quiz and course-context flows through agents.
Out of Scope Changes check✅ PassedThe added files and edits stay aligned with the agent migration scope and avoid the referenced quiz scoring and graph-write areas.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/145-quiz-agent

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jul 1, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-staginge8127aeCommit Preview URL

Branch Preview URL
Jul 02 2026, 06:11 AM

Comment threadbackend/tests/test_shared_course_context.py Fixed

@coderabbitaicoderabbitaiBot 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.

Actionable comments posted: 1

🧹 Nitpick comments (3)
backend/routes/quiz.py (1)

202-207: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Update the stale legacy-fallback comment.

Lines 203-205 still say this routes to the legacy fallback, but the new catch block returns HTTP 502. Please align the comment with the removed fallback path.

Suggested comment update
- # All questions dropped — degrade to legacy rather than serve- # an empty quiz. Raise a sentinel that generate_quiz catches- # and routes to the legacy fallback.+ # All questions dropped — do not serve an empty quiz. Raise a+ # sentinel that generate_quiz catches and degrades to HTTP 502.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@backend/routes/quiz.py` around lines 202 - 207, The comment in the quiz
generation path is stale because the `RuntimeError` from `quiz_agent` no longer
routes to a legacy fallback and is now handled by the `generate_quiz` catch
block as an HTTP 502. Update the inline comment near the `wire_questions`
validation in `generate_quiz`/the surrounding fallback logic to describe the
current behavior only, removing any mention of legacy fallback and keeping the
wording aligned with the new error handling.
backend/services/course_context_service.py (2)

53-54: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

No guard against a falsy/empty agent summary.

If the agent returns an empty or whitespace-only summary, no exception is raised, so the deterministic fallback never kicks in and a blank summary gets persisted to offering_summary.summary_text.

🛡️ Suggested fix
 try:
result = run_agent_sync(course_summary_agent.run(user_message))
- return result.output.summary+ summary = result.output.summary+ if not summary or not summary.strip():+ raise ValueError("course_summary_agent returned empty summary")+ return summary
except Exception:
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@backend/services/course_context_service.py` around lines 53 - 54, The course
summary flow in course_context_service’s summary generation path currently
returns result.output.summary directly, which allows empty or whitespace-only
values to pass through. Update the logic around
run_agent_sync(course_summary_agent.run(user_message)) to validate
result.output.summary and raise/trigger the existing fallback when it is falsy
or blank, so the deterministic summary is used instead of persisting an empty
offering summary.

52-61: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Log the agent failure before falling back
Add a warning log with exc_info=True before returning the template so agent/runtime failures are visible instead of disappearing into the fallback path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@backend/services/course_context_service.py` around lines 52 - 61, The
fallback in course_context_service’s summary generation swallows agent/runtime
failures silently. Update the try/except around
run_agent_sync(course_summary_agent.run(user_message)) so the exception is
logged as a warning with exc_info=True before returning the template summary,
keeping the fallback behavior but making failures visible; use the surrounding
course_context_service method and course_summary_agent.run as the key locations.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@backend/tests/test_quiz_routes.py`:
- Around line 23-29: The submit-route tests are only neutralizing
`quiz_context_agent.run`, but `_update_context` in `routes.quiz` still persists
context via `save_quiz_context(...)`, which can trigger hidden writes or masked
failures. Update the affected tests/helpers around `_noop_ctx_agent()` to also
patch `routes.quiz.save_quiz_context` to a no-op wherever the submit flow is
exercised, so the background context update is fully disabled.
---
Nitpick comments:
In `@backend/routes/quiz.py`:
- Around line 202-207: The comment in the quiz generation path is stale because
the `RuntimeError` from `quiz_agent` no longer routes to a legacy fallback and
is now handled by the `generate_quiz` catch block as an HTTP 502. Update the
inline comment near the `wire_questions` validation in `generate_quiz`/the
surrounding fallback logic to describe the current behavior only, removing any
mention of legacy fallback and keeping the wording aligned with the new error
handling.
In `@backend/services/course_context_service.py`:
- Around line 53-54: The course summary flow in course_context_service’s summary
generation path currently returns result.output.summary directly, which allows
empty or whitespace-only values to pass through. Update the logic around
run_agent_sync(course_summary_agent.run(user_message)) to validate
result.output.summary and raise/trigger the existing fallback when it is falsy
or blank, so the deterministic summary is used instead of persisting an empty
offering summary.
- Around line 52-61: The fallback in course_context_service’s summary generation
swallows agent/runtime failures silently. Update the try/except around
run_agent_sync(course_summary_agent.run(user_message)) so the exception is
logged as a warning with exc_info=True before returning the template summary,
keeping the fallback behavior but making failures visible; use the surrounding
course_context_service method and course_summary_agent.run as the key locations.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c7510e3b-9aed-40df-a904-a8939c75af36

📥 Commits

Reviewing files that changed from the base of the PR and between 5de742e and 4e1b878.

📒 Files selected for processing (8)
  • backend/agents/_providers.py
  • backend/agents/course_summary.py
  • backend/agents/quiz_context.py
  • backend/routes/quiz.py
  • backend/services/course_context_service.py
  • backend/tests/test_quiz_routes.py
  • backend/tests/test_shared_course_context.py
  • specs/145-quiz-course-context-agents.md

Comment threadbackend/tests/test_quiz_routes.py
Jose-Gael-Cruz-Lopezand others added 2 commits July 1, 2026 13:17
…_context in submit mocks
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The all-questions-drift branch's comment still described routing to the
legacy fallback, which #145 deleted. The RuntimeError is now caught by
generate_quiz's bare-except and returned as HTTP 502 (per
test_degrades_when_all_questions_drift). Comment-only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@AndresL230
AndresL230 merged commit f472300 into mainJul 2, 2026
6 checks passed
@AndresL230
AndresL230 deleted the feat/145-quiz-agent branch August 2, 2026 18:30
Sign up for freeto 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.

[P2] Agent migration: quiz generation + course-context regen → agent

2 participants

@Jose-Gael-Cruz-Lopez@AndresL230
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(quiz): quiz-context + course-summary → agents, retire quiz-gen fallback (#145) - #299

Merged
AndresL230 merged 3 commits into
mainfrom
feat/145-quiz-agent
Jul 2, 2026
Merged

feat(quiz): quiz-context + course-summary → agents, retire quiz-gen fallback (#145)#299
AndresL230 merged 3 commits into
mainfrom
feat/145-quiz-agent

Conversation

@Jose-Gael-Cruz-Lopez

@Jose-Gael-Cruz-LopezJose-Gael-Cruz-Lopez commented Jul 1, 2026

Copy link
Copy Markdown
Member

Closes#145 (Agent-migration epic #152, milestone #2).

What

Removes the last three raw call_gemini* seams from the quiz surface, folding them into the agent layer:

SiteBeforeAfter
quiz.py post-submit background taskcall_gemini_json (quiz-context regen)new agents/quiz_context.py (quiz_context_agent)
course_context_service.pycall_gemini (class summary)new agents/course_summary.py (course_summary_agent)
quiz.py_legacy_generate_quizcall_gemini_json fallbackremoved → graceful 502

Notes

  • QuizContext mirrors quiz_context_update.txt exactly (weak_areas, common_mistakes, questions_seen_summary, recommended_difficulty, notes); _update_context saves .model_dump(), so the stored quiz_context.context_json shape is unchanged.
  • Course summary keeps its deterministic template fallback on agent failure (no second LLM call), so a summary is always produced.
  • Quiz-gen fallback removed: on agent guardrail-trip or unexpected failure, generate_quiz returns 502 rather than serving a quiz from a second LLM path. The pre-agent 404 (unknown concept node) is unchanged, and the fast/smart model_pref override still works on the agent path.
  • Both sync call sites use the run_agent_sync bridge (from [P2] Agent migration: remaining one-shot LLM calls (study guide, social, health) → agents/seam #147).

⚠️ Coordination (#128/#129)

This PR is scoped to the LLM seams only. It does not touch quiz scoring, apply_graph_update, or the quiz_attempts writes in submit_quiz — the #128/#129 territory. The only change inside submit_quiz is swapping the background context-update's LLM call for the agent.

Testing

  • Rewrote the legacy-fallback quiz tests → degrade-to-502; repointed the 4 submit context-update patches onto quiz_context_agent; removed the obsolete legacy prompt-augmentation class + legacy-model tests; added quiz-context-save, course-summary success/fallback, and degrade tests.
  • Full backend suite: 826 passed (the 2 test_storage_service failures pre-exist on main — missing SUPABASE env). ruff clean.

Out of scope: flashcards (#146), deleting services/gemini_service.py (#151).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added AI-powered course summaries for instructors.
    • Added AI-generated quiz context updates to improve follow-up learning notes.
  • Bug Fixes

    • Quiz generation now fails gracefully with a clear temporary-unavailable response instead of switching to an older fallback path.
    • Course and quiz context updates now use more consistent, structured outputs.

…uiz-gen fallback (#145)
Removes the last raw call_gemini* seams from the quiz surface:
- New agents/quiz_context.py (QuizContext: weak_areas, common_mistakes,
questions_seen_summary, recommended_difficulty, notes — mirrors
quiz_context_update.txt). routes/quiz.py's post-submit background task runs it
via run_agent_sync and saves .model_dump() (unchanged context_json shape).
- New agents/course_summary.py (CourseSummary.summary). course_context_service's
_generate_summary_with_gemini runs it via run_agent_sync and keeps the
deterministic template fallback on agent failure (no second LLM call).
- Removed _legacy_generate_quiz + the MODEL_LITE/MODEL_SMART/call_gemini_json
import. On agent guardrail-trip or failure, generate_quiz now returns 502
instead of a raw-Gemini fallback; the pre-agent 404 (unknown node) is
unchanged. _providers registers course_summary (flash) + quiz_context (lite).
Coordination: does NOT touch scoring / apply_graph_update / quiz_attempts writes
(#128/#129 territory) — only the LLM seams.
Tests: rewrote the legacy-fallback quiz tests to degrade-to-502; repointed the
submit context-update patches onto quiz_context_agent; removed the obsolete
legacy prompt-augmentation + legacy-model tests; added quiz-context save,
course-summary success/fallback tests. Full suite 826 passed (2 pre-existing
storage-env failures). ruff clean.
Spec: specs/145-quiz-course-context-agents.md
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented Jul 1, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@AndresL230, you've reached your PR review limit, so we couldn't start this review.

Next review available in:31 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9c528394-d83a-42ad-98cc-2b4618ab8797

📥 Commits

Reviewing files that changed from the base of the PR and between 4e1b878 and e8127ae.

📒 Files selected for processing (3)
  • backend/routes/quiz.py
  • backend/tests/test_quiz_routes.py
  • backend/tests/test_shared_course_context.py
📝 Walkthrough

Walkthrough

This PR migrates quiz generation and course-context summary regeneration from raw Gemini calls to typed Pydantic AI agents. It adds course_summary and quiz_context agents, registers them in the provider task registry, wires them into routes/quiz.py and course_context_service.py, removes the legacy quiz-generation fallback (returning HTTP 502 on failure instead), and updates tests plus a new migration spec.

Changes

Agent migration

Layer / File(s)Summary
Provider task registry
backend/agents/_providers.py
Adds course_summary and quiz_context to AgentTask and their default models to _DEFAULTS.
Course summary agent
backend/agents/course_summary.py
New CourseSummary model, versioned system prompt, and course_summary_agent configured via model_for.
Quiz context agent
backend/agents/quiz_context.py
New QuizContext model mirroring legacy JSON schema, versioned prompt, and quiz_context_agent.
Quiz route rework
backend/routes/quiz.py
Removes _legacy_generate_quiz fallback (now returns 502 on agent failure) and replaces call_gemini_json in _update_context with quiz_context_agent via run_agent_sync.
Course context service rework
backend/services/course_context_service.py
Replaces call_gemini with course_summary_agent via run_agent_sync, retaining a deterministic fallback on failure.
Quiz route tests
backend/tests/test_quiz_routes.py
Mocks quiz_context_agent.run instead of call_gemini_json, adds 502-degradation tests replacing legacy fallback tests, removes legacy model-selection tests.
Course context tests
backend/tests/test_shared_course_context.py
Replaces prompt-augmentation tests with TestCourseSummaryAgent covering agent success/failure paths.
Migration spec
specs/145-quiz-course-context-agents.md
New spec documenting goals, test changes, and acceptance criteria for the agent migration.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Sequence Diagram(s)

sequenceDiagram
participant Client
participant QuizRoute as routes/quiz.py
participant QuizContextAgent as quiz_context_agent
participant CourseContextService as course_context_service.py
participant CourseSummaryAgent as course_summary_agent
Client->>QuizRoute: POST /api/quiz/generate
QuizRoute->>QuizRoute: run quiz agent
alt agent fails (UsageLimitExceeded / unexpected error)
QuizRoute-->>Client: HTTP 502
else success
QuizRoute-->>Client: quiz payload
end
Client->>QuizRoute: submit quiz answers
QuizRoute->>QuizContextAgent: run_agent_sync(quiz_context_agent.run(prompt))
QuizContextAgent-->>QuizRoute: QuizContext output
QuizRoute->>QuizRoute: save_quiz_context(result.output.model_dump())
CourseContextService->>CourseSummaryAgent: run_agent_sync(course_summary_agent.run(user_message))
alt agent succeeds
CourseSummaryAgent-->>CourseContextService: CourseSummary
else agent fails
CourseContextService->>CourseContextService: deterministic fallback summary
end
Loading

Possibly related issues

Possibly related PRs

  • SaplingLearn/Sapling#53: Both modify backend/services/course_context_service.py's course-level summary generation logic.
  • SaplingLearn/Sapling#71: Both modify backend/routes/quiz.py quiz generation/error-handling around agent-based and legacy fallback behavior.
  • SaplingLearn/Sapling#78: Both modify backend/agents/_providers.py's AgentTask/_DEFAULTS task registry.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 43.75% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly summarizes the main agent-migration change and fallback removal.
Description check✅ PassedThe description covers the goal, changes, related issue, testing, and reviewer notes, though with custom headings.
Linked Issues check✅ PassedThe PR matches #145 by removing the remaining call_gemini* seams and routing quiz and course-context flows through agents.
Out of Scope Changes check✅ PassedThe added files and edits stay aligned with the agent migration scope and avoid the referenced quiz scoring and graph-write areas.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/145-quiz-agent

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jul 1, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-staginge8127aeCommit Preview URL

Branch Preview URL
Jul 02 2026, 06:11 AM

Comment threadbackend/tests/test_shared_course_context.py Fixed

@coderabbitaicoderabbitaiBot 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.

Actionable comments posted: 1

🧹 Nitpick comments (3)
backend/routes/quiz.py (1)

202-207: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Update the stale legacy-fallback comment.

Lines 203-205 still say this routes to the legacy fallback, but the new catch block returns HTTP 502. Please align the comment with the removed fallback path.

Suggested comment update
- # All questions dropped — degrade to legacy rather than serve- # an empty quiz. Raise a sentinel that generate_quiz catches- # and routes to the legacy fallback.+ # All questions dropped — do not serve an empty quiz. Raise a+ # sentinel that generate_quiz catches and degrades to HTTP 502.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@backend/routes/quiz.py` around lines 202 - 207, The comment in the quiz
generation path is stale because the `RuntimeError` from `quiz_agent` no longer
routes to a legacy fallback and is now handled by the `generate_quiz` catch
block as an HTTP 502. Update the inline comment near the `wire_questions`
validation in `generate_quiz`/the surrounding fallback logic to describe the
current behavior only, removing any mention of legacy fallback and keeping the
wording aligned with the new error handling.
backend/services/course_context_service.py (2)

53-54: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

No guard against a falsy/empty agent summary.

If the agent returns an empty or whitespace-only summary, no exception is raised, so the deterministic fallback never kicks in and a blank summary gets persisted to offering_summary.summary_text.

🛡️ Suggested fix
 try:
result = run_agent_sync(course_summary_agent.run(user_message))
- return result.output.summary+ summary = result.output.summary+ if not summary or not summary.strip():+ raise ValueError("course_summary_agent returned empty summary")+ return summary
except Exception:
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@backend/services/course_context_service.py` around lines 53 - 54, The course
summary flow in course_context_service’s summary generation path currently
returns result.output.summary directly, which allows empty or whitespace-only
values to pass through. Update the logic around
run_agent_sync(course_summary_agent.run(user_message)) to validate
result.output.summary and raise/trigger the existing fallback when it is falsy
or blank, so the deterministic summary is used instead of persisting an empty
offering summary.

52-61: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Log the agent failure before falling back
Add a warning log with exc_info=True before returning the template so agent/runtime failures are visible instead of disappearing into the fallback path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@backend/services/course_context_service.py` around lines 52 - 61, The
fallback in course_context_service’s summary generation swallows agent/runtime
failures silently. Update the try/except around
run_agent_sync(course_summary_agent.run(user_message)) so the exception is
logged as a warning with exc_info=True before returning the template summary,
keeping the fallback behavior but making failures visible; use the surrounding
course_context_service method and course_summary_agent.run as the key locations.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@backend/tests/test_quiz_routes.py`:
- Around line 23-29: The submit-route tests are only neutralizing
`quiz_context_agent.run`, but `_update_context` in `routes.quiz` still persists
context via `save_quiz_context(...)`, which can trigger hidden writes or masked
failures. Update the affected tests/helpers around `_noop_ctx_agent()` to also
patch `routes.quiz.save_quiz_context` to a no-op wherever the submit flow is
exercised, so the background context update is fully disabled.
---
Nitpick comments:
In `@backend/routes/quiz.py`:
- Around line 202-207: The comment in the quiz generation path is stale because
the `RuntimeError` from `quiz_agent` no longer routes to a legacy fallback and
is now handled by the `generate_quiz` catch block as an HTTP 502. Update the
inline comment near the `wire_questions` validation in `generate_quiz`/the
surrounding fallback logic to describe the current behavior only, removing any
mention of legacy fallback and keeping the wording aligned with the new error
handling.
In `@backend/services/course_context_service.py`:
- Around line 53-54: The course summary flow in course_context_service’s summary
generation path currently returns result.output.summary directly, which allows
empty or whitespace-only values to pass through. Update the logic around
run_agent_sync(course_summary_agent.run(user_message)) to validate
result.output.summary and raise/trigger the existing fallback when it is falsy
or blank, so the deterministic summary is used instead of persisting an empty
offering summary.
- Around line 52-61: The fallback in course_context_service’s summary generation
swallows agent/runtime failures silently. Update the try/except around
run_agent_sync(course_summary_agent.run(user_message)) so the exception is
logged as a warning with exc_info=True before returning the template summary,
keeping the fallback behavior but making failures visible; use the surrounding
course_context_service method and course_summary_agent.run as the key locations.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c7510e3b-9aed-40df-a904-a8939c75af36

📥 Commits

Reviewing files that changed from the base of the PR and between 5de742e and 4e1b878.

📒 Files selected for processing (8)
  • backend/agents/_providers.py
  • backend/agents/course_summary.py
  • backend/agents/quiz_context.py
  • backend/routes/quiz.py
  • backend/services/course_context_service.py
  • backend/tests/test_quiz_routes.py
  • backend/tests/test_shared_course_context.py
  • specs/145-quiz-course-context-agents.md

Comment threadbackend/tests/test_quiz_routes.py
Jose-Gael-Cruz-Lopezand others added 2 commits July 1, 2026 13:17
…_context in submit mocks
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The all-questions-drift branch's comment still described routing to the
legacy fallback, which #145 deleted. The RuntimeError is now caught by
generate_quiz's bare-except and returned as HTTP 502 (per
test_degrades_when_all_questions_drift). Comment-only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@AndresL230
AndresL230 merged commit f472300 into mainJul 2, 2026
6 checks passed
@AndresL230
AndresL230 deleted the feat/145-quiz-agent branch August 2, 2026 18:30
Sign up for freeto 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.

[P2] Agent migration: quiz generation + course-context regen → agent

2 participants

@Jose-Gael-Cruz-Lopez@AndresL230
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

feat(quiz): quiz-context + course-summary → agents, retire quiz-gen fallback (#145) - #299

Merged
AndresL230 merged 3 commits into
mainfrom
feat/145-quiz-agent
Jul 2, 2026
Merged

feat(quiz): quiz-context + course-summary → agents, retire quiz-gen fallback (#145)#299
AndresL230 merged 3 commits into
mainfrom
feat/145-quiz-agent

Conversation

@Jose-Gael-Cruz-Lopez

@Jose-Gael-Cruz-LopezJose-Gael-Cruz-Lopez commented Jul 1, 2026

Copy link
Copy Markdown
Member

Closes#145 (Agent-migration epic #152, milestone #2).

What

Removes the last three raw call_gemini* seams from the quiz surface, folding them into the agent layer:

SiteBeforeAfter
quiz.py post-submit background taskcall_gemini_json (quiz-context regen)new agents/quiz_context.py (quiz_context_agent)
course_context_service.pycall_gemini (class summary)new agents/course_summary.py (course_summary_agent)
quiz.py_legacy_generate_quizcall_gemini_json fallbackremoved → graceful 502

Notes

  • QuizContext mirrors quiz_context_update.txt exactly (weak_areas, common_mistakes, questions_seen_summary, recommended_difficulty, notes); _update_context saves .model_dump(), so the stored quiz_context.context_json shape is unchanged.
  • Course summary keeps its deterministic template fallback on agent failure (no second LLM call), so a summary is always produced.
  • Quiz-gen fallback removed: on agent guardrail-trip or unexpected failure, generate_quiz returns 502 rather than serving a quiz from a second LLM path. The pre-agent 404 (unknown concept node) is unchanged, and the fast/smart model_pref override still works on the agent path.
  • Both sync call sites use the run_agent_sync bridge (from [P2] Agent migration: remaining one-shot LLM calls (study guide, social, health) → agents/seam #147).

⚠️ Coordination (#128/#129)

This PR is scoped to the LLM seams only. It does not touch quiz scoring, apply_graph_update, or the quiz_attempts writes in submit_quiz — the #128/#129 territory. The only change inside submit_quiz is swapping the background context-update's LLM call for the agent.

Testing

  • Rewrote the legacy-fallback quiz tests → degrade-to-502; repointed the 4 submit context-update patches onto quiz_context_agent; removed the obsolete legacy prompt-augmentation class + legacy-model tests; added quiz-context-save, course-summary success/fallback, and degrade tests.
  • Full backend suite: 826 passed (the 2 test_storage_service failures pre-exist on main — missing SUPABASE env). ruff clean.

Out of scope: flashcards (#146), deleting services/gemini_service.py (#151).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added AI-powered course summaries for instructors.
    • Added AI-generated quiz context updates to improve follow-up learning notes.
  • Bug Fixes

    • Quiz generation now fails gracefully with a clear temporary-unavailable response instead of switching to an older fallback path.
    • Course and quiz context updates now use more consistent, structured outputs.

…uiz-gen fallback (#145)
Removes the last raw call_gemini* seams from the quiz surface:
- New agents/quiz_context.py (QuizContext: weak_areas, common_mistakes,
questions_seen_summary, recommended_difficulty, notes — mirrors
quiz_context_update.txt). routes/quiz.py's post-submit background task runs it
via run_agent_sync and saves .model_dump() (unchanged context_json shape).
- New agents/course_summary.py (CourseSummary.summary). course_context_service's
_generate_summary_with_gemini runs it via run_agent_sync and keeps the
deterministic template fallback on agent failure (no second LLM call).
- Removed _legacy_generate_quiz + the MODEL_LITE/MODEL_SMART/call_gemini_json
import. On agent guardrail-trip or failure, generate_quiz now returns 502
instead of a raw-Gemini fallback; the pre-agent 404 (unknown node) is
unchanged. _providers registers course_summary (flash) + quiz_context (lite).
Coordination: does NOT touch scoring / apply_graph_update / quiz_attempts writes
(#128/#129 territory) — only the LLM seams.
Tests: rewrote the legacy-fallback quiz tests to degrade-to-502; repointed the
submit context-update patches onto quiz_context_agent; removed the obsolete
legacy prompt-augmentation + legacy-model tests; added quiz-context save,
course-summary success/fallback tests. Full suite 826 passed (2 pre-existing
storage-env failures). ruff clean.
Spec: specs/145-quiz-course-context-agents.md
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented Jul 1, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@AndresL230, you've reached your PR review limit, so we couldn't start this review.

Next review available in:31 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9c528394-d83a-42ad-98cc-2b4618ab8797

📥 Commits

Reviewing files that changed from the base of the PR and between 4e1b878 and e8127ae.

📒 Files selected for processing (3)
  • backend/routes/quiz.py
  • backend/tests/test_quiz_routes.py
  • backend/tests/test_shared_course_context.py
📝 Walkthrough

Walkthrough

This PR migrates quiz generation and course-context summary regeneration from raw Gemini calls to typed Pydantic AI agents. It adds course_summary and quiz_context agents, registers them in the provider task registry, wires them into routes/quiz.py and course_context_service.py, removes the legacy quiz-generation fallback (returning HTTP 502 on failure instead), and updates tests plus a new migration spec.

Changes

Agent migration

Layer / File(s)Summary
Provider task registry
backend/agents/_providers.py
Adds course_summary and quiz_context to AgentTask and their default models to _DEFAULTS.
Course summary agent
backend/agents/course_summary.py
New CourseSummary model, versioned system prompt, and course_summary_agent configured via model_for.
Quiz context agent
backend/agents/quiz_context.py
New QuizContext model mirroring legacy JSON schema, versioned prompt, and quiz_context_agent.
Quiz route rework
backend/routes/quiz.py
Removes _legacy_generate_quiz fallback (now returns 502 on agent failure) and replaces call_gemini_json in _update_context with quiz_context_agent via run_agent_sync.
Course context service rework
backend/services/course_context_service.py
Replaces call_gemini with course_summary_agent via run_agent_sync, retaining a deterministic fallback on failure.
Quiz route tests
backend/tests/test_quiz_routes.py
Mocks quiz_context_agent.run instead of call_gemini_json, adds 502-degradation tests replacing legacy fallback tests, removes legacy model-selection tests.
Course context tests
backend/tests/test_shared_course_context.py
Replaces prompt-augmentation tests with TestCourseSummaryAgent covering agent success/failure paths.
Migration spec
specs/145-quiz-course-context-agents.md
New spec documenting goals, test changes, and acceptance criteria for the agent migration.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Sequence Diagram(s)

sequenceDiagram
participant Client
participant QuizRoute as routes/quiz.py
participant QuizContextAgent as quiz_context_agent
participant CourseContextService as course_context_service.py
participant CourseSummaryAgent as course_summary_agent
Client->>QuizRoute: POST /api/quiz/generate
QuizRoute->>QuizRoute: run quiz agent
alt agent fails (UsageLimitExceeded / unexpected error)
QuizRoute-->>Client: HTTP 502
else success
QuizRoute-->>Client: quiz payload
end
Client->>QuizRoute: submit quiz answers
QuizRoute->>QuizContextAgent: run_agent_sync(quiz_context_agent.run(prompt))
QuizContextAgent-->>QuizRoute: QuizContext output
QuizRoute->>QuizRoute: save_quiz_context(result.output.model_dump())
CourseContextService->>CourseSummaryAgent: run_agent_sync(course_summary_agent.run(user_message))
alt agent succeeds
CourseSummaryAgent-->>CourseContextService: CourseSummary
else agent fails
CourseContextService->>CourseContextService: deterministic fallback summary
end
Loading

Possibly related issues

Possibly related PRs

  • SaplingLearn/Sapling#53: Both modify backend/services/course_context_service.py's course-level summary generation logic.
  • SaplingLearn/Sapling#71: Both modify backend/routes/quiz.py quiz generation/error-handling around agent-based and legacy fallback behavior.
  • SaplingLearn/Sapling#78: Both modify backend/agents/_providers.py's AgentTask/_DEFAULTS task registry.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 43.75% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly summarizes the main agent-migration change and fallback removal.
Description check✅ PassedThe description covers the goal, changes, related issue, testing, and reviewer notes, though with custom headings.
Linked Issues check✅ PassedThe PR matches #145 by removing the remaining call_gemini* seams and routing quiz and course-context flows through agents.
Out of Scope Changes check✅ PassedThe added files and edits stay aligned with the agent migration scope and avoid the referenced quiz scoring and graph-write areas.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/145-quiz-agent

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jul 1, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-staginge8127aeCommit Preview URL

Branch Preview URL
Jul 02 2026, 06:11 AM

Comment threadbackend/tests/test_shared_course_context.py Fixed

@coderabbitaicoderabbitaiBot 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.

Actionable comments posted: 1

🧹 Nitpick comments (3)
backend/routes/quiz.py (1)

202-207: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Update the stale legacy-fallback comment.

Lines 203-205 still say this routes to the legacy fallback, but the new catch block returns HTTP 502. Please align the comment with the removed fallback path.

Suggested comment update
- # All questions dropped — degrade to legacy rather than serve- # an empty quiz. Raise a sentinel that generate_quiz catches- # and routes to the legacy fallback.+ # All questions dropped — do not serve an empty quiz. Raise a+ # sentinel that generate_quiz catches and degrades to HTTP 502.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@backend/routes/quiz.py` around lines 202 - 207, The comment in the quiz
generation path is stale because the `RuntimeError` from `quiz_agent` no longer
routes to a legacy fallback and is now handled by the `generate_quiz` catch
block as an HTTP 502. Update the inline comment near the `wire_questions`
validation in `generate_quiz`/the surrounding fallback logic to describe the
current behavior only, removing any mention of legacy fallback and keeping the
wording aligned with the new error handling.
backend/services/course_context_service.py (2)

53-54: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

No guard against a falsy/empty agent summary.

If the agent returns an empty or whitespace-only summary, no exception is raised, so the deterministic fallback never kicks in and a blank summary gets persisted to offering_summary.summary_text.

🛡️ Suggested fix
 try:
result = run_agent_sync(course_summary_agent.run(user_message))
- return result.output.summary+ summary = result.output.summary+ if not summary or not summary.strip():+ raise ValueError("course_summary_agent returned empty summary")+ return summary
except Exception:
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@backend/services/course_context_service.py` around lines 53 - 54, The course
summary flow in course_context_service’s summary generation path currently
returns result.output.summary directly, which allows empty or whitespace-only
values to pass through. Update the logic around
run_agent_sync(course_summary_agent.run(user_message)) to validate
result.output.summary and raise/trigger the existing fallback when it is falsy
or blank, so the deterministic summary is used instead of persisting an empty
offering summary.

52-61: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Log the agent failure before falling back
Add a warning log with exc_info=True before returning the template so agent/runtime failures are visible instead of disappearing into the fallback path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@backend/services/course_context_service.py` around lines 52 - 61, The
fallback in course_context_service’s summary generation swallows agent/runtime
failures silently. Update the try/except around
run_agent_sync(course_summary_agent.run(user_message)) so the exception is
logged as a warning with exc_info=True before returning the template summary,
keeping the fallback behavior but making failures visible; use the surrounding
course_context_service method and course_summary_agent.run as the key locations.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@backend/tests/test_quiz_routes.py`:
- Around line 23-29: The submit-route tests are only neutralizing
`quiz_context_agent.run`, but `_update_context` in `routes.quiz` still persists
context via `save_quiz_context(...)`, which can trigger hidden writes or masked
failures. Update the affected tests/helpers around `_noop_ctx_agent()` to also
patch `routes.quiz.save_quiz_context` to a no-op wherever the submit flow is
exercised, so the background context update is fully disabled.
---
Nitpick comments:
In `@backend/routes/quiz.py`:
- Around line 202-207: The comment in the quiz generation path is stale because
the `RuntimeError` from `quiz_agent` no longer routes to a legacy fallback and
is now handled by the `generate_quiz` catch block as an HTTP 502. Update the
inline comment near the `wire_questions` validation in `generate_quiz`/the
surrounding fallback logic to describe the current behavior only, removing any
mention of legacy fallback and keeping the wording aligned with the new error
handling.
In `@backend/services/course_context_service.py`:
- Around line 53-54: The course summary flow in course_context_service’s summary
generation path currently returns result.output.summary directly, which allows
empty or whitespace-only values to pass through. Update the logic around
run_agent_sync(course_summary_agent.run(user_message)) to validate
result.output.summary and raise/trigger the existing fallback when it is falsy
or blank, so the deterministic summary is used instead of persisting an empty
offering summary.
- Around line 52-61: The fallback in course_context_service’s summary generation
swallows agent/runtime failures silently. Update the try/except around
run_agent_sync(course_summary_agent.run(user_message)) so the exception is
logged as a warning with exc_info=True before returning the template summary,
keeping the fallback behavior but making failures visible; use the surrounding
course_context_service method and course_summary_agent.run as the key locations.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c7510e3b-9aed-40df-a904-a8939c75af36

📥 Commits

Reviewing files that changed from the base of the PR and between 5de742e and 4e1b878.

📒 Files selected for processing (8)
  • backend/agents/_providers.py
  • backend/agents/course_summary.py
  • backend/agents/quiz_context.py
  • backend/routes/quiz.py
  • backend/services/course_context_service.py
  • backend/tests/test_quiz_routes.py
  • backend/tests/test_shared_course_context.py
  • specs/145-quiz-course-context-agents.md

Comment threadbackend/tests/test_quiz_routes.py
Jose-Gael-Cruz-Lopezand others added 2 commits July 1, 2026 13:17
…_context in submit mocks
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The all-questions-drift branch's comment still described routing to the
legacy fallback, which #145 deleted. The RuntimeError is now caught by
generate_quiz's bare-except and returned as HTTP 502 (per
test_degrades_when_all_questions_drift). Comment-only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@AndresL230
AndresL230 merged commit f472300 into mainJul 2, 2026
6 checks passed
@AndresL230
AndresL230 deleted the feat/145-quiz-agent branch August 2, 2026 18:30
Sign up for freeto 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.

[P2] Agent migration: quiz generation + course-context regen → agent

2 participants

@Jose-Gael-Cruz-Lopez@AndresL230