-
Notifications
You must be signed in to change notification settings - Fork 343
Hyperion
: Add AI code generation assistance for template, solution, and tests repositories
#11405
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Hyperion
: Add AI code generation assistance for template, solution, and tests repositories
#11405
Conversation
End-to-End (E2E) Test Results Summary
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @luis-gasparschroeder, looks really good already :)
I found some places for potential improvements.
Some question from my side: How does the previous state of the repository get accounted for? Especially for when build issues arise? Do you just discard all and regenerate? It is not 100% clear to me
src/main/java/de/tum/cit/aet/artemis/hyperion/service/HyperionRepositoryStructureService.java
Outdated
Show resolved
Hide resolved
...a/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionCodeGenerationStrategy.java
Outdated
Show resolved
Hide resolved
...a/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionCodeGenerationStrategy.java
Outdated
Show resolved
Hide resolved
.../java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionSolutionRepository.java
Outdated
Show resolved
Hide resolved
.../java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionTemplateRepository.java
Outdated
Show resolved
Hide resolved
src/main/java/de/tum/cit/aet/artemis/hyperion/web/HyperionCodeGenerationResource.java
Outdated
Show resolved
Hide resolved
.../cit/aet/artemis/hyperion/service/codegeneration/HyperionCodeGenerationExecutionService.java
Show resolved
Hide resolved
.../cit/aet/artemis/hyperion/service/codegeneration/HyperionCodeGenerationExecutionService.java
Outdated
Show resolved
Hide resolved
...tor/instructor-and-editor-container/code-editor-instructor-and-editor-container.component.ts
Outdated
Show resolved
Hide resolved
…rciseContextRendererService
The code generation does not delete files at this point in time. Consequently, the pre-existing files remain. This functionality can be added in a follow-up PR |
da81272
236bbe0
End-to-End (E2E) Test Results Summary
|
Those bugs were related to changes in other Hyperion modules. Thanks for pointing them out! I fixed all of them such that the logic is working again. |
End-to-End (E2E) Test Results Summary
|
c78609c
End-to-End (E2E) Test Results Summary
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did a full run through the code, no issue found 👍
End-to-End (E2E) Test Results Summary
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code looks clean. no visible red flags from my side. 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
WalkthroughAdds an AI-driven code generation feature (backend services, REST endpoint, DTOs, prompt templates, tests), frontend OpenAPI client and UI integration, i18n strings, prompt resources for solution/template/test flows, and smaller changes: Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor Instructor
participant UI as Angular UI
participant FE as HyperionCodeGenerationApiService
participant BE as HyperionCodeGenerationResource
participant Exec as HyperionCodeGenerationExecutionService
participant Strat as Repo Strategy (Solution/Template/Test)
participant Tmpl as HyperionPromptTemplateService
participant Chat as ChatClient
participant Git as GitService/RepositoryService
participant CI as ContinuousIntegrationTriggerService
participant DB as ResultRepository
Instructor->>UI: Click "Generate Code"
UI->>FE: generateCode(exerciseId, {repositoryType})
FE->>BE: POST /api/hyperion/.../generate-code
BE->>Exec: generateAndCompileCode(exercise, user, repositoryType)
Exec->>Git: Determine default branch / setup repo
Exec->>Strat: generateCode(user, exercise, prevLogs, repoStructure)
Strat->>Tmpl: Render prompts (plan → structure → headers → logic)
Tmpl-->>Strat: Rendered templates
Strat->>Chat: Send prompts iteratively
Chat-->>Strat: JSON responses (plan/files)
Strat-->>Exec: List<GeneratedFile>
Exec->>Git: Update files, commit → get commitHash
Exec->>CI: Trigger build for commit
loop Poll until timeout
Exec->>DB: Query latest Result
DB-->>Exec: Result (success/failure) or none
end
alt Success
Exec-->>BE: Result(success)
BE-->>FE: {success:true, attempts:n}
FE-->>UI: Show success
else Failure/timeout
Exec-->>BE: failed/timeout
BE-->>FE: {success:false, attempts:n, message}
FE-->>UI: Show error/partial message
end
Exec->>Git: Reset to original commit (cleanup)
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches🧪 Generate unit tests
📜 Recent review detailsConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
✅ Files skipped from review due to trivial changes (1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
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. 🧪 Early access (Sonnet 4.5): enabledWe are currently testing the Sonnet 4.5 model, which is expected to improve code review quality. However, this model may lead to increased noise levels in the review comments. Please disable the early access features if the noise level causes any inconvenience. Note:
Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 19
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/main/java/de/tum/cit/aet/artemis/programming/domain/Repository.java (1)
29-31
: Compilation issue:File
is unqualified — missingjava.io.File
importConstructor uses
File gitDir
but this class isn’t imported. This will not compile. Add the import (methodisValidFile
already uses the FQN, that can stay).import java.io.IOException; +import java.io.File; import java.nio.file.Path;
🧹 Nitpick comments (45)
src/main/java/de/tum/cit/aet/artemis/buildagent/service/BuildJobContainerService.java (2)
453-483
: Reduce overhead, add backoff/jitter, and avoid retrying non‑transient errors
- Compute the TAR once outside the loop to avoid repeated CPU/memory work per attempt.
- Don’t retry on NotFoundException/IllegalArgumentException; fail fast.
- Use exponential backoff with jitter; make retries/delay configurable.
- Log stack trace during retries for easier triage.
@@ - private void copyToContainer(Path sourcePath, String containerId) { - final int maxRetries = 3; - final long retryDelayMs = 1000; + private void copyToContainer(Path sourcePath, String containerId) { + final int maxRetries = copyToContainerMaxRetries; // configurable + final long baseDelayMs = copyToContainerBaseDelayMs; // configurable + + // Build TAR once; reuse the bytes for each attempt + final byte[] tarBytes = createTarArchive(sourcePath).toByteArray(); @@ - for (int attempt = 1; attempt <= maxRetries; attempt++) { - try (final var uploadStream = new ByteArrayInputStream(createTarArchive(sourcePath).toByteArray()); - final var copyToContainerCommand = buildAgentConfiguration.getDockerClient().copyArchiveToContainerCmd(containerId) - .withRemotePath(LOCAL_CI_DOCKER_CONTAINER_WORKING_DIRECTORY).withTarInputStream(uploadStream)) { - copyToContainerCommand.exec(); - return; - } - catch (Exception e) { + for (int attempt = 1; attempt <= maxRetries; attempt++) { + try (var uploadStream = new ByteArrayInputStream(tarBytes); + var copyToContainerCommand = buildAgentConfiguration.getDockerClient().copyArchiveToContainerCmd(containerId) + .withRemotePath(LOCAL_CI_DOCKER_CONTAINER_WORKING_DIRECTORY) + .withTarInputStream(uploadStream)) { + copyToContainerCommand.exec(); + return; + } + catch (NotFoundException | IllegalArgumentException e) { + // Non-transient: container missing/invalid input — don't retry + throw new LocalCIException("Copy to container failed (non-retryable) for " + containerId, e); + } + catch (Exception e) { if (attempt == maxRetries) { throw new LocalCIException("Could not copy to container " + containerId + " after " + maxRetries + " attempts", e); } - log.warn("Attempt {} failed to copy to container {}: {}. Retrying in {} ms...", attempt, containerId, e.getMessage(), retryDelayMs); - try { - Thread.sleep(retryDelayMs); - } - catch (InterruptedException ie) { + // exp-backoff with jitter + long delay = Math.min(baseDelayMs << (attempt - 1), 8000L); + long jitter = java.util.concurrent.ThreadLocalRandom.current().nextLong((delay / 5) + 1); + long sleepMs = delay + jitter; + log.warn("Attempt {} failed copying to container {}. Retrying in {} ms...", attempt, containerId, sleepMs, e); + try { + Thread.sleep(sleepMs); + } + catch (InterruptedException ie) { Thread.currentThread().interrupt(); throw new LocalCIException("Interrupted while retrying copy to container " + containerId, ie); } } } }Add configurable properties:
@@ import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; +import java.util.concurrent.ThreadLocalRandom; @@ @Value("${artemis.continuous-integration.container-flags-limit.max-memory-swap:0}") private int maxMemorySwap; + + @Value("${artemis.continuous-integration.copy-to-container.max-retries:3}") + private int copyToContainerMaxRetries; + + @Value("${artemis.continuous-integration.copy-to-container.base-delay-ms:1000}") + private long copyToContainerBaseDelayMs;Bounded retry is good and aligns with our local-CI retry learnings. Based on learnings.
487-503
: Close/finish TarArchiveOutputStream to produce well‑formed TARUse try‑with‑resources and call finish(); this avoids relying on implicit close behavior and ensures end‑of‑archive markers are written.
private ByteArrayOutputStream createTarArchive(Path sourcePath) { - - ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); - - TarArchiveOutputStream tarArchiveOutputStream = new TarArchiveOutputStream(byteArrayOutputStream); - - // This needs to be done in case the files have a long name - tarArchiveOutputStream.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX); - - try { - addFileToTar(tarArchiveOutputStream, sourcePath, ""); - } - catch (IOException e) { - throw new LocalCIException("Could not create tar archive", e); - } - return byteArrayOutputStream; + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + try (TarArchiveOutputStream tarOut = new TarArchiveOutputStream(byteArrayOutputStream)) { + tarOut.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX); // long names + addFileToTar(tarOut, sourcePath, ""); + tarOut.finish(); // write EOF blocks + return byteArrayOutputStream; + } + catch (IOException e) { + throw new LocalCIException("Could not create tar archive", e); + } }src/main/resources/prompts/hyperion/test/4_logic.st (1)
16-22
: Tighten output contract and test hygiene to avoid flaky runs.Recommend explicitly banning external I/O/dependencies and requiring strict-JSON output only. This reduces prompt leakage and CI flakes.
-**Important:** Only modify files in the `/test/` directory. Do not change source code, build files, or configuration files. +**Important:** Only modify files in the `/test/` directory. Do not change source code, build files, or configuration files. Do not add new dependencies or modify build tooling. Tests must not perform network calls, file I/O outside `/test/`, or rely on system time/timezone. @@ -3. **Ensure completeness:** Make sure all test cases from the plan are fully implemented. +3. **Ensure completeness:** Make sure all test cases from the plan are fully implemented. +4. **Determinism:** Avoid randomness; if unavoidable, seed RNGs. Do not use sleeps. Keep tests idempotent and fast (<2s each). @@ -After your analysis, provide the final code as a JSON object. The JSON object must contain a single key "files" which is an array of objects, where each object has a "path" and "content" key with the complete test code. +After your analysis, provide the final code as a JSON object. Return ONLY valid JSON (no prose/Markdown). The JSON must contain a single key "files", an array of objects with "path" and "content" containing the complete test code.src/main/resources/prompts/hyperion/template/2_file_structure.st (1)
7-27
: Include optional package name and sharpen structure rules.Adding packageName improves path correctness (e.g., Java/Kotlin), and clarifying uniqueness and JSON-only output reduces downstream errors.
**Programming Language:** {{programmingLanguage}} +**Package Name (optional):** +{{packageName}} @@ -2. **Apply conventions:** Consider the standard project layout and file naming conventions for the given programming language. +2. **Apply conventions:** Consider the standard project layout and file naming conventions for the given programming language. For languages with package folders (e.g., Java/Kotlin), derive directories from `packageName` if provided. -3. **List the files:** Create a list of all the template files that need to be created, including their full paths from the repository root. +3. **List the files:** Create a de‑duplicated list of all template files to create under `/src/` only, including full paths from the repository root. @@ -After your analysis, provide the file structure as a JSON object. The JSON object must contain a single key "files" which is an array of objects, where each object has a "path" key. +After your analysis, provide the file structure as a JSON object. Return ONLY valid JSON (no prose/Markdown). The JSON must contain a single key "files" which is an array of objects, each with a "path" key.src/main/resources/prompts/hyperion/solution/1_plan.st (1)
13-24
: Make output contract explicit and forbid emitting code at this stage.Prevent the model from returning code or extra text and require strict JSON only.
-**Important:** Only modify files in the `/src/` directory. Do not change build files, write tests, configuration files, or any files outside the source directory. +**Important:** Only modify files in the `/src/` directory. Do not change build files, write tests, configuration files, or any files outside the source directory. Do NOT output any code in this step. @@ -After your analysis, provide your final implementation plan as a JSON object. The JSON object must contain a single key "solutionPlan" with the detailed plan as a string. +After your analysis, provide your final implementation plan as a JSON object. Return ONLY valid JSON (no prose/Markdown). The JSON must contain a single key "solutionPlan" with the detailed plan as a string.src/main/resources/prompts/hyperion/template/1_plan.st (4)
7-9
: Prevent solution leakage explicitlyAdd a hard constraint that the solution code must never be copied verbatim into the template; use it only to infer public APIs, file names, and structure. Include a strict “no code reuse from solution” clause.
Apply:**Solution Code (for reference):** {{solutionCode}} +Do NOT copy or adapt any implementation from the solution. The solution is reference-only to infer structure, signatures, and file naming. Template output must not include complete algorithms or business logic.
Based on learnings
16-16
: Clarify src-only writes and forbid deletionsMake explicit that existing files are preserved and no deletions occur (matches current system behavior).
Apply:-**Important:** Only modify files in the `/src/` directory. Do not change build files, write tests, configuration files, or any files outside the source directory. +**Important:** Only create or update files in `/src/`. Do not delete or move existing files. Do not change build files, tests, configuration, or anything outside `/src/`.
22-27
: Add compile-safety scaffolding guidanceRequire TODOs and stubs that compile across languages without leaking logic (e.g., throw UnsupportedOperationException in Java).
Apply:3. **Plan template content:** Decide what to include (class shells, method signatures, basic structure) and what to remove (implementation logic, specific algorithms). -4. **Ensure compilability:** The template must compile and provide clear TODO markers where students should add implementation. +4. **Ensure compilability:** The template must compile and provide clear TODO markers. Use language-appropriate no-logic stubs: + - Java: throw new UnsupportedOperationException("TODO") or return minimal defaults. + - TS/JS: function bodies with TODO and placeholder returns. + - Python: raise NotImplementedError().Based on learnings
28-28
: Tighten JSON output contract (no Markdown, no trailing newline)Reduce parsing failures by banning code fences and enforcing a single-key JSON with escaped newlines.
Apply:-After your analysis, provide your final template plan as a JSON object. The JSON object must contain a single key "solutionPlan" with the detailed template plan as a string. +After your analysis, output a single JSON object with exactly one key "solutionPlan", whose value is a plain string (no Markdown/code fences). Escape quotes/newlines within the string. Do not append a trailing newline.src/main/webapp/app/programming/manage/code-editor/instructor-and-editor-container/code-editor-instructor-and-editor-container.component.ts (3)
28-45
: Remove duplicate imports in the component’s imports arrayNgbTooltip and ArtemisTranslatePipe are listed twice. Keep a single entry each.
Apply:TranslateDirective, - ArtemisTranslatePipe, + ArtemisTranslatePipe, CodeEditorContainerComponent, IncludedInScoreBadgeComponent, ProgrammingExerciseInstructorExerciseStatusComponent, NgbDropdown, NgbDropdownToggle, NgbDropdownMenu, NgbDropdownButtonItem, NgbDropdownItem, - NgbTooltip, + NgbTooltip, UpdatingResultComponent, ProgrammingExerciseStudentTriggerBuildButtonComponent, ProgrammingExerciseEditableInstructionComponent, - NgbTooltip, - ArtemisTranslatePipe, + // (duplicates removed)
90-116
: Use finalize() to centralize state resetAvoid duplicating isGeneratingCode.set(false) in both next and error.
Apply:+import { finalize } from 'rxjs/operators'; @@ - this.codeGenerationService.generateCode(this.exercise.id, request).subscribe({ + this.codeGenerationService + .generateCode(this.exercise.id, request) + .pipe(finalize(() => this.isGeneratingCode.set(false))) + .subscribe({ next: (response) => { - this.isGeneratingCode.set(false); if (response.success) { @@ }, error: () => { - this.isGeneratingCode.set(false); this.codeGenAlertService.addAlert({
71-83
: Optional: distinguish “no selection” from “unsupported repository”If selectedRepository is undefined, show a specific message instead of “unsupported”.
Apply:- if (this.selectedRepository !== RepositoryType.TEMPLATE && this.selectedRepository !== RepositoryType.SOLUTION && this.selectedRepository !== RepositoryType.TESTS) { + if (!this.selectedRepository) { + this.codeGenAlertService.addAlert({ type: AlertType.WARNING, translationKey: 'artemisApp.programmingExercise.codeGeneration.noRepositorySelected' }); + return; + } + if (this.selectedRepository !== RepositoryType.TEMPLATE && this.selectedRepository !== RepositoryType.SOLUTION && this.selectedRepository !== RepositoryType.TESTS) {src/main/java/de/tum/cit/aet/artemis/hyperion/dto/CodeGenerationRequestDTO.java (1)
13-15
: Mark repositoryType as required in OpenAPITS client currently declares repositoryType optional; add @Schema(required = true) so generated clients match server validation.
Apply:+import io.swagger.v3.oas.annotations.media.Schema; @@ -@JsonInclude(JsonInclude.Include.NON_EMPTY) -public record CodeGenerationRequestDTO(@NotNull RepositoryType repositoryType) { +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public record CodeGenerationRequestDTO(@Schema(required = true) @NotNull RepositoryType repositoryType) { }src/main/java/de/tum/cit/aet/artemis/hyperion/dto/CodeGenerationResultDTO.java (1)
9-24
: Avoid omitting required fields at the API boundary@JsonInclude.NON_EMPTY may drop
message
when empty, while the TS model marks it as required. Prefer NON_NULL so empty strings still serialize.-@JsonInclude(JsonInclude.Include.NON_EMPTY) +@JsonInclude(JsonInclude.Include.NON_NULL)src/main/java/de/tum/cit/aet/artemis/hyperion/service/HyperionProgrammingExerciseContextRendererService.java (3)
226-241
: Minor: typo in local variableRename
horizonalLine
→horizontalLine
for clarity.- String horizonalLine = "-".repeat(HR_WIDTH); - String header = (root != null && !root.isBlank() ? horizonalLine + "\n" + root + "/" + path + ":\n" + horizonalLine : horizonalLine + "\n" + path + ":\n" + horizonalLine); + String horizontalLine = "-".repeat(HR_WIDTH); + String header = (root != null && !root.isBlank() ? horizontalLine + "\n" + root + "/" + path + ":\n" + horizontalLine : horizontalLine + "\n" + path + ":\n" + horizontalLine);
279-316
: Remove unused parameter and guard against symlink loops
isLast
parameter is unused — simplify the signature and calls.- Consider using
Files.walkFileTree(..., FileVisitOption.FOLLOW_LINKS)
omitted (i.e., do not follow links) to avoid cycles in unusual repos.- private void generateTreeStructure(File directory, StringBuilder structure, String prefix, boolean isLast) { + private void generateTreeStructure(File directory, StringBuilder structure, String prefix) { ... - generateTreeStructure(repositoryRoot, structure, "", true); + generateTreeStructure(repositoryRoot, structure, ""); ... - generateTreeStructure(file, structure, newPrefix, false); + generateTreeStructure(file, structure, newPrefix);
327-370
: Consistent error signaling and DI style
- Method both returns fallback strings and throws
NetworkingException
; choose one strategy for callers (prefer returning a result wrapper or always throwing on failure).- Align with DI guideline (constructor injection): inject
GitService
into the service rather than passing it as a parameter.I can provide a small refactor sketch if you want.
src/main/java/de/tum/cit/aet/artemis/hyperion/dto/CodeGenerationResponseDTO.java (1)
23-29
: Redundant getters on a recordRecords already expose accessors (
solutionPlan()
,files()
). The extragetX()
methods add noise without clear benefit. Remove unless a framework strictly needs bean-style getters.- public String getSolutionPlan() { - return solutionPlan; - } - - public List<GeneratedFile> getFiles() { - return files; - }src/main/resources/prompts/hyperion/template/3_headers.st (1)
16-24
: Tighten output contract to avoid parsing failuresLLMs often prepend prose or fences. Instruct to return JSON only (no Markdown, no commentary) to keep the parser robust.
After your analysis, provide the result as a JSON object. The JSON object must contain a single key "files" which is an array of objects, where each object has a "path" and "content" key. +Return ONLY the JSON object described above: +- No Markdown fences. +- No additional text before or after the JSON. +- Do not include your reasoning in the output.src/test/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionCodeGenerationExecutionServiceTest.java (2)
7-13
: Prefer MockitoExtension over manual openMocks for JUnit 5Use the JUnit 5 Mockito extension to avoid manual lifecycle code and potential leaks. Also remove the explicit openMocks() call.
@@ -import org.mockito.MockitoAnnotations; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; @@ -class HyperionCodeGenerationExecutionServiceTest { +@ExtendWith(MockitoExtension.class) +class HyperionCodeGenerationExecutionServiceTest { @@ @BeforeEach void setup() { - MockitoAnnotations.openMocks(this); this.service = new HyperionCodeGenerationExecutionService(gitService, applicationContext, repositoryService, solutionProgrammingExerciseParticipationRepository, programmingSubmissionRepository, resultRepository, continuousIntegrationTriggerService, programmingExerciseParticipationService, repositoryStructureService);Also applies to: 66-71, 28-28
108-112
: Brittle assertion on exception message casingThe expectation hard-codes “auxiliary” in lower case. If the service uses the enum name (
AUXILIARY
) or changes formatting, this test will fail for non-functional reasons. Suggest asserting on a stable prefix and the enum value instead of case-sensitive full message.Example:
assertThatThrownBy(() -> ReflectionTestUtils.invokeMethod(service, "resolveStrategy", RepositoryType.AUXILIARY)) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining("Unsupported repository type for code generation:") .hasMessageContaining(RepositoryType.AUXILIARY.name().toLowerCase(Locale.ROOT));Please adjust to your actual message format.
src/test/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionTestRepositoryTest.java (3)
13-20
: Adopt MockitoExtension and drop manual openMocks()Modernize Mockito setup and simplify test lifecycle.
@@ -import org.mockito.MockitoAnnotations; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; @@ -class HyperionTestRepositoryServiceTest { +@ExtendWith(MockitoExtension.class) +class HyperionTestRepositoryServiceTest { @@ @BeforeEach void setup() { - MockitoAnnotations.openMocks(this); this.chatClient = ChatClient.create(chatModel); this.testRepository = new HyperionTestRepositoryService(programmingExerciseRepository, chatClient, templates, gitService);Also applies to: 58-63
141-148
: Exception mapping inconsistencyElsewhere you map AI exceptions to NetworkingException. Here you expect a plain RuntimeException for “Repository access failed”. For consistency and clearer error semantics, consider mapping repository access issues to NetworkingException as well and assert accordingly.
-assertThatThrownBy(() -> testRepository.generateSolutionPlan(user, exercise, "logs", "structure")).isInstanceOf(RuntimeException.class) - .hasMessageContaining("Repository access failed"); +assertThatThrownBy(() -> testRepository.generateSolutionPlan(user, exercise, "logs", "structure")) + .isInstanceOf(NetworkingException.class) + .hasMessageContaining("Repository access failed");
195-211
: Verify prompt step ordering with InOrder to catch regressionsYou verify count but not order. Use InOrder to assert plan → structure → headers → logic sequence.
var inOrder = org.mockito.Mockito.inOrder(templates, chatModel); inOrder.verify(templates).render(eq("/prompts/hyperion/test/2_file_structure.st"), any(Map.class)); inOrder.verify(chatModel).call(any(Prompt.class)); inOrder.verify(templates).render(eq("/prompts/hyperion/test/3_headers.st"), any(Map.class)); inOrder.verify(chatModel).call(any(Prompt.class)); inOrder.verify(templates).render(eq("/prompts/hyperion/test/4_logic.st"), any(Map.class)); inOrder.verify(chatModel).call(any(Prompt.class));src/test/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionCodeGenerationStrategyTest.java (4)
15-26
: Use MockitoExtension instead of manual openMocks()Streamline Mockito lifecycle with JUnit 5’s extension.
@@ -import org.mockito.MockitoAnnotations; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; @@ -class HyperionCodeGenerationServiceTest { +@ExtendWith(MockitoExtension.class) +class HyperionCodeGenerationServiceTest { @@ @BeforeEach void setup() { - MockitoAnnotations.openMocks(this); this.chatClient = ChatClient.create(chatModel); this.strategy = new TestCodeGenerationStrategy(programmingExerciseRepository, chatClient, templates);Also applies to: 57-63
86-92
: Assert prompt invocation order with InOrderYou check counts; asserting order for plan → structure → headers → logic will make the test more robust against accidental step reordering.
var inOrder = org.mockito.Mockito.inOrder(templates, chatModel); inOrder.verify(templates).render(eq("test-plan-template"), any(Map.class)); inOrder.verify(chatModel).call(any(Prompt.class)); inOrder.verify(templates).render(eq("test-structure-template"), any(Map.class)); inOrder.verify(chatModel).call(any(Prompt.class)); inOrder.verify(templates).render(eq("test-headers-template"), any(Map.class)); inOrder.verify(chatModel).call(any(Prompt.class)); inOrder.verify(templates).render(eq("test-logic-template"), any(Map.class)); inOrder.verify(chatModel).call(any(Prompt.class));
2-3
: File/class name mismatch may confuse readersThe file is named HyperionCodeGenerationStrategyTest.java but the class is HyperionCodeGenerationServiceTest. Consider aligning names for discoverability.
-class HyperionCodeGenerationServiceTest { +class HyperionCodeGenerationStrategyTest {Alternatively rename the file to match the class.
Also applies to: 38-38
206-215
: Add a quick assertion for getRepositoryType()Since the strategy overrides getRepositoryType() to SOLUTION, a tiny test helps catch accidental changes.
Example:
@Test void getRepositoryType_returnsSolution() { assertThat(strategy.getRepositoryType()).isEqualTo(RepositoryType.SOLUTION); }src/main/java/de/tum/cit/aet/artemis/hyperion/web/HyperionCodeGenerationResource.java (4)
95-101
: MakerepositoryType
required in the OpenAPI spec (client currently allows undefined).Server throws on
null
, but the generated TS model marks it optional. Mark it required in the OpenAPI definition to prevent invalid client calls and regenerate the client.
143-151
: Don’t swallow exceptions; return proper ProblemDetails or mapped error.Returning
null
and then fabricating a generic failure response hides useful diagnostics and breaks HTTP semantics. Prefer mapping toBadRequestAlertException
/ProblemDetail
(4xx/5xx) or include a specific errorKey in the DTO.
160-171
: Avoid hard‑coded attempts; derive from execution service.
attempts
is set to1
or3
regardless of what actually happened. Expose/return the real attempt count fromHyperionCodeGenerationExecutionService
and propagate it here.
34-37
: @lazy on a controller is unusual.Controllers are lightweight singletons; lazy initialization can delay endpoint availability. Consider removing
@Lazy
unless there’s a concrete startup-cost rationale.src/test/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionSolutionRepsitoryTest.java (2)
1-1
: Rename file to match class and fix typo.Consider
HyperionSolutionRepositoryServiceTest.java
to avoid “Repsitory” typo and align with conventions.
54-59
: Prefer JUnit 5 Mockito extension over manualopenMocks
.Use
@ExtendWith(MockitoExtension.class)
and field injection; it simplifies setup and lifecycle.-@BeforeEach -void setup() { - MockitoAnnotations.openMocks(this); - this.chatClient = ChatClient.create(chatModel); - this.solutionRepository = new HyperionSolutionRepositoryService(programmingExerciseRepository, chatClient, templates); -} +@ExtendWith(org.mockito.junit.jupiter.MockitoExtension.class) +class HyperionSolutionRepositoryServiceTest { + @BeforeEach + void setup() { + this.chatClient = ChatClient.create(chatModel); + this.solutionRepository = new HyperionSolutionRepositoryService(programmingExerciseRepository, chatClient, templates); + } +}src/test/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionTemplateRepositoryTest.java (3)
116-126
: Strengthen assertions for “warning/no-files/io-failure” scenarios.These tests only verify interactions. Add assertions against returned
CodeGenerationResponseDTO
(e.g., message content or emptyfiles
) to actually validate behavior.Also applies to: 141-152, 163-174
52-59
: Remove unused mocks and@TempDir
if not needed.
Repository
,VcsRepositoryUri
, and@TempDir
aren’t used; drop them to keep tests lean.Also applies to: 68-70
71-76
: Adopt Mockito JUnit 5 extension.Use
@ExtendWith(MockitoExtension.class)
instead of manualopenMocks
.src/main/webapp/app/openapi/api/hyperionCodeGenerationApi.service.ts (1)
67-69
: Disable TransferCache by default for POST.
transferCache
is generally useful for idempotent GETs. Defaulting it totrue
on POSTs is unnecessary and could be misleading.-const localVarTransferCache: boolean = options?.transferCache ?? true; +const localVarTransferCache: boolean = options?.transferCache ?? false;src/main/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionTemplateRepositoryService.java (1)
55-63
: Plan variables: good coverage (problem, solution, language, logs, structure).If
solutionCode
is a fallback message (no repo), consider including a boolean flag likehasSolutionCode
to let prompts branch accordingly.src/main/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionTestRepositoryService.java (1)
55-64
: Large context risk: cap/curate solutionCode before passing to prompts.Reading the full solution repository and injecting it verbatim can exceed provider token limits and slow requests. Consider truncating or filtering (e.g., only src/main/java, omit binaries, cap total chars) before adding to templateVariables, or perform filtering inside the renderer service.
Minimal in-method mitigation:
- String solutionCode = contextRenderer.getExistingSolutionCode(exercise, gitService); + String solutionCode = contextRenderer.getExistingSolutionCode(exercise, gitService); + final int MAX_CHARS = 120_000; + if (solutionCode.length() > MAX_CHARS) { + solutionCode = solutionCode.substring(0, MAX_CHARS) + "\n// [truncated]"; + }Also, ensure HyperionProgrammingExerciseContextRendererService reads the default branch via GitService (not hard-coded "main") and uses OS‑independent path checks when filtering to "src/". I can propose a small patch there if you want.
src/main/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionCodeGenerationExecutionService.java (5)
118-121
: Remove unused parameter in getDefaultBranch (or make it repo-specific).The method takes LocalVCRepositoryUri but ignores it; either drop the parameter or use it for per-repo defaults. Minimal clean-up:
- private String getDefaultBranch(LocalVCRepositoryUri repositoryUri) { - return gitService.getDefaultBranch(); - } + private String getDefaultBranch() { + return gitService.getDefaultBranch(); + }And call site:
- String defaultBranch = getDefaultBranch(repositoryUri); + String defaultBranch = getDefaultBranch();Also applies to: 153-156
332-336
: Reduce log level and remove padding newlines.Startup message at WARN with extra newlines is noisy. Use INFO or DEBUG and drop the blanks.
- log.warn("Starting code generation and compilation for exercise {} with repository type {}\n\n\n", exercise.getId(), repositoryType); + log.info("Starting code generation and compilation for exercise {} with repository type {}", exercise.getId(), repositoryType);
53-58
: Externalize TIMEOUT/POLL_INTERVAL/MAX_ITERATIONS to config.Make them @Value-injected (with sane defaults) to tune per environment without rebuilds.
- private static final int MAX_ITERATIONS = 3; - private static final long TIMEOUT = 180_000; // 3 minutes - private static final long POLL_INTERVAL = 3_000; // 3 seconds + private final int MAX_ITERATIONS = Integer.getInteger("hyperion.codegen.maxIterations", 3); + private final long TIMEOUT = Long.getLong("hyperion.codegen.timeoutMs", 180_000L); // 3 minutes + private final long POLL_INTERVAL = Long.getLong("hyperion.codegen.pollIntervalMs", 3_000L); // 3 secondsOr prefer Spring @value / @ConfigurationProperties.
316-320
: Use or remove theoriginalCommitHash
parameter incleanupRepository
The method signature implies resetting to the specified commit, but it unconditionally callsgitService.resetToOriginHead(repository)
and never usesoriginalCommitHash
. Either implement a method to reset to that hash (e.g.gitService.resetToCommit(repository, originalCommitHash)
) or remove/rename the parameter to avoid confusion.
179-203
: Remove redundant path validation and rely on repositoryService guards
deleteFile
andcreateFile
already validate and sanitizefilePath
viacheckIfPathIsValidAndExistanceAndReturnSafePath
andcheckIfPathAndFileAreValidAndReturnSafeFile
, so there’s no need for a customrepository.isValidFile
check.- (Optional) Wrap the
ByteArrayInputStream
in a try-with-resources for consistency, though closing aByteArrayInputStream
is effectively a no-op.
private String commitAndGetHash(Repository repository, User user, LocalVCRepositoryUri repositoryUri, ProgrammingExercise exercise) throws GitAPIException { | ||
repositoryService.commitChanges(repository, user); | ||
String newCommitHash = gitService.getLastCommitHash(repositoryUri).getName(); | ||
|
||
try { | ||
ProgrammingExerciseParticipation solutionParticipation = programmingExerciseParticipationService.retrieveSolutionParticipation(exercise); | ||
continuousIntegrationTriggerService.triggerBuild(solutionParticipation, newCommitHash, null); | ||
log.debug("Successfully triggered CI build for commit {} in exercise {}", newCommitHash, exercise.getId()); | ||
} | ||
catch (ContinuousIntegrationException e) { | ||
log.warn("Failed to trigger CI build for commit {} in exercise {}: {}", newCommitHash, exercise.getId(), e.getMessage()); | ||
} | ||
|
||
return newCommitHash; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Triggering CI always on solution participation breaks TEMPLATE/TESTS flows.
You commit to any repo, but CI is triggered on the solution participation only. Combined with result polling (also solution-only), TEMPLATE/TESTS runs can time out. Pass repositoryType and select the correct participation.
- private String commitAndGetHash(Repository repository, User user, LocalVCRepositoryUri repositoryUri, ProgrammingExercise exercise) throws GitAPIException {
+ private String commitAndGetHash(Repository repository, User user, LocalVCRepositoryUri repositoryUri,
+ ProgrammingExercise exercise, RepositoryType repositoryType) throws GitAPIException {
repositoryService.commitChanges(repository, user);
String newCommitHash = gitService.getLastCommitHash(repositoryUri).getName();
try {
- ProgrammingExerciseParticipation solutionParticipation = programmingExerciseParticipationService.retrieveSolutionParticipation(exercise);
- continuousIntegrationTriggerService.triggerBuild(solutionParticipation, newCommitHash, null);
+ ProgrammingExerciseParticipation participation = switch (repositoryType) {
+ case SOLUTION -> programmingExerciseParticipationService.retrieveSolutionParticipation(exercise);
+ case TEMPLATE -> programmingExerciseParticipationService.retrieveTemplateParticipation(exercise); // or equivalent
+ case TESTS -> programmingExerciseParticipationService.retrieveTestParticipation(exercise); // or equivalent
+ };
+ continuousIntegrationTriggerService.triggerBuild(participation, newCommitHash, null);
log.debug("Successfully triggered CI build for commit {} in exercise {}", newCommitHash, exercise.getId());
}
catch (ContinuousIntegrationException e) {
log.warn("Failed to trigger CI build for commit {} in exercise {}: {}", newCommitHash, exercise.getId(), e.getMessage());
}
return newCommitHash;
}
🏁 Script executed:
#!/bin/bash
rg -nP 'retrieve(Solution|Template|Test)Participation\s*\(' --type=java -C2
Length of output: 3302
Trigger CI build for correct participation type
commitAndGetHash
in HyperionCodeGenerationExecutionService currently always uses solution participation, which breaks TEMPLATE/TESTS flows (timeouts). Extend its signature to accept a RepositoryType
and switch on it to retrieve the appropriate participation. You’ll also need to add retrieveTemplateParticipation(...)
and retrieveTestParticipation(...)
methods in ProgrammingExerciseParticipationService to support those cases.
processGeneratedFiles(repository, generatedFiles, exercise); | ||
String newCommitHash = commitAndGetHash(repository, user, repositoryUri, exercise); | ||
Result result = waitForBuildResult(exercise, newCommitHash); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Propagate repositoryType to commit and result wait.
Wire repositoryType through to ensure CI trigger and result polling align with the repo you changed.
- processGeneratedFiles(repository, generatedFiles, exercise);
- String newCommitHash = commitAndGetHash(repository, user, repositoryUri, exercise);
- Result result = waitForBuildResult(exercise, newCommitHash);
+ processGeneratedFiles(repository, generatedFiles, exercise);
+ String newCommitHash = commitAndGetHash(repository, user, repositoryUri, exercise, strategy.getRepositoryType());
+ Result result = waitForBuildResult(exercise, strategy.getRepositoryType(), newCommitHash);
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In
src/main/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionCodeGenerationExecutionService.java
around lines 290 to 293, the repositoryType is not forwarded when committing and
when waiting for CI results; update the call sites to pass repositoryType into
commitAndGetHash(...) and waitForBuildResult(...), and then update those
methods' signatures (and all callers) to accept the repositoryType parameter so
the CI trigger and polling use the correct repository type. Ensure any
downstream uses (e.g., building repository URIs or CI client selection) consume
the new parameter and adjust unit tests/overloads accordingly.
private Result waitForBuildResult(ProgrammingExercise exercise, String commitHash) throws InterruptedException { | ||
long startTime = System.currentTimeMillis(); | ||
|
||
var solutionParticipation = solutionProgrammingExerciseParticipationRepository.findByProgrammingExerciseId(exercise.getId()).orElse(null); | ||
if (solutionParticipation == null) { | ||
log.warn("Could not find solution participation for exercise {}", exercise.getId()); | ||
return null; | ||
} | ||
|
||
int pollCount = 0; | ||
while (System.currentTimeMillis() - startTime < TIMEOUT) { | ||
try { | ||
ProgrammingSubmission submission = programmingSubmissionRepository | ||
.findFirstByParticipationIdAndCommitHashOrderByIdDescWithFeedbacksAndTeamStudents(solutionParticipation.getId(), commitHash); | ||
|
||
if (submission != null) { | ||
Optional<Result> result = resultRepository.findLatestResultWithFeedbacksAndTestcasesForSubmission(submission.getId()); | ||
if (result.isPresent()) { | ||
log.debug("Found build result for commit {} after {} polls ({}ms)", commitHash, pollCount, System.currentTimeMillis() - startTime); | ||
return result.get(); | ||
} | ||
} | ||
|
||
pollCount++; | ||
|
||
Thread.sleep(POLL_INTERVAL); | ||
} | ||
catch (Exception e) { | ||
log.warn("Exception while polling for build result for commit {}: {}. Continuing...", commitHash, e.getMessage()); | ||
Thread.sleep(POLL_INTERVAL); | ||
} | ||
} | ||
log.warn("Timed out waiting for build result for commit {} in exercise {} after {} polls ({}ms)", commitHash, exercise.getId(), pollCount, TIMEOUT); | ||
return null; // Timeout | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Result polling bound to solution participation and swallows interrupts.
- Always querying solutionParticipation causes TEMPLATE/TESTS runs to time out even if CI was triggered elsewhere.
- InterruptedException is caught by the broad catch and not rethrown, violating interruption semantics.
Refactor to select participation by repositoryType and properly handle interrupts:
- private Result waitForBuildResult(ProgrammingExercise exercise, String commitHash) throws InterruptedException {
+ private Result waitForBuildResult(ProgrammingExercise exercise, RepositoryType repositoryType, String commitHash) throws InterruptedException {
long startTime = System.currentTimeMillis();
- var solutionParticipation = solutionProgrammingExerciseParticipationRepository.findByProgrammingExerciseId(exercise.getId()).orElse(null);
- if (solutionParticipation == null) {
- log.warn("Could not find solution participation for exercise {}", exercise.getId());
- return null;
- }
+ ProgrammingExerciseParticipation participation = switch (repositoryType) {
+ case SOLUTION -> programmingExerciseParticipationService.retrieveSolutionParticipation(exercise);
+ case TEMPLATE -> programmingExerciseParticipationService.retrieveTemplateParticipation(exercise); // or equivalent
+ case TESTS -> programmingExerciseParticipationService.retrieveTestParticipation(exercise); // or equivalent
+ };
int pollCount = 0;
while (System.currentTimeMillis() - startTime < TIMEOUT) {
try {
ProgrammingSubmission submission = programmingSubmissionRepository
- .findFirstByParticipationIdAndCommitHashOrderByIdDescWithFeedbacksAndTeamStudents(solutionParticipation.getId(), commitHash);
+ .findFirstByParticipationIdAndCommitHashOrderByIdDescWithFeedbacksAndTeamStudents(participation.getId(), commitHash);
if (submission != null) {
Optional<Result> result = resultRepository.findLatestResultWithFeedbacksAndTestcasesForSubmission(submission.getId());
if (result.isPresent()) {
log.debug("Found build result for commit {} after {} polls ({}ms)", commitHash, pollCount, System.currentTimeMillis() - startTime);
return result.get();
}
}
pollCount++;
Thread.sleep(POLL_INTERVAL);
}
- catch (Exception e) {
+ catch (InterruptedException ie) {
+ Thread.currentThread().interrupt();
+ throw ie;
+ }
+ catch (Exception e) {
log.warn("Exception while polling for build result for commit {}: {}. Continuing...", commitHash, e.getMessage());
Thread.sleep(POLL_INTERVAL);
}
}
log.warn("Timed out waiting for build result for commit {} in exercise {} after {} polls ({}ms)", commitHash, exercise.getId(), pollCount, TIMEOUT);
return null; // Timeout
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
private Result waitForBuildResult(ProgrammingExercise exercise, String commitHash) throws InterruptedException { | |
long startTime = System.currentTimeMillis(); | |
var solutionParticipation = solutionProgrammingExerciseParticipationRepository.findByProgrammingExerciseId(exercise.getId()).orElse(null); | |
if (solutionParticipation == null) { | |
log.warn("Could not find solution participation for exercise {}", exercise.getId()); | |
return null; | |
} | |
int pollCount = 0; | |
while (System.currentTimeMillis() - startTime < TIMEOUT) { | |
try { | |
ProgrammingSubmission submission = programmingSubmissionRepository | |
.findFirstByParticipationIdAndCommitHashOrderByIdDescWithFeedbacksAndTeamStudents(solutionParticipation.getId(), commitHash); | |
if (submission != null) { | |
Optional<Result> result = resultRepository.findLatestResultWithFeedbacksAndTestcasesForSubmission(submission.getId()); | |
if (result.isPresent()) { | |
log.debug("Found build result for commit {} after {} polls ({}ms)", commitHash, pollCount, System.currentTimeMillis() - startTime); | |
return result.get(); | |
} | |
} | |
pollCount++; | |
Thread.sleep(POLL_INTERVAL); | |
} | |
catch (Exception e) { | |
log.warn("Exception while polling for build result for commit {}: {}. Continuing...", commitHash, e.getMessage()); | |
Thread.sleep(POLL_INTERVAL); | |
} | |
} | |
log.warn("Timed out waiting for build result for commit {} in exercise {} after {} polls ({}ms)", commitHash, exercise.getId(), pollCount, TIMEOUT); | |
return null; // Timeout | |
} | |
private Result waitForBuildResult(ProgrammingExercise exercise, RepositoryType repositoryType, String commitHash) throws InterruptedException { | |
long startTime = System.currentTimeMillis(); | |
ProgrammingExerciseParticipation participation = switch (repositoryType) { | |
case SOLUTION -> programmingExerciseParticipationService.retrieveSolutionParticipation(exercise); | |
case TEMPLATE -> programmingExerciseParticipationService.retrieveTemplateParticipation(exercise); | |
case TESTS -> programmingExerciseParticipationService.retrieveTestParticipation(exercise); | |
}; | |
int pollCount = 0; | |
while (System.currentTimeMillis() - startTime < TIMEOUT) { | |
try { | |
ProgrammingSubmission submission = programmingSubmissionRepository | |
.findFirstByParticipationIdAndCommitHashOrderByIdDescWithFeedbacksAndTeamStudents(participation.getId(), commitHash); | |
if (submission != null) { | |
Optional<Result> result = resultRepository.findLatestResultWithFeedbacksAndTestcasesForSubmission(submission.getId()); | |
if (result.isPresent()) { | |
log.debug("Found build result for commit {} after {} polls ({}ms)", commitHash, pollCount, System.currentTimeMillis() - startTime); | |
return result.get(); | |
} | |
} | |
pollCount++; | |
Thread.sleep(POLL_INTERVAL); | |
} | |
catch (InterruptedException ie) { | |
Thread.currentThread().interrupt(); | |
throw ie; | |
} | |
catch (Exception e) { | |
log.warn("Exception while polling for build result for commit {}: {}. Continuing...", commitHash, e.getMessage()); | |
Thread.sleep(POLL_INTERVAL); | |
} | |
} | |
log.warn("Timed out waiting for build result for commit {} in exercise {} after {} polls ({}ms)", commitHash, exercise.getId(), pollCount, TIMEOUT); | |
return null; // Timeout | |
} |
public HyperionCodeGenerationService(ProgrammingExerciseRepository programmingExerciseRepository, @Autowired(required = false) ChatClient chatClient, | ||
HyperionPromptTemplateService templates) { | ||
this.programmingExerciseRepository = programmingExerciseRepository; | ||
this.chatClient = chatClient; | ||
this.templates = templates; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical: ChatClient is optional in DI but used unconditionally → NPE risk.
Constructor marks ChatClient as optional (@Autowired(required = false)), yet callChatClient() dereferences it without guarding. If Hyperion is enabled but no ChatClient bean is present/misconfigured, this will crash at runtime.
Harden DI and fail fast:
@@
-import java.util.List;
-import java.util.Map;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
@@
-import org.springframework.context.annotation.Conditional;
+import org.springframework.context.annotation.Conditional;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
@@
-@Service
+@Service
@Lazy
@Conditional(HyperionEnabled.class)
+@ConditionalOnBean(ChatClient.class)
public abstract class HyperionCodeGenerationService {
@@
- public HyperionCodeGenerationService(ProgrammingExerciseRepository programmingExerciseRepository, @Autowired(required = false) ChatClient chatClient,
+ public HyperionCodeGenerationService(ProgrammingExerciseRepository programmingExerciseRepository, ChatClient chatClient,
HyperionPromptTemplateService templates) {
this.programmingExerciseRepository = programmingExerciseRepository;
- this.chatClient = chatClient;
+ this.chatClient = Objects.requireNonNull(chatClient, "ChatClient must be configured when Hyperion is enabled");
this.templates = templates;
}
Optionally, also guard empty responses:
@@
- var response = chatClient.prompt().user(rendered).call().entity(CodeGenerationResponseDTO.class);
- return response;
+ var response = chatClient.prompt().user(rendered).call().entity(CodeGenerationResponseDTO.class);
+ if (response == null) {
+ throw new NetworkingException("AI service returned an empty response");
+ }
+ return response;
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In
src/main/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionCodeGenerationService.java
around lines 45 to 50, the constructor accepts a ChatClient as optional but the
class calls it unconditionally (NPE risk); change the constructor to either
require a non-null ChatClient when Hyperion is enabled (throw an
IllegalStateException in the constructor with a clear message if chatClient ==
null and Hyperion feature flag/config indicates enabled) or keep it optional but
always null-check before use (wrap calls to chatClient in an if (chatClient !=
null) block and handle the absence by failing fast with a clear exception or
returning a safe fallback). Additionally, harden callChatClient (or any method
that dereferences chatClient) to handle null chatClient and empty/invalid
responses by validating the response and throwing a descriptive exception or
returning an Optional/empty result to callers.
protected CodeGenerationResponseDTO generateClassAndMethodHeaders(User user, ProgrammingExercise exercise, String solutionPlan, String repositoryStructure) | ||
throws NetworkingException { | ||
var fileStructure = defineFileStructure(user, exercise, solutionPlan, repositoryStructure); | ||
var templateVariables = Map.<String, Object>of("solutionPlan", solutionPlan, "fileStructure", fileStructure.getFiles(), "programmingLanguage", | ||
exercise.getProgrammingLanguage(), "repositoryStructure", repositoryStructure != null ? repositoryStructure : ""); | ||
return callChatClient("/prompts/hyperion/solution/3_headers.st", templateVariables); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Avoid re-running step 2 inside step 3 (duplicated LLM call and inconsistent artifacts).
generateClassAndMethodHeaders() invokes defineFileStructure(...) again. This repeats work the base pipeline already performed and can yield inconsistent, non-deterministic results while inflating cost/latency.
Apply this refactor together with the base-class signature change (see my comment on HyperionCodeGenerationService):
- protected CodeGenerationResponseDTO generateClassAndMethodHeaders(User user, ProgrammingExercise exercise, String solutionPlan, String repositoryStructure)
- throws NetworkingException {
- var fileStructure = defineFileStructure(user, exercise, solutionPlan, repositoryStructure);
- var templateVariables = Map.<String, Object>of("solutionPlan", solutionPlan, "fileStructure", fileStructure.getFiles(), "programmingLanguage",
- exercise.getProgrammingLanguage(), "repositoryStructure", repositoryStructure != null ? repositoryStructure : "");
- return callChatClient("/prompts/hyperion/solution/3_headers.st", templateVariables);
- }
+ protected CodeGenerationResponseDTO generateClassAndMethodHeaders(User user, ProgrammingExercise exercise, String solutionPlan,
+ CodeGenerationResponseDTO fileStructure, String repositoryStructure) throws NetworkingException {
+ var templateVariables = Map.<String, Object>of(
+ "solutionPlan", solutionPlan,
+ "fileStructure", fileStructure.getFiles(),
+ "programmingLanguage", exercise.getProgrammingLanguage(),
+ "repositoryStructure", repositoryStructure != null ? repositoryStructure : "");
+ return callChatClient("/prompts/hyperion/solution/3_headers.st", templateVariables);
+ }
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
protected CodeGenerationResponseDTO generateClassAndMethodHeaders(User user, ProgrammingExercise exercise, String solutionPlan, String repositoryStructure) | |
throws NetworkingException { | |
var fileStructure = defineFileStructure(user, exercise, solutionPlan, repositoryStructure); | |
var templateVariables = Map.<String, Object>of("solutionPlan", solutionPlan, "fileStructure", fileStructure.getFiles(), "programmingLanguage", | |
exercise.getProgrammingLanguage(), "repositoryStructure", repositoryStructure != null ? repositoryStructure : ""); | |
return callChatClient("/prompts/hyperion/solution/3_headers.st", templateVariables); | |
} | |
protected CodeGenerationResponseDTO generateClassAndMethodHeaders(User user, ProgrammingExercise exercise, String solutionPlan, | |
CodeGenerationResponseDTO fileStructure, String repositoryStructure) throws NetworkingException { | |
var templateVariables = Map.<String, Object>of( | |
"solutionPlan", solutionPlan, | |
"fileStructure", fileStructure.getFiles(), | |
"programmingLanguage", exercise.getProgrammingLanguage(), | |
"repositoryStructure", repositoryStructure != null ? repositoryStructure : ""); | |
return callChatClient("/prompts/hyperion/solution/3_headers.st", templateVariables); | |
} |
void cleanupRepository_withValidInputs_callsGitService() throws Exception { | ||
ReflectionTestUtils.invokeMethod(service, "cleanupRepository", gitService.getOrCheckoutRepository(null, false, "main", false), "commit-hash"); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test name says “callsGitService” but no interaction is verified; also repo is null
gitService.getOrCheckoutRepository(...)
returns null on a plain mock, so you're passing null into cleanupRepository
. No interaction is asserted, so the test doesn't prove its claim.
Options:
- Rename to reflect behavior (“doesNotThrowWithRepositoryAndCommitHash”).
- Or stub a repository and verify expected GitService interactions.
Minimal rename:
-void cleanupRepository_withValidInputs_callsGitService() throws Exception {
+void cleanupRepository_withRepositoryAndCommitHash_doesNotThrow() throws Exception {
ReflectionTestUtils.invokeMethod(service, "cleanupRepository", gitService.getOrCheckoutRepository(null, false, "main", false), "commit-hash");
}
If you want to keep the intent, stub and verify the exact calls made inside cleanupRepository
.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
void cleanupRepository_withValidInputs_callsGitService() throws Exception { | |
ReflectionTestUtils.invokeMethod(service, "cleanupRepository", gitService.getOrCheckoutRepository(null, false, "main", false), "commit-hash"); | |
} | |
void cleanupRepository_withRepositoryAndCommitHash_doesNotThrow() throws Exception { | |
ReflectionTestUtils.invokeMethod(service, "cleanupRepository", gitService.getOrCheckoutRepository(null, false, "main", false), "commit-hash"); | |
} |
🤖 Prompt for AI Agents
In
src/test/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionCodeGenerationExecutionServiceTest.java
around lines 138–140, the test name claims it "callsGitService" but currently
passes a null repository (because gitService.getOrCheckoutRepository on a plain
mock returns null) and asserts no interactions; either rename the test to
reflect it only ensures no exception (e.g.,
doesNotThrowWithRepositoryAndCommitHash) or, to keep the original intent, stub a
mock Repository and expected gitService behavior: mock a Repository instance,
when(gitService.getOrCheckoutRepository(null, false, "main",
false)).thenReturn(mockRepo), call cleanupRepository with that repo and commit
hash, then verify gitService.getOrCheckoutRepository(...) was invoked with the
correct args and verify the specific gitService/repository interactions
performed inside cleanupRepository (inspect the implementation to know which
methods to assert) and remove the misleading test name if you choose the rename
option.
@BeforeEach | ||
void setup() { | ||
MockitoAnnotations.openMocks(this); | ||
this.chatClient = ChatClient.create(chatModel); | ||
this.templateRepository = new HyperionTemplateRepositoryService(programmingExerciseRepository, chatClient, templates, gitService); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Constructor mismatch: test will not compile (missing contextRenderer
).
HyperionTemplateRepositoryService
requires (repo, chatClient, templates, gitService, contextRenderer)
. Inject a mocked HyperionProgrammingExerciseContextRendererService
and pass it in.
Apply this diff:
@@
- @Mock
- private Repository mockRepository;
-
- @Mock
- private VcsRepositoryUri mockRepositoryUri;
+ @Mock
+ private HyperionProgrammingExerciseContextRendererService contextRenderer;
@@
- this.templateRepository = new HyperionTemplateRepositoryService(programmingExerciseRepository, chatClient, templates, gitService);
+ this.templateRepository = new HyperionTemplateRepositoryService(programmingExerciseRepository, chatClient, templates, gitService, contextRenderer);
+ when(contextRenderer.getExistingSolutionCode(any(), any())).thenReturn("// stub");
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
@BeforeEach | |
void setup() { | |
MockitoAnnotations.openMocks(this); | |
this.chatClient = ChatClient.create(chatModel); | |
this.templateRepository = new HyperionTemplateRepositoryService(programmingExerciseRepository, chatClient, templates, gitService); | |
// Remove unused mocks | |
- @Mock | |
- private Repository mockRepository; | |
- | |
- @Mock | |
- private VcsRepositoryUri mockRepositoryUri; | |
// Add the missing contextRenderer mock | |
@Mock | |
private HyperionProgrammingExerciseContextRendererService contextRenderer; | |
@BeforeEach | |
void setup() { | |
MockitoAnnotations.openMocks(this); | |
this.chatClient = ChatClient.create(chatModel); | |
this.templateRepository = new HyperionTemplateRepositoryService( | |
programmingExerciseRepository, | |
chatClient, | |
templates, | |
gitService, | |
contextRenderer | |
); | |
when(contextRenderer.getExistingSolutionCode(any(), any())).thenReturn("// stub"); | |
} |
🤖 Prompt for AI Agents
In
src/test/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionTemplateRepositoryTest.java
around lines 71 to 76, the test setup calls the
HyperionTemplateRepositoryService constructor with four arguments but the class
requires a fifth parameter (contextRenderer). Update the setup to create and
inject a mocked HyperionProgrammingExerciseContextRendererService (e.g.,
annotate or instantiate a Mockito mock) and pass that mock as the fifth argument
when constructing HyperionTemplateRepositoryService so the test compiles.
@Test | ||
void generateSolutionPlan_withRepositoryAccessException_throwsNetworkingException() throws Exception { | ||
when(templates.render(any(String.class), any(Map.class))).thenReturn("rendered"); | ||
when(chatModel.call(any(Prompt.class))).thenThrow(new RuntimeException("Repository access failed")); | ||
|
||
assertThatThrownBy(() -> templateRepository.generateSolutionPlan(user, exercise, "logs", "structure")).isInstanceOf(RuntimeException.class) | ||
.hasMessageContaining("Repository access failed"); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test name vs behavior mismatch and wrong failure source.
generateSolutionPlan_withRepositoryAccessException_throwsNetworkingException
stubs chatModel
to throw RuntimeException
and asserts RuntimeException
. If you want to cover repository access issues, stub contextRenderer.getExistingSolutionCode
to throw NetworkingException
and assert that.
-when(templates.render(any(String.class), any(Map.class))).thenReturn("rendered");
-when(chatModel.call(any(Prompt.class))).thenThrow(new RuntimeException("Repository access failed"));
-
-assertThatThrownBy(() -> templateRepository.generateSolutionPlan(user, exercise, "logs", "structure")).isInstanceOf(RuntimeException.class)
- .hasMessageContaining("Repository access failed");
+when(templates.render(any(String.class), any(Map.class))).thenReturn("rendered");
+when(contextRenderer.getExistingSolutionCode(eq(exercise), eq(gitService)))
+ .thenThrow(new NetworkingException("Repository access failed"));
+
+assertThatThrownBy(() -> templateRepository.generateSolutionPlan(user, exercise, "logs", "structure"))
+ .isInstanceOf(NetworkingException.class)
+ .hasMessageContaining("Repository access failed");
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
@Test | |
void generateSolutionPlan_withRepositoryAccessException_throwsNetworkingException() throws Exception { | |
when(templates.render(any(String.class), any(Map.class))).thenReturn("rendered"); | |
when(chatModel.call(any(Prompt.class))).thenThrow(new RuntimeException("Repository access failed")); | |
assertThatThrownBy(() -> templateRepository.generateSolutionPlan(user, exercise, "logs", "structure")).isInstanceOf(RuntimeException.class) | |
.hasMessageContaining("Repository access failed"); | |
} | |
@Test | |
void generateSolutionPlan_withRepositoryAccessException_throwsNetworkingException() throws Exception { | |
when(templates.render(any(String.class), any(Map.class))).thenReturn("rendered"); | |
when(contextRenderer.getExistingSolutionCode(eq(exercise), eq(gitService))) | |
.thenThrow(new NetworkingException("Repository access failed")); | |
assertThatThrownBy(() -> templateRepository.generateSolutionPlan(user, exercise, "logs", "structure")) | |
.isInstanceOf(NetworkingException.class) | |
.hasMessageContaining("Repository access failed"); | |
} |
🤖 Prompt for AI Agents
In
src/test/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionTemplateRepositoryTest.java
around lines 154-161, the test name indicates it should verify handling of a
repository/networking error but currently stubs chatModel to throw
RuntimeException and asserts RuntimeException; change the test to stub
contextRenderer.getExistingSolutionCode(...) to throw a NetworkingException (or
the specific repository/networking exception type used in production) and assert
that templateRepository.generateSolutionPlan(...) throws that
NetworkingException, and update the test name to reflect the new behavior (e.g.,
generateSolutionPlan_withRepositoryAccessException_throwsNetworkingException).
@Test | ||
void generateSolutionPlan_includesSolutionCodeInTemplateVariables() throws Exception { | ||
String jsonResponse = "{\"solutionPlan\":\"test plan\",\"files\":[]}"; | ||
|
||
when(templates.render(any(String.class), any(Map.class))).thenReturn("rendered"); | ||
when(chatModel.call(any(Prompt.class))).thenReturn(createChatResponse(jsonResponse)); | ||
|
||
testRepository.generateSolutionPlan(user, exercise, "logs", "structure"); | ||
|
||
verify(templates).render(eq("/prompts/hyperion/test/1_plan.st"), any(Map.class)); | ||
verify(chatModel).call(any(Prompt.class)); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Test name implies verifying template variables; assertion currently doesn’t check them
“includesSolutionCodeInTemplateVariables” only verifies that render() was invoked. Capture the variables and assert expected keys/values (e.g., language, repositoryStructure, buildLogs, and any solution code inputs your service provides).
Sketch:
var captor = org.mockito.ArgumentCaptor.forClass(Map.class);
verify(templates).render(eq("/prompts/hyperion/test/1_plan.st"), captor.capture());
Map<String, Object> vars = captor.getValue();
assertThat(vars).containsKeys("language", "repositoryStructure", "buildLogs");
// If applicable in your impl, also assert concatenated solution code presence:
// assertThat((String) vars.get("solutionCode")).contains("class");
Please adapt the key names to your actual template model.
🏁 Script executed:
#!/bin/bash
rg -n 'generateSolutionPlan_includesSolutionCodeInTemplateVariables' -C5 src/test/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionTestRepositoryTest.java
Length of output: 546
Add assertions for template variables in generateSolutionPlan_includesSolutionCodeInTemplateVariables
. Capture the Map passed to templates.render
and assert it contains your expected keys. For example:
var captor = org.mockito.ArgumentCaptor.forClass(Map.class);
verify(templates).render(eq("/prompts/hyperion/test/1_plan.st"), captor.capture());
Map<String, Object> vars = captor.getValue();
assertThat(vars).containsKeys("language", "repositoryStructure", "buildLogs");
// If your implementation adds the solution code:
// assertThat((String) vars.get("solutionCode")).contains("class");
Adjust the key names to match your template model.
🤖 Prompt for AI Agents
In
src/test/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionTestRepositoryTest.java
around lines 90 to 101, the test currently verifies templates.render was called
but does not assert the template variables; update the test to capture the Map
passed to templates.render using an ArgumentCaptor for Map (e.g.,
ArgumentCaptor.forClass(Map.class)), call
verify(templates).render(eq("/prompts/hyperion/test/1_plan.st"),
captor.capture()), retrieve the captured Map and assert it contains the expected
keys such as "language", "repositoryStructure", and "buildLogs" (and if your
implementation supplies solution code, assert the "solutionCode" entry is
present and contains expected snippets like "class"); adjust key names to match
the template model.
@Test | ||
void generateSolutionPlan_withFailedRepositoryCheckout_usesWarningMessage() throws Exception { | ||
String jsonResponse = "{\"solutionPlan\":\"test plan\",\"files\":[]}"; | ||
when(templates.render(any(String.class), any(Map.class))).thenReturn("rendered"); | ||
when(chatModel.call(any(Prompt.class))).thenReturn(createChatResponse(jsonResponse)); | ||
|
||
testRepository.generateSolutionPlan(user, exercise, "logs", "structure"); | ||
|
||
verify(templates).render(eq("/prompts/hyperion/test/1_plan.st"), any(Map.class)); | ||
verify(chatModel).call(any(Prompt.class)); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Several tests are misnamed and do not simulate or assert the claimed behavior
- withFailedRepositoryCheckout_usesWarningMessage: no failure is simulated; no warning is asserted.
- withMultipleJavaFiles_concatenatesContent: no multi-file input is provided; no concatenation asserted.
- withIOExceptionDuringWalk_returnsErrorMessage: no IOException is induced; no error message asserted.
- withNoJavaFiles_returnsNoFilesMessage: no “no files” condition is simulated; no message asserted.
Either:
- Implement the respective conditions (e.g., stub GitService/repo traversal helpers to return multiple files, throw IO, etc.) and assert the specific template variables/messages, or
- Rename tests to reflect current (minimal) behavior.
Also applies to: 115-126, 128-139, 150-161
WalkthroughIntroduces Hyperion AI-assisted code generation: backend DTOs, services, strategies, REST endpoint, prompt templates, and tests; frontend API client, UI integration, and i18n texts. Adds build artifact copy retry, repository close logging, GitService default-branch accessor, and enum JSON serialization. Adds a ts-morph dev dependency. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor Instructor as UI (Instructor)
participant NG as Angular HyperionCodeGenerationApiService
participant API as HyperionCodeGenerationResource
participant Exec as HyperionCodeGenerationExecutionService
participant Strat as Strategy (Solution/Template/Tests)
participant Git as GitService/Repo
participant CI as CI Trigger/Build System
Instructor->>NG: generateCode(exerciseId, {repositoryType})
NG->>API: POST /api/hyperion/programming-exercises/{id}/generate-code
API->>Exec: generateAndCompileCode(exercise, user, repoType)
Exec->>Git: Setup repo, resolve default branch
Exec->>Exec: select strategy by repoType
Exec->>Strat: generateCode(user, exercise, prevLogs, repoStructure)
Strat->>API: (internal) render prompts + call chat client
Strat-->>Exec: List<GeneratedFile>
Exec->>Git: Update files, commit, push
Exec->>CI: Trigger build
loop Poll until success/timeout
Exec->>CI: Fetch build result
CI-->>Exec: Result/logs
end
Exec-->>API: Result
API-->>NG: CodeGenerationResultDTO
NG-->>Instructor: Show success/partial/error
sequenceDiagram
participant Exec as ExecutionService
participant Strat as Strategy
participant CI as CI System
rect rgba(200, 230, 255, 0.25)
note over Exec: Iterative generation cycle (max N)
loop For i in 1..MAX_ITERATIONS
Exec->>Strat: generateCode(...)
Strat-->>Exec: files
Exec->>Exec: commit & push
Exec->>CI: trigger build
alt Build succeeds
CI-->>Exec: SUCCESS
Exec-->>Exec: break
else Build fails/timeout
CI-->>Exec: FAILURE/logs
Exec->>Exec: collect logs and retry
end
end
end
note over Exec: Cleanup: reset to original commit
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests
Tip 👮 Agentic pre-merge checks are now available in preview!Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.
Please see the documentation for more information. Example: reviews:
pre_merge_checks:
custom_checks:
- name: "Undocumented Breaking Changes"
mode: "warning"
instructions: |
Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal). Please share your feedback with us on this Discord post. 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. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 20
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/main/java/de/tum/cit/aet/artemis/buildagent/service/BuildJobContainerService.java (1)
487-503
: Tar stream not finished/closed — can yield invalid archives and leaks.TarArchiveOutputStream should be finished and closed; otherwise end-of-archive blocks may be missing and buffers not flushed.
Apply this diff:
private ByteArrayOutputStream createTarArchive(Path sourcePath) { - - ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); - - TarArchiveOutputStream tarArchiveOutputStream = new TarArchiveOutputStream(byteArrayOutputStream); - - // This needs to be done in case the files have a long name - tarArchiveOutputStream.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX); - - try { - addFileToTar(tarArchiveOutputStream, sourcePath, ""); - } - catch (IOException e) { - throw new LocalCIException("Could not create tar archive", e); - } - return byteArrayOutputStream; + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + try (TarArchiveOutputStream tarArchiveOutputStream = new TarArchiveOutputStream(byteArrayOutputStream)) { + // Enable long file names + tarArchiveOutputStream.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX); + addFileToTar(tarArchiveOutputStream, sourcePath, ""); + tarArchiveOutputStream.finish(); + } + catch (IOException e) { + throw new LocalCIException("Could not create tar archive", e); + } + return byteArrayOutputStream; }
🧹 Nitpick comments (18)
src/main/java/de/tum/cit/aet/artemis/buildagent/service/BuildJobContainerService.java (1)
453-484
: Avoid re-tarring on every retry; reuse bytes and improve logging signal.Recomputing the tar for each attempt is wasteful for large repos. Create it once, reuse the byte[] per retry, and include the exception type in the warn log.
Apply this diff:
private void copyToContainer(Path sourcePath, String containerId) { final int maxRetries = 3; final long retryDelayMs = 1000; + final byte[] tarBytes = createTarArchive(sourcePath).toByteArray(); @@ - try (final var uploadStream = new ByteArrayInputStream(createTarArchive(sourcePath).toByteArray()); + try (final var uploadStream = new ByteArrayInputStream(tarBytes); final var copyToContainerCommand = buildAgentConfiguration.getDockerClient().copyArchiveToContainerCmd(containerId) .withRemotePath(LOCAL_CI_DOCKER_CONTAINER_WORKING_DIRECTORY).withTarInputStream(uploadStream)) { copyToContainerCommand.exec(); return; } catch (Exception e) { if (attempt == maxRetries) { throw new LocalCIException("Could not copy to container " + containerId + " after " + maxRetries + " attempts", e); } - log.warn("Attempt {} failed to copy to container {}: {}. Retrying in {} ms...", attempt, containerId, e.getMessage(), retryDelayMs); + log.warn("Attempt {} failed to copy to container {} ({}). Retrying in {} ms...", attempt, containerId, e.getClass().getSimpleName(), retryDelayMs); try { Thread.sleep(retryDelayMs); }Optional: exponential backoff (retryDelayMs * 2^(attempt-1)) and add a debug log with the stack trace on retries.
Based on learnings.
src/main/java/de/tum/cit/aet/artemis/programming/domain/Repository.java (1)
85-95
: Preserve stack traces in the new loggingRight now the warn/debug statements only log
e.getMessage()
, so we lose the stack trace that explains why the close failed. Please pass the exception itself to SLF4J so the stack is retained.- log.debug("Repository at {} was already closed: {}", getLocalPath(), e.getMessage()); + log.debug("Repository at {} was already closed", getLocalPath(), e); ... - log.warn("Unexpected error while closing repository at {}: {}", getLocalPath(), e.getMessage()); + log.warn("Unexpected error while closing repository at {}", getLocalPath(), e); ... - log.debug("Repository doClose() at {} already completed: {}", getLocalPath(), e.getMessage()); + log.debug("Repository doClose() at {} already completed", getLocalPath(), e);src/main/resources/prompts/hyperion/test/3_headers.st (1)
17-23
: Strengthen headers guidance to ensure compilable tests.Add explicit notes for package declarations/imports so generated test classes compile without edits.
Suggested additions after the steps list:
- Include the correct
package
declaration matching the file path under/src/test/java/
.- Add necessary imports for the chosen test framework (e.g., JUnit 5).
- Use language‑appropriate annotations/naming (e.g., JUnit
@Test
for Java). Avoid framework‑specific examples for other languages.src/main/resources/prompts/hyperion/template/1_plan.st (2)
28-28
: Output strictly JSON (no prose) to reduce parse failures.Explicitly require JSON‑only output.
-After your analysis, provide your final template plan as a JSON object. The JSON object must contain a single key "solutionPlan" with the detailed template plan as a string. +After your analysis, return only a JSON object (no extra text). The JSON object must contain a single key "solutionPlan" with the detailed template plan as a string.
1-28
: Naming clarity (optional): consider "templatePlan" instead of "solutionPlan".The key name “solutionPlan” for template generation can confuse future contributors. Consider renaming across prompts/DTOs in a follow‑up to “templatePlan”.
src/main/resources/prompts/hyperion/template/4_logic.st (2)
16-16
: Constrain edits to predeclared files to avoid uncontrolled file creation.Limit modifications to the files listed in
filesWithHeaders
to keep the pipeline deterministic.-**Important:** Only modify files in the `/src/` directory. Do not change build files, write tests, configuration files, or any files outside the source directory. +**Important:** Only modify files in the `/src/` directory, and only those present in `filesWithHeaders`. Do not change build files, write tests, configuration files, or any files outside the source directory.
24-24
: Add path-safety and non-solution guardrails.Reinforce safe paths and avoid accidentally solving the exercise.
Append after the return-format paragraph:
- All returned paths must be relative to
/src/
and must not include..
or be absolute.- Provide compiling placeholders and TODOs only; do not implement full algorithms or logic that would solve the assignment.
src/test/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionSolutionRepsitoryTest.java (1)
115-116
: Use static import for times() for consistency.Minor polish to match the style used in the other test class.
-verify(chatModel, org.mockito.Mockito.times(2)).call(any(Prompt.class)); +import static org.mockito.Mockito.times; +// ... +verify(chatModel, times(2)).call(any(Prompt.class));src/test/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionCodeGenerationStrategyTest.java (2)
38-41
: Align file and class names.The file is HyperionCodeGenerationStrategyTest.java but the class is HyperionCodeGenerationServiceTest. Aligning them improves discoverability (choose either “Service” in both or “Strategy” in both).
133-151
: Consider relaxing exact error‑message assertions.Asserting substrings tied to prose can be brittle across refactors/localization. If possible, assert exception type plus a stable error code or prefix.
src/main/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionTestRepositoryService.java (2)
55-64
: Minor: reduce repetition for repositoryStructure defaultYou repeat
repositoryStructure != null ? repositoryStructure : ""
. Consider a local var to improve readability.- var templateVariables = Map.<String, Object>of("problemStatement", exercise.getProblemStatement(), "solutionCode", solutionCode, "programmingLanguage", - exercise.getProgrammingLanguage(), "previousBuildLogs", previousBuildLogs != null ? previousBuildLogs : "", "repositoryStructure", - repositoryStructure != null ? repositoryStructure : ""); + var repoStruct = repositoryStructure != null ? repositoryStructure : ""; + var prevLogs = previousBuildLogs != null ? previousBuildLogs : ""; + var templateVariables = Map.<String, Object>of( + "problemStatement", exercise.getProblemStatement(), + "solutionCode", solutionCode, + "programmingLanguage", exercise.getProgrammingLanguage(), + "previousBuildLogs", prevLogs, + "repositoryStructure", repoStruct);
33-38
: Unused logger
log
is defined but never used. Either add useful debug statements per step or remove it.src/main/webapp/app/programming/manage/code-editor/instructor-and-editor-container/code-editor-instructor-and-editor-container.component.ts (3)
30-45
: Remove duplicate entries inimports
ArtemisTranslatePipe
andNgbTooltip
appear twice in theimports
array. Keep one of each.TranslateDirective, - ArtemisTranslatePipe, + ArtemisTranslatePipe, CodeEditorContainerComponent, IncludedInScoreBadgeComponent, ProgrammingExerciseInstructorExerciseStatusComponent, NgbDropdown, NgbDropdownToggle, NgbDropdownMenu, NgbDropdownButtonItem, NgbDropdownItem, - NgbTooltip, + NgbTooltip, UpdatingResultComponent, ProgrammingExerciseStudentTriggerBuildButtonComponent, ProgrammingExerciseEditableInstructionComponent, - NgbTooltip, - ArtemisTranslatePipe,Also applies to: 39-45
21-22
: Consolidate @angular/core importsCombine into a single import to reduce duplication.
-import { Component, ViewChild } from '@angular/core'; +import { Component, ViewChild, inject, signal } from '@angular/core'; ... -import { inject, signal } from '@angular/core';
90-117
: Option: ensure state reset viafinalize
HttpClient completes automatically, but
finalize
guaranteesisGeneratingCode
resets on all paths and keeps handlers focused.- this.codeGenerationService.generateCode(this.exercise.id, request).subscribe({ - next: (response) => { - this.isGeneratingCode.set(false); + this.codeGenerationService + .generateCode(this.exercise.id, request) + .pipe(finalize(() => this.isGeneratingCode.set(false))) + .subscribe({ + next: (response) => { ... - }, - error: () => { - this.isGeneratingCode.set(false); + }, + error: () => { this.codeGenAlertService.addAlert({ type: AlertType.DANGER, translationKey: 'artemisApp.programmingExercise.codeGeneration.error', translationParams: { repositoryType: this.selectedRepository }, }); - }, - }); + }, + });Add import:
import { finalize } from 'rxjs';src/main/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionCodeGenerationService.java (3)
69-70
: Return empty list instead of nullGuard against null
files
to keep the API contract non-null and simplify callers.- return coreLogicResponse.getFiles(); + return coreLogicResponse.getFiles() != null ? coreLogicResponse.getFiles() : List.of();
39-50
: Unused dependency in base
programmingExerciseRepository
is not used in this abstract class (and isprivate
, so subclasses can't access it). Either remove it from the base constructor/fields or expose aprotected
accessor if subclasses need it.
151-157
: LeveragegetRepositoryType()
for diagnosticsUse this in debug logs (e.g., around AI calls) to ease triaging across SOLUTION/TEMPLATE/TESTS runs.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (3)
openapi/openapi.yaml
is excluded by!**/*.yaml
package-lock.json
is excluded by!**/package-lock.json
src/main/resources/config/application-artemis.yml
is excluded by!**/*.yml
📒 Files selected for processing (42)
package.json
(1 hunks)src/main/java/de/tum/cit/aet/artemis/buildagent/service/BuildJobContainerService.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/hyperion/dto/ArtifactType.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/hyperion/dto/CodeGenerationRequestDTO.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/hyperion/dto/CodeGenerationResponseDTO.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/hyperion/dto/CodeGenerationResultDTO.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/hyperion/dto/GeneratedFile.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/hyperion/service/HyperionProgrammingExerciseContextRendererService.java
(4 hunks)src/main/java/de/tum/cit/aet/artemis/hyperion/service/HyperionPromptTemplateService.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionCodeGenerationExecutionService.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionCodeGenerationService.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionSolutionRepositoryService.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionTemplateRepositoryService.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionTestRepositoryService.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/hyperion/web/HyperionCodeGenerationResource.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/programming/domain/Repository.java
(3 hunks)src/main/java/de/tum/cit/aet/artemis/programming/service/GitService.java
(1 hunks)src/main/resources/prompts/hyperion/solution/1_plan.st
(1 hunks)src/main/resources/prompts/hyperion/solution/2_file_structure.st
(1 hunks)src/main/resources/prompts/hyperion/solution/3_headers.st
(1 hunks)src/main/resources/prompts/hyperion/solution/4_logic.st
(1 hunks)src/main/resources/prompts/hyperion/template/1_plan.st
(1 hunks)src/main/resources/prompts/hyperion/template/2_file_structure.st
(1 hunks)src/main/resources/prompts/hyperion/template/3_headers.st
(1 hunks)src/main/resources/prompts/hyperion/template/4_logic.st
(1 hunks)src/main/resources/prompts/hyperion/test/1_plan.st
(1 hunks)src/main/resources/prompts/hyperion/test/2_file_structure.st
(1 hunks)src/main/resources/prompts/hyperion/test/3_headers.st
(1 hunks)src/main/resources/prompts/hyperion/test/4_logic.st
(1 hunks)src/main/webapp/app/openapi/.openapi-generator/FILES
(2 hunks)src/main/webapp/app/openapi/api/hyperionCodeGenerationApi.service.ts
(1 hunks)src/main/webapp/app/openapi/model/codeGenerationRequestDTO.ts
(1 hunks)src/main/webapp/app/openapi/model/codeGenerationResultDTO.ts
(1 hunks)src/main/webapp/app/programming/manage/code-editor/instructor-and-editor-container/code-editor-instructor-and-editor-container.component.html
(4 hunks)src/main/webapp/app/programming/manage/code-editor/instructor-and-editor-container/code-editor-instructor-and-editor-container.component.ts
(4 hunks)src/main/webapp/i18n/de/programmingExercise.json
(1 hunks)src/main/webapp/i18n/en/programmingExercise.json
(1 hunks)src/test/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionCodeGenerationExecutionServiceTest.java
(1 hunks)src/test/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionCodeGenerationStrategyTest.java
(1 hunks)src/test/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionSolutionRepsitoryTest.java
(1 hunks)src/test/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionTemplateRepositoryTest.java
(1 hunks)src/test/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionTestRepositoryTest.java
(1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
src/main/java/**/*.java
⚙️ CodeRabbit configuration file
naming:CamelCase; principles:{single_responsibility,small_methods,no_duplication}; db:{perf_queries,datetime_not_timestamp}; rest:{stateless,singleton,delegate_logic,http_only,minimal_dtos}; dtos:{java_records,no_entities,min_data,single_resp}; di:constructor_injection; kiss:simple_code; file_handling:os_indep_paths; practices:{least_access,avoid_transactions,code_reuse,static_member_ref,prefer_primitives}; sql:{param_annotation,uppercase,avoid_subqueries};java:avoid_star_imports
Files:
src/main/java/de/tum/cit/aet/artemis/hyperion/dto/GeneratedFile.java
src/main/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionCodeGenerationExecutionService.java
src/main/java/de/tum/cit/aet/artemis/programming/service/GitService.java
src/main/java/de/tum/cit/aet/artemis/buildagent/service/BuildJobContainerService.java
src/main/java/de/tum/cit/aet/artemis/hyperion/service/HyperionProgrammingExerciseContextRendererService.java
src/main/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionTestRepositoryService.java
src/main/java/de/tum/cit/aet/artemis/programming/domain/Repository.java
src/main/java/de/tum/cit/aet/artemis/hyperion/dto/CodeGenerationResultDTO.java
src/main/java/de/tum/cit/aet/artemis/hyperion/web/HyperionCodeGenerationResource.java
src/main/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionTemplateRepositoryService.java
src/main/java/de/tum/cit/aet/artemis/hyperion/service/HyperionPromptTemplateService.java
src/main/java/de/tum/cit/aet/artemis/hyperion/dto/CodeGenerationResponseDTO.java
src/main/java/de/tum/cit/aet/artemis/hyperion/dto/ArtifactType.java
src/main/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionCodeGenerationService.java
src/main/java/de/tum/cit/aet/artemis/hyperion/dto/CodeGenerationRequestDTO.java
src/main/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionSolutionRepositoryService.java
src/main/webapp/**/*.ts
⚙️ CodeRabbit configuration file
Files:
src/main/webapp/app/openapi/model/codeGenerationResultDTO.ts
src/main/webapp/app/openapi/api/hyperionCodeGenerationApi.service.ts
src/main/webapp/app/openapi/model/codeGenerationRequestDTO.ts
src/main/webapp/app/programming/manage/code-editor/instructor-and-editor-container/code-editor-instructor-and-editor-container.component.ts
src/main/webapp/i18n/de/**/*.json
⚙️ CodeRabbit configuration file
German language translations should be informal (dutzen) and should never be formal (sietzen). So the user should always be addressed with "du/dein" and never with "sie/ihr".
Files:
src/main/webapp/i18n/de/programmingExercise.json
src/test/java/**/*.java
⚙️ CodeRabbit configuration file
test_naming: descriptive; test_size: small_specific; fixed_data: true; junit5_features: true; assert_use: assertThat; assert_specificity: true; archunit_use: enforce_package_rules; db_query_count_tests: track_performance; util_service_factory_pattern: true; avoid_db_access: true; mock_strategy: static_mocks; context_restart_minimize: true
Files:
src/test/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionTemplateRepositoryTest.java
src/test/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionTestRepositoryTest.java
src/test/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionCodeGenerationExecutionServiceTest.java
src/test/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionSolutionRepsitoryTest.java
src/test/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionCodeGenerationStrategyTest.java
src/main/webapp/**/*.html
⚙️ CodeRabbit configuration file
@if and @for are new and valid Angular syntax replacing *ngIf and *ngFor. They should always be used over the old style.
Files:
src/main/webapp/app/programming/manage/code-editor/instructor-and-editor-container/code-editor-instructor-and-editor-container.component.html
🧠 Learnings (23)
📚 Learning: 2025-02-06T17:24:13.928Z
Learnt from: magaupp
PR: ls1intum/Artemis#10265
File: src/main/resources/templates/dart/exercise/lib/bubble_sort.dart:1-1
Timestamp: 2025-02-06T17:24:13.928Z
Learning: In programming exercise template files (e.g., src/main/resources/templates/dart/exercise/*), implementations should be left empty or incomplete as they are meant to be completed by students as part of the exercise.
Applied to files:
src/main/resources/prompts/hyperion/solution/3_headers.st
src/main/resources/prompts/hyperion/template/2_file_structure.st
src/main/resources/prompts/hyperion/template/4_logic.st
src/main/resources/prompts/hyperion/template/1_plan.st
src/main/resources/prompts/hyperion/template/3_headers.st
📚 Learning: 2024-10-31T20:40:30.235Z
Learnt from: magaupp
PR: ls1intum/Artemis#9626
File: src/main/resources/templates/c_sharp/exercise/BubbleSort.cs:3-4
Timestamp: 2024-10-31T20:40:30.235Z
Learning: In this project, files under the `exercise` directory are incomplete exercises to be completed by students. Therefore, review comments suggesting implementations in these files may not be necessary.
Applied to files:
src/main/resources/prompts/hyperion/solution/3_headers.st
src/main/resources/prompts/hyperion/solution/4_logic.st
src/main/resources/prompts/hyperion/template/4_logic.st
src/main/resources/prompts/hyperion/template/3_headers.st
📚 Learning: 2025-02-11T15:40:53.440Z
Learnt from: Wallenstein61
PR: ls1intum/Artemis#9909
File: src/main/webapp/app/sharing/search-result-dto.model.ts:57-65
Timestamp: 2025-02-11T15:40:53.440Z
Learning: The ProjectDTO interface in src/main/webapp/app/sharing/search-result-dto.model.ts uses snake_case property names to maintain compatibility with the external sharing platform's API contract, which is an intentional deviation from Angular's camelCase convention.
Applied to files:
src/main/webapp/app/openapi/model/codeGenerationResultDTO.ts
📚 Learning: 2025-02-11T15:46:08.133Z
Learnt from: Wallenstein61
PR: ls1intum/Artemis#9909
File: src/main/webapp/app/sharing/search-result-dto.model.ts:17-42
Timestamp: 2025-02-11T15:46:08.133Z
Learning: The types in `UserProvidedMetadataDTO` interface in `search-result-dto.model.ts` must match the sharing platform's contract exactly to maintain compatibility. Avoid modifying these types even if they don't follow internal TypeScript conventions.
Applied to files:
src/main/webapp/app/openapi/model/codeGenerationResultDTO.ts
📚 Learning: 2025-09-05T09:15:47.371Z
Learnt from: jfr2102
PR: ls1intum/Artemis#11330
File: src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIMissingJobService.java:121-124
Timestamp: 2025-09-05T09:15:47.371Z
Learning: In LocalCIMissingJobService, the retry mechanism preserves build history while preventing infinite retries: the original MISSING job keeps its status unchanged but has its retryCount incremented to prevent re-processing after the retry limit is reached. A new build job is created with the incremented retryCount for the actual retry, ensuring that if the new job also goes missing, retries remain bounded and no infinite loops occur.
Applied to files:
src/main/java/de/tum/cit/aet/artemis/buildagent/service/BuildJobContainerService.java
📚 Learning: 2024-10-04T23:59:21.452Z
Learnt from: magaupp
PR: ls1intum/Artemis#9261
File: src/main/resources/templates/c_plus_plus/test/tests/TestOutput.py:9-10
Timestamp: 2024-10-04T23:59:21.452Z
Learning: The file `src/main/resources/templates/c_plus_plus/test/tests/TestOutput.py` was copied from the original C template, and the PR adds no changes to it.
Applied to files:
src/main/java/de/tum/cit/aet/artemis/hyperion/service/HyperionProgrammingExerciseContextRendererService.java
📚 Learning: 2024-10-04T23:41:59.440Z
Learnt from: magaupp
PR: ls1intum/Artemis#9261
File: src/main/resources/templates/c_plus_plus/test/testUtils/AbstractProgramTest.py:23-23
Timestamp: 2024-10-04T23:41:59.440Z
Learning: The file `src/main/resources/templates/c_plus_plus/test/testUtils/AbstractProgramTest.py` was copied from the original C template, and the PR adds no changes to it.
Applied to files:
src/main/java/de/tum/cit/aet/artemis/hyperion/service/HyperionProgrammingExerciseContextRendererService.java
📚 Learning: 2024-10-08T15:35:48.767Z
Learnt from: magaupp
PR: ls1intum/Artemis#9261
File: src/main/resources/templates/c_plus_plus/test/testUtils/junit/TestSuite.py:19-19
Timestamp: 2024-10-08T15:35:48.767Z
Learning: The file `src/main/resources/templates/c_plus_plus/test/testUtils/junit/TestSuite.py` was copied from the original C template, and the PR adds no changes to it.
Applied to files:
src/main/java/de/tum/cit/aet/artemis/hyperion/service/HyperionProgrammingExerciseContextRendererService.java
📚 Learning: 2025-01-25T17:22:31.410Z
Learnt from: magaupp
PR: ls1intum/Artemis#10202
File: src/main/resources/templates/ruby/test/test/test_structural.rb:5-18
Timestamp: 2025-01-25T17:22:31.410Z
Learning: When reviewing exercise templates, avoid adding tests that would pass immediately with the starter code. Tests should verify the actual implementation that students need to complete, not just the presence of classes and methods defined in the template.
Applied to files:
src/main/java/de/tum/cit/aet/artemis/hyperion/service/HyperionProgrammingExerciseContextRendererService.java
src/main/resources/prompts/hyperion/template/4_logic.st
src/main/resources/prompts/hyperion/template/1_plan.st
src/main/resources/prompts/hyperion/template/3_headers.st
📚 Learning: 2025-08-19T09:13:51.235Z
Learnt from: ekayandan
PR: ls1intum/Artemis#11027
File: src/main/java/de/tum/cit/aet/artemis/programming/service/GitArchiveHelper.java:0-0
Timestamp: 2025-08-19T09:13:51.235Z
Learning: The domain Repository class (de.tum.cit.aet.artemis.programming.domain.Repository) extends JGit's FileRepository, making it polymorphically compatible with JGit APIs while providing additional Artemis-specific methods like getParticipation() and getLocalPath().
Applied to files:
src/main/java/de/tum/cit/aet/artemis/programming/domain/Repository.java
📚 Learning: 2024-10-08T15:35:48.768Z
Learnt from: JohannesStoehr
PR: ls1intum/Artemis#8993
File: src/main/webapp/app/course/competencies/competency-management/competency-relation-graph.component.html:42-45
Timestamp: 2024-10-08T15:35:48.768Z
Learning: The translation keys concatenated with `typeExplanation` (e.g., `artemisApp.competency.relation.typeExplanation.ASSUMES`) are present in the `competency.json` files.
Applied to files:
src/main/java/de/tum/cit/aet/artemis/hyperion/web/HyperionCodeGenerationResource.java
📚 Learning: 2024-10-12T16:20:07.150Z
Learnt from: magaupp
PR: ls1intum/Artemis#9440
File: src/main/resources/templates/typescript/exercise/src/policy.ts:1-3
Timestamp: 2024-10-12T16:20:07.150Z
Learning: In the TypeScript exercise templates, the class structure is intentionally left as an exercise for students. Any showcase of best practices should be under the `solution` directory.
Applied to files:
src/main/resources/prompts/hyperion/template/2_file_structure.st
src/main/resources/prompts/hyperion/template/4_logic.st
src/main/resources/prompts/hyperion/template/1_plan.st
src/main/resources/prompts/hyperion/template/3_headers.st
📚 Learning: 2025-02-06T17:23:31.666Z
Learnt from: magaupp
PR: ls1intum/Artemis#10265
File: src/main/resources/templates/dart/exercise/lib/sort_strategy.dart:1-3
Timestamp: 2025-02-06T17:23:31.666Z
Learning: In programming exercise templates, implementation details should be left incomplete with TODO comments for students to implement, rather than providing the complete solution. This applies to the file `src/main/resources/templates/dart/exercise/lib/sort_strategy.dart` where the `SortStrategy` interface implementation is intentionally left as an exercise.
Applied to files:
src/main/resources/prompts/hyperion/template/2_file_structure.st
src/main/resources/prompts/hyperion/template/4_logic.st
src/main/resources/prompts/hyperion/template/1_plan.st
📚 Learning: 2024-10-31T20:40:57.001Z
Learnt from: magaupp
PR: ls1intum/Artemis#9626
File: src/main/resources/templates/c_sharp/exercise/MergeSort.cs:3-4
Timestamp: 2024-10-31T20:40:57.001Z
Learning: In the C# programming language templates, files under the `exercise` directory are incomplete exercises intended for students to complete.
Applied to files:
src/main/resources/prompts/hyperion/template/2_file_structure.st
src/main/resources/prompts/hyperion/template/4_logic.st
src/main/resources/prompts/hyperion/template/3_headers.st
📚 Learning: 2024-10-12T16:13:02.782Z
Learnt from: magaupp
PR: ls1intum/Artemis#9440
File: src/main/resources/templates/typescript/exercise/src/mergesort.ts:2-2
Timestamp: 2024-10-12T16:13:02.782Z
Learning: In files under the `exercise` directory, TODO comments are intentionally included for students to complete and should remain as is.
Applied to files:
src/main/resources/prompts/hyperion/template/4_logic.st
src/main/resources/prompts/hyperion/template/3_headers.st
📚 Learning: 2025-02-06T17:23:02.625Z
Learnt from: magaupp
PR: ls1intum/Artemis#10265
File: src/main/resources/templates/dart/exercise/lib/context.dart:1-3
Timestamp: 2025-02-06T17:23:02.625Z
Learning: In programming exercise templates, implementations should be left empty with TODO comments to allow students to complete the exercise themselves. This applies to the file `src/main/resources/templates/dart/exercise/lib/context.dart` where the Context class implementation is intentionally left as an exercise for students learning the Strategy Pattern.
Applied to files:
src/main/resources/prompts/hyperion/template/4_logic.st
📚 Learning: 2024-08-05T00:11:50.650Z
Learnt from: magaupp
PR: ls1intum/Artemis#8802
File: src/main/resources/templates/rust/exercise/src/context.rs:1-1
Timestamp: 2024-08-05T00:11:50.650Z
Learning: Code inside the `exercise` directories in the Artemis platform is provided to students and is intended for them to implement. TODO comments in these directories are meant to guide students and should not be addressed in the PR.
Applied to files:
src/main/resources/prompts/hyperion/template/4_logic.st
src/main/resources/prompts/hyperion/template/3_headers.st
📚 Learning: 2024-10-04T23:23:49.139Z
Learnt from: magaupp
PR: ls1intum/Artemis#9261
File: src/main/resources/templates/c_plus_plus/test/testUtils/TestFailedError.py:6-6
Timestamp: 2024-10-04T23:23:49.139Z
Learning: In the Artemis project, when adding new programming language templates like C++, files such as `src/main/resources/templates/c_plus_plus/test/testUtils/TestFailedError.py` may be copied from existing templates without modifications. In such cases, suggesting modifications to these files (e.g., removing unnecessary `pass` statements) may not be necessary.
Applied to files:
src/main/resources/prompts/hyperion/test/3_headers.st
src/main/resources/prompts/hyperion/test/4_logic.st
📚 Learning: 2024-09-21T14:50:40.979Z
Learnt from: edkaya
PR: ls1intum/Artemis#9343
File: src/main/webapp/app/overview/course-archive/course-archive.component.html:15-15
Timestamp: 2024-09-21T14:50:40.979Z
Learning: In `src/main/webapp/app/overview/course-archive/course-archive.component.html`, it's acceptable to use `<a>` elements without `href` attributes as action triggers, and suggestions to replace them with `<button>` elements are not necessary.
Applied to files:
src/main/webapp/app/programming/manage/code-editor/instructor-and-editor-container/code-editor-instructor-and-editor-container.component.html
📚 Learning: 2024-10-20T21:59:11.630Z
Learnt from: pzdr7
PR: ls1intum/Artemis#9505
File: src/main/webapp/app/exercises/programming/shared/code-editor/monaco/code-editor-monaco.component.html:9-9
Timestamp: 2024-10-20T21:59:11.630Z
Learning: In Angular templates within the Artemis project (e.g., `src/main/webapp/app/exercises/programming/shared/code-editor/monaco/code-editor-monaco.component.html`), properties like `selectedFile()`, `readOnlyManualFeedback()`, `highlightDifferences()`, and `course()` are signals. It is appropriate to call these signals directly in the template.
Applied to files:
src/main/webapp/app/programming/manage/code-editor/instructor-and-editor-container/code-editor-instructor-and-editor-container.component.ts
📚 Learning: 2025-08-21T17:30:20.530Z
Learnt from: MoritzSpengler
PR: ls1intum/Artemis#11297
File: src/main/webapp/app/quiz/shared/questions/drag-and-drop-question/drag-and-drop-question.component.spec.ts:34-34
Timestamp: 2025-08-21T17:30:20.530Z
Learning: FitTextDirective in src/main/webapp/app/quiz/shared/fit-text/fit-text.directive.ts is a standalone directive marked with standalone: true, so it should be imported in TestBed imports array, not declarations array.
Applied to files:
src/main/webapp/app/programming/manage/code-editor/instructor-and-editor-container/code-editor-instructor-and-editor-container.component.ts
📚 Learning: 2024-06-10T19:44:09.116Z
Learnt from: florian-glombik
PR: ls1intum/Artemis#8451
File: src/main/webapp/app/exercises/programming/shared/lifecycle/programming-exercise-lifecycle.component.html:187-187
Timestamp: 2024-06-10T19:44:09.116Z
Learning: Use `artemisTranslate` for handling translations in Angular templates within the Artemis platform, as per the project-specific requirements.
Applied to files:
src/main/webapp/app/programming/manage/code-editor/instructor-and-editor-container/code-editor-instructor-and-editor-container.component.ts
📚 Learning: 2025-09-01T10:20:40.706Z
Learnt from: Michael-Breu-UIbk
PR: ls1intum/Artemis#10989
File: src/main/webapp/app/programming/manage/detail/programming-exercise-detail.component.with-sharing.spec.ts:132-149
Timestamp: 2025-09-01T10:20:40.706Z
Learning: In the Artemis codebase, Angular component test files for ProgrammingExerciseDetailComponent follow a pattern where the component is imported but not explicitly declared in TestBed.configureTestingModule(), yet TestBed.createComponent() still works successfully. This pattern is consistently used across test files like programming-exercise-detail.component.spec.ts and programming-exercise-detail.component.with-sharing.spec.ts.
Applied to files:
src/main/webapp/app/programming/manage/code-editor/instructor-and-editor-container/code-editor-instructor-and-editor-container.component.ts
🧬 Code graph analysis (12)
src/main/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionCodeGenerationExecutionService.java (7)
src/main/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionTemplateRepositoryService.java (1)
Service
(28-94)src/main/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionSolutionRepositoryService.java (1)
Service
(24-69)src/main/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionTestRepositoryService.java (1)
Service
(28-94)src/main/java/de/tum/cit/aet/artemis/hyperion/service/HyperionProgrammingExerciseContextRendererService.java (1)
Service
(93-371)src/main/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionCodeGenerationService.java (1)
Service
(32-157)src/main/java/de/tum/cit/aet/artemis/programming/domain/Repository.java (1)
Repository
(19-102)src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCRepositoryUri.java (1)
LocalVCRepositoryUri
(17-318)
src/main/webapp/app/openapi/api/hyperionCodeGenerationApi.service.ts (4)
src/main/webapp/app/openapi/api.base.service.ts (1)
BaseService
(14-83)src/main/webapp/app/openapi/variables.ts (1)
BASE_PATH
(3-3)src/main/webapp/app/openapi/model/codeGenerationRequestDTO.ts (1)
CodeGenerationRequestDTO
(15-20)src/main/webapp/app/openapi/model/codeGenerationResultDTO.ts (1)
CodeGenerationResultDTO
(15-28)
src/main/java/de/tum/cit/aet/artemis/hyperion/service/HyperionProgrammingExerciseContextRendererService.java (1)
src/main/java/de/tum/cit/aet/artemis/programming/domain/Repository.java (1)
Repository
(19-102)
src/main/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionTestRepositoryService.java (4)
src/main/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionTemplateRepositoryService.java (1)
Service
(28-94)src/main/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionSolutionRepositoryService.java (1)
Service
(24-69)src/main/java/de/tum/cit/aet/artemis/hyperion/service/HyperionProgrammingExerciseContextRendererService.java (1)
Service
(93-371)src/main/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionCodeGenerationService.java (1)
Service
(32-157)
src/main/java/de/tum/cit/aet/artemis/hyperion/dto/CodeGenerationResultDTO.java (1)
src/main/webapp/app/openapi/model/codeGenerationResultDTO.ts (1)
CodeGenerationResultDTO
(15-28)
src/test/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionTemplateRepositoryTest.java (2)
src/main/java/de/tum/cit/aet/artemis/programming/domain/Repository.java (1)
Repository
(19-102)src/main/java/de/tum/cit/aet/artemis/programming/domain/VcsRepositoryUri.java (1)
VcsRepositoryUri
(11-180)
src/main/java/de/tum/cit/aet/artemis/hyperion/web/HyperionCodeGenerationResource.java (5)
src/main/webapp/app/core/user/user.model.ts (1)
User
(5-65)src/main/java/de/tum/cit/aet/artemis/hyperion/config/HyperionEnabled.java (1)
HyperionEnabled
(13-25)src/main/webapp/app/openapi/model/codeGenerationRequestDTO.ts (1)
CodeGenerationRequestDTO
(15-20)src/main/webapp/app/openapi/model/codeGenerationResultDTO.ts (1)
CodeGenerationResultDTO
(15-28)src/main/webapp/app/programming/shared/entities/programming-exercise.model.ts (1)
ProgrammingExercise
(47-96)
src/main/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionTemplateRepositoryService.java (4)
src/main/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionSolutionRepositoryService.java (1)
Service
(24-69)src/main/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionTestRepositoryService.java (1)
Service
(28-94)src/main/java/de/tum/cit/aet/artemis/hyperion/service/HyperionProgrammingExerciseContextRendererService.java (1)
Service
(93-371)src/main/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionCodeGenerationService.java (1)
Service
(32-157)
src/main/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionCodeGenerationService.java (4)
src/main/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionCodeGenerationExecutionService.java (1)
Service
(46-410)src/main/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionTemplateRepositoryService.java (1)
Service
(28-94)src/main/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionSolutionRepositoryService.java (1)
Service
(24-69)src/main/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionTestRepositoryService.java (1)
Service
(28-94)
src/main/java/de/tum/cit/aet/artemis/hyperion/dto/CodeGenerationRequestDTO.java (1)
src/main/webapp/app/openapi/model/codeGenerationRequestDTO.ts (1)
CodeGenerationRequestDTO
(15-20)
src/main/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionSolutionRepositoryService.java (4)
src/main/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionCodeGenerationExecutionService.java (1)
Service
(46-410)src/main/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionTemplateRepositoryService.java (1)
Service
(28-94)src/main/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionTestRepositoryService.java (1)
Service
(28-94)src/main/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionCodeGenerationService.java (1)
Service
(32-157)
src/main/webapp/app/programming/manage/code-editor/instructor-and-editor-container/code-editor-instructor-and-editor-container.component.ts (2)
src/main/webapp/app/shared/service/base-api-http.service.ts (1)
request
(40-70)src/main/webapp/app/openapi/model/codeGenerationRequestDTO.ts (3)
CodeGenerationRequestDTO
(15-20)RepositoryTypeEnum
(22-26)RepositoryTypeEnum
(27-27)
🪛 GitHub Actions: Test
src/main/java/de/tum/cit/aet/artemis/hyperion/service/HyperionProgrammingExerciseContextRendererService.java
[error] 327-327: Checkstyle: Expected @param tag for 'gitService'. [JavadocMethod]
src/main/java/de/tum/cit/aet/artemis/programming/domain/Repository.java
[error] 80-80: Checkstyle: Missing a Javadoc comment. [MissingJavadocMethod]
src/test/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionTemplateRepositoryTest.java
[error] 75-75: constructor HyperionTemplateRepositoryService(...) cannot be applied to given types; expected (ProgrammingExerciseRepository, ChatClient, HyperionPromptTemplateService, GitService, HyperionProgrammingExerciseContextRendererService) but found (ProgrammingExerciseRepository, ChatClient, HyperionPromptTemplateService, GitService) with missing argument(s).
src/test/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionTestRepositoryTest.java
[error] 62-62: constructor HyperionTestRepositoryService(...) cannot be applied to given types; expected (ProgrammingExerciseRepository, ChatClient, HyperionPromptTemplateService, GitService, HyperionProgrammingExerciseContextRendererService) but found (ProgrammingExerciseRepository, ChatClient, HyperionPromptTemplateService, GitService) with missing argument(s).
src/test/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionCodeGenerationExecutionServiceTest.java
[error] 69-69: Constructor HyperionCodeGenerationExecutionService(...) cannot be applied to given types; expected a longer argument list including HyperionSolutionRepositoryService, HyperionTemplateRepositoryService, HyperionTestRepositoryService, etc., but found fewer arguments.
🪛 GitHub Actions: CodeQL
src/test/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionTemplateRepositoryTest.java
[error] 75-75: constructor HyperionTemplateRepositoryService in class HyperionTemplateRepositoryService cannot be applied to given types; required: ProgrammingExerciseRepository,ChatClient,HyperionPromptTemplateService,GitService,HyperionProgrammingExerciseContextRendererService; found: ProgrammingExerciseRepository,ChatClient,HyperionPromptTemplateService,GitService; reason: actual and formal argument lists differ in length
src/test/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionTestRepositoryTest.java
[error] 62-62: constructor HyperionTestRepositoryService in class HyperionTestRepositoryService cannot be applied to given types; required: ProgrammingExerciseRepository,ChatClient,HyperionPromptTemplateService,GitService,HyperionProgrammingExerciseContextRendererService; found: ProgrammingExerciseRepository,ChatClient,HyperionPromptTemplateService,GitService; reason: actual and formal argument lists differ in length
src/test/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionCodeGenerationExecutionServiceTest.java
[error] 69-69: constructor HyperionCodeGenerationExecutionService in class HyperionCodeGenerationExecutionService cannot be applied to given types; required: GitService,RepositoryService,SolutionProgrammingExerciseParticipationRepository,ProgrammingSubmissionRepository,ResultRepository,ContinuousIntegrationTriggerService,ProgrammingExerciseParticipationService,HyperionProgrammingExerciseContextRendererService,HyperionSolutionRepositoryService,HyperionTemplateRepositoryService,HyperionTestRepositoryService; found: GitService,ApplicationContext,RepositoryService,SolutionProgrammingExerciseParticipationRepository,ProgrammingSubmissionRepository,ResultRepository,ContinuousIntegrationTriggerService,ProgrammingExerciseParticipationService,HyperionProgrammingExerciseContextRendererService; reason: actual and formal argument lists differ in length
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Codacy Static Code Analysis
- GitHub Check: client-tests-selected
🔇 Additional comments (9)
src/main/java/de/tum/cit/aet/artemis/buildagent/service/BuildJobContainerService.java (1)
456-463
: Good rationale for retries.The inline comment explains transient-failure cases well and addresses previous “why” feedback.
src/main/java/de/tum/cit/aet/artemis/hyperion/dto/CodeGenerationRequestDTO.java (1)
1-15
: LGTM on record DTO and validation usage.Using a Java record with
@NotNull
and minimal fields aligns with our DTO guidance.src/main/java/de/tum/cit/aet/artemis/hyperion/dto/CodeGenerationResponseDTO.java (2)
11-21
: LGTM on payload shape and NON_EMPTY.Structure matches the generation pipeline’s needs and stays minimal.
23-30
: Do not remove these getters
getSolutionPlan()
andgetFiles()
are called in service implementations and numerous tests; deleting them will break existing code. Leave them or first migrate all call sites to use the record’ssolutionPlan()
andfiles()
accessors.Likely an incorrect or invalid review comment.
src/test/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionSolutionRepsitoryTest.java (2)
118-133
: Good end‑to‑end coverage of multi‑step generation.Chaining responses and verifying two chat invocations accurately captures the headers flow.
35-49
: File rename verified and no remaining “Repsitory” references.src/test/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionCodeGenerationStrategyTest.java (1)
72-92
: Nice orchestration test — verifies all 4 steps and template invocations.Mocks and verifications are precise; asserts are specific.
src/main/webapp/i18n/de/programmingExercise.json (1)
223-234
: Translation keys validated
All artemisApp.programmingExercise.codeGeneration.* keys are correctly referenced in HTML and TS (no use of programmingExercise.problemStatement.codeGeneration), and there are no duplicate “codeGeneration” entries in the JSON.src/main/webapp/app/programming/manage/code-editor/instructor-and-editor-container/code-editor-instructor-and-editor-container.component.ts (1)
65-66
: Good use of signals for request stateUsing
isGeneratingCode = signal(false)
aligns with the project’s shift to signals and prevents double-submits.
@JsonInclude(JsonInclude.Include.NON_EMPTY) | ||
public record CodeGenerationRequestDTO(@NotNull RepositoryType repositoryType) { | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Make repositoryType required in the API contract (backend + OpenAPI/TS).
Backend requires @NotNull
, but the generated TS model marks it optional, causing 400s at runtime. Mark it required in OpenAPI and adjust the TS model generation.
Apply this diff:
import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.v3.oas.annotations.media.Schema;
@JsonInclude(JsonInclude.Include.NON_EMPTY)
-public record CodeGenerationRequestDTO(@NotNull RepositoryType repositoryType) {
+public record CodeGenerationRequestDTO(
+ @NotNull
+ @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "Type of repository to generate code for")
+ RepositoryType repositoryType) {
}
And ensure the generated TS model is non‑optional.
Run to verify (fails if still optional):
As per coding guidelines: dtos:{java_records,min_data}.
🏁 Script executed:
#!/bin/bash
rg -n "repositoryType\\?\\s*:" src/main/webapp/app/openapi/model/codeGenerationRequestDTO.ts && echo "Found optional repositoryType (fix needed)" || echo "OK: repositoryType is required"
Length of output: 111
Make repositoryType required in API contract
repositoryType
is still optional in src/main/webapp/app/openapi/model/codeGenerationRequestDTO.ts
(line 19). Add the @Schema(requiredMode = Schema.RequiredMode.REQUIRED,…)
annotation to the Java DTO, regenerate the OpenAPI/TS model, and ensure the TS property is non-optional (no ?
). Rerun the verification script.
🤖 Prompt for AI Agents
In
src/main/java/de/tum/cit/aet/artemis/hyperion/dto/CodeGenerationRequestDTO.java
around lines 13-15, the repositoryType field is currently not marked as required
for OpenAPI generation; add the @Schema(requiredMode =
Schema.RequiredMode.REQUIRED, description = "repository type") annotation to the
record parameter (and keep @NotNull), rebuild/regenerate the OpenAPI spec and
TypeScript models so
src/main/webapp/app/openapi/model/codeGenerationRequestDTO.ts reflects a
non-optional repositoryType (remove the `?`), and then rerun the verification
script to ensure the change passes.
@JsonInclude(JsonInclude.Include.NON_EMPTY) | ||
public record CodeGenerationResultDTO( | ||
/** | ||
* Whether the code generation and compilation was successful | ||
*/ | ||
boolean success, | ||
|
||
/** | ||
* Descriptive message about the generation result | ||
*/ | ||
String message, | ||
|
||
/** | ||
* Number of attempts made during the generation process | ||
*/ | ||
int attempts) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Keep success
/attempts
on failure responses
Line 9 adds @JsonInclude(JsonInclude.Include.NON_EMPTY)
to a record whose fields are primitives. With Jackson, Include.NON_EMPTY
treats false
and 0
as empty, so any failed generation (success == false
) or a first attempt (attempts == 0
) will drop these properties from the payload. The generated TS client (see codeGenerationResultDTO.ts
) expects success: boolean
and attempts: number
; when the fields disappear they deserialize as undefined
, breaking the API contract precisely when we need the data to signal failure. Please keep these fields always serialized—e.g. remove the annotation or switch to Include.NON_NULL
and keep primitives as-is.
-@JsonInclude(JsonInclude.Include.NON_EMPTY)
public record CodeGenerationResultDTO(
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
@JsonInclude(JsonInclude.Include.NON_EMPTY) | |
public record CodeGenerationResultDTO( | |
/** | |
* Whether the code generation and compilation was successful | |
*/ | |
boolean success, | |
/** | |
* Descriptive message about the generation result | |
*/ | |
String message, | |
/** | |
* Number of attempts made during the generation process | |
*/ | |
int attempts) { | |
public record CodeGenerationResultDTO( | |
/** | |
* Whether the code generation and compilation was successful | |
*/ | |
boolean success, | |
/** | |
* Descriptive message about the generation result | |
*/ | |
String message, | |
/** | |
* Number of attempts made during the generation process | |
*/ | |
int attempts) { |
🤖 Prompt for AI Agents
In
src/main/java/de/tum/cit/aet/artemis/hyperion/dto/CodeGenerationResultDTO.java
around lines 9–24, the @JsonInclude(JsonInclude.Include.NON_EMPTY) causes
primitive fields like boolean success and int attempts to be omitted when
false/0; change the annotation to @JsonInclude(JsonInclude.Include.NON_NULL) or
remove the @JsonInclude entirely so success and attempts are always serialized
(ensure imports remain valid), then run tests to confirm generated TS client
receives defined boolean/number values.
private String commitAndGetHash(Repository repository, User user, LocalVCRepositoryUri repositoryUri, ProgrammingExercise exercise) throws GitAPIException { | ||
repositoryService.commitChanges(repository, user); | ||
String newCommitHash = gitService.getLastCommitHash(repositoryUri).getName(); | ||
|
||
try { | ||
ProgrammingExerciseParticipation solutionParticipation = programmingExerciseParticipationService.retrieveSolutionParticipation(exercise); | ||
continuousIntegrationTriggerService.triggerBuild(solutionParticipation, newCommitHash, null); | ||
log.debug("Successfully triggered CI build for commit {} in exercise {}", newCommitHash, exercise.getId()); | ||
} | ||
catch (ContinuousIntegrationException e) { | ||
log.warn("Failed to trigger CI build for commit {} in exercise {}: {}", newCommitHash, exercise.getId(), e.getMessage()); | ||
} | ||
|
||
return newCommitHash; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Triggering CI on the wrong participation breaks template/test generation
commitAndGetHash
and waitForBuildResult
always refer to the solution participation, even when we are generating the template or test repositories. As soon as we work on TEMPLATE or TESTS, the commit hash we pass to the CI belongs to a different repository, and the follow-up polling still looks at the solution participation. The CI call cannot find that commit, so the build never completes and every iteration times out. Please resolve the participation/CI wiring per RepositoryType
(e.g. resolve the correct participation before triggering and while polling) so that template/test runs target their own repositories.
Also applies to: 375-408
🤖 Prompt for AI Agents
In
src/main/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionCodeGenerationExecutionService.java
around lines 229-243 (and similarly at 375-408), the code always resolves and
triggers CI on the solution participation even when committing to TEMPLATE or
TESTS repos; fix by resolving the correct participation based on the
RepositoryType of the repository being committed and use that same participation
for both triggering and subsequent polling. Change commitAndGetHash to accept a
RepositoryType (or a Participation resolved before calling) and: 1) after
commit, resolve the appropriate participation for the exercise and repository
type (solution, template, or tests), 2) pass that participation into
continuousIntegrationTriggerService.triggerBuild, 3) ensure
waitForBuildResult/polling logic receives and polls the same participation
instead of always using the solution participation, and 4) handle null/missing
participation and CI exceptions gracefully (log and bail out or retry as
existing behavior). Ensure analogous updates are applied to the code paths
around lines 375-408 so template/test flows target their own repositories.
import java.util.List; | ||
import java.util.Map; | ||
|
||
import org.slf4j.Logger; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make ChatClient mandatory (or fail fast) and harden error handling
chatClient
can be null (optional injection) but is dereferenced in callChatClient
, risking NPE at runtime. Prefer requiring it when Hyperion is enabled, and add a generic catch for unexpected runtime failures.
import java.util.List;
import java.util.Map;
+import java.util.Objects;
@@
- private final ChatClient chatClient;
+ private final ChatClient chatClient;
@@
- public HyperionCodeGenerationService(ProgrammingExerciseRepository programmingExerciseRepository, @Autowired(required = false) ChatClient chatClient,
- HyperionPromptTemplateService templates) {
+ public HyperionCodeGenerationService(ProgrammingExerciseRepository programmingExerciseRepository, ChatClient chatClient,
+ HyperionPromptTemplateService templates) {
this.programmingExerciseRepository = programmingExerciseRepository;
- this.chatClient = chatClient;
+ this.chatClient = Objects.requireNonNull(chatClient, "Hyperion AI ChatClient bean is missing. Enable and configure Spring AI.");
this.templates = templates;
}
@@
protected CodeGenerationResponseDTO callChatClient(String prompt, Map<String, Object> templateVariables) throws NetworkingException {
String rendered = templates.renderObject(prompt, templateVariables);
try {
var response = chatClient.prompt().user(rendered).call().entity(CodeGenerationResponseDTO.class);
return response;
}
catch (TransientAiException e) {
throw new NetworkingException("Temporary AI service issue. Please retry.", e);
}
catch (NonTransientAiException e) {
throw new NetworkingException("AI request failed due to configuration or input. Check model and request.", e);
}
+ catch (RuntimeException e) {
+ throw new NetworkingException("Unexpected error while contacting the AI service.", e);
+ }
}
Also applies to: 41-50, 81-93
🤖 Prompt for AI Agents
In
src/main/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionCodeGenerationService.java
(notably around lines 3-6, and also apply changes at 41-50 and 81-93): ensure
the injected chatClient is required when Hyperion is enabled by adding a
fail-fast null check at construction or initialization and throw an
IllegalStateException with a clear message if it's missing; in callChatClient
add defensive checks (throw early if chatClient is null) and wrap the existing
logic with a generic catch(Exception) to log the unexpected error and either
rethrow a runtime exception or return a controlled failure result; update
signatures/comments to reflect chatClient is mandatory and ensure logging
includes the exception details for easier troubleshooting.
import org.springframework.context.annotation.Conditional; | ||
import org.springframework.context.annotation.Lazy; | ||
import org.springframework.stereotype.Service; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Do not register abstract base as a Spring bean
Annotating an abstract class with @Service/@Lazy/@Conditional
is unnecessary and can confuse component scanning. Only the concrete strategies should be beans.
-import org.springframework.context.annotation.Conditional;
-import org.springframework.context.annotation.Lazy;
-import org.springframework.stereotype.Service;
+// removed: bean annotations are on concrete strategies
@@
-@Service
-@Lazy
-@Conditional(HyperionEnabled.class)
public abstract class HyperionCodeGenerationService {
Also applies to: 32-35
🤖 Prompt for AI Agents
In
src/main/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionCodeGenerationService.java
around lines 12-14 and 32-35, the abstract base class is annotated with Spring
stereotypes/annotations which registers it as a bean and confuses component
scanning; remove @Service, @Lazy, and @Conditional (and any other bean-defining
annotations) from the abstract base class and place them only on the concrete
strategy implementations so that only concrete classes are registered as Spring
beans while the abstract class remains a plain abstract type.
<button class="btn btn-outline-primary" disabled> | ||
<fa-icon [icon]="faSpinner" animation="spin" /> | ||
</button> | ||
} @else { | ||
<button class="btn btn-outline-primary" type="button" ngbDropdownToggle> | ||
<fa-icon [icon]="facArtemisIntelligence" /> | ||
<span class="ms-2" jhiTranslate="artemisApp.programmingExercise.codeGeneration.creationAssistance">Creation Assistance</span> | ||
</button> | ||
<div class="dropdown-menu" ngbDropdownMenu> | ||
<button | ||
type="button" | ||
class="dropdown-item" | ||
(click)="generateCode()" | ||
[disabled]="isGeneratingCode()" | ||
jhiTranslate="artemisApp.programmingExercise.codeGeneration.generateCode" | ||
> | ||
Generate Code |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add an accessible name for the spinner-only button
When isGeneratingCode()
returns true, the dropdown is replaced with a button that only contains an icon. Without any text or aria-label
, the control has no accessible name, so assistive technologies announce it as an unnamed button. Please add a translated label (e.g., via a visually hidden span or [attr.aria-label]
) to keep the button compliant while the spinner is shown.
Apply this diff to provide a hidden text label:
- @if (isGeneratingCode()) {
- <button class="btn btn-outline-primary" disabled>
- <fa-icon [icon]="faSpinner" animation="spin" />
- </button>
+ @if (isGeneratingCode()) {
+ <button class="btn btn-outline-primary" disabled>
+ <fa-icon [icon]="faSpinner" animation="spin" />
+ <span class="visually-hidden" jhiTranslate="artemisApp.programmingExercise.codeGeneration.creationAssistance"></span>
+ </button>
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
<button class="btn btn-outline-primary" disabled> | |
<fa-icon [icon]="faSpinner" animation="spin" /> | |
</button> | |
} @else { | |
<button class="btn btn-outline-primary" type="button" ngbDropdownToggle> | |
<fa-icon [icon]="facArtemisIntelligence" /> | |
<span class="ms-2" jhiTranslate="artemisApp.programmingExercise.codeGeneration.creationAssistance">Creation Assistance</span> | |
</button> | |
<div class="dropdown-menu" ngbDropdownMenu> | |
<button | |
type="button" | |
class="dropdown-item" | |
(click)="generateCode()" | |
[disabled]="isGeneratingCode()" | |
jhiTranslate="artemisApp.programmingExercise.codeGeneration.generateCode" | |
> | |
Generate Code | |
@if (isGeneratingCode()) { | |
<button class="btn btn-outline-primary" disabled> | |
<fa-icon [icon]="faSpinner" animation="spin" /> | |
<span class="visually-hidden" jhiTranslate="artemisApp.programmingExercise.codeGeneration.creationAssistance"></span> | |
</button> | |
} @else { | |
<button class="btn btn-outline-primary" type="button" ngbDropdownToggle> | |
<fa-icon [icon]="facArtemisIntelligence" /> | |
<span class="ms-2" jhiTranslate="artemisApp.programmingExercise.codeGeneration.creationAssistance">Creation Assistance</span> | |
</button> | |
<div class="dropdown-menu" ngbDropdownMenu> | |
<button | |
type="button" | |
class="dropdown-item" | |
(click)="generateCode()" | |
[disabled]="isGeneratingCode()" | |
jhiTranslate="artemisApp.programmingExercise.codeGeneration.generateCode" | |
> | |
Generate Code |
🤖 Prompt for AI Agents
In
src/main/webapp/app/programming/manage/code-editor/instructor-and-editor-container/code-editor-instructor-and-editor-container.component.html
around lines 135-151 the spinner-only button rendered when isGeneratingCode() is
true lacks an accessible name; add a translated accessible label by either
adding a visually hidden span with jhiTranslate inside the button (e.g. <span
class="visually-hidden" jhiTranslate="...">...</span>) or set [attr.aria-label]
to a translated string so assistive tech can announce the control while keeping
the visual spinner only; ensure the translation key used matches existing keys
(or add a new key) and that the label is present even when the button is
disabled.
"codeGeneration": { | ||
"creationAssistance": "Erstellungsassistenz", | ||
"generateCode": "Code generieren", | ||
"generating": "Generiere...", | ||
"description": "Generiere Lösung, Template und Test-Code mit KI", | ||
"tooltip": "KI Code-Generierung - Generiere Lösung, Template und Test-Code", | ||
"success": "{{repositoryType}} Code-Generierung erfolgreich abgeschlossen!", | ||
"partialSuccess": "{{repositoryType}} Code-Generierung mit einigen Problemen abgeschlossen. Bitte überprüfen Sie den generierten Code.", | ||
"error": "{{repositoryType}} Code-Generierung fehlgeschlagen. Bitte versuchen Sie es später erneut.", | ||
"unsupportedRepository": "Code-Generierung wird nur für Template-, Lösung- und Test-Repositories unterstützt.", | ||
"attempts": "Versuche: {{attempts}}" | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use informal German; replace “Sie” with “du”. Also fix “Lösung-” → “Lösungs-”.
Per project guideline for de translations, address the user with “du/dein”, never “Sie/Ihr”. Suggested patch also corrects the compound noun hyphenation.
As per coding guidelines
"codeGeneration": {
"creationAssistance": "Erstellungsassistenz",
"generateCode": "Code generieren",
"generating": "Generiere...",
"description": "Generiere Lösung, Template und Test-Code mit KI",
"tooltip": "KI Code-Generierung - Generiere Lösung, Template und Test-Code",
"success": "{{repositoryType}} Code-Generierung erfolgreich abgeschlossen!",
- "partialSuccess": "{{repositoryType}} Code-Generierung mit einigen Problemen abgeschlossen. Bitte überprüfen Sie den generierten Code.",
- "error": "{{repositoryType}} Code-Generierung fehlgeschlagen. Bitte versuchen Sie es später erneut.",
- "unsupportedRepository": "Code-Generierung wird nur für Template-, Lösung- und Test-Repositories unterstützt.",
+ "partialSuccess": "{{repositoryType}} Code-Generierung mit einigen Problemen abgeschlossen. Bitte prüfe den generierten Code.",
+ "error": "{{repositoryType}} Code-Generierung fehlgeschlagen. Bitte versuche es später erneut.",
+ "unsupportedRepository": "Code-Generierung wird nur für Template-, Lösungs- und Test-Repositories unterstützt.",
"attempts": "Versuche: {{attempts}}"
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
"codeGeneration": { | |
"creationAssistance": "Erstellungsassistenz", | |
"generateCode": "Code generieren", | |
"generating": "Generiere...", | |
"description": "Generiere Lösung, Template und Test-Code mit KI", | |
"tooltip": "KI Code-Generierung - Generiere Lösung, Template und Test-Code", | |
"success": "{{repositoryType}} Code-Generierung erfolgreich abgeschlossen!", | |
"partialSuccess": "{{repositoryType}} Code-Generierung mit einigen Problemen abgeschlossen. Bitte überprüfen Sie den generierten Code.", | |
"error": "{{repositoryType}} Code-Generierung fehlgeschlagen. Bitte versuchen Sie es später erneut.", | |
"unsupportedRepository": "Code-Generierung wird nur für Template-, Lösung- und Test-Repositories unterstützt.", | |
"attempts": "Versuche: {{attempts}}" | |
}, | |
"codeGeneration": { | |
"creationAssistance": "Erstellungsassistenz", | |
"generateCode": "Code generieren", | |
"generating": "Generiere...", | |
"description": "Generiere Lösung, Template und Test-Code mit KI", | |
"tooltip": "KI Code-Generierung - Generiere Lösung, Template und Test-Code", | |
"success": "{{repositoryType}} Code-Generierung erfolgreich abgeschlossen!", | |
"partialSuccess": "{{repositoryType}} Code-Generierung mit einigen Problemen abgeschlossen. Bitte prüfe den generierten Code.", | |
"error": "{{repositoryType}} Code-Generierung fehlgeschlagen. Bitte versuche es später erneut.", | |
"unsupportedRepository": "Code-Generierung wird nur für Template-, Lösungs- und Test-Repositories unterstützt.", | |
"attempts": "Versuche: {{attempts}}" | |
}, |
🤖 Prompt for AI Agents
In src/main/webapp/i18n/de/programmingExercise.json around lines 223 to 234, the
German strings use formal "Sie/Ihr" and an incorrect compound "Lösung-"; update
the translations to informal "du/dein" (or "deine" where grammatically
appropriate) and change "Lösung-" to "Lösungs-" in relevant keys (description,
tooltip, success, partialSuccess, error, and any other occurrences in this
block), keeping placeholders like {{repositoryType}} and {{attempts}} intact and
grammatical after the pronoun change.
this.service = new HyperionCodeGenerationExecutionService(gitService, applicationContext, repositoryService, solutionProgrammingExerciseParticipationRepository, | ||
programmingSubmissionRepository, resultRepository, continuousIntegrationTriggerService, programmingExerciseParticipationService, repositoryStructureService); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Constructor call is out of date and breaks the build
HyperionCodeGenerationExecutionService
no longer takes an ApplicationContext
; it now expects the three concrete strategy services in its constructor. The current instantiation causes the compile failure reported by the pipeline. Update the test to mock HyperionSolutionRepositoryService
, HyperionTemplateRepositoryService
, and HyperionTestRepositoryService
, remove the unused ApplicationContext
, and pass the new mocks into the constructor:
- @Mock
- private ApplicationContext applicationContext;
+ @Mock
+ private HyperionSolutionRepositoryService solutionRepositoryService;
+
+ @Mock
+ private HyperionTemplateRepositoryService templateRepositoryService;
+
+ @Mock
+ private HyperionTestRepositoryService testRepositoryService;
…
- this.service = new HyperionCodeGenerationExecutionService(gitService, applicationContext, repositoryService, solutionProgrammingExerciseParticipationRepository,
- programmingSubmissionRepository, resultRepository, continuousIntegrationTriggerService, programmingExerciseParticipationService, repositoryStructureService);
+ this.service = new HyperionCodeGenerationExecutionService(gitService, repositoryService, solutionProgrammingExerciseParticipationRepository, programmingSubmissionRepository,
+ resultRepository, continuousIntegrationTriggerService, programmingExerciseParticipationService, repositoryStructureService, solutionRepositoryService,
+ templateRepositoryService, testRepositoryService);
Adjust the strategy-resolution assertions to work with the injected mocks after this change.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
this.service = new HyperionCodeGenerationExecutionService(gitService, applicationContext, repositoryService, solutionProgrammingExerciseParticipationRepository, | |
programmingSubmissionRepository, resultRepository, continuousIntegrationTriggerService, programmingExerciseParticipationService, repositoryStructureService); | |
// … other @Mock declarations … | |
// Remove the old ApplicationContext mock and add the three strategy-service mocks | |
- @Mock | |
@Mock | |
private HyperionSolutionRepositoryService solutionRepositoryService; | |
@Mock | |
private HyperionTemplateRepositoryService templateRepositoryService; | |
@Mock | |
private HyperionTestRepositoryService testRepositoryService; | |
// … possibly other setup code … | |
@BeforeEach | |
void setup() { | |
MockitoAnnotations.openMocks(this); | |
this.service = new HyperionCodeGenerationExecutionService( | |
- gitService, | |
- applicationContext, | |
- repositoryService, | |
- solutionProgrammingExerciseParticipationRepository, | |
- programmingSubmissionRepository, | |
- resultRepository, | |
- continuousIntegrationTriggerService, | |
- programmingExerciseParticipationService, | |
gitService, | |
repositoryService, | |
solutionProgrammingExerciseParticipationRepository, | |
programmingSubmissionRepository, | |
resultRepository, | |
continuousIntegrationTriggerService, | |
programmingExerciseParticipationService, | |
repositoryStructureService, | |
solutionRepositoryService, | |
templateRepositoryService, | |
testRepositoryService); | |
} |
🧰 Tools
🪛 GitHub Actions: Test
[error] 69-69: Constructor HyperionCodeGenerationExecutionService(...) cannot be applied to given types; expected a longer argument list including HyperionSolutionRepositoryService, HyperionTemplateRepositoryService, HyperionTestRepositoryService, etc., but found fewer arguments.
🪛 GitHub Actions: CodeQL
[error] 69-69: constructor HyperionCodeGenerationExecutionService in class HyperionCodeGenerationExecutionService cannot be applied to given types; required: GitService,RepositoryService,SolutionProgrammingExerciseParticipationRepository,ProgrammingSubmissionRepository,ResultRepository,ContinuousIntegrationTriggerService,ProgrammingExerciseParticipationService,HyperionProgrammingExerciseContextRendererService,HyperionSolutionRepositoryService,HyperionTemplateRepositoryService,HyperionTestRepositoryService; found: GitService,ApplicationContext,RepositoryService,SolutionProgrammingExerciseParticipationRepository,ProgrammingSubmissionRepository,ResultRepository,ContinuousIntegrationTriggerService,ProgrammingExerciseParticipationService,HyperionProgrammingExerciseContextRendererService; reason: actual and formal argument lists differ in length
🤖 Prompt for AI Agents
In
src/test/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionCodeGenerationExecutionServiceTest.java
around lines 69-70, the test constructs HyperionCodeGenerationExecutionService
with an ApplicationContext which no longer exists; update the test by creating
mocks for HyperionSolutionRepositoryService, HyperionTemplateRepositoryService,
and HyperionTestRepositoryService, remove the ApplicationContext mock/variable,
and pass the three new mocks into the service constructor in place of the
ApplicationContext. Also update any assertions that resolved strategies via the
application context to assert against the injected mocks (e.g., verify that the
appropriate strategy mock is used or returned) so the test compiles and
validates strategy resolution with the injected services.
this.chatClient = ChatClient.create(chatModel); | ||
this.templateRepository = new HyperionTemplateRepositoryService(programmingExerciseRepository, chatClient, templates, gitService); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inject the missing repository structure service
HyperionTemplateRepositoryService
now requires a HyperionProgrammingExerciseContextRendererService
in its constructor. The test currently omits this dependency, triggering the compile error surfaced by CI. Add a mock for the renderer service and pass it into the constructor:
@Mock
private GitService gitService;
+ @Mock
+ private HyperionProgrammingExerciseContextRendererService repositoryStructureService;
+
…
- this.templateRepository = new HyperionTemplateRepositoryService(programmingExerciseRepository, chatClient, templates, gitService);
+ this.templateRepository = new HyperionTemplateRepositoryService(programmingExerciseRepository, chatClient, templates, gitService, repositoryStructureService);
This will bring the test signature back in sync with the production class.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
this.chatClient = ChatClient.create(chatModel); | |
this.templateRepository = new HyperionTemplateRepositoryService(programmingExerciseRepository, chatClient, templates, gitService); | |
@Mock | |
private GitService gitService; | |
@Mock | |
private HyperionProgrammingExerciseContextRendererService repositoryStructureService; | |
… | |
@BeforeEach | |
public void setup() { | |
this.chatClient = ChatClient.create(chatModel); | |
this.templateRepository = new HyperionTemplateRepositoryService( | |
programmingExerciseRepository, | |
chatClient, | |
templates, | |
gitService, | |
repositoryStructureService | |
); | |
} |
🧰 Tools
🪛 GitHub Actions: Test
[error] 75-75: constructor HyperionTemplateRepositoryService(...) cannot be applied to given types; expected (ProgrammingExerciseRepository, ChatClient, HyperionPromptTemplateService, GitService, HyperionProgrammingExerciseContextRendererService) but found (ProgrammingExerciseRepository, ChatClient, HyperionPromptTemplateService, GitService) with missing argument(s).
🪛 GitHub Actions: CodeQL
[error] 75-75: constructor HyperionTemplateRepositoryService in class HyperionTemplateRepositoryService cannot be applied to given types; required: ProgrammingExerciseRepository,ChatClient,HyperionPromptTemplateService,GitService,HyperionProgrammingExerciseContextRendererService; found: ProgrammingExerciseRepository,ChatClient,HyperionPromptTemplateService,GitService; reason: actual and formal argument lists differ in length
🤖 Prompt for AI Agents
In
src/test/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionTemplateRepositoryTest.java
around lines 74 to 76, the test constructs HyperionTemplateRepositoryService
without the new HyperionProgrammingExerciseContextRendererService dependency;
create a mock instance of HyperionProgrammingExerciseContextRendererService in
the test setup and pass that mock as an additional constructor argument when
instantiating HyperionTemplateRepositoryService so the test signature matches
the updated production constructor.
this.chatClient = ChatClient.create(chatModel); | ||
this.testRepository = new HyperionTestRepositoryService(programmingExerciseRepository, chatClient, templates, gitService); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add the new renderer dependency to the test setup
HyperionTestRepositoryService
’s constructor now includes HyperionProgrammingExerciseContextRendererService
. Because the test doesn’t supply this argument, it fails to compile (see pipeline log). Introduce a mock for the renderer service and include it when instantiating the service:
@Mock
private GitService gitService;
+ @Mock
+ private HyperionProgrammingExerciseContextRendererService repositoryStructureService;
+
…
- this.testRepository = new HyperionTestRepositoryService(programmingExerciseRepository, chatClient, templates, gitService);
+ this.testRepository = new HyperionTestRepositoryService(programmingExerciseRepository, chatClient, templates, gitService, repositoryStructureService);
Once updated, the test will align with the production constructor.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
this.chatClient = ChatClient.create(chatModel); | |
this.testRepository = new HyperionTestRepositoryService(programmingExerciseRepository, chatClient, templates, gitService); | |
// ---- around src/test/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionTestRepositoryTest.java ---- | |
@Mock | |
private GitService gitService; | |
// add mock for the new renderer dependency | |
@Mock | |
private HyperionProgrammingExerciseContextRendererService repositoryStructureService; | |
@BeforeEach | |
void init() { | |
this.chatClient = ChatClient.create(chatModel); | |
// include the new mock when instantiating the service | |
this.testRepository = new HyperionTestRepositoryService( | |
programmingExerciseRepository, | |
chatClient, | |
templates, | |
gitService, | |
repositoryStructureService | |
); | |
} | |
// ------------------------------------------------------------------------------------------------ |
🧰 Tools
🪛 GitHub Actions: Test
[error] 62-62: constructor HyperionTestRepositoryService(...) cannot be applied to given types; expected (ProgrammingExerciseRepository, ChatClient, HyperionPromptTemplateService, GitService, HyperionProgrammingExerciseContextRendererService) but found (ProgrammingExerciseRepository, ChatClient, HyperionPromptTemplateService, GitService) with missing argument(s).
🪛 GitHub Actions: CodeQL
[error] 62-62: constructor HyperionTestRepositoryService in class HyperionTestRepositoryService cannot be applied to given types; required: ProgrammingExerciseRepository,ChatClient,HyperionPromptTemplateService,GitService,HyperionProgrammingExerciseContextRendererService; found: ProgrammingExerciseRepository,ChatClient,HyperionPromptTemplateService,GitService; reason: actual and formal argument lists differ in length
🤖 Prompt for AI Agents
In
src/test/java/de/tum/cit/aet/artemis/hyperion/service/codegeneration/HyperionTestRepositoryTest.java
around lines 61 to 63, the test instantiation of HyperionTestRepositoryService
is missing the new constructor dependency
HyperionProgrammingExerciseContextRendererService; create a mock (e.g., using
Mockito.mock(HyperionProgrammingExerciseContextRendererService.class) or @Mock)
for the renderer and pass it as an additional argument when constructing
HyperionTestRepositoryService so the test compiles and mirrors the production
constructor.
End-to-End (E2E) Test Results Summary
|
beb8b29
End-to-End (E2E) Test Results Summary
|
This PR introduces a comprehensive code generation system that automatically creates solution code, student templates, and test suites for programming exercises.
Key Components:
Technical Implementation:
Checklist
General
Server
Client
authorities
to all new routes and checked the course groups for displaying navigation elements (links, buttons).Changes affecting Programming Exercises
Motivation and Context
Beforehand, instructors had to implement the solution, template, and test repository themselves. This was error-prone and time-intensive, as instructors have many other relevant tasks besides exercise creation. The code generation supports their workflow by suggesting and concrete and tested repository structured, aligned with the programming exercise.
Steps for Testing
Review Progress
Performance Review
The code generation takes between 1 and 5 minutes, depending on the number of re-iterations. The reason is the build latency and multi-stages LLM pipeline.
Using the OpenAI GPT-5 LLM, the code generation costs approximately $ 0.05 (assuming three reiterations and an already written problem statement).
Code Review
Manual Tests
Exam Mode Test
Performance Tests
Follow-Up PRs
The three follow-ups can utilize the existing infrastructure and should be within a reasonable workload (1-3 days).
Test Coverage
Screenshots
Hyperion_Code_Generation_with_Artemis.mp4
Summary by CodeRabbit
New Features
Bug Fixes
Tests
Documentation
Chores