Condividi le tue conversazioni con l'AI Bot!

Siamo entusiasti di annunciare una nuova funzionalità che ti consente di condividere facilmente le tue conversazioni con i bot AI sul nostro forum. Sia che tu abbia avuto uno scambio illuminante, divertente o utile con uno dei nostri bot AI, ora puoi condividere quell’interazione con altri in pochi clic. Ecco cosa devi sapere:

Come condivido una conversazione con un bot AI?

Quando visualizzi una conversazione con un bot AI, vedrai una nuova icona “Condividi” nel menu del post.

Il clic funziona come segue:

  1. Per conversazioni brevi: Se la conversazione è entro una certa lunghezza (fino a 2 interazioni), facendo clic sull’icona “Condividi” verrà copiata automaticamente l’intera conversazione negli appunti. Puoi quindi incollarla dove preferisci, che sia in un nuovo post, in un messaggio privato o al di fuori del forum.

  2. Per conversazioni più lunghe: Per interazioni più estese, facendo clic sull’icona “Condividi” si aprirà una finestra modale con un’anteprima della tua conversazione. Puoi utilizzare un cursore per selezionare quanta parte della conversazione desideri condividere. Una volta scelta l’intervallo desiderato, fai clic sul pulsante “Copia” per copiare la conversazione selezionata negli appunti.

Come apparirà la conversazione condivisa?

La conversazione condivisa sarà formattata in un blocco “dettagli” comprimibile, rendendola facile da leggere e gestire nei post. Includerà i nomi utente e il contenuto dei messaggi nell’intervallo che hai selezionato.

Esempio:

<details class='ai-quote'>
<summary>
<span>Greetings</span>
<span title='Conversation with AI'>AI</span>
</summary>

**sam:**

say hello

**gpt4_bot:**

Hello! How can I assist you today?
</details>

Verrà visualizzato come:

Greetings AI

sam:

say hello

gpt4_bot:

Hello! How can I assist you today?

Posso condividere conversazioni con qualsiasi bot AI?

Sì, puoi condividere conversazioni con qualsiasi bot AI abilitato sul nostro forum. Cerca semplicemente l’icona “Condividi” nel menu del post quando visualizzi la conversazione.

C’è un limite a quanto posso condividere della conversazione?

Non c’è un limite rigido, ma ti incoraggiamo a condividere solo le parti pertinenti della conversazione per mantenerla concisa e focalizzata per gli altri lettori.

Dove posso condividere queste conversazioni?

Puoi condividerle in un nuovo post del forum, in un messaggio privato ad altri utenti o anche al di fuori del forum, ovunque tu possa incollare testo dagli appunti.

La conversazione condivisa includerà informazioni personali?

Nessuna informazione personale sarà inclusa nel testo condiviso. Verranno copiati solo i nomi utente e il contenuto dei messaggi.

14 Mi Piace

FWIW, ho lavorato con GPT-4 per creare questo post (con alcune modifiche minori)

Discourse AI Bot: Nuova funzione di condivisione AI

sam:

Dato:

