dsh-plugin-litellm-bridge 2.0.6 → 2.0.7

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.
@@ -596,8 +596,16 @@ window.__ModuleLoader__.load({
596
596
  : h('div', { style: { opacity: 0.6 } }, '加载中…'),
597
597
  );
598
598
  }
599
- const prof = draft[pid];
600
- if (!prof) return h('div', { style: { opacity: 0.6 } }, '没有可用的 profile');
599
+ // 【防御】prof 可能是空对象或残缺结构。
600
+ // 先判断存在性再往下渲染,否则后面读 prof.agents[at] 会抛 TypeError。
601
+ const prof = draft?.[pid];
602
+ if (!prof || typeof prof !== 'object') {
603
+ return h('div', { style: S.notice('warn') },
604
+ `profile「${pid}」的数据不完整(缺少 agents 配置)。`,
605
+ h('div', { style: { ...S.hint } },
606
+ '可能来自旧版本的状态文件。可以在上方点击「+ 新建」创建新 profile,' +
607
+ '或删除当前 profile 后重建。'));
608
+ }
601
609
 
602
610
  const setAgent = (at, patch) =>
603
611
  setDraft((d) => {
@@ -750,7 +758,10 @@ window.__ModuleLoader__.load({
750
758
 
751
759
  // ---- 三条链:root / coder / research ----
752
760
  AGENT_TYPES.map((at) => {
753
- const cfg = prof.agents[at];
761
+ // 【防御】cfg 可能缺失(旧版 state.json 结构不全 / 宿主返回残缺)。
762
+ // 直接读 cfg.primary 会抛 TypeError,进而让整页崩掉。
763
+ const cfg = prof?.agents?.[at]
764
+ || { label: at, primary: '', timeoutMs: 600000, fallbacks: [] };
754
765
  const isSub = at !== 'root';
755
766
  const groupLabel = at === 'root' ? 'root · 主代理降级链'
756
767
  : at === 'coder' ? 'coder · 子代理降级链(Coder)'
@@ -1275,12 +1286,47 @@ window.__ModuleLoader__.load({
1275
1286
  );
1276
1287
  }
1277
1288
 
1278
- /** 用 try/catch 包住子渲染,异常时就地降级为错误块。 */
1279
- function Guard({ title, children }) {
1280
- try {
1281
- return children();
1282
- } catch (err) {
1283
- return h(ErrorBox, { title, error: err });
1289
+ /**
1290
+ * 错误边界 —— 必须是**真正的 React class 错误边界**。
1291
+ *
1292
+ * 【为什么之前那版是无效的】
1293
+ * 原来写的是:
1294
+ * function Guard({ title, children }) {
1295
+ * try { return children(); } catch (err) { ... }
1296
+ * }
1297
+ * 用法:h(Guard, {}, () => h(RouteTab, {...}))
1298
+ *
1299
+ * 问题:children() 只是**创建元素**(h() 是纯函数,返回一个普通对象),
1300
+ * 此时并不会渲染 RouteTab。真正的渲染发生在 Guard 返回之后、React 内部。
1301
+ * 所以 RouteTab 抛的异常**根本不在这个 try 里**,catch 永远抓不到,
1302
+ * React 直接向上冒泡 → 整个设置页空白(连错误框都没有)。
1303
+ * 这正是「点路由配置后整页全空、什么都不显示」的原因。
1304
+ *
1305
+ * 正确做法:class 组件的 getDerivedStateFromError / componentDidCatch,
1306
+ * 这是 React 唯一能捕获子组件渲染异常的机制。
1307
+ */
1308
+ class Guard extends React.Component {
1309
+ constructor(props) {
1310
+ super(props);
1311
+ this.state = { error: null };
1312
+ }
1313
+ static getDerivedStateFromError(error) {
1314
+ return { error };
1315
+ }
1316
+ componentDidCatch(error, info) {
1317
+ // 打到控制台便于排查(浏览器 F12)
1318
+ try {
1319
+ console.error('[litellm-bridge] 渲染异常:', error, info?.componentStack);
1320
+ } catch { /* ignore */ }
1321
+ }
1322
+ render() {
1323
+ if (this.state.error) {
1324
+ return h(ErrorBox, { title: this.props.title, error: this.state.error });
1325
+ }
1326
+ // children 既可能是元素也可能是函数,两种都支持
1327
+ return typeof this.props.children === 'function'
1328
+ ? this.props.children()
1329
+ : this.props.children;
1284
1330
  }
1285
1331
  }
1286
1332
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-plugin-litellm-bridge",
3
- "version": "2.0.6",
3
+ "version": "2.0.7",
4
4
  "description": "LiteLLM gateway bridge for DSH: assemble root / coder / research fallback chains from the DSH model pool and hand the order to a LiteLLM gateway that executes retries and fallbacks. The plugin performs no routing itself.",
5
5
  "keywords": [
6
6
  "dsh",