Skip to content

Add initial AI feedback module for PDF thesis artifacts#1149

Merged
bensofficial merged 10 commits into
developfrom
feat/add-initial-feedback-endpoint
Jun 30, 2026
Merged

Add initial AI feedback module for PDF thesis artifacts#1149
bensofficial merged 10 commits into
developfrom
feat/add-initial-feedback-endpoint

Conversation

@bensofficial

@bensofficial bensofficial commented Jun 28, 2026

Copy link
Copy Markdown
Member

Summary

Adds an experimental, feature-flagged AI feedback module that reviews uploaded thesis PDFs (initially proposals) by chunking the document into per-category LLM passes and merging the findings into a single structured result.

  • New POST /v2/ai-review/review-proposal endpoint (multipart upload, restricted to admin/advisor/supervisor) returning a ReviewResultDTO with findings (severity, title, category, description, page/section/quote locations).
  • ReviewService runs each ReviewCategory through LlmReviewer and merges intermediate results via a final Prompts.MERGER call against Spring AI's ChatClient. PdfService extracts per-page text and renders per-page images so the LLM gets both modalities.
  • Gated by a Spring Condition (AIFeaturesEnabledthesis-management.ai.enabled). When off, the controller and services aren't registered and the endpoint returns 404. Off by default in application.yml; enabled in application-dev.yml for local dev only.
  • Wires Spring AI's OpenAI starter (spring-ai-starter-model-openai) in server/build.gradle. Configurable via OPENAI_API_KEY / OPENAI_BASE_URL / OPENAI_CHAT_MODEL — defaults point at the chair's TUM GPU endpoint.
  • Documents the new flags in docs/CONFIGURATION.md and the experimental status / dev setup in docs/DEVELOPMENT.md.
  • Adds unit tests for ReviewController (with @TestPropertySource flipping the flag on), ReviewService, PdfService, and Prompts, plus fixture PDFs/images under server/src/test/resources/pdfs.

Status: work in progress / preview. The Azure provider path explicitly returns 400, and the controller still re-uploads the file rather than reusing the thesis service's already-stored artifact (TODO in the code). Treat as a foundation, not a finished feature.

Test plan

  • cd server && ./gradlew test — including the new feedback package tests
  • cd server && ./gradlew spotlessApply — no formatting drift
  • With the flag off (default), confirm POST /v2/ai-review/review-proposal returns 404
  • With the flag on and a reachable LLM endpoint, upload proposal-template.pdf and verify a populated ReviewResultDTO comes back

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added an experimental AI feedback flow for uploaded PDFs via POST /v2/ai-review/review-proposal, returning structured review results (category, summary, findings with per-page locations/images).
    • Added PDF extraction to support per-page text and rendered page images for review.
  • Bug Fixes
    • AI review is now opt-in via a feature flag; when disabled, the endpoint is not available, and unsupported provider categories are rejected.
  • Documentation
    • Documented new AI environment variables and added an “AI Feedback (Experimental)” configuration/feature-flag guide (including dev/test behavior).
  • Tests
    • Added integration/unit tests for the new endpoint, PDF extraction, and prompt/category mapping (including image snapshot verification).
  • Chores
    • Updated the server SBOM input fingerprint.

@coderabbitai

coderabbitai Bot commented Jun 28, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9c8d929a-8f81-425a-a401-8fc27a3f8b88

📥 Commits

Reviewing files that changed from the base of the PR and between ddc2e71 and 97ade87.

📒 Files selected for processing (6)
  • server/build.gradle
  • server/sbom/.input-hash
  • server/sbom/bom.json
  • server/src/main/java/de/tum/cit/aet/thesis/feedback/service/PdfService.java
  • server/src/main/java/de/tum/cit/aet/thesis/feedback/service/reviewer/LlmReviewer.java
  • server/src/test/java/de/tum/cit/aet/thesis/feedback/service/reviewer/LlmReviewerTest.java
✅ Files skipped from review due to trivial changes (1)
  • server/sbom/.input-hash
🚧 Files skipped from review as they are similar to previous changes (3)
  • server/build.gradle
  • server/src/main/java/de/tum/cit/aet/thesis/feedback/service/reviewer/LlmReviewer.java
  • server/src/main/java/de/tum/cit/aet/thesis/feedback/service/PdfService.java

Walkthrough

Adds an experimental AI feedback module for thesis PDF review behind a feature flag. It extracts PDF text and images, runs per-category LLM reviews, merges intermediate findings into one response, and exposes a multipart review endpoint. Configuration, prompts, docs, and tests were updated.

Changes

AI Feedback Module