commit 933784a873c781816857699d6d9138baf9f5a7de
Author: Sam <sam.saffron@gmail.com>
Date:   Fri Dec 29 19:47:47 2023 +1100

    FUNZIONE: consentire la facile condivisione delle conversazioni con il bot (#385)
    
    * FUNZIONE: consentire la facile condivisione delle conversazioni con il bot
    
    * Sfruttare la nuova API core i
    
    * Aggiunto test di sistema per la funzionalità di copia
    
    
    * Aggiornamento assets/javascripts/initializers/ai-bot-replies.js
    
    Co-authored-by: Alan Guo Xiang Tan <gxtan1990@gmail.com>
    
    * discourse in seguito invece di setTimeout
    
    * Aggiornamento spec/system/ai_bot/share_spec.rb
    
    Co-authored-by: Alan Guo Xiang Tan <gxtan1990@gmail.com>
    
    * feedback dalla revisione
    
    controllare solo l'intero payload
    
    * rimuovere codice non necessario
    
    * correggere il test
    
    ---------
    
    Co-authored-by: Alan Guo Xiang Tan <gxtan1990@gmail.com>

diff --git a/assets/javascripts/discourse/components/modal/share-modal.gjs b/assets/javascripts/discourse/components/modal/share-modal.gjs
new file mode 100644
index 0000000..b122f62
--- /dev/null
+++ b/assets/javascripts/discourse/components/modal/share-modal.gjs
@@ -0,0 +1,109 @@
+import Component from "@glimmer/component";
+import { tracked } from "@glimmer/tracking";
+import { Input } from "@ember/component";
+import { on } from "@ember/modifier";
+import { action } from "@ember/object";
+import { htmlSafe } from "@ember/template";
+import DButton from "discourse/components/d-button";
+import DModal from "discourse/components/d-modal";
+import discourseLater from "discourse-common/lib/later";
+import I18n from "I18n";
+import copyConversation from "../../lib/copy-conversation";
+
+const t = I18n.t.bind(I18n);
+
+export default class ShareModal extends Component {
+  @tracked contextValue = 1;
+  @tracked htmlContext = "";
+  @tracked maxContext = 0;
+  @tracked allPosts = [];
+  @tracked justCopiedText = "";
+
+  constructor() {
+    super(...arguments);
+
+    const postStream = this.args.model.topic.get("postStream");
+
+    let postNumbers = [];
+    // più semplice da capire rispetto ad Array.from
+    for (let i = 1; i <= this.args.model.post_number; i++) {
+      postNumbers.push(i);
+    }
+
+    this.allPosts = postNumbers
+      .map((postNumber) => {
+        let postId = postStream.findPostIdForPostNumber(postNumber);
+        if (postId) {
+          return postStream.findLoadedPost(postId);
+        }
+      })
+      .filter((post) => post);
+
+    this.maxContext = this.allPosts.length / 2;
+    this.contextValue = 1;
+
+    this.updateHtmlContext();
+  }
+
+  @action
+  updateHtmlContext() {
+    let context = [];
+
+    const start = this.allPosts.length - this.contextValue * 2;
+    for (let i = start; i < this.allPosts.length; i++) {
+      const post = this.allPosts[i];
+      context.push(`<p><b>${post.username}:</b></p>`);
+      context.push(post.cooked);
+    }
+    this.htmlContext = htmlSafe(context.join("\n"));
+  }
+
+  @action
+  async copyContext() {
+    const from =
+      this.allPosts[this.allPosts.length - this.contextValue * 2].post_number;
+    const to = this.args.model.post_number;
+    await copyConversation(this.args.model.topic, from, to);
+    this.justCopiedText = t("discourse_ai.ai_bot.conversation_shared");
+    discourseLater(() => {
+      this.justCopiedText = "";
+    }, 2000);
+  }
+
+  <template>
+    <DModal
+      class="ai-share-modal"
+      @title={{t "discourse_ai.ai_bot.share_modal.title"}}
+      @closeModal={{@closeModal}}
+    >
+      <:body>
+        <div class="ai-share-modal__preview">
+          {{this.htmlContext}}
+        </div>
+      </:body>
+
+      <:footer>
+        <div class="ai-share-modal__slider">
+          <Input
+            @type="range"
+            min="1"
+            max={{this.maxContext}}
+            @value={{this.contextValue}}
+            {{on "change" this.updateHtmlContext}}
+          />
+          <div class="ai-share-modal__context">
+            {{t "discourse_ai.ai_bot.share_modal.context"}}
+            {{this.contextValue}}
+          </div>
+        </div>
+        <DButton
+          class="btn-primary confirm"
+          @icon="copy"
+          @action={{this.copyContext}}
+          @label="discourse_ai.ai_bot.share_modal.copy"
+        />
+        <span class="ai-share-modal__just-copied">{{this.justCopiedText}}</span>
+      </:footer>
+    </DModal>
+  </template>
+}
diff --git a/assets/javascripts/discourse/lib/copy-conversation.js b/assets/javascripts/discourse/lib/copy-conversation.js
new file mode 100644
index 0000000..bcf3188
--- /dev/null
+++ b/assets/javascripts/discourse/lib/copy-conversation.js
@@ -0,0 +1,55 @@
+import { ajax } from "discourse/lib/ajax";
+import { clipboardCopy } from "discourse/lib/utilities";
+import I18n from "discourse-i18n";
+
+export default async function (topic, fromPostNumber, toPostNumber) {
+  const stream = topic.get("postStream");
+
+  let postNumbers = [];
+  // più semplice da capire rispetto ad Array.from
+  for (let i = fromPostNumber; i <= toPostNumber; i++) {
+    postNumbers.push(i);
+  }
+
+  const postIds = postNumbers.map((postNumber) => {
+    return stream.findPostIdForPostNumber(postNumber);
+  });
+
+  // abbiamo bisogno del raw per costruire, quindi il post stream non aiuterà
+
+  const url = `/t/${topic.id}/posts.json`;
+  const data = {
+    post_ids: postIds,
+    include_raw: true,
+  };
+
+  const response = await ajax(url, { data });
+
+  let buffer = [];
+  buffer.push("<details class='ai-quote'>");
+  buffer.push("<summary>");
+  buffer.push(`<span>${topic.title}</span>`);
+  buffer.push(
+    `<span title='${I18n.t("discourse_ai.ai_bot.ai_title")}'>${I18n.t(
+      "discourse_ai.ai_bot.ai_label"
+    )}</span>`
+  );
+  buffer.push("</summary>");
+
+  response.post_stream.posts.forEach((post) => {
+    buffer.push("");
+    buffer.push(`**${post.username}:**`);
+    buffer.push("");
+    buffer.push(post.raw);
+  });
+
+  buffer.push("</details>");
+
+  const text = buffer.join("\n");
+
+  if (window.discourseAiTestClipboard) {
+    window.discourseAiClipboard = text;
+  }
+
+  await clipboardCopy(text);
+}
diff --git a/assets/javascripts/initializers/ai-bot-replies.js b/assets/javascripts/initializers/ai-bot-replies.js
index e136963..050e1ed 100644
--- a/assets/javascripts/initializers/ai-bot-replies.js
+++ b/assets/javascripts/initializers/ai-bot-replies.js
@@ -6,6 +6,10 @@ import { withPluginApi } from "discourse/lib/plugin-api";
 import { cook } from "discourse/lib/text";
 import { registerWidgetShim } from "discourse/widgets/render-glimmer";
 import { composeAiBotMessage } from "discourse/plugins/discourse-ai/discourse/lib/ai-bot-helper";
+import ShareModal from "../discourse/components/modal/share-modal";
+import copyConversation from "../discourse/lib/copy-conversation";
+
+const AUTO_COPY_THRESHOLD = 4;
 
 function isGPTBot(user) {
   return user && [-110, -111, -112, -113].includes(user.id);
@@ -166,6 +170,43 @@ function initializePersonaDecorator(api) {
   );
 }
 
+function initializeShareButton(api) {
+  const currentUser = api.getCurrentUser();
+  if (!currentUser || !currentUser.ai_enabled_chat_bots) {
+    return;
+  }
+
+  let shareAiResponse = async function ({ post, showFeedback }) {
+    if (post.post_number <= AUTO_COPY_THRESHOLD) {
+      await copyConversation(post.topic, 1, post.post_number);
+      showFeedback("discourse_ai.ai_bot.conversation_shared");
+    } else {
+      modal.show(ShareModal, { model: post });
+    }
+  };
+
+  api.addPostMenuButton("share", (post) => {
+    // molto hacky e brutto, ma non c'è `.topic` in attrs
+    if (
+      !currentUser.ai_enabled_chat_bots.any(
+        (bot) => post.username === bot.username
+      )
+    ) {
+      return;
+    }
+
+    return {
+      action: shareAiResponse,
+      icon: "share",
+      className: "post-action-menu__share",
+      title: "discourse_ai.ai_bot.share",
+      position: "first",
+    };
+  });
+
+  const modal = api.container.lookup("service:modal");
+}
+
 export default {
   name: "discourse-ai-bot-replies",
 
@@ -179,6 +220,7 @@ export default {
       }
       withPluginApi("1.6.0", initializeAIBotReplies);
       withPluginApi("1.6.0", initializePersonaDecorator);
+      withPluginApi("1.22.0", (api) => initializeShareButton(api, container));
     }
   },
 };
