@dsh-plus/llm-pi 0.1.27 → 0.1.29

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 CHANGED
@@ -399,6 +399,73 @@ ${extra}
399
399
  }));
400
400
  }
401
401
  //#endregion
402
+ //#region src/client/constants.ts
403
+ /**
404
+ * 浏览器半内联常量:与服务端 packages/llm-pi/src/config.ts、compat.ts 逐字对齐。
405
+ * 浏览器半不能 import 服务端模块(tsdown 只打包 client 侧入口),
406
+ * 改动服务端这些常量时必须同步本文件。
407
+ * @module llm-pi/client/constants
408
+ */
409
+ /** 协议枚举(来源:config.ts PROTOCOL_IDS)。 */
410
+ const PROTOCOL_IDS = [
411
+ "openai-completions",
412
+ "openai-responses",
413
+ "anthropic-messages"
414
+ ];
415
+ /** thinking 档位(来源:config.ts THINKING_LEVELS)。 */
416
+ const THINKING_LEVELS = [
417
+ "off",
418
+ "minimal",
419
+ "low",
420
+ "medium",
421
+ "high",
422
+ "xhigh",
423
+ "max"
424
+ ];
425
+ /** 请求模态(来源:config.ts MODALITIES)。 */
426
+ const MODALITIES = ["text", "image"];
427
+ /** cacheRetention 枚举(来源:config.ts providerProfile.cacheRetention)。 */
428
+ const CACHE_RETENTION_OPTIONS = [
429
+ "none",
430
+ "short",
431
+ "long"
432
+ ];
433
+ /** transport 枚举(来源:config.ts providerProfile.transport)。 */
434
+ const TRANSPORT_OPTIONS = [
435
+ "sse",
436
+ "websocket",
437
+ "websocket-cached",
438
+ "auto"
439
+ ];
440
+ /** thinkingBudgets 档位键(来源:config.ts thinkingBudgets)。 */
441
+ const BUDGET_KEYS = [
442
+ "minimal",
443
+ "low",
444
+ "medium",
445
+ "high"
446
+ ];
447
+ /**
448
+ * 逐协议 compat 字段表:**服务端推导结果经 /catalog 下发**,浏览器半不再手抄
449
+ * (手抄遗漏官方新增字段 = UI 不渲染 + 保存被后端拒绝;见 compat-gates.ts)。
450
+ * 未拿到服务端表时用 EMPTY(UI 只渲染提示行),不放内置副本兜底——宁可少画
451
+ * 控件,也不给出可能与官方不符的字段集。
452
+ */
453
+ let compatTable = {};
454
+ /** 安装服务端下发的字段表(card 挂载/刷新目录时调用)。 */
455
+ function installCompatFields(table) {
456
+ compatTable = table;
457
+ }
458
+ /** 某协议的全部合法 compat 键(与服务端 compatFieldsOf 一致)。 */
459
+ function compatFieldsOf(api) {
460
+ return Object.keys(compatTable[api] ?? {});
461
+ }
462
+ /** 某协议某字段的取值约束(与服务端 compatFieldSpec 一致)。 */
463
+ function compatFieldSpec(api, field) {
464
+ return compatTable[api]?.[field];
465
+ }
466
+ /** api 未设置时的渲染回退组(最常见的协议;保存仍由后端按实际协议校验)。 */
467
+ const COMPAT_FALLBACK_API = "openai-completions";
468
+ //#endregion
402
469
  //#region src/client/draft.ts
403
470
  function numToText(value) {
404
471
  return value === void 0 ? "" : String(value);
@@ -737,6 +804,53 @@ ${extra}
737
804
  ]
738
805
  });
739
806
  }
