@vscode/chat-lib 0.0.5-9 → 0.1.1-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/dist/src/_internal/extension/inlineEdits/node/nextEditProvider.js +1 -1
  2. package/dist/src/_internal/extension/inlineEdits/node/nextEditProvider.js.map +1 -1
  3. package/dist/src/_internal/extension/prompt/node/chatMLFetcher.d.ts +23 -6
  4. package/dist/src/_internal/extension/prompt/node/chatMLFetcher.d.ts.map +1 -1
  5. package/dist/src/_internal/extension/prompt/node/chatMLFetcher.js +426 -177
  6. package/dist/src/_internal/extension/prompt/node/chatMLFetcher.js.map +1 -1
  7. package/dist/src/_internal/extension/prompt/node/chatMLFetcherTelemetry.d.ts +42 -0
  8. package/dist/src/_internal/extension/prompt/node/chatMLFetcherTelemetry.d.ts.map +1 -0
  9. package/dist/src/_internal/extension/prompt/node/chatMLFetcherTelemetry.js +171 -0
  10. package/dist/src/_internal/extension/prompt/node/chatMLFetcherTelemetry.js.map +1 -0
  11. package/dist/src/_internal/extension/xtab/common/promptCrafting.d.ts +4 -6
  12. package/dist/src/_internal/extension/xtab/common/promptCrafting.d.ts.map +1 -1
  13. package/dist/src/_internal/extension/xtab/common/promptCrafting.js +6 -4
  14. package/dist/src/_internal/extension/xtab/common/promptCrafting.js.map +1 -1
  15. package/dist/src/_internal/extension/xtab/common/tags.d.ts +13 -0
  16. package/dist/src/_internal/extension/xtab/common/tags.d.ts.map +1 -1
  17. package/dist/src/_internal/extension/xtab/common/tags.js +15 -1
  18. package/dist/src/_internal/extension/xtab/common/tags.js.map +1 -1
  19. package/dist/src/_internal/extension/xtab/node/xtabProvider.d.ts +0 -1
  20. package/dist/src/_internal/extension/xtab/node/xtabProvider.d.ts.map +1 -1
  21. package/dist/src/_internal/extension/xtab/node/xtabProvider.js +21 -57
  22. package/dist/src/_internal/extension/xtab/node/xtabProvider.js.map +1 -1
  23. package/dist/src/_internal/platform/chat/common/commonTypes.d.ts +16 -0
  24. package/dist/src/_internal/platform/chat/common/commonTypes.d.ts.map +1 -1
  25. package/dist/src/_internal/platform/configuration/common/configurationService.d.ts +19 -14
  26. package/dist/src/_internal/platform/configuration/common/configurationService.d.ts.map +1 -1
  27. package/dist/src/_internal/platform/configuration/common/configurationService.js +18 -18
  28. package/dist/src/_internal/platform/configuration/common/configurationService.js.map +1 -1
  29. package/dist/src/_internal/platform/endpoint/common/chatModelCapabilities.d.ts +11 -3
  30. package/dist/src/_internal/platform/endpoint/common/chatModelCapabilities.d.ts.map +1 -1
  31. package/dist/src/_internal/platform/endpoint/common/chatModelCapabilities.js +86 -9
  32. package/dist/src/_internal/platform/endpoint/common/chatModelCapabilities.js.map +1 -1
  33. package/dist/src/_internal/platform/endpoint/node/chatEndpoint.js +1 -1
  34. package/dist/src/_internal/platform/endpoint/node/chatEndpoint.js.map +1 -1
  35. package/dist/src/_internal/platform/endpoint/node/responsesApi.d.ts +2 -3
  36. package/dist/src/_internal/platform/endpoint/node/responsesApi.d.ts.map +1 -1
  37. package/dist/src/_internal/platform/endpoint/node/responsesApi.js +21 -5
  38. package/dist/src/_internal/platform/endpoint/node/responsesApi.js.map +1 -1
  39. package/dist/src/_internal/platform/github/common/githubAPI.d.ts +5 -6
  40. package/dist/src/_internal/platform/github/common/githubAPI.d.ts.map +1 -1
  41. package/dist/src/_internal/platform/github/common/githubAPI.js +11 -1
  42. package/dist/src/_internal/platform/github/common/githubAPI.js.map +1 -1
  43. package/dist/src/_internal/platform/github/common/githubService.d.ts +5 -17
  44. package/dist/src/_internal/platform/github/common/githubService.d.ts.map +1 -1
  45. package/dist/src/_internal/platform/github/common/githubService.js +1 -5
  46. package/dist/src/_internal/platform/github/common/githubService.js.map +1 -1
  47. package/dist/src/_internal/platform/inlineEdits/common/observableWorkspace.d.ts +0 -1
  48. package/dist/src/_internal/platform/inlineEdits/common/observableWorkspace.d.ts.map +1 -1
  49. package/dist/src/_internal/platform/inlineEdits/common/observableWorkspace.js +1 -1
  50. package/dist/src/_internal/platform/inlineEdits/common/observableWorkspace.js.map +1 -1
  51. package/dist/src/_internal/platform/inlineEdits/common/statelessNextEditProvider.d.ts +23 -11
  52. package/dist/src/_internal/platform/inlineEdits/common/statelessNextEditProvider.d.ts.map +1 -1
  53. package/dist/src/_internal/platform/inlineEdits/common/statelessNextEditProvider.js +51 -17
  54. package/dist/src/_internal/platform/inlineEdits/common/statelessNextEditProvider.js.map +1 -1
  55. package/dist/src/_internal/platform/log/common/logService.d.ts +1 -0
  56. package/dist/src/_internal/platform/log/common/logService.d.ts.map +1 -1
  57. package/dist/src/_internal/platform/log/common/logService.js +20 -0
  58. package/dist/src/_internal/platform/log/common/logService.js.map +1 -1
  59. package/dist/src/_internal/platform/networking/common/fetch.d.ts +0 -1
  60. package/dist/src/_internal/platform/networking/common/fetch.d.ts.map +1 -1
  61. package/dist/src/_internal/platform/networking/common/fetch.js +0 -8
  62. package/dist/src/_internal/platform/networking/common/fetch.js.map +1 -1
  63. package/dist/src/_internal/platform/networking/common/networking.d.ts +3 -0
  64. package/dist/src/_internal/platform/networking/common/networking.d.ts.map +1 -1
  65. package/dist/src/_internal/platform/networking/common/networking.js.map +1 -1
  66. package/dist/src/_internal/platform/openai/node/fetch.d.ts +2 -33
  67. package/dist/src/_internal/platform/openai/node/fetch.d.ts.map +1 -1
  68. package/dist/src/_internal/platform/openai/node/fetch.js +0 -401
  69. package/dist/src/_internal/platform/openai/node/fetch.js.map +1 -1
  70. package/dist/src/_internal/platform/requestLogger/node/requestLogger.d.ts +1 -2
  71. package/dist/src/_internal/platform/requestLogger/node/requestLogger.d.ts.map +1 -1
  72. package/dist/src/_internal/platform/requestLogger/node/requestLogger.js.map +1 -1
  73. package/dist/src/_internal/platform/workspaceRecorder/common/workspaceLog.d.ts +4 -0
  74. package/dist/src/_internal/platform/workspaceRecorder/common/workspaceLog.d.ts.map +1 -1
  75. package/dist/src/_internal/platform/workspaceRecorder/common/workspaceLog.js.map +1 -1
  76. package/dist/src/_internal/util/common/test/shims/vscodeTypesShim.d.ts.map +1 -1
  77. package/dist/src/_internal/util/common/test/shims/vscodeTypesShim.js +0 -1
  78. package/dist/src/_internal/util/common/test/shims/vscodeTypesShim.js.map +1 -1
  79. package/dist/src/_internal/vscodeTypes.d.ts +0 -1
  80. package/dist/src/_internal/vscodeTypes.d.ts.map +1 -1
  81. package/dist/src/_internal/vscodeTypes.js +1 -2
  82. package/dist/src/_internal/vscodeTypes.js.map +1 -1
  83. package/dist/src/main.d.ts +21 -0
  84. package/dist/src/main.d.ts.map +1 -1
  85. package/dist/src/main.js +55 -2
  86. package/dist/src/main.js.map +1 -1
  87. package/dist/src/package.json +173 -78
  88. package/package.json +2 -2
