@xynogen/pix-models 0.1.4 → 0.1.6

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.
Files changed (3) hide show
  1. package/README.md +2 -2
  2. package/package.json +1 -1
  3. package/src/models.ts +76 -58
package/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # pix-models
2
2
 
3
- Pi extension — enhanced `/models` picker with BenchLM ranks.
3
+ Pi extension — enhanced `/models` picker with coding score/rank.
4
4
 
5
5
  ## What it does
6
6
 
7
- Registers a `/models` slash command that replaces Pi's built-in `/model` selector with a richer TUI picker. Each row shows model name, provider, context window, cost per million tokens, and BenchLM rank/score when available. The list is sorted by BenchLM rank (best first), then alphabetically for unranked models. Fuzzy search filters the list as you type. Selecting a model switches the active model for the session. Model metadata is sourced from `~/.cache/pi/` via `pix-data`; BenchLM data is fetched and cached at startup. Requires `@xynogen/pix-data` as a dependency.
7
+ Registers a `/models` slash command that replaces Pi's built-in `/model` selector with a richer TUI picker. Each row shows model name, provider, context window, cost per million tokens, and a coding-focused score/rank when available. The list is sorted by coding score (best first), then alphabetically for unscored models. Fuzzy search filters the list as you type. Selecting a model switches the active model for the session. Model metadata is sourced from `~/.cache/pi/` via `pix-data`; the coding score/rank is computed locally from the modelgrep catalog (best = #1). Requires `@xynogen/pix-data` as a dependency.
8
8
 
9
9
  ## Install
10
10
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xynogen/pix-models",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Pi extension — enhanced /models picker with BenchLM ranks",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
package/src/models.ts CHANGED
@@ -23,7 +23,11 @@ import {
23
23
  Text,
24
24
  visibleWidth,
25
25
  } from "@earendil-works/pi-tui";
26
- import { lookupBenchmark, lookupModelsDev } from "@xynogen/pix-data";
26
+ import {
27
+ benchScoreColor,
28
+ lookupBenchmark,
29
+ lookupModelsDev,
30
+ } from "@xynogen/pix-data";
27
31
  import { patchOutBuiltinModelCommand } from "./patch-builtin";
28
32
 
29
33
  // ─── Pure logic (exported for tests) ─────────────────────────────────────────
@@ -115,11 +119,14 @@ async function showEnhancedPicker(
115
119
  m: (typeof available)[number];
116
120
  dev: ReturnType<typeof lookupModelsDev>;
117
121
  bench: ReturnType<typeof lookupBenchmark>;
122
+ // Rank among the user's *available* models, not the global catalog.
123
+ localRank: number | null;
118
124
  };
119
125
  const rows: Row[] = available.map((m) => ({
120
126
  m,
121
127
  dev: lookupModelsDev(m.provider, m.id),
122
- bench: lookupBenchmark(m.name ?? m.id),
128
+ bench: lookupBenchmark(m.id),
129
+ localRank: null,
123
130
  }));
124
131
 
125
132
  // Sort: by score desc (highest first), unscored last alphabetical
@@ -130,6 +137,10 @@ async function showEnhancedPicker(
130
137
  return (a.m.name ?? a.m.id).localeCompare(b.m.name ?? b.m.id);
131
138
  });
132
139
 
140
+ // Local rank = position among scored available models (best pickable = #1).
141
+ let localRank = 0;
142
+ for (const r of rows) if (r.bench) r.localRank = ++localRank;
143
+
133
144
  // Show all models (no deduplication)
134
145
  const dedupedRows = rows;
135
146
 
