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 +4 -4
- data/app/controllers/layered/assistant/messages_controller.rb +8 -14
- data/app/layered_resources/layered/assistant/message_resource.rb +44 -0
- data/config/routes.rb +1 -1
- data/lib/layered/assistant/version.rb +1 -1
- metadata +2 -2
- data/app/views/layered/assistant/messages/index.html.erb +0 -63
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c87ef1f390a6a4c102682cb4befebc1950f8566552f5213075a9d9f2ac4c7b49
|
|
4
|
+
data.tar.gz: 0d289b5e1f05d1f696ae8e37053ec75b51b3bf3c280091ceb33d836c0f06678b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 68cb5c184761c2ef7de4bf43cc6b84a32dfbe2b8cfd434cb3562c72265e7d9640bd3ad41f965a1c4cc04fec13c02b314cacf5780e1d779f2d08fec87aefa260a
|
|
7
|
+
data.tar.gz: 8f9cc996f9de499fa6d7c9afaf8f607eed0ea9c06c0f78f3e2ff8aeac67580a8a897002c0b5c350c668d76920311c9c2cbae10c2d11b452b99cd89a3c9c70533
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
module Layered
|
|
2
2
|
module Assistant
|
|
3
|
-
|
|
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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
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.
|
|
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>
|