layered-assistant-rails 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. checksums.yaml +7 -0
  2. data/AGENTS.md +94 -0
  3. data/LICENSE +201 -0
  4. data/README.md +176 -0
  5. data/Rakefile +15 -0
  6. data/app/assets/tailwind/layered/assistant/styles.css +13 -0
  7. data/app/controllers/concerns/layered/assistant/message_creation.rb +49 -0
  8. data/app/controllers/concerns/layered/assistant/public/session_conversations.rb +50 -0
  9. data/app/controllers/layered/assistant/application_controller.rb +25 -0
  10. data/app/controllers/layered/assistant/assistants_controller.rb +59 -0
  11. data/app/controllers/layered/assistant/conversations_controller.rb +77 -0
  12. data/app/controllers/layered/assistant/messages_controller.rb +57 -0
  13. data/app/controllers/layered/assistant/models_controller.rb +61 -0
  14. data/app/controllers/layered/assistant/panel/conversations_controller.rb +63 -0
  15. data/app/controllers/layered/assistant/panel/messages_controller.rb +44 -0
  16. data/app/controllers/layered/assistant/providers_controller.rb +55 -0
  17. data/app/controllers/layered/assistant/public/application_controller.rb +16 -0
  18. data/app/controllers/layered/assistant/public/assistants_controller.rb +16 -0
  19. data/app/controllers/layered/assistant/public/conversations_controller.rb +33 -0
  20. data/app/controllers/layered/assistant/public/messages_controller.rb +42 -0
  21. data/app/controllers/layered/assistant/public/panel/conversations_controller.rb +62 -0
  22. data/app/controllers/layered/assistant/public/panel/messages_controller.rb +50 -0
  23. data/app/controllers/layered/assistant/setup_controller.rb +9 -0
  24. data/app/helpers/layered/assistant/access_helper.rb +45 -0
  25. data/app/helpers/layered/assistant/messages_helper.rb +41 -0
  26. data/app/helpers/layered/assistant/panel_helper.rb +38 -0
  27. data/app/javascript/layered_assistant/composer_controller.js +30 -0
  28. data/app/javascript/layered_assistant/index.js +14 -0
  29. data/app/javascript/layered_assistant/message_streaming.js +124 -0
  30. data/app/javascript/layered_assistant/messages_controller.js +62 -0
  31. data/app/javascript/layered_assistant/panel_controller.js +36 -0
  32. data/app/javascript/layered_assistant/panel_nav_controller.js +16 -0
  33. data/app/javascript/layered_assistant/provider_template_controller.js +45 -0
  34. data/app/javascript/layered_assistant/vendor/marked.esm.js +72 -0
  35. data/app/jobs/layered/assistant/application_job.rb +6 -0
  36. data/app/jobs/layered/assistant/messages/response_job.rb +36 -0
  37. data/app/models/layered/assistant/application_record.rb +7 -0
  38. data/app/models/layered/assistant/assistant.rb +22 -0
  39. data/app/models/layered/assistant/conversation.rb +39 -0
  40. data/app/models/layered/assistant/message.rb +56 -0
  41. data/app/models/layered/assistant/model.rb +21 -0
  42. data/app/models/layered/assistant/provider.rb +49 -0
  43. data/app/services/layered/assistant/chunk_service.rb +80 -0
  44. data/app/services/layered/assistant/client_service.rb +18 -0
  45. data/app/services/layered/assistant/clients/anthropic.rb +24 -0
  46. data/app/services/layered/assistant/clients/base.rb +29 -0
  47. data/app/services/layered/assistant/clients/openai.rb +33 -0
  48. data/app/services/layered/assistant/messages_service.rb +58 -0
  49. data/app/services/layered/assistant/models/create_service.rb +50 -0
  50. data/app/services/layered/assistant/token_estimator.rb +11 -0
  51. data/app/views/layered/assistant/assistants/_form.html.erb +42 -0
  52. data/app/views/layered/assistant/assistants/edit.html.erb +6 -0
  53. data/app/views/layered/assistant/assistants/index.html.erb +45 -0
  54. data/app/views/layered/assistant/assistants/new.html.erb +6 -0
  55. data/app/views/layered/assistant/conversations/_form.html.erb +29 -0
  56. data/app/views/layered/assistant/conversations/edit.html.erb +6 -0
  57. data/app/views/layered/assistant/conversations/index.html.erb +63 -0
  58. data/app/views/layered/assistant/conversations/new.html.erb +6 -0
  59. data/app/views/layered/assistant/conversations/show.html.erb +25 -0
  60. data/app/views/layered/assistant/messages/_composer.html.erb +15 -0
  61. data/app/views/layered/assistant/messages/_message.html.erb +25 -0
  62. data/app/views/layered/assistant/messages/_system_prompt.html.erb +10 -0
  63. data/app/views/layered/assistant/messages/create.turbo_stream.erb +9 -0
  64. data/app/views/layered/assistant/messages/index.html.erb +46 -0
  65. data/app/views/layered/assistant/models/_form.html.erb +30 -0
  66. data/app/views/layered/assistant/models/edit.html.erb +6 -0
  67. data/app/views/layered/assistant/models/index.html.erb +54 -0
  68. data/app/views/layered/assistant/models/new.html.erb +6 -0
  69. data/app/views/layered/assistant/panel/conversations/_header.html.erb +23 -0
  70. data/app/views/layered/assistant/panel/conversations/index.html.erb +46 -0
  71. data/app/views/layered/assistant/panel/conversations/new.html.erb +23 -0
  72. data/app/views/layered/assistant/panel/conversations/show.html.erb +24 -0
  73. data/app/views/layered/assistant/panel/messages/_composer.html.erb +15 -0
  74. data/app/views/layered/assistant/panel/messages/create.turbo_stream.erb +9 -0
  75. data/app/views/layered/assistant/providers/_form.html.erb +81 -0
  76. data/app/views/layered/assistant/providers/edit.html.erb +6 -0
  77. data/app/views/layered/assistant/providers/index.html.erb +47 -0
  78. data/app/views/layered/assistant/providers/new.html.erb +6 -0
  79. data/app/views/layered/assistant/public/assistants/index.html.erb +34 -0
  80. data/app/views/layered/assistant/public/assistants/show.html.erb +23 -0
  81. data/app/views/layered/assistant/public/conversations/show.html.erb +24 -0
  82. data/app/views/layered/assistant/public/messages/_composer.html.erb +7 -0
  83. data/app/views/layered/assistant/public/messages/create.turbo_stream.erb +9 -0
  84. data/app/views/layered/assistant/public/panel/conversations/_header.html.erb +16 -0
  85. data/app/views/layered/assistant/public/panel/conversations/index.html.erb +48 -0
  86. data/app/views/layered/assistant/public/panel/conversations/new.html.erb +17 -0
  87. data/app/views/layered/assistant/public/panel/conversations/show.html.erb +23 -0
  88. data/app/views/layered/assistant/public/panel/messages/_composer.html.erb +7 -0
  89. data/app/views/layered/assistant/public/panel/messages/create.turbo_stream.erb +9 -0
  90. data/app/views/layered/assistant/setup/_setup.html.erb +121 -0
  91. data/app/views/layered/assistant/setup/index.html.erb +2 -0
  92. data/app/views/layouts/layered/assistant/_host_navigation.html.erb +0 -0
  93. data/app/views/layouts/layered/assistant/application.html.erb +32 -0
  94. data/config/importmap.rb +8 -0
  95. data/config/routes.rb +31 -0
  96. data/data/models.json +42 -0
  97. data/db/migrate/20260312000000_create_layered_assistant_tables.rb +63 -0
  98. data/lib/generators/layered/assistant/install_generator.rb +113 -0
  99. data/lib/generators/layered/assistant/migrations_generator.rb +47 -0
  100. data/lib/generators/layered/assistant/templates/initializer.rb +26 -0
  101. data/lib/layered/assistant/engine.rb +29 -0
  102. data/lib/layered/assistant/version.rb +5 -0
  103. data/lib/layered/assistant.rb +19 -0
  104. data/lib/layered-assistant-rails.rb +1 -0
  105. metadata +449 -0
