layered-assistant-rails 0.9.0 → 0.9.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 68ea0fb66720732e6e05a139e873ec6eb90912967e1a6e9c956461e76d7e347c
4
- data.tar.gz: f4e561987280115a3e160a42a747ee036aab7373c8f65fcc33d36eac01dafec7
3
+ metadata.gz: c87ef1f390a6a4c102682cb4befebc1950f8566552f5213075a9d9f2ac4c7b49
4
+ data.tar.gz: 0d289b5e1f05d1f696ae8e37053ec75b51b3bf3c280091ceb33d836c0f06678b
5
5
  SHA512:
6
- metadata.gz: 56758769136c2e033813dcf466444c778643483739bcafa5048962bca0786e167a04a6a9de9085eed038a8d492cd08c2328363cc1a98f2ea58024bd6096efd99
7
- data.tar.gz: 054c1b233c696bbec3d6c7e4e643c28fc69ffc378ce5ffa11b3110de59ed9cf8bbb87c9ed582b442f437a724a7d502609171c3022b67eb43f8b2a759a8190059
6
+ metadata.gz: 68cb5c184761c2ef7de4bf43cc6b84a32dfbe2b8cfd434cb3562c72265e7d9640bd3ad41f965a1c4cc04fec13c02b314cacf5780e1d779f2d08fec87aefa260a
7
+ data.tar.gz: 8f9cc996f9de499fa6d7c9afaf8f607eed0ea9c06c0f78f3e2ff8aeac67580a8a897002c0b5c350c668d76920311c9c2cbae10c2d11b452b99cd89a3c9c70533
@@ -1,15 +1,14 @@
1
1
  module Layered
2
2
  module Assistant
3
- class MessagesController < ApplicationController
3
+ # Messages are a layered resource for listing and deleting them; only
4
+ # `create` - the composer posting a message - is this engine's own.
5
+ class MessagesController < ResourcesController
4
6
  include MessageCreation
5
7
 
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.includes(:model).by_created_at)
12
- end
8
+ # Messages have no fields, which the gem reads as having no form, but
9
+ # `create` is the composer's rather than the gem's.
10
+ skip_before_action :require_layered_fields, only: [ :create ]
11
+ before_action :set_conversation, only: [ :create, :destroy ]
13
12
 
14
13
  def create
15
14
  model_id = scoped_model_id(message_params[:model_id])
@@ -37,9 +36,8 @@ module Layered
37
36
  end
38
37
 
39
38
  def destroy
40
- @message.destroy
39
+ super
41
40
  @conversation.update_token_totals!
42
- redirect_to layered_assistant.conversation_messages_path(@conversation), notice: "Message was successfully deleted."
43
41
  end
44
42
 
45
43
  private
@@ -48,10 +46,6 @@ module Layered
48
46
  @conversation = scoped(Conversation).find_by!(uid: params[:conversation_id])
49
47
  end
50
48
 
51
- def set_message
52
- @message = @conversation.messages.find(params[:id])
53
- end
54
-
55
49
  def message_params
56
50
  params.require(:message).permit(:content, :model_id)
57
51
  end
@@ -0,0 +1,44 @@
1
+ module Layered
2
+ module Assistant
3
+ # Messages are listed and deleted here; they are written through the
4
+ # composer, so there are no fields and no form.
5
+ class MessageResource < Layered::Resource::Base
6
+ model Message
7
+
8
+ columns [
9
+ { attribute: :role, primary: true },
10
+ { attribute: :content, sortable: false,
11
+ render: ->(record, view) { view.truncate(record.content, length: 100) } },
12
+ { attribute: :model_id, label: "Model", sortable: false,
13
+ render: ->(record) { record.model&.name } },
14
+ { attribute: :tokens, label: "Tokens", sortable: false,
15
+ render: ->(record, view) {
16
+ total = record.input_tokens.to_i + record.output_tokens.to_i
17
+ "#{'~' if record.tokens_estimated?}#{view.number_with_delimiter(total)}" if total > 0
18
+ } },
19
+ { attribute: :tokens_per_second, label: "Tok/s", sortable: false,
20
+ render: ->(record) {
21
+ if record.output_tokens.to_i > 0 && record.response_ms.to_i >= MessagesHelper::MIN_RESPONSE_MS_FOR_TPS
22
+ (record.output_tokens * 1000.0 / record.response_ms).round(1)
23
+ end
24
+ } },
25
+ { attribute: :ttft_ms, label: "TTFT",
26
+ render: ->(record) { "#{record.ttft_ms}ms" if record.ttft_ms } },
27
+ { attribute: :created_at, label: "Created",
28
+ render: ->(record) { record.created_at.to_fs(:short) } }
29
+ ]
30
+
31
+ search_fields [ :content ]
32
+
33
+ default_sort attribute: :created_at, direction: :asc
34
+
35
+ # The conversation is resolved through the engine's own scoping, so an
36
+ # out-of-scope one 404s before any message is reached.
37
+ def self.scope(controller)
38
+ controller.send(:scoped, Conversation)
39
+ .find_by!(uid: controller.params[:conversation_id])
40
+ .messages.includes(:model)
41
+ end
42
+ end
43
+ end
44
+ end
data/config/routes.rb CHANGED
@@ -16,7 +16,7 @@ Layered::Assistant::Engine.routes.draw do
16
16
  end