Layer / File(s) Summary
Dependencies, feature flag, and configuration
server/build.gradle, server/src/main/java/.../config/AIFeaturesEnabled.java, server/src/main/resources/application*.yml, server/src/test/resources/application.yml, server/sbom/.input-hash
Adds Spring AI dependencies, introduces the thesis-management.ai.enabled condition, wires OpenAI settings and feature-flag defaults across main/dev/test configuration, and updates the SBOM hash.
DTO and enum contracts
server/src/main/java/.../feedback/dto/*.java
Introduces the request, response, intermediate, and supporting DTO/enum types used by the AI review flow.
Prompt templates and ReviewCategory
server/src/main/java/.../reviewer/Prompts.java, server/src/main/java/.../reviewer/ReviewCategory.java
Centralizes prompt templates and maps review slugs to prompt-backed review categories with lookup support.
PdfService: text and image extraction
server/src/main/java/.../service/PdfService.java, server/src/test/java/.../service/PdfServiceTest.java
Implements PDF text extraction, PDF image rendering, page-limit validation, and the corresponding extraction tests and snapshots.
LlmReviewer and ReviewService orchestration
server/src/main/java/.../service/reviewer/LlmReviewer.java, server/src/main/java/.../service/ReviewService.java, server/src/test/java/.../service/ReviewServiceTest.java, server/src/test/java/.../service/reviewer/LlmReviewerTest.java
Builds per-category LLM requests from extracted PDF content, collects intermediate results, merges them into the final review response, and verifies the prompt and merge flow.
ReviewController REST endpoint
server/src/main/java/.../controller/ReviewController.java, server/src/test/java/.../controller/ReviewControllerTest.java
Adds the conditional multipart review endpoint with role checks and AZURE provider rejection, plus controller integration coverage.
Tests and fixtures
server/src/test/resources/pdfs/text.md
Provides supporting fixture content for the PDF extraction tests.
Docs
docs/CONFIGURATION.md, docs/DEVELOPMENT.md
Expands the configuration reference and development notes for the AI feedback module and its runtime profile behavior.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested reviewers

  • Claudia-Anthropica
  • vikikab
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% 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 name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding an initial AI feedback module for PDF thesis artifacts.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/add-initial-feedback-endpoint

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.

@Claudia-Anthropica Claudia-Anthropica left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@bensofficial The AI review pipeline shape is understandable, but this head currently has two failing server tests and the new Spring AI dependencies leave the bundled SBOM stale. I also left comments on request validation and PDF rendering bounds; please fix these before merge.

Comment thread server/build.gradle
Comment thread server/src/test/java/de/tum/cit/aet/thesis/feedback/service/PdfServiceTest.java Outdated

@Claudia-Anthropica Claudia-Anthropica left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@bensofficial The SBOM and the failing feedback tests are fixed now; the targeted feedback test package passes locally. I am still requesting changes because the request validation and PDF page-count guard are still missing in this PR, and I replied on those two threads.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🧹 Nitpick comments (11)
server/src/test/java/de/tum/cit/aet/thesis/feedback/controller/ReviewControllerTest.java (1)

3-4: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use AssertJ here to match the test conventions.

This test is on the right path, but the assertions should use assertThat(...) instead of JUnit’s assertEquals/assertArrayEquals.

Suggested change
-import static org.junit.jupiter.api.Assertions.assertArrayEquals;
-import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.assertj.core.api.Assertions.assertThat;
@@
-		assertEquals("LOCAL", capturedRequest.providerCategory().name());
-		assertEquals("proposal.pdf", capturedRequest.file().getOriginalFilename());
-		assertArrayEquals(proposalTemplateContent, capturedRequest.file().getBytes());
+		assertThat(capturedRequest.providerCategory().name()).isEqualTo("LOCAL");
+		assertThat(capturedRequest.file().getOriginalFilename()).isEqualTo("proposal.pdf");
+		assertThat(capturedRequest.file().getBytes()).isEqualTo(proposalTemplateContent);

As per path instructions, server/src/test/java/**/*.java: “Use AssertJ assertions (assertThat) over JUnit assertions”.

Also applies to: 83-85

🤖 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
`@server/src/test/java/de/tum/cit/aet/thesis/feedback/controller/ReviewControllerTest.java`
around lines 3 - 4, The test imports in ReviewControllerTest should follow the
AssertJ convention instead of JUnit assertions. Replace the JUnit static imports
with AssertJ’s assertThat usage, and update the affected assertions in this test
to use assertThat(...) for both array and scalar checks so the test matches the
surrounding server test style.

Source: Path instructions

server/src/main/java/de/tum/cit/aet/thesis/feedback/service/ReviewService.java (2)

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

Remove the production TODO or move it to an issue.

As per path instructions, "Avoid leaving TODO comments in production code".

🤖 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
`@server/src/main/java/de/tum/cit/aet/thesis/feedback/service/ReviewService.java`
at line 58, Remove the production TODO from ReviewService and replace it with a
proper implementation or move the note into an external issue/task tracker.
Locate the comment in the review flow around the ReviewService logic and delete
the TODO marker so no production code contains leftover planning notes.

