1414// specific language governing permissions and limitations
1515// under the License.
1616
17- import { Command , GenerateAgentCodeRequest , ProjectSource , AIChatMachineEventType } from "@wso2/ballerina-core" ;
17+ import { Command , GenerateAgentCodeRequest , ProjectSource , AIChatMachineEventType } from "@wso2/ballerina-core" ;
1818import { ModelMessage , stepCountIs , streamText } from "ai" ;
1919import { getAnthropicClient , getProviderCacheControl , ANTHROPIC_SONNET_4 } from "../connection" ;
2020import { getErrorMessage , populateHistoryForAgent } from "../utils" ;
@@ -38,6 +38,10 @@ import { LangfuseExporter } from 'langfuse-vercel';
3838import { NodeSDK } from '@opentelemetry/sdk-node' ;
3939import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node' ;
4040import { getProjectSource } from "../../utils/project-utils" ;
41+ import { sendTelemetryEvent , sendTelemetryException } from "../../../telemetry" ;
42+ import { TM_EVENT_BALLERINA_AI_GENERATION_SUBMITTED , TM_EVENT_BALLERINA_AI_GENERATION_COMPLETED , TM_EVENT_BALLERINA_AI_GENERATION_FAILED , TM_EVENT_BALLERINA_AI_GENERATION_ABORTED , TM_EVENT_BALLERINA_AI_GENERATION_DIAGNOSTICS , CMP_BALLERINA_AI_GENERATION } from "../../../telemetry" ;
43+ import { extension } from "../../../../BalExtensionContext" ;
44+
4145
4246const LANGFUSE_SECRET = process . env . LANGFUSE_SECRET ;
4347const LANGFUSE_PUBLIC = process . env . LANGFUSE_PUBLIC ;
@@ -74,6 +78,19 @@ export async function generateDesignCore(
7478
7579 const cacheOptions = await getProviderCacheControl ( ) ;
7680
81+ // Get state machine context for telemetry
82+ const stateContext = AIChatStateMachine . context ( ) ;
83+
84+ // Send telemetry when the user submits a query
85+ sendTelemetryEvent ( extension . ballerinaExtInstance , TM_EVENT_BALLERINA_AI_GENERATION_SUBMITTED , CMP_BALLERINA_AI_GENERATION , {
86+ projectId : stateContext . projectId || 'unknown' ,
87+ messageId : messageId ,
88+ command : Command . Design ,
89+ operationType : params . operationType ,
90+ isPlanMode : isPlanModeEnabled . toString ( ) ,
91+ approvalMode : stateContext . autoApproveEnabled ? 'auto' : 'manual' ,
92+ } ) ;
93+
7794 const modifiedFiles : string [ ] = [ ] ;
7895
7996 const userMessageContent = getUserPrompt ( params . usecase , tempProjectPath , projects , isPlanModeEnabled , params . codeContext ) ;
@@ -108,6 +125,9 @@ export async function generateDesignCore(
108125 [ DIAGNOSTICS_TOOL_NAME ] : createDiagnosticsTool ( tempProjectPath ) ,
109126 } ;
110127
128+ // Timing metrics for telemetry - capture at generation start
129+ const generationStartTime = Date . now ( ) ;
130+
111131 const { fullStream, response } = streamText ( {
112132 model : await getAnthropicClient ( ANTHROPIC_SONNET_4 ) ,
113133 maxOutputTokens : 8192 ,
@@ -140,6 +160,7 @@ export async function generateDesignCore(
140160 break ;
141161 }
142162 case "tool-call" : {
163+
143164 const toolName = part . toolName ;
144165 accumulateToolCall ( currentAssistantContent , part ) ;
145166
@@ -227,6 +248,22 @@ export async function generateDesignCore(
227248 if ( shouldCleanup ) {
228249 cleanupTempProject ( tempProjectPath ) ;
229250 }
251+
252+ // Send telemetry for generation error
253+ const errorObj = error instanceof Error ? error : new Error ( String ( error ) ) ;
254+ const errorTime = Date . now ( ) ;
255+ sendTelemetryException ( extension . ballerinaExtInstance , errorObj , CMP_BALLERINA_AI_GENERATION , {
256+ event : TM_EVENT_BALLERINA_AI_GENERATION_FAILED ,
257+ projectId : stateContext . projectId || 'unknown' ,
258+ messageId : messageId ,
259+ errorMessage : getErrorMessage ( error ) ,
260+ errorType : errorObj . name || 'Unknown' ,
261+ generationStartTime : generationStartTime . toString ( ) ,
262+ errorTime : errorTime . toString ( ) ,
263+ durationMs : ( errorTime - generationStartTime ) . toString ( ) ,
264+ } ) ;
265+
266+
230267 eventHandler ( { type : "error" , content : getErrorMessage ( error ) } ) ;
231268 return tempProjectPath ;
232269 }
@@ -236,6 +273,18 @@ export async function generateDesignCore(
236273 }
237274 case "abort" : {
238275 console . log ( "[Design] Aborted by user." ) ;
276+ const abortTime = Date . now ( ) ;
277+
278+ // Send telemetry for generation abort
279+ sendTelemetryEvent ( extension . ballerinaExtInstance , TM_EVENT_BALLERINA_AI_GENERATION_ABORTED , CMP_BALLERINA_AI_GENERATION , {
280+ projectId : stateContext . projectId || 'unknown' ,
281+ messageId : messageId ,
282+ generationStartTime : generationStartTime . toString ( ) ,
283+ abortTime : abortTime . toString ( ) ,
284+ durationMs : ( abortTime - generationStartTime ) . toString ( ) ,
285+ modifiedFilesCount : modifiedFiles . length . toString ( ) ,
286+ } ) ;
287+
239288 let messagesToSave : any [ ] = [ ] ;
240289 try {
241290 const partialResponse = await response ;
@@ -277,9 +326,19 @@ Generation stopped by user. The last in-progress task was not saved. Files have
277326 case "finish" : {
278327 const finalResponse = await response ;
279328 const assistantMessages = finalResponse . messages || [ ] ;
329+ const generationEndTime = Date . now ( ) ;
280330
281331 const finalDiagnostics = await checkCompilationErrors ( tempProjectPath ) ;
282332 if ( finalDiagnostics . diagnostics && finalDiagnostics . diagnostics . length > 0 ) {
333+ // Send telemetry for final diagnostics check
334+ sendTelemetryEvent ( extension . ballerinaExtInstance , TM_EVENT_BALLERINA_AI_GENERATION_DIAGNOSTICS , CMP_BALLERINA_AI_GENERATION , {
335+ projectId : stateContext . projectId || 'unknown' ,
336+ messageId : messageId ,
337+ hasErrors : 'true' ,
338+ errorCount : finalDiagnostics . diagnostics . length . toString ( ) ,
339+ stage : 'completion' ,
340+ } ) ;
341+
283342 eventHandler ( {
284343 type : "diagnostics" ,
285344 diagnostics : finalDiagnostics . diagnostics
@@ -297,14 +356,27 @@ Generation stopped by user. The last in-progress task was not saved. Files have
297356
298357 updateAndSaveChat ( messageId , userMessageContent , assistantMessages , eventHandler ) ;
299358 eventHandler ( { type : "stop" , command : Command . Design } ) ;
359+
360+ // Send telemetry for generation completion
361+ sendTelemetryEvent ( extension . ballerinaExtInstance , TM_EVENT_BALLERINA_AI_GENERATION_COMPLETED , CMP_BALLERINA_AI_GENERATION , {
362+ projectId : stateContext . projectId || 'unknown' ,
363+ messageId : messageId ,
364+ modifiedFilesCount : modifiedFiles . length . toString ( ) ,
365+ generationStartTime : generationStartTime . toString ( ) ,
366+ generationEndTime : generationEndTime . toString ( ) ,
367+ durationMs : ( generationEndTime - generationStartTime ) . toString ( ) ,
368+ isPlanMode : isPlanModeEnabled . toString ( ) ,
369+ approvalMode : stateContext . autoApproveEnabled ? 'auto' : 'manual' ,
370+ } ) ;
371+
300372 AIChatStateMachine . sendEvent ( {
301373 type : AIChatMachineEventType . FINISH_EXECUTION ,
302374 } ) ;
303375 await langfuseExporter . forceFlush ( ) ;
304376 return tempProjectPath ;
305377 }
306378 }
307- }
379+ }
308380
309381 return tempProjectPath ;
310382}
0 commit comments