@mcowger/opencode-plexus 1.0.4 → 1.1.0
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 +3 -1
- package/dist/index.js +27 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -227,8 +227,10 @@ Models with a falsy `id` are skipped. Missing metadata falls back to safe defaul
|
|
|
227
227
|
|
|
228
228
|
## Adapter behavior
|
|
229
229
|
|
|
230
|
-
- **pi** refreshes on
|
|
230
|
+
- **pi** refreshes on session start and through `/plexus refresh`. It accepts either root URLs or URLs ending in `/v1` and normalizes them before calling Plexus.
|
|
231
231
|
- **OpenCode** seeds the provider from cache or a placeholder model during config loading, then performs live discovery through the `provider.models` hook. If Plexus is slow or unavailable, OpenCode uses the cache and lets the refresh continue in the background.
|
|
232
|
+
- OpenCode models retain their upstream model ID, SDK dialect, release date, and reasoning metadata so OpenCode can generate its native GPT, Claude, Gemini, and OpenAI-compatible variants and apply its current request transforms. DeepSeek models also preserve `reasoning_content` across tool-call turns.
|
|
233
|
+
- OpenCode uses a 250K-token context window when Plexus supplies no context metadata; its output fallback remains 20% of that window.
|
|
232
234
|
- Both adapters convert Plexus's per-token base and tier rates to the per-million-token units expected by their host.
|
|
233
235
|
|
|
234
236
|
## Development
|
package/dist/index.js
CHANGED
|
@@ -16,7 +16,6 @@ var PLACEHOLDER_MODEL_ID = "plexus-unconfigured";
|
|
|
16
16
|
|
|
17
17
|
// ../plexus-models/src/convert.ts
|
|
18
18
|
var REASONING_PARAMS = new Set(["reasoning", "include_reasoning", "reasoning_effort"]);
|
|
19
|
-
var NON_CHAT_ID_PATTERN = /embedding|embed|tts|whisper|image-[0-9]|image\b.*gen|diffusion|dall-e|stable-diff|sdxl|dream/i;
|
|
20
19
|
var API_DIALECT_MAP = {
|
|
21
20
|
chat_completions: "openai-completions",
|
|
22
21
|
"openai-completions": "openai-completions",
|
|
@@ -49,15 +48,6 @@ function adjustBaseUrl(baseUrl, preferredApi) {
|
|
|
49
48
|
return stripped;
|
|
50
49
|
}
|
|
51
50
|
}
|
|
52
|
-
function isChatModel(model) {
|
|
53
|
-
const inputModalities = model.architecture?.input_modalities;
|
|
54
|
-
if (inputModalities !== undefined && !inputModalities.includes("text"))
|
|
55
|
-
return false;
|
|
56
|
-
const outputModalities = model.architecture?.output_modalities;
|
|
57
|
-
if (outputModalities !== undefined)
|
|
58
|
-
return outputModalities.includes("text");
|
|
59
|
-
return !NON_CHAT_ID_PATTERN.test(model.id);
|
|
60
|
-
}
|
|
61
51
|
var DEFAULT_MODELS_FETCH_TIMEOUT_MS = 1e4;
|
|
62
52
|
async function fetchPlexusModels(apiKey, modelsUrl, timeoutMs = DEFAULT_MODELS_FETCH_TIMEOUT_MS) {
|
|
63
53
|
const controller = new AbortController;
|
|
@@ -231,7 +221,7 @@ function createLogger(client) {
|
|
|
231
221
|
|
|
232
222
|
// src/mapper.ts
|
|
233
223
|
var REASONING_PARAMS2 = new Set(["reasoning", "include_reasoning", "reasoning_effort"]);
|
|
234
|
-
var DEFAULT_CONTEXT =
|
|
224
|
+
var DEFAULT_CONTEXT = 250000;
|
|
235
225
|
var PER_TOKEN_TO_PER_MILLION = 1e6;
|
|
236
226
|
function resolveModelProvider(model, baseURL) {
|
|
237
227
|
const preferredApi = mapPreferredApi(model.preferred_api);
|
|
@@ -289,11 +279,24 @@ function mapModality(m) {
|
|
|
289
279
|
return null;
|
|
290
280
|
}
|
|
291
281
|
}
|
|
282
|
+
function releaseDate(created) {
|
|
283
|
+
if (typeof created !== "number" || !Number.isFinite(created) || created <= 0)
|
|
284
|
+
return;
|
|
285
|
+
const date = new Date(created * 1000);
|
|
286
|
+
return Number.isNaN(date.getTime()) ? undefined : date.toISOString().slice(0, 10);
|
|
287
|
+
}
|
|
288
|
+
function interleavedReasoning(model, preferredApi) {
|
|
289
|
+
if (preferredApi === "openai-completions" && model.id.toLowerCase().includes("deepseek")) {
|
|
290
|
+
return { field: "reasoning_content" };
|
|
291
|
+
}
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
292
294
|
function buildInputModalities(model) {
|
|
293
295
|
const raw = model.architecture?.input_modalities ?? [];
|
|
294
296
|
const mapped = raw.map(mapModality).filter((m) => m !== null);
|
|
295
297
|
return mapped.length > 0 ? [...new Set(mapped)] : ["text"];
|
|
296
298
|
}
|
|
299
|
+
var NON_CHAT_ID_PATTERN = /embedding|embed|tts|whisper|image-[0-9]|image\b.*gen|diffusion|dall-e|stable-diff|sdxl|dream/i;
|
|
297
300
|
function buildOutputModalities(model) {
|
|
298
301
|
const raw = model.architecture?.output_modalities;
|
|
299
302
|
if (raw !== undefined) {
|
|
@@ -302,12 +305,14 @@ function buildOutputModalities(model) {
|
|
|
302
305
|
const mapped = raw.map(mapModality).filter((m) => m !== null);
|
|
303
306
|
return mapped.length > 0 ? [...new Set(mapped)] : ["text"];
|
|
304
307
|
}
|
|
308
|
+
if (NON_CHAT_ID_PATTERN.test(model.id))
|
|
309
|
+
return null;
|
|
305
310
|
return ["text"];
|
|
306
311
|
}
|
|
307
312
|
function buildModels(models, baseURL) {
|
|
308
313
|
const result = {};
|
|
309
314
|
for (const m of models) {
|
|
310
|
-
if (!m.id
|
|
315
|
+
if (!m.id)
|
|
311
316
|
continue;
|
|
312
317
|
const outputModalities = buildOutputModalities(m);
|
|
313
318
|
if (outputModalities === null)
|
|
@@ -323,7 +328,10 @@ function buildModels(models, baseURL) {
|
|
|
323
328
|
const hasCachePricing = cacheReadPrice > 0 || cacheWritePrice > 0;
|
|
324
329
|
const pricingTiers = buildPricingTiers(m);
|
|
325
330
|
const hasNonTextInput = inputModalities.some((mod) => mod !== "text");
|
|
331
|
+
const preferredApi = mapPreferredApi(m.preferred_api);
|
|
326
332
|
const provider = resolveModelProvider(m, baseURL);
|
|
333
|
+
const created = releaseDate(m.created);
|
|
334
|
+
const interleaved = interleavedReasoning(m, preferredApi);
|
|
327
335
|
const entry = {
|
|
328
336
|
id: m.id,
|
|
329
337
|
name: m.name ?? m.id,
|
|
@@ -347,7 +355,9 @@ function buildModels(models, baseURL) {
|
|
|
347
355
|
...params.some((p) => REASONING_PARAMS2.has(p)) ? { reasoning: true } : {},
|
|
348
356
|
...params.includes("temperature") ? { temperature: true } : {},
|
|
349
357
|
...hasNonTextInput ? { attachment: true } : {},
|
|
350
|
-
...pricingTiers ? { pricingTiers } : {}
|
|
358
|
+
...pricingTiers ? { pricingTiers } : {},
|
|
359
|
+
...created ? { release_date: created } : {},
|
|
360
|
+
...interleaved ? { interleaved } : {}
|
|
351
361
|
};
|
|
352
362
|
result[m.id] = entry;
|
|
353
363
|
}
|
|
@@ -379,7 +389,7 @@ function toRuntimeCapabilities(model) {
|
|
|
379
389
|
video: output.has("video"),
|
|
380
390
|
pdf: output.has("pdf")
|
|
381
391
|
},
|
|
382
|
-
interleaved: false
|
|
392
|
+
interleaved: model.interleaved ?? false
|
|
383
393
|
};
|
|
384
394
|
}
|
|
385
395
|
function toRuntimeModels(models, provider) {
|
|
@@ -391,7 +401,7 @@ function toRuntimeModels(models, provider) {
|
|
|
391
401
|
id: model.id,
|
|
392
402
|
providerID: provider.id,
|
|
393
403
|
api: {
|
|
394
|
-
id:
|
|
404
|
+
id: model.id,
|
|
395
405
|
url: model.provider?.api ?? providerApi,
|
|
396
406
|
npm: model.provider?.npm ?? providerNpm
|
|
397
407
|
},
|
|
@@ -417,7 +427,7 @@ function toRuntimeModels(models, provider) {
|
|
|
417
427
|
status: "active",
|
|
418
428
|
options: {},
|
|
419
429
|
headers: {},
|
|
420
|
-
release_date: ""
|
|
430
|
+
release_date: model.release_date ?? ""
|
|
421
431
|
};
|
|
422
432
|
}
|
|
423
433
|
return result;
|
|
@@ -609,6 +619,7 @@ var plugin2 = {
|
|
|
609
619
|
};
|
|
610
620
|
var src_default = plugin2;
|
|
611
621
|
export {
|
|
622
|
+
toRuntimeModels,
|
|
612
623
|
src_default as default,
|
|
613
624
|
buildModels,
|
|
614
625
|
REFRESH_TTL_MS,
|