Skip to content

Grand refactoring of the AI features#15688

Merged
koppor merged 284 commits into
JabRef:mainfrom
InAnYan:refactor/ai-1
May 20, 2026
Merged

Grand refactoring of the AI features#15688
koppor merged 284 commits into
JabRef:mainfrom
InAnYan:refactor/ai-1

Conversation

@InAnYan

@InAnYan InAnYan commented May 6, 2026

Copy link
Copy Markdown
Member

Related issues and pull requests

Closes https://github.com/JabRef/jabref-issue-melting-pot/issues/698

Relevant documentation change: JabRef/user-documentation#625

PR Description

A big refactoring PR of the AI features. Also has new features.

Main changes:

  • Refactored UI to use the MVVM pattern.
  • Added regenerate button to chat messages.
  • Added the "AI Chat Status" window to show the state of an AI chat, ingested files, chatted entries and miscalleneus actions.
  • Added an abstraction layer for document splitter algorithms.
  • Added an abstraction layer for summarization algorithms.
  • Added a "full-document" summarization algorithm which just sends the full paper to the user message.
  • Added an abstraction layer for token estimation algorithms.
  • Added an abstraction layer for "answer engines" (it controls how RAG is performed).
  • Added a "full text" document engine: the full paper is sent to the AI instead of embeddings (customizable).
  • Added some starting code to allow to customize AI parameters per session (for default values for summarization algorithm and answer engine is stored in AI preferences, but they can be customized in place).
  • Added abilities to cancel AI requests (summarization, chatting).
  • Added AI requirements (yes, it's a bit extra and low-priority, but I liked the idea of requirements tracking and wanted to see how it would work and look in practice).
  • Made the task and storage logic more clear (TrackedBackgroundTask, *TaskAggregator, InMemoryChache).
  • Added unit tests for many clases.
  • Migrated to other schema of storage for summaries, chat history, and embeddings. As a result wrote a migration.
  • (Controversial) added an AI library ID that is a part of database metadata. I admit I should've discussed this with the maintainers team, but it solves some architectural problems of storing AI artifacts.
  • Ocasinally added some helpers for JavaFX observables.
  • Chat messages now have a timestamp.
  • Added comments for some classes whose purpose is not understood on the first sight.

Preferences:
java_UP6kzObEUv
java_uZF4kDsdKw

Summarization:
java_8LOLlTuFZw

java_oh943SJSop

AI chat:
java_cXZPF3HcJa

java_UkpuHcJqox

Steps to test

Test summarization:

  1. Click on an entry with a linked file.
  2. Go to "AI Summary" tab.
  3. Wait for summary generation.
  4. Observe the summary.
  5. Try clicking on "Regenerate (custom)" and choose other summarization algorithm.

Test chatting:

  1. Click on an entry with a linked file.
  2. Go to "AI Chat" tab.
  3. Click on info button to open the chat status window.
  4. See that you have the current chat model, selected answer engine, look at the entries used in chat (more interesting in the group chat), check the status of ingestion.
  5. Close and chat with AI.
  6. Hover on an AI message and find the "rotation" button. Click on it to regenerate the response.
  7. Go to the AI chat status window and change the answer engine.
  8. Chat and compare the results.
  9. Close JabRef.
  10. Open again JabRef, the same library and entry.
  11. See that the chat history is preserved.

Test chat/summary migration:

  1. Run this PR.
  2. Open a library with entries you have chatted before.
  3. Open logs and see that there should be a log about "Successfully migrated chat messages/summaries".
  4. Open entries and see that you have the same chats and summaries.

Checklist

  • I own the copyright of the code submitted and I license it under the MIT license
  • I manually tested my changes in running JabRef (always required)
  • I added JUnit tests for changes (if applicable)
  • I added screenshots in the PR description (if change is visible to the user)
  • [/] I added a screenshot in the PR description showing a library with a single entry with me as author and as title the issue number
  • I described the change in CHANGELOG.md in a way that can be understood by the average user (if change is visible to the user)
  • I checked the user documentation for up to dateness and submitted a pull request to our user documentation repository

AI Usage Disclosure

While refactoring and adding new features I have used the AI tools like GitHub Copilot, IntelliJ Junie a lot. All AI generated code was carefully reviewed, edited and tested by me, and I take the full responsibility of the AI results.

Comment thread jabgui/src/main/resources/org/jabref/gui/preferences/ai/AiTab.fxml
koppor
koppor previously approved these changes May 19, 2026
@Siedlerchr Siedlerchr enabled auto-merge May 19, 2026 21:08
@Siedlerchr Siedlerchr disabled auto-merge May 19, 2026 21:08
@github-actions github-actions Bot added status: changes-required Pull requests that are not yet complete and removed status: no-bot-comments labels May 20, 2026
@koppor koppor enabled auto-merge May 20, 2026 07:47
@github-actions github-actions Bot added status: no-bot-comments and removed status: changes-required Pull requests that are not yet complete labels May 20, 2026
@koppor koppor added this pull request to the merge queue May 20, 2026
@github-actions github-actions Bot added the status: to-be-merged PRs which are accepted and should go into the merge-queue. label May 20, 2026
Merged via the queue into JabRef:main with commit fb23a09 May 20, 2026
54 of 56 checks passed
@koppor koppor deleted the refactor/ai-1 branch May 20, 2026 08:28
Siedlerchr added a commit that referenced this pull request May 20, 2026
* upstream/main:
  Update PULL_REQUEST_TEMPLATE.md (#15788)
  New Crowdin updates (#15787)
  Update heylogs to 0.18.0 and use github-actions format (#15786)
  Grand refactoring of the AI features (#15688)
  Chore(deps): Bump com.fasterxml:aalto-xml in /versions (#15782)
  Chore(deps): Bump org.junit:junit-bom from 6.0.3 to 6.1.0 in /versions (#15783)
  Fix default value for unwanted characters (#15743)
  Fix runner tag
  Fix runner for JBang (PR)
  Fix duplicate finder progress counter incrementing on empty queue polls (#15781)
  Refine JabKit CLI: positional input argument and check command group (#15759)
  Ignore exception in unregisterListener to prevent exception (#15761)
  Fix wrong usage of "key" (#15779)
  Fix Hayagriva export to nest identifiers under serial-number (#15750)
f0restron07 pushed a commit to f0restron07/jabref that referenced this pull request May 24, 2026
* Refactor chat history

* refactor chat history + start working on UI

* initroduce 1 class. idk why

* Move out database listeners

* middle work

* middle work

* middle work

* middle work

* Start working on tasks

* middle work

* Somewhat AiSummary

* middle work

* summary almost done

* middle work

* Finish AI summary

* Refactor locations

* Start working on AI chat UI

* Use single chat history repository

* Remove old classes

* Remove old classes x2

* Middle work

* middle work

* finish chat?

* Something works

* bug fixing

* fix Ai chat

* Implement status window

* Fixings

* rename identifiers

* Make group window

* Fix window close

* Some arch changes

* Add ADRs

* Some changes

* mid work

* Almost finished

* Add draft reqs

* fix ai chat

* add ai OFT

* add adr for messages

* Empty messages package

* add ADR on messages

* add new types

* Migrate to new type (todo: migrate messages v1->v2, show debug, export, oft)

* feat: mid work on removing

* refactor(ai): refactor AiDatabaseListener and AiFeature

* refactor(ai): remove AiTemplateKind

* refactor(ai): fix AiDatabaseListeners

* refactor(ai): start AiSummarizationLogic

* refactor(ai): petit change

* refactor(ai): remove some user message templates

* refactor(ai): add ai library id

* refactor(ai): refactor ChatIdentifier with ai library id

* refactor(ai): refactor AiSummaryIdentifier

* refactor(ai): rename AI identifiers

* refactor(ai): rename AI identifiers

* refactor(ai): remove AiTemplateKind.java

* refactor(ai): change tokenizators parameters

* refactor(ai): remove customimplementations package

* refactor(ai): make TokenizationAiFeature

* refactor(ai): change order of the responsibility chain

* refactor(ai): use string templates

* chore(gui): remove unused file

* fix(ai): add system message to the chat

* refactor(ai): use hash for ingested documents tracking

* refactor(ai): add GenerateEmbeddingsAiDatabaseListener and move files

* refactor(ai): add transfer of summaries

* refactor(ai): petit change

* refactor(ai): simplify templates

* refactor(ai): fix name

* refactor(ai): add factories

* refactor(ai): refactor summarization + add RAM layer

* refactor(ai): make migrations

* refactor(ai): remove features

* refactor(ai): remove AiChatLogic and introduce GenerateRagResponseTask

* refactor(ai): clean-up code

* Initial plan

* Initial plan

* feat: implement AI export feature for chat and summary

Agent-Logs-Url: https://github.com/InAnYan/jabref/sessions/97e3a57c-35e3-47c4-872e-370399072f7f

Co-authored-by: InAnYan <13097618+InAnYan@users.noreply.github.com>

* fix: use correct BibEntryWriter API for BibTeX serialization

Agent-Logs-Url: https://github.com/InAnYan/jabref/sessions/97e3a57c-35e3-47c4-872e-370399072f7f

Co-authored-by: InAnYan <13097618+InAnYan@users.noreply.github.com>

* refactor: extract helper methods to reduce duplication in AiChatView export

Agent-Logs-Url: https://github.com/InAnYan/jabref/sessions/97e3a57c-35e3-47c4-872e-370399072f7f

Co-authored-by: InAnYan <13097618+InAnYan@users.noreply.github.com>

* Implement follow-up questions feature for AI chat

Agent-Logs-Url: https://github.com/InAnYan/jabref/sessions/a7d0619b-da31-4013-b6bc-c945b94570c6

Co-authored-by: InAnYan <13097618+InAnYan@users.noreply.github.com>

* Fix magic number and duplicate regex pattern issues from code review

Agent-Logs-Url: https://github.com/InAnYan/jabref/sessions/a7d0619b-da31-4013-b6bc-c945b94570c6

Co-authored-by: InAnYan <13097618+InAnYan@users.noreply.github.com>

* Fix constant placement and remove duplicate constant for code review feedback

Agent-Logs-Url: https://github.com/InAnYan/jabref/sessions/a7d0619b-da31-4013-b6bc-c945b94570c6

Co-authored-by: InAnYan <13097618+InAnYan@users.noreply.github.com>

* Changes before error encountered

Agent-Logs-Url: https://github.com/InAnYan/jabref/sessions/d86d0212-7cca-4a28-b941-2d31a9b6bfbc

Co-authored-by: InAnYan <13097618+InAnYan@users.noreply.github.com>

* Address review comments: BackgroundTask for follow-up questions, ViewModel actions, cleanup

Agent-Logs-Url: https://github.com/InAnYan/jabref/sessions/96988cba-e6b3-47de-b08a-5099969cbca9

Co-authored-by: InAnYan <13097618+InAnYan@users.noreply.github.com>

* Update AiTab.java

* Update AiTab.java

* refactor(ai): update embeddings to use file hash

* Update GenerateFollowUpQuestions.java

* refactor: move export logic to view models, add interfaces, add markdown export template

- Introduce AiChatExporter and AiSummaryExporter interfaces
- Add configurable markdown export Velocity template to AiPreferences
  (AiDefaultTemplates, AiPreferences, JabRefCliPreferences, AiTemplateRenderer)
- Add ExportMessage helper class to AiTemplateRenderer for Velocity template context
- Rewrite AiChatMarkdownExporter to use Velocity template rendering
- Make AiChatJsonExporter and AiChatMarkdownExporter implement AiChatExporter
- Make AiSummaryJsonExporter and AiSummaryMarkdownExporter implement AiSummaryExporter
- Move exportMarkdown/exportJson from AiChatView to AiChatViewModel
- Move exportMarkdown/exportJson from AiSummaryView to AiSummaryViewModel
- Add markdown export template tab to AI preferences UI (AiTab.fxml, AiTab.java, AiTabViewModel)
- Fix wrong Javadoc in AiChatJsonExporter (said Markdown, now says JSON)

Agent-Logs-Url: https://github.com/InAnYan/jabref/sessions/0643754f-446a-4022-89a2-03f5a4e38465

Co-authored-by: InAnYan <13097618+InAnYan@users.noreply.github.com>

* refactor: improve method names per code review feedback

Agent-Logs-Url: https://github.com/InAnYan/jabref/sessions/0643754f-446a-4022-89a2-03f5a4e38465

Co-authored-by: InAnYan <13097618+InAnYan@users.noreply.github.com>

* refactor: add AiMetadata record, remove ExportMessage, use ChatMessage in templates

- Create AiMetadata record in org.jabref.model.ai with aiProvider, model, timestamp
- Update AiChatExporter and AiSummaryExporter interfaces to accept AiMetadata
- Remove ExportMessage helper class from AiTemplateRenderer; pass ChatMessage directly
- Update renderMarkdownChatExport to accept AiMetadata and List<ChatMessage>
- Update default template to use $message.role().getDisplayName() and $message.content()
- Update AiChatJsonExporter to use AiMetadata (single export method)
- Update AiChatMarkdownExporter to pass metadata and filtered ChatMessages
- Update AiSummaryMarkdownExporter and AiSummaryJsonExporter to accept AiMetadata
- Update AiChatViewModel.buildMetadata() from ChatModel
- Update AiSummaryViewModel.buildMetadata() from AiSummary

Agent-Logs-Url: https://github.com/InAnYan/jabref/sessions/ef6acf07-7477-4765-b518-b687ea537b01

Co-authored-by: InAnYan <13097618+InAnYan@users.noreply.github.com>

* style: convert /// comments to standard Javadoc in AiMetadata and AiTemplateRenderer

Agent-Logs-Url: https://github.com/InAnYan/jabref/sessions/ef6acf07-7477-4765-b518-b687ea537b01

Co-authored-by: InAnYan <13097618+InAnYan@users.noreply.github.com>

* refactor: embed AiMetadata in AiSummary; remove AiMetadata.empty()

- Remove AiMetadata.empty() factory method
- Add Jackson annotations to AiMetadata for JSON serialization
- AiSummary now stores AiMetadata (replacing timestamp/aiProvider/model fields)
- Update BibEntrySummarizator to build AiMetadata when creating AiSummary
- Update SummariesMigration to build AiMetadata when converting old summaries
- Update AiSummaryShowingView to use summary.metadata().xxx()
- Update AiSummaryViewModel.buildMetadata() to return summary.metadata() directly
- Update AiChatViewModel.buildMetadata() to inline empty-model fallback

Agent-Logs-Url: https://github.com/InAnYan/jabref/sessions/2ad7966f-6d03-41db-8c8e-17e6f58a421d

Co-authored-by: InAnYan <13097618+InAnYan@users.noreply.github.com>

* refactor: move export responsibility to AiSummaryShowingViewModel

- AiSummaryShowingViewModel now takes AiPreferences, FieldPreferences,
  BibEntryTypesManager, DialogService and owns the export logic directly
- AiSummaryShowingViewModel gains entryProperty (FullBibEntry) for export
- AiSummaryShowingView gains @Inject for GuiPreferences, DialogService,
  BibEntryTypesManager; passes them to AiSummaryShowingViewModel
- AiSummaryShowingView exposes entryProperty() so AiSummaryView can bind it
- AiSummaryView.setupBindings() now also binds summaryShowing.entryProperty()
- AiSummaryView no longer has exportMarkdown/exportJson FXML handlers
- AiSummaryViewModel export methods and related imports removed
- AiSummary.fxml: removed onExportMarkdown/onExportJson handler wiring
  (export is now fully self-contained in AiSummaryShowingView)

Agent-Logs-Url: https://github.com/InAnYan/jabref/sessions/a09f6212-dde3-4c26-9ee5-a22d1fe3bfa3

Co-authored-by: InAnYan <13097618+InAnYan@users.noreply.github.com>

* style: remove trailing blank line in AiSummaryShowingView

Agent-Logs-Url: https://github.com/InAnYan/jabref/sessions/a09f6212-dde3-4c26-9ee5-a22d1fe3bfa3

Co-authored-by: InAnYan <13097618+InAnYan@users.noreply.github.com>

* refactor(ai): add chat in memory cache

* refactor(ai): make compile

* refactor(ai): clean ups

* refactor(ai): fix migrations

* refactor(ai): quick fix for migrations

* refactor(ai): fix

* refactor(ai): fix migration

* refactor(ai): fix a bit the follow up questions

* refactor(ai): fix chat history scroll

* feat: redesign AI chat - move model to status window, add export there, move clear button right, add confirm dialog

Agent-Logs-Url: https://github.com/InAnYan/jabref/sessions/37c13a31-503d-4677-996a-8987a7aa7a22

Co-authored-by: InAnYan <13097618+InAnYan@users.noreply.github.com>

* refactor: extract formatChatModelLabel into named method in AiChatStatusView

Agent-Logs-Url: https://github.com/InAnYan/jabref/sessions/37c13a31-503d-4677-996a-8987a7aa7a22

Co-authored-by: InAnYan <13097618+InAnYan@users.noreply.github.com>

* refactor(ai): clean quickly

* refactor: move chat model building and export to AiChatStatusViewModel; bind chatHistory from AiChatViewModel

Agent-Logs-Url: https://github.com/InAnYan/jabref/sessions/07c35f08-0d20-4817-a568-88778f4e9c81

Co-authored-by: InAnYan <13097618+InAnYan@users.noreply.github.com>

* refactor(ai): add embedding model download cache

* refactor(ai): clear ingested documents on embedding model change

* refactor(ai): ui change

* refactor(ai): ui change for Ai Tab

* refactor(ai): restore regenerate message func

* refactor(ai): fix adr order

* refactor(ai): fix adr order

* refactor(ai): fix chatting requirements

* refactor(ai): change future feature

* refactor(ai): clean and fix file hasher tests

* refactor(ai): remove persited file ingestor test

* refactor(ai): refactor plain citation parsing with llm

* refactor(ai): less metadata changes

* refactor(ai): improve comment

* refactor(ai): refactor EmbeddingSimilarityMetric

* refactor(ai): quick modules change

* refactor(ai): remove ResolvedGroup

* refactor(ai): update AI docs

* refactor(ai): Rename PredefinedEmbeddingModel

* refactor(ai): change ChatMessage.Role

* refactor(ai): change ChatHistoryRecord

* refactor(ai): cleanups

* refactor(ai): remove ListenersHelper.java

* refactor(ai): remove CitationKeyCheck.java

* refactor(ai): fix AiPreferences

* refactor(ai): revert MVStoreBase

* refactor(ai): remove BibEntryListComparatorById

* refactor(ai): do not save if deleted

* refactor(ai): simplify chunked summarization logic

* refactor(ai): clean

* refactor(ai): clean

* refactor(ai): refactor

* refactor(ai): move exporters

* refactor(ai): static class refactors

* refactor(ai): remove comment

* refactor(ai): refactor answer engines

* refactor(ai): remove comment

* refactor(ai): cleanup + ingestion

* refactor(ai): cleanup

* refactor(ai): refactor ingestion + clean ups + follow-up questions

* refactor(ai): refactor bindings

* refactor(ai): simplify chat message

* refactor(ai): change UI ai chat

* refactor(ai): remove PropertiesHelper.java

* refactor(ai): block chat history during loading

* refactor(ai): refactor to use one status pane

* refactor(ai): move files + add tooltip

* refactor(ai): refactor bindings + cleanup

* refactor(ai): refactor ai default preferences

* refactor(ai): add API key changes in Preferences

* refactor(ai): convert javadoc to markdown

* Revert "refactor(ai): convert javadoc to markdown"

This reverts commit 5ed90c4.

* refactor(ai): make it run

* refactor(ai): fix + fix checkstyle

* refactor(ai): add tests

* refactor(ai): convert comments

* refactor(ai): docs

* refactor(ai): update ADR 0057

* refactor(ai): revert checkstyle

* refactor(ai): add MVStore comment

* refactor(ai): update docs

* refactor(ai): add testing resources

* refactor(ai): update from my review

* refactor(ai): add context menu for chat messages

* refactor(ai): remove assertions

* refactor(ai): just fixes

* refactor(ai): fix tracing

* refactor(ai): add CHANGELOG entry

* refactor(ai): add answer engine combobox

* docs(adr): use CUID2 for aiLibraryId

Add ADR-0059 documenting the choice of CUID2 over v4 UUID for the
`aiLibraryId` written into shared `.bib` files.

Co-authored-by: Ruslan <ruslanpopov1512@gmail.com>
Co-authored-by: ThiloteE <73715071+ThiloteE@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* refactor(ai): rename vBox/buttonsVBox in AiChatMessageView

Rename FXML-injected fields to describe their UI role rather than their
JavaFX type: `vBox` -> `bubble` (carries the chat-bubble styleClass),
`buttonsVBox` -> `buttons`. Matching `fx:id`s in `AiChatMessage.fxml`
updated.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* undo

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* refactor(ai): tidy AiChatMessageView

Use StringUtil.makeSafe for the null-to-empty content fallback, rename
the lambda parameter `v` to `role` in the role-comparison maps, and add
a short comment explaining the orElse(false) on the pseudo-class
bindings.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* refactor(ai): localize FileStatus and use Directories.getUserDirectory

In AiChatStatusViewModel, give FileStatus a private displayName field
populated through the constructor with Localization.lang(...) at each
constant, matching the ChatMessage.Role pattern.

Replace Path.of(System.getProperty("user.home")) with
Directories.getUserDirectory() in both AiChatStatusViewModel
(exportMarkdown / exportJson) and AiSummaryShowingViewModel.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* refactor(ai): collapse 2-value State enums to BooleanProperty

AiEntryChatViewModel and AiGroupChatViewModel both had a State enum
with only AI_TURNED_OFF and CHATTING, bound through BindingsHelper.bindEnum
on top of aiPreferences.enableAiProperty().not(). That is a boolean
dressed as an enum. Replace it with a BooleanProperty `enabled` bound
directly to aiPreferences.enableAiProperty(), and update the matching
view bindings to enabledProperty()/.not(). The multi-valued State enums
in AiChatViewModel and AiSummaryViewModel are left alone.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* Fix HTML block

* reactor(ai): fix markdown

* reactor(ai): fix new lines at the end

* reactor(ai): fix from review

* reactor(ai): fix do while loop

* reactor(ai): fix new lines

* Fix submodules

* reactor(ai): fix code style

* reactor(ai): fix annotations

* reactor(ai): fix links in markdown

* reactor(ai): fix CHANGELOG

* reactor(ai): update module-info.java

* reactor(ai): apply openrewrite

* reactor(ai): migrate to jackson 3

* reactor(ai): fix links

* reactor(ai): fix code style

* reactor(ai): fix arch tests

* reactor(ai): fix arch tests x2

* reactor(ai): add check if a file is already ingested

* reactor(ai): fix chat migration

* reactor(ai): fix code style

* fix(docs/requirements): add expert settings AI requirements

* reactor(ai): fix module info

* fix: fix module info

* fix: fix module info

* fix: build gradle

* reactor(ai): fix localization

* reactor(ai): fix tests

* Update CHANGELOG.md

* reactor(ai): fix order of ADR

* reactor(ai): add new good to the adr

* reactor(ai): refine requirements

* reactor(ai): refine null check

* reactor(ai): add qodo comments

* reactor(ai): add qodo comments

* reactor(ai): fix formatting

* reactor(ai): fix from qodo

* reactor(ai): move AI adrs

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: InAnYan <13097618+InAnYan@users.noreply.github.com>
Co-authored-by: Oliver Kopp <kopp.dev@gmail.com>
Co-authored-by: ThiloteE <73715071+ThiloteE@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Compliance violation component: ai status: no-bot-comments status: to-be-merged PRs which are accepted and should go into the merge-queue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants