From f3f6da2bc67bcca9187ff4ade163aa970cc19cea Mon Sep 17 00:00:00 2001 From: Timor Morrien Date: Sun, 25 Aug 2024 23:56:55 +0200 Subject: [PATCH 1/4] Iris Settings V3 --- docs/dev/guidelines/database.rst | 2 +- .../artemis/domain/iris/IrisTemplate.java | 65 -------- .../iris/session/IrisHestiaSession.java | 37 ----- .../domain/iris/session/IrisSession.java | 3 +- .../iris/settings/IrisChatSubSettings.java | 19 --- .../IrisCompetencyGenerationSubSettings.java | 20 --- .../iris/settings/IrisCourseSettings.java | 19 --- .../iris/settings/IrisExerciseSettings.java | 15 -- .../iris/settings/IrisGlobalSettings.java | 84 ---------- .../iris/settings/IrisHestiaSubSettings.java | 35 ---- ...tConverter.java => IrisListConverter.java} | 2 +- .../domain/iris/settings/IrisSettings.java | 6 - .../domain/iris/settings/IrisSubSettings.java | 29 ++-- .../iris/settings/IrisSubSettingsType.java | 2 +- .../iris/IrisHestiaSessionRepository.java | 35 ---- .../iris/IrisSettingsRepository.java | 2 - .../iris/IrisTemplateRepository.java | 11 -- .../pyris/PyrisConnectorService.java | 12 +- ...yrisModelDTO.java => PyrisVariantDTO.java} | 2 +- .../service/hestia/CodeHintService.java | 20 +-- .../iris/IrisDefaultTemplateService.java | 76 --------- .../service/iris/IrisSessionService.java | 8 +- .../dto/IrisCombinedChatSubSettingsDTO.java | 6 +- ...nedCompetencyGenerationSubSettingsDTO.java | 5 +- .../dto/IrisCombinedHestiaSubSettingsDTO.java | 13 -- .../iris/dto/IrisCombinedSettingsDTO.java | 2 +- .../session/IrisCourseChatSessionService.java | 3 +- .../IrisExerciseChatSessionService.java | 5 +- .../session/IrisHestiaSessionService.java | 114 -------------- .../iris/settings/IrisSettingsService.java | 96 +---------- .../iris/settings/IrisSubSettingsService.java | 149 +++++------------- .../web/rest/hestia/CodeHintResource.java | 36 ----- .../web/rest/iris/IrisModelsResource.java | 47 ------ .../web/rest/iris/IrisVariantsResource.java | 56 +++++++ .../changelog/20240825191919_changelog.xml | 81 ++++++++++ .../resources/config/liquibase/master.xml | 1 + .../manage/detail/course-detail.component.ts | 2 - .../iris/settings/iris-settings.model.ts | 15 +- .../iris/settings/iris-sub-settings.model.ts | 13 +- .../entities/iris/settings/iris-template.ts | 6 - .../{iris-model.ts => iris-variant.ts} | 2 +- .../exercise-hint-update.component.html | 15 -- src/main/webapp/app/iris/iris.module.ts | 2 - ...-common-sub-settings-update.component.html | 53 +++++-- ...is-common-sub-settings-update.component.ts | 73 +++++---- ...-autoupdate-settings-update.component.html | 20 --- ...al-autoupdate-settings-update.component.ts | 15 -- .../iris-settings-update.component.html | 44 ++---- .../iris-settings-update.component.ts | 19 +-- .../settings/shared/iris-enabled.component.ts | 3 - .../settings/shared/iris-settings.service.ts | 11 +- src/main/webapp/i18n/de/iris.json | 24 +-- src/main/webapp/i18n/en/iris.json | 24 +-- .../connector/IrisRequestMockProvider.java | 19 +-- .../iris/AbstractIrisIntegrationTest.java | 20 +-- .../iris/PyrisConnectorServiceTest.java | 21 +-- .../settings/IrisSettingsIntegrationTest.java | 43 ++--- ...mmon-sub-settings-update.component.spec.ts | 91 ++++++----- ...s-course-settings-update.component.spec.ts | 9 +- .../settings/iris-enabled.component.spec.ts | 29 ++-- ...exercise-settings-update.component.spec.ts | 6 - ...s-global-settings-update.component.spec.ts | 8 +- .../iris-settings-update-component.spec.ts | 17 +- .../component/iris/settings/mock-settings.ts | 26 +-- 64 files changed, 449 insertions(+), 1299 deletions(-) delete mode 100644 src/main/java/de/tum/in/www1/artemis/domain/iris/IrisTemplate.java delete mode 100644 src/main/java/de/tum/in/www1/artemis/domain/iris/session/IrisHestiaSession.java delete mode 100644 src/main/java/de/tum/in/www1/artemis/domain/iris/settings/IrisHestiaSubSettings.java rename src/main/java/de/tum/in/www1/artemis/domain/iris/settings/{IrisModelListConverter.java => IrisListConverter.java} (88%) delete mode 100644 src/main/java/de/tum/in/www1/artemis/repository/iris/IrisHestiaSessionRepository.java delete mode 100644 src/main/java/de/tum/in/www1/artemis/repository/iris/IrisTemplateRepository.java rename src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/dto/{PyrisModelDTO.java => PyrisVariantDTO.java} (68%) delete mode 100644 src/main/java/de/tum/in/www1/artemis/service/iris/IrisDefaultTemplateService.java delete mode 100644 src/main/java/de/tum/in/www1/artemis/service/iris/dto/IrisCombinedHestiaSubSettingsDTO.java delete mode 100644 src/main/java/de/tum/in/www1/artemis/service/iris/session/IrisHestiaSessionService.java delete mode 100644 src/main/java/de/tum/in/www1/artemis/web/rest/iris/IrisModelsResource.java create mode 100644 src/main/java/de/tum/in/www1/artemis/web/rest/iris/IrisVariantsResource.java create mode 100644 src/main/resources/config/liquibase/changelog/20240825191919_changelog.xml delete mode 100644 src/main/webapp/app/entities/iris/settings/iris-template.ts rename src/main/webapp/app/entities/iris/settings/{iris-model.ts => iris-variant.ts} (69%) delete mode 100644 src/main/webapp/app/iris/settings/iris-settings-update/iris-global-autoupdate-settings-update/iris-global-autoupdate-settings-update.component.html delete mode 100644 src/main/webapp/app/iris/settings/iris-settings-update/iris-global-autoupdate-settings-update/iris-global-autoupdate-settings-update.component.ts diff --git a/docs/dev/guidelines/database.rst b/docs/dev/guidelines/database.rst index 0c7119159a9d..e7fb461a676b 100644 --- a/docs/dev/guidelines/database.rst +++ b/docs/dev/guidelines/database.rst @@ -295,7 +295,7 @@ Best Practices // IrisSubSettings.java @Column(name = "allowed_models") @Convert(converter = IrisModelListConverter.class) - private TreeSet allowedModels = new TreeSet<>(); + private TreeSet allowedVariants = new TreeSet<>(); * **Ordered Collection with duplicates**: When you want to order the collection of (potentially duplicated) objects of the relationship, then always use a ``List``. It is important to note here that there is no inherent order in a database table. One could argue that you can use the ``id`` field for the ordering, but there are edge cases where this can lead to problems. Therefore, for an ordered collection with duplicates, **always** annotate it with ``@OrderColumn``. An order column indicates to Hibernate that we want to order our collection based on a specific column of our data table. By default, the column name it expects is *tablenameS\_order*. For ordered collections, we also recommend that you annotate them with ``cascade = CascadeType.ALL`` and ``orphanRemoval = true``. E.g.: diff --git a/src/main/java/de/tum/in/www1/artemis/domain/iris/IrisTemplate.java b/src/main/java/de/tum/in/www1/artemis/domain/iris/IrisTemplate.java deleted file mode 100644 index bcd06908178d..000000000000 --- a/src/main/java/de/tum/in/www1/artemis/domain/iris/IrisTemplate.java +++ /dev/null @@ -1,65 +0,0 @@ -package de.tum.in.www1.artemis.domain.iris; - -import java.util.Objects; - -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Table; - -import org.hibernate.annotations.Cache; -import org.hibernate.annotations.CacheConcurrencyStrategy; - -import com.fasterxml.jackson.annotation.JsonInclude; - -import de.tum.in.www1.artemis.domain.DomainObject; - -/** - * An IrisTemplate represents a handlebars template for Iris. - * It is sent to the Iris Python server to generate a response. - */ -@Entity -@Table(name = "iris_template") -@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) -@JsonInclude(JsonInclude.Include.NON_EMPTY) -public class IrisTemplate extends DomainObject { - - @Column(name = "content", columnDefinition = "LONGTEXT") - private String content; - - /** - * Empty constructor required for Hibernate and Jackson. - */ - public IrisTemplate() { - } - - /** - * Create a new IrisTemplate with content. - * - * @param content the content of the template - */ - public IrisTemplate(String content) { - this.content = content; - } - - public String getContent() { - return content; - } - - public void setContent(String template) { - this.content = template; - } - - @Override - public boolean equals(Object other) { - if (!super.equals(other)) { - return false; - } - IrisTemplate template = (IrisTemplate) other; - return Objects.equals(content, template.content); - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), content); - } -} diff --git a/src/main/java/de/tum/in/www1/artemis/domain/iris/session/IrisHestiaSession.java b/src/main/java/de/tum/in/www1/artemis/domain/iris/session/IrisHestiaSession.java deleted file mode 100644 index 71a6005062b1..000000000000 --- a/src/main/java/de/tum/in/www1/artemis/domain/iris/session/IrisHestiaSession.java +++ /dev/null @@ -1,37 +0,0 @@ -package de.tum.in.www1.artemis.domain.iris.session; - -import jakarta.persistence.DiscriminatorValue; -import jakarta.persistence.Entity; -import jakarta.persistence.ManyToOne; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonInclude; - -import de.tum.in.www1.artemis.domain.hestia.CodeHint; - -/** - * An Iris session for a hestia code hint. - * Currently used to generate descriptions for code hints. - */ -@Entity -@DiscriminatorValue("HESTIA") -@JsonInclude(JsonInclude.Include.NON_EMPTY) -public class IrisHestiaSession extends IrisSession { - - @ManyToOne - @JsonIgnore - private CodeHint codeHint; - - public CodeHint getCodeHint() { - return codeHint; - } - - public void setCodeHint(CodeHint codeHint) { - this.codeHint = codeHint; - } - - @Override - public String toString() { - return "IrisHestiaSession{" + "id=" + getId() + ", codeHint=" + (codeHint == null ? "null" : codeHint.getId()) + '}'; - } -} diff --git a/src/main/java/de/tum/in/www1/artemis/domain/iris/session/IrisSession.java b/src/main/java/de/tum/in/www1/artemis/domain/iris/session/IrisSession.java index 688d3a6b2b62..0ee26ae6dd1f 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/iris/session/IrisSession.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/iris/session/IrisSession.java @@ -28,7 +28,7 @@ /** * An IrisSession represents a list of messages of Artemis, a user, and an LLM. - * See {@link IrisExerciseChatSession} and {@link IrisHestiaSession} for concrete implementations. + * See {@link IrisExerciseChatSession} and {@link IrisCourseChatSession} for concrete implementations. */ @Entity @Table(name = "iris_session") @@ -40,7 +40,6 @@ @JsonSubTypes({ @JsonSubTypes.Type(value = IrisExerciseChatSession.class, name = "chat"), // TODO: Legacy. Should ideally be "exercise_chat" @JsonSubTypes.Type(value = IrisCourseChatSession.class, name = "course_chat"), - @JsonSubTypes.Type(value = IrisHestiaSession.class, name = "hestia"), }) // @formatter:on @JsonInclude(JsonInclude.Include.NON_EMPTY) diff --git a/src/main/java/de/tum/in/www1/artemis/domain/iris/settings/IrisChatSubSettings.java b/src/main/java/de/tum/in/www1/artemis/domain/iris/settings/IrisChatSubSettings.java index 82684b669684..7df1c5f09965 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/iris/settings/IrisChatSubSettings.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/iris/settings/IrisChatSubSettings.java @@ -1,31 +1,21 @@ package de.tum.in.www1.artemis.domain.iris.settings; import jakarta.annotation.Nullable; -import jakarta.persistence.CascadeType; import jakarta.persistence.Column; import jakarta.persistence.DiscriminatorValue; import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.OneToOne; import com.fasterxml.jackson.annotation.JsonInclude; -import de.tum.in.www1.artemis.domain.iris.IrisTemplate; - /** * An {@link IrisSubSettings} implementation for chat settings. * Chat settings notably provide settings for the rate limit. - * Chat settings provide a single {@link IrisTemplate} for the chat messages. */ @Entity @DiscriminatorValue("CHAT") @JsonInclude(JsonInclude.Include.NON_EMPTY) public class IrisChatSubSettings extends IrisSubSettings { - @Nullable - @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER) - private IrisTemplate template; - @Nullable @Column(name = "rate_limit") private Integer rateLimit; @@ -34,15 +24,6 @@ public class IrisChatSubSettings extends IrisSubSettings { @Column(name = "rate_limit_timeframe_hours") private Integer rateLimitTimeframeHours; - @Nullable - public IrisTemplate getTemplate() { - return template; - } - - public void setTemplate(@Nullable IrisTemplate template) { - this.template = template; - } - @Nullable public Integer getRateLimit() { return rateLimit; diff --git a/src/main/java/de/tum/in/www1/artemis/domain/iris/settings/IrisCompetencyGenerationSubSettings.java b/src/main/java/de/tum/in/www1/artemis/domain/iris/settings/IrisCompetencyGenerationSubSettings.java index 4dfba654ae88..d5eaf4876475 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/iris/settings/IrisCompetencyGenerationSubSettings.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/iris/settings/IrisCompetencyGenerationSubSettings.java @@ -1,36 +1,16 @@ package de.tum.in.www1.artemis.domain.iris.settings; -import jakarta.annotation.Nullable; -import jakarta.persistence.CascadeType; import jakarta.persistence.DiscriminatorValue; import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.OneToOne; import com.fasterxml.jackson.annotation.JsonInclude; -import de.tum.in.www1.artemis.domain.iris.IrisTemplate; - /** * An {@link IrisSubSettings} implementation for the settings for competency generation. - * CompetencyGeneration settings provide a single {@link IrisTemplate} */ @Entity @DiscriminatorValue("COMPETENCY_GENERATION") @JsonInclude(JsonInclude.Include.NON_EMPTY) public class IrisCompetencyGenerationSubSettings extends IrisSubSettings { - @Nullable - @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER) - private IrisTemplate template; - - @Nullable - public IrisTemplate getTemplate() { - return template; - } - - public void setTemplate(@Nullable IrisTemplate template) { - this.template = template; - } - } diff --git a/src/main/java/de/tum/in/www1/artemis/domain/iris/settings/IrisCourseSettings.java b/src/main/java/de/tum/in/www1/artemis/domain/iris/settings/IrisCourseSettings.java index f4b28bbd6ae6..0ea0a9a4c696 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/iris/settings/IrisCourseSettings.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/iris/settings/IrisCourseSettings.java @@ -32,19 +32,10 @@ public class IrisCourseSettings extends IrisSettings { @JoinColumn(name = "iris_lecture_ingestion_settings_id") private IrisLectureIngestionSubSettings irisLectureIngestionSettings; - @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER) - @JoinColumn(name = "iris_hestia_settings_id") - private IrisHestiaSubSettings irisHestiaSettings; - @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, optional = false) @JoinColumn(name = "iris_competency_generation_settings_id") private IrisCompetencyGenerationSubSettings irisCompetencyGenerationSettings; - @Override - public boolean isValid() { - return course != null; - } - public Course getCourse() { return course; } @@ -73,16 +64,6 @@ public void setIrisChatSettings(IrisChatSubSettings irisChatSettings) { this.irisChatSettings = irisChatSettings; } - @Override - public IrisHestiaSubSettings getIrisHestiaSettings() { - return irisHestiaSettings; - } - - @Override - public void setIrisHestiaSettings(IrisHestiaSubSettings irisHestiaSettings) { - this.irisHestiaSettings = irisHestiaSettings; - } - @Override public IrisCompetencyGenerationSubSettings getIrisCompetencyGenerationSettings() { return irisCompetencyGenerationSettings; diff --git a/src/main/java/de/tum/in/www1/artemis/domain/iris/settings/IrisExerciseSettings.java b/src/main/java/de/tum/in/www1/artemis/domain/iris/settings/IrisExerciseSettings.java index 404ef246b7ed..e7ca766e4ed7 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/iris/settings/IrisExerciseSettings.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/iris/settings/IrisExerciseSettings.java @@ -28,11 +28,6 @@ public class IrisExerciseSettings extends IrisSettings { @JoinColumn(name = "iris_chat_settings_id") private IrisChatSubSettings irisChatSettings; - @Override - public boolean isValid() { - return exercise != null; - } - public Exercise getExercise() { return exercise; } @@ -60,16 +55,6 @@ public void setIrisChatSettings(IrisChatSubSettings irisChatSettings) { this.irisChatSettings = irisChatSettings; } - @Override - public IrisHestiaSubSettings getIrisHestiaSettings() { - return null; - } - - @Override - public void setIrisHestiaSettings(IrisHestiaSubSettings irisHestiaSettings) { - - } - @Override public IrisCompetencyGenerationSubSettings getIrisCompetencyGenerationSettings() { return null; diff --git a/src/main/java/de/tum/in/www1/artemis/domain/iris/settings/IrisGlobalSettings.java b/src/main/java/de/tum/in/www1/artemis/domain/iris/settings/IrisGlobalSettings.java index 1a69d221cfe3..8c073bb7ee5c 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/iris/settings/IrisGlobalSettings.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/iris/settings/IrisGlobalSettings.java @@ -1,15 +1,12 @@ package de.tum.in.www1.artemis.domain.iris.settings; import jakarta.persistence.CascadeType; -import jakarta.persistence.Column; import jakarta.persistence.DiscriminatorValue; import jakarta.persistence.Entity; import jakarta.persistence.FetchType; import jakarta.persistence.JoinColumn; import jakarta.persistence.OneToOne; -import org.hibernate.Hibernate; - import com.fasterxml.jackson.annotation.JsonInclude; /** @@ -22,21 +19,6 @@ @JsonInclude(JsonInclude.Include.NON_EMPTY) public class IrisGlobalSettings extends IrisSettings { - @Column(name = "current_version") - private int currentVersion; - - @Column(name = "enable_auto_update_chat") - private boolean enableAutoUpdateChat; - - @Column(name = "enable_auto_update_hestia") - private boolean enableAutoUpdateHestia; - - @Column(name = "enable_auto_update_lecture_ingestion") - private boolean enableAutoUpdateLectureIngestion; - - @Column(name = "enable_auto_update_competency_generation") - private boolean enableAutoUpdateCompetencyGeneration; - @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, optional = false) @JoinColumn(name = "iris_chat_settings_id") private IrisChatSubSettings irisChatSettings; @@ -45,66 +27,10 @@ public class IrisGlobalSettings extends IrisSettings { @JoinColumn(name = "iris_lecture_ingestion_settings_id") private IrisLectureIngestionSubSettings irisLectureIngestionSettings; - @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, optional = false) - @JoinColumn(name = "iris_hestia_settings_id") - private IrisHestiaSubSettings irisHestiaSettings; - @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, optional = false) @JoinColumn(name = "iris_competency_generation_settings_id") private IrisCompetencyGenerationSubSettings irisCompetencyGenerationSettings; - @Override - public boolean isValid() { - var chatSettingsValid = !Hibernate.isInitialized(irisChatSettings) || irisChatSettings == null - || (irisChatSettings.getTemplate() != null && irisChatSettings.getTemplate().getContent() != null && !irisChatSettings.getTemplate().getContent().isEmpty()); - var hestiaSettingsValid = !Hibernate.isInitialized(irisHestiaSettings) || irisHestiaSettings == null - || (irisHestiaSettings.getTemplate() != null && irisHestiaSettings.getTemplate().getContent() != null && !irisHestiaSettings.getTemplate().getContent().isEmpty()); - var competencyGenerationSettingsValid = !Hibernate.isInitialized(irisCompetencyGenerationSettings) || irisCompetencyGenerationSettings == null - || (irisCompetencyGenerationSettings.getTemplate() != null && irisCompetencyGenerationSettings.getTemplate().getContent() != null - && !irisCompetencyGenerationSettings.getTemplate().getContent().isEmpty()); - return chatSettingsValid && hestiaSettingsValid && competencyGenerationSettingsValid; - } - - public int getCurrentVersion() { - return currentVersion; - } - - public void setCurrentVersion(int currentVersion) { - this.currentVersion = currentVersion; - } - - public boolean isEnableAutoUpdateChat() { - return enableAutoUpdateChat; - } - - public void setEnableAutoUpdateChat(boolean enableAutoUpdateChat) { - this.enableAutoUpdateChat = enableAutoUpdateChat; - } - - public boolean isEnableAutoUpdateLectureIngestion() { - return enableAutoUpdateLectureIngestion; - } - - public void setEnableAutoUpdateLectureIngestion(boolean enableAutoUpdateLectureIngestion) { - this.enableAutoUpdateLectureIngestion = enableAutoUpdateLectureIngestion; - } - - public boolean isEnableAutoUpdateHestia() { - return enableAutoUpdateHestia; - } - - public void setEnableAutoUpdateHestia(boolean enableAutoUpdateHestia) { - this.enableAutoUpdateHestia = enableAutoUpdateHestia; - } - - public boolean isEnableAutoUpdateCompetencyGeneration() { - return enableAutoUpdateCompetencyGeneration; - } - - public void setEnableAutoUpdateCompetencyGeneration(boolean enableAutoUpdateCompetencyGeneration) { - this.enableAutoUpdateCompetencyGeneration = enableAutoUpdateCompetencyGeneration; - } - @Override public IrisLectureIngestionSubSettings getIrisLectureIngestionSettings() { return irisLectureIngestionSettings; @@ -125,16 +51,6 @@ public void setIrisChatSettings(IrisChatSubSettings irisChatSettings) { this.irisChatSettings = irisChatSettings; } - @Override - public IrisHestiaSubSettings getIrisHestiaSettings() { - return irisHestiaSettings; - } - - @Override - public void setIrisHestiaSettings(IrisHestiaSubSettings irisHestiaSettings) { - this.irisHestiaSettings = irisHestiaSettings; - } - @Override public IrisCompetencyGenerationSubSettings getIrisCompetencyGenerationSettings() { return irisCompetencyGenerationSettings; diff --git a/src/main/java/de/tum/in/www1/artemis/domain/iris/settings/IrisHestiaSubSettings.java b/src/main/java/de/tum/in/www1/artemis/domain/iris/settings/IrisHestiaSubSettings.java deleted file mode 100644 index ceb69f3de6d8..000000000000 --- a/src/main/java/de/tum/in/www1/artemis/domain/iris/settings/IrisHestiaSubSettings.java +++ /dev/null @@ -1,35 +0,0 @@ -package de.tum.in.www1.artemis.domain.iris.settings; - -import jakarta.annotation.Nullable; -import jakarta.persistence.CascadeType; -import jakarta.persistence.DiscriminatorValue; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.OneToOne; - -import com.fasterxml.jackson.annotation.JsonInclude; - -import de.tum.in.www1.artemis.domain.iris.IrisTemplate; - -/** - * An {@link IrisSubSettings} implementation for the Hestia integration settings. - * Hestia settings provide a single {@link IrisTemplate} for the hestia code hint generation requests. - */ -@Entity -@DiscriminatorValue("HESTIA") -@JsonInclude(JsonInclude.Include.NON_EMPTY) -public class IrisHestiaSubSettings extends IrisSubSettings { - - @Nullable - @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER) - private IrisTemplate template; - - @Nullable - public IrisTemplate getTemplate() { - return template; - } - - public void setTemplate(@Nullable IrisTemplate template) { - this.template = template; - } -} diff --git a/src/main/java/de/tum/in/www1/artemis/domain/iris/settings/IrisModelListConverter.java b/src/main/java/de/tum/in/www1/artemis/domain/iris/settings/IrisListConverter.java similarity index 88% rename from src/main/java/de/tum/in/www1/artemis/domain/iris/settings/IrisModelListConverter.java rename to src/main/java/de/tum/in/www1/artemis/domain/iris/settings/IrisListConverter.java index c94c8810bca0..780f94fe2446 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/iris/settings/IrisModelListConverter.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/iris/settings/IrisListConverter.java @@ -9,7 +9,7 @@ import jakarta.persistence.Converter; @Converter -public class IrisModelListConverter implements AttributeConverter, String> { +public class IrisListConverter implements AttributeConverter, String> { @Override public String convertToDatabaseColumn(SortedSet type) { diff --git a/src/main/java/de/tum/in/www1/artemis/domain/iris/settings/IrisSettings.java b/src/main/java/de/tum/in/www1/artemis/domain/iris/settings/IrisSettings.java index 6a5d82b4d4c4..48710d6dcb96 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/iris/settings/IrisSettings.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/iris/settings/IrisSettings.java @@ -49,13 +49,7 @@ public abstract class IrisSettings extends DomainObject { public abstract void setIrisLectureIngestionSettings(IrisLectureIngestionSubSettings irisLectureIngestionSettings); - public abstract IrisHestiaSubSettings getIrisHestiaSettings(); - - public abstract void setIrisHestiaSettings(IrisHestiaSubSettings irisHestiaSettings); - public abstract IrisCompetencyGenerationSubSettings getIrisCompetencyGenerationSettings(); public abstract void setIrisCompetencyGenerationSettings(IrisCompetencyGenerationSubSettings irisCompetencyGenerationSubSettings); - - public abstract boolean isValid(); } diff --git a/src/main/java/de/tum/in/www1/artemis/domain/iris/settings/IrisSubSettings.java b/src/main/java/de/tum/in/www1/artemis/domain/iris/settings/IrisSubSettings.java index 50598cc4f8b3..d04bfa088956 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/iris/settings/IrisSubSettings.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/iris/settings/IrisSubSettings.java @@ -26,7 +26,6 @@ * IrisSubSettings is an abstract super class for the specific sub settings types. * Sub Settings are settings for a specific feature of Iris. * {@link IrisChatSubSettings} are used to specify settings for the chat feature. - * {@link IrisHestiaSubSettings} are used to specify settings for the Hestia integration. * {@link IrisCompetencyGenerationSubSettings} are used to specify settings for the competency generation feature. *

