mirror of
https://github.com/tiennm99/ccs.git
synced 2026-09-20 05:11:48 +00:00
fix: normalize stale copilot raptor-mini model
This commit is contained in:
1 parent
447033cb03
commit
c409a4522c
13 files changed
+633
-72
No files matched your search
@@ -11,6 +11,7 @@ import { getGlobalEnvConfig } from '../config/unified-config-loader';
|
||||
import { checkAuthStatus, isCopilotApiInstalled } from './copilot-auth';
|
||||
import { isDaemonRunning, startDaemon } from './copilot-daemon';
|
||||
import { ensureCopilotApi } from './copilot-package-manager';
|
||||
import { normalizeCopilotConfig } from './copilot-model-normalizer';
|
||||
import { CopilotStatus } from './types';
|
||||
import { fail, info, ok } from '../utils/ui';
|
||||
import { getWebSearchHookEnv } from '../utils/websearch-manager';
|
||||
@@ -21,16 +22,17 @@ import { stripClaudeCodeEnv } from '../utils/shell-executor';
|
||||
* Get full copilot status (auth + daemon).
|
||||
*/
|
||||
export async function getCopilotStatus(config: CopilotConfig): Promise<CopilotStatus> {
|
||||
const normalizedConfig = normalizeCopilotConfig(config);
|
||||
const [auth, daemonRunning] = await Promise.all([
|
||||
checkAuthStatus(),
|
||||
isDaemonRunning(config.port),
|
||||
isDaemonRunning(normalizedConfig.port),
|
||||
]);
|
||||
|
||||
return {
|
||||
auth,
|
||||
daemon: {
|
||||
running: daemonRunning,
|
||||
port: config.port,
|
||||
port: normalizedConfig.port,
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -43,17 +45,19 @@ export function generateCopilotEnv(
|
||||
config: CopilotConfig,
|
||||
claudeConfigDir?: string
|
||||
): Record<string, string> {
|
||||
const normalizedConfig = normalizeCopilotConfig(config);
|
||||
|
||||
// Use mapped models if configured, otherwise fall back to default model
|
||||
const opusModel = config.opus_model || config.model;
|
||||
const sonnetModel = config.sonnet_model || config.model;
|
||||
const haikuModel = config.haiku_model || config.model;
|
||||
const opusModel = normalizedConfig.opus_model || normalizedConfig.model;
|
||||
const sonnetModel = normalizedConfig.sonnet_model || normalizedConfig.model;
|
||||
const haikuModel = normalizedConfig.haiku_model || normalizedConfig.model;
|
||||
|
||||
// Use 127.0.0.1 instead of localhost for more reliable local connections
|
||||
// (bypasses DNS resolution and potential IPv6 issues)
|
||||
return {
|
||||
ANTHROPIC_BASE_URL: `http://127.0.0.1:${config.port}`,
|
||||
ANTHROPIC_BASE_URL: `http://127.0.0.1:${normalizedConfig.port}`,
|
||||
ANTHROPIC_AUTH_TOKEN: 'dummy', // copilot-api handles auth internally
|
||||
ANTHROPIC_MODEL: config.model,
|
||||
ANTHROPIC_MODEL: normalizedConfig.model,
|
||||
// Model tier mapping - allows different models for opus/sonnet/haiku
|
||||
ANTHROPIC_DEFAULT_OPUS_MODEL: opusModel,
|
||||
ANTHROPIC_DEFAULT_SONNET_MODEL: sonnetModel,
|
||||
@@ -79,6 +83,8 @@ export async function executeCopilotProfile(
|
||||
claudeConfigDir?: string,
|
||||
claudeCliPath: string = 'claude'
|
||||
): Promise<number> {
|
||||
const normalizedConfig = normalizeCopilotConfig(config);
|
||||
|
||||
// Ensure copilot-api is installed (auto-install if missing, auto-update if outdated)
|
||||
try {
|
||||
await ensureCopilotApi();
|
||||
@@ -111,17 +117,17 @@ export async function executeCopilotProfile(
|
||||
}
|
||||
|
||||
// Check if daemon is running or needs to be started
|
||||
let daemonRunning = await isDaemonRunning(config.port);
|
||||
let daemonRunning = await isDaemonRunning(normalizedConfig.port);
|
||||
|
||||
if (!daemonRunning) {
|
||||
if (config.auto_start) {
|
||||
if (normalizedConfig.auto_start) {
|
||||
console.log(info('Starting copilot-api daemon...'));
|
||||
const result = await startDaemon(config);
|
||||
const result = await startDaemon(normalizedConfig);
|
||||
if (!result.success) {
|
||||
console.error(fail(`Failed to start daemon: ${result.error}`));
|
||||
return 1;
|
||||
}
|
||||
console.log(ok(`Daemon started on port ${config.port}`));
|
||||
console.log(ok(`Daemon started on port ${normalizedConfig.port}`));
|
||||
daemonRunning = true;
|
||||
} else {
|
||||
console.error(fail('copilot-api daemon is not running.'));
|
||||
@@ -129,7 +135,7 @@ export async function executeCopilotProfile(
|
||||
console.error('Start the daemon:');
|
||||
console.error(' ccs copilot start');
|
||||
console.error('Fallback manual command:');
|
||||
console.error(` npx copilot-api start --port ${config.port}`);
|
||||
console.error(` npx copilot-api start --port ${normalizedConfig.port}`);
|
||||
console.error('');
|
||||
console.error('Or enable auto_start in config:');
|
||||
console.error(' ccs config (then enable auto_start in Copilot section)');
|
||||
@@ -138,7 +144,7 @@ export async function executeCopilotProfile(
|
||||
}
|
||||
|
||||
// Generate environment for Claude
|
||||
const copilotEnv = generateCopilotEnv(config, claudeConfigDir);
|
||||
const copilotEnv = generateCopilotEnv(normalizedConfig, claudeConfigDir);
|
||||
|
||||
// Get global env vars (DISABLE_TELEMETRY, etc.) for third-party profiles
|
||||
const globalEnvConfig = getGlobalEnvConfig();
|
||||
@@ -156,7 +162,7 @@ export async function executeCopilotProfile(
|
||||
CCS_PROFILE_TYPE: 'copilot',
|
||||
});
|
||||
|
||||
console.log(info(`Using GitHub Copilot proxy (model: ${config.model})`));
|
||||
console.log(info(`Using GitHub Copilot proxy (model: ${normalizedConfig.model})`));
|
||||
console.log('');
|
||||
|
||||
// Spawn Claude CLI
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
import { DEFAULT_COPILOT_CONFIG, type CopilotConfig } from '../config/unified-config-types';
|
||||
|
||||
const LEGACY_COPILOT_MODEL_FALLBACKS: Readonly<Record<string, string>> = Object.freeze({
|
||||
// copilot-api v0.7.0 no longer advertises this ID in /v1/models and rejects it at runtime.
|
||||
'raptor-mini': DEFAULT_COPILOT_CONFIG.model,
|
||||
});
|
||||
|
||||
export const DEPRECATED_COPILOT_MODEL_IDS = Object.freeze(
|
||||
Object.keys(LEGACY_COPILOT_MODEL_FALLBACKS)
|
||||
);
|
||||
|
||||
function trimModelId(value: string | null | undefined): string | undefined {
|
||||
if (typeof value !== 'string') return undefined;
|
||||
const trimmed = value.trim();
|
||||
return trimmed.length > 0 ? trimmed : undefined;
|
||||
}
|
||||
|
||||
export function normalizeCopilotModelId(
|
||||
model: string | null | undefined,
|
||||
fallbackModel: string = DEFAULT_COPILOT_CONFIG.model
|
||||
): string {
|
||||
const normalizedFallback = trimModelId(fallbackModel) ?? DEFAULT_COPILOT_CONFIG.model;
|
||||
const trimmedModel = trimModelId(model) ?? normalizedFallback;
|
||||
return LEGACY_COPILOT_MODEL_FALLBACKS[trimmedModel.toLowerCase()] ?? trimmedModel;
|
||||
}
|
||||
|
||||
export function normalizeOptionalCopilotModelId(
|
||||
model: string | null | undefined,
|
||||
fallbackModel: string
|
||||
): string | undefined {
|
||||
const trimmedModel = trimModelId(model);
|
||||
return trimmedModel ? normalizeCopilotModelId(trimmedModel, fallbackModel) : undefined;
|
||||
}
|
||||
|
||||
export function normalizeCopilotConfig(config: CopilotConfig): CopilotConfig {
|
||||
const model = normalizeCopilotModelId(config.model);
|
||||
return {
|
||||
...config,
|
||||
model,
|
||||
opus_model: normalizeOptionalCopilotModelId(config.opus_model, model),
|
||||
sonnet_model: normalizeOptionalCopilotModelId(config.sonnet_model, model),
|
||||
haiku_model: normalizeOptionalCopilotModelId(config.haiku_model, model),
|
||||
};
|
||||
}
|
||||
|
||||
export interface CopilotSettingsPayload {
|
||||
env?: Record<string, unknown>;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export function normalizeCopilotSettings(
|
||||
settings: CopilotSettingsPayload,
|
||||
fallbackConfig: CopilotConfig = DEFAULT_COPILOT_CONFIG
|
||||
): CopilotSettingsPayload {
|
||||
const normalizedConfig = normalizeCopilotConfig(fallbackConfig);
|
||||
const rawEnv =
|
||||
settings.env && typeof settings.env === 'object' && !Array.isArray(settings.env)
|
||||
? settings.env
|
||||
: {};
|
||||
|
||||
const model = normalizeCopilotModelId(
|
||||
typeof rawEnv.ANTHROPIC_MODEL === 'string' ? rawEnv.ANTHROPIC_MODEL : normalizedConfig.model,
|
||||
normalizedConfig.model
|
||||
);
|
||||
const opusModel =
|
||||
normalizeOptionalCopilotModelId(
|
||||
typeof rawEnv.ANTHROPIC_DEFAULT_OPUS_MODEL === 'string'
|
||||
? rawEnv.ANTHROPIC_DEFAULT_OPUS_MODEL
|
||||
: normalizedConfig.opus_model,
|
||||
model
|
||||
) ?? model;
|
||||
const sonnetModel =
|
||||
normalizeOptionalCopilotModelId(
|
||||
typeof rawEnv.ANTHROPIC_DEFAULT_SONNET_MODEL === 'string'
|
||||
? rawEnv.ANTHROPIC_DEFAULT_SONNET_MODEL
|
||||
: normalizedConfig.sonnet_model,
|
||||
model
|
||||
) ?? model;
|
||||
const haikuModel =
|
||||
normalizeOptionalCopilotModelId(
|
||||
typeof rawEnv.ANTHROPIC_DEFAULT_HAIKU_MODEL === 'string'
|
||||
? rawEnv.ANTHROPIC_DEFAULT_HAIKU_MODEL
|
||||
: normalizedConfig.haiku_model,
|
||||
model
|
||||
) ?? model;
|
||||
|
||||
return {
|
||||
...settings,
|
||||
env: {
|
||||
...rawEnv,
|
||||
ANTHROPIC_MODEL: model,
|
||||
ANTHROPIC_DEFAULT_OPUS_MODEL: opusModel,
|
||||
ANTHROPIC_DEFAULT_SONNET_MODEL: sonnetModel,
|
||||
ANTHROPIC_DEFAULT_HAIKU_MODEL: haikuModel,
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -155,16 +155,6 @@ export const DEFAULT_COPILOT_MODELS: CopilotModel[] = [
|
||||
minPlan: 'pro',
|
||||
multiplier: 0.25,
|
||||
},
|
||||
|
||||
// Fine-tuned Models
|
||||
{
|
||||
id: 'raptor-mini',
|
||||
name: 'Raptor Mini',
|
||||
provider: 'openai',
|
||||
minPlan: 'free',
|
||||
multiplier: 0,
|
||||
preview: true,
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -44,6 +44,14 @@ export {
|
||||
getDefaultModel,
|
||||
} from './copilot-models';
|
||||
|
||||
// Normalization
|
||||
export {
|
||||
DEPRECATED_COPILOT_MODEL_IDS,
|
||||
normalizeCopilotConfig,
|
||||
normalizeCopilotModelId,
|
||||
normalizeCopilotSettings,
|
||||
} from './copilot-model-normalizer';
|
||||
|
||||
// Usage
|
||||
export {
|
||||
normalizeCopilotUsage,
|
||||
|
||||
Reference in new issue
Block a user