From 8d46dbab132ccee5324f441bde7216cc23d3a185 Mon Sep 17 00:00:00 2001 From: M66B Date: Sun, 16 Jun 2024 15:49:18 +0200 Subject: [PATCH] AI summarize: select prompt --- app/src/main/java/eu/faircode/email/AI.java | 21 +++- .../eu/faircode/email/AdapterMessage.java | 8 +- .../main/java/eu/faircode/email/DeepL.java | 1 - .../java/eu/faircode/email/EntityRule.java | 2 +- .../email/FragmentDialogSummarize.java | 104 +++++++++++++++++- .../eu/faircode/email/FragmentMessages.java | 4 +- 6 files changed, 124 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/eu/faircode/email/AI.java b/app/src/main/java/eu/faircode/email/AI.java index 63d67b4452..1606edf5dd 100644 --- a/app/src/main/java/eu/faircode/email/AI.java +++ b/app/src/main/java/eu/faircode/email/AI.java @@ -172,7 +172,7 @@ public class AI { return context.getString(R.string.title_summarize); } - static Spanned getSummaryText(Context context, EntityMessage message) throws JSONException, IOException { + static Spanned getSummaryText(Context context, EntityMessage message, long template) throws JSONException, IOException { File file = message.getFile(context); if (!file.exists()) return null; @@ -190,16 +190,27 @@ public class AI { HtmlHelper.truncate(d, MAX_SUMMARIZE_TEXT_SIZE); + String templatePrompt = null; + if (template > 0L) { + DB db = DB.getInstance(context); + EntityAnswer t = db.answer().getAnswer(template); + if (t != null) { + String html = t.getData(context, null).getHtml(); + templatePrompt = JsoupEx.parse(html).body().text(); + } + } + StringBuilder sb = new StringBuilder(); if (OpenAI.isAvailable(context)) { String model = prefs.getString("openai_model", OpenAI.DEFAULT_MODEL); float temperature = prefs.getFloat("openai_temperature", OpenAI.DEFAULT_TEMPERATURE); - String prompt = prefs.getString("openai_summarize", OpenAI.DEFAULT_SUMMARY_PROMPT); + String defaultPrompt = prefs.getString("openai_summarize", OpenAI.DEFAULT_SUMMARY_PROMPT); boolean multimodal = prefs.getBoolean("openai_multimodal", false); List input = new ArrayList<>(); input.add(new OpenAI.Message(OpenAI.USER, - new OpenAI.Content[]{new OpenAI.Content(OpenAI.CONTENT_TEXT, prompt)})); + new OpenAI.Content[]{new OpenAI.Content(OpenAI.CONTENT_TEXT, + templatePrompt == null ? defaultPrompt : templatePrompt)})); if (!TextUtils.isEmpty(message.subject)) input.add(new OpenAI.Message(OpenAI.USER, @@ -226,12 +237,12 @@ public class AI { } else if (Gemini.isAvailable(context)) { String model = prefs.getString("gemini_model", Gemini.DEFAULT_MODEL); float temperature = prefs.getFloat("gemini_temperature", Gemini.DEFAULT_TEMPERATURE); - String prompt = prefs.getString("gemini_summarize", Gemini.DEFAULT_SUMMARY_PROMPT); + String defaultPrompt = prefs.getString("gemini_summarize", Gemini.DEFAULT_SUMMARY_PROMPT); String body = d.text(); List texts = new ArrayList<>(); - texts.add(prompt); + texts.add(templatePrompt == null ? defaultPrompt : templatePrompt); if (!TextUtils.isEmpty(message.subject)) texts.add(message.subject); if (!TextUtils.isEmpty(body)) diff --git a/app/src/main/java/eu/faircode/email/AdapterMessage.java b/app/src/main/java/eu/faircode/email/AdapterMessage.java index b85ca4b9d4..4b8bc5e5fd 100644 --- a/app/src/main/java/eu/faircode/email/AdapterMessage.java +++ b/app/src/main/java/eu/faircode/email/AdapterMessage.java @@ -4596,7 +4596,7 @@ public class AdapterMessage extends RecyclerView.Adapter() { + @Override + protected String onExecute(Context context, Bundle args) throws Throwable { + long template = args.getLong("template"); + + DB db = DB.getInstance(context); + EntityAnswer prompt = db.answer().getAnswer(template); + if (prompt == null) + return null; + + Document doc = JsoupEx.parse(prompt.getData(context, null).getHtml()); + Spanned spanned = HtmlHelper.fromDocument(context, doc, null, null); + return spanned.toString(); + } + + @Override + protected void onExecuted(Bundle args, String prompt) { + tvCaption.setText(prompt); + } + + @Override + protected void onException(Bundle args, Throwable ex) { + Log.unexpectedError(getParentFragmentManager(), ex); + } + }.execute(this, args, "ai:prompt"); + } - tvCaption.setText(AI.getSummarizePrompt(context)); tvFrom.setText(args.getString("from")); tvSubject.setText(args.getString("subject")); @@ -106,6 +146,7 @@ public class FragmentDialogSummarize extends FragmentDialogBase { @Override protected Spanned onExecute(Context context, Bundle args) throws Throwable { long id = args.getLong("id"); + long template = args.getLong("template"); DB db = DB.getInstance(context); EntityMessage message = db.message().getMessage(id); @@ -113,7 +154,7 @@ public class FragmentDialogSummarize extends FragmentDialogBase { return null; long start = new Date().getTime(); - Spanned summary = AI.getSummaryText(context, message); + Spanned summary = AI.getSummaryText(context, message, template); args.putLong("elapsed", new Date().getTime() - start); return summary; @@ -142,11 +183,68 @@ public class FragmentDialogSummarize extends FragmentDialogBase { return builder.create(); } - public static void summarize(EntityMessage message, FragmentManager fm) { + public static void summarize(EntityMessage message, FragmentManager fm, View anchor, LifecycleOwner owner) { + if (anchor == null) { + summarize(message, fm, null); + return; + } + + final Context context = anchor.getContext(); + + new SimpleTask>() { + @Override + protected List onExecute(Context context, Bundle args) throws Throwable { + DB db = DB.getInstance(context); + return db.answer().getAiPrompts(); + } + + @Override + protected void onExecuted(Bundle args, List prompts) { + if (prompts == null || prompts.isEmpty()) + summarize(message, fm, null); + else { + PopupMenuLifecycle popupMenu = new PopupMenuLifecycle(context, owner, anchor); + + String title = context.getString(R.string.title_advanced_default_prompt); + SpannableStringBuilder ssb = new SpannableStringBuilderEx(title); + ssb.setSpan(new RelativeSizeSpan(HtmlHelper.FONT_SMALL), 0, ssb.length(), 0); + popupMenu.getMenu() + .add(Menu.NONE, 1, 1, ssb) + .setIntent(new Intent().putExtra("id", -1L)); + + for (int i = 0; i < prompts.size(); i++) { + EntityAnswer prompt = prompts.get(i); + popupMenu.getMenu() + .add(Menu.NONE, i + 2, i + 2, prompt.name) + .setIntent(new Intent().putExtra("id", prompt.id)); + } + + popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + long id = item.getIntent().getLongExtra("id", -1L); + summarize(message, fm, id); + return true; + } + }); + + popupMenu.show(); + } + } + + @Override + protected void onException(Bundle args, Throwable ex) { + Log.unexpectedError(fm, ex); + } + }.execute(context, owner, new Bundle(), "AI:select"); + } + + private static void summarize(EntityMessage message, FragmentManager fm, Long template) { Bundle args = new Bundle(); args.putLong("id", message.id); args.putString("from", MessageHelper.formatAddresses(message.from)); args.putString("subject", message.subject); + args.putLong("template", template == null ? -1L : template); FragmentDialogSummarize fragment = new FragmentDialogSummarize(); fragment.setArguments(args); diff --git a/app/src/main/java/eu/faircode/email/FragmentMessages.java b/app/src/main/java/eu/faircode/email/FragmentMessages.java index 3e77c27989..800c71851e 100644 --- a/app/src/main/java/eu/faircode/email/FragmentMessages.java +++ b/app/src/main/java/eu/faircode/email/FragmentMessages.java @@ -1628,7 +1628,7 @@ public class FragmentMessages extends FragmentBase if (result == null || result.single == null || !result.single.content) return; - FragmentDialogSummarize.summarize(result.single, getParentFragmentManager()); + FragmentDialogSummarize.summarize(result.single, getParentFragmentManager(), ibSummarize, getViewLifecycleOwner()); } }); @@ -3575,7 +3575,7 @@ public class FragmentMessages extends FragmentBase private void onSwipeSummarize(final @NonNull TupleMessageEx message) { final Context context = getContext(); if (AI.isAvailable(context)) - FragmentDialogSummarize.summarize(message, getParentFragmentManager()); + FragmentDialogSummarize.summarize(message, getParentFragmentManager(), null, getViewLifecycleOwner()); else context.startActivity(new Intent(context, ActivitySetup.class) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK)