llm_meta_client 0.6.1 → 1.0.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +20 -0
- data/app/assets/stylesheets/llm_meta_client/generation_settings.css +26 -47
- data/lib/generators/llm_meta_client/scaffold/templates/app/controllers/chats_controller.rb +52 -17
- data/lib/generators/llm_meta_client/scaffold/templates/app/controllers/prompts_controller.rb +5 -2
- data/lib/generators/llm_meta_client/scaffold/templates/app/javascript/controllers/generation_settings_controller.js +1 -25
- data/lib/generators/llm_meta_client/scaffold/templates/app/models/chat.rb +12 -10
- data/lib/generators/llm_meta_client/scaffold/templates/app/views/chats/create.turbo_stream.erb +21 -19
- data/lib/generators/llm_meta_client/scaffold/templates/app/views/chats/edit.html.erb +10 -10
- data/lib/generators/llm_meta_client/scaffold/templates/app/views/chats/new.html.erb +1 -1
- data/lib/generators/llm_meta_client/scaffold/templates/app/views/chats/update.turbo_stream.erb +21 -20
- data/lib/generators/llm_meta_client/scaffold/templates/app/views/shared/_generation_settings_field.html.erb +10 -69
- data/lib/llm_meta_client/version.rb +1 -1
- metadata +5 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cd284d6a68a9bbad852fbcb19054ce2aeda512f0ffe17c2ac7029eb30907a7e4
|
|
4
|
+
data.tar.gz: 5fe0cd2beef4c9bbc7fb75ee49099558a07626e0affa6b9ba9b065eca940dc67
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 53f9fcccc2ffee1c08ba3df22bf95e08bc857d8da0cd748f520f26a7144b03dded4a3ceb4ca67b3108fda12d79f521ed88e5ce0b30cb8b232ac5185941d9832f
|
|
7
|
+
data.tar.gz: e5529bc832a453030d348cd8d01100bb664896d03cee385f83ee43b945c316b0c1d03d4227c65b23d6b360943e91e1c9727e7d52d1311b107eacf2bf7f58799f
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,26 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.0.0] - 2026-03-25
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- Replace generation settings UI from individual sliders to JSON textarea input
|
|
13
|
+
- Improve prompt execution branching logic to use `execution_id` instead of message UUID
|
|
14
|
+
- Use `find_by!` for proper 404 handling in controllers
|
|
15
|
+
- Use URL-based chat lookup in `chats#update` instead of session-based lookup
|
|
16
|
+
- Keep existing chat when switching model or LLM (update instead of creating new chat)
|
|
17
|
+
- Upgrade `prompt_navigator` dependency to `~> 1.0`
|
|
18
|
+
- Upgrade `chat_manager` dependency to `~> 1.0`
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- Fix Turbo Stream history sidebar element ID mismatch (`history-content` → `history-sidebar`)
|
|
23
|
+
- Fix `next_ann` for proper history card rendering
|
|
24
|
+
- Wrap inline JavaScript in IIFE to prevent variable conflicts across Turbo Stream updates
|
|
25
|
+
- Fix scroll event listener duplication across Turbo navigations
|
|
26
|
+
- Validate generation settings JSON input before sending to LLM
|
|
27
|
+
|
|
8
28
|
## [0.6.1] - 2026-03-19
|
|
9
29
|
|
|
10
30
|
### Fixed
|
|
@@ -31,60 +31,39 @@
|
|
|
31
31
|
padding: 12px 16px;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
.generation-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
34
|
+
.generation-settings-label {
|
|
35
|
+
display: block;
|
|
36
|
+
font-size: 13px;
|
|
37
|
+
font-weight: 600;
|
|
38
|
+
color: #374151;
|
|
39
|
+
margin-bottom: 6px;
|
|
40
|
+
}
|
|
40
41
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
42
|
+
.generation-settings-json-input {
|
|
43
|
+
width: 100%;
|
|
44
|
+
padding: 8px 10px;
|
|
45
|
+
border: 1px solid #d1d5db;
|
|
46
|
+
border-radius: 6px;
|
|
47
|
+
font-size: 13px;
|
|
48
|
+
font-family: "SFMono-Regular", "Consolas", "Liberation Mono", "Menlo", monospace;
|
|
49
|
+
background-color: white;
|
|
50
|
+
resize: vertical;
|
|
51
|
+
transition: border-color 0.2s;
|
|
52
|
+
box-sizing: border-box;
|
|
50
53
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
54
|
+
&:focus {
|
|
55
|
+
outline: none;
|
|
56
|
+
border-color: #3b82f6;
|
|
57
|
+
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
|
|
55
58
|
}
|
|
56
59
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
padding: 6px 10px;
|
|
60
|
-
border: 1px solid #d1d5db;
|
|
61
|
-
border-radius: 6px;
|
|
62
|
-
font-size: 13px;
|
|
63
|
-
background-color: white;
|
|
64
|
-
transition: border-color 0.2s;
|
|
65
|
-
|
|
66
|
-
&:focus {
|
|
67
|
-
outline: none;
|
|
68
|
-
border-color: #3b82f6;
|
|
69
|
-
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
&::placeholder {
|
|
73
|
-
color: #9ca3af;
|
|
74
|
-
}
|
|
60
|
+
&::placeholder {
|
|
61
|
+
color: #9ca3af;
|
|
75
62
|
}
|
|
76
63
|
}
|
|
77
64
|
|
|
78
|
-
.
|
|
79
|
-
font-weight: 700;
|
|
80
|
-
color: #3b82f6;
|
|
81
|
-
font-size: 13px;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
.setting-range-labels {
|
|
85
|
-
display: flex;
|
|
86
|
-
justify-content: space-between;
|
|
65
|
+
.generation-settings-hint {
|
|
87
66
|
font-size: 11px;
|
|
88
67
|
color: #9ca3af;
|
|
89
|
-
margin-top:
|
|
68
|
+
margin-top: 4px;
|
|
90
69
|
}
|
|
@@ -10,7 +10,8 @@ class ChatsController < ApplicationController
|
|
|
10
10
|
# Initialize chat context
|
|
11
11
|
initialize_chat current_user&.chats
|
|
12
12
|
|
|
13
|
-
@chat = current_user&.chats.includes(:messages).find_by(uuid: params[:id])
|
|
13
|
+
@chat = current_user&.chats.includes(:messages).find_by!(uuid: params[:id])
|
|
14
|
+
session[:chat_id] = @chat.id
|
|
14
15
|
@messages = @chat.ordered_messages
|
|
15
16
|
|
|
16
17
|
# Initialize history
|
|
@@ -71,6 +72,18 @@ class ChatsController < ApplicationController
|
|
|
71
72
|
initialize_history @chat&.ordered_by_descending_prompt_executions
|
|
72
73
|
|
|
73
74
|
if params[:message].present?
|
|
75
|
+
# Validate generation settings before proceeding
|
|
76
|
+
begin
|
|
77
|
+
generation_settings = generation_settings_param
|
|
78
|
+
rescue InvalidGenerationSettingsError => e
|
|
79
|
+
@error_message = e.message
|
|
80
|
+
respond_to do |format|
|
|
81
|
+
format.turbo_stream
|
|
82
|
+
format.html { redirect_to new_chat_path, alert: e.message }
|
|
83
|
+
end
|
|
84
|
+
return
|
|
85
|
+
end
|
|
86
|
+
|
|
74
87
|
# Add user message (will be rendered via turbo stream)
|
|
75
88
|
@prompt_execution, @user_message = @chat.add_user_message(params[:message],
|
|
76
89
|
params[:model],
|
|
@@ -82,7 +95,7 @@ class ChatsController < ApplicationController
|
|
|
82
95
|
|
|
83
96
|
# Send to LLM and get assistant response
|
|
84
97
|
begin
|
|
85
|
-
@assistant_message = @chat.add_assistant_response(@prompt_execution, jwt_token, tool_ids: tool_ids_param, generation_settings:
|
|
98
|
+
@assistant_message = @chat.add_assistant_response(@prompt_execution, jwt_token, tool_ids: tool_ids_param, generation_settings: generation_settings)
|
|
86
99
|
# Generate chat title from the user's prompt (only if title is not yet set)
|
|
87
100
|
@chat.generate_title(params[:message], jwt_token)
|
|
88
101
|
rescue StandardError => e
|
|
@@ -149,18 +162,26 @@ class ChatsController < ApplicationController
|
|
|
149
162
|
def update
|
|
150
163
|
jwt_token = current_user.id_token if user_signed_in?
|
|
151
164
|
|
|
152
|
-
#
|
|
153
|
-
@chat =
|
|
154
|
-
|
|
155
|
-
current_user,
|
|
156
|
-
llm_uuid: params[:api_key_uuid],
|
|
157
|
-
model: params[:model]
|
|
158
|
-
)
|
|
165
|
+
# Use the chat identified by the URL, not the session
|
|
166
|
+
@chat = current_user.chats.find(params[:id])
|
|
167
|
+
session[:chat_id] = @chat.id
|
|
159
168
|
@messages = @chat&.ordered_messages || []
|
|
160
169
|
# initialize history for the chat
|
|
161
170
|
initialize_history @chat&.ordered_by_descending_prompt_executions
|
|
162
171
|
|
|
163
172
|
if params[:message].present?
|
|
173
|
+
# Validate generation settings before proceeding
|
|
174
|
+
begin
|
|
175
|
+
generation_settings = generation_settings_param
|
|
176
|
+
rescue InvalidGenerationSettingsError => e
|
|
177
|
+
@error_message = e.message
|
|
178
|
+
respond_to do |format|
|
|
179
|
+
format.turbo_stream
|
|
180
|
+
format.html { redirect_to chat_path(@chat), alert: e.message }
|
|
181
|
+
end
|
|
182
|
+
return
|
|
183
|
+
end
|
|
184
|
+
|
|
164
185
|
# Add user message (will be rendered via turbo stream)
|
|
165
186
|
@prompt_execution, @user_message = @chat.add_user_message(params[:message],
|
|
166
187
|
params[:model],
|
|
@@ -172,7 +193,7 @@ class ChatsController < ApplicationController
|
|
|
172
193
|
|
|
173
194
|
# Send to LLM and get assistant response
|
|
174
195
|
begin
|
|
175
|
-
@assistant_message = @chat.add_assistant_response(@prompt_execution, jwt_token, tool_ids: tool_ids_param, generation_settings:
|
|
196
|
+
@assistant_message = @chat.add_assistant_response(@prompt_execution, jwt_token, tool_ids: tool_ids_param, generation_settings: generation_settings)
|
|
176
197
|
rescue StandardError => e
|
|
177
198
|
Rails.logger.error "Error in chat response: #{e.class} - #{e.message}\n#{e.backtrace&.join("\n")}"
|
|
178
199
|
@error_message = "An error occurred while getting the response. Please try again."
|
|
@@ -192,13 +213,27 @@ class ChatsController < ApplicationController
|
|
|
192
213
|
params[:tool_ids].presence || []
|
|
193
214
|
end
|
|
194
215
|
|
|
216
|
+
ALLOWED_GENERATION_KEYS = %w[temperature top_k top_p max_tokens repeat_penalty].freeze
|
|
217
|
+
|
|
218
|
+
class InvalidGenerationSettingsError < StandardError; end
|
|
219
|
+
|
|
195
220
|
def generation_settings_param
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
settings
|
|
200
|
-
|
|
201
|
-
settings
|
|
202
|
-
|
|
221
|
+
return {} if params[:generation_settings_json].blank?
|
|
222
|
+
|
|
223
|
+
parsed = JSON.parse(params[:generation_settings_json])
|
|
224
|
+
raise InvalidGenerationSettingsError, "Generation settings must be a JSON object" unless parsed.is_a?(Hash)
|
|
225
|
+
|
|
226
|
+
settings = parsed.slice(*ALLOWED_GENERATION_KEYS)
|
|
227
|
+
invalid_keys = parsed.keys - ALLOWED_GENERATION_KEYS
|
|
228
|
+
raise InvalidGenerationSettingsError, "Unknown keys: #{invalid_keys.join(', ')}" if invalid_keys.any?
|
|
229
|
+
|
|
230
|
+
non_numeric = settings.reject { |_k, v| v.is_a?(Numeric) }
|
|
231
|
+
if non_numeric.any?
|
|
232
|
+
raise InvalidGenerationSettingsError, "Values must be numeric: #{non_numeric.keys.join(', ')}"
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
settings.symbolize_keys
|
|
236
|
+
rescue JSON::ParserError => e
|
|
237
|
+
raise InvalidGenerationSettingsError, "Invalid JSON: #{e.message}"
|
|
203
238
|
end
|
|
204
239
|
end
|
data/lib/generators/llm_meta_client/scaffold/templates/app/controllers/prompts_controller.rb
CHANGED
|
@@ -4,8 +4,8 @@ class PromptsController < ApplicationController
|
|
|
4
4
|
skip_before_action :authenticate_user!, raise: false
|
|
5
5
|
|
|
6
6
|
def show
|
|
7
|
-
@prompt_execution = PromptNavigator::PromptExecution.
|
|
8
|
-
@message = @prompt_execution.
|
|
7
|
+
@prompt_execution = PromptNavigator::PromptExecution.find_by!(execution_id: params[:id])
|
|
8
|
+
@message = Message.where(prompt_navigator_prompt_execution: @prompt_execution).order(:created_at).first
|
|
9
9
|
@chat = @message.chat
|
|
10
10
|
@messages = @chat.ordered_messages
|
|
11
11
|
|
|
@@ -25,6 +25,9 @@ class PromptsController < ApplicationController
|
|
|
25
25
|
# Set active UUID for history sidebar highlighting
|
|
26
26
|
set_active_message_uuid(@prompt_execution.execution_id)
|
|
27
27
|
|
|
28
|
+
# Set branch_from_uuid so the form knows which message to branch from
|
|
29
|
+
@branch_from_uuid = @prompt_execution.execution_id
|
|
30
|
+
|
|
28
31
|
render "chats/edit"
|
|
29
32
|
rescue StandardError => e
|
|
30
33
|
Rails.logger.error "Error in PromptsController#show_by_uuid: #{e.class} - #{e.message}\n#{e.backtrace&.join("\n")}"
|
|
@@ -6,15 +6,7 @@ export default class extends Controller {
|
|
|
6
6
|
"toggleButton",
|
|
7
7
|
"toggleIcon",
|
|
8
8
|
"panel",
|
|
9
|
-
"
|
|
10
|
-
"temperatureValue",
|
|
11
|
-
"topKRange",
|
|
12
|
-
"topKValue",
|
|
13
|
-
"topPRange",
|
|
14
|
-
"topPValue",
|
|
15
|
-
"maxTokensInput",
|
|
16
|
-
"repeatPenaltyRange",
|
|
17
|
-
"repeatPenaltyValue",
|
|
9
|
+
"jsonInput",
|
|
18
10
|
]
|
|
19
11
|
|
|
20
12
|
connect() {
|
|
@@ -32,20 +24,4 @@ export default class extends Controller {
|
|
|
32
24
|
this.toggleIconTarget.classList.toggle("bi-chevron-up", this.expanded)
|
|
33
25
|
}
|
|
34
26
|
}
|
|
35
|
-
|
|
36
|
-
updateTemperature() {
|
|
37
|
-
this.temperatureValueTarget.textContent = this.temperatureRangeTarget.value
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
updateTopK() {
|
|
41
|
-
this.topKValueTarget.textContent = this.topKRangeTarget.value
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
updateTopP() {
|
|
45
|
-
this.topPValueTarget.textContent = this.topPRangeTarget.value
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
updateRepeatPenalty() {
|
|
49
|
-
this.repeatPenaltyValueTarget.textContent = this.repeatPenaltyRangeTarget.value
|
|
50
|
-
}
|
|
51
27
|
}
|
|
@@ -15,13 +15,11 @@ class Chat < ApplicationRecord
|
|
|
15
15
|
chat = find_by_session_chat_id(session, current_user)
|
|
16
16
|
return chat if llm_uuid.nil? || model.nil?
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
chat
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
model: model
|
|
24
|
-
)
|
|
18
|
+
if chat.present?
|
|
19
|
+
# Update LLM/model on existing chat if changed
|
|
20
|
+
chat.update!(llm_uuid: llm_uuid, model: model) if chat.needs_reset?(llm_uuid, model)
|
|
21
|
+
else
|
|
22
|
+
chat = create!(user: current_user, llm_uuid: llm_uuid, model: model)
|
|
25
23
|
session[:chat_id] = chat.id
|
|
26
24
|
end
|
|
27
25
|
|
|
@@ -49,13 +47,17 @@ class Chat < ApplicationRecord
|
|
|
49
47
|
end
|
|
50
48
|
|
|
51
49
|
# Add a user message to the chat
|
|
52
|
-
def add_user_message(message, model,
|
|
53
|
-
|
|
50
|
+
def add_user_message(message, model, branch_from_execution_id = nil)
|
|
51
|
+
previous_id = if branch_from_execution_id.present?
|
|
52
|
+
PromptNavigator::PromptExecution.find_by(execution_id: branch_from_execution_id)&.id
|
|
53
|
+
else
|
|
54
|
+
messages.where(role: "user").order(:created_at).last&.prompt_navigator_prompt_execution_id
|
|
55
|
+
end
|
|
54
56
|
prompt_execution = PromptNavigator::PromptExecution.create!(
|
|
55
57
|
prompt: message,
|
|
56
58
|
model: model,
|
|
57
59
|
configuration: "",
|
|
58
|
-
previous_id:
|
|
60
|
+
previous_id: previous_id
|
|
59
61
|
)
|
|
60
62
|
|
|
61
63
|
new_message = messages.create!(
|
data/lib/generators/llm_meta_client/scaffold/templates/app/views/chats/create.turbo_stream.erb
CHANGED
|
@@ -33,12 +33,12 @@
|
|
|
33
33
|
<%% # Update history sidebar - replace entire content to ensure update %>
|
|
34
34
|
<%% if @prompt_execution %>
|
|
35
35
|
<%%= turbo_stream.replace "history-sidebar" do %>
|
|
36
|
-
<div id="history-
|
|
36
|
+
<div id="history-sidebar">
|
|
37
37
|
<h2>History</h2>
|
|
38
38
|
<div class="history-stack" id="history-stack" data-controller="history">
|
|
39
39
|
<%%= render 'prompt_navigator/history_card', locals: {
|
|
40
40
|
ann: @prompt_execution,
|
|
41
|
-
next_ann:
|
|
41
|
+
next_ann: (@chat&.ordered_by_descending_prompt_executions || [])[1],
|
|
42
42
|
is_active: @prompt_execution.execution_id == @active_message_uuid,
|
|
43
43
|
card_path: ->(uuid) { prompt_path(uuid) }
|
|
44
44
|
} %>
|
|
@@ -60,25 +60,27 @@
|
|
|
60
60
|
<turbo-stream action="after" target="messages-list">
|
|
61
61
|
<template>
|
|
62
62
|
<script>
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
messageInput
|
|
67
|
-
|
|
68
|
-
|
|
63
|
+
(function() {
|
|
64
|
+
// Clear and refocus message input
|
|
65
|
+
const messageInput = document.getElementById('message-input');
|
|
66
|
+
if (messageInput) {
|
|
67
|
+
messageInput.value = '';
|
|
68
|
+
messageInput.focus();
|
|
69
|
+
}
|
|
69
70
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
71
|
+
// Update submit button state
|
|
72
|
+
const form = document.querySelector('[data-controller="chats-form"]');
|
|
73
|
+
if (form && messageInput) {
|
|
74
|
+
const event = new Event('input', { bubbles: true });
|
|
75
|
+
messageInput.dispatchEvent(event);
|
|
76
|
+
}
|
|
76
77
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
78
|
+
// Scroll to bottom
|
|
79
|
+
const chatMessages = document.getElementById('chat-messages');
|
|
80
|
+
if (chatMessages) {
|
|
81
|
+
chatMessages.scrollTop = chatMessages.scrollHeight;
|
|
82
|
+
}
|
|
83
|
+
})();
|
|
82
84
|
</script>
|
|
83
85
|
</template>
|
|
84
86
|
</turbo-stream>
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
id: "message-input",
|
|
31
31
|
data: { "chats-form-target": "prompt", action: "input->chats-form#updateSubmitButton" } %>
|
|
32
32
|
</div>
|
|
33
|
-
<%%= f.hidden_field :branch_from_uuid, value: params.dig(:chat, :branch_from_uuid) %>
|
|
33
|
+
<%%= f.hidden_field :branch_from_uuid, value: @branch_from_uuid || params.dig(:chat, :branch_from_uuid) %>
|
|
34
34
|
<div class="button-wrapper">
|
|
35
35
|
<%%= f.button type: "submit",
|
|
36
36
|
class: "send-button",
|
|
@@ -47,16 +47,18 @@
|
|
|
47
47
|
|
|
48
48
|
|
|
49
49
|
<script>
|
|
50
|
-
//
|
|
51
|
-
|
|
50
|
+
// Remove previous listener to prevent duplicates across Turbo navigations
|
|
51
|
+
if (window._scrollChatMessages) {
|
|
52
|
+
document.removeEventListener('turbo:load', window._scrollChatMessages);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
window._scrollChatMessages = function() {
|
|
52
56
|
const chatMessages = document.getElementById('chat-messages');
|
|
53
57
|
if (chatMessages) {
|
|
54
58
|
<%% if @target_message_id.present? %>
|
|
55
|
-
// Scroll to target message
|
|
56
59
|
const targetMessage = document.getElementById('message-<%%= @target_message_id %>');
|
|
57
60
|
if (targetMessage) {
|
|
58
61
|
targetMessage.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
59
|
-
// Highlight the target message
|
|
60
62
|
targetMessage.style.backgroundColor = '#fef3c7';
|
|
61
63
|
targetMessage.style.border = '2px solid #fbbf24';
|
|
62
64
|
setTimeout(() => {
|
|
@@ -69,10 +71,8 @@
|
|
|
69
71
|
chatMessages.scrollTop = chatMessages.scrollHeight;
|
|
70
72
|
<%% end %>
|
|
71
73
|
}
|
|
72
|
-
}
|
|
74
|
+
};
|
|
73
75
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
document.addEventListener('DOMContentLoaded', scrollChatMessages);
|
|
77
|
-
document.addEventListener('turbo:load', scrollChatMessages);
|
|
76
|
+
document.addEventListener('DOMContentLoaded', window._scrollChatMessages);
|
|
77
|
+
document.addEventListener('turbo:load', window._scrollChatMessages);
|
|
78
78
|
</script>
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
id: "message-input",
|
|
31
31
|
data: { "chats-form-target": "prompt", action: "input->chats-form#updateSubmitButton" } %>
|
|
32
32
|
</div>
|
|
33
|
-
<%%= f.hidden_field :branch_from_uuid, value: params.dig(:chat, :branch_from_uuid) %>
|
|
33
|
+
<%%= f.hidden_field :branch_from_uuid, value: @branch_from_uuid || params.dig(:chat, :branch_from_uuid) %>
|
|
34
34
|
<div class="button-wrapper">
|
|
35
35
|
<%%= f.button type: "submit",
|
|
36
36
|
class: "send-button",
|
data/lib/generators/llm_meta_client/scaffold/templates/app/views/chats/update.turbo_stream.erb
CHANGED
|
@@ -26,13 +26,12 @@
|
|
|
26
26
|
<%% # Update history sidebar - replace entire content to ensure update %>
|
|
27
27
|
<%% if @prompt_execution %>
|
|
28
28
|
<%%= turbo_stream.replace "history-sidebar" do %>
|
|
29
|
-
|
|
30
|
-
<div id="history-content">
|
|
29
|
+
<div id="history-sidebar">
|
|
31
30
|
<h2>History</h2>
|
|
32
31
|
<div class="history-stack" id="history-stack" data-controller="history">
|
|
33
32
|
<%%= render 'prompt_navigator/history_card', locals: {
|
|
34
33
|
ann: @prompt_execution,
|
|
35
|
-
next_ann:
|
|
34
|
+
next_ann: (@chat&.ordered_by_descending_prompt_executions || [])[1],
|
|
36
35
|
is_active: @prompt_execution.execution_id == @active_message_uuid,
|
|
37
36
|
card_path: ->(uuid) { prompt_path(uuid) }
|
|
38
37
|
} %>
|
|
@@ -54,25 +53,27 @@
|
|
|
54
53
|
<turbo-stream action="after" target="messages-list">
|
|
55
54
|
<template>
|
|
56
55
|
<script>
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
messageInput
|
|
61
|
-
|
|
62
|
-
|
|
56
|
+
(function() {
|
|
57
|
+
// Clear and refocus message input
|
|
58
|
+
const messageInput = document.getElementById('message-input');
|
|
59
|
+
if (messageInput) {
|
|
60
|
+
messageInput.value = '';
|
|
61
|
+
messageInput.focus();
|
|
62
|
+
}
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
64
|
+
// Update submit button state
|
|
65
|
+
const form = document.querySelector('[data-controller="chats-form"]');
|
|
66
|
+
if (form && messageInput) {
|
|
67
|
+
const event = new Event('input', { bubbles: true });
|
|
68
|
+
messageInput.dispatchEvent(event);
|
|
69
|
+
}
|
|
70
70
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
71
|
+
// Scroll to bottom
|
|
72
|
+
const chatMessages = document.getElementById('chat-messages');
|
|
73
|
+
if (chatMessages) {
|
|
74
|
+
chatMessages.scrollTop = chatMessages.scrollHeight;
|
|
75
|
+
}
|
|
76
|
+
})();
|
|
76
77
|
</script>
|
|
77
78
|
</template>
|
|
78
79
|
</turbo-stream>
|
|
@@ -13,75 +13,16 @@
|
|
|
13
13
|
</button>
|
|
14
14
|
</div>
|
|
15
15
|
<div class="generation-settings-panel" data-<%%= stimulus_controller %>-target="panel" style="display: none;">
|
|
16
|
-
<
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
<span>0 (deterministic)</span>
|
|
27
|
-
<span>2 (creative)</span>
|
|
28
|
-
</div>
|
|
29
|
-
</div>
|
|
30
|
-
|
|
31
|
-
<div class="generation-setting-item">
|
|
32
|
-
<label for="top_k">
|
|
33
|
-
Top-K
|
|
34
|
-
<span class="setting-value" data-<%%= stimulus_controller %>-target="topKValue">40</span>
|
|
35
|
-
</label>
|
|
36
|
-
<input type="range" name="top_k" id="top_k"
|
|
37
|
-
min="1" max="100" step="1" value="40"
|
|
38
|
-
data-<%%= stimulus_controller %>-target="topKRange"
|
|
39
|
-
data-action="input-><%%= stimulus_controller %>#updateTopK">
|
|
40
|
-
<div class="setting-range-labels">
|
|
41
|
-
<span>1 (focused)</span>
|
|
42
|
-
<span>100 (diverse)</span>
|
|
43
|
-
</div>
|
|
44
|
-
</div>
|
|
45
|
-
|
|
46
|
-
<div class="generation-setting-item">
|
|
47
|
-
<label for="top_p">
|
|
48
|
-
Top-P
|
|
49
|
-
<span class="setting-value" data-<%%= stimulus_controller %>-target="topPValue">0.9</span>
|
|
50
|
-
</label>
|
|
51
|
-
<input type="range" name="top_p" id="top_p"
|
|
52
|
-
min="0" max="1" step="0.05" value="0.9"
|
|
53
|
-
data-<%%= stimulus_controller %>-target="topPRange"
|
|
54
|
-
data-action="input-><%%= stimulus_controller %>#updateTopP">
|
|
55
|
-
<div class="setting-range-labels">
|
|
56
|
-
<span>0 (narrow)</span>
|
|
57
|
-
<span>1 (broad)</span>
|
|
58
|
-
</div>
|
|
59
|
-
</div>
|
|
60
|
-
|
|
61
|
-
<div class="generation-setting-item">
|
|
62
|
-
<label for="max_tokens">
|
|
63
|
-
Max Tokens
|
|
64
|
-
</label>
|
|
65
|
-
<input type="number" name="max_tokens" id="max_tokens"
|
|
66
|
-
min="1" max="128000" step="1" value=""
|
|
67
|
-
placeholder="Default (model-dependent)"
|
|
68
|
-
class="max-tokens-input"
|
|
69
|
-
data-<%%= stimulus_controller %>-target="maxTokensInput">
|
|
70
|
-
</div>
|
|
71
|
-
|
|
72
|
-
<div class="generation-setting-item">
|
|
73
|
-
<label for="repeat_penalty">
|
|
74
|
-
Repeat Penalty
|
|
75
|
-
<span class="setting-value" data-<%%= stimulus_controller %>-target="repeatPenaltyValue">1.1</span>
|
|
76
|
-
</label>
|
|
77
|
-
<input type="range" name="repeat_penalty" id="repeat_penalty"
|
|
78
|
-
min="1" max="2" step="0.05" value="1.1"
|
|
79
|
-
data-<%%= stimulus_controller %>-target="repeatPenaltyRange"
|
|
80
|
-
data-action="input-><%%= stimulus_controller %>#updateRepeatPenalty">
|
|
81
|
-
<div class="setting-range-labels">
|
|
82
|
-
<span>1.0 (no penalty)</span>
|
|
83
|
-
<span>2.0 (strong)</span>
|
|
84
|
-
</div>
|
|
16
|
+
<label for="generation_settings_json" class="generation-settings-label">
|
|
17
|
+
JSON format
|
|
18
|
+
</label>
|
|
19
|
+
<textarea name="generation_settings_json" id="generation_settings_json"
|
|
20
|
+
class="generation-settings-json-input"
|
|
21
|
+
rows="8"
|
|
22
|
+
placeholder='{"temperature": 0.7, "top_k": 40, "top_p": 0.9, "max_tokens": 4096, "repeat_penalty": 1.1}'
|
|
23
|
+
data-<%%= stimulus_controller %>-target="jsonInput"></textarea>
|
|
24
|
+
<div class="generation-settings-hint">
|
|
25
|
+
Available keys: temperature, top_k, top_p, max_tokens, repeat_penalty
|
|
85
26
|
</div>
|
|
86
27
|
</div>
|
|
87
28
|
</div>
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: llm_meta_client
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 1.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- dhq_boiler
|
|
@@ -49,28 +49,28 @@ dependencies:
|
|
|
49
49
|
requirements:
|
|
50
50
|
- - "~>"
|
|
51
51
|
- !ruby/object:Gem::Version
|
|
52
|
-
version: '0
|
|
52
|
+
version: '1.0'
|
|
53
53
|
type: :runtime
|
|
54
54
|
prerelease: false
|
|
55
55
|
version_requirements: !ruby/object:Gem::Requirement
|
|
56
56
|
requirements:
|
|
57
57
|
- - "~>"
|
|
58
58
|
- !ruby/object:Gem::Version
|
|
59
|
-
version: '0
|
|
59
|
+
version: '1.0'
|
|
60
60
|
- !ruby/object:Gem::Dependency
|
|
61
61
|
name: chat_manager
|
|
62
62
|
requirement: !ruby/object:Gem::Requirement
|
|
63
63
|
requirements:
|
|
64
64
|
- - "~>"
|
|
65
65
|
- !ruby/object:Gem::Version
|
|
66
|
-
version: '0
|
|
66
|
+
version: '1.0'
|
|
67
67
|
type: :runtime
|
|
68
68
|
prerelease: false
|
|
69
69
|
version_requirements: !ruby/object:Gem::Requirement
|
|
70
70
|
requirements:
|
|
71
71
|
- - "~>"
|
|
72
72
|
- !ruby/object:Gem::Version
|
|
73
|
-
version: '0
|
|
73
|
+
version: '1.0'
|
|
74
74
|
description: llm_meta_client provides a Rails Engine with scaffold and authentication
|
|
75
75
|
generators for building LLM-powered chat applications. Supports OpenAI, Anthropic,
|
|
76
76
|
Google, and Ollama providers.
|