modelfunction-cli 0.1.2 → 0.1.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/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  ## 安装
6
6
 
7
7
  ```bash
8
- npx -y modelfunction-cli@0.1.2 --version
8
+ npx -y modelfunction-cli@0.1.3 --version
9
9
  # 或
10
10
  npm i -D modelfunction-cli
11
11
  npx mf --version
@@ -83,7 +83,7 @@ INTERNAL_LLM_KEY=... mf eval --suite mf.suite.json \
83
83
  "mcpServers": {
84
84
  "modelfunction": {
85
85
  "command": "npx",
86
- "args": ["-y", "modelfunction-cli@0.1.2", "mcp"]
86
+ "args": ["-y", "modelfunction-cli@0.1.3", "mcp"]
87
87
  }
88
88
  }
89
89
  }
@@ -105,7 +105,7 @@ INTERNAL_LLM_KEY=... mf eval --suite mf.suite.json \
105
105
 
106
106
  ```bash
107
107
  # 1. 把 mf 挂成 MCP server
108
- claude mcp add modelfunction -- npx -y modelfunction-cli@0.1.2 mcp
108
+ claude mcp add modelfunction -- npx -y modelfunction-cli@0.1.3 mcp
109
109
  ```
110
110
 
111
111
  ```text
package/lib/cases.js CHANGED
@@ -167,15 +167,63 @@ export function suspectedPairs(tools) {
167
167
  if (union.size === 0) continue;
168
168
  let shared = 0;
169
169
  for (const token of union) if (a.has(token) && b.has(token)) shared += 1;
170
- const similarity = shared / union.size;
170
+ const lexical = shared / union.size;
171
+ const structural = structuralScore(tools[i], tools[j]);
172
+ const similarity = Math.max(lexical, structural);
171
173
  if (similarity >= PAIR_SIMILARITY) {
172
- pairs.push({ a: tools[i].name, b: tools[j].name, similarity });
174
+ pairs.push({
175
+ a: tools[i].name,
176
+ b: tools[j].name,
177
+ similarity,
178
+ basis: structural > lexical ? "structural" : "lexical"
179
+ });
173
180
  }
174
181
  }
175
182
  }
176
183
  return pairs.sort((x, y) => y.similarity - x.similarity);
177
184
  }
178
185
 
186
+ // Tools that share part of the name (search / file / read...) and the same
187
+ // parameter shape confuse the model even when the wording differs, which the
188
+ // lexical score alone misses. Name order is unreliable: `tavily_search` leads
189
+ // with the vendor, `search_docs` with the action.
190
+ function structuralScore(x, y) {
191
+ const left = nameTokens(x.name);
192
+ const right = nameTokens(y.name);
193
+ const sharedName = left.some((token) => right.includes(token));
194
+ if (!sharedName) return 0;
195
+ const params = paramNames(x);
196
+ const otherParams = paramNames(y);
197
+ if (params.size === 0 && otherParams.size === 0) return 0;
198
+ const paramOverlap = jaccard(params, otherParams);
199
+ if (paramOverlap < 0.34) return 0;
200
+ const requiredOverlap = jaccard(
201
+ new Set(x.parameters?.required ?? []),
202
+ new Set(y.parameters?.required ?? [])
203
+ );
204
+ return 0.5 + 0.5 * (0.6 * paramOverlap + 0.4 * requiredOverlap);
205
+ }
206
+
207
+ function nameTokens(name) {
208
+ return String(name ?? "")
209
+ .replace(/([a-z0-9])([A-Z])/g, "$1_$2")
210
+ .toLowerCase()
211
+ .split(/[^a-z0-9]+/)
212
+ .filter(Boolean);
213
+ }
214
+
215
+ function paramNames(tool) {
216
+ return new Set(Object.keys(tool.parameters?.properties ?? {}));
217
+ }
218
+
219
+ function jaccard(a, b) {
220
+ if (a.size === 0 && b.size === 0) return 1;
221
+ const union = new Set([...a, ...b]);
222
+ let shared = 0;
223
+ for (const item of union) if (a.has(item) && b.has(item)) shared += 1;
224
+ return shared / union.size;
225
+ }
226
+
179
227
  function tokens(text) {
180
228
  const set = new Set();
181
229
  const lowered = String(text ?? "").toLowerCase();
package/lib/lint.js CHANGED
@@ -178,7 +178,8 @@ export function lintSuite(suite) {
178
178
  pair: [pair.a, pair.b]
179
179
  });
180
180
  } else if (pair.similarity >= NO_BOUNDARY_SIMILARITY) {
181
- add("warn", "pair-no-boundary", pair.a, `与 ${pair.b} 的描述相似度 ${similarity},且两边的 description 都没写“不适用于”边界,模型只能靠相似度硬猜`, {
181
+ const reason = pair.basis === "structural" ? "名字与参数形状接近" : "描述相近";
182
+ add("warn", "pair-no-boundary", pair.a, `与 ${pair.b} ${reason}(相似度 ${similarity}),且两边都没写“不适用于”边界,模型只能靠相似度硬猜`, {
182
183
  pair: [pair.a, pair.b],
183
184
  suggestion: `在 ${pair.a} 和 ${pair.b} 的 description 里各写一句“不适用于什么”,并补一条歧义用例`
184
185
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "modelfunction-cli",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "mf — function-calling accuracy scorecard for AI agents",