@oh-my-pi/pi-catalog 16.1.9 → 16.1.11
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 +6 -0
- package/dist/types/model-manager.d.ts +2 -0
- package/package.json +3 -3
- package/src/compat/openai.ts +13 -5
- package/src/model-manager.ts +26 -2
- package/src/models.json +2 -4
- package/src/provider-models/openai-compat.ts +13 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [16.1.11] - 2026-06-21
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Fixed Umans `umans-glm-5.1` / `umans-glm-5.2` advertising native image input. The `models/info` endpoint reports `supports_vision: "via-handoff"` for the GLM models, meaning vision routes through a separate handoff pre-analysis step instead of accepting raw image blocks; `umansSupportsVision` treated any non-empty string as native vision support, so image prompts went directly to GLM and were rejected with `400 This model does not support image inputs`. The helper now requires `supports_vision === true`, the bundled GLM 5.1/5.2 rows are corrected to text-only, and stale mismatched Umans cache rows for those ids are dropped so the vision-handoff path runs even before a successful refresh. ([#3184](https://github.com/can1357/oh-my-pi/issues/3184))
|
|
10
|
+
|
|
5
11
|
## [16.1.9] - 2026-06-21
|
|
6
12
|
|
|
7
13
|
### Fixed
|
|
@@ -28,6 +28,8 @@ export interface ModelManagerOptions<TApi extends Api = Api, TModelsDevPayload =
|
|
|
28
28
|
cacheTtlMs?: number;
|
|
29
29
|
/** When true, a successful dynamic fetch is the complete provider catalog and prunes static-only models. */
|
|
30
30
|
dynamicModelsAuthoritative?: boolean;
|
|
31
|
+
/** Cached model ids to ignore when the cache was written against a different static catalog fingerprint. */
|
|
32
|
+
dropCachedModelIdsOnStaticMismatch?: readonly string[];
|
|
31
33
|
/** Optional dynamic endpoint fetcher. */
|
|
32
34
|
fetchDynamicModels?: () => Promise<readonly ModelSpec<TApi>[] | null>;
|
|
33
35
|
/** Optional models.dev fallback hook. */
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-catalog",
|
|
4
|
-
"version": "16.1.
|
|
4
|
+
"version": "16.1.11",
|
|
5
5
|
"description": "Model catalog for omp: bundled model database, provider discovery descriptors, model identity, classification, and equivalence",
|
|
6
6
|
"homepage": "https://omp.sh",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -34,12 +34,12 @@
|
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@bufbuild/protobuf": "^2.12.0",
|
|
37
|
-
"@oh-my-pi/pi-utils": "16.1.
|
|
37
|
+
"@oh-my-pi/pi-utils": "16.1.11",
|
|
38
38
|
"arktype": "^2.2.0",
|
|
39
39
|
"zod": "^4"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@oh-my-pi/pi-ai": "16.1.
|
|
42
|
+
"@oh-my-pi/pi-ai": "16.1.11",
|
|
43
43
|
"@types/bun": "^1.3.14"
|
|
44
44
|
},
|
|
45
45
|
"engines": {
|
package/src/compat/openai.ts
CHANGED
|
@@ -39,6 +39,10 @@ const GLM_CODING_PLAN_STREAM_IDLE_TIMEOUT_MS = 600_000;
|
|
|
39
39
|
const DEEPSEEK_REASONING_STREAM_IDLE_TIMEOUT_MS = 300_000;
|
|
40
40
|
/** Kimi K2.6 can spend several minutes reasoning before the first visible token. */
|
|
41
41
|
const KIMI_K26_REASONING_STREAM_IDLE_TIMEOUT_MS = 300_000;
|
|
42
|
+
/** Xiaomi MiMo Pro on api.xiaomimimo.com can stall ~2min before the first event (issue #1770). */
|
|
43
|
+
const XIAOMI_MIMO_STREAM_IDLE_TIMEOUT_MS = 300_000;
|
|
44
|
+
/** Alibaba Coding Plan (coding-intl.dashscope) qwen models idle before the first event (issue #1770). */
|
|
45
|
+
const ALIBABA_CODING_PLAN_STREAM_IDLE_TIMEOUT_MS = 600_000;
|
|
42
46
|
const MINIMAX_PROVIDER_OR_ID_PATTERN = /minimax/i;
|
|
43
47
|
const DSML_HEALING_PROVIDERS = new Set([
|
|
44
48
|
"ollama",
|
|
@@ -287,11 +291,15 @@ export function buildOpenAICompat(spec: ModelSpec<"openai-completions">): Resolv
|
|
|
287
291
|
const streamIdleTimeoutMs =
|
|
288
292
|
GLM_CODING_PLAN_MODEL_PATTERN.test(spec.id) && (isZai || isZhipu)
|
|
289
293
|
? GLM_CODING_PLAN_STREAM_IDLE_TIMEOUT_MS
|
|
290
|
-
:
|
|
291
|
-
?
|
|
292
|
-
:
|
|
293
|
-
?
|
|
294
|
-
:
|
|
294
|
+
: provider === "alibaba-coding-plan"
|
|
295
|
+
? ALIBABA_CODING_PLAN_STREAM_IDLE_TIMEOUT_MS
|
|
296
|
+
: isXiaomiMimo
|
|
297
|
+
? XIAOMI_MIMO_STREAM_IDLE_TIMEOUT_MS
|
|
298
|
+
: spec.reasoning && isKimiK26ModelId(spec.id)
|
|
299
|
+
? KIMI_K26_REASONING_STREAM_IDLE_TIMEOUT_MS
|
|
300
|
+
: spec.reasoning && isDirectDeepseekApi
|
|
301
|
+
? DEEPSEEK_REASONING_STREAM_IDLE_TIMEOUT_MS
|
|
302
|
+
: undefined;
|
|
295
303
|
|
|
296
304
|
// Fireworks "Fast" variants (`<id>-fast`) are served from the router
|
|
297
305
|
// namespace (`accounts/fireworks/routers/<id>-fast`), like Fire Pass, rather
|
package/src/model-manager.ts
CHANGED
|
@@ -39,6 +39,8 @@ export interface ModelManagerOptions<TApi extends Api = Api, TModelsDevPayload =
|
|
|
39
39
|
cacheTtlMs?: number;
|
|
40
40
|
/** When true, a successful dynamic fetch is the complete provider catalog and prunes static-only models. */
|
|
41
41
|
dynamicModelsAuthoritative?: boolean;
|
|
42
|
+
/** Cached model ids to ignore when the cache was written against a different static catalog fingerprint. */
|
|
43
|
+
dropCachedModelIdsOnStaticMismatch?: readonly string[];
|
|
42
44
|
/** Optional dynamic endpoint fetcher. */
|
|
43
45
|
fetchDynamicModels?: () => Promise<readonly ModelSpec<TApi>[] | null>;
|
|
44
46
|
/** Optional models.dev fallback hook. */
|
|
@@ -148,7 +150,13 @@ export async function resolveProviderModels<TApi extends Api = Api, TModelsDevPa
|
|
|
148
150
|
const shouldUseFreshCacheAsAuthoritative =
|
|
149
151
|
strategy === "online-if-uncached" && hasUsableFreshCache && hasAuthoritativeCache;
|
|
150
152
|
const dynamicFetchSucceeded = fetchedDynamicModels !== null;
|
|
151
|
-
const cacheModels = dynamicFetchSucceeded
|
|
153
|
+
const cacheModels = dynamicFetchSucceeded
|
|
154
|
+
? []
|
|
155
|
+
: dropCachedModelIdsOnStaticMismatch(
|
|
156
|
+
normalizeModelList<TApi>(cache?.models ?? []),
|
|
157
|
+
cacheFingerprintMatches,
|
|
158
|
+
options.dropCachedModelIdsOnStaticMismatch,
|
|
159
|
+
);
|
|
152
160
|
const dynamicModels = fetchedDynamicModels ?? [];
|
|
153
161
|
const mergedWithCache = mergeDynamicModels(mergeModelSources(staticModels, modelsDevModels), cacheModels);
|
|
154
162
|
const mergedModels = mergeDynamicModels(mergedWithCache, dynamicModels);
|
|
@@ -180,7 +188,11 @@ export async function resolveProviderModels<TApi extends Api = Api, TModelsDevPa
|
|
|
180
188
|
collapseBuiltModelVariants(
|
|
181
189
|
mergeDynamicModels(
|
|
182
190
|
mergeModelSources(staticModels, modelsDevModels),
|
|
183
|
-
|
|
191
|
+
dropCachedModelIdsOnStaticMismatch(
|
|
192
|
+
normalizeModelList<TApi>(latestCache?.models ?? cache?.models ?? []),
|
|
193
|
+
cacheFingerprintMatches,
|
|
194
|
+
options.dropCachedModelIdsOnStaticMismatch,
|
|
195
|
+
),
|
|
184
196
|
),
|
|
185
197
|
),
|
|
186
198
|
false,
|
|
@@ -248,6 +260,18 @@ function shouldFetchRemoteSources(
|
|
|
248
260
|
return false;
|
|
249
261
|
}
|
|
250
262
|
|
|
263
|
+
function dropCachedModelIdsOnStaticMismatch<TApi extends Api>(
|
|
264
|
+
models: readonly Model<TApi>[],
|
|
265
|
+
cacheFingerprintMatches: boolean,
|
|
266
|
+
ids: readonly string[] | undefined,
|
|
267
|
+
): Model<TApi>[] {
|
|
268
|
+
if (cacheFingerprintMatches || ids === undefined || ids.length === 0 || models.length === 0) {
|
|
269
|
+
return models.length === 0 ? [] : [...models];
|
|
270
|
+
}
|
|
271
|
+
const droppedIds = new Set(ids);
|
|
272
|
+
return models.filter(model => !droppedIds.has(model.id));
|
|
273
|
+
}
|
|
274
|
+
|
|
251
275
|
function mergeModelSources<TApi extends Api>(...sources: readonly (readonly Model<TApi>[])[]): Model<TApi>[] {
|
|
252
276
|
// Strip out empty/missing sources up front. The hot path is `(static, [])`
|
|
253
277
|
// (modelsDev disabled / failed) — a single non-empty source means we can
|
package/src/models.json
CHANGED
|
@@ -69284,8 +69284,7 @@
|
|
|
69284
69284
|
"baseUrl": "https://api.code.umans.ai",
|
|
69285
69285
|
"reasoning": true,
|
|
69286
69286
|
"input": [
|
|
69287
|
-
"text"
|
|
69288
|
-
"image"
|
|
69287
|
+
"text"
|
|
69289
69288
|
],
|
|
69290
69289
|
"cost": {
|
|
69291
69290
|
"input": 0,
|
|
@@ -69327,8 +69326,7 @@
|
|
|
69327
69326
|
]
|
|
69328
69327
|
},
|
|
69329
69328
|
"input": [
|
|
69330
|
-
"text"
|
|
69331
|
-
"image"
|
|
69329
|
+
"text"
|
|
69332
69330
|
],
|
|
69333
69331
|
"cost": {
|
|
69334
69332
|
"input": 0,
|
|
@@ -568,6 +568,7 @@ const UMANS_REASONING_EFFORT_BY_LEVEL: Record<string, Effort> = {
|
|
|
568
568
|
xhigh: Effort.XHigh,
|
|
569
569
|
};
|
|
570
570
|
const UMANS_DEFAULT_REASONING_EFFORTS = [Effort.Minimal, Effort.Low, Effort.Medium, Effort.High, Effort.XHigh] as const;
|
|
571
|
+
const UMANS_VIA_HANDOFF_MODEL_IDS = ["umans-glm-5.1", "umans-glm-5.2"] as const;
|
|
571
572
|
|
|
572
573
|
export interface UmansModelManagerConfig {
|
|
573
574
|
apiKey?: string;
|
|
@@ -586,8 +587,18 @@ function normalizeUmansBaseUrl(baseUrl: string | undefined): string {
|
|
|
586
587
|
return normalized.endsWith("/v1") ? normalized.slice(0, -3) : normalized;
|
|
587
588
|
}
|
|
588
589
|
|
|
590
|
+
/**
|
|
591
|
+
* Umans `models/info` reports `supports_vision: true` for natively
|
|
592
|
+
* vision-capable models and a non-empty string sentinel (e.g.
|
|
593
|
+
* `"via-handoff"`) for models that route image inputs through a vision
|
|
594
|
+
* handoff pre-analysis step instead of accepting raw image blocks. Only
|
|
595
|
+
* `true` means the model accepts image content directly; sentinel values
|
|
596
|
+
* MUST map to text-only so the agent's vision-handoff path runs instead
|
|
597
|
+
* of triggering an upstream HTTP 400 (`This model does not support image
|
|
598
|
+
* inputs`).
|
|
599
|
+
*/
|
|
589
600
|
function umansSupportsVision(value: unknown): boolean {
|
|
590
|
-
return value === true
|
|
601
|
+
return value === true;
|
|
591
602
|
}
|
|
592
603
|
|
|
593
604
|
function umansReasoningSupported(value: unknown): boolean {
|
|
@@ -704,6 +715,7 @@ export function umansModelManagerOptions(config?: UmansModelManagerConfig): Mode
|
|
|
704
715
|
return {
|
|
705
716
|
providerId: "umans",
|
|
706
717
|
dynamicModelsAuthoritative: true,
|
|
718
|
+
dropCachedModelIdsOnStaticMismatch: UMANS_VIA_HANDOFF_MODEL_IDS,
|
|
707
719
|
fetchDynamicModels: () => fetchUmansModelsInfo({ baseUrl, apiKey, fetch: config?.fetch, references }),
|
|
708
720
|
};
|
|
709
721
|
}
|