Skip to content

Iris: Add inline citations and deep linking to global search#12522

Draft
Nayer-kotry wants to merge 8 commits intodevelopfrom
feature/global-search/add-inline-citations-and-deep-linking
Draft

Iris: Add inline citations and deep linking to global search#12522
Nayer-kotry wants to merge 8 commits intodevelopfrom
feature/global-search/add-inline-citations-and-deep-linking

Conversation

@Nayer-kotry
Copy link
Copy Markdown
Contributor

@Nayer-kotry Nayer-kotry commented Apr 13, 2026

Summary

This PR extends the global lecture search with two features:

  1. Inline citations in the Iris answer panel — when a user asks Iris a question in the global search, the answer is rendered with inline source citations. Citations appear as skeleton placeholders immediately (first callback from Pyris), then are replaced with clickable source cards once Pyris finishes computing them (second callback).

  2. Deep-linking from search results to lecture units — clicking a lecture result or an Iris source card navigates directly to the relevant lecture unit, opening it at the exact video timestamp or PDF page reported by Pyris.

Checklist

General

Server

  • Important: I implemented the changes with a very good performance and prevented too many (unnecessary) and too complex database calls.
  • I strictly followed the principle of data economy for all database calls.
  • I strictly followed the server coding and design guidelines and the REST API guidelines.
  • I added multiple integration tests (Spring) related to the features (with a high test coverage).
  • I added pre-authorization annotations according to the guidelines and checked the course groups for all new REST Calls (security).
  • I documented the Java code using JavaDoc style.

Client

  • Important: I implemented the changes with a very good performance, prevented too many (unnecessary) REST calls and made sure the UI is responsive, even with large data (e.g. using paging).
  • I strictly followed the principle of data economy for all client-server REST calls.
  • I strictly followed the client coding guidelines.
  • I strictly followed the AET UI-UX guidelines.
  • Following the theming guidelines, I specified colors only in the theming variable files and checked that the changes look consistent in both the light and the dark theme.
  • I added multiple integration tests (Vitest) related to the features (with a high test coverage), while following the test guidelines.
  • I translated all newly inserted strings into English and German.

Motivation and Context

When the global search returns lecture results or Iris generates an answer referencing lecture content, users had no way to jump directly to the relevant moment in a lecture. They had to navigate to the lecture manually and scrub through the video or PDF themselves.

Additionally, the Iris answer panel showed a plain text answer with no indication of which sources backed each claim.

Description

Server

  • POST /api/iris/search-answer — new endpoint that accepts { query, limit } from the client, creates a SearchAnswerJob to track the request by user, and forwards the request to Pyris (/api/v1/search/lectures/ask) with the Artemis base URL and authentication token. Returns a job token.
  • POST /api/iris/internal/search/runs/{runId}/status — new internal callback endpoint that Pyris calls twice (first with cited=false to deliver the answer text, then with cited=true to deliver the answer with source citations). Each callback is forwarded to the requesting user via WebSocket as IrisSearchWebsocketDTO.
  • SearchAnswerJob — tracks in-flight Ask Iris jobs by user login and token, enabling the WebSocket delivery to the correct user.
  • IrisSearchAnswerCallbackDTO / IrisSearchWebsocketDTO — new DTOs for the two-phase Pyris callback and WebSocket message.
  • PyrisLectureSearchResultDTO — added @JsonAlias for snake_case field names (lecture_unit, page_number, start_time) for resilience.

Client

  • Iris answer component — subscribes to the WebSocket channel after calling ask(). On the first callback (answer text only), renders the answer with [CITATION_n] placeholders replaced by skeleton bubbles. On the second callback (answer + sources), replaces skeletons with IrisCitationTextComponent source cards that link directly to the lecture unit at the correct timestamp or page.
  • Lecture result cardsrouterLink now includes queryParams with unit, timestamp, and page values from the search result, enabling direct navigation to the relevant moment.
  • LectureSearchService.ask() — new method calling the /api/iris/search-answer endpoint.
  • LectureUnitInfo.startTime — added to carry the video timestamp from the search result to the deep link.

Bug fix

ensureValidDeepLinkTargets() in CourseLectureDetailsComponent was incorrectly clearing targetVideoTimestamp for any lecture unit that had a PDF attachment, even when the unit also had a video source. The fix makes the two checks independent: timestamp is cleared only when there is no video; page is cleared only when there is no PDF.

Steps for Testing

Prerequisites:

  • Iris enabled on the Artemis instance (Pyris running and connected)
  • A course with at least one lecture containing an AttachmentVideoUnit that has both a video source (HLS) and a PDF attachment
  • 1 Instructor and 1 Student enrolled in the course

