feat(telemetry): Implement AI usage telemetry pattern and apply to add-task
This commit introduces a standardized pattern for capturing and propagating AI usage telemetry (cost, tokens, model used) across the Task Master stack and applies it to the 'add-task' functionality. Key changes include: - **Telemetry Pattern Definition:** - Added defining the integration pattern for core logic, direct functions, MCP tools, and CLI commands. - Updated related rules (, , Usage: mcp [OPTIONS] COMMAND [ARGS]... MCP development tools ╭─ Options ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ --help Show this message and exit. │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─ Commands ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ version Show the MCP version. │ │ dev Run a MCP server with the MCP Inspector. │ │ run Run a MCP server. │ │ install Install a MCP server in the Claude desktop app. │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯, , ) to reference the new telemetry rule. - **Core Telemetry Implementation ():** - Refactored the unified AI service to generate and return a object alongside the main AI result. - Fixed an MCP server startup crash by removing redundant local loading of and instead using the imported from for cost calculations. - Added to the object. - ** Integration:** - Modified (core) to receive from the AI service, return it, and call the new UI display function for CLI output. - Updated to receive from the core function and include it in the payload of its response. - Ensured (MCP tool) correctly passes the through via . - Updated to correctly pass context (, ) to the core function and rely on it for CLI telemetry display. - **UI Enhancement:** - Added function to to show telemetry details in the CLI. - **Project Management:** - Added subtasks 77.6 through 77.12 to track the rollout of this telemetry pattern to other AI-powered commands (, , , , , , ). This establishes the foundation for tracking AI usage across the application.
This commit is contained in:
@@ -8,7 +8,8 @@ import {
|
||||
displayBanner,
|
||||
getStatusWithColor,
|
||||
startLoadingIndicator,
|
||||
stopLoadingIndicator
|
||||
stopLoadingIndicator,
|
||||
displayAiUsageSummary
|
||||
} from '../ui.js';
|
||||
import { readJSON, writeJSON, log as consoleLog, truncate } from '../utils.js';
|
||||
import { generateObjectService } from '../ai-services-unified.js';
|
||||
@@ -44,7 +45,9 @@ const AiTaskDataSchema = z.object({
|
||||
* @param {boolean} useResearch - Whether to use the research model (passed to unified service)
|
||||
* @param {Object} context - Context object containing session and potentially projectRoot
|
||||
* @param {string} [context.projectRoot] - Project root path (for MCP/env fallback)
|
||||
* @returns {number} The new task ID
|
||||
* @param {string} [context.commandName] - The name of the command being executed (for telemetry)
|
||||
* @param {string} [context.outputType] - The output type ('cli' or 'mcp', for telemetry)
|
||||
* @returns {Promise<object>} An object containing newTaskId and telemetryData
|
||||
*/
|
||||
async function addTask(
|
||||
tasksPath,
|
||||
@@ -56,7 +59,7 @@ async function addTask(
|
||||
manualTaskData = null,
|
||||
useResearch = false
|
||||
) {
|
||||
const { session, mcpLog, projectRoot } = context;
|
||||
const { session, mcpLog, projectRoot, commandName, outputType } = context;
|
||||
const isMCP = !!mcpLog;
|
||||
|
||||
// Create a consistent logFn object regardless of context
|
||||
@@ -78,6 +81,7 @@ async function addTask(
|
||||
);
|
||||
|
||||
let loadingIndicator = null;
|
||||
let aiServiceResponse = null; // To store the full response from AI service
|
||||
|
||||
// Create custom reporter that checks for MCP log
|
||||
const report = (message, level = 'info') => {
|
||||
@@ -229,29 +233,40 @@ async function addTask(
|
||||
// Start the loading indicator - only for text mode
|
||||
if (outputFormat === 'text') {
|
||||
loadingIndicator = startLoadingIndicator(
|
||||
`Generating new task with ${useResearch ? 'Research' : 'Main'} AI...`
|
||||
`Generating new task with ${useResearch ? 'Research' : 'Main'} AI..\n`
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
// Determine the service role based on the useResearch flag
|
||||
const serviceRole = useResearch ? 'research' : 'main';
|
||||
|
||||
report('DEBUG: Calling generateObjectService...', 'debug');
|
||||
// Call the unified AI service
|
||||
const aiGeneratedTaskData = await generateObjectService({
|
||||
role: serviceRole, // <-- Use the determined role
|
||||
session: session, // Pass session for API key resolution
|
||||
projectRoot: projectRoot, // <<< Pass projectRoot here
|
||||
schema: AiTaskDataSchema, // Pass the Zod schema
|
||||
objectName: 'newTaskData', // Name for the object
|
||||
|
||||
aiServiceResponse = await generateObjectService({
|
||||
// Capture the full response
|
||||
role: serviceRole,
|
||||
session: session,
|
||||
projectRoot: projectRoot,
|
||||
schema: AiTaskDataSchema,
|
||||
objectName: 'newTaskData',
|
||||
systemPrompt: systemPrompt,
|
||||
prompt: userPrompt
|
||||
prompt: userPrompt,
|
||||
commandName: commandName || 'add-task', // Use passed commandName or default
|
||||
outputType: outputType || (isMCP ? 'mcp' : 'cli') // Use passed outputType or derive
|
||||
});
|
||||
report('DEBUG: generateObjectService returned successfully.', 'debug');
|
||||
|
||||
if (
|
||||
!aiServiceResponse ||
|
||||
!aiServiceResponse.mainResult ||
|
||||
!aiServiceResponse.mainResult.object
|
||||
) {
|
||||
throw new Error(
|
||||
'AI service did not return the expected object structure.'
|
||||
);
|
||||
}
|
||||
taskData = aiServiceResponse.mainResult.object; // Extract the AI-generated task data
|
||||
|
||||
report('Successfully generated task data from AI.', 'success');
|
||||
taskData = aiGeneratedTaskData; // Assign the validated object
|
||||
} catch (error) {
|
||||
report(
|
||||
`DEBUG: generateObjectService caught error: ${error.message}`,
|
||||
@@ -362,11 +377,25 @@ async function addTask(
|
||||
{ padding: 1, borderColor: 'green', borderStyle: 'round' }
|
||||
)
|
||||
);
|
||||
|
||||
// Display AI Usage Summary if telemetryData is available
|
||||
if (
|
||||
aiServiceResponse &&
|
||||
aiServiceResponse.telemetryData &&
|
||||
(outputType === 'cli' || outputType === 'text')
|
||||
) {
|
||||
displayAiUsageSummary(aiServiceResponse.telemetryData, 'cli');
|
||||
}
|
||||
}
|
||||
|
||||
// Return the new task ID
|
||||
report(`DEBUG: Returning new task ID: ${newTaskId}`, 'debug');
|
||||
return newTaskId;
|
||||
report(
|
||||
`DEBUG: Returning new task ID: ${newTaskId} and telemetry.`,
|
||||
'debug'
|
||||
);
|
||||
return {
|
||||
newTaskId: newTaskId,
|
||||
telemetryData: aiServiceResponse ? aiServiceResponse.telemetryData : null
|
||||
};
|
||||
} catch (error) {
|
||||
// Stop any loading indicator on error
|
||||
if (loadingIndicator) {
|
||||
|
||||
Reference in New Issue
Block a user