diff --git a/assets/javascripts/lib/discourse-markdown/ai-tags.js b/assets/javascripts/lib/discourse-markdown/ai-tags.js
new file mode 100644
index 0000000..c2d9b67
--- /dev/null
+++ b/assets/javascripts/lib/discourse-markdown/ai-tags.js
@@ -0,0 +1,3 @@
+export function setup(helper) {
+  helper.allowList(["details[class=ai-quote]"]);
+}
diff --git a/assets/stylesheets/modules/ai-bot/common/bot-replies.scss b/assets/stylesheets/modules/ai-bot/common/bot-replies.scss
index 4943b9b..2a8ba80 100644
--- a/assets/stylesheets/modules/ai-bot/common/bot-replies.scss
+++ b/assets/stylesheets/modules/ai-bot/common/bot-replies.scss
@@ -67,3 +67,37 @@ article.streaming nav.post-controls .actions button.cancel-streaming {
   font-size: var(--font-down-1);
   padding-top: 3px;
 }
+
+details.ai-quote {
+  > summary {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    span:first-child {
+      margin-right: auto;
+    }
+    span:nth-child(2) {
+      font-size: var(--font-down-2);
+      background: var(--primary-medium);
+      padding: 2px 6px 0;
+      color: var(--secondary);
+    }
+  }
+}
+
+.ai-share-modal {
+  .d-modal__footer {
+    position: relative;
+    padding: 10px 20px 25px;
+    .btn-primary {
+      margin-left: auto;
+    }
+  }
+  &__just-copied {
+    position: absolute;
+    font-size: var(--font-down-1);
+    right: 20px;
+    bottom: 5px;
+    color: var(--success);
+  }
+}
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index e13f953..e2f2e7d 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -180,6 +180,16 @@ en:
         cancel_streaming: "Interrompi risposta"
         default_pm_prefix: "[PM AI bot senza titolo]"
         shortcut_title: "Inizia un PM con un bot AI"
