-
Notifications
You must be signed in to change notification settings - Fork 348
Adaptive learning: Allow instructors to use Atlas Companion for competency generation
#11482
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
Merged
+1,347
−433
Merged
Changes from all commits
Commits
Show all changes
36 commits
Select commit
Hold shift + click to select a range
8d55bd6
implemented agent's memory + migrated to gpt-4o + integrated artemis …
Yhmidi 8a9b451
fixed javadoc
Yhmidi ddb0ab1
Merge branch 'develop' into feature/agent-tools-integration
Yhmidi 7bc18c4
adapted previous tests to implementation changes
Yhmidi ce7fdca
implemented tests
Yhmidi 427d70e
coderabbit issue + style issue
Yhmidi 98b8c7c
simplified the competency_modified refreshing logic by parsing the ch…
Yhmidi 7b8385a
implemented sessionid logic to support multi-user chat : to have inde…
Yhmidi 8ffc8e2
removed the agent cached memory logic as it is inconsistent and not p…
Yhmidi 50481c2
removed debugging logs and fixed compile error
Yhmidi fda54a3
fixed client compile error
Yhmidi 17fe7be
fixed client compile error
Yhmidi 40da596
fixed client compile error
Yhmidi 9509cf6
fixed client compile error
Yhmidi 455a8d9
implemented more tests
Yhmidi c83dff8
added client tests
Yhmidi efcf434
added client tests
Yhmidi 5d36cfb
resolved issues pointed in review + improved coverage
Yhmidi 7be4264
improved coverage
Yhmidi a4fdeef
corrected client tests
Yhmidi 568a89d
made changes based on review
Yhmidi a66035e
fixed test
Yhmidi f596e9c
Merge branch 'develop' into feature/agent-tools-integration
Yhmidi 15f1feb
reverted taxonomy to using enum
Yhmidi 0085fe4
specified type for options and specs
Yhmidi 3c830a4
Merge branch 'develop' into feature/agent-tools-integration
MaximilianAnzinger e30f369
removed unnecessary autowired annotation
Yhmidi 2195bc2
added tools for atlasagenttoolservice
Yhmidi 56fb2b2
removed test repository usage
Yhmidi 802b12d
added tests for statements
Yhmidi 5ca35b2
Merge branch 'develop' into feature/agent-tools-integration
Yhmidi 73bef51
useless test removed
Yhmidi e42a223
added test for tools
Yhmidi 61b856a
added tests for prompt template
Yhmidi 1708b2f
Merge branch 'develop' into feature/agent-tools-integration
Yhmidi adfd0d4
fixed style test
Yhmidi File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
29 changes: 29 additions & 0 deletions
29
src/main/java/de/tum/cit/aet/artemis/atlas/config/AtlasAgentToolConfig.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| package de.tum.cit.aet.artemis.atlas.config; | ||
|
|
||
| import org.springframework.ai.tool.ToolCallbackProvider; | ||
| import org.springframework.ai.tool.method.MethodToolCallbackProvider; | ||
| import org.springframework.context.annotation.Bean; | ||
| import org.springframework.context.annotation.Conditional; | ||
| import org.springframework.context.annotation.Configuration; | ||
| import org.springframework.context.annotation.Lazy; | ||
|
|
||
| import de.tum.cit.aet.artemis.atlas.service.AtlasAgentToolsService; | ||
|
|
||
| @Lazy | ||
| @Configuration | ||
| @Conditional(AtlasEnabled.class) | ||
| public class AtlasAgentToolConfig { | ||
|
|
||
| /** | ||
| * Registers the tools found on the AtlasAgentToolsService bean. | ||
| * MethodToolCallbackProvider discovers @Tool-annotated methods on the provided instances | ||
| * and makes them available for Spring AI's tool calling system. | ||
| * | ||
| * @param toolsService the service containing @Tool-annotated methods | ||
| * @return ToolCallbackProvider that exposes the tools to Spring AI | ||
| */ | ||
| @Bean | ||
| public ToolCallbackProvider atlasToolCallbackProvider(AtlasAgentToolsService toolsService) { | ||
| return MethodToolCallbackProvider.builder().toolObjects(toolsService).build(); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
10 changes: 10 additions & 0 deletions
10
src/main/java/de/tum/cit/aet/artemis/atlas/service/AgentChatResult.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| package de.tum.cit.aet.artemis.atlas.service; | ||
|
|
||
| import jakarta.validation.constraints.NotNull; | ||
|
|
||
| /** | ||
| * Internal result object for Atlas Agent chat processing. | ||
| * Contains the response message and whether competencies were modified. | ||
| */ | ||
| public record AgentChatResult(@NotNull String message, boolean competenciesModified) { | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
201 changes: 201 additions & 0 deletions
201
src/main/java/de/tum/cit/aet/artemis/atlas/service/AtlasAgentToolsService.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,201 @@ | ||
| package de.tum.cit.aet.artemis.atlas.service; | ||
|
|
||
| import java.util.LinkedHashMap; | ||
| import java.util.Map; | ||
| import java.util.Optional; | ||
| import java.util.Set; | ||
|
|
||
| import org.springframework.ai.tool.annotation.Tool; | ||
| import org.springframework.ai.tool.annotation.ToolParam; | ||
| import org.springframework.context.annotation.Conditional; | ||
| import org.springframework.context.annotation.Lazy; | ||
| import org.springframework.stereotype.Service; | ||
| import org.springframework.web.context.annotation.RequestScope; | ||
|
|
||
| import com.fasterxml.jackson.core.JsonProcessingException; | ||
| import com.fasterxml.jackson.databind.ObjectMapper; | ||
|
|
||
| import de.tum.cit.aet.artemis.atlas.config.AtlasEnabled; | ||
| import de.tum.cit.aet.artemis.atlas.domain.competency.Competency; | ||
| import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyTaxonomy; | ||
| import de.tum.cit.aet.artemis.atlas.repository.CompetencyRepository; | ||
| import de.tum.cit.aet.artemis.core.domain.Course; | ||
| import de.tum.cit.aet.artemis.core.repository.CourseRepository; | ||
| import de.tum.cit.aet.artemis.exercise.domain.Exercise; | ||
| import de.tum.cit.aet.artemis.exercise.repository.ExerciseRepository; | ||
|
|
||
| /** | ||
| * Service providing tools for the Atlas Agent using Spring AI's @Tool annotation. | ||
| * Request-scoped to track tool calls per HTTP request. | ||
| */ | ||
| @RequestScope | ||
| @Lazy | ||
| @Service | ||
| @Conditional(AtlasEnabled.class) | ||
| public class AtlasAgentToolsService { | ||
Yhmidi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| private final ObjectMapper objectMapper; | ||
|
|
||
| private final CompetencyRepository competencyRepository; | ||
|
|
||
| private final CourseRepository courseRepository; | ||
|
|
||
| private final ExerciseRepository exerciseRepository; | ||
|
|
||
| // Track which modification tools were called during this request | ||
| private boolean competencyCreated = false; | ||
|
|
||
| public AtlasAgentToolsService(ObjectMapper objectMapper, CompetencyRepository competencyRepository, CourseRepository courseRepository, ExerciseRepository exerciseRepository) { | ||
| this.objectMapper = objectMapper; | ||
| this.competencyRepository = competencyRepository; | ||
| this.courseRepository = courseRepository; | ||
| this.exerciseRepository = exerciseRepository; | ||
| } | ||
|
|
||
| /** | ||
| * Tool for getting course competencies. | ||
| * | ||
| * @param courseId the course ID | ||
| * @return JSON representation of competencies | ||
| */ | ||
| @Tool(description = "Get all competencies for a course") | ||
| public String getCourseCompetencies(@ToolParam(description = "the ID of the course") Long courseId) { | ||
| Optional<Course> courseOptional = courseRepository.findById(courseId); | ||
| if (courseOptional.isEmpty()) { | ||
| return toJson(Map.of("error", "Course not found with ID: " + courseId)); | ||
| } | ||
|
|
||
| Set<Competency> competencies = competencyRepository.findAllByCourseId(courseId); | ||
|
|
||
| var competencyList = competencies.stream().map(competency -> { | ||
| Map<String, Object> competencyData = new LinkedHashMap<>(); | ||
| competencyData.put("id", competency.getId()); | ||
| competencyData.put("title", competency.getTitle()); | ||
| competencyData.put("description", competency.getDescription()); | ||
| competencyData.put("taxonomy", competency.getTaxonomy() != null ? competency.getTaxonomy().toString() : ""); | ||
| return competencyData; | ||
| }).toList(); | ||
|
|
||
| Map<String, Object> response = new LinkedHashMap<>(); | ||
| response.put("courseId", courseId); | ||
| response.put("competencies", competencyList); | ||
|
|
||
| return toJson(response); | ||
| } | ||
Yhmidi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| /** | ||
| * Tool for creating a new competency in a course. | ||
| * | ||
| * @param courseId the course ID | ||
| * @param title the competency title | ||
| * @param description the competency description | ||
| * @param taxonomyLevel the taxonomy level (REMEMBER, UNDERSTAND, APPLY, ANALYZE, EVALUATE, CREATE) | ||
| * @return JSON response indicating success or error | ||
| */ | ||
| @Tool(description = "Create a new competency for a course") | ||
| public String createCompetency(@ToolParam(description = "the ID of the course") Long courseId, @ToolParam(description = "the title of the competency") String title, | ||
| @ToolParam(description = "the description of the competency") String description, | ||
| @ToolParam(description = "the taxonomy level (REMEMBER, UNDERSTAND, APPLY, ANALYZE, EVALUATE, CREATE)") CompetencyTaxonomy taxonomyLevel) { | ||
| try { | ||
marlonnienaber marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| Optional<Course> courseOptional = courseRepository.findById(courseId); | ||
| if (courseOptional.isEmpty()) { | ||
| return toJson(Map.of("error", "Course not found with ID: " + courseId)); | ||
| } | ||
|
|
||
| Course course = courseOptional.get(); | ||
| Competency competency = new Competency(); | ||
| competency.setTitle(title); | ||
| competency.setDescription(description); | ||
| competency.setCourse(course); | ||
| competency.setTaxonomy(taxonomyLevel); | ||
|
|
||
| Competency savedCompetency = competencyRepository.save(competency); | ||
|
|
||
| this.competencyCreated = true; | ||
| Map<String, Object> competencyData = new LinkedHashMap<>(); | ||
| competencyData.put("id", savedCompetency.getId()); | ||
| competencyData.put("title", savedCompetency.getTitle()); | ||
| competencyData.put("description", savedCompetency.getDescription()); | ||
| competencyData.put("taxonomy", savedCompetency.getTaxonomy() != null ? savedCompetency.getTaxonomy().toString() : ""); | ||
| competencyData.put("courseId", courseId); | ||
|
|
||
| Map<String, Object> response = new LinkedHashMap<>(); | ||
| response.put("success", true); | ||
| response.put("competency", competencyData); | ||
|
|
||
| return toJson(response); | ||
| } | ||
| catch (Exception e) { | ||
| return toJson(Map.of("error", "Failed to create competency: " + e.getMessage())); | ||
| } | ||
| } | ||
Yhmidi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| /** | ||
| * Tool for getting course description. | ||
| * | ||
| * @param courseId the course ID | ||
| * @return the course description or empty string if not found | ||
| */ | ||
| @Tool(description = "Get the description of a course") | ||
| public String getCourseDescription(@ToolParam(description = "the ID of the course") Long courseId) { | ||
| return courseRepository.findById(courseId).map(Course::getDescription).orElse(""); | ||
| } | ||
Yhmidi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| /** | ||
| * Tool for getting exercises for a course. | ||
| * | ||
| * @param courseId the course ID | ||
| * @return JSON representation of exercises | ||
| */ | ||
| @Tool(description = "List exercises for a course") | ||
| public String getExercisesListed(@ToolParam(description = "the ID of the course") Long courseId) { | ||
| Optional<Course> courseOptional = courseRepository.findById(courseId); | ||
| if (courseOptional.isEmpty()) { | ||
| return toJson(Map.of("error", "Course not found with ID: " + courseId)); | ||
| } | ||
|
|
||
| Set<Exercise> exercises = exerciseRepository.findByCourseIds(Set.of(courseId)); | ||
|
|
||
| var exerciseList = exercises.stream().map(exercise -> { | ||
| Map<String, Object> exerciseData = new LinkedHashMap<>(); | ||
| exerciseData.put("id", exercise.getId()); | ||
| exerciseData.put("title", exercise.getTitle()); | ||
| exerciseData.put("type", exercise.getClass().getSimpleName()); | ||
| exerciseData.put("maxPoints", exercise.getMaxPoints() != null ? exercise.getMaxPoints() : 0); | ||
| exerciseData.put("releaseDate", exercise.getReleaseDate() != null ? exercise.getReleaseDate().toString() : ""); | ||
| exerciseData.put("dueDate", exercise.getDueDate() != null ? exercise.getDueDate().toString() : ""); | ||
| return exerciseData; | ||
| }).toList(); | ||
|
|
||
| Map<String, Object> response = new LinkedHashMap<>(); | ||
| response.put("courseId", courseId); | ||
| response.put("exercises", exerciseList); | ||
|
|
||
| return toJson(response); | ||
| } | ||
Yhmidi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| /** | ||
| * Check if any competency was created during this request. | ||
| * | ||
| * @return true if createCompetency was called during this request | ||
| */ | ||
| public boolean wasCompetencyCreated() { | ||
| return this.competencyCreated; | ||
| } | ||
|
|
||
| /** | ||
| * Convert object to JSON using Jackson ObjectMapper. | ||
| * | ||
| * @param object the object to serialize | ||
| * @return JSON string representation | ||
| */ | ||
| private String toJson(Object object) { | ||
| try { | ||
| return objectMapper.writeValueAsString(object); | ||
| } | ||
| catch (JsonProcessingException e) { | ||
| return "{\"error\": \"Failed to serialize response\"}"; | ||
| } | ||
| } | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.