Source: Path instructions


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

Preserve review category order through the merge step.

The loop runs ReviewCategory.values() in enum order, but new HashMap<>() drops that contract before buildMergePrompt(). That makes the final prompt less reproducible and harder to debug. A LinkedHashMap (or building the merge input directly in the loop) would keep the pipeline deterministic.

Also applies to: 74-96

🤖 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
`@server/src/main/java/de/tum/cit/aet/thesis/feedback/service/ReviewService.java`
at line 56, The merge input in ReviewService is losing the enum iteration order
because reviewResults is created as a HashMap, which makes buildMergePrompt()
non-deterministic. Change the collection used in the ReviewService flow to
preserve insertion order, such as a LinkedHashMap, or build the merge input
directly while iterating ReviewCategory.values() so the review categories stay
in enum order through the merge step.
server/src/test/java/de/tum/cit/aet/thesis/feedback/service/ReviewServiceTest.java (2)

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

Rename these tests to describe scenario and expected outcome.

testReview and testBuildMergePrompt are too generic for future failures and reports. As per path instructions, "Descriptive test method names that explain scenario and expected outcome".

Also applies to: 98-98

🤖 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
`@server/src/test/java/de/tum/cit/aet/thesis/feedback/service/ReviewServiceTest.java`
at line 70, Rename the generic test methods in ReviewServiceTest, especially
testReview and testBuildMergePrompt, so each name clearly states the scenario
and expected outcome. Update the method names to follow the path guidance for
descriptive test names, using the existing ReviewServiceTest, testReview, and
testBuildMergePrompt methods as the references to locate them.

Source: Path instructions


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

Derive the expected call count from ReviewCategory.

times(9) bakes the current enum size into the test, so adding or removing a review category will break this assertion even if the production loop is still correct. Verifying against ReviewCategory.values().length keeps the test aligned with the actual contract.

🤖 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
`@server/src/test/java/de/tum/cit/aet/thesis/feedback/service/ReviewServiceTest.java`
at line 92, The test in ReviewServiceTest hardcodes the llmReviewer.review call
count with times(9), which couples it to the current number of ReviewCategory
entries. Update the verification to derive the expected count from
ReviewCategory.values().length so the assertion stays aligned with the loop in
ReviewService and continues to match the contract if categories change.
server/src/main/java/de/tum/cit/aet/thesis/feedback/controller/ReviewController.java (1)

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

Remove the production TODO or track it outside the code.

As per path instructions, "Avoid leaving TODO comments in production code".

🤖 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
`@server/src/main/java/de/tum/cit/aet/thesis/feedback/controller/ReviewController.java`
at line 46, Remove the production TODO comment in ReviewController and, if the
follow-up is still needed, track it in an external issue instead of leaving it
in the code. Update the ReviewController logic so the comment about reusing the
already uploaded thesis-service file is either implemented or omitted, keeping
the production code free of TODOs.

Source: Path instructions

server/src/test/java/de/tum/cit/aet/thesis/feedback/service/PdfServiceTest.java (1)

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

Use AssertJ consistently in this class.

The file already uses assertThat, but most assertions still go through JUnit. Converting the rest will keep failure output consistent with the repo’s test style. As per path instructions, "Use AssertJ assertions (assertThat) over JUnit assertions".

Also applies to: 39-39, 67-74, 82-82, 92-113, 121-121

🤖 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
`@server/src/test/java/de/tum/cit/aet/thesis/feedback/service/PdfServiceTest.java`
at line 4, In PdfServiceTest, replace the remaining JUnit assertions imported
from Assertions with AssertJ-style assertions so the class uses assertThat
consistently throughout. Update the assertion blocks in the test methods
currently using JUnit helpers to use AssertJ equivalents, keeping the existing
test structure intact and ensuring the class only relies on the AssertJ
assertion style already present.

Source: Path instructions

server/src/main/java/de/tum/cit/aet/thesis/feedback/dto/FindingDTO.java (1)

3-5: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add @JsonInclude(NON_EMPTY) to this DTO.

This record is in the DTO path but currently serializes empty strings and empty collections instead of following the repository’s DTO contract.

Proposed fix
+import com.fasterxml.jackson.annotation.JsonInclude;
 import java.util.List;
 
+@JsonInclude(JsonInclude.Include.NON_EMPTY)
 public record FindingDTO(String severity, String category, String title, String description, List<Location> locations) {
 }

As per coding guidelines, server/**/dto/**/*.java: All DTOs use @JsonInclude(JsonInclude.Include.NON_EMPTY) to omit null, empty strings, and empty collections from JSON. Based on learnings, apply JsonInclude(JsonInclude.Include.NON_EMPTY) to all DTOs to minimize payload size by excluding null or empty values from JSON.