807
+ /**
808
+ * 整数字段:compat 的数值型开关(如 vllmPriority,官方 z.number().step(1))。
809
+ * 本地保留文本态(与 JsonField 同款 epoch 播种),仅在解析为整数时回写草稿——
810
+ * 小数/非数字显示错误且不提交,与服务端 checkValue 的整数语义一致
811
+ * (后端仍做最终校验)。
812
+ */
813
+ function IntegerField(props) {
814
+ const [text, setText] = (0, react.useState)(() => props.value === void 0 ? "" : String(props.value));
815
+ (0, react.useEffect)(() => {
816
+ setText(props.value === void 0 ? "" : String(props.value));
817
+ }, [props.epoch]);
818
+ const trimmed = text.trim();
819
+ const parsed = trimmed === "" ? void 0 : Number(trimmed);
820
+ const ok = trimmed === "" || parsed !== void 0 && Number.isInteger(parsed);
821
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
822
+ className: `lpc-field${props.wide === true ? " lpc-wide" : ""}`,
823
+ children: [
824
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
825
+ className: "lpc-head",
826
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
827
+ className: "lpc-label",
828
+ htmlFor: props.id,
829
+ children: props.label
830
+ })
831
+ }),
832
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
833
+ id: props.id,
834
+ className: `lpc-input${ok ? "" : " lpc-inputInvalid"}`,
835
+ type: "text",
836
+ inputMode: "numeric",
837
+ "aria-invalid": ok ? void 0 : true,
838
+ value: text,
839
+ disabled: props.disabled === true,
840
+ onChange: (event) => {
841
+ const next = event.target.value;
842
+ setText(next);
843
+ const value = next.trim() === "" ? void 0 : Number(next.trim());
844
+ props.onEdit(value !== void 0 && Number.isInteger(value) ? value : void 0);
845
+ }
846
+ }),
847
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
848
+ className: ok ? "lpc-hint" : "lpc-invalid",
849
+ children: ok ? props.hint ?? "" : props.invalidLabel
850
+ })
851
+ ]
852
+ });
853
+ }
740
854
  function SelectField(props) {
741
855
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
742
856
  className: `lpc-field${props.wide === true ? " lpc-wide" : ""}`,
@@ -894,114 +1008,6 @@ ${extra}
894
1008
  });
895
1009
  }
896
1010
  //#endregion
897
- //#region src/client/constants.ts
898
- /**
899
- * 浏览器半内联常量:与服务端 packages/llm-pi/src/config.ts、compat.ts 逐字对齐。
900
- * 浏览器半不能 import 服务端模块(tsdown 只打包 client 侧入口),
901
- * 改动服务端这些常量时必须同步本文件。
902
- * @module llm-pi/client/constants
903
- */
904
- /** 协议枚举(来源:config.ts PROTOCOL_IDS)。 */
905
- const PROTOCOL_IDS = [
906
- "openai-completions",
907
- "openai-responses",
908
- "anthropic-messages"
909
- ];
910
- /** thinking 档位(来源:config.ts THINKING_LEVELS)。 */
911
- const THINKING_LEVELS = [
912
- "off",
913
- "minimal",
914
- "low",
915
- "medium",
916
- "high",
917
- "xhigh",
918
- "max"
919
- ];
920
- /** 请求模态(来源:config.ts MODALITIES)。 */
921
- const MODALITIES = ["text", "image"];
922
- /** cacheRetention 枚举(来源:config.ts providerProfile.cacheRetention)。 */
923
- const CACHE_RETENTION_OPTIONS = [
924
- "none",
925
- "short",
926
- "long"
927
- ];
928
- /** transport 枚举(来源:config.ts providerProfile.transport)。 */
929
- const TRANSPORT_OPTIONS = [
930
- "sse",
931
- "websocket",
932
- "websocket-cached",
933
- "auto"
934
- ];
935
- /** thinkingBudgets 档位键(来源:config.ts thinkingBudgets)。 */
936
- const BUDGET_KEYS = [
937
- "minimal",
938
- "low",
939
- "medium",
940
- "high"
941
- ];
942
- /**
943
- * 逐协议 compat 字段表(来源:compat.ts,逐字段镜像官方 catalog.ts COMPAT_GATES)。
944
- * 只列 offer 字段(withhold 字段官方写时拒绝,UI 不提供);取值约束对齐官方
945
- * config.ts compatProfile schema。
946
- */
947
- const COMPAT_FIELDS = {
948
- "openai-completions": {
949
- supportsStore: "boolean",
950
- supportsDeveloperRole: "boolean",
951
- supportsReasoningEffort: "boolean",
952
- supportsUsageInStreaming: "boolean",
953
- supportsFinishReason: "boolean",
954
- maxTokensField: ["max_completion_tokens", "max_tokens"],
955
- requiresToolResultName: "boolean",
956
- requiresAssistantAfterToolResult: "boolean",
957
- requiresThinkingAsText: "boolean",
958
- requiresReasoningContentOnAssistantMessages: "boolean",
959
- thinkingFormat: [
960
- "openai",
961
- "deepseek",
962
- "openrouter",
963
- "together",
964
- "baseten",
965
- "zai",
966
- "qwen",
967
- "chat-template",
968
- "qwen-chat-template",
969
- "string-thinking",
970
- "ant-ling"
971
- ],
972
- chatTemplateKwargs: "object",
973
- chatTemplateArgs: "object",
974
- supportsThinkingTokenBudget: "boolean",
975
- supportsStrictMode: "boolean",
976
- cacheControlFormat: ["anthropic"],
977
- supportsLongCacheRetention: "boolean"
978
- },
979
- "openai-responses": {
980
- supportsDeveloperRole: "boolean",
981
- supportsStrictMode: "boolean",
982
- supportsLongCacheRetention: "boolean"
983
- },
984
- "anthropic-messages": {
985
- supportsEagerToolInputStreaming: "boolean",
986
- supportsLongCacheRetention: "boolean",
987
- supportsCacheControlOnTools: "boolean",
988
- supportsTemperature: "boolean",
989
- forceAdaptiveThinking: "boolean",
990
- allowEmptySignature: "boolean",
991
- supportsStrictTools: "boolean"
992
- }
993
- };
994
- /** 某协议的全部合法 compat 键(与服务端 compatFieldsOf 一致)。 */
995
- function compatFieldsOf(api) {
996
- return Object.keys(COMPAT_FIELDS[api] ?? {});
997
- }
998
- /** 某协议某字段的取值约束(与服务端 compatFieldSpec 一致)。 */
999
- function compatFieldSpec(api, field) {
1000
- return COMPAT_FIELDS[api]?.[field];
1001
- }
1002
- /** api 未设置时的渲染回退组(最常见的协议;保存仍由后端按实际协议校验)。 */
1003
- const COMPAT_FALLBACK_API = "openai-completions";
1004
- //#endregion
1005
1011
  //#region src/client/views/compat.tsx