17
17
  end
18
18
  resources :conversations, only: [] do
19
- resources :messages, only: [ :index, :create, :destroy ]
19
+ layered_resources :messages, namespace: "Layered::Assistant", controller: "messages", only: [ :index, :create, :destroy ]
20
20
  # Approving a tool call is the same act wherever it is rendered, and the
21
21
  # partial carrying the buttons is broadcast rather than requested, so it
22
22
  # cannot know which namespace it landed in. One route serves them all.
@@ -1,5 +1,5 @@
1
1
  module Layered
2
2
  module Assistant
3
- VERSION = "0.9.0"
3
+ VERSION = "0.9.1"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: layered-assistant-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 0.9.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - layered.ai
@@ -350,6 +350,7 @@ files:
350
350
  - app/jobs/layered/assistant/messages/tool_call_job.rb
351
351
  - app/layered_resources/layered/assistant/assistant_resource.rb
352
352
  - app/layered_resources/layered/assistant/conversation_resource.rb
353
+ - app/layered_resources/layered/assistant/message_resource.rb
353
354
  - app/layered_resources/layered/assistant/model_resource.rb
354
355
  - app/layered_resources/layered/assistant/persona_resource.rb
355
356
  - app/layered_resources/layered/assistant/provider_resource.rb
@@ -387,7 +388,6 @@ files:
387
388
  - app/views/layered/assistant/messages/_message.html.erb
388
389
  - app/views/layered/assistant/messages/_tool_message.html.erb
389
390
  - app/views/layered/assistant/messages/create.turbo_stream.erb
390
- - app/views/layered/assistant/messages/index.html.erb
391
391
  - app/views/layered/assistant/panel/conversations/_header.html.erb
392
392
  - app/views/layered/assistant/panel/conversations/index.html.erb
393
393
  - app/views/layered/assistant/panel/conversations/new.html.erb
@@ -1,63 +0,0 @@
1
- <%= l_ui_title_bar(
2
- title: "Messages",
3
- breadcrumbs: [["Conversations", layered_assistant.conversations_path]]
4
- ) %>
5
-
6
- <div class="l-ui-table-container l-ui-mt-4">
7
- <table class="l-ui-table">
8
- <caption class="l-ui-sr-only">Messages</caption>
9
- <thead class="l-ui-table__header">
10
- <tr>
11
- <th scope="col" class="l-ui-table__header-cell">Role</th>
12
- <th scope="col" class="l-ui-table__header-cell">Content</th>
13
- <th scope="col" class="l-ui-table__header-cell">Model</th>
14
- <th scope="col" class="l-ui-table__header-cell">Tokens</th>
15
- <th scope="col" class="l-ui-table__header-cell">Tok/s</th>
16
- <th scope="col" class="l-ui-table__header-cell">TTFT</th>
17
- <th scope="col" class="l-ui-table__header-cell">Created</th>
18
- <th scope="col" class="l-ui-table__header-cell l-ui-table__header-cell--action">Actions</th>
19
- </tr>
20
- </thead>
21
-
22
- <tbody class="l-ui-table__body">
23
- <% @messages.each do |message| %>
24
- <tr>
25
- <th scope="row" class="l-ui-table__cell l-ui-table__cell--primary">
26
- <%= message.role %>
27
- </th>
28
- <td class="l-ui-table__cell">
29
- <%= truncate(message.content, length: 100) %>
30
- </td>
31
- <td class="l-ui-table__cell">
32
- <%= message.model&.name %>
33
- </td>
34
- <td class="l-ui-table__cell">
35
- <% total = message.input_tokens.to_i + message.output_tokens.to_i %>
36
- <% if total > 0 %>
37
- <% prefix = message.tokens_estimated? ? "~" : "" %>
38
- <%= "#{prefix}#{number_with_delimiter(total)}" %>
39
- <% end %>
40
- </td>
41
- <td class="l-ui-table__cell">
42
- <% if message.output_tokens.to_i > 0 && message.response_ms.to_i >= Layered::Assistant::MessagesHelper::MIN_RESPONSE_MS_FOR_TPS %>
43
- <%= (message.output_tokens * 1000.0 / message.response_ms).round(1) %>
44
- <% end %>
45
- </td>
46
- <td class="l-ui-table__cell">
47
- <%= "#{message.ttft_ms}ms" if message.ttft_ms %>
48
- </td>
49
- <td class="l-ui-table__cell">
50
- <%= message.created_at.to_fs(:short) %>
51
- </td>
52
- <td class="l-ui-table__cell l-ui-table__cell--action">
53
- <%= link_to "Delete", layered_assistant.conversation_message_path(@conversation, message), class: "l-ui-table__action l-ui-table__action--danger", data: { turbo_method: :delete, turbo_confirm: "Are you sure?" } %>
54
- </td>
55
- </tr>
56
- <% end %>
57
- </tbody>
58
- </table>
59
-
60
- <div class="l-ui-mt-4">
61
- <%= l_ui_pagy(@pagy) %>
62
- </div>
63
- </div>