🤖 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 `@server/src/main/java/de/tum/cit/aet/thesis/feedback/dto/FindingDTO.java`
around lines 3 - 5, Add JsonInclude(JsonInclude.Include.NON_EMPTY) to the
FindingDTO record so it follows the DTO serialization contract and omits null,
empty strings, and empty collections. Update the FindingDTO declaration to
include the Jackson annotation, and ensure any required JsonInclude import is
added alongside the existing record fields.

Sources: Coding guidelines, Learnings

server/src/main/java/de/tum/cit/aet/thesis/feedback/dto/ReviewResultDTO.java (1)

3-5: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add @JsonInclude(NON_EMPTY) to the response DTO.

The response record is missing the DTO serialization rule used elsewhere in the project.

Proposed fix
+import com.fasterxml.jackson.annotation.JsonInclude;
 import java.util.List;
 
+@JsonInclude(JsonInclude.Include.NON_EMPTY)
 public record ReviewResultDTO(AssessmentCategory category, String summary, List<FindingDTO> findings) {
 }

As per coding guidelines, server/**/dto/**/*.java: All DTOs use @JsonInclude(JsonInclude.Include.NON_EMPTY) to omit null, empty strings, and empty collections from JSON. Based on learnings, apply JsonInclude(JsonInclude.Include.NON_EMPTY) to all DTOs to minimize payload size by excluding null or empty values from JSON.

🤖 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 `@server/src/main/java/de/tum/cit/aet/thesis/feedback/dto/ReviewResultDTO.java`
around lines 3 - 5, The ReviewResultDTO record is missing the project-wide JSON
serialization rule, so update this DTO to use JsonInclude with NON_EMPTY like
the other DTOs. Add the annotation to ReviewResultDTO so its category, summary,
and findings fields are omitted from JSON when null or empty, following the same
pattern used across server DTOs.

Sources: Coding guidelines, Learnings

server/src/main/java/de/tum/cit/aet/thesis/feedback/dto/IntermediateReviewResult.java (1)

3-5: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add @JsonInclude(NON_EMPTY) to this DTO.

This record also misses the DTO-level JSON inclusion rule, so empty findings payloads will be serialized instead of following the project convention.

Proposed fix
+import com.fasterxml.jackson.annotation.JsonInclude;
 import java.util.List;
 
+@JsonInclude(JsonInclude.Include.NON_EMPTY)
 public record IntermediateReviewResult(List<FindingDTO> findings) {
 }

As per coding guidelines, server/**/dto/**/*.java: All DTOs use @JsonInclude(JsonInclude.Include.NON_EMPTY) to omit null, empty strings, and empty collections from JSON. Based on learnings, apply JsonInclude(JsonInclude.Include.NON_EMPTY) to all DTOs to minimize payload size by excluding null or empty values from JSON.

🤖 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
`@server/src/main/java/de/tum/cit/aet/thesis/feedback/dto/IntermediateReviewResult.java`
around lines 3 - 5, The IntermediateReviewResult DTO is missing the project-wide
JSON inclusion rule, so empty findings can still be serialized. Add the
DTO-level JsonInclude annotation with NON_EMPTY on the IntermediateReviewResult
record, matching the convention used by other DTOs in this package so null and
empty values are omitted from JSON output.

Sources: Coding guidelines, Learnings

server/src/test/java/de/tum/cit/aet/thesis/feedback/service/reviewer/PromptsTest.java (1)

9-20: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Split this into focused tests and use AssertJ

This test mixes nine valid slugs with the invalid-slug case. A parameterized happy-path test plus a separate invalid-slug test would make failures easier to pinpoint, and assertThat/assertThatThrownBy would match the rest of the test suite better.

🤖 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
`@server/src/test/java/de/tum/cit/aet/thesis/feedback/service/reviewer/PromptsTest.java`
around lines 9 - 20, The current PromptsTest mixes all valid
ReviewCategory.fromSlug mappings with the invalid-slug case in one method,
making failures harder to isolate. Split it into a parameterized happy-path test
for the known slugs in ReviewCategory.fromSlug and a separate test for the
invalid input, and switch the assertions to AssertJ using assertThat for prompt
equality and assertThatThrownBy for the exception case.

Source: Path instructions

🤖 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 `@server/src/main/java/de/tum/cit/aet/thesis/feedback/dto/Location.java`:
- Around line 1-4: Add `@JsonInclude`(JsonInclude.Include.NON_EMPTY) to the
Location record so the DTO layer omits null and empty values from JSON. Update
the Location type itself in the dto package, following the same pattern used by
other server DTOs, and ensure page, section, and quote are excluded when absent
rather than serialized as empty payload fields.

