FEATURE: more elegant progress (#409)
Previous to this change it was very hard to tell if completion was stuck or not. This introduces a "dot" that follows the completion and starts flashing after 5 seconds.
This commit is contained in:
parent
b0a0cbe3ca
commit
05f7808057
|
@ -1,3 +1,4 @@
|
|||
import { later } from "@ember/runloop";
|
||||
import { hbs } from "ember-cli-htmlbars";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
|
@ -94,15 +95,41 @@ function initializeAIBotReplies(api) {
|
|||
onAIBotStreamedReply: function (data) {
|
||||
const post = this.model.postStream.findLoadedPost(data.post_id);
|
||||
|
||||
// it may take us a few seconds to load the post
|
||||
// we need to requeue the event
|
||||
if (!post && !data.done) {
|
||||
const refresh = this.onAIBotStreamedReply.bind(this);
|
||||
data.retries = data.retries || 5;
|
||||
data.retries -= 1;
|
||||
data.skipIfStreaming = true;
|
||||
if (data.retries > 0) {
|
||||
later(() => {
|
||||
refresh(data);
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
if (post) {
|
||||
if (data.raw) {
|
||||
const postElement = document.querySelector(
|
||||
`#post_${data.post_number}`
|
||||
);
|
||||
|
||||
if (
|
||||
data.skipIfStreaming &&
|
||||
postElement.classList.contains("streaming")
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
cook(data.raw).then((cooked) => {
|
||||
post.set("raw", data.raw);
|
||||
post.set("cooked", cooked);
|
||||
|
||||
document
|
||||
.querySelector(`#post_${data.post_number}`)
|
||||
.classList.add("streaming");
|
||||
// resets animation
|
||||
postElement.classList.remove("streaming");
|
||||
void postElement.offsetWidth;
|
||||
postElement.classList.add("streaming");
|
||||
|
||||
const cookedElement = document.createElement("div");
|
||||
cookedElement.innerHTML = cooked;
|
||||
|
@ -131,9 +158,12 @@ function initializeAIBotReplies(api) {
|
|||
this.model.details.allowed_users &&
|
||||
this.model.details.allowed_users.filter(isGPTBot).length >= 1
|
||||
) {
|
||||
// we attempt to recover the last message in the bus
|
||||
// so we subscribe at -2
|
||||
this.messageBus.subscribe(
|
||||
`discourse-ai/ai-bot/topic/${this.model.id}`,
|
||||
this.onAIBotStreamedReply.bind(this)
|
||||
this.onAIBotStreamedReply.bind(this),
|
||||
-2
|
||||
);
|
||||
}
|
||||
},
|
||||
|
|
|
@ -47,6 +47,35 @@ article.streaming nav.post-controls .actions button.cancel-streaming {
|
|||
display: inline-block;
|
||||
}
|
||||
|
||||
@keyframes flashing {
|
||||
0%,
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
article.streaming .cooked > {
|
||||
:not(ol):not(ul):not(pre):last-child::after,
|
||||
ol:last-child li:last-child p:last-child::after,
|
||||
ol:last-child li:last-child:not(:has(p))::after,
|
||||
ul:last-child li:last-child p:last-child::after,
|
||||
ul:last-child li:last-child:not(:has(p))::after,
|
||||
pre:last-child code::after {
|
||||
content: "\25CF";
|
||||
font-family: Söhne Circle, system-ui, -apple-system, Segoe UI, Roboto,
|
||||
Ubuntu, Cantarell, Noto Sans, sans-serif;
|
||||
line-height: normal;
|
||||
margin-left: 0.25rem;
|
||||
vertical-align: baseline;
|
||||
|
||||
animation: flashing 1.5s 3s infinite;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.ai-bot-available-bot-options {
|
||||
.ai-bot-available-bot-content {
|
||||
color: var(--primary-high);
|
||||
|
|
|
@ -130,13 +130,11 @@ en:
|
|||
explain: "Explain"
|
||||
illustrate_post: "Illustrate Post"
|
||||
painter:
|
||||
attribution:
|
||||
attribution:
|
||||
stable_diffusion_xl: "Image by Stable Diffusion XL"
|
||||
dall_e_3: "Image by DALL-E 3"
|
||||
|
||||
ai_bot:
|
||||
placeholder_reply: "I will reply shortly..."
|
||||
|
||||
personas:
|
||||
cannot_delete_system_persona: "System personas cannot be deleted, please disable it instead"
|
||||
cannot_edit_system_persona: "System personas can only be renamed, you may not edit commands or system prompt, instead disable and make a copy"
|
||||
|
|
|
@ -152,10 +152,17 @@ module DiscourseAi
|
|||
<summary>#{summary}</summary>
|
||||
<p>#{details}</p>
|
||||
</details>
|
||||
|
||||
HTML
|
||||
|
||||
placeholder << custom_raw if custom_raw
|
||||
if custom_raw
|
||||
placeholder << "\n"
|
||||
placeholder << custom_raw
|
||||
else
|
||||
# we need this for cursor placeholder to work
|
||||
# doing this in CSS is very hard
|
||||
# if changing test with a custom tool such as search
|
||||
placeholder << "<span></span>\n\n"
|
||||
end
|
||||
|
||||
placeholder
|
||||
end
|
||||
|
|
|
@ -107,10 +107,12 @@ module DiscourseAi
|
|||
PostCreator.create!(
|
||||
bot.bot_user,
|
||||
topic_id: post.topic_id,
|
||||
raw: I18n.t("discourse_ai.ai_bot.placeholder_reply"),
|
||||
raw: "",
|
||||
skip_validations: true,
|
||||
)
|
||||
|
||||
publish_update(reply_post, raw: "<p></p>")
|
||||
|
||||
redis_stream_key = "gpt_cancel:#{reply_post.id}"
|
||||
Discourse.redis.setex(redis_stream_key, 60, 1)
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ module DiscourseAi
|
|||
def invoke(bot_user, _llm)
|
||||
# max 4 prompts
|
||||
max_prompts = prompts.take(4)
|
||||
progress = +""
|
||||
progress = prompts.first
|
||||
|
||||
yield(progress)
|
||||
|
||||
|
@ -70,11 +70,7 @@ module DiscourseAi
|
|||
end
|
||||
end
|
||||
|
||||
while true
|
||||
progress << "."
|
||||
yield(progress)
|
||||
break if threads.all? { |t| t.join(2) }
|
||||
end
|
||||
break if threads.all? { |t| t.join(2) } while true
|
||||
|
||||
results = threads.filter_map(&:value)
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ module DiscourseAi
|
|||
selected_prompts = prompts.take(4)
|
||||
seeds = seeds.take(4) if seeds
|
||||
|
||||
progress = +""
|
||||
progress = prompts.first
|
||||
yield(progress)
|
||||
|
||||
results = nil
|
||||
|
@ -85,11 +85,7 @@ module DiscourseAi
|
|||
end
|
||||
end
|
||||
|
||||
while true
|
||||
progress << "."
|
||||
yield(progress)
|
||||
break if threads.all? { |t| t.join(2) }
|
||||
end
|
||||
break if threads.all? { |t| t.join(2) } while true
|
||||
|
||||
results = threads.map(&:value).compact
|
||||
|
||||
|
@ -115,7 +111,7 @@ module DiscourseAi
|
|||
end
|
||||
|
||||
@custom_raw = <<~RAW
|
||||
|
||||
|
||||
[grid]
|
||||
#{
|
||||
uploads
|
||||
|
|
|
@ -45,6 +45,7 @@ RSpec.describe DiscourseAi::AiBot::Bot do
|
|||
<summary>#{tool.summary}</summary>
|
||||
<p></p>
|
||||
</details>
|
||||
<span></span>
|
||||
|
||||
HTML
|
||||
|
||||
|
|
|
@ -67,7 +67,9 @@ RSpec.describe DiscourseAi::AiBot::Playground do
|
|||
done_signal = messages.pop
|
||||
expect(done_signal.data[:done]).to eq(true)
|
||||
|
||||
messages.each_with_index do |m, idx|
|
||||
# we need this for styling
|
||||
expect(messages.first.data[:raw]).to eq("<p></p>")
|
||||
messages[1..-1].each_with_index do |m, idx|
|
||||
expect(m.data[:raw]).to eq(expected_bot_response[0..idx])
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in New Issue