dsh-agentone 0.5.36 → 0.5.37

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/README.md CHANGED
@@ -71,7 +71,9 @@ dsh plugin --profile web add dsh-agentone
71
71
 
72
72
  - `$DSH_HOME/settings.yaml` 写入 `llm-pi-ai.providers.platform`(指向平台
73
73
  `/api/plugin/v1` 代理)与 `agent-default-model`,dsh 模型选择器出现
74
- 「平台模型」;
74
+ 「平台模型」;模型列表按系列聚簇 + 系列内版本降序写入(插件侧按平台
75
+ `sort_models_by_version` 同一规则兜底重排,旧版平台返回的上游原序也能得到
76
+ 稳定顺序,默认模型仍取平台下发的 `default_model`);
75
77
  - `$DSH_HOME/.credentials.yaml` 写入 `refs.AGENTONE_API_KEY`(0600),内容
76
78
  是 30 天插件令牌而非真实模型密钥——密钥不出平台,TokenHub 按用户计量;
77
79
  - 只覆盖 `platform` 命名空间,用户自己配置的其它 provider 原样保留;
package/lib/instance.js CHANGED
@@ -120,4 +120,4 @@ export function resolvePlatformUrl(config, credentials, env = process.env) {
120
120
  || credentials?.platform_url
121
121
  || config.platformUrl
122
122
  ).replace(/\/+$/, '');
123
- }
123
+ }
package/lib/model.js CHANGED
@@ -145,6 +145,90 @@ export function buildLogoutSettingsYaml(existingText) {
145
145
  return stringify(settings);
146
146
  }
147
147
 
