@@ -17,14 +17,12 @@ import { autorun, derived, IObservable, ObservableMap } from '../../../../base/c
1717import { StopWatch } from '../../../../base/common/stopwatch.js' ;
1818import { URI } from '../../../../base/common/uri.js' ;
1919import { OffsetRange } from '../../../../editor/common/core/ranges/offsetRange.js' ;
20- import { isLocation } from '../../../../editor/common/languages.js' ;
2120import { localize } from '../../../../nls.js' ;
2221import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js' ;
2322import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js' ;
2423import { ILogService } from '../../../../platform/log/common/log.js' ;
2524import { Progress } from '../../../../platform/progress/common/progress.js' ;
2625import { IStorageService , StorageScope , StorageTarget } from '../../../../platform/storage/common/storage.js' ;
27- import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js' ;
2826import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js' ;
2927import { IExtensionService } from '../../../services/extensions/common/extensions.js' ;
3028import { IMcpService } from '../../mcp/common/mcpTypes.js' ;
@@ -33,15 +31,15 @@ import { ChatModel, ChatRequestModel, ChatRequestRemovalReason, IChatModel, ICha
3331import { chatAgentLeader , ChatRequestAgentPart , ChatRequestAgentSubcommandPart , ChatRequestSlashCommandPart , ChatRequestTextPart , chatSubcommandLeader , getPromptText , IParsedChatRequest } from './chatParserTypes.js' ;
3432import { ChatRequestParser } from './chatRequestParser.js' ;
3533import { IChatCompleteResponse , IChatDetail , IChatFollowup , IChatProgress , IChatSendRequestData , IChatSendRequestOptions , IChatSendRequestResponseState , IChatService , IChatTransferredSessionData , IChatUserActionEvent } from './chatService.js' ;
36- import { ChatServiceTelemetry } from './chatServiceTelemetry.js' ;
34+ import { ChatRequestTelemetry , ChatServiceTelemetry } from './chatServiceTelemetry.js' ;
3735import { IChatSessionsService } from './chatSessionsService.js' ;
3836import { ChatSessionStore , IChatTransfer2 } from './chatSessionStore.js' ;
3937import { IChatSlashCommandService } from './chatSlashCommands.js' ;
4038import { IChatTransferService } from './chatTransferService.js' ;
4139import { ChatSessionUri } from './chatUri.js' ;
42- import { IChatRequestVariableEntry , isImageVariableEntry } from './chatVariableEntries.js' ;
40+ import { IChatRequestVariableEntry } from './chatVariableEntries.js' ;
4341import { ChatAgentLocation , ChatConfiguration , ChatModeKind } from './constants.js' ;
44- import { ChatMessageRole , IChatMessage , ILanguageModelsService } from './languageModels.js' ;
42+ import { ChatMessageRole , IChatMessage } from './languageModels.js' ;
4543import { ILanguageModelToolsService } from './languageModelToolsService.js' ;
4644
4745const serializedChatKey = 'interactive.sessions' ;
@@ -50,44 +48,6 @@ const globalChatKey = 'chat.workspaceTransfer';
5048
5149const SESSION_TRANSFER_EXPIRATION_IN_MILLISECONDS = 1000 * 60 ;
5250
53- type ChatProviderInvokedEvent = {
54- timeToFirstProgress : number | undefined ;
55- totalTime : number | undefined ;
56- result : 'success' | 'error' | 'errorWithOutput' | 'cancelled' | 'filtered' ;
57- requestType : 'string' | 'followup' | 'slashCommand' ;
58- chatSessionId : string ;
59- agent : string ;
60- agentExtensionId : string | undefined ;
61- slashCommand : string | undefined ;
62- location : ChatAgentLocation ;
63- citations : number ;
64- numCodeBlocks : number ;
65- isParticipantDetected : boolean ;
66- enableCommandDetection : boolean ;
67- attachmentKinds : string [ ] ;
68- model : string | undefined ;
69- } ;
70-
71- type ChatProviderInvokedClassification = {
72- timeToFirstProgress : { classification : 'SystemMetaData' ; purpose : 'PerformanceAndHealth' ; comment : 'The time in milliseconds from invoking the provider to getting the first data.' } ;
73- totalTime : { classification : 'SystemMetaData' ; purpose : 'PerformanceAndHealth' ; comment : 'The total time it took to run the provider\'s `provideResponseWithProgress`.' } ;
74- result : { classification : 'SystemMetaData' ; purpose : 'FeatureInsight' ; comment : 'Whether invoking the ChatProvider resulted in an error.' } ;
75- requestType : { classification : 'SystemMetaData' ; purpose : 'FeatureInsight' ; comment : 'The type of request that the user made.' } ;
76- chatSessionId : { classification : 'SystemMetaData' ; purpose : 'FeatureInsight' ; comment : 'A random ID for the session.' } ;
77- agent : { classification : 'SystemMetaData' ; purpose : 'FeatureInsight' ; comment : 'The type of agent used.' } ;
78- agentExtensionId : { classification : 'SystemMetaData' ; purpose : 'FeatureInsight' ; comment : 'The extension that contributed the agent.' } ;
79- slashCommand ?: { classification : 'SystemMetaData' ; purpose : 'FeatureInsight' ; comment : 'The type of slashCommand used.' } ;
80- location : { classification : 'SystemMetaData' ; purpose : 'FeatureInsight' ; comment : 'The location at which chat request was made.' } ;
81- citations : { classification : 'SystemMetaData' ; purpose : 'FeatureInsight' ; comment : 'The number of public code citations that were returned with the response.' } ;
82- numCodeBlocks : { classification : 'SystemMetaData' ; purpose : 'FeatureInsight' ; comment : 'The number of code blocks in the response.' } ;
83- isParticipantDetected : { classification : 'SystemMetaData' ; purpose : 'FeatureInsight' ; comment : 'Whether the participant was automatically detected.' } ;
84- enableCommandDetection : { classification : 'SystemMetaData' ; purpose : 'FeatureInsight' ; comment : 'Whether participation detection was disabled for this invocation.' } ;
85- attachmentKinds : { classification : 'SystemMetaData' ; purpose : 'FeatureInsight' ; comment : 'The types of variables/attachments that the user included with their query.' } ;
86- model : { classification : 'SystemMetaData' ; purpose : 'FeatureInsight' ; comment : 'The model used to generate the response.' } ;
87- owner : 'roblourens' ;
88- comment : 'Provides insight into the performance of Chat agents.' ;
89- } ;
90-
9151const maxPersistedSessions = 25 ;
9252
9353class CancellableRequest implements IDisposable {
@@ -160,13 +120,11 @@ export class ChatService extends Disposable implements IChatService {
160120 @ILogService private readonly logService : ILogService ,
161121 @IExtensionService private readonly extensionService : IExtensionService ,
162122 @IInstantiationService private readonly instantiationService : IInstantiationService ,
163- @ITelemetryService private readonly telemetryService : ITelemetryService ,
164123 @IWorkspaceContextService private readonly workspaceContextService : IWorkspaceContextService ,
165124 @IChatSlashCommandService private readonly chatSlashCommandService : IChatSlashCommandService ,
166125 @IChatAgentService private readonly chatAgentService : IChatAgentService ,
167126 @IConfigurationService private readonly configurationService : IConfigurationService ,
168127 @IChatTransferService private readonly chatTransferService : IChatTransferService ,
169- @ILanguageModelsService private readonly languageModelsService : ILanguageModelsService ,
170128 @IChatSessionsService private readonly chatSessionService : IChatSessionsService ,
171129 @IMcpService private readonly mcpService : IMcpService ,
172130 ) {
@@ -550,8 +508,6 @@ export class ChatService extends Disposable implements IChatService {
550508
551509 const isTransferred = this . transferredSessionData ?. sessionId === sessionId ;
552510 if ( isTransferred ) {
553- // TODO
554- // this.chatAgentService.toggleToolsAgentMode(this.transferredSessionData.toolsAgentModeEnabled);
555511 this . _transferredSessionData = undefined ;
556512 }
557513
@@ -779,6 +735,15 @@ export class ChatService extends Disposable implements IChatService {
779735 const agentSlashCommandPart = 'kind' in parsedRequest ? undefined : parsedRequest . parts . find ( ( r ) : r is ChatRequestAgentSubcommandPart => r instanceof ChatRequestAgentSubcommandPart ) ;
780736 const commandPart = 'kind' in parsedRequest ? undefined : parsedRequest . parts . find ( ( r ) : r is ChatRequestSlashCommandPart => r instanceof ChatRequestSlashCommandPart ) ;
781737 const requests = [ ...model . getRequests ( ) ] ;
738+ const requestTelemetry = this . instantiationService . createInstance ( ChatRequestTelemetry , {
739+ agentPart,
740+ agentSlashCommandPart,
741+ commandPart,
742+ sessionId : model . sessionId ,
743+ location : model . initialLocation ,
744+ options,
745+ enableCommandDetection
746+ } ) ;
782747
783748 let gotProgress = false ;
784749 const requestType = commandPart ? 'slashCommand' : 'string' ;
@@ -828,23 +793,14 @@ export class ChatService extends Disposable implements IChatService {
828793 return ;
829794 }
830795
831- this . telemetryService . publicLog2 < ChatProviderInvokedEvent , ChatProviderInvokedClassification > ( 'interactiveSessionProviderInvoked' , {
796+ requestTelemetry . complete ( {
832797 timeToFirstProgress : undefined ,
798+ result : 'cancelled' ,
833799 // Normally timings happen inside the EH around the actual provider. For cancellation we can measure how long the user waited before cancelling
834800 totalTime : stopWatch . elapsed ( ) ,
835- result : 'cancelled' ,
836801 requestType,
837- agent : detectedAgent ?. id ?? agentPart ?. agent . id ?? '' ,
838- agentExtensionId : detectedAgent ?. extensionId . value ?? agentPart ?. agent . extensionId . value ?? '' ,
839- slashCommand : agentSlashCommandPart ? agentSlashCommandPart . command . name : commandPart ?. slashCommand . command ,
840- chatSessionId : model . sessionId ,
841- location,
842- citations : request ?. response ?. codeCitations . length ?? 0 ,
843- numCodeBlocks : getCodeBlocks ( request . response ?. response . toString ( ) ?? '' ) . length ,
844- isParticipantDetected : ! ! detectedAgent ,
845- enableCommandDetection,
846- attachmentKinds : this . attachmentKindsForTelemetry ( request . variableData ) ,
847- model : this . resolveModelId ( options ?. userSelectedModelId ) ,
802+ detectedAgent,
803+ request,
848804 } ) ;
849805
850806 model . cancelRequest ( request ) ;
@@ -982,24 +938,16 @@ export class ChatService extends Disposable implements IChatService {
982938 rawResult . errorDetails && gotProgress ? 'errorWithOutput' :
983939 rawResult . errorDetails ? 'error' :
984940 'success' ;
985- const commandForTelemetry = agentSlashCommandPart ? agentSlashCommandPart . command . name : commandPart ?. slashCommand . command ;
986- this . telemetryService . publicLog2 < ChatProviderInvokedEvent , ChatProviderInvokedClassification > ( 'interactiveSessionProviderInvoked' , {
941+
942+ requestTelemetry . complete ( {
987943 timeToFirstProgress : rawResult . timings ?. firstProgress ,
988944 totalTime : rawResult . timings ?. totalElapsed ,
989945 result,
990946 requestType,
991- agent : detectedAgent ?. id ?? agentPart ?. agent . id ?? '' ,
992- agentExtensionId : detectedAgent ?. extensionId . value ?? agentPart ?. agent . extensionId . value ?? '' ,
993- slashCommand : commandForTelemetry ,
994- chatSessionId : model . sessionId ,
995- enableCommandDetection,
996- isParticipantDetected : ! ! detectedAgent ,
997- location,
998- citations : request . response ?. codeCitations . length ?? 0 ,
999- numCodeBlocks : getCodeBlocks ( request . response ?. response . toString ( ) ?? '' ) . length ,
1000- attachmentKinds : this . attachmentKindsForTelemetry ( request . variableData ) ,
1001- model : this . resolveModelId ( options ?. userSelectedModelId ) ,
947+ detectedAgent,
948+ request,
1002949 } ) ;
950+
1003951 model . setResponse ( request , rawResult ) ;
1004952 completeResponseCreated ( ) ;
1005953 this . trace ( 'sendRequest' , `Provider returned response for session ${ model . sessionId } ` ) ;
@@ -1008,6 +956,7 @@ export class ChatService extends Disposable implements IChatService {
1008956 if ( agentOrCommandFollowups ) {
1009957 agentOrCommandFollowups . then ( followups => {
1010958 model . setFollowups ( request , followups ) ;
959+ const commandForTelemetry = agentSlashCommandPart ? agentSlashCommandPart . command . name : commandPart ?. slashCommand . command ;
1011960 this . _chatServiceTelemetry . retrievedFollowups ( agentPart ?. agent . id ?? '' , commandForTelemetry , followups ?. length ?? 0 ) ;
1012961 } ) ;
1013962 }
@@ -1019,23 +968,13 @@ export class ChatService extends Disposable implements IChatService {
1019968 }
1020969 } catch ( err ) {
1021970 this . logService . error ( `Error while handling chat request: ${ toErrorMessage ( err , true ) } ` ) ;
1022- const result = 'error' ;
1023- this . telemetryService . publicLog2 < ChatProviderInvokedEvent , ChatProviderInvokedClassification > ( 'interactiveSessionProviderInvoked' , {
971+ requestTelemetry . complete ( {
1024972 timeToFirstProgress : undefined ,
1025973 totalTime : undefined ,
1026- result,
974+ result : 'error' ,
1027975 requestType,
1028- agent : detectedAgent ?. id ?? agentPart ?. agent . id ?? '' ,
1029- agentExtensionId : detectedAgent ?. extensionId . value ?? agentPart ?. agent . extensionId . value ?? '' ,
1030- slashCommand : agentSlashCommandPart ? agentSlashCommandPart . command . name : commandPart ?. slashCommand . command ,
1031- chatSessionId : model . sessionId ,
1032- location,
1033- citations : 0 ,
1034- numCodeBlocks : 0 ,
1035- enableCommandDetection,
1036- isParticipantDetected : ! ! detectedAgent ,
1037- attachmentKinds : request ? this . attachmentKindsForTelemetry ( request . variableData ) : [ ] ,
1038- model : this . resolveModelId ( options ?. userSelectedModelId )
976+ detectedAgent,
977+ request,
1039978 } ) ;
1040979 if ( request ) {
1041980 const rawResult : IChatAgentResult = { errorDetails : { message : err . message } } ;
@@ -1060,10 +999,6 @@ export class ChatService extends Disposable implements IChatService {
1060999 } ;
10611000 }
10621001
1063- private resolveModelId ( userSelectedModelId : string | undefined ) : string | undefined {
1064- return userSelectedModelId && this . languageModelsService . lookupLanguageModel ( userSelectedModelId ) ?. id ;
1065- }
1066-
10671002 private prepareContext ( attachedContextVariables : IChatRequestVariableEntry [ ] | undefined ) : IChatRequestVariableEntry [ ] {
10681003 attachedContextVariables ??= [ ] ;
10691004
@@ -1085,44 +1020,6 @@ export class ChatService extends Disposable implements IChatService {
10851020 return attachedContextVariables ;
10861021 }
10871022
1088- private attachmentKindsForTelemetry ( variableData : IChatRequestVariableData ) : string [ ] {
1089- // TODO this shows why attachments still have to be cleaned up somewhat
1090- return variableData . variables . map ( v => {
1091- if ( v . kind === 'implicit' ) {
1092- return 'implicit' ;
1093- } else if ( v . range ) {
1094- // 'range' is range within the prompt text
1095- if ( v . kind === 'tool' ) {
1096- return 'toolInPrompt' ;
1097- } else if ( v . kind === 'toolset' ) {
1098- return 'toolsetInPrompt' ;
1099- } else {
1100- return 'fileInPrompt' ;
1101- }
1102- } else if ( v . kind === 'command' ) {
1103- return 'command' ;
1104- } else if ( v . kind === 'symbol' ) {
1105- return 'symbol' ;
1106- } else if ( isImageVariableEntry ( v ) ) {
1107- return 'image' ;
1108- } else if ( v . kind === 'directory' ) {
1109- return 'directory' ;
1110- } else if ( v . kind === 'tool' ) {
1111- return 'tool' ;
1112- } else if ( v . kind === 'toolset' ) {
1113- return 'toolset' ;
1114- } else {
1115- if ( URI . isUri ( v . value ) ) {
1116- return 'file' ;
1117- } else if ( isLocation ( v . value ) ) {
1118- return 'location' ;
1119- } else {
1120- return 'otherAttachment' ;
1121- }
1122- }
1123- } ) ;
1124- }
1125-
11261023 private getHistoryEntriesFromModel ( requests : IChatRequestModel [ ] , sessionId : string , location : ChatAgentLocation , forAgentId : string ) : IChatAgentHistoryEntry [ ] {
11271024 const history : IChatAgentHistoryEntry [ ] = [ ] ;
11281025 const agent = this . chatAgentService . getAgent ( forAgentId ) ;
@@ -1294,26 +1191,3 @@ export class ChatService extends Disposable implements IChatService {
12941191 this . _chatSessionStore . logIndex ( ) ;
12951192 }
12961193}
1297-
1298- function getCodeBlocks ( text : string ) : string [ ] {
1299- const lines = text . split ( '\n' ) ;
1300- const codeBlockLanguages : string [ ] = [ ] ;
1301-
1302- let codeBlockState : undefined | { readonly delimiter : string ; readonly languageId : string } ;
1303- for ( let i = 0 ; i < lines . length ; i ++ ) {
1304- const line = lines [ i ] ;
1305-
1306- if ( codeBlockState ) {
1307- if ( new RegExp ( `^\\s*${ codeBlockState . delimiter } \\s*$` ) . test ( line ) ) {
1308- codeBlockLanguages . push ( codeBlockState . languageId ) ;
1309- codeBlockState = undefined ;
1310- }
1311- } else {
1312- const match = line . match ( / ^ ( \s * ) ( ` { 3 , } | ~ { 3 , } ) ( \w * ) / ) ;
1313- if ( match ) {
1314- codeBlockState = { delimiter : match [ 2 ] , languageId : match [ 3 ] } ;
1315- }
1316- }
1317- }
1318- return codeBlockLanguages ;
1319- }
0 commit comments