+        share: "Condividi conversazione AI"
+        conversation_shared: "Conversazione copiata negli appunti"
+
+        ai_label: "AI"
+        ai_title: "Conversazione con AI"
+
+        share_modal:
+          title: "Condividi conversazione AI"
+          copy: "Copia"
+          context: "Interazioni da condividere:"
 
         bot_names:
           gpt-4: "GPT-4"
diff --git a/spec/system/ai_bot/share_spec.rb b/spec/system/ai_bot/share_spec.rb
new file mode 100644
index 0000000..708e96b
--- /dev/null
+++ b/spec/system/ai_bot/share_spec.rb
@@ -0,0 +1,118 @@
+# frozen_string_literal: true
+RSpec.describe "Condividi conversazione", type: :system do
+  fab!(:admin) { Fabricate(:admin, username: "ai_sharer") }
+  let(:bot_user) { User.find(DiscourseAi::AiBot::EntryPoint::GPT4_ID) }
+
+  let(:pm) do
+    Fabricate(
+      :private_message_topic,
+      title: "Questo è il mio PM speciale",
+      user: admin,
+      topic_allowed_users: [
+        Fabricate.build(:topic_allowed_user, user: admin),
+        Fabricate.build(:topic_allowed_user, user: bot_user),
+      ],
+    )
+  end
+
+  let(:pm_posts) do
+    posts = []
+    i = 1
+    3.times do
+      posts << Fabricate(:post, topic: pm, user: admin, raw: "test test test risposta utente #{i}")
+      posts << Fabricate(:post, topic: pm, user: bot_user, raw: "test test test risposta bot #{i}")
+      i += 1
+    end
+
+    posts
+  end
+
+  before do
+    SiteSetting.ai_bot_enabled = true
+    SiteSetting.ai_bot_enabled_chat_bots = "gpt-4"
+    sign_in(admin)
+
+    bot_user.update!(username: "gpt-4")
+
+    Group.refresh_automatic_groups!
+    pm
+    pm_posts
+  end
+
+  it "può condividere una conversazione" do
+    clip_text = nil
+
+    visit(pm.url)
+
+    # la funzionalità degli appunti è estremamente difficile da testare
+    # avremmo bisogno di permessi speciali in chrome driver per abilitare l'accesso completo
+    # invece usiamo una variabile segreta per segnalare che vogliamo memorizzare gli appunti
+    # dati in window.discourseAiClipboard
+    page.execute_script("window.discourseAiTestClipboard = true")
+
+    find("#post_2 .post-action-menu__share").click
+
+    try_until_success do
+      clip_text = page.evaluate_script("window.discourseAiClipboard")
+      expect(clip_text).to be_present
+    end
+
+    conversation = (<<~TEXT).strip
+      <details class='ai-quote'>
+      <summary>
+      <span>Questo è il mio PM speciale</span>
+      <span title='Conversazione con AI'>AI</span>
+      </summary>
+
+      **ai_sharer:**
+
+      test test test risposta utente 1
+
+      **gpt-4:**
+
+      test test test risposta bot 1
+      </details>
+    TEXT
+
+    expect(conversation).to eq(clip_text)
+
+    # Testare anche la funzionalità della modale
+    page.evaluate_script("window.discourseAiClipboard = null")
+
+    find("#post_6 .post-action-menu__share").click
+    find(".ai-share-modal__slider input").set("2")
+    find(".ai-share-modal button.btn-primary").click
+
+    try_until_success do
+      clip_text = page.evaluate_script("window.discourseAiClipboard")
+      expect(clip_text).to be_present
+    end
+
+    conversation = (<<~TEXT).strip
+      <details class='ai-quote'>
+      <summary>
+      <span>Questo è il mio PM speciale</span>
+      <span title='Conversazione con AI'>AI</span>
+      </summary>
+
+      **ai_sharer:**
+
+      test test test risposta utente 2
+
+      **gpt-4:**
+
+      test test test risposta bot 2
+
+      **ai_sharer:**
+
+      test test test risposta utente 3
+
+      **gpt-4:**
+
+      test test test risposta bot 3
+      </details>
+    TEXT
+
+    expect(conversation).to eq(clip_text)
+  end
+end