* Also see {@link de.tum.in.www1.artemis.service.iris.settings.IrisSettingsService} for more information. @@ -41,7 +40,6 @@ @JsonSubTypes({ @JsonSubTypes.Type(value = IrisChatSubSettings.class, name = "chat"), @JsonSubTypes.Type(value = IrisLectureIngestionSubSettings.class, name = "lecture-ingestion"), - @JsonSubTypes.Type(value = IrisHestiaSubSettings.class, name = "hestia"), @JsonSubTypes.Type(value = IrisCompetencyGenerationSubSettings.class, name = "competency-generation") }) // @formatter:on @@ -51,13 +49,12 @@ public abstract class IrisSubSettings extends DomainObject { @Column(name = "enabled") private boolean enabled = false; - @Column(name = "allowed_models") - @Convert(converter = IrisModelListConverter.class) - private SortedSet allowedModels = new TreeSet<>(); + @Column(name = "allowed_variants", nullable = false) + @Convert(converter = IrisListConverter.class) + private SortedSet allowedVariants = new TreeSet<>(); - @Nullable - @Column(name = "preferred_model") - private String preferredModel; + @Column(name = "selected_variant", nullable = false) + private String selectedVariant; public boolean isEnabled() { return enabled; @@ -67,20 +64,20 @@ public void setEnabled(boolean enabled) { this.enabled = enabled; } - public SortedSet getAllowedModels() { - return allowedModels; + public SortedSet getAllowedVariants() { + return allowedVariants; } - public void setAllowedModels(SortedSet allowedModels) { - this.allowedModels = allowedModels; + public void setAllowedVariants(SortedSet allowedVariants) { + this.allowedVariants = allowedVariants; } @Nullable - public String getPreferredModel() { - return preferredModel; + public String getSelectedVariant() { + return selectedVariant; } - public void setPreferredModel(@Nullable String preferredModel) { - this.preferredModel = preferredModel; + public void setSelectedVariant(@Nullable String selectedVariant) { + this.selectedVariant = selectedVariant; } } diff --git a/src/main/java/de/tum/in/www1/artemis/domain/iris/settings/IrisSubSettingsType.java b/src/main/java/de/tum/in/www1/artemis/domain/iris/settings/IrisSubSettingsType.java index 73e214c48da0..270e4f84b4f0 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/iris/settings/IrisSubSettingsType.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/iris/settings/IrisSubSettingsType.java @@ -1,5 +1,5 @@ package de.tum.in.www1.artemis.domain.iris.settings; public enum IrisSubSettingsType { - CHAT, HESTIA, COMPETENCY_GENERATION, LECTURE_INGESTION + CHAT, COMPETENCY_GENERATION, LECTURE_INGESTION } diff --git a/src/main/java/de/tum/in/www1/artemis/repository/iris/IrisHestiaSessionRepository.java b/src/main/java/de/tum/in/www1/artemis/repository/iris/IrisHestiaSessionRepository.java deleted file mode 100644 index d5158b939686..000000000000 --- a/src/main/java/de/tum/in/www1/artemis/repository/iris/IrisHestiaSessionRepository.java +++ /dev/null @@ -1,35 +0,0 @@ -package de.tum.in.www1.artemis.repository.iris; - -import static org.springframework.data.jpa.repository.EntityGraph.EntityGraphType.LOAD; - -import java.util.List; - -import org.springframework.data.jpa.repository.EntityGraph; - -import de.tum.in.www1.artemis.domain.iris.session.IrisHestiaSession; -import de.tum.in.www1.artemis.repository.base.ArtemisJpaRepository; - -/** - * Repository interface for managing {@link IrisHestiaSession} entities. - * Provides custom queries for finding hestia sessions based on different criteria. - */ -public interface IrisHestiaSessionRepository extends ArtemisJpaRepository { - - /** - * Finds a list of {@link IrisHestiaSession} based on the exercise and user IDs. - * - * @param codeHintId The ID of the code hint. - * @return A list of hestia sessions sorted by creation date in descending order. - */ - List findByCodeHintIdOrderByCreationDateDesc(Long codeHintId); - - /** - * Finds a single {@link IrisHestiaSession} by its ID and eagerly loads all messages and their contents, - * as well as the code hint and its solution entries - * - * @param sessionId The ID of the session to find - * @return The session with the given ID - */ - @EntityGraph(type = LOAD, attributePaths = { "messages", "messages.content", "codeHint", "codeHint.exercise", "codeHint.solutionEntries" }) - IrisHestiaSession findWithMessagesAndContentsAndCodeHintById(long sessionId); -} diff --git a/src/main/java/de/tum/in/www1/artemis/repository/iris/IrisSettingsRepository.java b/src/main/java/de/tum/in/www1/artemis/repository/iris/IrisSettingsRepository.java index a56d0504e114..9f6770d66d22 100644 --- a/src/main/java/de/tum/in/www1/artemis/repository/iris/IrisSettingsRepository.java +++ b/src/main/java/de/tum/in/www1/artemis/repository/iris/IrisSettingsRepository.java @@ -23,7 +23,6 @@ public interface IrisSettingsRepository extends ArtemisJpaRepository findAllGlobalSettings(); @@ -37,7 +36,6 @@ default IrisGlobalSettings findGlobalSettingsElseThrow() { FROM IrisCourseSettings irisSettings LEFT JOIN FETCH irisSettings.irisChatSettings LEFT JOIN FETCH irisSettings.irisLectureIngestionSettings - LEFT JOIN FETCH irisSettings.irisHestiaSettings LEFT JOIN FETCH irisSettings.irisCompetencyGenerationSettings WHERE irisSettings.course.id = :courseId """) diff --git a/src/main/java/de/tum/in/www1/artemis/repository/iris/IrisTemplateRepository.java b/src/main/java/de/tum/in/www1/artemis/repository/iris/IrisTemplateRepository.java deleted file mode 100644 index 9cf3a0c9e943..000000000000 --- a/src/main/java/de/tum/in/www1/artemis/repository/iris/IrisTemplateRepository.java +++ /dev/null @@ -1,11 +0,0 @@ -package de.tum.in.www1.artemis.repository.iris; - -import de.tum.in.www1.artemis.domain.iris.IrisTemplate; -import de.tum.in.www1.artemis.repository.base.ArtemisJpaRepository; - -/** - * Spring Data repository for the IrisTemplate entity. - */ -public interface IrisTemplateRepository extends ArtemisJpaRepository { - -} diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/PyrisConnectorService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/PyrisConnectorService.java index bf241bc28837..b8ea3e12ea21 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/PyrisConnectorService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/PyrisConnectorService.java @@ -17,7 +17,8 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import de.tum.in.www1.artemis.service.connectors.pyris.dto.PyrisModelDTO; +import de.tum.in.www1.artemis.domain.iris.settings.IrisSubSettingsType; +import de.tum.in.www1.artemis.service.connectors.pyris.dto.PyrisVariantDTO; import de.tum.in.www1.artemis.service.connectors.pyris.dto.lectureingestionwebhook.PyrisWebhookLectureIngestionExecutionDTO; import de.tum.in.www1.artemis.service.iris.exception.IrisException; import de.tum.in.www1.artemis.service.iris.exception.IrisForbiddenException; @@ -48,13 +49,14 @@ public PyrisConnectorService(@Qualifier("pyrisRestTemplate") RestTemplate restTe } /** - * Requests all available models from Pyris + * Requests all available variants from Pyris for a feature * - * @return A list of available Models as IrisModelDTO + * @param feature The feature to get the variants for + * @return A list of available Models as IrisVariantDTO */ - public List getOfferedModels() throws PyrisConnectorException { + public List getOfferedVariants(IrisSubSettingsType feature) throws PyrisConnectorException { try { - var response = restTemplate.getForEntity(pyrisUrl + "/api/v1/models", PyrisModelDTO[].class); + var response = restTemplate.getForEntity(pyrisUrl + "/api/v1/pipelines/" + feature.name() + "/variants", PyrisVariantDTO[].class); if (!response.getStatusCode().is2xxSuccessful() || !response.hasBody()) { throw new PyrisConnectorException("Could not fetch offered models"); } diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/dto/PyrisModelDTO.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/dto/PyrisVariantDTO.java similarity index 68% rename from src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/dto/PyrisModelDTO.java rename to src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/dto/PyrisVariantDTO.java index f13876160d97..737cb6edc759 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/dto/PyrisModelDTO.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/dto/PyrisVariantDTO.java @@ -3,5 +3,5 @@ import com.fasterxml.jackson.annotation.JsonInclude; @JsonInclude(JsonInclude.Include.NON_EMPTY) -public record PyrisModelDTO(String id, String name, String description) { +public record PyrisVariantDTO(String id, String name, String description) { } diff --git a/src/main/java/de/tum/in/www1/artemis/service/hestia/CodeHintService.java b/src/main/java/de/tum/in/www1/artemis/service/hestia/CodeHintService.java index bf577df295f3..1ff758ee4d40 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/hestia/CodeHintService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/hestia/CodeHintService.java @@ -16,11 +16,9 @@ import de.tum.in.www1.artemis.domain.hestia.CodeHint; import de.tum.in.www1.artemis.domain.hestia.ProgrammingExerciseSolutionEntry; import de.tum.in.www1.artemis.domain.hestia.ProgrammingExerciseTask; -import de.tum.in.www1.artemis.domain.iris.session.IrisHestiaSession; import de.tum.in.www1.artemis.repository.hestia.CodeHintRepository; import de.tum.in.www1.artemis.repository.hestia.ProgrammingExerciseSolutionEntryRepository; import de.tum.in.www1.artemis.repository.hestia.ProgrammingExerciseTaskRepository; -import de.tum.in.www1.artemis.service.iris.session.IrisHestiaSessionService; import de.tum.in.www1.artemis.web.rest.errors.BadRequestAlertException; @Profile(PROFILE_CORE) @@ -29,17 +27,14 @@ public class CodeHintService { private static final Logger log = LoggerFactory.getLogger(CodeHintService.class); - private final Optional irisHestiaSessionService; - private final CodeHintRepository codeHintRepository; private final ProgrammingExerciseTaskRepository taskRepository; private final ProgrammingExerciseSolutionEntryRepository solutionEntryRepository; - public CodeHintService(Optional irisHestiaSessionService, CodeHintRepository codeHintRepository, ProgrammingExerciseTaskRepository taskRepository, + public CodeHintService(CodeHintRepository codeHintRepository, ProgrammingExerciseTaskRepository taskRepository, ProgrammingExerciseSolutionEntryRepository solutionEntryRepository) { - this.irisHestiaSessionService = irisHestiaSessionService; this.codeHintRepository = codeHintRepository; this.taskRepository = taskRepository; this.solutionEntryRepository = solutionEntryRepository; @@ -189,17 +184,4 @@ public void updateSolutionEntriesForCodeHint(CodeHint hint) { codeHintRepository.save(hint); } - - /** - * Generates a description and content for a code hint using the Iris subsystem. - * See {@link IrisHestiaSessionService#executeRequest(IrisHestiaSession)} for more information. - * - * @param codeHint The code hint to be generated - * @return The code hint with description and content - */ - public CodeHint generateDescriptionWithIris(CodeHint codeHint) { - var irisService = irisHestiaSessionService.orElseThrow(); - var session = irisService.getOrCreateSession(codeHint); - return irisService.executeRequest(session); - } } diff --git a/src/main/java/de/tum/in/www1/artemis/service/iris/IrisDefaultTemplateService.java b/src/main/java/de/tum/in/www1/artemis/service/iris/IrisDefaultTemplateService.java deleted file mode 100644 index 57dcd2210360..000000000000 --- a/src/main/java/de/tum/in/www1/artemis/service/iris/IrisDefaultTemplateService.java +++ /dev/null @@ -1,76 +0,0 @@ -package de.tum.in.www1.artemis.service.iris; - -import static de.tum.in.www1.artemis.config.Constants.PROFILE_CORE; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Path; -import java.util.Optional; - -import org.apache.commons.io.IOUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.annotation.Profile; -import org.springframework.core.io.Resource; -import org.springframework.stereotype.Service; - -import de.tum.in.www1.artemis.domain.iris.IrisTemplate; -import de.tum.in.www1.artemis.service.ResourceLoaderService; - -/** - * Service that loads default Iris templates from the resources/templates/iris folder. - */ -@Profile(PROFILE_CORE) -@Service -public class IrisDefaultTemplateService { - - private static final Logger log = LoggerFactory.getLogger(IrisDefaultTemplateService.class); - - private final ResourceLoaderService resourceLoaderService; - - public IrisDefaultTemplateService(ResourceLoaderService resourceLoaderService) { - this.resourceLoaderService = resourceLoaderService; - } - - /** - * Loads the default Iris template with the given file name. - * For example, "chat.hbs" will load the template from "resources/templates/iris/chat.hbs". - * - * @param templateFileName The file name of the template to load. - * @return The loaded Iris template, or an empty template if an IO error occurred. - */ - public IrisTemplate load(String templateFileName) { - Path filePath = Path.of("templates", "iris", templateFileName); - Resource resource = resourceLoaderService.getResource(filePath); - try { - String fileContent = IOUtils.toString(resource.getInputStream(), StandardCharsets.UTF_8); - return new IrisTemplate(fileContent); - } - catch (IOException e) { - log.error("Error while loading Iris template from file: {}", filePath, e); - return new IrisTemplate(""); - } - } - - /** - * Loads the global template version from the "resources/templates/iris/template-version.txt" file. - * - * @return an Optional containing the version loaded from the file, or an empty Optional if there was an error. - */ - public Optional loadGlobalTemplateVersion() { - Path filePath = Path.of("templates", "iris", "template-version.txt"); - Resource resource = resourceLoaderService.getResource(filePath); - try { - String fileContent = IOUtils.toString(resource.getInputStream(), StandardCharsets.UTF_8); - int version = Integer.parseInt(fileContent.trim()); - return Optional.of(version); - } - catch (IOException e) { - log.error("Error while loading global template version from file: {}", filePath, e); - } - catch (NumberFormatException e) { - log.error("Content of {} was not a parseable int!", filePath, e); - } - return Optional.empty(); - } -} diff --git a/src/main/java/de/tum/in/www1/artemis/service/iris/IrisSessionService.java b/src/main/java/de/tum/in/www1/artemis/service/iris/IrisSessionService.java index f455b13979fe..28fe076f2c62 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/iris/IrisSessionService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/iris/IrisSessionService.java @@ -10,13 +10,11 @@ import de.tum.in.www1.artemis.domain.iris.message.IrisMessage; import de.tum.in.www1.artemis.domain.iris.session.IrisCourseChatSession; import de.tum.in.www1.artemis.domain.iris.session.IrisExerciseChatSession; -import de.tum.in.www1.artemis.domain.iris.session.IrisHestiaSession; import de.tum.in.www1.artemis.domain.iris.session.IrisSession; import de.tum.in.www1.artemis.repository.UserRepository; import de.tum.in.www1.artemis.service.iris.session.IrisChatBasedFeatureInterface; import de.tum.in.www1.artemis.service.iris.session.IrisCourseChatSessionService; import de.tum.in.www1.artemis.service.iris.session.IrisExerciseChatSessionService; -import de.tum.in.www1.artemis.service.iris.session.IrisHestiaSessionService; import de.tum.in.www1.artemis.service.iris.session.IrisRateLimitedFeatureInterface; import de.tum.in.www1.artemis.service.iris.session.IrisSubFeatureInterface; import de.tum.in.www1.artemis.web.rest.errors.AccessForbiddenException; @@ -34,14 +32,11 @@ public class IrisSessionService { private final IrisCourseChatSessionService irisCourseChatSessionService; - private final IrisHestiaSessionService irisHestiaSessionService; - public IrisSessionService(UserRepository userRepository, IrisExerciseChatSessionService irisExerciseChatSessionService, - IrisCourseChatSessionService irisCourseChatSessionService, IrisHestiaSessionService irisHestiaSessionService) { + IrisCourseChatSessionService irisCourseChatSessionService) { this.userRepository = userRepository; this.irisExerciseChatSessionService = irisExerciseChatSessionService; this.irisCourseChatSessionService = irisCourseChatSessionService; - this.irisHestiaSessionService = irisHestiaSessionService; } /** @@ -136,7 +131,6 @@ private IrisSubFeatureWrapper getIrisSessionSubServic return switch (session) { case IrisExerciseChatSession chatSession -> (IrisSubFeatureWrapper) new IrisSubFeatureWrapper<>(irisExerciseChatSessionService, chatSession); case IrisCourseChatSession courseChatSession -> (IrisSubFeatureWrapper) new IrisSubFeatureWrapper<>(irisCourseChatSessionService, courseChatSession); - case IrisHestiaSession hestiaSession -> (IrisSubFeatureWrapper) new IrisSubFeatureWrapper<>(irisHestiaSessionService, hestiaSession); case null, default -> throw new BadRequestException("Unknown Iris session type " + session.getClass().getSimpleName()); }; } diff --git a/src/main/java/de/tum/in/www1/artemis/service/iris/dto/IrisCombinedChatSubSettingsDTO.java b/src/main/java/de/tum/in/www1/artemis/service/iris/dto/IrisCombinedChatSubSettingsDTO.java index c98fdb1663a0..a27b79cd5b8a 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/iris/dto/IrisCombinedChatSubSettingsDTO.java +++ b/src/main/java/de/tum/in/www1/artemis/service/iris/dto/IrisCombinedChatSubSettingsDTO.java @@ -6,10 +6,8 @@ import com.fasterxml.jackson.annotation.JsonInclude; -import de.tum.in.www1.artemis.domain.iris.IrisTemplate; - @JsonInclude(JsonInclude.Include.NON_EMPTY) -public record IrisCombinedChatSubSettingsDTO(boolean enabled, Integer rateLimit, Integer rateLimitTimeframeHours, @Nullable Set allowedModels, - @Nullable String preferredModel, @Nullable IrisTemplate template) { +public record IrisCombinedChatSubSettingsDTO(boolean enabled, Integer rateLimit, Integer rateLimitTimeframeHours, @Nullable Set allowedVariants, + @Nullable String selectedVariant) { } diff --git a/src/main/java/de/tum/in/www1/artemis/service/iris/dto/IrisCombinedCompetencyGenerationSubSettingsDTO.java b/src/main/java/de/tum/in/www1/artemis/service/iris/dto/IrisCombinedCompetencyGenerationSubSettingsDTO.java index e924be92e773..3abe7bb5706f 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/iris/dto/IrisCombinedCompetencyGenerationSubSettingsDTO.java +++ b/src/main/java/de/tum/in/www1/artemis/service/iris/dto/IrisCombinedCompetencyGenerationSubSettingsDTO.java @@ -6,9 +6,6 @@ import com.fasterxml.jackson.annotation.JsonInclude; -import de.tum.in.www1.artemis.domain.iris.IrisTemplate; - @JsonInclude(JsonInclude.Include.NON_EMPTY) -public record IrisCombinedCompetencyGenerationSubSettingsDTO(boolean enabled, @Nullable Set allowedModels, @Nullable String preferredModel, - @Nullable IrisTemplate template) { +public record IrisCombinedCompetencyGenerationSubSettingsDTO(boolean enabled, @Nullable Set allowedVariants, @Nullable String selectedVariant) { } diff --git a/src/main/java/de/tum/in/www1/artemis/service/iris/dto/IrisCombinedHestiaSubSettingsDTO.java b/src/main/java/de/tum/in/www1/artemis/service/iris/dto/IrisCombinedHestiaSubSettingsDTO.java deleted file mode 100644 index 315fb572194c..000000000000 --- a/src/main/java/de/tum/in/www1/artemis/service/iris/dto/IrisCombinedHestiaSubSettingsDTO.java +++ /dev/null @@ -1,13 +0,0 @@ -package de.tum.in.www1.artemis.service.iris.dto; - -import java.util.Set; - -import jakarta.annotation.Nullable; - -import com.fasterxml.jackson.annotation.JsonInclude; - -import de.tum.in.www1.artemis.domain.iris.IrisTemplate; - -@JsonInclude(JsonInclude.Include.NON_EMPTY) -public record IrisCombinedHestiaSubSettingsDTO(boolean enabled, @Nullable Set allowedModels, @Nullable String preferredModel, @Nullable IrisTemplate template) { -} diff --git a/src/main/java/de/tum/in/www1/artemis/service/iris/dto/IrisCombinedSettingsDTO.java b/src/main/java/de/tum/in/www1/artemis/service/iris/dto/IrisCombinedSettingsDTO.java index 80c662d9d6a1..48dcc9ab6b93 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/iris/dto/IrisCombinedSettingsDTO.java +++ b/src/main/java/de/tum/in/www1/artemis/service/iris/dto/IrisCombinedSettingsDTO.java @@ -4,5 +4,5 @@ @JsonInclude(JsonInclude.Include.NON_EMPTY) public record IrisCombinedSettingsDTO(IrisCombinedChatSubSettingsDTO irisChatSettings, IrisCombinedLectureIngestionSubSettingsDTO irisLectureIngestionSettings, - IrisCombinedHestiaSubSettingsDTO irisHestiaSettings, IrisCombinedCompetencyGenerationSubSettingsDTO irisCompetencyGenerationSettings) { + IrisCombinedCompetencyGenerationSubSettingsDTO irisCompetencyGenerationSettings) { } diff --git a/src/main/java/de/tum/in/www1/artemis/service/iris/session/IrisCourseChatSessionService.java b/src/main/java/de/tum/in/www1/artemis/service/iris/session/IrisCourseChatSessionService.java index bd5a4c25887b..f7985c10f063 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/iris/session/IrisCourseChatSessionService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/iris/session/IrisCourseChatSessionService.java @@ -114,7 +114,8 @@ public void checkRateLimit(User user) { */ @Override public void requestAndHandleResponse(IrisCourseChatSession session) { - requestAndHandleResponse(session, "default", null); + var variant = irisSettingsService.getCombinedIrisSettingsFor(session.getCourse(), false).irisChatSettings().selectedVariant(); + requestAndHandleResponse(session, variant, null); } private void requestAndHandleResponse(IrisCourseChatSession session, String variant, CompetencyJol competencyJol) { diff --git a/src/main/java/de/tum/in/www1/artemis/service/iris/session/IrisExerciseChatSessionService.java b/src/main/java/de/tum/in/www1/artemis/service/iris/session/IrisExerciseChatSessionService.java index 36508d8b0934..b681d59578fd 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/iris/session/IrisExerciseChatSessionService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/iris/session/IrisExerciseChatSessionService.java @@ -144,9 +144,8 @@ public void requestAndHandleResponse(IrisExerciseChatSession session) { var exercise = programmingExerciseRepository.findByIdWithTemplateAndSolutionParticipationElseThrow(chatSession.getExercise().getId()); var latestSubmission = getLatestSubmissionIfExists(exercise, chatSession.getUser()); - // TODO: Use settings to determine the variant - // var irisSettings = irisSettingsService.getCombinedIrisSettingsFor(chatSession.getExercise(), false); - pyrisPipelineService.executeExerciseChatPipeline("default", latestSubmission, exercise, chatSession); + var variant = irisSettingsService.getCombinedIrisSettingsFor(session.getExercise(), false).irisChatSettings().selectedVariant(); + pyrisPipelineService.executeExerciseChatPipeline(variant, latestSubmission, exercise, chatSession); } private Optional getLatestSubmissionIfExists(ProgrammingExercise exercise, User user) { diff --git a/src/main/java/de/tum/in/www1/artemis/service/iris/session/IrisHestiaSessionService.java b/src/main/java/de/tum/in/www1/artemis/service/iris/session/IrisHestiaSessionService.java deleted file mode 100644 index 3115be52e1b5..000000000000 --- a/src/main/java/de/tum/in/www1/artemis/service/iris/session/IrisHestiaSessionService.java +++ /dev/null @@ -1,114 +0,0 @@ -package de.tum.in.www1.artemis.service.iris.session; - -import java.time.ZonedDateTime; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.annotation.Profile; -import org.springframework.stereotype.Service; - -import com.fasterxml.jackson.annotation.JsonInclude; - -import de.tum.in.www1.artemis.domain.ProgrammingExercise; -import de.tum.in.www1.artemis.domain.User; -import de.tum.in.www1.artemis.domain.hestia.CodeHint; -import de.tum.in.www1.artemis.domain.iris.session.IrisHestiaSession; -import de.tum.in.www1.artemis.domain.iris.settings.IrisSubSettingsType; -import de.tum.in.www1.artemis.repository.iris.IrisHestiaSessionRepository; -import de.tum.in.www1.artemis.repository.iris.IrisSessionRepository; -import de.tum.in.www1.artemis.security.Role; -import de.tum.in.www1.artemis.service.AuthorizationCheckService; -import de.tum.in.www1.artemis.service.connectors.pyris.PyrisConnectorService; -import de.tum.in.www1.artemis.service.iris.settings.IrisSettingsService; - -/** - * Service to handle the Hestia integration of Iris. - */ -@Service -@Profile("iris") -public class IrisHestiaSessionService implements IrisButtonBasedFeatureInterface { - - private static final Logger log = LoggerFactory.getLogger(IrisHestiaSessionService.class); - - private final PyrisConnectorService pyrisConnectorService; - - private final IrisSettingsService irisSettingsService; - - private final AuthorizationCheckService authCheckService; - - private final IrisSessionRepository irisSessionRepository; - - private final IrisHestiaSessionRepository irisHestiaSessionRepository; - - public IrisHestiaSessionService(PyrisConnectorService pyrisConnectorService, IrisSettingsService irisSettingsService, AuthorizationCheckService authCheckService, - IrisSessionRepository irisSessionRepository, IrisHestiaSessionRepository irisHestiaSessionRepository) { - this.pyrisConnectorService = pyrisConnectorService; - this.irisSettingsService = irisSettingsService; - this.authCheckService = authCheckService; - this.irisSessionRepository = irisSessionRepository; - this.irisHestiaSessionRepository = irisHestiaSessionRepository; - } - - /** - * Creates a new Iris session for the given code hint. - * If there is already an existing session for the code hint from the last hour, it will be returned instead. - * - * @param codeHint The code hint to create the session for - * @return The Iris session for the code hint - */ - public IrisHestiaSession getOrCreateSession(CodeHint codeHint) { - var existingSessions = irisHestiaSessionRepository.findByCodeHintIdOrderByCreationDateDesc(codeHint.getId()); - // Return the newest session if there is one and it is not older than 1 hour - if (!existingSessions.isEmpty() && existingSessions.getFirst().getCreationDate().plusHours(1).isAfter(ZonedDateTime.now())) { - checkHasAccessTo(null, existingSessions.getFirst()); - return existingSessions.getFirst(); - } - - // Otherwise create a new session - var irisSession = new IrisHestiaSession(); - irisSession.setCodeHint(codeHint); - checkHasAccessTo(null, irisSession); - irisSession = irisSessionRepository.save(irisSession); - return irisSession; - } - - @JsonInclude(JsonInclude.Include.NON_EMPTY) - record HestiaDTO(CodeHint codeHint, IrisHestiaSession session, ProgrammingExercise exercise) { - } - - /** - * Generates the description and content for a code hint. - * It does not directly save the code hint, but instead returns it with the generated description and content. - * This way the instructor can still modify the code hint before saving it or discard the changes. - * - * @param session The Iris session to generate the description for - * @return The code hint with the generated description and content - */ - @Override - public CodeHint executeRequest(IrisHestiaSession session) { - // TODO: Re-add in a future PR. Remember to reenable the test cases! - return null; - } - - /** - * Checks if the user has at least the given role for the exercise of the code hint. - * - * @param user The user to check the access for - * @param session The Iris session to check the access for - */ - @Override - public void checkHasAccessTo(User user, IrisHestiaSession session) { - var exercise = session.getCodeHint().getExercise(); - authCheckService.checkHasAtLeastRoleForExerciseElseThrow(Role.EDITOR, exercise, user); - } - - /** - * Not supported for Iris Hestia sessions. - * - * @param session The session to get a message for - */ - @Override - public void checkIsFeatureActivatedFor(IrisHestiaSession session) { - irisSettingsService.isEnabledForElseThrow(IrisSubSettingsType.HESTIA, session.getCodeHint().getExercise()); - } -} diff --git a/src/main/java/de/tum/in/www1/artemis/service/iris/settings/IrisSettingsService.java b/src/main/java/de/tum/in/www1/artemis/service/iris/settings/IrisSettingsService.java index 0024704cf0d0..331e024e0ee6 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/iris/settings/IrisSettingsService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/iris/settings/IrisSettingsService.java @@ -8,7 +8,8 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.Objects; -import java.util.Optional; +import java.util.Set; +import java.util.TreeSet; import java.util.function.Supplier; import org.springframework.boot.context.event.ApplicationReadyEvent; @@ -19,20 +20,17 @@ import de.tum.in.www1.artemis.domain.Course; import de.tum.in.www1.artemis.domain.Exercise; import de.tum.in.www1.artemis.domain.User; -import de.tum.in.www1.artemis.domain.iris.IrisTemplate; import de.tum.in.www1.artemis.domain.iris.settings.IrisChatSubSettings; import de.tum.in.www1.artemis.domain.iris.settings.IrisCompetencyGenerationSubSettings; import de.tum.in.www1.artemis.domain.iris.settings.IrisCourseSettings; import de.tum.in.www1.artemis.domain.iris.settings.IrisExerciseSettings; import de.tum.in.www1.artemis.domain.iris.settings.IrisGlobalSettings; -import de.tum.in.www1.artemis.domain.iris.settings.IrisHestiaSubSettings; import de.tum.in.www1.artemis.domain.iris.settings.IrisLectureIngestionSubSettings; import de.tum.in.www1.artemis.domain.iris.settings.IrisSettings; import de.tum.in.www1.artemis.domain.iris.settings.IrisSubSettings; import de.tum.in.www1.artemis.domain.iris.settings.IrisSubSettingsType; import de.tum.in.www1.artemis.repository.iris.IrisSettingsRepository; import de.tum.in.www1.artemis.service.AuthorizationCheckService; -import de.tum.in.www1.artemis.service.iris.IrisDefaultTemplateService; import de.tum.in.www1.artemis.service.iris.dto.IrisCombinedSettingsDTO; import de.tum.in.www1.artemis.web.rest.errors.AccessForbiddenAlertException; import de.tum.in.www1.artemis.web.rest.errors.BadRequestAlertException; @@ -53,34 +51,14 @@ public class IrisSettingsService { private final IrisSubSettingsService irisSubSettingsService; - private final IrisDefaultTemplateService irisDefaultTemplateService; - private final AuthorizationCheckService authCheckService; - public IrisSettingsService(IrisSettingsRepository irisSettingsRepository, IrisSubSettingsService irisSubSettingsService, IrisDefaultTemplateService irisDefaultTemplateService, - AuthorizationCheckService authCheckService) { + public IrisSettingsService(IrisSettingsRepository irisSettingsRepository, IrisSubSettingsService irisSubSettingsService, AuthorizationCheckService authCheckService) { this.irisSettingsRepository = irisSettingsRepository; this.irisSubSettingsService = irisSubSettingsService; - this.irisDefaultTemplateService = irisDefaultTemplateService; this.authCheckService = authCheckService; } - private Optional loadGlobalTemplateVersion() { - return irisDefaultTemplateService.loadGlobalTemplateVersion(); - } - - private IrisTemplate loadDefaultChatTemplate() { - return irisDefaultTemplateService.load("chat.hbs"); - } - - private IrisTemplate loadDefaultHestiaTemplate() { - return irisDefaultTemplateService.load("hestia.hbs"); - } - - private IrisTemplate loadDefaultCompetencyGenerationTemplate() { - return irisDefaultTemplateService.load("competency-generation.hbs"); - } - /** * Hooks into the {@link ApplicationReadyEvent} and creates or updates the global IrisSettings object on startup. * @@ -97,10 +75,6 @@ public void execute(ApplicationReadyEvent event) throws Exception { if (allGlobalSettings.size() > 1) { var maxIdSettings = allGlobalSettings.stream().max(Comparator.comparingLong(IrisSettings::getId)).orElseThrow(); allGlobalSettings.stream().filter(settings -> !Objects.equals(settings.getId(), maxIdSettings.getId())).forEach(irisSettingsRepository::delete); - autoUpdateGlobalSettings(maxIdSettings); - } - else { - autoUpdateGlobalSettings(allGlobalSettings.stream().findFirst().get()); } } @@ -109,46 +83,20 @@ public void execute(ApplicationReadyEvent event) throws Exception { */ private void createInitialGlobalSettings() { var settings = new IrisGlobalSettings(); - settings.setCurrentVersion(loadGlobalTemplateVersion().orElse(0)); initializeIrisChatSettings(settings); initializeIrisLectureIngestionSettings(settings); - initializeIrisHestiaSettings(settings); initializeIrisCompetencyGenerationSettings(settings); irisSettingsRepository.save(settings); } - /** - * Auto updates the global IrisSettings object if the current version is outdated. - * - * @param settings The global IrisSettings object to update - */ - private void autoUpdateGlobalSettings(IrisGlobalSettings settings) { - Optional globalVersion = loadGlobalTemplateVersion(); - if (globalVersion.isEmpty() || settings.getCurrentVersion() < globalVersion.get()) { - if (settings.isEnableAutoUpdateChat() || settings.getIrisChatSettings() == null) { - initializeIrisChatSettings(settings); - } - if (settings.isEnableAutoUpdateLectureIngestion() || settings.getIrisLectureIngestionSettings() == null) { - initializeIrisLectureIngestionSettings(settings); - } - if (settings.isEnableAutoUpdateHestia() || settings.getIrisHestiaSettings() == null) { - initializeIrisHestiaSettings(settings); - } - if (settings.isEnableAutoUpdateCompetencyGeneration() || settings.getIrisCompetencyGenerationSettings() == null) { - initializeIrisCompetencyGenerationSettings(settings); - } - - globalVersion.ifPresent(settings::setCurrentVersion); - saveIrisSettings(settings); - } - } - private static T initializeSettings(T settings, Supplier constructor) { if (settings == null) { settings = constructor.get(); settings.setEnabled(false); + settings.setAllowedVariants(new TreeSet<>(Set.of("default"))); + settings.setSelectedVariant("default"); } return settings; } @@ -156,7 +104,6 @@ private static T initializeSettings(T settings, Supp private void initializeIrisChatSettings(IrisGlobalSettings settings) { var irisChatSettings = settings.getIrisChatSettings(); irisChatSettings = initializeSettings(irisChatSettings, IrisChatSubSettings::new); - irisChatSettings.setTemplate(loadDefaultChatTemplate()); settings.setIrisChatSettings(irisChatSettings); } @@ -166,17 +113,9 @@ private void initializeIrisLectureIngestionSettings(IrisGlobalSettings settings) settings.setIrisLectureIngestionSettings(irisLectureIngestionSettings); } - private void initializeIrisHestiaSettings(IrisGlobalSettings settings) { - var irisHestiaSettings = settings.getIrisHestiaSettings(); - irisHestiaSettings = initializeSettings(irisHestiaSettings, IrisHestiaSubSettings::new); - irisHestiaSettings.setTemplate(loadDefaultHestiaTemplate()); - settings.setIrisHestiaSettings(irisHestiaSettings); - } - private void initializeIrisCompetencyGenerationSettings(IrisGlobalSettings settings) { var irisCompetencyGenerationSettings = settings.getIrisCompetencyGenerationSettings(); irisCompetencyGenerationSettings = initializeSettings(irisCompetencyGenerationSettings, IrisCompetencyGenerationSubSettings::new); - irisCompetencyGenerationSettings.setTemplate(loadDefaultCompetencyGenerationTemplate()); settings.setIrisCompetencyGenerationSettings(irisCompetencyGenerationSettings); } @@ -213,9 +152,6 @@ private T saveNewIrisSettings(T settings) { if (settings instanceof IrisGlobalSettings) { throw new BadRequestAlertException("You can not create new global settings", "IrisSettings", "notGlobal"); } - if (!settings.isValid()) { - throw new BadRequestAlertException("New Iris settings are not valid", "IrisSettings", "notValid"); - } if (settings instanceof IrisCourseSettings courseSettings && irisSettingsRepository.findCourseSettings(courseSettings.getCourse().getId()).isPresent()) { throw new ConflictException("Iris settings for this course already exist", "IrisSettings", "alreadyExists"); } @@ -240,9 +176,6 @@ private T updateIrisSettings(long existingSettingsId, T if (!Objects.equals(existingSettingsId, settingsUpdate.getId())) { throw new ConflictException("Existing Iris settings ID does not match update ID", "IrisSettings", "idMismatch"); } - if (!settingsUpdate.isValid()) { - throw new BadRequestAlertException("Updated Iris settings are not valid", "IrisSettings", "notValid"); - } var existingSettings = irisSettingsRepository.findByIdElseThrow(existingSettingsId); @@ -268,17 +201,9 @@ else if (existingSettings instanceof IrisExerciseSettings exerciseSettings && se * @return The updated global Iris settings */ private IrisGlobalSettings updateGlobalSettings(IrisGlobalSettings existingSettings, IrisGlobalSettings settingsUpdate) { - existingSettings.setCurrentVersion(settingsUpdate.getCurrentVersion()); - - existingSettings.setEnableAutoUpdateChat(settingsUpdate.isEnableAutoUpdateChat()); - existingSettings.setEnableAutoUpdateLectureIngestion(settingsUpdate.isEnableAutoUpdateLectureIngestion()); - existingSettings.setEnableAutoUpdateHestia(settingsUpdate.isEnableAutoUpdateHestia()); - existingSettings.setEnableAutoUpdateCompetencyGeneration(settingsUpdate.isEnableAutoUpdateCompetencyGeneration()); - existingSettings.setIrisLectureIngestionSettings( irisSubSettingsService.update(existingSettings.getIrisLectureIngestionSettings(), settingsUpdate.getIrisLectureIngestionSettings(), null, GLOBAL)); existingSettings.setIrisChatSettings(irisSubSettingsService.update(existingSettings.getIrisChatSettings(), settingsUpdate.getIrisChatSettings(), null, GLOBAL)); - existingSettings.setIrisHestiaSettings(irisSubSettingsService.update(existingSettings.getIrisHestiaSettings(), settingsUpdate.getIrisHestiaSettings(), null, GLOBAL)); existingSettings.setIrisCompetencyGenerationSettings( irisSubSettingsService.update(existingSettings.getIrisCompetencyGenerationSettings(), settingsUpdate.getIrisCompetencyGenerationSettings(), null, GLOBAL)); @@ -298,8 +223,6 @@ private IrisCourseSettings updateCourseSettings(IrisCourseSettings existingSetti irisSubSettingsService.update(existingSettings.getIrisChatSettings(), settingsUpdate.getIrisChatSettings(), parentSettings.irisChatSettings(), COURSE)); existingSettings.setIrisLectureIngestionSettings(irisSubSettingsService.update(existingSettings.getIrisLectureIngestionSettings(), settingsUpdate.getIrisLectureIngestionSettings(), parentSettings.irisLectureIngestionSettings(), COURSE)); - existingSettings.setIrisHestiaSettings( - irisSubSettingsService.update(existingSettings.getIrisHestiaSettings(), settingsUpdate.getIrisHestiaSettings(), parentSettings.irisHestiaSettings(), COURSE)); existingSettings.setIrisCompetencyGenerationSettings(irisSubSettingsService.update(existingSettings.getIrisCompetencyGenerationSettings(), settingsUpdate.getIrisCompetencyGenerationSettings(), parentSettings.irisCompetencyGenerationSettings(), COURSE)); @@ -381,8 +304,7 @@ public IrisCombinedSettingsDTO getCombinedIrisGlobalSettings() { settingsList.add(getGlobalSettings()); return new IrisCombinedSettingsDTO(irisSubSettingsService.combineChatSettings(settingsList, false), - irisSubSettingsService.combineLectureIngestionSubSettings(settingsList, false), irisSubSettingsService.combineHestiaSettings(settingsList, false), - irisSubSettingsService.combineCompetencyGenerationSettings(settingsList, false)); + irisSubSettingsService.combineLectureIngestionSubSettings(settingsList, false), irisSubSettingsService.combineCompetencyGenerationSettings(settingsList, false)); } /** @@ -401,7 +323,7 @@ public IrisCombinedSettingsDTO getCombinedIrisSettingsFor(Course course, boolean settingsList.add(irisSettingsRepository.findCourseSettings(course.getId()).orElse(null)); return new IrisCombinedSettingsDTO(irisSubSettingsService.combineChatSettings(settingsList, minimal), - irisSubSettingsService.combineLectureIngestionSubSettings(settingsList, minimal), irisSubSettingsService.combineHestiaSettings(settingsList, minimal), + irisSubSettingsService.combineLectureIngestionSubSettings(settingsList, minimal), irisSubSettingsService.combineCompetencyGenerationSettings(settingsList, minimal)); } @@ -422,7 +344,7 @@ public IrisCombinedSettingsDTO getCombinedIrisSettingsFor(Exercise exercise, boo settingsList.add(getRawIrisSettingsFor(exercise)); return new IrisCombinedSettingsDTO(irisSubSettingsService.combineChatSettings(settingsList, minimal), - irisSubSettingsService.combineLectureIngestionSubSettings(settingsList, minimal), irisSubSettingsService.combineHestiaSettings(settingsList, minimal), + irisSubSettingsService.combineLectureIngestionSubSettings(settingsList, minimal), irisSubSettingsService.combineCompetencyGenerationSettings(settingsList, minimal)); } @@ -449,7 +371,6 @@ public IrisCourseSettings getDefaultSettingsFor(Course course) { settings.setCourse(course); settings.setIrisLectureIngestionSettings(new IrisLectureIngestionSubSettings()); settings.setIrisChatSettings(new IrisChatSubSettings()); - settings.setIrisHestiaSettings(new IrisHestiaSubSettings()); settings.setIrisCompetencyGenerationSettings(new IrisCompetencyGenerationSubSettings()); return settings; } @@ -522,7 +443,6 @@ public void deleteSettingsFor(Exercise exercise) { private boolean isFeatureEnabledInSettings(IrisCombinedSettingsDTO settings, IrisSubSettingsType type) { return switch (type) { case CHAT -> settings.irisChatSettings().enabled(); - case HESTIA -> settings.irisHestiaSettings().enabled(); case COMPETENCY_GENERATION -> settings.irisCompetencyGenerationSettings().enabled(); case LECTURE_INGESTION -> settings.irisLectureIngestionSettings().enabled(); }; diff --git a/src/main/java/de/tum/in/www1/artemis/service/iris/settings/IrisSubSettingsService.java b/src/main/java/de/tum/in/www1/artemis/service/iris/settings/IrisSubSettingsService.java index e4a609a23b56..7ed7ec3ad4c5 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/iris/settings/IrisSubSettingsService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/iris/settings/IrisSubSettingsService.java @@ -12,11 +12,9 @@ import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; -import de.tum.in.www1.artemis.domain.iris.IrisTemplate; import de.tum.in.www1.artemis.domain.iris.settings.IrisChatSubSettings; import de.tum.in.www1.artemis.domain.iris.settings.IrisCompetencyGenerationSubSettings; import de.tum.in.www1.artemis.domain.iris.settings.IrisExerciseSettings; -import de.tum.in.www1.artemis.domain.iris.settings.IrisHestiaSubSettings; import de.tum.in.www1.artemis.domain.iris.settings.IrisLectureIngestionSubSettings; import de.tum.in.www1.artemis.domain.iris.settings.IrisSettings; import de.tum.in.www1.artemis.domain.iris.settings.IrisSettingsType; @@ -24,7 +22,6 @@ import de.tum.in.www1.artemis.service.AuthorizationCheckService; import de.tum.in.www1.artemis.service.iris.dto.IrisCombinedChatSubSettingsDTO; import de.tum.in.www1.artemis.service.iris.dto.IrisCombinedCompetencyGenerationSubSettingsDTO; -import de.tum.in.www1.artemis.service.iris.dto.IrisCombinedHestiaSubSettingsDTO; import de.tum.in.www1.artemis.service.iris.dto.IrisCombinedLectureIngestionSubSettingsDTO; /** @@ -74,10 +71,9 @@ public IrisChatSubSettings update(IrisChatSubSettings currentSettings, IrisChatS currentSettings.setRateLimit(newSettings.getRateLimit()); currentSettings.setRateLimitTimeframeHours(newSettings.getRateLimitTimeframeHours()); } - currentSettings.setAllowedModels(selectAllowedModels(currentSettings.getAllowedModels(), newSettings.getAllowedModels())); - currentSettings.setPreferredModel(validatePreferredModel(currentSettings.getPreferredModel(), newSettings.getPreferredModel(), currentSettings.getAllowedModels(), - parentSettings != null ? parentSettings.allowedModels() : null)); - currentSettings.setTemplate(newSettings.getTemplate()); + currentSettings.setAllowedVariants(selectAllowedVariants(currentSettings.getAllowedVariants(), newSettings.getAllowedVariants())); + currentSettings.setSelectedVariant(validateSelectedVariant(currentSettings.getSelectedVariant(), newSettings.getSelectedVariant(), currentSettings.getAllowedVariants(), + parentSettings != null ? parentSettings.allowedVariants() : null)); return currentSettings; } @@ -112,40 +108,6 @@ public IrisLectureIngestionSubSettings update(IrisLectureIngestionSubSettings cu return currentSettings; } - /** - * Updates a Hestia sub settings object. - * If the new settings are null, the current settings will be deleted (except if the parent settings are null == if the settings are global). - * Special notes: - * - If the user is not an admin the allowed models will not be updated. - * - If the user is not an admin the preferred model will only be updated if it is included in the allowed models. - * - * @param currentSettings Current Hestia sub settings. - * @param newSettings Updated Hestia sub settings. - * @param parentSettings Parent Hestia sub settings. - * @param settingsType Type of the settings the sub settings belong to. - * @return Updated Hestia sub settings. - */ - public IrisHestiaSubSettings update(IrisHestiaSubSettings currentSettings, IrisHestiaSubSettings newSettings, IrisCombinedHestiaSubSettingsDTO parentSettings, - IrisSettingsType settingsType) { - if (newSettings == null) { - if (parentSettings == null) { - throw new IllegalArgumentException("Cannot delete the Hestia settings"); - } - return null; - } - if (currentSettings == null) { - currentSettings = new IrisHestiaSubSettings(); - } - if (settingsType == IrisSettingsType.EXERCISE || authCheckService.isAdmin()) { - currentSettings.setEnabled(newSettings.isEnabled()); - } - currentSettings.setAllowedModels(selectAllowedModels(currentSettings.getAllowedModels(), newSettings.getAllowedModels())); - currentSettings.setPreferredModel(validatePreferredModel(currentSettings.getPreferredModel(), newSettings.getPreferredModel(), currentSettings.getAllowedModels(), - parentSettings != null ? parentSettings.allowedModels() : null)); - currentSettings.setTemplate(newSettings.getTemplate()); - return currentSettings; - } - /** * Updates a Competency Generation sub settings object. * If the new settings are null, the current settings will be deleted (except if the parent settings are null == if the settings are global). @@ -172,11 +134,10 @@ public IrisCompetencyGenerationSubSettings update(IrisCompetencyGenerationSubSet } if (authCheckService.isAdmin()) { currentSettings.setEnabled(newSettings.isEnabled()); - currentSettings.setAllowedModels(selectAllowedModels(currentSettings.getAllowedModels(), newSettings.getAllowedModels())); + currentSettings.setAllowedVariants(selectAllowedVariants(currentSettings.getAllowedVariants(), newSettings.getAllowedVariants())); } - currentSettings.setPreferredModel(validatePreferredModel(currentSettings.getPreferredModel(), newSettings.getPreferredModel(), currentSettings.getAllowedModels(), - parentSettings != null ? parentSettings.allowedModels() : null)); - currentSettings.setTemplate(newSettings.getTemplate()); + currentSettings.setSelectedVariant(validateSelectedVariant(currentSettings.getSelectedVariant(), newSettings.getSelectedVariant(), currentSettings.getAllowedVariants(), + parentSettings != null ? parentSettings.allowedVariants() : null)); return currentSettings; } @@ -185,12 +146,12 @@ public IrisCompetencyGenerationSubSettings update(IrisCompetencyGenerationSubSet * If the user is an admin, all models are allowed. * Otherwise, only models that are allowed by the parent settings or the current settings are allowed. * - * @param allowedModels The allowed models of the current settings. - * @param updatedAllowedModels The allowed models of the updated settings. + * @param allowedVariants The allowed models of the current settings. + * @param updatedAllowedVariants The allowed models of the updated settings. * @return The filtered allowed models. */ - private SortedSet selectAllowedModels(SortedSet allowedModels, SortedSet updatedAllowedModels) { - return authCheckService.isAdmin() ? updatedAllowedModels : allowedModels; + private SortedSet selectAllowedVariants(SortedSet allowedVariants, SortedSet updatedAllowedVariants) { + return authCheckService.isAdmin() ? updatedAllowedVariants : allowedVariants; } /** @@ -198,23 +159,23 @@ private SortedSet selectAllowedModels(SortedSet allowedModels, S * If the user is an admin, all models are allowed. * Otherwise, only models that are allowed by the current settings are allowed. * - * @param preferredModel The preferred model of the current settings. - * @param newPreferredModel The preferred model of the updated settings. - * @param allowedModels The allowed models of the current settings. - * @param parentAllowedModels The allowed models of the parent settings. + * @param selectedVariant The preferred model of the current settings. + * @param newSelectedVariant The preferred model of the updated settings. + * @param allowedVariants The allowed models of the current settings. + * @param parentAllowedVariants The allowed models of the parent settings. * @return The validated preferred model. */ - private String validatePreferredModel(String preferredModel, String newPreferredModel, Set allowedModels, Set parentAllowedModels) { - if (newPreferredModel == null || newPreferredModel.isBlank()) { + private String validateSelectedVariant(String selectedVariant, String newSelectedVariant, Set allowedVariants, Set parentAllowedVariants) { + if (newSelectedVariant == null || newSelectedVariant.isBlank()) { return null; } - var canChangePreferredModel = authCheckService.isAdmin() || (allowedModels != null && !allowedModels.isEmpty() && allowedModels.contains(newPreferredModel)) - || ((allowedModels == null || allowedModels.isEmpty()) && parentAllowedModels != null && parentAllowedModels.contains(newPreferredModel)); - if (canChangePreferredModel) { - return newPreferredModel; + var canChangeSelectedVariant = authCheckService.isAdmin() || (allowedVariants != null && !allowedVariants.isEmpty() && allowedVariants.contains(newSelectedVariant)) + || ((allowedVariants == null || allowedVariants.isEmpty()) && parentAllowedVariants != null && parentAllowedVariants.contains(newSelectedVariant)); + if (canChangeSelectedVariant) { + return newSelectedVariant; } - return preferredModel; + return selectedVariant; } /** @@ -229,10 +190,9 @@ private String validatePreferredModel(String preferredModel, String newPreferred public IrisCombinedChatSubSettingsDTO combineChatSettings(ArrayList settingsList, boolean minimal) { var enabled = getCombinedEnabled(settingsList, IrisSettings::getIrisChatSettings); var rateLimit = getCombinedRateLimit(settingsList); - var allowedModels = minimal ? getCombinedAllowedModels(settingsList, IrisSettings::getIrisChatSettings) : null; - var preferredModel = minimal ? getCombinedPreferredModel(settingsList, IrisSettings::getIrisChatSettings) : null; - var template = minimal ? getCombinedTemplate(settingsList, IrisSettings::getIrisChatSettings, IrisChatSubSettings::getTemplate) : null; - return new IrisCombinedChatSubSettingsDTO(enabled, rateLimit, null, allowedModels, preferredModel, template); + var allowedVariants = !minimal ? getCombinedAllowedVariants(settingsList, IrisSettings::getIrisChatSettings) : null; + var selectedVariant = !minimal ? getCombinedSelectedVariant(settingsList, IrisSettings::getIrisChatSettings) : null; + return new IrisCombinedChatSubSettingsDTO(enabled, rateLimit, null, allowedVariants, selectedVariant); } /** @@ -249,24 +209,6 @@ public IrisCombinedLectureIngestionSubSettingsDTO combineLectureIngestionSubSett return new IrisCombinedLectureIngestionSubSettingsDTO(enabled); } - /** - * Combines the Hestia settings of multiple {@link IrisSettings} objects. - * If minimal is true, the returned object will only contain the enabled field. - * The minimal version can safely be sent to students. - * - * @param settingsList List of {@link IrisSettings} objects to combine. - * @param minimal Whether to return a minimal version of the combined settings. - * @return Combined Hestia settings. - */ - public IrisCombinedHestiaSubSettingsDTO combineHestiaSettings(ArrayList settingsList, boolean minimal) { - var actualSettingsList = settingsList.stream().filter(settings -> !(settings instanceof IrisExerciseSettings)).toList(); - var enabled = getCombinedEnabled(actualSettingsList, IrisSettings::getIrisHestiaSettings); - var allowedModels = minimal ? getCombinedAllowedModels(actualSettingsList, IrisSettings::getIrisHestiaSettings) : null; - var preferredModel = minimal ? getCombinedPreferredModel(actualSettingsList, IrisSettings::getIrisHestiaSettings) : null; - var template = minimal ? getCombinedTemplate(actualSettingsList, IrisSettings::getIrisHestiaSettings, IrisHestiaSubSettings::getTemplate) : null; - return new IrisCombinedHestiaSubSettingsDTO(enabled, allowedModels, preferredModel, template); - } - /** * Combines the Competency Generation settings of multiple {@link IrisSettings} objects. * If minimal is true, the returned object will only contain the enabled field. @@ -279,11 +221,9 @@ public IrisCombinedHestiaSubSettingsDTO combineHestiaSettings(ArrayList settingsList, boolean minimal) { var actualSettingsList = settingsList.stream().filter(settings -> !(settings instanceof IrisExerciseSettings)).toList(); var enabled = getCombinedEnabled(actualSettingsList, IrisSettings::getIrisCompetencyGenerationSettings); - var allowedModels = minimal ? getCombinedAllowedModels(actualSettingsList, IrisSettings::getIrisCompetencyGenerationSettings) : null; - var preferredModel = minimal ? getCombinedPreferredModel(actualSettingsList, IrisSettings::getIrisCompetencyGenerationSettings) : null; - var template = minimal ? getCombinedTemplate(actualSettingsList, IrisSettings::getIrisCompetencyGenerationSettings, IrisCompetencyGenerationSubSettings::getTemplate) - : null; - return new IrisCombinedCompetencyGenerationSubSettingsDTO(enabled, allowedModels, preferredModel, template); + var allowedVariants = !minimal ? getCombinedAllowedVariants(actualSettingsList, IrisSettings::getIrisCompetencyGenerationSettings) : null; + var selectedVariant = !minimal ? getCombinedSelectedVariant(actualSettingsList, IrisSettings::getIrisCompetencyGenerationSettings) : null; + return new IrisCombinedCompetencyGenerationSubSettingsDTO(enabled, allowedVariants, selectedVariant); } /** @@ -320,43 +260,28 @@ private Integer getCombinedRateLimit(List settingsList) { } /** - * Combines the allowedModels field of multiple {@link IrisSettings} objects. - * Simply takes the last allowedModels. + * Combines the allowedVariants field of multiple {@link IrisSettings} objects. + * Simply takes the last allowedVariants. * * @param settingsList List of {@link IrisSettings} objects to combine. * @param subSettingsFunction Function to get the sub settings from an IrisSettings object. - * @return Combined allowedModels field. + * @return Combined allowedVariants field. */ - private Set getCombinedAllowedModels(List settingsList, Function subSettingsFunction) { - return settingsList.stream().filter(Objects::nonNull).map(subSettingsFunction).filter(Objects::nonNull).map(IrisSubSettings::getAllowedModels).filter(Objects::nonNull) + private Set getCombinedAllowedVariants(List settingsList, Function subSettingsFunction) { + return settingsList.stream().filter(Objects::nonNull).map(subSettingsFunction).filter(Objects::nonNull).map(IrisSubSettings::getAllowedVariants).filter(Objects::nonNull) .filter(models -> !models.isEmpty()).reduce((first, second) -> second).orElse(new TreeSet<>()); } /** - * Combines the preferredModel field of multiple {@link IrisSettings} objects. - * Simply takes the last preferredModel. - * TODO + * Combines the selectedVariant field of multiple {@link IrisSettings} objects. + * Simply takes the last selectedVariant. * * @param settingsList List of {@link IrisSettings} objects to combine. * @param subSettingsFunction Function to get the sub settings from an IrisSettings object. - * @return Combined preferredModel field. + * @return Combined selectedVariant field. */ - private String getCombinedPreferredModel(List settingsList, Function subSettingsFunction) { - return settingsList.stream().filter(Objects::nonNull).map(subSettingsFunction).filter(Objects::nonNull).map(IrisSubSettings::getPreferredModel) + private String getCombinedSelectedVariant(List settingsList, Function subSettingsFunction) { + return settingsList.stream().filter(Objects::nonNull).map(subSettingsFunction).filter(Objects::nonNull).map(IrisSubSettings::getSelectedVariant) .filter(model -> model != null && !model.isBlank()).reduce((first, second) -> second).orElse(null); } - - /** - * Combines the template field of multiple {@link IrisSettings} objects. - * Simply takes the last template. - * - * @param settingsList List of {@link IrisSettings} objects to combine. - * @param templateFunction Function to get the template from the sub settings from an IrisSettings object. - * @return Combined template field. - */ - private IrisTemplate getCombinedTemplate(List settingsList, Function subSettingsFunction, - Function templateFunction) { - return settingsList.stream().filter(Objects::nonNull).map(subSettingsFunction).filter(Objects::nonNull).map(templateFunction) - .filter(template -> template != null && template.getContent() != null && !template.getContent().isBlank()).reduce((first, second) -> second).orElse(null); - } } diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/hestia/CodeHintResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/hestia/CodeHintResource.java index 6c4231e06df0..f5e822133792 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/hestia/CodeHintResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/hestia/CodeHintResource.java @@ -21,7 +21,6 @@ import de.tum.in.www1.artemis.domain.ProgrammingExercise; import de.tum.in.www1.artemis.domain.hestia.CodeHint; -import de.tum.in.www1.artemis.domain.iris.settings.IrisSubSettingsType; import de.tum.in.www1.artemis.repository.ProgrammingExerciseRepository; import de.tum.in.www1.artemis.repository.hestia.CodeHintRepository; import de.tum.in.www1.artemis.repository.hestia.ProgrammingExerciseSolutionEntryRepository; @@ -97,41 +96,6 @@ public ResponseEntity> generateCodeHintsForExercise(@PathVariable return ResponseEntity.ok(codeHints); } - /** - * {@code POST programming-exercises/:exerciseId/code-hints/:codeHintId/generate-description} : Generate a description for a code hint using Iris. - * - * @param exerciseId The id of the exercise of the code hint - * @param codeHintId The id of the code hint - * @return the {@link ResponseEntity} with status {@code 200 (Ok)} and with body the updated code hint - */ - // TODO: move into some IrisResource - @Profile("iris") - @PostMapping("programming-exercises/{exerciseId}/code-hints/{codeHintId}/generate-description") - @EnforceAtLeastEditorInExercise - public ResponseEntity generateDescriptionForCodeHint(@PathVariable Long exerciseId, @PathVariable Long codeHintId) { - log.debug("REST request to generate description with Iris for CodeHint: {}", codeHintId); - - ProgrammingExercise exercise = programmingExerciseRepository.findByIdElseThrow(exerciseId); - irisSettingsService.orElseThrow().isEnabledForElseThrow(IrisSubSettingsType.HESTIA, exercise); - - // Hints for exam exercises are not supported at the moment - if (exercise.isExamExercise()) { - throw new AccessForbiddenException("Code hints for exams are currently not supported"); - } - - var codeHint = codeHintRepository.findByIdWithSolutionEntriesElseThrow(codeHintId); - if (!Objects.equals(codeHint.getExercise().getId(), exercise.getId())) { - throw new ConflictException("The code hint does not belong to the exercise", "CodeHint", "codeHintExerciseConflict"); - } - - if (codeHint.getSolutionEntries().isEmpty()) { - throw new ConflictException("The code hint does not have any solution entries", "CodeHint", "codeHintNoSolutionEntries"); - } - - codeHint = codeHintService.generateDescriptionWithIris(codeHint); - return ResponseEntity.ok(codeHint); - } - /** * {@code DELETE programming-exercises/:exerciseId/code-hints/:codeHintId/solution-entries/:solutionEntryId} : * Removes a solution entry from a code hint. diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/iris/IrisModelsResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/iris/IrisModelsResource.java deleted file mode 100644 index 781975c1aa9b..000000000000 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/iris/IrisModelsResource.java +++ /dev/null @@ -1,47 +0,0 @@ -package de.tum.in.www1.artemis.web.rest.iris; - -import java.util.List; - -import org.springframework.context.annotation.Profile; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import de.tum.in.www1.artemis.security.annotations.EnforceAtLeastEditor; -import de.tum.in.www1.artemis.service.connectors.pyris.PyrisConnectorException; -import de.tum.in.www1.artemis.service.connectors.pyris.PyrisConnectorService; -import de.tum.in.www1.artemis.service.connectors.pyris.dto.PyrisModelDTO; -import de.tum.in.www1.artemis.web.rest.errors.InternalServerErrorException; - -/** - * REST controller for managing the models Pyris provides. - */ -@Profile("iris") -@RestController -@RequestMapping("api/") -public class IrisModelsResource { - - private final PyrisConnectorService pyrisConnectorService; - - public IrisModelsResource(PyrisConnectorService pyrisConnectorService) { - this.pyrisConnectorService = pyrisConnectorService; - } - - /** - * GET iris/models: Retrieve all available models offered by Pyris - * - * @return the {@link ResponseEntity} with status {@code 200 (Ok)} and with body a List of the models - */ - @GetMapping("iris/models") - @EnforceAtLeastEditor - public ResponseEntity> getAllModels() { - try { - var models = pyrisConnectorService.getOfferedModels(); - return ResponseEntity.ok(models); - } - catch (PyrisConnectorException e) { - throw new InternalServerErrorException("Could not fetch available Iris models"); - } - } -} diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/iris/IrisVariantsResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/iris/IrisVariantsResource.java new file mode 100644 index 000000000000..96f3b754ff7d --- /dev/null +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/iris/IrisVariantsResource.java @@ -0,0 +1,56 @@ +package de.tum.in.www1.artemis.web.rest.iris; + +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Profile; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import de.tum.in.www1.artemis.domain.iris.settings.IrisSubSettingsType; +import de.tum.in.www1.artemis.security.annotations.EnforceAtLeastEditor; +import de.tum.in.www1.artemis.service.connectors.pyris.PyrisConnectorException; +import de.tum.in.www1.artemis.service.connectors.pyris.PyrisConnectorService; +import de.tum.in.www1.artemis.service.connectors.pyris.dto.PyrisVariantDTO; +import de.tum.in.www1.artemis.web.rest.errors.InternalServerErrorException; + +/** + * REST controller for managing the variants Pyris provides. + */ +@Profile("iris") +@RestController +@RequestMapping("api/") +public class IrisVariantsResource { + + private static final Logger log = LoggerFactory.getLogger(IrisVariantsResource.class); + + private final PyrisConnectorService pyrisConnectorService; + + public IrisVariantsResource(PyrisConnectorService pyrisConnectorService) { + this.pyrisConnectorService = pyrisConnectorService; + } + + /** + * GET iris/variants/{feature}: Retrieve all available variants offered by Pyris for a certain feature + * + * @param featureRaw the feature for which to retrieve the variants + * @return the {@link ResponseEntity} with status {@code 200 (Ok)} and with body a List of the variants + */ + @GetMapping("iris/variants/{feature}") + @EnforceAtLeastEditor + public ResponseEntity> getAllVariants(@PathVariable("feature") String featureRaw) { + var feature = IrisSubSettingsType.valueOf(featureRaw.toUpperCase().replace("-", "_")); + try { + var variants = pyrisConnectorService.getOfferedVariants(feature); + return ResponseEntity.ok(variants); + } + catch (PyrisConnectorException e) { + log.error("Could not fetch available variants for feature {}", feature, e); + throw new InternalServerErrorException("Could not fetch available variants for feature " + feature); + } + } +} diff --git a/src/main/resources/config/liquibase/changelog/20240825191919_changelog.xml b/src/main/resources/config/liquibase/changelog/20240825191919_changelog.xml new file mode 100644 index 000000000000..3ae7fd7ea038 --- /dev/null +++ b/src/main/resources/config/liquibase/changelog/20240825191919_changelog.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + DELETE FROM iris_sub_settings WHERE discriminator = 'HESTIA'; + + + + + + + + + + + UPDATE iris_sub_settings + SET allowed_variants = 'default', selected_variant = 'default' + WHERE id IN ( + SELECT iris_chat_settings_id FROM iris_settings WHERE discriminator = 'GLOBAL' + UNION + SELECT iris_competency_generation_settings_id FROM iris_settings WHERE discriminator = 'GLOBAL' + UNION + SELECT iris_lecture_ingestion_settings_id FROM iris_settings WHERE discriminator = 'GLOBAL' + ); + + + + + + + + + + + + + + DELETE FROM iris_json_message_content WHERE id IN ( + SELECT iris_message_content.id FROM iris_message_content + JOIN iris_message ON iris_message_content.message_id = iris_message.id + JOIN iris_session ON iris_message.session_id = iris_session.id + WHERE iris_session.discriminator = 'HESTIA' + ); + DELETE FROM iris_text_message_content WHERE id IN ( + SELECT iris_message_content.id FROM iris_message_content + JOIN iris_message ON iris_message_content.message_id = iris_message.id + JOIN iris_session ON iris_message.session_id = iris_session.id + WHERE iris_session.discriminator = 'HESTIA' + ); + DELETE FROM iris_message_content WHERE message_id IN ( + SELECT iris_message.id FROM iris_message + JOIN iris_session ON iris_message.session_id = iris_session.id + WHERE iris_session.discriminator = 'HESTIA' + ); + DELETE FROM iris_message WHERE session_id IN ( + SELECT id FROM iris_session WHERE discriminator = 'HESTIA' + ); + DELETE FROM iris_session WHERE discriminator = 'HESTIA'; + + + + + + + diff --git a/src/main/resources/config/liquibase/master.xml b/src/main/resources/config/liquibase/master.xml index c5be79948d2f..1f9b6f490969 100644 --- a/src/main/resources/config/liquibase/master.xml +++ b/src/main/resources/config/liquibase/master.xml @@ -21,6 +21,7 @@ + diff --git a/src/main/webapp/app/course/manage/detail/course-detail.component.ts b/src/main/webapp/app/course/manage/detail/course-detail.component.ts index 5954f3c0b7c0..29858e79b334 100644 --- a/src/main/webapp/app/course/manage/detail/course-detail.component.ts +++ b/src/main/webapp/app/course/manage/detail/course-detail.component.ts @@ -50,7 +50,6 @@ export class CourseDetailComponent implements OnInit, OnDestroy { communicationEnabled: boolean; irisEnabled = false; irisChatEnabled = false; - irisHestiaEnabled = false; ltiEnabled = false; isAthenaEnabled = false; tutorialEnabled = false; @@ -96,7 +95,6 @@ export class CourseDetailComponent implements OnInit, OnDestroy { if (this.irisEnabled) { const irisSettings = await firstValueFrom(this.irisSettingsService.getGlobalSettings()); this.irisChatEnabled = irisSettings?.irisChatSettings?.enabled ?? false; - this.irisHestiaEnabled = irisSettings?.irisHestiaSettings?.enabled ?? false; } this.route.data.subscribe(({ course }) => { if (course) { diff --git a/src/main/webapp/app/entities/iris/settings/iris-settings.model.ts b/src/main/webapp/app/entities/iris/settings/iris-settings.model.ts index 017c3ea2cffa..ab7f3b475908 100644 --- a/src/main/webapp/app/entities/iris/settings/iris-settings.model.ts +++ b/src/main/webapp/app/entities/iris/settings/iris-settings.model.ts @@ -1,10 +1,5 @@ import { BaseEntity } from 'app/shared/model/base-entity'; -import { - IrisChatSubSettings, - IrisCompetencyGenerationSubSettings, - IrisHestiaSubSettings, - IrisLectureIngestionSubSettings, -} from 'app/entities/iris/settings/iris-sub-settings.model'; +import { IrisChatSubSettings, IrisCompetencyGenerationSubSettings, IrisLectureIngestionSubSettings } from 'app/entities/iris/settings/iris-sub-settings.model'; export enum IrisSettingsType { GLOBAL = 'global', @@ -17,21 +12,14 @@ export abstract class IrisSettings implements BaseEntity { type: IrisSettingsType; irisChatSettings?: IrisChatSubSettings; irisLectureIngestionSettings?: IrisLectureIngestionSubSettings; - irisHestiaSettings?: IrisHestiaSubSettings; irisCompetencyGenerationSettings?: IrisCompetencyGenerationSubSettings; } export class IrisGlobalSettings implements IrisSettings { id?: number; type = IrisSettingsType.GLOBAL; - currentVersion?: number; - enableAutoUpdateChat?: boolean; - enableAutoUpdateLectureIngestion?: boolean; - enableAutoUpdateHestia?: boolean; - enableAutoUpdateCompetencyGeneration?: boolean; irisChatSettings?: IrisChatSubSettings; irisLectureIngestionSettings?: IrisLectureIngestionSubSettings; - irisHestiaSettings?: IrisHestiaSubSettings; irisCompetencyGenerationSettings?: IrisCompetencyGenerationSubSettings; } @@ -41,7 +29,6 @@ export class IrisCourseSettings implements IrisSettings { courseId?: number; irisChatSettings?: IrisChatSubSettings; irisLectureIngestionSettings?: IrisLectureIngestionSubSettings; - irisHestiaSettings?: IrisHestiaSubSettings; irisCompetencyGenerationSettings?: IrisCompetencyGenerationSubSettings; } diff --git a/src/main/webapp/app/entities/iris/settings/iris-sub-settings.model.ts b/src/main/webapp/app/entities/iris/settings/iris-sub-settings.model.ts index 8848394f1350..626155a43555 100644 --- a/src/main/webapp/app/entities/iris/settings/iris-sub-settings.model.ts +++ b/src/main/webapp/app/entities/iris/settings/iris-sub-settings.model.ts @@ -1,9 +1,7 @@ import { BaseEntity } from 'app/shared/model/base-entity'; -import { IrisTemplate } from 'app/entities/iris/settings/iris-template'; export enum IrisSubSettingsType { CHAT = 'chat', - HESTIA = 'hestia', COMPETENCY_GENERATION = 'competency-generation', LECTURE_INGESTION = 'lecture-ingestion', } @@ -12,13 +10,12 @@ export abstract class IrisSubSettings implements BaseEntity { id?: number; type: IrisSubSettingsType; enabled = false; - allowedModels?: string[]; - preferredModel?: string; + allowedVariants?: string[]; + selectedVariant?: string; } export class IrisChatSubSettings extends IrisSubSettings { type = IrisSubSettingsType.CHAT; - template?: IrisTemplate; rateLimit?: number; rateLimitTimeframeHours?: number; } @@ -28,12 +25,6 @@ export class IrisLectureIngestionSubSettings extends IrisSubSettings { autoIngestOnLectureAttachmentUpload: boolean; } -export class IrisHestiaSubSettings extends IrisSubSettings { - type = IrisSubSettingsType.HESTIA; - template?: IrisTemplate; -} - export class IrisCompetencyGenerationSubSettings extends IrisSubSettings { type = IrisSubSettingsType.COMPETENCY_GENERATION; - template?: IrisTemplate; } diff --git a/src/main/webapp/app/entities/iris/settings/iris-template.ts b/src/main/webapp/app/entities/iris/settings/iris-template.ts deleted file mode 100644 index eb0c8a90041c..000000000000 --- a/src/main/webapp/app/entities/iris/settings/iris-template.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { BaseEntity } from 'app/shared/model/base-entity'; - -export class IrisTemplate implements BaseEntity { - id?: number; - content = ''; -} diff --git a/src/main/webapp/app/entities/iris/settings/iris-model.ts b/src/main/webapp/app/entities/iris/settings/iris-variant.ts similarity index 69% rename from src/main/webapp/app/entities/iris/settings/iris-model.ts rename to src/main/webapp/app/entities/iris/settings/iris-variant.ts index 94a7f9202d92..3fbecfee8c49 100644 --- a/src/main/webapp/app/entities/iris/settings/iris-model.ts +++ b/src/main/webapp/app/entities/iris/settings/iris-variant.ts @@ -1,4 +1,4 @@ -export class IrisModel { +export class IrisVariant { id: string; name: string; description: string; diff --git a/src/main/webapp/app/exercises/shared/exercise-hint/manage/exercise-hint-update.component.html b/src/main/webapp/app/exercises/shared/exercise-hint/manage/exercise-hint-update.component.html index 5c80f6e0c868..6bf170d0bac9 100644 --- a/src/main/webapp/app/exercises/shared/exercise-hint/manage/exercise-hint-update.component.html +++ b/src/main/webapp/app/exercises/shared/exercise-hint/manage/exercise-hint-update.component.html @@ -45,21 +45,6 @@

