test(hooks): add image analyzer regression coverage

This commit is contained in:
Tam Nhu Tran committed 2026-03-07 14:50:39 +07:00
1 parent 0060c77c25
commit 50973752ba
2 files changed
+250 -19

No files matched your search

+47 -19
View File
@@ -12,7 +12,7 @@
* CCS_CURRENT_PROVIDER - Current CLIProxy provider (e.g., agy, gemini, codex)
* CCS_IMAGE_ANALYSIS_TIMEOUT=60 - Timeout in seconds (default: 60)
* CCS_PROFILE_TYPE - Profile type (account/default skip)
* ANTHROPIC_MODEL - Fallback model if provider not in mapping
* ANTHROPIC_MODEL - Chat model env (not used for image analysis fallback)
* CCS_DEBUG=1 - Enable debug output
*
* Exit codes:
@@ -147,6 +147,46 @@ function parseProviderModels(envValue) {
return result;
}
/**
* Extract concatenated text content from CLIProxy response blocks.
* Skips thinking and other non-text blocks.
*/
function extractTextContent(response) {
if (!response || !Array.isArray(response.content)) {
return null;
}
const textBlocks = response.content
.filter((block) => block && block.type === 'text' && typeof block.text === 'string')
.map((block) => block.text)
.filter((text) => text.trim());
if (textBlocks.length === 0) {
return null;
}
return textBlocks.join('\n\n');
}
/**
* Parse raw CLIProxy response body and extract text content.
*/
function parseCliProxyResponse(data) {
let response;
try {
response = JSON.parse(data);
} catch (err) {
throw new Error(`Failed to parse response: ${err.message}`);
}
const text = extractTextContent(response);
if (!text) {
throw new Error('No text content in response');
}
return text;
}
/**
* Get model for current provider from provider_models mapping
* Returns primary model only (for display/logging)
@@ -160,13 +200,14 @@ function getModelForProvider() {
/**
* Get list of models to try in order:
* 1. provider_models[current_provider] (if exists)
* 2. DEFAULT_MODEL
* 3. ANTHROPIC_MODEL from profile (if different and exists)
* 2. DEFAULT_MODEL when no provider-specific vision model is configured
*
* ANTHROPIC_MODEL is intentionally ignored here because the chat model may
* not be vision-capable on the current provider route.
*/
function getModelsToTry() {
const currentProvider = process.env.CCS_CURRENT_PROVIDER || '';
const providerModels = parseProviderModels(process.env.CCS_IMAGE_ANALYSIS_PROVIDER_MODELS);
const anthropicModel = process.env.ANTHROPIC_MODEL;
const models = [];
const seen = new Set();
@@ -185,9 +226,6 @@ function getModelsToTry() {
seen.add(DEFAULT_MODEL);
}
// 3. ANTHROPIC_MODEL fallback — skip, the main chat model is not
// necessarily vision-capable and adds unnecessary retry latency
return models;
}
@@ -381,20 +419,10 @@ function analyzeViaCliProxy(base64Data, mediaType, model, timeoutMs) {
}
try {
const response = JSON.parse(data);
// Find the first text block — skip thinking blocks which use
// .thinking instead of .text (e.g. codex models with thinking enabled)
const textBlock = response.content?.find(b => b.type === 'text' && b.text);
const text = textBlock?.text;
if (!text) {
reject(new Error('No text content in response'));
return;
}
const text = parseCliProxyResponse(data);
resolve(text);
} catch (err) {
reject(new Error(`Failed to parse response: ${err.message}`));
reject(err);
}
});
}