Skip to content

Commit acbae2a

Browse files
authored
Iris: Integrate Memiris into the course chat (#11219)
1 parent 362357a commit acbae2a

File tree

51 files changed

+990
-66
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+990
-66
lines changed

src/main/java/de/tum/cit/aet/artemis/core/domain/User.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,10 @@ public class User extends AbstractAuditingEntity implements Participant {
198198
@Column(name = "external_llm_usage_accepted")
199199
private ZonedDateTime externalLLMUsageAccepted = null;
200200

201+
@NotNull
202+
@Column(name = "memiris_enabled", nullable = false)
203+
private boolean memirisEnabled = false;
204+
201205
@OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
202206
@JsonIgnoreProperties("user")
203207
@JoinColumn(name = "learner_profile_id")
@@ -526,4 +530,12 @@ public Bytes getExternalId() {
526530
}
527531
return BytesConverter.longToBytes(this.getId());
528532
}
533+
534+
public boolean isMemirisEnabled() {
535+
return memirisEnabled;
536+
}
537+
538+
public void setMemirisEnabled(boolean memirisEnabled) {
539+
this.memirisEnabled = memirisEnabled;
540+
}
529541
}

src/main/java/de/tum/cit/aet/artemis/core/dto/UserDTO.java

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -83,19 +83,21 @@ public class UserDTO extends AuditingEntityDTO {
8383

8484
private ZonedDateTime externalLLMUsageAccepted;
8585

86+
private boolean memirisEnabled = false;
87+
8688
public UserDTO() {
8789
// Empty constructor needed for Jackson.
8890
}
8991

9092
public UserDTO(User user) {
9193
this(user.getId(), user.getLogin(), user.getName(), user.getFirstName(), user.getLastName(), user.getEmail(), user.getVisibleRegistrationNumber(), user.getActivated(),
9294
user.getImageUrl(), user.getLangKey(), user.isInternal(), user.getCreatedBy(), user.getCreatedDate(), user.getLastModifiedBy(), user.getLastModifiedDate(),
93-
user.getAuthorities(), user.getGroups(), user.getOrganizations(), user.getExternalLLMUsageAcceptedTimestamp());
95+
user.getAuthorities(), user.getGroups(), user.getOrganizations(), user.getExternalLLMUsageAcceptedTimestamp(), user.isMemirisEnabled());
9496
}
9597

