@vscode/chat-lib 0.0.5-12 → 0.0.5-14

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