In
`@server/src/main/java/de/tum/cit/aet/thesis/feedback/dto/ReviewRequestDTO.java`:
- Around line 3-5: ReviewRequestDTO should enforce that both providerCategory
and file are present at the request boundary instead of allowing null binding.
Add bean validation constraints to the ReviewRequestDTO record components, and
ensure the controller path that consumes ReviewRequestDTO triggers validation so
missing multipart fields fail fast with a 400. Use the ReviewRequestDTO record
itself and the request-handling endpoint that accepts it to locate the fix.

In `@server/src/main/java/de/tum/cit/aet/thesis/feedback/service/PdfService.java`:
- Around line 41-54: The PDF parsing path in PdfService.extractTextFromPdf
currently turns unreadable or non-PDF uploads into a generic RuntimeException,
which will surface as a server error instead of a client error. Update the error
handling around file bytes conversion and the PagePdfDocumentReader
reader.read() call to throw or preserve a dedicated bad-request type that
controller advice can map to 400, and keep the distinction clear in any related
Loader.loadPDF path as well. Use the PdfService.extractTextFromPdf and
PagePdfDocumentReader symbols to locate the fix.

In
`@server/src/main/java/de/tum/cit/aet/thesis/feedback/service/reviewer/LlmReviewer.java`:
- Around line 54-60: The review flow in LlmReviewer.review is sending the entire
document text and all page images in one chatClient call, which will not scale
for larger PDFs. Update review() to process pages in bounded chunks or windows
and invoke the model per batch instead of building one pagesText for the full
list. Keep the existing prompt construction (sharedPrompt, taskPrompt,
guidelinesPrompt) but apply it per batch, then merge the
IntermediateReviewResult outputs into a final result for the caller.

In
`@server/src/main/java/de/tum/cit/aet/thesis/feedback/service/reviewer/Prompts.java`:
- Around line 117-129: The proposal length guidance in Prompts is inconsistent
because it says both “around 5-7 pages” and “at most 4-6 text pages.” Update the
proposal instructions to a single, non-conflicting range in the relevant prompt
text so the length requirement is clear and stable; adjust the wording near the
proposal overview and the “Proposal Structure” section to match one consistent
target length.

In
`@server/src/main/java/de/tum/cit/aet/thesis/feedback/service/ReviewService.java`:
- Around line 67-68: The merger prompt in ReviewService.buildMergePrompt and the
chatClient.prompt() call is treating intermediate findings as raw prompt text,
which lets title, description, and especially quote influence the second LLM
call. Update the flow so reviewResults are serialized and passed as structured
JSON or otherwise fully escaped/fenced content, and adjust Prompts.MERGER to
explicitly instruct the model to treat all intermediate fields as data only, not
instructions.

In `@server/src/main/resources/application.yml`:
- Around line 59-65: The shared OpenAI configuration in application.yml
hardcodes a real external base-url, so any environment enabling ai features will
send data there by default. Move the concrete endpoint out of the shared
ai.openai.base-url setting into a local/dev-only profile file, and keep the
shared application.yml env-driven so the endpoint must be explicitly provided
via OPENAI_BASE_URL. Update the ai.openai section to use only environment
variables in the shared config, and preserve the existing OPENAI_API_KEY and
model/temperature pattern.

In
`@server/src/test/java/de/tum/cit/aet/thesis/feedback/service/PdfServiceTest.java`:
- Around line 106-110: The snapshot helper in PdfServiceTest should not create
or write missing files; keep assertImageMatchesSnapshot read-only by removing
the Files.createDirectories/Files.write path and instead failing immediately
with a clear message when the expected snapshot is absent. Update the logic in
assertImageMatchesSnapshot so the test only compares against existing fixtures
under src/test/resources and never mutates them.

---

Nitpick comments:
In
`@server/src/main/java/de/tum/cit/aet/thesis/feedback/controller/ReviewController.java`:
- Line 46: Remove the production TODO comment in ReviewController and, if the
follow-up is still needed, track it in an external issue instead of leaving it
in the code. Update the ReviewController logic so the comment about reusing the
already uploaded thesis-service file is either implemented or omitted, keeping
the production code free of TODOs.

In `@server/src/main/java/de/tum/cit/aet/thesis/feedback/dto/FindingDTO.java`:
- Around line 3-5: Add JsonInclude(JsonInclude.Include.NON_EMPTY) to the
FindingDTO record so it follows the DTO serialization contract and omits null,
empty strings, and empty collections. Update the FindingDTO declaration to
include the Jackson annotation, and ensure any required JsonInclude import is
added alongside the existing record fields.

In
`@server/src/main/java/de/tum/cit/aet/thesis/feedback/dto/IntermediateReviewResult.java`:
- Around line 3-5: The IntermediateReviewResult DTO is missing the project-wide
JSON inclusion rule, so empty findings can still be serialized. Add the
DTO-level JsonInclude annotation with NON_EMPTY on the IntermediateReviewResult
record, matching the convention used by other DTOs in this package so null and
empty values are omitted from JSON output.

