@oh-my-pi/pi-catalog 18.13.1 → 18.13.2
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/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [18.13.2] - 2026-10-27
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added LM Studio decision models to discovery: models LM Studio reports as `type: "decision"` are listed as `judge` models served over its `/v1/decisions` endpoint, alongside its chat models ([#17169](https://github.com/can1357/oh-my-pi/pull/17169) by [@richiestanaway](https://github.com/richiestanaway))
|
|
10
|
+
|
|
5
11
|
## [18.13.1] - 2026-10-27
|
|
6
12
|
|
|
7
13
|
### Added
|
|
@@ -377,24 +377,32 @@ export declare const KIMI_CODE_DEFAULT_MAX_TOKENS = 32000;
|
|
|
377
377
|
export declare function kimiCodeMaxTokens(modelId: string, fallback?: number): number;
|
|
378
378
|
export declare function kimiCodeMaxTokens(modelId: string, fallback: number | null): number | null;
|
|
379
379
|
export declare function kimiCodeModelManagerOptions(config?: KimiCodeModelManagerConfig): ModelManagerOptions<"openai-completions">;
|
|
380
|
-
/** Native LM Studio metadata keyed by model id from `/api/v0/models`. */
|
|
380
|
+
/** Native LM Studio metadata keyed by model id from `/api/v0/models` and `/api/v1/models`. */
|
|
381
381
|
export interface LmStudioNativeModelMetadata {
|
|
382
382
|
input: ("text" | "image")[];
|
|
383
383
|
contextWindow?: number;
|
|
384
|
+
/**
|
|
385
|
+
* LM Studio reports a decision model (`type: "decision"`): it answers typed
|
|
386
|
+
* questions on `/v1/decisions` and rejects chat completions.
|
|
387
|
+
*/
|
|
388
|
+
decision?: true;
|
|
384
389
|
}
|
|
385
390
|
/** Options for LM Studio's optional native metadata probe. */
|
|
386
391
|
export interface LmStudioNativeModelMetadataOptions {
|
|
387
392
|
headers?: Record<string, string>;
|
|
388
393
|
signal?: AbortSignal;
|
|
389
394
|
}
|
|
390
|
-
/**
|
|
395
|
+
/**
|
|
396
|
+
* Fetches LM Studio native model metadata used to mark VLM models as
|
|
397
|
+
* image-capable and decision models as judges.
|
|
398
|
+
*/
|
|
391
399
|
export declare function fetchLmStudioNativeModelMetadata(baseUrl: string, fetchImpl?: FetchImpl, options?: LmStudioNativeModelMetadataOptions): Promise<Map<string, LmStudioNativeModelMetadata> | null>;
|
|
392
400
|
export interface LmStudioModelManagerConfig {
|
|
393
401
|
apiKey?: string;
|
|
394
402
|
baseUrl?: string;
|
|
395
403
|
fetch?: FetchImpl;
|
|
396
404
|
}
|
|
397
|
-
export declare function lmStudioModelManagerOptions(config?: LmStudioModelManagerConfig): ModelManagerOptions<
|
|
405
|
+
export declare function lmStudioModelManagerOptions(config?: LmStudioModelManagerConfig): ModelManagerOptions<Api>;
|
|
398
406
|
export interface SyntheticModelManagerConfig {
|
|
399
407
|
apiKey?: string;
|
|
400
408
|
baseUrl?: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oh-my-pi/pi-catalog",
|
|
3
|
-
"version": "18.13.
|
|
3
|
+
"version": "18.13.2",
|
|
4
4
|
"description": "Model catalog for omp: bundled model database, provider discovery descriptors, model identity, classification, and equivalence",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -93,12 +93,12 @@
|
|
|
93
93
|
"gen:proto": "bun scripts/generate-protocols.ts"
|
|
94
94
|
},
|
|
95
95
|
"dependencies": {
|
|
96
|
-
"@oh-my-pi/omptype": "18.13.
|
|
97
|
-
"@oh-my-pi/pi-utils": "18.13.
|
|
96
|
+
"@oh-my-pi/omptype": "18.13.2",
|
|
97
|
+
"@oh-my-pi/pi-utils": "18.13.2"
|
|
98
98
|
},
|
|
99
99
|
"devDependencies": {
|
|
100
100
|
"@bgotink/kdl": "0.4.0",
|
|
101
|
-
"@oh-my-pi/pi-ai": "18.13.
|
|
101
|
+
"@oh-my-pi/pi-ai": "18.13.2",
|
|
102
102
|
"@types/bun": "^1.3.14"
|
|
103
103
|
},
|
|
104
104
|
"engines": {
|
|
@@ -4271,10 +4271,15 @@ export function kimiCodeModelManagerOptions(
|
|
|
4271
4271
|
// 12.5. LM Studio
|
|
4272
4272
|
// ---------------------------------------------------------------------------
|
|
4273
4273
|
|
|
4274
|
-
/** Native LM Studio metadata keyed by model id from `/api/v0/models`. */
|
|
4274
|
+
/** Native LM Studio metadata keyed by model id from `/api/v0/models` and `/api/v1/models`. */
|
|
4275
4275
|
export interface LmStudioNativeModelMetadata {
|
|
4276
4276
|
input: ("text" | "image")[];
|
|
4277
4277
|
contextWindow?: number;
|
|
4278
|
+
/**
|
|
4279
|
+
* LM Studio reports a decision model (`type: "decision"`): it answers typed
|
|
4280
|
+
* questions on `/v1/decisions` and rejects chat completions.
|
|
4281
|
+
*/
|
|
4282
|
+
decision?: true;
|
|
4278
4283
|
}
|
|
4279
4284
|
|
|
4280
4285
|
/** Options for LM Studio's optional native metadata probe. */
|
|
@@ -4298,13 +4303,56 @@ function getLmStudioCapabilityNames(value: unknown): string[] {
|
|
|
4298
4303
|
return value.flatMap(item => (typeof item === "string" ? [item.toLowerCase()] : []));
|
|
4299
4304
|
}
|
|
4300
4305
|
|
|
4306
|
+
function getLmStudioModelType(entry: Record<string, unknown>): string {
|
|
4307
|
+
return typeof entry.type === "string" ? entry.type.toLowerCase() : "";
|
|
4308
|
+
}
|
|
4309
|
+
|
|
4301
4310
|
function getLmStudioNativeInput(entry: Record<string, unknown>): ("text" | "image")[] {
|
|
4302
|
-
const modelType =
|
|
4311
|
+
const modelType = getLmStudioModelType(entry);
|
|
4312
|
+
// A decision model stays `type: "decision"` when it reads images; LM Studio
|
|
4313
|
+
// says so through `has_vision_adapter` instead.
|
|
4314
|
+
if (modelType === "decision") {
|
|
4315
|
+
return entry.has_vision_adapter === true ? ["text", "image"] : ["text"];
|
|
4316
|
+
}
|
|
4303
4317
|
const capabilities = getLmStudioCapabilityNames(entry.capabilities);
|
|
4304
4318
|
const supportsImage = modelType === "vlm" || capabilities.includes("vision") || capabilities.includes("image");
|
|
4305
4319
|
return supportsImage ? ["text", "image"] : ["text"];
|
|
4306
4320
|
}
|
|
4307
4321
|
|
|
4322
|
+
/**
|
|
4323
|
+
* Decision models from LM Studio's v1 listing (`GET /api/v1/models`), keyed by
|
|
4324
|
+
* model key and by every loaded instance id. The v0 listing predates decision
|
|
4325
|
+
* models, so this is where `type: "decision"` is reported.
|
|
4326
|
+
*/
|
|
4327
|
+
function getLmStudioV1DecisionModels(payload: unknown): Map<string, LmStudioNativeModelMetadata> {
|
|
4328
|
+
const metadata = new Map<string, LmStudioNativeModelMetadata>();
|
|
4329
|
+
if (!isRecord(payload) || !Array.isArray(payload.models)) {
|
|
4330
|
+
return metadata;
|
|
4331
|
+
}
|
|
4332
|
+
for (const entry of payload.models) {
|
|
4333
|
+
if (!isRecord(entry) || getLmStudioModelType(entry) !== "decision") {
|
|
4334
|
+
continue;
|
|
4335
|
+
}
|
|
4336
|
+
const instances = Array.isArray(entry.loaded_instances) ? entry.loaded_instances.filter(isRecord) : [];
|
|
4337
|
+
const loadedContextWindow = instances
|
|
4338
|
+
.map(instance => (isRecord(instance.config) ? toPositiveNumber(instance.config.context_length, null) : null))
|
|
4339
|
+
.find(window => window !== null);
|
|
4340
|
+
const contextWindow = loadedContextWindow ?? toPositiveNumber(entry.max_context_length, null) ?? undefined;
|
|
4341
|
+
const model: LmStudioNativeModelMetadata = {
|
|
4342
|
+
input: getLmStudioNativeInput(entry),
|
|
4343
|
+
...(contextWindow === undefined ? {} : { contextWindow }),
|
|
4344
|
+
decision: true,
|
|
4345
|
+
};
|
|
4346
|
+
const ids = [entry.key, ...instances.map(instance => instance.id)];
|
|
4347
|
+
for (const id of ids) {
|
|
4348
|
+
if (typeof id === "string" && id.length > 0) {
|
|
4349
|
+
metadata.set(id, model);
|
|
4350
|
+
}
|
|
4351
|
+
}
|
|
4352
|
+
}
|
|
4353
|
+
return metadata;
|
|
4354
|
+
}
|
|
4355
|
+
|
|
4308
4356
|
function getLmStudioNativeContextWindow(entry: Record<string, unknown>): number | undefined {
|
|
4309
4357
|
// LM Studio serves `loaded_context_length` (the window the running instance
|
|
4310
4358
|
// actually accepts) alongside `max_context_length` (the architectural
|
|
@@ -4323,43 +4371,52 @@ function getLmStudioNativeContextWindow(entry: Record<string, unknown>): number
|
|
|
4323
4371
|
);
|
|
4324
4372
|
}
|
|
4325
4373
|
|
|
4326
|
-
/**
|
|
4374
|
+
/**
|
|
4375
|
+
* Fetches LM Studio native model metadata used to mark VLM models as
|
|
4376
|
+
* image-capable and decision models as judges.
|
|
4377
|
+
*/
|
|
4327
4378
|
export async function fetchLmStudioNativeModelMetadata(
|
|
4328
4379
|
baseUrl: string,
|
|
4329
4380
|
fetchImpl: FetchImpl = fetch,
|
|
4330
4381
|
options?: LmStudioNativeModelMetadataOptions,
|
|
4331
4382
|
): Promise<Map<string, LmStudioNativeModelMetadata> | null> {
|
|
4332
4383
|
const nativeBaseUrl = toLmStudioNativeBaseUrl(baseUrl);
|
|
4333
|
-
const
|
|
4384
|
+
const fetchJson = async (path: string, signal?: AbortSignal): Promise<unknown> => {
|
|
4334
4385
|
try {
|
|
4335
|
-
const response = await fetchImpl(`${nativeBaseUrl}
|
|
4386
|
+
const response = await fetchImpl(`${nativeBaseUrl}${path}`, {
|
|
4336
4387
|
method: "GET",
|
|
4337
4388
|
headers: { Accept: "application/json", ...options?.headers },
|
|
4338
4389
|
signal,
|
|
4339
4390
|
});
|
|
4340
|
-
|
|
4341
|
-
return null;
|
|
4342
|
-
}
|
|
4343
|
-
const payload = await response.json();
|
|
4344
|
-
if (!isRecord(payload) || !Array.isArray(payload.data)) {
|
|
4345
|
-
return null;
|
|
4346
|
-
}
|
|
4347
|
-
const metadata = new Map<string, LmStudioNativeModelMetadata>();
|
|
4348
|
-
for (const entry of payload.data) {
|
|
4349
|
-
if (!isRecord(entry) || typeof entry.id !== "string" || entry.id.length === 0) {
|
|
4350
|
-
continue;
|
|
4351
|
-
}
|
|
4352
|
-
const contextWindow = getLmStudioNativeContextWindow(entry);
|
|
4353
|
-
metadata.set(entry.id, {
|
|
4354
|
-
input: getLmStudioNativeInput(entry),
|
|
4355
|
-
...(contextWindow === undefined ? {} : { contextWindow }),
|
|
4356
|
-
});
|
|
4357
|
-
}
|
|
4358
|
-
return metadata;
|
|
4391
|
+
return response.ok ? await response.json() : null;
|
|
4359
4392
|
} catch {
|
|
4360
4393
|
return null;
|
|
4361
4394
|
}
|
|
4362
4395
|
};
|
|
4396
|
+
const fetchMetadata = async (signal?: AbortSignal): Promise<Map<string, LmStudioNativeModelMetadata> | null> => {
|
|
4397
|
+
// Builds without the v1 listing answer 404 there and keep v0 metadata alone.
|
|
4398
|
+
const [v0, v1] = await Promise.all([fetchJson("/api/v0/models", signal), fetchJson("/api/v1/models", signal)]);
|
|
4399
|
+
const decisions = getLmStudioV1DecisionModels(v1);
|
|
4400
|
+
if ((!isRecord(v0) || !Array.isArray(v0.data)) && decisions.size === 0) {
|
|
4401
|
+
return null;
|
|
4402
|
+
}
|
|
4403
|
+
const metadata = new Map<string, LmStudioNativeModelMetadata>();
|
|
4404
|
+
for (const entry of isRecord(v0) && Array.isArray(v0.data) ? v0.data : []) {
|
|
4405
|
+
if (!isRecord(entry) || typeof entry.id !== "string" || entry.id.length === 0) {
|
|
4406
|
+
continue;
|
|
4407
|
+
}
|
|
4408
|
+
const contextWindow = getLmStudioNativeContextWindow(entry);
|
|
4409
|
+
metadata.set(entry.id, {
|
|
4410
|
+
input: getLmStudioNativeInput(entry),
|
|
4411
|
+
...(contextWindow === undefined ? {} : { contextWindow }),
|
|
4412
|
+
...(getLmStudioModelType(entry) === "decision" ? { decision: true as const } : {}),
|
|
4413
|
+
});
|
|
4414
|
+
}
|
|
4415
|
+
for (const [id, model] of decisions) {
|
|
4416
|
+
metadata.set(id, model);
|
|
4417
|
+
}
|
|
4418
|
+
return metadata;
|
|
4419
|
+
};
|
|
4363
4420
|
if (options?.signal !== undefined) {
|
|
4364
4421
|
return fetchMetadata(options.signal);
|
|
4365
4422
|
}
|
|
@@ -4372,9 +4429,7 @@ export interface LmStudioModelManagerConfig {
|
|
|
4372
4429
|
fetch?: FetchImpl;
|
|
4373
4430
|
}
|
|
4374
4431
|
|
|
4375
|
-
export function lmStudioModelManagerOptions(
|
|
4376
|
-
config?: LmStudioModelManagerConfig,
|
|
4377
|
-
): ModelManagerOptions<"openai-completions"> {
|
|
4432
|
+
export function lmStudioModelManagerOptions(config?: LmStudioModelManagerConfig): ModelManagerOptions<Api> {
|
|
4378
4433
|
const apiKey = config?.apiKey;
|
|
4379
4434
|
const baseUrl = config?.baseUrl ?? Bun.env.LM_STUDIO_BASE_URL ?? "http://127.0.0.1:1234/v1";
|
|
4380
4435
|
const references = createBundledReferenceMap<"openai-completions">("lm-studio" as any);
|
|
@@ -4402,13 +4457,17 @@ export function lmStudioModelManagerOptions(
|
|
|
4402
4457
|
if (!nativeMetadata) {
|
|
4403
4458
|
return models;
|
|
4404
4459
|
}
|
|
4405
|
-
return models.map(model => {
|
|
4460
|
+
return models.map((model): ModelSpec<Api> => {
|
|
4406
4461
|
const metadata = nativeMetadata.get(model.id);
|
|
4407
4462
|
if (!metadata) {
|
|
4408
4463
|
return model;
|
|
4409
4464
|
}
|
|
4410
4465
|
return {
|
|
4411
4466
|
...model,
|
|
4467
|
+
// Decision models answer only on `/v1/decisions` (OpenAI's Decisions API).
|
|
4468
|
+
...(metadata.decision
|
|
4469
|
+
? { api: "openai-decisions", kind: "judge", reasoning: false, supportsTools: false }
|
|
4470
|
+
: {}),
|
|
4412
4471
|
input: metadata.input,
|
|
4413
4472
|
contextWindow: metadata.contextWindow ?? model.contextWindow,
|
|
4414
4473
|
};
|