@@ -47,28 +47,40 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
47
47
  };
48
48
  Object.defineProperty(exports, "__esModule", { value: true });
49
49
  exports.ChatMLFetcherImpl = exports.AbstractChatMLFetcher = void 0;
50
+ exports.createTelemetryData = createTelemetryData;
51
+ exports.locationToIntent = locationToIntent;
50
52
  const prompt_tsx_1 = require("@vscode/prompt-tsx");
53
+ const authentication_1 = require("../../../platform/authentication/common/authentication");
51
54
  const chatMLFetcher_1 = require("../../../platform/chat/common/chatMLFetcher");
55
+ const chatQuotaService_1 = require("../../../platform/chat/common/chatQuotaService");
52
56
  const commonTypes_1 = require("../../../platform/chat/common/commonTypes");
53
57
  const conversationOptions_1 = require("../../../platform/chat/common/conversationOptions");
54
58
  const globalStringUtils_1 = require("../../../platform/chat/common/globalStringUtils");
59
+ const interactionService_1 = require("../../../platform/chat/common/interactionService");
55
60
  const configurationService_1 = require("../../../platform/configuration/common/configurationService");
61
+ const capiClient_1 = require("../../../platform/endpoint/common/capiClient");
56
62
  const autoChatEndpoint_1 = require("../../../platform/endpoint/node/autoChatEndpoint");
57
63
  const logService_1 = require("../../../platform/log/common/logService");
64
+ const fetch_1 = require("../../../platform/networking/common/fetch");
58
65
  const fetcherService_1 = require("../../../platform/networking/common/fetcherService");
66
+ const networking_1 = require("../../../platform/networking/common/networking");
59
67
  const openai_1 = require("../../../platform/networking/common/openai");
60
- const fetch_1 = require("../../../platform/openai/node/fetch");
68
+ const chatStream_1 = require("../../../platform/networking/node/chatStream");
69
+ const stream_1 = require("../../../platform/networking/node/stream");
70
+ const fetch_2 = require("../../../platform/openai/node/fetch");
61
71
  const requestLogger_1 = require("../../../platform/requestLogger/node/requestLogger");
62
72
  const telemetry_1 = require("../../../platform/telemetry/common/telemetry");
63
73
  const telemetryData_1 = require("../../../platform/telemetry/common/telemetryData");
64
74
  const anomalyDetection_1 = require("../../../util/common/anomalyDetection");
75
+ const crypto_1 = require("../../../util/common/crypto");
65
76
  const errorsUtil = __importStar(require("../../../util/common/errors"));
66
77
  const errors_1 = require("../../../util/vs/base/common/errors");
67
78
  const event_1 = require("../../../util/vs/base/common/event");
68
79
  const uuid_1 = require("../../../util/vs/base/common/uuid");
69
- const instantiation_1 = require("../../../util/vs/platform/instantiation/common/instantiation");
70
80
  const openAIEndpoint_1 = require("../../byok/node/openAIEndpoint");
71
81
  const constants_1 = require("../../common/constants");
