llm_meta_widget 0.7.1 → 0.7.3

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: d31a9eff714d507069705778f72423f8c95e6a89f54b3fc2ffcac0aaa5ee3f87
4
- data.tar.gz: 935d3e398c66fc17ffb7803f5aac9968c8440875b7ecc55b430d9679710ad5f4
3
+ metadata.gz: b2ed58b9f42be3c20c0b3cd3c7ca4bfa7742daf95d94af95efb499de84e3c55a
4
+ data.tar.gz: 6916c9639a124219b8637dfb985b96fff8eb6bc8390bbb57143b521003d60b19
5
5
  SHA512:
6
- metadata.gz: 648767e2345974f46bb27ef6512fbafb0c91308b62fcc978eed6d7df1282815842dc13b825ae739f97661b2ef8067d764efa2fcdeac0ed3d1145e3cbd7d93eb1
7
- data.tar.gz: b2f584221106fd4e96cabf612df56aafb84268d89aac07c5ebfcbb6f03c10d67196de2bf33e648e533c88e6992ef820c5b0fca60b3965250322e6ad15e511b6a
6
+ metadata.gz: ecef782468671eac0d28b2846cee99e21bf57c1bcdcd04027d7feb066d8c0b706d80f4357b61d37e2ea5307ebe9f8ace40ac6ace63e59a8c88a84b4123a6d327
7
+ data.tar.gz: fcd6bdcc159d1368e910a46109fb3a38688f528809b8e0fd4b4fbcb57c2d87e2795cbf497f967d49b9ade039b60d90fd7a558bf4e973d6c66a42648aa5c8b768
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Embeddable browser chat widget for the [llm_meta](https://github.com/pubannotation) ecosystem.
4
4
 
5
- Client-orchestrated: the widget fetches host-side action schemas + host-published `.well-known/mcp.json` manifests at boot, dispatches tool_calls locally (page-embedded actions) or directly to MCP endpoints (host-wide well-known), and consumes the meta-server's SSE `single_llm_calls` API. Ships one Rails helper + one view partial + a served orchestrator JS module.
5
+ Client-orchestrated: the widget fetches host-side action schemas + host-published `.well-known/mcp.json` manifests at boot, dispatches tool_calls locally (page-embedded actions) or directly to MCP endpoints (host-wide well-known), and consumes the meta-server's SSE `single_llm_calls` API. Ships as a custom element in one self-contained ES module — on npm for any host, and in a Rails gem whose helper serves the identical file.
6
6
 
7
7
  **No** Devise, DB migrations, ChatManager, or PromptNavigator. Adds only `rails >= 8.0` as a runtime dep — so hosts that haven't bumped to 8.1 can adopt it without a Rails upgrade.
8
8
 
@@ -32,7 +32,7 @@ step, no Node at runtime.
32
32
 
33
33
  ```ruby
34
34
  # Gemfile
35
- gem "llm_meta_widget", "~> 0.4"
35
+ gem "llm_meta_widget", "~> 0.7"
36
36
  ```
37
37
 
38
38
  ```
@@ -62,8 +62,8 @@ the role label turns into a spinning gear reading "Working…", and the reply
62
62
  streams in.
63
63
 
64
64
  If the panel opens but nothing comes back, it is almost always CORS. The hub
65
- must list your app's origin in its `config/initializers/cors.rb` for the
66
- `/api/*` resource, because the widget calls the hub **from the visitor's
65
+ must list your app's origin in its `CORS_ORIGINS` environment variable for
66
+ the `/api/*` resource, because the widget calls the hub **from the visitor's
67
67
  browser**, not from your server. Open the browser console: a blocked request
68
68
  says so explicitly.
69
69
 
@@ -248,7 +248,7 @@ By default the widget renders two pickers below the input textarea:
248
248
  - **Model dropdown** — populated from the hub's `GET /api/llms` (anon path returns Ollama-only, since the widget's LLM calls use `api_key_uuid: "ollama-local"`).
249
249
  - **Tool picker** — populated from the hub's `GET /api/mcp_servers` (anon path returns `public_to_anonymous: true` servers). Two-level UX: server bulk-toggle + individual tool checkboxes on expand.
250
250
 
251
- Both pickers require **CORS**: the meta-server must include the host's origin in its `config/initializers/cors.rb` allowlist (`/api/*` resource). Without CORS, the fetch is silently blocked and pickers stay empty.
251
+ Both pickers require **CORS**: the meta-server must allow the host's origin on its `/api/*` resource. That allowlist is an environment variable, not code — `CORS_ORIGINS`, a comma-separated list — so adding a host is a deployment change and a restart, with no commit and nothing about one deployment's hosts published in the repo. Without CORS the fetch is silently blocked and the pickers stay empty.
252
252
 
253
253
  To adjust picker behavior at the helper call site:
254
254
 
@@ -257,8 +257,8 @@ To adjust picker behavior at the helper call site:
257
257
  llm_url: "https://your-meta-server.example",
258
258
  tool_hub_url: "https://your-meta-server.example",
259
259
  model: "qwen3-6-35b-fast", # initial selection
260
- enable_model_picker: true, # false → hide picker, use fixed `model:`
261
- enable_tool_picker: true, # false → hide picker, no Class-1 tools
260
+ enable_model_picker: true, # false → omit picker, use fixed `model:`
261
+ enable_tool_picker: true, # false → omit picker, no Class-1 tools
262
262
  models: nil, # nil = all anon models; ["qwen3-6-35b-fast", …] = allowlist
263
263
  hub_tools: nil # nil = all anon-public MCPs; ["togomcp", …] = allowlist by server name
264
264
  ) %>
@@ -289,8 +289,8 @@ To lock the widget to the fixed `model:` prop and disable Class-1 tools entirely
289
289
  | `well_known_urls:` | `nil` | `nil` = auto-discover same-origin; explicit array = fetch those; `[]` = disable |
290
290
  | `greeting:` | `nil` | First thing a visitor sees when the panel opens, above the offered prompt templates. `nil` = a generic line |
291
291
  | `max_rounds:` | `3` | Cap on tool-call rounds per LLM turn. Page actions cost a round each since 0.4.0 — raise it for multi-step flows |
292
- | `enable_model_picker:` | `true` | Show model dropdown (Level-1) |
293
- | `enable_tool_picker:` | `true` | Show tool picker (Level-1) |
292
+ | `enable_model_picker:` | `true` | Show model dropdown (Level-1). `false` removes it from the DOM |
293
+ | `enable_tool_picker:` | `true` | Show tool picker (Level-1). `false` removes it from the DOM |
294
294
  | `models:` | `nil` | Model-name allowlist; `nil` = all anon-available |
295
295
  | `hub_tools:` | `nil` | MCP-server-name allowlist; `nil` = all anon-public |
296
296
 
@@ -404,6 +404,10 @@ it most, since they must already know your form to press it.
404
404
  three tool classes plus prompts and resources. Its
405
405
  `app/views/annotation/text_annotation.html.erb` and `app/controllers/mcp_controller.rb`
406
406
  are the fullest example available.
407
+ - **Shipping the element** — `docs/custom-element-distribution.md` is the design note
408
+ behind the custom-element move: the npm package and its CDN URL, why `main`,
409
+ `exports` and `sideEffects` are set the way they are, and why the gem and the npm
410
+ package must never drift in version.
407
411
  - **Issues and questions** — <https://github.com/jdkim/llm_meta_widget/issues>
408
412
 
409
413
  ## License
@@ -50,8 +50,6 @@ const MARKUP = `
50
50
  </div>
51
51
  <div class="lmw-messages"></div>
52
52
  <div class="lmw-input-container">
53
- host MCP server actually offers one, so a tools-only server leaves
54
- the widget exactly as it was. %>
55
53
  <div class="lmw-prompts" style="display:none"></div>
56
54
  <form class="lmw-form">
57
55
  <div class="lmw-input-wrapper">
@@ -62,25 +60,19 @@ const MARKUP = `
62
60
  </svg>
63
61
  </button>
64
62
  </div>
65
- <% if enable_model_picker || enable_tool_picker %>
66
- <div class="lmw-input-controls">
67
- <% if enable_model_picker %>
68
- <select class="lmw-model-picker" title="Select model" aria-label="Select model" style="display:none">
69
- </select>
70
- <% end %>
71
- <% if enable_tool_picker %>
72
- <details class="lmw-tools-picker">
73
- <summary>
74
- 🔧 Tools
75
- <span class="lmw-tools-count lmw-tools-count-zero">0</span>
76
- </summary>
77
- <div class="lmw-tools-list">
78
- <div class="lmw-tools-empty">Loading…</div>
79
- </div>
80
- </details>
81
- <% end %>
82
- </div>
83
- <% end %>
63
+ <div class="lmw-input-controls">
64
+ <select class="lmw-model-picker" title="Select model" aria-label="Select model" style="display:none">
65
+ </select>
66
+ <details class="lmw-tools-picker">
67
+ <summary>
68
+ 🔧 Tools
69
+ <span class="lmw-tools-count lmw-tools-count-zero">0</span>
70
+ </summary>
71
+ <div class="lmw-tools-list">
72
+ <div class="lmw-tools-empty">Loading…</div>
73
+ </div>
74
+ </details>
75
+ </div>
84
76
  </form>
85
77
  </div>
86
78
  </div>
@@ -126,6 +118,11 @@ if (typeof customElements !== "undefined" && !customElements.get("llm-meta-widge
126
118
  // Everything below is the panel's original logic, moved verbatim from the ERB
127
119
  // template. The only change is the sixteen bindings above it, which the
128
120
  // template used to inject.
121
+ // Removes a node if it is there. Used to honour the picker flags: see boot().
122
+ function dropNode(node) {
123
+ if (node && node.parentNode) node.parentNode.removeChild(node);
124
+ }
125
+
129
126
  function boot(cfg) {
130
127
  var LLM_BASE = cfg.LLM_BASE;
131
128
  var TOOL_HUB_BASE = cfg.TOOL_HUB_BASE;
@@ -151,6 +148,14 @@ function boot(cfg) {
151
148
  var inputEl = root.querySelector(".lmw-input");
152
149
  var clearBtn = root.querySelector(".lmw-clear");
153
150
  var hideBtn = root.querySelector(".lmw-hide");
151
+ // The ERB partial omitted these nodes entirely when their flag was false.
152
+ // One static template cannot express that, so prune instead — otherwise a
153
+ // disabled Tools picker still renders, and the lookups below never return
154
+ // the null the rest of boot() is written against.
155
+ if (!ENABLE_MODEL_PICKER) dropNode(root.querySelector(".lmw-model-picker"));
156
+ if (!ENABLE_TOOL_PICKER) dropNode(root.querySelector(".lmw-tools-picker"));
157
+ if (!ENABLE_MODEL_PICKER && !ENABLE_TOOL_PICKER) dropNode(root.querySelector(".lmw-input-controls"));
158
+
154
159
  var modelPicker = root.querySelector(".lmw-model-picker"); // null when disabled
155
160
  var toolsPicker = root.querySelector(".lmw-tools-picker"); // null when disabled
156
161
  var toolsListEl = root.querySelector(".lmw-tools-list"); // null when disabled
@@ -3564,8 +3564,6 @@ var MARKUP = `
3564
3564
  </div>
3565
3565
  <div class="lmw-messages"></div>
3566
3566
  <div class="lmw-input-container">
3567
- host MCP server actually offers one, so a tools-only server leaves
3568
- the widget exactly as it was. %>
3569
3567
  <div class="lmw-prompts" style="display:none"></div>
3570
3568
  <form class="lmw-form">
3571
3569
  <div class="lmw-input-wrapper">
@@ -3576,25 +3574,19 @@ var MARKUP = `
3576
3574
  </svg>
3577
3575
  </button>
3578
3576
  </div>
3579
- <% if enable_model_picker || enable_tool_picker %>
3580
- <div class="lmw-input-controls">
3581
- <% if enable_model_picker %>
3582
- <select class="lmw-model-picker" title="Select model" aria-label="Select model" style="display:none">
3583
- </select>
3584
- <% end %>
3585
- <% if enable_tool_picker %>
3586
- <details class="lmw-tools-picker">
3587
- <summary>
3588
- \u{1F527} Tools
3589
- <span class="lmw-tools-count lmw-tools-count-zero">0</span>
3590
- </summary>
3591
- <div class="lmw-tools-list">
3592
- <div class="lmw-tools-empty">Loading\u2026</div>
3593
- </div>
3594
- </details>
3595
- <% end %>
3596
- </div>
3597
- <% end %>
3577
+ <div class="lmw-input-controls">
3578
+ <select class="lmw-model-picker" title="Select model" aria-label="Select model" style="display:none">
3579
+ </select>
3580
+ <details class="lmw-tools-picker">
3581
+ <summary>
3582
+ \u{1F527} Tools
3583
+ <span class="lmw-tools-count lmw-tools-count-zero">0</span>
3584
+ </summary>
3585
+ <div class="lmw-tools-list">
3586
+ <div class="lmw-tools-empty">Loading\u2026</div>
3587
+ </div>
3588
+ </details>
3589
+ </div>
3598
3590
  </form>
3599
3591
  </div>
3600
3592
  </div>
@@ -3628,6 +3620,9 @@ var LlmMetaWidgetElement = class extends HTMLElement {
3628
3620
  if (typeof customElements !== "undefined" && !customElements.get("llm-meta-widget")) {
3629
3621
  customElements.define("llm-meta-widget", LlmMetaWidgetElement);
3630
3622
  }
3623
+ function dropNode(node) {
3624
+ if (node && node.parentNode) node.parentNode.removeChild(node);
3625
+ }
3631
3626
  function boot(cfg) {
3632
3627
  var LLM_BASE = cfg.LLM_BASE;
3633
3628
  var TOOL_HUB_BASE = cfg.TOOL_HUB_BASE;
@@ -3652,6 +3647,9 @@ function boot(cfg) {
3652
3647
  var inputEl = root.querySelector(".lmw-input");
3653
3648
  var clearBtn = root.querySelector(".lmw-clear");
3654
3649
  var hideBtn = root.querySelector(".lmw-hide");
3650
+ if (!ENABLE_MODEL_PICKER) dropNode(root.querySelector(".lmw-model-picker"));
3651
+ if (!ENABLE_TOOL_PICKER) dropNode(root.querySelector(".lmw-tools-picker"));
3652
+ if (!ENABLE_MODEL_PICKER && !ENABLE_TOOL_PICKER) dropNode(root.querySelector(".lmw-input-controls"));
3655
3653
  var modelPicker = root.querySelector(".lmw-model-picker");
3656
3654
  var toolsPicker = root.querySelector(".lmw-tools-picker");
3657
3655
  var toolsListEl = root.querySelector(".lmw-tools-list");
@@ -1,3 +1,3 @@
1
1
  module LlmMetaWidget
2
- VERSION = "0.7.1"
2
+ VERSION = "0.7.3"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: llm_meta_widget
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.1
4
+ version: 0.7.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - jdkim