In
`@server/src/main/java/de/tum/cit/aet/thesis/feedback/dto/ReviewResultDTO.java`:
- Around line 3-5: The ReviewResultDTO record is missing the project-wide JSON
serialization rule, so update this DTO to use JsonInclude with NON_EMPTY like
the other DTOs. Add the annotation to ReviewResultDTO so its category, summary,
and findings fields are omitted from JSON when null or empty, following the same
pattern used across server DTOs.

In
`@server/src/main/java/de/tum/cit/aet/thesis/feedback/service/ReviewService.java`:
- Line 58: Remove the production TODO from ReviewService and replace it with a
proper implementation or move the note into an external issue/task tracker.
Locate the comment in the review flow around the ReviewService logic and delete
the TODO marker so no production code contains leftover planning notes.
- Line 56: The merge input in ReviewService is losing the enum iteration order
because reviewResults is created as a HashMap, which makes buildMergePrompt()
non-deterministic. Change the collection used in the ReviewService flow to
preserve insertion order, such as a LinkedHashMap, or build the merge input
directly while iterating ReviewCategory.values() so the review categories stay
in enum order through the merge step.

In
`@server/src/test/java/de/tum/cit/aet/thesis/feedback/controller/ReviewControllerTest.java`:
- Around line 3-4: The test imports in ReviewControllerTest should follow the
AssertJ convention instead of JUnit assertions. Replace the JUnit static imports
with AssertJ’s assertThat usage, and update the affected assertions in this test
to use assertThat(...) for both array and scalar checks so the test matches the
surrounding server test style.

In
`@server/src/test/java/de/tum/cit/aet/thesis/feedback/service/PdfServiceTest.java`:
- Line 4: In PdfServiceTest, replace the remaining JUnit assertions imported
from Assertions with AssertJ-style assertions so the class uses assertThat
consistently throughout. Update the assertion blocks in the test methods
currently using JUnit helpers to use AssertJ equivalents, keeping the existing
test structure intact and ensuring the class only relies on the AssertJ
assertion style already present.

In
`@server/src/test/java/de/tum/cit/aet/thesis/feedback/service/reviewer/PromptsTest.java`:
- Around line 9-20: The current PromptsTest mixes all valid
ReviewCategory.fromSlug mappings with the invalid-slug case in one method,
making failures harder to isolate. Split it into a parameterized happy-path test
for the known slugs in ReviewCategory.fromSlug and a separate test for the
invalid input, and switch the assertions to AssertJ using assertThat for prompt
equality and assertThatThrownBy for the exception case.

In
`@server/src/test/java/de/tum/cit/aet/thesis/feedback/service/ReviewServiceTest.java`:
- Line 70: Rename the generic test methods in ReviewServiceTest, especially
testReview and testBuildMergePrompt, so each name clearly states the scenario
and expected outcome. Update the method names to follow the path guidance for
descriptive test names, using the existing ReviewServiceTest, testReview, and
testBuildMergePrompt methods as the references to locate them.
- Line 92: The test in ReviewServiceTest hardcodes the llmReviewer.review call
count with times(9), which couples it to the current number of ReviewCategory
entries. Update the verification to derive the expected count from
ReviewCategory.values().length so the assertion stays aligned with the loop in
ReviewService and continues to match the contract if categories change.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 82837810-8524-42f0-8011-e0525c30167b

📥 Commits

Reviewing files that changed from the base of the PR and between 586e6ca and f35fc10.

⛔ Files ignored due to path filters (6)
  • server/src/test/resources/pdfs/hello-world.pdf is excluded by !**/*.pdf, !**/*.pdf
  • server/src/test/resources/pdfs/pdf-images/proposal-template-page-1.png is excluded by !**/*.png, !**/*.png
  • server/src/test/resources/pdfs/pdf-images/proposal-template-page-2.png is excluded by !**/*.png, !**/*.png
  • server/src/test/resources/pdfs/pdf-images/proposal-template-page-3.png is excluded by !**/*.png, !**/*.png
  • server/src/test/resources/pdfs/pdf-images/proposal-template-page-4.png is excluded by !**/*.png, !**/*.png
  • server/src/test/resources/pdfs/proposal-template.pdf is excluded by !**/*.pdf, !**/*.pdf