148
+ // 模型选择器展示顺序:同一系列(模型 id 首段,如 deepseek / glm / kimi)聚簇,
149
+ // 系列内版本降序,容量降配档(kimi-k3-256k)排在本版本标准档之后。
150
+ //
151
+ // 为什么插件也要排一遍:平台 /api/plugin/v1/models 的 data 顺序在旧版平台上
152
+ // 直接沿用 AirRouter /models 的返回顺序(上游一变用户看到的列表就跟着变),
153
+ // 平台侧的排序修复要等部署才生效。插件把平台返回的列表按同一套规则重排后
154
+ // 再写进 dsh,旧平台也能拿到稳定顺序。规则与
155
+ // app/services/dsh_model_capabilities.py 的 sort_models_by_version 完全一致
156
+ // (系列聚簇 + 数字段补零取负降序 + 字母段字母序 + 容量段垫底),
157
+ // 平台已经排好时这里是幂等的,不会出现两边顺序漂移。
158
+ //
159
+ // 只改展示顺序:defaultModel 仍取平台下发的 default_model,排序不参与决策
160
+ //(平台未下发时回退列表首项,此时取排序后的首项,比上游原序更确定)。
161
+ const VERSION_SLOTS = 6;
162
+ const VERSION_NUMBERS = /\d+/g;
163
+ // 容量限定段(256k / 1m):同版本的降配档,不是更新的版本号。
164
+ // 必须连着字母一起匹配:VERSION_NUMBERS 切出来的只有数字部分。
165
+ const CAPACITY_QUALIFIER = /\d+(?:k|m|b)\b/g;
166
+
167
+ /** 模型 id → 系列键(id 首段小写;没有连字符的裸 id 整体作为分组键)。 */
168
+ function modelFamily(id) {
169
+ const lowered = String(id ?? '').trim().toLowerCase();
170
+ const at = lowered.indexOf('-');
171
+ return at > 0 ? lowered.slice(0, at) : lowered;
172
+ }
173
+
174
+ /** 系列内的版本比较键:数字段取负(升序即版本降序)、字母段、容量降配标记。 */
175
+ function versionKey(id) {
176
+ const lowered = String(id ?? '').trim().toLowerCase();
177
+ const capacityHits = lowered.match(CAPACITY_QUALIFIER) ?? [];
178
+ const numbers = (lowered.replace(CAPACITY_QUALIFIER, ' ').match(VERSION_NUMBERS) ?? [])
179
+ .slice(0, VERSION_SLOTS)
180
+ .map((part) => -Number(part));
181
+ while (numbers.length < VERSION_SLOTS) numbers.push(0);
182
+ return {
183
+ numbers,
184
+ words: lowered.split('-').filter((part) => part && !/^\d+$/.test(part)),
185
+ downgraded: capacityHits.length > 0,
186
+ };
187
+ }
188
+
189
+ /** Python 字符串/元组比较口径(逐码点),与平台 sorted() 的结果对齐。 */
190
+ function compareStrings(left, right) {
191
+ if (left === right) return 0;
192
+ return left < right ? -1 : 1;
193
+ }
194
+
195
+ function compareVersionKeys(left, right) {
196
+ for (let index = 0; index < VERSION_SLOTS; index += 1) {
197
+ if (left.numbers[index] !== right.numbers[index]) return left.numbers[index] - right.numbers[index];
198
+ }
199
+ const shared = Math.min(left.words.length, right.words.length);
200
+ for (let index = 0; index < shared; index += 1) {
201
+ const compared = compareStrings(left.words[index], right.words[index]);
202
+ if (compared !== 0) return compared;
203
+ }
204
+ if (left.words.length !== right.words.length) return left.words.length - right.words.length;
205
+ if (left.downgraded !== right.downgraded) return left.downgraded ? 1 : -1;
206
+ return 0;
207
+ }
208
+
209
+ /**
210
+ * 按系列聚簇、系列内版本降序排模型条目(只改展示顺序,不改集合成员)。
211
+ *
212
+ * 入参可以是平台条目对象(取 .id)或字符串 id;排序是确定性的,同样的输入
213
+ * 必得同样的输出,完全不依赖平台 / AirRouter 的返回顺序。
214
+ */
215
+ export function sortPlatformModels(models) {
216
+ const entries = (Array.isArray(models) ? models : []).map((item, index) => ({
217
+ item,
218
+ index,
219
+ id: typeof item === 'string' ? item : String(item?.id ?? ''),
220
+ }));
221
+ entries.sort(
222
+ (left, right) =>
223
+ compareStrings(modelFamily(left.id), modelFamily(right.id))
224
+ // 原始下标兜底:与 Python sorted 的稳定性同口径,完全相同键保持原序
225
+ || compareVersionKeys(versionKey(left.id), versionKey(right.id))
226
+ || compareStrings(left.id.trim().toLowerCase(), right.id.trim().toLowerCase())
227
+ || left.index - right.index,
228
+ );
229
+ return entries.map((entry) => entry.item);
230
+ }
231
+
148
232
  export async function fetchPlatformModels(platformUrl, accessToken) {
149
233
  const response = await platformFetch(`${platformUrl.replace(/\/+$/, '')}/api/plugin/v1/models`, {
150
234
  headers: { Authorization: `Bearer ${accessToken}` },
@@ -155,17 +239,19 @@ export async function fetchPlatformModels(platformUrl, accessToken) {
155
239
  throw await platformError(response, '获取平台模型列表失败');
156
240
  }
157
241
  const data = await response.json().catch(() => ({}));
158
- const models = Array.isArray(data.data)
159
- ? data.data
160
- .map((item) =>
161
- typeof item === 'string'
162
- ? item
163
- : item?.id
164
- ? { id: item.id, input: item.input, reasoning: item.reasoning, contextWindow: item.contextWindow }
165
- : null,
166
- )
167
- .filter(Boolean)
168
- : [];
242
+ const models = sortPlatformModels(
243
+ Array.isArray(data.data)
244
+ ? data.data
245
+ .map((item) =>
246
+ typeof item === 'string'
247
+ ? item
248
+ : item?.id
249
+ ? { id: item.id, input: item.input, reasoning: item.reasoning, contextWindow: item.contextWindow }
250
+ : null,
251
+ )
252
+ .filter(Boolean)
253
+ : [],
254
+ );
169
255
  if (models.length === 0) throw new Error('平台返回的可用模型列表为空');
170
256
  const direct =
171
257
  data.direct && typeof data.direct.base_url === 'string' && typeof data.direct.api_key === 'string'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-agentone",
3
- "version": "0.5.36",
3
+ "version": "0.5.37",
4
4
  "description": "AgentOne 平台集成:平台模型、SkillHub 技能、套餐与 CLI;lark-cli 首装一键初始化飞书应用;平台托管实例免登录自动授权",
5
5
  "type": "module",
6
6
  "scripts": {