mirror of
https://github.com/tiennm99/ccs.git
synced 2026-09-05 00:18:05 +00:00
fix(delegation): dynamic model display from settings
- Read ANTHROPIC_MODEL from profile settings instead of hardcoding - Display model name in full uppercase (GLM-4.7, not Glm-4.7) - Add null/undefined guard to getModelDisplayName - Remove hardcoded GLM-4.6/GLM-4.6 (Thinking) display names Closes #431
This commit is contained in:
@@ -14,7 +14,7 @@ import { ui, warn, info } from '../utils/ui';
|
|||||||
import { type ExecutionOptions, type ExecutionResult, type StreamMessage } from './executor/types';
|
import { type ExecutionOptions, type ExecutionResult, type StreamMessage } from './executor/types';
|
||||||
import { StreamBuffer, formatToolVerbose } from './executor/stream-parser';
|
import { StreamBuffer, formatToolVerbose } from './executor/stream-parser';
|
||||||
import { buildExecutionResult } from './executor/result-aggregator';
|
import { buildExecutionResult } from './executor/result-aggregator';
|
||||||
import { getCcsDir } from '../utils/config-manager';
|
import { getCcsDir, getModelDisplayName } from '../utils/config-manager';
|
||||||
|
|
||||||
// Re-export types for consumers
|
// Re-export types for consumers
|
||||||
export type { ExecutionOptions, ExecutionResult, StreamMessage } from './executor/types';
|
export type { ExecutionOptions, ExecutionResult, StreamMessage } from './executor/types';
|
||||||
@@ -196,8 +196,7 @@ export class HeadlessExecutor {
|
|||||||
const streamBuffer = new StreamBuffer();
|
const streamBuffer = new StreamBuffer();
|
||||||
|
|
||||||
if (showProgress) {
|
if (showProgress) {
|
||||||
const modelName =
|
const modelName = getModelDisplayName(profile);
|
||||||
profile === 'glm' ? 'GLM-4.6' : profile === 'kimi' ? 'Kimi' : profile.toUpperCase();
|
|
||||||
console.error(ui.info(`Delegating to ${modelName}...`));
|
console.error(ui.info(`Delegating to ${modelName}...`));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import * as path from 'path';
|
|||||||
import { execSync } from 'child_process';
|
import { execSync } from 'child_process';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import { ui } from '../utils/ui';
|
import { ui } from '../utils/ui';
|
||||||
|
import { getModelDisplayName } from '../utils/config-manager';
|
||||||
import type { ExecutionResult, ExecutionError, PermissionDenial } from './executor/types';
|
import type { ExecutionResult, ExecutionError, PermissionDenial } from './executor/types';
|
||||||
|
|
||||||
// Alias for backward compatibility
|
// Alias for backward compatibility
|
||||||
@@ -58,7 +59,7 @@ class ResultFormatter {
|
|||||||
let output = '';
|
let output = '';
|
||||||
|
|
||||||
// Header box
|
// Header box
|
||||||
const modelName = this.getModelDisplayName(profile);
|
const modelName = getModelDisplayName(profile);
|
||||||
const headerIcon = success ? '[i]' : '[X]';
|
const headerIcon = success ? '[i]' : '[X]';
|
||||||
output += ui.box(`${headerIcon} Delegated to ${modelName} (ccs:${profile})`, {
|
output += ui.box(`${headerIcon} Delegated to ${modelName} (ccs:${profile})`, {
|
||||||
borderStyle: 'round',
|
borderStyle: 'round',
|
||||||
@@ -225,7 +226,7 @@ class ResultFormatter {
|
|||||||
*/
|
*/
|
||||||
private static formatInfoTable(result: ExecutionResult): string {
|
private static formatInfoTable(result: ExecutionResult): string {
|
||||||
const { cwd, profile, duration, exitCode, sessionId, totalCost, numTurns } = result;
|
const { cwd, profile, duration, exitCode, sessionId, totalCost, numTurns } = result;
|
||||||
const modelName = this.getModelDisplayName(profile);
|
const modelName = getModelDisplayName(profile);
|
||||||
const durationSec = (duration / 1000).toFixed(1);
|
const durationSec = (duration / 1000).toFixed(1);
|
||||||
|
|
||||||
const rows: string[][] = [
|
const rows: string[][] = [
|
||||||
@@ -253,20 +254,6 @@ class ResultFormatter {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get display name for model profile
|
|
||||||
*/
|
|
||||||
private static getModelDisplayName(profile: string): string {
|
|
||||||
const displayNames: Record<string, string> = {
|
|
||||||
glm: 'GLM-4.6',
|
|
||||||
glmt: 'GLM-4.6 (Thinking)',
|
|
||||||
kimi: 'Kimi',
|
|
||||||
default: 'Claude',
|
|
||||||
};
|
|
||||||
|
|
||||||
return displayNames[profile] || profile.toUpperCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Truncate string to max length
|
* Truncate string to max length
|
||||||
*/
|
*/
|
||||||
@@ -283,7 +270,7 @@ class ResultFormatter {
|
|||||||
static async formatMinimal(result: ExecutionResult): Promise<string> {
|
static async formatMinimal(result: ExecutionResult): Promise<string> {
|
||||||
await ui.init();
|
await ui.init();
|
||||||
const { profile, success, duration } = result;
|
const { profile, success, duration } = result;
|
||||||
const modelName = this.getModelDisplayName(profile);
|
const modelName = getModelDisplayName(profile);
|
||||||
const icon = success ? ui.ok('') : ui.fail('');
|
const icon = success ? ui.ok('') : ui.fail('');
|
||||||
const durationSec = (duration / 1000).toFixed(1);
|
const durationSec = (duration / 1000).toFixed(1);
|
||||||
|
|
||||||
@@ -326,7 +313,7 @@ class ResultFormatter {
|
|||||||
await ui.init();
|
await ui.init();
|
||||||
|
|
||||||
const { profile, duration, sessionId, totalCost, permissionDenials } = result;
|
const { profile, duration, sessionId, totalCost, permissionDenials } = result;
|
||||||
const modelName = this.getModelDisplayName(profile);
|
const modelName = getModelDisplayName(profile);
|
||||||
const timeoutMin = (duration / 60000).toFixed(1);
|
const timeoutMin = (duration / 60000).toFixed(1);
|
||||||
|
|
||||||
let output = '';
|
let output = '';
|
||||||
|
|||||||
@@ -246,3 +246,37 @@ export function getSettingsPath(profile: string): string {
|
|||||||
|
|
||||||
return expandedPath;
|
return expandedPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get display name for a profile by reading ANTHROPIC_MODEL from settings
|
||||||
|
* @param profile - Profile name (glm, glmt, kimi, custom, etc.)
|
||||||
|
* @returns Formatted display name (e.g., 'GLM-4.7', 'Kimi', 'Custom-Model')
|
||||||
|
*/
|
||||||
|
export function getModelDisplayName(profile: string): string {
|
||||||
|
if (!profile) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const settingsPath = path.join(getCcsDir(), `${profile}.settings.json`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (fs.existsSync(settingsPath)) {
|
||||||
|
const content = fs.readFileSync(settingsPath, 'utf8');
|
||||||
|
const settings = JSON.parse(content) as { env?: { ANTHROPIC_MODEL?: string } };
|
||||||
|
const model = settings.env?.ANTHROPIC_MODEL;
|
||||||
|
|
||||||
|
if (model) {
|
||||||
|
// Format: 'glm-4.7' -> 'GLM-4.7' (uppercase letters, preserve numbers)
|
||||||
|
return model
|
||||||
|
.split('-')
|
||||||
|
.map((part) => part.toUpperCase())
|
||||||
|
.join('-');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Fall through to default
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: profile name uppercase
|
||||||
|
return profile.toUpperCase();
|
||||||
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ describe('ResultFormatter', () => {
|
|||||||
|
|
||||||
const formatted = await ResultFormatter.format(result);
|
const formatted = await ResultFormatter.format(result);
|
||||||
|
|
||||||
assert.ok(formatted.includes('Delegated to GLM-4.6'));
|
assert.ok(formatted.toLowerCase().includes('delegated to glm'));
|
||||||
assert.ok(formatted.includes('ccs:glm'));
|
assert.ok(formatted.includes('ccs:glm'));
|
||||||
assert.ok(formatted.includes('/home/user/project'));
|
assert.ok(formatted.includes('/home/user/project'));
|
||||||
assert.ok(formatted.includes('2.3s'));
|
assert.ok(formatted.includes('2.3s'));
|
||||||
@@ -179,11 +179,13 @@ describe('ResultFormatter', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const glmFormatted = await ResultFormatter.format(glmResult);
|
const glmFormatted = await ResultFormatter.format(glmResult);
|
||||||
assert.ok(glmFormatted.includes('GLM-4.6'));
|
// Model display reads from settings or falls back to profile uppercase
|
||||||
|
// Use case-insensitive check since format may vary (GLM, Glm-4.7, etc.)
|
||||||
|
assert.ok(glmFormatted.toLowerCase().includes('glm'));
|
||||||
|
|
||||||
const kimiResult = { ...glmResult, profile: 'kimi' };
|
const kimiResult = { ...glmResult, profile: 'kimi' };
|
||||||
const kimiFormatted = await ResultFormatter.format(kimiResult);
|
const kimiFormatted = await ResultFormatter.format(kimiResult);
|
||||||
assert.ok(kimiFormatted.includes('Kimi'));
|
assert.ok(kimiFormatted.toLowerCase().includes('kimi'));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -220,7 +222,8 @@ describe('ResultFormatter', () => {
|
|||||||
const minimal = await ResultFormatter.formatMinimal(result);
|
const minimal = await ResultFormatter.formatMinimal(result);
|
||||||
|
|
||||||
assert.ok(minimal.includes('[OK]'));
|
assert.ok(minimal.includes('[OK]'));
|
||||||
assert.ok(minimal.includes('GLM-4.6'));
|
// Model display reads from settings or falls back to profile uppercase
|
||||||
|
assert.ok(minimal.toLowerCase().includes('glm'));
|
||||||
assert.ok(minimal.includes('1.5s'));
|
assert.ok(minimal.includes('1.5s'));
|
||||||
assert.ok(minimal.split('\n').length <= 3);
|
assert.ok(minimal.split('\n').length <= 3);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user