Lecture result deep-linking:

  1. Log in as a Student.
  2. Open the global search (navbar search icon).
  3. Search for a term that matches a lecture unit containing a video (e.g. a keyword from the slide title).
  4. Click a lecture result card — verify the lecture unit opens and the video starts at the timestamp shown in the result (not at 00:00).
  5. Repeat for a result with a page number — verify the PDF opens at the correct page.

Ask Iris with inline citations:

  1. Log in as a Student.
  2. Open the global search and enter a question (e.g. "What is a process?").
  3. Click "Ask Iris" (or the Iris button in the search panel).
  4. Verify skeleton citation bubbles appear while Iris is computing sources.
  5. After a few seconds, verify the skeleton bubbles are replaced by source cards with the lecture name, unit name, and a timestamp/page link.
  6. Click a source card — verify navigation to the correct lecture unit at the correct position.

Bug fix verification:

  1. Open a lecture unit that has both a video and a PDF attachment.
  2. Navigate to it via a URL with ?unit=<id>&timestamp=30&page=2.
  3. Verify the video starts at 0:30 AND the PDF opens at page 2 (previously the timestamp was ignored).

Testserver States

You can manage test servers using Helios. Check environment statuses in the environment list. To deploy to a test server, go to the CI/CD page, find your PR or branch, and trigger the deployment.

Review Progress

Performance Review

  • I (as a reviewer) confirm that the client changes (in particular related to REST calls and UI responsiveness) are implemented with a very good performance even for very large courses with more than 2000 students.
  • I (as a reviewer) confirm that the server changes (in particular related to database calls) are implemented with a very good performance even for very large courses with more than 2000 students.

Code Review

  • Code Review 1
  • Code Review 2

Manual Tests

  • Test 1
  • Test 2

Test Coverage

Client

Class/File Line Coverage Lines Expects Ratio
global-search-iris-answer.component.ts 94.28% 157 33 21.0
global-search-lecture-results.component.ts 92.15% 103 28 27.2
iris-search-result.model.ts not found (modified) 6 ? ?
lecture-search-result.model.ts not found (modified) 21 ? ?
lecture-search.service.ts 100.00% 16 9 56.3
course-lecture-details.component.ts 76.36% 251 28 11.2

Server

Class/File Line Coverage Lines
IrisSearchAnswerCallbackDTO.java 100.00% 7
IrisSearchWebsocketDTO.java 100.00% 7
PyrisConnectorService.java 65.47% 269
PyrisJobService.java 96.00% 161
IrisSearchAskClientRequestDTO.java 100.00% 8
PyrisLectureSearchResultDTO.java 100.00% 16
PyrisSearchAskRequestDTO.java 100.00% 10
PyrisSearchAskResponseDTO.java not found (deleted) ?
SearchAnswerJob.java 50.00% 10
IrisLectureSearchResource.java 100.00% 47
PyrisInternalStatusUpdateResource.java 79.66% 161

Last updated: 2026-04-13 08:54:02 UTC

Screenshots

Edutelligence counterpart: ls1intum/edutelligence#506

Closes IRIS-289

Introduces the server-side plumbing for the async Ask Iris flow:
- SearchAnswerJob tracks in-flight ask requests by user login
- IrisSearchAnswerCallbackDTO receives Pyris's two-phase callback
- IrisSearchWebsocketDTO delivers the answer to the client over WebSocket
- IrisSearchAskClientRequestDTO is the client-facing request body
- PyrisJobService gains addSearchAnswerJob()
- PyrisConnectorService gains searchAsk() to forward requests to Pyris
- PyrisSearchAskRequestDTO updated with artemis_base_url and authentication_token
- PyrisSearchAskResponseDTO removed (superseded by the job/callback model)
- POST api/iris/search-answer: accepts query+limit from the client,
  creates a SearchAnswerJob, forwards the request to Pyris, and
  returns the job token immediately (202 Accepted)
- POST api/iris/internal/search/runs/{runId}/status: receives Pyris's
  two-phase callback (cited=false then cited=true) and forwards each
  payload to the requesting user via WebSocket
Pyris sends camelCase in its API responses but the primary Pydantic field
names are snake_case. Adding @JsonAlias("snake_case") ensures the DTO
can deserialize both formats, making the mapping resilient to changes in
Pyris serialization configuration.
…rch service

- LectureUnitInfo gains optional startTime (seconds) for video deep-linking
- IrisSearchWebsocketDTO model added for the WebSocket answer payload
- LectureSearchService gains ask() which calls /api/iris/search-answer
  and returns the job token used to subscribe to the WebSocket channel
…deep links

The Iris answer panel now drives the full Ask Iris flow:
- Subscribes to the WebSocket channel returned by ask() and receives two
  callbacks: plain markdown with skeleton citation placeholders (cited=false),
  then the final answer with inline [cite:N] markers (cited=true)
- Renders partial HTML with skeleton bubbles while the answer streams in,
  then switches to IrisCitationTextComponent for the final cited answer
- Source cards list the referenced lecture units with routerLinks that
  deep-link to the exact unit, page, and video timestamp
- Loading states: idle → loading → partial → complete
…ult cards

Result cards now navigate to the exact position in the lecture unit:
- unit=<id> selects and expands the target lecture unit
- page=<n> scrolls the PDF viewer to the matched slide
- timestamp=<s> seeks the HLS video player to the matched segment (seconds)
Both keyboard (Enter) and click navigation include all three params.
…nd PDF

ensureValidDeepLinkTargets previously cleared targetVideoTimestamp whenever
the attachment link ended in .pdf, even if the unit also had a videoSource.
This caused the video player to always start at 00:00 for lecture units that
combine a video recording with PDF slides.

Fix: clear each param independently — only clear the timestamp when there is
no video source, only clear the page when there is no PDF attachment. Units
with both preserve both deep-link targets simultaneously.

Also passes targetTimestamp through to jhi-video-player and exposes the
transcript layout in the video player template.
- IrisRequestMockProvider: add mockSearchAsk() and mockSearchAskError()
- IrisLectureSearchIntegrationTest: add ask_shouldReturn202WithToken,
  ask_whenPyrisFails, ask_asUnauthenticated, and
  searchCallback_withValidToken / withInvalidToken tests
- iris-answer spec: updated for the new WebSocket-driven loading states
  and inline citation rendering
- lecture-results spec: updated for the added deep-link query params
- lecture-search.service spec: updated for the new ask() method
@github-project-automation github-project-automation bot moved this to Work In Progress in Artemis Development Apr 13, 2026
@github-actions github-actions bot added tests server Pull requests that update Java code. (Added Automatically!) client Pull requests that update TypeScript code. (Added Automatically!) core Pull requests that affect the corresponding module iris Pull requests that affect the corresponding module lecture Pull requests that affect the corresponding module labels Apr 13, 2026
@Nayer-kotry Nayer-kotry changed the title Feature/global search/add inline citations and deep linking iris; Add inline citations and deep linking to global search Apr 13, 2026
@Nayer-kotry Nayer-kotry changed the title iris; Add inline citations and deep linking to global search iris: Add inline citations and deep linking to global search Apr 13, 2026
@Nayer-kotry Nayer-kotry changed the title iris: Add inline citations and deep linking to global search Iris: Add inline citations and deep linking to global search Apr 13, 2026
@github-actions
Copy link
Copy Markdown

@Nayer-kotry Test coverage has been automatically updated in the PR description.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 13, 2026

End-to-End Test Results

Phase Status Details
Phase 1 (Relevant) ✅ Passed
TestsPassed ✅SkippedFailedTime ⏱
Phase 1: E2E Test Report28 ran28 passed0 skipped0 failed3m 16s
Phase 2 (Remaining) ✅ Passed
TestsPassed ✅Skipped ⚠️FailedTime ⏱
Phase 2: E2E Test Report224 ran222 passed2 skipped0 failed24m 21s

Test Strategy: Two-phase execution

  • Phase 1: e2e/Login.spec.ts e2e/Logout.spec.ts e2e/SystemHealth.spec.ts e2e/course/CourseManagement.spec.ts e2e/lecture/
  • Phase 2: e2e/atlas/ e2e/course/CourseChannelMessages.spec.ts e2e/course/CourseDirectMessages.spec.ts e2e/course/CourseExercise.spec.ts e2e/course/CourseGroupChatMessages.spec.ts e2e/course/CourseMessageInteractions.spec.ts e2e/course/CourseOnboarding.spec.ts e2e/exam/ExamAssessment.spec.ts e2e/exam/ExamChecklists.spec.ts e2e/exam/ExamCreationDeletion.spec.ts e2e/exam/ExamDateVerification.spec.ts e2e/exam/ExamManagement.spec.ts e2e/exam/ExamParticipation.spec.ts e2e/exam/ExamResults.spec.ts e2e/exam/ExamTestRun.spec.ts e2e/exam/test-exam/ e2e/exercise/ExerciseImport.spec.ts e2e/exercise/file-upload/ e2e/exercise/modeling/ e2e/exercise/programming/ e2e/exercise/quiz-exercise/ e2e/exercise/text/

Overall: ✅ All E2E tests passed (both phases)

🔗 Workflow Run · 📊 Test Report

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

client Pull requests that update TypeScript code. (Added Automatically!) core Pull requests that affect the corresponding module iris Pull requests that affect the corresponding module lecture Pull requests that affect the corresponding module server Pull requests that update Java code. (Added Automatically!) tests

Projects

Status: Work In Progress

Development

Successfully merging this pull request may close these issues.

1 participant