@mcowger/oh-my-pi-plexus 1.3.8 → 1.3.9
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 +42 -0
- package/dist/extension.js +117 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -244,6 +244,47 @@ Both adapters also accept pi-style environment interpolation in configured strin
|
|
|
244
244
|
|
|
245
245
|
---
|
|
246
246
|
|
|
247
|
+
## Model suppression
|
|
248
|
+
|
|
249
|
+
All plugin packages support suppressing models by name or pattern so undesired models do not appear in model selectors or cache restored lists.
|
|
250
|
+
|
|
251
|
+
### Pattern matching syntax
|
|
252
|
+
|
|
253
|
+
- **Exact match** (case-insensitive): Matches model `id`, `name`, or short ID (suffix after `/` or `:`). Example: `"gpt-4o"`, `"Claude 3.5 Sonnet"`.
|
|
254
|
+
- **Glob wildcards**: Supports `*` (0+ characters) and `?` (1 character). Example: `"gpt-3.5*"`, `"*deprecated*"`, `"anthropic/*"`.
|
|
255
|
+
- **Regex patterns**: Prefix with `regex:`. Example: `"regex:^gpt-[34]"`.
|
|
256
|
+
|
|
257
|
+
### Configuration methods
|
|
258
|
+
|
|
259
|
+
- **Environment variables**:
|
|
260
|
+
```sh
|
|
261
|
+
export PLEXUS_SUPPRESS_MODELS="gpt-3.5*, *deprecated*, whisper"
|
|
262
|
+
```
|
|
263
|
+
`PLEXUS_EXCLUDE_MODELS` is also accepted. Values can be comma-, semicolon-, or newline-separated.
|
|
264
|
+
|
|
265
|
+
- **pi / Oh My Pi config**: Add `suppressModels` (or `suppress`) to `config.json`:
|
|
266
|
+
```json
|
|
267
|
+
{
|
|
268
|
+
"baseUrl": "https://plexus.example.com",
|
|
269
|
+
"suppressModels": ["gpt-3.5*", "*deprecated*"]
|
|
270
|
+
}
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
- **OpenCode config**: Add `suppressModels` (or `suppress`) under `provider.plexus.options` in `opencode.json`:
|
|
274
|
+
```json
|
|
275
|
+
{
|
|
276
|
+
"provider": {
|
|
277
|
+
"plexus": {
|
|
278
|
+
"options": {
|
|
279
|
+
"suppressModels": ["gpt-3.5*", "claude-2*"]
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
247
288
|
## Package layout
|
|
248
289
|
|
|
249
290
|
```
|
|
@@ -252,6 +293,7 @@ packages/
|
|
|
252
293
|
src/
|
|
253
294
|
types.ts # wire types (PlexusApiModel, PlexusModelDescriptor, etc.)
|
|
254
295
|
convert.ts # model fetching, conversion, compat detection
|
|
296
|
+
suppress.ts # model pattern suppression / exclusion matching
|
|
255
297
|
index.ts # barrel export
|
|
256
298
|
plexus-pi/ # pi host adapter
|
|
257
299
|
src/
|
package/dist/extension.js
CHANGED
|
@@ -109228,6 +109228,55 @@ var init_devin = __esm(() => {
|
|
|
109228
109228
|
NO_REASONING_LABEL_PATTERN = /\bno thinking\b/i;
|
|
109229
109229
|
});
|
|
109230
109230
|
|
|
109231
|
+
// ../plexus-models/src/suppress.ts
|
|
109232
|
+
function parseSuppressionPatterns(raw) {
|
|
109233
|
+
if (!raw)
|
|
109234
|
+
return [];
|
|
109235
|
+
const items = Array.isArray(raw) ? raw : raw.split(/[\n,;]+/);
|
|
109236
|
+
return items.map((s) => s.trim()).filter((s) => s.length > 0);
|
|
109237
|
+
}
|
|
109238
|
+
function getEnvSuppressedModels() {
|
|
109239
|
+
const env = typeof process !== "undefined" && process?.env ? process.env : {};
|
|
109240
|
+
const raw = env.PLEXUS_SUPPRESS_MODELS ?? env.PLEXUS_EXCLUDE_MODELS;
|
|
109241
|
+
return parseSuppressionPatterns(raw);
|
|
109242
|
+
}
|
|
109243
|
+
function isModelSuppressed(model, patterns) {
|
|
109244
|
+
const envPatterns = getEnvSuppressedModels();
|
|
109245
|
+
const explicitPatterns = parseSuppressionPatterns(patterns);
|
|
109246
|
+
const allPatterns = [...envPatterns, ...explicitPatterns];
|
|
109247
|
+
if (allPatterns.length === 0)
|
|
109248
|
+
return false;
|
|
109249
|
+
const id = model.id.toLowerCase();
|
|
109250
|
+
const name = (model.name ?? "").toLowerCase();
|
|
109251
|
+
const shortId = id.includes("/") ? id.split("/").pop() : id.includes(":") ? id.split(":").pop() : id;
|
|
109252
|
+
for (const pattern of allPatterns) {
|
|
109253
|
+
if (matchesPattern(id, name, shortId, pattern)) {
|
|
109254
|
+
return true;
|
|
109255
|
+
}
|
|
109256
|
+
}
|
|
109257
|
+
return false;
|
|
109258
|
+
}
|
|
109259
|
+
function matchesPattern(id, name, shortId, pattern) {
|
|
109260
|
+
const p = pattern.toLowerCase();
|
|
109261
|
+
if (p.startsWith("regex:")) {
|
|
109262
|
+
try {
|
|
109263
|
+
const re = new RegExp(pattern.slice(6), "i");
|
|
109264
|
+
return re.test(id) || re.test(name) || re.test(shortId);
|
|
109265
|
+
} catch {
|
|
109266
|
+
return false;
|
|
109267
|
+
}
|
|
109268
|
+
}
|
|
109269
|
+
if (p.includes("*") || p.includes("?")) {
|
|
109270
|
+
const regexStr = "^" + p.replace(/([.+^${}()|[\]\\])/g, "\\$1").replace(/\*/g, ".*").replace(/\?/g, ".") + "$";
|
|
109271
|
+
try {
|
|
109272
|
+
const re = new RegExp(regexStr, "i");
|
|
109273
|
+
return re.test(id) || re.test(name) || re.test(shortId);
|
|
109274
|
+
} catch {
|
|
109275
|
+
return false;
|
|
109276
|
+
}
|
|
109277
|
+
}
|
|
109278
|
+
return id === p || name === p || shortId === p;
|
|
109279
|
+
}
|
|
109231
109280
|
// ../plexus-models/src/convert.ts
|
|
109232
109281
|
var REASONING_PARAMS = new Set(["reasoning", "include_reasoning", "reasoning_effort"]);
|
|
109233
109282
|
var NON_CHAT_PATTERN = /(?:^|[\W_])(?:embed(?:ding|dings)?|transcri(?:be[ds]?|ptions?)|whisper|speech[\W_]*to[\W_]*text|stt|text[\W_]*to[\W_]*speech|tts|image[\W_]*(?:gen(?:eration)?|\d+)|diffusion|dall[\W_]*e|stable[\W_]*diffusion|sdxl|dream)(?:$|[\W_])/i;
|
|
@@ -109361,11 +109410,68 @@ function isChatModel(model) {
|
|
|
109361
109410
|
const apiHints = Array.isArray(model.preferred_api) ? model.preferred_api.join(" ") : model.preferred_api ?? "";
|
|
109362
109411
|
return !NON_CHAT_PATTERN.test(`${model.id} ${model.name ?? ""} ${apiHints}`);
|
|
109363
109412
|
}
|
|
109364
|
-
function
|
|
109413
|
+
function parseSuppressionPatterns2(input) {
|
|
109414
|
+
if (!input)
|
|
109415
|
+
return [];
|
|
109416
|
+
const rawItems = Array.isArray(input) ? input : [input];
|
|
109417
|
+
const patterns = [];
|
|
109418
|
+
for (const item of rawItems) {
|
|
109419
|
+
if (!item || typeof item !== "string")
|
|
109420
|
+
continue;
|
|
109421
|
+
const parts = item.split(/[\n,]/);
|
|
109422
|
+
for (const part of parts) {
|
|
109423
|
+
const trimmed = part.trim();
|
|
109424
|
+
if (trimmed.length > 0) {
|
|
109425
|
+
patterns.push(trimmed);
|
|
109426
|
+
}
|
|
109427
|
+
}
|
|
109428
|
+
}
|
|
109429
|
+
return patterns;
|
|
109430
|
+
}
|
|
109431
|
+
function isSuppressedModel(model, suppress) {
|
|
109432
|
+
if (!model || !model.id)
|
|
109433
|
+
return false;
|
|
109434
|
+
const patterns = parseSuppressionPatterns2(suppress);
|
|
109435
|
+
if (patterns.length === 0)
|
|
109436
|
+
return false;
|
|
109437
|
+
const idLower = model.id.toLowerCase();
|
|
109438
|
+
const nameLower = (model.name ?? "").toLowerCase();
|
|
109439
|
+
for (const pattern of patterns) {
|
|
109440
|
+
const patternLower = pattern.toLowerCase();
|
|
109441
|
+
if (idLower === patternLower || nameLower && nameLower === patternLower) {
|
|
109442
|
+
return true;
|
|
109443
|
+
}
|
|
109444
|
+
if (pattern.startsWith("/") && pattern.lastIndexOf("/") > 0) {
|
|
109445
|
+
const lastSlash = pattern.lastIndexOf("/");
|
|
109446
|
+
const regexBody = pattern.slice(1, lastSlash);
|
|
109447
|
+
const regexFlags = pattern.slice(lastSlash + 1) || "i";
|
|
109448
|
+
try {
|
|
109449
|
+
const re = new RegExp(regexBody, regexFlags);
|
|
109450
|
+
if (re.test(model.id) || model.name && re.test(model.name)) {
|
|
109451
|
+
return true;
|
|
109452
|
+
}
|
|
109453
|
+
} catch {}
|
|
109454
|
+
}
|
|
109455
|
+
if (pattern.includes("*") || pattern.includes("?")) {
|
|
109456
|
+
try {
|
|
109457
|
+
const escaped = patternLower.replace(/[.+^$()|[{}]\\]/g, "\\$&");
|
|
109458
|
+
const regexStr = "^" + escaped.replace(/\*/g, ".*").replace(/\?/g, ".") + "$";
|
|
109459
|
+
const globRe = new RegExp(regexStr, "i");
|
|
109460
|
+
if (globRe.test(model.id) || model.name && globRe.test(model.name)) {
|
|
109461
|
+
return true;
|
|
109462
|
+
}
|
|
109463
|
+
} catch {}
|
|
109464
|
+
}
|
|
109465
|
+
}
|
|
109466
|
+
return false;
|
|
109467
|
+
}
|
|
109468
|
+
function convertDescriptors(models, baseUrl, suppress) {
|
|
109365
109469
|
const result = [];
|
|
109366
109470
|
for (const m of models) {
|
|
109367
109471
|
if (!isChatModel(m))
|
|
109368
109472
|
continue;
|
|
109473
|
+
if (isSuppressedModel(m, suppress))
|
|
109474
|
+
continue;
|
|
109369
109475
|
result.push(convertToDescriptor(m, baseUrl));
|
|
109370
109476
|
}
|
|
109371
109477
|
return result;
|
|
@@ -109579,6 +109685,12 @@ function getBaseUrl() {
|
|
|
109579
109685
|
const raw = getRawBaseUrl();
|
|
109580
109686
|
return raw ? normalizeApiBase(raw) : null;
|
|
109581
109687
|
}
|
|
109688
|
+
function getSuppressedModels() {
|
|
109689
|
+
const config = getConfigSync();
|
|
109690
|
+
const envSuppressed = getEnvSuppressedModels();
|
|
109691
|
+
const configSuppressed = parseSuppressionPatterns(config.suppressModels ?? config.suppress);
|
|
109692
|
+
return [...envSuppressed, ...configSuppressed];
|
|
109693
|
+
}
|
|
109582
109694
|
|
|
109583
109695
|
// src/cache.ts
|
|
109584
109696
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
@@ -129293,8 +129405,9 @@ function getProviderApiKeyConfig() {
|
|
|
129293
129405
|
var currentModels = [];
|
|
129294
129406
|
function plexusExtension(pi) {
|
|
129295
129407
|
const cached3 = readCachedModelsSync();
|
|
129408
|
+
const suppressPatterns = getSuppressedModels();
|
|
129296
129409
|
const startupBaseUrl = getBaseUrl() ?? "http://localhost/v1";
|
|
129297
|
-
const startupModels = cached3?.models.map(descriptorToOhMyPiModel)
|
|
129410
|
+
const startupModels = (cached3?.models ?? []).filter((m) => !isModelSuppressed({ id: m.id, name: m.name }, suppressPatterns)).map(descriptorToOhMyPiModel);
|
|
129298
129411
|
log("startup", {
|
|
129299
129412
|
cachedModelCount: startupModels.length,
|
|
129300
129413
|
startupBaseUrl
|
|
@@ -129417,7 +129530,8 @@ async function doRefresh(pi, apiKey, ctx) {
|
|
|
129417
129530
|
}
|
|
129418
129531
|
try {
|
|
129419
129532
|
const { models: apiModels, raw } = await fetchPlexusModels(apiKey, modelsUrl);
|
|
129420
|
-
const
|
|
129533
|
+
const suppressPatterns = getSuppressedModels();
|
|
129534
|
+
const descriptors3 = convertDescriptors(apiModels, baseUrl, suppressPatterns);
|
|
129421
129535
|
const ohMyPiModels = descriptors3.map(descriptorToOhMyPiModel);
|
|
129422
129536
|
await Promise.all([writeCachedModels(descriptors3), writeRawResponse(raw)]);
|
|
129423
129537
|
currentModels = ohMyPiModels;
|