@@ -142,7 +153,9 @@ async function showEnhancedPicker(
142
153
 
143
154
  // Find max rank width across all benchmarked rows for # padding
144
155
  const maxRankWidth = Math.max(
145
- ...dedupedRows.map((r) => (r.bench ? String(r.bench.rank).length : 0)),
156
+ ...dedupedRows.map((r) =>
157
+ r.localRank ? String(r.localRank).length : 0,
158
+ ),
146
159
  1,
147
160
  );
148
161
 
@@ -152,64 +165,69 @@ async function showEnhancedPicker(
152
165
 
153
166
  // Track rank per item value so fuzzy results can prioritize ranked models.
154
167
  const rankByValue = new Map<string, number>();
155
- for (const { m, bench } of dedupedRows) {
156
- if (bench) rankByValue.set(`${m.provider}/${m.id}`, bench.rank);
168
+ for (const { m, localRank } of dedupedRows) {
169
+ if (localRank) rankByValue.set(`${m.provider}/${m.id}`, localRank);
157
170
  }
158
171
 
159
- const items: SelectItem[] = dedupedRows.map(({ m, dev, bench }) => {
160
- const isCurrent =
161
- current && m.provider === current.provider && m.id === current.id;
162
-
163
- // Label: marker + muted '#' + bright rank + accent-colored model name
164
- const marker = isCurrent ? theme.fg(accent, "▶") : " ";
165
- let rankPrefix: string;
166
- if (bench) {
167
- const rankStr = String(bench.rank).padEnd(maxRankWidth);
168
- rankPrefix = mute("#") + theme.fg("warning", rankStr);
169
- } else {
170
- rankPrefix = " ".repeat(maxRankWidth + 1);
171
- }
172
- // Display model id only; m.provider is routing provider, not part of id.
173
- const idColored = theme.fg(accent, m.id);
174
- const label = `${marker} ${rankPrefix} ${idColored}`;
175
-
176
- // Description: ctx · cost · score stars
177
- // Colors: ctx muted · cost success (free muted) · score+stars warning
178
- const ctxRaw = fmtCtx(dev?.limit?.context ?? 0);
179
- const ctxStr = mute(ctxRaw.padStart(4));
180
- const rawCost = fmtCost(dev);
181
- let costSeg: string;
182
- if (rawCost === "—") {
183
- costSeg = theme.fg("dim", "—".padEnd(10));
184
- } else if (rawCost === "free") {
185
- costSeg = mute("free".padEnd(10));
186
- } else {
187
- costSeg = theme.fg("success", rawCost.padEnd(10));
188
- }
189
- let benchSeg = "";
190
- if (bench) {
191
- const score = bench.overallScore ?? "?";
192
- const s = bench.overallScore;
193
- let filled = 1;
194
- if (typeof s === "number") {
195
- if (s >= 90) filled = 5;
196
- else if (s >= 80) filled = 4;
197
- else if (s >= 70) filled = 3;
198
- else if (s >= 50) filled = 2;
172
+ const items: SelectItem[] = dedupedRows.map(
173
+ ({ m, dev, bench, localRank }) => {
174
+ const isCurrent =
175
+ current && m.provider === current.provider && m.id === current.id;
176
+
177
+ // Label: marker + muted '#' + bright rank + accent-colored model name
178
+ const marker = isCurrent ? theme.fg(accent, "▶") : " ";
179
+ let rankPrefix: string;
180
+ if (localRank) {
181
+ const rankStr = String(localRank).padEnd(maxRankWidth);
182
+ const rankColor =
183
+ localRank <= 3 ? "success" : localRank <= 7 ? "warning" : "error";
184
+ rankPrefix = mute("#") + theme.fg(rankColor, rankStr);
185
+ } else {
186
+ rankPrefix = " ".repeat(maxRankWidth + 1);
199
187
  }
200
- const starBar =
201
- theme.fg("warning", "★".repeat(filled)) +
202
- mute("☆".repeat(5 - filled));
203
- benchSeg = `⚡${theme.fg("warning", String(score))} ${starBar}`;
204
- }
205
- const desc = [ctxStr, costSeg, benchSeg].filter(Boolean).join(sep);
188
+ // Display model id only; m.provider is routing provider, not part of id.
189
+ const idColored = theme.fg(accent, m.id);
190
+ const label = `${marker} ${rankPrefix} ${idColored}`;
191
+
192
+ // Description: ctx · cost · score stars
193
+ // Colors: ctx muted · cost success (free muted) · score+stars warning
194
+ const ctxRaw = fmtCtx(dev?.limit?.context ?? 0);
195
+ const ctxStr = mute(ctxRaw.padStart(4));
196
+ const rawCost = fmtCost(dev);
197
+ let costSeg: string;
198
+ if (rawCost === "—") {
199
+ costSeg = theme.fg("dim", "—".padEnd(10));
200
+ } else if (rawCost === "free") {
201
+ costSeg = mute("free".padEnd(10));
202
+ } else {
203
+ costSeg = theme.fg("success", rawCost.padEnd(10));
204
+ }
205
+ let benchSeg = "";
206
+ if (bench) {
207
+ const score = bench.overallScore ?? "?";
208
+ const s = bench.overallScore;
209
+ const scoreColor = benchScoreColor(s);
210
+ let filled = 1;
211
+ if (typeof s === "number") {
212
+ if (s >= 90) filled = 5;
213
+ else if (s >= 80) filled = 4;
214
+ else if (s >= 70) filled = 3;
215
+ else if (s >= 50) filled = 2;
216
+ }
217
+ const starBar =
218
+ theme.fg(scoreColor, "★".repeat(filled)) +
219
+ mute("☆".repeat(5 - filled));
220
+ benchSeg = `⚡${theme.fg(scoreColor, String(score))} ${starBar}`;
221
+ }
222
+ const desc = [ctxStr, costSeg, benchSeg].filter(Boolean).join(sep);
206
223
 
207
- return {
208
- value: `${m.provider}/${m.id}`,
209
- label,
210
- description: desc,
211
- };
212
- });
224
+ return {
225
+ value: `${m.provider}/${m.id}`,
226
+ label,
227
+ description: desc,
228
+ };
229
+ },
230
+ );
213
231
 
214
232
  const currentIdx = current
215
233
  ? items.findIndex(
@@ -225,7 +243,7 @@ async function showEnhancedPicker(
225
243
  new Text(
226
244
  theme.fg(
227
245
  "dim",
228
- "context & pricing from models.dev · ranks from benchlm.ai",
246
+ "context · pricing · coding rank & score from modelgrep.com",
229
247
  ),
230
248
  ),
231
249
  );