- @if (exerciseHint.type === HintType.CODE && irisSettings?.irisHestiaSettings?.enabled) { -
- Generate description - -
- }
diff --git a/src/main/webapp/app/iris/iris.module.ts b/src/main/webapp/app/iris/iris.module.ts index 5473966d0275..aacdbd0b64cf 100644 --- a/src/main/webapp/app/iris/iris.module.ts +++ b/src/main/webapp/app/iris/iris.module.ts @@ -14,7 +14,6 @@ import { IrisCommonSubSettingsUpdateComponent } from './settings/iris-settings-u import { IrisCourseSettingsUpdateComponent } from 'app/iris/settings/iris-course-settings-update/iris-course-settings-update.component'; import { IrisExerciseSettingsUpdateComponent } from 'app/iris/settings/iris-exercise-settings-update/iris-exercise-settings-update.component'; import { IrisLogoComponent } from './iris-logo/iris-logo.component'; -import { IrisGlobalAutoupdateSettingsUpdateComponent } from './settings/iris-settings-update/iris-global-autoupdate-settings-update/iris-global-autoupdate-settings-update.component'; import { IrisExerciseChatbotButtonComponent } from 'app/iris/exercise-chatbot/exercise-chatbot-button.component'; import { IrisChatbotWidgetComponent } from 'app/iris/exercise-chatbot/widget/chatbot-widget.component'; import { IrisEnabledComponent } from 'app/iris/settings/shared/iris-enabled.component'; @@ -36,7 +35,6 @@ import { CourseChatbotComponent } from 'app/iris/course-chatbot/course-chatbot.c IrisExerciseSettingsUpdateComponent, IrisCommonSubSettingsUpdateComponent, IrisLogoComponent, - IrisGlobalAutoupdateSettingsUpdateComponent, IrisEnabledComponent, ChatStatusBarComponent, IrisLogoButtonComponent, diff --git a/src/main/webapp/app/iris/settings/iris-settings-update/iris-common-sub-settings-update/iris-common-sub-settings-update.component.html b/src/main/webapp/app/iris/settings/iris-settings-update/iris-common-sub-settings-update/iris-common-sub-settings-update.component.html index 4132e9fab587..95238efecaf9 100644 --- a/src/main/webapp/app/iris/settings/iris-settings-update/iris-common-sub-settings-update/iris-common-sub-settings-update.component.html +++ b/src/main/webapp/app/iris/settings/iris-settings-update/iris-common-sub-settings-update/iris-common-sub-settings-update.component.html @@ -24,39 +24,62 @@ jhiTranslate="artemisApp.iris.settings.subSettings.enabled.off" > -

