@dsh-plus/llm-pi 0.1.26 → 0.1.28
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 +130 -109
- package/lib/index.d.ts +12 -5
- package/lib/index.js +352 -147
- package/package.json +27 -27
- package/src/client/api.ts +8 -0
- package/src/client/card.tsx +6 -2
- package/src/client/client.ts +4 -24
- package/src/client/constants.ts +12 -52
- package/src/client/fields.tsx +58 -0
- package/src/client/i18n.ts +14 -6
- package/src/client/views/compat.tsx +18 -2
- package/src/client/views/models.tsx +5 -4
- package/src/client/views/provider-fields.tsx +2 -1
- package/src/client/views/providers.tsx +3 -2
- package/src/compat-gates.ts +265 -0
- package/src/compat.ts +70 -141
- package/src/config-api.ts +29 -0
- package/src/config.ts +7 -0
- package/src/resolve-dsh.ts +54 -3
package/lib/client.js
CHANGED
|
@@ -168,7 +168,10 @@ window.__ModuleLoader__.load({
|
|
|
168
168
|
confirm: "Confirm",
|
|
169
169
|
cancel: "Cancel"
|
|
170
170
|
};
|
|
171
|
-
/**
|
|
171
|
+
/**
|
|
172
|
+
* own 覆盖同键合并(返回新对象,入参不可变)。
|
|
173
|
+
* 返回类型保留两侧键集合,调用方据此派生 DictKey。
|
|
174
|
+
*/
|
|
172
175
|
function mergeDict(base, own) {
|
|
173
176
|
return {
|
|
174
177
|
...base,
|
|
@@ -396,6 +399,73 @@ ${extra}
|
|
|
396
399
|
}));
|
|
397
400
|
}
|
|
398
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
|
|
399
469
|
//#region src/client/draft.ts
|
|
400
470
|
function numToText(value) {
|
|
401
471
|
return value === void 0 ? "" : String(value);
|
|
@@ -734,6 +804,53 @@ ${extra}
|
|
|
734
804
|
]
|
|
735
805
|
});
|
|
736
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
|
+
}
|
|
737
854
|
function SelectField(props) {
|
|
738
855
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
739
856
|
className: `lpc-field${props.wide === true ? " lpc-wide" : ""}`,
|
|
@@ -891,114 +1008,6 @@ ${extra}
|
|
|
891
1008
|
});
|
|
892
1009
|
}
|
|
893
1010
|
//#endregion
|
|
894
|
-
//#region src/client/constants.ts
|
|
895
|
-
/**
|
|
896
|
-
* 浏览器半内联常量:与服务端 packages/llm-pi/src/config.ts、compat.ts 逐字对齐。
|
|
897
|
-
* 浏览器半不能 import 服务端模块(tsdown 只打包 client 侧入口),
|
|
898
|
-
* 改动服务端这些常量时必须同步本文件。
|
|
899
|
-
* @module llm-pi/client/constants
|
|
900
|
-
*/
|
|
901
|
-
/** 协议枚举(来源:config.ts PROTOCOL_IDS)。 */
|
|
902
|
-
const PROTOCOL_IDS = [
|
|
903
|
-
"openai-completions",
|
|
904
|
-
"openai-responses",
|
|
905
|
-
"anthropic-messages"
|
|
906
|
-
];
|
|
907
|
-
/** thinking 档位(来源:config.ts THINKING_LEVELS)。 */
|
|
908
|
-
const THINKING_LEVELS = [
|
|
909
|
-
"off",
|
|
910
|
-
"minimal",
|
|
911
|
-
"low",
|
|
912
|
-
"medium",
|
|
913
|
-
"high",
|
|
914
|
-
"xhigh",
|
|
915
|
-
"max"
|
|
916
|
-
];
|
|
917
|
-
/** 请求模态(来源:config.ts MODALITIES)。 */
|
|
918
|
-
const MODALITIES = ["text", "image"];
|
|
919
|
-
/** cacheRetention 枚举(来源:config.ts providerProfile.cacheRetention)。 */
|
|
920
|
-
const CACHE_RETENTION_OPTIONS = [
|
|
921
|
-
"none",
|
|
922
|
-
"short",
|
|
923
|
-
"long"
|
|
924
|
-
];
|
|
925
|
-
/** transport 枚举(来源:config.ts providerProfile.transport)。 */
|
|
926
|
-
const TRANSPORT_OPTIONS = [
|
|
927
|
-
"sse",
|
|
928
|
-
"websocket",
|
|
929
|
-
"websocket-cached",
|
|
930
|
-
"auto"
|
|
931
|
-
];
|
|
932
|
-
/** thinkingBudgets 档位键(来源:config.ts thinkingBudgets)。 */
|
|
933
|
-
const BUDGET_KEYS = [
|
|
934
|
-
"minimal",
|
|
935
|
-
"low",
|
|
936
|
-
"medium",
|
|
937
|
-
"high"
|
|
938
|
-
];
|
|
939
|
-
/**
|
|
940
|
-
* 逐协议 compat 字段表(来源:compat.ts,逐字段镜像官方 catalog.ts COMPAT_GATES)。
|
|
941
|
-
* 只列 offer 字段(withhold 字段官方写时拒绝,UI 不提供);取值约束对齐官方
|
|
942
|
-
* config.ts compatProfile schema。
|
|
943
|
-
*/
|
|
944
|
-
const COMPAT_FIELDS = {
|
|
945
|
-
"openai-completions": {
|
|
946
|
-
supportsStore: "boolean",
|
|
947
|
-
supportsDeveloperRole: "boolean",
|
|
948
|
-
supportsReasoningEffort: "boolean",
|
|
949
|
-
supportsUsageInStreaming: "boolean",
|
|
950
|
-
supportsFinishReason: "boolean",
|
|
951
|
-
maxTokensField: ["max_completion_tokens", "max_tokens"],
|
|
952
|
-
requiresToolResultName: "boolean",
|
|
953
|
-
requiresAssistantAfterToolResult: "boolean",
|
|
954
|
-
requiresThinkingAsText: "boolean",
|
|
955
|
-
requiresReasoningContentOnAssistantMessages: "boolean",
|
|
956
|
-
thinkingFormat: [
|
|
957
|
-
"openai",
|
|
958
|
-
"deepseek",
|
|
959
|
-
"openrouter",
|
|
960
|
-
"together",
|
|
961
|
-
"baseten",
|
|
962
|
-
"zai",
|
|
963
|
-
"qwen",
|
|
964
|
-
"chat-template",
|
|
965
|
-
"qwen-chat-template",
|
|
966
|
-
"string-thinking",
|
|
967
|
-
"ant-ling"
|
|
968
|
-
],
|
|
969
|
-
chatTemplateKwargs: "object",
|
|
970
|
-
chatTemplateArgs: "object",
|
|
971
|
-
supportsThinkingTokenBudget: "boolean",
|
|
972
|
-
supportsStrictMode: "boolean",
|
|
973
|
-
cacheControlFormat: ["anthropic"],
|
|
974
|
-
supportsLongCacheRetention: "boolean"
|
|
975
|
-
},
|
|
976
|
-
"openai-responses": {
|
|
977
|
-
supportsDeveloperRole: "boolean",
|
|
978
|
-
supportsStrictMode: "boolean",
|
|
979
|
-
supportsLongCacheRetention: "boolean"
|
|
980
|
-
},
|
|
981
|
-
"anthropic-messages": {
|
|
982
|
-
supportsEagerToolInputStreaming: "boolean",
|
|
983
|
-
supportsLongCacheRetention: "boolean",
|
|
984
|
-
supportsCacheControlOnTools: "boolean",
|
|
985
|
-
supportsTemperature: "boolean",
|
|
986
|
-
forceAdaptiveThinking: "boolean",
|
|
987
|
-
allowEmptySignature: "boolean",
|
|
988
|
-
supportsStrictTools: "boolean"
|
|
989
|
-
}
|
|
990
|
-
};
|
|
991
|
-
/** 某协议的全部合法 compat 键(与服务端 compatFieldsOf 一致)。 */
|
|
992
|
-
function compatFieldsOf(api) {
|
|
993
|
-
return Object.keys(COMPAT_FIELDS[api] ?? {});
|
|
994
|
-
}
|
|
995
|
-
/** 某协议某字段的取值约束(与服务端 compatFieldSpec 一致)。 */
|
|
996
|
-
function compatFieldSpec(api, field) {
|
|
997
|
-
return COMPAT_FIELDS[api]?.[field];
|
|
998
|
-
}
|
|
999
|
-
/** api 未设置时的渲染回退组(最常见的协议;保存仍由后端按实际协议校验)。 */
|
|
1000
|
-
const COMPAT_FALLBACK_API = "openai-completions";
|
|
1001
|
-
//#endregion
|
|
1002
1011
|
//#region src/client/views/compat.tsx
|
|
1003
1012
|
/** api 变更后裁剪 compat:只保留新渲染组的字段,避免保存时被后端拒绝。 */
|
|
1004
1013
|
function pruneCompatForApi(compat, api) {
|
|
@@ -1052,6 +1061,15 @@ ${extra}
|
|
|
1052
1061
|
invalidText: props.t("invalidJson"),
|
|
1053
1062
|
onEdit: (value) => setField(field, value)
|
|
1054
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);
|
|
1055
1073
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SelectField, {
|
|
1056
1074
|
id: `${props.idPrefix}-${field}`,
|
|
1057
1075
|
label: field,
|
|
@@ -1742,6 +1760,7 @@ ${extra}
|
|
|
1742
1760
|
if (!alive) return;
|
|
1743
1761
|
setKitSource(result.kitSource ?? null);
|
|
1744
1762
|
setModelsDevStatus(result.status ?? null);
|
|
1763
|
+
if (result.compat !== void 0) installCompatFields(result.compat.fields);
|
|
1745
1764
|
}).catch(() => {});
|
|
1746
1765
|
return () => {
|
|
1747
1766
|
alive = false;
|
|
@@ -2023,6 +2042,7 @@ ${extra}
|
|
|
2023
2042
|
retryPolicy: "重试策略(JSON)",
|
|
2024
2043
|
retryPolicyHint: "dsh-llm RetryPolicy 形状;非法 JSON 不会提交。",
|
|
2025
2044
|
invalidJson: "JSON 格式错误(该字段不会提交)。",
|
|
2045
|
+
invalidInteger: "必须是整数(该字段不会提交)。",
|
|
2026
2046
|
compatGroup: "Compat 覆盖",
|
|
2027
2047
|
compatApiHint: "api 未设置时暂按 openai-completions 字段组渲染;保存时后端按实际协议校验。",
|
|
2028
2048
|
compatUnset: "未设置",
|
|
@@ -2103,6 +2123,7 @@ ${extra}
|
|
|
2103
2123
|
retryPolicy: "Retry policy (JSON)",
|
|
2104
2124
|
retryPolicyHint: "dsh-llm RetryPolicy shape; invalid JSON is not submitted.",
|
|
2105
2125
|
invalidJson: "Invalid JSON (this field will not be submitted).",
|
|
2126
|
+
invalidInteger: "Must be an integer (this field will not be submitted).",
|
|
2106
2127
|
compatGroup: "Compat overrides",
|
|
2107
2128
|
compatApiHint: "When api is unset, fields render per openai-completions; the backend validates per the effective protocol.",
|
|
2108
2129
|
compatUnset: "Unset",
|
package/lib/index.d.ts
CHANGED
|
@@ -4,13 +4,13 @@ import { Context } from "@deepseek-ai/cordis";
|
|
|
4
4
|
/** settings 命名空间(字面量即合法命名空间,0.1.2-alpha.2 起编译期校验;webui 配置卡片与插件运行期读取同一份)。 */
|
|
5
5
|
declare const SETTINGS_NS = "dsh-plus-llm-pi";
|
|
6
6
|
/** 本插件可为手写 route 提供的协议实现(与官方 PROTOCOLS 表一致)。 */
|
|
7
|
-
declare const PROTOCOL_IDS: readonly [
|
|
7
|
+
declare const PROTOCOL_IDS: readonly ['openai-completions', 'openai-responses', 'anthropic-messages'];
|
|
8
8
|
type ProtocolId = (typeof PROTOCOL_IDS)[number];
|
|
9
9
|
/** pi-ai 思考档位,升级序。 */
|
|
10
|
-
declare const THINKING_LEVELS: readonly [
|
|
11
|
-
declare const MODALITIES: readonly [
|
|
10
|
+
declare const THINKING_LEVELS: readonly ['off', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'];
|
|
11
|
+
declare const MODALITIES: readonly ['text', 'image'];
|
|
12
12
|
/** 路由适配器种类:pi = PiAiAdapter(默认);deepseek = 官方 DeepSeekAdapter(文件通道)。 */
|
|
13
|
-
declare const ADAPTER_KINDS: readonly [
|
|
13
|
+
declare const ADAPTER_KINDS: readonly ['pi', 'deepseek'];
|
|
14
14
|
type AdapterKind = (typeof ADAPTER_KINDS)[number];
|
|
15
15
|
type ThinkingLevel = (typeof THINKING_LEVELS)[number];
|
|
16
16
|
type Modality = (typeof MODALITIES)[number];
|
|
@@ -35,6 +35,13 @@ interface ProviderProfileConfig {
|
|
|
35
35
|
extends?: string;
|
|
36
36
|
displayName?: string;
|
|
37
37
|
api?: ProtocolId;
|
|
38
|
+
/**
|
|
39
|
+
* 端点;缺省继承 extends 源 provider 的端点。
|
|
40
|
+
*
|
|
41
|
+
* 不并 `| undefined`:schemastery 的输入契约用 null 表示「显式置空」,省略即未设置;
|
|
42
|
+
* 消费方按 `baseURL ?? 默认值` 处理。并上 undefined 会与 ObjectS 的 `string | null`
|
|
43
|
+
* 冲突(exactOptionalPropertyTypes 下二者不可互赋)。
|
|
44
|
+
*/
|
|
38
45
|
baseURL?: string;
|
|
39
46
|
apiKeyEnv?: string;
|
|
40
47
|
headers?: Record<string, string>;
|
|
@@ -89,7 +96,7 @@ declare const Config: z<LlmPiConfigInput, LlmPiConfig>;
|
|
|
89
96
|
//#endregion
|
|
90
97
|
//#region src/index.d.ts
|
|
91
98
|
declare const name = "dsh-plus-llm-pi";
|
|
92
|
-
declare const inject: readonly [
|
|
99
|
+
declare const inject: readonly ['credentials', 'llm'];
|
|
93
100
|
declare function apply(ctx: Context, config: LlmPiConfig): Promise<void>;
|
|
94
101
|
//#endregion
|
|
95
102
|
export { Config, type LlmPiConfig, type ModelEntryConfig, type ProviderProfileConfig, SETTINGS_NS, apply, inject, name };
|