llm_meta_client 1.0.1 → 1.0.2

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: 95bb1f421088cc0c287b4f79e961455a1a5275b860b372fb23523ddc8781d37a
4
- data.tar.gz: d4f92a150de0d9996d3ea6c54e69fe24989763f4bd359ce46a6b7ff69d36ecb7
3
+ metadata.gz: 5dde8e658e36a04b651c91bfcbd34d23b639449c4781529cd5c061e49e52cc53
4
+ data.tar.gz: 56d464e06df9afb9a92ef5dcafee8d806158fb1c333dc5a44800a1737095e68b
5
5
  SHA512:
6
- metadata.gz: 480919bad702190fec4166d8a2659121ff62b1880123e6db168d9a1c8a8b7f80f23ca175e2e6f4a31b96e6bde97fd0723d95caa71439845a5017c351c18e373e
7
- data.tar.gz: c2600201eccae124c9929eabcd8b7d755b7dbac4092cd5441c55d1caf79c839af8f0b41b49d7bf1c2e6a324e33c6bd1957d29a1ac3e837a7ab6924d727e8ae91
6
+ metadata.gz: 3a5442c238211a0432a26e54cd7923c1782d11178679a694cb2ac60ddb3bbd02365431bf0bbba61c9f505e65d1c3b8f60303a23dbb4752813fda580bfb997a4f
7
+ data.tar.gz: 955f0bb38816e24504041962e6b2a4cd9531ea89ad805c503624de927c9339383276c146dad1b346f64521f0cd3dfed985d2b5707676efdad27e998eb3441f60
data/CHANGELOG.md CHANGED
@@ -5,6 +5,23 @@ 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.2] - 2026-03-27
9
+
10
+ ### Added
11
+
12
+ - Add client-side validation for Generation Settings JSON
13
+
14
+ ## [1.0.1] - 2026-03-25
15
+
16
+ ### Fixed
17
+
18
+ - Fix: normalize Ollama llm_type in server resource options
19
+ - Fix: update branch_from_uuid after LLM response
20
+
21
+ ### Changed
22
+
23
+ - Refactor: move llm_uuid and model from Chat to PromptExecution
24
+
8
25
  ## [1.0.0] - 2026-03-25
9
26
 
10
27
  ### Changed
@@ -62,6 +62,21 @@
62
62
  }
63
63
  }
64
64
 
65
+ .generation-settings-json-input--invalid {
66
+ border-color: #ef4444;
67
+
68
+ &:focus {
69
+ border-color: #ef4444;
70
+ box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.1);
71
+ }
72
+ }
73
+
74
+ .generation-settings-error {
75
+ font-size: 12px;
76
+ color: #ef4444;
77
+ margin-top: 4px;
78
+ }
79
+
65
80
  .generation-settings-hint {
66
81
  font-size: 11px;
67
82
  color: #9ca3af;
@@ -13,7 +13,15 @@ export default class extends Controller {
13
13
  }
14
14
 
15
15
  // Handle form submission to show user message immediately
16
- submit() {
16
+ submit(event) {
17
+ // Check generation settings validity before submitting
18
+ const gsController = this.#generationSettingsController()
19
+ if (gsController && !gsController.isValid) {
20
+ event.preventDefault()
21
+ gsController.validate()
22
+ return
23
+ }
24
+
17
25
  // Don't prevent default - let Turbo handle the form submission
18
26
  // Just add the user message to the DOM immediately
19
27
  const messageContent = this.promptTarget.value.trim()
@@ -64,6 +72,12 @@ export default class extends Controller {
64
72
  }
65
73
  }
66
74
 
75
+ #generationSettingsController() {
76
+ const el = this.element.querySelector('[data-controller*="generation-settings"]')
77
+ if (!el) return null
78
+ return this.application.getControllerForElementAndIdentifier(el, "generation-settings")
79
+ }
80
+
67
81
  #canSubmit() {
68
82
  // Text field and prompt field can be validated using HTML5's required attribute,
69
83
  // so we delegate to checkValidity() to utilize standard validation
@@ -1,5 +1,7 @@
1
1
  import { Controller } from "@hotwired/stimulus"
2
2
 
3
+ const ALLOWED_KEYS = ["temperature", "top_k", "top_p", "max_tokens", "repeat_penalty"]
4
+
3
5
  // Connects to data-controller="generation-settings"
4
6
  export default class extends Controller {
5
7
  static targets = [
@@ -7,6 +9,7 @@ export default class extends Controller {
7
9
  "toggleIcon",
8
10
  "panel",
9
11
  "jsonInput",
12
+ "error",
10
13
  ]
11
14
 
12
15
  connect() {
@@ -24,4 +27,72 @@ export default class extends Controller {
24
27
  this.toggleIconTarget.classList.toggle("bi-chevron-up", this.expanded)
25
28
  }
26
29
  }
30
+
31
+ validate() {
32
+ const input = this.jsonInputTarget.value.trim()
33
+
34
+ if (!input) {
35
+ this.#clearError()
36
+ return
37
+ }
38
+
39
+ let parsed
40
+ try {
41
+ parsed = JSON.parse(input)
42
+ } catch (e) {
43
+ this.#showError("Invalid JSON syntax")
44
+ return
45
+ }
46
+
47
+ if (typeof parsed !== "object" || Array.isArray(parsed) || parsed === null) {
48
+ this.#showError("Must be a JSON object (e.g. {\"temperature\": 0.7})")
49
+ return
50
+ }
51
+
52
+ const unknownKeys = Object.keys(parsed).filter(k => !ALLOWED_KEYS.includes(k))
53
+ if (unknownKeys.length > 0) {
54
+ this.#showError(`Unknown keys: ${unknownKeys.join(", ")}`)
55
+ return
56
+ }
57
+
58
+ const nonNumeric = Object.entries(parsed).filter(([, v]) => typeof v !== "number")
59
+ if (nonNumeric.length > 0) {
60
+ this.#showError(`Values must be numeric: ${nonNumeric.map(([k]) => k).join(", ")}`)
61
+ return
62
+ }
63
+
64
+ this.#clearError()
65
+ }
66
+
67
+ get isValid() {
68
+ if (!this.hasJsonInputTarget) return true
69
+ const input = this.jsonInputTarget.value.trim()
70
+ if (!input) return true
71
+
72
+ try {
73
+ const parsed = JSON.parse(input)
74
+ if (typeof parsed !== "object" || Array.isArray(parsed) || parsed === null) return false
75
+ if (Object.keys(parsed).some(k => !ALLOWED_KEYS.includes(k))) return false
76
+ if (Object.values(parsed).some(v => typeof v !== "number")) return false
77
+ return true
78
+ } catch {
79
+ return false
80
+ }
81
+ }
82
+
83
+ #showError(message) {
84
+ if (this.hasErrorTarget) {
85
+ this.errorTarget.textContent = message
86
+ this.errorTarget.style.display = "block"
87
+ }
88
+ this.jsonInputTarget.classList.add("generation-settings-json-input--invalid")
89
+ }
90
+
91
+ #clearError() {
92
+ if (this.hasErrorTarget) {
93
+ this.errorTarget.textContent = ""
94
+ this.errorTarget.style.display = "none"
95
+ }
96
+ this.jsonInputTarget.classList.remove("generation-settings-json-input--invalid")
97
+ }
27
98
  }
@@ -20,7 +20,9 @@
20
20
  class="generation-settings-json-input"
21
21
  rows="8"
22
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>
23
+ data-<%%= stimulus_controller %>-target="jsonInput"
24
+ data-action="input-><%%= stimulus_controller %>#validate"></textarea>
25
+ <div class="generation-settings-error" data-<%%= stimulus_controller %>-target="error" style="display: none;"></div>
24
26
  <div class="generation-settings-hint">
25
27
  Available keys: temperature, top_k, top_p, max_tokens, repeat_penalty
26
28
  </div>
@@ -1,3 +1,3 @@
1
1
  module LlmMetaClient
2
- VERSION = "1.0.1"
2
+ VERSION = "1.0.2"
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: 1.0.1
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - dhq_boiler