-: +

+: @if (parentSubSettings) {
}
- @for (model of allIrisModels; track model) { + @for (variant of availableVariants; track variant) {
-
}
+
:
+
+
+ +
+ @if (parentSubSettings) { + + } + @for (model of allowedVariants; track model) { + + } +
+
+ @if (!subSettings?.selectedVariant) { + {{ getSelectedVariantNameParent() }} + } +
diff --git a/src/main/webapp/app/iris/settings/iris-settings-update/iris-common-sub-settings-update/iris-common-sub-settings-update.component.ts b/src/main/webapp/app/iris/settings/iris-settings-update/iris-common-sub-settings-update/iris-common-sub-settings-update.component.ts index 723b8ddb21a9..ba5bd6573691 100644 --- a/src/main/webapp/app/iris/settings/iris-settings-update/iris-common-sub-settings-update/iris-common-sub-settings-update.component.ts +++ b/src/main/webapp/app/iris/settings/iris-settings-update/iris-common-sub-settings-update/iris-common-sub-settings-update.component.ts @@ -1,10 +1,11 @@ import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core'; import { IrisSubSettings, IrisSubSettingsType } from 'app/entities/iris/settings/iris-sub-settings.model'; -import { IrisModel } from 'app/entities/iris/settings/iris-model'; +import { IrisVariant } from 'app/entities/iris/settings/iris-variant'; import { AccountService } from 'app/core/auth/account.service'; import { ButtonType } from 'app/shared/components/button.component'; import { faTrash } from '@fortawesome/free-solid-svg-icons'; import { IrisSettingsType } from 'app/entities/iris/settings/iris-settings.model'; +import { IrisSettingsService } from 'app/iris/settings/shared/iris-settings.service'; @Component({ selector: 'jhi-iris-common-sub-settings-update', @@ -17,9 +18,6 @@ export class IrisCommonSubSettingsUpdateComponent implements OnInit, OnChanges { @Input() parentSubSettings?: IrisSubSettings; - @Input() - allIrisModels: IrisModel[]; - @Input() settingsType: IrisSettingsType; @@ -28,9 +26,11 @@ export class IrisCommonSubSettingsUpdateComponent implements OnInit, OnChanges { isAdmin: boolean; - inheritAllowedModels: boolean; + inheritAllowedVariants: boolean; + + availableVariants: IrisVariant[] = []; - allowedIrisModels: IrisModel[]; + allowedVariants: IrisVariant[] = []; enabled: boolean; @@ -42,49 +42,62 @@ export class IrisCommonSubSettingsUpdateComponent implements OnInit, OnChanges { // Icons faTrash = faTrash; - constructor(accountService: AccountService) { + constructor( + accountService: AccountService, + private irisSettingsService: IrisSettingsService, + ) { this.isAdmin = accountService.isAdmin(); } ngOnInit() { this.enabled = this.subSettings?.enabled ?? false; - this.allowedIrisModels = this.getAvailableModels(); - this.inheritAllowedModels = !!(!this.subSettings?.allowedModels && this.parentSubSettings); + this.loadVariants(); + this.inheritAllowedVariants = !!(!this.subSettings?.allowedVariants && this.parentSubSettings); } ngOnChanges(changes: SimpleChanges): void { - if (changes.allIrisModels) { - this.allowedIrisModels = this.getAvailableModels(); + if (changes.availableVariants) { + this.allowedVariants = this.getAllowedVariants(); } if (changes.subSettings) { this.enabled = this.subSettings?.enabled ?? false; } } - getAvailableModels(): IrisModel[] { - return this.allIrisModels.filter((model) => (this.subSettings?.allowedModels ?? this.parentSubSettings?.allowedModels ?? []).includes(model.id)); + loadVariants(): void { + if (!this.subSettings?.type) { + return; + } + this.irisSettingsService.getVariantsForFeature(this.subSettings?.type).subscribe((variants) => { + this.availableVariants = variants ?? this.availableVariants; + this.allowedVariants = this.getAllowedVariants(); + }); + } + + getAllowedVariants(): IrisVariant[] { + return this.availableVariants.filter((variant) => (this.subSettings?.allowedVariants ?? this.parentSubSettings?.allowedVariants ?? []).includes(variant.id)); } - getPreferredModelName(): string | undefined { - return this.allIrisModels.find((model) => model.id === this.subSettings?.preferredModel)?.name ?? this.subSettings?.preferredModel; + getSelectedVariantName(): string | undefined { + return this.availableVariants.find((variant) => variant.id === this.subSettings?.selectedVariant)?.name ?? this.subSettings?.selectedVariant; } - getPreferredModelNameParent(): string | undefined { - return this.allIrisModels.find((model) => model.id === this.parentSubSettings?.preferredModel)?.name ?? this.parentSubSettings?.preferredModel; + getSelectedVariantNameParent(): string | undefined { + return this.availableVariants.find((variant) => variant.id === this.parentSubSettings?.selectedVariant)?.name ?? this.parentSubSettings?.selectedVariant; } - onAllowedIrisModelsSelectionChange(model: IrisModel) { - this.inheritAllowedModels = false; - if (this.allowedIrisModels.includes(model)) { - this.allowedIrisModels = this.allowedIrisModels.filter((m) => m !== model); + onAllowedIrisVariantsSelectionChange(variant: IrisVariant) { + this.inheritAllowedVariants = false; + if (this.allowedVariants.map((variant) => variant.id).includes(variant.id)) { + this.allowedVariants = this.allowedVariants.filter((m) => m.id !== variant.id); } else { - this.allowedIrisModels.push(model); + this.allowedVariants.push(variant); } - this.subSettings!.allowedModels = this.allowedIrisModels.map((model) => model.id); + this.subSettings!.allowedVariants = this.allowedVariants.map((variant) => variant.id); } - setModel(model: IrisModel | undefined) { - this.subSettings!.preferredModel = model?.id; + setVariant(variant: IrisVariant | undefined) { + this.subSettings!.selectedVariant = variant?.id; } onEnabledChange() { @@ -101,12 +114,12 @@ export class IrisCommonSubSettingsUpdateComponent implements OnInit, OnChanges { this.onEnabledChange(); } - onInheritAllowedModelsChange() { - if (this.inheritAllowedModels) { - this.subSettings!.allowedModels = undefined; - this.allowedIrisModels = this.getAvailableModels(); + onInheritAllowedVariantsChange() { + if (this.inheritAllowedVariants) { + this.subSettings!.allowedVariants = undefined; + this.allowedVariants = this.getAllowedVariants(); } else { - this.subSettings!.allowedModels = this.allowedIrisModels.map((model) => model.id); + this.subSettings!.allowedVariants = this.allowedVariants.map((variant) => variant.id); } } diff --git a/src/main/webapp/app/iris/settings/iris-settings-update/iris-global-autoupdate-settings-update/iris-global-autoupdate-settings-update.component.html b/src/main/webapp/app/iris/settings/iris-settings-update/iris-global-autoupdate-settings-update/iris-global-autoupdate-settings-update.component.html deleted file mode 100644 index efb780138e8c..000000000000 --- a/src/main/webapp/app/iris/settings/iris-settings-update/iris-global-autoupdate-settings-update/iris-global-autoupdate-settings-update.component.html +++ /dev/null @@ -1,20 +0,0 @@ -@if (irisSettings) { -
-
- - -
-
- - -
-
- - -
-
- - -
-
-} diff --git a/src/main/webapp/app/iris/settings/iris-settings-update/iris-global-autoupdate-settings-update/iris-global-autoupdate-settings-update.component.ts b/src/main/webapp/app/iris/settings/iris-settings-update/iris-global-autoupdate-settings-update/iris-global-autoupdate-settings-update.component.ts deleted file mode 100644 index 404132633566..000000000000 --- a/src/main/webapp/app/iris/settings/iris-settings-update/iris-global-autoupdate-settings-update/iris-global-autoupdate-settings-update.component.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Component, EventEmitter, Input, Output } from '@angular/core'; -import { IrisGlobalSettings } from 'app/entities/iris/settings/iris-settings.model'; -import { IrisSubSettings } from 'app/entities/iris/settings/iris-sub-settings.model'; - -@Component({ - selector: 'jhi-iris-global-autoupdate-settings-update', - templateUrl: './iris-global-autoupdate-settings-update.component.html', -}) -export class IrisGlobalAutoupdateSettingsUpdateComponent { - @Input() - irisSettings?: IrisGlobalSettings; - - @Output() - onChanges = new EventEmitter(); -} diff --git a/src/main/webapp/app/iris/settings/iris-settings-update/iris-settings-update.component.html b/src/main/webapp/app/iris/settings/iris-settings-update/iris-settings-update.component.html index b28a3a4c690c..c1e15d609c1f 100644 --- a/src/main/webapp/app/iris/settings/iris-settings-update/iris-settings-update.component.html +++ b/src/main/webapp/app/iris/settings/iris-settings-update/iris-settings-update.component.html @@ -11,18 +11,11 @@ @if (irisSettings) {
- @if (settingsType === GLOBAL) { -
-

- -
- }

@@ -34,33 +27,21 @@

-

- } - @if (settingsType === COURSE) { -
- - -
- } - @if (settingsType !== EXERCISE) { -
-
-

- +
+ + @if (settingsType === COURSE) { +
+ + +
+ }
} @if (settingsType !== EXERCISE) { @@ -70,7 +51,6 @@

diff --git a/src/main/webapp/app/iris/settings/iris-settings-update/iris-settings-update.component.ts b/src/main/webapp/app/iris/settings/iris-settings-update/iris-settings-update.component.ts index 5bde2f10f791..aa9adac6be64 100644 --- a/src/main/webapp/app/iris/settings/iris-settings-update/iris-settings-update.component.ts +++ b/src/main/webapp/app/iris/settings/iris-settings-update/iris-settings-update.component.ts @@ -6,15 +6,9 @@ import { Observable } from 'rxjs'; import { AlertService } from 'app/core/util/alert.service'; import { ButtonType } from 'app/shared/components/button.component'; import { faRotate, faSave } from '@fortawesome/free-solid-svg-icons'; -import { IrisModel } from 'app/entities/iris/settings/iris-model'; import { ComponentCanDeactivate } from 'app/shared/guard/can-deactivate.model'; import { cloneDeep, isEqual } from 'lodash-es'; -import { - IrisChatSubSettings, - IrisCompetencyGenerationSubSettings, - IrisHestiaSubSettings, - IrisLectureIngestionSubSettings, -} from 'app/entities/iris/settings/iris-sub-settings.model'; +import { IrisChatSubSettings, IrisCompetencyGenerationSubSettings, IrisLectureIngestionSubSettings } from 'app/entities/iris/settings/iris-sub-settings.model'; import { AccountService } from 'app/core/auth/account.service'; @Component({ @@ -30,7 +24,6 @@ export class IrisSettingsUpdateComponent implements OnInit, DoCheck, ComponentCa public exerciseId?: number; public irisSettings?: IrisSettings; public parentIrisSettings?: IrisSettings; - public allIrisModels?: IrisModel[]; originalIrisSettings?: IrisSettings; @@ -77,13 +70,6 @@ export class IrisSettingsUpdateComponent implements OnInit, DoCheck, ComponentCa return !this.isDirty; } - loadIrisModels(): void { - this.irisSettingsService.getIrisModels().subscribe((models) => { - this.allIrisModels = models; - this.isLoading = false; - }); - } - loadIrisSettings(): void { this.isLoading = true; this.loadIrisSettingsObservable().subscribe((settings) => { @@ -116,9 +102,6 @@ export class IrisSettingsUpdateComponent implements OnInit, DoCheck, ComponentCa if (!this.irisSettings.irisLectureIngestionSettings) { this.irisSettings.irisLectureIngestionSettings = new IrisLectureIngestionSubSettings(); } - if (!this.irisSettings.irisHestiaSettings) { - this.irisSettings.irisHestiaSettings = new IrisHestiaSubSettings(); - } if (!this.irisSettings.irisCompetencyGenerationSettings) { this.irisSettings.irisCompetencyGenerationSettings = new IrisCompetencyGenerationSubSettings(); } diff --git a/src/main/webapp/app/iris/settings/shared/iris-enabled.component.ts b/src/main/webapp/app/iris/settings/shared/iris-enabled.component.ts index ef8d44419841..be64bd5d856b 100644 --- a/src/main/webapp/app/iris/settings/shared/iris-enabled.component.ts +++ b/src/main/webapp/app/iris/settings/shared/iris-enabled.component.ts @@ -56,9 +56,6 @@ export class IrisEnabledComponent implements OnInit { case IrisSubSettingsType.CHAT: this.irisSubSettings = this.irisSettings?.irisChatSettings; break; - case IrisSubSettingsType.HESTIA: - this.irisSubSettings = this.irisSettings?.irisHestiaSettings; - break; case IrisSubSettingsType.COMPETENCY_GENERATION: this.irisSubSettings = this.irisSettings?.irisCompetencyGenerationSettings; break; diff --git a/src/main/webapp/app/iris/settings/shared/iris-settings.service.ts b/src/main/webapp/app/iris/settings/shared/iris-settings.service.ts index 475540bad156..7273e5958cf6 100644 --- a/src/main/webapp/app/iris/settings/shared/iris-settings.service.ts +++ b/src/main/webapp/app/iris/settings/shared/iris-settings.service.ts @@ -3,7 +3,8 @@ import { HttpClient, HttpResponse } from '@angular/common/http'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { IrisCourseSettings, IrisExerciseSettings, IrisGlobalSettings } from 'app/entities/iris/settings/iris-settings.model'; -import { IrisModel } from 'app/entities/iris/settings/iris-model'; +import { IrisVariant } from 'app/entities/iris/settings/iris-variant'; +import { IrisSubSettingsType } from 'app/entities/iris/settings/iris-sub-settings.model'; /** * Service for calling the Iris settings endpoints on the server @@ -90,9 +91,11 @@ export class IrisSettingsService { } /** - * Get the global Iris settings + * Get the available variants for a feature */ - getIrisModels(): Observable { - return this.http.get(`${this.resourceUrl}/iris/models`, { observe: 'response' }).pipe(map((res: HttpResponse) => res.body ?? [])); + getVariantsForFeature(feature: IrisSubSettingsType): Observable { + return this.http + .get(`${this.resourceUrl}/iris/variants/${feature}`, { observe: 'response' }) + .pipe(map((res: HttpResponse) => res.body ?? [])); } } diff --git a/src/main/webapp/i18n/de/iris.json b/src/main/webapp/i18n/de/iris.json index 34572e7581bd..4044841119d8 100644 --- a/src/main/webapp/i18n/de/iris.json +++ b/src/main/webapp/i18n/de/iris.json @@ -28,14 +28,14 @@ "hestiaSettings": "Hestia Einstellungen", "competencyGenerationSettings": "Kompetenzgenerierung Einstellungen", "enabled-disabled": "Aktiviert/Deaktiviert", - "models": { - "title": "Modelle", - "allowedModels": { - "title": "Erlaubte Modelle", - "inheritSwitch": "Erbe erlaubte Modelle" + "variants": { + "title": "Varianten", + "allowedVariants": { + "title": "Erlaubte Varianten", + "inheritSwitch": "Erbe erlaubte Varianten" }, - "preferredModel": { - "title": "Präferiertes Modell", + "selectedVariant": { + "title": "Genuzte Variante", "inherit": "Erben" } }, @@ -63,21 +63,13 @@ "global": "Globale Iris Einstellungen", "course": "Kurs Iris Einstellungen", "programmingExercise": "Programmieraufgabe Iris Einstellungen" - }, - "autoUpdate": { - "title": "Auto Update Einstellungen", - "tooltip": "Wenn aktiviert, werden die spezifischen globalen Iris Einstellungen automatisch aktualisiert, wenn eine neue Version von Artemis neue Iris Einstellungen bereitstellt.", - "chatLabel": "Auto Update der Chat Einstellungen", - "hestiaLabel": "Auto Update der Hestia Einstellungen", - "lectureIngestionLabel": "Auto Update der Vorlesungen Erfassung Einstellungen", - "competencyGenerationLabel": "Auto Update der Kompetenzgenerierung Einstellungen" } }, "error": { "forbidden": "Artemis ist nicht konfiguriert, um Iris zu verwenden. (Ungültiges Token)", "internalPyrisError": "Ein interner Fehler beim Kommunizieren mit dem LLM ist aufgetreten. Die Fehlermeldung lautet: {{ pyrisErrorMessage }}.", "invalidTemplate": "Die Vorlage ist ungültig. Die Fehlermeldung lautet: {{ pyrisErrorMessage }}.", - "noModelAvailable": "Das Modell {{ model }} steht nicht zur Verfügung. Bitte kontaktiere einen Administrator, wenn das Problem weiterhin besteht.", + "noVariantAvailable": "Die Variante {{ variant }} steht nicht zur Verfügung. Bitte kontaktiere einen Administrator, wenn das Problem weiterhin besteht.", "noResponse": "Es wurde keine Antwort von Iris empfangen. Bitte kontaktiere einen Administrator, wenn das Problem weiterhin besteht.", "parseResponse": "Ein Fehler ist beim Parsen der Antwort von Iris aufgetreten. Ursache: {{ cause }}" }, diff --git a/src/main/webapp/i18n/en/iris.json b/src/main/webapp/i18n/en/iris.json index 6afbb928d5c9..21aed4b477e7 100644 --- a/src/main/webapp/i18n/en/iris.json +++ b/src/main/webapp/i18n/en/iris.json @@ -28,14 +28,14 @@ "hestiaSettings": "Hestia Settings", "competencyGenerationSettings": "Competency Generation Settings", "enabled-disabled": "Enabled/Disabled", - "models": { - "title": "Models", - "allowedModels": { - "title": "Allowed Models", - "inheritSwitch": "Inherit Allowed Models" + "variants": { + "title": "Variants", + "allowedVariants": { + "title": "Allowed Variants", + "inheritSwitch": "Inherit Allowed Variants" }, - "preferredModel": { - "title": "Preferred Model", + "selectedVariant": { + "title": "Selected Variant", "inherit": "Inherit" } }, @@ -63,21 +63,13 @@ "global": "Global Iris Settings", "course": "Course Iris Settings", "exercise": "Exercise Iris Settings" - }, - "autoUpdate": { - "title": "Auto Update Settings", - "tooltip": "If enabled, the specific global Iris settings will be automatically updated when a new release of Artemis provides new Iris settings.", - "chatLabel": "Auto Update Chat Settings", - "hestiaLabel": "Auto Update Hestia Settings", - "lectureIngestionLabel": "Auto Update Lecture Ingestion Settings", - "competencyGenerationLabel": "Auto Update Competency Generation Settings" } }, "error": { "forbidden": "Artemis is not configured to use Iris. (Invalid token)", "internalPyrisError": "An internal error when communicating with the LLM occurred. Error message is: {{ pyrisErrorMessage }}.", "invalidTemplate": "The template is invalid. Error message is: {{ pyrisErrorMessage }}.", - "noModelAvailable": "Model {{ model }} is not available to use. Please contact your administrator if this problem persists.", + "noVariantAvailable": "Variant {{ variant }} is not available to use. Please contact your administrator if this problem persists.", "noResponse": "No response from Iris was received.", "parseResponse": "An error occurred while parsing the response from Iris. Cause: {{ cause }}" }, diff --git a/src/test/java/de/tum/in/www1/artemis/connector/IrisRequestMockProvider.java b/src/test/java/de/tum/in/www1/artemis/connector/IrisRequestMockProvider.java index 3f248a9e598c..747f801f61f4 100644 --- a/src/test/java/de/tum/in/www1/artemis/connector/IrisRequestMockProvider.java +++ b/src/test/java/de/tum/in/www1/artemis/connector/IrisRequestMockProvider.java @@ -27,8 +27,9 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import de.tum.in.www1.artemis.domain.iris.settings.IrisSubSettingsType; import de.tum.in.www1.artemis.service.connectors.pyris.dto.PyrisHealthStatusDTO; -import de.tum.in.www1.artemis.service.connectors.pyris.dto.PyrisModelDTO; +import de.tum.in.www1.artemis.service.connectors.pyris.dto.PyrisVariantDTO; import de.tum.in.www1.artemis.service.connectors.pyris.dto.chat.exercise.PyrisExerciseChatPipelineExecutionDTO; import de.tum.in.www1.artemis.service.connectors.pyris.dto.competency.PyrisCompetencyExtractionPipelineExecutionDTO; import de.tum.in.www1.artemis.service.connectors.pyris.dto.lectureingestionwebhook.PyrisWebhookLectureIngestionExecutionDTO; @@ -51,8 +52,8 @@ public class IrisRequestMockProvider { @Value("${artemis.iris.url}/api/v1/webhooks") private URL webhooksApiURL; - @Value("${artemis.iris.url}/api/v1/models") - private URL modelsApiURL; + @Value("${artemis.iris.url}/api/v1/pipelines/") + private String variantsApiBaseURL; @Value("${artemis.iris.url}/api/v1/health/") private URL healthApiURL; @@ -138,11 +139,11 @@ public void mockIngestionWebhookRunError(int httpStatus) { // @formatter:on } - public void mockModelsResponse() throws JsonProcessingException { - var irisModelDTO = new PyrisModelDTO("TEST_MODEL", "Test model", "Test description"); - var irisModelDTOArray = new PyrisModelDTO[] { irisModelDTO }; + public void mockVariantsResponse(IrisSubSettingsType feature) throws JsonProcessingException { + var irisModelDTO = new PyrisVariantDTO("TEST_MODEL", "Test model", "Test description"); + var irisModelDTOArray = new PyrisVariantDTO[] { irisModelDTO }; // @formatter:off - mockServer.expect(ExpectedCount.once(), requestTo(modelsApiURL.toString())) + mockServer.expect(ExpectedCount.once(), requestTo(variantsApiBaseURL + feature.name() + "/variants")) .andExpect(method(HttpMethod.GET)) .andRespond(withSuccess(mapper.writeValueAsString(irisModelDTOArray), MediaType.APPLICATION_JSON)); // @formatter:on @@ -168,9 +169,9 @@ public void mockStatusResponses() throws JsonProcessingException { /** * Mocks a get model error from the Pyris models endpoint */ - public void mockModelsError() { + public void mockVariantsError(IrisSubSettingsType feature) { // @formatter:off - mockServer.expect(ExpectedCount.once(), requestTo(modelsApiURL.toString())) + mockServer.expect(ExpectedCount.once(), requestTo(variantsApiBaseURL + feature.name() + "/variants")) .andExpect(method(HttpMethod.GET)) .andRespond(withRawStatus(418)); // @formatter:on diff --git a/src/test/java/de/tum/in/www1/artemis/iris/AbstractIrisIntegrationTest.java b/src/test/java/de/tum/in/www1/artemis/iris/AbstractIrisIntegrationTest.java index dba05fa055cd..afb9e98259b6 100644 --- a/src/test/java/de/tum/in/www1/artemis/iris/AbstractIrisIntegrationTest.java +++ b/src/test/java/de/tum/in/www1/artemis/iris/AbstractIrisIntegrationTest.java @@ -20,12 +20,10 @@ import de.tum.in.www1.artemis.connector.IrisRequestMockProvider; import de.tum.in.www1.artemis.domain.Course; import de.tum.in.www1.artemis.domain.ProgrammingExercise; -import de.tum.in.www1.artemis.domain.iris.IrisTemplate; import de.tum.in.www1.artemis.domain.iris.settings.IrisSubSettings; import de.tum.in.www1.artemis.exercise.programming.ProgrammingExerciseUtilService; import de.tum.in.www1.artemis.repository.ProgrammingExerciseRepository; import de.tum.in.www1.artemis.repository.iris.IrisSettingsRepository; -import de.tum.in.www1.artemis.repository.iris.IrisTemplateRepository; import de.tum.in.www1.artemis.service.iris.settings.IrisSettingsService; public abstract class AbstractIrisIntegrationTest extends AbstractSpringIntegrationLocalCILocalVCTest { @@ -33,9 +31,6 @@ public abstract class AbstractIrisIntegrationTest extends AbstractSpringIntegrat @Autowired protected IrisSettingsService irisSettingsService; - @Autowired - protected IrisTemplateRepository irisTemplateRepository; - @Autowired @Qualifier("irisRequestMockProvider") protected IrisRequestMockProvider irisRequestMockProvider; @@ -64,7 +59,6 @@ void tearDown() throws Exception { protected void activateIrisGlobally() { var globalSettings = irisSettingsService.getGlobalSettings(); activateSubSettings(globalSettings.getIrisChatSettings()); - activateSubSettings(globalSettings.getIrisHestiaSettings()); activateSubSettings(globalSettings.getIrisLectureIngestionSettings()); activateSubSettings(globalSettings.getIrisCompetencyGenerationSettings()); irisSettingsRepository.save(globalSettings); @@ -77,21 +71,16 @@ protected void activateIrisGlobally() { */ private void activateSubSettings(IrisSubSettings settings) { settings.setEnabled(true); - settings.setPreferredModel(null); - settings.setAllowedModels(new TreeSet<>(Set.of("dummy"))); + settings.setSelectedVariant("default"); + settings.setAllowedVariants(new TreeSet<>(Set.of("default"))); } protected void activateIrisFor(Course course) { var courseSettings = irisSettingsService.getDefaultSettingsFor(course); activateSubSettings(courseSettings.getIrisChatSettings()); - courseSettings.getIrisChatSettings().setTemplate(createDummyTemplate()); - - activateSubSettings(courseSettings.getIrisHestiaSettings()); - courseSettings.getIrisHestiaSettings().setTemplate(createDummyTemplate()); activateSubSettings(courseSettings.getIrisCompetencyGenerationSettings()); - courseSettings.getIrisCompetencyGenerationSettings().setTemplate(createDummyTemplate()); activateSubSettings(courseSettings.getIrisLectureIngestionSettings()); @@ -101,14 +90,9 @@ protected void activateIrisFor(Course course) { protected void activateIrisFor(ProgrammingExercise exercise) { var exerciseSettings = irisSettingsService.getDefaultSettingsFor(exercise); activateSubSettings(exerciseSettings.getIrisChatSettings()); - exerciseSettings.getIrisChatSettings().setTemplate(createDummyTemplate()); irisSettingsRepository.save(exerciseSettings); } - protected IrisTemplate createDummyTemplate() { - return new IrisTemplate("Hello World"); - } - /** * Verify that the given messages were sent through the websocket for the given user and topic. * diff --git a/src/test/java/de/tum/in/www1/artemis/iris/PyrisConnectorServiceTest.java b/src/test/java/de/tum/in/www1/artemis/iris/PyrisConnectorServiceTest.java index c1ff9d381d47..4e2cf821570e 100644 --- a/src/test/java/de/tum/in/www1/artemis/iris/PyrisConnectorServiceTest.java +++ b/src/test/java/de/tum/in/www1/artemis/iris/PyrisConnectorServiceTest.java @@ -5,12 +5,13 @@ import java.util.stream.Stream; -import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.MethodSource; import org.springframework.beans.factory.annotation.Autowired; +import de.tum.in.www1.artemis.domain.iris.settings.IrisSubSettingsType; import de.tum.in.www1.artemis.service.connectors.pyris.PyrisConnectorException; import de.tum.in.www1.artemis.service.connectors.pyris.PyrisConnectorService; import de.tum.in.www1.artemis.service.iris.exception.IrisForbiddenException; @@ -49,20 +50,22 @@ void testExceptionIngestionV2(int httpStatus, Class exceptionClass) { assertThatThrownBy(() -> pyrisConnectorService.executeLectureWebhook("fullIngestion", null)).isInstanceOf(exceptionClass); } - @Test - void testOfferedModels() throws Exception { - irisRequestMockProvider.mockModelsResponse(); + @ParameterizedTest + @EnumSource(IrisSubSettingsType.class) + void testOfferedModels(IrisSubSettingsType feature) throws Exception { + irisRequestMockProvider.mockVariantsResponse(feature); - var offeredModels = pyrisConnectorService.getOfferedModels(); + var offeredModels = pyrisConnectorService.getOfferedVariants(feature); assertThat(offeredModels).hasSize(1); assertThat(offeredModels.getFirst().id()).isEqualTo("TEST_MODEL"); } - @Test - void testOfferedModelsError() { - irisRequestMockProvider.mockModelsError(); + @ParameterizedTest + @EnumSource(IrisSubSettingsType.class) + void testOfferedModelsError(IrisSubSettingsType feature) { + irisRequestMockProvider.mockVariantsError(feature); - assertThatThrownBy(() -> pyrisConnectorService.getOfferedModels()).isInstanceOf(PyrisConnectorException.class); + assertThatThrownBy(() -> pyrisConnectorService.getOfferedVariants(feature)).isInstanceOf(PyrisConnectorException.class); } } diff --git a/src/test/java/de/tum/in/www1/artemis/iris/settings/IrisSettingsIntegrationTest.java b/src/test/java/de/tum/in/www1/artemis/iris/settings/IrisSettingsIntegrationTest.java index 300e9ae14d0d..3605ec80a28e 100644 --- a/src/test/java/de/tum/in/www1/artemis/iris/settings/IrisSettingsIntegrationTest.java +++ b/src/test/java/de/tum/in/www1/artemis/iris/settings/IrisSettingsIntegrationTest.java @@ -17,7 +17,6 @@ import de.tum.in.www1.artemis.domain.iris.settings.IrisCompetencyGenerationSubSettings; import de.tum.in.www1.artemis.domain.iris.settings.IrisCourseSettings; import de.tum.in.www1.artemis.domain.iris.settings.IrisExerciseSettings; -import de.tum.in.www1.artemis.domain.iris.settings.IrisHestiaSubSettings; import de.tum.in.www1.artemis.domain.iris.settings.IrisLectureIngestionSubSettings; import de.tum.in.www1.artemis.domain.iris.settings.IrisSettings; import de.tum.in.www1.artemis.iris.AbstractIrisIntegrationTest; @@ -56,7 +55,7 @@ void getMissingSettingsForCourse() throws Exception { assertThat(loadedSettings2).isNotNull().usingRecursiveComparison().ignoringFieldsOfTypes(HashSet.class, TreeSet.class).ignoringActualNullFields() .isEqualTo(irisSettingsService.getCombinedIrisSettingsFor(course, false)); assertThat(loadedSettings1).isNotNull().usingRecursiveComparison() - .ignoringFields("id", "course", "irisChatSettings.id", "iris_lecture_ingestion_settings_id", "irisHestiaSettings.id", "irisCompetencyGenerationSettings.id") + .ignoringFields("id", "course", "irisChatSettings.id", "iris_lecture_ingestion_settings_id", "irisCompetencyGenerationSettings.id") .isEqualTo(irisSettingsService.getDefaultSettingsFor(course)); } @@ -71,8 +70,8 @@ void getCourseSettings() throws Exception { var loadedSettings2 = request.get("/api/courses/" + course.getId() + "/iris-settings", HttpStatus.OK, IrisCombinedSettingsDTO.class); assertThat(loadedSettings1).isNotNull().usingRecursiveComparison() - .ignoringFields("id", "course", "irisChatSettings.id", "irisLectureIngestionSettings.id", "irisHestiaSettings.id", "irisCompetencyGenerationSettings.id") - .ignoringExpectedNullFields().isEqualTo(loadedSettings2); + .ignoringFields("id", "course", "irisChatSettings.id", "irisLectureIngestionSettings.id", "irisCompetencyGenerationSettings.id").ignoringExpectedNullFields() + .isEqualTo(loadedSettings2); assertThat(loadedSettings1).isNotNull().usingRecursiveComparison().ignoringFields("course") .isEqualTo(irisSettingsRepository.findCourseSettings(course.getId()).orElseThrow()); } @@ -87,10 +86,9 @@ void getCourseSettingsAsUser() throws Exception { request.get("/api/courses/" + course.getId() + "/raw-iris-settings", HttpStatus.FORBIDDEN, IrisSettings.class); var loadedSettings = request.get("/api/courses/" + course.getId() + "/iris-settings", HttpStatus.OK, IrisCombinedSettingsDTO.class); - assertThat(loadedSettings) - .isNotNull().usingRecursiveComparison().ignoringCollectionOrderInFields("irisChatSettings.allowedModels", "irisLectureIngestionSettings.allowedModels", - "irisCompetencyGenerationSettings.allowedModels", "irisHestiaSettings.allowedModels") - .ignoringFields("id").isEqualTo(irisSettingsService.getCombinedIrisSettingsFor(course, true)); + assertThat(loadedSettings).isNotNull().usingRecursiveComparison().ignoringCollectionOrderInFields("irisChatSettings.allowedVariants", + "irisLectureIngestionSettings.allowedVariants", "irisCompetencyGenerationSettings.allowedVariants").ignoringFields("id") + .isEqualTo(irisSettingsService.getCombinedIrisSettingsFor(course, true)); } @Test @@ -103,7 +101,6 @@ void updateCourseSettings1() throws Exception { var loadedSettings1 = request.get("/api/courses/" + course.getId() + "/raw-iris-settings", HttpStatus.OK, IrisSettings.class); loadedSettings1.getIrisChatSettings().setEnabled(false); - loadedSettings1.getIrisHestiaSettings().setEnabled(false); loadedSettings1.getIrisCompetencyGenerationSettings().setEnabled(false); loadedSettings1.getIrisLectureIngestionSettings().setEnabled(false); @@ -115,7 +112,6 @@ void updateCourseSettings1() throws Exception { assertThat(updatedSettings.getId()).isEqualTo(loadedSettings1.getId()); assertThat(updatedSettings.getIrisLectureIngestionSettings().getId()).isEqualTo(loadedSettings1.getIrisLectureIngestionSettings().getId()); assertThat(updatedSettings.getIrisChatSettings().getId()).isEqualTo(loadedSettings1.getIrisChatSettings().getId()); - assertThat(updatedSettings.getIrisHestiaSettings().getId()).isEqualTo(loadedSettings1.getIrisHestiaSettings().getId()); assertThat(updatedSettings.getIrisCompetencyGenerationSettings().getId()).isEqualTo(loadedSettings1.getIrisCompetencyGenerationSettings().getId()); } @@ -129,12 +125,10 @@ void updateCourseSettings2() throws Exception { var loadedSettings1 = request.get("/api/courses/" + course.getId() + "/raw-iris-settings", HttpStatus.OK, IrisSettings.class); var chatSubSettingsId = loadedSettings1.getIrisChatSettings().getId(); - var hestiaSubSettingsId = loadedSettings1.getIrisHestiaSettings().getId(); var competencyGenerationSubSettingsId = loadedSettings1.getIrisCompetencyGenerationSettings().getId(); var lectureIngestionSubSettingsId = loadedSettings1.getIrisLectureIngestionSettings().getId(); loadedSettings1.setIrisLectureIngestionSettings(null); loadedSettings1.setIrisChatSettings(null); - loadedSettings1.setIrisHestiaSettings(null); loadedSettings1.setIrisCompetencyGenerationSettings(null); var updatedSettings = request.putWithResponseBody("/api/courses/" + course.getId() + "/raw-iris-settings", loadedSettings1, IrisSettings.class, HttpStatus.OK); @@ -145,7 +139,6 @@ void updateCourseSettings2() throws Exception { // Original subsettings should not exist anymore assertThat(irisSubSettingsRepository.findById(lectureIngestionSubSettingsId)).isEmpty(); assertThat(irisSubSettingsRepository.findById(chatSubSettingsId)).isEmpty(); - assertThat(irisSubSettingsRepository.findById(hestiaSubSettingsId)).isEmpty(); assertThat(irisSubSettingsRepository.findById(competencyGenerationSubSettingsId)).isEmpty(); } @@ -159,18 +152,11 @@ void updateCourseSettings3() throws Exception { courseSettings.setCourse(course); courseSettings.setIrisChatSettings(new IrisChatSubSettings()); courseSettings.getIrisChatSettings().setEnabled(true); - courseSettings.getIrisChatSettings().setTemplate(createDummyTemplate()); - courseSettings.getIrisChatSettings().setPreferredModel(null); - - courseSettings.setIrisHestiaSettings(new IrisHestiaSubSettings()); - courseSettings.getIrisHestiaSettings().setEnabled(true); - courseSettings.getIrisHestiaSettings().setTemplate(createDummyTemplate()); - courseSettings.getIrisHestiaSettings().setPreferredModel(null); + courseSettings.getIrisChatSettings().setSelectedVariant(null); courseSettings.setIrisCompetencyGenerationSettings(new IrisCompetencyGenerationSubSettings()); courseSettings.getIrisCompetencyGenerationSettings().setEnabled(true); - courseSettings.getIrisCompetencyGenerationSettings().setTemplate(createDummyTemplate()); - courseSettings.getIrisCompetencyGenerationSettings().setPreferredModel(null); + courseSettings.getIrisCompetencyGenerationSettings().setSelectedVariant(null); courseSettings.setIrisLectureIngestionSettings(new IrisLectureIngestionSubSettings()); courseSettings.getIrisLectureIngestionSettings().setEnabled(true); @@ -179,10 +165,8 @@ void updateCourseSettings3() throws Exception { var loadedSettings1 = request.get("/api/courses/" + course.getId() + "/raw-iris-settings", HttpStatus.OK, IrisSettings.class); assertThat(updatedSettings).usingRecursiveComparison().ignoringFields("course").isEqualTo(loadedSettings1); - assertThat(loadedSettings1) - .usingRecursiveComparison().ignoringFields("id", "course", "irisChatSettings.id", "irisChatSettings.template.id", "irisLectureIngestionSettings.id", - "irisHestiaSettings.id", "irisHestiaSettings.template.id", "irisCompetencyGenerationSettings.id", "irisCompetencyGenerationSettings.template.id") - .isEqualTo(courseSettings); + assertThat(loadedSettings1).usingRecursiveComparison().ignoringFields("id", "course", "irisChatSettings.id", "irisChatSettings.template.id", + "irisLectureIngestionSettings.id", "irisCompetencyGenerationSettings.id", "irisCompetencyGenerationSettings.template.id").isEqualTo(courseSettings); } @Test @@ -214,7 +198,6 @@ void getProgrammingExerciseSettings() throws Exception { assertThat(loadedSettings1).isNotNull().usingRecursiveComparison().ignoringFields("id", "exercise", "irisChatSettings.id").ignoringExpectedNullFields() .isEqualTo(loadedSettings2); - assertThat(loadedSettings1.getIrisHestiaSettings()).isNull(); assertThat(loadedSettings1.getIrisCompetencyGenerationSettings()).isNull(); assertThat(loadedSettings1.getIrisLectureIngestionSettings()).isNull(); assertThat(loadedSettings1).isNotNull().usingRecursiveComparison().ignoringFields("exercise") @@ -233,7 +216,7 @@ void getProgrammingExerciseSettingsAsUser() throws Exception { var loadedSettings = request.get("/api/programming-exercises/" + programmingExercise.getId() + "/iris-settings", HttpStatus.OK, IrisCombinedSettingsDTO.class); assertThat(loadedSettings).isNotNull().usingRecursiveComparison().ignoringFields("id") - .ignoringCollectionOrderInFields("irisChatSettings.allowedModels", "irisCompetencyGenerationSettings.allowedModels", "irisHestiaSettings.allowedModels") + .ignoringCollectionOrderInFields("irisChatSettings.allowedVariants", "irisCompetencyGenerationSettings.allowedVariants") .isEqualTo(irisSettingsService.getCombinedIrisSettingsFor(programmingExercise, true)); } @@ -271,7 +254,6 @@ void updateProgrammingExerciseSettings2() throws Exception { var chatSubSettingsId = loadedSettings1.getIrisChatSettings().getId(); loadedSettings1.setIrisChatSettings(null); - loadedSettings1.setIrisHestiaSettings(null); var updatedSettings = request.putWithResponseBody("/api/programming-exercises/" + programmingExercise.getId() + "/raw-iris-settings", loadedSettings1, IrisSettings.class, HttpStatus.OK); @@ -294,8 +276,7 @@ void updateProgrammingExerciseSettings3() throws Exception { exerciseSettings.setExercise(programmingExercise); exerciseSettings.setIrisChatSettings(new IrisChatSubSettings()); exerciseSettings.getIrisChatSettings().setEnabled(true); - exerciseSettings.getIrisChatSettings().setTemplate(createDummyTemplate()); - exerciseSettings.getIrisChatSettings().setPreferredModel(null); + exerciseSettings.getIrisChatSettings().setSelectedVariant(null); var updatedSettings = request.putWithResponseBody("/api/programming-exercises/" + programmingExercise.getId() + "/raw-iris-settings", exerciseSettings, IrisSettings.class, HttpStatus.OK); diff --git a/src/test/javascript/spec/component/iris/settings/iris-common-sub-settings-update.component.spec.ts b/src/test/javascript/spec/component/iris/settings/iris-common-sub-settings-update.component.spec.ts index 0a3bf65f8433..2b36487e4576 100644 --- a/src/test/javascript/spec/component/iris/settings/iris-common-sub-settings-update.component.spec.ts +++ b/src/test/javascript/spec/component/iris/settings/iris-common-sub-settings-update.component.spec.ts @@ -1,39 +1,43 @@ import { ArtemisTestModule } from '../../../test.module'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { FormsModule } from '@angular/forms'; -import { IrisTemplate } from 'app/entities/iris/settings/iris-template'; import { IrisChatSubSettings } from 'app/entities/iris/settings/iris-sub-settings.model'; import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; import { MockDirective, MockPipe } from 'ng-mocks'; import { SimpleChange, SimpleChanges } from '@angular/core'; import { IrisCommonSubSettingsUpdateComponent } from 'app/iris/settings/iris-settings-update/iris-common-sub-settings-update/iris-common-sub-settings-update.component'; -import { mockModels } from './mock-settings'; +import { mockVariants } from './mock-settings'; import { IrisSettingsType } from 'app/entities/iris/settings/iris-settings.model'; import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; +import { IrisSettingsService } from 'app/iris/settings/shared/iris-settings.service'; +import { of } from 'rxjs'; function baseSettings() { - const mockTemplate = new IrisTemplate(); - mockTemplate.id = 1; - mockTemplate.content = 'Hello World'; const irisSubSettings = new IrisChatSubSettings(); irisSubSettings.id = 2; irisSubSettings.enabled = true; - const allowedModels = mockModels(); - allowedModels.pop(); - irisSubSettings.allowedModels = allowedModels.map((model) => model.id!); - irisSubSettings.preferredModel = allowedModels[0].id!; + const allowedVariants = mockVariants(); + allowedVariants.pop(); + irisSubSettings.allowedVariants = allowedVariants.map((model) => model.id!); + irisSubSettings.selectedVariant = allowedVariants[0].id!; return irisSubSettings; } describe('IrisCommonSubSettingsUpdateComponent Component', () => { let comp: IrisCommonSubSettingsUpdateComponent; let fixture: ComponentFixture; + let getVariantsSpy: jest.SpyInstance; beforeEach(() => { TestBed.configureTestingModule({ imports: [ArtemisTestModule, FormsModule, MockDirective(NgbTooltip), MockPipe(ArtemisTranslatePipe)], declarations: [IrisCommonSubSettingsUpdateComponent], - }).compileComponents(); + }) + .compileComponents() + .then(() => { + const irisSettingsService = TestBed.inject(IrisSettingsService); + getVariantsSpy = jest.spyOn(irisSettingsService, 'getVariantsForFeature').mockReturnValue(of(mockVariants())); + }); fixture = TestBed.createComponent(IrisCommonSubSettingsUpdateComponent); comp = fixture.componentInstance; }); @@ -45,28 +49,29 @@ describe('IrisCommonSubSettingsUpdateComponent Component', () => { it('child setup works', () => { comp.subSettings = baseSettings(); comp.parentSubSettings = baseSettings(); - comp.allIrisModels = mockModels(); + comp.availableVariants = mockVariants(); comp.settingsType = IrisSettingsType.EXERCISE; fixture.detectChanges(); + expect(getVariantsSpy).toHaveBeenCalledOnce(); expect(comp.enabled).toBeTrue(); - expect(comp.inheritAllowedModels).toBeFalse(); - expect(comp.allowedIrisModels).toEqual([mockModels()[0]]); + expect(comp.inheritAllowedVariants).toBeFalse(); + expect(comp.allowedVariants).toEqual([mockVariants()[0]]); }); it('parent setup works', () => { const subSettings = baseSettings(); - subSettings.allowedModels = undefined; - subSettings.preferredModel = undefined; + subSettings.allowedVariants = undefined; + subSettings.selectedVariant = undefined; comp.subSettings = subSettings; comp.parentSubSettings = baseSettings(); - comp.allIrisModels = mockModels(); + comp.availableVariants = mockVariants(); comp.settingsType = IrisSettingsType.EXERCISE; fixture.detectChanges(); expect(comp.enabled).toBeTrue(); - expect(comp.inheritAllowedModels).toBeTrue(); - expect(comp.allowedIrisModels).toEqual([mockModels()[0]]); + expect(comp.inheritAllowedVariants).toBeTrue(); + expect(comp.allowedVariants).toEqual([mockVariants()[0]]); }); it('prevents enabling chat settings if the parent chat settings disabled', () => { @@ -75,7 +80,7 @@ describe('IrisCommonSubSettingsUpdateComponent Component', () => { comp.parentSubSettings.enabled = false; comp.isAdmin = true; comp.settingsType = IrisSettingsType.EXERCISE; - comp.allIrisModels = mockModels(); + comp.availableVariants = mockVariants(); fixture.detectChanges(); expect(comp.inheritDisabled).toBeTrue(); @@ -88,7 +93,7 @@ describe('IrisCommonSubSettingsUpdateComponent Component', () => { comp.parentSubSettings.enabled = false; comp.isAdmin = true; comp.settingsType = IrisSettingsType.COURSE; - comp.allIrisModels = mockModels(); + comp.availableVariants = mockVariants(); fixture.detectChanges(); expect(comp.inheritDisabled).toBeTrue(); @@ -96,34 +101,34 @@ describe('IrisCommonSubSettingsUpdateComponent Component', () => { }); it('change allowed model', () => { - const allIrisModels = mockModels(); + const availableVariants = mockVariants(); comp.subSettings = baseSettings(); comp.parentSubSettings = baseSettings(); - comp.allIrisModels = allIrisModels; + comp.availableVariants = availableVariants; comp.settingsType = IrisSettingsType.EXERCISE; fixture.detectChanges(); - comp.onAllowedIrisModelsSelectionChange(allIrisModels[1]); - expect(comp.allowedIrisModels).toEqual([allIrisModels[0], allIrisModels[1]]); - comp.onAllowedIrisModelsSelectionChange(allIrisModels[0]); - expect(comp.allowedIrisModels).toEqual([allIrisModels[1]]); + comp.onAllowedIrisVariantsSelectionChange(availableVariants[1]); + expect(comp.allowedVariants).toEqual([availableVariants[0], availableVariants[1]]); + comp.onAllowedIrisVariantsSelectionChange(availableVariants[0]); + expect(comp.allowedVariants).toEqual([availableVariants[1]]); }); it('change preferred model', () => { comp.subSettings = baseSettings(); comp.parentSubSettings = baseSettings(); - comp.allIrisModels = mockModels(); + comp.availableVariants = mockVariants(); comp.settingsType = IrisSettingsType.EXERCISE; fixture.detectChanges(); - comp.setModel(mockModels()[1]); - expect(comp.subSettings!.preferredModel).toBe(mockModels()[1].id); + comp.setVariant(mockVariants()[1]); + expect(comp.subSettings!.selectedVariant).toBe(mockVariants()[1].id); }); it('change enabled', () => { comp.subSettings = baseSettings(); comp.parentSubSettings = baseSettings(); - comp.allIrisModels = mockModels(); + comp.availableVariants = mockVariants(); comp.settingsType = IrisSettingsType.EXERCISE; fixture.detectChanges(); @@ -139,41 +144,41 @@ describe('IrisCommonSubSettingsUpdateComponent Component', () => { it('change inherit allowed models', () => { comp.subSettings = baseSettings(); comp.parentSubSettings = baseSettings(); - comp.allIrisModels = mockModels(); + comp.availableVariants = mockVariants(); comp.settingsType = IrisSettingsType.EXERCISE; fixture.detectChanges(); - comp.inheritAllowedModels = true; - comp.onInheritAllowedModelsChange(); - expect(comp.subSettings!.allowedModels).toBeUndefined(); - expect(comp.allowedIrisModels).toEqual(comp.getAvailableModels()); + comp.inheritAllowedVariants = true; + comp.onInheritAllowedVariantsChange(); + expect(comp.subSettings!.allowedVariants).toBeUndefined(); + expect(comp.allowedVariants).toEqual(comp.getAllowedVariants()); - comp.inheritAllowedModels = false; - comp.onInheritAllowedModelsChange(); - expect(comp.subSettings!.allowedModels).toEqual(comp.allowedIrisModels.map((model) => model.id)); + comp.inheritAllowedVariants = false; + comp.onInheritAllowedVariantsChange(); + expect(comp.subSettings!.allowedVariants).toEqual(comp.allowedVariants.map((model) => model.id)); }); it('ngOnChanges works', () => { comp.subSettings = baseSettings(); comp.parentSubSettings = baseSettings(); - comp.allIrisModels = mockModels(); + comp.availableVariants = mockVariants(); comp.settingsType = IrisSettingsType.EXERCISE; fixture.detectChanges(); const newSubSettings = baseSettings(); newSubSettings.enabled = false; - const newModels = mockModels(); + const newModels = mockVariants(); newModels.pop(); const changes: SimpleChanges = { subSettings: new SimpleChange(comp.subSettings, newSubSettings, false), - allIrisModels: new SimpleChange(comp.allIrisModels, newModels, false), + availableVariants: new SimpleChange(comp.availableVariants, newModels, false), }; comp.subSettings = newSubSettings; - comp.allIrisModels = mockModels(); + comp.availableVariants = mockVariants(); comp.ngOnChanges(changes); expect(comp.enabled).toBeFalse(); - expect(comp.allowedIrisModels).toEqual(newModels); + expect(comp.allowedVariants).toEqual(newModels); }); }); diff --git a/src/test/javascript/spec/component/iris/settings/iris-course-settings-update.component.spec.ts b/src/test/javascript/spec/component/iris/settings/iris-course-settings-update.component.spec.ts index c5f5a5af8fa8..c8ea667ad0aa 100644 --- a/src/test/javascript/spec/component/iris/settings/iris-course-settings-update.component.spec.ts +++ b/src/test/javascript/spec/component/iris/settings/iris-course-settings-update.component.spec.ts @@ -6,7 +6,6 @@ import { MockComponent, MockDirective, MockProvider } from 'ng-mocks'; import { BehaviorSubject, of } from 'rxjs'; import { ButtonComponent } from 'app/shared/components/button.component'; import { IrisCommonSubSettingsUpdateComponent } from 'app/iris/settings/iris-settings-update/iris-common-sub-settings-update/iris-common-sub-settings-update.component'; -import { IrisGlobalAutoupdateSettingsUpdateComponent } from 'app/iris/settings/iris-settings-update/iris-global-autoupdate-settings-update/iris-global-autoupdate-settings-update.component'; import { mockEmptySettings, mockSettings } from './mock-settings'; import { ActivatedRoute, Params } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; @@ -24,7 +23,6 @@ describe('IrisCourseSettingsUpdateComponent Component', () => { const route = { parent: { params: routeParamsSubject.asObservable() } } as ActivatedRoute; let paramsSpy: jest.SpyInstance; let getSettingsSpy: jest.SpyInstance; - //let getModelsSpy: jest.SpyInstance; let getParentSettingsSpy: jest.SpyInstance; beforeEach(() => { @@ -34,7 +32,6 @@ describe('IrisCourseSettingsUpdateComponent Component', () => { IrisCourseSettingsUpdateComponent, IrisSettingsUpdateComponent, MockComponent(IrisCommonSubSettingsUpdateComponent), - MockComponent(IrisGlobalAutoupdateSettingsUpdateComponent), MockComponent(ButtonComponent), MockDirective(NgModel), ], @@ -50,7 +47,6 @@ describe('IrisCourseSettingsUpdateComponent Component', () => { const irisSettings = mockSettings(); getSettingsSpy = jest.spyOn(irisSettingsService, 'getUncombinedCourseSettings').mockReturnValue(of(irisSettings)); - //getModelsSpy = jest.spyOn(irisSettingsService, 'getIrisModels').mockReturnValue(of(mockModels())); getParentSettingsSpy = jest.spyOn(irisSettingsService, 'getGlobalSettings').mockReturnValue(of(irisSettings)); }); fixture = TestBed.createComponent(IrisCourseSettingsUpdateComponent); @@ -67,11 +63,9 @@ describe('IrisCourseSettingsUpdateComponent Component', () => { expect(comp.courseId).toBe(1); expect(comp.settingsUpdateComponent).toBeTruthy(); expect(getSettingsSpy).toHaveBeenCalledWith(1); - //expect(getModelsSpy).toHaveBeenCalledOnce(); expect(getParentSettingsSpy).toHaveBeenCalledOnce(); - expect(fixture.debugElement.query(By.directive(IrisGlobalAutoupdateSettingsUpdateComponent))).toBeFalsy(); - expect(fixture.debugElement.queryAll(By.directive(IrisCommonSubSettingsUpdateComponent))).toHaveLength(4); + expect(fixture.debugElement.queryAll(By.directive(IrisCommonSubSettingsUpdateComponent))).toHaveLength(3); }); it('Can deactivate correctly', () => { @@ -100,7 +94,6 @@ describe('IrisCourseSettingsUpdateComponent Component', () => { comp.settingsUpdateComponent!.fillEmptyIrisSubSettings(); expect(comp.settingsUpdateComponent!.irisSettings.irisChatSettings).toBeTruthy(); expect(comp.settingsUpdateComponent!.irisSettings.irisLectureIngestionSettings).toBeTruthy(); - expect(comp.settingsUpdateComponent!.irisSettings.irisHestiaSettings).toBeTruthy(); expect(comp.settingsUpdateComponent!.irisSettings.irisCompetencyGenerationSettings).toBeTruthy(); }); }); diff --git a/src/test/javascript/spec/component/iris/settings/iris-enabled.component.spec.ts b/src/test/javascript/spec/component/iris/settings/iris-enabled.component.spec.ts index a79259f2268a..c5efd2d4c445 100644 --- a/src/test/javascript/spec/component/iris/settings/iris-enabled.component.spec.ts +++ b/src/test/javascript/spec/component/iris/settings/iris-enabled.component.spec.ts @@ -47,7 +47,7 @@ describe('IrisEnabledComponent', () => { expect(comp).toBeDefined(); }); - it.each([IrisSubSettingsType.CHAT, IrisSubSettingsType.HESTIA, IrisSubSettingsType.COMPETENCY_GENERATION])('should load exercise', async (subSettingstype) => { + it.each([IrisSubSettingsType.CHAT, IrisSubSettingsType.COMPETENCY_GENERATION])('should load exercise', async (subSettingstype) => { const getExerciseSettingsSpy = jest.spyOn(irisSettingsService, 'getUncombinedProgrammingExerciseSettings').mockReturnValue(of(irisSettings)); comp.exercise = exercise; comp.irisSubSettingsType = subSettingstype; @@ -58,19 +58,16 @@ describe('IrisEnabledComponent', () => { expect(comp.irisSubSettings).toBeDefined(); }); - it.each([IrisSubSettingsType.CHAT, IrisSubSettingsType.HESTIA, IrisSubSettingsType.COMPETENCY_GENERATION, IrisSubSettingsType.LECTURE_INGESTION])( - 'should load course', - async (subSettingstype) => { - const getExerciseSettingsSpy = jest.spyOn(irisSettingsService, 'getUncombinedCourseSettings').mockReturnValue(of(irisSettings)); - comp.course = course; - comp.irisSubSettingsType = subSettingstype; - fixture.detectChanges(); - expect(getExerciseSettingsSpy).toHaveBeenCalledOnce(); - await Promise.resolve(); - expect(comp.irisSettings).toBe(irisSettings); - expect(comp.irisSubSettings).toBeDefined(); - }, - ); + it.each([IrisSubSettingsType.CHAT, IrisSubSettingsType.COMPETENCY_GENERATION, IrisSubSettingsType.LECTURE_INGESTION])('should load course', async (subSettingstype) => { + const getExerciseSettingsSpy = jest.spyOn(irisSettingsService, 'getUncombinedCourseSettings').mockReturnValue(of(irisSettings)); + comp.course = course; + comp.irisSubSettingsType = subSettingstype; + fixture.detectChanges(); + expect(getExerciseSettingsSpy).toHaveBeenCalledOnce(); + await Promise.resolve(); + expect(comp.irisSettings).toBe(irisSettings); + expect(comp.irisSubSettings).toBeDefined(); + }); it('should set exercise enabled', async () => { const setSettingsSpy = jest.spyOn(irisSettingsService, 'setProgrammingExerciseSettings').mockReturnValue(of(new HttpResponse({ body: null as any as IrisSettings }))); @@ -89,8 +86,8 @@ describe('IrisEnabledComponent', () => { const setSettingsSpy = jest.spyOn(irisSettingsService, 'setCourseSettings').mockReturnValue(of(new HttpResponse({ body: null as any as IrisSettings }))); comp.course = course; comp.irisSettings = irisSettings; - comp.irisSubSettingsType = IrisSubSettingsType.HESTIA; - comp.irisSubSettings = irisSettings.irisHestiaSettings; + comp.irisSubSettingsType = IrisSubSettingsType.CHAT; + comp.irisSubSettings = irisSettings.irisChatSettings; comp.setEnabled(true); expect(setSettingsSpy).toHaveBeenCalledOnce(); diff --git a/src/test/javascript/spec/component/iris/settings/iris-exercise-settings-update.component.spec.ts b/src/test/javascript/spec/component/iris/settings/iris-exercise-settings-update.component.spec.ts index d99ce839e8a5..8724f926064d 100644 --- a/src/test/javascript/spec/component/iris/settings/iris-exercise-settings-update.component.spec.ts +++ b/src/test/javascript/spec/component/iris/settings/iris-exercise-settings-update.component.spec.ts @@ -6,7 +6,6 @@ import { MockComponent, MockDirective, MockProvider } from 'ng-mocks'; import { BehaviorSubject, of } from 'rxjs'; import { ButtonComponent } from 'app/shared/components/button.component'; import { IrisCommonSubSettingsUpdateComponent } from 'app/iris/settings/iris-settings-update/iris-common-sub-settings-update/iris-common-sub-settings-update.component'; -import { IrisGlobalAutoupdateSettingsUpdateComponent } from 'app/iris/settings/iris-settings-update/iris-global-autoupdate-settings-update/iris-global-autoupdate-settings-update.component'; import { mockSettings } from './mock-settings'; import { IrisExerciseSettingsUpdateComponent } from 'app/iris/settings/iris-exercise-settings-update/iris-exercise-settings-update.component'; import { ActivatedRoute, Params } from '@angular/router'; @@ -24,7 +23,6 @@ describe('IrisExerciseSettingsUpdateComponent Component', () => { const route = { parent: { params: routeParamsSubject.asObservable() } } as ActivatedRoute; let paramsSpy: jest.SpyInstance; let getSettingsSpy: jest.SpyInstance; - //let getModelsSpy: jest.SpyInstance; let getParentSettingsSpy: jest.SpyInstance; beforeEach(() => { @@ -34,7 +32,6 @@ describe('IrisExerciseSettingsUpdateComponent Component', () => { IrisExerciseSettingsUpdateComponent, IrisSettingsUpdateComponent, MockComponent(IrisCommonSubSettingsUpdateComponent), - MockComponent(IrisGlobalAutoupdateSettingsUpdateComponent), MockComponent(ButtonComponent), MockDirective(NgModel), ], @@ -50,7 +47,6 @@ describe('IrisExerciseSettingsUpdateComponent Component', () => { const irisSettings = mockSettings(); getSettingsSpy = jest.spyOn(irisSettingsService, 'getUncombinedProgrammingExerciseSettings').mockReturnValue(of(irisSettings)); - //getModelsSpy = jest.spyOn(irisSettingsService, 'getIrisModels').mockReturnValue(of(mockModels())); getParentSettingsSpy = jest.spyOn(irisSettingsService, 'getCombinedCourseSettings').mockReturnValue(of(irisSettings)); }); fixture = TestBed.createComponent(IrisExerciseSettingsUpdateComponent); @@ -68,10 +64,8 @@ describe('IrisExerciseSettingsUpdateComponent Component', () => { expect(comp.exerciseId).toBe(2); expect(comp.settingsUpdateComponent).toBeTruthy(); expect(getSettingsSpy).toHaveBeenCalledWith(2); - //expect(getModelsSpy).toHaveBeenCalledOnce(); expect(getParentSettingsSpy).toHaveBeenCalledWith(1); - expect(fixture.debugElement.query(By.directive(IrisGlobalAutoupdateSettingsUpdateComponent))).toBeFalsy(); expect(fixture.debugElement.queryAll(By.directive(IrisCommonSubSettingsUpdateComponent))).toHaveLength(1); }); diff --git a/src/test/javascript/spec/component/iris/settings/iris-global-settings-update.component.spec.ts b/src/test/javascript/spec/component/iris/settings/iris-global-settings-update.component.spec.ts index a00353681299..44783963ac05 100644 --- a/src/test/javascript/spec/component/iris/settings/iris-global-settings-update.component.spec.ts +++ b/src/test/javascript/spec/component/iris/settings/iris-global-settings-update.component.spec.ts @@ -6,7 +6,6 @@ import { MockComponent, MockDirective, MockProvider } from 'ng-mocks'; import { of } from 'rxjs'; import { ButtonComponent } from 'app/shared/components/button.component'; import { IrisCommonSubSettingsUpdateComponent } from 'app/iris/settings/iris-settings-update/iris-common-sub-settings-update/iris-common-sub-settings-update.component'; -import { IrisGlobalAutoupdateSettingsUpdateComponent } from 'app/iris/settings/iris-settings-update/iris-global-autoupdate-settings-update/iris-global-autoupdate-settings-update.component'; import { mockSettings } from './mock-settings'; import { NgModel } from '@angular/forms'; import { IrisGlobalSettingsUpdateComponent } from 'app/iris/settings/iris-global-settings-update/iris-global-settings-update.component'; @@ -19,7 +18,6 @@ describe('IrisGlobalSettingsUpdateComponent Component', () => { let fixture: ComponentFixture; let irisSettingsService: IrisSettingsService; let getSettingsSpy: jest.SpyInstance; - //let getModelsSpy: jest.SpyInstance; beforeEach(() => { TestBed.configureTestingModule({ @@ -28,7 +26,6 @@ describe('IrisGlobalSettingsUpdateComponent Component', () => { IrisGlobalSettingsUpdateComponent, IrisSettingsUpdateComponent, MockComponent(IrisCommonSubSettingsUpdateComponent), - MockComponent(IrisGlobalAutoupdateSettingsUpdateComponent), MockComponent(ButtonComponent), MockDirective(NgModel), ], @@ -41,7 +38,6 @@ describe('IrisGlobalSettingsUpdateComponent Component', () => { // Setup const irisSettings = mockSettings(); getSettingsSpy = jest.spyOn(irisSettingsService, 'getGlobalSettings').mockReturnValue(of(irisSettings)); - //getModelsSpy = jest.spyOn(irisSettingsService, 'getIrisModels').mockReturnValue(of(mockModels())); }); fixture = TestBed.createComponent(IrisGlobalSettingsUpdateComponent); comp = fixture.componentInstance; @@ -55,10 +51,8 @@ describe('IrisGlobalSettingsUpdateComponent Component', () => { fixture.detectChanges(); expect(comp.settingsUpdateComponent).toBeTruthy(); expect(getSettingsSpy).toHaveBeenCalledOnce(); - //expect(getModelsSpy).toHaveBeenCalledOnce(); - expect(fixture.debugElement.query(By.directive(IrisGlobalAutoupdateSettingsUpdateComponent))).toBeTruthy(); - expect(fixture.debugElement.queryAll(By.directive(IrisCommonSubSettingsUpdateComponent))).toHaveLength(4); + expect(fixture.debugElement.queryAll(By.directive(IrisCommonSubSettingsUpdateComponent))).toHaveLength(3); }); it('Can deactivate correctly', () => { diff --git a/src/test/javascript/spec/component/iris/settings/iris-settings-update-component.spec.ts b/src/test/javascript/spec/component/iris/settings/iris-settings-update-component.spec.ts index 1c3a6fb751b4..4fe2f213f845 100644 --- a/src/test/javascript/spec/component/iris/settings/iris-settings-update-component.spec.ts +++ b/src/test/javascript/spec/component/iris/settings/iris-settings-update-component.spec.ts @@ -2,12 +2,11 @@ import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testin import { By } from '@angular/platform-browser'; import { IrisSettingsUpdateComponent } from 'app/iris/settings/iris-settings-update/iris-settings-update.component'; import { IrisSettingsType } from 'app/entities/iris/settings/iris-settings.model'; -import { mockSettings } from './mock-settings'; +import { mockSettings, mockVariants } from './mock-settings'; import { ArtemisTestModule } from '../../../test.module'; import { NgModel } from '@angular/forms'; import { MockComponent, MockDirective, MockPipe, MockProvider } from 'ng-mocks'; import { IrisCommonSubSettingsUpdateComponent } from 'app/iris/settings/iris-settings-update/iris-common-sub-settings-update/iris-common-sub-settings-update.component'; -import { IrisGlobalAutoupdateSettingsUpdateComponent } from 'app/iris/settings/iris-settings-update/iris-global-autoupdate-settings-update/iris-global-autoupdate-settings-update.component'; import { ButtonComponent } from 'app/shared/components/button.component'; import { IrisSettingsService } from 'app/iris/settings/shared/iris-settings.service'; import { of } from 'rxjs'; @@ -17,12 +16,12 @@ import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; describe('IrisSettingsUpdateComponent', () => { let component: IrisSettingsUpdateComponent; let fixture: ComponentFixture; + let getVariantsSpy: jest.SpyInstance; beforeEach(() => { TestBed.configureTestingModule({ imports: [ArtemisTestModule], declarations: [ - IrisGlobalAutoupdateSettingsUpdateComponent, IrisCourseSettingsUpdateComponent, IrisSettingsUpdateComponent, IrisCommonSubSettingsUpdateComponent, @@ -42,15 +41,12 @@ describe('IrisSettingsUpdateComponent', () => { .then(() => { fixture = TestBed.createComponent(IrisSettingsUpdateComponent); component = fixture.componentInstance; + + const irisSettingsService = TestBed.inject(IrisSettingsService); + getVariantsSpy = jest.spyOn(irisSettingsService, 'getVariantsForFeature').mockReturnValue(of(mockVariants())); }); }); - it('should display global auto-update settings only if settingsType is GLOBAL', () => { - component.irisSettings = mockSettings(); - component.settingsType = IrisSettingsType.GLOBAL; - fixture.detectChanges(); - const globalSettingsElement = fixture.debugElement.query(By.css('jhi-iris-global-autoupdate-settings-update')); - expect(globalSettingsElement).toBeTruthy(); - }); + it('should display the checkbox for lecture ingestion when settingsType is COURSE', fakeAsync(() => { component.irisSettings = mockSettings(); component.settingsType = IrisSettingsType.COURSE; @@ -65,5 +61,6 @@ describe('IrisSettingsUpdateComponent', () => { expect(lectureIngestionElement).not.toBeNull(); expect(checkboxElement).toBeTruthy(); expect(labelElement).toBeTruthy(); + expect(getVariantsSpy).toHaveBeenCalled(); })); }); diff --git a/src/test/javascript/spec/component/iris/settings/mock-settings.ts b/src/test/javascript/spec/component/iris/settings/mock-settings.ts index f9338cd78cc4..6c542caf9a91 100644 --- a/src/test/javascript/spec/component/iris/settings/mock-settings.ts +++ b/src/test/javascript/spec/component/iris/settings/mock-settings.ts @@ -1,51 +1,33 @@ -import { IrisModel } from 'app/entities/iris/settings/iris-model'; -import { IrisTemplate } from 'app/entities/iris/settings/iris-template'; -import { - IrisChatSubSettings, - IrisCompetencyGenerationSubSettings, - IrisHestiaSubSettings, - IrisLectureIngestionSubSettings, -} from 'app/entities/iris/settings/iris-sub-settings.model'; +import { IrisVariant } from 'app/entities/iris/settings/iris-variant'; +import { IrisChatSubSettings, IrisCompetencyGenerationSubSettings, IrisLectureIngestionSubSettings } from 'app/entities/iris/settings/iris-sub-settings.model'; import { IrisGlobalSettings } from 'app/entities/iris/settings/iris-settings.model'; export function mockSettings() { - const mockTemplate = new IrisTemplate(); - mockTemplate.id = 1; - mockTemplate.content = 'Hello World'; const mockChatSettings = new IrisChatSubSettings(); mockChatSettings.id = 1; - mockChatSettings.template = mockTemplate; mockChatSettings.enabled = true; const mockLectureIngestionSettings = new IrisLectureIngestionSubSettings(); mockLectureIngestionSettings.id = 7; mockLectureIngestionSettings.enabled = true; mockLectureIngestionSettings.autoIngestOnLectureAttachmentUpload = true; - const mockHestiaSettings = new IrisHestiaSubSettings(); - mockHestiaSettings.id = 2; - mockHestiaSettings.template = mockTemplate; - mockHestiaSettings.enabled = true; const mockCompetencyGenerationSettings = new IrisCompetencyGenerationSubSettings(); mockCompetencyGenerationSettings.id = 5; mockCompetencyGenerationSettings.enabled = false; const irisSettings = new IrisGlobalSettings(); irisSettings.id = 1; irisSettings.irisChatSettings = mockChatSettings; - irisSettings.irisHestiaSettings = mockHestiaSettings; irisSettings.irisCompetencyGenerationSettings = mockCompetencyGenerationSettings; irisSettings.irisLectureIngestionSettings = mockLectureIngestionSettings; return irisSettings; } export function mockEmptySettings() { - const mockTemplate = new IrisTemplate(); - mockTemplate.id = 1; - mockTemplate.content = 'Hello World'; const irisSettings = new IrisGlobalSettings(); irisSettings.id = 1; return irisSettings; } -export function mockModels() { +export function mockVariants() { return [ { id: '1', @@ -57,5 +39,5 @@ export function mockModels() { name: 'Model 2', description: 'Model 2 Description', }, - ] as IrisModel[]; + ] as IrisVariant[]; } From 420a9129ba1e58d98afe11e47d57ebd54e8dac11 Mon Sep 17 00:00:00 2001 From: Timor Morrien Date: Tue, 8 Oct 2024 00:08:27 +0200 Subject: [PATCH 2/4] Fix merge issues --- .../IrisHestiaSessionRepository.java | 28 ------------------- .../repository/IrisTemplateRepository.java | 11 -------- .../iris/web/IrisVariantsResource.java | 12 ++++---- 3 files changed, 6 insertions(+), 45 deletions(-) delete mode 100644 src/main/java/de/tum/cit/aet/artemis/iris/repository/IrisHestiaSessionRepository.java delete mode 100644 src/main/java/de/tum/cit/aet/artemis/iris/repository/IrisTemplateRepository.java diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/repository/IrisHestiaSessionRepository.java b/src/main/java/de/tum/cit/aet/artemis/iris/repository/IrisHestiaSessionRepository.java deleted file mode 100644 index 22a14bd98bd7..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/iris/repository/IrisHestiaSessionRepository.java +++ /dev/null @@ -1,28 +0,0 @@ -package de.tum.cit.aet.artemis.iris.repository; - -import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_IRIS; - -import java.util.List; - -import org.springframework.context.annotation.Profile; -import org.springframework.stereotype.Repository; - -import de.tum.cit.aet.artemis.core.repository.base.ArtemisJpaRepository; -import de.tum.cit.aet.artemis.iris.domain.session.IrisHestiaSession; - -/** - * Repository interface for managing {@link IrisHestiaSession} entities. - * Provides custom queries for finding hestia sessions based on different criteria. - */ -@Repository -@Profile(PROFILE_IRIS) -public interface IrisHestiaSessionRepository extends ArtemisJpaRepository { - - /** - * Finds a list of {@link IrisHestiaSession} based on the exercise and user IDs. - * - * @param codeHintId The ID of the code hint. - * @return A list of hestia sessions sorted by creation date in descending order. - */ - List findByCodeHintIdOrderByCreationDateDesc(Long codeHintId); -} diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/repository/IrisTemplateRepository.java b/src/main/java/de/tum/cit/aet/artemis/iris/repository/IrisTemplateRepository.java deleted file mode 100644 index 2b1a930d7aef..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/iris/repository/IrisTemplateRepository.java +++ /dev/null @@ -1,11 +0,0 @@ -package de.tum.cit.aet.artemis.iris.repository; - -import de.tum.cit.aet.artemis.core.repository.base.ArtemisJpaRepository; -import de.tum.cit.aet.artemis.iris.domain.IrisTemplate; - -/** - * Spring Data repository for the IrisTemplate entity. - */ -public interface IrisTemplateRepository extends ArtemisJpaRepository { - -} diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/web/IrisVariantsResource.java b/src/main/java/de/tum/cit/aet/artemis/iris/web/IrisVariantsResource.java index 96f3b754ff7d..cc0cef0a1f98 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/web/IrisVariantsResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/web/IrisVariantsResource.java @@ -11,12 +11,12 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import de.tum.in.www1.artemis.domain.iris.settings.IrisSubSettingsType; -import de.tum.in.www1.artemis.security.annotations.EnforceAtLeastEditor; -import de.tum.in.www1.artemis.service.connectors.pyris.PyrisConnectorException; -import de.tum.in.www1.artemis.service.connectors.pyris.PyrisConnectorService; -import de.tum.in.www1.artemis.service.connectors.pyris.dto.PyrisVariantDTO; -import de.tum.in.www1.artemis.web.rest.errors.InternalServerErrorException; +import de.tum.cit.aet.artemis.core.exception.InternalServerErrorException; +import de.tum.cit.aet.artemis.core.security.annotations.EnforceAtLeastEditor; +import de.tum.cit.aet.artemis.iris.domain.settings.IrisSubSettingsType; +import de.tum.cit.aet.artemis.iris.service.pyris.PyrisConnectorException; +import de.tum.cit.aet.artemis.iris.service.pyris.PyrisConnectorService; +import de.tum.cit.aet.artemis.iris.service.pyris.dto.PyrisVariantDTO; /** * REST controller for managing the variants Pyris provides. From 3869102238242e3538f243049c68c45a76c1dfec Mon Sep 17 00:00:00 2001 From: Timor Morrien Date: Tue, 8 Oct 2024 00:40:38 +0200 Subject: [PATCH 3/4] Improve endpoints --- .../service/pyris/PyrisConnectorService.java | 4 +-- .../iris/web/IrisSettingsResource.java | 26 +++++++++---------- .../web/hestia/CodeHintResource.java | 2 -- 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisConnectorService.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisConnectorService.java index f88b427b0e1b..f41de6b6c97d 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisConnectorService.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisConnectorService.java @@ -5,8 +5,6 @@ import java.util.Arrays; import java.util.List; -import de.tum.cit.aet.artemis.iris.domain.settings.IrisSubSettingsType; -import de.tum.cit.aet.artemis.iris.service.pyris.dto.PyrisVariantDTO; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Qualifier; @@ -21,9 +19,11 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import de.tum.cit.aet.artemis.iris.domain.settings.IrisSubSettingsType; import de.tum.cit.aet.artemis.iris.exception.IrisException; import de.tum.cit.aet.artemis.iris.exception.IrisForbiddenException; import de.tum.cit.aet.artemis.iris.exception.IrisInternalPyrisErrorException; +import de.tum.cit.aet.artemis.iris.service.pyris.dto.PyrisVariantDTO; import de.tum.cit.aet.artemis.iris.service.pyris.dto.lectureingestionwebhook.PyrisWebhookLectureIngestionExecutionDTO; import de.tum.cit.aet.artemis.iris.web.open.PublicPyrisStatusUpdateResource; diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/web/IrisSettingsResource.java b/src/main/java/de/tum/cit/aet/artemis/iris/web/IrisSettingsResource.java index 32676da073bb..a4f51180b159 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/web/IrisSettingsResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/web/IrisSettingsResource.java @@ -14,9 +14,13 @@ import de.tum.cit.aet.artemis.core.repository.CourseRepository; import de.tum.cit.aet.artemis.core.repository.UserRepository; import de.tum.cit.aet.artemis.core.security.Role; -import de.tum.cit.aet.artemis.core.security.annotations.EnforceAtLeastEditor; import de.tum.cit.aet.artemis.core.security.annotations.EnforceAtLeastInstructor; -import de.tum.cit.aet.artemis.core.security.annotations.EnforceAtLeastStudent; +import de.tum.cit.aet.artemis.core.security.annotations.enforceRoleInCourse.EnforceAtLeastEditorInCourse; +import de.tum.cit.aet.artemis.core.security.annotations.enforceRoleInCourse.EnforceAtLeastInstructorInCourse; +import de.tum.cit.aet.artemis.core.security.annotations.enforceRoleInCourse.EnforceAtLeastStudentInCourse; +import de.tum.cit.aet.artemis.core.security.annotations.enforceRoleInExercise.EnforceAtLeastEditorInExercise; +import de.tum.cit.aet.artemis.core.security.annotations.enforceRoleInExercise.EnforceAtLeastInstructorInExercise; +import de.tum.cit.aet.artemis.core.security.annotations.enforceRoleInExercise.EnforceAtLeastStudentInExercise; import de.tum.cit.aet.artemis.core.service.AuthorizationCheckService; import de.tum.cit.aet.artemis.iris.domain.settings.IrisCourseSettings; import de.tum.cit.aet.artemis.iris.domain.settings.IrisExerciseSettings; @@ -71,10 +75,9 @@ public ResponseEntity getGlobalSettings() { * @return the {@link ResponseEntity} with status {@code 200 (Ok)} and with body the settings, or with status {@code 404 (Not Found)} if the course could not be found. */ @GetMapping("courses/{courseId}/raw-iris-settings") - @EnforceAtLeastEditor + @EnforceAtLeastEditorInCourse public ResponseEntity getRawCourseSettings(@PathVariable Long courseId) { var course = courseRepository.findByIdElseThrow(courseId); - authCheckService.checkHasAtLeastRoleInCourseElseThrow(Role.EDITOR, course, null); var irisSettings = irisSettingsService.getRawIrisSettingsFor(course); return ResponseEntity.ok(irisSettings); } @@ -86,7 +89,7 @@ public ResponseEntity getRawCourseSettings(@PathVariable Long cour * @return the {@link ResponseEntity} with status {@code 200 (Ok)} and with body the settings, or with status {@code 404 (Not Found)} if the exercise could not be found. */ @GetMapping("programming-exercises/{exerciseId}/raw-iris-settings") - @EnforceAtLeastEditor + @EnforceAtLeastEditorInExercise public ResponseEntity getRawProgrammingExerciseSettings(@PathVariable Long exerciseId) { var exercise = programmingExerciseRepository.findByIdElseThrow(exerciseId); var user = userRepository.getUserWithGroupsAndAuthorities(); @@ -103,11 +106,10 @@ public ResponseEntity getRawProgrammingExerciseSettings(@PathVaria * @return the {@link ResponseEntity} with status {@code 200 (Ok)} and with body the settings, or with status {@code 404 (Not Found)} if the course could not be found. */ @GetMapping("courses/{courseId}/iris-settings") - @EnforceAtLeastStudent + @EnforceAtLeastStudentInCourse public ResponseEntity getCourseSettings(@PathVariable Long courseId) { var course = courseRepository.findByIdElseThrow(courseId); var user = userRepository.getUserWithGroupsAndAuthorities(); - authCheckService.checkHasAtLeastRoleInCourseElseThrow(Role.STUDENT, course, user); // Editors can see the full settings, students only the reduced settings var getReduced = !authCheckService.isAtLeastEditorInCourse(course, user); @@ -122,11 +124,10 @@ public ResponseEntity getCourseSettings(@PathVariable L * @return the {@link ResponseEntity} with status {@code 200 (Ok)} and with body the settings, or with status {@code 404 (Not Found)} if the exercise could not be found. */ @GetMapping("programming-exercises/{exerciseId}/iris-settings") - @EnforceAtLeastStudent + @EnforceAtLeastStudentInExercise public ResponseEntity getProgrammingExerciseSettings(@PathVariable Long exerciseId) { var exercise = programmingExerciseRepository.findByIdElseThrow(exerciseId); var user = userRepository.getUserWithGroupsAndAuthorities(); - authCheckService.checkHasAtLeastRoleForExerciseElseThrow(Role.STUDENT, exercise, user); var combinedIrisSettings = irisSettingsService.getCombinedIrisSettingsFor(exercise, irisSettingsService.shouldShowMinimalSettings(exercise, user)); return ResponseEntity.ok(combinedIrisSettings); @@ -140,10 +141,9 @@ public ResponseEntity getProgrammingExerciseSettings(@P * @return the {@link ResponseEntity} with status {@code 200 (Ok)} and with body the updated settings, or with status {@code 404 (Not Found)} if the course could not be found. */ @PutMapping("courses/{courseId}/raw-iris-settings") - @EnforceAtLeastEditor + @EnforceAtLeastInstructorInCourse public ResponseEntity updateCourseSettings(@PathVariable Long courseId, @RequestBody IrisCourseSettings settings) { var course = courseRepository.findByIdElseThrow(courseId); - authCheckService.checkHasAtLeastRoleInCourseElseThrow(Role.EDITOR, course, null); settings.setCourse(course); var updatedSettings = irisSettingsService.saveIrisSettings(settings); return ResponseEntity.ok(updatedSettings); @@ -158,11 +158,9 @@ public ResponseEntity updateCourseSettings(@PathVariable Lon * found. */ @PutMapping("programming-exercises/{exerciseId}/raw-iris-settings") - @EnforceAtLeastInstructor + @EnforceAtLeastInstructorInExercise public ResponseEntity updateProgrammingExerciseSettings(@PathVariable Long exerciseId, @RequestBody IrisExerciseSettings settings) { var exercise = programmingExerciseRepository.findByIdElseThrow(exerciseId); - var user = userRepository.getUserWithGroupsAndAuthorities(); - authCheckService.checkHasAtLeastRoleForExerciseElseThrow(Role.INSTRUCTOR, exercise, user); settings.setExercise(exercise); var updatedSettings = irisSettingsService.saveIrisSettings(settings); return ResponseEntity.ok(updatedSettings); diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/web/hestia/CodeHintResource.java b/src/main/java/de/tum/cit/aet/artemis/programming/web/hestia/CodeHintResource.java index ca8b3d14b767..122aa11b7a17 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/web/hestia/CodeHintResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/web/hestia/CodeHintResource.java @@ -1,11 +1,9 @@ package de.tum.cit.aet.artemis.programming.web.hestia; import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE; -import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_IRIS; import java.util.List; import java.util.Objects; -import java.util.Optional; import java.util.Set; import org.slf4j.Logger; From ce497fc4a8ecd49045fcf955bc059f6b80b7d122 Mon Sep 17 00:00:00 2001 From: Timor Morrien Date: Fri, 11 Oct 2024 16:19:26 +0200 Subject: [PATCH 4/4] Fix one package name --- .../de/tum/cit/aet/artemis/iris/web/IrisVariantsResource.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/web/IrisVariantsResource.java b/src/main/java/de/tum/cit/aet/artemis/iris/web/IrisVariantsResource.java index cc0cef0a1f98..9342d1522023 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/web/IrisVariantsResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/web/IrisVariantsResource.java @@ -1,4 +1,4 @@ -package de.tum.in.www1.artemis.web.rest.iris; +package de.tum.cit.aet.artemis.iris.web; import java.util.List;