📒 Files selected for processing (27)
  • docs/CONFIGURATION.md
  • docs/DEVELOPMENT.md
  • server/build.gradle
  • server/sbom/.input-hash
  • server/sbom/bom.json
  • server/src/main/java/de/tum/cit/aet/thesis/feedback/config/AIFeaturesEnabled.java
  • server/src/main/java/de/tum/cit/aet/thesis/feedback/controller/ReviewController.java
  • server/src/main/java/de/tum/cit/aet/thesis/feedback/dto/AssessmentCategory.java
  • server/src/main/java/de/tum/cit/aet/thesis/feedback/dto/FindingDTO.java
  • server/src/main/java/de/tum/cit/aet/thesis/feedback/dto/IntermediateReviewResult.java
  • server/src/main/java/de/tum/cit/aet/thesis/feedback/dto/Location.java
  • server/src/main/java/de/tum/cit/aet/thesis/feedback/dto/ProviderCategory.java
  • server/src/main/java/de/tum/cit/aet/thesis/feedback/dto/ReviewRequestDTO.java
  • server/src/main/java/de/tum/cit/aet/thesis/feedback/dto/ReviewResultDTO.java
  • server/src/main/java/de/tum/cit/aet/thesis/feedback/service/PdfService.java
  • server/src/main/java/de/tum/cit/aet/thesis/feedback/service/ReviewService.java
  • server/src/main/java/de/tum/cit/aet/thesis/feedback/service/reviewer/LlmReviewer.java
  • server/src/main/java/de/tum/cit/aet/thesis/feedback/service/reviewer/Prompts.java
  • server/src/main/java/de/tum/cit/aet/thesis/feedback/service/reviewer/ReviewCategory.java
  • server/src/main/resources/application-dev.yml
  • server/src/main/resources/application.yml
  • server/src/test/java/de/tum/cit/aet/thesis/feedback/controller/ReviewControllerTest.java
  • server/src/test/java/de/tum/cit/aet/thesis/feedback/service/PdfServiceTest.java
  • server/src/test/java/de/tum/cit/aet/thesis/feedback/service/ReviewServiceTest.java
  • server/src/test/java/de/tum/cit/aet/thesis/feedback/service/reviewer/PromptsTest.java
  • server/src/test/resources/application.yml
  • server/src/test/resources/pdfs/text.md

Comment thread server/src/main/java/de/tum/cit/aet/thesis/feedback/dto/ReviewRequestDTO.java Outdated
Comment thread server/src/main/resources/application.yml

@Claudia-Anthropica Claudia-Anthropica left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@bensofficial The validation, SBOM, and previously failing feedback tests look good now; I ran the targeted feedback package locally and it passes. I’m still requesting changes because the PDF page-count guard is set to 120 pages, which is too loose for this proposal endpoint; I reopened and replied on that thread.

@Claudia-Anthropica Claudia-Anthropica left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@bensofficial All requested feedback is settled now; I’m accepting the 120-page cap as the current constraint for thesis-sized uploads. Build/test/security checks are passing, with only the dev-container deploy job still pending, so approving.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
server/src/main/java/de/tum/cit/aet/thesis/feedback/service/ReviewService.java (2)

69-70: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Do not log full intermediate findings.

intermediateResult contains model output derived from uploaded thesis PDFs, including quoted document text. Persisting that at debug level leaks sensitive manuscript content into application logs.

Suggested fix
-			log.debug("Review result for category {}: {}", category.getSlug(), intermediateResult);
+			log.debug("Review result for category {} contains {} findings", category.getSlug(), intermediateResult.findings().size());
🤖 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
`@server/src/main/java/de/tum/cit/aet/thesis/feedback/service/ReviewService.java`
around lines 69 - 70, The debug log in ReviewService.review is leaking sensitive
model output because it prints the full IntermediateReviewResult, which may
include quoted thesis content. Remove the object dump from the log and replace
it with a minimal summary using safe identifiers like category.getSlug() and
non-sensitive metadata from intermediateResult, so the method still logs
progress without exposing manuscript text.

63-64: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Preserve category order in the merger input.

Line 63 uses a HashMap, but this prompt is built from an ordered enum walk. Switching to LinkedHashMap keeps the merger input stable across runs instead of relying on map iteration order. (docs.oracle.com)

Suggested fix
-		Map<String, IntermediateReviewResult> reviewResults = new HashMap<>();
+		Map<String, IntermediateReviewResult> reviewResults = new LinkedHashMap<>();
🤖 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
`@server/src/main/java/de/tum/cit/aet/thesis/feedback/service/ReviewService.java`
around lines 63 - 64, The merger input in ReviewService should preserve the enum
traversal order instead of using an unordered HashMap. Update the reviewResults
collection in ReviewService to use LinkedHashMap so the category sequence stays
stable across runs and the prompt assembly remains deterministic.
🧹 Nitpick comments (1)
server/src/main/java/de/tum/cit/aet/thesis/feedback/service/ReviewService.java (1)

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

Remove the production TODO.

Please track this outside the code or replace it with a concrete issue reference. As per path instructions, "Avoid leaving TODO comments in production code."

🤖 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
`@server/src/main/java/de/tum/cit/aet/thesis/feedback/service/ReviewService.java`
at line 65, The TODO comment in ReviewService should not remain in production
code. Remove the TODO from the review process code path and, if needed, replace
it with a concrete issue reference or tracking note outside the source; use the
ReviewService review flow as the target area to locate and clean up the comment.