82
+ const chatMLFetcherTelemetry_1 = require("./chatMLFetcherTelemetry");
83
+ const strings_1 = require("../../../util/vs/base/common/strings");
72
84
  class AbstractChatMLFetcher {
73
85
  constructor(options) {
74
86
  this.options = options;
@@ -97,13 +109,16 @@ class AbstractChatMLFetcher {
97
109
  }
98
110
  exports.AbstractChatMLFetcher = AbstractChatMLFetcher;
99
111
  let ChatMLFetcherImpl = class ChatMLFetcherImpl extends AbstractChatMLFetcher {
100
- constructor(_fetcherService, _telemetryService, _requestLogger, _logService, _instantiationService, options) {
112
+ constructor(_fetcherService, _telemetryService, _requestLogger, _logService, _authenticationService, _interactionService, _chatQuotaService, _capiClientService, options) {
101
113
  super(options);
102
114
  this._fetcherService = _fetcherService;
103
115
  this._telemetryService = _telemetryService;
104
116
  this._requestLogger = _requestLogger;
105
117
  this._logService = _logService;
106
- this._instantiationService = _instantiationService;
118
+ this._authenticationService = _authenticationService;
119
+ this._interactionService = _interactionService;
120
+ this._chatQuotaService = _chatQuotaService;
121
+ this._capiClientService = _capiClientService;
107
122
  }
108
123
  /**
109
124
  * Note: the returned array of strings may be less than `n` (e.g., in case there were errors during streaming)
@@ -142,9 +157,7 @@ let ChatMLFetcherImpl = class ChatMLFetcherImpl extends AbstractChatMLFetcher {
142
157
  model: chatEndpoint.model,
143
158
  ourRequestId,
144
159
  location: opts.location,
145
- postOptions,
146
160
  body: requestBody,
147
- tools: requestBody.tools,
148
161
  ignoreStatefulMarker: opts.ignoreStatefulMarker
149
162
  });
150
163
  let tokenCount = -1;
@@ -155,14 +168,14 @@ let ChatMLFetcherImpl = class ChatMLFetcherImpl extends AbstractChatMLFetcher {
155
168
  const payloadValidationResult = isValidChatPayload(opts.messages, postOptions);
156
169
  if (!payloadValidationResult.isValid) {
157
170
  response = {
158
- type: fetch_1.FetchResponseKind.Failed,
171
+ type: fetch_2.FetchResponseKind.Failed,
159
172
  modelRequestId: undefined,
160
- failKind: fetch_1.ChatFailKind.ValidationFailed,
173
+ failKind: fetch_2.ChatFailKind.ValidationFailed,
161
174
  reason: payloadValidationResult.reason,
162
175
  };
163
176
  }
164
177
  else {
165
- response = await this._instantiationService.invokeFunction(accessor => (0, fetch_1.fetchAndStreamChat)(accessor, chatEndpoint, requestBody, baseTelemetry, streamRecorder.callback, requestOptions.secretKey, opts.location, ourRequestId, postOptions.n, userInitiatedRequest, token, telemetryProperties, opts.useFetcher));
178
+ response = await this._fetchAndStreamChat(chatEndpoint, requestBody, baseTelemetry, streamRecorder.callback, requestOptions.secretKey, opts.location, ourRequestId, postOptions.n, token, userInitiatedRequest, telemetryProperties, opts.useFetcher);
166
179
  tokenCount = await chatEndpoint.acquireTokenizer().countMessagesTokens(messages);
167
180
  const extensionId = source?.extensionId ?? constants_1.EXTENSION_ID;
168
181
  this._onDidMakeChatMLRequest.fire({
@@ -175,7 +188,7 @@ let ChatMLFetcherImpl = class ChatMLFetcherImpl extends AbstractChatMLFetcher {
175
188
  const timeToFirstToken = Date.now() - baseTelemetry.issuedTime;
176
189
  pendingLoggedChatRequest?.markTimeToFirstToken(timeToFirstToken);
177
190
  switch (response.type) {
178
- case fetch_1.FetchResponseKind.Success: {
191
+ case fetch_2.FetchResponseKind.Success: {
179
192
  const result = await this.processSuccessfulResponse(response, messages, requestBody, ourRequestId, maxResponseTokens, tokenCount, timeToFirstToken, streamRecorder, baseTelemetry, chatEndpoint, userInitiatedRequest);
180
193
  // Handle FilteredRetry case with augmented messages
181
194
  if (result.type === commonTypes_1.ChatFetchResponseType.FilteredRetry) {
@@ -225,8 +238,8 @@ let ChatMLFetcherImpl = class ChatMLFetcherImpl extends AbstractChatMLFetcher {
225
238
  pendingLoggedChatRequest?.resolve(result, streamRecorder.deltas);
226
239
  return result;
227
240
  }
228
- case fetch_1.FetchResponseKind.Canceled:
229
- this._sendCancellationTelemetry({
241
+ case fetch_2.FetchResponseKind.Canceled:
242
+ chatMLFetcherTelemetry_1.ChatMLFetcherTelemetrySender.sendCancellationTelemetry(this._telemetryService, {
230
243
  source: telemetryProperties.messageSource ?? 'unknown',
231
244
  requestId: ourRequestId,
232
245
  model: chatEndpoint.model,
@@ -247,9 +260,9 @@ let ChatMLFetcherImpl = class ChatMLFetcherImpl extends AbstractChatMLFetcher {
247
260
  });
248
261
  pendingLoggedChatRequest?.resolveWithCancelation();
249
262
  return this.processCanceledResponse(response, ourRequestId);
250
- case fetch_1.FetchResponseKind.Failed: {
263
+ case fetch_2.FetchResponseKind.Failed: {
251
264
  const processed = this.processFailedResponse(response, ourRequestId);
252
- this._sendResponseErrorTelemetry(processed, telemetryProperties, ourRequestId, chatEndpoint, requestBody, tokenCount, maxResponseTokens, timeToFirstToken, this.filterImageMessages(messages));
265
+ chatMLFetcherTelemetry_1.ChatMLFetcherTelemetrySender.sendResponseErrorTelemetry(this._telemetryService, processed, telemetryProperties, ourRequestId, chatEndpoint, requestBody, tokenCount, maxResponseTokens, timeToFirstToken, this.filterImageMessages(messages));
253
266
  pendingLoggedChatRequest?.resolve(processed);
254
267
  return processed;
255
268
  }
@@ -283,7 +296,7 @@ let ChatMLFetcherImpl = class ChatMLFetcherImpl extends AbstractChatMLFetcher {
283
296
  }
284
297
  }
285
298
  if (processed.type === commonTypes_1.ChatFetchResponseType.Canceled) {
286
- this._sendCancellationTelemetry({
299
+ chatMLFetcherTelemetry_1.ChatMLFetcherTelemetrySender.sendCancellationTelemetry(this._telemetryService, {
287
300
  source: telemetryProperties.messageSource ?? 'unknown',
288
301
  requestId: ourRequestId,
289
302
  model: chatEndpoint.model,
@@ -301,170 +314,357 @@ let ChatMLFetcherImpl = class ChatMLFetcherImpl extends AbstractChatMLFetcher {
301
314
  });
302
315
  }
303
316
  else {
304
- this._sendResponseErrorTelemetry(processed, telemetryProperties, ourRequestId, chatEndpoint, requestBody, tokenCount, maxResponseTokens, timeToError, this.filterImageMessages(messages));
317
+ chatMLFetcherTelemetry_1.ChatMLFetcherTelemetrySender.sendResponseErrorTelemetry(this._telemetryService, processed, telemetryProperties, ourRequestId, chatEndpoint, requestBody, tokenCount, maxResponseTokens, timeToError, this.filterImageMessages(messages));
305
318
  }
306
319
  pendingLoggedChatRequest?.resolve(processed);
307
320
  return processed;
308
321
  }
309
322
  }
310
- _sendCancellationTelemetry({ source, requestId, model, apiType, associatedRequestId }, { totalTokenMax, promptTokenCount, tokenCountMax, timeToFirstToken, timeToFirstTokenEmitted, timeToCancelled, isVisionRequest, isBYOK, isAuto }) {
311
- /* __GDPR__
312
- "response.cancelled" : {
313
- "owner": "digitarald",
314
- "comment": "Report canceled service responses for quality.",
315
- "model": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Model selection for the response" },
316
- "apiType": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "API type for the response- chat completions or responses" },
317
- "source": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Source for why the request was made" },
318
- "requestId": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Id of the request" },
319
- "associatedRequestId": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "Another request ID that this request is associated with (eg, the originating request of a summarization request)." },
320
- "totalTokenMax": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Maximum total token window", "isMeasurement": true },
321
- "promptTokenCount": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Number of prompt tokens", "isMeasurement": true },
322
- "tokenCountMax": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Maximum generated tokens", "isMeasurement": true },
323
- "timeToFirstToken": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Time to first token", "isMeasurement": true },
324
- "timeToFirstTokenEmitted": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Time to first token emitted (visible text)", "isMeasurement": true },
325
- "timeToCancelled": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Time to first token", "isMeasurement": true },
326
- "isVisionRequest": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Whether the request was for a vision model", "isMeasurement": true },
327
- "isBYOK": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "Whether the request was for a BYOK model", "isMeasurement": true },
328
- "isAuto": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "Whether the request was for an Auto model", "isMeasurement": true },
329
- "retryAfterErrorCategory": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "If the response failed and this is a retry attempt, this contains the error category." },
330
- "retryAfterFilterCategory": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "If the response was filtered and this is a retry attempt, this contains the original filtered content category." }
323
+ async _fetchAndStreamChat(chatEndpointInfo, request, baseTelemetryData, finishedCb, secretKey, location, ourRequestId, nChoices, cancellationToken, userInitiatedRequest, telemetryProperties, useFetcher) {
324
+ if (cancellationToken.isCancellationRequested) {
325
+ return { type: fetch_2.FetchResponseKind.Canceled, reason: 'before fetch request' };
326
+ }
327
+ this._logService.debug(`modelMaxPromptTokens ${chatEndpointInfo.modelMaxPromptTokens}`);
328
+ this._logService.debug(`modelMaxResponseTokens ${request.max_tokens ?? 2048}`);
329
+ this._logService.debug(`chat model ${chatEndpointInfo.model}`);
330
+ secretKey ??= (await this._authenticationService.getCopilotToken()).token;
331
+ if (!secretKey) {
332
+ // If no key is set we error
333
+ const urlOrRequestMetadata = (0, networking_1.stringifyUrlOrRequestMetadata)(chatEndpointInfo.urlOrRequestMetadata);
334
+ this._logService.error(`Failed to send request to ${urlOrRequestMetadata} due to missing key`);
335
+ (0, stream_1.sendCommunicationErrorTelemetry)(this._telemetryService, `Failed to send request to ${urlOrRequestMetadata} due to missing key`);
336
+ return {
337
+ type: fetch_2.FetchResponseKind.Failed,
338
+ modelRequestId: undefined,
339
+ failKind: fetch_2.ChatFailKind.TokenExpiredOrInvalid,
340
+ reason: 'key is missing'
341
+ };
342
+ }
343
+ // Generate unique ID to link input and output messages
344
+ const modelCallId = (0, uuid_1.generateUuid)();
345
+ const response = await this._fetchWithInstrumentation(chatEndpointInfo, ourRequestId, request, secretKey, location, cancellationToken, userInitiatedRequest, { ...telemetryProperties, modelCallId }, useFetcher);
346
+ if (cancellationToken.isCancellationRequested) {
347
+ const body = await response.body();
348
+ try {
349
+ // Destroy the stream so that the server is hopefully notified we don't want any more data
350
+ // and can cancel/forget about the request itself.
351
+ body.destroy();
352
+ }
353
+ catch (e) {
354
+ this._logService.error(e, `Error destroying stream`);
355
+ this._telemetryService.sendGHTelemetryException(e, 'Error destroying stream');
331
356
  }
332
- */
333
- this._telemetryService.sendTelemetryEvent('response.cancelled', { github: true, microsoft: true }, {
334
- apiType,
335
- source,
336
- requestId,
337
- model,
338
- associatedRequestId,
357
+ return { type: fetch_2.FetchResponseKind.Canceled, reason: 'after fetch request' };
358
+ }
359
+ if (response.status === 200 && this._authenticationService.copilotToken?.isFreeUser && this._authenticationService.copilotToken?.isChatQuotaExceeded) {
360
+ this._authenticationService.resetCopilotToken();
361
+ }
362
+ if (response.status !== 200) {
363
+ const telemetryData = createTelemetryData(chatEndpointInfo, location, ourRequestId);
364
+ this._logService.info('Request ID for failed request: ' + ourRequestId);
365
+ return this._handleError(telemetryData, response, ourRequestId);
366
+ }
367
+ // Extend baseTelemetryData with modelCallId for output messages
368
+ const extendedBaseTelemetryData = baseTelemetryData.extendedBy({ modelCallId });
369
+ const chatCompletions = await chatEndpointInfo.processResponseFromChatEndpoint(this._telemetryService, this._logService, response, nChoices ?? /* OpenAI's default */ 1, finishedCb, extendedBaseTelemetryData, cancellationToken);
370
+ // CAPI will return us a Copilot Edits Session Header which is our token to using the speculative decoding endpoint
371
+ // We should store this in the auth service for easy use later
372
+ if (response.headers.get('Copilot-Edits-Session')) {
373
+ this._authenticationService.speculativeDecodingEndpointToken = response.headers.get('Copilot-Edits-Session') ?? undefined;
374
+ }
375
+ this._chatQuotaService.processQuotaHeaders(response.headers);
376
+ return {
377
+ type: fetch_2.FetchResponseKind.Success,
378
+ chatCompletions,
379
+ };
380
+ }
381
+ async _fetchWithInstrumentation(chatEndpoint, ourRequestId, request, secretKey, location, cancellationToken, userInitiatedRequest, telemetryProperties, useFetcher) {
382
+ // If request contains an image, we include this header.
383
+ const additionalHeaders = {
384
+ 'X-Interaction-Id': this._interactionService.interactionId,
385
+ 'X-Initiator': userInitiatedRequest ? 'user' : 'agent', // Agent = a system request / not the primary user query.
386
+ };
387
+ if (request.messages?.some((m) => Array.isArray(m.content) ? m.content.some(c => 'image_url' in c) : false) && chatEndpoint.supportsVision) {
388
+ additionalHeaders['Copilot-Vision-Request'] = 'true';
389
+ }
390
+ const telemetryData = telemetryData_1.TelemetryData.createAndMarkAsIssued({
391
+ endpoint: 'completions',
392
+ engineName: 'chat',
393
+ uiKind: commonTypes_1.ChatLocation.toString(location),
394
+ ...telemetryProperties // This includes the modelCallId from fetchAndStreamChat
339
395
  }, {
340
- totalTokenMax,
341
- promptTokenCount,
342
- tokenCountMax,
343
- timeToFirstToken,
344
- timeToFirstTokenEmitted,
345
- timeToCancelled,
346
- isVisionRequest,
347
- isBYOK,
348
- isAuto
396
+ maxTokenWindow: chatEndpoint.modelMaxPromptTokens
349
397
  });
350
- }
351
- _sendResponseErrorTelemetry(processed, telemetryProperties, ourRequestId, chatEndpointInfo, requestBody, tokenCount, maxResponseTokens, timeToFirstToken, isVisionRequest) {
352
- /* __GDPR__
353
- "response.error" : {
354
- "owner": "digitarald",
355
- "comment": "Report quality issue for when a service response failed.",
356
- "type": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Type of issue" },
357
- "reason": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Reason of issue" },
358
- "model": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Model selection for the response" },
359
- "apiType": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "API type for the response- chat completions or responses" },
360
- "source": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Source for why the request was made" },
361
- "requestId": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Id of the request" },
362
- "associatedRequestId": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "Another request ID that this request is associated with (eg, the originating request of a summarization request)." },
363
- "reasoningEffort": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Reasoning effort level" },
364
- "reasoningSummary": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Reasoning summary level" },
365
- "totalTokenMax": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Maximum total token window", "isMeasurement": true },
366
- "promptTokenCount": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Number of prompt tokens", "isMeasurement": true },
367
- "tokenCountMax": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Maximum generated tokens", "isMeasurement": true },
368
- "timeToFirstToken": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Time to first token", "isMeasurement": true },
369
- "timeToFirstTokenEmitted": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Time to first token emitted (visible text)", "isMeasurement": true },
370
- "isVisionRequest": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Whether the request was for a vision model", "isMeasurement": true },
371
- "isBYOK": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "Whether the request was for a BYOK model", "isMeasurement": true },
372
- "isAuto": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "Whether the request was for an Auto model", "isMeasurement": true },
373
- "retryAfterErrorCategory": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "If the response failed and this is a retry attempt, this contains the error category." },
374
- "retryAfterFilterCategory": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "If the response was filtered and this is a retry attempt, this contains the original filtered content category." }
398
+ for (const [key, value] of Object.entries(request)) {
399
+ if (key === 'messages' || key === 'input') {
400
+ continue;
401
+ } // Skip messages (PII)
402
+ telemetryData.properties[`request.option.${key}`] = JSON.stringify(value) ?? 'undefined';
403
+ }
404
+ // The request ID we are passed in is sent in the request to the proxy, and included in our pre-request telemetry.
405
+ // We hope (but do not rely on) that the model will use the same ID in the response, allowing us to correlate
406
+ // the request and response.
407
+ telemetryData.properties['headerRequestId'] = ourRequestId;
408
+ this._telemetryService.sendGHTelemetryEvent('request.sent', telemetryData.properties, telemetryData.measurements);
409
+ const requestStart = Date.now();
410
+ const intent = locationToIntent(location);
411
+ // Wrap the Promise with success/error callbacks so we can log/measure it
412
+ return (0, networking_1.postRequest)(this._fetcherService, this._telemetryService, this._capiClientService, chatEndpoint, secretKey, await (0, crypto_1.createRequestHMAC)(process.env.HMAC_SECRET), intent, ourRequestId, request, additionalHeaders, cancellationToken, useFetcher).then(response => {
413
+ const apim = response.headers.get('apim-request-id');
414
+ if (apim) {
415
+ this._logService.debug(`APIM request id: ${apim}`);
375
416
  }
376
- */
377
- this._telemetryService.sendTelemetryEvent('response.error', { github: true, microsoft: true }, {
378
- type: processed.type,
379
- reason: processed.reason,
380
- source: telemetryProperties?.messageSource ?? 'unknown',
381
- requestId: ourRequestId,
382
- model: chatEndpointInfo.model,
383
- apiType: chatEndpointInfo.apiType,
384
- reasoningEffort: requestBody.reasoning?.effort,
385
- reasoningSummary: requestBody.reasoning?.summary,
386
- associatedRequestId: telemetryProperties?.associatedRequestId,
387
- ...(telemetryProperties?.retryAfterErrorCategory ? { retryAfterErrorCategory: telemetryProperties.retryAfterErrorCategory } : {}),
388
- ...(telemetryProperties?.retryAfterFilterCategory ? { retryAfterFilterCategory: telemetryProperties.retryAfterFilterCategory } : {})
389
- }, {
390
- totalTokenMax: chatEndpointInfo.modelMaxPromptTokens ?? -1,
391
- promptTokenCount: tokenCount,
392
- tokenCountMax: maxResponseTokens,
393
- timeToFirstToken,
394
- isVisionRequest: isVisionRequest ? 1 : -1,
395
- isBYOK: (0, openAIEndpoint_1.isBYOKModel)(chatEndpointInfo),
396
- isAuto: (0, autoChatEndpoint_1.isAutoModel)(chatEndpointInfo)
417
+ const ghRequestId = response.headers.get('x-github-request-id');
418
+ if (ghRequestId) {
419
+ this._logService.debug(`GH request id: ${ghRequestId}`);
420
+ }
421
+ // This ID is hopefully the one the same as ourRequestId, but it is not guaranteed.
422
+ // If they are different then we will override the original one we set in telemetryData above.
423
+ const modelRequestId = (0, fetch_1.getRequestId)(response, undefined);
424
+ telemetryData.extendWithRequestId(modelRequestId);
425
+ // TODO: Add response length (requires parsing)
426
+ const totalTimeMs = Date.now() - requestStart;
427
+ telemetryData.measurements.totalTimeMs = totalTimeMs;
428
+ this._logService.debug(`request.response: [${(0, networking_1.stringifyUrlOrRequestMetadata)(chatEndpoint.urlOrRequestMetadata)}], took ${totalTimeMs} ms`);
429
+ this._telemetryService.sendGHTelemetryEvent('request.response', telemetryData.properties, telemetryData.measurements);
430
+ return response;
431
+ })
432
+ .catch(error => {
433
+ if (this._fetcherService.isAbortError(error)) {
434
+ // If we cancelled a network request, we don't want to log a `request.error`
435
+ throw error;
436
+ }
437
+ const warningTelemetry = telemetryData.extendedBy({ error: 'Network exception' });
438
+ this._telemetryService.sendGHTelemetryEvent('request.shownWarning', warningTelemetry.properties, warningTelemetry.measurements);
439
+ telemetryData.properties.code = String(error.code ?? '');
440
+ telemetryData.properties.errno = String(error.errno ?? '');
441
+ telemetryData.properties.message = String(error.message ?? '');
442
+ telemetryData.properties.type = String(error.type ?? '');
443
+ const totalTimeMs = Date.now() - requestStart;
444
+ telemetryData.measurements.totalTimeMs = totalTimeMs;
445
+ this._logService.debug(`request.response: [${(0, networking_1.stringifyUrlOrRequestMetadata)(chatEndpoint.urlOrRequestMetadata)}] took ${totalTimeMs} ms`);
446
+ this._telemetryService.sendGHTelemetryEvent('request.error', telemetryData.properties, telemetryData.measurements);
447
+ throw error;
448
+ })
449
+ .finally(() => {
450
+ (0, chatStream_1.sendEngineMessagesTelemetry)(this._telemetryService, request.messages ?? [], telemetryData, false, this._logService);
397
451
  });
398
452
  }
453
+ async _handleError(telemetryData, response, requestId) {
454
+ const modelRequestIdObj = (0, fetch_1.getRequestId)(response, undefined);
455
+ requestId = modelRequestIdObj.headerRequestId || requestId;
456
+ modelRequestIdObj.headerRequestId = requestId;
457
+ telemetryData.properties.error = `Response status was ${response.status}`;
458
+ telemetryData.properties.status = String(response.status);
459
+ this._telemetryService.sendGHTelemetryEvent('request.shownWarning', telemetryData.properties, telemetryData.measurements);
460
+ const text = await response.text();
461
+ let jsonData;
462
+ try {
463
+ jsonData = JSON.parse(text);
464
+ jsonData = jsonData?.error ?? jsonData; // Extract nested error object if it exists
465
+ }
466
+ catch {
467
+ // JSON parsing failed, it's not json content.
468
+ }
469
+ if (400 <= response.status && response.status < 500) {
470
+ if (response.status === 400 && text.includes('off_topic')) {
471
+ return {
472
+ type: fetch_2.FetchResponseKind.Failed,
473
+ modelRequestId: modelRequestIdObj,
474
+ failKind: fetch_2.ChatFailKind.OffTopic,
475
+ reason: 'filtered as off_topic by intent classifier: message was not programming related',
476
+ };
477
+ }
478
+ if (response.status === 401 && text.includes('authorize_url') && jsonData?.authorize_url) {
479
+ return {
480
+ type: fetch_2.FetchResponseKind.Failed,
481
+ modelRequestId: modelRequestIdObj,
482
+ failKind: fetch_2.ChatFailKind.AgentUnauthorized,
483
+ reason: response.statusText || response.statusText,
484
+ data: jsonData
485
+ };
486
+ }
487
+ if (response.status === 400 && jsonData?.code === 'previous_response_not_found') {
488
+ return {
489
+ type: fetch_2.FetchResponseKind.Failed,
490
+ modelRequestId: modelRequestIdObj,
491
+ failKind: fetch_2.ChatFailKind.InvalidPreviousResponseId,
492
+ reason: jsonData.message || 'Invalid previous response ID',
493
+ data: jsonData,
494
+ };
495
+ }
496
+ if (response.status === 401 || response.status === 403) {
497
+ // Token has expired or invalid, fetch a new one on next request
498
+ // TODO(drifkin): these actions should probably happen in vsc specific code
499
+ this._authenticationService.resetCopilotToken(response.status);
500
+ return {
501
+ type: fetch_2.FetchResponseKind.Failed,
502
+ modelRequestId: modelRequestIdObj,
503
+ failKind: fetch_2.ChatFailKind.TokenExpiredOrInvalid,
504
+ reason: jsonData?.message || `token expired or invalid: ${response.status}`,
505
+ };
506
+ }
507
+ if (response.status === 402) {
508
+ // When we receive a 402, we have exceed a quota
509
+ // This is stored on the token so let's refresh it
510
+ this._authenticationService.resetCopilotToken(response.status);
511
+ const retryAfter = response.headers.get('retry-after');
512
+ const convertToDate = (retryAfterString) => {
513
+ if (!retryAfterString) {
514
+ return undefined;
515
+ }
516
+ // Try treating it as a date
517
+ const retryAfterDate = new Date(retryAfterString);
518
+ if (!isNaN(retryAfterDate.getDate())) {
519
+ return retryAfterDate;
520
+ }
521
+ // It is not a date, try treating it as a duration from the current date
522
+ const retryAfterDuration = parseInt(retryAfterString, 10);
523
+ if (isNaN(retryAfterDuration)) {
524
+ return undefined;
525
+ }
526
+ return new Date(Date.now() + retryAfterDuration * 1000);
527
+ };
528
+ const retryAfterDate = convertToDate(retryAfter);
529
+ return {
530
+ type: fetch_2.FetchResponseKind.Failed,
531
+ modelRequestId: modelRequestIdObj,
532
+ failKind: fetch_2.ChatFailKind.QuotaExceeded,
533
+ reason: jsonData?.message ?? 'Free tier quota exceeded',
534
+ data: {
535
+ capiError: jsonData,
536
+ retryAfter: retryAfterDate
537
+ }
538
+ };
539
+ }
540
+ if (response.status === 404) {
541
+ let errorReason;
542
+ // Check if response body is valid JSON
543
+ if (!jsonData) {
544
+ errorReason = text;
545
+ }
546
+ else {
547
+ errorReason = JSON.stringify(jsonData);
548
+ }
549
+ return {
550
+ type: fetch_2.FetchResponseKind.Failed,
551
+ modelRequestId: modelRequestIdObj,
552
+ failKind: fetch_2.ChatFailKind.NotFound,
553
+ reason: errorReason
554
+ };
555
+ }
556
+ if (response.status === 422) {
557
+ return {
558
+ type: fetch_2.FetchResponseKind.Failed,
559
+ modelRequestId: modelRequestIdObj,
560
+ failKind: fetch_2.ChatFailKind.ContentFilter,
561
+ reason: 'Filtered by Responsible AI Service'
562
+ };
563
+ }
564
+ if (response.status === 424) {
565
+ return {
566
+ type: fetch_2.FetchResponseKind.Failed,
567
+ modelRequestId: modelRequestIdObj,
568
+ failKind: fetch_2.ChatFailKind.AgentFailedDependency,
569
+ reason: text
570
+ };
571
+ }
572
+ if (response.status === 429) {
573
+ let rateLimitReason = text;
574
+ rateLimitReason = jsonData?.message ?? jsonData?.code;
575
+ if (text.includes('extension_blocked') && jsonData?.code === 'extension_blocked' && jsonData?.type === 'rate_limit_error') {
576
+ return {
577
+ type: fetch_2.FetchResponseKind.Failed,
578
+ modelRequestId: modelRequestIdObj,
579
+ failKind: fetch_2.ChatFailKind.ExtensionBlocked,
580
+ reason: 'Extension blocked',
581
+ data: {
582
+ ...jsonData?.message,
583
+ retryAfter: response.headers.get('retry-after'),
584
+ }
585
+ };
586
+ }
587
+ // HTTP 429 Too Many Requests
588
+ return {
589
+ type: fetch_2.FetchResponseKind.Failed,
590
+ modelRequestId: modelRequestIdObj,
591
+ failKind: fetch_2.ChatFailKind.RateLimited,
592
+ reason: rateLimitReason,
593
+ data: {
594
+ retryAfter: response.headers.get('retry-after'),
595
+ rateLimitKey: response.headers.get('x-ratelimit-exceeded'),
596
+ capiError: jsonData
597
+ }
598
+ };
599
+ }
600
+ if (response.status === 466) {
601
+ this._logService.info(text);
602
+ return {
603
+ type: fetch_2.FetchResponseKind.Failed,
604
+ modelRequestId: modelRequestIdObj,
605
+ failKind: fetch_2.ChatFailKind.ClientNotSupported,
606
+ reason: `client not supported: ${text}`
607
+ };
608
+ }
609
+ if (response.status === 499) {
610
+ this._logService.info('Cancelled by server');
611
+ return {
612
+ type: fetch_2.FetchResponseKind.Failed,
613
+ modelRequestId: modelRequestIdObj,
614
+ failKind: fetch_2.ChatFailKind.ServerCanceled,
615
+ reason: 'canceled by server'
616
+ };
617
+ }
618
+ }
619
+ else if (500 <= response.status && response.status < 600) {
620
+ if (response.status === 503) {
621
+ return {
622
+ type: fetch_2.FetchResponseKind.Failed,
623
+ modelRequestId: modelRequestIdObj,
624
+ failKind: fetch_2.ChatFailKind.RateLimited,
625
+ reason: 'Upstream provider rate limit hit',
626
+ data: {
627
+ retryAfter: null,
628
+ rateLimitKey: null,
629
+ capiError: { code: 'upstream_provider_rate_limit', message: text }
630
+ }
631
+ };
632
+ }
633
+ const reasonNoText = `Server error: ${response.status}`;
634
+ const reason = `${reasonNoText} ${text}`;
635
+ this._logService.error(reason);
636
+ // HTTP 5xx Server Error
637
+ return {
638
+ type: fetch_2.FetchResponseKind.Failed,
639
+ modelRequestId: modelRequestIdObj,
640
+ failKind: fetch_2.ChatFailKind.ServerError,
641
+ reason: reasonNoText,
642
+ };
643
+ }
644
+ this._logService.error(`Request Failed: ${response.status} ${text}`);
645
+ (0, stream_1.sendCommunicationErrorTelemetry)(this._telemetryService, 'Unhandled status from server: ' + response.status, text);
646
+ return {
647
+ type: fetch_2.FetchResponseKind.Failed,
648
+ modelRequestId: modelRequestIdObj,
649
+ failKind: fetch_2.ChatFailKind.Unknown,
650
+ reason: `Request Failed: ${response.status} ${text}`
651
+ };
652
+ }
399
653
  async processSuccessfulResponse(response, messages, requestBody, requestId, maxResponseTokens, promptTokenCount, timeToFirstToken, streamRecorder, baseTelemetry, chatEndpointInfo, userInitiatedRequest) {
400
654
  const completions = [];
401
655
  for await (const chatCompletion of response.chatCompletions) {
402
- /* __GDPR__
403
- "response.success" : {
404
- "owner": "digitarald",
405
- "comment": "Report quality details for a successful service response.",
406
- "reason": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Reason for why a response finished" },
407
- "filterReason": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Reason for why a response was filtered" },
408
- "source": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Source of the initial request" },
409
- "initiatorType": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "Whether the request was initiated by a user or an agent" },
410
- "model": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Model selection for the response" },
411
- "modelInvoked": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Actual model invoked for the response" },
412
- "apiType": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "API type for the response- chat completions or responses" },
413
- "requestId": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Id of the current turn request" },
414
- "associatedRequestId": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "Another request ID that this request is associated with (eg, the originating request of a summarization request)." },
415
- "reasoningEffort": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Reasoning effort level" },
416
- "reasoningSummary": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Reasoning summary level" },
417
- "totalTokenMax": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Maximum total token window", "isMeasurement": true },
418
- "clientPromptTokenCount": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Number of prompt tokens, locally counted", "isMeasurement": true },
419
- "promptTokenCount": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Number of prompt tokens, server side counted", "isMeasurement": true },
420
- "promptCacheTokenCount": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Number of prompt tokens hitting cache as reported by server", "isMeasurement": true },
421
- "tokenCountMax": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Maximum generated tokens", "isMeasurement": true },
422
- "tokenCount": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Number of generated tokens", "isMeasurement": true },
423
- "reasoningTokens": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Number of reasoning tokens", "isMeasurement": true },
424
- "acceptedPredictionTokens": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "Number of tokens in the prediction that appeared in the completion", "isMeasurement": true },
425
- "rejectedPredictionTokens": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "Number of tokens in the prediction that appeared in the completion", "isMeasurement": true },
426
- "completionTokens": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "Number of tokens in the output", "isMeasurement": true },
427
- "timeToFirstToken": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Time to first token", "isMeasurement": true },
428
- "timeToFirstTokenEmitted": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Time to first token emitted (visible text)", "isMeasurement": true },
429
- "timeToComplete": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Time to complete the request", "isMeasurement": true },
430
- "isVisionRequest": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Whether the request was for a vision model", "isMeasurement": true },
431
- "isBYOK": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "Whether the request was for a BYOK model", "isMeasurement": true },
432
- "isAuto": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "Whether the request was for an Auto model", "isMeasurement": true },
433
- "retryAfterErrorCategory": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "If the response failed and this is a retry attempt, this contains the error category." },
434
- "retryAfterFilterCategory": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "If the response was filtered and this is a retry attempt, this contains the original filtered content category." }
435
- }
436
- */
437
- this._telemetryService.sendTelemetryEvent('response.success', { github: true, microsoft: true }, {
438
- reason: chatCompletion.finishReason,
439
- filterReason: chatCompletion.filterReason,
440
- source: baseTelemetry?.properties.messageSource ?? 'unknown',
441
- initiatorType: userInitiatedRequest ? 'user' : 'agent',
442
- model: chatEndpointInfo?.model,
443
- modelInvoked: chatCompletion.model,
444
- apiType: chatEndpointInfo?.apiType,
656
+ chatMLFetcherTelemetry_1.ChatMLFetcherTelemetrySender.sendSuccessTelemetry(this._telemetryService, {
445
657
  requestId,
446
- associatedRequestId: baseTelemetry?.properties.associatedRequestId,
447
- reasoningEffort: requestBody.reasoning?.effort,
448
- reasoningSummary: requestBody.reasoning?.summary,
449
- ...(baseTelemetry?.properties.retryAfterErrorCategory ? { retryAfterErrorCategory: baseTelemetry.properties.retryAfterErrorCategory } : {}),
450
- ...(baseTelemetry?.properties.retryAfterFilterCategory ? { retryAfterFilterCategory: baseTelemetry.properties.retryAfterFilterCategory } : {}),
451
- }, {
452
- totalTokenMax: chatEndpointInfo?.modelMaxPromptTokens ?? -1,
453
- tokenCountMax: maxResponseTokens,
454
- promptTokenCount: chatCompletion.usage?.prompt_tokens,
455
- promptCacheTokenCount: chatCompletion.usage?.prompt_tokens_details?.cached_tokens,
456
- clientPromptTokenCount: promptTokenCount,
457
- tokenCount: chatCompletion.usage?.total_tokens,
458
- reasoningTokens: chatCompletion.usage?.completion_tokens_details?.reasoning_tokens,
459
- acceptedPredictionTokens: chatCompletion.usage?.completion_tokens_details?.accepted_prediction_tokens,
460
- rejectedPredictionTokens: chatCompletion.usage?.completion_tokens_details?.rejected_prediction_tokens,
461
- completionTokens: chatCompletion.usage?.completion_tokens,
658
+ chatCompletion,
659
+ baseTelemetry,
660
+ userInitiatedRequest,
661
+ chatEndpointInfo,
662
+ requestBody,
663
+ maxResponseTokens,
664
+ promptTokenCount,
462
665
  timeToFirstToken,
463
666
  timeToFirstTokenEmitted: (baseTelemetry && streamRecorder.firstTokenEmittedTime) ? streamRecorder.firstTokenEmittedTime - baseTelemetry.issuedTime : -1,
464
- timeToComplete: baseTelemetry ? Date.now() - baseTelemetry.issuedTime : -1,
465
- isVisionRequest: this.filterImageMessages(messages) ? 1 : -1,
466
- isBYOK: (0, openAIEndpoint_1.isBYOKModel)(chatEndpointInfo),
467
- isAuto: (0, autoChatEndpoint_1.isAutoModel)(chatEndpointInfo)
667
+ hasImageMessages: this.filterImageMessages(messages),
468
668
  });
469
669
  if (!this.isRepetitive(chatCompletion, baseTelemetry?.properties)) {
470
670
  completions.push(chatCompletion);
@@ -563,38 +763,38 @@ let ChatMLFetcherImpl = class ChatMLFetcherImpl extends AbstractChatMLFetcher {
563
763
  processFailedResponse(response, requestId) {
564
764
  const serverRequestId = response.modelRequestId?.gitHubRequestId;
565
765
  const reason = response.reason;
566
- if (response.failKind === fetch_1.ChatFailKind.RateLimited) {
766
+ if (response.failKind === fetch_2.ChatFailKind.RateLimited) {
567
767
  return { type: commonTypes_1.ChatFetchResponseType.RateLimited, reason, requestId, serverRequestId, retryAfter: response.data?.retryAfter, rateLimitKey: (response.data?.rateLimitKey || ''), capiError: response.data?.capiError };
568
768
  }
569
- if (response.failKind === fetch_1.ChatFailKind.QuotaExceeded) {
769
+ if (response.failKind === fetch_2.ChatFailKind.QuotaExceeded) {
570
770
  return { type: commonTypes_1.ChatFetchResponseType.QuotaExceeded, reason, requestId, serverRequestId, retryAfter: response.data?.retryAfter, capiError: response.data?.capiError };
571
771
  }
572
- if (response.failKind === fetch_1.ChatFailKind.OffTopic) {
772
+ if (response.failKind === fetch_2.ChatFailKind.OffTopic) {
573
773
  return { type: commonTypes_1.ChatFetchResponseType.OffTopic, reason, requestId, serverRequestId };
574
774
  }
575
- if (response.failKind === fetch_1.ChatFailKind.TokenExpiredOrInvalid || response.failKind === fetch_1.ChatFailKind.ClientNotSupported || reason.includes('Bad request: ')) {
775
+ if (response.failKind === fetch_2.ChatFailKind.TokenExpiredOrInvalid || response.failKind === fetch_2.ChatFailKind.ClientNotSupported || reason.includes('Bad request: ')) {
576
776
  return { type: commonTypes_1.ChatFetchResponseType.BadRequest, reason, requestId, serverRequestId };
577
777
  }
578
- if (response.failKind === fetch_1.ChatFailKind.ServerError) {
778
+ if (response.failKind === fetch_2.ChatFailKind.ServerError) {
579
779
  return { type: commonTypes_1.ChatFetchResponseType.Failed, reason, requestId, serverRequestId };
580
780
  }
581
- if (response.failKind === fetch_1.ChatFailKind.ContentFilter) {
781
+ if (response.failKind === fetch_2.ChatFailKind.ContentFilter) {
582
782
  return { type: commonTypes_1.ChatFetchResponseType.PromptFiltered, reason, category: openai_1.FilterReason.Prompt, requestId, serverRequestId };
583
783
  }
584
- if (response.failKind === fetch_1.ChatFailKind.AgentUnauthorized) {
784
+ if (response.failKind === fetch_2.ChatFailKind.AgentUnauthorized) {
585
785
  return { type: commonTypes_1.ChatFetchResponseType.AgentUnauthorized, reason, authorizationUrl: response.data.authorize_url, requestId, serverRequestId };
586
786
  }
587
- if (response.failKind === fetch_1.ChatFailKind.AgentFailedDependency) {
787
+ if (response.failKind === fetch_2.ChatFailKind.AgentFailedDependency) {
588
788
  return { type: commonTypes_1.ChatFetchResponseType.AgentFailedDependency, reason, requestId, serverRequestId };
589
789
  }
590
- if (response.failKind === fetch_1.ChatFailKind.ExtensionBlocked) {
790
+ if (response.failKind === fetch_2.ChatFailKind.ExtensionBlocked) {
591
791
  const retryAfter = typeof response.data?.retryAfter === 'number' ? response.data.retryAfter : 300;
592
792
  return { type: commonTypes_1.ChatFetchResponseType.ExtensionBlocked, reason, requestId, retryAfter, learnMoreLink: response.data?.learnMoreLink ?? '', serverRequestId };
593
793
  }
594
- if (response.failKind === fetch_1.ChatFailKind.NotFound) {
794
+ if (response.failKind === fetch_2.ChatFailKind.NotFound) {
595
795
  return { type: commonTypes_1.ChatFetchResponseType.NotFound, reason, requestId, serverRequestId };
596
796
  }
597
- if (response.failKind === fetch_1.ChatFailKind.InvalidPreviousResponseId) {
797
+ if (response.failKind === fetch_2.ChatFailKind.InvalidPreviousResponseId) {
598
798
  return { type: commonTypes_1.ChatFetchResponseType.InvalidStatefulMarker, reason, requestId, serverRequestId };
599
799
  }
600
800
  return { type: commonTypes_1.ChatFetchResponseType.Failed, reason, requestId, serverRequestId };
@@ -629,11 +829,13 @@ let ChatMLFetcherImpl = class ChatMLFetcherImpl extends AbstractChatMLFetcher {
629
829
  }
630
830
  this._logService.error(errorsUtil.fromUnknown(err), `Error on conversation request`);
631
831
  this._telemetryService.sendGHTelemetryException(err, 'Error on conversation request');
632
- // this.logger.exception(err, `Error on conversation request`);
832
+ const errorDetail = fetcher.getUserMessageForFetcherError(err);
833
+ const scrubbedErrorDetail = this.scrubErrorDetail(errorDetail);
633
834
  if (fetcher.isInternetDisconnectedError(err)) {
634
835
  return {
635
836
  type: commonTypes_1.ChatFetchResponseType.NetworkError,
636
837
  reason: `It appears you're not connected to the internet, please check your network connection and try again.`,
838
+ reasonDetail: scrubbedErrorDetail,
637
839
  requestId: requestId,
638
840
  serverRequestId: undefined,
639
841
  };
@@ -641,7 +843,8 @@ let ChatMLFetcherImpl = class ChatMLFetcherImpl extends AbstractChatMLFetcher {
641
843
  else if (fetcher.isFetcherError(err)) {
642
844
  return {
643
845
  type: commonTypes_1.ChatFetchResponseType.NetworkError,
644
- reason: fetcher.getUserMessageForFetcherError(err),
846
+ reason: errorDetail,
847
+ reasonDetail: scrubbedErrorDetail,
645
848
  requestId: requestId,
646
849
  serverRequestId: undefined,
647
850
  };
@@ -650,11 +853,21 @@ let ChatMLFetcherImpl = class ChatMLFetcherImpl extends AbstractChatMLFetcher {
650
853
  return {
651
854
  type: commonTypes_1.ChatFetchResponseType.Failed,
652
855
  reason: 'Error on conversation request. Check the log for more details.',
856
+ reasonDetail: scrubbedErrorDetail,
653
857
  requestId: requestId,
654
858
  serverRequestId: undefined,
655
859
  };
656
860
  }
657
861
  }
862
+ scrubErrorDetail(errorDetail) {
863
+ errorDetail = errorDetail.replaceAll(/(logged in as )([^\s]+)/ig, '$1<login>');
864
+ const username = this._authenticationService.copilotToken?.username;
865
+ if (!username) {
866
+ return errorDetail;
867
+ }
868
+ const regex = new RegExp((0, strings_1.escapeRegExpCharacters)(username), 'ig');
869
+ return errorDetail.replaceAll(regex, '<login>');
870
+ }
658
871
  };
659
872
  exports.ChatMLFetcherImpl = ChatMLFetcherImpl;
660
873
  exports.ChatMLFetcherImpl = ChatMLFetcherImpl = __decorate([
@@ -662,8 +875,11 @@ exports.ChatMLFetcherImpl = ChatMLFetcherImpl = __decorate([
662
875
  __param(1, telemetry_1.ITelemetryService),
663
876
  __param(2, requestLogger_1.IRequestLogger),
664
877
  __param(3, logService_1.ILogService),
665
- __param(4, instantiation_1.IInstantiationService),
666
- __param(5, conversationOptions_1.IConversationOptions)
878
+ __param(4, authentication_1.IAuthenticationService),
879
+ __param(5, interactionService_1.IInteractionService),
880
+ __param(6, chatQuotaService_1.IChatQuotaService),
881
+ __param(7, capiClient_1.ICAPIClientService),
882
+ __param(8, conversationOptions_1.IConversationOptions)
667
883
  ], ChatMLFetcherImpl);
668
884
  /**
669
885
  * Validates a chat request payload to ensure it is valid
@@ -690,4 +906,37 @@ function isValidChatPayload(messages, postOptions) {
690
906
  function asUnexpected(reason) {
691
907
  return `Prompt failed validation with the reason: ${reason}. Please file an issue.`;
692
908
  }
909
+ function createTelemetryData(chatEndpointInfo, location, headerRequestId) {
910
+ return telemetryData_1.TelemetryData.createAndMarkAsIssued({
911
+ endpoint: 'completions',
912
+ engineName: 'chat',
913
+ uiKind: commonTypes_1.ChatLocation.toString(location),
914
+ headerRequestId
915
+ });
916
+ }
917
+ /**
918
+ * WARNING: The value that is returned from this function drives the disablement of RAI for full-file rewrite requests
919
+ * in Copilot Edits, Copilot Chat, Agent Mode, and Inline Chat.
920
+ * If your chat location generates full-file rewrite requests and you are unsure if changing something here will cause problems, please talk to @roblourens
921
+ */
922
+ function locationToIntent(location) {
923
+ switch (location) {
924
+ case commonTypes_1.ChatLocation.Panel:
925
+ return 'conversation-panel';
926
+ case commonTypes_1.ChatLocation.Editor:
927
+ return 'conversation-inline';
928
+ case commonTypes_1.ChatLocation.EditingSession:
929
+ return 'conversation-edits';
930
+ case commonTypes_1.ChatLocation.Notebook:
931
+ return 'conversation-notebook';
932
+ case commonTypes_1.ChatLocation.Terminal:
933
+ return 'conversation-terminal';
934
+ case commonTypes_1.ChatLocation.Other:
935
+ return 'conversation-other';
936
+ case commonTypes_1.ChatLocation.Agent:
937
+ return 'conversation-agent';
938
+ case commonTypes_1.ChatLocation.ResponsesProxy:
939
+ return 'responses-proxy';
940
+ }
941
+ }
693
942
  //# sourceMappingURL=chatMLFetcher.js.map