feat(cliproxy): wire composite variant into main execution flow

- Detect composite variants in ProfileDetector with tier metadata
- Pass isComposite/compositeTiers/compositeDefaultTier through ccs.ts
- Handle composite in loadConfigSafe() legacy adapter
- Add validateCompositeVariants() for provider string validation
- Display 'composite' label in API profile listing

Refs #506
This commit is contained in:
Tam Nhu Tran
2026-02-12 04:09:19 +07:00
parent 4926c200f2
commit 07bef9f50e
5 changed files with 101 additions and 14 deletions
+42 -5
View File
@@ -13,7 +13,13 @@ import * as fs from 'fs';
import * as path from 'path';
import { findSimilarStrings, expandPath } from '../utils/helpers';
import { Config, Settings, ProfileMetadata } from '../types';
import { UnifiedConfig, CopilotConfig } from '../config/unified-config-types';
import {
UnifiedConfig,
CopilotConfig,
CLIProxyVariantConfig,
CompositeVariantConfig,
CompositeTierConfig,
} from '../config/unified-config-types';
import { loadUnifiedConfig, isUnifiedMode } from '../config/unified-config-loader';
import { getCcsDir } from '../utils/config-manager';
@@ -46,6 +52,16 @@ export interface ProfileDetectionResult {
env?: Record<string, string>;
/** For copilot profile: the copilot config */
copilotConfig?: CopilotConfig;
/** For composite variants: true when variant mixes providers per tier */
isComposite?: boolean;
/** For composite variants: which tier is the default */
compositeDefaultTier?: 'opus' | 'sonnet' | 'haiku';
/** For composite variants: per-tier provider+model mappings */
compositeTiers?: {
opus: CompositeTierConfig;
sonnet: CompositeTierConfig;
haiku: CompositeTierConfig;
};
}
export interface AllProfiles {
@@ -110,12 +126,29 @@ class ProfileDetector {
// Check CLIProxy variants first
if (config.cliproxy?.variants?.[profileName]) {
const variant = config.cliproxy.variants[profileName];
// Handle composite variants
if ('type' in variant && variant.type === 'composite') {
const composite = variant as CompositeVariantConfig;
return {
type: 'cliproxy',
name: profileName,
provider: composite.tiers[composite.default_tier].provider as CLIProxyProfileName,
settingsPath: composite.settings,
port: composite.port,
isComposite: true,
compositeDefaultTier: composite.default_tier,
compositeTiers: composite.tiers,
};
}
const singleVariant = variant as CLIProxyVariantConfig;
return {
type: 'cliproxy',
name: profileName,
provider: variant.provider as CLIProxyProfileName,
settingsPath: variant.settings,
port: variant.port,
provider: singleVariant.provider as CLIProxyProfileName,
settingsPath: singleVariant.settings,
port: singleVariant.port,
};
}
@@ -376,7 +409,11 @@ class ProfileDetector {
lines.push('CLIProxy variants (unified config):');
variants.forEach((name) => {
const variant = unifiedConfig.cliproxy?.variants[name];
lines.push(` - ${name} (${variant?.provider || 'unknown'})`);
const label =
variant && 'type' in variant && variant.type === 'composite'
? 'composite'
: (variant as { provider?: string })?.provider || 'unknown';
lines.push(` - ${name} (${label})`);
});
}