Iris: Add inline citations and deep linking to global search#12522
Draft
Nayer-kotry wants to merge 8 commits intodevelopfrom
Draft
Iris: Add inline citations and deep linking to global search#12522Nayer-kotry wants to merge 8 commits intodevelopfrom
Iris: Add inline citations and deep linking to global search#12522Nayer-kotry wants to merge 8 commits intodevelopfrom
Conversation
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
iris; Add inline citations and deep linking to global search
iris; Add inline citations and deep linking to global searchiris: Add inline citations and deep linking to global search
iris: Add inline citations and deep linking to global searchIris: Add inline citations and deep linking to global search
|
@Nayer-kotry Test coverage has been automatically updated in the PR description. |
End-to-End Test Results
Test Strategy: Two-phase execution
Overall: ✅ All E2E tests passed (both phases) 🔗 Workflow Run · 📊 Test Report |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR extends the global lecture search with two features:
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).
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
Client
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 aSearchAnswerJobto 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 withcited=falseto deliver the answer text, then withcited=trueto deliver the answer with source citations). Each callback is forwarded to the requesting user via WebSocket asIrisSearchWebsocketDTO.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@JsonAliasfor snake_case field names (lecture_unit,page_number,start_time) for resilience.Client
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 withIrisCitationTextComponentsource cards that link directly to the lecture unit at the correct timestamp or page.routerLinknow includesqueryParamswithunit,timestamp, andpagevalues from the search result, enabling direct navigation to the relevant moment.LectureSearchService.ask()— new method calling the/api/iris/search-answerendpoint.LectureUnitInfo.startTime— added to carry the video timestamp from the search result to the deep link.Bug fix
ensureValidDeepLinkTargets()inCourseLectureDetailsComponentwas incorrectly clearingtargetVideoTimestampfor 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:
AttachmentVideoUnitthat has both a video source (HLS) and a PDF attachmentLecture result deep-linking:
Ask Iris with inline citations:
Bug fix verification:
?unit=<id>×tamp=30&page=2.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
Code Review
Manual Tests
Test Coverage
Client
Server
Last updated: 2026-04-13 08:54:02 UTC
Screenshots
Edutelligence counterpart: ls1intum/edutelligence#506
Closes IRIS-289