@@ -0,0 +1,59 @@
1
+ module Layered
2
+ module Assistant
3
+ class AssistantsController < ApplicationController
4
+ before_action :set_assistant, only: [:edit, :update, :destroy]
5
+ before_action :set_models, only: [:new, :create, :edit, :update]
6
+
7
+ def index
8
+ @page_title = "Assistants"
9
+ @pagy, @assistants = pagy(Assistant.by_name)
10
+ end
11
+
12
+ def new
13
+ @page_title = "New assistant"
14
+ @assistant = Assistant.new
15
+ end
16
+
17
+ def create
18
+ @assistant = Assistant.new(assistant_params)
19
+
20
+ if @assistant.save
21
+ redirect_to layered_assistant.assistants_path, notice: "Assistant was successfully created."
22
+ else
23
+ render :new, status: :unprocessable_entity
24
+ end
25
+ end
26
+
27
+ def edit
28
+ @page_title = "Edit assistant"
29
+ end
30
+
31
+ def update
32
+ if @assistant.update(assistant_params)
33
+ redirect_to layered_assistant.assistants_path, notice: "Assistant was successfully updated."
34
+ else
35
+ render :edit, status: :unprocessable_entity
36
+ end
37
+ end
38
+
39
+ def destroy
40
+ @assistant.destroy
41
+ redirect_to layered_assistant.assistants_path, notice: "Assistant was successfully deleted."
42
+ end
43
+
44
+ private
45
+
46
+ def set_assistant
47
+ @assistant = Assistant.find(params[:id])
48
+ end
49
+
50
+ def set_models
51
+ @models = Model.available
52
+ end
53
+
54
+ def assistant_params
55
+ params.require(:assistant).permit(:name, :description, :system_prompt, :default_model_id, :public)
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,77 @@
1
+ module Layered
2
+ module Assistant
3
+ class ConversationsController < ApplicationController
4
+ before_action :set_conversation, only: [:show, :edit, :update, :destroy]
5
+ before_action :set_assistants, only: [:new, :create]
6
+
7
+ def index
8
+ if params[:assistant_id]
9
+ @assistant = Assistant.find(params[:assistant_id])
10
+ @page_title = "Conversations - #{@assistant.name}"
11
+ @pagy, @conversations = pagy(@assistant.conversations.includes(:owner).by_created_at)
12
+ else
13
+ @page_title = "Conversations"
14
+ @pagy, @conversations = pagy(Conversation.includes(:assistant, :owner).by_created_at)
15
+ end
16
+ end
17
+
18
+ def show
19
+ @page_title = @conversation.name
20
+ @messages = @conversation.messages.includes(:model).by_created_at
21
+ @models = Model.available
22
+ @selected_model_id = @messages.last&.model_id || @conversation.assistant.default_model_id || @models.first&.id
23
+ end
24
+
25
+ def new
26
+ @page_title = "New conversation"
27
+ @conversation = Conversation.new(params.permit(conversation: [:assistant_id])[:conversation])
28
+ end
29
+
30
+ def create
31
+ @conversation = Conversation.new(conversation_params)
32
+ @conversation.owner = l_ui_current_user
33
+ @conversation.name = Conversation.default_name if @conversation.name.blank?
34
+ if @conversation.save
35
+ redirect_to layered_assistant.conversation_path(@conversation), notice: "Conversation was successfully created."
36
+ else
37
+ render :new, status: :unprocessable_entity
38
+ end
39
+ end
40
+
41
+ def edit
42
+ @page_title = "Edit conversation"
43
+ end
44
+
45
+ def update
46
+ if @conversation.update(conversation_params)
47
+ redirect_to layered_assistant.conversations_path, notice: "Conversation was successfully updated."
48
+ else
49
+ render :edit, status: :unprocessable_entity
50
+ end
51
+ end
52
+
53
+ def destroy
54
+ @conversation.destroy
55
+ redirect_to layered_assistant.conversations_path, notice: "Conversation was successfully deleted."
56
+ end
57
+
58
+ private
59
+
60
+ def set_conversation
61
+ @conversation = Conversation.find(params[:id])
62
+ end
63
+
64
+ def set_assistants
65
+ @assistants = Assistant.by_name
66
+ end
67
+
68
+ def conversation_params
69
+ if action_name == "create"
70
+ params.require(:conversation).permit(:name, :assistant_id)
71
+ else
72
+ params.require(:conversation).permit(:name)
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,57 @@
1
+ module Layered
2
+ module Assistant
3
+ class MessagesController < ApplicationController
4
+ include MessageCreation
5
+
6
+ before_action :set_conversation
7
+ before_action :set_message, only: [:destroy]
8
+
9
+ def index
10
+ @page_title = "Messages"
11
+ @pagy, @messages = pagy(@conversation.messages.by_created_at)
12
+ end
13
+
14
+ def create
15
+ result = create_messages_for(
16
+ conversation: @conversation,
17
+ content: message_params[:content],
18
+ model_id: message_params[:model_id]
19
+ )
20
+ @message = result[:message]
21
+
22
+ unless @message.persisted?
23
+ return head :unprocessable_entity
24
+ end
25
+
26
+ @assistant_message = result[:assistant_message]
27
+ @models = result[:models]
28
+ @selected_model_id = result[:selected_model_id]
29
+ @error = result[:error]
30
+
31
+ respond_to do |format|
32
+ format.turbo_stream
33
+ end
34
+ end
35
+
36
+ def destroy
37
+ @message.destroy
38
+ @conversation.update_token_totals!
39
+ redirect_to layered_assistant.conversation_messages_path(@conversation), notice: "Message was successfully deleted."
40
+ end
41
+
42
+ private
43
+
44
+ def set_conversation
45
+ @conversation = Conversation.find(params[:conversation_id])
46
+ end
47
+
48
+ def set_message
49
+ @message = @conversation.messages.find(params[:id])
50
+ end
51
+
52
+ def message_params
53
+ params.require(:message).permit(:content, :model_id)
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,61 @@
1
+ module Layered
2
+ module Assistant
3
+ class ModelsController < ApplicationController
4
+ before_action :set_provider
5
+ before_action :set_model, only: [:edit, :update, :destroy]
6
+
7
+ def index
8
+ @pagy, @models = pagy(@provider.models.sorted)
9
+ end
10
+
11
+ def new
12
+ @model = @provider.models.new
13
+ end
14
+
15
+ def create
16
+ @model = @provider.models.new(model_params)
17
+
18
+ if @model.save
19
+ redirect_to layered_assistant.provider_models_path(@provider), notice: "Model was successfully created."
20
+ else
21
+ render :new, status: :unprocessable_entity
22
+ end
23
+ end
24
+
25
+ def edit
26
+ end
27
+
28
+ def update
29
+ if @model.update(model_params)
30
+ redirect_to layered_assistant.provider_models_path(@provider), notice: "Model was successfully updated."
31
+ else
32
+ render :edit, status: :unprocessable_entity
33
+ end
34
+ end
35
+
36
+ def destroy
37
+ if @model.destroy
38
+ redirect_to layered_assistant.provider_models_path(@provider), notice: "Model was successfully deleted."
39
+ else
40
+ redirect_to layered_assistant.provider_models_path(@provider), alert: @model.errors.full_messages.to_sentence
41
+ end
42
+ rescue ActiveRecord::InvalidForeignKey
43
+ redirect_to layered_assistant.provider_models_path(@provider), alert: "Cannot delete a model that is in use."
44
+ end
45
+
46
+ private
47
+
48
+ def set_provider
49
+ @provider = Provider.find(params[:provider_id])
50
+ end
51
+
52
+ def set_model
53
+ @model = @provider.models.find(params[:id])
54
+ end
55
+
56
+ def model_params
57
+ params.require(:model).permit(:name, :identifier, :enabled)
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,63 @@
1
+ module Layered
2
+ module Assistant
3
+ module Panel
4
+ class ConversationsController < ApplicationController
5
+ layout false
6
+
7
+ before_action :set_conversation, only: [:show, :destroy]
8
+ before_action :set_conversations, only: [:index, :show]
9
+ before_action :set_assistants, only: [:index, :show, :new, :create]
10
+
11
+ def index
12
+ end
13
+
14
+ def show
15
+ @messages = @conversation.messages.includes(:model).by_created_at
16
+ @models = Model.available
17
+ @selected_model_id = @messages.last&.model_id || @conversation.assistant.default_model_id || @models.first&.id
18
+ end
19
+
20
+ def new
21
+ @conversation = Conversation.new
22
+ end
23
+
24
+ def create
25
+ @conversation = Conversation.new(conversation_params)
26
+ @conversation.owner = l_ui_current_user
27
+ @conversation.name = Conversation.default_name if @conversation.name.blank?
28
+
29
+ if @conversation.save
30
+ redirect_to layered_assistant.panel_conversation_path(@conversation)
31
+ else
32
+ render :new, status: :unprocessable_entity
33
+ end
34
+ end
35
+
36
+ def destroy
37
+ @conversation.destroy
38
+ redirect_to layered_assistant.panel_conversations_path
39
+ end
40
+
41
+ private
42
+
43
+ def set_conversation
44
+ @conversation = Conversation.find(params[:id])
45
+ end
46
+
47
+ def set_assistants
48
+ @assistants = Assistant.by_name
49
+ end
50
+
51
+ def set_conversations
52
+ scope = Conversation.by_created_at
53
+ scope = scope.where(assistant_id: params[:assistant_id]) if params[:assistant_id].present?
54
+ @conversations = scope.limit(20)
55
+ end
56
+
57
+ def conversation_params
58
+ params.require(:conversation).permit(:assistant_id, :name)
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,44 @@
1
+ module Layered
2
+ module Assistant
3
+ module Panel
4
+ class MessagesController < ApplicationController
5
+ layout false
6
+ include MessageCreation
7
+
8
+ before_action :set_conversation
9
+
10
+ def create
11
+ result = create_messages_for(
12
+ conversation: @conversation,
13
+ content: message_params[:content],
14
+ model_id: message_params[:model_id]
15
+ )
16
+ @message = result[:message]
17
+
18
+ unless @message.persisted?
19
+ return head :unprocessable_entity
20
+ end
21
+
22
+ @assistant_message = result[:assistant_message]
23
+ @models = result[:models]
24
+ @selected_model_id = result[:selected_model_id]
25
+ @error = result[:error]
26
+
27
+ respond_to do |format|
28
+ format.turbo_stream
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def set_conversation
35
+ @conversation = Conversation.find(params[:conversation_id])
36
+ end
37
+
38
+ def message_params
39
+ params.require(:message).permit(:content, :model_id)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,55 @@
1
+ module Layered
2
+ module Assistant
3
+ class ProvidersController < ApplicationController
4
+ before_action :set_provider, only: [:edit, :update, :destroy]
5
+
6
+ def index
7
+ @page_title = "Providers"
8
+ @pagy, @providers = pagy(Provider.sorted)
9
+ end
10
+
11
+ def new
12
+ @page_title = "New Provider"
13
+ @provider = Provider.new
14
+ end
15
+
16
+ def create
17
+ @provider = Provider.new(provider_params)
18
+
19
+ if @provider.save
20
+ Models::CreateService.new(@provider).call if params[:provider][:create_models] == "1"
21
+ redirect_to layered_assistant.providers_path, notice: "Provider was successfully created."
22
+ else
23
+ render :new, status: :unprocessable_entity
24
+ end
25
+ end
26
+
27
+ def edit
28
+ @page_title = "Edit Provider"
29
+ end
30
+
31
+ def update
32
+ if @provider.update(provider_params)
33
+ redirect_to layered_assistant.providers_path, notice: "Provider was successfully updated."
34
+ else
35
+ render :edit, status: :unprocessable_entity
36
+ end
37
+ end
38
+
39
+ def destroy
40
+ @provider.destroy
41
+ redirect_to layered_assistant.providers_path, notice: "Provider was successfully deleted."
42
+ end
43
+
44
+ private
45
+
46
+ def set_provider
47
+ @provider = Provider.find(params[:id])
48
+ end
49
+
50
+ def provider_params
51
+ params.require(:provider).permit(:name, :protocol, :url, :secret, :enabled)
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,16 @@
1
+ module Layered
2
+ module Assistant
3
+ module Public
4
+ class ApplicationController < Layered::Assistant::ApplicationController
5
+ skip_before_action :layered_assistant_authorize!
6
+ include SessionConversations
7
+
8
+ private
9
+
10
+ def set_public_assistant
11
+ @assistant = Assistant.publicly_available.find(params[:assistant_id] || params[:id])
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ module Layered
2
+ module Assistant
3
+ module Public
4
+ class AssistantsController < ApplicationController
5
+ before_action :set_public_assistant, only: [:show]
6
+
7
+ def index
8
+ @pagy, @assistants = pagy(Assistant.publicly_available.by_name)
9
+ end
10
+
11
+ def show
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,33 @@
1
+ module Layered
2
+ module Assistant
3
+ module Public
4
+ class ConversationsController < ApplicationController
5
+ before_action :set_public_assistant, only: [:create]
6
+ before_action :set_conversation, only: [:show]
7
+
8
+ def create
9
+ @conversation = @assistant.conversations.new(name: Conversation.default_name)
10
+
11
+ if @conversation.save
12
+ add_conversation_to_session(@conversation)
13
+ redirect_to layered_assistant.public_conversation_path(@conversation)
14
+ else
15
+ redirect_to layered_assistant.public_assistant_path(@assistant)
16
+ end
17
+ end
18
+
19
+ def show
20
+ @messages = @conversation.messages.includes(:model).by_created_at
21
+ end
22
+
23
+ private
24
+
25
+ def set_conversation
26
+ @conversation = find_session_conversation(params[:id])
27
+ rescue ActiveRecord::RecordNotFound
28
+ redirect_to layered_assistant.public_assistants_path
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,42 @@
1
+ module Layered
2
+ module Assistant
3
+ module Public
4
+ class MessagesController < ApplicationController
5
+ include MessageCreation
6
+
7
+ before_action :set_conversation
8
+
9
+ def create
10
+ result = create_messages_for(
11
+ conversation: @conversation,
12
+ content: message_params[:content],
13
+ model_id: @conversation.assistant.default_model_id
14
+ )
15
+ @message = result[:message]
16
+
17
+ unless @message.persisted?
18
+ return head :unprocessable_entity
19
+ end
20
+
21
+ @error = result[:error]
22
+
23
+ respond_to do |format|
24
+ format.turbo_stream
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def set_conversation
31
+ @conversation = find_session_conversation(params[:conversation_id])
32
+ rescue ActiveRecord::RecordNotFound
33
+ redirect_to layered_assistant.public_assistants_path
34
+ end
35
+
36
+ def message_params
37
+ params.require(:message).permit(:content)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,62 @@
1
+ module Layered
2
+ module Assistant
3
+ module Public
4
+ module Panel
5
+ class ConversationsController < Public::ApplicationController
6
+ layout false
7
+
8
+ before_action :set_public_assistant, only: [:index, :new, :create]
9
+ before_action :set_conversation, only: [:show]
10
+ before_action :set_session_conversations, only: [:index, :show]
11
+
12
+ def index
13
+ end
14
+
15
+ def new
16
+ end
17
+
18
+ def create
19
+ @conversation = @assistant.conversations.new(name: Conversation.default_name)
20
+
21
+ if @conversation.save
22
+ add_conversation_to_session(@conversation)
23
+ redirect_to layered_assistant.public_panel_conversation_path(@conversation)
24
+ else
25
+ render :new, status: :unprocessable_entity
26
+ end
27
+ end
28
+
29
+ def show
30
+ @messages = @conversation.messages.includes(:model).by_created_at
31
+ end
32
+
33
+ private
34
+
35
+ def set_conversation
36
+ @conversation = find_session_conversation(params[:id])
37
+ rescue ActiveRecord::RecordNotFound
38
+ assistant = Conversation.find_by(id: params[:id])&.assistant
39
+ if assistant&.public?
40
+ redirect_to layered_assistant.public_panel_conversations_path(assistant_id: assistant.id)
41
+ else
42
+ redirect_to layered_assistant.public_assistants_path
43
+ end
44
+ end
45
+
46
+ def set_session_conversations
47
+ assistant_id = @conversation&.assistant_id || @assistant&.id
48
+ @conversations = if session_conversation_ids.any?
49
+ scope = Conversation.joins(:assistant).merge(Assistant.publicly_available)
50
+ .where(id: session_conversation_ids)
51
+ .by_created_at
52
+ scope = scope.where(assistant_id: assistant_id) if assistant_id
53
+ scope.limit(20)
54
+ else
55
+ Conversation.none
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,50 @@
1
+ module Layered
2
+ module Assistant
3
+ module Public
4
+ module Panel
5
+ class MessagesController < Public::ApplicationController
6
+ layout false
7
+ include MessageCreation
8
+
9
+ before_action :set_conversation
10
+
11
+ def create
12
+ result = create_messages_for(
13
+ conversation: @conversation,
14
+ content: message_params[:content],
15
+ model_id: @conversation.assistant.default_model_id
16
+ )
17
+ @message = result[:message]
18
+
19
+ unless @message.persisted?
20
+ return head :unprocessable_entity
21
+ end
22
+
23
+ @error = result[:error]
24
+
25
+ respond_to do |format|
26
+ format.turbo_stream
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def set_conversation
33
+ @conversation = find_session_conversation(params[:conversation_id])
34
+ rescue ActiveRecord::RecordNotFound
35
+ assistant = Conversation.find_by(id: params[:conversation_id])&.assistant
36
+ if assistant&.public?
37
+ redirect_to layered_assistant.new_public_panel_conversation_path(assistant_id: assistant.id)
38
+ else
39
+ redirect_to layered_assistant.public_assistants_path
40
+ end
41
+ end
42
+
43
+ def message_params
44
+ params.require(:message).permit(:content)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,9 @@
1
+ module Layered
2
+ module Assistant
3
+ class SetupController < ApplicationController
4
+ def index
5
+ @page_title = "Setup"
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,45 @@
1
+ module Layered
2
+ module Assistant
3
+ module AccessHelper
4
+ def l_assistant_accessible?
5
+ block = Layered::Assistant.authorize_block
6
+ return false unless block
7
+
8
+ checker = AccessibilityChecker.new(self)
9
+ checker.accessible?(&block)
10
+ end
11
+
12
+ class AccessibilityChecker
13
+ def initialize(context)
14
+ @context = context
15
+ @blocked = false
16
+ end
17
+
18
+ def accessible?(&block)
19
+ instance_exec(&block)
20
+ !@blocked
21
+ end
22
+
23
+ def head(*)
24
+ @blocked = true
25
+ end
26
+
27
+ def redirect_to(*)
28
+ @blocked = true
29
+ end
30
+
31
+ private
32
+
33
+ def method_missing(method, ...)
34
+ @context.send(method, ...)
35
+ end
36
+
37
+ def respond_to_missing?(method, include_private = false)
38
+ @context.respond_to?(method, include_private)
39
+ end
40
+ end
41
+
42
+ private_constant :AccessibilityChecker
43
+ end
44
+ end
45
+ end