1006
1012
  /** api 变更后裁剪 compat:只保留新渲染组的字段,避免保存时被后端拒绝。 */
1007
1013
  function pruneCompatForApi(compat, api) {
@@ -1055,6 +1061,15 @@ ${extra}
1055
1061
  invalidText: props.t("invalidJson"),
1056
1062
  onEdit: (value) => setField(field, value)
1057
1063
  }, field);
1064
+ if (spec === "integer" || spec === "number") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IntegerField, {
1065
+ id: `${props.idPrefix}-${field}`,
1066
+ label: field,
1067
+ value: props.compat[field],
1068
+ epoch: props.epoch,
1069
+ invalidLabel: props.t("invalidInteger"),
1070
+ disabled: props.disabled === true,
1071
+ onEdit: (value) => setField(field, value)
1072
+ }, field);
1058
1073
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SelectField, {
1059
1074
  id: `${props.idPrefix}-${field}`,
1060
1075
  label: field,
@@ -1745,6 +1760,7 @@ ${extra}
1745
1760
  if (!alive) return;
1746
1761
  setKitSource(result.kitSource ?? null);
1747
1762
  setModelsDevStatus(result.status ?? null);
1763
+ if (result.compat !== void 0) installCompatFields(result.compat.fields);
1748
1764
  }).catch(() => {});
1749
1765
  return () => {
1750
1766
  alive = false;
@@ -2026,6 +2042,7 @@ ${extra}
2026
2042
  retryPolicy: "重试策略(JSON)",
2027
2043
  retryPolicyHint: "dsh-llm RetryPolicy 形状;非法 JSON 不会提交。",
2028
2044
  invalidJson: "JSON 格式错误(该字段不会提交)。",
2045
+ invalidInteger: "必须是整数(该字段不会提交)。",
2029
2046
  compatGroup: "Compat 覆盖",
2030
2047
  compatApiHint: "api 未设置时暂按 openai-completions 字段组渲染;保存时后端按实际协议校验。",
2031
2048
  compatUnset: "未设置",
@@ -2106,6 +2123,7 @@ ${extra}
2106
2123
  retryPolicy: "Retry policy (JSON)",
2107
2124
  retryPolicyHint: "dsh-llm RetryPolicy shape; invalid JSON is not submitted.",
2108
2125
  invalidJson: "Invalid JSON (this field will not be submitted).",
2126
+ invalidInteger: "Must be an integer (this field will not be submitted).",
2109
2127
  compatGroup: "Compat overrides",
2110
2128
  compatApiHint: "When api is unset, fields render per openai-completions; the backend validates per the effective protocol.",
2111
2129
  compatUnset: "Unset",