Spiega la nuova funzionalità in Discourse AI

gpt4_bot:

La nuova funzionalità introdotta in Discourse AI consente agli utenti di condividere facilmente le conversazioni che hanno avuto con i bot AI all’interno della piattaforma Discourse. Questa funzionalità è particolarmente utile per gli utenti che desiderano condividere le intuizioni o le informazioni ricevute dall’interazione con i bot AI con altri utenti o per il proprio riferimento.

Ecco una panoramica di come funziona la funzionalità e quali modifiche sono state apportate:

  1. Componente Modale di Condivisione: È stato aggiunto un nuovo componente Glimmer (ShareModal), che fornisce un’interfaccia utente per selezionare l’intervallo della conversazione da condividere e copiarlo negli appunti. Questa modale include un cursore per scegliere quanto contesto della conversazione includere durante la condivisione.

  2. Funzionalità di Copia Conversazione: È stata implementata una nuova funzione JavaScript (copyConversation) per gestire la copia effettiva del testo della conversazione. Costruisce la conversazione recuperando i post grezzi tra i numeri di post specificati e la formatta con Markdown, inclusa un tag details (<details class='ai-quote'>) per rendere il contenuto condiviso collassabile.

  3. Pulsante Menu Post: È stato aggiunto un nuovo pulsante nel menu dei post (share) che attiva la funzionalità di condivisione. Se la conversazione è breve (fino a una certa soglia), verrà automaticamente copiata negli appunti. Per conversazioni più lunghe, verrà visualizzata la modale di condivisione, consentendo all’utente di selezionare la parte della conversazione da condividere.

  4. Stile: Sono stati aggiunti nuovi stili SCSS (bot-replies.scss) per stilare la modale di condivisione e il tag details della conversazione condivisa.

  5. Modifiche alle Localizzazioni: Il file client.en.yml è stato aggiornato per includere nuove stringhe di localizzazione per la funzionalità di condivisione, come etichette dei pulsanti e suggerimenti.

  6. Test di Sistema: È stato aggiunto un nuovo test di sistema (share_spec.rb) per garantire che la funzionalità di condivisione funzioni come previsto.

  7. Whitelisting Markdown: È stato creato il file ai-tags.js per whitelistare il tag personalizzato details[class=ai-quote] utilizzato per la conversazione condivisa, in modo che sia consentito nel renderer Markdown di Discourse.

  8. Co-autoria: Il messaggio del commit indica che Alan Guo Xiang Tan ha co-autorizzato alcune delle modifiche, suggerendo uno sforzo collaborativo nello sviluppo di questa funzionalità.

