feat(cliproxy): add thinking config to catalog --json and test false values

- Include thinking support configuration (type, min, max, levels) in
  JSON output for reasoning-capable models
- Add test verifying explicit false booleans are preserved (not omitted)
- Add test verifying thinking config appears for thinking models

Built [OnSteroids](https://onsteroids.ai)
This commit is contained in:
Sergey Galuza
2026-04-11 20:12:46 +02:00
parent 9ac1ac9804
commit 22acd96ba4
2 changed files with 36 additions and 0 deletions
@@ -10,6 +10,7 @@ import {
import { getCatalogRoutingSnapshot } from '../../cliproxy/catalog-routing';
import { ensureManagedModelPrefixes } from '../../cliproxy/managed-model-prefixes';
import { getProxyTarget } from '../../cliproxy/proxy-target-resolver';
import type { ThinkingSupport } from '../../cliproxy/model-catalog';
import type { CLIProxyProvider } from '../../cliproxy/types';
import type { RemoteModelInfo } from '../../cliproxy/management-api-types';
import type { CliproxyProviderRoutingHints } from '../../shared/cliproxy-model-routing';
@@ -183,6 +184,7 @@ interface CatalogJsonModel {
deprecated?: boolean;
deprecationReason?: string;
broken?: boolean;
thinking?: ThinkingSupport;
extendedContext?: boolean;
nativeImageInput?: boolean;
}
@@ -203,6 +205,7 @@ export function handleCatalogJson(): void {
if (m.deprecated !== undefined) entry.deprecated = m.deprecated;
if (m.deprecationReason !== undefined) entry.deprecationReason = m.deprecationReason;
if (m.broken !== undefined) entry.broken = m.broken;
if (m.thinking !== undefined) entry.thinking = m.thinking;
if (m.extendedContext !== undefined) entry.extendedContext = m.extendedContext;
if (m.nativeImageInput !== undefined) entry.nativeImageInput = m.nativeImageInput;
return entry;
@@ -75,6 +75,39 @@ describe('cliproxy catalog --json output', () => {
}
});
it('includes explicit false boolean values in output', () => {
handleCatalogJson();
const parsed = JSON.parse(capturedOutput[0]) as Record<
string,
Array<Record<string, unknown>>
>;
const allModels = Object.values(parsed).flat();
// Static catalog has models with extendedContext: false
const withExplicitFalse = allModels.filter((m) => m.extendedContext === false);
expect(withExplicitFalse.length).toBeGreaterThan(0);
});
it('includes thinking configuration when present on models', () => {
handleCatalogJson();
const parsed = JSON.parse(capturedOutput[0]) as Record<
string,
Array<Record<string, unknown>>
>;
const allModels = Object.values(parsed).flat();
// Static catalog has thinking models (e.g. Claude Opus 4.6 Thinking)
const withThinking = allModels.filter((m) => m.thinking !== undefined);
expect(withThinking.length).toBeGreaterThan(0);
for (const model of withThinking) {
const thinking = model.thinking as Record<string, unknown>;
expect(['budget', 'levels', 'none']).toContain(thinking.type);
}
});
it('outputs minified JSON (single line, no whitespace formatting)', () => {
handleCatalogJson();