Add Markdown and JSON export buttons to AI Summary and Chat tabs#14486
Conversation
|
@CXZHANG0508 Please do not close PRs and open new ones. Just push changes to your branch. |
|
Understood, thanks! |
|
Hi, thanks for your contribution. Please use a proper PR title. "Feat ai export" is not enough to quickly understand from the table what this PR is about. Remember that in open source you occansionally happen to work with other people together and as long as we dont have the technology for mind sharing and telepathy, we need to keep communicating in a mutual understandable way. |
|
Thank you for your feedback! I apologize for the previous simple title. I have updated the PR title to be more descriptive.I hope that is clearer. |
| StringBuilder bibtex = new StringBuilder(); | ||
| bibtex.append("@").append(entry.getType().getName()).append("{").append(entry.getCitationKey().orElse("")).append(",\n"); | ||
| for (Field field : entry.getFields()) { | ||
| bibtex.append(" ").append(field.getName()).append(" = {").append(entry.getField(field).orElse("")).append("},\n"); | ||
| } | ||
| bibtex.append("}"); | ||
| root.put("entry_bibtex", bibtex.toString()); |
There was a problem hiding this comment.
No - please use getStringRepresentation will be available with #14504
| private final Runnable exportMarkdownCallback; | ||
| private final Runnable exportJsonCallback; |
There was a problem hiding this comment.
Annotate with @Nullable.
I wonder when this can be null? Maybe, it is always non-null? Please double check the call of SummaryShowingComponent and the ifs later at onExportMarkdown.
There was a problem hiding this comment.
I think, it needs to be annotated with @NonNull? (because of your change in line 33)
| sb.append("## Bibtex\n\n```bibtex\n"); | ||
| sb.append("@").append(entry.getType().getName()).append("{").append(entry.getCitationKey().orElse("")).append(",\n"); | ||
| for (Field field : entry.getFields()) { | ||
| String value = entry.getField(field).orElse(""); | ||
| sb.append(" ").append(field.getName()).append(" = {").append(value).append("},\n"); | ||
| } | ||
| sb.append("}\n```\n\n"); | ||
| sb.append("## ").append(contentTitle).append("\n\n"); | ||
| sb.append(contentBody); |
There was a problem hiding this comment.
For BibEntry export use getStringRepresentation will be available with #14504
There was a problem hiding this comment.
I will wait for it to be merged and then update this PR to use the new method immediately.
|
See #13868 (comment) why there are two PRs. |
| @FXML private Text summaryInfoText; | ||
| @FXML private CheckBox markdownCheckbox; | ||
| @FXML | ||
| private Text summaryInfoText; | ||
| @FXML | ||
| private CheckBox markdownCheckbox; |
There was a problem hiding this comment.
This is some other code style. But I think, its OK to keep the reformatting.
| @FXML private Loadable uiLoadableChatHistory; | ||
| @FXML private ChatHistoryComponent uiChatHistory; | ||
| @FXML private Button notificationsButton; | ||
| @FXML private ChatPromptComponent chatPrompt; | ||
| @FXML private Label noticeText; | ||
| @FXML private Hyperlink exQuestion1; | ||
| @FXML private Hyperlink exQuestion2; | ||
| @FXML private Hyperlink exQuestion3; | ||
| @FXML private HBox exQuestionBox; | ||
| @FXML private HBox followUpQuestionsBox; | ||
| @FXML | ||
| private Loadable uiLoadableChatHistory; | ||
| @FXML | ||
| private ChatHistoryComponent uiChatHistory; | ||
| @FXML | ||
| private Button notificationsButton; | ||
| @FXML | ||
| private ChatPromptComponent chatPrompt; | ||
| @FXML | ||
| private Label noticeText; | ||
| @FXML | ||
| private Hyperlink exQuestion1; | ||
| @FXML | ||
| private Hyperlink exQuestion2; | ||
| @FXML | ||
| private Hyperlink exQuestion3; | ||
| @FXML | ||
| private HBox exQuestionBox; | ||
| @FXML | ||
| private HBox followUpQuestionsBox; |
There was a problem hiding this comment.
Can you revert this reformatting?
There was a problem hiding this comment.
Thanks, I will fix this shortly.
| this.entryTypesManager = entryTypesManager; | ||
| this.fieldPreferences = fieldPreferences; |
There was a problem hiding this comment.
Please - group arguments semantically together.
- aiPreferences
- (new) fieldPreferences
- (new) entryTypesManager
- dialogService
Reason: we opted for dependency injection via constructor - and Java method parameters are a mess somehow... By grouping together preferences, we try to reduce the mess.
There was a problem hiding this comment.
This was somehow achieved, but not fully.
OK for now.
| Files.writeString(path, content, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); | ||
| dialogService.notify(Localization.lang("Export successful")); | ||
| } catch (IOException e) { | ||
| LOGGER.error(Localization.lang("Problem occurred while writing the export file"), e); |
There was a problem hiding this comment.
No Localization in logging. Maybe, a hint should be added to https://devdocs.jabref.org/code-howtos/logging.html.
| dialogService.notify(Localization.lang("Export successful")); | ||
| } catch (IOException e) { | ||
| LOGGER.error(Localization.lang("Problem occurred while writing the export file"), e); | ||
| dialogService.showErrorDialogAndWait(Localization.lang("Save failed"), e); |
There was a problem hiding this comment.
Why unspecific here? - I think "Problem occurred while writing the export file" is the better text.
However, please check JabRef_en.properties for other error messages. Maybe you find some with %0 included, where ex.getLocaliizedMessage() is passed to.
| LOGGER.error(Localization.lang("Problem occurred while writing the export file"), e); | ||
| dialogService.showErrorDialogAndWait(Localization.lang("Save failed"), e); |
| TaskExecutor taskExecutor | ||
| TaskExecutor taskExecutor, | ||
| BibEntryTypesManager entryTypesManager, | ||
| FieldPreferences fieldPreferences |
| </graphic> | ||
| <items> | ||
| <MenuItem text="%Export in human-readable format (Markdown)" onAction="#exportMarkdown"/> | ||
| <MenuItem text="%Export in machine-readable format (JSON)" onAction="#exportJson"/> |
There was a problem hiding this comment.
| <MenuItem text="%Export in machine-readable format (JSON)" onAction="#exportJson"/> | |
| <MenuItem text="%Export to JSON" onAction="#exportJson"/> |
Reason: JSON is also considered human readable - in comparison to a binary format
| role = "assistant"; | ||
| content = aiMessage.text(); | ||
| } else { | ||
| continue; |
There was a problem hiding this comment.
@InAnYan @ThiloteE We should include ErrorMessage, shouldn't we?
@CXZHANG0508 Please remove "ErrorMessage" - you have it written twice in the comment
| Map<String, String> msgMap = new HashMap<>(); | ||
| msgMap.put("role", role); | ||
| msgMap.put("content", content); |
There was a problem hiding this comment.
You can shorten this with Map.of(...)
Sid track: However, shouldn't this be a record with String role, String content? - But I think, this is OK, because it might be over-engineered that way
| Back=Back | ||
| Forward=Forward | ||
|
|
||
| Export\ successful=Export successful |
There was a problem hiding this comment.
Please re-use
export operation finished successfully.
Maybe, you can remove "operation" in the translation string (and change the other parts of JabRef)
Reason: We want to use the same user-facing strings for the same thing - and not have a huge set of similar strings.
| Export\ chat\ history=Export chat history | ||
| No\ summary\ available\ to\ export=No summary available to export | ||
| No\ chat\ history\ to\ export=No chat history to export | ||
| Problem\ occurred\ while\ writing\ the\ export\ file=Problem occurred while writing the export file |
There was a problem hiding this comment.
Maaybe re-use
Failed to export file.
There was a problem hiding this comment.
Fixed the missing localization key issue.
There was a problem hiding this comment.
What I meant: Please re-use existing translations. But OK for me to keep two strings. "Someone" will unify later.
097ae3f to
26a6d06
Compare
koppor
left a comment
There was a problem hiding this comment.
Checked, works OKish.
Since we run out of capacity for more rounds of this, I merge.
We will create follow-up issues for improvement.
Thank you for the work so for!
| this.entryTypesManager = entryTypesManager; | ||
| this.fieldPreferences = fieldPreferences; |
There was a problem hiding this comment.
This was somehow achieved, but not fully.
OK for now.
| this.entries = entries; | ||
| this.entries = stateManager.getSelectedEntries(); |
There was a problem hiding this comment.
Why this change - need to be fixed in a follow-up.
|
@CXZHANG0508 I think, I now collected all follow-ups. The most buggy behavior is described in #14647. Maybe, you have an idea. Then you can assign yourself and solve the issue. |
…Ref#14486) * Add export buttons for AI summary and chat Why: Quite often, JabRef users would like to share a conversation with AI, or store it in some text file that would be read later without opening JabRef. However, currently we don't have an option of saving the conversation or summary to some external file. How: - Add `MenuButton` with export options to `SummaryShowingComponent.fxml` and `AiChatComponent.fxml`. - Implement `exportMarkdown` method to construct Markdown content including the BibTeX source code. - Implement `exportJson` method using Jackson to generate JSON output that adheres to the OpenAI conversation format. - Add necessary localization keys to `JabRef_en.properties`. - Update `CHANGELOG.md` to reflect the new feature. Tags: ai, export, json, markdown Fixes JabRef#13868 * refactor(ai): move export logic to AiExporter to fix architecture violation * refactor(ai): fix architecture violations and checkstyle issues * feat(ai): Add Markdown and JSON export for AI Summary and AI Chat * style: reformat code to match guidelines * feat(ai): Add Markdown and JSON export for AI Summary and AI Chat * feat(ai): Add Markdown and JSON export for AI Summary and AI Chat * feat(ai): fix some format problem * feat(ai): fix some format problem * feat(ai): fix some format problem * fixed the formatting issue,but not the getStringRepresentation method * feat(ai): fix some format problem * Fix AiExporter to use BibEntry.getStringRepresentation * feat(ai): fix the unit tests problem * I have reordered the constructor parameters in relevant functions to group them semantically as suggested * I have reordered the constructor parameters in relevant functions to group them semantically as suggested * Add comment explaining why specific chat messages are ignored in export * fix some format problem * fix some format problem * Refactor constructor parameter order for semantic grouping * fix some formatting issues * Refactor AiExporter to strictly use StringJoiner, add the errorMessage * fix some formatting issues * rename variable sj to stringJoiner and use explicit empty lines * retry * retry * retry * fix the match issue * support group export and enforce markdown linting * fix the formatting issue * fix the formatting issue * reTry * Retry * Retry * Retry

Closes #13868
Implemented the export functionality for AI Summary and AI Chat as requested. Users can now save the content to Markdown (human-readable) or JSON (machine-readable, OpenAI format). Added an export menu button to the UI and handled the file saving logic.
Steps to test
Open an entry that has a citation key and a linked PDF. I tested the UI logic in a simulated local environment (since I don't have an AI API key)
Go to the AI Summary or AI Chat tab.
Mandatory checks
CHANGELOG.mdin a way that is understandable for the average user (if change is visible to the user)