Nel complesso, questa funzionalità migliora l’esperienza dell’utente rendendo più facile condividere interazioni preziose con i bot AI, promuovendo la collaborazione e la condivisione delle conoscenze all’interno della comunità Discourse.

sam:

Prova a spiegare la funzionalità agli utenti del forum rispondendo alle domande comuni che avranno al riguardo.

gpt4_bot:

Annuncio della nuova funzionalità: Condividi le tue conversazioni con i bot AI!

Siamo entusiasti di annunciare una nuova funzionalità che ti permette di condividere facilmente le tue conversazioni con i bot AI sul nostro forum. Che tu abbia avuto uno scambio illuminante, divertente o utile con uno dei nostri bot AI, ora puoi condividere quell’interazione con altri in pochi clic. Ecco cosa devi sapere:

Come condivido una conversazione con un bot AI?

Quando visualizzi una conversazione con un bot AI, vedrai una nuova icona “Condividi” nel menu del post. Cliccando su questa icona avrai due opzioni:

  1. Per conversazioni brevi: Se la conversazione rientra in una certa lunghezza (fino a quattro interazioni), cliccando sull’icona “Condividi” verrà automaticamente copiata l’intera conversazione negli appunti. Potrai quindi incollarla dove vuoi, sia in un nuovo post, in un messaggio privato o fuori dal forum.

  2. Per conversazioni lunghe: Per interazioni più estese, cliccando sull’icona “Condividi” si aprirà una modale con un’anteprima della tua conversazione. Puoi usare un cursore per selezionare quanto della conversazione vuoi condividere. Una volta scelto l’intervallo desiderato, clicca sul pulsante “Copia” per copiare la conversazione selezionata negli appunti.

Come apparirà la conversazione condivisa?

La conversazione condivisa sarà formattata in un blocco “details” collassabile, rendendola facile da leggere e gestire nei post. Includerà i nomi utente e il contenuto dei messaggi nell’intervallo selezionato.

Posso condividere conversazioni con qualsiasi bot AI?

Sì, puoi condividere conversazioni con qualsiasi bot AI abilitato sul nostro forum. Cerca semplicemente l’icona “Condividi” nel menu del post quando visualizzi la conversazione.

C’è un limite a quanto della conversazione posso condividere?

Non c’è un limite rigido, ma ti invitiamo a condividere solo le parti rilevanti della conversazione per mantenere le cose concise e focalizzate per gli altri lettori.

Dove posso condividere queste conversazioni?

Puoi condividerle in un nuovo post del forum, in un messaggio privato ad altri utenti o anche fuori dal forum, ovunque puoi incollare testo dagli appunti.

La conversazione condivisa includerà informazioni personali?

Nessuna informazione personale sarà inclusa nel testo condiviso. Verranno copiati solo i nomi utente e il contenuto dei messaggi.

Cosa succede se incontro problemi con la funzionalità di condivisione?

Se incontri problemi o hai feedback sulla funzionalità di condivisione, non esitare a contattare il nostro team di supporto o pubblicare nella sezione feedback del nostro forum. Siamo qui per aiutarti e siamo sempre alla ricerca di modi per migliorare la tua esperienza.

Speriamo che tu apprezzi questa nuova funzionalità e non vediamo l’ora di vedere le interessanti conversazioni che condividerai!

12 Mi Piace