dsh-provider-qoder 0.4.2 → 0.4.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.
- package/lib/client.js +476 -169
- package/lib/index.d.ts +6 -0
- package/lib/index.js +701 -452
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -223,6 +223,7 @@ window.__ModuleLoader__.load({
|
|
|
223
223
|
refreshing: "正在刷新…",
|
|
224
224
|
userQuotaTitle: "个人额度",
|
|
225
225
|
orgResourceTitle: "组织资源包",
|
|
226
|
+
addOnQuotaTitle: "增值额度",
|
|
226
227
|
dedicatedResourceTitle: "专属资源包",
|
|
227
228
|
dedicatedResourceExpiresAt: "有效期至:{value}",
|
|
228
229
|
quotaRemaining: "剩余 {value}%",
|
|
@@ -294,6 +295,7 @@ window.__ModuleLoader__.load({
|
|
|
294
295
|
refreshing: "Refreshing…",
|
|
295
296
|
userQuotaTitle: "Personal Quota",
|
|
296
297
|
orgResourceTitle: "Org Resource Package",
|
|
298
|
+
addOnQuotaTitle: "Add-on Credits",
|
|
297
299
|
dedicatedResourceTitle: "Dedicated Resource Package",
|
|
298
300
|
dedicatedResourceExpiresAt: "Expires at: {value}",
|
|
299
301
|
quotaRemaining: "{value}% remaining",
|
|
@@ -362,6 +364,146 @@ window.__ModuleLoader__.load({
|
|
|
362
364
|
}
|
|
363
365
|
}
|
|
364
366
|
//#endregion
|
|
367
|
+
//#region src/client/model-catalog-editor.ts
|
|
368
|
+
/** One mounted region owns its directory and asynchronous edits. */
|
|
369
|
+
var QoderModelCatalogEditor = class {
|
|
370
|
+
region;
|
|
371
|
+
operations;
|
|
372
|
+
active = true;
|
|
373
|
+
confirmed;
|
|
374
|
+
snapshot;
|
|
375
|
+
listeners = /* @__PURE__ */ new Set();
|
|
376
|
+
constructor(region, models, operations) {
|
|
377
|
+
this.region = region;
|
|
378
|
+
this.operations = operations;
|
|
379
|
+
this.confirmed = models.map(cloneCatalogModel);
|
|
380
|
+
this.snapshot = {
|
|
381
|
+
models: this.confirmed,
|
|
382
|
+
catalog: this.confirmed,
|
|
383
|
+
unavailableIds: /* @__PURE__ */ new Set()
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
getSnapshot = () => this.snapshot;
|
|
387
|
+
subscribe = (listener) => {
|
|
388
|
+
this.listeners.add(listener);
|
|
389
|
+
return () => {
|
|
390
|
+
this.listeners.delete(listener);
|
|
391
|
+
};
|
|
392
|
+
};
|
|
393
|
+
/** ConfigForms supplies accepted values, including its recovery read after refusal. */
|
|
394
|
+
replaceModels(models) {
|
|
395
|
+
const followsConfig = this.snapshot.catalog === this.confirmed;
|
|
396
|
+
this.confirmed = models.map(cloneCatalogModel);
|
|
397
|
+
const selected = new Map(this.confirmed.map((model) => [model.id, model]));
|
|
398
|
+
this.publish({
|
|
399
|
+
...this.snapshot,
|
|
400
|
+
models: this.confirmed,
|
|
401
|
+
catalog: followsConfig ? this.confirmed : this.snapshot.catalog.map((model) => selected.get(model.id) ?? model)
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
activate() {
|
|
405
|
+
this.active = true;
|
|
406
|
+
}
|
|
407
|
+
dispose() {
|
|
408
|
+
this.active = false;
|
|
409
|
+
this.listeners.clear();
|
|
410
|
+
}
|
|
411
|
+
publish(snapshot) {
|
|
412
|
+
if (!this.active) return;
|
|
413
|
+
this.snapshot = snapshot;
|
|
414
|
+
for (const listener of this.listeners) listener();
|
|
415
|
+
}
|
|
416
|
+
async discover() {
|
|
417
|
+
if (!this.active || this.snapshot.pending) return;
|
|
418
|
+
this.publish({
|
|
419
|
+
...this.snapshot,
|
|
420
|
+
pending: "discovering",
|
|
421
|
+
failure: void 0,
|
|
422
|
+
failureMessage: void 0
|
|
423
|
+
});
|
|
424
|
+
try {
|
|
425
|
+
const result = await this.operations.discoverModels();
|
|
426
|
+
if (!this.active) return;
|
|
427
|
+
if (!result.ok) {
|
|
428
|
+
this.publish({
|
|
429
|
+
...this.snapshot,
|
|
430
|
+
pending: void 0,
|
|
431
|
+
failure: "modelsFetchFailed",
|
|
432
|
+
failureMessage: result.error.message
|
|
433
|
+
});
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
const reconciled = reconcileQoderModels(this.snapshot.catalog, result.value);
|
|
437
|
+
await this.save(reconciled.selected, reconciled.catalog, reconciled.unavailableIds);
|
|
438
|
+
} catch (error) {
|
|
439
|
+
this.publish({
|
|
440
|
+
...this.snapshot,
|
|
441
|
+
pending: void 0,
|
|
442
|
+
failure: "modelsFetchFailed",
|
|
443
|
+
failureMessage: error instanceof Error ? error.message : void 0
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
async toggle(id, enabled) {
|
|
448
|
+
if (!this.active || this.snapshot.pending || this.snapshot.unavailableIds.has(id)) return;
|
|
449
|
+
const selected = new Set(this.snapshot.models.map((model) => model.id));
|
|
450
|
+
if (enabled) selected.add(id);
|
|
451
|
+
else selected.delete(id);
|
|
452
|
+
await this.save(this.snapshot.catalog.filter((model) => selected.has(model.id) && !this.snapshot.unavailableIds.has(model.id)));
|
|
453
|
+
}
|
|
454
|
+
async selectTier(id, tierKey) {
|
|
455
|
+
if (!this.active || this.snapshot.pending || !this.snapshot.models.some((model) => model.id === id)) return;
|
|
456
|
+
const tokenCount = this.snapshot.catalog.find((model) => model.id === id)?.contextOptions?.[tierKey]?.tokenCount;
|
|
457
|
+
if (typeof tokenCount !== "number" || !Number.isFinite(tokenCount) || tokenCount <= 0) return;
|
|
458
|
+
const apply = (candidate) => candidate.id === id ? {
|
|
459
|
+
...candidate,
|
|
460
|
+
contextTier: tierKey,
|
|
461
|
+
contextWindow: tokenCount
|
|
462
|
+
} : candidate;
|
|
463
|
+
await this.save(this.snapshot.models.map(apply), this.snapshot.catalog.map(apply));
|
|
464
|
+
}
|
|
465
|
+
async save(models, catalog = this.snapshot.catalog, unavailableIds = this.snapshot.unavailableIds) {
|
|
466
|
+
if (models.length === 0) {
|
|
467
|
+
this.publish({
|
|
468
|
+
...this.snapshot,
|
|
469
|
+
pending: void 0,
|
|
470
|
+
failure: "modelsRequired",
|
|
471
|
+
failureMessage: void 0
|
|
472
|
+
});
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
const submitted = models.map(cloneCatalogModel);
|
|
476
|
+
const confirmedAtSubmission = this.confirmed;
|
|
477
|
+
this.publish({
|
|
478
|
+
...this.snapshot,
|
|
479
|
+
pending: "saving",
|
|
480
|
+
failure: void 0,
|
|
481
|
+
failureMessage: void 0
|
|
482
|
+
});
|
|
483
|
+
let saved = false;
|
|
484
|
+
try {
|
|
485
|
+
saved = await this.operations.storeModels(this.region, submitted);
|
|
486
|
+
} catch {}
|
|
487
|
+
if (!this.active) return;
|
|
488
|
+
if (saved) {
|
|
489
|
+
if (this.confirmed === confirmedAtSubmission) this.confirmed = submitted;
|
|
490
|
+
const accepted = new Map(this.confirmed.map((model) => [model.id, model]));
|
|
491
|
+
const directory = new Map(catalog.map((model) => [model.id, model]));
|
|
492
|
+
for (const [id, model] of accepted) directory.set(id, model);
|
|
493
|
+
this.publish({
|
|
494
|
+
models: this.confirmed,
|
|
495
|
+
catalog: [...directory.values()],
|
|
496
|
+
unavailableIds: new Set([...unavailableIds].filter((id) => !accepted.has(id)))
|
|
497
|
+
});
|
|
498
|
+
} else this.publish({
|
|
499
|
+
models: this.confirmed,
|
|
500
|
+
catalog: this.confirmed,
|
|
501
|
+
unavailableIds: /* @__PURE__ */ new Set(),
|
|
502
|
+
failure: "saveFailed"
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
};
|
|
506
|
+
//#endregion
|
|
365
507
|
//#region \0dsh-css:/home/runner/work/dsh-provider-qoder/dsh-provider-qoder/src/client/QoderCredentialCard.module.css.mjs
|
|
366
508
|
const css$1 = ".SRqrhW_credential{color:var(--dsw-alias-label-primary);border-radius:10px;margin-top:12px}.SRqrhW_credential p{margin:8px 0 0}.SRqrhW_credentialEditor{box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-xl,12px);flex-direction:column;gap:12px;padding:12px 14px;display:flex}.SRqrhW_credentialRowHead{align-items:center;gap:10px;display:flex}.SRqrhW_credentialIdentity{align-items:center;gap:6px;min-width:0;display:inline-flex}.SRqrhW_credentialName{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:500;line-height:22px}.SRqrhW_credentialDot{box-sizing:border-box;border-radius:50%;flex:none;width:8px;height:8px;display:inline-block}.SRqrhW_credentialDotConfigured{background:var(--dsw-alias-state-success-primary)}.SRqrhW_credentialDotMissing{background:var(--dsw-alias-state-error-primary)}.SRqrhW_regionBadge{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary);border-radius:4px;align-items:center;padding:1px 6px;font-size:11px;font-weight:500;line-height:16px;display:inline-flex}.SRqrhW_regionSelector{flex-direction:column;gap:8px;margin-bottom:14px;display:flex}.SRqrhW_regionGroup{gap:8px;display:flex}.SRqrhW_regionOption{border:1px solid var(--dsw-alias-border-l2);cursor:pointer;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);user-select:none;border-radius:8px;align-items:center;gap:6px;padding:6px 12px;font-size:12px;line-height:18px;transition:all .12s;display:inline-flex}.SRqrhW_regionOption:hover:not(.SRqrhW_regionOptionDisabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.SRqrhW_credential button.SRqrhW_regionOptionActive{border-color:var(--dsw-alias-brand-primary);background:var(--dsw-alias-interactive-bg-selected);color:var(--dsw-alias-brand-primary);font-weight:500}.SRqrhW_regionOptionDisabled{cursor:not-allowed;opacity:.5}.SRqrhW_editButton{box-sizing:border-box;font:inherit;background:0 0;margin-left:auto;font-size:12px;line-height:18px;border:1px solid var(--dsw-alias-border-l2)!important;border-radius:14px!important;height:28px!important;padding:0 10px!important}.SRqrhW_editButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-solid)}.SRqrhW_editor{background:var(--dsw-alias-bg-module-platform);border-radius:12px;padding:14px 16px}.SRqrhW_customized{border:.5px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-xl,12px);background:var(--dsw-alias-bg-module-platform);margin-top:12px;transition:all .12s;overflow:hidden}.SRqrhW_customizedSummary{box-sizing:border-box;border-radius:var(--dsw-radius-xl,12px);cursor:pointer;width:100%;color:var(--dsw-alias-label-primary);user-select:none;align-items:center;gap:8px;margin:0;padding:12px 14px;font-size:13px;font-weight:600;line-height:18px;list-style:none;transition:background .12s;display:flex}.SRqrhW_customizedSummary:hover{background:var(--dsw-alias-interactive-bg-hover)}.SRqrhW_modelSummaryBadge{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-tertiary);border-radius:4px;align-items:center;padding:1px 6px;font-size:11px;font-weight:400;line-height:16px;display:inline-flex}.SRqrhW_customizedSummary::-webkit-details-marker{display:none}.SRqrhW_customizedSummary:before{content:\"\";border-bottom:1.5px solid;border-right:1.5px solid;width:5px;height:5px;transition:transform .12s;transform:rotate(-45deg)translate(-1px,-1px)}.SRqrhW_customized[open]>.SRqrhW_customizedSummary:before{transform:rotate(45deg)translate(-1px,-1px)}.SRqrhW_customized[open]>.SRqrhW_customizedSummary{border-bottom:.5px solid var(--dsw-alias-border-l2);border-bottom-right-radius:0;border-bottom-left-radius:0}.SRqrhW_customizedBody{padding:12px 14px 14px}.SRqrhW_modelCatalog{flex-direction:column;gap:10px;display:flex}.SRqrhW_modelList{flex-direction:column;gap:10px;max-height:360px;padding-right:4px;display:flex;overflow-y:auto}.SRqrhW_modelList::-webkit-scrollbar{width:4px}.SRqrhW_modelList::-webkit-scrollbar-thumb{background:var(--dsw-alias-border-l3);border-radius:2px}.SRqrhW_modelCatalogHead{justify-content:space-between;align-items:flex-start;gap:12px;display:flex}.SRqrhW_modelCatalogHeadCompact{justify-content:flex-end;align-items:center;gap:12px;display:flex}.SRqrhW_modelCatalogTitle{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:500;line-height:18px}.SRqrhW_modelCatalogMeta{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;margin:2px 0 0!important}.SRqrhW_linkButton{box-sizing:border-box;font:inherit;background:0 0;font-size:12px;line-height:18px;border:none!important;border-radius:14px!important;height:28px!important;padding:0 10px!important}.SRqrhW_linkButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.SRqrhW_modelChoice{border-radius:6px;flex-direction:column;gap:4px;padding:6px 8px;font-size:13px;display:flex}.SRqrhW_modelChoice:hover{background:var(--dsw-alias-interactive-bg-hover)}.SRqrhW_modelChoiceHead{cursor:pointer;grid-template-columns:auto minmax(0,1fr) auto auto auto;align-items:center;gap:8px;display:grid}.SRqrhW_modelTier{align-items:center;gap:8px;padding-left:24px;display:flex}.SRqrhW_modelTierLabel{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:18px}.SRqrhW_modelTierSelect{box-sizing:border-box;background:var(--dsw-alias-bg-layer-1);height:24px;color:inherit;font:inherit;cursor:pointer;border:0;border-radius:6px;padding:0 6px;font-size:12px}.SRqrhW_modelTierHint{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:18px;margin:0!important}.SRqrhW_modelChoiceName{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.SRqrhW_modelChoice code{color:var(--dsw-alias-label-tertiary);font-size:11px}.SRqrhW_modelRate{min-width:34px;color:var(--dsw-alias-brand-primary);text-align:right;font-size:12px;font-weight:600}.SRqrhW_modelUnavailable{cursor:not-allowed;opacity:.55}.SRqrhW_modelBadge{color:var(--dsw-alias-state-error-primary);font-size:11px}@media (prefers-reduced-motion:reduce){.SRqrhW_customizedSummary:before{transition:none}}.SRqrhW_head{justify-content:space-between;align-items:center;gap:12px;margin-bottom:20px;display:flex}.SRqrhW_heading{margin:0;font-size:18px;font-weight:600}.SRqrhW_status{color:var(--dsw-alias-label-secondary);font-size:12px}.SRqrhW_configured,.SRqrhW_success{color:var(--dsw-alias-state-success-primary)}.SRqrhW_label{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:500;line-height:18px;display:block}.SRqrhW_input{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);width:100%;height:32px;color:var(--dsw-alias-label-primary);border-radius:8px;outline:none;margin-top:6px;padding:0 10px}.SRqrhW_input:focus{border-color:var(--dsw-alias-brand-primary)}.SRqrhW_input::placeholder{color:var(--dsw-alias-label-dimmed)}.SRqrhW_input:disabled{cursor:not-allowed;opacity:.4}.SRqrhW_hint{color:var(--dsw-alias-label-secondary);font-size:12px}.SRqrhW_actions{justify-content:flex-end;gap:8px;margin-top:14px;display:flex}.SRqrhW_credential button{cursor:pointer;height:32px;color:var(--dsw-alias-label-primary);border:1px solid #0000;border-radius:16px;padding:0 12px}.SRqrhW_credential button:disabled{cursor:not-allowed;opacity:.4}.SRqrhW_primary{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)!important}.SRqrhW_primary:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover)}.SRqrhW_secondary{background:0 0;border-color:var(--dsw-alias-border-l2)!important}.SRqrhW_secondary:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.SRqrhW_danger{background:0 0;border-color:var(--dsw-alias-state-error-primary)!important;color:var(--dsw-alias-state-error-primary)!important}.SRqrhW_danger:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger)}.SRqrhW_error{color:var(--dsw-alias-state-error-primary);font-size:12px}.SRqrhW_success{font-size:12px}.SRqrhW_accountSection{border:.5px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-xl,12px);background:var(--dsw-alias-bg-module-platform);flex-direction:column;gap:10px;margin-top:12px;padding:12px 14px;display:flex}.SRqrhW_accountHead{justify-content:space-between;align-items:center;gap:8px;display:flex}.SRqrhW_userInfo{align-items:center;gap:10px;min-width:0;display:flex}.SRqrhW_avatar{background:var(--dsw-alias-brand-primary);width:32px;height:32px;color:var(--dsw-alias-label-primary-foreground);text-transform:uppercase;border-radius:50%;flex-shrink:0;justify-content:center;align-items:center;font-size:14px;font-weight:600;display:flex}.SRqrhW_userDetails{flex-direction:column;gap:2px;min-width:0;display:flex}.SRqrhW_userName{color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;font-size:13px;font-weight:600;overflow:hidden}.SRqrhW_userEmail{color:var(--dsw-alias-label-tertiary);text-overflow:ellipsis;white-space:nowrap;font-size:11px;overflow:hidden}.SRqrhW_refreshBtn{border:1px solid var(--dsw-alias-border-l2)!important;height:26px!important;color:var(--dsw-alias-label-secondary)!important;background:0 0!important;border-radius:13px!important;padding:0 8px!important;font-size:12px!important}.SRqrhW_refreshBtn:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)!important}.SRqrhW_quotaBlock{border-top:1px solid var(--dsw-alias-border-l2);flex-direction:column;gap:6px;padding-top:8px;display:flex}.SRqrhW_quotaHeader{justify-content:space-between;align-items:center;gap:8px;display:flex}.SRqrhW_quotaLabel{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:600}.SRqrhW_quotaTop{justify-content:space-between;align-items:baseline;gap:8px;font-size:12px;display:flex}.SRqrhW_quotaTop strong{color:var(--dsw-alias-brand-primary);font-size:13px}.SRqrhW_quotaDescription{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px;margin:0!important}.SRqrhW_quotaProgress{background:var(--dsw-alias-border-l3);width:100%;height:6px;accent-color:var(--dsw-alias-brand-primary);-webkit-appearance:none;appearance:none;border:0;border-radius:999px;overflow:hidden}.SRqrhW_quotaProgress::-webkit-progress-bar{background:var(--dsw-alias-border-l3);border-radius:999px}.SRqrhW_quotaProgress::-webkit-progress-value{background:var(--dsw-alias-brand-primary);border-radius:999px}.SRqrhW_quotaProgress::-moz-progress-bar{background:var(--dsw-alias-brand-primary);border-radius:999px}.SRqrhW_quotaMeta{color:var(--dsw-alias-label-tertiary);justify-content:space-between;align-items:center;gap:8px;font-size:11px;display:flex}.SRqrhW_accountLoadingText,.SRqrhW_accountEmptyText{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px}.SRqrhW_userNameRow{align-items:center;gap:8px;display:flex}.SRqrhW_planBadge{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-brand-primary);border-radius:4px;align-items:center;padding:1px 6px;font-size:11px;font-weight:600;line-height:16px;display:inline-flex}.SRqrhW_orgTag{color:var(--dsw-alias-label-tertiary);font-size:11px}.SRqrhW_suspendedBanner{background:var(--dsw-alias-state-error-subtle,#ff4d4f1a);color:var(--dsw-alias-state-error-primary);border-radius:6px;align-items:center;gap:6px;margin-bottom:8px;padding:6px 10px;font-size:12px;display:flex}.SRqrhW_searchModeSection{border:.5px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-xl,12px);background:var(--dsw-alias-bg-module-platform);flex-direction:column;gap:10px;margin-top:12px;padding:12px 14px;display:flex}.SRqrhW_searchModeHintText{color:var(--dsw-alias-label-secondary);margin:4px 0 0;font-size:12px;line-height:18px}";
|
|
367
509
|
const tagId$1 = "dsh-provider-qoder/QoderCredentialCard.module.css";
|
|
@@ -449,57 +591,26 @@ window.__ModuleLoader__.load({
|
|
|
449
591
|
};
|
|
450
592
|
//#endregion
|
|
451
593
|
//#region src/client/QoderModelCatalog.tsx
|
|
452
|
-
|
|
453
|
-
return models.length === 0 ? "modelsRequired" : void 0;
|
|
454
|
-
}
|
|
594
|
+
/** The key resets directory state whenever the active Qoder service region changes. */
|
|
455
595
|
function QoderModelCatalog(props) {
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
const [
|
|
460
|
-
const
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
setUnavailableIds(reconciled.unavailableIds);
|
|
475
|
-
onChange(reconciled.selected);
|
|
476
|
-
};
|
|
477
|
-
const toggleModel = (id, enabled) => {
|
|
478
|
-
if (unavailableIds.has(id)) return;
|
|
479
|
-
const source = catalog ?? models;
|
|
480
|
-
if (catalog === void 0) setCatalog(source);
|
|
481
|
-
const nextSelected = new Set(selectedIds);
|
|
482
|
-
if (enabled) nextSelected.add(id);
|
|
483
|
-
else nextSelected.delete(id);
|
|
484
|
-
onChange(source.filter((model) => nextSelected.has(model.id) && !unavailableIds.has(model.id)));
|
|
485
|
-
};
|
|
486
|
-
const changeContextTier = (id, tierKey) => {
|
|
487
|
-
const source = catalog ?? models;
|
|
488
|
-
if (catalog === void 0) setCatalog(source);
|
|
489
|
-
const apply = (model) => {
|
|
490
|
-
if (model.id !== id) return model;
|
|
491
|
-
const tokenCount = model.contextOptions?.[tierKey]?.tokenCount;
|
|
492
|
-
if (typeof tokenCount !== "number" || !Number.isFinite(tokenCount) || tokenCount <= 0) return model;
|
|
493
|
-
return {
|
|
494
|
-
...model,
|
|
495
|
-
contextTier: tierKey,
|
|
496
|
-
contextWindow: tokenCount
|
|
497
|
-
};
|
|
498
|
-
};
|
|
499
|
-
const nextCatalog = source.map(apply);
|
|
500
|
-
setCatalog(nextCatalog);
|
|
501
|
-
onChange(models.map(apply));
|
|
502
|
-
};
|
|
596
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RegionModelCatalog, { ...props }, props.region);
|
|
597
|
+
}
|
|
598
|
+
function RegionModelCatalog({ operations, models, region, disabled, fetchDisabled, hideTitle, t }) {
|
|
599
|
+
const [editor] = (0, react.useState)(() => new QoderModelCatalogEditor(region, models, operations));
|
|
600
|
+
const snapshot = (0, react.useSyncExternalStore)(editor.subscribe, editor.getSnapshot, editor.getSnapshot);
|
|
601
|
+
(0, react.useLayoutEffect)(() => {
|
|
602
|
+
editor.activate();
|
|
603
|
+
return () => editor.dispose();
|
|
604
|
+
}, [editor]);
|
|
605
|
+
(0, react.useLayoutEffect)(() => {
|
|
606
|
+
editor.replaceModels(models);
|
|
607
|
+
}, [editor, models]);
|
|
608
|
+
const displayedModels = snapshot.catalog;
|
|
609
|
+
const unavailableIds = snapshot.unavailableIds;
|
|
610
|
+
const selectedIds = new Set(snapshot.models.map((model) => model.id));
|
|
611
|
+
const busy = snapshot.pending !== void 0;
|
|
612
|
+
const fetching = snapshot.pending === "discovering";
|
|
613
|
+
const failure = snapshot.failureMessage || (snapshot.failure ? t(snapshot.failure) : void 0);
|
|
503
614
|
const renderRate = (model) => {
|
|
504
615
|
if (model.isFree || model.priceFactor === 0) return t("modelRateFree");
|
|
505
616
|
if (model.priceFactor === void 0) return t("modelRateUnknown");
|
|
@@ -522,13 +633,13 @@ window.__ModuleLoader__.load({
|
|
|
522
633
|
children: t("modelsTitle")
|
|
523
634
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
524
635
|
className: QoderCredentialCard_module_css_default.modelCatalogMeta,
|
|
525
|
-
children: t("modelsEnabled", { count: models.length })
|
|
636
|
+
children: t("modelsEnabled", { count: snapshot.models.length })
|
|
526
637
|
})] }) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
527
638
|
type: "button",
|
|
528
639
|
className: QoderCredentialCard_module_css_default.linkButton,
|
|
529
|
-
disabled: disabled ||
|
|
640
|
+
disabled: disabled || busy || Boolean(fetchDisabled),
|
|
530
641
|
onClick: () => {
|
|
531
|
-
|
|
642
|
+
if (!disabled && !fetchDisabled) editor.discover();
|
|
532
643
|
},
|
|
533
644
|
children: fetching ? t("modelsFetching") : t("modelsFetch")
|
|
534
645
|
})]
|
|
@@ -553,9 +664,9 @@ window.__ModuleLoader__.load({
|
|
|
553
664
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
554
665
|
type: "checkbox",
|
|
555
666
|
checked: !unavailable && selectedIds.has(model.id),
|
|
556
|
-
disabled: disabled || unavailable,
|
|
667
|
+
disabled: disabled || busy || unavailable,
|
|
557
668
|
onChange: (event) => {
|
|
558
|
-
|
|
669
|
+
if (!disabled) editor.toggle(model.id, event.currentTarget.checked);
|
|
559
670
|
}
|
|
560
671
|
}),
|
|
561
672
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
@@ -580,9 +691,9 @@ window.__ModuleLoader__.load({
|
|
|
580
691
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
|
|
581
692
|
className: QoderCredentialCard_module_css_default.modelTierSelect,
|
|
582
693
|
value: selectedTier ?? "",
|
|
583
|
-
disabled: disabled || unavailable || !selectedIds.has(model.id),
|
|
694
|
+
disabled: disabled || busy || unavailable || !selectedIds.has(model.id),
|
|
584
695
|
onChange: (event) => {
|
|
585
|
-
|
|
696
|
+
if (!disabled) editor.selectTier(model.id, event.currentTarget.value);
|
|
586
697
|
},
|
|
587
698
|
children: tiers.map((tier) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
588
699
|
value: tier.key,
|
|
@@ -615,18 +726,31 @@ window.__ModuleLoader__.load({
|
|
|
615
726
|
}
|
|
616
727
|
/**
|
|
617
728
|
* Settings card for the Qoder subscription: credential state, subscriber
|
|
618
|
-
* profile, and every quota block the provider reports.
|
|
729
|
+
* profile, and every quota block the provider reports. Region changes remount
|
|
730
|
+
* the account state before any old profile can be displayed.
|
|
619
731
|
*/
|
|
620
|
-
function QoderAccountCard(
|
|
732
|
+
function QoderAccountCard(props) {
|
|
733
|
+
const modelSnapshot = (0, react.useSyncExternalStore)(props.operations.subscribeModels, props.operations.getModelSnapshot, props.operations.getModelSnapshot);
|
|
734
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RegionAccountCard, {
|
|
735
|
+
...props,
|
|
736
|
+
modelSnapshot
|
|
737
|
+
}, regionOf(modelSnapshot.value));
|
|
738
|
+
}
|
|
739
|
+
function RegionAccountCard({ operations, t, activeLocale, modelSnapshot }) {
|
|
621
740
|
const [credentialState, setCredentialState] = (0, react.useState)({ status: "loading" });
|
|
622
741
|
const [accountState, setAccountState] = (0, react.useState)({ status: "idle" });
|
|
623
742
|
const [refreshing, setRefreshing] = (0, react.useState)(false);
|
|
624
743
|
const [credentialRevision, setCredentialRevision] = (0, react.useState)(0);
|
|
625
|
-
const [modelSaveError, setModelSaveError] = (0, react.useState)();
|
|
626
744
|
const latestAccountRequest = (0, react.useRef)(0);
|
|
627
|
-
const
|
|
745
|
+
const latestCredentialRequest = (0, react.useRef)(0);
|
|
746
|
+
(0, react.useLayoutEffect)(() => () => {
|
|
747
|
+
latestAccountRequest.current++;
|
|
748
|
+
latestCredentialRequest.current++;
|
|
749
|
+
}, []);
|
|
628
750
|
const loadCredential = (0, react.useCallback)(async () => {
|
|
751
|
+
const requestId = ++latestCredentialRequest.current;
|
|
629
752
|
const info = await operations.describe();
|
|
753
|
+
if (requestId !== latestCredentialRequest.current) return;
|
|
630
754
|
setCredentialState(info === void 0 ? { status: "failed" } : {
|
|
631
755
|
status: "ready",
|
|
632
756
|
info
|
|
@@ -781,13 +905,14 @@ window.__ModuleLoader__.load({
|
|
|
781
905
|
const { profile, usage, plan } = accountState.account;
|
|
782
906
|
const userQuota = usage?.userQuota;
|
|
783
907
|
const orgPackage = usage?.orgResourcePackage;
|
|
908
|
+
const addOnQuota = usage?.addOnQuota;
|
|
784
909
|
const dedicatedPackages = usage?.dedicatedResourcePackages ?? [];
|
|
785
910
|
const locale = activeLocale();
|
|
786
911
|
const avatarInitial = (profile.name || profile.email || "Q").charAt(0).toUpperCase();
|
|
787
912
|
const resetDate = formatResetDate(usage?.expiresAt);
|
|
788
913
|
const planExpiry = plan?.endDate ? formatResetDate(plan.endDate) : void 0;
|
|
789
914
|
const isSuspended = plan?.organization?.isSuspended === true;
|
|
790
|
-
const hasQuota = (userQuota?.total ?? 0) > 0 || (orgPackage?.total ?? 0) > 0 || dedicatedPackages.some((pkg) => pkg.total > 0);
|
|
915
|
+
const hasQuota = (userQuota?.total ?? 0) > 0 || (orgPackage?.total ?? 0) > 0 || (addOnQuota?.total ?? 0) > 0 || dedicatedPackages.some((pkg) => pkg.total > 0);
|
|
791
916
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
792
917
|
className: QoderCredentialCard_module_css_default.accountSection,
|
|
793
918
|
children: [
|
|
@@ -844,6 +969,7 @@ window.__ModuleLoader__.load({
|
|
|
844
969
|
}) : null,
|
|
845
970
|
renderQuota("userQuota", t("userQuotaTitle"), userQuota, { resetDate }),
|
|
846
971
|
renderQuota("orgResource", t("orgResourceTitle"), orgPackage, { resetDate }),
|
|
972
|
+
renderQuota("addOnQuota", t("addOnQuotaTitle"), addOnQuota),
|
|
847
973
|
dedicatedPackages.map((pkg, index) => renderQuota(`dedicatedResource-${pkg.id ?? index}`, resolveLocalizedText(pkg.title, locale) ?? t("dedicatedResourceTitle"), pkg, {
|
|
848
974
|
expiresAt: formatResetDate(pkg.expiresAt),
|
|
849
975
|
description: resolveLocalizedText(pkg.description, locale)
|
|
@@ -922,16 +1048,6 @@ window.__ModuleLoader__.load({
|
|
|
922
1048
|
const currentRegion = regionOf(modelSnapshot.value);
|
|
923
1049
|
const currentModels = modelsOf(modelSnapshot.value, currentRegion);
|
|
924
1050
|
const canModifyModels = modelSnapshot.status === "ready" && modelSnapshot.writable;
|
|
925
|
-
const handleModelsChange = async (models) => {
|
|
926
|
-
if (!canModifyModels) return;
|
|
927
|
-
setModelSaveError(void 0);
|
|
928
|
-
const failure = validateModelCatalog(models);
|
|
929
|
-
if (failure) {
|
|
930
|
-
setModelSaveError(t(failure));
|
|
931
|
-
return;
|
|
932
|
-
}
|
|
933
|
-
if (!await operations.storeModels(currentRegion, models)) setModelSaveError(t("saveFailed"));
|
|
934
|
-
};
|
|
935
1051
|
const renderModelCatalogSection = () => {
|
|
936
1052
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
|
|
937
1053
|
className: QoderCredentialCard_module_css_default.customized,
|
|
@@ -941,29 +1057,23 @@ window.__ModuleLoader__.load({
|
|
|
941
1057
|
className: QoderCredentialCard_module_css_default.modelSummaryBadge,
|
|
942
1058
|
children: t("modelsEnabled", { count: currentModels.length })
|
|
943
1059
|
})]
|
|
944
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.
|
|
1060
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
945
1061
|
className: QoderCredentialCard_module_css_default.customizedBody,
|
|
946
|
-
children:
|
|
1062
|
+
children: modelSnapshot.status === "loading" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
947
1063
|
className: QoderCredentialCard_module_css_default.hint,
|
|
948
1064
|
children: t("modelsLoading")
|
|
949
1065
|
}) : modelSnapshot.status === "unavailable" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
950
1066
|
className: QoderCredentialCard_module_css_default.error,
|
|
951
1067
|
children: t("modelsUnavailable")
|
|
952
1068
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(QoderModelCatalog, {
|
|
1069
|
+
region: currentRegion,
|
|
953
1070
|
operations,
|
|
954
1071
|
models: currentModels,
|
|
955
1072
|
disabled: !canModifyModels,
|
|
956
1073
|
fetchDisabled: !configured,
|
|
957
1074
|
hideTitle: true,
|
|
958
|
-
t
|
|
959
|
-
|
|
960
|
-
handleModelsChange(models);
|
|
961
|
-
}
|
|
962
|
-
}), modelSaveError ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
963
|
-
className: QoderCredentialCard_module_css_default.error,
|
|
964
|
-
role: "alert",
|
|
965
|
-
children: modelSaveError
|
|
966
|
-
}) : null]
|
|
1075
|
+
t
|
|
1076
|
+
})
|
|
967
1077
|
})]
|
|
968
1078
|
});
|
|
969
1079
|
};
|
|
@@ -1227,17 +1337,102 @@ window.__ModuleLoader__.load({
|
|
|
1227
1337
|
"qoder-subscription"
|
|
1228
1338
|
].includes(provider ?? "");
|
|
1229
1339
|
}
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1340
|
+
//#endregion
|
|
1341
|
+
//#region src/client/session-tier-state.ts
|
|
1342
|
+
/** A mounted view of the host's accepted selection; it owns no manual tier map. */
|
|
1343
|
+
var SessionTierState = class {
|
|
1344
|
+
snapshot = {
|
|
1345
|
+
loading: true,
|
|
1346
|
+
saving: false,
|
|
1347
|
+
error: false
|
|
1348
|
+
};
|
|
1349
|
+
listeners = /* @__PURE__ */ new Set();
|
|
1350
|
+
generation = 0;
|
|
1351
|
+
active = false;
|
|
1352
|
+
lifetime = 0;
|
|
1353
|
+
unsubscribe;
|
|
1354
|
+
scope;
|
|
1355
|
+
operations;
|
|
1356
|
+
constructor(scope, operations) {
|
|
1357
|
+
this.scope = scope;
|
|
1358
|
+
this.operations = operations;
|
|
1239
1359
|
}
|
|
1240
|
-
|
|
1360
|
+
getSnapshot = () => this.snapshot;
|
|
1361
|
+
subscribe = (listener) => {
|
|
1362
|
+
this.listeners.add(listener);
|
|
1363
|
+
return () => {
|
|
1364
|
+
this.listeners.delete(listener);
|
|
1365
|
+
};
|
|
1366
|
+
};
|
|
1367
|
+
publish(value) {
|
|
1368
|
+
this.snapshot = {
|
|
1369
|
+
...this.snapshot,
|
|
1370
|
+
...value
|
|
1371
|
+
};
|
|
1372
|
+
for (const listener of this.listeners) listener();
|
|
1373
|
+
}
|
|
1374
|
+
activate() {
|
|
1375
|
+
this.active = true;
|
|
1376
|
+
this.lifetime++;
|
|
1377
|
+
this.publish({ saving: false });
|
|
1378
|
+
this.unsubscribe = this.operations.subscribeSessionTiers?.(this.scope, () => {
|
|
1379
|
+
this.refresh();
|
|
1380
|
+
});
|
|
1381
|
+
this.refresh();
|
|
1382
|
+
}
|
|
1383
|
+
dispose() {
|
|
1384
|
+
this.active = false;
|
|
1385
|
+
this.generation++;
|
|
1386
|
+
this.unsubscribe?.();
|
|
1387
|
+
this.unsubscribe = void 0;
|
|
1388
|
+
}
|
|
1389
|
+
matches(value) {
|
|
1390
|
+
return !!value && value.region === this.scope.region && value.sessionId === this.scope.sessionId && value.modelId === this.scope.modelId;
|
|
1391
|
+
}
|
|
1392
|
+
async refresh() {
|
|
1393
|
+
if (!this.active) return;
|
|
1394
|
+
const generation = ++this.generation;
|
|
1395
|
+
this.publish({ loading: true });
|
|
1396
|
+
try {
|
|
1397
|
+
const selection = await this.operations.readSessionTier?.(this.scope);
|
|
1398
|
+
if (this.active && generation === this.generation) this.publish(this.matches(selection) ? {
|
|
1399
|
+
selection,
|
|
1400
|
+
loading: false,
|
|
1401
|
+
error: false
|
|
1402
|
+
} : {
|
|
1403
|
+
loading: false,
|
|
1404
|
+
error: true
|
|
1405
|
+
});
|
|
1406
|
+
} catch {
|
|
1407
|
+
if (this.active && generation === this.generation) this.publish({
|
|
1408
|
+
loading: false,
|
|
1409
|
+
error: true
|
|
1410
|
+
});
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
async select(tierKey) {
|
|
1414
|
+
if (!this.active || this.snapshot.loading || this.snapshot.saving || !this.snapshot.selection) return;
|
|
1415
|
+
if (tierKey === this.snapshot.selection.tierKey && !this.snapshot.error) return;
|
|
1416
|
+
const generation = ++this.generation;
|
|
1417
|
+
const lifetime = this.lifetime;
|
|
1418
|
+
this.publish({
|
|
1419
|
+
saving: true,
|
|
1420
|
+
error: false
|
|
1421
|
+
});
|
|
1422
|
+
try {
|
|
1423
|
+
const { sessionId, modelId, region } = this.scope;
|
|
1424
|
+
const selection = await this.operations.setSessionTier?.(sessionId, modelId, tierKey, region);
|
|
1425
|
+
if (this.active && generation === this.generation) this.publish(this.matches(selection) ? {
|
|
1426
|
+
selection,
|
|
1427
|
+
error: false
|
|
1428
|
+
} : { error: true });
|
|
1429
|
+
} catch {
|
|
1430
|
+
if (this.active && generation === this.generation) this.publish({ error: true });
|
|
1431
|
+
} finally {
|
|
1432
|
+
if (this.active && lifetime === this.lifetime) this.publish({ saving: false });
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
};
|
|
1241
1436
|
//#endregion
|
|
1242
1437
|
//#region \0dsh-css:/home/runner/work/dsh-provider-qoder/dsh-provider-qoder/src/client/QoderContextSelect.module.css.mjs
|
|
1243
1438
|
const css = ".hiDrJa_root{align-items:center;display:inline-flex;position:relative}.hiDrJa_trigger{height:28px;color:var(--dsw-alias-label-secondary);cursor:pointer;user-select:none;background:0 0;border:none;border-radius:24px;outline:none;align-items:center;gap:4px;padding:0 8px;font-size:13px;font-weight:500;line-height:20px;transition:background .12s,color .12s;display:flex}.hiDrJa_trigger:hover,.hiDrJa_triggerActive{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.hiDrJa_trigger:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-border-l3)}.hiDrJa_icon{color:var(--dsw-alias-label-caption);justify-content:center;align-items:center;display:inline-flex}.hiDrJa_label{white-space:nowrap}.hiDrJa_chevron{color:var(--dsw-alias-label-caption);align-items:center;transition:transform .12s;display:inline-flex}.hiDrJa_chevronOpen{transform:rotate(180deg)}.hiDrJa_menu{z-index:1100;background:var(--dsw-menu-surface-fill);min-width:180px;backdrop-filter:var(--dsw-menu-backdrop-filter);border:1px solid var(--dsw-alias-border-l1);box-shadow:var(--dsw-elevation-prominent);color:var(--dsw-alias-label-primary);border-radius:14px;flex-direction:column;padding:4px;animation:.12s hiDrJa_fadeIn;display:flex;position:absolute;bottom:calc(100% + 8px);right:0}@keyframes hiDrJa_fadeIn{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}.hiDrJa_menuHeader{text-transform:uppercase;letter-spacing:.5px;color:var(--dsw-alias-label-tertiary);border-bottom:1px solid var(--dsw-alias-border-l1);margin-bottom:4px;padding:6px 10px 4px;font-size:11px;font-weight:600}.hiDrJa_option{box-sizing:border-box;width:100%;color:inherit;cursor:pointer;text-align:left;background:0 0;border:none;border-radius:8px;justify-content:space-between;align-items:center;gap:8px;padding:6px 10px;font-size:13px;line-height:20px;transition:background .1s;display:flex}.hiDrJa_option:hover{background:var(--dsw-alias-interactive-bg-hover)}.hiDrJa_optionSelected{color:var(--dsw-alias-state-brand);font-weight:600}.hiDrJa_optionInfo{align-items:center;gap:6px;display:flex}.hiDrJa_tag{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-caption);border-radius:4px;padding:1px 5px;font-size:11px;font-weight:500}.hiDrJa_check{color:var(--dsw-alias-state-brand);align-items:center;font-size:12px;display:inline-flex}.hiDrJa_error{color:var(--dsw-alias-label-primary);max-width:240px;font-size:12px}";
|
|
@@ -1270,30 +1465,9 @@ window.__ModuleLoader__.load({
|
|
|
1270
1465
|
//#endregion
|
|
1271
1466
|
//#region src/client/QoderContextSelect.tsx
|
|
1272
1467
|
function QoderContextSelect(props) {
|
|
1273
|
-
const { directory, operations,
|
|
1274
|
-
const [open, setOpen] = (0, react.useState)(false);
|
|
1275
|
-
const [manualTiers, setManualTiers] = (0, react.useState)({});
|
|
1276
|
-
const [pending, setPending] = (0, react.useState)(false);
|
|
1277
|
-
const [error, setError] = (0, react.useState)(null);
|
|
1278
|
-
const saving = (0, react.useRef)(false);
|
|
1279
|
-
const rootRef = (0, react.useRef)(null);
|
|
1468
|
+
const { directory, operations, sessionId, useProjection } = props;
|
|
1280
1469
|
const directoryState = (0, react.useSyncExternalStore)((fn) => directory ? directory.subscribe(fn) : () => () => {}, () => directory ? directory.getSnapshot() : null);
|
|
1281
1470
|
const modelSnapshot = (0, react.useSyncExternalStore)((fn) => operations ? operations.subscribeModels(fn) : () => () => {}, () => operations ? operations.getModelSnapshot() : null);
|
|
1282
|
-
(0, react.useEffect)(() => {
|
|
1283
|
-
if (!open) return;
|
|
1284
|
-
const handleClickOutside = (e) => {
|
|
1285
|
-
if (rootRef.current && !rootRef.current.contains(e.target)) setOpen(false);
|
|
1286
|
-
};
|
|
1287
|
-
const handleKeyDown = (e) => {
|
|
1288
|
-
if (e.key === "Escape") setOpen(false);
|
|
1289
|
-
};
|
|
1290
|
-
document.addEventListener("mousedown", handleClickOutside);
|
|
1291
|
-
document.addEventListener("keydown", handleKeyDown);
|
|
1292
|
-
return () => {
|
|
1293
|
-
document.removeEventListener("mousedown", handleClickOutside);
|
|
1294
|
-
document.removeEventListener("keydown", handleKeyDown);
|
|
1295
|
-
};
|
|
1296
|
-
}, [open]);
|
|
1297
1471
|
const modelProj = useProjection ? useProjection("modelSelection") : void 0;
|
|
1298
1472
|
const pressure = useProjection ? useProjection("contextPressure") : void 0;
|
|
1299
1473
|
if (!operations) return null;
|
|
@@ -1309,34 +1483,71 @@ window.__ModuleLoader__.load({
|
|
|
1309
1483
|
const currentProviderId = current.provider ?? (current.model.includes("/") ? current.model.split("/")[0] : void 0);
|
|
1310
1484
|
const activeModel = models.find((m) => m.id === currentModelId || m.id === current.model);
|
|
1311
1485
|
if (!isQoderProvider(currentProviderId) || !activeModel) return null;
|
|
1312
|
-
|
|
1313
|
-
if (
|
|
1314
|
-
|
|
1486
|
+
if (contextTiersOf(activeModel).length <= 1) return null;
|
|
1487
|
+
if (!sessionId) return null;
|
|
1488
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SessionContextSelect, {
|
|
1489
|
+
...props,
|
|
1490
|
+
sessionId,
|
|
1491
|
+
operations,
|
|
1492
|
+
region,
|
|
1493
|
+
activeModel,
|
|
1494
|
+
historicalWindow: pressure?.contextWindow,
|
|
1495
|
+
lastUsed: modelProj?.lastUsed
|
|
1496
|
+
}, JSON.stringify([
|
|
1315
1497
|
region,
|
|
1316
1498
|
sessionId,
|
|
1317
1499
|
activeModel.id
|
|
1318
|
-
])
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
const
|
|
1323
|
-
const
|
|
1500
|
+
]));
|
|
1501
|
+
}
|
|
1502
|
+
function SessionContextSelect({ operations, sessionId, region, activeModel, t, historicalWindow, lastUsed }) {
|
|
1503
|
+
const [open, setOpen] = (0, react.useState)(false);
|
|
1504
|
+
const rootRef = (0, react.useRef)(null);
|
|
1505
|
+
const state = (0, react.useMemo)(() => new SessionTierState({
|
|
1506
|
+
region,
|
|
1507
|
+
sessionId,
|
|
1508
|
+
modelId: activeModel.id
|
|
1509
|
+
}, operations), [
|
|
1510
|
+
region,
|
|
1511
|
+
sessionId,
|
|
1512
|
+
activeModel.id,
|
|
1513
|
+
operations
|
|
1514
|
+
]);
|
|
1515
|
+
const snapshot = (0, react.useSyncExternalStore)(state.subscribe, state.getSnapshot);
|
|
1516
|
+
(0, react.useLayoutEffect)(() => {
|
|
1517
|
+
state.activate();
|
|
1518
|
+
return () => state.dispose();
|
|
1519
|
+
}, [state]);
|
|
1520
|
+
(0, react.useEffect)(() => {
|
|
1521
|
+
state.refresh();
|
|
1522
|
+
}, [
|
|
1523
|
+
state,
|
|
1524
|
+
JSON.stringify(activeModel),
|
|
1525
|
+
historicalWindow,
|
|
1526
|
+
lastUsed?.provider,
|
|
1527
|
+
lastUsed?.model
|
|
1528
|
+
]);
|
|
1529
|
+
(0, react.useEffect)(() => {
|
|
1530
|
+
if (!open) return;
|
|
1531
|
+
const handleClickOutside = (e) => {
|
|
1532
|
+
if (rootRef.current && !rootRef.current.contains(e.target)) setOpen(false);
|
|
1533
|
+
};
|
|
1534
|
+
const handleKeyDown = (e) => {
|
|
1535
|
+
if (e.key === "Escape") setOpen(false);
|
|
1536
|
+
};
|
|
1537
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
1538
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
1539
|
+
return () => {
|
|
1540
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
1541
|
+
document.removeEventListener("keydown", handleKeyDown);
|
|
1542
|
+
};
|
|
1543
|
+
}, [open]);
|
|
1544
|
+
const tiers = contextTiersOf(activeModel);
|
|
1545
|
+
const currentTier = snapshot.selection;
|
|
1546
|
+
const selectedTierKey = currentTier?.tierKey;
|
|
1547
|
+
const pending = snapshot.loading || snapshot.saving;
|
|
1548
|
+
const selectTier = (tierKey) => {
|
|
1324
1549
|
setOpen(false);
|
|
1325
|
-
|
|
1326
|
-
if (tierKey === selectedTierKey && error?.key !== sessionKey) return;
|
|
1327
|
-
saving.current = true;
|
|
1328
|
-
setPending(true);
|
|
1329
|
-
setError(null);
|
|
1330
|
-
try {
|
|
1331
|
-
if (await saveContextSelection(operations, sessionId, region, activeModel.id, tierKey) === "saved") setManualTiers((prev) => ({
|
|
1332
|
-
...prev,
|
|
1333
|
-
[sessionKey]: tierKey
|
|
1334
|
-
}));
|
|
1335
|
-
else setError({ key: sessionKey });
|
|
1336
|
-
} finally {
|
|
1337
|
-
saving.current = false;
|
|
1338
|
-
setPending(false);
|
|
1339
|
-
}
|
|
1550
|
+
state.select(tierKey);
|
|
1340
1551
|
};
|
|
1341
1552
|
const defaultSuffix = t ? t("contextTierDefaultSuffix").replace(/[()()]/g, "") : "默认";
|
|
1342
1553
|
const selectTitle = t ? t("contextSelectTitle") : "选择上下文大小";
|
|
@@ -1348,10 +1559,10 @@ window.__ModuleLoader__.load({
|
|
|
1348
1559
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
1349
1560
|
type: "button",
|
|
1350
1561
|
className: `${QoderContextSelect_module_css_default.trigger} ${open ? QoderContextSelect_module_css_default.triggerActive : ""}`,
|
|
1351
|
-
disabled: pending || !
|
|
1562
|
+
disabled: pending || !currentTier,
|
|
1352
1563
|
"aria-expanded": open,
|
|
1353
1564
|
"aria-label": selectTitle,
|
|
1354
|
-
title: `${selectTitle}: ${formatContextTokens(currentTier.tokenCount)}`,
|
|
1565
|
+
title: `${selectTitle}: ${currentTier ? formatContextTokens(currentTier.tokenCount) : "…"}`,
|
|
1355
1566
|
onClick: () => setOpen((prev) => !prev),
|
|
1356
1567
|
children: [
|
|
1357
1568
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
@@ -1366,7 +1577,7 @@ window.__ModuleLoader__.load({
|
|
|
1366
1577
|
}),
|
|
1367
1578
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1368
1579
|
className: QoderContextSelect_module_css_default.label,
|
|
1369
|
-
children: formatContextTokens(currentTier.tokenCount)
|
|
1580
|
+
children: currentTier ? formatContextTokens(currentTier.tokenCount) : "…"
|
|
1370
1581
|
}),
|
|
1371
1582
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1372
1583
|
className: `${QoderContextSelect_module_css_default.chevron} ${open ? QoderContextSelect_module_css_default.chevronOpen : ""}`,
|
|
@@ -1384,10 +1595,21 @@ window.__ModuleLoader__.load({
|
|
|
1384
1595
|
})
|
|
1385
1596
|
]
|
|
1386
1597
|
}),
|
|
1387
|
-
error &&
|
|
1598
|
+
snapshot.error && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1388
1599
|
role: "alert",
|
|
1389
1600
|
className: QoderContextSelect_module_css_default.error,
|
|
1390
|
-
children:
|
|
1601
|
+
children: [
|
|
1602
|
+
t ? t("contextSelectFailed") : "contextSelectFailed",
|
|
1603
|
+
" ",
|
|
1604
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1605
|
+
type: "button",
|
|
1606
|
+
disabled: pending,
|
|
1607
|
+
onClick: () => {
|
|
1608
|
+
state.refresh();
|
|
1609
|
+
},
|
|
1610
|
+
children: t ? t("refresh") : "refresh"
|
|
1611
|
+
})
|
|
1612
|
+
]
|
|
1391
1613
|
}),
|
|
1392
1614
|
open && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1393
1615
|
className: QoderContextSelect_module_css_default.menu,
|
|
@@ -1401,6 +1623,7 @@ window.__ModuleLoader__.load({
|
|
|
1401
1623
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
1402
1624
|
type: "button",
|
|
1403
1625
|
role: "menuitemradio",
|
|
1626
|
+
disabled: pending,
|
|
1404
1627
|
"aria-checked": isSelected,
|
|
1405
1628
|
className: `${QoderContextSelect_module_css_default.option} ${isSelected ? QoderContextSelect_module_css_default.optionSelected : ""}`,
|
|
1406
1629
|
onClick: () => {
|
|
@@ -1456,6 +1679,97 @@ window.__ModuleLoader__.load({
|
|
|
1456
1679
|
return typeof value === "string" && qoderRpcErrorCodes.includes(value);
|
|
1457
1680
|
}
|
|
1458
1681
|
//#endregion
|
|
1682
|
+
//#region src/client/session-tier-events.ts
|
|
1683
|
+
/** One authenticated host change stream shared by the mounted composer controls. */
|
|
1684
|
+
function createSessionTierEvents(send = (...args) => globalThis.fetch(...args)) {
|
|
1685
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
1686
|
+
let active;
|
|
1687
|
+
let disposed = false;
|
|
1688
|
+
const notify = (scope) => {
|
|
1689
|
+
for (const entry of listeners) if (!scope || entry.scope.region === scope.region && entry.scope.sessionId === scope.sessionId && entry.scope.modelId === scope.modelId) try {
|
|
1690
|
+
entry.listener();
|
|
1691
|
+
} catch {}
|
|
1692
|
+
};
|
|
1693
|
+
const start = () => {
|
|
1694
|
+
if (active || disposed || !listeners.size) return;
|
|
1695
|
+
const controller = new AbortController();
|
|
1696
|
+
active = controller;
|
|
1697
|
+
follow(controller).finally(() => {
|
|
1698
|
+
if (active === controller) active = void 0;
|
|
1699
|
+
});
|
|
1700
|
+
};
|
|
1701
|
+
async function follow(controller) {
|
|
1702
|
+
let delay = 1e3;
|
|
1703
|
+
while (!controller.signal.aborted) {
|
|
1704
|
+
try {
|
|
1705
|
+
const response = await send(qoderRpcPath("sessionTierEvents"), {
|
|
1706
|
+
method: "POST",
|
|
1707
|
+
credentials: "include",
|
|
1708
|
+
signal: controller.signal,
|
|
1709
|
+
headers: { "content-type": "application/json" },
|
|
1710
|
+
body: "{}"
|
|
1711
|
+
});
|
|
1712
|
+
if (!response.ok || !response.body) throw new Error("Context tier stream unavailable");
|
|
1713
|
+
const reader = response.body.getReader();
|
|
1714
|
+
const decoder = new TextDecoder();
|
|
1715
|
+
let buffered = "";
|
|
1716
|
+
try {
|
|
1717
|
+
while (!controller.signal.aborted) {
|
|
1718
|
+
const { value, done } = await reader.read();
|
|
1719
|
+
if (done) break;
|
|
1720
|
+
buffered += decoder.decode(value, { stream: true });
|
|
1721
|
+
let newline;
|
|
1722
|
+
while ((newline = buffered.indexOf("\n")) >= 0) {
|
|
1723
|
+
const item = JSON.parse(buffered.slice(0, newline));
|
|
1724
|
+
buffered = buffered.slice(newline + 1);
|
|
1725
|
+
delay = 1e3;
|
|
1726
|
+
if (item.ready === true) notify();
|
|
1727
|
+
else if ((item.region === "global" || item.region === "china") && typeof item.sessionId === "string" && typeof item.modelId === "string") notify(item);
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
} finally {
|
|
1731
|
+
await reader.cancel().catch(() => {});
|
|
1732
|
+
reader.releaseLock();
|
|
1733
|
+
}
|
|
1734
|
+
} catch {}
|
|
1735
|
+
if (controller.signal.aborted) break;
|
|
1736
|
+
await new Promise((resolve) => {
|
|
1737
|
+
const finish = () => {
|
|
1738
|
+
clearTimeout(timer);
|
|
1739
|
+
controller.signal.removeEventListener("abort", finish);
|
|
1740
|
+
resolve();
|
|
1741
|
+
};
|
|
1742
|
+
const timer = setTimeout(finish, delay);
|
|
1743
|
+
controller.signal.addEventListener("abort", finish, { once: true });
|
|
1744
|
+
});
|
|
1745
|
+
delay = Math.min(delay * 2, 3e4);
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
return {
|
|
1749
|
+
subscribe(scope, listener) {
|
|
1750
|
+
const entry = {
|
|
1751
|
+
scope,
|
|
1752
|
+
listener
|
|
1753
|
+
};
|
|
1754
|
+
listeners.add(entry);
|
|
1755
|
+
start();
|
|
1756
|
+
return () => {
|
|
1757
|
+
listeners.delete(entry);
|
|
1758
|
+
if (!listeners.size) {
|
|
1759
|
+
active?.abort();
|
|
1760
|
+
active = void 0;
|
|
1761
|
+
}
|
|
1762
|
+
};
|
|
1763
|
+
},
|
|
1764
|
+
dispose() {
|
|
1765
|
+
disposed = true;
|
|
1766
|
+
listeners.clear();
|
|
1767
|
+
active?.abort();
|
|
1768
|
+
active = void 0;
|
|
1769
|
+
}
|
|
1770
|
+
};
|
|
1771
|
+
}
|
|
1772
|
+
//#endregion
|
|
1459
1773
|
//#region src/client/rpc-client.ts
|
|
1460
1774
|
/**
|
|
1461
1775
|
* Browser caller for the Qoder settings RPC.
|
|
@@ -1551,21 +1865,9 @@ window.__ModuleLoader__.load({
|
|
|
1551
1865
|
}), "provider-qoder: credential copy");
|
|
1552
1866
|
const credentials = ctx.remote.credentials;
|
|
1553
1867
|
const rpc = createQoderRpcCaller();
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
const readModelSnapshot = () =>
|
|
1557
|
-
const snapshot = modelScope.getSnapshot();
|
|
1558
|
-
if (snapshot.value) {
|
|
1559
|
-
const region = snapshot.value.region ?? "global";
|
|
1560
|
-
if (historyRegion !== void 0 && historyRegion !== region) historyRegionChanged = true;
|
|
1561
|
-
historyRegion = region;
|
|
1562
|
-
}
|
|
1563
|
-
return snapshot;
|
|
1564
|
-
};
|
|
1565
|
-
readModelSnapshot();
|
|
1566
|
-
ctx.effect(() => modelScope.subscribe(() => {
|
|
1567
|
-
readModelSnapshot();
|
|
1568
|
-
}), "provider-qoder: context history region");
|
|
1868
|
+
const tierEvents = createSessionTierEvents();
|
|
1869
|
+
ctx.effect(() => () => tierEvents.dispose(), "provider-qoder: session context tier events");
|
|
1870
|
+
const readModelSnapshot = () => modelScope.getSnapshot();
|
|
1569
1871
|
const operations = {
|
|
1570
1872
|
describe: async () => {
|
|
1571
1873
|
try {
|
|
@@ -1591,16 +1893,15 @@ window.__ModuleLoader__.load({
|
|
|
1591
1893
|
},
|
|
1592
1894
|
getAccount: async (force) => await rpc.call("account", { force }),
|
|
1593
1895
|
getModelSnapshot: readModelSnapshot,
|
|
1594
|
-
canRestoreContextHistory: () => !historyRegionChanged,
|
|
1595
1896
|
subscribeModels: (listener) => modelScope.subscribe(listener),
|
|
1596
1897
|
storeModels: async (region, models) => {
|
|
1597
1898
|
try {
|
|
1598
|
-
const
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
}) !== false;
|
|
1899
|
+
const clonedModels = JSON.parse(JSON.stringify(models));
|
|
1900
|
+
return await modelScope.mutate([{
|
|
1901
|
+
op: "set",
|
|
1902
|
+
path: ["modelsByRegion", region],
|
|
1903
|
+
value: clonedModels
|
|
1904
|
+
}]) !== false;
|
|
1604
1905
|
} catch {
|
|
1605
1906
|
return false;
|
|
1606
1907
|
}
|
|
@@ -1622,16 +1923,22 @@ window.__ModuleLoader__.load({
|
|
|
1622
1923
|
discoverModels: async () => await rpc.call("models", {}),
|
|
1623
1924
|
setSessionTier: async (sessionId, modelId, tierKey, region) => {
|
|
1624
1925
|
try {
|
|
1625
|
-
|
|
1926
|
+
const res = await rpc.call("sessionTier", {
|
|
1626
1927
|
sessionId,
|
|
1627
1928
|
modelId,
|
|
1628
1929
|
tierKey,
|
|
1629
1930
|
region
|
|
1630
|
-
})
|
|
1931
|
+
});
|
|
1932
|
+
return res.ok ? res.value : void 0;
|
|
1631
1933
|
} catch {
|
|
1632
|
-
return
|
|
1934
|
+
return;
|
|
1633
1935
|
}
|
|
1634
1936
|
},
|
|
1937
|
+
readSessionTier: async (scope) => {
|
|
1938
|
+
const result = await rpc.call("readSessionTier", scope);
|
|
1939
|
+
return result.ok ? result.value : void 0;
|
|
1940
|
+
},
|
|
1941
|
+
subscribeSessionTiers: (scope, listener) => tierEvents.subscribe(scope, listener),
|
|
1635
1942
|
subscribe: (listener) => ctx.remote.$on("credentials/reference-updated", (ref) => {
|
|
1636
1943
|
if (ref === "QODER_MANAGED_CREDENTIAL") listener();
|
|
1637
1944
|
})
|