DEV: chat streaming (#25736)

This commit introduces the possibility to stream messages. To allow plugins to use streaming this commit also ships a `ChatSDK` library to allow to interact with few parts of discourse chat.

```ruby
ChatSDK::Message.create_with_stream(raw: "test") do |helper|
  5.times do |i|
    is_streaming = helper.stream(raw: "more #{i}")
    next if !is_streaming
    sleep 2
  end
end
```

This commit also introduces all the frontend parts:
- messages can now be marked as streaming
- when streaming their content will be updated when a new content is appended
- a special UI will be showing (a blinking indicator)
- a cancel button allows the user to stop the streaming, when cancelled `helper.stream(...)` will return `false`, and the plugin can decide exit early
This commit is contained in:
Joffrey JAFFEUX
2024-02-20 09:49:19 +01:00
committed by GitHub
parent b057f1b2b4
commit d8d756cd2f
30 changed files with 815 additions and 24 deletions

View File

@ -0,0 +1,80 @@
# frozen_string_literal: true
module ChatSDK
class Thread
include Chat::WithServiceHelper
# Updates the title of a specified chat thread.
#
# @param title [String] The new title for the chat thread.
# @param thread_id [Integer] The ID of the chat thread to be updated.
# @param guardian [Guardian] The guardian object representing the user's permissions.
# @return [Chat::Thread] The updated thread object with the new title.
#
# @example Updating the title of a chat thread
# ChatSDK::Thread.update_title(title: "New Thread Title", thread_id: 1, guardian: Guardian.new)
def self.update_title(**params)
new.update(title: params[:title], thread_id: params[:thread_id], guardian: params[:guardian])
end
def self.update(**params)
new.update(**params)
end
# Retrieves messages from a specified thread.
#
# @param thread_id [Integer] The ID of the chat thread from which to fetch messages.
# @param guardian [Guardian] The guardian object representing the user's permissions.
# @return [Array<Chat::Message>] An array of message objects from the specified thread.
#
# @example Fetching messages from a thread with additional parameters
# ChatSDK::Thread.messages(thread_id: 1, guardian: Guardian.new)
#
def self.messages(thread_id:, guardian:, **params)
new.messages(thread_id: thread_id, guardian: guardian, **params)
end
def messages(thread_id:, guardian:, **params)
with_service(
Chat::ListChannelThreadMessages,
thread_id: thread_id,
guardian: guardian,
**params,
direction: "future",
) do
on_success { result.messages }
on_failed_policy(:can_view_thread) { raise "Guardian can't view thread" }
on_failed_policy(:target_message_exists) { raise "Target message doesn't exist" }
on_failed_policy(:ensure_thread_enabled) do
raise "Threading is not enabled for this channel"
end
on_failure do
p Chat::StepsInspector.new(result)
raise "Unexpected error"
end
end
end
def update(**params)
with_service(Chat::UpdateThread, **params) do
on_model_not_found(:channel) do
raise "Couldn’t find channel with id: `#{params[:channel_id]}`"
end
on_model_not_found(:thread) do
raise "Couldn’t find thread with id: `#{params[:thread_id]}`"
end
on_failed_policy(:can_view_channel) { raise "Guardian can't view channel" }
on_failed_policy(:can_edit_thread) { raise "Guardian can't edit thread" }
on_failed_policy(:threading_enabled_for_channel) do
raise "Threading is not enabled for this channel"
end
on_failed_contract { |contract| raise contract.errors.full_messages.join(", ") }
on_success { result.thread_instance }
on_failure do
p Chat::StepsInspector.new(result)
raise "Unexpected error"
end
end
end
end
end