llm_meta_client 0.4.0 → 0.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 620e720960f5e05d563885fa2e00900311d6a7b4543fa13f9d3c476515edd40c
4
- data.tar.gz: 33981d0657e520260b569873c5547e3279eba4325f88a812d8bf28d18941ac88
3
+ metadata.gz: 60116965474267f22c077da1849611c0b6c4490a876a98deebfa243d2f9aef49
4
+ data.tar.gz: 748fdc1631edcb65c7792b1dc13a3ae8d0625422c05d4e4a47732f9c8167a7d9
5
5
  SHA512:
6
- metadata.gz: a557328075624b4ff7d17eda2ed181e178f5dfda39a23a9f87029141e04b22b78e6f648f81d751f6c92add2f0b54462b28c443bce2d4cf6fcc90f0dee5e1f175
7
- data.tar.gz: f5b8ecaeec9af8153d8bbbed4d6056ba5213d267fba01381088d1f53bee80d6f1525d04992b79325476da5ce664976da9ab831844b02828e6b2db9279eb02ef0
6
+ metadata.gz: c87684a604a914fe6097a1277948d009530f83a07ec7991aa5b6caa92ce8d2eb9568b4a55b648ba2b50f12e06bb9df95e31333bd211a56289cad4c3c3c8ddc33
7
+ data.tar.gz: 6ba2df271d37e40be53981e40c8c416239318872118019da88bdd35a845607d28e95d9f33e53328442b85f309448e30e6d7b8a874dcbd68996760be61ea97cb3
data/CHANGELOG.md CHANGED
@@ -5,6 +5,16 @@ 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
+ ## [0.5.0] - 2026-03-17
9
+
10
+ ### Added
11
+
12
+ - Generation settings support:
13
+ - `generation_settings` parameter in `ServerQuery` API layer for configuring LLM generation parameters
14
+ - `generation_settings` threading through `Chat` model
15
+ - `generation_settings` parameter extraction in `ChatsController`
16
+ - Generation settings UI components for configuring parameters in chat forms
17
+
8
18
  ## [0.4.0] - 2026-03-11
9
19
 
10
20
  ### Added
@@ -0,0 +1,90 @@
1
+ /* Generation Settings */
2
+ .generation-settings-field {
3
+ margin-bottom: 10px;
4
+ }
5
+
6
+ .generation-settings-toggle-button {
7
+ display: flex;
8
+ align-items: center;
9
+ gap: 6px;
10
+ background: none;
11
+ border: 1px solid #d1d5db;
12
+ border-radius: 6px;
13
+ padding: 6px 12px;
14
+ font-size: 13px;
15
+ font-weight: 600;
16
+ color: #374151;
17
+ cursor: pointer;
18
+ transition: border-color 0.2s, background-color 0.2s;
19
+
20
+ &:hover {
21
+ background-color: #f9fafb;
22
+ border-color: #9ca3af;
23
+ }
24
+ }
25
+
26
+ .generation-settings-panel {
27
+ margin-top: 8px;
28
+ border: 1px solid #e5e7eb;
29
+ border-radius: 8px;
30
+ background-color: #f9fafb;
31
+ padding: 12px 16px;
32
+ }
33
+
34
+ .generation-setting-item {
35
+ margin-bottom: 14px;
36
+
37
+ &:last-child {
38
+ margin-bottom: 0;
39
+ }
40
+
41
+ label {
42
+ display: flex;
43
+ align-items: center;
44
+ gap: 8px;
45
+ font-size: 13px;
46
+ font-weight: 600;
47
+ color: #374151;
48
+ margin-bottom: 4px;
49
+ }
50
+
51
+ input[type="range"] {
52
+ width: 100%;
53
+ accent-color: #3b82f6;
54
+ cursor: pointer;
55
+ }
56
+
57
+ .max-tokens-input {
58
+ width: 100%;
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
+ }
75
+ }
76
+ }
77
+
78
+ .setting-value {
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;
87
+ font-size: 11px;
88
+ color: #9ca3af;
89
+ margin-top: 2px;
90
+ }
@@ -34,6 +34,7 @@ module LlmMetaClient
34
34
  template "app/views/shared/_api_key_field.html.erb"
