fix(ai,tasks): Enhance AI provider robustness and task processing

This commit introduces several improvements to AI interactions and
task management functionalities:

- AI Provider Enhancements (for Telemetry & Robustness):
    - :
        - Added a check in  to ensure
          is a string, throwing an error if not. This prevents downstream
           errors (e.g., in ).
    - , , :
        - Standardized return structures for their respective
          and  functions to consistently include /
          and  fields. This aligns them with other providers (like
          Anthropic, Google, Perplexity) for consistent telemetry data
          collection, as part of implementing subtask 77.14 and similar work.

- Task Expansion ():
    - Updated  to be more explicit
      about using an empty array  for empty  to
      better guide AI output.
    - Implemented a pre-emptive cleanup step in
      to replace malformed  with
      before JSON parsing. This improves resilience to AI output quirks,
      particularly observed with Perplexity.

- Adjusts issue in commands.js where successfulRemovals would be undefined. It's properly invoked from the result variable now.

- Updates supported models for Gemini
These changes address issues observed during E2E tests, enhance the
reliability of AI-driven task analysis and expansion, and promote
consistent telemetry data across multiple AI providers.
This commit is contained in:
Eyal Toledano
2025-05-14 19:04:03 -04:00
parent 79a41543d5
commit ca5ec03cd8
10 changed files with 490 additions and 131 deletions

View File

@@ -31,20 +31,47 @@ async function generateOpenRouterText({
const openrouter = createOpenRouter({ apiKey });
const model = openrouter.chat(modelId); // Assuming chat model
const { text } = await generateText({
// Capture the full result from generateText
const result = await generateText({
model,
messages,
maxTokens,
temperature,
...rest // Pass any additional parameters
});
return text;
// Check if text and usage are present
if (!result || typeof result.text !== 'string') {
log(
'warn',
`OpenRouter generateText for model ${modelId} did not return expected text.`,
{ result }
);
throw new Error('Failed to extract text from OpenRouter response.');
}
if (!result.usage) {
log(
'warn',
`OpenRouter generateText for model ${modelId} did not return usage data.`,
{ result }
);
// Decide if this is critical. For now, let it pass but telemetry will be incomplete.
}
log('debug', `OpenRouter generateText completed for model ${modelId}`);
// Return text and usage
return {
text: result.text,
usage: {
inputTokens: result.usage.promptTokens,
outputTokens: result.usage.completionTokens
}
};
} catch (error) {
log(
'error',
`OpenRouter generateText failed for model ${modelId}: ${error.message}`
);
// Re-throw the error for the unified layer to handle retries/fallbacks
throw error;
}
}
@@ -132,12 +159,12 @@ async function generateOpenRouterObject({
const openrouter = createOpenRouter({ apiKey });
const model = openrouter.chat(modelId);
const { object } = await generateObject({
// Capture the full result from generateObject
const result = await generateObject({
model,
schema,
mode: 'tool', // Standard mode for most object generation
mode: 'tool',
tool: {
// Define the tool based on the schema
name: objectName,
description: `Generate an object conforming to the ${objectName} schema.`,
parameters: schema
@@ -145,10 +172,36 @@ async function generateOpenRouterObject({
messages,
maxTokens,
temperature,
maxRetries, // Pass maxRetries if supported by generateObject
maxRetries,
...rest
});
return object;
// Check if object and usage are present
if (!result || typeof result.object === 'undefined') {
log(
'warn',
`OpenRouter generateObject for model ${modelId} did not return expected object.`,
{ result }
);
throw new Error('Failed to extract object from OpenRouter response.');
}
if (!result.usage) {
log(
'warn',
`OpenRouter generateObject for model ${modelId} did not return usage data.`,
{ result }
);
}
log('debug', `OpenRouter generateObject completed for model ${modelId}`);
// Return object and usage
return {
object: result.object,
usage: {
inputTokens: result.usage.promptTokens,
outputTokens: result.usage.completionTokens
}
};
} catch (error) {
log(
'error',