@oh-my-pi/pi-coding-agent 13.12.5 → 13.12.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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,17 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [13.12.6] - 2026-03-15
6
+ ### Changed
7
+
8
+ - Updated llama.cpp model discovery to read context window from the `/props` endpoint's `default_generation_settings.n_ctx` field instead of using hardcoded 128000 default
9
+ - Updated llama.cpp model discovery to detect vision capabilities from the `/props` endpoint's `modalities.vision` field instead of defaulting to text-only input
10
+ - Changed llama.cpp `maxTokens` calculation to respect discovered context window limits, capping at 8192 or the server's context window, whichever is smaller
11
+
12
+ ### Fixed
13
+
14
+ - Fixed llama.cpp auto-discovery to read context window and vision support from the native `/props` endpoint instead of relying on hardcoded defaults
15
+
5
16
  ## [13.12.5] - 2026-03-15
6
17
 
7
18
  ### Added
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/pi-coding-agent",
4
- "version": "13.12.5",
4
+ "version": "13.12.6",
5
5
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
6
6
  "homepage": "https://github.com/can1357/oh-my-pi",
7
7
  "author": "Can Boluk",
@@ -41,12 +41,12 @@
41
41
  },
42
42
  "dependencies": {
43
43
  "@mozilla/readability": "^0.6",
44
- "@oh-my-pi/omp-stats": "13.12.5",
45
- "@oh-my-pi/pi-agent-core": "13.12.5",
46
- "@oh-my-pi/pi-ai": "13.12.5",
47
- "@oh-my-pi/pi-natives": "13.12.5",
48
- "@oh-my-pi/pi-tui": "13.12.5",
49
- "@oh-my-pi/pi-utils": "13.12.5",
44
+ "@oh-my-pi/omp-stats": "13.12.6",
45
+ "@oh-my-pi/pi-agent-core": "13.12.6",
46
+ "@oh-my-pi/pi-ai": "13.12.6",
47
+ "@oh-my-pi/pi-natives": "13.12.6",
48
+ "@oh-my-pi/pi-tui": "13.12.6",
49
+ "@oh-my-pi/pi-utils": "13.12.6",
50
50
  "@sinclair/typebox": "^0.34",
51
51
  "@xterm/headless": "^6.0",
52
52
  "ajv": "^8.18",
@@ -372,6 +372,11 @@ type OllamaDiscoveredModelMetadata = {
372
372
  contextWindow?: number;
373
373
  };
374
374
 
375
+ type LlamaCppDiscoveredServerMetadata = {
376
+ contextWindow?: number;
377
+ input?: ("text" | "image")[];
378
+ };
379
+
375
380
  /**
376
381
  * Resolve an API key config value to an actual key.
377
382
  * Checks environment variable first, then treats as literal.
@@ -416,6 +421,25 @@ function extractOllamaContextWindow(payload: Record<string, unknown>): number |
416
421
  return match ? toPositiveNumberOrUndefined(match[1]) : undefined;
417
422
  }
418
423
 
424
+ function extractLlamaCppContextWindow(payload: Record<string, unknown>): number | undefined {
425
+ const generationSettings = payload.default_generation_settings;
426
+ if (isRecord(generationSettings)) {
427
+ const contextWindow = toPositiveNumberOrUndefined(generationSettings.n_ctx);
428
+ if (contextWindow !== undefined) {
429
+ return contextWindow;
430
+ }
431
+ }
432
+ return toPositiveNumberOrUndefined(payload.n_ctx);
433
+ }
434
+
435
+ function extractLlamaCppInputCapabilities(payload: Record<string, unknown>): ("text" | "image")[] | undefined {
436
+ const modalities = payload.modalities;
437
+ if (!isRecord(modalities)) {
438
+ return undefined;
439
+ }
440
+ return modalities.vision === true ? ["text", "image"] : ["text"];
441
+ }
442
+
419
443
  function extractGoogleOAuthToken(value: string | undefined): string | undefined {
420
444
  if (!isAuthenticated(value)) return undefined;
421
445
  try {
@@ -1225,6 +1249,32 @@ export class ModelRegistry {
1225
1249
  return this.#applyProviderModelOverrides(providerConfig.provider, discovered);
1226
1250
  }
1227
1251
 
1252
+ async #discoverLlamaCppServerMetadata(
1253
+ baseUrl: string,
1254
+ headers: Record<string, string> | undefined,
1255
+ ): Promise<LlamaCppDiscoveredServerMetadata | null> {
1256
+ const propsUrl = `${this.#toLlamaCppNativeBaseUrl(baseUrl)}/props`;
1257
+ try {
1258
+ const response = await fetch(propsUrl, {
1259
+ headers,
1260
+ signal: AbortSignal.timeout(150),
1261
+ });
1262
+ if (!response.ok) {
1263
+ return null;
1264
+ }
1265
+ const payload = (await response.json()) as unknown;
1266
+ if (!isRecord(payload)) {
1267
+ return null;
1268
+ }
1269
+ return {
1270
+ contextWindow: extractLlamaCppContextWindow(payload),
1271
+ input: extractLlamaCppInputCapabilities(payload),
1272
+ };
1273
+ } catch {
1274
+ return null;
1275
+ }
1276
+ }
1277
+
1228
1278
  async #discoverLlamaCppModels(providerConfig: DiscoveryProviderConfig): Promise<Model<Api>[]> {
1229
1279
  const baseUrl = this.#normalizeLlamaCppBaseUrl(providerConfig.baseUrl);
1230
1280
  const modelsUrl = `${baseUrl}/models`;
@@ -1235,10 +1285,13 @@ export class ModelRegistry {
1235
1285
  headers.Authorization = `Bearer ${apiKey}`;
1236
1286
  }
1237
1287
 
1238
- const response = await fetch(modelsUrl, {
1239
- headers,
1240
- signal: AbortSignal.timeout(250),
1241
- });
1288
+ const [response, serverMetadata] = await Promise.all([
1289
+ fetch(modelsUrl, {
1290
+ headers,
1291
+ signal: AbortSignal.timeout(250),
1292
+ }),
1293
+ this.#discoverLlamaCppServerMetadata(baseUrl, headers),
1294
+ ]);
1242
1295
  if (!response.ok) {
1243
1296
  throw new Error(`HTTP ${response.status} from ${modelsUrl}`);
1244
1297
  }
@@ -1256,10 +1309,10 @@ export class ModelRegistry {
1256
1309
  provider: providerConfig.provider,
1257
1310
  baseUrl,
1258
1311
  reasoning: false,
1259
- input: ["text"],
1312
+ input: serverMetadata?.input ?? ["text"],
1260
1313
  cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
1261
- contextWindow: 128000,
1262
- maxTokens: 8192,
1314
+ contextWindow: serverMetadata?.contextWindow ?? 128000,
1315
+ maxTokens: Math.min(serverMetadata?.contextWindow ?? Number.POSITIVE_INFINITY, 8192),
1263
1316
  headers,
1264
1317
  compat: {
1265
1318
  supportsStore: false,
@@ -1331,6 +1384,18 @@ export class ModelRegistry {
1331
1384
  }
1332
1385
  }
1333
1386
 
1387
+ #toLlamaCppNativeBaseUrl(baseUrl: string): string {
1388
+ try {
1389
+ const parsed = new URL(baseUrl);
1390
+ const trimmedPath = parsed.pathname.replace(/\/+$/g, "");
1391
+ parsed.pathname = trimmedPath.endsWith("/v1") ? trimmedPath.slice(0, -3) || "/" : trimmedPath || "/";
1392
+ const normalized = `${parsed.protocol}//${parsed.host}${parsed.pathname}`;
1393
+ return normalized.endsWith("/") ? normalized.slice(0, -1) : normalized;
1394
+ } catch {
1395
+ return baseUrl.endsWith("/v1") ? baseUrl.slice(0, -3) : baseUrl;
1396
+ }
1397
+ }
1398
+
1334
1399
  #normalizeLmStudioBaseUrl(baseUrl?: string): string {
1335
1400
  const defaultBaseUrl = "http://127.0.0.1:1234/v1";
1336
1401
  const raw = baseUrl || defaultBaseUrl;