35
35
  template "app/views/shared/_model_field.html.erb"
36
36
  template "app/views/shared/_tool_selector_field.html.erb"
37
+ template "app/views/shared/_generation_settings_field.html.erb"
37
38
  template "app/views/layouts/application.html.erb"
38
39
  template "app/views/layouts/_header.html.erb"
39
40
  template "app/views/layouts/_sidebar.html.erb"
@@ -44,6 +45,7 @@ module LlmMetaClient
44
45
  template "app/javascript/controllers/chats_form_controller.js"
45
46
  template "app/javascript/controllers/chat_title_edit_controller.js"
46
47
  template "app/javascript/controllers/tool_selector_controller.js"
48
+ template "app/javascript/controllers/generation_settings_controller.js"
47
49
  copy_file "app/javascript/popover.js"
48
50
  end
49
51
 
@@ -82,7 +82,7 @@ class ChatsController < ApplicationController
82
82
 
83
83
  # Send to LLM and get assistant response
84
84
  begin
85
- @assistant_message = @chat.add_assistant_response(@prompt_execution, jwt_token, tool_ids: tool_ids_param)
85
+ @assistant_message = @chat.add_assistant_response(@prompt_execution, jwt_token, tool_ids: tool_ids_param, generation_settings: generation_settings_param)
86
86
  # Generate chat title from the user's prompt (only if title is not yet set)
87
87
  @chat.generate_title(params[:message], jwt_token)
88
88
  rescue StandardError => e
@@ -172,7 +172,7 @@ class ChatsController < ApplicationController
172
172
 
173
173
  # Send to LLM and get assistant response
174
174
  begin
175
- @assistant_message = @chat.add_assistant_response(@prompt_execution, jwt_token, tool_ids: tool_ids_param)
175
+ @assistant_message = @chat.add_assistant_response(@prompt_execution, jwt_token, tool_ids: tool_ids_param, generation_settings: generation_settings_param)
176
176
  rescue StandardError => e
177
177
  Rails.logger.error "Error in chat response: #{e.class} - #{e.message}\n#{e.backtrace&.join("\n")}"
178
178
  @error_message = "An error occurred while getting the response. Please try again."
@@ -191,4 +191,14 @@ class ChatsController < ApplicationController
191
191
  def tool_ids_param
192
192
  params[:tool_ids].presence || []
193
193
  end
194
+
195
+ def generation_settings_param
196
+ settings = {}
197
+ settings[:temperature] = params[:temperature].to_f if params[:temperature].present?
198
+ settings[:top_k] = params[:top_k].to_i if params[:top_k].present?
199
+ settings[:top_p] = params[:top_p].to_f if params[:top_p].present?
200
+ settings[:max_tokens] = params[:max_tokens].to_i if params[:max_tokens].present?
201
+ settings[:repeat_penalty] = params[:repeat_penalty].to_f if params[:repeat_penalty].present?
202
+ settings
203
+ end
194
204
  end
@@ -0,0 +1,51 @@
1
+ import { Controller } from "@hotwired/stimulus"
2
+
3
+ // Connects to data-controller="generation-settings"
4
+ export default class extends Controller {
5
+ static targets = [
6
+ "toggleButton",
7
+ "toggleIcon",
8
+ "panel",
9
+ "temperatureRange",
10
+ "temperatureValue",
11
+ "topKRange",
12
+ "topKValue",
13
+ "topPRange",
14
+ "topPValue",
15
+ "maxTokensInput",
16
+ "repeatPenaltyRange",
17
+ "repeatPenaltyValue",
18
+ ]
19
+
20
+ connect() {
21
+ this.expanded = false
22
+ }
23
+
24
+ toggle() {
25
+ if (!this.hasPanelTarget) return
26
+
27
+ this.expanded = !this.expanded
28
+ this.panelTarget.style.display = this.expanded ? "block" : "none"
29
+
30
+ if (this.hasToggleIconTarget) {
31
+ this.toggleIconTarget.classList.toggle("bi-chevron-down", !this.expanded)
32
+ this.toggleIconTarget.classList.toggle("bi-chevron-up", this.expanded)
33
+ }
34
+ }
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
+ }
@@ -67,8 +67,8 @@ class Chat < ApplicationRecord
67
67
  end
