mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-14 12:16:38 +00:00
Merge pull request #680 from 0xble/fix/image-analyzer-thinking-blocks
fix(hooks): handle thinking blocks in image analyzer response parsing
This commit is contained in:
2 files changed
+254
-20
No files matched your search
@@ -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();
|
||||
@@ -177,17 +218,14 @@ function getModelsToTry() {
|
||||
seen.add(providerModels[currentProvider]);
|
||||
}
|
||||
|
||||
// 2. Default model
|
||||
if (!seen.has(DEFAULT_MODEL)) {
|
||||
// 2. Default model — only when no provider-specific model is configured,
|
||||
// since DEFAULT_MODEL (gemini-2.5-flash) may not be routable on the
|
||||
// current provider's CLIProxy endpoint (e.g. codex, claude)
|
||||
if (models.length === 0 && !seen.has(DEFAULT_MODEL)) {
|
||||
models.push(DEFAULT_MODEL);
|
||||
seen.add(DEFAULT_MODEL);
|
||||
}
|
||||
|
||||
// 3. ANTHROPIC_MODEL fallback
|
||||
if (anthropicModel && !seen.has(anthropicModel)) {
|
||||
models.push(anthropicModel);
|
||||
}
|
||||
|
||||
return models;
|
||||
}
|
||||
|
||||
@@ -381,17 +419,10 @@ function analyzeViaCliProxy(base64Data, mediaType, model, timeoutMs) {
|
||||
}
|
||||
|
||||
try {
|
||||
const response = JSON.parse(data);
|
||||
const text = response.content?.[0]?.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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user