9698
public UserDTO(Long id, String login, String name, String firstName, String lastName, String email, String visibleRegistrationNumber, boolean activated, String imageUrl,
9799
String langKey, boolean internal, String createdBy, Instant createdDate, String lastModifiedBy, Instant lastModifiedDate, Set<Authority> authorities,
98-
Set<String> groups, Set<Organization> organizations, ZonedDateTime externalLLMUsageAccepted) {
100+
Set<String> groups, Set<Organization> organizations, ZonedDateTime externalLLMUsageAccepted, boolean memirisEnabled) {
99101

100102
this.id = id;
101103
this.login = login;
@@ -118,6 +120,7 @@ public UserDTO(Long id, String login, String name, String firstName, String last
118120
this.groups = groups;
119121
this.organizations = organizations;
120122
this.externalLLMUsageAccepted = externalLLMUsageAccepted;
123+
this.memirisEnabled = memirisEnabled;
121124
}
122125

123126
public Long getId() {
@@ -253,13 +256,6 @@ public boolean getAskToSetupPasskey() {
253256
return askToSetupPasskey;
254257
}
255258

256-
@Override
257-
public String toString() {
258-
return "UserDTO{" + "login='" + login + '\'' + ", firstName='" + firstName + '\'' + ", lastName='" + lastName + '\'' + ", email='" + email + '\'' + ", imageUrl='"
259-
+ imageUrl + '\'' + ", activated=" + activated + ", langKey='" + langKey + '\'' + ", createdBy=" + getCreatedBy() + ", createdDate=" + getCreatedDate()
260-
+ ", lastModifiedBy='" + getLastModifiedBy() + '\'' + ", lastModifiedDate=" + getLastModifiedDate() + ", authorities=" + authorities + "}";
261-
}
262-
263259
public boolean isInternal() {
264260
return internal;
265261
}
@@ -275,4 +271,19 @@ public ZonedDateTime getExternalLLMUsageAccepted() {
275271
public void setExternalLLMUsageAccepted(ZonedDateTime externalLLMUsageAccepted) {
276272
this.externalLLMUsageAccepted = externalLLMUsageAccepted;
277273
}
274+
275+
public boolean isMemirisEnabled() {
276+
return memirisEnabled;
277+
}
278+
279+
public void setMemirisEnabled(boolean memirisEnabled) {
280+
this.memirisEnabled = memirisEnabled;
281+
}
282+
283+
@Override
284+
public String toString() {
285+
return "UserDTO{" + "login='" + login + '\'' + ", firstName='" + firstName + '\'' + ", lastName='" + lastName + '\'' + ", email='" + email + '\'' + ", imageUrl='"
286+
+ imageUrl + '\'' + ", activated=" + activated + ", langKey='" + langKey + '\'' + ", createdBy=" + getCreatedBy() + ", createdDate=" + getCreatedDate()
287+
+ ", lastModifiedBy='" + getLastModifiedBy() + '\'' + ", lastModifiedDate=" + getLastModifiedDate() + ", authorities=" + authorities + "}";
288+
}
278289
}

src/main/java/de/tum/cit/aet/artemis/core/repository/UserRepository.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -742,6 +742,15 @@ void updateUserVcsAccessToken(@Param("userId") long userId, @Param("vcsAccessTok
742742
""")
743743
void updateExternalLLMUsageAcceptedToDate(@Param("userId") long userId, @Param("acceptDatetime") ZonedDateTime acceptDatetime);
744744

745+
@Modifying
746+
@Transactional // ok because of modifying query
747+
@Query("""
748+
UPDATE User user
749+
SET user.memirisEnabled = :memirisEnabled
750+
WHERE user.id = :userId
751+
""")
752+
void updateMemirisEnabled(@Param("userId") long userId, @Param("memirisEnabled") boolean memirisEnabled);
753+
745754
@Query("""
746755
SELECT DISTINCT team.students AS student
747756
FROM Team team

src/main/java/de/tum/cit/aet/artemis/core/service/feature/Feature.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22

33
// Must be the same as FeatureToggle in feature-toggle.service.ts on the client side
44
public enum Feature {
5-
ProgrammingExercises, PlagiarismChecks, Exports, LearningPaths, Science, StandardizedCompetencies, StudentCourseAnalyticsDashboard, TutorSuggestions
5+
ProgrammingExercises, PlagiarismChecks, Exports, LearningPaths, Science, StandardizedCompetencies, StudentCourseAnalyticsDashboard, TutorSuggestions, Memiris
66
}

src/main/java/de/tum/cit/aet/artemis/core/service/feature/FeatureToggleService.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,9 @@ private void initFeatures() {
7575
features = hazelcastInstance.getMap("features");
7676

7777
// Features that are neither enabled nor disabled should be enabled by default
78-
// This ensures that all features (except the Science API and TutorSuggestions) are enabled once the system starts up
78+
// This ensures that all features (except the Science API, TutorSuggestions, and Memiris) are enabled once the system starts up
7979
for (Feature feature : Feature.values()) {
80-
if (!features.containsKey(feature) && feature != Feature.Science && feature != Feature.TutorSuggestions) {
80+
if (!features.containsKey(feature) && feature != Feature.Science && feature != Feature.TutorSuggestions && feature != Feature.Memiris) {
8181
features.put(feature, true);
8282
}
8383
}
@@ -89,6 +89,10 @@ private void initFeatures() {
8989
if (!features.containsKey(Feature.TutorSuggestions)) {
9090
features.put(Feature.TutorSuggestions, false);
9191
}
92+
93+
if (!features.containsKey(Feature.Memiris)) {
94+
features.put(Feature.Memiris, false);
95+
}
9296
}
9397

9498
/**

src/main/java/de/tum/cit/aet/artemis/core/web/AccountResource.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,4 +255,18 @@ public ResponseEntity<Void> removeProfilePicture() throws URISyntaxException {
255255
}
256256
return ResponseEntity.ok().build();
257257
}
258+
259+
/**
260+
* PUT account/enable-memiris : sets the memirisEnabled flag for the user to true or false.
261+
*
262+
* @param memirisEnabled the boolean indicating whether Memiris is enabled or not
263+
* @return the ResponseEntity with status 200 (OK)
264+
*/
265+
@PutMapping("account/enable-memiris")
266+
@EnforceAtLeastStudent
267+
public ResponseEntity<Void> setMemirisEnabled(@RequestBody boolean memirisEnabled) {
268+
User user = userRepository.getUser();
269+
userRepository.updateMemirisEnabled(user.getId(), memirisEnabled);
270+
return ResponseEntity.ok().build();
271+
}
258272
}

src/main/java/de/tum/cit/aet/artemis/iris/domain/message/IrisMessage.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,16 @@
2020

2121
import org.hibernate.annotations.Cache;
2222
import org.hibernate.annotations.CacheConcurrencyStrategy;
23+
import org.hibernate.annotations.JdbcTypeCode;
24+
import org.hibernate.type.SqlTypes;
2325

2426
import com.fasterxml.jackson.annotation.JsonIgnore;
2527
import com.fasterxml.jackson.annotation.JsonInclude;
2628
import com.fasterxml.jackson.annotation.JsonProperty;
2729

2830
import de.tum.cit.aet.artemis.core.domain.DomainObject;
2931
import de.tum.cit.aet.artemis.iris.domain.session.IrisSession;
32+
import de.tum.cit.aet.artemis.iris.dto.MemirisMemoryDTO;
3033

3134
/**
3235
* An IrisMessage represents a single message in an IrisSession.
@@ -59,6 +62,14 @@ public class IrisMessage extends DomainObject {
5962
@OneToMany(mappedBy = "message", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
6063
private List<IrisMessageContent> content = new ArrayList<>();
6164

65+
@JdbcTypeCode(SqlTypes.JSON)
66+
@Column(name = "accessed_memories", columnDefinition = "json")
67+
private List<MemirisMemoryDTO> accessedMemories = new ArrayList<>();
68+
69+
@JdbcTypeCode(SqlTypes.JSON)
70+
@Column(name = "created_memories", columnDefinition = "json")
71+
private List<MemirisMemoryDTO> createdMemories = new ArrayList<>();
72+
6273
@Transient
6374
private Integer messageDifferentiator; // is supposed to be only a part of the dto and helps the client application to differentiate messages it should add to the message store
6475

@@ -111,6 +122,22 @@ public void addContent(IrisMessageContent... content) {
111122
}
112123
}
113124

125+
public List<MemirisMemoryDTO> getAccessedMemories() {
126+
return accessedMemories;
127+
}
128+
129+
public void setAccessedMemories(List<MemirisMemoryDTO> accessedMemories) {
130+
this.accessedMemories = accessedMemories;
131+
}
132+
133+
public List<MemirisMemoryDTO> getCreatedMemories() {
134+
return createdMemories;
135+
}
136+
137+
public void setCreatedMemories(List<MemirisMemoryDTO> createdMemories) {
138+
this.createdMemories = createdMemories;
139+
}
140+
114141
@JsonProperty
115142
public Integer getMessageDifferentiator() {
116143
return messageDifferentiator;
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package de.tum.cit.aet.artemis.iris.dto;
2+
3+
import java.util.List;
4+
5+
import com.fasterxml.jackson.annotation.JsonInclude;
6+
7+
@JsonInclude(JsonInclude.Include.NON_EMPTY)
8+
public record MemirisMemoryDTO(String id, String title, String content, List<String> learnings, List<String> connections, boolean slept_on, boolean deleted) {
9+
}

src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisJobService.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,14 +103,14 @@ public String createTokenForJob(Function<String, PyrisJob> tokenToJobFunction) {
103103

104104
public String addExerciseChatJob(Long courseId, Long exerciseId, Long sessionId) {
105105
var token = generateJobIdToken();
106-
var job = new ExerciseChatJob(token, courseId, exerciseId, sessionId, null);
106+
var job = new ExerciseChatJob(token, courseId, exerciseId, sessionId, null, null, null);
107107
getPyrisJobMap().put(token, job);
108108
return token;
109109
}
110110

111-
public String addCourseChatJob(Long courseId, Long sessionId) {
111+
public String addCourseChatJob(Long courseId, Long sessionId, Long userMessageId) {
112112
var token = generateJobIdToken();
113-
var job = new CourseChatJob(token, courseId, sessionId, null);
113+
var job = new CourseChatJob(token, courseId, sessionId, null, userMessageId, null);
114114
getPyrisJobMap().put(token, job);
115115
return token;
116116
}
@@ -125,7 +125,7 @@ public String addCourseChatJob(Long courseId, Long sessionId) {
125125
*/
126126
public String addTutorSuggestionJob(Long postId, Long courseId, Long sessionId) {
127127
var token = generateJobIdToken();
128-
var job = new TutorSuggestionJob(token, postId, courseId, sessionId, null);
128+
var job = new TutorSuggestionJob(token, postId, courseId, sessionId, null, null, null);
129129
getPyrisJobMap().put(token, job);
130130
return token;
131131
}

src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisPipelineService.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
import de.tum.cit.aet.artemis.core.domain.Course;
2929
import de.tum.cit.aet.artemis.core.repository.UserRepository;
3030
import de.tum.cit.aet.artemis.core.service.course.CourseLoadService;
31+
import de.tum.cit.aet.artemis.core.service.feature.Feature;
32+
import de.tum.cit.aet.artemis.core.service.feature.FeatureToggleService;
3133
import de.tum.cit.aet.artemis.exercise.domain.Exercise;
3234
import de.tum.cit.aet.artemis.exercise.domain.participation.StudentParticipation;
3335
import de.tum.cit.aet.artemis.exercise.repository.StudentParticipationRepository;
@@ -81,12 +83,14 @@ public class PyrisPipelineService {
8183

8284
private final UserRepository userRepository;
8385

86+
private final FeatureToggleService featureToggleService;
87+
8488
@Value("${server.url}")
8589
private String artemisBaseUrl;
8690

8791
public PyrisPipelineService(PyrisConnectorService pyrisConnectorService, PyrisJobService pyrisJobService, PyrisDTOService pyrisDTOService,
8892
IrisChatWebsocketService irisChatWebsocketService, Optional<LearningMetricsApi> learningMetricsApi, StudentParticipationRepository studentParticipationRepository,
89-
UserRepository userRepository, CourseLoadService courseLoadService) {
93+
UserRepository userRepository, CourseLoadService courseLoadService, FeatureToggleService featureToggleService) {
9094
this.pyrisConnectorService = pyrisConnectorService;
9195
this.pyrisJobService = pyrisJobService;
9296
this.pyrisDTOService = pyrisDTOService;
@@ -95,6 +99,7 @@ public PyrisPipelineService(PyrisConnectorService pyrisConnectorService, PyrisJo
9599
this.studentParticipationRepository = studentParticipationRepository;
96100
this.userRepository = userRepository;
97101
this.courseLoadService = courseLoadService;
102+
this.featureToggleService = featureToggleService;
98103
}
99104

100105
/**
@@ -210,13 +215,17 @@ private <T, U> void executeCourseChatPipeline(String variant, String customInstr
210215
var api = learningMetricsApi.orElseThrow(() -> new AtlasNotPresentException(LearningMetricsApi.class));
211216

212217
// @formatter:off
218+
var lastMessageId = !session.getMessages().isEmpty() ? session.getMessages().getLast().getId() : null;
213219
executePipeline(
214220
"course-chat",
215221
variant,
216222
eventVariant,
217-
pyrisJobService.addCourseChatJob(courseId, session.getId()), executionDto -> {
223+
pyrisJobService.addCourseChatJob(courseId, session.getId(), lastMessageId), executionDto -> {
218224
var fullCourse = loadCourseWithParticipationOfStudent(courseId, studentId);
219225
var user = userRepository.findByIdElseThrow(studentId);
226+
if (!featureToggleService.isFeatureEnabled(Feature.Memiris)) {
227+
user.setMemirisEnabled(false);
228+
}
220229
return new PyrisCourseChatPipelineExecutionDTO<>(
221230
PyrisExtendedCourseDTO.of(fullCourse),
222231
api.getStudentCourseMetrics(studentId, courseId),

0 commit comments

Comments
 (0)