68
68
 
69
69
  # Add assistant response by sending to LLM
70
- def add_assistant_response(prompt_execution, jwt_token, tool_ids: [])
71
- response_content = send_to_llm(jwt_token, tool_ids: tool_ids)
70
+ def add_assistant_response(prompt_execution, jwt_token, tool_ids: [], generation_settings: {})
71
+ response_content = send_to_llm(jwt_token, tool_ids: tool_ids, generation_settings: generation_settings)
72
72
  prompt_execution.update!(
73
73
  llm_platform: llm_type(jwt_token),
74
74
  response: response_content
@@ -122,7 +122,7 @@ class Chat < ApplicationRecord
122
122
  end
123
123
 
124
124
  # Send messages to LLM and get response
125
- def send_to_llm(jwt_token, tool_ids: [])
125
+ def send_to_llm(jwt_token, tool_ids: [], generation_settings: {})
126
126
  # Get LLM options
127
127
  llm_options = LlmMetaClient::ServerResource.available_llm_options(jwt_token)
128
128
 
@@ -149,6 +149,6 @@ class Chat < ApplicationRecord
149
149
  summarized_context += "Additional prompt: Responses from the assistant must consist solely of the response body."
150
150
 
151
151
  # Send chat request using LlmMetaClient::ServerQuery
152
- LlmMetaClient::ServerQuery.new.call(jwt_token, llm_uuid, model, summarized_context, prompt, tool_ids: tool_ids)
152
+ LlmMetaClient::ServerQuery.new.call(jwt_token, llm_uuid, model, summarized_context, prompt, tool_ids: tool_ids, generation_settings: generation_settings)
153
153
  end
154
154
  end
@@ -18,6 +18,7 @@
18
18
  <%% if user_signed_in? %>
19
19
  <%%= render "shared/tool_selector_field", stimulus_controller: "tool-selector" %>
20
20
  <%% end %>
21
+ <%%= render "shared/generation_settings_field", stimulus_controller: "generation-settings" %>
21
22
  <%% end %>
22
23
  <div class="input-wrapper">
23
24
  <%%= f.text_area :message,
@@ -18,6 +18,7 @@
18
18
  <%% if user_signed_in? %>
19
19
  <%%= render "shared/tool_selector_field", stimulus_controller: "tool-selector" %>
20
20
  <%% end %>
21
+ <%%= render "shared/generation_settings_field", stimulus_controller: "generation-settings" %>
21
22
  <%% end %>
22
23
  <div class="input-wrapper">
23
24
  <%%= f.text_area :message,
@@ -0,0 +1,87 @@
1
+ <%%
2
+ stimulus_controller = local_assigns[:stimulus_controller] || "generation-settings"
3
+ %>
4
+ <div class="generation-settings-field" data-controller="<%%= stimulus_controller %>">
5
+ <div class="generation-settings-toggle">
6
+ <button type="button"
7
+ class="generation-settings-toggle-button"
8
+ data-<%%= stimulus_controller %>-target="toggleButton"
9
+ data-action="click-><%%= stimulus_controller %>#toggle">
10
+ <i class="bi bi-sliders"></i>
11
+ Generation Settings
12
+ <i class="bi bi-chevron-down toggle-icon" data-<%%= stimulus_controller %>-target="toggleIcon"></i>
13
+ </button>
14
+ </div>
15
+ <div class="generation-settings-panel" data-<%%= stimulus_controller %>-target="panel" style="display: none;">
16
+ <div class="generation-setting-item">
17
+ <label for="temperature">
18
+ Temperature
19
+ <span class="setting-value" data-<%%= stimulus_controller %>-target="temperatureValue">0.7</span>
20
+ </label>
21
+ <input type="range" name="temperature" id="temperature"
22
+ min="0" max="2" step="0.1" value="0.7"
23
+ data-<%%= stimulus_controller %>-target="temperatureRange"
24
+ data-action="input-><%%= stimulus_controller %>#updateTemperature">
25
+ <div class="setting-range-labels">
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>
85
+ </div>
86
+ </div>
87
+ </div>
@@ -1,11 +1,11 @@
1
1
  module LlmMetaClient
2
2
  class ServerQuery
3
- def call(id_token, api_key_uuid, model_id, context, user_content, tool_ids: [])
3
+ def call(id_token, api_key_uuid, model_id, context, user_content, tool_ids: [], generation_settings: {})
4
4
  debug_log "Context: #{context}"
5
5
  context_and_user_content = "Context:#{context}, User Prompt: #{user_content}"
6
6
  debug_log "Request to LLM: \n===>\n#{context_and_user_content}\n===>"
7
7
 
8
- response = request(api_key_uuid, id_token, model_id, context_and_user_content, tool_ids)
8
+ response = request(api_key_uuid, id_token, model_id, context_and_user_content, tool_ids, generation_settings)
9
9
 
10
10
  raise Exceptions::ServerError, "LLM server returned HTTP #{response.code}" unless response.success?
11
11
 
@@ -28,12 +28,13 @@ module LlmMetaClient
28
28
  Rails.logger.info(message) if Rails.env.development?
29
29
  end
30
30
 
31
- def request(api_key_uuid, id_token, model_id, user_content, tool_ids)
31
+ def request(api_key_uuid, id_token, model_id, user_content, tool_ids, generation_settings)
32
32
  headers = { "Content-Type" => "application/json" }
33
33
  headers["Authorization"] = "Bearer #{id_token}" if id_token.present?
34
34
 
35
35
  body = { prompt: user_content.to_s }
36
36
  body[:tool_ids] = tool_ids if tool_ids.present?
37
+ body[:generation_settings] = generation_settings if generation_settings.present?
37
38
 
38
39
  HTTParty.post(
39
40
  url(api_key_uuid, model_id),
@@ -1,3 +1,3 @@
1
1
  module LlmMetaClient
2
- VERSION = "0.4.0"
2
+ VERSION = "0.5.0"
3
3
  end
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.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - dhq_boiler
@@ -85,6 +85,7 @@ files:
85
85
  - README.md
86
86
  - Rakefile
87
87
  - app/assets/stylesheets/llm_meta_client/application.css
88
+ - app/assets/stylesheets/llm_meta_client/generation_settings.css
88
89
  - app/controllers/llm_meta_client/application_controller.rb
89
90
  - app/helpers/llm_meta_client/application_helper.rb
90
91
  - app/jobs/llm_meta_client/application_job.rb
@@ -106,6 +107,7 @@ files:
106
107
  - lib/generators/llm_meta_client/scaffold/templates/app/controllers/prompts_controller.rb
107
108
  - lib/generators/llm_meta_client/scaffold/templates/app/javascript/controllers/chat_title_edit_controller.js
108
109
  - lib/generators/llm_meta_client/scaffold/templates/app/javascript/controllers/chats_form_controller.js
110
+ - lib/generators/llm_meta_client/scaffold/templates/app/javascript/controllers/generation_settings_controller.js
109
111
  - lib/generators/llm_meta_client/scaffold/templates/app/javascript/controllers/llm_selector_controller.js
110
112
  - lib/generators/llm_meta_client/scaffold/templates/app/javascript/controllers/tool_selector_controller.js
111
113
  - lib/generators/llm_meta_client/scaffold/templates/app/javascript/popover.js
@@ -122,6 +124,7 @@ files:
122
124
  - lib/generators/llm_meta_client/scaffold/templates/app/views/layouts/application.html.erb
123
125
  - lib/generators/llm_meta_client/scaffold/templates/app/views/shared/_api_key_field.html.erb
124
126
  - lib/generators/llm_meta_client/scaffold/templates/app/views/shared/_family_field.html.erb
127
+ - lib/generators/llm_meta_client/scaffold/templates/app/views/shared/_generation_settings_field.html.erb
125
128
  - lib/generators/llm_meta_client/scaffold/templates/app/views/shared/_model_field.html.erb
126
129
  - lib/generators/llm_meta_client/scaffold/templates/app/views/shared/_tool_selector_field.html.erb
127
130
  - lib/generators/llm_meta_client/scaffold/templates/config/initializers/llm_service.rb