Source: Path instructions

🤖 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 `@server/src/main/java/de/tum/cit/aet/thesis/feedback/service/PdfService.java`:
- Around line 104-108: The page-limit check in PdfService.assertWithinPageLimit
only enforces the maximum page count, so empty PDFs still pass through. Update
this helper to reject pageCount values of 0 (or less, if applicable) with a
ResourceInvalidParametersException, alongside the existing MAX_PAGES upper-bound
validation. Keep the fix localized in PdfService so ReviewService.review() and
the extractors never receive zero-page input.

---

Outside diff comments:
In
`@server/src/main/java/de/tum/cit/aet/thesis/feedback/service/ReviewService.java`:
- Around line 69-70: The debug log in ReviewService.review is leaking sensitive
model output because it prints the full IntermediateReviewResult, which may
include quoted thesis content. Remove the object dump from the log and replace
it with a minimal summary using safe identifiers like category.getSlug() and
non-sensitive metadata from intermediateResult, so the method still logs
progress without exposing manuscript text.
- Around line 63-64: The merger input in ReviewService should preserve the enum
traversal order instead of using an unordered HashMap. Update the reviewResults
collection in ReviewService to use LinkedHashMap so the category sequence stays
stable across runs and the prompt assembly remains deterministic.

---

Nitpick comments:
In
`@server/src/main/java/de/tum/cit/aet/thesis/feedback/service/ReviewService.java`:
- Line 65: The TODO comment in ReviewService should not remain in production
code. Remove the TODO from the review process code path and, if needed, replace
it with a concrete issue reference or tracking note outside the source; use the
ReviewService review flow as the target area to locate and clean up the comment.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 65655432-3e30-43ba-86f1-9d5a7093c4cd

📥 Commits

Reviewing files that changed from the base of the PR and between f35fc10 and ddc2e71.

📒 Files selected for processing (9)
  • server/src/main/java/de/tum/cit/aet/thesis/feedback/controller/ReviewController.java
  • server/src/main/java/de/tum/cit/aet/thesis/feedback/dto/FindingDTO.java
  • server/src/main/java/de/tum/cit/aet/thesis/feedback/dto/Location.java
  • server/src/main/java/de/tum/cit/aet/thesis/feedback/dto/ReviewRequestDTO.java
  • server/src/main/java/de/tum/cit/aet/thesis/feedback/dto/ReviewResultDTO.java
  • server/src/main/java/de/tum/cit/aet/thesis/feedback/service/PdfService.java
  • server/src/main/java/de/tum/cit/aet/thesis/feedback/service/ReviewService.java
  • server/src/main/java/de/tum/cit/aet/thesis/feedback/service/reviewer/Prompts.java
  • server/src/test/java/de/tum/cit/aet/thesis/feedback/service/ReviewServiceTest.java
🚧 Files skipped from review as they are similar to previous changes (7)
  • server/src/main/java/de/tum/cit/aet/thesis/feedback/dto/FindingDTO.java
  • server/src/main/java/de/tum/cit/aet/thesis/feedback/dto/Location.java
  • server/src/main/java/de/tum/cit/aet/thesis/feedback/dto/ReviewResultDTO.java
  • server/src/main/java/de/tum/cit/aet/thesis/feedback/dto/ReviewRequestDTO.java
  • server/src/test/java/de/tum/cit/aet/thesis/feedback/service/ReviewServiceTest.java
  • server/src/main/java/de/tum/cit/aet/thesis/feedback/service/reviewer/Prompts.java
  • server/src/main/java/de/tum/cit/aet/thesis/feedback/controller/ReviewController.java

@Claudia-Anthropica Claudia-Anthropica left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@bensofficial The latest fixes address the previous threads and the targeted feedback tests pass locally. I found one remaining prompt-injection gap in the first reviewer pass; see the inline comment.

@bensofficial bensofficial self-assigned this Jun 28, 2026
@bensofficial bensofficial added the enhancement New feature or request label Jun 28, 2026

@Claudia-Anthropica Claudia-Anthropica left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@bensofficial The latest fix addresses the LlmReviewer prompt-injection thread, and the focused test passes locally. The build/test/security checks are passing; only the dev-container deploy job is still pending, so I’m approving.

@Claudia-Anthropica Claudia-Anthropica left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@bensofficial The dependency/SBOM refresh looks fine; I rechecked the feedback module and the calendar paths affected by the ical4j bump. Focused local tests passed, and GitHub build/test/e2e are green with only deploy and JavaScript/TypeScript CodeQL still pending, so approving.

@bensofficial bensofficial merged commit 0a49e4f into develop Jun 30, 2026
12 checks passed
@bensofficial bensofficial deleted the feat/add-initial-feedback-endpoint branch June 30, 2026 14:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants