@luckydraw/cumulus 1.0.11 → 1.0.13
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.
- package/CHANGELOG.md +17 -0
- package/dist/gateway/adapters/webchat.d.ts +1 -1
- package/dist/gateway/adapters/webchat.d.ts.map +1 -1
- package/dist/gateway/adapters/webchat.js +1 -0
- package/dist/gateway/adapters/webchat.js.map +1 -1
- package/dist/gateway/config.d.ts +75 -2
- package/dist/gateway/config.d.ts.map +1 -1
- package/dist/gateway/config.js +101 -5
- package/dist/gateway/config.js.map +1 -1
- package/dist/gateway/daemon.js +16 -0
- package/dist/gateway/daemon.js.map +1 -1
- package/dist/gateway/hooks.d.ts +1 -0
- package/dist/gateway/hooks.d.ts.map +1 -1
- package/dist/gateway/hooks.js +1 -0
- package/dist/gateway/hooks.js.map +1 -1
- package/dist/gateway/server.d.ts +2 -0
- package/dist/gateway/server.d.ts.map +1 -1
- package/dist/gateway/server.js +19 -1
- package/dist/gateway/server.js.map +1 -1
- package/dist/gateway/static/widget.js +356 -54
- package/dist/lib/agentic-loop.d.ts +10 -0
- package/dist/lib/agentic-loop.d.ts.map +1 -1
- package/dist/lib/agentic-loop.js +64 -1
- package/dist/lib/agentic-loop.js.map +1 -1
- package/dist/lib/content-store.d.ts +82 -3
- package/dist/lib/content-store.d.ts.map +1 -1
- package/dist/lib/content-store.js +159 -21
- package/dist/lib/content-store.js.map +1 -1
- package/dist/lib/embeddings.d.ts +14 -0
- package/dist/lib/embeddings.d.ts.map +1 -1
- package/dist/lib/embeddings.js +1 -1
- package/dist/lib/embeddings.js.map +1 -1
- package/dist/lib/gateway.d.ts +52 -2
- package/dist/lib/gateway.d.ts.map +1 -1
- package/dist/lib/gateway.js +120 -32
- package/dist/lib/gateway.js.map +1 -1
- package/dist/lib/huggingface-provider.d.ts +12 -0
- package/dist/lib/huggingface-provider.d.ts.map +1 -1
- package/dist/lib/huggingface-provider.js +45 -14
- package/dist/lib/huggingface-provider.js.map +1 -1
- package/package.json +1 -1
|
@@ -50,10 +50,40 @@
|
|
|
50
50
|
// Claude CLI variants and direct API models have different runtime contracts,
|
|
51
51
|
// but the UI presents them as one provider-facing catalog. Keep these transforms
|
|
52
52
|
// pure so their losslessness/default rules can be regression-tested directly.
|
|
53
|
+
// Task 147: a model entry's provider is `custom:<id>` when it runs on a
|
|
54
|
+
// user-registered OpenAI-compatible endpoint. The catalog carries that string
|
|
55
|
+
// verbatim so a round-trip can never silently re-home a model onto one of the
|
|
56
|
+
// three built-in providers.
|
|
57
|
+
function isCustomProvider(provider) {
|
|
58
|
+
return typeof provider === 'string' && provider.indexOf('custom:') === 0;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Derive a registry id from a display name. Must satisfy the SAME shape the
|
|
62
|
+
// gateway validates ([a-z0-9][a-z0-9_-]*), or the provider is dropped on save
|
|
63
|
+
// and its models silently fall back to the Claude path.
|
|
64
|
+
function slugifyProviderId(label) {
|
|
65
|
+
var slug = String(label || '')
|
|
66
|
+
.toLowerCase()
|
|
67
|
+
.replace(/[^a-z0-9_-]+/g, '-')
|
|
68
|
+
.replace(/^-+/, '')
|
|
69
|
+
.replace(/-+$/, '');
|
|
70
|
+
return slug;
|
|
71
|
+
}
|
|
72
|
+
|
|
53
73
|
function gatewayConfigToModelCatalog(config) {
|
|
54
74
|
config = config || {};
|
|
55
75
|
var topModels = Array.isArray(config.models) ? config.models : [];
|
|
56
76
|
var claudeModels = Array.isArray(config.claudeModels) ? config.claudeModels : [];
|
|
77
|
+
var customProviders = (Array.isArray(config.customProviders) ? config.customProviders : []).map(
|
|
78
|
+
function (provider) {
|
|
79
|
+
return {
|
|
80
|
+
id: String(provider.id || ''),
|
|
81
|
+
label: String(provider.label || provider.id || ''),
|
|
82
|
+
baseUrl: String(provider.baseUrl || ''),
|
|
83
|
+
maskedKey: String(provider.apiKey || ''),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
);
|
|
57
87
|
var sentinel = topModels.find(function (model) {
|
|
58
88
|
return model.id === 'claude' || model.provider === 'claude-cli';
|
|
59
89
|
}) || { id: 'claude', label: 'Claude (CLI)', provider: 'claude-cli' };
|
|
@@ -81,7 +111,11 @@
|
|
|
81
111
|
catalog.push({
|
|
82
112
|
id: model.id,
|
|
83
113
|
label: model.label || model.name || model.id,
|
|
84
|
-
provider: model.provider
|
|
114
|
+
provider: isCustomProvider(model.provider)
|
|
115
|
+
? model.provider
|
|
116
|
+
: model.provider === 'openai'
|
|
117
|
+
? 'openai'
|
|
118
|
+
: 'huggingface',
|
|
85
119
|
contextWindow: typeof model.contextWindow === 'number' ? model.contextWindow : undefined,
|
|
86
120
|
gatewayDefault: globalDefault === model.id,
|
|
87
121
|
providerDefault: false,
|
|
@@ -96,7 +130,11 @@
|
|
|
96
130
|
) {
|
|
97
131
|
catalog[0].gatewayDefault = true;
|
|
98
132
|
}
|
|
99
|
-
return {
|
|
133
|
+
return {
|
|
134
|
+
catalog: catalog,
|
|
135
|
+
sentinel: Object.assign({}, sentinel),
|
|
136
|
+
customProviders: customProviders,
|
|
137
|
+
};
|
|
100
138
|
}
|
|
101
139
|
|
|
102
140
|
function modelCatalogToGatewayConfig(state) {
|
|
@@ -137,7 +175,11 @@
|
|
|
137
175
|
var entry = Object.assign({}, model.source || {});
|
|
138
176
|
entry.id = model.id;
|
|
139
177
|
entry.label = model.label || model.id;
|
|
140
|
-
entry.provider = model.provider
|
|
178
|
+
entry.provider = isCustomProvider(model.provider)
|
|
179
|
+
? model.provider
|
|
180
|
+
: model.provider === 'openai'
|
|
181
|
+
? 'openai'
|
|
182
|
+
: 'huggingface';
|
|
141
183
|
delete entry.default;
|
|
142
184
|
if (typeof model.contextWindow === 'number' && model.contextWindow > 0) {
|
|
143
185
|
entry.contextWindow = model.contextWindow;
|
|
@@ -150,13 +192,38 @@
|
|
|
150
192
|
models: [sentinel].concat(directModels),
|
|
151
193
|
claudeModels: claudeModels,
|
|
152
194
|
model: selected && selected.provider !== 'anthropic' ? selected.id : 'claude',
|
|
195
|
+
// Registry travels without credentials — addCredentialPatch fills those
|
|
196
|
+
// in, so an untouched key round-trips as absent and the server preserves it.
|
|
197
|
+
customProviders: (Array.isArray(state.customProviders) ? state.customProviders : []).map(
|
|
198
|
+
function (provider) {
|
|
199
|
+
return {
|
|
200
|
+
id: provider.id,
|
|
201
|
+
label: provider.label || provider.id,
|
|
202
|
+
baseUrl: provider.baseUrl,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
),
|
|
153
206
|
};
|
|
154
207
|
}
|
|
155
208
|
|
|
156
|
-
|
|
209
|
+
// Task 147. `customProviders` is optional: when supplied, a model referencing
|
|
210
|
+
// a provider that is not in the registry is rejected here rather than saved —
|
|
211
|
+
// the gateway resolves an orphaned `custom:<id>` fail-closed to the Claude
|
|
212
|
+
// path, so the model would silently run somewhere else instead of erroring.
|
|
213
|
+
function validateModelCatalog(catalog, customProviders) {
|
|
214
|
+
var known = null;
|
|
215
|
+
if (Array.isArray(customProviders)) {
|
|
216
|
+
known = {};
|
|
217
|
+
customProviders.forEach(function (provider) {
|
|
218
|
+
known['custom:' + provider.id] = true;
|
|
219
|
+
});
|
|
220
|
+
}
|
|
157
221
|
var seen = { claude: true };
|
|
158
222
|
for (var i = 0; i < catalog.length; i++) {
|
|
159
223
|
var model = catalog[i];
|
|
224
|
+
if (known && isCustomProvider(model.provider) && !known[model.provider]) {
|
|
225
|
+
return 'Unknown provider for ' + (model.id || 'model') + ': ' + model.provider;
|
|
226
|
+
}
|
|
160
227
|
model.id = String(model.id || '').trim();
|
|
161
228
|
model.label = String(model.label || model.id).trim() || model.id;
|
|
162
229
|
if (!model.id) return 'Every model needs an ID';
|
|
@@ -173,6 +240,23 @@
|
|
|
173
240
|
return '';
|
|
174
241
|
}
|
|
175
242
|
|
|
243
|
+
/** Task 147: registry-level validation, mirroring the gateway's own rules. */
|
|
244
|
+
function validateCustomProviderList(customProviders) {
|
|
245
|
+
var seen = {};
|
|
246
|
+
for (var i = 0; i < customProviders.length; i++) {
|
|
247
|
+
var provider = customProviders[i];
|
|
248
|
+
var id = String(provider.id || '').trim();
|
|
249
|
+
var baseUrl = String(provider.baseUrl || '').trim();
|
|
250
|
+
if (!id) return 'Every custom provider needs a name';
|
|
251
|
+
if (seen[id]) return 'Duplicate custom provider: ' + id;
|
|
252
|
+
seen[id] = true;
|
|
253
|
+
if (!/^https?:\/\/\S+$/.test(baseUrl)) {
|
|
254
|
+
return 'Base URL for ' + (provider.label || id) + ' must start with http:// or https://';
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return '';
|
|
258
|
+
}
|
|
259
|
+
|
|
176
260
|
function addCredentialPatch(payload, credentials) {
|
|
177
261
|
[
|
|
178
262
|
{ provider: 'openai', key: 'openaiApiKey' },
|
|
@@ -184,6 +268,15 @@
|
|
|
184
268
|
if (credential.clear) payload[entry.key] = null;
|
|
185
269
|
else if (value && value.indexOf('•') === -1) payload[entry.key] = value;
|
|
186
270
|
});
|
|
271
|
+
// Custom providers carry their own credential inside the registry entry
|
|
272
|
+
// (task 147), keyed in the same credential maps by `custom:<id>`. Same rule
|
|
273
|
+
// as the scalars above: null clears, a real value sets, absent preserves.
|
|
274
|
+
(payload.customProviders || []).forEach(function (provider) {
|
|
275
|
+
var credential = credentials['custom:' + provider.id] || {};
|
|
276
|
+
var value = String(credential.value || '').trim();
|
|
277
|
+
if (credential.clear) provider.apiKey = null;
|
|
278
|
+
else if (value && value.indexOf('•') === -1) provider.apiKey = value;
|
|
279
|
+
});
|
|
187
280
|
return payload;
|
|
188
281
|
}
|
|
189
282
|
|
|
@@ -1561,6 +1654,13 @@
|
|
|
1561
1654
|
' display: grid; grid-template-columns: 120px minmax(0,1fr) auto; align-items: center; gap: 10px;',
|
|
1562
1655
|
' padding: 11px 14px; background: #1b1c1f; border-top: 1px solid #2a2b30;',
|
|
1563
1656
|
'}',
|
|
1657
|
+
// Task 147: same band as the credential row, above the model list, so a
|
|
1658
|
+
// custom provider's endpoint reads as part of the provider, not a model.
|
|
1659
|
+
'.cumulus-settings-provider-endpoint {',
|
|
1660
|
+
' display: grid; grid-template-columns: 120px minmax(0,1fr) auto; align-items: center; gap: 10px;',
|
|
1661
|
+
' padding: 10px 14px; background: #1b1c1f; border-top: 1px solid #2a2b30;',
|
|
1662
|
+
'}',
|
|
1663
|
+
'.cumulus-settings-provider-endpoint label { color: #9a9da5; font-size: 11.5px; }',
|
|
1564
1664
|
'.cumulus-settings-credential-note {',
|
|
1565
1665
|
' color: #7d808a; font-size: 11.5px; padding: 11px 14px; background: #1b1c1f; border-top: 1px solid #2a2b30;',
|
|
1566
1666
|
'}',
|
|
@@ -1619,6 +1719,8 @@
|
|
|
1619
1719
|
' .cumulus-settings-add-row .cumulus-settings-add-btn { grid-column: 1 / -1; }',
|
|
1620
1720
|
' .cumulus-settings-provider-credential { grid-template-columns: 1fr auto; gap: 5px; }',
|
|
1621
1721
|
' .cumulus-settings-provider-credential label { grid-column: 1 / -1; }',
|
|
1722
|
+
' .cumulus-settings-provider-endpoint { grid-template-columns: 1fr auto; gap: 5px; }',
|
|
1723
|
+
' .cumulus-settings-provider-endpoint label { grid-column: 1 / -1; }',
|
|
1622
1724
|
' .cumulus-settings-default-card { flex-direction: column; align-items: stretch; gap: 6px; }',
|
|
1623
1725
|
' .cumulus-settings-edit-grid { grid-template-columns: 1fr 1fr; }',
|
|
1624
1726
|
'}',
|
|
@@ -6392,16 +6494,8 @@
|
|
|
6392
6494
|
var addProvider = document.createElement('select');
|
|
6393
6495
|
addProvider.className = 'cumulus-settings-select';
|
|
6394
6496
|
addProvider.setAttribute('data-testid', 'webchat-gw-add-provider');
|
|
6395
|
-
|
|
6396
|
-
|
|
6397
|
-
{ value: 'openai', label: 'OpenAI' },
|
|
6398
|
-
{ value: 'huggingface', label: 'HuggingFace' },
|
|
6399
|
-
].forEach(function (provider) {
|
|
6400
|
-
var option = document.createElement('option');
|
|
6401
|
-
option.value = provider.value;
|
|
6402
|
-
option.textContent = provider.label;
|
|
6403
|
-
addProvider.appendChild(option);
|
|
6404
|
-
});
|
|
6497
|
+
// Options are built by renderAddProviderOptions() from providerDefs — the
|
|
6498
|
+
// one list that also drives the cards and the default dropdown (task 147).
|
|
6405
6499
|
var addId = document.createElement('input');
|
|
6406
6500
|
addId.type = 'text';
|
|
6407
6501
|
addId.className = 'cumulus-settings-input';
|
|
@@ -6432,6 +6526,51 @@
|
|
|
6432
6526
|
addCard.appendChild(addWrap);
|
|
6433
6527
|
body.appendChild(addCard);
|
|
6434
6528
|
|
|
6529
|
+
// ── Custom OpenAI-compatible providers (task 147) ────────────────────────
|
|
6530
|
+
// One mechanism for every third-party host that speaks chat/completions:
|
|
6531
|
+
// Vultr, Together, Groq, Fireworks, vLLM, LM Studio, Ollama. Each carries
|
|
6532
|
+
// its own credential — the OpenAI/HuggingFace keys belong to those hosts.
|
|
6533
|
+
var customCard = document.createElement('div');
|
|
6534
|
+
customCard.className = 'cumulus-settings-add-card';
|
|
6535
|
+
var customTitle = document.createElement('div');
|
|
6536
|
+
customTitle.className = 'cumulus-settings-add-title';
|
|
6537
|
+
customTitle.textContent = 'Add a custom provider';
|
|
6538
|
+
customCard.appendChild(customTitle);
|
|
6539
|
+
var customHint = document.createElement('div');
|
|
6540
|
+
customHint.className = 'cumulus-settings-subtitle';
|
|
6541
|
+
customHint.textContent =
|
|
6542
|
+
'Any OpenAI-compatible endpoint. Paste the base URL from its docs — /chat/completions is added if missing.';
|
|
6543
|
+
customCard.appendChild(customHint);
|
|
6544
|
+
var customWrap = document.createElement('div');
|
|
6545
|
+
customWrap.className = 'cumulus-settings-add-row';
|
|
6546
|
+
var customLabelInput = document.createElement('input');
|
|
6547
|
+
customLabelInput.type = 'text';
|
|
6548
|
+
customLabelInput.className = 'cumulus-settings-input';
|
|
6549
|
+
customLabelInput.placeholder = 'Name (e.g. Vultr)';
|
|
6550
|
+
customLabelInput.setAttribute('data-testid', 'webchat-gw-addprovider-label');
|
|
6551
|
+
var customUrlInput = document.createElement('input');
|
|
6552
|
+
customUrlInput.type = 'text';
|
|
6553
|
+
customUrlInput.className = 'cumulus-settings-input';
|
|
6554
|
+
customUrlInput.placeholder = 'https://api.example.com/v1';
|
|
6555
|
+
customUrlInput.setAttribute('data-testid', 'webchat-gw-addprovider-url');
|
|
6556
|
+
var customKeyInput = document.createElement('input');
|
|
6557
|
+
customKeyInput.type = 'password';
|
|
6558
|
+
customKeyInput.autocomplete = 'off';
|
|
6559
|
+
customKeyInput.className = 'cumulus-settings-input';
|
|
6560
|
+
customKeyInput.placeholder = 'API key';
|
|
6561
|
+
customKeyInput.setAttribute('data-testid', 'webchat-gw-addprovider-key');
|
|
6562
|
+
var customAddBtn = document.createElement('button');
|
|
6563
|
+
customAddBtn.type = 'button';
|
|
6564
|
+
customAddBtn.textContent = '+ Add';
|
|
6565
|
+
customAddBtn.className = 'cumulus-settings-add-btn';
|
|
6566
|
+
customAddBtn.setAttribute('data-testid', 'webchat-gw-addprovider-btn');
|
|
6567
|
+
customWrap.appendChild(customLabelInput);
|
|
6568
|
+
customWrap.appendChild(customUrlInput);
|
|
6569
|
+
customWrap.appendChild(customKeyInput);
|
|
6570
|
+
customWrap.appendChild(customAddBtn);
|
|
6571
|
+
customCard.appendChild(customWrap);
|
|
6572
|
+
body.appendChild(customCard);
|
|
6573
|
+
|
|
6435
6574
|
// ── License (task 129) — persist-only: the key lands in gateway.config.json
|
|
6436
6575
|
// but enforcement re-verifies at startup, so it takes effect on reload (127).
|
|
6437
6576
|
var licenseLabel = document.createElement('div');
|
|
@@ -6494,7 +6633,7 @@
|
|
|
6494
6633
|
});
|
|
6495
6634
|
document.body.appendChild(backdrop);
|
|
6496
6635
|
|
|
6497
|
-
var gwState = { catalog: [], sentinel: null };
|
|
6636
|
+
var gwState = { catalog: [], sentinel: null, customProviders: [] };
|
|
6498
6637
|
var credentialInputs = {};
|
|
6499
6638
|
var credentialValues = { openai: '', huggingface: '', license: '' };
|
|
6500
6639
|
var credentialClears = { openai: false, huggingface: false, license: false };
|
|
@@ -6526,27 +6665,45 @@
|
|
|
6526
6665
|
});
|
|
6527
6666
|
var gwLicenseMasked = '';
|
|
6528
6667
|
var gwLicenseInfo = null;
|
|
6529
|
-
|
|
6530
|
-
|
|
6531
|
-
|
|
6532
|
-
|
|
6533
|
-
|
|
6534
|
-
|
|
6535
|
-
|
|
6536
|
-
|
|
6537
|
-
|
|
6538
|
-
|
|
6539
|
-
|
|
6540
|
-
|
|
6541
|
-
|
|
6542
|
-
|
|
6543
|
-
|
|
6544
|
-
|
|
6545
|
-
|
|
6546
|
-
|
|
6547
|
-
|
|
6548
|
-
|
|
6549
|
-
|
|
6668
|
+
// Task 147: the three built-in providers are fixed; custom
|
|
6669
|
+
// OpenAI-compatible endpoints are appended from the registry. Cards, the
|
|
6670
|
+
// default dropdown, the add-model select and the credential rows all read
|
|
6671
|
+
// this ONE list, so they cannot disagree about which providers exist.
|
|
6672
|
+
var providerDefs = [];
|
|
6673
|
+
function buildProviderDefs() {
|
|
6674
|
+
var defs = [
|
|
6675
|
+
{
|
|
6676
|
+
id: 'anthropic',
|
|
6677
|
+
title: 'Anthropic',
|
|
6678
|
+
description: 'Claude CLI models · authentication is managed by the Claude CLI',
|
|
6679
|
+
},
|
|
6680
|
+
{
|
|
6681
|
+
id: 'openai',
|
|
6682
|
+
title: 'OpenAI',
|
|
6683
|
+
description: 'Responses API models with direct tool use',
|
|
6684
|
+
credential: 'openaiApiKey',
|
|
6685
|
+
credentialTestId: 'webchat-gw-openaikey',
|
|
6686
|
+
},
|
|
6687
|
+
{
|
|
6688
|
+
id: 'huggingface',
|
|
6689
|
+
title: 'HuggingFace',
|
|
6690
|
+
description: 'OpenAI-compatible models served through the HuggingFace router',
|
|
6691
|
+
credential: 'hfApiKey',
|
|
6692
|
+
credentialTestId: 'webchat-gw-hfkey',
|
|
6693
|
+
},
|
|
6694
|
+
];
|
|
6695
|
+
(gwState.customProviders || []).forEach(function (provider) {
|
|
6696
|
+
defs.push({
|
|
6697
|
+
id: 'custom:' + provider.id,
|
|
6698
|
+
title: provider.label || provider.id,
|
|
6699
|
+
description: 'Custom OpenAI-compatible endpoint',
|
|
6700
|
+
credential: 'custom',
|
|
6701
|
+
credentialTestId: 'webchat-gw-customkey-' + provider.id,
|
|
6702
|
+
custom: provider,
|
|
6703
|
+
});
|
|
6704
|
+
});
|
|
6705
|
+
return defs;
|
|
6706
|
+
}
|
|
6550
6707
|
|
|
6551
6708
|
function setSettingsStatus(message, kind) {
|
|
6552
6709
|
gwStatus.textContent = message || '';
|
|
@@ -6714,6 +6871,59 @@
|
|
|
6714
6871
|
return row;
|
|
6715
6872
|
}
|
|
6716
6873
|
|
|
6874
|
+
// Task 147: a custom provider owns an endpoint and can be removed. Removal
|
|
6875
|
+
// is REFUSED while models still reference it — an orphaned `custom:<id>`
|
|
6876
|
+
// resolves fail-closed to the Claude path, so the model would silently run
|
|
6877
|
+
// somewhere else instead of erroring.
|
|
6878
|
+
function renderCustomProviderControls(card, providerDef) {
|
|
6879
|
+
var row = document.createElement('div');
|
|
6880
|
+
row.className = 'cumulus-settings-provider-endpoint';
|
|
6881
|
+
var label = document.createElement('label');
|
|
6882
|
+
label.textContent = 'Base URL';
|
|
6883
|
+
var input = document.createElement('input');
|
|
6884
|
+
input.type = 'text';
|
|
6885
|
+
input.className = 'cumulus-settings-input';
|
|
6886
|
+
input.value = providerDef.custom.baseUrl || '';
|
|
6887
|
+
input.placeholder = 'https://api.example.com/v1';
|
|
6888
|
+
input.setAttribute('aria-label', providerDef.title + ' base URL');
|
|
6889
|
+
input.setAttribute('data-testid', 'webchat-gw-customurl-' + providerDef.custom.id);
|
|
6890
|
+
input.addEventListener('input', function () {
|
|
6891
|
+
providerDef.custom.baseUrl = input.value;
|
|
6892
|
+
});
|
|
6893
|
+
var remove = document.createElement('button');
|
|
6894
|
+
remove.type = 'button';
|
|
6895
|
+
remove.className = 'cumulus-settings-clear-btn';
|
|
6896
|
+
remove.textContent = 'Remove provider';
|
|
6897
|
+
remove.setAttribute('data-testid', 'webchat-gw-removeprovider-' + providerDef.custom.id);
|
|
6898
|
+
remove.addEventListener('click', function () {
|
|
6899
|
+
var inUse = gwState.catalog.filter(function (model) {
|
|
6900
|
+
return model.provider === providerDef.id;
|
|
6901
|
+
});
|
|
6902
|
+
if (inUse.length) {
|
|
6903
|
+
setSettingsStatus(
|
|
6904
|
+
'Remove its ' +
|
|
6905
|
+
inUse.length +
|
|
6906
|
+
' model' +
|
|
6907
|
+
(inUse.length === 1 ? '' : 's') +
|
|
6908
|
+
' first — they would stop running on this endpoint',
|
|
6909
|
+
'error'
|
|
6910
|
+
);
|
|
6911
|
+
return;
|
|
6912
|
+
}
|
|
6913
|
+
gwState.customProviders = gwState.customProviders.filter(function (entry) {
|
|
6914
|
+
return entry.id !== providerDef.custom.id;
|
|
6915
|
+
});
|
|
6916
|
+
delete credentialValues[providerDef.id];
|
|
6917
|
+
delete credentialClears[providerDef.id];
|
|
6918
|
+
setSettingsStatus('', '');
|
|
6919
|
+
renderAll();
|
|
6920
|
+
});
|
|
6921
|
+
row.appendChild(label);
|
|
6922
|
+
row.appendChild(input);
|
|
6923
|
+
row.appendChild(remove);
|
|
6924
|
+
card.appendChild(row);
|
|
6925
|
+
}
|
|
6926
|
+
|
|
6717
6927
|
function renderCredential(card, provider) {
|
|
6718
6928
|
if (!provider.credential) {
|
|
6719
6929
|
var note = document.createElement('div');
|
|
@@ -6787,6 +6997,7 @@
|
|
|
6787
6997
|
head.appendChild(heading);
|
|
6788
6998
|
head.appendChild(count);
|
|
6789
6999
|
card.appendChild(head);
|
|
7000
|
+
if (provider.custom) renderCustomProviderControls(card, provider);
|
|
6790
7001
|
var list = document.createElement('div');
|
|
6791
7002
|
list.className = 'cumulus-settings-provider-models';
|
|
6792
7003
|
if (!providerModels.length) {
|
|
@@ -6865,7 +7076,27 @@
|
|
|
6865
7076
|
defaultSelect.value = current ? current.id : gwState.catalog[0].id;
|
|
6866
7077
|
}
|
|
6867
7078
|
|
|
7079
|
+
// Rebuild the provider list from state before anything reads it, so a
|
|
7080
|
+
// provider added or removed this session reaches the cards, the default
|
|
7081
|
+
// dropdown and the add-model select in the same pass (task 147).
|
|
7082
|
+
function renderAddProviderOptions() {
|
|
7083
|
+
var previous = addProvider.value;
|
|
7084
|
+
addProvider.innerHTML = '';
|
|
7085
|
+
providerDefs.forEach(function (provider) {
|
|
7086
|
+
var option = document.createElement('option');
|
|
7087
|
+
option.value = provider.id;
|
|
7088
|
+
option.textContent = provider.title;
|
|
7089
|
+
addProvider.appendChild(option);
|
|
7090
|
+
});
|
|
7091
|
+
addProvider.value = previous || 'anthropic';
|
|
7092
|
+
if (!addProvider.value) addProvider.value = 'anthropic';
|
|
7093
|
+
addContext.disabled = addProvider.value === 'anthropic';
|
|
7094
|
+
if (addContext.disabled) addContext.value = '';
|
|
7095
|
+
}
|
|
7096
|
+
|
|
6868
7097
|
function renderAll() {
|
|
7098
|
+
providerDefs = buildProviderDefs();
|
|
7099
|
+
renderAddProviderOptions();
|
|
6869
7100
|
renderDefaultSelect();
|
|
6870
7101
|
renderGwModels();
|
|
6871
7102
|
}
|
|
@@ -6874,6 +7105,41 @@
|
|
|
6874
7105
|
addContext.disabled = addProvider.value === 'anthropic';
|
|
6875
7106
|
if (addContext.disabled) addContext.value = '';
|
|
6876
7107
|
});
|
|
7108
|
+
|
|
7109
|
+
customAddBtn.addEventListener('click', function () {
|
|
7110
|
+
var label = (customLabelInput.value || '').trim();
|
|
7111
|
+
var baseUrl = (customUrlInput.value || '').trim();
|
|
7112
|
+
var id = slugifyProviderId(label);
|
|
7113
|
+
if (!label || !id) {
|
|
7114
|
+
setSettingsStatus('Provider name is required (letters or digits)', 'error');
|
|
7115
|
+
return;
|
|
7116
|
+
}
|
|
7117
|
+
if (!/^https?:\/\/\S+$/.test(baseUrl)) {
|
|
7118
|
+
setSettingsStatus('Base URL must start with http:// or https://', 'error');
|
|
7119
|
+
return;
|
|
7120
|
+
}
|
|
7121
|
+
if (
|
|
7122
|
+
gwState.customProviders.some(function (provider) {
|
|
7123
|
+
return provider.id === id;
|
|
7124
|
+
})
|
|
7125
|
+
) {
|
|
7126
|
+
setSettingsStatus('A provider named "' + label + '" already exists', 'error');
|
|
7127
|
+
return;
|
|
7128
|
+
}
|
|
7129
|
+
gwState.customProviders.push({ id: id, label: label, baseUrl: baseUrl, maskedKey: '' });
|
|
7130
|
+
// Key is held in the same credential maps as the built-in providers, so
|
|
7131
|
+
// it rides the one save path and the one masking rule.
|
|
7132
|
+
var key = (customKeyInput.value || '').trim();
|
|
7133
|
+
if (key) {
|
|
7134
|
+
credentialValues['custom:' + id] = key;
|
|
7135
|
+
credentialClears['custom:' + id] = false;
|
|
7136
|
+
}
|
|
7137
|
+
customLabelInput.value = '';
|
|
7138
|
+
customUrlInput.value = '';
|
|
7139
|
+
customKeyInput.value = '';
|
|
7140
|
+
setSettingsStatus('', '');
|
|
7141
|
+
renderAll();
|
|
7142
|
+
});
|
|
6877
7143
|
addBtn.addEventListener('click', function () {
|
|
6878
7144
|
var id = (addId.value || '').trim();
|
|
6879
7145
|
var provider = addProvider.value;
|
|
@@ -6915,6 +7181,28 @@
|
|
|
6915
7181
|
renderAll();
|
|
6916
7182
|
});
|
|
6917
7183
|
|
|
7184
|
+
// One place that turns a GET/PUT /api/config response into editor state,
|
|
7185
|
+
// so the load and save paths cannot drift on credential seeding (Rule #8).
|
|
7186
|
+
function adoptGatewayConfigResponse(response) {
|
|
7187
|
+
gwState = gatewayConfigToModelCatalog(response);
|
|
7188
|
+
credentialValues.openai = response.openaiApiKey || '';
|
|
7189
|
+
credentialValues.huggingface = response.hfApiKey || '';
|
|
7190
|
+
credentialValues.license = response.licenseKey || '';
|
|
7191
|
+
credentialClears.openai = false;
|
|
7192
|
+
credentialClears.huggingface = false;
|
|
7193
|
+
credentialClears.license = false;
|
|
7194
|
+
// Custom-provider keys arrive MASKED inside their registry entries. Seed
|
|
7195
|
+
// them the same way as the scalars: a masked value is ignored on save,
|
|
7196
|
+
// so an untouched key is preserved rather than read as "cleared".
|
|
7197
|
+
gwState.customProviders.forEach(function (provider) {
|
|
7198
|
+
var key = 'custom:' + provider.id;
|
|
7199
|
+
credentialValues[key] = provider.maskedKey || '';
|
|
7200
|
+
credentialClears[key] = false;
|
|
7201
|
+
});
|
|
7202
|
+
gwLicenseMasked = response.licenseKey || '';
|
|
7203
|
+
gwLicenseInfo = response.license || null;
|
|
7204
|
+
}
|
|
7205
|
+
|
|
6918
7206
|
function refreshOpenModelCatalogs() {
|
|
6919
7207
|
var refresh = new XMLHttpRequest();
|
|
6920
7208
|
refresh.open('GET', '/api/models');
|
|
@@ -6935,14 +7223,16 @@
|
|
|
6935
7223
|
}
|
|
6936
7224
|
|
|
6937
7225
|
saveBtn.addEventListener('click', function () {
|
|
6938
|
-
var validationError =
|
|
7226
|
+
var validationError =
|
|
7227
|
+
validateCustomProviderList(gwState.customProviders || []) ||
|
|
7228
|
+
validateModelCatalog(gwState.catalog, gwState.customProviders || []);
|
|
6939
7229
|
if (validationError) {
|
|
6940
7230
|
setSettingsStatus(validationError, 'error');
|
|
6941
7231
|
return;
|
|
6942
7232
|
}
|
|
6943
7233
|
saveBtn.disabled = true;
|
|
6944
7234
|
setSettingsStatus('Saving…', '');
|
|
6945
|
-
var
|
|
7235
|
+
var credentialMap = {
|
|
6946
7236
|
openai: {
|
|
6947
7237
|
value: credentialValues.openai,
|
|
6948
7238
|
clear: credentialClears.openai,
|
|
@@ -6955,7 +7245,15 @@
|
|
|
6955
7245
|
value: credentialValues.license,
|
|
6956
7246
|
clear: credentialClears.license,
|
|
6957
7247
|
},
|
|
7248
|
+
};
|
|
7249
|
+
(gwState.customProviders || []).forEach(function (provider) {
|
|
7250
|
+
var key = 'custom:' + provider.id;
|
|
7251
|
+
credentialMap[key] = {
|
|
7252
|
+
value: credentialValues[key],
|
|
7253
|
+
clear: credentialClears[key],
|
|
7254
|
+
};
|
|
6958
7255
|
});
|
|
7256
|
+
var payload = addCredentialPatch(modelCatalogToGatewayConfig(gwState), credentialMap);
|
|
6959
7257
|
var save = new XMLHttpRequest();
|
|
6960
7258
|
save.open('PUT', '/api/config');
|
|
6961
7259
|
save.setRequestHeader('Content-Type', 'application/json');
|
|
@@ -6966,15 +7264,7 @@
|
|
|
6966
7264
|
if (save.status === 200) {
|
|
6967
7265
|
try {
|
|
6968
7266
|
var response = JSON.parse(save.responseText);
|
|
6969
|
-
|
|
6970
|
-
credentialClears.openai = false;
|
|
6971
|
-
credentialClears.huggingface = false;
|
|
6972
|
-
credentialClears.license = false;
|
|
6973
|
-
credentialValues.openai = response.openaiApiKey || '';
|
|
6974
|
-
credentialValues.huggingface = response.hfApiKey || '';
|
|
6975
|
-
credentialValues.license = response.licenseKey || '';
|
|
6976
|
-
gwLicenseMasked = response.licenseKey || '';
|
|
6977
|
-
gwLicenseInfo = response.license || null;
|
|
7267
|
+
adoptGatewayConfigResponse(response);
|
|
6978
7268
|
renderAll();
|
|
6979
7269
|
renderLicenseState(gwLicenseMasked, gwLicenseInfo);
|
|
6980
7270
|
setSettingsStatus(
|
|
@@ -6994,6 +7284,10 @@
|
|
|
6994
7284
|
save.send(JSON.stringify(payload));
|
|
6995
7285
|
});
|
|
6996
7286
|
|
|
7287
|
+
// First paint before the config arrives: the three built-in providers are
|
|
7288
|
+
// known without the server, so the shell renders immediately.
|
|
7289
|
+
renderAll();
|
|
7290
|
+
|
|
6997
7291
|
(function loadGatewayConfig() {
|
|
6998
7292
|
var load = new XMLHttpRequest();
|
|
6999
7293
|
load.open('GET', '/api/config');
|
|
@@ -7003,12 +7297,7 @@
|
|
|
7003
7297
|
if (load.status === 200) {
|
|
7004
7298
|
try {
|
|
7005
7299
|
var response = JSON.parse(load.responseText);
|
|
7006
|
-
|
|
7007
|
-
credentialValues.openai = response.openaiApiKey || '';
|
|
7008
|
-
credentialValues.huggingface = response.hfApiKey || '';
|
|
7009
|
-
credentialValues.license = response.licenseKey || '';
|
|
7010
|
-
gwLicenseMasked = response.licenseKey || '';
|
|
7011
|
-
gwLicenseInfo = response.license || null;
|
|
7300
|
+
adoptGatewayConfigResponse(response);
|
|
7012
7301
|
renderAll();
|
|
7013
7302
|
renderLicenseState(gwLicenseMasked, gwLicenseInfo);
|
|
7014
7303
|
} catch (e) {
|
|
@@ -7118,9 +7407,18 @@
|
|
|
7118
7407
|
// Model id → provider ("huggingface" | "openai" | "cli"), filled by loadModels
|
|
7119
7408
|
// from /api/models so the breadcrumb names the real provider (task 118).
|
|
7120
7409
|
var modelProviders = {};
|
|
7410
|
+
// Custom-provider id → display label, from /api/models (task 147). That
|
|
7411
|
+
// endpoint serves id + label only — never the base URL, which is private
|
|
7412
|
+
// infrastructure and it accepts namespace-scoped app keys (097 P7).
|
|
7413
|
+
var customProviderLabels = {};
|
|
7121
7414
|
function providerLabel(id) {
|
|
7122
7415
|
if (id === 'claude') return 'Anthropic';
|
|
7123
|
-
|
|
7416
|
+
var provider = modelProviders[id];
|
|
7417
|
+
if (isCustomProvider(provider)) {
|
|
7418
|
+
var customId = provider.slice('custom:'.length);
|
|
7419
|
+
return customProviderLabels[customId] || customId;
|
|
7420
|
+
}
|
|
7421
|
+
if (provider === 'openai') return 'OpenAI';
|
|
7124
7422
|
return 'HuggingFace';
|
|
7125
7423
|
}
|
|
7126
7424
|
function updateBreadcrumb() {
|
|
@@ -7153,6 +7451,10 @@
|
|
|
7153
7451
|
var previousModel = preserveSelection ? modelSelect.value : '';
|
|
7154
7452
|
var previousClaudeModel = preserveSelection ? claudeModelSelect.value : '';
|
|
7155
7453
|
modelProviders = {};
|
|
7454
|
+
customProviderLabels = {};
|
|
7455
|
+
(response.customProviders || []).forEach(function (provider) {
|
|
7456
|
+
customProviderLabels[provider.id] = provider.label || provider.id;
|
|
7457
|
+
});
|
|
7156
7458
|
modelSelect.innerHTML = '';
|
|
7157
7459
|
(response.models || []).forEach(function (model) {
|
|
7158
7460
|
var option = document.createElement('option');
|
|
@@ -122,5 +122,15 @@ export interface ResumeState {
|
|
|
122
122
|
/** Turns completed so far. */
|
|
123
123
|
turns: number;
|
|
124
124
|
}
|
|
125
|
+
/**
|
|
126
|
+
* Text carried by a response's content blocks.
|
|
127
|
+
*
|
|
128
|
+
* The loop otherwise builds its answer from streamed `text` events, which never
|
|
129
|
+
* include reasoning — providers accumulate that channel separately and only fold it
|
|
130
|
+
* into `content` as a last-resort text block (`huggingface-provider.ts`:
|
|
131
|
+
* `fullText || reasoningText`). Reading the blocks is what makes that fallback
|
|
132
|
+
* reachable at all; before task 150 it was dead code for every agentic turn.
|
|
133
|
+
*/
|
|
134
|
+
export declare function textFromContent(content: Anthropic.ContentBlock[] | undefined): string;
|
|
125
135
|
export declare function runAgenticLoop(options: AgenticLoopOptions): Promise<AgenticLoopResult>;
|
|
126
136
|
//# sourceMappingURL=agentic-loop.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agentic-loop.d.ts","sourceRoot":"","sources":["../../src/lib/agentic-loop.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"agentic-loop.d.ts","sourceRoot":"","sources":["../../src/lib/agentic-loop.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,SAAS,MAAM,mBAAmB,CAAC;AAG/C,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,cAAc,EAAe,MAAM,eAAe,CAAC;AAE3F,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAK7C,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,WAAW,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,UAAU,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC,CAAC;IAClG,6CAA6C;IAC7C,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,sFAAsF;IACtF,QAAQ,CAAC,EAAE,YAAY,EAAE,CAAC;IAC1B,iFAAiF;IACjF,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gFAAgF;IAChF,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IACnE,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACxE,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,wCAAwC;IACxC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6GAA6G;IAC7G,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,cAAc,CAAC;IAC3B,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAC9D;AAED,MAAM,WAAW,iBAAiB;IAChC,iFAAiF;IACjF,MAAM,EAAE,MAAM,GAAG,aAAa,CAAC;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,KAAK,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC/B,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,OAAO,CAAC;KAClB,CAAC,CAAC;IACH,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE;QACL,gBAAgB,EAAE,MAAM,CAAC;QACzB,iBAAiB,EAAE,MAAM,CAAC;QAC1B,eAAe,EAAE,MAAM,CAAC;QACxB,mBAAmB,EAAE,MAAM,CAAC;KAC7B,CAAC;IACF,uFAAuF;IACvF,UAAU,CAAC,EAAE,iBAAiB,CAAC;CAChC;AAED,oDAAoD;AACpD,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,4DAA4D;AAC5D,MAAM,WAAW,iBAAiB;IAChC,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAC;IACjB,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,oDAAoD;IACpD,aAAa,EAAE,OAAO,CAAC;IACvB,yEAAyE;IACzE,SAAS,CAAC,EAAE,YAAY,EAAE,CAAC;IAC3B,gDAAgD;IAChD,SAAS,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,WAAW,EAAE,WAAW,CAAC;CAC1B;AAED,sEAAsE;AACtE,MAAM,WAAW,WAAW;IAC1B,sFAAsF;IACtF,QAAQ,EAAE,SAAS,CAAC,YAAY,EAAE,CAAC;IACnC,gDAAgD;IAChD,SAAS,EAAE,MAAM,CAAC;IAClB,mCAAmC;IACnC,SAAS,EAAE,KAAK,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC/B,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,OAAO,CAAC;KAClB,CAAC,CAAC;IACH,kFAAkF;IAClF,mBAAmB,EAAE,SAAS,CAAC,oBAAoB,EAAE,CAAC;IACtD,wCAAwC;IACxC,eAAe,EAAE,MAAM,CAAC;IACxB,gCAAgC;IAChC,KAAK,EAAE;QACL,gBAAgB,EAAE,MAAM,CAAC;QACzB,iBAAiB,EAAE,MAAM,CAAC;QAC1B,eAAe,EAAE,MAAM,CAAC;QACxB,mBAAmB,EAAE,MAAM,CAAC;KAC7B,CAAC;IACF,8BAA8B;IAC9B,KAAK,EAAE,MAAM,CAAC;CACf;AAoBD;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,SAAS,CAAC,YAAY,EAAE,GAAG,SAAS,GAAG,MAAM,CAMrF;AAED,wBAAsB,cAAc,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAoiB5F"}
|